Skip to content

Commit 8f848de

Browse files
authored
Merge pull request #3759 from roystgnr/mesh_preparation_finer
Make is_prepared() more fine-grained
2 parents c0cf044 + a3571d7 commit 8f848de

11 files changed

Lines changed: 582 additions & 109 deletions

include/mesh/mesh_base.h

Lines changed: 238 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class MeshBase : public ParallelObject
165165
virtual ~MeshBase ();
166166

167167
/**
168-
* A partitioner to use at each prepare_for_use()
168+
* A partitioner to use at each partitioning
169169
*/
170170
virtual std::unique_ptr<Partitioner> & partitioner() { return _partitioner; }
171171

@@ -194,25 +194,158 @@ class MeshBase : public ParallelObject
194194
* No Node is removed from the mesh, however even NodeElem elements
195195
* are deleted, so the remaining Nodes will be considered "unused"
196196
* and cleared unless they are reconnected to new elements before
197-
* the next \p prepare_for_use()
197+
* the next preparation step.
198198
*
199199
* This does not affect BoundaryInfo data; any boundary information
200200
* associated elements should already be cleared.
201201
*/
202202
virtual void clear_elems () = 0;
203203

204204
/**
205-
* \returns \p true if the mesh has been prepared via a call
206-
* to \p prepare_for_use, \p false otherwise.
205+
* \returns \p true if the mesh is marked as having undergone all of
206+
* the preparation done in a call to \p prepare_for_use, \p false
207+
* otherwise.
207208
*/
208209
bool is_prepared () const
209-
{ return _is_prepared; }
210+
{ return _preparation; }
210211

211212
/**
212-
* Tells this we have done some operation where we should no longer consider ourself prepared
213+
* \returns the \p Preparation structure with details about in what
214+
* ways \p this mesh is currently prepared or unprepared. This
215+
* structure may change in the future when cache designs change.
216+
*/
217+
struct Preparation;
218+
219+
Preparation preparation () const
220+
{ return _preparation; }
221+
222+
#ifdef LIBMESH_ENABLE_DEPRECATED
223+
/**
224+
* Tells this we have done some operation where we should no longer
225+
* consider ourself prepared. This is a very coarse setting; it is
226+
* generally more efficient to mark finer-grained settings instead.
227+
*
228+
* This method name is now deprecated, in part to match the less
229+
* awkward unset_has_ names of the more fine-grained methods, in
230+
* part as a way to prompt older user codes to use the more
231+
* fine-grained methods where they can, to speed up the
232+
* complete_preparation() calls afterward.
213233
*/
214234
void set_isnt_prepared()
215-
{ _is_prepared = false; }
235+
{ libmesh_deprecated(); _preparation = false; }
236+
#endif // LIBMESH_ENABLE_DEPRECATED
237+
238+
/**
239+
* Tells this we have done some operation where we should no longer
240+
* consider ourself prepared. This is a very coarse setting; it is
241+
* generally more efficient to mark finer-grained settings instead.
242+
*/
243+
void unset_is_prepared()
244+
{ _preparation = false; }
245+
246+
/**
247+
* Tells this we have done some operation creating unpartitioned
248+
* elements.
249+
*
250+
* User code which adds elements to this mesh must either partition
251+
* them too or call this method.
252+
*/
253+
void unset_is_partitioned()
254+
{ _preparation.is_partitioned = false; }
255+
256+
/**
257+
* Tells this we have done some operation (e.g. adding objects to a
258+
* distributed mesh on one processor only) which can lose
259+
* synchronization of id counts.
260+
*
261+
* User code which does distributed additions of nodes or elements
262+
* must call either this method or \p update_parallel_id_counts().
263+
*/
264+
void unset_has_synched_id_counts()
265+
{ _preparation.has_synched_id_counts = false; }
266+
267+
/**
268+
* Tells this we have done some operation (e.g. adding elements
269+
* without setting their neighbor pointers, or adding disjoint
270+
* neighbor boundary pairs) which requires neighbor pointers to be
271+
* determined later.
272+
*
273+
* User code which adds new elements to this mesh must call this
274+
* function or manually set neighbor pointer from and to those
275+
* elements.
276+
*/
277+
void unset_has_neighbor_ptrs()
278+
{ _preparation.has_neighbor_ptrs = false; }
279+
280+
/**
281+
* Tells this we have done some operation (e.g. adding elements with
282+
* a new dimension or subdomain value) which may invalidate cached
283+
* summaries of element data.
284+
*
285+
* User code which adds new elements to this mesh must call this
286+
* function.
287+
*/
288+
void unset_has_cached_elem_data()
289+
{ _preparation.has_cached_elem_data = false; }
290+
291+
/**
292+
* Tells this we have done some operation (e.g. refining elements
293+
* with interior parents) which requires interior parent pointers to
294+
* be found later.
295+
*
296+
* Most user code will not need to call this method; any user code
297+
* that manipulates interior parents or their boundary elements may
298+
* be an exception.
299+
*/
300+
void unset_has_interior_parent_ptrs()
301+
{ _preparation.has_interior_parent_ptrs = false; }
302+
303+
/**
304+
* Tells this we have done some operation (e.g. repartitioning)
305+
* which may have left elements as ghosted which on a distributed
306+
* mesh should be remote.
307+
*
308+
* User code should probably never need to use this; we can set it
309+
* in Partitioner. Any user code which manually repartitions
310+
* elements on distributed meshes may need to call this manually, in
311+
* addition to manually communicating elements with newly-created
312+
* ghosting requirements.
313+
*/
314+
void unset_has_removed_remote_elements()
315+
{ _preparation.has_removed_remote_elements = false; }
316+
317+
/**
318+
* Tells this we have done some operation (e.g. coarsening)
319+
* which may have left orphaned nodes in need of removal.
320+
*
321+
* Most user code should probably never need to use this; we can set
322+
* it in MeshRefinement. User code which deletes elements without
323+
* carefully deleting orphaned nodes should call this manually.
324+
*/
325+
void unset_has_removed_orphaned_nodes()
326+
{ _preparation.has_removed_orphaned_nodes = false; }
327+
328+
/**
329+
* Tells this we have done some operation (e.g. adding or removing
330+
* elements) which may require a reinit() of custom ghosting
331+
* functors.
332+
*
333+
* User code which adds or removes elements should call this method.
334+
* User code which moves nodes ... should probably call this method,
335+
* in case ghosting functors depending on position exist?
336+
*/
337+
void unset_has_reinit_ghosting_functors()
338+
{ _preparation.has_reinit_ghosting_functors = false; }
339+
340+
/**
341+
* Tells this we have done some operation which may have invalidated
342+
* our cached boundary id sets.
343+
*
344+
* User code which removes elements, or which adds or removes
345+
* boundary entries, should call this method.
346+
*/
347+
void unset_has_boundary_id_sets()
348+
{ _preparation.has_boundary_id_sets = false; }
216349

