Nektar++
InputGmsh.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: InputGmsh.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: GMSH converter.
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 
35 #include <string>
36 #include <iostream>
37 using namespace std;
38 
39 #include <tuple>
40 
50 
51 #include "InputGmsh.h"
52 
53 using namespace Nektar::NekMeshUtils;
54 
55 namespace Nektar
56 {
57 namespace Utilities
58 {
59 
60 ModuleKey InputGmsh::className = GetModuleFactory().RegisterCreatorFunction(
61  ModuleKey(eInputModule, "msh"), InputGmsh::create, "Reads Gmsh msh file.");
62 
63 std::map<unsigned int, ElmtConfig> InputGmsh::elmMap = InputGmsh::GenElmMap();
64 
65 /**
66  * @brief Reorder Gmsh nodes so that they appear in a tensor product format
67  * suitable for the interior of a Nektar++ quadrilateral.
68  *
69  * For an example, consider a second order quadrilateral. This routine will
70  * produce a permutation map that applies the following permutation:
71  *
72  * ```
73  * 3---6---2 6---7---8
74  * | | | |
75  * 7 8 5 ---> 3 4 5
76  * | | | |
77  * 0---4---1 0---1---2
78  *
79  * Gmsh tensor-product
80  * ```
81  *
82  * We assume that Gmsh uses a recursive ordering system, so that interior nodes
83  * are reordered by calling this function recursively.
84  *
85  * @param nodes The integer IDs of the nodes to be reordered, in Gmsh format.
86  * @param n The number of nodes in one coordinate direction. If this is
87  * zero we assume no reordering needs to be done and return the
88  * identity permutation.
89  *
90  * @return The nodes vector in tensor-product ordering.
91  */
92 std::vector<int> quadTensorNodeOrdering(const std::vector<int> &nodes, int n)
93 {
94  std::vector<int> nodeList;
95 
96  // Triangle
97  nodeList.resize(nodes.size());
98 
99  // Vertices and edges
100  nodeList[0] = nodes[0];
101  if (n > 1)
102  {
103  nodeList[n - 1] = nodes[1];
104  nodeList[n * n - 1] = nodes[2];
105  nodeList[n * (n - 1)] = nodes[3];
106  }
107  for (int i = 1; i < n - 1; ++i)
108  {
109  nodeList[i] = nodes[4 + i - 1];
110  }
111  for (int i = 1; i < n - 1; ++i)
112  {
113  nodeList[n * n - 1 - i] = nodes[4 + 2 * (n - 2) + i - 1];
114  }
115 
116  // Interior (recursion)
117  if (n > 2)
118  {
119  // Reorder interior nodes
120  std::vector<int> interior((n - 2) * (n - 2));
121  std::copy(
122  nodes.begin() + 4 + 4 * (n - 2), nodes.end(), interior.begin());
123  interior = quadTensorNodeOrdering(interior, n - 2);
124 
125  // Copy into full node list
126  for (int j = 1; j < n - 1; ++j)
127  {
128  nodeList[j * n] = nodes[4 + 3 * (n - 2) + n - 2 - j];
129  for (int i = 1; i < n - 1; ++i)
130  {
131  nodeList[j * n + i] = interior[(j - 1) * (n - 2) + (i - 1)];
132  }
133  nodeList[(j + 1) * n - 1] = nodes[4 + (n - 2) + j - 1];
134  }
135  }
136 
137  return nodeList;
138 }
139 
140 /**
141  * @brief Reorder Gmsh nodes so that they appear in a "tensor product" format
142  * suitable for the interior of a Nektar++ triangle.
143  *
144  * For an example, consider a third-order triangle. This routine will produce a
145  * permutation map that applies the following permutation:
146  *
147  * ```
148  * 2 9
149  * | \ | \
150  * 7 6 7 7
151  * | \ | \
152  * 8 9 5 4 5 6
153  * | \ | \
154  * 0---3---4---1 0---1---2---3
155  *
156  * Gmsh tensor-product
157  * ```
158  *
159  * We assume that Gmsh uses a recursive ordering system, so that interior nodes
160  * are reordered by calling this function recursively.
161  *
162  * @param nodes The integer IDs of the nodes to be reordered, in Gmsh format.
163  * @param n The number of nodes in one coordinate direction. If this is
164  * zero we assume no reordering needs to be done and return the
165  * identity permutation.
166  *
167  * @return The nodes vector in tensor-product ordering.
168  */
169 std::vector<int> triTensorNodeOrdering(const std::vector<int> &nodes, int n)
170 {
171  std::vector<int> nodeList;
172  int cnt2;
173 
174  nodeList.resize(nodes.size());
175 
176  // Vertices
177  nodeList[0] = nodes[0];
178  if (n > 1)
179  {
180  nodeList[n - 1] = nodes[1];
181  nodeList[n * (n + 1) / 2 - 1] = nodes[2];
182  }
183 
184  // Edges
185  int cnt = n;
186  for (int i = 1; i < n - 1; ++i)
187  {
188  nodeList[i] = nodes[3 + i - 1];
189  nodeList[cnt] = nodes[3 + 3 * (n - 2) - i];
190  nodeList[cnt + n - i - 1] = nodes[3 + (n - 2) + i - 1];
191  cnt += n - i;
192  }
193 
194  // Interior (recursion)
195  if (n > 3)
196  {
197  // Reorder interior nodes
198  std::vector<int> interior((n - 3) * (n - 2) / 2);
199  std::copy(
200  nodes.begin() + 3 + 3 * (n - 2), nodes.end(), interior.begin());
201  interior = triTensorNodeOrdering(interior, n - 3);
202 
203  // Copy into full node list
204  cnt = n;
205  cnt2 = 0;
206  for (int j = 1; j < n - 2; ++j)
207  {
208  for (int i = 0; i < n - j - 2; ++i)
209  {
210  nodeList[cnt + i + 1] = interior[cnt2 + i];
211  }
212  cnt += n - j;
213  cnt2 += n - 2 - j;
214  }
215  }
216 
217  return nodeList;
218 }
219 
220 typedef std::tuple<int, int, int> Mode;
221 struct cmpop
222 {
223  bool operator()(Mode const &a, Mode const &b) const
224  {
225  if (std::get<0>(a) < std::get<0>(b))
226  {
227  return true;
228  }
229  if (std::get<0>(a) > std::get<0>(b))
230  {
231  return false;
232  }
233  if (std::get<1>(a) < std::get<1>(b))
234  {
235  return true;
236  }
237  if (std::get<1>(a) > std::get<1>(b))
238  {
239  return false;
240  }
241  if (std::get<2>(a) < std::get<2>(b))
242  {
243  return true;
244  }
245 
246  return false;
247  }
248 };
249 
250 /**
251  * @brief Reorder Gmsh nodes so that they appear in a "tensor product" format
252  * suitable for the interior of a Nektar++ tetrahedron.
253  *
254  * For an example, consider a second order tetrahedron. This routine will
255  * produce a permutation map that applies the following permutation:
256  *
257  * ```
258  * 2 5
259  * ,/|`\ ,/|`\
260  * ,/ | `\ ,/ | `\
261  * ,6 '. `5 ,3 '. `4
262  * ,/ 8 `\ ,/ 8 `\
263  * ,/ | `\ ,/ | `\
264  * 0--------4--'.--------1 0--------1--'.--------2
265  * `\. | ,/ `\. | ,/
266  * `\. | ,9 `\. | ,7
267  * `7. '. ,/ `6. '. ,/
268  * `\. |/ `\. |/
269  * `3 `9
270  *
271  * Gmsh tensor-product
272  * ```
273  *
274  * We assume that Gmsh uses a recursive ordering system, so that interior nodes
275  * are reordered by calling this function recursively.
276  *
277  * @param nodes The integer IDs of the nodes to be reordered, in Gmsh format.
278  * @param n The number of nodes in one coordinate direction. If this is
279  * zero we assume no reordering needs to be done and return the
280  * identity permutation.
281  *
282  * @return The nodes vector in tensor-product ordering.
283  */
284 std::vector<int> tetTensorNodeOrdering(const std::vector<int> &nodes, int n)
285 {
286  std::vector<int> nodeList;
287  int nTri = n*(n+1)/2;
288  int nTet = n*(n+1)*(n+2)/6;
289 
290  nodeList.resize(nodes.size());
291  nodeList[0] = nodes[0];
292 
293  if (n == 1)
294  {
295  return nodeList;
296  }
297 
298  // Vertices
299  nodeList[n - 1] = nodes[1];
300  nodeList[nTri - 1] = nodes[2];
301  nodeList[nTet - 1] = nodes[3];
302 
303  if (n == 2)
304  {
305  return nodeList;
306  }
307 
308  // Set up a map that takes (a,b,c) -> m to help us figure out where things
309  // are inside the tetrahedron.
310  std::map<Mode, int, cmpop> tmp;
311 
312  for (int k = 0, cnt = 0; k < n; ++k)
313  {
314  for (int j = 0; j < n - k; ++j)
315  {
316  for (int i = 0; i < n - k - j; ++i)
317  {
318  tmp[Mode(i,j,k)] = cnt++;
319  }
320  }
321  }
322 
323  // Edges
324  for (int i = 1; i < n-1; ++i)
325  {
326  int eI = i-1;
327  nodeList[tmp[Mode(i,0,0)]] = nodes[4 + eI];
328  nodeList[tmp[Mode(n-1-i,i,0)]] = nodes[4 + (n-2) + eI];
329  nodeList[tmp[Mode(0,n-1-i,0)]] = nodes[4 + 2*(n-2) + eI];
330  nodeList[tmp[Mode(0,0,n-1-i)]] = nodes[4 + 3*(n-2) + eI];
331  nodeList[tmp[Mode(0,i,n-1-i)]] = nodes[4 + 4*(n-2) + eI];
332  nodeList[tmp[Mode(i,0,n-1-i)]] = nodes[4 + 5*(n-2) + eI];
333  }
334 
335  if (n == 3)
336  {
337  return nodeList;
338  }
339 
340  // For faces, we use the triTensorNodeOrdering routine to make our lives
341  // slightly easier.
342  int nFacePts = (n-3)*(n-2)/2;
343 
344  // Grab face points and reorder into a tensor-product type format
345  vector<vector<int> > tmpNodes(4);
346  int offset = 4 + 6*(n-2);
347 
348  for (int i = 0; i < 4; ++i)
349  {
350  tmpNodes[i].resize(nFacePts);
351  for (int j = 0; j < nFacePts; ++j)
352  {
353  tmpNodes[i][j] = nodes[offset++];
354  }
355  tmpNodes[i] = triTensorNodeOrdering(tmpNodes[i], n-3);
356  }
357 
358  if (n > 4)
359  {
360  // Now align faces
361  vector<int> triVertId(3), toAlign(3);
362  triVertId[0] = 0;
363  triVertId[1] = 1;
364  triVertId[2] = 2;
365 
366  // Faces 0,2: triangle verts {0,2,1} --> {0,1,2}
367  HOTriangle<int> hoTri(triVertId, tmpNodes[0]);
368  toAlign[0] = 0;
369  toAlign[1] = 2;
370  toAlign[2] = 1;
371 
372  hoTri.Align(toAlign);
373  tmpNodes[0] = hoTri.surfVerts;
374 
375  hoTri.surfVerts = tmpNodes[2];
376  hoTri.Align(toAlign);
377  tmpNodes[2] = hoTri.surfVerts;
378 
379  // Face 3: triangle verts {1,2,0} --> {0,1,2}
380  toAlign[0] = 1;
381  toAlign[1] = 2;
382  toAlign[2] = 0;
383 
384  hoTri.surfVerts = tmpNodes[3];
385  hoTri.Align(toAlign);
386  tmpNodes[3] = hoTri.surfVerts;
387  }
388 
389  // Now apply faces. Note that faces 3 and 2 are swapped between Gmsh and
390  // Nektar++ order.
391  for (int j = 1, cnt = 0; j < n-2; ++j)
392  {
393  for (int i = 1; i < n-j-1; ++i, ++cnt)
394  {
395  nodeList[tmp[Mode(i,j,0)]] = tmpNodes[0][cnt];
396  nodeList[tmp[Mode(i,0,j)]] = tmpNodes[1][cnt];
397  nodeList[tmp[Mode(n-1-i-j,i,j)]] = tmpNodes[3][cnt];
398  nodeList[tmp[Mode(0,i,j)]] = tmpNodes[2][cnt];
399  }
400  }
401 
402  if (n == 4)
403  {
404  return nodeList;
405  }
406 
407  // Finally, recurse on interior volume
408  vector<int> intNodes, tmpInt;
409  for (int i = offset; i < nTet; ++i)
410  {
411  intNodes.push_back(nodes[i]);
412  }
413  tmpInt = tetTensorNodeOrdering(intNodes, n-4);
414 
415  for (int k = 1, cnt = 0; k < n - 2; ++k)
416  {
417  for (int j = 1; j < n - k - 1; ++j)
418  {
419  for (int i = 1; i < n - k - j - 1; ++i)
420  {
421  nodeList[tmp[Mode(i,j,k)]] = tmpInt[cnt++];
422  }
423  }
424  }
425 
426  return nodeList;
427 }
428 
429 /**
430  * @brief Reorder Gmsh nodes so that they appear in a "tensor product" format
431  * suitable for the interior of a Nektar++ prism. This routine is specifically
432  * designed for *interior* Gmsh nodes only.
433  *
434  * Prisms are a bit of a special case, in that interior nodes are heterogeneous
435  * in the number of points they use. As an example, for a second-order interior
436  * of a fourth-order prism, this routine calculates the mapping taking us
437  *
438  * ```
439  * ,6 ,8
440  * .-' | \ .-' | \
441  * ,-3 | \ ,-5 | \
442  * ,-' | \ | \ ,-' | \ | \
443  * 2 | \7-------8 2 | \6-------7
444  * | \ | ,-' \ ,-' | \ | ,-' \ ,-'
445  * | \,5------,0' | \,3------,4'
446  * |,-' \ ,-' |,-' \ ,-'
447  * 1-------4' 0-------1'
448  *
449  * Gmsh tensor-product
450  * ```
451  *
452  * @todo Make this routine work for higher orders. The Gmsh ordering seems
453  * a little different than other elements, therefore the functionality is
454  * (for now) hard-coded to orders less than 5.
455  *
456  * @param nodes The integer IDs of the nodes to be reordered, in Gmsh format.
457  * @param n The number of nodes in one coordinate direction. If this is
458  * zero we assume no reordering needs to be done and return the
459  * identity permutation.
460  *
461  * @return The nodes vector in tensor-product ordering.
462  */
463 std::vector<int> prismTensorNodeOrdering(const std::vector<int> &nodes, int n)
464 {
465  std::vector<int> nodeList;
466 
467  if (n == 0)
468  {
469  return nodeList;
470  }
471 
472  nodeList.resize(nodes.size());
473 
474  if (n == 2)
475  {
476  nodeList[0] = nodes[1];
477  nodeList[1] = nodes[0];
478  return nodeList;
479  }
480 
481  // For some reason, this ordering is different. Whereas Gmsh usually orders
482  // vertices first, followed by edges, this ordering goes VVE-VVE-VVE for the
483  // three edges that contain edge-interior information, but also vertex
484  // orientation is not the same as the original Gmsh prism. The below is done
485  // by looking at the ordering by hand and is hence why we don't support
486  // order > 4 right now.
487  if (n == 3)
488  {
489  nodeList[0] = nodes[1];
490  nodeList[1] = nodes[4];
491  nodeList[2] = nodes[2];
492  nodeList[3] = nodes[5];
493  nodeList[4] = nodes[0];
494  nodeList[5] = nodes[3];
495  nodeList[6] = nodes[7];
496  nodeList[7] = nodes[8];
497  nodeList[8] = nodes[6];
498  }
499 
500  ASSERTL0(n < 4, "Prism Gmsh input and output is incomplete for orders "
501  "larger than 4");
502 
503  return nodeList;
504 }
505 
506 /**
507  * @brief Reorder Gmsh nodes so that they appear in a "tensor product" format
508  * suitable for the interior of a Nektar++ hexahedron.
509  *
510  * For an example, consider a second order hexahedron. This routine will produce
511  * a permutation map that applies the following permutation:
512  *
513  * ```
514  * 3----13----2 6----7-----8
515  * |\ |\ |\ |\
516  * |15 24 | 14 |15 16 | 18
517  * 9 \ 20 11 \ 3 \ 4 5 \
518  * | 7----19+---6 | 24---25+---26
519  * |22 | 26 | 23| |12 | 13 | 14|
520  * 0---+-8----1 | 0---+-1----2 |
521  * \ 17 25 \ 18 \ 21 22 \ 23
522  * 10 | 21 12| 9 | 10 11|
523  * \| \| \| \|
524  * 4----16----5 18---19----20
525  *
526  * Gmsh tensor-product
527  * ```
528  *
529  * We assume that Gmsh uses a recursive ordering system, so that interior nodes
530  * are reordered by calling this function recursively.
531  *
532  * @param nodes The integer IDs of the nodes to be reordered, in Gmsh format.
533  * @param n The number of nodes in one coordinate direction. If this is
534  * zero we assume no reordering needs to be done and return the
535  * identity permutation.
536  *
537  * @return The nodes vector in tensor-product ordering.
538  */
539 std::vector<int> hexTensorNodeOrdering(const std::vector<int> &nodes, int n)
540 {
541  int i, j, k;
542  std::vector<int> nodeList;
543 
544  nodeList.resize(nodes.size());
545  nodeList[0] = nodes[0];
546 
547  if (n == 1)
548  {
549  return nodeList;
550  }
551 
552  // Vertices: same order as Nektar++
553  nodeList[n - 1] = nodes[1];
554  nodeList[n*n -1] = nodes[2];
555  nodeList[n*(n-1)] = nodes[3];
556  nodeList[n*n*(n-1)] = nodes[4];
557  nodeList[n - 1 + n*n*(n-1)] = nodes[5];
558  nodeList[n*n -1 + n*n*(n-1)] = nodes[6];
559  nodeList[n*(n-1) + n*n*(n-1)] = nodes[7];
560 
561  if (n == 2)
562  {
563  return nodeList;
564  }
565 
566  int hexEdges[12][2] = {
567  { 0, 1 }, { n-1, n }, { n*n-1, -1 }, { n*(n-1), -n },
568  { 0, n*n }, { n-1, n*n }, { n*n - 1, n*n }, { n*(n-1), n*n },
569  { n*n*(n-1), 1 }, { n*n*(n-1) + n-1, n }, { n*n*n-1, -1 },
570  { n*n*(n-1) + n*(n-1), -n }
571  };
572  int hexFaces[6][3] = {
573  { 0, 1, n }, { 0, 1, n*n }, { n-1, n, n*n },
574  { n*(n-1), 1, n*n }, { 0, n, n*n }, { n*n*(n-1), 1, n }
575  };
576  int gmshToNekEdge[12] = {0, -3, 4, 1, 5, 2, 6, 7, 8, -11, 9, 10};
577 
578  // Edges
579  int offset = 8;
580  for (int i = 0; i < 12; ++i)
581  {
582  int e = abs(gmshToNekEdge[i]);
583 
584  if (gmshToNekEdge[i] >= 0)
585  {
586  for (int j = 1; j < n-1; ++j)
587  {
588  nodeList[hexEdges[e][0] + j*hexEdges[e][1]] = nodes[offset++];
589  }
590  }
591  else
592  {
593  for (int j = 1; j < n-1; ++j)
594  {
595  nodeList[hexEdges[e][0] + (n-j-1)*hexEdges[e][1]] = nodes[offset++];
596  }
597  }
598  }
599 
600  // Faces
601  int gmsh2NekFace[6] = {0, 1, 4, 2, 3, 5};
602 
603  // Map which defines orientation between Gmsh and Nektar++ faces.
604  StdRegions::Orientation faceOrient[6] = {
610  StdRegions::eDir1FwdDir1_Dir2FwdDir2};
611 
612  for (i = 0; i < 6; ++i)
613  {
614  int n2 = (n-2)*(n-2);
615  int face = gmsh2NekFace[i];
616  offset = 8 + 12 * (n-2) + i * n2;
617 
618  // Create a list of interior face nodes for this face only.
619  vector<int> faceNodes(n2);
620  for (j = 0; j < n2; ++j)
621  {
622  faceNodes[j] = nodes[offset + j];
623  }
624 
625  // Now get the reordering of this face, which puts Gmsh
626  // recursive ordering into Nektar++ row-by-row order.
627  faceNodes = quadTensorNodeOrdering(faceNodes, n-2);
628  vector<int> tmp(n2);
629 
630  // Finally reorient the face according to the geometry
631  // differences.
632  if (faceOrient[i] == StdRegions::eDir1FwdDir1_Dir2FwdDir2)
633  {
634  // Orientation is the same, just copy.
635  tmp = faceNodes;
636  }
637  else if (faceOrient[i] == StdRegions::eDir1FwdDir2_Dir2FwdDir1)
638  {
639  // Tranposed faces
640  for (j = 0; j < n-2; ++j)
641  {
642  for (k = 0; k < n-2; ++k)
643  {
644  tmp[j * (n-2) + k] = faceNodes[k * (n-2) + j];
645  }
646  }
647  }
648  else if (faceOrient[i] == StdRegions::eDir1BwdDir1_Dir2FwdDir2)
649  {
650  for (j = 0; j < n-2; ++j)
651  {
652  for (k = 0; k < n-2; ++k)
653  {
654  tmp[j * (n-2) + k] = faceNodes[j * (n-2) + (n - k - 3)];
655  }
656  }
657  }
658 
659  // Now put this into the right place in the output array
660  for (k = 1; k < n-1; ++k)
661  {
662  for (j = 1; j < n-1; ++j)
663  {
664  nodeList[hexFaces[face][0] + j*hexFaces[face][1] + k*hexFaces[face][2]]
665  = faceNodes[(k-1)*(n-2) + j-1];
666  }
667  }
668  }
669 
670  // Finally, recurse on interior volume
671  vector<int> intNodes, tmpInt;
672  for (int i = 8 + 12 * (n-2) + 6 * (n-2) * (n-2); i < n*n*n; ++i)
673  {
674  intNodes.push_back(nodes[i]);
675  }
676 
677  if (intNodes.size())
678  {
679  tmpInt = hexTensorNodeOrdering(intNodes, n-2);
680  for (int k = 1, cnt = 0; k < n - 1; ++k)
681  {
682  for (int j = 1; j < n - 1; ++j)
683  {
684  for (int i = 1; i < n - 1; ++i)
685  {
686  nodeList[i + j * n + k * n * n] = tmpInt[cnt++];
687  }
688  }
689  }
690  }
691 
692  return nodeList;
693 }
694 
695 
696 /**
697  * @brief Set up InputGmsh object.
698  *
699  */
700 InputGmsh::InputGmsh(MeshSharedPtr m)
701  : InputModule(m), m_version(0.0), m_prevId(-1), m_maxTagId(-1)
702 {
703 }
704 
706 {
707 }
708 
709 /**
710  * @brief Representation of Gmsh entity so that we can extract physical tag IDs.
711  */
713 {
714  double minX, minY, minZ;
715  double maxX, maxY, maxZ;
716  std::vector<int> physicalTags;
717 };
718 
719 /**
720  * Gmsh file contains a list of nodes and their coordinates, along with a list
721  * of elements and those nodes which define them. We read in and store the list
722  * of nodes in #m_node and store the list of elements in #m_element. Each new
723  * element is supplied with a list of entries from m_node which defines the
724  * element. Finally some mesh statistics are printed.
725  *
726  * @param pFilename Filename of Gmsh file to read.
727  */
729 {
730  // Open the file stream.
731  OpenStream();
732 
733  m_mesh->m_expDim = 0;
734  m_mesh->m_spaceDim = 0;
735  string line;
736  int fileType = 0;
737  int nVBlocks = 0;
738  int nVertices = 0;
739  int nEBlocks = 0;
740  int nElements = 0;
741  int tag = 0;
742  int elm_type = 0;
743  int tmp = 0;
744 
745  if (m_mesh->m_verbose)
746  {
747  cout << "InputGmsh: Start reading file..." << endl;
748  }
749 
750  std::vector<std::map<int, GmshEntity>> entityMap(4);
751 
752  while (!m_mshFile.eof())
753  {
754  getline(m_mshFile, line);
755  stringstream s(line);
756  string word;
757  s >> word;
758 
759  // Process file format.
760  if (word == "$MeshFormat")
761  {
762  getline(m_mshFile, line);
763  stringstream s(line);
764  s >> m_version;
765  s >> fileType;
766  ASSERTL0(fileType == 0, "Cannot read binary Gmsh files.")
767  ASSERTL0(m_version <= 4.1, ".msh file format versions greater than 4.1 are not currently supported.")
768  }
769  // Process entities (v4+)
770  else if (word == "$Entities")
771  {
772  size_t nEntity[4];
773  getline(m_mshFile, line);
774  stringstream s(line);
775 
776  s >> nEntity[0] >> nEntity[1] >> nEntity[2] >> nEntity[3];
777 
778  for (int i = 0; i < 4; ++i)
779  {
780  for (int j = 0; j < nEntity[i]; ++j)
781  {
782  getline(m_mshFile, line);
783  stringstream si(line);
784  GmshEntity ent;
785 
786  int entityId;
787  si >> entityId >> ent.minX >> ent.minY >> ent.minZ;
788 
789  if (i > 0)
790  {
791  si >> ent.maxX >> ent.maxY >> ent.maxZ;
792  }
793 
794  int nPhysTags, tmp;
795  si >> nPhysTags;
796 
797  for (int k = 0; k < nPhysTags; ++k)
798  {
799  si >> tmp;
800  ent.physicalTags.push_back(tmp);
801  }
802 
803  entityMap[i][entityId] = ent;
804  }
805  }
806  }
807  // Process nodes.
808  else if (word == "$Nodes")
809  {
810  getline(m_mshFile, line);
811  stringstream s(line);
812 
813  if (m_version >= 4.0)
814  {
815  s >> nVBlocks;
816 
817  for (int i = 0; i < nVBlocks; ++i)
818  {
819  getline(m_mshFile, line);
820  stringstream si(line);
821  si >> tmp >> tmp >> tmp >> nVertices;
822 
823  if (m_version == 4.0)
824  {
825  for (int i = 0; i < nVertices; ++i)
826  {
827  ReadNextNode();
828  }
829  }
830  else if (m_version == 4.1)
831  {
832  ReadNextNodeBlock(nVertices);
833  }
834  }
835  }
836  else
837  {
838  s >> nVertices;
839 
840  for (int i = 0; i < nVertices; ++i)
841  {
842  ReadNextNode();
843  }
844  }
845  }
846  // Process elements
847  else if (word == "$Elements")
848  {
849  getline(m_mshFile, line);
850  stringstream s(line);
851 
852  if (m_version >= 4.0)
853  {
854  s >> nEBlocks;
855 
856  for (int i = 0; i < nEBlocks; ++i)
857  {
858  getline(m_mshFile, line);
859  stringstream si(line);
860 
861  int tagDim;
862  if (m_version == 4.0)
863  {
864  si >> tag >> tagDim >> elm_type >> nElements;
865  }
866  else
867  {
868  si >> tagDim >> tag >> elm_type >> nElements;
869  }
870  // Query tag in map & don't bother constructing non-physical
871  // surfaces.
872  std::vector<int> physIds = entityMap[tagDim][tag].physicalTags;
873 
874  if (physIds.size() == 0)
875  {
876  for (int j = 0; j < nElements; ++j)
877  {
878  getline(m_mshFile, line);
879  }
880  }
881  else
882  {
883  tag = physIds[0];
884  for (int j = 0; j < nElements; ++j)
885  {
886  ReadNextElement(tag, elm_type);
887  }
888  }
889 
890  }
891  }
892  else
893  {
894  s >> nElements;
895  for (int i = 0; i < nElements; ++i)
896  {
897  ReadNextElement();
898  }
899  }
900  }
901  }
902  m_mshFile.reset();
903 
904  // Go through element and remap tags if necessary.
905  map<int, map<LibUtilities::ShapeType, int> > compMap;
906 
907  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
908  {
909  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
910  LibUtilities::ShapeType type = el->GetConf().m_e;
911 
912  vector<int> tags = el->GetTagList();
913  int tag = tags[0];
914 
915  auto cIt = compMap.find(tag);
916 
917  if (cIt == compMap.end())
918  {
919  compMap[tag][type] = tag;
920  continue;
921  }
922 
923  // Reset tag for this element.
924  auto sIt = cIt->second.find(type);
925  if (sIt == cIt->second.end())
926  {
927  m_maxTagId++;
928  cIt->second[type] = m_maxTagId;
929  tags[0] = m_maxTagId;
930  el->SetTagList(tags);
931  }
932  else if (sIt->second != tag)
933  {
934  tags[0] = sIt->second;
935  el->SetTagList(tags);
936  }
937  }
938 
939  bool printInfo = false;
940  for (auto &cIt : compMap)
941  {
942  if (cIt.second.size() > 1)
943  {
944  printInfo = true;
945  break;
946  }
947  }
948 
949  if (printInfo)
950  {
951  cout << "Multiple elements in composite detected; remapped:" << endl;
952  for (auto &cIt : compMap)
953  {
954  if (cIt.second.size() > 1)
955  {
956  auto sIt = cIt.second.begin();
957  cout << "- Tag " << cIt.first << " => " << sIt->second << " ("
958  << LibUtilities::ShapeTypeMap[sIt->first] << ")";
959  sIt++;
960 
961  for (; sIt != cIt.second.end(); ++sIt)
962  {
963  cout << ", " << sIt->second << " ("
964  << LibUtilities::ShapeTypeMap[sIt->first] << ")";
965  }
966 
967  cout << endl;
968  }
969  }
970  }
971 
972  // Process rest of mesh.
973  ProcessVertices();
974  ProcessEdges();
975  ProcessFaces();
976  ProcessElements();
978 }
979 
980 /**
981  * Read in next node block for v4 format
982  */
983 void InputGmsh::ReadNextNodeBlock(int nVertices)
984 {
985  string line;
986  double x = 0, y = 0, z = 0;
987  vector<int> id(nVertices);
988 
989  for (int i = 0; i < nVertices; ++i)
990  {
991  getline(m_mshFile, line);
992  stringstream st(line);
993  st >> id[i];
994  }
995 
996  for (int i = 0; i < nVertices; ++i)
997  {
998  getline(m_mshFile, line);
999  stringstream st(line);
1000  st >> x >> y >> z;
1001 
1002  SaveNode(id[i], x, y, z);
1003  }
1004 }
1005 
1006 /**
1007  * Read in next node
1008  */
1010 {
1011  string line;
1012  getline(m_mshFile, line);
1013  stringstream st(line);
1014  double x = 0, y = 0, z = 0;
1015  int id = 0;
1016  st >> id >> x >> y >> z;
1017 
1018  SaveNode(id, x, y, z);
1019 }
1020 
1021 /**
1022  * Save node into mesh
1023  */
1025 {
1026  if ((x * x) > 0.000001 && m_mesh->m_spaceDim < 1)
1027  {
1028  m_mesh->m_spaceDim = 1;
1029  }
1030  if ((y * y) > 0.000001 && m_mesh->m_spaceDim < 2)
1031  {
1032  m_mesh->m_spaceDim = 2;
1033  }
1034  if ((z * z) > 0.000001 && m_mesh->m_spaceDim < 3)
1035  {
1036  m_mesh->m_spaceDim = 3;
1037  }
1038 
1039  id -= 1; // counter starts at 0
1040 
1041  if (!m_idMap.size())
1042  {
1043  if (id - m_prevId == 1)
1044  {
1045  m_prevId = id;
1046  }
1047  else
1048  {
1049  // Build m_idMap so far
1050  for (int i = 0; i < m_mesh->m_node.size(); ++i)
1051  {
1052  m_idMap[i] = i;
1053  }
1054 
1055  m_idMap[id] = m_mesh->m_node.size();
1056  }
1057  }
1058  else
1059  {
1060  m_idMap[id] = m_mesh->m_node.size();
1061  }
1062 
1063  m_mesh->m_node.push_back(std::shared_ptr<Node>(new Node(id, x, y, z)));
1064 }
1065 
1066 /**
1067  * Read in next element
1068  */
1069 void InputGmsh::ReadNextElement(int tag, int elm_type)
1070 {
1071  string line;
1072  getline(m_mshFile, line);
1073  stringstream st(line);
1074  int id = 0, num_tag = 0, num_nodes = 0;
1075 
1076  st >> id;
1077  id -= 1; // counter starts at 0
1078 
1079  if (m_version < 4.0)
1080  {
1081  st >> elm_type >> num_tag;
1082  }
1083 
1084  auto it = elmMap.find(elm_type);
1085  if (it == elmMap.end())
1086  {
1087  cerr << "Error: element type " << elm_type << " not supported" << endl;
1088  abort();
1089  }
1090 
1091  // Read element tags (version 2 only)
1092  vector<int> tags;
1093  for (int j = 0; j < num_tag; ++j)
1094  {
1095  int tag = 0;
1096  st >> tag;
1097  tags.push_back(tag);
1098  }
1099  if (m_version >= 4.0)
1100  {
1101  tags.push_back(tag);
1102  }
1103  tags.resize(1);
1104 
1105  m_maxTagId = max(m_maxTagId, tags[0]);
1106 
1107  // Read element node list
1108  vector<NodeSharedPtr> nodeList;
1109  num_nodes = GetNnodes(elm_type);
1110  for (int k = 0; k < num_nodes; ++k)
1111  {
1112  int node = 0;
1113  st >> node;
1114  node -= 1; // counter starts at 0
1115  if (!m_idMap.size())
1116  {
1117  nodeList.push_back(m_mesh->m_node[node]);
1118  }
1119  else
1120  {
1121  nodeList.push_back(m_mesh->m_node[m_idMap[node]]);
1122  }
1123  }
1124 
1125  // Look up reordering.
1126  auto oIt = m_orderingMap.find(elm_type);
1127 
1128  // If it's not created, then create it.
1129  if (oIt == m_orderingMap.end())
1130  {
1131  oIt =
1132  m_orderingMap.insert(make_pair(elm_type, CreateReordering(elm_type)))
1133  .first;
1134  }
1135 
1136  // Apply reordering map where necessary.
1137  if (oIt->second.size() > 0)
1138  {
1139  vector<int> &mapping = oIt->second;
1140  vector<NodeSharedPtr> tmp = nodeList;
1141  for (int i = 0; i < mapping.size(); ++i)
1142  {
1143  nodeList[i] = tmp[mapping[i]];
1144  }
1145  }
1146 
1147  // Create element
1149  it->second.m_e, it->second, nodeList, tags);
1150 
1151  // Determine mesh expansion dimension
1152  if (E->GetDim() > m_mesh->m_expDim)
1153  {
1154  m_mesh->m_expDim = E->GetDim();
1155  }
1156  m_mesh->m_element[E->GetDim()].push_back(E);
1157 }
1158 
1159 /**
1160  * For a given msh ID, return the corresponding number of nodes.
1161  */
1162 int InputGmsh::GetNnodes(unsigned int InputGmshEntity)
1163 {
1164  int nNodes;
1165 
1166  auto it = elmMap.find(InputGmshEntity);
1167 
1168  if (it == elmMap.end())
1169  {
1170  cerr << "Unknown element type " << InputGmshEntity << endl;
1171  abort();
1172  }
1173 
1174  switch (it->second.m_e)
1175  {
1176  case LibUtilities::ePoint:
1177  nNodes = Point::GetNumNodes(it->second);
1178  break;
1180  nNodes = Line::GetNumNodes(it->second);
1181  break;
1183  nNodes = Triangle::GetNumNodes(it->second);
1184  break;
1186  nNodes = Quadrilateral::GetNumNodes(it->second);
1187  break;
1189  nNodes = Tetrahedron::GetNumNodes(it->second);
1190  it->second.m_faceCurveType = LibUtilities::eNodalTriEvenlySpaced;
1191  break;
1193  nNodes = Pyramid::GetNumNodes(it->second);
1194  break;
1195  case LibUtilities::ePrism:
1196  nNodes = Prism::GetNumNodes(it->second);
1197  break;
1199  nNodes = Hexahedron::GetNumNodes(it->second);
1200  break;
1201  default:
1202  cerr << "Unknown element type!" << endl;
1203  abort();
1204  break;
1205  }
1206 
1207  return nNodes;
1208 }
1209 
1210 /**
1211  * @brief Create a reordering map for a given element.
1212  *
1213  * Since Gmsh and Nektar++ have different vertex, edge and face
1214  * orientations, we need to reorder the nodes in a Gmsh MSH file so that
1215  * they work with the Nektar++ orderings, since this is what is used in
1216  * the elements defined in the converter.
1217  */
1218 vector<int> InputGmsh::CreateReordering(unsigned int InputGmshEntity)
1219 {
1220  auto it = elmMap.find(InputGmshEntity);
1221 
1222  if (it == elmMap.end())
1223  {
1224  cerr << "Unknown element type " << InputGmshEntity << endl;
1225  abort();
1226  }
1227 
1228  // For specific elements, call the appropriate function to perform
1229  // the renumbering.
1230  switch (it->second.m_e)
1231  {
1233  return TriReordering(it->second);
1234  break;
1236  return QuadReordering(it->second);
1237  break;
1239  return TetReordering(it->second);
1240  break;
1241  case LibUtilities::ePrism:
1242  return PrismReordering(it->second);
1243  break;
1245  return HexReordering(it->second);
1246  break;
1248  return LineReordering(it->second);
1249  default:
1250  break;
1251  }
1252 
1253  // Default: no reordering.
1254  vector<int> returnVal;
1255  return returnVal;
1256 }
1257 
1259 {
1260  const int order = conf.m_order;
1261 
1262  vector<int> mapping(order+1);
1263  for(int i = 0; i < order+1; i++)
1264  {
1265  mapping[i] = i;
1266  }
1267 
1268  return mapping;
1269 }
1270 
1271 /**
1272  * @brief Create a reordering for triangles.
1273  */
1275 {
1276  const int order = conf.m_order;
1277  const int n = order - 1;
1278 
1279  // Copy vertices.
1280  vector<int> mapping(3);
1281  for (int i = 0; i < 3; ++i)
1282  {
1283  mapping[i] = i;
1284  }
1285 
1286  if (order == 1)
1287  {
1288  return mapping;
1289  }
1290 
1291  // Curvilinear edges.
1292  mapping.resize(3 + 3 * n);
1293 
1294  for (int i = 3; i < 3 + 3 * n; ++i)
1295  {
1296  mapping[i] = i;
1297  }
1298 
1299  if (!conf.m_faceNodes)
1300  {
1301  return mapping;
1302  }
1303 
1304  // Interior nodes.
1305  vector<int> interior(n * (n - 1) / 2);
1306  for (int i = 0; i < interior.size(); ++i)
1307  {
1308  interior[i] = i + 3 + 3 * n;
1309  }
1310 
1311  if (interior.size() > 0)
1312  {
1313  interior = triTensorNodeOrdering(interior, n - 1);
1314  }
1315 
1316  mapping.insert(mapping.end(), interior.begin(), interior.end());
1317  return mapping;
1318 }
1319 
1320 /**
1321  * @brief Create a reordering for quadrilaterals.
1322  */
1324 {
1325  const int order = conf.m_order;
1326  const int n = order - 1;
1327 
1328  // Copy vertices.
1329  vector<int> mapping(4);
1330  for (int i = 0; i < 4; ++i)
1331  {
1332  mapping[i] = i;
1333  }
1334 
1335  if (order == 1)
1336  {
1337  return mapping;
1338  }
1339 
1340  // Curvilinear edges.
1341  mapping.resize(4 + 4 * n);
1342 
1343  for (int i = 4; i < 4 + 4 * n; ++i)
1344  {
1345  mapping[i] = i;
1346  }
1347 
1348  if (!conf.m_faceNodes)
1349  {
1350  return mapping;
1351  }
1352 
1353  // Interior nodes.
1354  vector<int> interior(n * n);
1355  for (int i = 0; i < interior.size(); ++i)
1356  {
1357  interior[i] = i + 4 + 4 * n;
1358  }
1359 
1360  if (interior.size() > 0)
1361  {
1362  interior = quadTensorNodeOrdering(interior, n);
1363  }
1364  mapping.insert(mapping.end(), interior.begin(), interior.end());
1365  return mapping;
1366 }
1367 
1368 /**
1369  * @brief Create a reordering for tetrahedra.
1370  */
1372 {
1373  const int order = conf.m_order;
1374  const int n = order - 1;
1375  const int n2 = n * (n - 1) / 2;
1376 
1377  int i, j;
1378  vector<int> mapping(4);
1379 
1380  // Copy vertices.
1381  for (i = 0; i < 4; ++i)
1382  {
1383  mapping[i] = i;
1384  }
1385 
1386  if (order == 1)
1387  {
1388  return mapping;
1389  }
1390 
1391  // Curvilinear edges.
1392  mapping.resize(4 + 6 * n);
1393 
1394  // Curvilinear edges.
1395  static int gmshToNekEdge[6] = {0, 1, 2, 3, 5, 4};
1396  static int gmshToNekRev[6] = {0, 0, 1, 1, 1, 1};
1397 
1398  // Reorder edges.
1399  int offset, cnt = 4;
1400  for (i = 0; i < 6; ++i)
1401  {
1402  offset = 4 + n * gmshToNekEdge[i];
1403 
1404  if (gmshToNekRev[i])
1405  {
1406  for (int j = 0; j < n; ++j)
1407  {
1408  mapping[offset + n - j - 1] = cnt++;
1409  }
1410  }
1411  else
1412  {
1413  for (int j = 0; j < n; ++j)
1414  {
1415  mapping[offset + j] = cnt++;
1416  }
1417  }
1418  }
1419 
1420  if (conf.m_faceNodes == false || n2 == 0)
1421  {
1422  return mapping;
1423  }
1424 
1425  // Curvilinear faces.
1426  mapping.resize(4 + 6 * n + 4 * n2);
1427 
1428  static int gmshToNekFace[4] = {0, 1, 3, 2};
1429 
1430  vector<int> triVertId(3);
1431  triVertId[0] = 0;
1432  triVertId[1] = 1;
1433  triVertId[2] = 2;
1434 
1435  // Loop over Gmsh faces
1436  for (i = 0; i < 4; ++i)
1437  {
1438  int face = gmshToNekFace[i];
1439  int offset2 = 4 + 6 * n + i * n2;
1440  offset = 4 + 6 * n + face * n2;
1441 
1442  // Create a list of interior face nodes for this face only.
1443  vector<int> faceNodes(n2);
1444  vector<int> toAlign(3);
1445  for (j = 0; j < n2; ++j)
1446  {
1447  faceNodes[j] = offset2 + j;
1448  }
1449 
1450  // Now get the reordering of this face, which puts Gmsh
1451  // recursive ordering into Nektar++ row-by-row order.
1452  vector<int> tmp = triTensorNodeOrdering(faceNodes, n - 1);
1453  HOTriangle<int> hoTri(triVertId, tmp);
1454 
1455  // Apply reorientation
1456  if (i == 0 || i == 2)
1457  {
1458  // Triangle verts {0,2,1} --> {0,1,2}
1459  toAlign[0] = 0;
1460  toAlign[1] = 2;
1461  toAlign[2] = 1;
1462  hoTri.Align(toAlign);
1463  }
1464  else if (i == 3)
1465  {
1466  // Triangle verts {1,2,0} --> {0,1,2}
1467  toAlign[0] = 1;
1468  toAlign[1] = 2;
1469  toAlign[2] = 0;
1470  hoTri.Align(toAlign);
1471  }
1472 
1473  // Fill in mapping.
1474  for (j = 0; j < n2; ++j)
1475  {
1476  mapping[offset + j] = hoTri.surfVerts[j];
1477  }
1478  }
1479 
1480  if (conf.m_volumeNodes == false)
1481  {
1482  return mapping;
1483  }
1484 
1485  const int nInt = (order - 3) * (order - 2) * (order - 1) / 6;
1486  if (nInt <= 0)
1487  {
1488  return mapping;
1489  }
1490 
1491  if (nInt == 1)
1492  {
1493  mapping.push_back(mapping.size());
1494  return mapping;
1495  }
1496 
1497  int ntot = (order+1)*(order+2)*(order+3)/6;
1498  vector<int> interior;
1499 
1500  for (int i = 4 + 6 * n + 4 * n2; i < ntot; ++i)
1501  {
1502  interior.push_back(i);
1503  }
1504 
1505  if (interior.size() > 0)
1506  {
1507  interior = tetTensorNodeOrdering(interior, order-3);
1508  }
1509 
1510  mapping.insert(mapping.end(), interior.begin(), interior.end());
1511 
1512  return mapping;
1513 }
1514 
1515 /**
1516  * @brief Create a reordering for prisms.
1517  *
1518  * Note that whilst Gmsh MSH files have the capability to support
1519  * high-order prisms, presently Gmsh does not seem to be capable of
1520  * generating higher than second-order prismatic meshes, so most of the
1521  * following is untested.
1522  */
1524 {
1525  const int order = conf.m_order;
1526  const int n = order - 1;
1527 
1528  int i;
1529  vector<int> mapping(6);
1530 
1531  // To get from Gmsh -> Nektar++ prism, coordinates axes are
1532  // different; need to mirror in the triangular faces, and then
1533  // reorder vertices to make ordering anticlockwise on base quad.
1534  static int nekToGmshVerts[6] = {3, 4, 1, 0, 5, 2};
1535  // Inverse (gmsh vert -> Nektar++ vert): 3->0, 4->1, 1->2, 0->3, 5->4, 2->5
1536 
1537  for (i = 0; i < 6; ++i)
1538  {
1539  mapping[i] = nekToGmshVerts[i];
1540  }
1541 
1542  if (order == 1)
1543  {
1544  return mapping;
1545  }
1546 
1547  // Curvilinear edges.
1548  mapping.resize(6 + 9 * n);
1549 
1550  // Nektar++ edges:
1551  // 0 = 1 = 2 = 3 = 4 = 5 = 6 = 7 = 8 =
1552  // {0,1}, {1,2}, {3,2}, {0,3}, {0,4}, {1,4}, {2,5}, {3,5}, {4,5}
1553  // Gmsh edges (from Geo/MPrism.h of Gmsh source):
1554  // {0,1}, {0,2}, {0,3}, {1,2}, {1,4}, {2,5}, {3,4}, {3,5}, {4,5}
1555  // Apply inverse of gmshToNekVerts map:
1556  // {3,2}, {3,5}, {3,0}, {2,5}, {2,1}, {5,4}, {0,1}, {0,4}, {1,4}
1557  // Nektar++ mapping (negative indicates reverse orientation):
1558  // = 2 = 7 = -3 = 6 = -1 = -8 = 0 = 4 = 5
1559  static int gmshToNekEdge[9] = {2, 7, 3, 6, 1, 8, 0, 4, 5};
1560  static int gmshToNekRev[9] = {0, 0, 1, 0, 1, 1, 0, 0, 0};
1561 
1562  // Reorder edges.
1563  int offset, cnt = 6;
1564  for (i = 0; i < 9; ++i)
1565  {
1566  offset = 6 + n * gmshToNekEdge[i];
1567 
1568  if (gmshToNekRev[i])
1569  {
1570  for (int j = 0; j < n; ++j)
1571  {
1572  mapping[offset + n - j - 1] = cnt++;
1573  }
1574  }
1575  else
1576  {
1577  for (int j = 0; j < n; ++j)
1578  {
1579  mapping[offset + j] = cnt++;
1580  }
1581  }
1582  }
1583 
1584  if (conf.m_faceNodes == false)
1585  {
1586  return mapping;
1587  }
1588 
1589  int nTriInt = n * (n - 1) / 2;
1590  int nQuadInt = n * n;
1591 
1592  // Nektar++ faces:
1593  // 0 = {0,1,2,3}, 1 = {0,1,4}, 2 = {1,2,5,4}, 3 = {3,2,5}, 4 = {0,3,5,4}
1594  // Gmsh faces (from Geo/MPrism.h of Gmsh source):
1595  // {0,2,1}, {3,4,5}, {0,1,4,3}, {0,3,5,2}, {1,2,5,4}
1596  // Apply inverse of gmshToNekVerts map:
1597  // {3,5,2}, {0,1,4}, {3,2,1,0}, {3,0,4,5}, {2,5,4,1}
1598  // = 3 = 1 = 0 = 4 = 2
1599  // This gives gmsh -> Nektar++ faces:
1600  static int gmshToNekFace[5] = {3, 1, 0, 4, 2};
1601 
1602  // Face offsets
1603  vector<int> offsets(5), offsets2(5);
1604  offset = 6 + 9 * n;
1605 
1606  // Offsets in the gmsh order: face ordering is TTQQQ
1607  offsets[0] = offset;
1608  offsets[1] = offset + nTriInt;
1609  offsets[2] = offset + 2 * nTriInt;
1610  offsets[3] = offset + 2 * nTriInt + nQuadInt;
1611  offsets[4] = offset + 2 * nTriInt + 2 * nQuadInt;
1612 
1613  // Offsets in the Nektar++ order: face ordering is QTQTQ
1614  offsets2[0] = offset;
1615  offsets2[1] = offset + nQuadInt;
1616  offsets2[2] = offset + nQuadInt + nTriInt;
1617  offsets2[3] = offset + 2 * nQuadInt + nTriInt;
1618  offsets2[4] = offset + 2 * nQuadInt + 2 * nTriInt;
1619 
1620  mapping.resize(6 + 9 * n + 3 * nQuadInt + 2 * nTriInt);
1621 
1622  offset = 6 + 9 * n;
1623 
1624  vector<int> triVertId(3);
1625  triVertId[0] = 0;
1626  triVertId[1] = 1;
1627  triVertId[2] = 2;
1628 
1629  vector<int> quadVertId(4);
1630  quadVertId[0] = 0;
1631  quadVertId[1] = 1;
1632  quadVertId[2] = 2;
1633  quadVertId[3] = 3;
1634 
1635  for (i = 0; i < 5; ++i)
1636  {
1637  int face = gmshToNekFace[i];
1638  int offset2 = offsets[i];
1639  offset = offsets2[face];
1640 
1641  bool tri = i < 2;
1642  int nFacePts = tri ? nTriInt : nQuadInt;
1643 
1644  if (nFacePts == 0)
1645  {
1646  continue;
1647  }
1648 
1649  // Create a list of interior face nodes for this face only.
1650  vector<int> faceNodes(nFacePts);
1651  vector<int> toAlign(tri ? 3 : 4);
1652  for (int j = 0; j < nFacePts; ++j)
1653  {
1654  faceNodes[j] = offset2 + j;
1655  }
1656 
1657  if (tri)
1658  {
1659  // Now get the reordering of this face, which puts Gmsh
1660  // recursive ordering into Nektar++ row-by-row order.
1661  vector<int> tmp = triTensorNodeOrdering(faceNodes, n - 1);
1662  HOTriangle<int> hoTri(triVertId, tmp);
1663 
1664  // Apply reorientation
1665  if (i == 0)
1666  {
1667  // Triangle verts {0,2,1} --> {0,1,2}
1668  toAlign[0] = 0;
1669  toAlign[1] = 2;
1670  toAlign[2] = 1;
1671  hoTri.Align(toAlign);
1672  }
1673 
1674  // Fill in mapping.
1675  for (int j = 0; j < nTriInt; ++j)
1676  {
1677  mapping[offset + j] = hoTri.surfVerts[j];
1678  }
1679  }
1680  else
1681  {
1682  vector<int> tmp = quadTensorNodeOrdering(faceNodes, n);
1683  HOQuadrilateral<int> hoQuad(quadVertId, tmp);
1684 
1685  // Apply reorientation
1686  if (i == 2)
1687  {
1688  toAlign[0] = 3;
1689  toAlign[1] = 2;
1690  toAlign[2] = 1;
1691  toAlign[3] = 0;
1692  }
1693  else if (i == 3)
1694  {
1695  toAlign[0] = 1;
1696  toAlign[1] = 0;
1697  toAlign[2] = 3;
1698  toAlign[3] = 2;
1699  }
1700  else if (i == 4)
1701  {
1702  toAlign[0] = 3;
1703  toAlign[1] = 0;
1704  toAlign[2] = 1;
1705  toAlign[3] = 2;
1706  }
1707 
1708  hoQuad.Align(toAlign);
1709 
1710  // Fill in mapping.
1711  for (int j = 0; j < nQuadInt; ++j)
1712  {
1713  mapping[offset + j] = hoQuad.surfVerts[j];
1714  }
1715  }
1716  }
1717 
1718  if (conf.m_volumeNodes == false)
1719  {
1720  return mapping;
1721  }
1722 
1723  // Interior points
1724  offset = offsets[4] + nQuadInt;
1725  vector<int> intPoints, tmp;
1726 
1727  for (int i = offset; i < (order+1) * (order+1) * (order+2) / 2; ++i)
1728  {
1729  intPoints.push_back(i);
1730  }
1731 
1732  // Reorder interior points
1733  tmp = prismTensorNodeOrdering(intPoints, order - 1);
1734  mapping.insert(mapping.end(), tmp.begin(), tmp.end());
1735 
1736  return mapping;
1737 }
1738 
1739 /**
1740  * @brief Create a reordering for hexahedra.
1741  */
1743 {
1744  const int order = conf.m_order;
1745  const int n = order - 1;
1746  const int n2 = n * n;
1747  int i, j, k;
1748 
1749  vector<int> mapping;
1750 
1751  // Map taking Gmsh edges to Nektar++ edges.
1752  static int gmshToNekEdge[12] = {0, -3, 4, 1, 5, 2, 6, 7, 8, -11, 9, 10};
1753 
1754  // Push back vertices.
1755  mapping.resize(8);
1756  for (i = 0; i < 8; ++i)
1757  {
1758  mapping[i] = i;
1759  }
1760 
1761  if (order == 1)
1762  {
1763  return mapping;
1764  }
1765 
1766  // Curvilinear edges
1767  mapping.resize(8 + 12 * n);
1768 
1769  // Reorder edges.
1770  int cnt = 8, offset;
1771  for (i = 0; i < 12; ++i)
1772  {
1773  int edge = gmshToNekEdge[i];
1774  offset = 8 + n * abs(edge);
1775 
1776  if (edge < 0)
1777  {
1778  for (int j = 0; j < n; ++j)
1779  {
1780  mapping[offset + n - j - 1] = cnt++;
1781  }
1782  }
1783  else
1784  {
1785  for (int j = 0; j < n; ++j)
1786  {
1787  mapping[offset + j] = cnt++;
1788  }
1789  }
1790  }
1791 
1792  if (conf.m_faceNodes == false || n2 == 0)
1793  {
1794  return mapping;
1795  }
1796 
1797  // Curvilinear face nodes.
1798  mapping.resize(8 + 12 * n + 6 * n2);
1799 
1800  // Map which takes Gmsh -> Nektar++ faces in the local element.
1801  static int gmsh2NekFace[6] = {0, 1, 4, 2, 3, 5};
1802 
1803  // Map which defines orientation between Gmsh and Nektar++ faces.
1804  StdRegions::Orientation faceOrient[6] = {
1810  StdRegions::eDir1FwdDir1_Dir2FwdDir2};
1811 
1812  for (i = 0; i < 6; ++i)
1813  {
1814  int face = gmsh2NekFace[i];
1815  int offset2 = 8 + 12 * n + i * n2;
1816  offset = 8 + 12 * n + face * n2;
1817 
1818  // Create a list of interior face nodes for this face only.
1819  vector<int> faceNodes(n2);
1820  for (j = 0; j < n2; ++j)
1821  {
1822  faceNodes[j] = offset2 + j;
1823  }
1824 
1825  // Now get the reordering of this face, which puts Gmsh
1826  // recursive ordering into Nektar++ row-by-row order.
1827  vector<int> tmp = quadTensorNodeOrdering(faceNodes, n);
1828 
1829  // Finally reorient the face according to the geometry
1830  // differences.
1831  if (faceOrient[i] == StdRegions::eDir1FwdDir1_Dir2FwdDir2)
1832  {
1833  // Orientation is the same, just copy.
1834  for (j = 0; j < n2; ++j)
1835  {
1836  mapping[offset + j] = tmp[j];
1837  }
1838  }
1839  else if (faceOrient[i] == StdRegions::eDir1FwdDir2_Dir2FwdDir1)
1840  {
1841  // Tranposed faces
1842  for (j = 0; j < n; ++j)
1843  {
1844  for (k = 0; k < n; ++k)
1845  {
1846  mapping[offset + j * n + k] = tmp[k * n + j];
1847  }
1848  }
1849  }
1850  else if (faceOrient[i] == StdRegions::eDir1BwdDir1_Dir2FwdDir2)
1851  {
1852  for (j = 0; j < n; ++j)
1853  {
1854  for (k = 0; k < n; ++k)
1855  {
1856  mapping[offset + j * n + k] = tmp[j * n + (n - k - 1)];
1857  }
1858  }
1859  }
1860  }
1861 
1862  if (conf.m_volumeNodes == false)
1863  {
1864  return mapping;
1865  }
1866 
1867  const int totPoints = (order + 1) * (order + 1) * (order + 1);
1868  vector<int> interior;
1869  for (i = 8 + 12 * n + 6 * n2; i < totPoints; ++i)
1870  {
1871  interior.push_back(i);
1872  }
1873 
1874  interior = hexTensorNodeOrdering(interior, order - 1);
1875  mapping.insert(mapping.end(), interior.begin(), interior.end());
1876 
1877  return mapping;
1878 }
1879 
1880 /*
1881  * @brief Populate the element map #elmMap.
1882  *
1883  * This function primarily populates the element mapping #elmMap,
1884  * which takes a msh ID used by Gmsh and translates to element type,
1885  * element order and whether the element is incomplete (i.e. whether
1886  * it contains solely boundary nodes, or just face nodes). Note that
1887  * some of these elements, such as prisms of order >= 3, cannot yet be
1888  * generated by Gmsh.
1889  */
1890 std::map<unsigned int, ElmtConfig> InputGmsh::GenElmMap()
1891 {
1892  using namespace LibUtilities;
1893  std::map<unsigned int, ElmtConfig> tmp;
1894 
1895  // Elmt type, order, face, volume
1896  tmp[ 1] = ElmtConfig(eSegment, 1, false, false);
1897  tmp[ 2] = ElmtConfig(eTriangle, 1, false, false);
1898  tmp[ 3] = ElmtConfig(eQuadrilateral, 1, false, false);
1899  tmp[ 4] = ElmtConfig(eTetrahedron, 1, false, false);
1900  tmp[ 5] = ElmtConfig(eHexahedron, 1, false, false);
1901  tmp[ 6] = ElmtConfig(ePrism, 1, false, false);
1902  tmp[ 7] = ElmtConfig(ePyramid, 1, false, false);
1903  tmp[ 8] = ElmtConfig(eSegment, 2, true, false);
1904  tmp[ 9] = ElmtConfig(eTriangle, 2, false, false);
1905  tmp[ 10] = ElmtConfig(eQuadrilateral, 2, true, false);
1906  tmp[ 11] = ElmtConfig(eTetrahedron, 2, false, false);
1907  tmp[ 12] = ElmtConfig(eHexahedron, 2, true, true);
1908  tmp[ 13] = ElmtConfig(ePrism, 2, true, false);
1909  tmp[ 14] = ElmtConfig(ePyramid, 2, true, false);
1910  tmp[ 15] = ElmtConfig(ePoint, 1, false, false);
1911  tmp[ 16] = ElmtConfig(eQuadrilateral, 2, false, false);
1912  tmp[ 17] = ElmtConfig(eHexahedron, 2, false, false);
1913  tmp[ 18] = ElmtConfig(ePrism, 2, false, false);
1914  tmp[ 19] = ElmtConfig(ePyramid, 2, false, false);
1915  tmp[ 20] = ElmtConfig(eTriangle, 3, false, false);
1916  tmp[ 21] = ElmtConfig(eTriangle, 3, true, false);
1917  tmp[ 22] = ElmtConfig(eTriangle, 4, false, false);
1918  tmp[ 23] = ElmtConfig(eTriangle, 4, true, false);
1919  tmp[ 24] = ElmtConfig(eTriangle, 5, false, false);
1920  tmp[ 25] = ElmtConfig(eTriangle, 5, true, false);
1921  tmp[ 26] = ElmtConfig(eSegment, 3, true, false);
1922  tmp[ 27] = ElmtConfig(eSegment, 4, true, false);
1923  tmp[ 28] = ElmtConfig(eSegment, 5, true, false);
1924  tmp[ 29] = ElmtConfig(eTetrahedron, 3, true, false);
1925  tmp[ 30] = ElmtConfig(eTetrahedron, 4, true, true);
1926  tmp[ 31] = ElmtConfig(eTetrahedron, 5, true, true);
1927  tmp[ 32] = ElmtConfig(eTetrahedron, 4, true, false);
1928  tmp[ 33] = ElmtConfig(eTetrahedron, 5, true, false);
1929  tmp[ 36] = ElmtConfig(eQuadrilateral, 3, true, false);
1930  tmp[ 37] = ElmtConfig(eQuadrilateral, 4, true, false);
1931  tmp[ 38] = ElmtConfig(eQuadrilateral, 5, true, false);
1932  tmp[ 39] = ElmtConfig(eQuadrilateral, 3, false, false);
1933  tmp[ 40] = ElmtConfig(eQuadrilateral, 4, false, false);
1934  tmp[ 41] = ElmtConfig(eQuadrilateral, 5, false, false);
1935  tmp[ 42] = ElmtConfig(eTriangle, 6, true, false);
1936  tmp[ 43] = ElmtConfig(eTriangle, 7, true, false);
1937  tmp[ 44] = ElmtConfig(eTriangle, 8, true, false);
1938  tmp[ 45] = ElmtConfig(eTriangle, 9, true, false);
1939  tmp[ 46] = ElmtConfig(eTriangle, 10, true, false);
1940  tmp[ 47] = ElmtConfig(eQuadrilateral, 6, true, false);
1941  tmp[ 48] = ElmtConfig(eQuadrilateral, 7, true, false);
1942  tmp[ 49] = ElmtConfig(eQuadrilateral, 8, true, false);
1943  tmp[ 50] = ElmtConfig(eQuadrilateral, 9, true, false);
1944  tmp[ 51] = ElmtConfig(eQuadrilateral, 10, true, false);
1945  tmp[ 52] = ElmtConfig(eTriangle, 6, false, false);
1946  tmp[ 53] = ElmtConfig(eTriangle, 7, false, false);
1947  tmp[ 54] = ElmtConfig(eTriangle, 8, false, false);
1948  tmp[ 55] = ElmtConfig(eTriangle, 9, false, false);
1949  tmp[ 56] = ElmtConfig(eTriangle, 10, false, false);
1950  tmp[ 57] = ElmtConfig(eQuadrilateral, 6, false, false);
1951  tmp[ 58] = ElmtConfig(eQuadrilateral, 7, false, false);
1952  tmp[ 59] = ElmtConfig(eQuadrilateral, 8, false, false);
1953  tmp[ 60] = ElmtConfig(eQuadrilateral, 9, false, false);
1954  tmp[ 61] = ElmtConfig(eQuadrilateral, 10, false, false);
1955  tmp[ 62] = ElmtConfig(eSegment, 6, true, false);
1956  tmp[ 63] = ElmtConfig(eSegment, 7, true, false);
1957  tmp[ 64] = ElmtConfig(eSegment, 8, true, false);
1958  tmp[ 65] = ElmtConfig(eSegment, 9, true, false);
1959  tmp[ 66] = ElmtConfig(eSegment, 10, true, false);
1960  tmp[ 71] = ElmtConfig(eTetrahedron, 6, true, true);
1961  tmp[ 72] = ElmtConfig(eTetrahedron, 7, true, true);
1962  tmp[ 73] = ElmtConfig(eTetrahedron, 8, true, true);
1963  tmp[ 74] = ElmtConfig(eTetrahedron, 9, true, true);
1964  tmp[ 75] = ElmtConfig(eTetrahedron, 10, true, true);
1965  tmp[ 79] = ElmtConfig(eTetrahedron, 6, true, false);
1966  tmp[ 80] = ElmtConfig(eTetrahedron, 7, true, false);
1967  tmp[ 81] = ElmtConfig(eTetrahedron, 8, true, false);
1968  tmp[ 82] = ElmtConfig(eTetrahedron, 9, true, false);
1969  tmp[ 83] = ElmtConfig(eTetrahedron, 10, true, false);
1970  tmp[ 90] = ElmtConfig(ePrism, 3, true, true);
1971  tmp[ 91] = ElmtConfig(ePrism, 4, true, true);
1972  tmp[ 92] = ElmtConfig(eHexahedron, 3, true, true);
1973  tmp[ 93] = ElmtConfig(eHexahedron, 4, true, true);
1974  tmp[ 94] = ElmtConfig(eHexahedron, 5, true, true);
1975  tmp[ 95] = ElmtConfig(eHexahedron, 6, true, true);
1976  tmp[ 96] = ElmtConfig(eHexahedron, 7, true, true);
1977  tmp[ 97] = ElmtConfig(eHexahedron, 8, true, true);
1978  tmp[ 98] = ElmtConfig(eHexahedron, 9, true, true);
1979  tmp[ 99] = ElmtConfig(eHexahedron, 3, true, false);
1980  tmp[100] = ElmtConfig(eHexahedron, 4, true, false);
1981  tmp[101] = ElmtConfig(eHexahedron, 5, true, false);
1982  tmp[102] = ElmtConfig(eHexahedron, 6, true, false);
1983  tmp[103] = ElmtConfig(eHexahedron, 7, true, false);
1984  tmp[104] = ElmtConfig(eHexahedron, 8, true, false);
1985  tmp[105] = ElmtConfig(eHexahedron, 9, true, false);
1986  tmp[106] = ElmtConfig(ePrism, 5, true, true);
1987  tmp[107] = ElmtConfig(ePrism, 6, true, true);
1988  tmp[108] = ElmtConfig(ePrism, 7, true, true);
1989  tmp[109] = ElmtConfig(ePrism, 8, true, true);
1990  tmp[110] = ElmtConfig(ePrism, 9, true, true);
1991  tmp[111] = ElmtConfig(ePrism, 3, true, false);
1992  tmp[112] = ElmtConfig(ePrism, 4, true, false);
1993  tmp[113] = ElmtConfig(ePrism, 5, true, false);
1994  tmp[114] = ElmtConfig(ePrism, 6, true, false);
1995  tmp[115] = ElmtConfig(ePrism, 7, true, false);
1996  tmp[116] = ElmtConfig(ePrism, 8, true, false);
1997  tmp[117] = ElmtConfig(ePrism, 9, true, false);
1998  tmp[118] = ElmtConfig(ePyramid, 3, true, true);
1999  tmp[119] = ElmtConfig(ePyramid, 4, true, true);
2000  tmp[120] = ElmtConfig(ePyramid, 5, true, true);
2001  tmp[121] = ElmtConfig(ePyramid, 6, true, true);
2002  tmp[122] = ElmtConfig(ePyramid, 7, true, true);
2003  tmp[123] = ElmtConfig(ePyramid, 8, true, true);
2004  tmp[124] = ElmtConfig(ePyramid, 9, true, true);
2005  tmp[125] = ElmtConfig(ePyramid, 3, true, false);
2006  tmp[126] = ElmtConfig(ePyramid, 4, true, false);
2007  tmp[127] = ElmtConfig(ePyramid, 5, true, false);
2008  tmp[128] = ElmtConfig(ePyramid, 6, true, false);
2009  tmp[129] = ElmtConfig(ePyramid, 7, true, false);
2010  tmp[130] = ElmtConfig(ePyramid, 7, true, false);
2011  tmp[131] = ElmtConfig(ePyramid, 8, true, false);
2012 
2013  return tmp;
2014 }
2015 
2016 }
2017 }
bool m_faceNodes
Denotes whether the element contains face nodes. For 2D elements, if this is true then the element co...
Definition: ElementConfig.h:81
bool operator()(Mode const &a, Mode const &b) const
Definition: InputGmsh.cpp:223
std::vector< int > prismTensorNodeOrdering(const std::vector< int > &nodes, int n)
Reorder Gmsh nodes so that they appear in a "tensor product" format suitable for the interior of a Ne...
Definition: InputGmsh.cpp:463
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
std::vector< int > triTensorNodeOrdering(const std::vector< int > &nodes, int n)
Reorder Gmsh nodes so that they appear in a "tensor product" format suitable for the interior of a Ne...
Definition: InputGmsh.cpp:169
Basic information about an element.
Definition: ElementConfig.h:49
io::filtering_istream m_mshFile
Input stream.
void ReadNextNodeBlock(int nVertices=0)
Definition: InputGmsh.cpp:983
void Align(std::vector< int > vertId)
Align this surface to a given vertex ID.
Definition: HOAlignment.h:134
std::tuple< int, int, int > Mode
Definition: InputGmsh.cpp:220
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a quadrilateral.
std::vector< int > quadTensorNodeOrdering(const std::vector< int > &nodes, int n)
Reorder Gmsh nodes so that they appear in a tensor product format suitable for the interior of a Nekt...
Definition: InputGmsh.cpp:92
std::vector< T > surfVerts
The quadrilateral surface vertices – templated so that this can either be nodes or IDs...
Definition: HOAlignment.h:224
STL namespace.
std::vector< T > surfVerts
The triangle surface vertices – templated so that this can either be nodes or IDs.
Definition: HOAlignment.h:64
static std::vector< int > PrismReordering(NekMeshUtils::ElmtConfig conf)
Create a reordering for prisms.
Definition: InputGmsh.cpp:1523
std::vector< int > physicalTags
Definition: InputGmsh.cpp:716
std::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Definition: Mesh.h:156
def copy(self)
Definition: pycml.py:2663
ElementFactory & GetElementFactory()
Definition: Element.cpp:44
static std::vector< int > TetReordering(NekMeshUtils::ElmtConfig conf)
Create a reordering for tetrahedra.
Definition: InputGmsh.cpp:1371
std::vector< int > hexTensorNodeOrdering(const std::vector< int > &nodes, int n)
Reorder Gmsh nodes so that they appear in a "tensor product" format suitable for the interior of a Ne...
Definition: InputGmsh.cpp:539
const char *const ShapeTypeMap[]
Definition: ShapeType.hpp:67
std::unordered_map< int, std::vector< int > > m_orderingMap
Definition: InputGmsh.h:90
unsigned int m_order
Order of the element.
Definition: ElementConfig.h:88
static std::vector< int > HexReordering(NekMeshUtils::ElmtConfig conf)
Create a reordering for hexahedra.
Definition: InputGmsh.cpp:1742
NEKMESHUTILS_EXPORT void OpenStream()
Open a file for input.
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a line.
Definition: Line.cpp:187
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
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a prism.
Definition: Prism.cpp:233
static std::vector< int > CreateReordering(unsigned int InputGmshEntity)
Create a reordering map for a given element.
Definition: InputGmsh.cpp:1218
bool m_volumeNodes
Denotes whether the element contains volume (i.e. interior) nodes. These are not supported by either ...
Definition: ElementConfig.h:86
virtual NEKMESHUTILS_EXPORT void ProcessFaces(bool ReprocessFaces=true)
Extract element faces.
void SaveNode(int id, NekDouble x=0, NekDouble y=0, NekDouble z=0)
Definition: InputGmsh.cpp:1024
std::shared_ptr< Element > ElementSharedPtr
Definition: Edge.h:49
virtual NEKMESHUTILS_EXPORT void ProcessElements()
Generate element IDs.
static std::vector< int > LineReordering(NekMeshUtils::ElmtConfig conf)
Definition: InputGmsh.cpp:1258
void ReadNextElement(int tag=0, int elm_type=0)
Definition: InputGmsh.cpp:1069
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a triangle.
Definition: Triangle.cpp:159
Representation of Gmsh entity so that we can extract physical tag IDs.
Definition: InputGmsh.cpp:712
double NekDouble
static std::vector< int > QuadReordering(NekMeshUtils::ElmtConfig conf)
Create a reordering for quadrilaterals.
Definition: InputGmsh.cpp:1323
void Align(std::vector< int > vertId)
Align this surface to a given vertex ID.
Definition: HOAlignment.h:270
A lightweight struct for dealing with high-order triangle alignment.
Definition: HOAlignment.h:49
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a hexahedron.
Definition: Hexahedron.cpp:272
Abstract base class for input modules.
A lightweight struct for dealing with high-order quadrilateral alignment.
Definition: HOAlignment.h:208
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a tetrahedron.
virtual NEKMESHUTILS_EXPORT void ProcessVertices()
Extract element vertices.
std::vector< int > tetTensorNodeOrdering(const std::vector< int > &nodes, int n)
Reorder Gmsh nodes so that they appear in a "tensor product" format suitable for the interior of a Ne...
Definition: InputGmsh.cpp:284
int GetNnodes(unsigned int InputGmshEntity)
Definition: InputGmsh.cpp:1162
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a pyramid.
Definition: Pyramid.cpp:176
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
static NEKMESHUTILS_EXPORT unsigned int GetNumNodes(ElmtConfig pConf)
Return the number of nodes defining a point (i.e. return 1).
Definition: Point.cpp:67
2D Evenly-spaced points on a Triangle
Definition: PointsType.h:71
static std::vector< int > TriReordering(NekMeshUtils::ElmtConfig conf)
Create a reordering for triangles.
Definition: InputGmsh.cpp:1274
std::pair< ModuleType, std::string > ModuleKey
static std::map< unsigned int, NekMeshUtils::ElmtConfig > GenElmMap()
Definition: InputGmsh.cpp:1890
virtual NEKMESHUTILS_EXPORT void ProcessComposites()
Generate composites.
std::map< int, int > m_idMap
Definition: InputGmsh.h:84
static std::map< unsigned int, NekMeshUtils::ElmtConfig > elmMap
Definition: InputGmsh.h:67
ModuleFactory & GetModuleFactory()