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