From 30110828298c1d089d8926ba59efd7d6eeea9349 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 16 Jun 2026 14:51:12 -0700 Subject: [PATCH 1/4] Add preparation flag related to sub id to name map And make sure we do sync the map when we're completing preparation --- include/mesh/mesh_base.h | 17 +++++++++++++++-- src/mesh/mesh_base.C | 19 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/include/mesh/mesh_base.h b/include/mesh/mesh_base.h index 50ae9947a0..f5244d4ebf 100644 --- a/include/mesh/mesh_base.h +++ b/include/mesh/mesh_base.h @@ -340,6 +340,16 @@ class MeshBase : public ParallelObject void unset_has_boundary_id_sets() { _preparation.has_boundary_id_sets = false; } + /** + * Tells this we have done some operation which may have left the + * subdomain id to name map inconsistent across processors. + * + * User code which adds or changes subdomain names should call this + * method. + */ + void unset_has_synched_subdomain_name_map() + { _preparation.has_synched_subdomain_name_map = false; } + /** * \returns \p true if all elements and nodes of the mesh * exist on the current processor, \p false otherwise @@ -1903,7 +1913,7 @@ class MeshBase : public ParallelObject * \returns A writable reference to the whole subdomain name map */ std::map & set_subdomain_name_map () - { return _block_id_to_name; } + { this->unset_has_synched_subdomain_name_map(); return _block_id_to_name; } const std::map & get_subdomain_name_map () const { return _block_id_to_name; } @@ -1999,7 +2009,9 @@ class MeshBase : public ParallelObject * subdomain ids, but distributed mesh generators may only know * about part of a mesh when creating names. This method can * synchronize the subdomain id to name map across processors, - * assuming no conflicts exist. + * assuming no conflicts exist. It is called automatically during + * complete_preparation() unless the map is already known to be + * synchronized. */ void sync_subdomain_name_map(); @@ -2088,6 +2100,7 @@ class MeshBase : public ParallelObject bool has_removed_orphaned_nodes; bool has_boundary_id_sets; bool has_reinit_ghosting_functors; + bool has_synched_subdomain_name_map; }; protected: diff --git a/src/mesh/mesh_base.C b/src/mesh/mesh_base.C index d0e35cf27b..eebb5eb0d3 100644 --- a/src/mesh/mesh_base.C +++ b/src/mesh/mesh_base.C @@ -938,6 +938,13 @@ void MeshBase::complete_preparation() if (!_preparation.has_cached_elem_data) this->cache_elem_data(); + // libMesh expects every processor to know about every subdomain + // name, but distributed mesh generators may only have set names for + // the part of the mesh they know about, so make sure the map is + // consistent everywhere. + if (!_preparation.has_synched_subdomain_name_map) + this->sync_subdomain_name_map(); + // Search the mesh for elements that have a neighboring element // of dim+1 and set that element as the interior parent if (!_preparation.has_interior_parent_ptrs) @@ -1879,6 +1886,7 @@ bool MeshBase::get_count_lower_dim_elems_in_point_locator() const std::string & MeshBase::subdomain_name(subdomain_id_type id) { + this->unset_has_synched_subdomain_name_map(); return _block_id_to_name[id]; } @@ -2050,6 +2058,8 @@ void MeshBase::sync_subdomain_name_map() parallel_object_only(); this->comm().set_union(_block_id_to_name); + + _preparation.has_synched_subdomain_name_map = true; } @@ -2816,7 +2826,8 @@ MeshBase::Preparation::Preparation() : has_removed_remote_elements(false), has_removed_orphaned_nodes(false), has_boundary_id_sets(false), - has_reinit_ghosting_functors(false) + has_reinit_ghosting_functors(false), + has_synched_subdomain_name_map(false) {} MeshBase::Preparation::operator bool() const @@ -2829,7 +2840,8 @@ MeshBase::Preparation::operator bool() const has_removed_remote_elements && has_removed_orphaned_nodes && has_reinit_ghosting_functors && - has_boundary_id_sets; + has_boundary_id_sets && + has_synched_subdomain_name_map; } MeshBase::Preparation & @@ -2844,6 +2856,7 @@ MeshBase::Preparation::operator= (bool set_all) has_removed_orphaned_nodes = set_all; has_reinit_ghosting_functors = set_all; has_boundary_id_sets = set_all; + has_synched_subdomain_name_map = set_all; return *this; } @@ -2869,6 +2882,8 @@ MeshBase::Preparation::operator== (const Preparation & other) const return false; if (has_boundary_id_sets != other.has_boundary_id_sets) return false; + if (has_synched_subdomain_name_map != other.has_synched_subdomain_name_map) + return false; return true; } From 1ff420f46dcc6f9c6b599fcd12c623778fae37d8 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 16 Jun 2026 21:07:26 -0700 Subject: [PATCH 2/4] Move preparation copy construction after map copy constrution --- src/mesh/distributed_mesh.C | 4 ++-- src/mesh/replicated_mesh.C | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/distributed_mesh.C b/src/mesh/distributed_mesh.C index 768d4884e8..f2676ea1a1 100644 --- a/src/mesh/distributed_mesh.C +++ b/src/mesh/distributed_mesh.C @@ -202,8 +202,6 @@ DistributedMesh::DistributedMesh (const MeshBase & other_mesh) : this->copy_constraint_rows(other_mesh); - this->_preparation = other_mesh.preparation(); - auto & this_boundary_info = this->get_boundary_info(); const auto & other_boundary_info = other_mesh.get_boundary_info(); @@ -211,6 +209,8 @@ DistributedMesh::DistributedMesh (const MeshBase & other_mesh) : this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map(); + this->_preparation = other_mesh.preparation(); + #ifdef LIBMESH_ENABLE_UNIQUE_ID _next_unique_id = other_mesh.parallel_max_unique_id() + this->processor_id(); diff --git a/src/mesh/replicated_mesh.C b/src/mesh/replicated_mesh.C index d74dfdea6f..f533c48c75 100644 --- a/src/mesh/replicated_mesh.C +++ b/src/mesh/replicated_mesh.C @@ -112,8 +112,6 @@ ReplicatedMesh::ReplicatedMesh (const MeshBase & other_mesh) : this->copy_constraint_rows(other_mesh); - this->_preparation = other_mesh.preparation(); - auto & this_boundary_info = this->get_boundary_info(); const auto & other_boundary_info = other_mesh.get_boundary_info(); @@ -121,6 +119,8 @@ ReplicatedMesh::ReplicatedMesh (const MeshBase & other_mesh) : this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map(); + this->_preparation = other_mesh.preparation(); + // If other_mesh is distributed, then we've got parts of it on each // processor but we're not replicated yet; fix that. if (!other_mesh.is_serial()) From 96af3e6ab2232aa7d2feda6936dbabd3ecbfba12 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 7 Jul 2026 10:17:11 -0700 Subject: [PATCH 3/4] More precise assertion in get_global_boundary_ids --- src/mesh/boundary_info.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/boundary_info.C b/src/mesh/boundary_info.C index 40ded1ed57..a58c4882e1 100644 --- a/src/mesh/boundary_info.C +++ b/src/mesh/boundary_info.C @@ -3612,7 +3612,7 @@ void BoundaryInfo::clear_stitched_boundary_side_ids (const boundary_id_type side const std::set & BoundaryInfo::get_global_boundary_ids() const { - libmesh_assert(_mesh && _mesh->is_prepared()); + libmesh_assert(_mesh && _mesh->_preparation.has_boundary_id_sets); return _global_boundary_ids; } From facfa4bba9b6f9e48d4f4e254ee411d8edd2c8a7 Mon Sep 17 00:00:00 2001 From: Alex Lindsay Date: Tue, 7 Jul 2026 16:39:20 -0700 Subject: [PATCH 4/4] Revert "Remove method call that's not yet present" This reverts commit 295fe3ea50f749f0db9c1e11c109b1e42f8bde83. --- src/mesh/mesh_base.C | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/mesh_base.C b/src/mesh/mesh_base.C index eebb5eb0d3..72eea24e98 100644 --- a/src/mesh/mesh_base.C +++ b/src/mesh/mesh_base.C @@ -1908,6 +1908,8 @@ void MeshBase::set_subdomain_name(const subdomain_id_type id, { if (synchronous) parallel_object_only(); + else + this->unset_has_synched_subdomain_name_map(); _block_id_to_name[id] = name; }