Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make is_prepared() more fine-grained #3759

Open
wants to merge 3 commits into
base: devel
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 156 additions & 22 deletions include/mesh/mesh_base.h
Original file line number Diff line number Diff line change
@@ -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> & partitioner() { return _partitioner; }

@@ -178,25 +178,103 @@ 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.
*/
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<T> * 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.
2 changes: 2 additions & 0 deletions src/mesh/boundary_info.C
Original file line number Diff line number Diff line change
@@ -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;
}


6 changes: 6 additions & 0 deletions src/mesh/distributed_mesh.C
Original file line number Diff line number Diff line change
@@ -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;
}


93 changes: 68 additions & 25 deletions src/mesh/mesh_base.C
Original file line number Diff line number Diff line change
@@ -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<unsigned 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,15 +913,17 @@ MeshBase::reinit_ghosting_functors()
libmesh_assert(gf);
gf->mesh_reinit();
}

_preparation.has_reinit_ghosting_functors = true;
}

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,16 +1780,24 @@ void MeshBase::cache_elem_data()
}
}
#endif // LIBMESH_DIM > 1

_preparation.has_cached_elem_data = true;
}

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<dof_id_type, std::vector<dof_id_type>> node_to_elem;
@@ -1839,6 +1880,8 @@ void MeshBase::detect_interior_parents()
}
}
}

_preparation.has_interior_parent_ptrs = true;
}


4 changes: 4 additions & 0 deletions src/mesh/replicated_mesh.C
Original file line number Diff line number Diff line change
@@ -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());

6 changes: 3 additions & 3 deletions src/mesh/unstructured_mesh.C
Original file line number Diff line number Diff line change
@@ -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 ();

/**