MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
gmsh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "mesh_headers.hpp"
13#include "vtk.hpp"
15
16using namespace std;
17using namespace mfem::bin_io;
18
19namespace mfem
20{
21
22namespace gmsh
23{
24
25/// Given barycentric indices @a b of a node in a tetrahedral element of degree
26/// @a ref, return its Gmsh index.
27static int BarycentricToGmshTet(int *b, int ref)
28{
29 int i = b[0];
30 int j = b[1];
31 int k = b[2];
32 int l = b[3];
33 bool ibdr = (i == 0);
34 bool jbdr = (j == 0);
35 bool kbdr = (k == 0);
36 bool lbdr = (l == 0);
37 if (ibdr && jbdr && kbdr)
38 {
39 return 0;
40 }
41 else if (jbdr && kbdr && lbdr)
42 {
43 return 1;
44 }
45 else if (ibdr && kbdr && lbdr)
46 {
47 return 2;
48 }
49 else if (ibdr && jbdr && lbdr)
50 {
51 return 3;
52 }
53 int offset = 4;
54 if (jbdr && kbdr) // Edge DOF on j == 0 and k == 0
55 {
56 return offset + i - 1;
57 }
58 else if (kbdr && lbdr) // Edge DOF on k == 0 and l == 0
59 {
60 return offset + ref - 1 + j - 1;
61 }
62 else if (ibdr && kbdr) // Edge DOF on i == 0 and k == 0
63 {
64 return offset + 2 * (ref - 1) + ref - j - 1;
65 }
66 else if (ibdr && jbdr) // Edge DOF on i == 0 and j == 0
67 {
68 return offset + 3 * (ref - 1) + ref - k - 1;
69 }
70 else if (ibdr && lbdr) // Edge DOF on i == 0 and l == 0
71 {
72 return offset + 4 * (ref - 1) + ref - k - 1;
73 }
74 else if (jbdr && lbdr) // Edge DOF on j == 0 and l == 0
75 {
76 return offset + 5 * (ref - 1) + ref - k - 1;
77 }
78
79 // Recursive numbering for the faces
80 offset += 6 * (ref - 1);
81 if (kbdr)
82 {
83 int b_out[3];
84 b_out[0] = j-1;
85 b_out[1] = i-1;
86 b_out[2] = ref - i - j - 1;
87 return offset + BarycentricToVTKTriangle(b_out, ref-3);
88 }
89 else if (jbdr)
90 {
91 int b_out[3];
92 b_out[0] = i-1;
93 b_out[1] = k-1;
94 b_out[2] = ref - i - k - 1;
95 offset += (ref - 1) * (ref - 2) / 2;
96 return offset + BarycentricToVTKTriangle(b_out, ref-3);
97 }
98 else if (ibdr)
99 {
100 int b_out[3];
101 b_out[0] = k-1;
102 b_out[1] = j-1;
103 b_out[2] = ref - j - k - 1;
104 offset += (ref - 1) * (ref - 2);
105 return offset + BarycentricToVTKTriangle(b_out, ref-3);
106 }
107 else if (lbdr)
108 {
109 int b_out[3];
110 b_out[0] = ref-j-k-1;
111 b_out[1] = j-1;
112 b_out[2] = k-1;
113 offset += 3 * (ref - 1) * (ref - 2) / 2;
114 return offset + BarycentricToVTKTriangle(b_out, ref-3);
115 }
116
117 // Recursive numbering for interior
118 {
119 int b_out[4];
120 b_out[0] = i-1;
121 b_out[1] = j-1;
122 b_out[2] = k-1;
123 b_out[3] = ref - i - j - k - 1;
124 offset += 2 * (ref - 1) * (ref - 2);
125 return offset + BarycentricToGmshTet(b_out, ref-4);
126 }
127}
128
129/// Given the Cartesian indices @a idx_in of a node in a quadrilateral of order
130/// @a ref, return its Gmsh index.
131static int CartesianToGmshQuad(int idx_in[], int ref)
132{
133 int i = idx_in[0];
134 int j = idx_in[1];
135 // Do we lie on any of the edges
136 bool ibdr = (i == 0 || i == ref);
137 bool jbdr = (j == 0 || j == ref);
138 if (ibdr && jbdr) // Vertex DOF
139 {
140 return (i ? (j ? 2 : 1) : (j ? 3 : 0));
141 }
142 int offset = 4;
143 if (jbdr) // Edge DOF on j==0 or j==ref
144 {
145 return offset + (j ? 3*ref - 3 - i : i - 1);
146 }
147 else if (ibdr) // Edge DOF on i==0 or i==ref
148 {
149 return offset + (i ? ref - 1 + j - 1 : 4*ref - 4 - j);
150 }
151 else // Recursive numbering for interior
152 {
153 int idx_out[2];
154 idx_out[0] = i-1;
155 idx_out[1] = j-1;
156 offset += 4 * (ref - 1);
157 return offset + CartesianToGmshQuad(idx_out, ref-2);
158 }
159}
160
161/// Given the Cartesian indices @a idx_in of a node in a hexahedron of order
162/// @a ref, return its Gmsh index.
163static int CartesianToGmshHex(int idx_in[], int ref)
164{
165 int i = idx_in[0];
166 int j = idx_in[1];
167 int k = idx_in[2];
168 // Do we lie on any of the edges
169 bool ibdr = (i == 0 || i == ref);
170 bool jbdr = (j == 0 || j == ref);
171 bool kbdr = (k == 0 || k == ref);
172 if (ibdr && jbdr && kbdr) // Vertex DOF
173 {
174 return (i ? (j ? (k ? 6 : 2) : (k ? 5 : 1)) :
175 (j ? (k ? 7 : 3) : (k ? 4 : 0)));
176 }
177 int offset = 8;
178 if (jbdr && kbdr) // Edge DOF on x-directed edge
179 {
180 return offset + (j ? (k ? 12*ref-12-i: 6*ref-6-i) :
181 (k ? 8*ref-9+i: i-1));
182 }
183 else if (ibdr && kbdr) // Edge DOF on y-directed edge
184 {
185 return offset + (k ? (i ? 10*ref-11+j: 9*ref-10+j) :
186 (i ? 3*ref-4+j: ref-2+j));
187 }
188 else if (ibdr && jbdr) // Edge DOF on z-directed edge
189 {
190 return offset + (i ? (j ? 6*ref-7+k: 4*ref-5+k) :
191 (j ? 7*ref-8+k: 2*ref-3+k));
192 }
193 else if (ibdr) // Face DOF on x-directed face
194 {
195 int idx_out[2];
196 idx_out[0] = i ? j-1 : k-1;
197 idx_out[1] = i ? k-1 : j-1;
198 offset += (12 + (i ? 3 : 2) * (ref - 1)) * (ref - 1);
199 return offset + CartesianToGmshQuad(idx_out, ref-2);
200 }
201 else if (jbdr) // Face DOF on y-directed face
202 {
203 int idx_out[2];
204 idx_out[0] = j ? ref-i-1 : i-1;
205 idx_out[1] = j ? k-1 : k-1;
206 offset += (12 + (j ? 4 : 1) * (ref - 1)) * (ref - 1);
207 return offset + CartesianToGmshQuad(idx_out, ref-2);
208 }
209 else if (kbdr) // Face DOF on z-directed face
210 {
211 int idx_out[2];
212 idx_out[0] = k ? i-1 : j-1;
213 idx_out[1] = k ? j-1 : i-1;
214 offset += (12 + (k ? 5 : 0) * (ref - 1)) * (ref - 1);
215 return offset + CartesianToGmshQuad(idx_out, ref-2);
216 }
217 else // Recursive numbering for interior
218 {
219 int idx_out[3];
220 idx_out[0] = i-1;
221 idx_out[1] = j-1;
222 idx_out[2] = k-1;
223
224 offset += (12 + 6 * (ref - 1)) * (ref - 1);
225 return offset + CartesianToGmshHex(idx_out, ref-2);
226 }
227}
228
229/// Given the indices @a idx_in of a node in a prism of order @a ref, return its
230/// Gmsh index.
231static int WedgeToGmshPrism(int idx_in[], int ref)
232{
233 int i = idx_in[0];
234 int j = idx_in[1];
235 int k = idx_in[2];
236 int l = ref - i -j;
237 bool ibdr = (i == 0);
238 bool jbdr = (j == 0);
239 bool kbdr = (k == 0 || k == ref);
240 bool lbdr = (l == 0);
241 if (ibdr && jbdr && kbdr)
242 {
243 return k ? 3 : 0;
244 }
245 else if (jbdr && lbdr && kbdr)
246 {
247 return k ? 4 : 1;
248 }
249 else if (ibdr && lbdr && kbdr)
250 {
251 return k ? 5 : 2;
252 }
253 int offset = 6;
254 if (jbdr && kbdr)
255 {
256 return offset + (k ? 6 * (ref - 1) + i - 1: i - 1);
257 }
258 else if (ibdr && kbdr)
259 {
260 return offset + (k ? 7 * (ref -1) + j-1 : ref - 1 + j - 1);
261 }
262 else if (ibdr && jbdr)
263 {
264 return offset + 2 * (ref - 1) + k - 1;
265 }
266 else if (lbdr && kbdr)
267 {
268 return offset + (k ? 8 * (ref -1) + j - 1 : 3 * (ref - 1) + j - 1);
269 }
270 else if (jbdr && lbdr)
271 {
272 return offset + 4 * (ref - 1) + k - 1;
273 }
274 else if (ibdr && lbdr)
275 {
276 return offset + 5 * (ref - 1) + k - 1;
277 }
278 offset += 9 * (ref-1);
279 if (kbdr) // Triangular faces at k=0 and k=ref
280 {
281 int b_out[3];
282 b_out[0] = k ? i-1 : j-1;
283 b_out[1] = k ? j-1 : i-1;
284 b_out[2] = ref - i - j - 1;
285 offset += k ? (ref-1)*(ref-2) / 2: 0;
286 return offset + BarycentricToVTKTriangle(b_out, ref-3);
287 }
288 offset += (ref-1)*(ref-2);
289 if (jbdr) // Quadrilateral face at j=0
290 {
291 int idx_out[2];
292 idx_out[0] = i-1;
293 idx_out[1] = k-1;
294 return offset + CartesianToGmshQuad(idx_out, ref-2);
295 }
296 else if (ibdr) // Quadrilateral face at i=0
297 {
298 int idx_out[2];
299 idx_out[0] = k-1;
300 idx_out[1] = j-1;
301 offset += (ref-1)*(ref-1);
302 return offset + CartesianToGmshQuad(idx_out, ref-2);
303 }
304 else if (lbdr) // Quadrilateral face at l=ref-i-j=0
305 {
306 int idx_out[2];
307 idx_out[0] = j-1;
308 idx_out[1] = k-1;
309 offset += 2*(ref-1)*(ref-1);
310 return offset + CartesianToGmshQuad(idx_out, ref-2);
311 }
312 offset += 3*(ref-1)*(ref-1);
313 // The Gmsh Prism interiors are a tensor product of segments of order ref-2
314 // and triangles of order ref-3
315 {
316 int b_out[3];
317 b_out[0] = i-1;
318 b_out[1] = j-1;
319 b_out[2] = ref - i - j - 1;
320 int ot = BarycentricToVTKTriangle(b_out, ref-3);
321 int os = (k==1) ? 0 : (k == ref-1 ? 1 : k);
322 return offset + (ref-1) * ot + os;
323 }
324}
325
326/// Given the Cartesian indices @a idx_in of a node in a pyramid of order @a ref
327/// return its Gmsh index.
328static int CartesianToGmshPyramid(int idx_in[], int ref)
329{
330 int i = idx_in[0];
331 int j = idx_in[1];
332 int k = idx_in[2];
333 // Do we lie on any of the edges
334 bool ibdr = (i == 0 || i == ref-k);
335 bool jbdr = (j == 0 || j == ref-k);
336 bool kbdr = (k == 0);
337 if (ibdr && jbdr && kbdr)
338 {
339 return i ? (j ? 2 : 1): (j ? 3 : 0);
340 }
341 else if (k == ref)
342 {
343 return 4;
344 }
345 int offset = 5;
346 if (jbdr && kbdr)
347 {
348 return offset + (j ? (6 * ref - 6 - i) : (i - 1));
349 }
350 else if (ibdr && kbdr)
351 {
352 return offset + (i ? (3 * ref - 4 + j) : (ref - 2 + j));
353 }
354 else if (ibdr && jbdr)
355 {
356 return offset + (i ? (j ? 6 : 4) : (j ? 7 : 2 )) * (ref-1) + k - 1;
357 }
358 offset += 8*(ref-1);
359 if (jbdr)
360 {
361 int b_out[3];
362 b_out[0] = j ? ref - i - k - 1 : i - 1;
363 b_out[1] = k - 1;
364 b_out[2] = (j ? i - 1 : ref - i - k - 1);
365 offset += (j ? 3 : 0) * (ref - 1) * (ref - 2) / 2;
366 return offset + BarycentricToVTKTriangle(b_out, ref-3);
367 }
368 else if (ibdr)
369 {
370 int b_out[3];
371 b_out[0] = i ? j - 1: ref - j - k - 1;
372 b_out[1] = k - 1;
373 b_out[2] = (i ? ref - j - k - 1: j - 1);
374 offset += (i ? 2 : 1) * (ref - 1) * (ref - 2) / 2;
375 return offset + BarycentricToVTKTriangle(b_out, ref-3);
376 }
377 else if (kbdr)
378 {
379 int idx_out[2];
380 idx_out[0] = k ? i-1 : j-1;
381 idx_out[1] = k ? j-1 : i-1;
382 offset += 2 * (ref - 1) * (ref - 2);
383 return offset + CartesianToGmshQuad(idx_out, ref-2);
384 }
385 offset += (2 * (ref - 2) + (ref - 1)) * (ref - 1) ;
386 {
387 int idx_out[3];
388 idx_out[0] = i-1;
389 idx_out[1] = j-1;
390 idx_out[2] = k-1;
391 return offset + CartesianToGmshPyramid(idx_out, ref-3);
392 }
393}
394
395/// Form the mapping from MFEM to Gmsh segment indices.
396static void HOSegmentMapping(int order, int *map)
397{
398 map[0] = 0;
399 map[order] = 1;
400 for (int i=1; i<order; i++)
401 {
402 map[i] = i + 1;
403 }
404}
405
406/// Form the mapping from MFEM to Gmsh triangle indices.
407static void HOTriangleMapping(int order, int *map)
408{
409 int b[3];
410 int o = 0;
411 for (b[1]=0; b[1]<=order; ++b[1])
412 {
413 for (b[0]=0; b[0]<=order-b[1]; ++b[0])
414 {
415 b[2] = order - b[0] - b[1];
416 map[o] = BarycentricToVTKTriangle(b, order);
417 o++;
418 }
419 }
420}
421
422/// Form the mapping from MFEM to Gmsh quadrilateral indices.
423static void HOQuadrilateralMapping(int order, int *map)
424{
425 int b[2];
426 int o = 0;
427 for (b[1]=0; b[1]<=order; b[1]++)
428 {
429 for (b[0]=0; b[0]<=order; b[0]++)
430 {
431 map[o] = CartesianToGmshQuad(b, order);
432 o++;
433 }
434 }
435}
436
437/// Form the mapping from MFEM to Gmsh tetrahedron indices.
438static void HOTetrahedronMapping(int order, int *map)
439{
440 int b[4];
441 int o = 0;
442 for (b[2]=0; b[2]<=order; ++b[2])
443 {
444
445 for (b[1]=0; b[1]<=order-b[2]; ++b[1])
446 {
447 for (b[0]=0; b[0]<=order-b[1]-b[2]; ++b[0])
448 {
449 b[3] = order - b[0] - b[1] - b[2];
450 map[o] = BarycentricToGmshTet(b, order);
451 o++;
452 }
453 }
454 }
455}
456
457/// Form the mapping from MFEM to Gmsh hexahedron indices.
458static void HOHexahedronMapping(int order, int *map)
459{
460 int b[3];
461 int o = 0;
462 for (b[2]=0; b[2]<=order; b[2]++)
463 {
464 for (b[1]=0; b[1]<=order; b[1]++)
465 {
466 for (b[0]=0; b[0]<=order; b[0]++)
467 {
468 map[o] = CartesianToGmshHex(b, order);
469 o++;
470 }
471 }
472 }
473}
474
475/// Form the mapping from MFEM to Gmsh prism indices.
476static void HOPrismMapping(int order, int *map)
477{
478 int b[3];
479 int o = 0;
480 for (b[2]=0; b[2]<=order; b[2]++)
481 {
482 for (b[1]=0; b[1]<=order; b[1]++)
483 {
484 for (b[0]=0; b[0]<=order - b[1]; b[0]++)
485 {
486 map[o] = WedgeToGmshPrism(b, order);
487 o++;
488 }
489 }
490 }
491}
492
493/// Form the mapping from MFEM to Gmsh pyramid indices.
494static void HOPyramidMapping(int order, int *map)
495{
496 int b[3];
497 int o = 0;
498 for (b[2]=0; b[2]<=order; b[2]++)
499 {
500 for (b[1]=0; b[1]<=order - b[2]; b[1]++)
501 {
502 for (b[0]=0; b[0]<=order - b[2]; b[0]++)
503 {
504 map[o] = CartesianToGmshPyramid(b, order);
505 o++;
506 }
507 }
508 }
509}
510
511/// Number of nodes in an element of type @a geom with order @a order.
512static int NumNodesInElement(Geometry::Type geom, int order)
513{
514 return GlobGeometryRefiner.Refine(geom, order, 1)->RefPts.GetNPoints();
515}
516
517/// @brief Return the space dimension (at least 1) given a 3D bounding box.
518///
519/// If some of the sides of the box have zero (or very small) sides, then that
520/// dimension is not counted.
521static int GetSpaceDimension(double bb_min[3], double bb_max[3])
522{
523 static constexpr double bb_tol = 1e-14;
524 const double bb_size = max(bb_max[0] - bb_min[0],
525 max(bb_max[1] - bb_min[1],
526 bb_max[2] - bb_min[2]));
527 int sd = 1;
528 if (bb_max[1] - bb_min[1] > bb_size * bb_tol)
529 {
530 sd += 1;
531 }
532 if (bb_max[2] - bb_min[2] > bb_size * bb_tol)
533 {
534 sd += 1;
535 }
536 return sd;
537}
538
539/// Skip ahead in the input stream until the next section, which opens on a new
540/// line beginning with $ (but not beginning with $End, which ends the previous
541/// section).
542static string GoToNextSection(istream &input)
543{
544 string line;
545 while (getline(input, line))
546 {
547 filter_dos(line);
548 // Find the next line that starts with '$', but does not start with "$End"
549 if (line.size() >= 1 &&
550 line[0] == '$' &&
551 (line.size() < 4 || line.compare(1, 3, "End") != 0))
552 {
553 return line.substr(1, string::npos);
554 }
555 }
556 return "";
557}
558
559/// Read a double-quoted string from the input stream, and return the result
560/// (without the enclosing quotes).
561static string ReadQuotedString(istream &input)
562{
563 char c;
564 // Find opening quote
565 while (input.get(c))
566 {
567 if (c == '"') { break; }
568 }
569 MFEM_VERIFY(input, "Error reading string.");
570
571 string result;
572 while (input.get(c))
573 {
574 // Find closing quote
575 if (c == '"')
576 {
577 return result;
578 }
579 result.push_back(c);
580 }
581 MFEM_ABORT("Failed to read string.");
582}
583
584void ChompNewline(istream &input)
585{
586 if (input.peek() == '\r') { input.get(); }
587 MFEM_VERIFY(input.get() == '\n', "Inconsistent newlines.");
588}
589
590/// Enum for supported Gmsh mesh file versions.
591enum class GmshVersion { V2_2, V4_1 };
592
593/// @brief Helper class for reading Gmsh meshes.
594///
595/// This is an internal helper class that is not intended for use by the
596/// end-user; see Mesh::ReadGmshMesh for its usage.
597///
598/// This class implements common functionality and state needed to read Gmsh
599/// meshes in version 2.2 and 4.1 format.
600class GmshReader
601{
602 /// List of supported Gmsh element types. types[geom][order-1] contains the
603 /// Gmsh element type number for the element of the given geometry and order.
604 vector<vector<int>> types =
605 {
606 {15}, // point
607 {1, 8, 26, 27, 28, 62, 63, 64, 65, 66}, // segment
608 {2, 9, 21, 23, 25, 42, 43, 44, 45, 46}, // triangle
609 {3, 10, 36, 37, 38, 47, 48, 49, 50, 51}, // quadrilateral
610 {4, 11, 29, 30, 31, 71, 72, 73, 74, 75}, // tetrahedron
611 {5, 12, 92, 93, 94, 95, 96, 97, 98}, // hexahedron
612 {6, 13, 90, 91, 106, 107, 108, 109, 110}, // prism
613 {7, 14, 118, 119, 120, 121, 122, 123, 124} // pyramid
614 };
615 /// Permutations mapping from MFEM lexicographic ordering to Gmsh ordering,
616 /// for a given element type and order. Constructed lazily.
617 unordered_map<pair<Geometry::Type, int>, vector<int>, PairHasher> node_maps;
618
619 bool has_positive_attrs = false;
620 bool has_non_positive_attrs = false;
621
622 istream &input; ///< The input stream to read from.
623
624 BinaryOrASCII is_binary; ///< Is the file in binary or ASCII format?
625 int data_size; ///< Data size in bytes (meaning depends on file format).
626 GmshVersion version; ///< The version of Gmsh format.
627
628 /// A map between a serial number of the vertex and its number in the file
629 /// (there may be gaps in the numbering, and also Gmsh enumerates vertices
630 /// starting from 1, not 0)
631 unordered_map<int, int> vertex_map;
632
633 /// A map containing names of physical curves, surfaces, and volumes. The
634 /// first index is the dimension of the physical manifold, the second index is
635 /// the element attribute number of the set, and the string is the assigned
636 /// name.
637 unordered_map<int,unordered_map<int,string> > phys_names_by_dim;
638
639 /// Gmsh always outputs coordinates in 3D, but MFEM distinguishes between the
640 /// mesh element dimension (Dim) and the dimension of the space in which the
641 /// mesh is embedded (spaceDim). For example, a 2D MFEM mesh has Dim = 2 and
642 /// spaceDim = 2, while a 2D surface mesh in 3D has Dim = 2 but spaceDim = 3.
643 /// We set spaceDim by measuring the mesh bounding box and checking for a
644 /// lower dimensional subspace. The assumption is that the mesh is at least
645 /// 2D if the y-dimension of the box is non-trivial and 3D if the z-dimension
646 /// is non-trivial. Note that with these assumptions a 2D mesh parallel to
647 /// the yz plane will be considered a surface mesh embedded in 3D whereas the
648 /// same 2D mesh parallel to the xy plane will be considered a 2D mesh.
649 ///@{
650 const double inf = numeric_limits<double>::infinity();
651 double bb_min[3] = {inf, inf, inf};
652 double bb_max[3] = {-inf, -inf, -inf};
653 ///@}
654
655 int mesh_order = -1; ///< Mesh order. Variable order meshes are not supported.
656 bool periodic = false; ///< Is the mesh periodic?
657
658 /// Node indices of high-order elements, such that ho_el_nodes[dim][e][i] is
659 /// the i-th node index of the e-th element of dimension dim.
660 vector<vector<vector<int>>> ho_el_nodes{4};
661
662 vector<int> v2v; ///< Periodic vertex mapping (for periodic meshes only).
663
664 /// Get the geometry type and polynomial degree for a given Gmsh element
665 /// type.
666 pair<Geometry::Type, int> GetGeometryAndOrder(int element_type) const
667 {
668 for (int g = Geometry::POINT; g < Geometry::NUM_GEOMETRIES; ++g)
669 {
670 const vector<int> &types_g = types[g];
671 const auto it = lower_bound(types_g.begin(), types_g.end(), element_type);
672 if (it != types_g.end() && *it == element_type)
673 {
674 return {Geometry::Type(g), int(distance(types_g.begin(), it) + 1)};
675 }
676 }
677 MFEM_ABORT("Unknown Gmsh element type.");
678 }
679
680 /// Return node map if it exists, otherwise lazily construct it.
681 const vector<int> &GetNodeMap(Geometry::Type geom, int order)
682 {
683 auto it = node_maps.find(make_pair(geom, order));
684 if (it == node_maps.end())
685 {
686 const int n_nodes = NumNodesInElement(geom, order);
687 auto ret = node_maps.emplace(piecewise_construct,
688 forward_as_tuple(geom, order),
689 forward_as_tuple(n_nodes));
690 auto &map = ret.first->second;
691 auto data = map.data();
692 switch (geom)
693 {
694 case Geometry::SEGMENT: HOSegmentMapping(order, data); break;
695 case Geometry::TRIANGLE: HOTriangleMapping(order, data); break;
696 case Geometry::SQUARE: HOQuadrilateralMapping(order, data); break;
697 case Geometry::TETRAHEDRON: HOTetrahedronMapping(order, data); break;
698 case Geometry::CUBE: HOHexahedronMapping(order, data); break;
699 case Geometry::PRISM: HOPrismMapping(order, data); break;
700 case Geometry::PYRAMID: HOPyramidMapping(order, data); break;
701 default: MFEM_ABORT("Unsupported element type.");
702 }
703 return map;
704 }
705 else
706 {
707 return it->second;
708 }
709 }
710
711 /// Add the physical names (in @a phys_names_by_dim) to the mesh's attribute
712 /// sets and boundary attribute sets.
713 void AddPhysicalNames(Mesh &mesh)
714 {
715 // Process boundary attribute set names
716 for (auto const &bdr_attr : phys_names_by_dim[mesh.Dimension() - 1])
717 {
718 if (!mesh.bdr_attribute_sets.AttributeSetExists(bdr_attr.second))
719 {
720 mesh.bdr_attribute_sets.CreateAttributeSet(bdr_attr.second);
721 }
722 mesh.bdr_attribute_sets.AddToAttributeSet(bdr_attr.second, bdr_attr.first);
723 }
724
725 // Process element attribute set names
726 for (auto const &attr : phys_names_by_dim[mesh.Dimension()])
727 {
728 if (!mesh.attribute_sets.AttributeSetExists(attr.second))
729 {
730 mesh.attribute_sets.CreateAttributeSet(attr.second);
731 }
732 mesh.attribute_sets.AddToAttributeSet(attr.second, attr.first);
733 }
734 }
735
736 void AddElements(Mesh &mesh, vector<vector<unique_ptr<Element>>> &elems_by_dim)
737 {
738 if (elems_by_dim[3].size() > 0) { mesh.Dim = 3; }
739 else if (elems_by_dim[2].size() > 0) { mesh.Dim = 2; }
740 else { mesh.Dim = 1; }
741
742 mesh.NumOfElements = elems_by_dim[mesh.Dim].size();
743 mesh.elements.SetSize(mesh.NumOfElements);
744 for (int i = 0; i < mesh.NumOfElements; ++i)
745 {
746 mesh.elements[i] = elems_by_dim[mesh.Dim][i].release();
747 }
748 mesh.NumOfBdrElements = elems_by_dim[mesh.Dim - 1].size();
749 mesh.boundary.SetSize(mesh.NumOfBdrElements);
750 for (int i = 0; i < mesh.NumOfBdrElements; ++i)
751 {
752 mesh.boundary[i] = elems_by_dim[mesh.Dim - 1][i].release();
753 }
754 }
755
756 /// In the periodic vertex mapping @a v2v, there may be chains or cycles.
757 /// This will simplify all chains so that they are one link only, and break
758 /// any cycles.
759 void SimplifyPeriodicLinks()
760 {
761 // Follow existing long chains of duplicate->primary in v2v array. Upon
762 // completion of this loop, each v2v[duplicate] will point to a true
763 // primary vertex. This algorithm is useful for periodicity defined in
764 // multiple directions.
765 for (int duplicate = 0; duplicate < int(v2v.size()); duplicate++)
766 {
767 int primary = v2v[duplicate];
768 if (primary != duplicate)
769 {
770 // This loop will end if it finds a circular dependency.
771 while (v2v[primary] != primary && primary != duplicate)
772 {
773 primary = v2v[primary];
774 }
775 if (primary == duplicate)
776 {
777 // If primary and duplicate are the same vertex, circular
778 // dependency exists. We need to fix the problem, we choose
779 // duplicate.
780 v2v[duplicate] = duplicate;
781 }
782 else
783 {
784 // The long chain has ended on the true primary vertex.
785 v2v[duplicate] = primary;
786 }
787 }
788 }
789 }
790
791 /// In the list of Elements @a els, replace periodic vertices using the
792 /// periodic identification map @a v2v.
793 void ReplacePeriodicVertices(Array<Element*> &els) const
794 {
795 for (int i = 0; i < els.Size(); i++)
796 {
797 Element *e = els[i];
798 int *v = e->GetVertices();
799 for (int j = 0; j < e->GetNVertices(); j++)
800 {
801 v[j] = v2v[v[j]];
802 }
803 }
804 }
805
806 /// Set the attribute of element @a e to @a attribute. If the attribute is
807 /// non-positive, set it to 1. Keep track if non-positive or positive
808 /// attributes are encountered to potentially report errors to the user.
809 void SetAttribute(Element *e, int attribute)
810 {
811 if (attribute < 1)
812 {
813 has_non_positive_attrs = true;
814 attribute = 1; // Resetting non-positive attributes to be 1.
815 }
816 else
817 {
818 has_positive_attrs = true;
819 }
820 e->SetAttribute(attribute);
821 }
822
823 /// Create and return a new Element of the given geometry, with specified
824 /// attribute. If the element is higher-order, store the high-order node
825 /// indices.
826 template <typename I>
827 Element *NewElement(Mesh &mesh, Geometry::Type geom, int el_order,
828 const vector<I> &el_nodes, int attribute)
829 {
830 auto e = mesh.NewElement(geom);
831 int *v = e->GetVertices();
832 for (int i = 0; i < e->GetNVertices(); ++i)
833 {
834 v[i] = vertex_map[el_nodes[i]];
835 }
836 SetAttribute(e, attribute);
837
838 // Store high-order node locations
839 const int dim = Geometry::Dimension[geom];
840 if (el_order > 1)
841 {
842 const int n_elem_nodes = NumNodesInElement(geom, el_order);
843 const vector<int> &map = GetNodeMap(geom, el_order);
844 auto &nodes = ho_el_nodes[dim].emplace_back(n_elem_nodes);
845 for (int i = 0; i < n_elem_nodes; ++i)
846 {
847 nodes[i] = vertex_map[el_nodes[map[i]]];
848 }
849 }
850
851 return e;
852 }
853
854 /// Check that all attributes are positive (or, if none are positive, give a
855 /// warning that they have been replaced by 1).
856 void CheckAttributes() const
857 {
858 if (has_non_positive_attrs)
859 {
860 // If mesh has a mix of positive and non-positive attributes, this is
861 // a user error. All attributes should be positive.
862 MFEM_VERIFY(!has_positive_attrs,
863 "Non-positive element attribute in Gmsh mesh!\n"
864 "By default Gmsh sets element tags (attributes)"
865 " to '0' but MFEM requires that they be"
866 " positive integers.\n"
867 "Use \"Physical Curve\", \"Physical Surface\","
868 " or \"Physical Volume\" to set tags/attributes"
869 " for all curves, surfaces, or volumes in your"
870 " Gmsh geometry to values which are >= 1.");
871 // If the mesh has only non-positive attributes, this could be because
872 // Gmsh by default will set zero attributes if no physical entities are
873 // defined. In this case, we warn the user, and set attributes to 1.
874 MFEM_WARNING("Gmsh reader: all element attributes were zero.\n"
875 "MFEM only supports positive element attributes.\n"
876 "Setting all element attributes to 1.\n");
877 }
878 }
879
880 /// @brief Read the mesh in Gmsh 4.1 format from the input stream into the
881 /// Mesh @a mesh.
882 void ReadGmsh4Mesh(Mesh &mesh)
883 {
884 MFEM_VERIFY(data_size == sizeof(size_t), "Incompatible Gmsh mesh.");
885
886 const auto b = is_binary;
887 unordered_map<pair<int,int>, int, PairHasher> entity_physical_tag;
888
889 string section;
890 do
891 {
892 section = GoToNextSection(input);
893 if (section == "PhysicalNames")
894 {
895 // $PhysicalNames is always encoded in ASCII
896 const int n_phys_names = ReadBinaryOrASCII<int>(input, ASCII);
897 for (int i = 0; i < n_phys_names; ++i)
898 {
899 const int phys_name_dim = ReadBinaryOrASCII<int>(input, ASCII);
900 const int phys_name_tag = ReadBinaryOrASCII<int>(input, ASCII);
901 const string phys_name = ReadQuotedString(input);
902
903 phys_names_by_dim[phys_name_dim][phys_name_tag] = phys_name;
904 }
905 }
906 else if (section == "Entities")
907 {
908 const size_t n_points = ReadBinaryOrASCII<size_t>(input, b);
909 const size_t n_curves = ReadBinaryOrASCII<size_t>(input, b);
910 const size_t n_surfaces = ReadBinaryOrASCII<size_t>(input, b);
911 const size_t n_volumes = ReadBinaryOrASCII<size_t>(input, b);
912
913 const size_t n_entities[4] = {n_points, n_curves, n_surfaces, n_volumes};
914
915 for (int d = 0; d <= 3; ++d)
916 {
917 for (size_t i = 0; i < n_entities[d]; ++i)
918 {
919 const int tag = ReadBinaryOrASCII<int>(input, b);
920 Skip<double>(input, d == 0 ? 3 : 6, b); // Skip X, Y, Z
921 const size_t n_phys_tags = ReadBinaryOrASCII<size_t>(input, b);
922 for (size_t iphys = 0; iphys < n_phys_tags; ++iphys)
923 {
924 const int phys_tag = ReadBinaryOrASCII<int>(input, b);
925 entity_physical_tag[ {d, tag}] = phys_tag;
926 }
927 if (d > 0)
928 {
929 const size_t n_bounding = ReadBinaryOrASCII<size_t>(input, b);
930 Skip<int>(input, n_bounding, b);
931 }
932 }
933 }
934 }
935 else if (section == "Nodes")
936 {
937 const size_t n_blocks = ReadBinaryOrASCII<size_t>(input, b);
938 const size_t n_nodes = ReadBinaryOrASCII<size_t>(input, b);
939 Skip<size_t>(input, 2, b); // Skip min and max tags
940
941 mesh.NumOfVertices = n_nodes;
942 mesh.vertices.SetSize(n_nodes);
943 size_t vertex_counter = 0;
944
945 double c[3];
946
947 for (size_t iblock = 0; iblock < n_blocks; ++iblock)
948 {
949 Skip<int>(input, 2, b); // Skip entity dim and ta
950 const int is_parametric = ReadBinaryOrASCII<int>(input, b);
951 const size_t n_nodes_in_block = ReadBinaryOrASCII<size_t>(input, b);
952
953 MFEM_VERIFY(!is_parametric, "Parametric nodes not supported.");
954
955 vector<size_t> node_tags(n_nodes_in_block);
956 for (size_t i = 0; i < n_nodes_in_block; ++i)
957 {
958 const size_t node_tag = ReadBinaryOrASCII<size_t>(input, b);
959 node_tags[i] = node_tag;
960 }
961 for (size_t i = 0; i < n_nodes_in_block; ++i)
962 {
963 for (int d = 0; d < 3; ++d)
964 {
965 c[d] = ReadBinaryOrASCII<double>(input, b);
966 bb_min[d] = min(bb_min[d], c[d]);
967 bb_max[d] = max(bb_max[d], c[d]);
968 }
969 vertex_map[node_tags[i]] = vertex_counter;
970 mesh.vertices[vertex_counter] = Vertex(c[0], c[1], c[2]);
971 vertex_counter += 1;
972 }
973 }
974 mesh.spaceDim = GetSpaceDimension(bb_min, bb_max);
975 }
976 else if (section == "Elements")
977 {
978 const size_t n_blocks = ReadBinaryOrASCII<size_t>(input, b);
979 Skip<size_t>(input, 3, b); // Skip n_elements and min/max tags.
980
981 vector<vector<unique_ptr<Element>>> elems_by_dim(4);
982
983 for (size_t iblock = 0; iblock < n_blocks; ++iblock)
984 {
985 const int entity_dim = ReadBinaryOrASCII<int>(input, b);
986 const int entity_tag = ReadBinaryOrASCII<int>(input, b);
987 const int element_type = ReadBinaryOrASCII<int>(input, b);
988 const size_t n_elements = ReadBinaryOrASCII<size_t>(input, b);
989
990 for (size_t ie = 0; ie < n_elements; ++ie)
991 {
992 Skip<size_t>(input, 1, b); // Skip element tag
993 const auto [geom, el_order] = GetGeometryAndOrder(element_type);
994
995 // We can encounter point elements if they are present in the
996 // mesh as "physical points". These always have order 1, so we
997 // don't use them to infer the order of the mesh.
998 if (geom != Geometry::POINT)
999 {
1000 if (mesh_order < 0) { mesh_order = el_order; }
1001 MFEM_VERIFY(mesh_order == el_order,
1002 "Variable order Gmsh meshes are not supported");
1003 }
1004
1005 const int n_elem_nodes = NumNodesInElement(geom, el_order);
1006 vector<size_t> node_tags(n_elem_nodes);
1007 for (int inode = 0; inode < n_elem_nodes; ++inode)
1008 {
1009 node_tags[inode] = ReadBinaryOrASCII<size_t>(input, b);
1010 }
1011
1012 const int attribute = entity_physical_tag[ {entity_dim, entity_tag}];
1013 auto e = NewElement(mesh, geom, el_order, node_tags, attribute);
1014 elems_by_dim[Geometry::Dimension[geom]].emplace_back(e);
1015 }
1016 }
1017
1018 AddElements(mesh, elems_by_dim);
1019 }
1020 else if (section == "Periodic")
1021 {
1022 const size_t n_periodic = ReadBinaryOrASCII<size_t>(input, b);
1023 if (n_periodic == 0) { continue; }
1024
1025 periodic = true;
1026 v2v.resize(mesh.NumOfVertices);
1027 for (int i = 0; i < mesh.NumOfVertices; i++) { v2v[i] = i; }
1028
1029 for (size_t i = 0; i < n_periodic; ++i)
1030 {
1031 Skip<int>(input, 3, b); // Skip entity information
1032 const size_t n_affine = ReadBinaryOrASCII<size_t>(input, b);
1033 Skip<double>(input, n_affine, b); // Skip affine information
1034 const size_t n_nodes = ReadBinaryOrASCII<size_t>(input, b);
1035 for (size_t j = 0; j < n_nodes; ++j)
1036 {
1037 const size_t node_num = ReadBinaryOrASCII<size_t>(input, b);
1038 const size_t primary_node_num = ReadBinaryOrASCII<size_t>(input, b);
1039 v2v[vertex_map.at(node_num)] = vertex_map.at(primary_node_num);
1040 }
1041 }
1042 }
1043 }
1044 while (!section.empty());
1045 }
1046
1047 /// @brief Read the mesh in Gmsh 2.2 format from the input stream into the
1048 /// Mesh @a mesh.
1049 void ReadGmsh2Mesh(Mesh &mesh)
1050 {
1051 const auto b = is_binary;
1052 MFEM_VERIFY(data_size == sizeof(double), "Incompatible data size.");
1053
1054 string section;
1055 do
1056 {
1057 section = GoToNextSection(input);
1058 if (section == "Nodes")
1059 {
1061 ChompNewline(input);
1062 mesh.vertices.SetSize(mesh.NumOfVertices);
1063 double c[3];
1064 for (int v = 0; v < mesh.NumOfVertices; ++v)
1065 {
1066 const int node_num = ReadBinaryOrASCII<int>(input, b);
1067 for (int d = 0; d < 3; ++d)
1068 {
1069 c[d] = ReadBinaryOrASCII<double>(input, b);
1070 bb_min[d] = min(bb_min[d], c[d]);
1071 bb_max[d] = max(bb_max[d], c[d]);
1072 }
1073 mesh.vertices[v] = Vertex(c[0], c[1], c[2]);
1074 vertex_map[node_num] = v;
1075 }
1076 mesh.spaceDim = GetSpaceDimension(bb_min, bb_max);
1077 MFEM_VERIFY(vertex_map.size() == size_t(mesh.NumOfVertices),
1078 "Gmsh node indices are not unique.");
1079 }
1080 else if (section == "Elements")
1081 {
1082 const int num_elements = ReadBinaryOrASCII<int>(input, ASCII);
1083 ChompNewline(input);
1084 int num_el_read = 0;
1085
1086 vector<vector<unique_ptr<Element>>> elems_by_dim(4);
1087
1088 while (num_el_read < num_elements)
1089 {
1090 auto add_element = [&](int el_type, int el_phys_tag, Geometry::Type geom,
1091 int el_order, const vector<int> &el_nodes)
1092 {
1093 if (geom != Geometry::POINT)
1094 {
1095 if (mesh_order < 0) { mesh_order = el_order; }
1096 MFEM_VERIFY(mesh_order == el_order,
1097 "Variable order Gmsh meshes are not supported");
1098 }
1099 Element *e = NewElement(mesh, geom, el_order, el_nodes, el_phys_tag);
1100 elems_by_dim[Geometry::Dimension[geom]].emplace_back(e);
1101 };
1102
1103 if (b)
1104 {
1105 // Header
1106 const int el_type = ReadBinaryOrASCII<int>(input, BINARY);
1107 const int n_els = ReadBinaryOrASCII<int>(input, BINARY);
1108 const int n_tags = ReadBinaryOrASCII<int>(input, BINARY);
1109 const auto [geom, el_order] = GetGeometryAndOrder(el_type);
1110 const int n_el_nodes = NumNodesInElement(geom, el_order);
1111 vector<int> el_nodes(n_el_nodes);
1112 // Element blocks
1113 for (int e = 0; e < n_els; ++e)
1114 {
1115 Skip<int>(input, 1, BINARY); // Skip element number
1116 int el_phys_tag = 0;
1117 if (n_tags > 0)
1118 {
1119 el_phys_tag = ReadBinaryOrASCII<int>(input, BINARY);
1120 Skip<int>(input, n_tags - 1, BINARY);
1121 }
1122 for (int i = 0; i < n_el_nodes; ++i)
1123 {
1124 el_nodes[i] = ReadBinaryOrASCII<int>(input, BINARY);
1125 }
1126 add_element(el_type, el_phys_tag, geom, el_order, el_nodes);
1127 num_el_read += 1;
1128 }
1129 }
1130 else
1131 {
1132 Skip<int>(input, 1, ASCII); // Skip element number
1133 const int el_type = ReadBinaryOrASCII<int>(input, ASCII);
1134 const int n_tags = ReadBinaryOrASCII<int>(input, ASCII);
1135 int el_phys_tag = 0;
1136 if (n_tags > 0)
1137 {
1138 el_phys_tag = ReadBinaryOrASCII<int>(input, ASCII);
1139 Skip<int>(input, n_tags - 1, ASCII);
1140 }
1141 const auto [geom, el_order] = GetGeometryAndOrder(el_type);
1142 const int n_el_nodes = NumNodesInElement(geom, el_order);
1143 vector<int> el_nodes(n_el_nodes);
1144 for (int i = 0; i < n_el_nodes; ++i)
1145 {
1146 el_nodes[i] = ReadBinaryOrASCII<int>(input, ASCII);
1147 }
1148 add_element(el_type, el_phys_tag, geom, el_order, el_nodes);
1149 num_el_read += 1;
1150 }
1151 }
1152
1153 AddElements(mesh, elems_by_dim);
1154 }
1155 else if (section == "PhysicalNames")
1156 {
1157 const int num_names = ReadBinaryOrASCII<int>(input, ASCII);
1158 for (int i = 0; i < num_names; ++i)
1159 {
1160 const int phys_dim = ReadBinaryOrASCII<int>(input, ASCII);
1161 const int phys_tag = ReadBinaryOrASCII<int>(input, ASCII);
1162 phys_names_by_dim[phys_dim][phys_tag] = ReadQuotedString(input);
1163 }
1164 }
1165 else if (section == "Periodic")
1166 {
1167 const int n_periodic_entities = ReadBinaryOrASCII<int>(input, ASCII);
1168 if (n_periodic_entities == 0) { continue; }
1169
1170 periodic = true;
1171 v2v.resize(mesh.NumOfVertices);
1172 for (int i = 0; i < mesh.NumOfVertices; i++) { v2v[i] = i; }
1173
1174 for (int i = 0; i < n_periodic_entities; i++)
1175 {
1176 Skip<int>(input, 3, ASCII); // Skip dimension, tag, and master tag
1177 ChompNewline(input);
1178 // Next section might be "Affine"; if so, skip.
1179 if (input.peek() == 'A')
1180 {
1181 MFEM_VERIFY(ReadBinaryOrASCII<string>(input, ASCII) == "Affine",
1182 "Cannot find Affine transformation");
1183 string line;
1184 getline(input, line);
1185 }
1186 const int n_nodes = ReadBinaryOrASCII<int>(input, ASCII);
1187 for (int j = 0; j < n_nodes; ++j)
1188 {
1189 const int node_num = ReadBinaryOrASCII<int>(input, ASCII);
1190 const int primary_node_num = ReadBinaryOrASCII<int>(input, ASCII);
1191 v2v[vertex_map.at(node_num)] = vertex_map.at(primary_node_num);
1192 }
1193 }
1194 }
1195 }
1196 while (section != "");
1197 }
1198
1199public:
1200
1201 /// @brief Read the mesh from the input stream @a input_ into mesh @a mesh.
1202 ///
1203 /// Meshes in Gmsh format 2.2 or 4.1 and in either binary or ASCII can be
1204 /// read; the format is determined automatically.
1205 GmshReader(istream &input_, Mesh &mesh) : input(input_)
1206 {
1207 const string version_str = ReadBinaryOrASCII<string>(input, ASCII);
1208 MFEM_VERIFY(version_str == "2.2" || version_str == "4.1",
1209 "Unsupported Gmsh file version. Supported versions: 2.2 and 4.1");
1210 version = version_str == "2.2" ? GmshVersion::V2_2 : GmshVersion::V4_1;
1211 is_binary = BinaryOrASCII(ReadBinaryOrASCII<bool>(input, ASCII));
1212 data_size = ReadBinaryOrASCII<int>(input, ASCII);
1213 ChompNewline(input);
1214 if (is_binary)
1215 {
1216 const int one = ReadBinaryOrASCII<int>(input, BINARY);
1217 MFEM_VERIFY(one == 1, "Incompatible endianness.");
1218 }
1219
1220 if (version == GmshVersion::V4_1)
1221 {
1222 ReadGmsh4Mesh(mesh);
1223 }
1224 else if (version == GmshVersion::V2_2)
1225 {
1226 ReadGmsh2Mesh(mesh);
1227 }
1228
1229 // Make sure all element and boundary attributes are positive.
1230 CheckAttributes();
1231
1232 // Merge periodic vertices
1233 if (periodic)
1234 {
1235 // If the mesh is low-order, we need to populate ho_el_nodes before
1236 // periodic vertices are identified in order to set the L2 nodes grid
1237 // function.
1238 if (mesh_order == 1)
1239 {
1240 ho_el_nodes[mesh.Dim].resize(mesh.NumOfElements);
1241 for (int ie = 0; ie < mesh.NumOfElements; ++ie)
1242 {
1243 const Element *e = mesh.elements[ie];
1244 const int nv = e->GetNVertices();
1245 const int *v = e->GetVertices();
1246 ho_el_nodes[mesh.Dim][ie].resize(nv);
1247 const vector<int> &map = GetNodeMap(e->GetGeometryType(), 1);
1248 for (int i = 0; i < nv; ++i)
1249 {
1250 ho_el_nodes[mesh.Dim][ie][i] = v[map[i]];
1251 }
1252 }
1253 }
1254 SimplifyPeriodicLinks();
1255 ReplacePeriodicVertices(mesh.elements);
1256 ReplacePeriodicVertices(mesh.boundary);
1257 }
1258
1259 // If the elements are high-order, keep a copy of the nodes before removing
1260 // unused vertices.
1261 Array<Vertex> ho_vertices;
1262 if (mesh_order > 1 || periodic) { ho_vertices = mesh.vertices; }
1263
1264 AddPhysicalNames(mesh);
1265 mesh.RemoveUnusedVertices();
1266 mesh.FinalizeTopology();
1267
1268 // Now that the mesh topology has been fully created, set the high-order
1269 // nodal information (if needed). For periodic meshes, we need to set the
1270 // L2 grid function.
1271 if (mesh_order > 1 || periodic)
1272 {
1273 // Gmsh uses uniform nodal points
1274 const int bt = BasisType::ClosedUniform;
1276 if (periodic) { fec = new L2_FECollection(mesh_order, mesh.Dim, bt); }
1277 else { fec = new H1_FECollection(mesh_order, mesh.Dim, bt); }
1279 &mesh, fec, mesh.spaceDim, Ordering::byVDIM);
1280 GridFunction *nodes_gf = new GridFunction(fes);
1281 // The nodal grid function, owned by mesh, will own fec and fec
1282 nodes_gf->MakeOwner(fec);
1283 mesh.SetNodalGridFunction(nodes_gf, true);
1284 Array<int> vdofs;
1285 for (int e = 0; e < mesh.NumOfElements; ++e)
1286 {
1287 const FiniteElement *fe = fes->GetFE(e);
1288 auto *nfe = dynamic_cast<const NodalFiniteElement*>(fe);
1289 MFEM_ASSERT(nfe, "Invalid FE");
1290 const Array<int> &lex = nfe->GetLexicographicOrdering();
1291 fes->GetElementVDofs(e, vdofs);
1292 const int n = vdofs.Size() / mesh.spaceDim;
1293 for (int i = 0; i < n; ++i)
1294 {
1295 const int ii = lex.IsEmpty() ? i : lex[i];
1296 Vertex v = ho_vertices[ho_el_nodes[mesh.Dim][e][i]];
1297 for (int d = 0; d < mesh.spaceDim; ++d)
1298 {
1299 (*nodes_gf)[vdofs[ii + d*n]] = v(d);
1300 }
1301 }
1302 }
1303 // Set curvature to use Gauss-Lobatto instead of uniform basis
1304 mesh.SetCurvature(mesh_order, periodic, mesh.spaceDim, Ordering::byVDIM);
1305 }
1306 }
1307};
1308
1309} // namespace gmsh
1310
1311void Mesh::ReadGmshMesh(istream &input)
1312{
1313 gmsh::GmshReader(input, *this);
1314}
1315
1316} // namespace mfem
bool IsEmpty() const
Does the Array have Size zero.
Definition array.hpp:359
int Size() const
Return the logical size of the array.
Definition array.hpp:192
bool AttributeSetExists(const std::string &name) const
Return true if the named attribute set is present.
void AddToAttributeSet(const std::string &set_name, int attr)
Add a single entry to an existing attribute set.
Array< int > & CreateAttributeSet(const std::string &set_name)
Create an empty named attribute set.
@ ClosedUniform
Nodes: x_i = i/(n-1), i=0,...,n-1.
Definition fe_base.hpp:39
Abstract data type element.
Definition element.hpp:29
Geometry::Type GetGeometryType() const
Definition element.hpp:55
virtual void GetVertices(Array< int > &v) const =0
Get the indices defining the vertices.
void SetAttribute(const int attr)
Set element's attribute.
Definition element.hpp:61
virtual int GetNVertices() const =0
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
Definition fespace.cpp:299
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3860
Abstract class for all finite elements.
Definition fe_base.hpp:294
RefinedGeometry * Refine(Geometry::Type Geom, int Times, int ETimes=1)
Definition geom.cpp:1136
static const int Dimension[NumGeom]
Definition geom.hpp:51
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:53
void MakeOwner(FiniteElementCollection *fec_)
Make the GridFunction the owner of fec_owned and fes.
Definition gridfunc.hpp:160
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:255
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
Mesh data type.
Definition mesh.hpp:67
Array< Vertex > vertices
Definition mesh.hpp:110
Element * NewElement(int geom)
Definition mesh.cpp:4978
int NumOfBdrElements
Definition mesh.hpp:84
int Dim
Definition mesh.hpp:81
AttributeSets bdr_attribute_sets
Named sets of boundary element attributes.
Definition mesh.hpp:315
void FinalizeTopology(bool generate_bdr=true)
Finalize the construction of the secondary topology (connectivity) data of a Mesh.
Definition mesh.cpp:3660
virtual void SetCurvature(int order, bool discont=false, int space_dim=-1, int ordering=1, int pyr_type=1)
Set the curvature of the mesh nodes using the given polynomial degree.
Definition mesh.cpp:7211
void SetNodalGridFunction(GridFunction *nodes, bool make_owner=false)
Definition mesh.cpp:7200
int NumOfVertices
Definition mesh.hpp:84
AttributeSets attribute_sets
Named sets of element attributes.
Definition mesh.hpp:312
int NumOfElements
Definition mesh.hpp:84
int spaceDim
Definition mesh.hpp:82
Array< Element * > boundary
Definition mesh.hpp:111
void ReadGmshMesh(std::istream &input)
Definition gmsh.cpp:1311
Array< Element * > elements
Definition mesh.hpp:105
void RemoveUnusedVertices()
Remove unused vertices and rebuild mesh connectivity.
Definition mesh.cpp:14107
Class for standard nodal finite elements.
Definition fe_base.hpp:798
IntegrationRule RefPts
Definition geom.hpp:321
Data type for vertex.
Definition vertex.hpp:23
int dim
Definition ex24.cpp:53
Vector bb_min
Definition ex41.cpp:38
Vector bb_max
Definition ex41.cpp:38
real_t b
Definition lissajous.cpp:42
void Skip(std::istream &input, int num, BinaryOrASCII binary)
Skip num values of type T from the input stream, in either binary or ASCII format,...
Definition binaryio.hpp:103
BinaryOrASCII
Enum to specify if values should be read in binary or ASCII format.
Definition binaryio.hpp:30
T ReadBinaryOrASCII(std::istream &input, BinaryOrASCII binary)
Read and return a value of type T from the input stream, in either binary or ASCII format,...
Definition binaryio.hpp:86
GmshVersion
Enum for supported Gmsh mesh file versions.
Definition gmsh.cpp:591
void ChompNewline(istream &input)
Definition gmsh.cpp:584
GeometryRefiner GlobGeometryRefiner
Definition geom.cpp:2014
void filter_dos(std::string &line)
Check for, and remove, a trailing '\r' from and std::string.
Definition text.hpp:45
int BarycentricToVTKTriangle(int *b, int ref)
Return the VTK node index of the barycentric point b in a triangle with refinement level ref.
Definition vtk.cpp:161
STL namespace.
Helper class for hashing std::pair of hashable types.
std::array< int, NCMesh::MaxFaceNodes > nodes