diff --git a/include/mesh/mesh_base.h b/include/mesh/mesh_base.h index b1c40a44ee..67e13a62bc 100644 --- a/include/mesh/mesh_base.h +++ b/include/mesh/mesh_base.h @@ -1193,6 +1193,13 @@ class MeshBase : public ParallelObject void allow_find_neighbors(bool allow) { _skip_find_neighbors = !allow; } bool allow_find_neighbors() const { return !_skip_find_neighbors; } + /** + * If \p false is passed then this mesh will no longer work to detect + * interior parents when being prepared for use + */ + void allow_detect_interior_parents(bool allow) { _skip_detect_interior_parents = !allow; } + bool allow_detect_interior_parents() const { return !_skip_detect_interior_parents; } + /** * If false is passed in then this mesh will no longer have remote * elements deleted when being prepared for use; i.e. even a @@ -1390,7 +1397,8 @@ class MeshBase : public ParallelObject virtual void read (const std::string & name, void * mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, - bool skip_find_neighbors=false) = 0; + bool skip_find_neighbors=false, + bool skip_detect_interior_parents=false) = 0; virtual void write (const std::string & name) const = 0; /** @@ -1986,6 +1994,11 @@ class MeshBase : public ParallelObject */ bool _skip_find_neighbors; + /** + * If this is \p true then we will skip \p detect_interior_parents in \p prepare_for_use + */ + bool _skip_detect_interior_parents; + /** * If this is false then even on DistributedMesh remote elements * will not be deleted during mesh preparation. diff --git a/include/mesh/unstructured_mesh.h b/include/mesh/unstructured_mesh.h index be3c5798f4..417620322d 100644 --- a/include/mesh/unstructured_mesh.h +++ b/include/mesh/unstructured_mesh.h @@ -109,7 +109,8 @@ class UnstructuredMesh : public MeshBase virtual void read (const std::string & name, void * mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, - bool skip_find_neighbors=false) override; + bool skip_find_neighbors=false, + bool skip_detect_interior_parents=false) override; /** * Write the file specified by \p name. Attempts to figure out the * proper method by the file extension. diff --git a/src/mesh/distributed_mesh.C b/src/mesh/distributed_mesh.C index 24e352322d..5c7df35133 100644 --- a/src/mesh/distributed_mesh.C +++ b/src/mesh/distributed_mesh.C @@ -194,6 +194,7 @@ DistributedMesh::DistributedMesh (const MeshBase & other_mesh) : this->copy_nodes_and_elements(other_mesh, true); this->allow_find_neighbors(other_mesh.allow_find_neighbors()); + this->allow_detect_interior_parents(other_mesh.allow_detect_interior_parents()); this->allow_renumbering(other_mesh.allow_renumbering()); this->allow_remote_element_removal(other_mesh.allow_remote_element_removal()); this->skip_partitioning(other_mesh.skip_partitioning()); diff --git a/src/mesh/mesh_base.C b/src/mesh/mesh_base.C index 535117af6b..de7a854b0a 100644 --- a/src/mesh/mesh_base.C +++ b/src/mesh/mesh_base.C @@ -78,6 +78,7 @@ MeshBase::MeshBase (const Parallel::Communicator & comm_in, _skip_all_partitioning(libMesh::on_command_line("--skip-partitioning")), _skip_renumber_nodes_and_elements(false), _skip_find_neighbors(false), + _skip_detect_interior_parents(false), _allow_remote_element_removal(true), _allow_node_and_elem_unique_id_overlap(false), _spatial_dimension(d), @@ -114,6 +115,7 @@ MeshBase::MeshBase (const MeshBase & other_mesh) : _skip_all_partitioning(other_mesh._skip_all_partitioning), _skip_renumber_nodes_and_elements(other_mesh._skip_renumber_nodes_and_elements), _skip_find_neighbors(other_mesh._skip_find_neighbors), + _skip_detect_interior_parents(other_mesh._skip_detect_interior_parents), _allow_remote_element_removal(other_mesh._allow_remote_element_removal), _allow_node_and_elem_unique_id_overlap(other_mesh._allow_node_and_elem_unique_id_overlap), _elem_dims(other_mesh._elem_dims), @@ -201,6 +203,7 @@ MeshBase& MeshBase::operator= (MeshBase && other_mesh) _skip_all_partitioning = other_mesh.skip_partitioning(); _skip_renumber_nodes_and_elements = !(other_mesh.allow_renumbering()); _skip_find_neighbors = !(other_mesh.allow_find_neighbors()); + _skip_detect_interior_parents = !(other_mesh.allow_detect_interior_parents()); _allow_remote_element_removal = other_mesh.allow_remote_element_removal(); _allow_node_and_elem_unique_id_overlap = other_mesh.allow_node_and_elem_unique_id_overlap(); _block_id_to_name = std::move(other_mesh._block_id_to_name); @@ -305,6 +308,8 @@ bool MeshBase::locally_equals (const MeshBase & other_mesh) const return false; if (_skip_find_neighbors != other_mesh._skip_find_neighbors) return false; + if (_skip_detect_interior_parents != other_mesh._skip_detect_interior_parents) + return false; if (_allow_remote_element_removal != other_mesh._allow_remote_element_removal) return false; if (_allow_node_and_elem_unique_id_overlap != other_mesh._allow_node_and_elem_unique_id_overlap) @@ -896,7 +901,8 @@ void MeshBase::prepare_for_use () // Search the mesh for elements that have a neighboring element // of dim+1 and set that element as the interior parent - this->detect_interior_parents(); + if (!_skip_detect_interior_parents) + this->detect_interior_parents(); // Fix up node unique ids in case mesh generation code didn't take // exceptional care to do so. @@ -1908,6 +1914,50 @@ void MeshBase::detect_interior_parents() if (this->elem_dimensions().size() == 1) return; + // In this function we find only +1 dimensional interior parents, + // (so, for a given element el, the interior parent p must satisfy p.dim() == el.dim() + 1). + // Therefore, we can avoid checking the existence of interior parents + // for all those elements el such there there is no p with p.dim() == el.dim() + 1. + // We store whether to skip any given dimension in the construction of interior parents + // inside the vector in dimensions_to_skip_for_interior_parents. + std::vector skip_dimension_for_interior_parents(LIBMESH_DIM+1); // all false by default + skip_dimension_for_interior_parents.back() = true; + + // Moreover, in the following, we will build a node-to-elem map. + // It is among the elems of this map that we will look for interior parents. + // Therefore, we can skip all elems p such that there is no el with el.dim() == p.dim() - 1. + // We store whether to skip any given dimension in the construction of the node-to-elem map + // in the vector skip_dimensions_for_node_to_el_map. + std::vector skip_dimensions_for_node_to_el_map(LIBMESH_DIM+1); // all false by default + skip_dimensions_for_node_to_el_map[*this->elem_dimensions().begin()] = true; + + // We also create a flag to know if all dimensions should be skipped, + // and if we should therefore return early. + bool skip_all_dimensions = true; + + // Fill dimensions_to_skip_for_interior_parents and dimensions_to_skip_for_node_to_el_map. + { + const std::set & elem_dimensions = this->elem_dimensions(); + + auto it = elem_dimensions.begin(); + auto next = std::next(it); + + for (; next != elem_dimensions.end(); ++it, ++next) + { + if (*it + 1 != *next) // note: elem_dimensions is already sorted + { + skip_dimension_for_interior_parents[*it] = true; + skip_dimensions_for_node_to_el_map[*next] = true; + } + else if (!skip_dimension_for_interior_parents[*it]) + skip_all_dimensions = false; + } + } + + // There is nothing to do if all dimensions should be skipped. + if (skip_all_dimensions) + return; + // Do we have interior parent pointers going to a different mesh? // If so then we'll still check to make sure that's the only place // they go, so we can libmesh_not_implemented() if not. @@ -1918,6 +1968,10 @@ void MeshBase::detect_interior_parents() for (const auto & elem : this->element_ptr_range()) { + // Ignore element if it cannot be interior parent of any other elem. + if (skip_dimensions_for_node_to_el_map[elem->dim()]) + continue; + // Populating the node_to_elem map, same as MeshTools::build_nodes_to_elem_map for (auto n : make_range(elem->n_vertices())) { @@ -1930,8 +1984,9 @@ void MeshBase::detect_interior_parents() // Automatically set interior parents for (const auto & element : this->element_ptr_range()) { - // Ignore an 3D element or an element that already has an interior parent - if (element->dim()>=LIBMESH_DIM || element->interior_parent()) + // Ignore elements with dimensions to skip + // or elements that already have an interior parent. + if (skip_dimension_for_interior_parents[element->dim()] || element->interior_parent()) continue; // Start by generating a SET of elements that are dim+1 to the current @@ -1944,7 +1999,15 @@ void MeshBase::detect_interior_parents() for (auto n : make_range(element->n_vertices())) { - std::vector & element_ids = node_to_elem[element->node_id(n)]; + auto it = node_to_elem.find(element->node_id(n)); + // Check at first that this node is not isolated. + if (it == node_to_elem.end()) + { + found_interior_parents = false; + break; + } + + std::vector & element_ids = it->second; for (const auto & eid : element_ids) if (this->elem_ref(eid).dim() == element->dim()+1) neighbors[n].insert(eid); diff --git a/src/mesh/replicated_mesh.C b/src/mesh/replicated_mesh.C index 059d3fcbe7..5d86bcdff0 100644 --- a/src/mesh/replicated_mesh.C +++ b/src/mesh/replicated_mesh.C @@ -104,6 +104,7 @@ ReplicatedMesh::ReplicatedMesh (const MeshBase & other_mesh) : this->copy_nodes_and_elements(other_mesh, true); this->allow_find_neighbors(other_mesh.allow_find_neighbors()); + this->allow_detect_interior_parents(other_mesh.allow_detect_interior_parents()); this->allow_renumbering(other_mesh.allow_renumbering()); this->allow_remote_element_removal(other_mesh.allow_remote_element_removal()); this->skip_partitioning(other_mesh.skip_partitioning()); diff --git a/src/mesh/unstructured_mesh.C b/src/mesh/unstructured_mesh.C index dfa28eb504..2d4ffa8b4c 100644 --- a/src/mesh/unstructured_mesh.C +++ b/src/mesh/unstructured_mesh.C @@ -834,9 +834,11 @@ void UnstructuredMesh::copy_nodes_and_elements(const MeshBase & other_mesh, const bool allowed_renumbering = this->allow_renumbering(); const bool allowed_find_neighbors = this->allow_find_neighbors(); const bool allowed_elem_removal = this->allow_remote_element_removal(); + const bool allowed_detect_detect_interior_parents = this->allow_detect_interior_parents(); this->allow_renumbering(false); this->allow_remote_element_removal(false); this->allow_find_neighbors(!skip_find_neighbors); + this->allow_detect_interior_parents(other_mesh.allow_detect_interior_parents()); // We should generally be able to skip *all* partitioning here // because we're only adding one already-consistent mesh to another. @@ -848,6 +850,7 @@ void UnstructuredMesh::copy_nodes_and_elements(const MeshBase & other_mesh, //But in the long term, don't change our policies. this->allow_find_neighbors(allowed_find_neighbors); + this->allow_detect_interior_parents(allowed_detect_detect_interior_parents); this->allow_renumbering(allowed_renumbering); this->allow_remote_element_removal(allowed_elem_removal); this->skip_partitioning(skipped_partitioning); @@ -1272,7 +1275,8 @@ void UnstructuredMesh::find_neighbors (const bool reset_remote_elements, void UnstructuredMesh::read (const std::string & name, void *, bool skip_renumber_nodes_and_elements, - bool skip_find_neighbors) + bool skip_find_neighbors, + bool skip_detect_interior_parents) { // Set the skip_renumber_nodes_and_elements flag on all processors // if necessary. @@ -1294,9 +1298,15 @@ void UnstructuredMesh::read (const std::string & name, // Done reading the mesh. Now prepare it for use. const bool old_allow_find_neighbors = this->allow_find_neighbors(); + const bool old_allow_detect_interior_parents = this->allow_detect_interior_parents(); + this->allow_find_neighbors(!skip_find_neighbors); + this->allow_detect_interior_parents(!skip_detect_interior_parents); + this->prepare_for_use(); + this->allow_find_neighbors(old_allow_find_neighbors); + this->allow_detect_interior_parents(old_allow_detect_interior_parents); }