Wildmeshing Toolkit
Loading...
Searching...
No Matches
MshReader.cpp
Go to the documentation of this file.
1#include "MshReader.hpp"
3
4#include <set>
5
6#include <wmtk/EdgeMesh.hpp>
7#include <wmtk/PointMesh.hpp>
8#include <wmtk/TetMesh.hpp>
9#include <wmtk/TriMesh.hpp>
11#include <wmtk/utils/Logger.hpp>
13#include <wmtk/utils/orient.hpp>
14
15#include "mshio/MshSpec.h"
16#include "predicates.h"
17
18namespace wmtk::io {
19MshReader::MshReader() = default;
20MshReader::~MshReader() = default;
21
22template <int DIM>
24{
25 extract_vertices<DIM>();
26 extract_simplex_elements<DIM>();
27}
29{
30 for (int dim = 3; dim > 0; --dim) {
31 if (get_num_simplex_elements(dim) > 0) {
32 return dim;
33 }
34 }
35 return 0;
36}
37
38std::shared_ptr<Mesh> MshReader::read(
39 const std::filesystem::path& filename,
40 const bool ignore_z_if_zero,
41 const std::vector<std::string>& attrs)
42{
43 m_spec = mshio::load_msh(filename.string());
44 m_embedded_dimension = ignore_z_if_zero ? AUTO_EMBEDDED_DIMENSION : 3;
45
46 std::vector<std::vector<std::string>> split_attrs(get_mesh_dimension() + 1);
47
48 split_attrs.back() = attrs;
49
50 return generate(split_attrs);
51}
52// reading from an msh file while preserve the specified attributes, only supports valid scalar
53// attributes on tets in a tetmesh
54std::shared_ptr<Mesh> MshReader::read(
55 const std::filesystem::path& filename,
56 const int64_t embedded_dimension,
57 const std::vector<std::vector<std::string>>& extra_attributes)
58{
59 m_spec = mshio::load_msh(filename.string());
60 m_embedded_dimension = embedded_dimension == -1 ? get_embedded_dimension() : embedded_dimension;
61
62
63 return generate(extra_attributes);
64}
65
66std::shared_ptr<Mesh> MshReader::read(
67 const std::filesystem::path& filename,
68 const int64_t embedded_dimension)
69{
70 m_spec = mshio::load_msh(filename.string());
71 m_embedded_dimension = embedded_dimension == -1 ? get_embedded_dimension() : embedded_dimension;
72
73
74 return generate();
75}
76
77
78const mshio::NodeBlock* MshReader::get_vertex_block(int DIM) const
79{
80 for (const auto& block : m_spec.nodes.entity_blocks) {
81 if (block.entity_dim == DIM) {
82 return &block;
83 }
84 }
85 return nullptr;
86}
87
88const mshio::ElementBlock* MshReader::get_simplex_element_block(int DIM) const
89{
90 for (const auto& block : m_spec.elements.entity_blocks) {
91 if (block.entity_dim == DIM) {
92 return &block;
93 }
94 }
95 return nullptr;
96}
97
98size_t MshReader::get_num_vertices(int DIM) const
99{
100 const auto* block = get_vertex_block(DIM);
101 if (block != nullptr) {
102 return block->num_nodes_in_block;
103 } else {
104 return 0;
105 }
106}
107
109{
110 const auto* block = get_simplex_element_block(DIM);
111 if (block != nullptr) {
112 return block->num_elements_in_block;
113 } else {
114 return 0;
115 }
116}
117
118
120{
121 const mshio::NodeBlock* block = get_vertex_block(get_mesh_dimension());
122 assert(block != nullptr);
123
124 return block->entity_dim;
125}
126
127template <int DIM>
129{
130 const mshio::NodeBlock* block = get_vertex_block(DIM);
131 assert(block != nullptr);
132
133 const size_t num_vertices = block->num_nodes_in_block;
134 const auto embedded_dimension =
136 V.resize(num_vertices, embedded_dimension);
137
138 // previously this code returned if false, but later on there is an assumption the vertex block
139 // is filled out so it is now an assertion
140 assert(num_vertices > 0);
141
142 assert(embedded_dimension <= 3);
143 assert(embedded_dimension > 0);
144
146 using ConstMapType = typename Vector::ConstMapType;
147
148 const size_t tag_offset = block->tags.front();
149 for (size_t i = 0; i < num_vertices; i++) {
150 size_t tag = block->tags[i] - tag_offset;
151 ConstMapType vec(block->data.data() + i * 3);
152 V.row(tag) = vec.head(embedded_dimension).transpose();
153 }
154
156 const double max = V.col(2).array().maxCoeff();
157 const double min = V.col(2).array().minCoeff();
158
159 if (min == 0 && max == 0) {
160 V = V.leftCols(2).eval();
161 }
162 }
163}
164
165template <int DIM>
167{
168 const mshio::NodeBlock* vertex_block = get_vertex_block(DIM);
169 const mshio::ElementBlock* element_block = get_simplex_element_block(DIM);
170 if (element_block == nullptr) return;
171 assert(vertex_block != nullptr);
172
173 const size_t num_elements = element_block->num_elements_in_block;
174 if (num_elements == 0) return;
175 assert(vertex_block->num_nodes_in_block != 0);
176
177 S.resize(num_elements, DIM + 1);
178
179 const size_t vert_tag_offset = vertex_block->tags.front();
180 const size_t elem_tag_offset = element_block->data.front();
181 for (size_t i = 0; i < num_elements; i++) {
182 const size_t tag = element_block->data[i * (DIM + 2)] - elem_tag_offset;
183 assert(tag < num_elements);
184 const size_t* element = element_block->data.data() + i * (DIM + 2) + 1;
185
187 using ConstMapType = typename Vector::ConstMapType;
188
189 ConstMapType element_vec(element);
190 const auto vertex_tag_offset_vec = Vector::Constant(vert_tag_offset);
191
192 S.row(tag) = (element_vec - vertex_tag_offset_vec).transpose().template cast<int64_t>();
193 }
194}
195template <int DIM>
196auto MshReader::construct() -> std::shared_ptr<wmtk::utils::mesh_type_from_dimension_t<DIM>>
197{
199 auto ret = std::make_shared<MeshType>();
200 ret->initialize(S);
201 return ret;
202}
203
204// Old code to read attribute, we might need it in the future
205// std::vector<std::string> MshReader::get_vertex_attribute_names(int DIM) const
206//{
207// std::vector<std::string> attr_names;
208// attr_names.reserve(m_spec.node_data.size());
209// for (const auto& data : m_spec.node_data) {
210// const auto& int_tags = data.header.int_tags;
211// if (int_tags.size() >= 5 && int_tags[4] == DIM) {
212// attr_names.push_back(data.header.string_tags.front());
213// }
214// }
215// return attr_names;
216//}
217
218// std::vector<std::string> MshReader::get_element_attribute_names(int DIM) const
219//{
220// std::vector<std::string> attr_names;
221// attr_names.reserve(m_spec.element_data.size());
222// for (const auto& data : m_spec.element_data) {
223// const auto& int_tags = data.header.int_tags;
224// if (int_tags.size() >= 5 && int_tags[4] == DIM) {
225// attr_names.push_back(data.header.string_tags.front());
226// }
227// }
228// return attr_names;
229// }
230
231// void MshReader::extract_vertex_attribute(
232// std::shared_ptr<wmtk::TetMesh> m,
233// const std::string& attr_name,
234// int DIM)
235//{
236// const auto* vertex_block = get_vertex_block(DIM);
237// const size_t tag_offset = vertex_block->tags.front();
238// for (const auto& data : m_spec.node_data) {
239// if (data.header.string_tags.front() != attr_name) continue;
240// if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
241// throw std::runtime_error("Attribute " + attr_name + " is of the wrong DIM.");
242// }
243//
244// // for (const auto& entry : data.entries) {
245// // const size_t tag = entry.tag - tag_offset;
246// // assert(tag < vertex_block->num_nodes_in_block);
247// // set_attr(tag, entry.data);
248// // }
249//
250// // figure out what data type the attribute stores
251// // ...
252//
253// for (const auto& entry : data.entries) {
254// // auto attr_handle = m->register_attribute<double>(
255// // std::string(attr_name) + std::to_string(index),
256// // PrimitiveType::Tetrahedron,
257// // m->get_all(m->top_simplex_type()).size());
258// // auto acc = m->create_accessor<double>(attr_handle);
259// const size_t tag = entry.tag - tag_offset;
260// assert(tag < vertex_block->num_nodes_in_block);
261// // set_attr(tag, entry.data);
262//
263// Eigen::VectorX<long> eigendata;
264// eigendata.resize(entry.data.size());
265// for (int i = 0; i < entry.data.size(); ++i) {
266// eigendata.row(i) << entry.data[i];
267// }
268// // wmtk::attribute::MeshAttributeHandle tag_handle =
269// // wmtk::mesh_utils::set_matrix_attribute(
270// // eigendata,
271// // std::string(attr_name) + std::to_string(index),
272// // PrimitiveType::Tetrahedron,
273// // *(m.get()));
274// // for (const wmtk::Tuple& tup : m->get_all(m->top_simplex_type())) {
275// // acc.scalar_attribute(tup) = entry.data;
276// // }
277// }
278// }
279//}
280
281// for now, this function only supports reading a single attribute on the tet mesh
283 Mesh& m,
284 const std::string& attr_name,
285 PrimitiveType primitive_type)
286{
287 assert(primitive_type == m.top_simplex_type());
288 const int64_t primitive_dimension = get_primitive_type_id(primitive_type);
289 const mshio::ElementBlock* element_block = get_simplex_element_block(primitive_dimension);
290 const auto tuples = m.get_all(m.top_simplex_type());
291 const size_t tag_offset = element_block->data.front();
292
293 for (const auto& data : m_spec.element_data) {
294 if (data.header.string_tags.front() != attr_name) {
295 continue;
296 }
297 assert(data.entries.size() == tuples.size());
298
299 const int64_t attr_dim = data.header.int_tags[1];
300
301 auto tag_handle = m.register_attribute<double>(attr_name, primitive_type, attr_dim);
302 auto acc = m.create_accessor<double>(tag_handle);
303
304 for (size_t i = 0; i < tuples.size(); ++i) {
305 const auto& entry = data.entries[i];
306 const size_t tag = entry.tag - tag_offset;
307 assert(tag < element_block->num_elements_in_block);
308 assert(tag == i);
309 if (attr_dim == 1) {
310 acc.scalar_attribute(tuples[i]) = entry.data[0];
311 } else {
312 acc.vector_attribute(tuples[i]) =
313 Eigen::Map<const Eigen::VectorXd>(entry.data.data(), entry.data.size());
314 }
315 }
316 return;
317 }
318}
319
320auto MshReader::generate(const std::optional<std::vector<std::vector<std::string>>>&
321 extra_attributes_opt) -> std::shared_ptr<Mesh>
322{
323 std::shared_ptr<Mesh> res;
324 switch (get_mesh_dimension()) {
325 case 3: res = generateT<3>(); break;
326 case 2: res = generateT<2>(); break;
327 case 1: res = generateT<1>(); break;
328 default:
329 case 0: res = std::make_shared<PointMesh>();
330 }
331 assert(V.size() > 0);
332
333 if (extra_attributes_opt.has_value()) {
334 const auto& extra_attributes = extra_attributes_opt.value();
335 // save all valid scalar attributes on tets
336 std::set<std::string> valid_attr_names;
337 for (const auto& data : m_spec.element_data) {
338 // validness: int_tags[2] stores the dimension of an element_data, which should
339 // match the size of tets
340 if (data.header.int_tags[2] != get_num_simplex_elements(3)) {
341 continue;
342 }
343 valid_attr_names.insert(data.header.string_tags.front());
344 }
345 for (size_t dim = 0; dim < extra_attributes.size(); ++dim) {
346 const auto& attr_names = extra_attributes[dim];
347 for (const std::string& attr : attr_names) {
348 if (valid_attr_names.count(attr) == 0) {
349 log_and_throw_error("Attribute " + attr + " does not exist in the msh file.");
350 }
351 extract_element_attribute(*res, attr, get_primitive_type_from_id(dim));
352 }
353 }
354 }
356 return res;
357}
358
359template <int DIM>
360auto MshReader::generateT() -> std::shared_ptr<wmtk::utils::mesh_type_from_dimension_t<DIM>>
361{
362 extract<DIM>();
363 validate<DIM>();
364 return construct<DIM>();
365}
366
367template <int DIM>
370
371template <>
372void MshReader::validate<3>()
373{
374 assert(V.cols() == 3);
375 exactinit();
376 {
377 // check inversion
378
379 auto Vs = V(S.row(0), Eigen::all);
380
381
382 if (wmtk::utils::wmtk_orient3d(Vs.transpose()) < 0) {
383 // swap col 0 and 1 of S
384 S.col(0).swap(S.col(1));
385 wmtk::logger().info(
386 "Input tet orientation is inverted, swapping col 0 and 1 of TV matrix.");
387 }
388 }
389
390 {
391 // check consistency
392 for (int64_t i = 0; i < S.rows(); i++) {
393 auto row = S.row(i);
394 auto Vs = V(row, Eigen::all);
395 auto orient = wmtk::utils::wmtk_orient3d(Vs.transpose());
396
397 if (orient < 0) {
398 std::swap(row.x(), row.y());
399 // log_and_throw_error("Input tet orientation is inconsistent.");
400 } else if (orient == 0) {
401 log_and_throw_error("Input tet is degenerated.");
402 }
403 }
404 }
405
406 {
407 // check consistency
408 for (int64_t i = 0; i < S.rows(); i++) {
409 auto row = S.row(i);
410 auto Vs = V(row, Eigen::all);
411 auto orient = wmtk::utils::wmtk_orient3d(Vs.transpose());
412
413 if (orient < 0) {
414 auto a = Vs.row(0);
415 auto b = Vs.row(1);
416 auto tmp = a.eval();
417 a = b;
418 b = a;
419 // log_and_throw_error("Input tet orientation is inconsistent.");
420 } else if (orient == 0) {
421 log_and_throw_error("Input tet is degenerated.");
422 }
423 }
424 }
425}
426} // namespace wmtk::io
attribute::MeshAttributeHandle register_attribute(const std::string &name, PrimitiveType type, int64_t size, bool replace=false, T default_value=T(0))
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition Mesh.cpp:18
PrimitiveType top_simplex_type() const
Definition Mesh.hpp:982
const mshio::ElementBlock * get_simplex_element_block(int DIM) const
Definition MshReader.cpp:88
size_t get_num_simplex_elements(int DIM) const
mshio::MshSpec m_spec
Definition MshReader.hpp:87
int get_mesh_dimension() const
Definition MshReader.cpp:28
auto construct() -> std::shared_ptr< wmtk::utils::mesh_type_from_dimension_t< DIM > >
int get_embedded_dimension() const
static const int64_t AUTO_EMBEDDED_DIMENSION
Definition MshReader.hpp:94
auto generateT() -> std::shared_ptr< wmtk::utils::mesh_type_from_dimension_t< DIM > >
std::shared_ptr< Mesh > generate(const std::optional< std::vector< std::vector< std::string > > > &extra_attributes={})
std::shared_ptr< Mesh > read(const std::filesystem::path &filename, const bool ignore_z_if_zero, const std::vector< std::string > &extra_facet_attributes={})
Definition MshReader.cpp:38
int64_t m_embedded_dimension
Definition MshReader.hpp:88
size_t get_num_vertices(int DIM) const
Definition MshReader.cpp:98
const mshio::NodeBlock * get_vertex_block(int DIM) const
Definition MshReader.cpp:78
Eigen::MatrixXd V
Definition MshReader.hpp:91
void extract_simplex_elements()
void extract_element_attribute(wmtk::Mesh &m, const std::string &attr_name, PrimitiveType pt)
attribute::MeshAttributeHandle set_matrix_attribute(const Mat &data, const std::string &name, const PrimitiveType &type, Mesh &mesh)
Definition mesh_utils.hpp:9
typename mesh_type_from_dimension< DIM >::type mesh_type_from_dimension_t
int wmtk_orient3d(const Eigen::Ref< const Eigen::Vector3< Rational > > &p0, const Eigen::Ref< const Eigen::Vector3< Rational > > &p1, const Eigen::Ref< const Eigen::Vector3< Rational > > &p2, const Eigen::Ref< const Eigen::Vector3< Rational > > &p3)
Definition orient.cpp:75
constexpr PrimitiveType get_primitive_type_from_id(int8_t id)
Get the primitive type corresponding to its unique integer id.
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:101
Eigen::Matrix< T, R, 1 > Vector
Definition Types.hpp:17
constexpr int8_t get_primitive_type_id(PrimitiveType t)
Get a unique integer id corresponding to each primitive type.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:58