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 std::vector<std::string> get_all_element_attribute_names()
const
272 std::vector<std::string> attr_names;
273 attr_names.reserve(m_spec.element_data.size());
274 for (
const auto& data : m_spec.element_data) {
275 attr_names.push_back(data.header.string_tags.front());
280 template <
typename Fn>
281 void extract_edge_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
283 extract_vertex_attribute<1>(attr_name, std::forward<Fn>(set_attr));
286 template <
typename Fn>
287 void extract_face_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
289 extract_vertex_attribute<2>(attr_name, std::forward<Fn>(set_attr));
292 template <
typename Fn>
293 void extract_tet_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
295 extract_vertex_attribute<3>(attr_name, std::forward<Fn>(set_attr));
298 template <
typename Fn>
299 void extract_edge_attribute(
const std::string& attr_name, Fn&& set_attr)
301 extract_element_attribute<1>(attr_name, std::forward<Fn>(set_attr));
304 template <
typename Fn>
305 void extract_face_attribute(
const std::string& attr_name, Fn&& set_attr)
307 extract_element_attribute<2>(attr_name, std::forward<Fn>(set_attr));
310 template <
typename Fn>
311 void extract_tet_attribute(
const std::string& attr_name, Fn&& set_attr)
313 extract_element_attribute<3>(attr_name, std::forward<Fn>(set_attr));
316 const std::vector<mshio::PhysicalGroup>& get_physical_groups()
318 return m_spec.physical_groups;
326 for (
const mshio::PhysicalGroup& ph : m_spec.physical_groups) {
327 if (ph.name == name) {
334 void get_VF(
const int& dim,
const int& tag, MatrixXd& V, MatrixXi& F)
336 assert(dim > 0 && tag > 0);
338 const size_t n_V = get_num_vertices(dim, tag);
339 const size_t n_F = get_num_simplex_elements(dim, tag);
343 extract_vertices(dim, tag, [&V](
size_t i,
double x,
double y,
double z) {
344 V.row(i) = Vector3d(x, y, z);
348 F.resize(n_F, dim + 1);
351 extract_simplex_elements<1>(
352 [&F](
size_t i,
size_t v0,
size_t v1) { F.row(i) = Vector2i((
int)v0, (
int)v1); },
356 extract_simplex_elements<2>(
357 [&F](
size_t i,
size_t v0,
size_t v1,
size_t v2) {
358 F.row(i) = Vector3i((
int)v0, (
int)v1, (
int)v2);
363 extract_simplex_elements<3>(
364 [&F](
size_t i,
size_t v0,
size_t v1,
size_t v2,
size_t v3) {
365 F.row(i) = Vector4i((
int)v0, (
int)v1, (
int)v2, (
int)v3);
369 default: log_and_throw_error(
"Cannot extract elements for dimension {}", dim);
374 void get_VF(
const mshio::PhysicalGroup& ph, MatrixXd& V, MatrixXi& F)
376 get_VF(ph.dim, ph.tag, V, F);
379 void save(
const std::string& filename,
bool binary =
true)
381 m_spec.mesh_format.file_type = binary;
382 mshio::validate_spec(m_spec);
383 mshio::save_msh(filename, m_spec);
386 void save(std::ostream& out,
bool binary =
true)
388 m_spec.mesh_format.file_type = binary;
389 mshio::validate_spec(m_spec);
390 mshio::save_msh(out, m_spec);
393 void load(
const std::string& filename) { m_spec = mshio::load_msh(filename); }
395 void load(std::istream& in) { m_spec = mshio::load_msh(in); }
398 template <
int DIM,
typename Fn>
399 void add_vertices(
size_t num_vertices,
const Fn& get_vertex_cb)
401 static_assert(DIM >= 1 && DIM <= 3,
"Only 1,2,3D elements are supported!");
402 if (num_vertices == 0) {
403 logger().trace(
"Adding empty vertex block.");
405 mshio::NodeBlock block;
406 block.num_nodes_in_block = num_vertices;
407 block.tags.reserve(num_vertices);
408 block.data.reserve(num_vertices * 3);
409 block.entity_dim = DIM;
410 block.entity_tag = (int)m_spec.nodes.num_entity_blocks + 1;
412 const size_t tag_offset = m_spec.nodes.max_node_tag;
413 for (
size_t i = 0; i < num_vertices; i++) {
414 const auto& v = get_vertex_cb(i);
415 block.tags.push_back(tag_offset + i + 1);
416 block.data.push_back(v[0]);
417 block.data.push_back(v[1]);
418 block.data.push_back(v[2]);
421 m_spec.nodes.num_entity_blocks += 1;
422 m_spec.nodes.num_nodes += num_vertices;
423 m_spec.nodes.min_node_tag = 1;
424 m_spec.nodes.max_node_tag += num_vertices;
425 m_spec.nodes.entity_blocks.push_back(std::move(block));
428 template <
int DIM,
typename Fn>
429 void add_simplex_elements(
size_t num_elements,
const Fn& get_element_cb)
432 DIM == 1 || DIM == 2 || DIM == 3,
433 "Only 1,2,3D simplex elements are supported");
434 if (num_elements == 0)
return;
436 if (m_spec.nodes.num_nodes == 0) {
437 log_and_throw_error(
"Please add a vertex block before adding elements.");
439 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
441 if (vertex_block.entity_dim != DIM) {
443 "It seems the last added vertex block has different dimension "
444 "than the elements you want to add.");
447 mshio::ElementBlock block;
448 block.entity_dim = DIM;
449 block.entity_tag = vertex_block.entity_tag;
450 if constexpr (DIM == 1) {
451 block.element_type = 1;
452 }
else if constexpr (DIM == 2) {
453 block.element_type = 2;
455 block.element_type = 4;
457 block.num_elements_in_block = num_elements;
459 const size_t vertex_offset =
460 vertex_block.num_nodes_in_block == 0 ? 0 : vertex_block.tags.front() - 1;
461 if (vertex_offset == 0) {
463 "Do not offset vertex IDs for this element block as vertex block was empty");
466 const size_t tag_offset = m_spec.elements.max_element_tag;
467 block.data.reserve(num_elements * (DIM + 2));
468 for (
size_t i = 0; i < num_elements; i++) {
469 const auto& e = get_element_cb(i);
470 block.data.push_back(tag_offset + i + 1);
471 for (
size_t j = 0; j <= DIM; j++) {
472 block.data.push_back(vertex_offset + e[j] + 1);
476 m_spec.elements.num_entity_blocks++;
477 m_spec.elements.num_elements += num_elements;
478 m_spec.elements.min_element_tag = 1;
479 m_spec.elements.max_element_tag += num_elements;
480 m_spec.elements.entity_blocks.push_back(std::move(block));
483 template <
int NUM_FIELDS,
int ELEMENT_DIM,
typename Fn>
484 void add_vertex_attribute(
const std::string& name,
const Fn& get_attribute_cb)
487 NUM_FIELDS == 1 || NUM_FIELDS == 3 || NUM_FIELDS == 9,
488 "Only scalar, vector and tensor fields are supported as attribute!");
489 if (m_spec.nodes.entity_blocks.empty()) {
490 log_and_throw_error(
"Please add vertices before adding vertex attributes.");
492 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
493 const size_t num_vertices = vertex_block.num_nodes_in_block;
494 assert(num_vertices != 0);
496 if (vertex_block.entity_dim != ELEMENT_DIM) {
498 "It seems the last added vertex block has different dimension "
499 "from the vertex attribute you want to add.");
503 data.header.string_tags = {name};
504 data.header.real_tags = {0.0};
505 data.header.int_tags = {0, NUM_FIELDS, int(num_vertices), 0, ELEMENT_DIM};
507 data.entries.resize(num_vertices);
508 for (
size_t i = 0; i < num_vertices; i++) {
509 auto& entry = data.entries[i];
510 entry.tag = vertex_block.tags[i];
511 entry.data.reserve(NUM_FIELDS);
512 const auto& attr = get_attribute_cb(i);
513 if constexpr (NUM_FIELDS == 1) {
514 entry.data.push_back(attr);
516 for (
size_t j = 0; j < NUM_FIELDS; j++) {
517 entry.data.push_back(attr[j]);
522 m_spec.node_data.push_back(std::move(data));
525 template <
int NUM_FIELDS,
int ELEMENT_DIM,
typename Fn>
526 void add_element_attribute(
const std::string& name,
const Fn& get_attribute_cb)
529 NUM_FIELDS == 1 || NUM_FIELDS == 3 || NUM_FIELDS == 9,
530 "Only scalar, vector and tensor fields are supported as attribute!");
531 if (m_spec.elements.entity_blocks.empty()) {
532 throw std::runtime_error(
"Please add elements before adding element attributes!");
534 const auto& elem_block = m_spec.elements.entity_blocks.back();
535 if (elem_block.entity_dim != ELEMENT_DIM) {
536 throw std::runtime_error(
537 "It seems the last added element block has different dimension "
538 "than the element attribute you want to add.");
540 const size_t num_elements = elem_block.num_elements_in_block;
543 data.header.string_tags = {name};
544 data.header.real_tags = {0.0};
545 data.header.int_tags = {0, NUM_FIELDS, int(num_elements), 0, ELEMENT_DIM};
547 data.entries.resize(num_elements);
548 for (
size_t i = 0; i < num_elements; i++) {
549 auto& entry = data.entries[i];
550 entry.tag = elem_block.data[i * (ELEMENT_DIM + 2)];
551 entry.data.reserve(NUM_FIELDS);
552 const auto& attr = get_attribute_cb(i);
553 if constexpr (NUM_FIELDS == 1) {
554 entry.data.push_back(attr);
556 for (
size_t j = 0; j < NUM_FIELDS; j++) {
557 entry.data.push_back(attr[j]);
562 m_spec.element_data.push_back(std::move(data));
565 const mshio::NodeBlock* get_vertex_block(
const int dim)
const
567 for (
const auto& block : m_spec.nodes.entity_blocks) {
568 if (block.entity_dim == dim) {
575 const mshio::NodeBlock* get_vertex_block(
const int dim,
const int tag)
const
577 for (
const auto& block : m_spec.nodes.entity_blocks) {
578 if (block.entity_dim == dim && block.entity_tag == tag) {
585 const mshio::ElementBlock* get_simplex_element_block(
const int dim)
const
587 for (
const auto& block : m_spec.elements.entity_blocks) {
588 if (block.entity_dim == dim) {
595 const mshio::ElementBlock* get_simplex_element_block(
const int dim,
const int tag)
const
597 for (
const auto& block : m_spec.elements.entity_blocks) {
598 if (block.entity_dim == dim && block.entity_tag == tag) {
605 size_t get_num_vertices(
const int dim)
const
607 const auto* block = get_vertex_block(dim);
608 if (block !=
nullptr) {
609 return block->num_nodes_in_block;
615 size_t get_num_vertices(
const int dim,
const int tag)
const
617 const auto* block = get_vertex_block(dim, tag);
618 if (block !=
nullptr) {
619 return block->num_nodes_in_block;
625 size_t get_num_simplex_elements(
const int dim)
const
627 const auto* block = get_simplex_element_block(dim);
628 if (block !=
nullptr) {
629 return block->num_elements_in_block;
635 size_t get_num_simplex_elements(
const int dim,
const int tag)
const
637 const auto* block = get_simplex_element_block(dim, tag);
638 if (block !=
nullptr) {
639 return block->num_elements_in_block;
645 template <
typename Fn>
646 void extract_vertices(
const int dim, Fn&& set_vertex_cb)
const
648 const auto* block = get_vertex_block(dim);
649 if (block ==
nullptr)
return;
651 const size_t num_vertices = block->num_nodes_in_block;
652 if (num_vertices == 0)
return;
654 const size_t tag_offset = block->tags.front();
655 for (
size_t i = 0; i < num_vertices; i++) {
656 size_t tag = block->tags[i] - tag_offset;
657 set_vertex_cb(tag, block->data[i * 3], block->data[i * 3 + 1], block->data[i * 3 + 2]);
661 template <
typename Fn>
662 void extract_vertices(
const int dim,
const int tag, Fn&& set_vertex_cb)
const
664 const auto* block = get_vertex_block(dim, tag);
665 if (block ==
nullptr)
return;
667 const size_t num_vertices = block->num_nodes_in_block;
668 if (num_vertices == 0)
return;
670 const size_t tag_offset = block->tags.front();
671 for (
size_t i = 0; i < num_vertices; i++) {
672 size_t tag = block->tags[i] - tag_offset;
673 set_vertex_cb(tag, block->data[i * 3], block->data[i * 3 + 1], block->data[i * 3 + 2]);
677 template <
int DIM,
typename Fn>
678 void extract_simplex_elements(Fn&& set_element_cb,
const int tag = -1)
const
680 const auto* vertex_block = get_vertex_block(DIM);
681 const auto* element_block =
682 tag == -1 ? get_simplex_element_block(DIM) : get_simplex_element_block(DIM, tag);
683 if (element_block ==
nullptr) {
686 assert(vertex_block !=
nullptr);
688 const size_t num_elements = element_block->num_elements_in_block;
689 if (num_elements == 0)
return;
690 assert(vertex_block->num_nodes_in_block != 0);
692 const size_t vert_tag_offset = vertex_block->tags.front();
693 const size_t elem_tag_offset = element_block->data.front();
694 for (
size_t i = 0; i < num_elements; i++) {
695 const size_t tag = element_block->data[i * (DIM + 2)] - elem_tag_offset;
696 assert(tag < num_elements);
697 const auto* element = element_block->data.data() + i * (DIM + 2) + 1;
699 if constexpr (DIM == 1) {
700 set_element_cb(tag, element[0] - vert_tag_offset, element[1] - vert_tag_offset);
701 }
else if constexpr (DIM == 2) {
704 element[0] - vert_tag_offset,
705 element[1] - vert_tag_offset,
706 element[2] - vert_tag_offset);
707 }
else if constexpr (DIM == 3) {
710 element[0] - vert_tag_offset,
711 element[1] - vert_tag_offset,
712 element[2] - vert_tag_offset,
713 element[3] - vert_tag_offset);
719 std::vector<std::string> get_vertex_attribute_names()
const
721 std::vector<std::string> attr_names;
722 attr_names.reserve(m_spec.node_data.size());
723 for (
const auto& data : m_spec.node_data) {
724 const auto& int_tags = data.header.int_tags;
725 if (int_tags.size() >= 5 && int_tags[4] == DIM) {
726 attr_names.push_back(data.header.string_tags.front());
733 std::vector<std::string> get_element_attribute_names()
const
735 std::vector<std::string> attr_names;
736 attr_names.reserve(m_spec.element_data.size());
737 for (
const auto& data : m_spec.element_data) {
738 const auto& int_tags = data.header.int_tags;
739 if (int_tags.size() >= 5 && int_tags[4] == DIM) {
740 attr_names.push_back(data.header.string_tags.front());
746 template <
int DIM,
typename Fn>
747 void extract_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
749 const auto* vertex_block = get_vertex_block(DIM);
750 const size_t tag_offset = vertex_block->tags.front();
751 for (
const auto& data : m_spec.node_data) {
752 if (data.header.string_tags.front() != attr_name)
continue;
753 if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
754 log_and_throw_error(
"Attribute " + attr_name +
" is of the wrong DIM.");
757 for (
const auto& entry : data.entries) {
758 const size_t tag = entry.tag - tag_offset;
759 assert(tag < vertex_block->num_nodes_in_block);
760 set_attr(tag, entry.data);
765 template <
int DIM,
typename Fn>
766 void extract_element_attribute(
const std::string& attr_name, Fn&& set_attr)
768 const auto* element_block = get_simplex_element_block(DIM);
769 const size_t tag_offset = element_block->data.front();
770 for (
const auto& data : m_spec.element_data) {
771 if (data.header.string_tags.front() != attr_name)
continue;
772 if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
773 throw std::runtime_error(
"Attribute " + attr_name +
" is of the wrong DIM.");
776 for (
const mshio::DataEntry& entry : data.entries) {
777 const size_t tag = entry.tag - tag_offset;
778 assert(tag < element_block->num_elements_in_block);
779 set_attr(tag, entry.data);
786 mshio::MshSpec m_spec;