18 #include "libmesh/libmesh_config.h" 25 #include "libmesh/mesh_tet_interface.h" 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" 41 std::unordered_set<Elem *>
42 flood_component (std::unordered_set<Elem *> & all_components,
47 std::unordered_set<Elem *> current_component;
49 std::unordered_set<Elem *> elements_to_consider = {elem};
51 while (!elements_to_consider.empty())
53 std::unordered_set<Elem *> next_elements_to_consider;
55 for (
Elem * considering : elements_to_consider)
57 all_components.insert(considering);
58 current_component.insert(considering);
60 for (
auto s :
make_range(considering->n_sides()))
66 "Tet generation encountered a 2D element with a null neighbor, but a\n" 67 "boundary must be a 2D closed manifold (surface).\n");
69 if (all_components.find(neigh) == all_components.end())
70 next_elements_to_consider.insert(neigh);
73 elements_to_consider = next_elements_to_consider;
76 return current_component;
81 Real six_times_signed_tet_volume (
const Point & p1,
85 return p1(0)*p2(1)*p3(2)
95 Real six_times_signed_volume (
const std::unordered_set<Elem *> component)
99 for (
const Elem * elem: component)
101 libmesh_assert_equal_to(elem->
dim(), 2);
103 six_vol += six_times_signed_tet_volume(elem->
point(0),
118 _verbosity(0), _desired_volume(0), _smooth_after_generating(false),
128 (std::unique_ptr<std::vector<std::unique_ptr<UnstructuredMesh>>> holes)
130 _holes = std::move(holes);
152 #ifdef LIBMESH_ENABLE_UNIQUE_ID 159 std::unordered_set<Elem *> elems_to_delete;
161 std::vector<std::unique_ptr<Elem>> elems_to_add;
164 for (
auto * elem :
mesh.active_element_ptr_range())
166 libmesh_error_msg_if (elem->
dim() < 2,
167 "Cannot use meshes with 0D or 1D elements to define a volume");
171 if (elem->
dim() == 2)
176 elems_to_delete.insert(elem);
186 libmesh_not_implemented_msg
187 (
"Tetrahedralizaton of adapted meshes is not currently supported");
192 Elem * side_elem = elems_to_add.back().get();
203 side_elem->
set_id(max_orig_id + max_sides*elem->
id() + s);
204 #ifdef LIBMESH_ENABLE_UNIQUE_ID 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;
227 std::unique_ptr<Elem> my_side, their_side;
229 for (
auto & elem : elems_to_add)
236 auto bounds = side_to_elem_map.equal_range(key);
237 if (bounds.first != bounds.second)
240 while (bounds.first != bounds.second)
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)
249 side_to_elem_map.erase (bounds.first);
256 side_to_elem_map.emplace
257 (key, std::make_pair(elem.get(), cast_int<unsigned char>(s)));
264 for (
auto & elem : elems_to_add)
271 for (
Elem * elem : elems_to_delete)
276 for (
auto & elem : elems_to_add)
294 std::vector<std::unordered_set<Elem *>> components;
295 std::unordered_set<Elem *> in_component;
297 for (
auto * elem :
mesh.element_ptr_range())
298 if (!in_component.count(elem))
299 components.emplace_back(flood_component(in_component, elem));
301 const std::unordered_set<Elem *> * biggest_component =
nullptr;
302 Real biggest_six_vol = 0;
303 for (
const auto & component : components)
305 Real six_vol = six_times_signed_volume(component);
306 if (std::abs(six_vol) > std::abs(biggest_six_vol))
308 biggest_six_vol = six_vol;
309 biggest_component = &component;
313 if (!biggest_component)
314 libmesh_error_msg(
"No non-zero-volume component found among " <<
315 components.size() <<
" boundary components");
317 for (
const auto & component : components)
318 if (&component != biggest_component)
320 for (
Elem * elem: component)
325 for (
Elem * elem: component)
327 if (biggest_six_vol < 0)
350 std::set<MeshTetInterface::SurfaceIntegrity> returnval;
354 if (extents(0) == 0 ||
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));
366 std::set<MeshTetInterface::SurfaceIntegrity> my_returnval;
367 const Real my_ref_area;
368 const unsigned int my_verbosity;
370 TriChecker (
Real ref_area,
unsigned int verbosity) :
371 my_returnval(), my_ref_area(ref_area),
372 my_verbosity(verbosity) {}
374 my_returnval(), my_ref_area(other.my_ref_area),
375 my_verbosity(other.my_verbosity) {}
379 for (
const Elem * elem : range)
384 if (my_verbosity >= 50)
385 std::cerr <<
"Non-Tri3: " << elem->
get_info() << std::endl;
392 if (my_verbosity >= 50)
393 std::cerr <<
"Degenerate element: " << elem->
get_info() << std::endl;
401 if (neigh ==
nullptr)
403 if (my_verbosity >= 50)
404 std::cerr <<
"Element missing neighbor " << s <<
": " << elem->
get_info() << std::endl;
414 if (my_verbosity >= 50)
415 std::cerr <<
"Element missing backlink " << s <<
": " << elem->
get_info() << std::endl;
427 if (i1 >= 3 || i2 >= 3)
429 if (my_verbosity >= 50)
430 std::cerr <<
"Element with bad neighbor " << s <<
" nodes: " << elem->
get_info() << std::endl;
437 if ((i2 + 1)%3 != i1)
439 if (my_verbosity >= 50)
440 std::cerr <<
"Element orientation mismatch with neighbor " << s <<
": " << elem->
get_info() << std::endl;
449 if (my_verbosity >= 50)
450 std::cerr <<
"Element with bad links on neighbor " << s <<
": " << elem->
get_info() << std::endl;
458 void join(TriChecker & other) {
459 my_returnval.merge(other.my_returnval);
463 TriChecker checker (ref_area, this->
_verbosity);
469 returnval.merge(checker.my_returnval);
472 std::set<char> int_set;
474 (returnval.begin(), returnval.end(),
475 std::inserter<std::set<char>>(int_set, int_set.end()),
479 (int_set.begin(), int_set.end(),
480 std::inserter<std::set<SurfaceIntegrity>>(returnval, returnval.end()),
491 libmesh_parallel_only(this->
_mesh.
comm());
493 std::set<MeshTetInterface::SurfaceIntegrity> integrityproblems =
497 if (integrityproblems.empty() ||
498 integrityproblems.count(
NON_TRI3) ||
500 return integrityproblems;
516 return integrityproblems;
519 if (integrityproblems.empty())
520 return integrityproblems;
524 libmesh_assert_equal_to(integrityproblems.size(), 1);
525 libmesh_assert_equal_to(integrityproblems.count(
NON_ORIENTED), 1);
538 const Node * lowest_point = (*this->
_mesh.elements_begin())->node_ptr(0);
541 std::unordered_set<dof_id_type> attached_elements;
543 for (
Elem * elem : this->
_mesh.element_ptr_range())
547 if (node(0) < (*lowest_point)(0))
549 lowest_point = &node;
550 attached_elements.clear();
552 if (&node == lowest_point)
553 attached_elements.insert(elem->
id());
557 Elem * best_elem =
nullptr;
558 Real best_abs_normal_0 = 0;
566 const Real abs_normal_0 = std::abs(normal(0));
568 if (!best_elem || abs_normal_0 > best_abs_normal_0)
571 best_abs_normal_0 = abs_normal_0;
575 if (abs_normal_0 == normal(0))
582 std::unordered_set<dof_id_type> frontier_elements{best_elem->
id()},
585 while (!frontier_elements.empty())
587 const dof_id_type elem_id = *frontier_elements.begin();
597 const unsigned int i1 = neigh->
local_node(n1->id());
599 libmesh_assert_less(i1, 3);
600 libmesh_assert_less(i2, 3);
604 const bool frontier_neigh = frontier_elements.count(neigh_id);
605 const bool finished_neigh = finished_elements.count(neigh_id);
608 if ((i2 + 1)%3 != i1)
611 if (frontier_neigh || finished_neigh)
612 return integrityproblems;
617 if (!frontier_neigh && !finished_neigh)
618 frontier_elements.insert(neigh_id);
621 finished_elements.insert(elem_id);
622 frontier_elements.erase(elem_id);
634 (
const std::set<MeshTetInterface::SurfaceIntegrity> & result)
const 636 std::ostringstream err_msg;
641 err_msg <<
"Error! Conforming Delaunay mesh tetrahedralization requires a convex hull." << std::endl;
643 if (result.count(NON_TRI3))
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;
648 if (result.count(MISSING_NEIGHBOR))
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;
653 if (result.count(EMPTY_MESH))
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;
658 if (result.count(MISSING_BACKLINK))
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;
663 if (result.count(BAD_NEIGHBOR_NODES))
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;
668 if (result.count(NON_ORIENTED))
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;
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;
680 libmesh_error_msg(err_msg.str());
687 for (
auto & elem : this->
_mesh.element_ptr_range())
A Node is like a Point, but with more information.
virtual unique_id_type parallel_max_unique_id() const =0
std::string get_info() const
Prints relevant information about the element to a string.
IntRange< unsigned short > side_index_range() const
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
static constexpr Real TOLERANCE
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
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.
virtual dof_id_type low_order_key(const unsigned int s) const =0
This is the base class from which all geometric element types are derived.
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.
const Parallel::Communicator & comm() const
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_...
The StoredRange class defines a contiguous, divisible set of objects.
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
MeshTetInterface(UnstructuredMesh &mesh)
Constructor.
void set_interior_parent(Elem *p)
Sets the pointer to the element's interior_parent.
SurfaceIntegrity
Enumeration of possible surface mesh integrity issues.
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
The UnstructuredMesh class is derived from the MeshBase class.
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
const Point & min() const
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
virtual ~MeshTetInterface()
Default destructor in base class.
virtual dof_id_type max_elem_id() const =0
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
const ConstElemRange & active_local_element_stored_range() const
static BoundingBox volume_to_surface_mesh(UnstructuredMesh &mesh)
Remove volume elements from the given mesh, after converting their outer boundary faces to surface el...
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
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.
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.
virtual void flip(BoundaryInfo *boundary_info)=0
Flips the element (by swapping node and neighbor pointers) to have a mapping Jacobian of opposite sig...
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Defines a Cartesian bounding box by the two corner extremum.
virtual const Elem * elem_ptr(const dof_id_type i) const =0
virtual unsigned int n_sides() const =0
const Elem * neighbor_ptr(unsigned int i) const
unsigned int level() const
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
virtual unsigned int n_vertices() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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...
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual unsigned short dim() const =0
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
const Node * node_ptr(const unsigned int i) const
virtual bool is_replicated() const
virtual const Elem & elem_ref(const dof_id_type i) const
virtual Real volume() const
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...
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...
unsigned int local_node(const dof_id_type i) const
void union_with(const Point &p)
Enlarges this bounding box to include the given point.
const Point & max() const
virtual dof_id_type n_elem() const =0
unsigned int _verbosity
verbosity setting
UnstructuredMesh & _mesh
Local reference to the mesh we are working with.
virtual ElemType type() const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
const Point & point(const unsigned int i) const
void delete_2D_hull_elements()
Delete original convex hull elements from the Mesh after performing a Delaunay tetrahedralization.
void ErrorVector unsigned int
void set_union(T &data, const unsigned int root_id) const
const RemoteElem * remote_elem