Wildmeshing Toolkit
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 
18 namespace wmtk::io {
19 MshReader::MshReader() = default;
20 MshReader::~MshReader() = default;
21 
22 template <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 
38 std::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
54 std::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 
66 std::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 
78 const 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 
88 const 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 
98 size_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 
127 template <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 
165 template <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 }
195 template <int DIM>
196 auto 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 
321  const std::optional<std::vector<std::vector<std::string>>>& extra_attributes_opt)
322  -> std::shared_ptr<Mesh>
323 {
324  std::shared_ptr<Mesh> res;
325  switch (get_mesh_dimension()) {
326  case 3: res = generateT<3>(); break;
327  case 2: res = generateT<2>(); break;
328  case 1: res = generateT<1>(); break;
329  default:
330  case 0: res = std::make_shared<PointMesh>();
331  }
332  assert(V.size() > 0);
333 
334  if (extra_attributes_opt.has_value()) {
335  const auto& extra_attributes = extra_attributes_opt.value();
336  // save all valid scalar attributes on tets
337  std::set<std::string> valid_attr_names;
338  for (const auto& data : m_spec.element_data) {
339  // validness: int_tags[2] stores the dimension of an element_data, which should
340  // match the size of tets
341  if (data.header.int_tags[2] != get_num_simplex_elements(3)) {
342  continue;
343  }
344  valid_attr_names.insert(data.header.string_tags.front());
345  }
346  for (size_t dim = 0; dim < extra_attributes.size(); ++dim) {
347  const auto& attr_names = extra_attributes[dim];
348  for (const std::string& attr : attr_names) {
349  if (valid_attr_names.count(attr) == 0) {
350  log_and_throw_error("Attribute " + attr + " does not exist in the msh file.");
351  }
352  extract_element_attribute(*res, attr, get_primitive_type_from_id(dim));
353  }
354  }
355  }
357  return res;
358 }
359 
360 template <int DIM>
361 auto MshReader::generateT() -> std::shared_ptr<wmtk::utils::mesh_type_from_dimension_t<DIM>>
362 {
363  extract<DIM>();
364  validate<DIM>();
365  return construct<DIM>();
366 }
367 
368 template <int DIM>
370 {}
371 
372 template <>
373 void MshReader::validate<3>()
374 {
375  assert(V.cols() == 3);
376  exactinit();
377  {
378  // check inversion
379 
380  auto Vs = V(S.row(0), Eigen::all);
381 
382 
383  if (wmtk::utils::wmtk_orient3d(Vs.transpose()) < 0) {
384  // swap col 0 and 1 of S
385  S.col(0).swap(S.col(1));
386  wmtk::logger().info(
387  "Input tet orientation is inverted, swapping col 0 and 1 of TV matirx.");
388  }
389  }
390 
391  {
392  // check consistency
393  for (int64_t i = 0; i < S.rows(); i++) {
394  auto row = S.row(i);
395  auto Vs = V(row, Eigen::all);
396  auto orient = wmtk::utils::wmtk_orient3d(Vs.transpose());
397 
398  if (orient < 0) {
399  std::swap(row.x(), row.y());
400  // log_and_throw_error("Input tet orientation is inconsistent.");
401  } else if (orient == 0) {
402  log_and_throw_error("Input tet is degenerated.");
403  }
404  }
405  }
406 
407  {
408  // check consistency
409  for (int64_t i = 0; i < S.rows(); i++) {
410  auto row = S.row(i);
411  auto Vs = V(row, Eigen::all);
412  auto orient = wmtk::utils::wmtk_orient3d(Vs.transpose());
413 
414  if (orient < 0) {
415  auto a = Vs.row(0);
416  auto b = Vs.row(1);
417  auto tmp = a.eval();
418  a = b;
419  b = a;
420  // log_and_throw_error("Input tet orientation is inconsistent.");
421  } else if (orient == 0) {
422  log_and_throw_error("Input tet is degenerated.");
423  }
424  }
425  }
426 }
427 } // namespace wmtk::io
attribute::MeshAttributeHandle register_attribute(const std::string &name, PrimitiveType type, int64_t size, bool replace=false, T default_value=T(0))
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition: Mesh.cpp:18
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
PrimitiveType top_simplex_type() const
Definition: Mesh.hpp:996
const mshio::ElementBlock * get_simplex_element_block(int DIM) const
Definition: MshReader.cpp:88
auto construct() -> std::shared_ptr< wmtk::utils::mesh_type_from_dimension_t< DIM >>
Definition: MshReader.cpp:196
size_t get_num_simplex_elements(int DIM) const
Definition: MshReader.cpp:108
mshio::MshSpec m_spec
Definition: MshReader.hpp:87
int get_mesh_dimension() const
Definition: MshReader.cpp:28
int get_embedded_dimension() const
Definition: MshReader.cpp:119
static const int64_t AUTO_EMBEDDED_DIMENSION
Definition: MshReader.hpp:94
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
auto generateT() -> std::shared_ptr< wmtk::utils::mesh_type_from_dimension_t< DIM >>
Definition: MshReader.cpp:361
Eigen::MatrixXd V
Definition: MshReader.hpp:91
void extract_simplex_elements()
Definition: MshReader.cpp:166
std::shared_ptr< Mesh > generate(const std::optional< std::vector< std::vector< std::string >>> &extra_attributes={})
Definition: MshReader.cpp:320
void extract_element_attribute(wmtk::Mesh &m, const std::string &attr_name, PrimitiveType pt)
Definition: MshReader.cpp:282
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
void exactinit()
Definition: orient.cpp:28
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