217350
/**
218351
* \returns \p true if all elements and nodes of the mesh
@@ -260,7 +393,9 @@ class MeshBase : public ParallelObject
260393
* except for "ghosts" which touch a local element, and deletes
261394
* all nodes which are not part of a local or ghost element
262395
*/
263-
virtual void delete_remote_elements () {}
396+
virtual void delete_remote_elements () {
397+
_preparation.has_removed_remote_elements = true;
398+
}
264399

265400
/**
266401
* Loops over ghosting functors and calls mesh_reinit()
@@ -400,16 +535,17 @@ class MeshBase : public ParallelObject
400535
* higher dimensions is checked. Also, x-z and y-z planar meshes are
401536
* considered to have spatial dimension == 3.
402537
*
403-
* The spatial dimension is updated during prepare_for_use() based
538+
* The spatial dimension is updated during mesh preparation based
404539
* on the dimensions of the various elements present in the Mesh,
405-
* but is *never automatically decreased* by this function.
540+
* but is *never automatically decreased*.
406541
*
407542
* For example, if the user calls set_spatial_dimension(2) and then
408543
* later inserts 3D elements into the mesh,
409544
* Mesh::spatial_dimension() will return 3 after the next call to
410-
* prepare_for_use(). On the other hand, if the user calls
411-
* set_spatial_dimension(3) and then inserts only x-aligned 1D
412-
* elements into the Mesh, mesh.spatial_dimension() will remain 3.
545+
* prepare_for_use() or complete_preparation(). On the other hand,
546+
* if the user calls set_spatial_dimension(3) and then inserts only
547+
* x-aligned 1D elements into the Mesh, mesh.spatial_dimension()
548+
* will remain 3.
413549
*/
414550
unsigned int spatial_dimension () const;
415551

