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));
321 for (
const mshio::PhysicalGroup& ph : m_spec.physical_groups) {
322 if (ph.name == name) {
329 void get_VF(
const int& dim,
const int& tag, MatrixXd& V, MatrixXi& F)
331 assert(dim > 0 && tag > 0);
333 const size_t n_V = get_num_vertices(dim, tag);
334 const size_t n_F = get_num_simplex_elements(dim, tag);
338 extract_vertices(dim, tag, [&V](
size_t i,
double x,
double y,
double z) {
339 V.row(i) = Vector3d(x, y, z);
343 F.resize(n_F, dim + 1);
346 extract_simplex_elements<1>([&F](
size_t i,
size_t v0,
size_t v1) {
347 F.row(i) = Vector2i((
int)v0, (
int)v1);
351 extract_simplex_elements<2>([&F](
size_t i,
size_t v0,
size_t v1,
size_t v2) {
352 F.row(i) = Vector3i((
int)v0, (
int)v1, (
int)v2);
356 extract_simplex_elements<3>(
357 [&F](
size_t i,
size_t v0,
size_t v1,
size_t v2,
size_t v3) {
358 F.row(i) = Vector4i((
int)v0, (
int)v1, (
int)v2, (
int)v3);
361 default: log_and_throw_error(
"Cannot extract elements for dimension {}", dim);
366 void save(
const std::string& filename,
bool binary =
true)
368 m_spec.mesh_format.file_type = binary;
369 mshio::validate_spec(m_spec);
370 mshio::save_msh(filename, m_spec);
373 void save(std::ostream& out,
bool binary =
true)
375 m_spec.mesh_format.file_type = binary;
376 mshio::validate_spec(m_spec);
377 mshio::save_msh(out, m_spec);
380 void load(
const std::string& filename) { m_spec = mshio::load_msh(filename); }
382 void load(std::istream& in) { m_spec = mshio::load_msh(in); }
385 template <
int DIM,
typename Fn>
386 void add_vertices(
size_t num_vertices,
const Fn& get_vertex_cb)
388 static_assert(DIM >= 1 && DIM <= 3,
"Only 1,2,3D elements are supported!");
389 if (num_vertices == 0) {
390 logger().trace(
"Adding empty vertex block.");
392 mshio::NodeBlock block;
393 block.num_nodes_in_block = num_vertices;
394 block.tags.reserve(num_vertices);
395 block.data.reserve(num_vertices * 3);
396 block.entity_dim = DIM;
397 block.entity_tag = (int)m_spec.nodes.num_entity_blocks + 1;
399 const size_t tag_offset = m_spec.nodes.max_node_tag;
400 for (
size_t i = 0; i < num_vertices; i++) {
401 const auto& v = get_vertex_cb(i);
402 block.tags.push_back(tag_offset + i + 1);
403 block.data.push_back(v[0]);
404 block.data.push_back(v[1]);
405 block.data.push_back(v[2]);
408 m_spec.nodes.num_entity_blocks += 1;
409 m_spec.nodes.num_nodes += num_vertices;
410 m_spec.nodes.min_node_tag = 1;
411 m_spec.nodes.max_node_tag += num_vertices;
412 m_spec.nodes.entity_blocks.push_back(std::move(block));
415 template <
int DIM,
typename Fn>
416 void add_simplex_elements(
size_t num_elements,
const Fn& get_element_cb)
419 DIM == 1 || DIM == 2 || DIM == 3,
420 "Only 1,2,3D simplex elements are supported");
421 if (num_elements == 0)
return;
423 if (m_spec.nodes.num_nodes == 0) {
424 log_and_throw_error(
"Please add a vertex block before adding elements.");
426 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
428 if (vertex_block.entity_dim != DIM) {
430 "It seems the last added vertex block has different dimension "
431 "than the elements you want to add.");
434 mshio::ElementBlock block;
435 block.entity_dim = DIM;
436 block.entity_tag = vertex_block.entity_tag;
437 if constexpr (DIM == 1) {
438 block.element_type = 1;
439 }
else if constexpr (DIM == 2) {
440 block.element_type = 2;
442 block.element_type = 4;
444 block.num_elements_in_block = num_elements;
446 const size_t vertex_offset =
447 vertex_block.num_nodes_in_block == 0 ? 0 : vertex_block.tags.front() - 1;
448 if (vertex_offset == 0) {
450 "Do not offset vertex IDs for this element block as vertex block was empty");
453 const size_t tag_offset = m_spec.elements.max_element_tag;
454 block.data.reserve(num_elements * (DIM + 2));
455 for (
size_t i = 0; i < num_elements; i++) {
456 const auto& e = get_element_cb(i);
457 block.data.push_back(tag_offset + i + 1);
458 for (
size_t j = 0; j <= DIM; j++) {
459 block.data.push_back(vertex_offset + e[j] + 1);
463 m_spec.elements.num_entity_blocks++;
464 m_spec.elements.num_elements += num_elements;
465 m_spec.elements.min_element_tag = 1;
466 m_spec.elements.max_element_tag += num_elements;
467 m_spec.elements.entity_blocks.push_back(std::move(block));
470 template <
int NUM_FIELDS,
int ELEMENT_DIM,
typename Fn>
471 void add_vertex_attribute(
const std::string& name,
const Fn& get_attribute_cb)
474 NUM_FIELDS == 1 || NUM_FIELDS == 3 || NUM_FIELDS == 9,
475 "Only scalar, vector and tensor fields are supported as attribute!");
476 if (m_spec.nodes.entity_blocks.empty()) {
477 throw std::runtime_error(
"Please add vertices before adding vertex attributes.");
479 const auto& vertex_block = m_spec.nodes.entity_blocks.back();
480 const size_t num_vertices = vertex_block.num_nodes_in_block;
481 assert(num_vertices != 0);
483 if (vertex_block.entity_dim != ELEMENT_DIM) {
484 throw std::runtime_error(
485 "It seems the last added vertex block has different dimension "
486 "from the vertex attribute you want to add.");
490 data.header.string_tags = {name};
491 data.header.real_tags = {0.0};
492 data.header.int_tags = {0, NUM_FIELDS, int(num_vertices), 0, ELEMENT_DIM};
494 data.entries.resize(num_vertices);
495 for (
size_t i = 0; i < num_vertices; i++) {
496 auto& entry = data.entries[i];
497 entry.tag = vertex_block.tags[i];
498 entry.data.reserve(NUM_FIELDS);
499 const auto& attr = get_attribute_cb(i);
500 if constexpr (NUM_FIELDS == 1) {
501 entry.data.push_back(attr);
503 for (
size_t j = 0; j < NUM_FIELDS; j++) {
504 entry.data.push_back(attr[j]);
509 m_spec.node_data.push_back(std::move(data));
512 template <
int NUM_FIELDS,
int ELEMENT_DIM,
typename Fn>
513 void add_element_attribute(
const std::string& name,
const Fn& get_attribute_cb)
516 NUM_FIELDS == 1 || NUM_FIELDS == 3 || NUM_FIELDS == 9,
517 "Only scalar, vector and tensor fields are supported as attribute!");
518 if (m_spec.elements.entity_blocks.empty()) {
519 throw std::runtime_error(
"Please add elements before adding element attributes!");
521 const auto& elem_block = m_spec.elements.entity_blocks.back();
522 if (elem_block.entity_dim != ELEMENT_DIM) {
523 throw std::runtime_error(
524 "It seems the last added element block has different dimension "
525 "than the element attribute you want to add.");
527 const size_t num_elements = elem_block.num_elements_in_block;
530 data.header.string_tags = {name};
531 data.header.real_tags = {0.0};
532 data.header.int_tags = {0, NUM_FIELDS, int(num_elements), 0, ELEMENT_DIM};
534 data.entries.resize(num_elements);
535 for (
size_t i = 0; i < num_elements; i++) {
536 auto& entry = data.entries[i];
537 entry.tag = elem_block.data[i * (ELEMENT_DIM + 2)];
538 entry.data.reserve(NUM_FIELDS);
539 const auto& attr = get_attribute_cb(i);
540 if constexpr (NUM_FIELDS == 1) {
541 entry.data.push_back(attr);
543 for (
size_t j = 0; j < NUM_FIELDS; j++) {
544 entry.data.push_back(attr[j]);
549 m_spec.element_data.push_back(std::move(data));
552 const mshio::NodeBlock* get_vertex_block(
const int dim)
const
554 for (
const auto& block : m_spec.nodes.entity_blocks) {
555 if (block.entity_dim == dim) {
562 const mshio::NodeBlock* get_vertex_block(
const int dim,
const int tag)
const
564 for (
const auto& block : m_spec.nodes.entity_blocks) {
565 if (block.entity_dim == dim && block.entity_tag == tag) {
572 const mshio::ElementBlock* get_simplex_element_block(
const int dim)
const
574 for (
const auto& block : m_spec.elements.entity_blocks) {
575 if (block.entity_dim == dim) {
582 const mshio::ElementBlock* get_simplex_element_block(
const int dim,
const int tag)
const
584 for (
const auto& block : m_spec.elements.entity_blocks) {
585 if (block.entity_dim == dim && block.entity_tag == tag) {
592 size_t get_num_vertices(
const int dim)
const
594 const auto* block = get_vertex_block(dim);
595 if (block !=
nullptr) {
596 return block->num_nodes_in_block;
602 size_t get_num_vertices(
const int dim,
const int tag)
const
604 const auto* block = get_vertex_block(dim, tag);
605 if (block !=
nullptr) {
606 return block->num_nodes_in_block;
612 size_t get_num_simplex_elements(
const int dim)
const
614 const auto* block = get_simplex_element_block(dim);
615 if (block !=
nullptr) {
616 return block->num_elements_in_block;
622 size_t get_num_simplex_elements(
const int dim,
const int tag)
const
624 const auto* block = get_simplex_element_block(dim, tag);
625 if (block !=
nullptr) {
626 return block->num_elements_in_block;
632 template <
typename Fn>
633 void extract_vertices(
const int dim, Fn&& set_vertex_cb)
const
635 const auto* block = get_vertex_block(dim);
636 if (block ==
nullptr)
return;
638 const size_t num_vertices = block->num_nodes_in_block;
639 if (num_vertices == 0)
return;
641 const size_t tag_offset = block->tags.front();
642 for (
size_t i = 0; i < num_vertices; i++) {
643 size_t tag = block->tags[i] - tag_offset;
644 set_vertex_cb(tag, block->data[i * 3], block->data[i * 3 + 1], block->data[i * 3 + 2]);
648 template <
typename Fn>
649 void extract_vertices(
const int dim,
const int tag, Fn&& set_vertex_cb)
const
651 const auto* block = get_vertex_block(dim, tag);
652 if (block ==
nullptr)
return;
654 const size_t num_vertices = block->num_nodes_in_block;
655 if (num_vertices == 0)
return;
657 const size_t tag_offset = block->tags.front();
658 for (
size_t i = 0; i < num_vertices; i++) {
659 size_t tag = block->tags[i] - tag_offset;
660 set_vertex_cb(tag, block->data[i * 3], block->data[i * 3 + 1], block->data[i * 3 + 2]);
664 template <
int DIM,
typename Fn>
665 void extract_simplex_elements(Fn&& set_element_cb)
const
667 const auto* vertex_block = get_vertex_block(DIM);
668 const auto* element_block = get_simplex_element_block(DIM);
669 if (element_block ==
nullptr)
return;
670 assert(vertex_block !=
nullptr);
672 const size_t num_elements = element_block->num_elements_in_block;
673 if (num_elements == 0)
return;
674 assert(vertex_block->num_nodes_in_block != 0);
676 const size_t vert_tag_offset = vertex_block->tags.front();
677 const size_t elem_tag_offset = element_block->data.front();
678 for (
size_t i = 0; i < num_elements; i++) {
679 const size_t tag = element_block->data[i * (DIM + 2)] - elem_tag_offset;
680 assert(tag < num_elements);
681 const auto* element = element_block->data.data() + i * (DIM + 2) + 1;
683 if constexpr (DIM == 1) {
684 set_element_cb(tag, element[0] - vert_tag_offset, element[1] - vert_tag_offset);
685 }
else if constexpr (DIM == 2) {
688 element[0] - vert_tag_offset,
689 element[1] - vert_tag_offset,
690 element[2] - vert_tag_offset);
691 }
else if constexpr (DIM == 3) {
694 element[0] - vert_tag_offset,
695 element[1] - vert_tag_offset,
696 element[2] - vert_tag_offset,
697 element[3] - vert_tag_offset);
703 std::vector<std::string> get_vertex_attribute_names()
const
705 std::vector<std::string> attr_names;
706 attr_names.reserve(m_spec.node_data.size());
707 for (
const auto& data : m_spec.node_data) {
708 const auto& int_tags = data.header.int_tags;
709 if (int_tags.size() >= 5 && int_tags[4] == DIM) {
710 attr_names.push_back(data.header.string_tags.front());
717 std::vector<std::string> get_element_attribute_names()
const
719 std::vector<std::string> attr_names;
720 attr_names.reserve(m_spec.element_data.size());
721 for (
const auto& data : m_spec.element_data) {
722 const auto& int_tags = data.header.int_tags;
723 if (int_tags.size() >= 5 && int_tags[4] == DIM) {
724 attr_names.push_back(data.header.string_tags.front());
730 template <
int DIM,
typename Fn>
731 void extract_vertex_attribute(
const std::string& attr_name, Fn&& set_attr)
733 const auto* vertex_block = get_vertex_block(DIM);
734 const size_t tag_offset = vertex_block->tags.front();
735 for (
const auto& data : m_spec.node_data) {
736 if (data.header.string_tags.front() != attr_name)
continue;
737 if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
738 log_and_throw_error(
"Attribute " + attr_name +
" is of the wrong DIM.");
741 for (
const auto& entry : data.entries) {
742 const size_t tag = entry.tag - tag_offset;
743 assert(tag < vertex_block->num_nodes_in_block);
744 set_attr(tag, entry.data);
749 template <
int DIM,
typename Fn>
750 void extract_element_attribute(
const std::string& attr_name, Fn&& set_attr)
752 const auto* element_block = get_simplex_element_block(DIM);
753 const size_t tag_offset = element_block->data.front();
754 for (
const auto& data : m_spec.element_data) {
755 if (data.header.string_tags.front() != attr_name)
continue;
756 if (data.header.int_tags.size() >= 5 && data.header.int_tags[4] != DIM) {
757 throw std::runtime_error(
"Attribute " + attr_name +
" is of the wrong DIM.");
760 for (
const mshio::DataEntry& entry : data.entries) {
761 const size_t tag = entry.tag - tag_offset;
762 assert(tag < element_block->num_elements_in_block);
763 set_attr(tag, entry.data);
770 mshio::MshSpec m_spec;