LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
gmsh_reader.cc
1#include "gmsh_reader.h"
2
3#include <lf/geometry/geometry.h>
4
5#include <boost/fusion/adapted/std_tuple.hpp>
6#include <boost/spirit/include/qi.hpp>
7#include <boost/spirit/include/qi_string.hpp>
8#include <fstream>
9
10#include "eigen_fusion_adapter.h"
11
12using size_type = lf::mesh::Mesh::size_type;
13
14// Structures that represent the MshFile:
15namespace lf::io {
16
18 size_type physical_entity_nr) const {
19 auto physical_entities = PhysicalEntityNr(e);
20 return std::find(physical_entities.begin(), physical_entities.end(),
21 physical_entity_nr) != physical_entities.end();
22}
23
24GmshReader::GmshReader(std::unique_ptr<mesh::MeshFactory> factory,
25 const GmshFileVariant& msh_file)
26 : mesh_factory_(std::move(factory)) {
27 std::visit([this](const auto& mf) { InitGmshFile(mf); }, msh_file);
28}
29
30GmshReader::GmshReader(std::unique_ptr<mesh::MeshFactory> factory,
31 const std::string& filename)
32 : GmshReader(std::move(factory), ReadGmshFile(filename)) {}
33
35 dim_t codim) const {
36 LF_ASSERT_MSG(!name.empty(), "name is empty");
37 auto [begin, end] = name_2_nr_.equal_range(name); // NOLINT
38 if (begin == end) {
39 throw base::LfException("No Physical Entity with this name found.");
40 }
41 auto result = *begin;
42 ++begin;
43 if (begin == end) {
44 if (codim == dim_t(-1) || codim == result.second.second) {
45 return result.second.first;
46 }
47 } else {
48 if (codim == dim_t(-1)) {
50 "There are multiple physical entities with the name " + name +
51 ", please specify also the codimension.");
52 }
53 if (result.second.second == codim) {
54 return result.second.first;
55 }
56 while (begin->second.second != codim && begin != end) {
57 ++begin;
58 }
59 if (begin->second.second == codim) {
60 return begin->second.first;
61 }
62 }
63 throw base::LfException("Physical Entity with name='" + name +
64 "' and codimension=" + std::to_string(codim) +
65 "' not found.");
66}
67
69 dim_t codim) const {
70 auto [begin, end] = nr_2_name_.equal_range(number); // NOLINT
71 if (begin == end) {
72 throw base::LfException("Physical entity with number " +
73 std::to_string(number) + " not found.");
74 }
75 auto result = *begin;
76 ++begin;
77 if (begin == end) {
78 if (codim == dim_t(-1) || result.second.second == codim) {
79 return result.second.first;
80 }
81 } else {
82 if (codim == dim_t(-1)) {
84 "There are multiple physical entities with the Number " +
85 std::to_string(number) + ", please specify also the codimension");
86 }
87 if (result.second.second == codim) {
88 return result.second.first;
89 }
90 while (begin->second.second != codim && begin != end) {
91 ++begin;
92 }
93 if (begin->second.second == codim) {
94 return begin->second.first;
95 }
96 }
98 "Physical entity with number=" + std::to_string(number) +
99 ", codim=" + std::to_string(codim) + " not found.");
100}
101
102std::vector<std::pair<size_type, std::string>> GmshReader::PhysicalEntities(
103 dim_t codim) const {
104 std::vector<std::pair<size_type, std::string>> result;
105 for (const auto& p : nr_2_name_) {
106 if (p.second.second != codim) {
107 continue;
108 }
109 result.emplace_back(p.first, p.second.first);
110 }
111 return result;
112}
113
114std::vector<size_type> GmshReader::PhysicalEntityNr(
115 const mesh::Entity& e) const {
116 return physical_nrs_->operator()(e);
117}
118
120 // 1) Check Gmsh_file and initialize
122
123 dim_t dim_mesh = mesh_factory_->DimMesh();
124 dim_t dim_world = mesh_factory_->DimWorld();
125 LF_VERIFY_MSG(
126 dim_mesh >= 2 && dim_mesh <= 3 && dim_world >= 2 && dim_world <= 3,
127 "GmshReader supports only 2D and 3D meshes.");
128
129 // 1) Determine which nodes of gmsh are also nodes of the LehrFEM++ mesh +
130 // count number of entities of each codimension (exclude auxilliary nodes).
131 // This is necessary because e.g. second order meshes in gmsh need a lot of
132 // auxilliary nodes to define their geometry...
134 // is_main_node[i] = true means that gmsh-node i is one of the main nodes of
135 // an element
136 std::vector<bool> is_main_node(msh_file.Nodes.size(), false);
137
138 // mi2gi = mesh_index_2_gmsh_index
139 // mi2gi[c][i] contains the gmsh entities that belong to the mesh entity with
140 // codim = c and mesh index i.
141 std::vector<std::vector<std::vector<size_type>>> mi2gi(dim_mesh + 1);
142
143 {
144 // count the number of entities for each codimension and reserve space:
145 std::vector<size_type> num_entities(mesh_factory_->DimMesh() + 1, 0);
146
147 for (const auto& e : msh_file.Elements) {
148 LF_ASSERT_MSG(DimOf(e.Type) <= dim_mesh,
149 "mesh_factory->DimMesh() = "
150 << dim_mesh
151 << ", but msh-file contains entities with dimension "
152 << DimOf(e.Type));
153
154 ++num_entities[DimOf(e.Type)];
155
156 if (e.Type != GMshFileV2::ElementType::POINT) {
157 // mark main nodes
158 auto ref_el = RefElOf(e.Type);
159 for (unsigned int i = 0; i < ref_el.NumNodes(); ++i) {
160 auto node_number = e.NodeNumbers[i];
161 if (is_main_node.size() <= node_number) {
162 is_main_node.resize(node_number + 1);
163 }
164 is_main_node[node_number] = true;
165 }
166 }
167 }
168
169 for (dim_t c = 0; c <= dim_mesh; ++c) {
170 mi2gi[c].reserve(num_entities[dim_mesh - c]);
171 }
172
173 LF_ASSERT_MSG(num_entities[dim_mesh] > 0,
174 "MshFile contains no elements with dimension " << dim_mesh);
175 }
176
177 // 2) Insert main nodes into MeshFactory
179
180 // gmsh_index_2_mesh_index for nodes:
181 // gi2mi[i] = j means that gmsh node with gmsh index i has mesh index j
182 std::vector<size_type> gi2mi;
183 gi2mi.resize(is_main_node.size(), -1);
184
185 // gi2i[i] = j implies msh_file.Nodes[j].first = i
186 std::vector<size_type> gi2i;
187 gi2i.resize(is_main_node.size(), -1);
188
189 for (std::size_t i = 0; i < msh_file.Nodes.size(); ++i) {
190 const auto& n = msh_file.Nodes[i];
191 if (gi2i.size() <= n.first) {
192 gi2i.resize(n.first + 1, -1);
193 }
194 gi2i[n.first] = i;
195
196 if (is_main_node.size() <= n.first || !is_main_node[n.first]) {
197 continue;
198 }
199
200 size_type mi;
201 if (dim_world == 2) {
202 LF_ASSERT_MSG(
203 n.second(2) == 0,
204 "In a 2D GmshMesh, the z-coordinate of every node must be zero");
205 mi = mesh_factory_->AddPoint(n.second.topRows(2));
206 } else {
207 mi = mesh_factory_->AddPoint(n.second);
208 }
209 gi2mi[n.first] = mi;
210 }
211
212 // 3) Insert entities (except nodes) into MeshFactory:
214
215 std::size_t begin = 0;
216 for (std::size_t end = 0; end < msh_file.Elements.size(); ++end) {
217 const auto& begin_element = msh_file.Elements[begin];
218 const auto& end_element = msh_file.Elements[end];
219 auto ref_el = RefElOf(end_element.Type);
220 auto codim = dim_mesh - ref_el.Dimension();
221 if (begin_element.NodeNumbers == end_element.NodeNumbers && begin != end &&
222 begin_element.Type == end_element.Type) {
223 // This entity appears more than once
224 mi2gi[codim].back().push_back(end);
225 continue;
226 }
227
228 begin = end;
229 if (ref_el == base::RefEl::kPoint()) {
230 auto mesh_index = gi2mi[end_element.NodeNumbers[0]];
231 // check that this node is a main node (auxilliary nodes are not inserted
232 // into the mesh!):
233 if (mesh_index != std::numeric_limits<unsigned int>::max()) {
234 // special case, this entity is a point (which has already been
235 // inserted)
236 if (mi2gi[dim_mesh].size() <= mesh_index) {
237 mi2gi[dim_mesh].resize(mesh_index + 1);
238 }
239 mi2gi[dim_mesh][mesh_index].push_back(end);
240 }
241 } else {
242 // gmsh element is not a point -> insert entity:
243 auto num_nodes = end_element.NodeNumbers.size();
244 Eigen::MatrixXd node_coords(dim_world, num_nodes);
245 for (std::size_t i = 0; i < num_nodes; ++i) {
246 auto node_coord =
247 msh_file.Nodes[gi2i[end_element.NodeNumbers[i]]].second;
248 if (dim_world == 2) {
249 node_coords.col(i) = node_coord.topRows(2);
250 } else {
251 node_coords.col(i) = node_coord;
252 }
253 }
254 std::unique_ptr<geometry::Geometry> geom;
255
256 switch (end_element.Type) {
258 ref_el = base::RefEl::kSegment();
259 geom = std::make_unique<geometry::SegmentO1>(node_coords);
260 break;
262 ref_el = base::RefEl::kSegment();
263 geom = std::make_unique<geometry::SegmentO2>(node_coords);
264 break;
266 ref_el = base::RefEl::kTria();
267 geom = std::make_unique<geometry::TriaO1>(node_coords);
268 break;
270 ref_el = base::RefEl::kTria();
271 geom = std::make_unique<geometry::TriaO2>(node_coords);
272 break;
274 ref_el = base::RefEl::kQuad();
275 geom = std::make_unique<geometry::QuadO1>(node_coords);
276 break;
278 ref_el = base::RefEl::kQuad();
279 geom = std::make_unique<geometry::QuadO2>(node_coords);
280 break;
282 ref_el = base::RefEl::kQuad();
283 geom = std::make_unique<geometry::QuadO2>(node_coords.leftCols(8));
284 break;
285 default:
286 LF_VERIFY_MSG(false, "Gmsh element type "
287 << end_element.Type
288 << " not (yet) supported by GmshReader.");
289 }
290 std::vector<size_type> main_nodes(ref_el.NumNodes());
291 for (dim_t i = 0; i < ref_el.NumNodes(); ++i) {
292 main_nodes[i] = gi2mi[end_element.NodeNumbers[i]];
293 }
294
295 mesh_factory_->AddEntity(ref_el, main_nodes, std::move(geom));
296 mi2gi[codim].emplace_back(std::vector{static_cast<unsigned int>(end)});
297 }
298 }
299
300 // 4) Construct mesh
302 mesh_ = mesh_factory_->Build();
303
304 // 5) Build MeshDataSet that assigns the physical entitiies:
307 mesh::utils::make_AllCodimMeshDataSet<std::vector<size_type>>(mesh_);
308
309 for (dim_t c = 0; c <= dim_mesh; ++c) {
310 for (const auto* const e : mesh_->Entities(c)) {
311 auto mi = mesh_->Index(*e);
312 if (c == dim_mesh && mi >= mi2gi[dim_mesh].size()) {
313 // this point did not appear as a gmsh element in the file -> don't
314 // assign any physical entity nr.
315 continue;
316 }
317 if (mi2gi[c].size() > mi) {
318 std::vector<size_type> temp;
319 for (auto& gmsh_index : mi2gi[c][mi]) {
320 temp.push_back(msh_file.Elements[gmsh_index].PhysicalEntityNr);
321 }
322
323 physical_nrs_->operator()(*e) = std::move(temp);
324 }
325 }
326 }
327
328 // 6) Create mapping physicalEntityNr <-> physicalEntityName:
330
331 for (const auto& pe : msh_file.PhysicalEntities) {
332 name_2_nr_.insert(
333 std::pair{pe.Name, std::pair{pe.Number, dim_mesh - pe.Dimension}});
334 nr_2_name_.insert(
335 std::pair{pe.Number, std::pair{pe.Name, dim_mesh - pe.Dimension}});
336 }
337
338 if (!msh_file.Periodic.empty()) {
339 /*LOGGER_ENTRY(logger_,
340 "WARNING: GMSH File contains periodic boundary relations "
341 "between elements. These are ignored by GmshReader.",
342 3);*/
343 }
344}
345
347 // 1) Check Gmsh_file and initialize
349
350 dim_t dim_mesh = mesh_factory_->DimMesh();
351 dim_t dim_world = mesh_factory_->DimWorld();
352 LF_VERIFY_MSG(
353 dim_mesh >= 2 && dim_mesh <= 3 && dim_world >= 2 && dim_world <= 3,
354 "GmshReader supports only 2D and 3D meshes.");
355
356 // 1) Determine which nodes of gmsh are also nodes of the LehrFEM++ mesh +
357 // count number of entities of each codimension (exclude auxilliary nodes).
358 // This is necessary because e.g. second order meshes in gmsh need a lot of
359 // auxilliary nodes to define their geometry...
361 auto min_node_tag = msh_file.nodes.min_node_tag;
362 auto max_node_tag = msh_file.nodes.max_node_tag;
363
364 // is_main_node[i] = true means that gmsh-node (i + min_node_tag) is one of
365 // the main nodes of an element
366 std::vector<bool> is_main_node(max_node_tag - min_node_tag + 1, false);
367
368 // mi2gi = mesh_index_2_gmsh_index
369 // mi2gi[c][i] contains the index of the gmsh entity block that contains
370 // the mesh entity with codim = c and mesh index i.
371 std::vector<std::vector<std::vector<std::size_t>>> mi2gi(dim_mesh + 1);
372
373 {
374 // count the number of entities for each codimension and reserve space:
375 std::vector<size_type> num_entities(mesh_factory_->DimMesh() + 1, 0);
376
377 for (const auto& eb : msh_file.elements.element_blocks) {
378 LF_ASSERT_MSG(eb.dimension <= dim_mesh,
379 "mesh_factory->DimMesh() = "
380 << dim_mesh
381 << ", but msh-file contains entities with dimension "
382 << DimOf(eb.element_type));
383 LF_ASSERT_MSG(eb.dimension == DimOf(eb.element_type),
384 "error in GmshFile: Mismatch between entity block type ("
385 << eb.element_type << ") and dimension ("
386 << DimOf(eb.element_type) << ")");
387
388 num_entities[eb.dimension] += eb.elements.size();
389
390 if (eb.dimension == dim_mesh) {
391 // mark main nodes
392 auto ref_el = RefElOf(eb.element_type);
393 for (const auto& element : eb.elements) {
394 const auto& node_indices = std::get<1>(element);
395 for (unsigned int i = 0; i < ref_el.NumNodes(); ++i) {
396 auto node_tag = node_indices[i];
397 if (is_main_node.size() <= node_tag - min_node_tag) {
398 is_main_node.resize(node_tag - min_node_tag + 1);
399 }
400 is_main_node[node_tag - min_node_tag] = true;
401 }
402 }
403 }
404 }
405
406 for (dim_t c = 0; c <= dim_mesh; ++c) {
407 mi2gi[c].reserve(num_entities[dim_mesh - c]);
408 }
409
410 LF_ASSERT_MSG(num_entities[dim_mesh] > 0,
411 "MshFile contains no elements with dimension " << dim_mesh);
412 }
413
414 // 2) Insert main nodes into MeshFactory
416
417 // gmsh_index_2_mesh_index for nodes:
418 // gi2mi[i] = j means that gmsh node with gmsh tag i+min_node_tag has mesh
419 // index j
420 std::vector<size_type> gi2mi;
421 gi2mi.resize(is_main_node.size(), -1);
422
423 // gi2i[i] = (j,k) implies msh_file.nodes.node_blocks[j].nodes[k].first = i +
424 // min_node_tag
425 std::vector<std::pair<size_type, size_type>> gi2i;
426 gi2i.resize(is_main_node.size(), {-1, -1});
427
428 for (std::size_t j = 0; j < msh_file.nodes.node_blocks.size(); ++j) {
429 const auto& node_block = msh_file.nodes.node_blocks[j];
430 for (std::size_t k = 0; k < node_block.nodes.size(); ++k) {
431 const auto& n = node_block.nodes[k];
432 LF_ASSERT_MSG(gi2i.size() > n.first - min_node_tag,
433 "error: node_tag is higher than max_node_tag");
434
435 gi2i[n.first - min_node_tag] = std::make_pair(j, k);
436
437 if (!is_main_node[n.first - min_node_tag]) {
438 continue;
439 }
440
441 size_type mi;
442 if (dim_world == 2) {
443 LF_ASSERT_MSG(
444 n.second(2) == 0,
445 "In a 2D GmshMesh, the z-coordinate of every node must be zero");
446 mi = mesh_factory_->AddPoint(n.second.topRows(2));
447 } else {
448 mi = mesh_factory_->AddPoint(n.second);
449 }
450 gi2mi[n.first - min_node_tag] = mi;
451 }
452 }
453
454 // 3) Insert entities (except nodes) into MeshFactory:
456
457 for (std::size_t ebi = 0; ebi < msh_file.elements.element_blocks.size();
458 ++ebi) {
459 const auto& element_block = msh_file.elements.element_blocks[ebi];
460 std::size_t begin = 0;
461 auto ref_el = RefElOf(element_block.element_type);
462 auto codim = dim_mesh - ref_el.Dimension();
463
464 for (std::size_t end = 0; end < element_block.elements.size(); ++end) {
465 const auto& begin_element = element_block.elements[begin];
466 const auto& end_element = element_block.elements[end];
467
468 if (begin_element.second == end_element.second && begin != end) {
469 // This entity appears more than once
470 mi2gi[codim].back().emplace_back(ebi);
471 continue;
472 }
473
474 begin = end;
475 if (ref_el == base::RefEl::kPoint()) {
476 // check that this node is a main node (auxiliary nodes are not
477 // inserted into the mesh!):
478
479 auto mesh_index = gi2mi[end_element.second[0] - min_node_tag];
480 if (mesh_index != std::numeric_limits<unsigned int>::max()) {
481 // special case, this entity is a point (which has already been
482 // inserted)
483 if (mi2gi[dim_mesh].size() <= mesh_index) {
484 mi2gi[dim_mesh].resize(mesh_index + 1);
485 }
486 mi2gi[dim_mesh][mesh_index].emplace_back(ebi);
487 }
488 } else {
489 // gmsh element is not a point -> insert entity:
490 auto num_nodes = end_element.second.size();
491 Eigen::MatrixXd node_coords(dim_world, num_nodes);
492 for (std::size_t i = 0; i < num_nodes; ++i) {
493 auto& gi2i_e = gi2i[end_element.second[i] - min_node_tag];
494 auto node_coord = msh_file.nodes.node_blocks[gi2i_e.first]
495 .nodes[gi2i_e.second]
496 .second;
497 if (dim_world == 2) {
498 node_coords.col(i) = node_coord.topRows(2);
499 } else {
500 node_coords.col(i) = node_coord;
501 }
502 }
503 std::unique_ptr<geometry::Geometry> geom;
504
505 switch (element_block.element_type) {
507 ref_el = base::RefEl::kSegment();
508 geom = std::make_unique<geometry::SegmentO1>(node_coords);
509 break;
511 ref_el = base::RefEl::kSegment();
512 geom = std::make_unique<geometry::SegmentO2>(node_coords);
513 break;
515 ref_el = base::RefEl::kTria();
516 geom = std::make_unique<geometry::TriaO1>(node_coords);
517 break;
519 ref_el = base::RefEl::kTria();
520 geom = std::make_unique<geometry::TriaO2>(node_coords);
521 break;
523 ref_el = base::RefEl::kQuad();
524 geom = std::make_unique<geometry::QuadO1>(node_coords);
525 break;
527 ref_el = base::RefEl::kQuad();
528 geom = std::make_unique<geometry::QuadO2>(node_coords);
529 break;
531 ref_el = base::RefEl::kQuad();
532 geom = std::make_unique<geometry::QuadO2>(node_coords.leftCols(8));
533 break;
534 default:
535 LF_VERIFY_MSG(false, "Gmsh element type "
536 << element_block.element_type
537 << " not (yet) supported by GmshReader.");
538 }
539 std::vector<size_type> main_nodes(ref_el.NumNodes());
540 for (dim_t i = 0; i < ref_el.NumNodes(); ++i) {
541 main_nodes[i] = gi2mi[end_element.second[i] - min_node_tag];
542 }
543
544 mesh_factory_->AddEntity(ref_el, main_nodes, std::move(geom));
545 mi2gi[codim].push_back(std::vector<std::size_t>{ebi});
546 }
547 }
548 }
549
550 // 4) Construct mesh
552 mesh_ = mesh_factory_->Build();
553
554 // 5) Create a mapping gmsh entity -> physical entity
556
557 // GMsh Entity index to GMsh physical entity index.
558 // gmei2gphi[c][i] contains all physical entity tags that belong to entity
559 // with tag i and codim c
560 std::array<std::vector<std::vector<int>>, 4> gmei2gmphi;
561
562 auto makeMap = [](std::vector<std::vector<int>>& result, auto& entities) {
563 // find largest entity tag:
564 int max_entity_tag = 0;
565 for (auto& e : entities) {
566 max_entity_tag = std::max(max_entity_tag, e.tag);
567 }
568 result.resize(max_entity_tag + 1);
569
570 // build map:
571 for (auto& e : entities) {
572 result[e.tag] = e.physical_tags;
573 }
574 };
575
576 if (msh_file.partitioned_entities.num_partitions == 0) {
577 makeMap(gmei2gmphi[dim_mesh], std::get<0>(msh_file.entities));
578 makeMap(gmei2gmphi[dim_mesh - 1], std::get<1>(msh_file.entities));
579 makeMap(gmei2gmphi[dim_mesh - 2], std::get<2>(msh_file.entities));
580 if (dim_mesh == 3) {
581 makeMap(gmei2gmphi[0], std::get<3>(msh_file.entities));
582 }
583 } else {
584 makeMap(gmei2gmphi[dim_mesh],
585 std::get<0>(msh_file.partitioned_entities.partitioned_entities));
586 makeMap(gmei2gmphi[dim_mesh - 1],
587 std::get<1>(msh_file.partitioned_entities.partitioned_entities));
588 makeMap(gmei2gmphi[dim_mesh - 2],
589 std::get<2>(msh_file.partitioned_entities.partitioned_entities));
590 if (dim_mesh == 3) {
591 makeMap(gmei2gmphi[0],
592 std::get<3>(msh_file.partitioned_entities.partitioned_entities));
593 }
594 }
595
596 // 6) Build MeshDataSet that assigns the physical entitiies:
599 mesh::utils::make_AllCodimMeshDataSet<std::vector<size_type>>(mesh_);
600
601 for (dim_t c = 0; c <= dim_mesh; ++c) {
602 for (const auto* e : mesh_->Entities(c)) {
603 auto mi = mesh_->Index(*e);
604 if (c == dim_mesh && mi >= mi2gi[dim_mesh].size()) {
605 // this point did not appear as a gmsh element in the file -> don't
606 // assign any physical entity nr.
607 continue;
608 }
609 if (mi2gi[c].size() > mi) {
610 std::vector<size_type> temp;
611 for (auto& gmsh_index : mi2gi[c][mi]) {
612 const auto& element_block =
613 msh_file.elements.element_blocks[gmsh_index];
614 auto& physical_tags = gmei2gmphi[c][element_block.entity_tag];
615 temp.insert(std::end(temp), std::begin(physical_tags),
616 std::end(physical_tags));
617 }
618
619 physical_nrs_->operator()(*e) = std::move(temp);
620 }
621 }
622 }
623
624 // 7) Create mapping physicalEntityNr <-> physicalEntityName:
626
627 for (const auto& pn : msh_file.physical_names) {
628 name_2_nr_.insert(std::pair{
629 pn.name, std::pair{pn.physical_tag, dim_mesh - pn.dimension}});
630 nr_2_name_.insert(std::pair{pn.physical_tag,
631 std::pair{pn.name, dim_mesh - pn.dimension}});
632 }
633
634 if (!msh_file.periodic_links.empty()) {
635 /*LOGGER_ENTRY(logger_,
636 "WARNING: GMSH File contains periodic boundary relations "
637 "between elements. These are ignored by GmshReader.",
638 3);*/
639 }
640}
641
642std::variant<GMshFileV2, GMshFileV4> ReadGmshFile(const std::string& filename) {
643 // Open file and copy it into memory:
645 std::ifstream in(filename, std::ios_base::in | std::ios_base::binary);
646 if (!in) {
647 std::string error("Could not open file ");
648 error += filename;
649 throw lf::base::LfException(error);
650 }
651
652 std::string storage;
653 in.unsetf(std::ios::skipws); // no white space skipping
654 std::copy(std::istream_iterator<char>(in), std::istream_iterator<char>(),
655 std::back_inserter(storage));
656
657 // Parse header to determine if we are dealing with ASCII format or binary
658 // format + little or big endian:
660 auto iter = storage.cbegin();
661 auto end = storage.cend();
662
663 namespace qi = boost::spirit::qi;
664 namespace ascii = boost::spirit::ascii;
665
666 // version, is_binary, sizeof(size_t), 1 (as int)
667 std::tuple<std::string, bool, int, int> header;
668 qi::rule<decltype(iter), std::string()> version_parser;
669
670 version_parser %= +(ascii::alnum | ascii::punct);
671 qi::rule<decltype(iter), decltype(header)(), ascii::space_type> header_parser;
672 header_parser %= qi::lit("$MeshFormat") >>
673 (qi::hold[(version_parser >> qi::lit('0') >>
674 qi::attr(false) >> qi::int_ >> qi::attr(1))] |
675 (version_parser >> qi::lit('1') >> qi::attr(true) >>
676 qi::int_ >> qi::little_dword)) >>
677 qi::lit("$EndMeshFormat");
678
679 bool succesful;
680 succesful = qi::phrase_parse(iter, end, header_parser, ascii::space, header);
681
682 LF_VERIFY_MSG(succesful, "Could not read header of file " << filename);
683
684 if (std::get<0>(header) == "4.1") {
685 return ReadGmshFileV4(iter, end, std::get<0>(header), std::get<1>(header),
686 std::get<2>(header), std::get<3>(header), filename);
687 }
688 if (std::get<0>(header) == "2.2") {
689 return readGmshFileV2(iter, end, std::get<0>(header), std::get<1>(header),
690 std::get<2>(header), std::get<3>(header), filename);
691 }
692 LF_VERIFY_MSG(false, "GmshFiles with Version " << std::get<0>(header)
693 << " are not yet supported.");
694}
695
696} // namespace lf::io
A simple, general purpose exception class that is thrown by LehrFEM++ if something is wrong.
Definition: lf_exception.h:21
static constexpr RefEl kSegment()
Returns the (1-dimensional) reference segment.
Definition: ref_el.h:150
static constexpr RefEl kPoint()
Returns the (0-dimensional) reference point.
Definition: ref_el.h:141
static constexpr RefEl kTria()
Returns the reference triangle.
Definition: ref_el.h:158
static constexpr RefEl kQuad()
Returns the reference quadrilateral.
Definition: ref_el.h:166
Reads a Gmsh *.msh file into a mesh::MeshFactory and provides a link between mesh::Entity objects and...
Definition: gmsh_reader.h:70
void InitGmshFile(const GMshFileV2 &msh_file)
Definition: gmsh_reader.cc:119
std::shared_ptr< mesh::Mesh > mesh_
The underlying grid created by the grid factory.
Definition: gmsh_reader.h:178
base::RefEl::dim_t dim_t
Definition: gmsh_reader.h:73
bool IsPhysicalEntity(const mesh::Entity &e, size_type physical_entity_nr) const
Test whether the given entity belongs to a Gmsh physical entity.
Definition: gmsh_reader.cc:17
std::vector< size_type > PhysicalEntityNr(const mesh::Entity &e) const
Return the Physical Entity Numbers associated with this mesh entity, sorted ascending.
Definition: gmsh_reader.cc:114
mesh::Mesh::size_type size_type
Definition: gmsh_reader.h:72
std::shared_ptr< mesh::utils::AllCodimMeshDataSet< std::vector< size_type > > > physical_nrs_
The PhysicalEntityNr of every entity (0 if not set):
Definition: gmsh_reader.h:184
std::string PhysicalEntityNr2Name(size_type number, dim_t codim=-1) const
Gives the name of a physical entity (inverse of PhysicalEntityName2Nr())
Definition: gmsh_reader.cc:68
std::multimap< size_type, std::pair< std::string, dim_t > > nr_2_name_
Map from physicalEntity nr -> name, codim.
Definition: gmsh_reader.h:190
GmshReader(std::unique_ptr< mesh::MeshFactory > factory, const GmshFileVariant &msh_file)
Create a new GmshReader from the given MshFile (advanced usage)
Definition: gmsh_reader.cc:24
std::multimap< std::string, std::pair< size_type, dim_t > > name_2_nr_
Map from physicalEntity name -> nr, codim.
Definition: gmsh_reader.h:187
size_type PhysicalEntityName2Nr(const std::string &name, dim_t codim=-1) const
maps the name of a physical entity to the physical entity number of gmsh.
Definition: gmsh_reader.cc:34
std::vector< std::pair< size_type, std::string > > PhysicalEntities(dim_t codim) const
Retrieve a list of all (Gmsh) physical entities of the given codim.
Definition: gmsh_reader.cc:102
std::variant< GMshFileV2, GMshFileV4 > GmshFileVariant
Definition: gmsh_reader.h:74
std::unique_ptr< mesh::MeshFactory > mesh_factory_
Definition: gmsh_reader.h:180
Interface class representing a topological entity in a cellular complex
Definition: entity.h:39
lf::base::size_type size_type
Mesh input (from file) and output (in various formats) facilities.
Definition: gmsh_file_v2.cc:35
GMshFileV2 readGmshFileV2(std::string::const_iterator begin, std::string::const_iterator end, const std::string &version, bool is_binary, int size_t_size, int one, const std::string &filename)
Read a *.msh file from disk and copy it's contents into the MshFile Datastructure.
std::variant< GMshFileV2, GMshFileV4 > ReadGmshFile(const std::string &filename)
Definition: gmsh_reader.cc:642
GMshFileV4 ReadGmshFileV4(std::string::const_iterator begin, std::string::const_iterator end, const std::string &version, bool is_binary, int size_t_size, int one, const std::string &filename)
Read a GmshFile with format 4 and return it as an in-memory struct.
int DimOf(GMshFileV2::ElementType et)
Dimension of the GmshElement type.
base::RefEl RefElOf(GMshFileV2::ElementType et)
Reference element type of a GmshElementType.
A representation of a .msh file (V2) in a c++ data structure.
Definition: gmsh_file_v2.h:18
std::vector< PhysicalEntity > PhysicalEntities
A list of all Physical entities that have a name.
Definition: gmsh_file_v2.h:62
std::vector< Element > Elements
A list of all Elements (Points,Lines,Surfaces or Volumes) present in the *.msh file.
Definition: gmsh_file_v2.h:196
std::vector< std::pair< size_type, Eigen::Vector3d > > Nodes
The nodes that make up this mesh.
Definition: gmsh_file_v2.h:72
std::vector< PeriodicEntity > Periodic
Definition: gmsh_file_v2.h:228
std::vector< ElementBlock > element_blocks
A list of all Elements (Points,Lines,Surfaces or Volumes) present in the *.msh file organized in bloc...
Definition: gmsh_file_v4.h:437
std::size_t min_node_tag
Smallest node tag that exists.
Definition: gmsh_file_v4.h:302
std::size_t max_node_tag
biggest node tag that exists
Definition: gmsh_file_v4.h:305
std::vector< NodeBlock > node_blocks
The nodes that make up this mesh organized in blocks.
Definition: gmsh_file_v4.h:311
std::tuple< std::vector< PartitionedPointEntity >, std::vector< PartitionedEntity >, std::vector< PartitionedEntity >, std::vector< PartitionedEntity > > partitioned_entities
a list of partitioned entities in this mesh.
Definition: gmsh_file_v4.h:258
std::size_t num_partitions
total number of mesh partitions
Definition: gmsh_file_v4.h:231
A representation of a .msh file (V4) in a c++ data structure.
Definition: gmsh_file_v4.h:20
std::vector< PeriodicLink > periodic_links
Definition: gmsh_file_v4.h:481
Elements elements
Information about all mesh elements in this file.
Definition: gmsh_file_v4.h:443
PartitionedEntities partitioned_entities
Information about partitioned entities (in case the mesh has been partitioned)
Definition: gmsh_file_v4.h:265
std::vector< PhysicalName > physical_names
A list of all Physical entities that have a name.
Definition: gmsh_file_v4.h:60
std::tuple< std::vector< PointEntity >, std::vector< Entity >, std::vector< Entity >, std::vector< Entity > > entities
the (Gmsh-) entities present in this mesh. An entity typically corresponds to a geometrical object sp...
Definition: gmsh_file_v4.h:125