OGS
GmshReader.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "GmshReader.h"
5
6#include <algorithm>
7#include <array>
8#include <fstream>
9#include <map>
10#include <type_traits>
11#include <vector>
12
13#include "BaseLib/FileTools.h"
22#include "MeshLib/Mesh.h"
23#include "MeshLib/Node.h"
25
26namespace FileIO
27{
28namespace GMSH
29{
30bool isGMSHMeshFile(const std::string& fname)
31{
32 std::ifstream input(fname.c_str());
33
34 if (!input)
35 {
36 ERR("isGMSHMeshFile(): Could not open file {:s}.", fname);
37 return false;
38 }
39
40 std::string header_first_line;
41 input >> header_first_line;
42 if (header_first_line.find("$MeshFormat") != std::string::npos)
43 {
44 // read version
45 std::string version;
46 std::getline(input, version);
47 std::getline(input, version);
48 INFO("isGMSHMeshFile(): Found GMSH mesh file version: {:s}.", version);
49 input.close();
50 return true;
51 }
52
53 return false;
54}
55
56void readNodeIDs(std::ifstream& in, unsigned n_nodes,
57 std::vector<unsigned>& node_ids,
58 std::map<unsigned, unsigned> const& id_map)
59{
60 unsigned idx;
61 for (unsigned i = 0; i < n_nodes; i++)
62 {
63 in >> idx;
64 node_ids.push_back(id_map.at(idx));
65 }
66}
67
68template <typename ElementType>
69std::pair<MeshLib::Element*, int> createElement(
70 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
71 int const mat_id, std::map<unsigned, unsigned> const& id_map)
72{
73 std::vector<unsigned> node_ids;
74 readNodeIDs(in, ElementType::n_all_nodes, node_ids, id_map);
75
76 std::array<MeshLib::Node*, ElementType::n_all_nodes> element_nodes;
77
78 std::transform(begin(node_ids), end(node_ids), begin(element_nodes),
79 [&nodes](auto const id) { return nodes[id]; });
80
81 return std::make_pair(new ElementType(element_nodes), mat_id);
82}
83
84template <>
85std::pair<MeshLib::Element*, int> createElement<MeshLib::Tri>(
86 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
87 int const mat_id, std::map<unsigned, unsigned> const& id_map)
88{
89 std::vector<unsigned> node_ids;
90 readNodeIDs(in, 3, node_ids, id_map);
91
92 std::array<MeshLib::Node*, 3> element_nodes;
93
94 std::transform(std::rbegin(node_ids), std::rend(node_ids),
95 begin(element_nodes),
96 [&nodes](auto const id) { return nodes[id]; });
97
98 return std::make_pair(new MeshLib::Tri(element_nodes), mat_id);
99}
100
101template <>
102std::pair<MeshLib::Element*, int> createElement<MeshLib::Tet10>(
103 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
104 int const mat_id, std::map<unsigned, unsigned> const& id_map)
105{
106 std::vector<unsigned> node_ids;
107 readNodeIDs(in, MeshLib::Tet10::n_all_nodes, node_ids, id_map);
108
109 std::swap(node_ids[8], node_ids[9]);
110
111 std::array<MeshLib::Node*, MeshLib::Tet10::n_all_nodes> element_nodes;
112
113 std::transform(begin(node_ids), end(node_ids), begin(element_nodes),
114 [&nodes](auto const id) { return nodes[id]; });
115
116 return std::make_pair(new MeshLib::Tet10(element_nodes), mat_id);
117}
118
119template <>
120std::pair<MeshLib::Element*, int> createElement<MeshLib::Hex20>(
121 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
122 int const mat_id, std::map<unsigned, unsigned> const& id_map)
123{
124 std::vector<unsigned> node_ids;
125 readNodeIDs(in, MeshLib::Hex20::n_all_nodes, node_ids, id_map);
126
127 std::array<MeshLib::Node*, MeshLib::Hex20::n_all_nodes> element_nodes;
128
129 constexpr std::array node_order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 11,
130 13, 9, 16, 18, 19, 17, 10, 12, 14, 15};
131
132 std::transform(begin(node_order), end(node_order), begin(element_nodes),
133 [&node_ids, &nodes](auto const id)
134 { return nodes[node_ids[id]]; });
135
136 return std::make_pair(new MeshLib::Hex20(element_nodes), mat_id);
137}
138
139template <>
140std::pair<MeshLib::Element*, int> createElement<MeshLib::Prism15>(
141 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
142 int const mat_id, std::map<unsigned, unsigned> const& id_map)
143{
144 std::vector<unsigned> node_ids;
145 readNodeIDs(in, MeshLib::Prism15::n_all_nodes, node_ids, id_map);
146
147 std::array<MeshLib::Node*, MeshLib::Prism15::n_all_nodes> element_nodes;
148
149 constexpr std::array node_order = {0, 1, 2, 3, 4, 5, 6, 9,
150 7, 12, 14, 13, 8, 10, 11};
151
152 std::transform(begin(node_order), end(node_order), begin(element_nodes),
153 [&node_ids, &nodes](auto const id)
154 { return nodes[node_ids[id]]; });
155
156 return std::make_pair(new MeshLib::Prism15(element_nodes), mat_id);
157}
158
159template <>
160std::pair<MeshLib::Element*, int> createElement<MeshLib::Pyramid13>(
161 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
162 int const mat_id, std::map<unsigned, unsigned> const& id_map)
163{
164 std::vector<unsigned> node_ids;
165 readNodeIDs(in, MeshLib::Pyramid13::n_all_nodes, node_ids, id_map);
166 std::array<MeshLib::Node*, MeshLib::Pyramid13::n_all_nodes> element_nodes;
167
168 constexpr std::array node_order = {0, 1, 2, 3, 4, 5, 8,
169 10, 6, 7, 9, 11, 12};
170
171 std::transform(begin(node_order), end(node_order), begin(element_nodes),
172 [&node_ids, &nodes](auto const id)
173 { return nodes[node_ids[id]]; });
174
175 return std::make_pair(new MeshLib::Pyramid13(element_nodes), mat_id);
176}
177
178std::pair<MeshLib::Element*, int> readElement(
179 std::ifstream& in, std::vector<MeshLib::Node*> const& nodes,
180 std::map<unsigned, unsigned> const& id_map,
181 bool const is_created_with_gmsh2)
182{
183 unsigned idx;
184 unsigned type;
185 unsigned n_tags;
186 unsigned dummy;
187 int mat_id;
188
189 // The element format is structured like this:
190 // Gmsh version 4:
191 // element-id element-type n-tags physical-entity elementary entity node-ids
192 // Gmsh version 2:
193 // element-id element-type n-tags elementary physical-entity entity
194 // node-ids
195
196 in >> idx >> type >> n_tags;
197
198 if (!is_created_with_gmsh2)
199 {
200 in >> mat_id >> dummy;
201 }
202 else // for meshes created by using Gmsh version 2
203 {
204 in >> dummy >> mat_id;
205 }
206
207 switch (type)
208 {
209 case 1:
210 {
211 return createElement<MeshLib::Line>(in, nodes, mat_id, id_map);
212 }
213 case 2:
214 {
215 return createElement<MeshLib::Tri>(in, nodes, mat_id, id_map);
216 }
217 case 3:
218 {
219 return createElement<MeshLib::Quad>(in, nodes, mat_id, id_map);
220 }
221 case 4:
222 {
223 return createElement<MeshLib::Tet>(in, nodes, mat_id, id_map);
224 }
225 case 5:
226 {
227 return createElement<MeshLib::Hex>(in, nodes, mat_id, id_map);
228 }
229 case 6:
230 {
231 return createElement<MeshLib::Prism>(in, nodes, mat_id, id_map);
232 }
233 case 7:
234 {
235 return createElement<MeshLib::Pyramid>(in, nodes, mat_id, id_map);
236 }
237 case 8: // 3-node second order line.
238 {
239 return createElement<MeshLib::Line3>(in, nodes, mat_id, id_map);
240 }
241 case 9: // 6-node second order triangle.
242 {
243 return createElement<MeshLib::Tri6>(in, nodes, mat_id, id_map);
244 }
245 case 10: // 9-node second order quadrangle.
246 {
247 return createElement<MeshLib::Quad9>(in, nodes, mat_id, id_map);
248 }
249 case 11: // 10-node second order tetrahedron.
250 {
251 return createElement<MeshLib::Tet10>(in, nodes, mat_id, id_map);
252 }
253 case 16: // 8-node second order quadrangle.
254 {
255 return createElement<MeshLib::Quad8>(in, nodes, mat_id, id_map);
256 }
257 case 17: // 20-node second order hexahedron.
258 {
259 return createElement<MeshLib::Hex20>(in, nodes, mat_id, id_map);
260 }
261 case 18: // 15-node second order prism.
262 {
263 return createElement<MeshLib::Prism15>(in, nodes, mat_id, id_map);
264 }
265 case 19: // 13-node second order pyramid.
266 {
267 return createElement<MeshLib::Pyramid13>(in, nodes, mat_id, id_map);
268 }
269 case 15:
270 in >> dummy; // skip rest of line
271 break;
272 default:
273 WARN("readGMSHMesh(): Unknown element type {:d}.", type);
274 break;
275 }
276 return std::make_pair(nullptr, -1);
277}
278
279MeshLib::Mesh* readGMSHMesh(std::string const& fname,
280 bool const is_created_with_gmsh2)
281{
282 if (!is_created_with_gmsh2)
283 {
284 WARN(
285 "If the mesh is generated with Gmsh version 2 and it is saved"
286 " (or exported) as \"Version 2 ASCII\" format, the flag"
287 " --gmsh2_physical_id must be used for a correct conversion from "
288 "physical id to MaterialIDs.");
289 }
290
291 std::string line;
292 std::ifstream in(fname.c_str(), std::ios::in);
293 if (!in.is_open())
294 {
295 WARN("readGMSHMesh() - Could not open file {:s}.", fname);
296 return nullptr;
297 }
298
299 std::getline(in, line); // $MeshFormat keyword
300 if (line.find("$MeshFormat") == std::string::npos)
301 {
302 in.close();
303 WARN("No GMSH file format recognized.");
304 return nullptr;
305 }
306
307 std::getline(in, line); // version-number file-type data-size
308 if (line.substr(0, 3) != "2.2")
309 {
310 WARN("Wrong gmsh file format version '{:s}'.", line.substr(0, 3));
311 return nullptr;
312 }
313
314 if (line[4] != '0')
315 {
316 WARN("Currently reading gmsh binary file type is not supported.");
317 return nullptr;
318 }
319 std::getline(in, line); //$EndMeshFormat
320
321 std::vector<MeshLib::Node*> nodes;
322 std::vector<MeshLib::Element*> elements;
323 std::vector<int> materials;
324 std::map<unsigned, unsigned> id_map;
325 while (line.find("$EndElements") == std::string::npos)
326 {
327 // Node data
328 std::getline(in, line); //$Nodes Keywords
329 if (line.find("$Nodes") != std::string::npos)
330 {
331 std::size_t n_nodes(0);
332 long id;
333 double x;
334 double y;
335 double z;
336 in >> n_nodes >> std::ws;
337 nodes.resize(n_nodes);
338 for (std::size_t i = 0; i < n_nodes; i++)
339 {
340 in >> id >> x >> y >> z >> std::ws;
341 id_map.insert(std::map<unsigned, unsigned>::value_type(id, i));
342 nodes[i] = new MeshLib::Node(x, y, z, id);
343 }
344 std::getline(in, line); // End Node keyword $EndNodes
345 }
346
347 // Element data
348 if (line.find("$Elements") != std::string::npos)
349 {
350 std::size_t n_elements(0);
351 if (!(in >> n_elements >> std::ws))
352 { // number-of-elements
353 ERR("Read GMSH mesh does not contain any elements");
354 }
355 elements.reserve(n_elements);
356 materials.reserve(n_elements);
357 for (std::size_t i = 0; i < n_elements; i++)
358 {
359 MeshLib::Element* elem(nullptr);
360 int mat_id(0);
361 std::tie(elem, mat_id) =
362 readElement(in, nodes, id_map, is_created_with_gmsh2);
363
364 if (elem)
365 {
366 elements.push_back(elem);
367 materials.push_back(mat_id);
368 }
369 }
370 std::getline(in, line); // END keyword
371 }
372
373 if (line.find("PhysicalNames") != std::string::npos)
374 {
375 std::size_t n_lines(0);
376 in >> n_lines >> std::ws; // number-of-lines
377 for (std::size_t i = 0; i < n_lines; i++)
378 {
379 std::getline(in, line);
380 }
381 std::getline(in, line); // END keyword
382 }
383 }
384 in.close();
385 if (elements.empty())
386 {
387 for (auto& node : nodes)
388 {
389 delete node;
390 }
391 return nullptr;
392 }
393
395 BaseLib::extractBaseNameWithoutExtension(fname), nodes, elements,
396 true /* compute_element_neighbors */));
397
398 auto* const material_ids =
400 "MaterialIDs", MeshLib::MeshItemType::Cell, 1);
401 if (!material_ids)
402 {
403 WARN("Could not create PropertyVector for MaterialIDs in Mesh.");
404 }
405 else
406 {
407 material_ids->assign(materials);
408 }
409
411
412 INFO("\t... finished.");
413 INFO("Nr. Nodes: {:d}.", nodes.size());
414 INFO("Nr. Elements: {:d}.", elements.size());
415
416 return mesh;
417}
418
419} // end namespace GMSH
420} // end namespace FileIO
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
Properties & getProperties()
Definition Mesh.h:125
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
static std::size_t condense(MeshLib::Mesh &mesh)
std::string extractBaseNameWithoutExtension(std::string const &pathname)
std::pair< MeshLib::Element *, int > createElement< MeshLib::Tri >(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, int const mat_id, std::map< unsigned, unsigned > const &id_map)
MeshLib::Mesh * readGMSHMesh(std::string const &fname, bool const is_created_with_gmsh2)
std::pair< MeshLib::Element *, int > createElement< MeshLib::Prism15 >(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, int const mat_id, std::map< unsigned, unsigned > const &id_map)
std::pair< MeshLib::Element *, int > readElement(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, std::map< unsigned, unsigned > const &id_map, bool const is_created_with_gmsh2)
std::pair< MeshLib::Element *, int > createElement< MeshLib::Pyramid13 >(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, int const mat_id, std::map< unsigned, unsigned > const &id_map)
bool isGMSHMeshFile(const std::string &fname)
std::pair< MeshLib::Element *, int > createElement< MeshLib::Tet10 >(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, int const mat_id, std::map< unsigned, unsigned > const &id_map)
std::pair< MeshLib::Element *, int > createElement< MeshLib::Hex20 >(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, int const mat_id, std::map< unsigned, unsigned > const &id_map)
std::pair< MeshLib::Element *, int > createElement(std::ifstream &in, std::vector< MeshLib::Node * > const &nodes, int const mat_id, std::map< unsigned, unsigned > const &id_map)
void readNodeIDs(std::ifstream &in, unsigned n_nodes, std::vector< unsigned > &node_ids, std::map< unsigned, unsigned > const &id_map)
TemplateElement< MeshLib::TetRule10 > Tet10
Definition Tet.h:15
TemplateElement< MeshLib::HexRule20 > Hex20
Definition Hex.h:15
TemplateElement< MeshLib::PyramidRule13 > Pyramid13
Definition Pyramid.h:15
TemplateElement< MeshLib::TriRule3 > Tri
Definition Tri.h:15
TemplateElement< MeshLib::PrismRule15 > Prism15
Definition Prism.h:15