diff --git a/include/mesh/mesh_base.h b/include/mesh/mesh_base.h index 64cc7c50184..d430653269c 100644 --- a/include/mesh/mesh_base.h +++ b/include/mesh/mesh_base.h @@ -149,7 +149,7 @@ class MeshBase : public ParallelObject virtual ~MeshBase (); /** - * A partitioner to use at each prepare_for_use() + * A partitioner to use at each partitioning */ virtual std::unique_ptr & partitioner() { return _partitioner; } @@ -178,7 +178,7 @@ class MeshBase : public ParallelObject * No Node is removed from the mesh, however even NodeElem elements * are deleted, so the remaining Nodes will be considered "unused" * and cleared unless they are reconnected to new elements before - * the next \p prepare_for_use() + * the next preparation step. * * This does not affect BoundaryInfo data; any boundary information * associated elements should already be cleared. @@ -186,17 +186,95 @@ class MeshBase : public ParallelObject virtual void clear_elems () = 0; /** - * \returns \p true if the mesh has been prepared via a call - * to \p prepare_for_use, \p false otherwise. + * \returns \p true if the mesh has undergone all the preparation + * done in a call to \p prepare_for_use, \p false otherwise. */ bool is_prepared () const - { return _is_prepared; } + { return _preparation; } /** - * Tells this we have done some operation where we should no longer consider ourself prepared + * Tells this we have done some operation where we should no longer + * consider ourself prepared. This is a very coarse setting; it may + * be preferable to mark finer-grained settings instead. */ void set_isnt_prepared() - { _is_prepared = false; } + { _preparation = false; } + + /** + * Tells this we have done some operation creating unpartitioned + * elements. + */ + void set_isnt_partitioned() + { _preparation.is_partitioned = false; } + + /** + * Tells this we have done some operation (e.g. adding elements to a + * distributed mesh on one processor only) which can lose + * synchronization of id counts. + */ + void set_hasnt_synched_id_counts() + { _preparation.has_synched_id_counts = false; } + + /** + * Tells this we have done some operation (e.g. adding elements + * without setting their neighbor pointers) which requires neighbor + * pointers to be found later + */ + void set_hasnt_neighbor_ptrs() + { _preparation.has_neighbor_ptrs = false; } + + /** + * Tells this we have done some operation (e.g. adding elements with + * a new dimension or subdomain value) which may invalidate cached + * summaries of element data + */ + void set_hasnt_cached_elem_data() + { _preparation.has_cached_elem_data = false; } + + /** + * Tells this we have done some operation (e.g. refining elements + * with interior parents) which requires interior parent pointers to + * be found later + */ + void set_hasnt_interior_parent_ptrs() + { _preparation.has_interior_parent_ptrs = false; } + + /** + * Tells this we have done some operation (e.g. repartitioning) + * which may have left elements as ghosted which on a distributed + * mesh should be remote. + * + * User code should probably never need to use this; we can set it + * in Partitioner. + */ + void set_hasnt_removed_remote_elements() + { _preparation.has_removed_remote_elements = false; } + + /** + * Tells this we have done some operation (e.g. coarsening) + * which may have left orphaned nodes in need of removal. + * + * Most user code should probably never need to use this; we can set + * it in MeshRefinement. + */ + void set_hasnt_removed_orphaned_nodes() + { _preparation.has_removed_orphaned_nodes = false; } + + /** + * Tells this we have done some operation (e.g. adding or removing + * elements) which may require a reinit() of custom ghosting + * functors. + */ + void hasnt_reinit_ghosting_functors() + { _preparation.has_reinit_ghosting_functors = false; } + + /** + * Tells this we have done some operation (e.g. removing elements or + * adding new boundary entries) which may have invalidated our + * cached boundary id sets. + */ + void set_hasnt_boundary_id_sets() + { _preparation.has_boundary_id_sets = false; } /** * \returns \p true if all elements and nodes of the mesh @@ -244,7 +322,9 @@ class MeshBase : public ParallelObject * except for "ghosts" which touch a local element, and deletes * all nodes which are not part of a local or ghost element */ - virtual void delete_remote_elements () {} + virtual void delete_remote_elements () { + _preparation.has_removed_remote_elements = true; + } /** * Loops over ghosting functors and calls mesh_reinit() @@ -731,7 +811,7 @@ class MeshBase : public ParallelObject * To ensure a specific element id, call e->set_id() before adding it; * only do this in parallel if you are manually keeping ids consistent. * - * Users should call MeshBase::prepare_for_use() after elements are + * Users should call MeshBase::complete_preparation() after elements are * added to and/or deleted from the mesh. */ virtual Elem * add_elem (Elem * e) = 0; @@ -750,7 +830,7 @@ class MeshBase : public ParallelObject * Insert elem \p e to the element array, preserving its id * and replacing/deleting any existing element with the same id. * - * Users should call MeshBase::prepare_for_use() after elements are + * Users should call MeshBase::complete_preparation() after elements are * added to and/or deleted from the mesh. */ virtual Elem * insert_elem (Elem * e) = 0; @@ -769,8 +849,8 @@ class MeshBase : public ParallelObject * Removes element \p e from the mesh. This method must be * implemented in derived classes in such a way that it does not * invalidate element iterators. Users should call - * MeshBase::prepare_for_use() after elements are added to and/or - * deleted from the mesh. + * MeshBase::complete_preparation() after elements are added to + * and/or deleted from the mesh. * * \note Calling this method may produce isolated nodes, i.e. nodes * not connected to any element. @@ -837,7 +917,7 @@ class MeshBase : public ParallelObject /** * Removes any orphaned nodes, nodes not connected to any elements. - * Typically done automatically in prepare_for_use + * Typically done automatically in a preparation step */ void remove_orphaned_nodes (); @@ -1111,12 +1191,19 @@ class MeshBase : public ParallelObject const std::vector * default_values = nullptr); /** - * Prepare a newly ecreated (or read) mesh for use. - * This involves 4 steps: - * 1.) call \p find_neighbors() - * 2.) call \p partition() - * 3.) call \p renumber_nodes_and_elements() - * 4.) call \p cache_elem_data() + * Prepare a newly created (or read) mesh for use. + * This involves several steps: + * 1.) renumbering (if enabled) + * 2.) removing any orphaned nodes + * 3.) updating parallel id counts + * 4.) finding neighbor links + * 5.) caching summarized element data + * 6.) finding interior parent links + * 7.) clearing any old point locator + * 8.) calling reinit() on ghosting functors + * 9.) repartitioning (if enabled) + * 10.) removing any remote elements (if enabled) + * 11.) regenerating summarized boundary id sets * * The argument to skip renumbering is now deprecated - to prevent a * mesh from being renumbered, set allow_renumbering(false). The argument to skip @@ -1131,6 +1218,15 @@ class MeshBase : public ParallelObject void prepare_for_use (const bool skip_renumber_nodes_and_elements); void prepare_for_use (); + /* + * Prepare a newly created or modified mesh for use. + * + * Unlike \p prepare_for_use(), \p complete_preparation() performs + * *only* those preparatory steps that have been marked as + * necessary in the MeshBase::Preparation state. + */ + void complete_preparation(); + /** * Call the default partitioner (currently \p metis_partition()). */ @@ -1835,9 +1931,47 @@ class MeshBase : public ParallelObject unsigned char _default_mapping_data; /** - * Flag indicating if the mesh has been prepared for use. - */ - bool _is_prepared; + * Flags indicating in what ways the mesh has been prepared for use. + */ + struct Preparation { + bool is_partitioned = false, + has_synched_id_counts = false, + has_neighbor_ptrs = false, + has_cached_elem_data = false, + has_interior_parent_ptrs = false, + has_removed_remote_elements = false, + has_removed_orphaned_nodes = false, + has_boundary_id_sets = false, + has_reinit_ghosting_functors = false; + + operator bool() const { + return is_partitioned && + has_synched_id_counts && + has_neighbor_ptrs && + has_cached_elem_data && + has_interior_parent_ptrs && + has_removed_remote_elements && + has_removed_orphaned_nodes && + has_reinit_ghosting_functors && + has_boundary_id_sets; + } + + Preparation & operator= (bool set_all) { + is_partitioned = set_all; + has_synched_id_counts = set_all; + has_neighbor_ptrs = set_all; + has_cached_elem_data = set_all; + has_interior_parent_ptrs = set_all; + has_removed_remote_elements = set_all; + has_removed_orphaned_nodes = set_all; + has_reinit_ghosting_functors = set_all; + has_boundary_id_sets = set_all; + + return *this; + } + }; + + Preparation _preparation; /** * A \p PointLocator class for this mesh. diff --git a/src/mesh/boundary_info.C b/src/mesh/boundary_info.C index 98ff1177a45..02cdc04599a 100644 --- a/src/mesh/boundary_info.C +++ b/src/mesh/boundary_info.C @@ -421,6 +421,8 @@ void BoundaryInfo::regenerate_id_sets() _communicator.set_union(_es_id_to_name); _communicator.set_union(_global_boundary_ids); } + + _mesh->_preparation.has_boundary_id_sets = true; } diff --git a/src/mesh/distributed_mesh.C b/src/mesh/distributed_mesh.C index d0349da4975..65c14238f56 100644 --- a/src/mesh/distributed_mesh.C +++ b/src/mesh/distributed_mesh.C @@ -290,6 +290,8 @@ void DistributedMesh::update_parallel_id_counts() ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) * (this->n_processors() + 1) + this->processor_id(); #endif + + this->_preparation.has_synched_id_counts = true; } @@ -1621,6 +1623,8 @@ void DistributedMesh::renumber_nodes_and_elements () } } + this->_preparation.has_removed_orphaned_nodes = true; + if (_skip_renumber_nodes_and_elements) { this->update_parallel_id_counts(); @@ -1786,6 +1790,8 @@ void DistributedMesh::delete_remote_elements() this->libmesh_assert_valid_parallel_ids(); this->libmesh_assert_valid_parallel_flags(); #endif + + this->_preparation.has_removed_remote_elements = true; } diff --git a/src/mesh/mesh_base.C b/src/mesh/mesh_base.C index ec964876b33..36ed4cbd212 100644 --- a/src/mesh/mesh_base.C +++ b/src/mesh/mesh_base.C @@ -62,7 +62,7 @@ MeshBase::MeshBase (const Parallel::Communicator & comm_in, _n_parts (1), _default_mapping_type(LAGRANGE_MAP), _default_mapping_data(0), - _is_prepared (false), + _preparation (), _point_locator (), _count_lower_dim_elems_in_point_locator(true), _partitioner (), @@ -93,7 +93,7 @@ MeshBase::MeshBase (const MeshBase & other_mesh) : _n_parts (other_mesh._n_parts), _default_mapping_type(other_mesh._default_mapping_type), _default_mapping_data(other_mesh._default_mapping_data), - _is_prepared (other_mesh._is_prepared), + _preparation (other_mesh._preparation), _point_locator (), _count_lower_dim_elems_in_point_locator(other_mesh._count_lower_dim_elems_in_point_locator), _partitioner (), @@ -162,7 +162,7 @@ MeshBase& MeshBase::operator= (MeshBase && other_mesh) _n_parts = other_mesh.n_partitions(); _default_mapping_type = other_mesh.default_mapping_type(); _default_mapping_data = other_mesh.default_mapping_data(); - _is_prepared = other_mesh.is_prepared(); + _preparation = other_mesh._preparation; _point_locator = std::move(other_mesh._point_locator); _count_lower_dim_elems_in_point_locator = other_mesh.get_count_lower_dim_elems_in_point_locator(); #ifdef LIBMESH_ENABLE_UNIQUE_ID @@ -221,6 +221,7 @@ bool MeshBase::operator== (const MeshBase & other_mesh) const bool MeshBase::locally_equals (const MeshBase & other_mesh) const { +<<<<<<< HEAD // Check whether (almost) everything in the base is equal // // We don't check _next_unique_id here, because it's expected to @@ -237,7 +238,7 @@ bool MeshBase::locally_equals (const MeshBase & other_mesh) const return false; if (_default_mapping_data != other_mesh._default_mapping_data) return false; - if (_is_prepared != other_mesh._is_prepared) + if (_preparation != other_mesh._preparation) return false; if (_count_lower_dim_elems_in_point_locator != other_mesh._count_lower_dim_elems_in_point_locator) @@ -515,6 +516,8 @@ void MeshBase::change_elemset_id(elemset_id_type old_id, elemset_id_type new_id) unsigned int MeshBase::spatial_dimension () const { + libmesh_assert(_preparation.has_cached_elem_data); + return cast_int(_spatial_dimension); } @@ -724,6 +727,8 @@ void MeshBase::remove_orphaned_nodes () for (const auto & node : this->node_ptr_range()) if (!connected_nodes.count(node)) this->delete_node(node); + + _preparation.has_removed_orphaned_nodes = true; } @@ -760,9 +765,23 @@ void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements) this->prepare_for_use(); } + void MeshBase::prepare_for_use () { - LOG_SCOPE("prepare_for_use()", "MeshBase"); + // Mark everything as unprepared, except for those things we've been + // told we don't need to prepare, for backwards compatibility + this->clear_point_locator(); + _preparation = false; + _preparation.has_neighbor_ptrs = _skip_find_neighbors; + _preparation.has_removed_remote_elements = !_allow_remote_element_removal; + + this->complete_preparation(); +} + + +void MeshBase::complete_preparation() +{ + LOG_SCOPE("complete_preparation()", "MeshBase"); parallel_object_only(); @@ -797,15 +816,21 @@ void MeshBase::prepare_for_use () // using, but our partitioner might need that consistency and/or // might be confused by orphaned nodes. if (!_skip_renumber_nodes_and_elements) - this->renumber_nodes_and_elements(); + { + if (!_preparation.has_removed_orphaned_nodes || + !_preparation.has_synched_id_counts) + this->renumber_nodes_and_elements(); + } else { - this->remove_orphaned_nodes(); - this->update_parallel_id_counts(); + if (!_preparation.has_removed_orphaned_nodes) + this->remove_orphaned_nodes(); + if (!_preparation.has_synched_id_counts) + this->update_parallel_id_counts(); } // Let all the elements find their neighbors - if (!_skip_find_neighbors) + if (!_skip_find_neighbors && !_preparation.has_neighbor_ptrs) this->find_neighbors(); // The user may have set boundary conditions. We require that the @@ -818,11 +843,13 @@ void MeshBase::prepare_for_use () // Search the mesh for all the dimensions of the elements // and cache them. - this->cache_elem_data(); + if (!_preparation.has_cached_elem_data) + this->cache_elem_data(); // 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 (!_preparation.has_interior_parent_ptrs) + this->detect_interior_parents(); // Fix up node unique ids in case mesh generation code didn't take // exceptional care to do so. @@ -834,39 +861,41 @@ void MeshBase::prepare_for_use () MeshTools::libmesh_assert_valid_unique_ids(*this); #endif - // Reset our PointLocator. Any old locator is invalidated any time - // the elements in the underlying elements in the mesh have changed, - // so we clear it here. - this->clear_point_locator(); - // Allow our GhostingFunctor objects to reinit if necessary. // Do this before partitioning and redistributing, and before // deleting remote elements. - this->reinit_ghosting_functors(); + if (!_preparation.has_reinit_ghosting_functors) + this->reinit_ghosting_functors(); // Partition the mesh unless *all* partitioning is to be skipped. // If only noncritical partitioning is to be skipped, the // partition() call will still check for orphaned nodes. - if (!skip_partitioning()) + if (!skip_partitioning() && !_preparation.is_partitioned) this->partition(); + else + _preparation.is_partitioned = true; // If we're using DistributedMesh, we'll probably want it // parallelized. - if (this->_allow_remote_element_removal) + if (this->_allow_remote_element_removal && + !_preparation.has_removed_remote_elements) this->delete_remote_elements(); + else + _preparation.has_removed_remote_elements = true; // Much of our boundary info may have been for now-remote parts of the mesh, // in which case we don't want to keep local copies of data meant to be // local. On the other hand we may have deleted, or the user may have added in // a distributed fashion, boundary data that is meant to be global. So we // handle both of those scenarios here - this->get_boundary_info().regenerate_id_sets(); + if (!_preparation.has_boundary_id_sets) + this->get_boundary_info().regenerate_id_sets(); if (!_skip_renumber_nodes_and_elements) this->renumber_nodes_and_elements(); - // The mesh is now prepared for use. - _is_prepared = true; + // The mesh is now prepared for use, and it should know it. + libmesh_assert(_preparation); #ifdef DEBUG MeshTools::libmesh_assert_valid_boundary_ids(*this); @@ -884,6 +913,8 @@ MeshBase::reinit_ghosting_functors() libmesh_assert(gf); gf->mesh_reinit(); } + + _preparation.has_reinit_ghosting_functors = true; } void MeshBase::clear () @@ -891,8 +922,8 @@ void MeshBase::clear () // Reset the number of partitions _n_parts = 1; - // Reset the _is_prepared flag - _is_prepared = false; + // Reset the preparation flags + _preparation = false; // Clear boundary information if (boundary_info) @@ -1557,6 +1588,8 @@ void MeshBase::partition (const unsigned int n_parts) // Make sure any other locally cached data is correct this->update_post_partitioning(); } + + _preparation.is_partitioned = true; } void MeshBase::all_second_order (const bool full_ordered) @@ -1747,6 +1780,8 @@ void MeshBase::cache_elem_data() } } #endif // LIBMESH_DIM > 1 + + _preparation.has_cached_elem_data = true; } void MeshBase::detect_interior_parents() @@ -1754,9 +1789,15 @@ void MeshBase::detect_interior_parents() // This requires an inspection on every processor parallel_object_only(); + // This requires up-to-date mesh dimensions in cache + libmesh_assert(_preparation.has_cached_elem_data); + // Check if the mesh contains mixed dimensions. If so, then set interior parents, otherwise return. if (this->elem_dimensions().size() == 1) - return; + { + _preparation.has_interior_parent_ptrs = true; + return; + } //This map will be used to set interior parents std::unordered_map> node_to_elem; @@ -1839,6 +1880,8 @@ void MeshBase::detect_interior_parents() } } } + + _preparation.has_interior_parent_ptrs = true; } diff --git a/src/mesh/replicated_mesh.C b/src/mesh/replicated_mesh.C index 4ea7d94ebfc..90523c8a2e8 100644 --- a/src/mesh/replicated_mesh.C +++ b/src/mesh/replicated_mesh.C @@ -686,6 +686,8 @@ void ReplicatedMesh::update_parallel_id_counts() #ifdef LIBMESH_ENABLE_UNIQUE_ID _next_unique_id = this->parallel_max_unique_id(); #endif + + this->_preparation.has_synched_id_counts = true; } @@ -863,6 +865,8 @@ void ReplicatedMesh::renumber_nodes_and_elements () } } + this->_preparation.has_removed_orphaned_nodes = true; + libmesh_assert_equal_to (next_free_elem, _elements.size()); libmesh_assert_equal_to (next_free_node, _nodes.size()); diff --git a/src/mesh/unstructured_mesh.C b/src/mesh/unstructured_mesh.C index d98c7f99d4b..e44201cdb47 100644 --- a/src/mesh/unstructured_mesh.C +++ b/src/mesh/unstructured_mesh.C @@ -1202,15 +1202,15 @@ void UnstructuredMesh::find_neighbors (const bool reset_remote_elements, libmesh_assert(current_elem->interior_parent()); } } - #endif // AMR - #ifdef DEBUG MeshTools::libmesh_assert_valid_neighbors(*this, !reset_remote_elements); MeshTools::libmesh_assert_valid_amr_interior_parents(*this); #endif + + this->_preparation.has_neighbor_ptrs = true; } @@ -1470,7 +1470,7 @@ void UnstructuredMesh::all_first_order () * elements, so that the node ids * are correct */ - if (!this->_is_prepared) + if (this->is_prepared()) this->renumber_nodes_and_elements (); /**