25 template <
typename Fn>
26 void add_edge_vertices(
size_t num_vertices,
const Fn& get_vertex_cb)
28 add_vertices<1>(num_vertices, get_vertex_cb);
34 template <
typename Fn>
37 add_vertices<2>(num_vertices, get_vertex_cb);
40 template <
typename Fn>
41 void add_tet_vertices(
size_t num_vertices,
const Fn& get_vertex_cb)
43 add_vertices<3>(num_vertices, get_vertex_cb);
46 void add_empty_vertices(
int dim)
48 if (dim < 1 || dim > 3) {
49 log_and_throw_error(
"Only 1,2,3D elements are supported!");
52 if (m_spec.nodes.entity_blocks.empty()) {
53 log_and_throw_error(
"First vertex block cannot be empty.");
56 mshio::NodeBlock block;
57 block.num_nodes_in_block = 0;
58 block.entity_dim = dim;
59 block.entity_tag = (int)m_spec.nodes.num_entity_blocks + 1;
61 m_spec.nodes.num_entity_blocks += 1;
62 m_spec.nodes.entity_blocks.push_back(std::move(block));
65 void add_edge_vertices() { add_empty_vertices(1); }
67 void add_tet_vertices() { add_empty_vertices(3); }
69 template <
typename Fn>
70 void add_edges(
size_t num_edges,
const Fn& get_edge_cb)
72 add_simplex_elements<1>(num_edges, get_edge_cb);
83 template <
typename Fn>
84 void add_faces(
size_t num_faces,
const Fn& get_face_cb)
86 add_simplex_elements<2>(num_faces, get_face_cb);
89 template <
typename Fn>
90 void add_tets(
size_t num_tets,
const Fn& get_tet_cb)
92 add_simplex_elements<3>(num_tets, get_tet_cb);
95 template <
int NUM_FIELDS,
typename Fn>
96 void add_edge_vertex_attribute(
const std::string& name,
const Fn& get_attribute_cb)
98 add_vertex_attribute<NUM_FIELDS, 1>(name, get_attribute_cb);
101 template <
int NUM_FIELDS,
typename Fn>
102 void add_face_vertex_attribute(
const std::string& name,
const Fn& get_attribute_cb)
104 add_vertex_attribute<NUM_FIELDS, 2>(name, get_attribute_cb);
107 template <
int NUM_FIELDS,
typename Fn>
108 void add_tet_vertex_attribute(
const std::string& name,
const Fn& get_attribute_cb)
110 add_vertex_attribute<NUM_FIELDS, 3>(name, get_attribute_cb);
113 template <
int NUM_FIELDS,
typename Fn>
114 void add_edge_attribute(
const std::string& name,
const Fn& get_attribute_cb)
116 add_element_attribute<NUM_FIELDS, 1>(name, get_attribute_cb);
119 template <
int NUM_FIELDS,
typename Fn>
120 void add_face_attribute(
const std::string& name,
const Fn& get_attribute_cb)
122 add_element_attribute<NUM_FIELDS, 2>(name, get_attribute_cb);
125 template <
int NUM_FIELDS,
typename Fn>
126 void add_tet_attribute(
const std::string& name,
const Fn& get_attribute_cb)
128 add_element_attribute<NUM_FIELDS, 3>(name, get_attribute_cb);
136 auto& entities = m_spec.entities;
138 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
139 const int tag = vertex_block.entity_tag;
140 const int dim = vertex_block.entity_dim;
142 double min_x = std::numeric_limits<double>::max();
143 double min_y = std::numeric_limits<double>::max();
144 double min_z = std::numeric_limits<double>::max();
145 double max_x = std::numeric_limits<double>::lowest();
146 double max_y = std::numeric_limits<double>::lowest();
147 double max_z = std::numeric_limits<double>::lowest();
148 for (
size_t i = 0; i < vertex_block.num_nodes_in_block; ++i) {
149 min_x = std::min(vertex_block.data[3 * i + 0], min_x);
150 min_y = std::min(vertex_block.data[3 * i + 1], min_y);
151 min_z = std::min(vertex_block.data[3 * i + 2], min_z);
152 max_x = std::max(vertex_block.data[3 * i + 0], max_x);
153 max_y = std::max(vertex_block.data[3 * i + 1], max_y);
154 max_z = std::max(vertex_block.data[3 * i + 2], max_z);
157 mshio::PhysicalGroup ph;
161 m_spec.physical_groups.emplace_back(ph);
165 mshio::PointEntity e{tag, min_x, min_y, min_z};
166 e.physical_group_tags.push_back(tag);
167 entities.points.push_back(e);
171 mshio::CurveEntity e{tag, min_x, min_y, min_z, max_x, max_y, max_z};
172 e.physical_group_tags.push_back(tag);
173 entities.curves.push_back(e);
177 mshio::SurfaceEntity e{tag, min_x, min_y, min_z, max_x, max_y, max_z};
178 e.physical_group_tags.push_back(tag);
179 entities.surfaces.push_back(e);
183 mshio::VolumeEntity e{tag, min_x, min_y, min_z, max_x, max_y, max_z};
184 e.physical_group_tags.push_back(tag);
185 entities.volumes.push_back(e);
188 default: log_and_throw_error(
"Entity of dim {} cannot be written to .msh.");
192 inline size_t get_num_edge_vertices()
const {
return get_num_vertices(1); }
194 inline size_t get_num_face_vertices()
const {
return get_num_vertices(2); }
196 inline size_t get_num_tet_vertices()
const {
return get_num_vertices(3); }
198 inline size_t get_num_edges()
const {
return get_num_simplex_elements(1); }
200 inline size_t get_num_faces()
const {
return get_num_simplex_elements(2); }
202 inline size_t get_num_tets()
const {
return get_num_simplex_elements(3); }
204 template <
typename Fn>
205 void extract_tet_vertices(Fn&& set_vertex_cb)
const
207 return extract_vertices(3, std::forward<Fn>(set_vertex_cb));
210 template <
typename Fn>
211 void extract_face_vertices(Fn&& set_vertex_cb)
const
213 return extract_vertices(2, std::forward<Fn>(set_vertex_cb));
216 template <
typename Fn>
217 void extract_edge_vertices(Fn&& set_vertex_cb)
const
219 extract_vertices(1, std::forward<Fn>(set_vertex_cb));
222 template <
typename Fn>
223 void extract_edges(Fn&& set_edge_cb)
const
225 extract_simplex_elements<1>(std::forward<Fn>(set_edge_cb));
228 template <
typename Fn>
229 void extract_faces(Fn&& set_face_cb)
const
231 extract_simplex_elements<2>(std::forward<Fn>(set_face_cb));
234 template <
typename Fn>
235 void extract_tets(Fn&& set_tet_cb)
const
237 extract_simplex_elements<3>(std::forward<Fn>(set_tet_cb));
240 std::vector<std::string> get_edge_vertex_attribute_names()
const
242 return get_vertex_attribute_names<1>();
245 std::vector<std::string> get_face_vertex_attribute_names()
const
247 return get_vertex_attribute_names<2>();
250 std::vector<std::string> get_tet_vertex_attribute_names()
const
252 return get_vertex_attribute_names<3>();
255 std::vector<std::string> get_edge_attribute_names()
const
257 return get_element_attribute_names<1>();
260 std::vector<std::string> get_face_attribute_names()
const
262 return get_element_attribute_names<2>();
265 std::vector<std::string> get_tet_attribute_names()
const
267 return get_element_attribute_names<3>();
270 template <
typename Fn>
271 void extract_edge_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
273 extract_vertex_attribute<1>(attr_name, std::forward<Fn>(set_attr));
276 template <
typename Fn>
277 void extract_face_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
279 extract_vertex_attribute<2>(attr_name, std::forward<Fn>(set_attr));
282 template <
typename Fn>
283 void extract_tet_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
285 extract_vertex_attribute<3>(attr_name, std::forward<Fn>(set_attr));
288 template <
typename Fn>
289 void extract_edge_attribute(
const std::string& attr_name, Fn&& set_attr)
291 extract_element_attribute<1>(attr_name, std::forward<Fn>(set_attr));
294 template <
typename Fn>
295 void extract_face_attribute(
const std::string& attr_name, Fn&& set_attr)
297 extract_element_attribute<2>(attr_name, std::forward<Fn>(set_attr));
300 template <
typename Fn>
301 void extract_tet_attribute(
const std::string& attr_name, Fn&& set_attr)
303 extract_element_attribute<3>(attr_name, std::forward<Fn>(set_attr));
311 for (
const mshio::PhysicalGroup& ph : m_spec.physical_groups) {
312 if (ph.name == name) {
319 void get_VF(
const int& dim,
const int& tag, MatrixXd& V, MatrixXi& F)
321 assert(dim > 0 && tag > 0);
323 const size_t n_V = get_num_vertices(dim, tag);
324 const size_t n_F = get_num_simplex_elements(dim, tag);
328 extract_vertices(dim, tag, [&V](
size_t i,
double x,
double y,
double z) {
329 V.row(i) = Vector3d(x, y, z);
333 F.resize(n_F, dim + 1);
336 extract_simplex_elements<1>(
337 [&F](
size_t i,
size_t v0,
size_t v1) { F.row(i) = Vector2i(v0, v1); });
340 extract_simplex_elements<2>([&F](
size_t i,
size_t v0,
size_t v1,
size_t v2) {
341 F.row(i) = Vector3i((
int)v0, (
int)v1, (
int)v2);
345 extract_simplex_elements<3>(
346 [&F](
size_t i,
size_t v0,
size_t v1,
size_t v2,
size_t v3) {
347 F.row(i) = Vector4i((
int)v0, (
int)v1, (
int)v2, (
int)v3);
350 default: log_and_throw_error(
"Cannot extract elements for dimension {}", dim);
355 void save(
const std::string& filename,
bool binary =
true)
357 m_spec.mesh_format.file_type = binary;
358 mshio::validate_spec(m_spec);
359 mshio::save_msh(filename, m_spec);
362 void save(std::ostream& out,
bool binary =
true)
364 m_spec.mesh_format.file_type = binary;
365 mshio::validate_spec(m_spec);
366 mshio::save_msh(out, m_spec);
369 void load(
const std::string& filename) { m_spec = mshio::load_msh(filename); }
371 void load(std::istream& in) { m_spec = mshio::load_msh(in); }
374 template <
int DIM,
typename Fn>
375 void add_vertices(
size_t num_vertices,
const Fn& get_vertex_cb)
377 static_assert(DIM >= 1 && DIM <= 3,
"Only 1,2,3D elements are supported!");
378 if (num_vertices == 0) {
379 logger().trace(
"Adding empty vertex block.");
381 mshio::NodeBlock block;
382 block.num_nodes_in_block = num_vertices;
383 block.tags.reserve(num_vertices);
384 block.data.reserve(num_vertices * 3);
385 block.entity_dim = DIM;
386 block.entity_tag = (int)m_spec.nodes.num_entity_blocks + 1;
388 const size_t tag_offset = m_spec.nodes.max_node_tag;
389 for (
size_t i = 0; i < num_vertices; i++) {
390 const auto& v = get_vertex_cb(i);
391 block.tags.push_back(tag_offset + i + 1);
392 block.data.push_back(v[0]);
393 block.data.push_back(v[1]);
394 block.data.push_back(v[2]);
397 m_spec.nodes.num_entity_blocks += 1;
398 m_spec.nodes.num_nodes += num_vertices;
399 m_spec.nodes.min_node_tag = 1;
400 m_spec.nodes.max_node_tag += num_vertices;
401 m_spec.nodes.entity_blocks.push_back(std::move(block));
404 template <
int DIM,
typename Fn>
405 void add_simplex_elements(
size_t num_elements,
const Fn& get_element_cb)
408 DIM == 1 || DIM == 2 || DIM == 3,
409 "Only 1,2,3D simplex elements are supported");
410 if (num_elements == 0)
return;
412 if (m_spec.nodes.num_nodes == 0) {
413 log_and_throw_error(
"Please add a vertex block before adding elements.");
415 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
417 if (vertex_block.entity_dim != DIM) {
418 log_and_throw_error(
"It seems the last added vertex block has different dimension "
419 "than the elements you want to add.");
422 mshio::ElementBlock block;
423 block.entity_dim = DIM;
424 block.entity_tag = vertex_block.entity_tag;
425 if constexpr (DIM == 1) {
426 block.element_type = 1;
427 }
else if constexpr (DIM == 2) {
428 block.element_type = 2;
430 block.element_type = 4;
432 block.num_elements_in_block = num_elements;
434 const size_t vertex_offset =
435 vertex_block.num_nodes_in_block == 0 ? 0 : vertex_block.tags.front() - 1;
436 if (vertex_offset == 0) {
438 "Do not offset vertex IDs for this element block as vertex block was empty");
441 const size_t tag_offset = m_spec.elements.max_element_tag;
442 block.data.reserve(num_elements * (DIM + 2));
443 for (
size_t i = 0; i < num_elements; i++) {
444 const auto& e = get_element_cb(i);
445 block.data.push_back(tag_offset + i + 1);
446 for (
size_t j = 0; j <= DIM; j++) {
447 block.data.push_back(vertex_offset + e[j] + 1);
451 m_spec.elements.num_entity_blocks++;
452 m_spec.elements.num_elements += num_elements;
453 m_spec.elements.min_element_tag = 1;
454 m_spec.elements.max_element_tag += num_elements;
455 m_spec.elements.entity_blocks.push_back(std::move(block));
458 template <
int NUM_FIELDS,
int ELEMENT_DIM,
typename Fn>
459 void add_vertex_attribute(
const std::string& name,
const Fn& get_attribute_cb)
462 NUM_FIELDS == 1 || NUM_FIELDS == 3 || NUM_FIELDS == 9,
463 "Only scalar, vector and tensor fields are supported as attribute!");
464 if (m_spec.nodes.entity_blocks.empty()) {
465 throw std::runtime_error(
"Please add vertices before adding vertex attributes.");
467 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
468 const size_t num_vertices = vertex_block.num_nodes_in_block;
469 assert(num_vertices != 0);
471 if (vertex_block.entity_dim != ELEMENT_DIM) {
472 throw std::runtime_error(
"It seems the last added vertex block has different dimension "
473 "from the vertex attribute you want to add.");
477 data.header.string_tags = {name};
478 data.header.real_tags = {0.0};
479 data.header.int_tags = {0, NUM_FIELDS, int(num_vertices), 0, ELEMENT_DIM};
481 data.entries.resize(num_vertices);
482 for (
size_t i = 0; i < num_vertices; i++) {
483 auto& entry = data.entries[i];
484 entry.tag = vertex_block.tags[i];
485 entry.data.reserve(NUM_FIELDS);
486 const auto& attr = get_attribute_cb(i);
487 if constexpr (NUM_FIELDS == 1) {
488 entry.data.push_back(attr);
490 for (
size_t j = 0; j < NUM_FIELDS; j++) {
491 entry.data.push_back(attr[j]);
496 m_spec.node_data.push_back(std::move(data));
499 template <
int NUM_FIELDS,
int ELEMENT_DIM,
typename Fn>
500 void add_element_attribute(
const std::string& name,
const Fn& get_attribute_cb)
503 NUM_FIELDS == 1 || NUM_FIELDS == 3 || NUM_FIELDS == 9,
504 "Only scalar, vector and tensor fields are supported as attribute!");
505 if (m_spec.elements.entity_blocks.empty()) {
506 throw std::runtime_error(
"Please add elements before adding element attributes!");
508 const auto& elem_block = m_spec.elements.entity_blocks.back();
509 if (elem_block.entity_dim != ELEMENT_DIM) {
510 throw std::runtime_error(
511 "It seems the last added element block has different dimension "
512 "than the element attribute you want to add.");
514 const size_t num_elements = elem_block.num_elements_in_block;
517 data.header.string_tags = {name};
518 data.header.real_tags = {0.0};
519 data.header.int_tags = {0, NUM_FIELDS, int(num_elements), 0, ELEMENT_DIM};
521 data.entries.resize(num_elements);
522 for (
size_t i = 0; i < num_elements; i++) {
523 auto& entry = data.entries[i];
524 entry.tag = elem_block.data[i * (ELEMENT_DIM + 2)];
525 entry.data.reserve(NUM_FIELDS);
526 const auto& attr = get_attribute_cb(i);
527 if constexpr (NUM_FIELDS == 1) {
528 entry.data.push_back(attr);
530 for (
size_t j = 0; j < NUM_FIELDS; j++) {
531 entry.data.push_back(attr[j]);
536 m_spec.element_data.push_back(std::move(data));
539 const mshio::NodeBlock* get_vertex_block(
const int dim)
const
541 for (
const auto& block : m_spec.nodes.entity_blocks) {
542 if (block.entity_dim == dim) {
549 const mshio::NodeBlock* get_vertex_block(
const int dim,
const int tag)
const
551 for (
const auto& block : m_spec.nodes.entity_blocks) {
552 if (block.entity_dim == dim && block.entity_tag == tag) {
559 const mshio::ElementBlock* get_simplex_element_block(
const int dim)
const
561 for (
const auto& block : m_spec.elements.entity_blocks) {
562 if (block.entity_dim == dim) {
569 const mshio::ElementBlock* get_simplex_element_block(
const int dim,
const int tag)
const
571 for (
const auto& block : m_spec.elements.entity_blocks) {
572 if (block.entity_dim == dim && block.entity_tag == tag) {
579 size_t get_num_vertices(
const int dim)
const
581 const auto* block = get_vertex_block(dim);
582 if (block !=
nullptr) {
583 return block->num_nodes_in_block;
589 size_t get_num_vertices(
const int dim,
const int tag)
const
591 const auto* block = get_vertex_block(dim, tag);
592 if (block !=
nullptr) {
593 return block->num_nodes_in_block;
599 size_t get_num_simplex_elements(
const int dim)
const
601 const auto* block = get_simplex_element_block(dim);
602 if (block !=
nullptr) {
603 return block->num_elements_in_block;
609 size_t get_num_simplex_elements(
const int dim,
const int tag)
const
611 const auto* block = get_simplex_element_block(dim, tag);
612 if (block !=
nullptr) {
613 return block->num_elements_in_block;
619 template <
typename Fn>
620 void extract_vertices(
const int dim, Fn&& set_vertex_cb)
const
622 const auto* block = get_vertex_block(dim);
623 if (block ==
nullptr)
return;
625 const size_t num_vertices = block->num_nodes_in_block;
626 if (num_vertices == 0)
return;
628 const size_t tag_offset = block->tags.front();
629 for (
size_t i = 0; i < num_vertices; i++) {
630 size_t tag = block->tags[i] - tag_offset;
631 set_vertex_cb(tag, block->data[i * 3], block->data[i * 3 + 1], block->data[i * 3 + 2]);
635 template <
typename Fn>
636 void extract_vertices(
const int dim,
const int tag, Fn&& set_vertex_cb)
const
638 const auto* block = get_vertex_block(dim, tag);
639 if (block ==
nullptr)
return;
641 const size_t num_vertices = block->num_nodes_in_block;
642 if (num_vertices == 0)
return;
644 const size_t tag_offset = block->tags.front();
645 for (
size_t i = 0; i < num_vertices; i++) {
646 size_t tag = block->tags[i] - tag_offset;
647 set_vertex_cb(tag, block->data[i * 3], block->data[i * 3 + 1], block->data[i * 3 + 2]);
651 template <
int DIM,
typename Fn>
652 void extract_simplex_elements(Fn&& set_element_cb)
const
654 const auto* vertex_block = get_vertex_block(DIM);
655 const auto* element_block = get_simplex_element_block(DIM);
656 if (element_block ==
nullptr)
return;
657 assert(vertex_block !=
nullptr);
659 const size_t num_elements = element_block->num_elements_in_block;
660 if (num_elements == 0)
return;
661 assert(vertex_block->num_nodes_in_block != 0);
663 const size_t vert_tag_offset = vertex_block->tags.front();
664 const size_t elem_tag_offset = element_block->data.front();
665 for (
size_t i = 0; i < num_elements; i++) {
666 const size_t tag = element_block->data[i * (DIM + 2)] - elem_tag_offset;
667 assert(tag < num_elements);
668 const auto* element = element_block->data.data() + i * (DIM + 2) + 1;
670 if constexpr (DIM == 1) {
671 set_element_cb(tag, element[0] - vert_tag_offset, element[1] - vert_tag_offset);
672 }
else if constexpr (DIM == 2) {
675 element[0] - vert_tag_offset,
676 element[1] - vert_tag_offset,
677 element[2] - vert_tag_offset);
678 }
else if constexpr (DIM == 3) {
681 element[0] - vert_tag_offset,
682 element[1] - vert_tag_offset,
683 element[2] - vert_tag_offset,
684 element[3] - vert_tag_offset);
690 std::vector<std::string> get_vertex_attribute_names()
const
692 std::vector<std::string> attr_names;
693 attr_names.reserve(m_spec.node_data.size());
694 for (
const auto& data : m_spec.node_data) {
695 const auto& int_tags = data.header.int_tags;
696 if (int_tags.size() >= 5 && int_tags[4] == DIM) {
697 attr_names.push_back(data.header.string_tags.front());
704 std::vector<std::string> get_element_attribute_names()
const
706 std::vector<std::string> attr_names;
707 attr_names.reserve(m_spec.element_data.size());
708 for (
const auto& data : m_spec.element_data) {
709 const auto& int_tags = data.header.int_tags;
710 if (int_tags.size() >= 5 && int_tags[4] == DIM) {
711 attr_names.push_back(data.header.string_tags.front());
717 template <
int DIM,
typename Fn>
718 void extract_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
720 const auto* vertex_block = get_vertex_block(DIM);
721 const size_t tag_offset = vertex_block->tags.front();
722 for (
const auto& data : m_spec.node_data) {
723 if (data.header.string_tags.front() != attr_name)
continue;
724 if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
725 log_and_throw_error(
"Attribute " + attr_name +
" is of the wrong DIM.");
728 for (
const auto& entry : data.entries) {
729 const size_t tag = entry.tag - tag_offset;
730 assert(tag < vertex_block->num_nodes_in_block);
731 set_attr(tag, entry.data);
736 template <
int DIM,
typename Fn>
737 void extract_element_attribute(
const std::string& attr_name, Fn&& set_attr)
739 const auto* element_block = get_simplex_element_block(DIM);
740 const size_t tag_offset = element_block->data.front();
741 for (
const auto& data : m_spec.element_data) {
742 if (data.header.string_tags.front() != attr_name)
continue;
743 if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
744 throw std::runtime_error(
"Attribute " + attr_name +
" is of the wrong DIM.");
747 for (
const mshio::DataEntry& entry : data.entries) {
748 const size_t tag = entry.tag - tag_offset;
749 assert(tag < element_block->num_elements_in_block);
750 set_attr(tag, entry.data);
757 mshio::MshSpec m_spec;