27static int BarycentricToGmshTet(
int *
b,
int ref)
37 if (ibdr && jbdr && kbdr)
41 else if (jbdr && kbdr && lbdr)
45 else if (ibdr && kbdr && lbdr)
49 else if (ibdr && jbdr && lbdr)
56 return offset + i - 1;
58 else if (kbdr && lbdr)
60 return offset + ref - 1 + j - 1;
62 else if (ibdr && kbdr)
64 return offset + 2 * (ref - 1) + ref - j - 1;
66 else if (ibdr && jbdr)
68 return offset + 3 * (ref - 1) + ref - k - 1;
70 else if (ibdr && lbdr)
72 return offset + 4 * (ref - 1) + ref - k - 1;
74 else if (jbdr && lbdr)
76 return offset + 5 * (ref - 1) + ref - k - 1;
80 offset += 6 * (ref - 1);
86 b_out[2] = ref - i - j - 1;
94 b_out[2] = ref - i - k - 1;
95 offset += (ref - 1) * (ref - 2) / 2;
103 b_out[2] = ref - j - k - 1;
104 offset += (ref - 1) * (ref - 2);
110 b_out[0] = ref-j-k-1;
113 offset += 3 * (ref - 1) * (ref - 2) / 2;
123 b_out[3] = ref - i - j - k - 1;
124 offset += 2 * (ref - 1) * (ref - 2);
125 return offset + BarycentricToGmshTet(b_out, ref-4);
131static int CartesianToGmshQuad(
int idx_in[],
int ref)
136 bool ibdr = (i == 0 || i == ref);
137 bool jbdr = (j == 0 || j == ref);
140 return (i ? (j ? 2 : 1) : (j ? 3 : 0));
145 return offset + (j ? 3*ref - 3 - i : i - 1);
149 return offset + (i ? ref - 1 + j - 1 : 4*ref - 4 - j);
156 offset += 4 * (ref - 1);
157 return offset + CartesianToGmshQuad(idx_out, ref-2);
163static int CartesianToGmshHex(
int idx_in[],
int ref)
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)
174 return (i ? (j ? (k ? 6 : 2) : (k ? 5 : 1)) :
175 (j ? (k ? 7 : 3) : (k ? 4 : 0)));
180 return offset + (j ? (k ? 12*ref-12-i: 6*ref-6-i) :
181 (k ? 8*ref-9+i: i-1));
183 else if (ibdr && kbdr)
185 return offset + (k ? (i ? 10*ref-11+j: 9*ref-10+j) :
186 (i ? 3*ref-4+j: ref-2+j));
188 else if (ibdr && jbdr)
190 return offset + (i ? (j ? 6*ref-7+k: 4*ref-5+k) :
191 (j ? 7*ref-8+k: 2*ref-3+k));
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);
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);
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);
224 offset += (12 + 6 * (ref - 1)) * (ref - 1);
225 return offset + CartesianToGmshHex(idx_out, ref-2);
231static int WedgeToGmshPrism(
int idx_in[],
int ref)
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)
245 else if (jbdr && lbdr && kbdr)
249 else if (ibdr && lbdr && kbdr)
256 return offset + (k ? 6 * (ref - 1) + i - 1: i - 1);
258 else if (ibdr && kbdr)
260 return offset + (k ? 7 * (ref -1) + j-1 : ref - 1 + j - 1);
262 else if (ibdr && jbdr)
264 return offset + 2 * (ref - 1) + k - 1;
266 else if (lbdr && kbdr)
268 return offset + (k ? 8 * (ref -1) + j - 1 : 3 * (ref - 1) + j - 1);
270 else if (jbdr && lbdr)
272 return offset + 4 * (ref - 1) + k - 1;
274 else if (ibdr && lbdr)
276 return offset + 5 * (ref - 1) + k - 1;
278 offset += 9 * (ref-1);
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;
288 offset += (ref-1)*(ref-2);
294 return offset + CartesianToGmshQuad(idx_out, ref-2);
301 offset += (ref-1)*(ref-1);
302 return offset + CartesianToGmshQuad(idx_out, ref-2);
309 offset += 2*(ref-1)*(ref-1);
310 return offset + CartesianToGmshQuad(idx_out, ref-2);
312 offset += 3*(ref-1)*(ref-1);
319 b_out[2] = ref - i - j - 1;
321 int os = (k==1) ? 0 : (k == ref-1 ? 1 : k);
322 return offset + (ref-1) * ot + os;
328static int CartesianToGmshPyramid(
int idx_in[],
int ref)
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)
339 return i ? (j ? 2 : 1): (j ? 3 : 0);
348 return offset + (j ? (6 * ref - 6 - i) : (i - 1));
350 else if (ibdr && kbdr)
352 return offset + (i ? (3 * ref - 4 + j) : (ref - 2 + j));
354 else if (ibdr && jbdr)
356 return offset + (i ? (j ? 6 : 4) : (j ? 7 : 2 )) * (ref-1) + k - 1;
362 b_out[0] = j ? ref - i - k - 1 : i - 1;
364 b_out[2] = (j ? i - 1 : ref - i - k - 1);
365 offset += (j ? 3 : 0) * (ref - 1) * (ref - 2) / 2;
371 b_out[0] = i ? j - 1: ref - j - k - 1;
373 b_out[2] = (i ? ref - j - k - 1: j - 1);
374 offset += (i ? 2 : 1) * (ref - 1) * (ref - 2) / 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);
385 offset += (2 * (ref - 2) + (ref - 1)) * (ref - 1) ;
391 return offset + CartesianToGmshPyramid(idx_out, ref-3);
396static void HOSegmentMapping(
int order,
int *map)
400 for (
int i=1; i<order; i++)
407static void HOTriangleMapping(
int order,
int *map)
411 for (
b[1]=0;
b[1]<=order; ++
b[1])
413 for (
b[0]=0;
b[0]<=order-
b[1]; ++
b[0])
415 b[2] = order -
b[0] -
b[1];
423static void HOQuadrilateralMapping(
int order,
int *map)
427 for (
b[1]=0;
b[1]<=order;
b[1]++)
429 for (
b[0]=0;
b[0]<=order;
b[0]++)
431 map[o] = CartesianToGmshQuad(
b, order);
438static void HOTetrahedronMapping(
int order,
int *map)
442 for (
b[2]=0;
b[2]<=order; ++
b[2])
445 for (
b[1]=0;
b[1]<=order-
b[2]; ++
b[1])
447 for (
b[0]=0;
b[0]<=order-
b[1]-
b[2]; ++
b[0])
449 b[3] = order -
b[0] -
b[1] -
b[2];
450 map[o] = BarycentricToGmshTet(
b, order);
458static void HOHexahedronMapping(
int order,
int *map)
462 for (
b[2]=0;
b[2]<=order;
b[2]++)
464 for (
b[1]=0;
b[1]<=order;
b[1]++)
466 for (
b[0]=0;
b[0]<=order;
b[0]++)
468 map[o] = CartesianToGmshHex(
b, order);
476static void HOPrismMapping(
int order,
int *map)
480 for (
b[2]=0;
b[2]<=order;
b[2]++)
482 for (
b[1]=0;
b[1]<=order;
b[1]++)
484 for (
b[0]=0;
b[0]<=order -
b[1];
b[0]++)
486 map[o] = WedgeToGmshPrism(
b, order);
494static void HOPyramidMapping(
int order,
int *map)
498 for (
b[2]=0;
b[2]<=order;
b[2]++)
500 for (
b[1]=0;
b[1]<=order -
b[2];
b[1]++)
502 for (
b[0]=0;
b[0]<=order -
b[2];
b[0]++)
504 map[o] = CartesianToGmshPyramid(
b, order);
521static int GetSpaceDimension(
double bb_min[3],
double bb_max[3])
523 static constexpr double bb_tol = 1e-14;
542static string GoToNextSection(istream &input)
545 while (getline(input, line))
549 if (line.size() >= 1 &&
551 (line.size() < 4 || line.compare(1, 3,
"End") != 0))
553 return line.substr(1, string::npos);
561static string ReadQuotedString(istream &input)
567 if (c ==
'"') {
break; }
569 MFEM_VERIFY(input,
"Error reading string.");
581 MFEM_ABORT(
"Failed to read string.");
586 if (input.peek() ==
'\r') { input.get(); }
587 MFEM_VERIFY(input.get() ==
'\n',
"Inconsistent newlines.");
604 vector<vector<int>> types =
607 {1, 8, 26, 27, 28, 62, 63, 64, 65, 66},
608 {2, 9, 21, 23, 25, 42, 43, 44, 45, 46},
609 {3, 10, 36, 37, 38, 47, 48, 49, 50, 51},
610 {4, 11, 29, 30, 31, 71, 72, 73, 74, 75},
611 {5, 12, 92, 93, 94, 95, 96, 97, 98},
612 {6, 13, 90, 91, 106, 107, 108, 109, 110},
613 {7, 14, 118, 119, 120, 121, 122, 123, 124}
617 unordered_map<pair<Geometry::Type, int>, vector<int>,
PairHasher> node_maps;
619 bool has_positive_attrs =
false;
620 bool has_non_positive_attrs =
false;
631 unordered_map<int, int> vertex_map;
637 unordered_map<int,unordered_map<int,string> > phys_names_by_dim;
650 const double inf = numeric_limits<double>::infinity();
651 double bb_min[3] = {inf, inf, inf};
652 double bb_max[3] = {-inf, -inf, -inf};
656 bool periodic =
false;
660 vector<vector<vector<int>>> ho_el_nodes{4};
666 pair<Geometry::Type, int> GetGeometryAndOrder(
int element_type)
const
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)
674 return {
Geometry::Type(g), int(distance(types_g.begin(), it) + 1)};
677 MFEM_ABORT(
"Unknown Gmsh element type.");
683 auto it = node_maps.find(make_pair(geom, order));
684 if (it == node_maps.end())
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();
701 default: MFEM_ABORT(
"Unsupported element type.");
713 void AddPhysicalNames(
Mesh &mesh)
716 for (
auto const &bdr_attr : phys_names_by_dim[mesh.Dimension() - 1])
726 for (
auto const &attr : phys_names_by_dim[mesh.Dimension()])
736 void AddElements(
Mesh &mesh, vector<vector<unique_ptr<Element>>> &elems_by_dim)
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; }
746 mesh.
elements[i] = elems_by_dim[mesh.
Dim][i].release();
752 mesh.
boundary[i] = elems_by_dim[mesh.
Dim - 1][i].release();
759 void SimplifyPeriodicLinks()
765 for (
int duplicate = 0; duplicate < int(v2v.size()); duplicate++)
767 int primary = v2v[duplicate];
768 if (primary != duplicate)
771 while (v2v[primary] != primary && primary != duplicate)
773 primary = v2v[primary];
775 if (primary == duplicate)
780 v2v[duplicate] = duplicate;
785 v2v[duplicate] = primary;
795 for (
int i = 0; i < els.
Size(); i++)
809 void SetAttribute(
Element *e,
int attribute)
813 has_non_positive_attrs =
true;
818 has_positive_attrs =
true;
826 template <
typename I>
828 const vector<I> &el_nodes,
int attribute)
834 v[i] = vertex_map[el_nodes[i]];
836 SetAttribute(e, attribute);
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)
847 nodes[i] = vertex_map[el_nodes[map[i]]];
856 void CheckAttributes()
const
858 if (has_non_positive_attrs)
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.");
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");
882 void ReadGmsh4Mesh(
Mesh &mesh)
884 MFEM_VERIFY(data_size ==
sizeof(
size_t),
"Incompatible Gmsh mesh.");
886 const auto b = is_binary;
887 unordered_map<pair<int,int>, int,
PairHasher> entity_physical_tag;
892 section = GoToNextSection(input);
893 if (section ==
"PhysicalNames")
897 for (
int i = 0; i < n_phys_names; ++i)
901 const string phys_name = ReadQuotedString(input);
903 phys_names_by_dim[phys_name_dim][phys_name_tag] = phys_name;
906 else if (section ==
"Entities")
913 const size_t n_entities[4] = {n_points, n_curves, n_surfaces, n_volumes};
915 for (
int d = 0; d <= 3; ++d)
917 for (
size_t i = 0; i < n_entities[d]; ++i)
922 for (
size_t iphys = 0; iphys < n_phys_tags; ++iphys)
925 entity_physical_tag[ {d, tag}] = phys_tag;
935 else if (section ==
"Nodes")
943 size_t vertex_counter = 0;
947 for (
size_t iblock = 0; iblock < n_blocks; ++iblock)
953 MFEM_VERIFY(!is_parametric,
"Parametric nodes not supported.");
955 vector<size_t> node_tags(n_nodes_in_block);
956 for (
size_t i = 0; i < n_nodes_in_block; ++i)
959 node_tags[i] = node_tag;
961 for (
size_t i = 0; i < n_nodes_in_block; ++i)
963 for (
int d = 0; d < 3; ++d)
966 bb_min[d] = min(bb_min[d], c[d]);
967 bb_max[d] = max(bb_max[d], c[d]);
969 vertex_map[node_tags[i]] = vertex_counter;
974 mesh.
spaceDim = GetSpaceDimension(bb_min, bb_max);
976 else if (section ==
"Elements")
981 vector<vector<unique_ptr<Element>>> elems_by_dim(4);
983 for (
size_t iblock = 0; iblock < n_blocks; ++iblock)
990 for (
size_t ie = 0; ie < n_elements; ++ie)
993 const auto [geom, el_order] = GetGeometryAndOrder(element_type);
1000 if (mesh_order < 0) { mesh_order = el_order; }
1001 MFEM_VERIFY(mesh_order == el_order,
1002 "Variable order Gmsh meshes are not supported");
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)
1012 const int attribute = entity_physical_tag[ {entity_dim, entity_tag}];
1013 auto e = NewElement(mesh, geom, el_order, node_tags, attribute);
1018 AddElements(mesh, elems_by_dim);
1020 else if (section ==
"Periodic")
1023 if (n_periodic == 0) {
continue; }
1027 for (
int i = 0; i < mesh.
NumOfVertices; i++) { v2v[i] = i; }
1029 for (
size_t i = 0; i < n_periodic; ++i)
1035 for (
size_t j = 0; j < n_nodes; ++j)
1039 v2v[vertex_map.at(node_num)] = vertex_map.at(primary_node_num);
1044 while (!section.empty());
1049 void ReadGmsh2Mesh(
Mesh &mesh)
1051 const auto b = is_binary;
1052 MFEM_VERIFY(data_size ==
sizeof(
double),
"Incompatible data size.");
1057 section = GoToNextSection(input);
1058 if (section ==
"Nodes")
1067 for (
int d = 0; d < 3; ++d)
1070 bb_min[d] = min(bb_min[d], c[d]);
1071 bb_max[d] = max(bb_max[d], c[d]);
1074 vertex_map[node_num] = v;
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.");
1080 else if (section ==
"Elements")
1084 int num_el_read = 0;
1086 vector<vector<unique_ptr<Element>>> elems_by_dim(4);
1088 while (num_el_read < num_elements)
1090 auto add_element = [&](
int el_type,
int el_phys_tag,
Geometry::Type geom,
1091 int el_order,
const vector<int> &el_nodes)
1095 if (mesh_order < 0) { mesh_order = el_order; }
1096 MFEM_VERIFY(mesh_order == el_order,
1097 "Variable order Gmsh meshes are not supported");
1099 Element *e = NewElement(mesh, geom, el_order, el_nodes, el_phys_tag);
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);
1113 for (
int e = 0; e < n_els; ++e)
1116 int el_phys_tag = 0;
1122 for (
int i = 0; i < n_el_nodes; ++i)
1126 add_element(el_type, el_phys_tag, geom, el_order, el_nodes);
1135 int el_phys_tag = 0;
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)
1148 add_element(el_type, el_phys_tag, geom, el_order, el_nodes);
1153 AddElements(mesh, elems_by_dim);
1155 else if (section ==
"PhysicalNames")
1158 for (
int i = 0; i < num_names; ++i)
1162 phys_names_by_dim[phys_dim][phys_tag] = ReadQuotedString(input);
1165 else if (section ==
"Periodic")
1168 if (n_periodic_entities == 0) {
continue; }
1172 for (
int i = 0; i < mesh.
NumOfVertices; i++) { v2v[i] = i; }
1174 for (
int i = 0; i < n_periodic_entities; i++)
1179 if (input.peek() ==
'A')
1182 "Cannot find Affine transformation");
1184 getline(input, line);
1187 for (
int j = 0; j < n_nodes; ++j)
1191 v2v[vertex_map.at(node_num)] = vertex_map.at(primary_node_num);
1196 while (section !=
"");
1205 GmshReader(istream &input_,
Mesh &mesh) : input(input_)
1208 MFEM_VERIFY(version_str ==
"2.2" || version_str ==
"4.1",
1209 "Unsupported Gmsh file version. Supported versions: 2.2 and 4.1");
1217 MFEM_VERIFY(one == 1,
"Incompatible endianness.");
1222 ReadGmsh4Mesh(mesh);
1226 ReadGmsh2Mesh(mesh);
1238 if (mesh_order == 1)
1246 ho_el_nodes[mesh.
Dim][ie].resize(nv);
1248 for (
int i = 0; i < nv; ++i)
1250 ho_el_nodes[mesh.
Dim][ie][i] = v[map[i]];
1254 SimplifyPeriodicLinks();
1255 ReplacePeriodicVertices(mesh.
elements);
1256 ReplacePeriodicVertices(mesh.
boundary);
1262 if (mesh_order > 1 || periodic) { ho_vertices = mesh.
vertices; }
1264 AddPhysicalNames(mesh);
1271 if (mesh_order > 1 || periodic)
1289 MFEM_ASSERT(nfe,
"Invalid FE");
1290 const Array<int> &lex = nfe->GetLexicographicOrdering();
1293 for (
int i = 0; i < n; ++i)
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)
1299 (*nodes_gf)[vdofs[ii + d*n]] = v(d);