Nektar++
2DGenerator.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: Generator2D.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: 2D generator object methods.
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 #include <algorithm>
35 #include <math.h>
36 
38 
40 
43 
44 #include <boost/algorithm/string.hpp>
45 
46 using namespace std;
47 namespace Nektar
48 {
49 namespace NekMeshUtils
50 {
51 
52 ModuleKey Generator2D::className = GetModuleFactory().RegisterCreatorFunction(
53  ModuleKey(eProcessModule, "2dgenerator"), Generator2D::create,
54  "Generates a 2D mesh");
55 
56 Generator2D::Generator2D(MeshSharedPtr m) : ProcessModule(m)
57 {
58  m_config["blcurves"] =
59  ConfigOption(false, "", "Generate parallelograms on these curves");
60  m_config["blthick"] =
61  ConfigOption(false, "0.0", "Parallelogram layer thickness");
62  m_config["periodic"] =
63  ConfigOption(false, "", "Set of pairs of periodic curves");
64  m_config["bltadjust"] =
65  ConfigOption(false, "2.0", "Boundary layer thickness adjustment");
66  m_config["adjustblteverywhere"] =
67  ConfigOption(true, "0", "Adjust thickness everywhere");
68  m_config["smoothbl"] =
69  ConfigOption(true, "0", "Smooth the BL normal directions to avoid "
70  "(nearly) intersecting normals");
71  m_config["spaceoutbl"] = ConfigOption(
72  false, "0.5", "Threshold to space out BL according to Delta");
73  m_config["nospaceoutsurf"] =
74  ConfigOption(false, "", "Surfaces where spacing out shouldn't be used");
75 }
76 
78 {
79 }
80 
82 {
83  // Check that cad is 2D
84  Array<OneD, NekDouble> bndBox = m_mesh->m_cad->GetBoundingBox();
85  ASSERTL0(fabs(bndBox[5] - bndBox[4]) < 1.0e-7, "CAD isn't 2D");
86 
87  if (m_mesh->m_verbose)
88  {
89  cout << endl << "2D meshing" << endl;
90  cout << endl << "\tCurve meshing:" << endl << endl;
91  }
92  m_mesh->m_numNodes = m_mesh->m_cad->GetNumVerts();
94  m_thickness.DefineFunction("x y z", m_config["blthick"].as<string>());
95  ParseUtils::GenerateSeqVector(m_config["blcurves"].as<string>(),
96  m_blCurves);
97 
98  // find the ends of the BL curves
99  if (m_config["blcurves"].beenSet)
100  {
101  FindBLEnds();
102  }
103 
104  // linear mesh all curves
105  for (int i = 1; i <= m_mesh->m_cad->GetNumCurve(); i++)
106  {
107  if (m_mesh->m_verbose)
108  {
109  LibUtilities::PrintProgressbar(i, m_mesh->m_cad->GetNumCurve(),
110  "Curve progress");
111  }
112  vector<unsigned int>::iterator f =
113  find(m_blCurves.begin(), m_blCurves.end(), i);
114  if (f == m_blCurves.end())
115  {
116  m_curvemeshes[i] =
118 
119  // Fheck if this curve is at an end of the BL
120  // If so, define an offset for the second node, corresponding to the
121  // BL thickness
122  if (m_blends.count(i))
123  {
124  vector<CADVertSharedPtr> vertices =
125  m_mesh->m_cad->GetCurve(i)->GetVertex();
127  NekDouble t;
128 
129  // offset needed at first node (or both)
130  if (m_blends[i] == 0 || m_blends[i] == 2)
131  {
132  loc = vertices[0]->GetLoc();
133  t = m_thickness.Evaluate(m_thickness_ID, loc[0], loc[1],
134  loc[2], 0.0);
135  m_curvemeshes[i]->SetOffset(0, t);
136  }
137  // offset needed at second node (or both)
138  if (m_blends[i] == 1 || m_blends[i] == 2)
139  {
140  loc = vertices[1]->GetLoc();
141  t = m_thickness.Evaluate(m_thickness_ID, loc[0], loc[1],
142  loc[2], 0.0);
143  m_curvemeshes[i]->SetOffset(1, t);
144  }
145  }
146  }
147  else
148  {
150  i, m_mesh, m_config["blthick"].as<string>());
151  }
152  m_curvemeshes[i]->Mesh();
153  }
154 
155  ////////
156  // consider periodic curves
157 
158  if (m_config["periodic"].beenSet)
159  {
160  PeriodicPrep();
161  MakePeriodic();
162  }
163 
164  ////////////////////////////////////////
165 
166  if (m_config["blcurves"].beenSet)
167  {
168  // we need to do the boundary layer generation in a face by face basis
169  MakeBLPrep();
170  for (int i = 1; i <= m_mesh->m_cad->GetNumSurf(); i++)
171  {
172  MakeBL(i);
173  }
174 
175  // If the BL doesn't form closed loops, we need to remove the outside
176  // nodes from the curve meshes
177  for (auto &ic : m_blends)
178  {
179  vector<NodeSharedPtr> nodes =
180  m_curvemeshes[ic.first]->GetMeshPoints();
181 
182  if (ic.second == 0 || ic.second == 2)
183  {
184  nodes.erase(nodes.begin());
185  }
186  if (ic.second == 1 || ic.second == 2)
187  {
188  nodes.erase(nodes.end() - 1);
189  }
190 
191  // Rebuild the curvemesh without the first node, the last node or
192  // both
193  m_curvemeshes[ic.first] =
195  nodes);
196  }
197  }
198 
199  if (m_mesh->m_verbose)
200  {
201  cout << endl << "\tFace meshing:" << endl << endl;
202  }
203  // linear mesh all surfaces
204  for (int i = 1; i <= m_mesh->m_cad->GetNumSurf(); i++)
205  {
206  if (m_mesh->m_verbose)
207  {
208  LibUtilities::PrintProgressbar(i, m_mesh->m_cad->GetNumSurf(),
209  "Face progress");
210  }
211 
213  i, m_mesh, m_curvemeshes, 99 + i);
214  m_facemeshes[i]->Mesh();
215  }
216 
217  ////////////////////////////////////
218 
219  EdgeSet::iterator it;
220  for (auto &it : m_mesh->m_edgeSet)
221  {
222  vector<NodeSharedPtr> ns;
223  ns.push_back(it->m_n1);
224  ns.push_back(it->m_n2);
225  // for each iterator create a LibUtilities::eSegement
226  // push segment into m_mesh->m_element[1]
227  // tag for the elements shoudl be the CAD number of the curves
228  ElmtConfig conf(LibUtilities::eSegment, 1, false, false);
229  vector<int> tags;
230  tags.push_back(it->m_parentCAD->GetId());
232  LibUtilities::eSegment, conf, ns, tags);
233  m_mesh->m_element[1].push_back(E2);
234  }
235 
236  ProcessVertices();
237  ProcessEdges();
238  ProcessFaces();
239  ProcessElements();
241  Report();
242 }
243 
245 {
246  // Set of CAD vertices
247  // Vertices of each curve are added to the set if not found and removed from
248  // the set if found
249  // This leaves us with a set of vertices that are at the end of BL open
250  // loops
251  set<CADVertSharedPtr> cadverts;
252 
253  for (auto &it : m_blCurves)
254  {
255  vector<CADVertSharedPtr> vertices =
256  m_mesh->m_cad->GetCurve(it)->GetVertex();
257 
258  for (auto &iv : vertices)
259  {
260  set<CADVertSharedPtr>::iterator is = cadverts.find(iv);
261 
262  if (is != cadverts.end())
263  {
264  cadverts.erase(is);
265  }
266  else
267  {
268  cadverts.insert(iv);
269  }
270  }
271  }
272 
273  // Build m_blends based on the previously constructed set of vertices
274  // m_blends is a map of curve number (the curves right outside the BL open
275  // loops) to the offset node number: 0, 1 or 2 (for both)
276  for (int i = 1; i <= m_mesh->m_cad->GetNumCurve(); ++i)
277  {
278  if (find(m_blCurves.begin(), m_blCurves.end(), i) != m_blCurves.end())
279  {
280  continue;
281  }
282 
283  vector<CADVertSharedPtr> vertices =
284  m_mesh->m_cad->GetCurve(i)->GetVertex();
285 
286  for (int j = 0; j < 2; ++j)
287  {
288  if (!cadverts.count(vertices[j]))
289  {
290  continue;
291  }
292 
293  if (m_blends.count(i))
294  {
295  m_blends[i] = 2;
296  }
297  else
298  {
299  m_blends[i] = j;
300  }
301  }
302  }
303 }
304 
306 {
307  if (m_mesh->m_verbose)
308  {
309  cout << endl << "\tBoundary layer meshing:" << endl << endl;
310  }
311 
312  // identify the nodes and edges which will become the boundary layer.
313 
314  for (auto &it : m_blCurves)
315  {
316  vector<EdgeSharedPtr> localedges = m_curvemeshes[it]->GetMeshEdges();
317  for (auto &ie : localedges)
318  {
319  m_blEdges.push_back(ie);
320  m_nodesToEdge[ie->m_n1].push_back(ie);
321  m_nodesToEdge[ie->m_n2].push_back(ie);
322  }
323  }
324 }
325 
326 void Generator2D::MakeBL(int faceid)
327 {
328  map<int, Array<OneD, NekDouble>> edgeNormals;
329  int eid = 0;
330  for (auto &it : m_blCurves)
331  {
333  m_mesh->m_cad->GetCurve(it)->GetOrienationWRT(faceid);
334  vector<EdgeSharedPtr> es = m_curvemeshes[it]->GetMeshEdges();
335  // on each !!!EDGE!!! calculate a normal
336  // always to the left unless edgeo is 1
337  // normal must be done in the parametric space (and then projected back)
338  // because of face orientation
339  for (auto &ie : es)
340  {
341  ie->m_id = eid++;
342  Array<OneD, NekDouble> p1, p2;
343  p1 = ie->m_n1->GetCADSurfInfo(faceid);
344  p2 = ie->m_n2->GetCADSurfInfo(faceid);
346  n[0] = p1[1] - p2[1];
347  n[1] = p2[0] - p1[0];
348  if (edgeo == CADOrientation::eBackwards)
349  {
350  n[0] *= -1.0;
351  n[1] *= -1.0;
352  }
353  NekDouble mag = sqrt(n[0] * n[0] + n[1] * n[1]);
354  n[0] /= mag;
355  n[1] /= mag;
357  np[0] = p1[0] + n[0];
358  np[1] = p1[1] + n[1];
359  Array<OneD, NekDouble> loc = ie->m_n1->GetLoc();
360  Array<OneD, NekDouble> locp = m_mesh->m_cad->GetSurf(faceid)->P(np);
361  n[0] = locp[0] - loc[0];
362  n[1] = locp[1] - loc[1];
363  mag = sqrt(n[0] * n[0] + n[1] * n[1]);
364  n[0] /= mag;
365  n[1] /= mag;
366  edgeNormals[ie->m_id] = n;
367  }
368  }
369 
370  bool adjust = m_config["bltadjust"].beenSet;
371  NekDouble divider = m_config["bltadjust"].as<NekDouble>();
372  bool adjustEverywhere = m_config["adjustblteverywhere"].beenSet;
373  bool smoothbl = m_config["smoothbl"].beenSet;
374  bool spaceoutbl = m_config["spaceoutbl"].beenSet;
375  NekDouble spaceoutthr = m_config["spaceoutbl"].as<NekDouble>();
376 
377  if (divider < 2.0)
378  {
379  WARNINGL0(false, "BndLayerAdjustment too low, corrected to 2.0");
380  divider = 2.0;
381  }
382 
383  map<NodeSharedPtr, NodeSharedPtr> nodeNormals;
384  for (auto &it : m_nodesToEdge)
385  {
386  ASSERTL0(it.second.size() == 1 || it.second.size() == 2,
387  "weirdness, most likely bl_surfs are incorrect");
388 
389  // If node at the end of the BL open loop, the "normal node" isn't
390  // constructed by computing a normal but found on the adjacent curve
391  if (it.second.size() == 1)
392  {
393  vector<CADCurveSharedPtr> curves = it.first->GetCADCurves();
394 
395  vector<EdgeSharedPtr> edges =
396  m_curvemeshes[curves[0]->GetId()]->GetMeshEdges();
397  vector<EdgeSharedPtr>::iterator ie =
398  find(edges.begin(), edges.end(), it.second[0]);
399  int rightCurve =
400  (ie == edges.end()) ? curves[0]->GetId() : curves[1]->GetId();
401 
402  vector<NodeSharedPtr> nodes =
403  m_curvemeshes[rightCurve]->GetMeshPoints();
404  nodeNormals[it.first] =
405  (nodes[0] == it.first) ? nodes[1] : nodes[nodes.size() - 2];
406 
407  continue;
408  }
409 
410  Array<OneD, NekDouble> n(3, 0.0);
411  Array<OneD, NekDouble> n1 = edgeNormals[it.second[0]->m_id];
412  Array<OneD, NekDouble> n2 = edgeNormals[it.second[1]->m_id];
413  n[0] = (n1[0] + n2[0]) / 2.0;
414  n[1] = (n1[1] + n2[1]) / 2.0;
415  NekDouble mag = sqrt(n[0] * n[0] + n[1] * n[1]);
416  n[0] /= mag;
417  n[1] /= mag;
418  NekDouble t = m_thickness.Evaluate(m_thickness_ID, it.first->m_x,
419  it.first->m_y, 0.0, 0.0);
420  // Adjust thickness according to angle between normals
421  if (adjust)
422  {
423  if (adjustEverywhere || it.first->GetNumCadCurve() > 1)
424  {
425  NekDouble angle = acos(n1[0] * n2[0] + n1[1] * n2[1]);
426  angle = (angle > M_PI) ? 2 * M_PI - angle : angle;
427  t /= cos(angle / divider);
428  }
429  }
430 
431  n[0] = n[0] * t + it.first->m_x;
432  n[1] = n[1] * t + it.first->m_y;
433  NodeSharedPtr nn = std::shared_ptr<Node>(
434  new Node(m_mesh->m_numNodes++, n[0], n[1], 0.0));
435  CADSurfSharedPtr s = m_mesh->m_cad->GetSurf(faceid);
436  Array<OneD, NekDouble> uv = s->locuv(n);
437  nn->SetCADSurf(s, uv);
438  nodeNormals[it.first] = nn;
439  }
440 
441  // Check for any intersecting boundary layer normals and smooth them if
442  // needed
443  if (smoothbl)
444  {
445  // Nodes that need normal smoothing and their unit normal
446  map<NodeSharedPtr, vector<NodeSharedPtr>> unitNormals;
447  // Nodes that need normal smoothing and their BL thickness
448  map<NodeSharedPtr, NekDouble> dist;
449 
450  int count = 0;
451 
452  do
453  {
454  unitNormals.clear();
455  dist.clear();
456 
457  for (const auto &it : m_blEdges)
458  {
459  // Line intersection based on
460  // https://stackoverflow.com/a/565282/7241595
461  NodeSharedPtr p = it->m_n1;
462  NodeSharedPtr q = it->m_n2;
463 
464  Node r = *nodeNormals[p] - *p;
465  Node s = *nodeNormals[q] - *q;
466 
467  NekDouble d = r.curl(s).m_z;
468 
469  // Should probably use tolerance to check parallelism
470  if (d == 0)
471  {
472  continue;
473  }
474 
475  NekDouble t = (*q - *p).curl(s).m_z / d;
476  NekDouble u = (*q - *p).curl(r).m_z / d;
477 
478  // Check for intersection of the infinite continuation of one
479  // normal with the other. A tolerance of 0.5 times the length of
480  // thenormalis used. Could maybe be decreased to a less
481  // aggressive value.
482  if ((-0.5 < t && t <= 1.5) || (-0.5 < u && u <= 1.5))
483  {
484  dist[p] = sqrt(r.abs2());
485  dist[q] = sqrt(s.abs2());
486 
487  NodeSharedPtr sum =
488  make_shared<Node>(r / dist[p] + s / dist[q]);
489 
490  unitNormals[p].push_back(sum);
491  unitNormals[q].push_back(sum);
492  }
493  }
494 
495  // Smooth each normal one by one
496  for (const auto &it : unitNormals)
497  {
498  Node avg(0, 0.0, 0.0, 0.0);
499 
500  for (const auto &i : it.second)
501  {
502  avg += *i;
503  }
504 
505  avg /= sqrt(avg.abs2());
506 
507  // Create new BL node with smoothed normal
508  NodeSharedPtr nn = std::shared_ptr<Node>(
509  new Node(nodeNormals[it.first]->GetID(),
510  it.first->m_x + avg.m_x * dist[it.first],
511  it.first->m_y + avg.m_y * dist[it.first], 0.0));
512  CADSurfSharedPtr s =
513  *nodeNormals[it.first]->GetCADSurfs().begin();
514  Array<OneD, NekDouble> uv = s->locuv(nn->GetLoc());
515  nn->SetCADSurf(s, uv);
516 
517  nodeNormals[it.first] = nn;
518  }
519  } while (unitNormals.size() && count++ < 50);
520 
521  if (m_mesh->m_verbose)
522  {
523  if (count < 50)
524  {
525  cout << "\t\tNormals smoothed in " << count << " iterations."
526  << endl;
527  }
528  else
529  {
530  cout << "\t\tNormals smoothed. Algorithm didn't converge after "
531  << count << " iterations." << endl;
532  }
533  }
534  }
535 
536  // Space out the outter BL nodes to better fit the local required Delta
537  if (spaceoutbl)
538  {
539  if (spaceoutthr < 0.0 || spaceoutthr > 1.0)
540  {
541  WARNINGL0(false, "The boundary layer space out threshold should be "
542  "between 0 and 1. It will now be adjusted to "
543  "0.5.");
544  spaceoutthr = 0.5;
545  }
546 
547  vector<unsigned int> nospaceoutsurf;
548  ParseUtils::GenerateSeqVector(m_config["nospaceoutsurf"].as<string>(),
549  nospaceoutsurf);
550 
551  // List of connected nodes at need spacing out
552  vector<deque<NodeSharedPtr>> nodesToMove;
553 
554  int count = 0;
555 
556  // This will supposedly spread the number of nodes to be moved until
557  // sufficient space is found
558  do
559  {
560  nodesToMove.clear();
561 
562  // Find which nodes need to be spaced out
563  for (const auto &ie : m_blEdges)
564  {
565  auto it = find(nospaceoutsurf.begin(), nospaceoutsurf.end(),
566  ie->m_parentCAD->GetId());
567  if (it != nospaceoutsurf.end())
568  {
569  continue;
570  }
571 
572  NodeSharedPtr n1 = nodeNormals[ie->m_n1];
573  NodeSharedPtr n2 = nodeNormals[ie->m_n2];
574 
575  NekDouble targetD =
576  m_mesh->m_octree->Query(((*n1 + *n2) / 2.0).GetLoc());
577  NekDouble realD = sqrt((*n1 - *n2).abs2());
578 
579  // Add nodes if condition fulfilled
580  if (realD < spaceoutthr * targetD)
581  {
582  bool connected = false;
583 
584  for (auto &il : nodesToMove)
585  {
586  if (il.front() == n1)
587  {
588  il.push_front(n2);
589  connected = true;
590  break;
591  }
592  if (il.front() == n2)
593  {
594  il.push_front(n1);
595  connected = true;
596  break;
597  }
598  if (il.back() == n1)
599  {
600  il.push_back(n2);
601  connected = true;
602  break;
603  }
604  if (il.back() == n2)
605  {
606  il.push_back(n1);
607  connected = true;
608  break;
609  }
610  }
611 
612  // Create new set of connected nodes if necessary
613  if (!connected)
614  {
615  deque<NodeSharedPtr> newList;
616  newList.push_back(n1);
617  newList.push_back(n2);
618 
619  nodesToMove.push_back(newList);
620  }
621  }
622  }
623 
624  for (int i = 0;; ++i)
625  {
626  // Reconnect sets of connected nodes together if need be. Done
627  // once before ad once after expanding the set by one node to
628  // find extra space.
629  for (int i1 = 0; i1 < nodesToMove.size(); ++i1)
630  {
631  NodeSharedPtr n11 = nodesToMove[i1].front();
632  NodeSharedPtr n12 = nodesToMove[i1].back();
633 
634  for (int i2 = i1 + 1; i2 < nodesToMove.size(); ++i2)
635  {
636  NodeSharedPtr n21 = nodesToMove[i2].front();
637  NodeSharedPtr n22 = nodesToMove[i2].back();
638 
639  if (n11 == n21 || n11 == n22 || n12 == n21 ||
640  n12 == n22)
641  {
642  if (n11 == n21 || n12 == n22)
643  {
644  reverse(nodesToMove[i2].begin(),
645  nodesToMove[i2].end());
646  n21 = nodesToMove[i2].front();
647  n22 = nodesToMove[i2].back();
648  }
649 
650  if (n11 == n22)
651  {
652  nodesToMove[i1].insert(nodesToMove[i1].begin(),
653  nodesToMove[i2].begin(),
654  nodesToMove[i2].end() -
655  1);
656  }
657  else
658  {
659  nodesToMove[i1].insert(nodesToMove[i1].end(),
660  nodesToMove[i2].begin() +
661  1,
662  nodesToMove[i2].end());
663  }
664 
665  nodesToMove.erase(nodesToMove.begin() + i2);
666  continue;
667  }
668  }
669  }
670 
671  if (i >= 1)
672  {
673  break;
674  }
675 
676  set<EdgeSharedPtr> addedEdges;
677 
678  // Expand each set of connected nodes by one node to allow for
679  // extra space
680  for (auto &il : nodesToMove)
681  {
682  NodeSharedPtr n11 = *(il.begin() + 0);
683  NodeSharedPtr n12 = *(il.begin() + 1);
684 
685  NodeSharedPtr n13 = *(il.rbegin() + 1);
686  NodeSharedPtr n14 = *(il.rbegin() + 0);
687 
688  for (const auto &ie : m_blEdges)
689  {
690  auto it =
691  find(nospaceoutsurf.begin(), nospaceoutsurf.end(),
692  ie->m_parentCAD->GetId());
693  if (addedEdges.count(ie) || it != nospaceoutsurf.end())
694  {
695  continue;
696  }
697 
698  NodeSharedPtr n21 = nodeNormals[ie->m_n1];
699  NodeSharedPtr n22 = nodeNormals[ie->m_n2];
700 
701  NodeSharedPtr frontPush, backPush;
702 
703  if (n11)
704  {
705  if (n11 == n21 && n12 != n22)
706  {
707  frontPush = n22;
708  }
709  else if (n11 == n22 && n12 != n21)
710  {
711  frontPush = n21;
712  }
713 
714  if (frontPush)
715  {
716  il.push_front(frontPush);
717  n11.reset();
718  addedEdges.insert(ie);
719  }
720  }
721  if (n14 && !frontPush)
722  {
723  if (n14 == n21 && n13 != n22)
724  {
725  backPush = n22;
726  }
727  if (n14 == n22 && n13 != n21)
728  {
729  backPush = n21;
730  }
731 
732  if (backPush)
733  {
734  il.push_back(backPush);
735  n14.reset();
736  addedEdges.insert(ie);
737  }
738  }
739 
740  if (!n11 && !n14)
741  {
742  break;
743  }
744  }
745  }
746  }
747 
748  // Actual spacing out of the nodes. Done by simple linear
749  // interpolation between the 2 end nodes. Weights come from the
750  // required Delta or each edge.
751  for (const auto &il : nodesToMove)
752  {
753  NodeSharedPtr ni = il.front();
754  NodeSharedPtr nf = il.back();
755 
756  vector<NekDouble> deltas;
757  NekDouble total = 0.0;
758 
759  for (int i = 0; i < il.size() - 1; ++i)
760  {
761  NodeSharedPtr n1 = il[i];
762  NodeSharedPtr n2 = il[i + 1];
763 
764  deltas.push_back(
765  m_mesh->m_octree->Query(((*n1 + *n2) / 2.0).GetLoc()));
766  total += deltas.back();
767  }
768 
769  for (auto &id : deltas)
770  {
771  id /= total;
772  }
773 
774  NekDouble runningTotal = 0.0;
775 
776  for (int i = 1; i < il.size() - 1; ++i)
777  {
778  runningTotal += deltas[i - 1];
780  (*ni * (1.0 - runningTotal) + *nf * runningTotal)
781  .GetLoc();
782 
784  m_mesh->m_cad->GetSurf(faceid)->locuv(loc);
785 
786  il[i]->Move(loc, faceid, uv);
787  }
788  }
789  } while (nodesToMove.size() && count++ < 50);
790 
791  if (m_mesh->m_verbose)
792  {
793  if (count < 50)
794  {
795  cout << "\t\tBL spaced out in " << count << " iterations."
796  << endl;
797  }
798  else
799  {
800  cout << "\t\tBL spaced out. Algorithm didn't converge after "
801  << count << " iterations." << endl;
802  }
803  }
804  }
805 
806  for (auto &it : m_blCurves)
807  {
809  m_mesh->m_cad->GetCurve(it)->GetOrienationWRT(faceid);
810  vector<NodeSharedPtr> ns = m_curvemeshes[it]->GetMeshPoints();
811  vector<NodeSharedPtr> newNs;
812  for (auto &in : ns)
813  {
814  newNs.push_back(nodeNormals[in]);
815  }
816  m_curvemeshes[it] =
818  if (edgeo == CADOrientation::eBackwards)
819  {
820  reverse(ns.begin(), ns.end());
821  }
822  for (int i = 0; i < ns.size() - 1; ++i)
823  {
824  vector<NodeSharedPtr> qns;
825  qns.push_back(ns[i]);
826  qns.push_back(ns[i + 1]);
827  qns.push_back(nodeNormals[ns[i + 1]]);
828  qns.push_back(nodeNormals[ns[i]]);
829  ElmtConfig conf(LibUtilities::eQuadrilateral, 1, false, false);
830  vector<int> tags;
831  tags.push_back(101);
833  LibUtilities::eQuadrilateral, conf, qns, tags);
834  E->m_parentCAD = m_mesh->m_cad->GetSurf(faceid);
835  for (int j = 0; j < E->GetEdgeCount(); ++j)
836  {
837  pair<EdgeSet::iterator, bool> testIns;
838  EdgeSharedPtr ed = E->GetEdge(j);
839  // look for edge in m_mesh edgeset from curves
840  EdgeSet::iterator s = m_mesh->m_edgeSet.find(ed);
841  if (!(s == m_mesh->m_edgeSet.end()))
842  {
843  ed = *s;
844  E->SetEdge(j, *s);
845  }
846  }
847  m_mesh->m_element[2].push_back(E);
848  }
849  }
850 }
851 
853 {
854  m_periodicPairs.clear();
855  set<unsigned> periodic;
856 
857  // Build periodic curve pairs
858  string s = m_config["periodic"].as<string>();
859  vector<string> lines;
860 
861  boost::split(lines, s, boost::is_any_of(":"));
862 
863  for (auto &il : lines)
864  {
865  vector<string> tmp;
866  boost::split(tmp, il, boost::is_any_of(","));
867 
868  ASSERTL0(tmp.size() == 2, "periodic pairs ill-defined");
869 
870  vector<unsigned> data(2);
871  data[0] = boost::lexical_cast<unsigned>(tmp[0]);
872  data[1] = boost::lexical_cast<unsigned>(tmp[1]);
873 
874  ASSERTL0(!periodic.count(data[0]), "curve already periodic");
875  ASSERTL0(!periodic.count(data[1]), "curve already periodic");
876 
877  m_periodicPairs[data[0]] = data[1];
878  periodic.insert(data[0]);
879  periodic.insert(data[1]);
880  }
881 }
882 
884 {
885  // Override slave curves
886 
887  for (auto &ip : m_periodicPairs)
888  {
889  m_curvemeshes[ip.second]->PeriodicOverwrite(m_curvemeshes[ip.first]);
890  }
891 
892  if (m_mesh->m_verbose)
893  {
894  cout << "\t\tPeriodic boundary conditions" << endl;
895  for (auto &it : m_periodicPairs)
896  {
897  cout << "\t\t\tCurves " << it.first << " => " << it.second << endl;
898  }
899  cout << endl;
900  }
901 }
902 
904 {
905  if (m_mesh->m_verbose)
906  {
907  int ns = m_mesh->m_vertexSet.size();
908  int es = m_mesh->m_edgeSet.size();
909  int ts = m_mesh->m_element[2].size();
910  int ep = ns - es + ts;
911  cout << endl << "\tSurface mesh statistics" << endl;
912  cout << "\t\tNodes: " << ns << endl;
913  cout << "\t\tEdges: " << es << endl;
914  cout << "\t\tTriangles " << ts << endl;
915  cout << "\t\tEuler-PoincarĂ© characteristic: " << ep << endl;
916  }
917 }
918 }
919 }
std::shared_ptr< CADSurf > CADSurfSharedPtr
Definition: CADCurve.h:51
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
Basic information about an element.
Definition: ElementConfig.h:49
int PrintProgressbar(const int position, const int goal, const std::string message, int lastprogress=-1)
Prints a progressbar.
Definition: Progressbar.hpp:67
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
std::vector< unsigned int > m_blCurves
list of curves needing a BL
Definition: 2DGenerator.h:87
NekDouble Evaluate(const int id)
Evaluate a function which depends only on constants and/or parameters.
std::map< int, FaceMeshSharedPtr > m_facemeshes
map of individual surface meshes from parametric surfaces
Definition: 2DGenerator.h:81
STL namespace.
NekDouble m_y
Y-coordinate.
Definition: Node.h:410
std::shared_ptr< Edge > EdgeSharedPtr
Shared pointer to an edge.
Definition: Edge.h:136
std::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Definition: Mesh.h:156
ElementFactory & GetElementFactory()
Definition: Element.cpp:44
std::map< unsigned, unsigned > m_blends
map of curves and Bl ends: 0, 1 or 2 (for both)
Definition: 2DGenerator.h:89
Represents a point in the domain.
Definition: Node.h:62
std::shared_ptr< Node > NodeSharedPtr
Definition: CADVert.h:49
int m_thickness_ID
BL thickness expression ID.
Definition: 2DGenerator.h:95
std::map< unsigned, unsigned > m_periodicPairs
map of periodic curve pairs
Definition: 2DGenerator.h:85
LibUtilities::Interpreter m_thickness
BL thickness expression.
Definition: 2DGenerator.h:93
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
std::pair< ModuleType, std::string > ModuleKey
virtual NEKMESHUTILS_EXPORT void ProcessFaces(bool ReprocessFaces=true)
Extract element faces.
std::shared_ptr< Element > ElementSharedPtr
Definition: Edge.h:49
virtual NEKMESHUTILS_EXPORT void ProcessElements()
Generate element IDs.
Represents a command-line configuration option.
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:223
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
std::vector< EdgeSharedPtr > m_blEdges
list of BL edges
Definition: 2DGenerator.h:91
double NekDouble
std::map< std::string, ConfigOption > m_config
List of configuration values.
NEKMESHUTILS_EXPORT Node curl(const Node &pSrc) const
Definition: Node.h:166
NekDouble m_x
X-coordinate.
Definition: Node.h:408
Abstract base class for processing modules.
virtual NEKMESHUTILS_EXPORT void ProcessVertices()
Extract element vertices.
virtual NEKMESHUTILS_EXPORT void ProcessEdges(bool ReprocessEdges=true)
Extract element edges.
InputIterator find(InputIterator first, InputIterator last, InputIterator startingpoint, const EqualityComparable &value)
Definition: StdRegions.hpp:358
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:199
std::map< NodeSharedPtr, std::vector< EdgeSharedPtr > > m_nodesToEdge
map of BL curve nodes to adjacent edges
Definition: 2DGenerator.h:97
NekDouble m_z
Z-coordinate.
Definition: Node.h:412
std::pair< ModuleType, std::string > ModuleKey
std::map< int, CurveMeshSharedPtr > m_curvemeshes
map of individual curve meshes of the curves in the domain
Definition: 2DGenerator.h:83
virtual NEKMESHUTILS_EXPORT void ProcessComposites()
Generate composites.
ModuleFactory & GetModuleFactory()
static bool GenerateSeqVector(const std::string &str, std::vector< unsigned int > &out)
Takes a comma-separated compressed string and converts it to entries in a vector. ...
Definition: ParseUtils.cpp:108