libMesh
Loading...
Searching...
No Matches
mesh_tet_interface.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2023 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18#include "libmesh/libmesh_config.h"
19
20
21// C++ includes
22#include <sstream>
23
24// Local includes
25#include "libmesh/mesh_tet_interface.h"
26
27#include "libmesh/boundary_info.h"
28#include "libmesh/elem.h"
29#include "libmesh/elem_range.h"
30#include "libmesh/mesh_modification.h"
31#include "libmesh/mesh_serializer.h"
32#include "libmesh/mesh_tools.h"
33#include "libmesh/remote_elem.h"
34#include "libmesh/unstructured_mesh.h"
35
37
38namespace {
39 using namespace libMesh;
40
41 std::unordered_set<Elem *>
42 flood_component (std::unordered_set<Elem *> & all_components,
43 Elem * elem)
44 {
45 libmesh_assert(!all_components.count(elem));
46
47 std::unordered_set<Elem *> current_component;
48
49 std::unordered_set<Elem *> elements_to_consider = {elem};
50
51 while (!elements_to_consider.empty())
52 {
53 std::unordered_set<Elem *> next_elements_to_consider;
54
55 for (Elem * considering : elements_to_consider)
56 {
57 all_components.insert(considering);
58 current_component.insert(considering);
59
60 for (auto s : make_range(considering->n_sides()))
61 {
62 Elem * neigh = considering->neighbor_ptr(s);
63
64 libmesh_error_msg_if
65 (!neigh,
66 "Tet generation encountered a 2D element with a null neighbor, but a\n"
67 "boundary must be a 2D closed manifold (surface).\n");
68
69 if (all_components.find(neigh) == all_components.end())
70 next_elements_to_consider.insert(neigh);
71 }
72 }
73 elements_to_consider = next_elements_to_consider;
74 }
75
76 return current_component;
77 }
78
79 // Returns six times the signed volume of a tet formed by the given
80 // 3 points and the origin
81 Real six_times_signed_tet_volume (const Point & p1,
82 const Point & p2,
83 const Point & p3)
84 {
85 return p1(0)*p2(1)*p3(2)
86 - p1(0)*p3(1)*p2(2)
87 - p2(0)*p1(1)*p3(2)
88 + p2(0)*p3(1)*p1(2)
89 + p3(0)*p1(1)*p2(2)
90 - p3(0)*p2(1)*p1(2);
91 }
92
93 // Returns six times the signed volume of the space defined by the
94 // manifold of surface elements in component
95 Real six_times_signed_volume (const std::unordered_set<Elem *> component)
96 {
97 Real six_vol = 0;
98
99 for (const Elem * elem: component)
100 {
101 libmesh_assert_equal_to(elem->dim(), 2);
102 for (auto n : make_range(elem->n_vertices()-2))
103 six_vol += six_times_signed_tet_volume(elem->point(0),
104 elem->point(n+1),
105 elem->point(n+2));
106 }
107
108 return six_vol;
109 }
110}
111
112namespace libMesh
113{
114
115//----------------------------------------------------------------------
116// MeshTetInterface class members
118 _verbosity(0), _desired_volume(0), _smooth_after_generating(false),
119 _elem_type(TET4), _mesh(mesh)
120{
121}
122
123
125
126
128 (std::unique_ptr<std::vector<std::unique_ptr<UnstructuredMesh>>> holes)
129{
130 _holes = std::move(holes);
131}
132
133
135{
136 // If we've been handed an unprepared mesh then we need to be made
137 // aware of that and fix that; we're relying on neighbor pointers.
139
140 if (!mesh.is_prepared())
142
143 // We'll return a bounding box for use by subclasses in basic sanity checks.
144 BoundingBox surface_bb;
145
146 // First convert all volume boundaries to surface elements; this
147 // gives us a manifold bounding the mesh, though it may not be a
148 // connected manifold even if the volume mesh was connected.
149 {
150 // Make sure ids are in sync and valid on a DistributedMesh
151 const dof_id_type max_orig_id = mesh.max_elem_id();
152#ifdef LIBMESH_ENABLE_UNIQUE_ID
153 const unique_id_type max_unique_id = mesh.parallel_max_unique_id();
154#endif
155
156 // Change this if we add arbitrary polyhedra...
157 const dof_id_type max_sides = 6;
158
159 std::unordered_set<Elem *> elems_to_delete;
160
161 std::vector<std::unique_ptr<Elem>> elems_to_add;
162
163 // Convert all faces to surface elements
164 for (auto * elem : mesh.active_element_ptr_range())
165 {
166 libmesh_error_msg_if (elem->dim() < 2,
167 "Cannot use meshes with 0D or 1D elements to define a volume");
168
169 // If we've already got 2D elements then those are (part of)
170 // our surface.
171 if (elem->dim() == 2)
172 continue;
173
174 // 3D elements will be removed after we've extracted their
175 // surface faces.
176 elems_to_delete.insert(elem);
177
178 for (auto s : make_range(elem->n_sides()))
179 {
180 // If there's a neighbor on this side then there's not a
181 // boundary
182 if (elem->neighbor_ptr(s))
183 {
184 // We're not supporting AMR meshes here yet
185 if (elem->level() != elem->neighbor_ptr(s)->level())
186 libmesh_not_implemented_msg
187 ("Tetrahedralizaton of adapted meshes is not currently supported");
188 continue;
189 }
190
191 elems_to_add.push_back(elem->build_side_ptr(s));
192 Elem * side_elem = elems_to_add.back().get();
193
194 // Wipe the interior_parent before it can become a
195 // dangling pointer later
196 side_elem->set_interior_parent(nullptr);
197
198 // If the mesh is replicated then its automatic id
199 // setting is fine. If not, then we need unambiguous ids
200 // independent of element traversal.
201 if (!mesh.is_replicated())
202 {
203 side_elem->set_id(max_orig_id + max_sides*elem->id() + s);
204#ifdef LIBMESH_ENABLE_UNIQUE_ID
205 side_elem->set_unique_id(max_unique_id + max_sides*elem->id() + s);
206#endif
207 }
208 }
209 }
210
211 // If the mesh is replicated then its automatic neighbor finding
212 // is fine. If not, then we need to insert them ourselves, but
213 // it's easy because we can use the fact (from our implementation
214 // above) that our new elements have no parents or children, plus
215 // the fact (from the tiny fraction of homology I understand) that
216 // a manifold boundary is a manifold with no boundary.
217 //
218 // See UnstructuredMesh::find_neighbors() for more explanation of
219 // (a more complicated version of) the algorithm here.
220 if (!mesh.is_replicated())
221 {
222 typedef dof_id_type key_type;
223 typedef std::pair<Elem *, unsigned char> val_type;
224 typedef std::unordered_multimap<key_type, val_type> map_type;
225 map_type side_to_elem_map;
226
227 std::unique_ptr<Elem> my_side, their_side;
228
229 for (auto & elem : elems_to_add)
230 {
231 for (auto s : elem->side_index_range())
232 {
233 if (elem->neighbor_ptr(s))
234 continue;
235 const dof_id_type key = elem->low_order_key(s);
236 auto bounds = side_to_elem_map.equal_range(key);
237 if (bounds.first != bounds.second)
238 {
239 elem->side_ptr(my_side, s);
240 while (bounds.first != bounds.second)
241 {
242 Elem * potential_neighbor = bounds.first->second.first;
243 const unsigned int ns = bounds.first->second.second;
244 potential_neighbor->side_ptr(their_side, ns);
245 if (*my_side == *their_side)
246 {
247 elem->set_neighbor(s, potential_neighbor);
248 potential_neighbor->set_neighbor(ns, elem.get());
249 side_to_elem_map.erase (bounds.first);
250 break;
251 }
252 ++bounds.first;
253 }
254
255 if (!elem->neighbor_ptr(s))
256 side_to_elem_map.emplace
257 (key, std::make_pair(elem.get(), cast_int<unsigned char>(s)));
258 }
259 }
260 }
261
262 // At this point we *should* have a match for everything, so
263 // anything we don't have a match for is remote.
264 for (auto & elem : elems_to_add)
265 for (auto s : elem->side_index_range())
266 if (!elem->neighbor_ptr(s))
267 elem->set_neighbor(s, const_cast<RemoteElem*>(remote_elem));
268 }
269
270 // Remove volume and edge elements
271 for (Elem * elem : elems_to_delete)
272 mesh.delete_elem(elem);
273
274 // Add the new elements outside the loop so we don't risk
275 // invalidating iterators.
276 for (auto & elem : elems_to_add)
277 mesh.add_elem(std::move(elem));
278 }
279
280 // Fix up neighbor pointers, element counts, etc.
282
283 // We're making tets; we need to start with tris
285
286 // Partition surface into connected components. At this point I'm
287 // finally going to give up and serialize, because at least we got
288 // from 3D down to 2D first, and because I don't want to have to
289 // turn flood_component into a while loop with a parallel sync in
290 // the middle, and because we do have to serialize *eventually*
291 // anyways unless we get a parallel tetrahedralizer backend someday.
292 MeshSerializer mesh_serializer(mesh);
293
294 std::vector<std::unordered_set<Elem *>> components;
295 std::unordered_set<Elem *> in_component;
296
297 for (auto * elem : mesh.element_ptr_range())
298 if (!in_component.count(elem))
299 components.emplace_back(flood_component(in_component, elem));
300
301 const std::unordered_set<Elem *> * biggest_component = nullptr;
302 Real biggest_six_vol = 0;
303 for (const auto & component : components)
304 {
305 Real six_vol = six_times_signed_volume(component);
306 if (std::abs(six_vol) > std::abs(biggest_six_vol))
307 {
308 biggest_six_vol = six_vol;
309 biggest_component = &component;
310 }
311 }
312
313 if (!biggest_component)
314 libmesh_error_msg("No non-zero-volume component found among " <<
315 components.size() << " boundary components");
316
317 for (const auto & component : components)
318 if (&component != biggest_component)
319 {
320 for (Elem * elem: component)
321 mesh.delete_elem(elem);
322 }
323 else
324 {
325 for (Elem * elem: component)
326 {
327 if (biggest_six_vol < 0)
328 elem->flip(&mesh.get_boundary_info());
329
330 for (auto & node : elem->node_ref_range())
331 surface_bb.union_with(node);
332 }
333 }
334
336
337 return surface_bb;
338}
339
340
341std::set<MeshTetInterface::SurfaceIntegrity> MeshTetInterface::check_hull_integrity() const
342{
343 // Check for easy return: if the Mesh is empty (i.e. if
344 // somebody called triangulate_conformingDelaunayMesh on
345 // a Mesh with no elements, then hull integrity check must
346 // fail...
347 if (_mesh.n_elem() == 0)
348 return {EMPTY_MESH};
349
350 std::set<MeshTetInterface::SurfaceIntegrity> returnval;
351
353 const Point extents = bb.max() - bb.min();
354 if (extents(0) == 0 ||
355 extents(1) == 0 ||
356 extents(2) == 0)
357 returnval.insert(DEGENERATE_MESH);
358
359 // Figure a area to use for relative tolerances when detecting
360 // degenerate elements
361 const Real ref_area = std::abs(extents(0) * extents(1)) +
362 std::abs(extents(0) * extents(2)) +
363 std::abs(extents(1) * extents(2));
364
365 struct TriChecker {
366 std::set<MeshTetInterface::SurfaceIntegrity> my_returnval;
367 const Real my_ref_area;
368 const unsigned int my_verbosity;
369
370 TriChecker (Real ref_area, unsigned int verbosity) :
371 my_returnval(), my_ref_area(ref_area),
372 my_verbosity(verbosity) {}
373 TriChecker (TriChecker & other, Threads::split) :
374 my_returnval(), my_ref_area(other.my_ref_area),
375 my_verbosity(other.my_verbosity) {}
376
377 void operator()(const ConstElemRange & range) {
378
379 for (const Elem * elem : range)
380 {
381 // Check for proper element type
382 if (elem->type() != TRI3)
383 {
384 if (my_verbosity >= 50)
385 std::cerr << "Non-Tri3: " << elem->get_info() << std::endl;
386 my_returnval.insert(NON_TRI3);
387 }
388
389 // Make sure it's a decent element.
390 if (elem->volume() < my_ref_area * TOLERANCE * TOLERANCE)
391 {
392 if (my_verbosity >= 50)
393 std::cerr << "Degenerate element: " << elem->get_info() << std::endl;
394 my_returnval.insert(DEGENERATE_ELEMENT);
395 }
396
397 for (auto s : elem->side_index_range())
398 {
399 const Elem * const neigh = elem->neighbor_ptr(s);
400
401 if (neigh == nullptr)
402 {
403 if (my_verbosity >= 50)
404 std::cerr << "Element missing neighbor " << s << ": " << elem->get_info() << std::endl;
405 my_returnval.insert(MISSING_NEIGHBOR);
406 continue;
407 }
408
409 // Make sure our neighbor points back to us
410 const unsigned int nn = neigh->which_neighbor_am_i(elem);
411
412 if (nn >= 3)
413 {
414 if (my_verbosity >= 50)
415 std::cerr << "Element missing backlink " << s << ": " << elem->get_info() << std::endl;
416 my_returnval.insert(MISSING_BACKLINK);
417 continue;
418 }
419
420 // Our neighbor should have the same the edge nodes we do on
421 // the neighboring edgei
422 const Node * const n1 = elem->node_ptr(s);
423 const Node * const n2 = elem->node_ptr((s+1)%3);
424
425 const unsigned int i1 = neigh->local_node(n1->id());
426 const unsigned int i2 = neigh->local_node(n2->id());
427 if (i1 >= 3 || i2 >= 3)
428 {
429 if (my_verbosity >= 50)
430 std::cerr << "Element with bad neighbor " << s << " nodes: " << elem->get_info() << std::endl;
431 my_returnval.insert(BAD_NEIGHBOR_NODES);
432 continue;
433 }
434
435 // It should have those edge nodes in the opposite order
436 // (because they have the same orientation we do)
437 if ((i2 + 1)%3 != i1)
438 {
439 if (my_verbosity >= 50)
440 std::cerr << "Element orientation mismatch with neighbor " << s << ": " << elem->get_info() << std::endl;
441 my_returnval.insert(NON_ORIENTED);
442 continue;
443 }
444
445 // And it should have those edge nodes in the expected
446 // places relative to its neighbor link
447 if (i2 != nn)
448 {
449 if (my_verbosity >= 50)
450 std::cerr << "Element with bad links on neighbor " << s << ": " << elem->get_info() << std::endl;
451 my_returnval.insert(BAD_NEIGHBOR_LINKS);
452 continue;
453 }
454 }
455 }
456 }
457
458 void join(TriChecker & other) {
459 my_returnval.merge(other.my_returnval);
460 }
461 };
462
463 TriChecker checker (ref_area, this->_verbosity);
464
466 (this->_mesh.active_local_element_stored_range(), checker);
467
468 // Join problems found in threaded loop
469 returnval.merge(checker.my_returnval);
470
471 // Join problems found on other ranks
472 std::set<char> int_set;
473 std::transform
474 (returnval.begin(), returnval.end(),
475 std::inserter<std::set<char>>(int_set, int_set.end()),
476 [](SurfaceIntegrity i){return int(i);});
477 _mesh.comm().set_union(int_set);
478 std::transform
479 (int_set.begin(), int_set.end(),
480 std::inserter<std::set<SurfaceIntegrity>>(returnval, returnval.end()),
481 [](int i){return SurfaceIntegrity(i);});
482
483 return returnval;
484}
485
486
487
488std::set<MeshTetInterface::SurfaceIntegrity> MeshTetInterface::improve_hull_integrity()
489{
490 // We don't really do anything parallel here, but we aspire to.
491 libmesh_parallel_only(this->_mesh.comm());
492
493 std::set<MeshTetInterface::SurfaceIntegrity> integrityproblems =
494 this->check_hull_integrity();
495
496 // If we have no problem, or a problem we can't fix, we're done.
497 if (integrityproblems.empty() ||
498 integrityproblems.count(NON_TRI3) ||
499 integrityproblems.count(EMPTY_MESH))
500 return integrityproblems;
501
502 // Possibly the user gave us an unprepared mesh with missing or bad
503 // neighbor links?
504 if (integrityproblems.count(MISSING_NEIGHBOR) ||
505 integrityproblems.count(MISSING_BACKLINK) ||
506 integrityproblems.count(BAD_NEIGHBOR_LINKS))
507 {
508 this->_mesh.find_neighbors();
509 integrityproblems = this->check_hull_integrity();
510 }
511
512 // If find_neighbors() doesn't fix these, I give up.
513 if (integrityproblems.count(MISSING_NEIGHBOR) ||
514 integrityproblems.count(MISSING_BACKLINK) ||
515 integrityproblems.count(BAD_NEIGHBOR_LINKS))
516 return integrityproblems;
517
518 // find_neighbors() might have fixed everything
519 if (integrityproblems.empty())
520 return integrityproblems;
521
522 // A non-oriented (but orientable!) surface is the only thing we
523 // shouldn't have fixed or given up on by now.
524 libmesh_assert_equal_to(integrityproblems.size(), 1);
525 libmesh_assert_equal_to(integrityproblems.count(NON_ORIENTED), 1);
526
527 // We need one known-good triangle to start from. We'll pick the
528 // most-negative-x normal among the triangles on the most-negative-x
529 // point.
530
531 // We'll just implement this in serial for now.
532 MeshSerializer mesh_serializer(this->_mesh);
533
534 // I don't see why we'd need boundary info here, but maybe we'll
535 // want to preserve edge/node conditions eventually?
536 BoundaryInfo & bi = this->_mesh.get_boundary_info();
537
538 const Node * lowest_point = (*this->_mesh.elements_begin())->node_ptr(0);
539
540 // Index by ids, not pointers, for consistency in parallel
541 std::unordered_set<dof_id_type> attached_elements;
542
543 for (Elem * elem : this->_mesh.element_ptr_range())
544 {
545 for (const Node & node : elem->node_ref_range())
546 {
547 if (node(0) < (*lowest_point)(0))
548 {
549 lowest_point = &node;
550 attached_elements.clear();
551 }
552 if (&node == lowest_point)
553 attached_elements.insert(elem->id());
554 }
555 }
556
557 Elem * best_elem = nullptr;
558 Real best_abs_normal_0 = 0;
559
560 for (dof_id_type id : attached_elements)
561 {
562 Elem * elem = this->_mesh.elem_ptr(id);
563 const Point e01 = elem->point(1) - elem->point(0);
564 const Point e02 = elem->point(2) - elem->point(0);
565 const Point normal = e01.cross(e02).unit();
566 const Real abs_normal_0 = std::abs(normal(0));
567
568 if (!best_elem || abs_normal_0 > best_abs_normal_0)
569 {
570 best_elem = elem;
571 best_abs_normal_0 = abs_normal_0;
572
573 // Make sure that element is actually a good one, by
574 // flipping it if it's not.
575 if (abs_normal_0 == normal(0))
576 elem->flip(&bi);
577 }
578 }
579
580 // Now flood-fill from that element to get a consistent orientation
581 // for the others.
582 std::unordered_set<dof_id_type> frontier_elements{best_elem->id()},
583 finished_elements{};
584
585 while (!frontier_elements.empty())
586 {
587 const dof_id_type elem_id = *frontier_elements.begin();
588 Elem & elem = this->_mesh.elem_ref(elem_id);
589 for (auto s : elem.side_index_range())
590 {
591 Elem * neigh = elem.neighbor_ptr(s);
592 libmesh_assert(neigh);
593 libmesh_assert_less(neigh->which_neighbor_am_i(&elem), 3);
594
595 const Node * const n1 = elem.node_ptr(s);
596 const Node * const n2 = elem.node_ptr((s+1)%3);
597 const unsigned int i1 = neigh->local_node(n1->id());
598 const unsigned int i2 = neigh->local_node(n2->id());
599 libmesh_assert_less(i1, 3);
600 libmesh_assert_less(i2, 3);
601
602 const dof_id_type neigh_id = neigh->id();
603
604 const bool frontier_neigh = frontier_elements.count(neigh_id);
605 const bool finished_neigh = finished_elements.count(neigh_id);
606
607 // Are we flipped?
608 if ((i2 + 1)%3 != i1)
609 {
610 // Are we a Moebius strip??? We give up.
611 if (frontier_neigh || finished_neigh)
612 return integrityproblems;
613
614 neigh->flip(&bi);
615 }
616
617 if (!frontier_neigh && !finished_neigh)
618 frontier_elements.insert(neigh_id);
619 }
620
621 finished_elements.insert(elem_id);
622 frontier_elements.erase(elem_id);
623 }
624
625 this->_mesh.find_neighbors();
626
627 libmesh_assert(this->check_hull_integrity().empty());
628
629 return {};
630}
631
632
634 (const std::set<MeshTetInterface::SurfaceIntegrity> & result) const
635{
636 std::ostringstream err_msg;
637
638 if (result.empty()) // success
639 return;
640
641 err_msg << "Error! Conforming Delaunay mesh tetrahedralization requires a convex hull." << std::endl;
642
643 if (result.count(NON_TRI3))
644 {
645 err_msg << "At least one non-Tri3 element was found in the input boundary mesh. ";
646 err_msg << "Our constrained Delaunay tetrahedralization boundary must be a triangulation of Tri3 elements." << std::endl;
647 }
648 if (result.count(MISSING_NEIGHBOR))
649 {
650 err_msg << "At least one triangle without three neighbors was found in the input boundary mesh. ";
651 err_msg << "A constrained Delaunay tetrahedralization boundary must be a triangular manifold without boundary." << std::endl;
652 }
653 if (result.count(EMPTY_MESH))
654 {
655 err_msg << "The input boundary mesh was empty!" << std::endl;
656 err_msg << "Our constrained Delaunay tetrahedralization boundary must be a triangulation of Tri3 elements." << std::endl;
657 }
658 if (result.count(MISSING_BACKLINK))
659 {
660 err_msg << "At least one triangle neighbor without a return neighbor link was found in the input boundary mesh. ";
661 err_msg << "A constrained Delaunay tetrahedralization boundary must be a conforming and non-adaptively-refined mesh." << std::endl;
662 }
663 if (result.count(BAD_NEIGHBOR_NODES))
664 {
665 err_msg << "At least one triangle neighbor without expected node links was found in the input boundary mesh. ";
666 err_msg << "A constrained Delaunay tetrahedralization boundary must be a conforming and non-adaptively-refined mesh." << std::endl;
667 }
668 if (result.count(NON_ORIENTED))
669 {
670 err_msg << "At least one triangle neighbor with an inconsistent orientation was found in the input boundary mesh. ";
671 err_msg << "A constrained Delaunay tetrahedralization boundary must be an oriented Tri3 mesh." << std::endl;
672 }
673 if (result.count(BAD_NEIGHBOR_LINKS))
674 err_msg << "At least one triangle neighbor with inconsistent node and neighbor links was found in the input boundary mesh." << std::endl;
675 if (result.count(DEGENERATE_ELEMENT))
676 err_msg << "At least one input triangle is degenerate, with near-zero area relative to the manifold." << std::endl;
677 if (result.count(DEGENERATE_MESH))
678 err_msg << "The input mesh is degenerate, with zero thickness in at least one direction." << std::endl;
679
680 libmesh_error_msg(err_msg.str());
681}
682
683
684
686{
687 for (auto & elem : this->_mesh.element_ptr_range())
688 {
689 // Check for proper element type. Yes, we legally delete elements while
690 // iterating over them because no entries from the underlying container
691 // are actually erased.
692 if (elem->type() == TRI3)
693 _mesh.delete_elem(elem);
694 }
695
696 // We just removed any boundary info associated with hull element
697 // edges, so let's update the boundary id caches.
699}
700
701
702
704{
705 switch (_elem_type)
706 {
707 case TET4:
708 return;
709 case TET10:
711 break;
712 case TET14:
714 break;
715 default:
716 libmesh_not_implemented();
717 }
718}
719
720
721
722} // namespace libMesh
void set_union(T &data, const unsigned int root_id) const
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
Defines a Cartesian bounding box by the two corner extremum.
const Point & max() const
const Point & min() const
void union_with(const Point &p)
Enlarges this bounding box to include the given point.
dof_id_type & set_id()
Definition dof_object.h:827
dof_id_type id() const
Definition dof_object.h:819
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:848
This is the base class from which all geometric element types are derived.
Definition elem.h:96
const Point & point(const unsigned int i) const
Definition elem.h:2462
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition elem.h:2635
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition elem.h:2936
virtual dof_id_type low_order_key(const unsigned int s) const =0
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition elem.h:2682
void set_interior_parent(Elem *p)
Sets the pointer to the element's interior_parent.
Definition elem.C:1222
unsigned int local_node(const dof_id_type i) const
Definition elem.h:2496
virtual unsigned short dim() const =0
virtual void flip(BoundaryInfo *boundary_info)=0
Flips the element (by swapping node and neighbor pointers) to have a mapping Jacobian of opposite sig...
unsigned int level() const
Definition elem.h:3091
std::string get_info() const
Prints relevant information about the element to a string.
Definition elem.C:2956
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
virtual Real volume() const
Definition elem.C:3462
virtual ElemType type() const =0
virtual unsigned int n_sides() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
IntRange< unsigned short > side_index_range() const
Definition elem.h:2727
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
bool is_prepared() const
Definition mesh_base.C:1064
virtual dof_id_type n_elem() const =0
virtual bool is_replicated() const
Definition mesh_base.h:379
void all_second_order(const bool full_ordered=true)
Calls the range-based version of this function with a range consisting of all elements in the mesh.
Definition mesh_base.C:1803
const ConstElemRange & active_local_element_stored_range() const
Definition mesh_base.C:1954
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition mesh_base.C:824
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
virtual const Elem * elem_ptr(const dof_id_type i) const =0
virtual dof_id_type max_elem_id() const =0
virtual void all_complete_order()
Calls the range-based version of this function with a range consisting of all elements in the mesh.
Definition mesh_base.C:1808
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
virtual unique_id_type parallel_max_unique_id() const =0
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
void process_hull_integrity_result(const std::set< SurfaceIntegrity > &result) const
This function prints an informative message and throws an exception based on the output of the check_...
ElemType _elem_type
The exact type of tetrahedra we intend to construct.
UnstructuredMesh & _mesh
Local reference to the mesh we are working with.
std::unique_ptr< std::vector< std::unique_ptr< UnstructuredMesh > > > _holes
A pointer to a vector of meshes each defining a hole.
MeshTetInterface(UnstructuredMesh &mesh)
Constructor.
unsigned int _verbosity
verbosity setting
void delete_2D_hull_elements()
Delete original convex hull elements from the Mesh after performing a Delaunay tetrahedralization.
void attach_hole_list(std::unique_ptr< std::vector< std::unique_ptr< UnstructuredMesh > > > holes)
Attaches a vector of Mesh pointers defining holes which will be meshed around.
std::set< SurfaceIntegrity > check_hull_integrity() const
This function checks the integrity of the current set of elements in the Mesh to see if they comprise...
std::set< SurfaceIntegrity > improve_hull_integrity()
This function checks the integrity of the current set of elements in the Mesh, and corrects what it c...
void increase_tet_order()
Converts all linear tet elements to the type requested by _elem_type, if that type differs from TET4.
virtual ~MeshTetInterface()
Default destructor in base class.
SurfaceIntegrity
Enumeration of possible surface mesh integrity issues.
static BoundingBox volume_to_surface_mesh(UnstructuredMesh &mesh)
Remove volume elements from the given mesh, after converting their outer boundary faces to surface el...
A Node is like a Point, but with more information.
Definition node.h:55
const Parallel::Communicator & comm() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
In parallel meshes where a ghost element has neighbors which do not exist on the local processor,...
Definition remote_elem.h:61
The StoredRange class defines a contiguous, divisible set of objects.
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
The UnstructuredMesh class is derived from the MeshBase class.
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true) override
Other functions from MeshBase requiring re-definition.
MeshBase & mesh
void all_tri(MeshBase &mesh)
Subdivides any non-simplex elements in a Mesh to produce simplex (triangular in 2D,...
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition mesh_tools.C:566
bool valid_is_prepared(const MeshBase &mesh)
A function for testing whether a mesh's cached is_prepared() setting is not a false positive.
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
The libMesh namespace provides an interface to certain functionality in the library.
uint8_t unique_id_type
Definition id_types.h:86
libmesh_assert(ctx)
const RemoteElem * remote_elem
Definition remote_elem.C:57
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176