@@ -742,7 +878,7 @@ class MeshBase : public ParallelObject
742878
* To ensure a specific element id, call e->set_id() before adding it;
743879
* only do this in parallel if you are manually keeping ids consistent.
744880
*
745-
* Users should call MeshBase::prepare_for_use() after elements are
881+
* Users should call MeshBase::complete_preparation() after elements are
746882
* added to and/or deleted from the mesh.
747883
*/
748884
virtual Elem * add_elem (Elem * e) = 0;
@@ -761,7 +897,7 @@ class MeshBase : public ParallelObject
761897
* Insert elem \p e to the element array, preserving its id
762898
* and replacing/deleting any existing element with the same id.
763899
*
764-
* Users should call MeshBase::prepare_for_use() after elements are
900+
* Users should call MeshBase::complete_preparation() after elements are
765901
* added to and/or deleted from the mesh.
766902
*/
767903
virtual Elem * insert_elem (Elem * e) = 0;
@@ -780,8 +916,8 @@ class MeshBase : public ParallelObject
780916
* Removes element \p e from the mesh. This method must be
781917
* implemented in derived classes in such a way that it does not
782918
* invalidate element iterators. Users should call
783-
* MeshBase::prepare_for_use() after elements are added to and/or
784-
* deleted from the mesh.
919+
* MeshBase::complete_preparation() after elements are added to
920+
* and/or deleted from the mesh.
785921
*
786922
* \note Calling this method may produce isolated nodes, i.e. nodes
787923
* not connected to any element.
@@ -848,7 +984,7 @@ class MeshBase : public ParallelObject
848984

849985
/**
850986
* Removes any orphaned nodes, nodes not connected to any elements.
851-
* Typically done automatically in prepare_for_use
987+
* Typically done automatically in a preparation step
852988
*/
853989
void remove_orphaned_nodes ();
854990

@@ -1122,12 +1258,26 @@ class MeshBase : public ParallelObject
11221258
const std::vector<T> * default_values = nullptr);
11231259

11241260
/**
1125-
* Prepare a newly ecreated (or read) mesh for use.
1126-
* This involves 4 steps:
1127-
* 1.) call \p find_neighbors()
1128-
* 2.) call \p partition()
1129-
* 3.) call \p renumber_nodes_and_elements()
1130-
* 4.) call \p cache_elem_data()
1261+
* Prepare a newly created (or read) mesh for use.
1262+
* This involves several steps:
1263+
* 1.) renumbering (if enabled)
1264+
* 2.) removing any orphaned nodes
1265+
* 3.) updating parallel id counts
1266+
* 4.) finding neighbor links
1267+
* 5.) caching summarized element data
1268+
* 6.) finding interior parent links
1269+
* 7.) clearing any old point locator
1270+
* 8.) calling reinit() on ghosting functors
1271+
* 9.) repartitioning (if enabled)
1272+
* 10.) removing any remote elements (if enabled)
1273+
* 11.) regenerating summarized boundary id sets
1274+
*
1275+
* For backwards compatibility, prepare_for_use() performs *all* those
1276+
* steps, regardless of the official preparation() state of the
1277+
* mesh. In codes which have maintained a valid preparation() state
1278+
* via methods such as unset_has_synched_id_counts(), calling
1279+
* complete_preparation() will result in a fully-prepared mesh at
1280+
* less cost.
11311281
*
11321282
* The argument to skip renumbering is now deprecated - to prevent a
11331283
* mesh from being renumbered, set allow_renumbering(false). The argument to skip
@@ -1144,6 +1294,15 @@ class MeshBase : public ParallelObject
11441294
#endif // LIBMESH_ENABLE_DEPRECATED
11451295
void prepare_for_use ();
11461296

1297+
/*
1298+
* Prepare a newly created or modified mesh for use.
1299+
*
1300+
* Unlike \p prepare_for_use(), \p complete_preparation() performs
1301+
* *only* those preparatory steps that have been marked as
1302+
* necessary in the MeshBase::Preparation state.
1303+
*/
1304+
void complete_preparation();
1305+
11471306
/**
11481307
* Call the default partitioner (currently \p metis_partition()).
11491308
*/
@@ -1844,6 +2003,59 @@ class MeshBase : public ParallelObject
18442003
const boundary_id_type b2);
18452004
#endif
18462005

