1#include "gmsh_reader.h"
3#include <lf/geometry/geometry.h>
5#include <boost/fusion/adapted/std_tuple.hpp>
6#include <boost/spirit/include/qi.hpp>
7#include <boost/spirit/include/qi_string.hpp>
10#include "eigen_fusion_adapter.h"
20 return std::find(physical_entities.begin(), physical_entities.end(),
21 physical_entity_nr) != physical_entities.end();
26 : mesh_factory_(std::move(factory)) {
27 std::visit([
this](
const auto& mf) {
InitGmshFile(mf); }, msh_file);
31 const std::string& filename)
36 LF_ASSERT_MSG(!name.empty(),
"name is empty");
37 auto [begin, end] =
name_2_nr_.equal_range(name);
44 if (codim ==
dim_t(-1) || codim == result.second.second) {
45 return result.second.first;
48 if (codim ==
dim_t(-1)) {
50 "There are multiple physical entities with the name " + name +
51 ", please specify also the codimension.");
53 if (result.second.second == codim) {
54 return result.second.first;
56 while (begin->second.second != codim && begin != end) {
59 if (begin->second.second == codim) {
60 return begin->second.first;
64 "' and codimension=" + std::to_string(codim) +
70 auto [begin, end] =
nr_2_name_.equal_range(number);
73 std::to_string(number) +
" not found.");
78 if (codim ==
dim_t(-1) || result.second.second == codim) {
79 return result.second.first;
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");
87 if (result.second.second == codim) {
88 return result.second.first;
90 while (begin->second.second != codim && begin != end) {
93 if (begin->second.second == codim) {
94 return begin->second.first;
98 "Physical entity with number=" + std::to_string(number) +
99 ", codim=" + std::to_string(codim) +
" not found.");
104 std::vector<std::pair<size_type, std::string>> result;
106 if (p.second.second != codim) {
109 result.emplace_back(p.first, p.second.first);
126 dim_mesh >= 2 && dim_mesh <= 3 && dim_world >= 2 && dim_world <= 3,
127 "GmshReader supports only 2D and 3D meshes.");
136 std::vector<bool> is_main_node(msh_file.
Nodes.size(),
false);
141 std::vector<std::vector<std::vector<size_type>>> mi2gi(dim_mesh + 1);
145 std::vector<size_type> num_entities(
mesh_factory_->DimMesh() + 1, 0);
147 for (
const auto& e : msh_file.
Elements) {
148 LF_ASSERT_MSG(
DimOf(e.Type) <= dim_mesh,
149 "mesh_factory->DimMesh() = "
151 <<
", but msh-file contains entities with dimension "
154 ++num_entities[
DimOf(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);
164 is_main_node[node_number] =
true;
169 for (
dim_t c = 0; c <= dim_mesh; ++c) {
170 mi2gi[c].reserve(num_entities[dim_mesh - c]);
173 LF_ASSERT_MSG(num_entities[dim_mesh] > 0,
174 "MshFile contains no elements with dimension " << dim_mesh);
182 std::vector<size_type> gi2mi;
183 gi2mi.resize(is_main_node.size(), -1);
186 std::vector<size_type> gi2i;
187 gi2i.resize(is_main_node.size(), -1);
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);
196 if (is_main_node.size() <= n.first || !is_main_node[n.first]) {
201 if (dim_world == 2) {
204 "In a 2D GmshMesh, the z-coordinate of every node must be zero");
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) {
224 mi2gi[codim].back().push_back(end);
230 auto mesh_index = gi2mi[end_element.NodeNumbers[0]];
233 if (mesh_index != std::numeric_limits<unsigned int>::max()) {
236 if (mi2gi[dim_mesh].size() <= mesh_index) {
237 mi2gi[dim_mesh].resize(mesh_index + 1);
239 mi2gi[dim_mesh][mesh_index].push_back(end);
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) {
247 msh_file.
Nodes[gi2i[end_element.NodeNumbers[i]]].second;
248 if (dim_world == 2) {
249 node_coords.col(i) = node_coord.topRows(2);
251 node_coords.col(i) = node_coord;
254 std::unique_ptr<geometry::Geometry> geom;
256 switch (end_element.Type) {
259 geom = std::make_unique<geometry::SegmentO1>(node_coords);
263 geom = std::make_unique<geometry::SegmentO2>(node_coords);
267 geom = std::make_unique<geometry::TriaO1>(node_coords);
271 geom = std::make_unique<geometry::TriaO2>(node_coords);
275 geom = std::make_unique<geometry::QuadO1>(node_coords);
279 geom = std::make_unique<geometry::QuadO2>(node_coords);
283 geom = std::make_unique<geometry::QuadO2>(node_coords.leftCols(8));
286 LF_VERIFY_MSG(
false,
"Gmsh element type "
288 <<
" not (yet) supported by GmshReader.");
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]];
295 mesh_factory_->AddEntity(ref_el, main_nodes, std::move(geom));
296 mi2gi[codim].emplace_back(std::vector{
static_cast<unsigned int>(end)});
307 mesh::utils::make_AllCodimMeshDataSet<std::vector<size_type>>(
mesh_);
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()) {
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);
333 std::pair{pe.Name, std::pair{pe.Number, dim_mesh - pe.Dimension}});
335 std::pair{pe.Number, std::pair{pe.Name, dim_mesh - pe.Dimension}});
353 dim_mesh >= 2 && dim_mesh <= 3 && dim_world >= 2 && dim_world <= 3,
354 "GmshReader supports only 2D and 3D meshes.");
366 std::vector<bool> is_main_node(max_node_tag - min_node_tag + 1,
false);
371 std::vector<std::vector<std::vector<std::size_t>>> mi2gi(dim_mesh + 1);
375 std::vector<size_type> num_entities(
mesh_factory_->DimMesh() + 1, 0);
378 LF_ASSERT_MSG(eb.dimension <= dim_mesh,
379 "mesh_factory->DimMesh() = "
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) <<
")");
388 num_entities[eb.dimension] += eb.elements.size();
390 if (eb.dimension == dim_mesh) {
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);
400 is_main_node[node_tag - min_node_tag] =
true;
406 for (
dim_t c = 0; c <= dim_mesh; ++c) {
407 mi2gi[c].reserve(num_entities[dim_mesh - c]);
410 LF_ASSERT_MSG(num_entities[dim_mesh] > 0,
411 "MshFile contains no elements with dimension " << dim_mesh);
420 std::vector<size_type> gi2mi;
421 gi2mi.resize(is_main_node.size(), -1);
425 std::vector<std::pair<size_type, size_type>> gi2i;
426 gi2i.resize(is_main_node.size(), {-1, -1});
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");
435 gi2i[n.first - min_node_tag] = std::make_pair(j, k);
437 if (!is_main_node[n.first - min_node_tag]) {
442 if (dim_world == 2) {
445 "In a 2D GmshMesh, the z-coordinate of every node must be zero");
450 gi2mi[n.first - min_node_tag] = mi;
460 std::size_t begin = 0;
461 auto ref_el =
RefElOf(element_block.element_type);
462 auto codim = dim_mesh - ref_el.Dimension();
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];
468 if (begin_element.second == end_element.second && begin != end) {
470 mi2gi[codim].back().emplace_back(ebi);
479 auto mesh_index = gi2mi[end_element.second[0] - min_node_tag];
480 if (mesh_index != std::numeric_limits<unsigned int>::max()) {
483 if (mi2gi[dim_mesh].size() <= mesh_index) {
484 mi2gi[dim_mesh].resize(mesh_index + 1);
486 mi2gi[dim_mesh][mesh_index].emplace_back(ebi);
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];
495 .nodes[gi2i_e.second]
497 if (dim_world == 2) {
498 node_coords.col(i) = node_coord.topRows(2);
500 node_coords.col(i) = node_coord;
503 std::unique_ptr<geometry::Geometry> geom;
505 switch (element_block.element_type) {
508 geom = std::make_unique<geometry::SegmentO1>(node_coords);
512 geom = std::make_unique<geometry::SegmentO2>(node_coords);
516 geom = std::make_unique<geometry::TriaO1>(node_coords);
520 geom = std::make_unique<geometry::TriaO2>(node_coords);
524 geom = std::make_unique<geometry::QuadO1>(node_coords);
528 geom = std::make_unique<geometry::QuadO2>(node_coords);
532 geom = std::make_unique<geometry::QuadO2>(node_coords.leftCols(8));
535 LF_VERIFY_MSG(
false,
"Gmsh element type "
536 << element_block.element_type
537 <<
" not (yet) supported by GmshReader.");
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];
544 mesh_factory_->AddEntity(ref_el, main_nodes, std::move(geom));
545 mi2gi[codim].push_back(std::vector<std::size_t>{ebi});
560 std::array<std::vector<std::vector<int>>, 4> gmei2gmphi;
562 auto makeMap = [](std::vector<std::vector<int>>& result,
auto& entities) {
564 int max_entity_tag = 0;
565 for (
auto& e : entities) {
566 max_entity_tag = std::max(max_entity_tag, e.tag);
568 result.resize(max_entity_tag + 1);
571 for (
auto& e : entities) {
572 result[e.tag] = e.physical_tags;
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));
581 makeMap(gmei2gmphi[0], std::get<3>(msh_file.
entities));
584 makeMap(gmei2gmphi[dim_mesh],
586 makeMap(gmei2gmphi[dim_mesh - 1],
588 makeMap(gmei2gmphi[dim_mesh - 2],
591 makeMap(gmei2gmphi[0],
599 mesh::utils::make_AllCodimMeshDataSet<std::vector<size_type>>(
mesh_);
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()) {
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 =
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));
629 pn.name, std::pair{pn.physical_tag, dim_mesh - pn.dimension}});
631 std::pair{pn.name, dim_mesh - pn.dimension}});
642std::variant<GMshFileV2, GMshFileV4>
ReadGmshFile(
const std::string& filename) {
645 std::ifstream in(filename, std::ios_base::in | std::ios_base::binary);
647 std::string error(
"Could not open file ");
653 in.unsetf(std::ios::skipws);
654 std::copy(std::istream_iterator<char>(in), std::istream_iterator<char>(),
655 std::back_inserter(storage));
660 auto iter = storage.cbegin();
661 auto end = storage.cend();
663 namespace qi = boost::spirit::qi;
664 namespace ascii = boost::spirit::ascii;
667 std::tuple<std::string, bool, int, int> header;
668 qi::rule<
decltype(iter), std::string()> version_parser;
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");
680 succesful = qi::phrase_parse(iter, end, header_parser, ascii::space, header);
682 LF_VERIFY_MSG(succesful,
"Could not read header of file " << filename);
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);
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);
692 LF_VERIFY_MSG(
false,
"GmshFiles with Version " << std::get<0>(header)
693 <<
" are not yet supported.");
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.
static constexpr RefEl kPoint()
Returns the (0-dimensional) reference point.
static constexpr RefEl kTria()
Returns the reference triangle.
static constexpr RefEl kQuad()
Returns the reference quadrilateral.
Reads a Gmsh *.msh file into a mesh::MeshFactory and provides a link between mesh::Entity objects and...
void InitGmshFile(const GMshFileV2 &msh_file)
std::shared_ptr< mesh::Mesh > mesh_
The underlying grid created by the grid factory.
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
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
std::unique_ptr< mesh::MeshFactory > mesh_factory_
Interface class representing a topological entity in a cellular complex
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...