OGS
MeshRevision.cpp
Go to the documentation of this file.
1
15#include "MeshRevision.h"
16
17#include <numeric>
18#include <range/v3/algorithm/copy.hpp>
19#include <range/v3/range/conversion.hpp>
20#include <range/v3/view/transform.hpp>
21
22#include "BaseLib/Algorithm.h"
23#include "BaseLib/Logging.h"
24#include "GeoLib/Grid.h"
27#include "MeshLib/Mesh.h"
28#include "MeshLib/Properties.h"
30
31namespace MeshToolsLib
32{
35unsigned lutPrismThirdNode(unsigned const id1, unsigned const id2)
36{
37 if ((id1 == 0 && id2 == 1) || (id1 == 1 && id2 == 0))
38 {
39 return 2;
40 }
41 if ((id1 == 1 && id2 == 2) || (id1 == 2 && id2 == 1))
42 {
43 return 0;
44 }
45 if ((id1 == 0 && id2 == 2) || (id1 == 2 && id2 == 0))
46 {
47 return 1;
48 }
49 if ((id1 == 3 && id2 == 4) || (id1 == 4 && id2 == 3))
50 {
51 return 5;
52 }
53 if ((id1 == 4 && id2 == 5) || (id1 == 5 && id2 == 4))
54 {
55 return 3;
56 }
57 if ((id1 == 3 && id2 == 5) || (id1 == 5 && id2 == 3))
58 {
59 return 4;
60 }
61 return std::numeric_limits<unsigned>::max();
62}
63} // namespace MeshToolsLib
64
65namespace
66{
67template <typename ElementType>
68std::unique_ptr<MeshLib::Element> createElement(
69 std::span<MeshLib::Node* const> const element_nodes,
70 std::vector<MeshLib::Node*> const& nodes,
71 std::array<std::size_t, ElementType::n_all_nodes> const local_ids)
72{
73 using namespace MeshLib::views;
74 auto lookup_in = [](auto const& values)
75 {
76 return ranges::views::transform([&values](std::size_t const n)
77 { return values[n]; });
78 };
79
80 std::array<MeshLib::Node*, ElementType::n_all_nodes> new_nodes;
81 ranges::copy(local_ids | lookup_in(element_nodes) | ids | lookup_in(nodes),
82 begin(new_nodes));
83
84 return std::make_unique<ElementType>(new_nodes);
85}
86
88unsigned subdivideQuad(MeshLib::Element const* const quad,
89 std::vector<MeshLib::Node*> const& nodes,
90 std::vector<MeshLib::Element*>& new_elements)
91{
92 std::array<std::size_t, 3> const tri1_node_ids{0, 1, 2};
93 new_elements.push_back(
94 createElement<MeshLib::Tri>(quad->nodes(), nodes, tri1_node_ids)
95 .release());
96
97 std::array<std::size_t, 3> const tri2_node_ids{0, 2, 3};
98 new_elements.push_back(
99 createElement<MeshLib::Tri>(quad->nodes(), nodes, tri2_node_ids)
100 .release());
101
102 return 2;
103}
104
106unsigned subdividePrism(MeshLib::Element const* const prism,
107 std::vector<MeshLib::Node*> const& nodes,
108 std::vector<MeshLib::Element*>& new_elements)
109{
110 auto addTetrahedron =
111 [&prism, &nodes, &new_elements](std::array<std::size_t, 4> const ids)
112 {
113 new_elements.push_back(
114 createElement<MeshLib::Tet>(prism->nodes(), nodes, ids).release());
115 };
116
117 addTetrahedron({0, 1, 2, 3});
118 addTetrahedron({3, 2, 4, 5});
119 addTetrahedron({2, 1, 3, 4});
120
121 return 3;
122}
123
125unsigned subdivideHex(MeshLib::Element const* const hex,
126 std::vector<MeshLib::Node*> const& nodes,
127 std::vector<MeshLib::Element*>& new_elements)
128{
129 auto prism1 =
130 createElement<MeshLib::Prism>(hex->nodes(), nodes, {0, 2, 1, 4, 6, 5});
131 subdividePrism(prism1.get(), nodes, new_elements);
132
133 auto prism2 =
134 createElement<MeshLib::Prism>(hex->nodes(), nodes, {4, 6, 7, 0, 2, 3});
135 subdividePrism(prism2.get(), nodes, new_elements);
136
137 return 6;
138}
139
141unsigned subdividePyramid(MeshLib::Element const* const pyramid,
142 std::vector<MeshLib::Node*> const& nodes,
143 std::vector<MeshLib::Element*>& new_elements)
144{
145 auto addTetrahedron =
146 [&pyramid, &nodes, &new_elements](std::array<std::size_t, 4> const ids)
147 {
148 new_elements.push_back(
149 createElement<MeshLib::Tet>(pyramid->nodes(), nodes, ids)
150 .release());
151 };
152
153 addTetrahedron({0, 1, 2, 4});
154 addTetrahedron({0, 2, 3, 4});
155
156 return 2;
157}
158
162 const std::vector<MeshLib::Node*>& nodes)
163{
164 std::array<std::size_t, 2> line_node_ids = {0, 0};
165 for (unsigned i = 1; i < element->getNumberOfBaseNodes(); ++i)
166 {
167 if (element->getNode(i)->getID() != element->getNode(0)->getID())
168 {
169 line_node_ids[1] = i;
170 break;
171 }
172 }
173 assert(line_node_ids[1] != 0);
174 return createElement<MeshLib::Line>(element->nodes(), nodes, line_node_ids)
175 .release();
176}
177
181 const std::vector<MeshLib::Node*>& nodes)
182{
183 // TODO?
184 // In theory three unique nodes could also be reduced to two lines e.g. with
185 // a quad where two diametral nodes collapse. This case is currently not
186 // implemented!
187 std::array<MeshLib::Node*, 3> tri_nodes;
188 tri_nodes[0] = nodes[element->getNode(0)->getID()];
189 tri_nodes[2] = nullptr;
190 for (unsigned i = 1; i < element->getNumberOfBaseNodes(); ++i)
191 {
192 if (element->getNode(i)->getID() != tri_nodes[0]->getID())
193 {
194 tri_nodes[1] = nodes[element->getNode(i)->getID()];
195 for (unsigned j = i + 1; j < element->getNumberOfBaseNodes(); ++j)
196 {
197 if (element->getNode(j)->getID() != tri_nodes[1]->getID())
198 {
199 tri_nodes[2] = nodes[element->getNode(j)->getID()];
200 break;
201 }
202 }
203 if (tri_nodes[2])
204 {
205 break;
206 }
207 }
208 }
209 assert(tri_nodes[2] != nullptr);
210 return new MeshLib::Tri(tri_nodes);
211}
212
216 MeshLib::Element const* const element,
217 std::vector<MeshLib::Node*> const& nodes,
218 unsigned const min_elem_dim = 1)
219{
220 std::array<MeshLib::Node*, 4> new_nodes;
221 unsigned count(0);
222 new_nodes[count++] = nodes[element->getNode(0)->getID()];
223 for (unsigned i = 1; i < element->getNumberOfBaseNodes(); ++i)
224 {
225 if (count > 3)
226 {
227 break;
228 }
229 bool unique_node(true);
230 for (unsigned j = 0; j < i; ++j)
231 {
232 if (element->getNode(i)->getID() == element->getNode(j)->getID())
233 {
234 unique_node = false;
235 break;
236 }
237 }
238 if (unique_node)
239 {
240 new_nodes[count++] = nodes[element->getNode(i)->getID()];
241 };
242 }
243
244 // test if quad or tet
245 const bool isQuad(MathLib::isCoplanar(*new_nodes[0], *new_nodes[1],
246 *new_nodes[2], *new_nodes[3]));
247 if (isQuad && min_elem_dim < 3)
248 {
249 MeshLib::Element* elem(new MeshLib::Quad(new_nodes));
250 for (unsigned i = 1; i < 3; ++i)
251 {
252 if (elem->validate().none())
253 {
254 return elem;
255 }
256
257 // change node order if not convex
258 MeshLib::Node* tmp = new_nodes[i + 1];
259 new_nodes[i + 1] = new_nodes[i];
260 new_nodes[i] = tmp;
261 }
262 return elem;
263 }
264 if (!isQuad)
265 {
266 return new MeshLib::Tet(new_nodes);
267 }
268 // is quad but min elem dim == 3
269
270 return nullptr;
271}
272
275void reducePyramid(MeshLib::Element const* const org_elem,
276 unsigned const n_unique_nodes,
277 std::vector<MeshLib::Node*> const& nodes,
278 std::vector<MeshLib::Element*>& new_elements,
279 unsigned const min_elem_dim)
280{
281 if (n_unique_nodes == 4)
282 {
283 MeshLib::Element* elem(
284 constructFourNodeElement(org_elem, nodes, min_elem_dim));
285 if (elem)
286 {
287 new_elements.push_back(elem);
288 }
289 }
290 else if (n_unique_nodes == 3 && min_elem_dim < 3)
291 {
292 new_elements.push_back(constructTri(org_elem, nodes));
293 }
294 else if (n_unique_nodes == 2 && min_elem_dim == 1)
295 {
296 new_elements.push_back(constructLine(org_elem, nodes));
297 }
298}
299
303unsigned reducePrism(MeshLib::Element const* const org_elem,
304 unsigned const n_unique_nodes,
305 std::vector<MeshLib::Node*> const& nodes,
306 std::vector<MeshLib::Element*>& new_elements,
307 unsigned const min_elem_dim)
308{
309 auto addTetrahedron =
310 [&org_elem, &nodes, &new_elements](std::array<std::size_t, 4> const ids)
311 {
312 new_elements.push_back(
313 createElement<MeshLib::Tet>(org_elem->nodes(), nodes, ids)
314 .release());
315 };
316
317 // TODO?
318 // In theory a node from the bottom triangle and a node from the top
319 // triangle that are not connected by an edge could collapse, resulting in a
320 // combination of tri and quad elements. This case is currently not tested.
321
322 // if one of the non-triangle edges collapsed, elem can be reduced to a
323 // pyramid, otherwise it will be two tets
324 if (n_unique_nodes == 5)
325 {
326 for (unsigned i = 0; i < 5; ++i)
327 {
328 for (unsigned j = i + 1; j < 6; ++j)
329 {
330 if (i != j && org_elem->getNode(i)->getID() ==
331 org_elem->getNode(j)->getID())
332 {
333 // non triangle edge collapsed
334 if (i % 3 == j % 3)
335 {
336 addTetrahedron(
337 {(i + 1) % 3, (i + 2) % 3, i, (i + 1) % 3 + 3});
338 addTetrahedron(
339 {(i + 1) % 3 + 3, (i + 2) % 3, i, (i + 2) % 3 + 3});
340 return 2;
341 }
342
343 // triangle edge collapsed
344 const unsigned i_offset = (i > 2) ? i - 3 : i + 3;
345 const unsigned j_offset = (i > 2) ? j - 3 : j + 3;
346 const unsigned k = MeshToolsLib::lutPrismThirdNode(i, j);
347 if (k == std::numeric_limits<unsigned>::max())
348 {
349 ERR("Unexpected error during prism reduction.");
350 return 0;
351 }
352 const unsigned k_offset = (i > 2) ? k - 3 : k + 3;
353
354 addTetrahedron({i_offset, j_offset, k_offset, i});
355
356 const unsigned l =
357 (MathLib::isCoplanar(*org_elem->getNode(i_offset),
358 *org_elem->getNode(k_offset),
359 *org_elem->getNode(i),
360 *org_elem->getNode(k)))
361 ? j
362 : i;
363 const unsigned l_offset = (i > 2) ? l - 3 : l + 3;
364 addTetrahedron({l_offset, k_offset, i, k});
365 return 2;
366 }
367 }
368 }
369 }
370 else if (n_unique_nodes == 4)
371 {
372 MeshLib::Element* const elem(
373 constructFourNodeElement(org_elem, nodes, min_elem_dim));
374 if (elem)
375 {
376 new_elements.push_back(elem);
377 }
378 }
379 else if (n_unique_nodes == 3 && min_elem_dim < 3)
380 {
381 new_elements.push_back(constructTri(org_elem, nodes));
382 }
383 else if (n_unique_nodes == 2 && min_elem_dim == 1)
384 {
385 new_elements.push_back(constructLine(org_elem, nodes));
386 }
387 return 1;
388}
389
392std::array<unsigned, 4> lutHexCuttingQuadNodes(unsigned id1, unsigned id2)
393{
394 if (id1 == 0 && id2 == 1)
395 {
396 return {3, 2, 5, 4};
397 }
398 if (id1 == 1 && id2 == 2)
399 {
400 return {0, 3, 6, 5};
401 }
402 if (id1 == 2 && id2 == 3)
403 {
404 return {1, 0, 7, 6};
405 }
406 if (id1 == 3 && id2 == 0)
407 {
408 return {2, 1, 4, 7};
409 }
410 if (id1 == 4 && id2 == 5)
411 {
412 return {0, 1, 6, 7};
413 }
414 if (id1 == 5 && id2 == 6)
415 {
416 return {1, 2, 7, 4};
417 }
418 if (id1 == 6 && id2 == 7)
419 {
420 return {2, 3, 4, 5};
421 }
422 if (id1 == 7 && id2 == 4)
423 {
424 return {3, 0, 5, 6};
425 }
426 if (id1 == 0 && id2 == 4)
427 {
428 return {3, 7, 5, 1};
429 }
430 if (id1 == 1 && id2 == 5)
431 {
432 return {0, 4, 6, 2};
433 }
434 if (id1 == 2 && id2 == 6)
435 {
436 return {1, 5, 7, 3};
437 }
438 if (id1 == 3 && id2 == 7)
439 {
440 return {2, 6, 4, 0};
441 }
442 if (id1 == 1 && id2 == 0)
443 {
444 return {2, 3, 4, 5};
445 }
446 if (id1 == 2 && id2 == 1)
447 {
448 return {3, 0, 5, 6};
449 }
450 if (id1 == 3 && id2 == 2)
451 {
452 return {0, 1, 6, 7};
453 }
454 if (id1 == 0 && id2 == 3)
455 {
456 return {1, 2, 7, 4};
457 }
458 if (id1 == 5 && id2 == 4)
459 {
460 return {1, 0, 7, 6};
461 }
462 if (id1 == 6 && id2 == 5)
463 {
464 return {2, 1, 4, 7};
465 }
466 if (id1 == 7 && id2 == 6)
467 {
468 return {3, 2, 5, 4};
469 }
470 if (id1 == 4 && id2 == 7)
471 {
472 return {0, 3, 6, 5};
473 }
474 if (id1 == 4 && id2 == 0)
475 {
476 return {7, 3, 1, 5};
477 }
478 if (id1 == 5 && id2 == 1)
479 {
480 return {4, 0, 2, 6};
481 }
482 if (id1 == 6 && id2 == 2)
483 {
484 return {5, 1, 3, 7};
485 }
486 if (id1 == 7 && id2 == 3)
487 {
488 return {6, 2, 0, 4};
489 }
490
491 OGS_FATAL(
492 "lutHexCuttingQuadNodes() for nodes {} and {} does not have a valid "
493 "return value.",
494 id1, id2);
495}
496
499unsigned lutHexDiametralNode(unsigned const id)
500{
501 constexpr std::array<unsigned, 8> hex_diametral_node_ids = {
502 {6, 7, 4, 5, 2, 3, 0, 1}};
503
504 return hex_diametral_node_ids[id];
505}
506
509std::pair<unsigned, unsigned> lutHexBackNodes(unsigned const i,
510 unsigned const j,
511 unsigned const k,
512 unsigned const l)
513{
514 // collapsed edges are *not* connected
515 if (lutHexDiametralNode(i) == k)
516 {
517 return {i, lutHexDiametralNode(l)};
518 }
519 if (lutHexDiametralNode(i) == l)
520 {
521 return {i, lutHexDiametralNode(k)};
522 }
523 if (lutHexDiametralNode(j) == k)
524 {
525 return {j, lutHexDiametralNode(l)};
526 }
527 if (lutHexDiametralNode(j) == l)
528 {
529 return {j, lutHexDiametralNode(k)};
530 }
531
532 // collapsed edges *are* connected
533 if (i == k)
534 {
535 return {lutHexDiametralNode(l), j};
536 }
537 if (i == l)
538 {
539 return {lutHexDiametralNode(k), j};
540 }
541 if (j == k)
542 {
543 return {lutHexDiametralNode(l), i};
544 }
545 if (j == l)
546 {
547 return {lutHexDiametralNode(k), i};
548 }
549
550 return {std::numeric_limits<unsigned>::max(),
551 std::numeric_limits<unsigned>::max()};
552}
553
554// In an element with 5 unique nodes, return the node that will be the top of
555// the resulting pyramid.
556unsigned findPyramidTopNode(MeshLib::Element const& element,
557 std::array<std::size_t, 4> const& base_node_ids)
558{
559 const std::size_t nNodes(element.getNumberOfBaseNodes());
560 for (std::size_t i = 0; i < nNodes; ++i)
561 {
562 bool top_node = true;
563 for (unsigned j = 0; j < 4; ++j)
564 {
565 if (element.getNode(i)->getID() == base_node_ids[j])
566 {
567 top_node = false;
568 }
569 }
570 if (top_node)
571 {
572 return i;
573 }
574 }
575 return std::numeric_limits<unsigned>::max(); // should never be reached if
576 // called correctly
577}
578
582unsigned reduceHex(MeshLib::Element const* const org_elem,
583 unsigned const n_unique_nodes,
584 std::vector<MeshLib::Node*> const& nodes,
585 std::vector<MeshLib::Element*>& new_elements,
586 unsigned const min_elem_dim)
587{
588 // TODO?
589 // if two diametral nodes collapse, all kinds of bizarre (2D-)element
590 // combinations could be the result. this case is currently not implemented!
591
592 if (n_unique_nodes == 7)
593 {
594 // reduce to prism + pyramid
595 for (unsigned i = 0; i < 7; ++i)
596 {
597 for (unsigned j = i + 1; j < 8; ++j)
598 {
599 if (org_elem->getNode(i)->getID() ==
600 org_elem->getNode(j)->getID())
601 {
602 const std::array<unsigned, 4> base_node_ids(
604 std::array<std::size_t, 5> const pyr_node_ids = {
605 base_node_ids[0], base_node_ids[1], base_node_ids[2],
606 base_node_ids[3], i};
607 new_elements.push_back(
609 nodes, pyr_node_ids)
610 .release());
611
612 if (i < 4 && j >= 4)
613 {
614 std::swap(i, j);
615 }
616 std::array<std::size_t, 6> const prism_node_ids{
617 base_node_ids[0], base_node_ids[3],
618 lutHexDiametralNode(j), base_node_ids[1],
619 base_node_ids[2], lutHexDiametralNode(i)};
620 new_elements.push_back(
621 createElement<MeshLib::Prism>(org_elem->nodes(), nodes,
622 prism_node_ids)
623 .release());
624 return 2;
625 }
626 }
627 }
628 }
629 else if (n_unique_nodes == 6)
630 {
631 // reduce to prism
632 for (unsigned i = 0; i < 6; ++i)
633 {
634 const MeshLib::Element* face(org_elem->getFace(i));
635 if (face->getNode(0)->getID() == face->getNode(1)->getID() &&
636 face->getNode(2)->getID() == face->getNode(3)->getID())
637 {
638 std::array<std::size_t, 6> const prism_node_ids{
640 getNodeIDinElement(*org_elem, face->getNode(0))),
642 getNodeIDinElement(*org_elem, face->getNode(1))),
643 getNodeIDinElement(*org_elem, face->getNode(2)),
645 getNodeIDinElement(*org_elem, face->getNode(2))),
647 getNodeIDinElement(*org_elem, face->getNode(3))),
648 getNodeIDinElement(*org_elem, face->getNode(0))};
649
650 new_elements.push_back(
651 createElement<MeshLib::Prism>(org_elem->nodes(), nodes,
652 prism_node_ids)
653 .release());
654 delete face;
655 return 1;
656 }
657 if (face->getNode(0)->getID() == face->getNode(3)->getID() &&
658 face->getNode(1)->getID() == face->getNode(2)->getID())
659 {
660 std::array<std::size_t, 6> const prism_node_ids{
662 getNodeIDinElement(*org_elem, face->getNode(0))),
664 getNodeIDinElement(*org_elem, face->getNode(3))),
665 getNodeIDinElement(*org_elem, face->getNode(2)),
667 getNodeIDinElement(*org_elem, face->getNode(1))),
669 getNodeIDinElement(*org_elem, face->getNode(2))),
670 getNodeIDinElement(*org_elem, face->getNode(0))};
671 new_elements.push_back(
672 createElement<MeshLib::Prism>(org_elem->nodes(), nodes,
673 prism_node_ids)
674 .release());
675 delete face;
676 return 1;
677 }
678 delete face;
679 }
680 // reduce to four tets -> divide into 2 prisms such that each has one
681 // collapsed node
682 for (unsigned i = 0; i < 7; ++i)
683 {
684 for (unsigned j = i + 1; j < 8; ++j)
685 {
686 if (org_elem->getNode(i)->getID() ==
687 org_elem->getNode(j)->getID())
688 {
689 for (unsigned k = i; k < 7; ++k)
690 {
691 for (unsigned l = k + 1; l < 8; ++l)
692 {
693 if (!(i == k && j == l) && org_elem->isEdge(i, j) &&
694 org_elem->isEdge(k, l) &&
695 org_elem->getNode(k)->getID() ==
696 org_elem->getNode(l)->getID())
697 {
698 const std::pair<unsigned, unsigned> back(
699 lutHexBackNodes(i, j, k, l));
700 if (back.first ==
701 std::numeric_limits<unsigned>::max() ||
702 back.second ==
703 std::numeric_limits<unsigned>::max())
704 {
705 ERR("Unexpected error during Hex "
706 "reduction");
707 return 0;
708 }
709
710 std::array<unsigned, 4> const cutting_plane(
711 lutHexCuttingQuadNodes(back.first,
712 back.second));
713 std::array<std::size_t, 6> const pris1_node_ids{
714 back.first, cutting_plane[0],
715 cutting_plane[3], back.second,
716 cutting_plane[1], cutting_plane[2]};
717 auto prism1 = createElement<MeshLib::Prism>(
718 org_elem->nodes(), nodes, pris1_node_ids);
719 unsigned nNewElements =
720 reducePrism(prism1.get(), 5, nodes,
721 new_elements, min_elem_dim);
722
723 std::array<std::size_t, 6> const pris2_node_ids{
724 lutHexDiametralNode(back.first),
725 cutting_plane[0],
726 cutting_plane[3],
727 lutHexDiametralNode(back.second),
728 cutting_plane[1],
729 cutting_plane[2]};
730 auto prism2 = createElement<MeshLib::Prism>(
731 org_elem->nodes(), nodes, pris2_node_ids);
732 nNewElements +=
733 reducePrism(prism2.get(), 5, nodes,
734 new_elements, min_elem_dim);
735 return nNewElements;
736 }
737 }
738 }
739 }
740 }
741 }
742 }
743 else if (n_unique_nodes == 5)
744 {
745 MeshLib::Element* tet1(constructFourNodeElement(org_elem, nodes));
746 std::array<std::size_t, 4> const first_four_node_ids = {
747 {tet1->getNode(0)->getID(), tet1->getNode(1)->getID(),
748 tet1->getNode(2)->getID(), tet1->getNode(3)->getID()}};
749 unsigned const fifth_node =
750 findPyramidTopNode(*org_elem, first_four_node_ids);
751
752 bool tet_changed(false);
754 {
755 delete tet1;
756 tet_changed = true;
757 std::array const tet1_nodes = {
758 nodes[first_four_node_ids[0]], nodes[first_four_node_ids[1]],
759 nodes[first_four_node_ids[2]],
760 nodes[org_elem->getNode(fifth_node)->getID()]};
761 new_elements.push_back(new MeshLib::Tet(tet1_nodes));
762 }
763 else
764 {
765 new_elements.push_back(tet1);
766 }
767
768 std::array const tet2_nodes = {
769 (tet_changed) ? nodes[first_four_node_ids[0]]
770 : nodes[first_four_node_ids[1]],
771 nodes[first_four_node_ids[2]], nodes[first_four_node_ids[3]],
772 nodes[org_elem->getNode(fifth_node)->getID()]};
773 new_elements.push_back(new MeshLib::Tet(tet2_nodes));
774 return 2;
775 }
776 else if (n_unique_nodes == 4)
777 {
778 MeshLib::Element* elem(
779 constructFourNodeElement(org_elem, nodes, min_elem_dim));
780 if (elem)
781 {
782 new_elements.push_back(elem);
783 return 1;
784 }
785 }
786 else if (n_unique_nodes == 3 && min_elem_dim < 3)
787 {
788 new_elements.push_back(constructTri(org_elem, nodes));
789 return 1;
790 }
791 else if (min_elem_dim == 1)
792 {
793 new_elements.push_back(constructLine(org_elem, nodes));
794 return 1;
795 }
796 return 0;
797}
798
806std::size_t subdivideElement(MeshLib::Element const* const element,
807 std::vector<MeshLib::Node*> const& nodes,
808 std::vector<MeshLib::Element*>& elements)
809{
811 {
812 return subdivideQuad(element, nodes, elements);
813 }
815 {
816 return subdivideHex(element, nodes, elements);
817 }
819 {
820 return subdividePyramid(element, nodes, elements);
821 }
823 {
824 return subdividePrism(element, nodes, elements);
825 }
826 return 0;
827}
828
829// Revises an element by removing collapsed nodes, using the nodes vector from
830// the result mesh.
831std::size_t reduceElement(MeshLib::Element const* const element,
832 unsigned const n_unique_nodes,
833 std::vector<MeshLib::Node*> const& nodes,
834 std::vector<MeshLib::Element*>& elements,
835 unsigned const min_elem_dim)
836{
837 /***************
838 * TODO: modify neighbouring elements if one elements has been subdivided
839 ***************/
841 min_elem_dim == 1)
842 {
843 elements.push_back(constructLine(element, nodes));
844 return 1;
845 }
846 if ((element->getGeomType() == MeshLib::MeshElemType::QUAD) ||
848 {
849 if (n_unique_nodes == 3 && min_elem_dim < 3)
850 {
851 elements.push_back(constructTri(element, nodes));
852 }
853 else if (min_elem_dim == 1)
854 {
855 elements.push_back(constructLine(element, nodes));
856 }
857 return 1;
858 }
860 {
861 return reduceHex(element, n_unique_nodes, nodes, elements,
862 min_elem_dim);
863 }
865 {
866 reducePyramid(element, n_unique_nodes, nodes, elements, min_elem_dim);
867 return 1;
868 }
870 {
871 return reducePrism(element, n_unique_nodes, nodes, elements,
872 min_elem_dim);
873 }
874
875 ERR("Unknown element type.");
876 return 0;
877}
880unsigned getNumberOfUniqueNodes(MeshLib::Element const* const element)
881{
882 unsigned const nNodes(element->getNumberOfBaseNodes());
883 unsigned count(nNodes);
884
885 for (unsigned i = 0; i < nNodes - 1; ++i)
886 {
887 for (unsigned j = i + 1; j < nNodes; ++j)
888 {
889 if (element->getNode(i)->getID() == element->getNode(j)->getID())
890 {
891 count--;
892 break;
893 }
894 }
895 }
896 return count;
897}
898
899template <typename T>
900void fillNodeProperty(std::vector<T>& new_prop,
901 std::vector<T> const& old_prop,
902 std::vector<size_t> const& node_ids)
903{
904 std::size_t const n_nodes = node_ids.size();
905 for (std::size_t i = 0; i < n_nodes; ++i)
906 {
907 if (node_ids[i] != i)
908 {
909 continue;
910 }
911 new_prop.push_back(old_prop[i]);
912 }
913}
914
915template <typename T>
916void fillElemProperty(std::vector<T>& new_prop,
917 std::vector<T> const& old_prop,
918 std::vector<size_t> const& elem_ids)
919{
920 std::transform(elem_ids.cbegin(), elem_ids.cend(),
921 std::back_inserter(new_prop),
922 [&](std::size_t const i) { return old_prop[i]; });
923}
924
929 std::vector<std::size_t> const& node_ids,
930 std::vector<std::size_t> const& elem_ids)
931{
932 auto const prop_names = props.getPropertyVectorNames();
933 MeshLib::Properties new_properties;
934
935 for (auto name : prop_names)
936 {
938 1))
939 {
940 auto const* p = props.getPropertyVector<int>(
942 auto new_node_vec = new_properties.createNewPropertyVector<int>(
944 fillNodeProperty(*new_node_vec, *p, node_ids);
945 continue;
946 }
948 1))
949 {
950 auto const* p = props.getPropertyVector<float>(
952 auto new_node_vec = new_properties.createNewPropertyVector<float>(
954 fillNodeProperty(*new_node_vec, *p, node_ids);
955 continue;
956 }
957 if (props.existsPropertyVector<double>(name,
959 {
960 auto const* p = props.getPropertyVector<double>(
962 auto new_node_vec = new_properties.createNewPropertyVector<double>(
964 fillNodeProperty(*new_node_vec, *p, node_ids);
965 continue;
966 }
968 1))
969 {
970 auto const* p = props.getPropertyVector<int>(
972 auto new_cell_vec = new_properties.createNewPropertyVector<int>(
974 fillElemProperty(*new_cell_vec, *p, elem_ids);
975 continue;
976 }
978 1))
979 {
980 auto const* p = props.getPropertyVector<float>(
982 auto new_cell_vec = new_properties.createNewPropertyVector<float>(
984 fillElemProperty(*new_cell_vec, *p, elem_ids);
985 continue;
986 }
987 if (props.existsPropertyVector<double>(name,
989 {
990 auto const* p = props.getPropertyVector<double>(
992 auto new_cell_vec = new_properties.createNewPropertyVector<double>(
994 fillElemProperty(*new_cell_vec, *p, elem_ids);
995 continue;
996 }
997 WARN("PropertyVector {:s} not being converted.", name);
998 }
999 return new_properties;
1000}
1001} // namespace
1002
1003namespace MeshToolsLib
1004{
1006
1007unsigned MeshRevision::getNumberOfCollapsibleNodes(double const eps) const
1008{
1009 std::vector<std::size_t> const id_map = collapseNodeIndices(eps);
1010 std::size_t const nNodes = id_map.size();
1011 unsigned count(0);
1012 for (std::size_t i = 0; i < nNodes; ++i)
1013 {
1014 if (i != id_map[i])
1015 {
1016 count++;
1017 }
1018 }
1019 return count;
1020}
1021
1022MeshLib::Mesh* MeshRevision::simplifyMesh(const std::string& new_mesh_name,
1023 double const eps,
1024 unsigned const min_elem_dim) const
1025{
1026 if (this->_mesh.getNumberOfElements() == 0)
1027 {
1028 return nullptr;
1029 }
1030
1031 std::vector<MeshLib::Element*> const& elements(this->_mesh.getElements());
1032 auto const node_ids = collapseNodeIndices(eps);
1033 std::vector<MeshLib::Node*> new_nodes =
1034 this->constructNewNodesArray(node_ids);
1035 std::vector<MeshLib::Element*> new_elements;
1036 std::vector<std::size_t> element_ids;
1037
1038 for (std::size_t k(0); k < elements.size(); ++k)
1039 {
1040 MeshLib::Element const* const elem(elements[k]);
1041 unsigned const n_unique_nodes(getNumberOfUniqueNodes(elem));
1042 if (n_unique_nodes == elem->getNumberOfBaseNodes() &&
1043 elem->getDimension() >= min_elem_dim)
1044 {
1045 ElementErrorCode const e = elem->validate();
1047 {
1048 std::size_t const n_new_elements(
1049 subdivideElement(elem, new_nodes, new_elements));
1050 if (n_new_elements == 0)
1051 {
1052 ERR("Element {:d} has unknown element type.", k);
1054 BaseLib::cleanupVectorElements(new_nodes, new_elements);
1055 return nullptr;
1056 }
1057 element_ids.insert(element_ids.end(), n_new_elements, k);
1058 }
1059 else
1060 {
1061 new_elements.push_back(MeshLib::copyElement(elem, new_nodes));
1062 element_ids.push_back(k);
1063 }
1064 }
1065 else if (n_unique_nodes < elem->getNumberOfBaseNodes() &&
1066 n_unique_nodes > 1)
1067 {
1068 std::size_t const n_new_elements(reduceElement(
1069 elem, n_unique_nodes, new_nodes, new_elements, min_elem_dim));
1070 element_ids.insert(element_ids.end(), n_new_elements, k);
1071 }
1072 else
1073 {
1074 ERR("Something is wrong, more unique nodes than actual nodes");
1075 }
1076 }
1077
1078 auto const& props = _mesh.getProperties();
1079 MeshLib::Properties const new_properties =
1080 copyProperties(props, node_ids, element_ids);
1081
1083 if (!new_elements.empty())
1084 {
1085 return new MeshLib::Mesh(new_mesh_name, new_nodes, new_elements,
1086 true /* compute_element_neighbors */,
1087 new_properties);
1088 }
1089
1090 BaseLib::cleanupVectorElements(new_nodes, new_elements);
1091 return nullptr;
1092}
1093
1094std::vector<std::size_t> MeshRevision::collapseNodeIndices(
1095 double const eps) const
1096{
1097 const std::vector<MeshLib::Node*>& nodes(_mesh.getNodes());
1098 const std::size_t nNodes(_mesh.getNumberOfNodes());
1099 std::vector<std::size_t> id_map(nNodes);
1100 const double half_eps(eps / 2.0);
1101 const double sqr_eps(eps * eps);
1102 std::iota(id_map.begin(), id_map.end(), 0);
1103
1104 GeoLib::Grid<MeshLib::Node> const grid(nodes.begin(), nodes.end(), 64);
1105
1106 for (std::size_t k = 0; k < nNodes; ++k)
1107 {
1108 MeshLib::Node const* const node(nodes[k]);
1109 if (node->getID() != k)
1110 {
1111 continue;
1112 }
1113 std::vector<std::vector<MeshLib::Node*> const*> const node_vectors(
1114 grid.getPntVecsOfGridCellsIntersectingCube(*node, half_eps));
1115
1116 const std::size_t nVectors(node_vectors.size());
1117 for (std::size_t i = 0; i < nVectors; ++i)
1118 {
1119 const std::vector<MeshLib::Node*>& cell_vector(*node_vectors[i]);
1120 const std::size_t nGridCellNodes(cell_vector.size());
1121 for (std::size_t j = 0; j < nGridCellNodes; ++j)
1122 {
1123 MeshLib::Node const* const test_node(cell_vector[j]);
1124 // are node indices already identical (i.e. nodes will be
1125 // collapsed)
1126 if (id_map[node->getID()] == id_map[test_node->getID()])
1127 {
1128 continue;
1129 }
1130
1131 // if test_node has already been collapsed to another node x,
1132 // ignore it (if the current node would need to be collapsed
1133 // with x it would already have happened when x was tested)
1134 if (test_node->getID() != id_map[test_node->getID()])
1135 {
1136 continue;
1137 }
1138
1139 // calc distance
1140 if (MathLib::sqrDist(*node, *test_node) < sqr_eps)
1141 {
1142 id_map[test_node->getID()] = node->getID();
1143 }
1144 }
1145 }
1146 }
1147 return id_map;
1148}
1149
1150std::vector<MeshLib::Node*> MeshRevision::constructNewNodesArray(
1151 const std::vector<std::size_t>& id_map) const
1152{
1153 const std::vector<MeshLib::Node*>& nodes(_mesh.getNodes());
1154 const std::size_t nNodes(nodes.size());
1155 std::vector<MeshLib::Node*> new_nodes;
1156 new_nodes.reserve(nNodes);
1157 for (std::size_t k = 0; k < nNodes; ++k)
1158 {
1159 // all nodes that have not been collapsed with other nodes are copied
1160 // into new array
1161 if (nodes[k]->getID() == id_map[k])
1162 {
1163 std::size_t const id(new_nodes.size());
1164 new_nodes.push_back(new MeshLib::Node(
1165 (*nodes[k])[0], (*nodes[k])[1], (*nodes[k])[2], id));
1166 nodes[k]->setID(id); // the node in the old array gets the index of
1167 // the same node in the new array
1168 }
1169 // the other nodes are not copied and get the index of the nodes they
1170 // will have been collapsed with
1171 else
1172 {
1173 nodes[k]->setID(nodes[id_map[k]]->getID());
1174 }
1175 }
1176 return new_nodes;
1177}
1178
1179} // namespace MeshToolsLib
Definition of Duplicate functions.
#define OGS_FATAL(...)
Definition Error.h:26
Definition of the Grid class.
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Definition of the class Properties that implements a container of properties.
Definition of the MeshRevision class.
Definition of the Mesh class.
Collects error flags for mesh elements.
std::vector< std::vector< POINT * > const * > getPntVecsOfGridCellsIntersectingCube(P const &center, double half_len) const
Definition Grid.h:254
std::size_t getID() const
virtual MeshElemType getGeomType() const =0
virtual ElementErrorCode validate() const =0
virtual const Element * getFace(unsigned i) const =0
Returns the i-th face of the element.
virtual unsigned getNumberOfBaseNodes() const =0
virtual const Node * getNode(unsigned idx) const =0
virtual bool isEdge(unsigned i, unsigned j) const =0
Returns true if these two indices form an edge and false otherwise.
virtual constexpr unsigned getDimension() const =0
Get dimension of the mesh element.
constexpr std::span< Node *const > nodes() const
Span of element's nodes, their pointers actually.
Definition Element.h:72
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:106
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:109
void resetNodeIDs()
Resets the IDs of all mesh-nodes to their position in the node vector.
Definition Mesh.cpp:159
Properties & getProperties()
Definition Mesh.h:134
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:100
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:97
Property manager on mesh items. Class Properties manages scalar, vector or matrix properties....
Definition Properties.h:36
std::vector< std::string > getPropertyVectorNames() const
bool existsPropertyVector(std::string_view name) const
Definition Properties.h:74
PropertyVector< T > * createNewPropertyVector(std::string_view name, MeshItemType mesh_item_type, std::size_t n_components=1)
Definition Properties.h:17
PropertyVector< T > const * getPropertyVector(std::string_view name) const
MeshRevision(MeshLib::Mesh &mesh)
unsigned getNumberOfCollapsibleNodes(double eps=std::numeric_limits< double >::epsilon()) const
Returns the number of potentially collapsible nodes.
MeshLib::Mesh & _mesh
The original mesh used for constructing the class.
MeshLib::Mesh * simplifyMesh(const std::string &new_mesh_name, double eps, unsigned min_elem_dim=1) const
std::vector< MeshLib::Node * > constructNewNodesArray(const std::vector< std::size_t > &id_map) const
std::vector< std::size_t > collapseNodeIndices(double eps) const
void cleanupVectorElements(std::vector< T * > &items)
Definition Algorithm.h:252
bool isCoplanar(const MathLib::Point3d &a, const MathLib::Point3d &b, const MathLib::Point3d &c, const MathLib::Point3d &d)
Checks if the four given points are located on a plane.
double sqrDist(MathLib::Point3d const &p0, MathLib::Point3d const &p1)
Definition Point3d.cpp:26
MeshLib specific, lazy, non-owning, non-mutating, composable range views.
Definition Mesh.h:223
TemplateElement< MeshLib::TetRule4 > Tet
Definition Tet.h:25
TemplateElement< MeshLib::TriRule3 > Tri
Definition Tri.h:26
Element * copyElement(Element const *const element, const std::vector< Node * > &nodes, std::vector< std::size_t > const *const id_map)
unsigned lutPrismThirdNode(unsigned const id1, unsigned const id2)
std::size_t reduceElement(MeshLib::Element const *const element, unsigned const n_unique_nodes, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &elements, unsigned const min_elem_dim)
unsigned lutHexDiametralNode(unsigned const id)
MeshLib::Element * constructTri(MeshLib::Element const *const element, const std::vector< MeshLib::Node * > &nodes)
MeshLib::Element * constructLine(MeshLib::Element const *const element, const std::vector< MeshLib::Node * > &nodes)
unsigned reduceHex(MeshLib::Element const *const org_elem, unsigned const n_unique_nodes, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements, unsigned const min_elem_dim)
unsigned reducePrism(MeshLib::Element const *const org_elem, unsigned const n_unique_nodes, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements, unsigned const min_elem_dim)
unsigned getNumberOfUniqueNodes(MeshLib::Element const *const element)
unsigned subdivideHex(MeshLib::Element const *const hex, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements)
Subdivides a Hex with nonplanar faces into tets.
std::unique_ptr< MeshLib::Element > createElement(std::span< MeshLib::Node *const > const element_nodes, std::vector< MeshLib::Node * > const &nodes, std::array< std::size_t, ElementType::n_all_nodes > const local_ids)
std::size_t subdivideElement(MeshLib::Element const *const element, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &elements)
void reducePyramid(MeshLib::Element const *const org_elem, unsigned const n_unique_nodes, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements, unsigned const min_elem_dim)
MeshLib::Properties copyProperties(MeshLib::Properties const &props, std::vector< std::size_t > const &node_ids, std::vector< std::size_t > const &elem_ids)
unsigned subdividePrism(MeshLib::Element const *const prism, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements)
Subdivides a prism with nonplanar quad faces into two tets.
std::array< unsigned, 4 > lutHexCuttingQuadNodes(unsigned id1, unsigned id2)
std::pair< unsigned, unsigned > lutHexBackNodes(unsigned const i, unsigned const j, unsigned const k, unsigned const l)
unsigned subdividePyramid(MeshLib::Element const *const pyramid, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements)
Subdivides a pyramid with a nonplanar base into two tets.
unsigned findPyramidTopNode(MeshLib::Element const &element, std::array< std::size_t, 4 > const &base_node_ids)
MeshLib::Element * constructFourNodeElement(MeshLib::Element const *const element, std::vector< MeshLib::Node * > const &nodes, unsigned const min_elem_dim=1)
unsigned subdivideQuad(MeshLib::Element const *const quad, std::vector< MeshLib::Node * > const &nodes, std::vector< MeshLib::Element * > &new_elements)
Subdivides a nonplanar quad into two triangles.
void fillElemProperty(std::vector< T > &new_prop, std::vector< T > const &old_prop, std::vector< size_t > const &elem_ids)
void fillNodeProperty(std::vector< T > &new_prop, std::vector< T > const &old_prop, std::vector< size_t > const &node_ids)