2006+
/**
2007+
* Flags indicating in what ways a mesh has been prepared for use.
2008+
*/
2009+
struct Preparation
2010+
{
2011+
bool is_partitioned = false,
2012+
has_synched_id_counts = false,
2013+
has_neighbor_ptrs = false,
2014+
has_cached_elem_data = false,
2015+
has_interior_parent_ptrs = false,
2016+
has_removed_remote_elements = false,
2017+
has_removed_orphaned_nodes = false,
2018+
has_boundary_id_sets = false,
2019+
has_reinit_ghosting_functors = false;
2020+
2021+
operator bool() const {
2022+
return is_partitioned &&
2023+
has_synched_id_counts &&
2024+
has_neighbor_ptrs &&
2025+
has_cached_elem_data &&
2026+
has_interior_parent_ptrs &&
2027+
has_removed_remote_elements &&
2028+
has_removed_orphaned_nodes &&
2029+
has_reinit_ghosting_functors &&
2030+
has_boundary_id_sets;
2031+
}
2032+
2033+
Preparation & operator= (bool set_all) {
2034+
is_partitioned = set_all;
2035+
has_synched_id_counts = set_all;
2036+
has_neighbor_ptrs = set_all;
2037+
has_cached_elem_data = set_all;
2038+
has_interior_parent_ptrs = set_all;
2039+
has_removed_remote_elements = set_all;
2040+
has_removed_orphaned_nodes = set_all;
2041+
has_reinit_ghosting_functors = set_all;
2042+
has_boundary_id_sets = set_all;
2043+
2044+
return *this;
2045+
}
2046+
2047+
bool operator== (const Preparation & other) {
2048+
return is_partitioned == other.is_partitioned &&
2049+
has_synched_id_counts == other.has_synched_id_counts &&
2050+
has_neighbor_ptrs == other.has_neighbor_ptrs &&
2051+
has_cached_elem_data == other.has_cached_elem_data &&
2052+
has_interior_parent_ptrs == other.has_interior_parent_ptrs &&
2053+
has_removed_remote_elements == other.has_removed_remote_elements &&
2054+
has_removed_orphaned_nodes == other.has_removed_orphaned_nodes &&
2055+
has_reinit_ghosting_functors == other.has_reinit_ghosting_functors &&
2056+
has_boundary_id_sets == other.has_boundary_id_sets;
2057+
}
2058+
};
18472059

18482060
protected:
18492061

@@ -1923,9 +2135,9 @@ class MeshBase : public ParallelObject
19232135
unsigned char _default_mapping_data;
19242136

19252137
/**
1926-
* Flag indicating if the mesh has been prepared for use.
2138+
* Flags indicating in what ways \p this mesh has been prepared.
19272139
*/
1928-
bool _is_prepared;
2140+
Preparation _preparation;
19292141

19302142
/**
19312143
* A \p PointLocator class for this mesh.

include/mesh/unstructured_mesh.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,25 @@ class UnstructuredMesh : public MeshBase
263263
* If an \p id_remapping map is provided, then element subdomain ids
264264
* in \p other_mesh will be converted using it before adding them to
265265
* \p this mesh.
266+
*
267+
* For backwards compatibility, this does some limited mesh
268+
* preparation after the copy: everything except for renumbering,
269+
* remote element removal, and partitioning. To skip just the
270+
* step of that preparation which finds new neighbor_ptr links
271+
* between elements, set \p skip_find_neighbors. To skip all of
272+
* that preparation, set \p skip_preparation. If preparation is
273+
* skipped, it is the users responsibility to set the flags
274+
* indicating what preparation may still be necessary before using
275+
* the mesh later.
266276
*/
267277
virtual void copy_nodes_and_elements (const MeshBase & other_mesh,
268278
const bool skip_find_neighbors = false,
269279
dof_id_type element_id_offset = 0,
270280
dof_id_type node_id_offset = 0,
271281
unique_id_type unique_id_offset = 0,
272282
std::unordered_map<subdomain_id_type, subdomain_id_type> *
273-
id_remapping = nullptr);
283+
id_remapping = nullptr,
284+
const bool skip_preparation = false);
274285

275286
/**
276287
* Move node and elements from other_mesh to this mesh.

0 commit comments

Comments
 (0)