Nektar++
ProcessTetSplit.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: ProcessTetSplit.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: Split prisms -> tets
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 
38 #include <LocalRegions/PrismExp.h>
39 
41 
42 #include "ProcessTetSplit.h"
43 
44 using namespace std;
45 using namespace Nektar::NekMeshUtils;
46 
47 namespace Nektar
48 {
49 namespace Utilities
50 {
51 
52 typedef std::pair<int, int> ipair;
53 
54 ModuleKey ProcessTetSplit::className =
56  ModuleKey(eProcessModule, "tetsplit"),
57  ProcessTetSplit::create,
58  "Split prismatic elements to tetrahedra");
59 
60 ProcessTetSplit::ProcessTetSplit(MeshSharedPtr m) : ProcessModule(m)
61 {
62  m_config["nq"] =
63  ConfigOption(false, "3", "Number of points in high order elements.");
64 }
65 
67 {
68 }
69 
71 {
72  int nodeId = m_mesh->m_vertexSet.size();
73 
74  // Set up map which identifies edges (as pairs of vertex ids)
75  // including their vertices to the offset/stride in the 3d array of
76  // collapsed co-ordinates. Note that this map also includes the
77  // diagonal edges along quadrilateral faces which will be used to
78  // add high-order information to the split tetrahedra.
79  map<ipair, ipair> edgeMap;
80 
81  int nq = m_config["nq"].as<int>();
82  int ne = nq - 2; // Edge interior
83  int nft = (ne - 1) * ne / 2; // Face interior (triangle)
84  int nfq = ne * ne; // Face interior (quad)
85  int o = 6;
86 
87  // Standard prismatic edges (0->8)
88  edgeMap[ipair(0, 1)] = ipair(o, 1);
89  edgeMap[ipair(1, 2)] = ipair(o + 1 * ne, 1);
90  edgeMap[ipair(3, 2)] = ipair(o + 2 * ne, 1);
91  edgeMap[ipair(0, 3)] = ipair(o + 3 * ne, 1);
92  edgeMap[ipair(0, 4)] = ipair(o + 4 * ne, 1);
93  edgeMap[ipair(1, 4)] = ipair(o + 5 * ne, 1);
94  edgeMap[ipair(2, 5)] = ipair(o + 6 * ne, 1);
95  edgeMap[ipair(3, 5)] = ipair(o + 7 * ne, 1);
96  edgeMap[ipair(4, 5)] = ipair(o + 8 * ne, 1);
97 
98  // Face 0 diagonals
99  o += 9 * ne;
100  edgeMap[ipair(0, 2)] = ipair(o, ne + 1);
101  edgeMap[ipair(1, 3)] = ipair(o + ne - 1, ne - 1);
102 
103  // Face 2 diagonals
104  o += nfq + nft;
105  edgeMap[ipair(1, 5)] = ipair(o, ne + 1);
106  edgeMap[ipair(2, 4)] = ipair(o + ne - 1, ne - 1);
107 
108  // Face 4 diagonals
109  o += nfq + nft;
110  edgeMap[ipair(0, 5)] = ipair(o, ne + 1);
111  edgeMap[ipair(3, 4)] = ipair(o + ne - 1, ne - 1);
112 
113  // Default PointsType.
115 
116  /*
117  * Split all element types into tetrahedra. This is based on the
118  * algorithm found in:
119  *
120  * "How to Subdivide Pyramids, Prisms and Hexahedra into
121  * Tetrahedra", J. Dompierre et al.
122  */
123 
124  // Denotes a set of indices inside m_mesh->m_element[m_mesh->m_expDim-1]
125  // which are
126  // to be removed. These are precisely the quadrilateral boundary
127  // faces which will be replaced by two triangular faces.
128  set<int> toRemove;
129 
130  // Represents table 2 of paper; each row i represents a rotation of
131  // the prism nodes such that vertex i is placed at position 0.
132  static int indir[6][6] = {{0, 1, 2, 3, 4, 5},
133  {1, 2, 0, 4, 5, 3},
134  {2, 0, 1, 5, 3, 4},
135  {3, 5, 4, 0, 2, 1},
136  {4, 3, 5, 1, 0, 2},
137  {5, 4, 3, 2, 1, 0}};
138 
139  // Represents table 3 of paper; the first three rows are the three
140  // tetrahedra if the first condition is met; the latter three rows
141  // are the three tetrahedra if the second condition is met.
142  static int prismTet[6][4] = {{0, 1, 2, 5},
143  {0, 1, 5, 4},
144  {0, 4, 5, 3},
145  {0, 1, 2, 4},
146  {0, 4, 2, 5},
147  {0, 4, 5, 3}};
148 
149  // Represents the order of tetrahedral edges (in Nektar++ ordering).
150  static int tetEdges[6][2] = {
151  {0, 1}, {1, 2}, {0, 2}, {0, 3}, {1, 3}, {2, 3}};
152 
153  // A tetrahedron nodes -> faces map.
154  static int tetFaceNodes[4][3] = {
155  {0, 1, 2}, {0, 1, 3}, {1, 2, 3}, {0, 2, 3}};
156 
157  static NodeSharedPtr stdPrismNodes[6] = {
158  NodeSharedPtr(new Node(0, -1, -1, -1)),
159  NodeSharedPtr(new Node(1, 1, -1, -1)),
160  NodeSharedPtr(new Node(2, 1, 1, -1)),
161  NodeSharedPtr(new Node(3, -1, 1, -1)),
162  NodeSharedPtr(new Node(4, -1, -1, 1)),
163  NodeSharedPtr(new Node(5, -1, 1, 1))};
164 
165  // Generate equally spaced nodal points on triangle.
166  Array<OneD, NekDouble> rp(nq * (nq + 1) / 2), sp(nq * (nq + 1) / 2);
168  LibUtilities::PointsManager()[elec]->GetPoints(rp, sp);
169 
170  // Make a copy of the element list.
171  vector<ElementSharedPtr> el = m_mesh->m_element[m_mesh->m_expDim];
172  m_mesh->m_element[m_mesh->m_expDim].clear();
173 
174  for (int i = 0; i < el.size(); ++i)
175  {
176  if (el[i]->GetConf().m_e != LibUtilities::ePrism)
177  {
178  m_mesh->m_element[m_mesh->m_expDim].push_back(el[i]);
179  continue;
180  }
181 
182  vector<NodeSharedPtr> nodeList(6);
183 
184  // Map Nektar++ ordering (vertices 0,1,2,3 are base quad) to
185  // paper ordering (vertices 0,1,2 are first triangular face).
186  int mapPrism[6] = {0, 1, 4, 3, 2, 5};
187  for (int j = 0; j < 6; ++j)
188  {
189  nodeList[j] = el[i]->GetVertex(mapPrism[j]);
190  }
191 
192  // Determine minimum ID of the nodes in this prism.
193  int minElId = nodeList[0]->m_id;
194  int minId = 0;
195  for (int j = 1; j < 6; ++j)
196  {
197  int curId = nodeList[j]->m_id;
198  if (curId < minElId)
199  {
200  minElId = curId;
201  minId = j;
202  }
203  }
204 
205  int offset;
206 
207  // Split prism using paper criterion.
208  int id1 = min(nodeList[indir[minId][1]]->m_id,
209  nodeList[indir[minId][5]]->m_id);
210  int id2 = min(nodeList[indir[minId][2]]->m_id,
211  nodeList[indir[minId][4]]->m_id);
212 
213  if (id1 < id2)
214  {
215  offset = 0;
216  }
217  else if (id1 > id2)
218  {
219  offset = 3;
220  }
221  else
222  {
223  cerr << "Connectivity issue with prism->tet splitting." << endl;
224  abort();
225  }
226 
227  // Create local prismatic region so that co-ordinates of the
228  // mapped element can be read from.
230  std::dynamic_pointer_cast<SpatialDomains::PrismGeom>(
231  el[i]->GetGeom(m_mesh->m_spaceDim));
234  nq,
238  nq,
242  B0, B0, B1, geomLayer);
243 
244  // Get the coordinates of the high order prismatic element.
245  Array<OneD, NekDouble> wsp(3 * nq * nq * nq);
247  x[0] = wsp;
248  x[1] = wsp + 1 * nq * nq * nq;
249  x[2] = wsp + 2 * nq * nq * nq;
250  qs->GetCoords(x[0], x[1], x[2]);
251 
256 
257  // Process face data. Initially put coordinates into equally
258  // spaced nodal distribution.
262 
263  int nCoeffs = nodalPrism->GetNcoeffs();
264  Array<OneD, NekDouble> wsp2(3 * nCoeffs);
266  xn[0] = wsp2;
267  xn[1] = wsp2 + 1 * nCoeffs;
268  xn[2] = wsp2 + 2 * nCoeffs;
269 
270  for (int j = 0; j < 3; ++j)
271  {
272  Array<OneD, NekDouble> tmp(nodalPrism->GetNcoeffs());
273  qs->FwdTrans(x[j], tmp);
274  nodalPrism->ModalToNodal(tmp, xn[j]);
275  }
276 
277  // Store map of nodes.
278  map<int, int> prismVerts;
279  for (int j = 0; j < 6; ++j)
280  {
281  prismVerts[el[i]->GetVertex(j)->m_id] = j;
282  }
283 
284  for (int j = 0; j < 3; ++j)
285  {
286  vector<NodeSharedPtr> tetNodes(4);
287 
288  // Extract vertices for tetrahedron.
289  for (int k = 0; k < 4; ++k)
290  {
291  tetNodes[k] = nodeList[indir[minId][prismTet[j + offset][k]]];
292  }
293 
294  // Add high order information to tetrahedral edges.
295  for (int k = 0; k < 6; ++k)
296  {
297  // Determine prismatic nodes which correspond with this
298  // edge. Apply prism map to this to get Nektar++
299  // ordering (this works since as a permutation,
300  // prismMap^2 = id).
301  int n1 = mapPrism
302  [indir[minId][prismTet[j + offset][tetEdges[k][0]]]];
303  int n2 = mapPrism
304  [indir[minId][prismTet[j + offset][tetEdges[k][1]]]];
305 
306  // Find offset/stride
307  auto it = edgeMap.find(pair<int, int>(n1, n2));
308  if (it == edgeMap.end())
309  {
310  it = edgeMap.find(pair<int, int>(n2, n1));
311  if (it == edgeMap.end())
312  {
313  cerr << "Couldn't find prism edges " << n1 << " " << n2
314  << endl;
315  abort();
316  }
317  // Extract vertices -- reverse order.
318  for (int l = ne - 1; l >= 0; --l)
319  {
320  int pos = it->second.first + l * it->second.second;
321  tetNodes.push_back(NodeSharedPtr(new Node(
322  nodeId++, xn[0][pos], xn[1][pos], xn[2][pos])));
323  }
324  }
325  else
326  {
327  // Extract vertices -- forwards order.
328  for (int l = 0; l < ne; ++l)
329  {
330  int pos = it->second.first + l * it->second.second;
331  tetNodes.push_back(NodeSharedPtr(new Node(
332  nodeId++, xn[0][pos], xn[1][pos], xn[2][pos])));
333  }
334  }
335  }
336 
337  // Create new tetrahedron with edge curvature.
338  vector<int> tags = el[i]->GetTagList();
339  ElmtConfig conf(LibUtilities::eTetrahedron, nq - 1, false, false);
341  LibUtilities::eTetrahedron, conf, tetNodes, tags);
342 
343  // Extract interior face data.
344  for (int k = 0; k < 4; ++k)
345  {
346  // First determine which nodes of prism are being used
347  // for this face.
348  FaceSharedPtr face = elmt->GetFace(k);
349  vector<NodeSharedPtr> triNodes(3);
350 
351  for (int l = 0; l < 3; ++l)
352  {
353  NodeSharedPtr v = face->m_vertexList[l];
354  triNodes[l] = stdPrismNodes[prismVerts[v->m_id]];
355  }
356 
357  // Create a triangle with the standard nodes of the
358  // prism.
359  vector<int> tags;
360  ElmtConfig conf(LibUtilities::eTriangle, 1, false, false);
362  LibUtilities::eTriangle, conf, triNodes, tags);
363  SpatialDomains::GeometrySharedPtr triGeom = elmt->GetGeom(3);
364  triGeom->FillGeom();
365  o = 3 + 3 * ne;
366  face->m_curveType = LibUtilities::eNodalTriEvenlySpaced;
367 
368  for (int l = 0; l < nft; ++l)
369  {
370  Array<OneD, NekDouble> tmp1(2), tmp2(3);
371  tmp1[0] = rp[o + l];
372  tmp1[1] = sp[o + l];
373  tmp2[0] = triGeom->GetCoord(0, tmp1);
374  tmp2[1] = triGeom->GetCoord(1, tmp1);
375  tmp2[2] = triGeom->GetCoord(2, tmp1);
376 
377  // PhysEvaluate on prism geometry.
378  NekDouble xc = geomLayer->GetCoord(0, tmp2);
379  NekDouble yc = geomLayer->GetCoord(1, tmp2);
380  NekDouble zc = geomLayer->GetCoord(2, tmp2);
381  face->m_faceNodes.push_back(
382  NodeSharedPtr(new Node(nodeId++, xc, yc, zc)));
383  }
384  }
385 
386  m_mesh->m_element[m_mesh->m_expDim].push_back(elmt);
387  }
388 
389  // Now check to see if this one of the quadrilateral faces is
390  // associated with a boundary condition. If it is, we split the
391  // face into two triangles and mark the existing face for
392  // removal.
393  //
394  // Note that this algorithm has significant room for improvement
395  // and is likely one of the least optimal approachs - however
396  // implementation is simple.
397  for (int fid = 0; fid < 5; fid += 2)
398  {
399  int bl = el[i]->GetBoundaryLink(fid);
400 
401  if (bl == -1)
402  {
403  continue;
404  }
405 
406  vector<NodeSharedPtr> triNodeList(3);
407  vector<int> faceNodes(3);
408  vector<int> tmp;
409  vector<int> tagBE;
410  ElmtConfig bconf(LibUtilities::eTriangle, 1, true, true);
411  ElementSharedPtr elmt;
412 
413  // Mark existing boundary face for removal.
414  toRemove.insert(bl);
415  tagBE = m_mesh->m_element[m_mesh->m_expDim - 1][bl]->GetTagList();
416 
417  // First loop over tets.
418  for (int j = 0; j < 3; ++j)
419  {
420  // Now loop over faces.
421  for (int k = 0; k < 4; ++k)
422  {
423  // Finally determine Nektar++ local node numbers for
424  // this face.
425  for (int l = 0; l < 3; ++l)
426  {
427  faceNodes[l] = mapPrism
428  [indir[minId]
429  [prismTet[j + offset][tetFaceNodes[k][l]]]];
430  }
431 
432  tmp = faceNodes;
433  sort(faceNodes.begin(), faceNodes.end());
434 
435  // If this face matches a triple denoting a split
436  // quad face, add the face to the expansion list.
437  if ((fid == 0 && ((faceNodes[0] == 0 && faceNodes[1] == 1 &&
438  faceNodes[2] == 2) ||
439  (faceNodes[0] == 0 && faceNodes[1] == 2 &&
440  faceNodes[2] == 3) ||
441  (faceNodes[0] == 0 && faceNodes[1] == 1 &&
442  faceNodes[2] == 3) ||
443  (faceNodes[0] == 1 && faceNodes[1] == 2 &&
444  faceNodes[2] == 3))) ||
445  (fid == 2 && ((faceNodes[0] == 1 && faceNodes[1] == 2 &&
446  faceNodes[2] == 5) ||
447  (faceNodes[0] == 1 && faceNodes[1] == 4 &&
448  faceNodes[2] == 5) ||
449  (faceNodes[0] == 1 && faceNodes[1] == 2 &&
450  faceNodes[2] == 4) ||
451  (faceNodes[0] == 2 && faceNodes[1] == 4 &&
452  faceNodes[2] == 5))) ||
453  (fid == 4 && ((faceNodes[0] == 0 && faceNodes[1] == 3 &&
454  faceNodes[2] == 5) ||
455  (faceNodes[0] == 0 && faceNodes[1] == 4 &&
456  faceNodes[2] == 5) ||
457  (faceNodes[0] == 0 && faceNodes[1] == 3 &&
458  faceNodes[2] == 4) ||
459  (faceNodes[0] == 3 && faceNodes[1] == 4 &&
460  faceNodes[2] == 5))))
461  {
462  triNodeList[0] = nodeList[mapPrism[tmp[0]]];
463  triNodeList[1] = nodeList[mapPrism[tmp[1]]];
464  triNodeList[2] = nodeList[mapPrism[tmp[2]]];
466  LibUtilities::eTriangle, bconf, triNodeList, tagBE);
467  m_mesh->m_element[m_mesh->m_expDim - 1].push_back(elmt);
468  }
469  }
470  }
471  }
472  }
473 
474  // Remove 2D elements.
475  vector<ElementSharedPtr> tmp;
476  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim - 1].size(); ++i)
477  {
478  if (toRemove.find(i) == toRemove.end())
479  {
480  tmp.push_back(m_mesh->m_element[m_mesh->m_expDim - 1][i]);
481  }
482  }
483 
484  m_mesh->m_element[m_mesh->m_expDim - 1] = tmp;
485 
486  // Re-process mesh to eliminate duplicate vertices and edges.
487  ProcessVertices();
488  ProcessEdges();
489  ProcessFaces();
490  ProcessElements();
492 }
493 }
494 }
Basic information about an element.
Definition: ElementConfig.h:49
Principle Modified Functions .
Definition: BasisType.h:48
STL namespace.
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
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:58
std::shared_ptr< Face > FaceSharedPtr
Definition: Face.h:155
Principle Orthogonal Functions .
Definition: BasisType.h:46
1D Evenly-spaced points using Lagrange polynomial
Definition: PointsType.h:64
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
std::pair< int, int > ipair
std::pair< ModuleType, std::string > ModuleKey
virtual NEKMESHUTILS_EXPORT void ProcessFaces(bool ReprocessFaces=true)
Extract element faces.
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.
std::shared_ptr< Geometry > GeometrySharedPtr
Definition: Geometry.h:53
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
PointsManagerT & PointsManager(void)
Principle Orthogonal Functions .
Definition: BasisType.h:45
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.
std::shared_ptr< StdNodalPrismExp > StdNodalPrismExpSharedPtr
std::shared_ptr< PrismExp > PrismExpSharedPtr
Definition: PrismExp.h:220
Abstract base class for processing modules.
virtual void Process()
Write mesh to output file.
virtual NEKMESHUTILS_EXPORT void ProcessVertices()
Extract element vertices.
3D Evenly-spaced points on a Prism
Definition: PointsType.h:74
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
2D Evenly-spaced points on a Triangle
Definition: PointsType.h:71
std::shared_ptr< PrismGeom > PrismGeomSharedPtr
Definition: PrismGeom.h:88
std::pair< ModuleType, std::string > ModuleKey
Describes the specification for a Basis.
Definition: Basis.h:49
virtual NEKMESHUTILS_EXPORT void ProcessComposites()
Generate composites.
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:51
ModuleFactory & GetModuleFactory()