Nektar++
ProcessBL.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: ProcessBL.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: Refine prismatic or quadrilateral boundary layer elements.
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 
35 #include <string>
36 
42 #include <LocalRegions/HexExp.h>
43 #include <LocalRegions/PrismExp.h>
44 #include <LocalRegions/QuadExp.h>
45 
47 #include "ProcessBL.h"
48 
49 using namespace std;
50 using namespace Nektar::NekMeshUtils;
51 
52 namespace Nektar
53 {
54 namespace Utilities
55 {
56 ModuleKey ProcessBL::className = GetModuleFactory().RegisterCreatorFunction(
58  ProcessBL::create,
59  "Refines a prismatic or quadrilateral boundary layer.");
60 
61 int **helper2d(int lda, int arr[][2])
62 {
63  int **ret = new int *[lda];
64  for (int i = 0; i < lda; ++i)
65  {
66  ret[i] = new int[2];
67  ret[i][0] = arr[i][0];
68  ret[i][1] = arr[i][1];
69  }
70  return ret;
71 }
72 
73 int **helper2d(int lda, int arr[][4])
74 {
75  int **ret = new int *[lda];
76  for (int i = 0; i < lda; ++i)
77  {
78  ret[i] = new int[4];
79  ret[i][0] = arr[i][0];
80  ret[i][1] = arr[i][1];
81  ret[i][2] = arr[i][2];
82  ret[i][3] = arr[i][3];
83  }
84  return ret;
85 }
86 
88 {
89  int size;
90  int layerOff;
91  int *edge;
92  int *offset;
93  int *inc;
94  int **conn;
96  int *bfaces;
97 };
98 
100 {
101  int size;
102  int *edge;
103  int **edgeVert;
104  int *offset;
105  int *inc;
106 };
107 
108 ProcessBL::ProcessBL(MeshSharedPtr m) : ProcessModule(m)
109 {
110  // BL mesh configuration.
111  m_config["layers"] =
112  ConfigOption(false, "2", "Number of layers to refine.");
113  m_config["nq"] =
114  ConfigOption(false, "5", "Number of points in high order elements.");
115  m_config["surf"] =
116  ConfigOption(false, "", "Tag identifying surface connected to prism.");
117  m_config["r"] =
118  ConfigOption(false, "2.0", "Ratio to use in geometry progression.");
119 }
120 
122 {
123 }
124 
126 {
127  if (m_mesh->m_verbose)
128  {
129  cout << "ProcessBL: Refining boundary layer..." << endl;
130  }
131  int dim = m_mesh->m_expDim;
132  switch (dim)
133  {
134  case 2:
135  {
136  BoundaryLayer2D();
137  }
138  break;
139 
140  case 3:
141  {
142  BoundaryLayer3D();
143  }
144  break;
145 
146  default:
147  ASSERTL0(0,"Dimension not supported");
148  break;
149  }
150 
151  // Re-process mesh to eliminate duplicate vertices and edges.
152  ProcessVertices();
153  ProcessEdges();
154  ProcessFaces();
155  ProcessElements();
157 }
158 
160 {
161  int nodeId = m_mesh->m_vertexSet.size();
162  int nl = m_config["layers"].as<int>();
163  int nq = m_config["nq"]. as<int>();
164 
165  // determine if geometric ratio is string or a constant.
167  NekDouble r = 1;
168  int rExprId = -1;
169  bool ratioIsString = false;
170 
171  if (m_config["r"].isType<NekDouble>())
172  {
173  r = m_config["r"].as<NekDouble>();
174  }
175  else
176  {
177  std::string rstr = m_config["r"].as<string>();
178  rExprId = rEval.DefineFunction("x y z", rstr);
179  ratioIsString = true;
180  }
181 
182  // Default PointsType.
184 
185  // Map which takes element ID to edge on surface. This enables
186  // splitting to occur in either y-direction of the prism.
187  map<int, int> splitEls;
188 
189  // edgeMap associates geometry edge IDs to the (nl+1) vertices which
190  // are generated along that edge when a prism is split, and is used
191  // to avoid generation of duplicate vertices. It is stored as an
192  // unordered map for speed.
193  std::unordered_map<int, vector<NodeSharedPtr> > edgeMap;
194 
195  string surf = m_config["surf"].as<string>();
196  if (surf.size() > 0)
197  {
198  vector<unsigned int> surfs;
199  ParseUtils::GenerateSeqVector(surf, surfs);
200  sort(surfs.begin(), surfs.end());
201 
202  // If surface is defined, process list of elements to find those
203  // that are connected to it.
204  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
205  {
206  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
207  int nSurf = el->GetEdgeCount();
208 
209  for (int j = 0; j < nSurf; ++j)
210  {
211  int bl = el->GetBoundaryLink(j);
212  if (bl == -1)
213  {
214  continue;
215  }
216 
217  ElementSharedPtr bEl = m_mesh->m_element[m_mesh->m_expDim-1][bl];
218  vector<int> tags = bEl->GetTagList();
219  vector<int> inter;
220 
221  sort(tags.begin(), tags.end());
222  set_intersection(surfs.begin(), surfs.end(),
223  tags .begin(), tags .end(),
224  back_inserter(inter));
225  ASSERTL0(inter.size() <= 1,
226  "Intersection of surfaces wrong");
227 
228  if (inter.size() == 1)
229  {
230  if (el->GetConf().m_e != LibUtilities::eQuadrilateral)
231  {
232  cerr << "WARNING: Found non-quad element "
233  << "to split in surface " << surf
234  << "; ignoring" << endl;
235  continue;
236  }
237 
238  if (splitEls.count(el->GetId()) > 0)
239  {
240  cerr << "WARNING: quad already found; "
241  << "ignoring" << endl;
242  continue;
243  }
244 
245  splitEls[el->GetId()] = j;
246  }
247  }
248  }
249  }
250  else
251  {
252  ASSERTL0(false, "Surface must be specified.");
253  }
254 
255  if (splitEls.size() == 0)
256  {
257  cerr << "WARNING: No elements detected to split." << endl;
258  return;
259  }
260 
261  // Erase all elements from the element list. Elements will be
262  // re-added as they are split.
263  vector<ElementSharedPtr> el = m_mesh->m_element[m_mesh->m_expDim];
264  m_mesh->m_element[m_mesh->m_expDim].clear();
265 
266  // Iterate over list of elements of expansion dimension.
267  for (int i = 0; i < el.size(); ++i)
268  {
269 
270 
271  if (splitEls.count(el[i]->GetId()) == 0)
272  {
273  m_mesh->m_element[m_mesh->m_expDim].push_back(el[i]);
274  continue;
275  }
276 
277  // Find other boundary faces if any
278  std::map<int, int> bLink;
279  for (int j = 0; j < 4; j++)
280  {
281  int bl = el[i]->GetBoundaryLink(j);
282  if ( (bl != -1) && ( j != splitEls[el[i]->GetId()]) )
283  {
284  bLink[j] = bl;
285  }
286  }
287 
288  // Get elemental geometry object.
290  std::dynamic_pointer_cast<SpatialDomains::QuadGeom>(
291  el[i]->GetGeom(m_mesh->m_spaceDim));
292 
293  // Determine whether to use reverse points.
294  // (if edges 1 or 2 are on the surface)
295  LibUtilities::PointsType t = ( (splitEls[el[i]->GetId()]+1) %4) < 2 ?
298 
299 
300  if(ratioIsString) // determine value of r base on geom
301  {
302  NekDouble x,y,z;
303  NekDouble x1,y1,z1;
304  int nverts = geom->GetNumVerts();
305 
306  x = y = z = 0.0;
307 
308  for(int i = 0; i < nverts; ++i)
309  {
310  geom->GetVertex(i)->GetCoords(x1,y1,z1);
311  x += x1; y += y1; z += z1;
312  }
313  x /= (NekDouble) nverts;
314  y /= (NekDouble) nverts;
315  z /= (NekDouble) nverts;
316  r = rEval.Evaluate(rExprId,x,y,z,0.0);
317  }
318 
319  // Create basis.
322  LibUtilities::PointsKey(nq,pt));
325  LibUtilities::PointsKey(nl+1, t, r));
326 
327  // Create local region.
329  if (splitEls[el[i]->GetId()] % 2)
330  {
332  B1,B0,geom);
333  }
334  else
335  {
337  B0,B1,geom);
338  }
339 
340  // Grab co-ordinates.
341  Array<OneD, NekDouble> x(nq*(nl+1), 0.0);
342  Array<OneD, NekDouble> y(nq*(nl+1), 0.0);
343  Array<OneD, NekDouble> z(nq*(nl+1), 0.0);
344  q->GetCoords(x,y,z);
345 
346  vector<vector<NodeSharedPtr> > edgeNodes(2);
347 
348  // Loop over edges to be split.
349  for (int j = 0; j < 2; ++j)
350  {
351  int locEdge = (splitEls[el[i]->GetId()]+1+2*j)%4;
352  int edgeId = el[i]->GetEdge(locEdge)->m_id;
353 
354  // Determine whether we have already generated vertices
355  // along this edge.
356  auto eIt = edgeMap.find(edgeId);
357 
358  if (eIt == edgeMap.end())
359  {
360  // If not then resize storage to hold new points.
361  edgeNodes[j].resize(nl+1);
362 
363  // Re-use existing vertices at endpoints of edge to
364  // avoid duplicating the existing vertices.
365  edgeNodes[j][0] = el[i]->GetVertex(locEdge);
366  edgeNodes[j][nl] = el[i]->GetVertex((locEdge+1)%4);
367 
368  // Variable geometric ratio
369  if(ratioIsString)
370  {
371  NekDouble x0,y0;
372  NekDouble x1,y1;
373  NekDouble xm,ym,zm=0.0;
374 
375  // -> Find edge end and mid points
376  x0 = edgeNodes[j][0]->m_x;
377  y0 = edgeNodes[j][0]->m_y;
378 
379  x1 = edgeNodes[j][nl]->m_x;
380  y1 = edgeNodes[j][nl]->m_y;
381 
382  xm = 0.5*(x0+x1);
383  ym = 0.5*(y0+y1);
384 
385  // evaluate r factor based on mid point value
386  NekDouble rnew;
387  rnew = rEval.Evaluate(rExprId,xm,ym,zm,0.0);
388 
389  // Get basis with new r;
392  LibUtilities::PointsKey Pkey(nl+1, t, rnew);
394  = LibUtilities::PointsManager()[Pkey];
395 
396  const Array<OneD, const NekDouble> z = newP->GetZ();
397 
398  // Create new interior nodes based on this new blend
399  for (int k = 1; k < nl; ++k)
400  {
401  xm = 0.5*(1+z[k])*(x1-x0) + x0;
402  ym = 0.5*(1+z[k])*(y1-y0) + y0;
403  zm = 0.0;
404  edgeNodes[j][k] = NodeSharedPtr(
405  new Node(nodeId++, xm,ym,zm));
406  }
407  }
408  else
409  {
410  // Create new interior nodes.
411  int pos = 0;
412  for (int k = 1; k < nl; ++k)
413  {
414  switch (locEdge)
415  {
416  case 0:
417  pos = k;
418  break;
419  case 1:
420  pos = nq -1 + k*nq;
421  break;
422  case 2:
423  pos = nq*(nl+1) -1 - k;
424  break;
425  case 3:
426  pos = nq*nl - k*nq;
427  break;
428  default:
430  "Quad edge should be < 4.");
431  break;
432  }
433  edgeNodes[j][k] = NodeSharedPtr(
434  new Node(nodeId++, x[pos], y[pos], z[pos]));
435  }
436  }
437 
438  // Store these edges in edgeMap.
439  edgeMap[edgeId] = edgeNodes[j];
440  }
441  else
442  {
443  // Check orientation
444  if (eIt->second[0] == el[i]->GetVertex(locEdge))
445  {
446  // Same orientation: copy nodes
447  edgeNodes[j] = eIt->second;
448  }
449  else
450  {
451  // Reversed orientation: copy in reversed order
452  edgeNodes[j].resize(nl+1);
453  for (int k = 0; k < nl+1; ++k)
454  {
455  edgeNodes[j][k] = eIt->second[nl-k];
456  }
457  }
458 
459  }
460  }
461 
462  // Create element layers.
463  for (int j = 0; j < nl; ++j)
464  {
465  // Get corner vertices.
466  vector<NodeSharedPtr> nodeList(4);
467  switch (splitEls[el[i]->GetId()])
468  {
469  case 0:
470  {
471  nodeList[0] = edgeNodes[1][nl-j ];
472  nodeList[1] = edgeNodes[0][j ];
473  nodeList[2] = edgeNodes[0][j+1];
474  nodeList[3] = edgeNodes[1][nl-j-1];
475  break;
476  }
477  case 1:
478  {
479  nodeList[0] = edgeNodes[1][j];
480  nodeList[1] = edgeNodes[1][j+1];
481  nodeList[2] = edgeNodes[0][nl-j-1];
482  nodeList[3] = edgeNodes[0][nl-j];
483  break;
484  }
485  case 2:
486  {
487  nodeList[0] = edgeNodes[0][nl-j ];
488  nodeList[1] = edgeNodes[1][j ];
489  nodeList[2] = edgeNodes[1][j+1];
490  nodeList[3] = edgeNodes[0][nl-j-1];
491  break;
492  }
493  case 3:
494  {
495  nodeList[0] = edgeNodes[0][j];
496  nodeList[1] = edgeNodes[0][j+1];
497  nodeList[2] = edgeNodes[1][nl-j-1];
498  nodeList[3] = edgeNodes[1][nl-j];
499  break;
500  }
501  }
502  // Create the element.
503  ElmtConfig conf(LibUtilities::eQuadrilateral, 1, true, false, true);
505  CreateInstance(
506  LibUtilities::eQuadrilateral,conf,nodeList,el[i]->GetTagList());
507 
508  // Add high order nodes to split edges.
509  for (int l = 0; l < 2; ++l)
510  {
511  int locEdge = (splitEls[el[i]->GetId()]+2*l)%4;
512  EdgeSharedPtr HOedge = elmt->GetEdge(
513  locEdge);
514  int pos = 0;
515  for (int k = 1; k < nq-1; ++k)
516  {
517  switch (locEdge)
518  {
519  case 0:
520  pos = j*nq+ k;
521  break;
522  case 1:
523  pos = j+1 + k*(nl+1);
524  break;
525  case 2:
526  pos = (j+1)*nq + (nq-1) - k;
527  break;
528  case 3:
529  pos = (nl+1)*(nq-1) + j - k*(nl+1);
530  break;
531  default:
533  "Quad edge should be < 4.");
534  break;
535  }
536  HOedge->m_edgeNodes.push_back(
538  new Node(nodeId++,x[pos],y[pos],0.0)));
539  }
540  HOedge->m_curveType = pt;
541  }
542 
543  // Change the elements on the boundary
544  // to match the layers
545  for (auto &it : bLink)
546  {
547  int eid = it.first;
548  int bl = it.second;
549 
550  if (j == 0)
551  {
552  // For first layer reuse existing 2D element.
553  ElementSharedPtr e = m_mesh->m_element[m_mesh->m_expDim-1][bl];
554  for (int k = 0; k < 2; ++k)
555  {
556  e->SetVertex(
557  k, nodeList[(eid+k)%4]);
558  }
559  }
560  else
561  {
562  // For all other layers create new element.
563  vector<NodeSharedPtr> qNodeList(2);
564  for (int k = 0; k < 2; ++k)
565  {
566  qNodeList[k] = nodeList[(eid+k)%4];
567  }
568  vector<int> tagBE;
569  tagBE = m_mesh->m_element[m_mesh->m_expDim-1][bl]->GetTagList();
570  ElmtConfig bconf(LibUtilities::eSegment,1,true,true,false);
571  ElementSharedPtr boundaryElmt = GetElementFactory().
572  CreateInstance(LibUtilities::eSegment,bconf,
573  qNodeList,tagBE);
574  m_mesh->m_element[m_mesh->m_expDim-1].push_back(boundaryElmt);
575  }
576  }
577 
578  m_mesh->m_element[m_mesh->m_expDim].push_back(elmt);
579  }
580  }
581 }
582 
584 {
585  // A set containing all element types which are valid.
586  set<LibUtilities::ShapeType> validElTypes;
587  validElTypes.insert(LibUtilities::ePrism);
588  validElTypes.insert(LibUtilities::eHexahedron);
589 
590  int nodeId = m_mesh->m_vertexSet.size();
591  int nl = m_config["layers"].as<int>();
592  int nq = m_config["nq"].as<int>();
593 
594  // determine if geometric ratio is string or a constant.
596  NekDouble r = 1;
597  int rExprId = -1;
598  bool ratioIsString = false;
599 
600  if (m_config["r"].isType<NekDouble>())
601  {
602  r = m_config["r"].as<NekDouble>();
603  }
604  else
605  {
606  std::string rstr = m_config["r"].as<string>();
607  rExprId = rEval.DefineFunction("x y z", rstr);
608  ratioIsString = true;
609  }
610 
611  // Prismatic node -> face map.
612  int prismFaceNodes[5][4] = {
613  {0, 1, 2, 3}, {0, 1, 4, -1}, {1, 2, 5, 4}, {3, 2, 5, -1}, {0, 3, 5, 4}};
614  int hexFaceNodes[6][4] = {{0, 1, 2, 3},
615  {0, 1, 5, 4},
616  {1, 2, 6, 5},
617  {3, 2, 6, 7},
618  {0, 3, 7, 4},
619  {4, 5, 6, 7}};
620  map<LibUtilities::ShapeType, int **> faceNodeMap;
621  faceNodeMap[LibUtilities::ePrism] = helper2d(5, prismFaceNodes);
622  faceNodeMap[LibUtilities::eHexahedron] = helper2d(6, hexFaceNodes);
623 
624  // Default PointsType.
626 
627  // Map which takes element ID to face on surface. This enables
628  // splitting to occur in either y-direction of the prism.
629  std::unordered_map<int, int> splitEls;
630 
631  // Set up maps which takes an edge (in nektar++ ordering) and return
632  // their offset and stride in the 3d array of collapsed quadrature
633  // points. Note that this map includes only the edges that are on
634  // the triangular faces as the edges in the normal direction are
635  // linear.
636  map<LibUtilities::ShapeType, map<int, SplitMapHelper> > splitMap;
637  int po = nq * (nl + 1);
638 
639  SplitMapHelper splitPrism;
640  int splitMapEdgePrism[6] = {0, 2, 4, 5, 6, 7};
641  int splitMapOffsetPrism[6] = {0, nq, 0, nq - 1, nq + nq - 1, nq};
642  int splitMapIncPrism[6] = {1, 1, po, po, po, po};
643  int splitMapBFacesPrism[3] = {0, 2, 4};
644  int splitMapConnPrism[6][2] = {
645  {0, 0}, {1, 0}, {1, 1}, {0, 1}, {2, 0}, {2, 1}};
646  splitPrism.size = 6;
647  splitPrism.layerOff = nq;
648  splitPrism.edge = splitMapEdgePrism;
649  splitPrism.offset = splitMapOffsetPrism;
650  splitPrism.inc = splitMapIncPrism;
651  splitPrism.conn = helper2d(6, splitMapConnPrism);
652  splitPrism.bfacesSize = 3;
653  splitPrism.bfaces = splitMapBFacesPrism;
654  splitMap[LibUtilities::ePrism][1] = splitPrism;
655  splitMap[LibUtilities::ePrism][3] = splitPrism;
656 
657  int ho = nq * (nq - 1);
658  int tl = nq * nq;
659  SplitMapHelper splitHex0;
660  int splitMapEdgeHex0[8] = {0, 1, 2, 3, 8, 9, 10, 11};
661  int splitMapOffsetHex0[8] = {
662  0, nq - 1, tl - 1, ho, tl, tl + nq - 1, 2 * tl - 1, tl + ho};
663  int splitMapIncHex0[8] = {1, nq, -1, -nq, 1, nq, -1, -nq};
664  int splitMapBFacesHex0[4] = {1, 2, 3, 4};
665  int splitMapConnHex0[8][2] = {
666  {0, 0}, {1, 0}, {2, 0}, {3, 0}, {0, 1}, {1, 1}, {2, 1}, {3, 1}};
667  splitHex0.size = 8;
668  splitHex0.layerOff = nq * nq;
669  splitHex0.edge = splitMapEdgeHex0;
670  splitHex0.offset = splitMapOffsetHex0;
671  splitHex0.inc = splitMapIncHex0;
672  splitHex0.conn = helper2d(8, splitMapConnHex0);
673  splitHex0.bfacesSize = 4;
674  splitHex0.bfaces = splitMapBFacesHex0;
675  splitMap[LibUtilities::eHexahedron][0] = splitHex0;
676  splitMap[LibUtilities::eHexahedron][5] = splitHex0;
677 
678  // splitEdge enumerates the edges in the standard prism along which
679  // new nodes should be generated. These edges are the three between
680  // the two triangular faces.
681  //
682  // edgeVertMap specifies the vertices which comprise those edges in
683  // splitEdge; for example splitEdge[0] = 3 which connects vertices 0
684  // and 3.
685  //
686  // edgeOffset holds the offset of each of edges 3, 1 and 8
687  // respectively inside the collapsed coordinate system.
688  map<LibUtilities::ShapeType, map<int, SplitEdgeHelper> > splitEdge;
689 
690  int splitPrismEdges[3] = {3, 1, 8};
691  int splitPrismEdgeVert[3][2] = {{0, 3}, {1, 2}, {4, 5}};
692  int splitPrismOffset[3] = {0, nq - 1, nq * (nl + 1) * (nq - 1)};
693  int splitPrismInc[3] = {nq, nq, nq};
694  SplitEdgeHelper splitPrismEdge;
695  splitPrismEdge.size = 3;
696  splitPrismEdge.edge = splitPrismEdges;
697  splitPrismEdge.edgeVert = helper2d(3, splitPrismEdgeVert);
698  splitPrismEdge.offset = splitPrismOffset;
699  splitPrismEdge.inc = splitPrismInc;
700  splitEdge[LibUtilities::ePrism][1] = splitPrismEdge;
701  splitEdge[LibUtilities::ePrism][3] = splitPrismEdge;
702 
703  int splitHex0Edges[4] = {4, 5, 6, 7};
704  int splitHex0EdgeVert[4][2] = {{0, 4}, {1, 5}, {2, 6}, {3, 7}};
705  int splitHex0Offset[4] = {0, nq - 1, nq * nq - 1, nq * (nq - 1)};
706  int splitHex0Inc[4] = {nq * nq, nq * nq, nq * nq, nq * nq};
707  SplitEdgeHelper splitHex0Edge;
708  splitHex0Edge.size = 4;
709  splitHex0Edge.edge = splitHex0Edges;
710  splitHex0Edge.edgeVert = helper2d(4, splitHex0EdgeVert);
711  splitHex0Edge.offset = splitHex0Offset;
712  splitHex0Edge.inc = splitHex0Inc;
713  splitEdge[LibUtilities::eHexahedron][0] = splitHex0Edge;
714  splitEdge[LibUtilities::eHexahedron][5] = splitHex0Edge;
715 
716  map<LibUtilities::ShapeType, map<int, bool> > revPoints;
717  revPoints[LibUtilities::ePrism][1] = true;
718  revPoints[LibUtilities::ePrism][3] = false;
719 
720  revPoints[LibUtilities::eHexahedron][0] = true;
721  revPoints[LibUtilities::eHexahedron][5] = false;
722 
723  // edgeMap associates geometry edge IDs to the (nl+1) vertices which
724  // are generated along that edge when a prism is split, and is used
725  // to avoid generation of duplicate vertices. It is stored as an
726  // unordered map for speed.
727  std::unordered_map<int, vector<NodeSharedPtr> > edgeMap;
728 
729  string surf = m_config["surf"].as<string>();
730  if (surf.size() > 0)
731  {
732  vector<unsigned int> surfs;
733  ParseUtils::GenerateVector(surf, surfs);
734  sort(surfs.begin(), surfs.end());
735 
736  // If surface is defined, process list of elements to find those
737  // that are connected to it.
738  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
739  {
740  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
741  int nSurf = el->GetFaceCount();
742 
743  for (int j = 0; j < nSurf; ++j)
744  {
745  int bl = el->GetBoundaryLink(j);
746  if (bl == -1)
747  {
748  continue;
749  }
750 
751  ElementSharedPtr bEl =
752  m_mesh->m_element[m_mesh->m_expDim - 1][bl];
753  vector<int> tags = bEl->GetTagList();
754  vector<int> inter;
755 
756  sort(tags.begin(), tags.end());
757  set_intersection(surfs.begin(),
758  surfs.end(),
759  tags.begin(),
760  tags.end(),
761  back_inserter(inter));
762  ASSERTL0(inter.size() <= 1, "Intersection of surfaces wrong");
763 
764  if (inter.size() == 1)
765  {
766  if (el->GetConf().m_e == LibUtilities::ePrism)
767  {
768  if (j % 2 == 0)
769  {
770  cerr << "WARNING: Found quadrilateral face " << j
771  << " on surface " << surf
772  << " connected to prism; ignoring." << endl;
773  continue;
774  }
775 
776  if (splitEls.count(el->GetId()) > 0)
777  {
778  cerr << "WARNING: prism already found; "
779  << "ignoring" << endl;
780  }
781 
782  splitEls[el->GetId()] = j;
783  }
784  else if (validElTypes.count(el->GetConf().m_e) == 0)
785  {
786  cerr << "WARNING: Unsupported element type "
787  << "found in surface " << j << "; "
788  << "ignoring" << endl;
789  continue;
790  }
791  }
792  }
793  }
794  }
795  else
796  {
797  // Otherwise, add all prismatic elements and assume face 1 of
798  // the prism lies on the surface.
799  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
800  {
801  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
802 
803  if (el->GetConf().m_e == LibUtilities::ePrism)
804  {
805  splitEls[el->GetId()] = 1;
806  }
807  else if (validElTypes.count(el->GetConf().m_e) > 0)
808  {
809  splitEls[el->GetId()] = 0;
810  }
811  else
812  {
813  continue;
814  }
815  }
816  }
817 
818  if (splitEls.size() == 0)
819  {
820  cerr << "WARNING: No elements detected to split." << endl;
821  return;
822  }
823 
824  // Erase all elements from the element list. Elements will be
825  // re-added as they are split.
826  vector<ElementSharedPtr> el = m_mesh->m_element[m_mesh->m_expDim];
827  m_mesh->m_element[m_mesh->m_expDim].clear();
828 
829  map<int, SpatialDomains::Geometry3DSharedPtr> geomMap;
830  for (int i = 0; i < el.size(); ++i)
831  {
832  const int elId = el[i]->GetId();
833  if (splitEls.find(elId) == splitEls.end())
834  {
835  continue;
836  }
837 
838  // Get elemental geometry object and put into map.
839  geomMap[elId] = std::dynamic_pointer_cast<SpatialDomains::Geometry3D>(
840  el[i]->GetGeom(m_mesh->m_spaceDim));
841  }
842 
843  // Iterate over list of elements of expansion dimension.
844  for (int i = 0; i < el.size(); ++i)
845  {
846  const int elId = el[i]->GetId();
847  auto sIt = splitEls.find(elId);
848 
849  if (sIt == splitEls.end())
850  {
851  m_mesh->m_element[m_mesh->m_expDim].push_back(el[i]);
852  continue;
853  }
854 
855  SpatialDomains::Geometry3DSharedPtr geom = geomMap[elId];
856 
857  const int faceNum = sIt->second;
858  LibUtilities::ShapeType elType = el[i]->GetConf().m_e;
859 
860  SplitMapHelper &sMap = splitMap[elType][faceNum];
861  SplitEdgeHelper &sEdge = splitEdge[elType][faceNum];
862 
863  // Find quadrilateral boundary faces if any
864  std::map<int, int> bLink;
865  for (int j = 0; j < sMap.bfacesSize; ++j)
866  {
867  int bl = el[i]->GetBoundaryLink(sMap.bfaces[j]);
868  if (bl != -1)
869  {
870  bLink[sMap.bfaces[j]] = bl;
871  }
872  }
873 
874  // Determine whether to use reverse points.
876  revPoints[elType][faceNum] ? LibUtilities::eBoundaryLayerPoints
878 
879  // Determine value of r based on geometry.
880  if (ratioIsString)
881  {
882  NekDouble x, y, z;
883  NekDouble x1, y1, z1;
884  int nverts = geom->GetNumVerts();
885 
886  x = y = z = 0.0;
887 
888  for (int i = 0; i < nverts; ++i)
889  {
890  geom->GetVertex(i)->GetCoords(x1, y1, z1);
891  x += x1;
892  y += y1;
893  z += z1;
894  }
895  x /= (NekDouble)nverts;
896  y /= (NekDouble)nverts;
897  z /= (NekDouble)nverts;
898  r = rEval.Evaluate(rExprId, x, y, z, 0.0);
899  }
900 
902 
903  if (elType == LibUtilities::ePrism)
904  {
905  // Create basis.
909  2,
910  LibUtilities::PointsKey(nl + 1, t, r));
913 
914  // Create local region.
916  std::dynamic_pointer_cast<SpatialDomains::PrismGeom>(geom);
918  B0, B1, B2, g);
919  }
920  else if (elType == LibUtilities::eHexahedron)
921  {
922  // Create basis.
926  2,
927  LibUtilities::PointsKey(nl + 1, t, r));
928 
929  // Create local region.
931  std::dynamic_pointer_cast<SpatialDomains::HexGeom>(geom);
933  B0, B0, B1, g);
934  }
935 
936  // Grab co-ordinates.
937  Array<OneD, NekDouble> x(nq * nq * (nl + 1));
938  Array<OneD, NekDouble> y(nq * nq * (nl + 1));
939  Array<OneD, NekDouble> z(nq * nq * (nl + 1));
940  q->GetCoords(x, y, z);
941 
942  int nSplitEdge = sEdge.size;
943  vector<vector<NodeSharedPtr> > edgeNodes(nSplitEdge);
944 
945  // Loop over edges to be split.
946  for (int j = 0; j < nSplitEdge; ++j)
947  {
948  int locEdge = sEdge.edge[j];
949  int edgeId = el[i]->GetEdge(locEdge)->m_id;
950 
951  // Determine whether we have already generated vertices
952  // along this edge.
953  auto eIt = edgeMap.find(edgeId);
954 
955  if (eIt == edgeMap.end())
956  {
957  // If not then resize storage to hold new points.
958  edgeNodes[j].resize(nl + 1);
959 
960  // Re-use existing vertices at endpoints of edge to
961  // avoid duplicating the existing vertices.
962  edgeNodes[j][0] = el[i]->GetVertex(sEdge.edgeVert[j][0]);
963  edgeNodes[j][nl] = el[i]->GetVertex(sEdge.edgeVert[j][1]);
964 
965  // Variable geometric ratio
966  if (ratioIsString)
967  {
968  NekDouble x0, y0, z0;
969  NekDouble x1, y1, z1;
970  NekDouble xm, ym, zm;
971 
972  // -> Find edge end and mid points
973  x0 = x[sEdge.offset[j]];
974  y0 = y[sEdge.offset[j]];
975  z0 = z[sEdge.offset[j]];
976 
977  x1 = x[sEdge.offset[j] + nl * nq];
978  y1 = y[sEdge.offset[j] + nl * nq];
979  z1 = z[sEdge.offset[j] + nl * nq];
980 
981  xm = 0.5 * (x0 + x1);
982  ym = 0.5 * (y0 + y1);
983  zm = 0.5 * (z0 + z1);
984 
985  // evaluate r factor based on mid point value
986  NekDouble rnew;
987  rnew = rEval.Evaluate(rExprId, xm, ym, zm, 0.0);
988 
989  // Get basis with new r;
990  LibUtilities::PointsKey Pkey(nl + 1, t, rnew);
993 
994  const Array<OneD, const NekDouble> z = newP->GetZ();
995 
996  // Create new interior nodes based on this new blend
997  for (int k = 1; k < nl; ++k)
998  {
999  xm = 0.5 * (1 + z[k]) * (x1 - x0) + x0;
1000  ym = 0.5 * (1 + z[k]) * (y1 - y0) + y0;
1001  zm = 0.5 * (1 + z[k]) * (z1 - z0) + z0;
1002  edgeNodes[j][k] =
1003  NodeSharedPtr(new Node(nodeId++, xm, ym, zm));
1004  }
1005  }
1006  else
1007  {
1008  // Create new interior nodes.
1009  for (int k = 1; k < nl; ++k)
1010  {
1011  int pos = sEdge.offset[j] + k * sEdge.inc[j];
1012  edgeNodes[j][k] = NodeSharedPtr(
1013  new Node(nodeId++, x[pos], y[pos], z[pos]));
1014  }
1015  }
1016 
1017  // Store these edges in edgeMap.
1018  edgeMap[edgeId] = edgeNodes[j];
1019  }
1020  else
1021  {
1022  edgeNodes[j] = eIt->second;
1023  }
1024  }
1025 
1026  // Create element layers.
1027  for (int j = 0; j < nl; ++j)
1028  {
1029  // Offset of this layer within the collapsed coordinate
1030  // system.
1031  int offset = j * sMap.layerOff;
1032 
1033  // Get corner vertices.
1034  vector<NodeSharedPtr> nodeList(sMap.size);
1035  for (int k = 0; k < sMap.size; ++k)
1036  {
1037  nodeList[k] = edgeNodes[sMap.conn[k][0]][j + sMap.conn[k][1]];
1038  }
1039 
1040  // Create the element.
1041  ElmtConfig conf(elType, 1, true, true, false);
1043  elType, conf, nodeList, el[i]->GetTagList());
1044 
1045  // Add high order nodes to split prismatic edges.
1046  for (int l = 0; l < sMap.size; ++l)
1047  {
1048  EdgeSharedPtr HOedge = elmt->GetEdge(sMap.edge[l]);
1049  for (int k = 1; k < nq - 1; ++k)
1050  {
1051  int pos = offset + sMap.offset[l] + k * sMap.inc[l];
1052  HOedge->m_edgeNodes.push_back(NodeSharedPtr(
1053  new Node(nodeId++, x[pos], y[pos], z[pos])));
1054  }
1055  HOedge->m_curveType = pt;
1056  }
1057 
1058  // Change the surface elements to match the layers of
1059  // elements on the boundary of the domain.
1060  for (auto &it : bLink)
1061  {
1062  int fid = it.first;
1063  int bl = it.second;
1064 
1065  vector<NodeSharedPtr> qNodeList(4);
1066  for (int k = 0; k < 4; ++k)
1067  {
1068  qNodeList[k] = nodeList[faceNodeMap[elType][fid][k]];
1069  }
1070  vector<int> tagBE;
1071  tagBE =
1072  m_mesh->m_element[m_mesh->m_expDim - 1][bl]->GetTagList();
1073  ElmtConfig bconf(
1074  LibUtilities::eQuadrilateral, 1, true, true, false);
1075  ElementSharedPtr boundaryElmt =
1077  LibUtilities::eQuadrilateral, bconf, qNodeList, tagBE);
1078 
1079  // Overwrite first layer boundary element with new
1080  // boundary element, otherwise push this back to end of
1081  // the boundary list
1082  if (j == 0)
1083  {
1084  m_mesh->m_element[m_mesh->m_expDim - 1][bl] = boundaryElmt;
1085  }
1086  else
1087  {
1088  m_mesh->m_element[m_mesh->m_expDim - 1].push_back(
1089  boundaryElmt);
1090  }
1091  }
1092 
1093  m_mesh->m_element[m_mesh->m_expDim].push_back(elmt);
1094  }
1095  }
1096 }
1097 }
1098 }
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
Basic information about an element.
Definition: ElementConfig.h:49
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mod...
Definition: ErrorUtil.hpp:209
std::shared_ptr< Geometry3D > Geometry3DSharedPtr
Definition: Geometry3D.h:52
std::shared_ptr< QuadGeom > QuadGeomSharedPtr
Definition: HexGeom.h:46
Principle Modified Functions .
Definition: BasisType.h:48
STL namespace.
std::shared_ptr< Edge > EdgeSharedPtr
Shared pointer to an edge.
Definition: Edge.h:136
int ** helper2d(int lda, int arr[][2])
Definition: ProcessBL.cpp:61
std::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Definition: Mesh.h:156
ElementFactory & GetElementFactory()
Definition: Element.cpp:44
std::shared_ptr< Node > NodeSharedPtr
Definition: CADVert.h:49
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< Points< NekDouble > > PointsSharedPtr
virtual void Process()
Write mesh to output file.
Definition: ProcessBL.cpp:125
Principle Modified Functions .
Definition: BasisType.h:49
std::shared_ptr< Element > ElementSharedPtr
Definition: Edge.h:49
virtual NEKMESHUTILS_EXPORT void ProcessElements()
Generate element IDs.
Represents a command-line configuration option.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
PointsManagerT & PointsManager(void)
Defines a specification for a set of points.
Definition: Points.h:59
double NekDouble
std::map< std::string, ConfigOption > m_config
List of configuration values.
static bool GenerateVector(const std::string &str, std::vector< T > &out)
Takes a comma-separated string and converts it to entries in a vector.
Definition: ParseUtils.cpp:135
Interpreter class for the evaluation of mathematical expressions.
Definition: Interpreter.h:77
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:65
1D power law distribution for boundary layer points
Definition: PointsType.h:68
3D geometry information
Definition: Geometry3D.h:67
Abstract base class for processing modules.
std::shared_ptr< HexGeom > HexGeomSharedPtr
Definition: HexGeom.h:90
virtual NEKMESHUTILS_EXPORT void ProcessVertices()
Extract element vertices.
1D power law distribution for boundary layer points
Definition: PointsType.h:67
virtual NEKMESHUTILS_EXPORT void ProcessEdges(bool ReprocessEdges=true)
Extract element edges.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:199
std::shared_ptr< PrismGeom > PrismGeomSharedPtr
Definition: PrismGeom.h:88
std::pair< ModuleType, std::string > ModuleKey
std::shared_ptr< QuadExp > QuadExpSharedPtr
Definition: QuadExp.h:293
Describes the specification for a Basis.
Definition: Basis.h:49
virtual NEKMESHUTILS_EXPORT void ProcessComposites()
Generate composites.
int ** helper2d(int lda, int arr[][4])
Definition: ProcessBL.cpp:73
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:51
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