Nektar++
ProcessPerAlign.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: ProcessPerAlign.cpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: Reorder composites to align periodic boundaries.
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 
36 
37 #include <LocalRegions/SegExp.h>
38 #include <LocalRegions/QuadExp.h>
39 #include <LocalRegions/TriExp.h>
41 
43 
44 #include "ProcessPerAlign.h"
45 
46 #include <boost/algorithm/string.hpp>
48 
49 using namespace std;
50 using namespace Nektar::NekMeshUtils;
51 
52 namespace Nektar
53 {
54 namespace Utilities
55 {
56 
57 ModuleKey ProcessPerAlign::className =
59  ModuleKey(eProcessModule, "peralign"), ProcessPerAlign::create);
60 
61 /**
62  * @class ProcessPerAlign
63  */
64 
65 /**
66  * @brief Default constructor.
67  */
68 ProcessPerAlign::ProcessPerAlign(MeshSharedPtr m) : ProcessModule(m)
69 {
70  m_config["surf1"] =
71  ConfigOption(false, "-1", "Tag identifying first surface.");
72  m_config["surf2"] =
73  ConfigOption(false, "-1", "Tag identifying first surface.");
74  m_config["dir"] = ConfigOption(
75  false, "", "Direction in which to align (either x, y, or z; "
76  "or vector with components separated by a comma). "
77  "If rot is specified this is interpreted as the axis or rotation");
78  m_config["rot"] = ConfigOption(
79  false, "", "Rotation to align composites in radians, i.e. PI/20");
80  m_config["orient"] =
81  ConfigOption(true, "0", "Attempt to reorient tets and prisms");
82  m_config["tol"] =
83  ConfigOption(false, "1e-8", "Tolerance to which to check planes are the same after rotation/translation");
84 }
85 
86 /**
87  * @brief Destructor.
88  */
90 {
91 }
92 
94 {
95  int surf1 = m_config["surf1"].as<int>();
96  int surf2 = m_config["surf2"].as<int>();
97  string dir = m_config["dir"].as<string>();
98  string rot = m_config["rot"].as<string>();
99  bool orient = m_config["orient"].as<bool>();
100  string tolerance = m_config["tol"].as<string>();
101 
102  if (surf1 == -1)
103  {
104  cerr << "WARNING: surf1 must be set to a positive integer. "
105  << "Skipping periodic alignment." << endl;
106  return;
107  }
108 
109  if (surf2 == -1)
110  {
111  cerr << "WARNING: surf2 must be set to a positive integer. "
112  << "Skipping periodic alignment." << endl;
113  return;
114  }
115 
116  vector<string> tmp1;
117  boost::split(tmp1, dir, boost::is_any_of(","));
118 
119  vector<string> tmp2;
120  boost::split(tmp2, rot, boost::is_any_of(","));
121  bool rotalign = false;
122 
123  NekDouble vec[3] = {0.0, 0.0, 0.0};
124  NekDouble rotangle = 0.0;
125 
126  if (tmp2[0] != "")
127  {
128  // set up for syntax -m peralign:dir=“x":rot="PI/11":surf1=3:surf2=4:tol=1e-6
129  rotalign = true;
130  // Evaluate expression since may be give as function of PI
132  int ExprId = strEval.DefineFunction(" ", tmp2[0]);
133  rotangle = strEval.Evaluate(ExprId);
134 
135  // negate angle since we want to rotate second composite back
136  // to this one.
137  rotangle *= -1.0;
138 
139  ASSERTL0(tmp1.size() == 1,"rot must also be accompanied "
140  "with a dir=\"x\",dir=\"y\" or dir=\"z\" option "
141  "to specify axes of rotation");
142  }
143  else if (tmp1.size() == 1)
144  {
145  //if the direction is not specified and its a 2D mesh and there is CAD
146  //it can figure out the dir on its own
147  if (!dir.size() && m_mesh->m_spaceDim == 2 && m_mesh->m_cad)
148  {
150  m_mesh->m_cad->GetPeriodicTranslationVector(surf1, surf2);
151  NekDouble mag = sqrt(T[0] * T[0] + T[1] * T[1]);
152 
153  vec[0] = T[0] / mag;
154  vec[1] = T[1] / mag;
155  vec[2] = T[2] / mag;
156  }
157  else
158  {
159  if (dir != "x" && dir != "y" && dir != "z")
160  {
161  cerr << "WARNING: dir must be set to either x, y or z. "
162  << "Skipping periodic alignment." << endl;
163  return;
164  }
165 
166  vec[0] = (dir == "x") ? 1.0 : 0.0;
167  vec[1] = (dir == "y") ? 1.0 : 0.0;
168  vec[2] = (dir == "z") ? 1.0 : 0.0;
169  }
170  }
171  else if (tmp1.size() == 3)
172  {
173  vec[0] = boost::lexical_cast<NekDouble>(tmp1[0]);
174  vec[1] = boost::lexical_cast<NekDouble>(tmp1[1]);
175  vec[2] = boost::lexical_cast<NekDouble>(tmp1[2]);
176  }
177  else
178  {
179  ASSERTL0(false,"expected three components or letter for direction");
180  }
181 
182  auto it1 = m_mesh->m_composite.find(surf1);
183  auto it2 = m_mesh->m_composite.find(surf2);
184 
185  if (it1 == m_mesh->m_composite.end())
186  {
187  cerr << "WARNING: Couldn't find surface " << surf1
188  << ". Skipping periodic alignment." << endl;
189  return;
190  }
191 
192  if (it2 == m_mesh->m_composite.end())
193  {
194  cerr << "WARNING: Couldn't find surface " << surf2 << ", "
195  << "skipping periodic alignment." << endl;
196  return;
197  }
198 
199  CompositeSharedPtr c1 = it1->second;
200  CompositeSharedPtr c2 = it2->second;
201 
202  if (c1->m_items.size() != c2->m_items.size())
203  {
204  cerr << "WARNING: Surfaces " << surf1 << " and " << surf2
205  << " have different numbers of elements. Skipping periodic"
206  << " alignment." << endl;
207  return;
208  }
209 
210  c1->m_reorder = false;
211  c2->m_reorder = false;
212 
213  map<int, pair<FaceSharedPtr, vector<int> > > perFaces;
214 
215  // Loop over elements, calculate centroids of elements in c2.
216  map<int, Node> centroidMap;
217  for (int i = 0; i < c2->m_items.size(); ++i)
218  {
219  Node centroid;
220  for (int j = 0; j < c2->m_items[i]->GetVertexCount(); ++j)
221  {
222  centroid += *(c2->m_items[i]->GetVertex(j));
223  }
224  centroid /= (NekDouble)c2->m_items[i]->GetVertexCount();
225 
226 
227  if(rotalign) // rotate centroid
228  {
229  centroid.Rotate(dir,rotangle);
230  }
231 
232  centroidMap[i] = centroid;
233  }
234 
235  std::unordered_set<int> elmtDone;
236  map<int, int> elmtPairs;
237  map<int, int> vertCheck;
238 
239  for (int i = 0; i < c1->m_items.size(); ++i)
240  {
241  Node centroid;
242  for (int j = 0; j < c1->m_items[i]->GetVertexCount(); ++j)
243  {
244  centroid += *(c1->m_items[i]->GetVertex(j));
245  }
246  centroid /= (NekDouble)c1->m_items[i]->GetVertexCount();
247 
248  bool found = false;
249  NekDouble tol = boost::lexical_cast<NekDouble>(tolerance);
250  for (auto &it : centroidMap)
251  {
252  if (elmtDone.count(it.first) > 0)
253  {
254  continue;
255  }
256 
257  Node dx = it.second - centroid;
258  bool match;
259  if(rotalign)
260  {
261  match = (sqrt(dx.abs2())< tol);
262  }
263  else
264  {
265  match = (fabs(fabs(dx.m_x * vec[0] + dx.m_y * vec[1] +
266  dx.m_z * vec[2]) /
267  sqrt(dx.abs2()) - 1.0) < tol);
268  }
269 
270  if(match)
271  {
272  // Found match
273  int id1, id2;
274 
275  if (c1->m_items[i]->GetConf().m_e == LibUtilities::eSegment)
276  {
277  id1 = c1->m_items[i]->GetEdgeLink()->m_id;
278  id2 = c2->m_items[it.first]->GetEdgeLink()->m_id;
279  }
280  else
281  {
282  id1 = c1->m_items[i]->GetFaceLink()->m_id;
283  id2 = c2->m_items[it.first]->GetFaceLink()->m_id;
284  }
285 
286  elmtDone.insert(it.first);
287  elmtPairs[i] = it.first;
288 
289  // Identify periodic vertices
290  int nVerts = c1->m_items[i]->GetVertexCount();
291  vector<int> perVerts(nVerts, 0), perVertsInv(nVerts, 0);
292 
293  if (orient)
294  {
295  for (int k = 0; k < nVerts; ++k)
296  {
297  NodeSharedPtr n1 =
298  c1->m_items[i]->GetFaceLink()->m_vertexList[k];
299  int l;
300  NekDouble mindn = 1000;
301 
302  for (l = 0; l < nVerts; ++l)
303  {
304  NodeSharedPtr n2 = c2->m_items[it.first]
305  ->GetFaceLink()
306  ->m_vertexList[l];
307 
308  if(rotalign) // rotate n2
309  {
310  Node n2tmp = *n2;
311  n2tmp.Rotate(dir,rotangle);
312  Node dn = n2tmp - *n1;
313  NekDouble dnabs = sqrt(dn.abs2());
314  match = (dnabs< tol);
315  mindn = (dnabs < mindn)? dnabs:mindn;
316  }
317  else
318  {
319  Node dn = *n2 - *n1;
320 
321  NekDouble dnabs = fabs(fabs(dn.m_x * vec[0] +
322  dn.m_y * vec[1] +
323  dn.m_z * vec[2]) /
324  sqrt(dn.abs2()) - 1.0);
325  match = (dnabs < tol);
326  mindn = (dnabs < mindn)? dnabs:mindn;
327  }
328 
329  if(match)
330  {
331  perVerts[k] = l;
332  perVertsInv[l] = k;
333 
334  int id1 = n1->m_id;
335  int id2 = n2->m_id;
336  if (vertCheck.count(id1) == 0)
337  {
338  vertCheck[id1] = id2;
339  }
340  else
341  {
342  ASSERTL0(vertCheck[id1] == id2,
343  "Periodic vertex already "
344  "identified!");
345  }
346  break;
347  }
348  }
349  ASSERTL1(l < nVerts,
350  "Could not identify periodic vertices, nearest distance was " + boost::lexical_cast<string>(mindn));
351  }
352 
353  int tot1 = 0, tot2 = 0;
354  for (int k = 0; k < nVerts; ++k)
355  {
356  tot1 += perVerts[k];
357  tot2 += perVertsInv[k];
358  }
359  ASSERTL0(tot1 == nVerts * (nVerts - 1) / 2 &&
360  tot2 == nVerts * (nVerts - 1) / 2,
361  "Error identifying periodic vertices");
362  }
363 
364  if (c2->m_items[i]->GetConf().m_e != LibUtilities::eSegment)
365  {
366  perFaces[id1] = make_pair(
367  c2->m_items[it.first]->GetFaceLink(), perVerts);
368  perFaces[id2] =
369  make_pair(c1->m_items[i]->GetFaceLink(), perVertsInv);
370  }
371  found = true;
372  break;
373  }
374  }
375 
376  if (!found)
377  {
378  cerr << "WARNING: Could not find matching edge for surface "
379  << "element " << c1->m_items[i]->GetId() << ". "
380  << "Skipping periodic alignment." << endl;
381  return;
382  }
383  }
384 
385  // Reorder vectors.
386  vector<ElementSharedPtr> tmp = c2->m_items;
387 
388  for (int i = 0; i < tmp.size(); ++i)
389  {
390  c2->m_items[i] = tmp[elmtPairs[i]];
391  }
392 
393  if (orient)
394  {
395  ReorderPrisms(perFaces);
396  }
397 }
398 
399 }
400 }
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
int DefineFunction(const std::string &vlist, const std::string &expr)
Defines a function for the purposes of evaluation.
NEKMESHUTILS_EXPORT NekDouble abs2() const
Definition: Node.h:156
NekDouble Evaluate(const int id)
Evaluate a function which depends only on constants and/or parameters.
STL namespace.
NekDouble m_y
Y-coordinate.
Definition: Node.h:410
std::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Definition: Mesh.h:156
Represents a point in the domain.
Definition: Node.h:62
std::shared_ptr< Node > NodeSharedPtr
Definition: CADVert.h:49
NEKMESHUTILS_EXPORT void ReorderPrisms(PerMap &perFaces)
Reorder node IDs so that prisms and tetrahedra are aligned correctly.
std::pair< ModuleType, std::string > ModuleKey
int m_id
ID of node.
Definition: Node.h:406
Represents a command-line configuration option.
void Rotate(std::string dir, NekDouble angle)
Definition: Node.h:339
double NekDouble
std::map< std::string, ConfigOption > m_config
List of configuration values.
Interpreter class for the evaluation of mathematical expressions.
Definition: Interpreter.h:77
NekDouble m_x
X-coordinate.
Definition: Node.h:408
Abstract base class for processing modules.
virtual void Process()
Write mesh to output file.
virtual ~ProcessPerAlign()
Destructor.
std::shared_ptr< Composite > CompositeSharedPtr
Shared pointer to a composite.
Definition: Composite.h:70
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:199
NekDouble m_z
Z-coordinate.
Definition: Node.h:412
std::pair< ModuleType, std::string > ModuleKey
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
Definition: ErrorUtil.hpp:250
ModuleFactory & GetModuleFactory()