From 2771ebb7037f8840b94f4f3c550f7712b006ce5d Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 3 Dec 2025 13:14:07 -0600 Subject: [PATCH 01/16] Don't try to use an unprepared mesh in testing This wasn't failing the test, because the kernel here didn't need the unprepared data, but we'd like to simply assert that we're not assembling or solving on unprepared meshes. --- tests/systems/periodic_bc_test.C | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/systems/periodic_bc_test.C b/tests/systems/periodic_bc_test.C index f7c4b18051b..f4705a8d016 100644 --- a/tests/systems/periodic_bc_test.C +++ b/tests/systems/periodic_bc_test.C @@ -215,7 +215,7 @@ private: // Okay, *now* the mesh knows to save periodic neighbors mesh.allow_remote_element_removal(true); - mesh.delete_remote_elements(); + mesh.prepare_for_use(); const unsigned int u_var = sys.add_variable("u", fe_type); sys.attach_assemble_function (periodic_bc_test_poisson); From 352273974d2a94a6a9bbf8782bca52a742f5dc86 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 3 Dec 2025 13:15:17 -0600 Subject: [PATCH 02/16] cache_elem_data() after modifying mesh subdomains Otherwise the mesh still thinks its fully prepared even when it isn't and we can't assert valid_is_prepared() on it. --- tests/mesh/mesh_smoother_test.C | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/mesh/mesh_smoother_test.C b/tests/mesh/mesh_smoother_test.C index cfe220b2820..e828e9f6533 100644 --- a/tests/mesh/mesh_smoother_test.C +++ b/tests/mesh/mesh_smoother_test.C @@ -592,6 +592,10 @@ public: mesh.comm().sum(distorted_subdomain_volumes[sub_id]); } + + // We've just invalidated the get_mesh_subdomains() cache by + // adding a new one; fix it. + mesh.cache_elem_data(); } // Get the mesh order From 464fbaa91160cc9d601a4e1007e5fb6ecf04897b Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Thu, 4 Dec 2025 13:26:54 -0600 Subject: [PATCH 03/16] Assign _children_on_boundary in BoundaryInfo = --- src/mesh/boundary_info.C | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/boundary_info.C b/src/mesh/boundary_info.C index e8561d333cc..e3d00bb448f 100644 --- a/src/mesh/boundary_info.C +++ b/src/mesh/boundary_info.C @@ -138,6 +138,8 @@ BoundaryInfo & BoundaryInfo::operator=(const BoundaryInfo & other_boundary_info) for (const auto & [elem, id_pair] : other_boundary_info._boundary_side_id) _boundary_side_id.emplace(_mesh->elem_ptr(elem->id()), id_pair); + _children_on_boundary = other_boundary_info._children_on_boundary; + _boundary_ids = other_boundary_info._boundary_ids; _global_boundary_ids = other_boundary_info._global_boundary_ids; _side_boundary_ids = other_boundary_info._side_boundary_ids; From 77a639ff4bfdbf67451e297360f3d02ee4c4c827 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Thu, 4 Dec 2025 20:55:01 -0600 Subject: [PATCH 04/16] Get interior parent pointers *right* when copying We've added some tricky interior_mesh options that our copy_nodes_and_elements code wasn't handling correctly, and it was possible for a clone() of such a mesh to fail its operator== assertion. --- src/mesh/unstructured_mesh.C | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/mesh/unstructured_mesh.C b/src/mesh/unstructured_mesh.C index dfa28eb5047..935780b5ddb 100644 --- a/src/mesh/unstructured_mesh.C +++ b/src/mesh/unstructured_mesh.C @@ -735,7 +735,7 @@ void UnstructuredMesh::copy_nodes_and_elements(const MeshBase & other_mesh, // Hold off on trying to set the interior parent because we may actually // add lower dimensional elements before their interior parents - if (el->dim() < other_mesh.mesh_dimension() && old->interior_parent()) + if (old->interior_parent()) ip_map[old] = el.get(); #ifdef LIBMESH_ENABLE_AMR @@ -797,9 +797,51 @@ void UnstructuredMesh::copy_nodes_and_elements(const MeshBase & other_mesh, } } - for (auto & elem_pair : ip_map) - elem_pair.second->set_interior_parent( - this->elem_ptr(elem_pair.first->interior_parent()->id() + element_id_offset)); + // If the other_mesh had some interior parents, we may need to + // copy those pointers (if they're to elements in a third mesh), + // or create new equivalent pointers (if they're to elements we + // just copied), or scream and die (if the other mesh had interior + // parents from a third mesh but we already have interior parents + // that aren't to that same third mesh. + if (!ip_map.empty()) + { + bool existing_interior_parents = false; + for (const auto & elem : this->element_ptr_range()) + if (elem->interior_parent()) + { + existing_interior_parents = true; + break; + } + + MeshBase * other_interior_mesh = + const_cast(&other_mesh.interior_mesh()); + + // If we don't already have interior parents, then we can just + // use whatever interior_mesh we need for the incoming + // elements. + if (!existing_interior_parents) + { + if (other_interior_mesh == &other_mesh) + this->set_interior_mesh(*this); + else + this->set_interior_mesh(*other_interior_mesh); + } + + if (other_interior_mesh == &other_mesh && + _interior_mesh == this) + for (auto & elem_pair : ip_map) + elem_pair.second->set_interior_parent( + this->elem_ptr(elem_pair.first->interior_parent()->id() + element_id_offset)); + else if (other_interior_mesh == _interior_mesh) + for (auto & elem_pair : ip_map) + { + Elem * ip = const_cast(elem_pair.first->interior_parent()); + libmesh_assert(ip == other_interior_mesh->elem_ptr(ip->id())); + elem_pair.second->set_interior_parent(ip); + } + else + libmesh_error_msg("Cannot copy boundary elements between meshes with different interior meshes"); + } // Loop (again) over the elements to fill in the neighbors if (skip_find_neighbors) From 361fc2363bc1039b4f74a55fd4422f8edaa9e612 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Fri, 5 Dec 2025 17:23:17 -0600 Subject: [PATCH 05/16] Update bcid sets with new bcids in eigen ex3 --- examples/eigenproblems/eigenproblems_ex3/eigenproblems_ex3.C | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/eigenproblems/eigenproblems_ex3/eigenproblems_ex3.C b/examples/eigenproblems/eigenproblems_ex3/eigenproblems_ex3.C index 75f68ba8eed..65a0dca5f2a 100644 --- a/examples/eigenproblems/eigenproblems_ex3/eigenproblems_ex3.C +++ b/examples/eigenproblems/eigenproblems_ex3/eigenproblems_ex3.C @@ -170,6 +170,8 @@ int main (int argc, char ** argv) if (elem->neighbor_ptr (side) == nullptr) mesh.get_boundary_info().add_side(elem, side, BOUNDARY_ID); + mesh.get_boundary_info().regenerate_id_sets(); + // Print information about the mesh to the screen. mesh.print_info(); From 77dec96893865113ea64ec31aa91caf37ceb8b83 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Mon, 8 Dec 2025 17:55:02 -0600 Subject: [PATCH 06/16] Update boundary id sets after boundary update --- examples/fem_system/fem_system_ex3/fem_system_ex3.C | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/fem_system/fem_system_ex3/fem_system_ex3.C b/examples/fem_system/fem_system_ex3/fem_system_ex3.C index 820d829232a..1ca8f370592 100644 --- a/examples/fem_system/fem_system_ex3/fem_system_ex3.C +++ b/examples/fem_system/fem_system_ex3/fem_system_ex3.C @@ -173,6 +173,8 @@ int main (int argc, char ** argv) mesh.get_boundary_info().add_edge(elem, e, edge_boundary_id); } + mesh.get_boundary_info().regenerate_id_sets(); + // Create an equation systems object. EquationSystems equation_systems (mesh); From 746bb783481b21c93f8b281765bcc7bcc7a7e8c8 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Tue, 9 Dec 2025 13:38:03 -0600 Subject: [PATCH 07/16] Remove unnecessary side_id_map computation This must have been an atavism from a previous refactoring. --- src/mesh/boundary_info.C | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mesh/boundary_info.C b/src/mesh/boundary_info.C index e3d00bb448f..1cbcc351bd7 100644 --- a/src/mesh/boundary_info.C +++ b/src/mesh/boundary_info.C @@ -513,9 +513,8 @@ void BoundaryInfo::sync (const std::set & requested_boundary_i boundary_mesh.set_n_partitions() = _mesh->n_partitions(); std::map node_id_map; - std::map, dof_id_type> side_id_map; - this->_find_id_maps(requested_boundary_ids, 0, &node_id_map, 0, &side_id_map, subdomains_relative_to); + this->_find_id_maps(requested_boundary_ids, 0, &node_id_map, 0, nullptr, subdomains_relative_to); // Let's add all the boundary nodes we found to the boundary mesh for (const auto & node : _mesh->node_ptr_range()) From 52aa297ab14d82904ab905ee53bd8073fcbb29cd Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Tue, 9 Dec 2025 13:38:31 -0600 Subject: [PATCH 08/16] Fix side_id_map generation on refined meshes We never caught this before because we were never doing much subsequent mesh modification to the output, but as soon as we start heavier testing we run into code that screams about the inconsistent parent/child id ordering. --- src/mesh/boundary_info.C | 60 ++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/mesh/boundary_info.C b/src/mesh/boundary_info.C index 1cbcc351bd7..6801a86cf86 100644 --- a/src/mesh/boundary_info.C +++ b/src/mesh/boundary_info.C @@ -3301,11 +3301,28 @@ void BoundaryInfo::_find_id_maps(const std::set & requested_bo std::map, dof_id_type> * side_id_map, const std::set & subdomains_relative_to) { - // We'll do the same modulus trick that DistributedMesh uses to avoid - // id conflicts between different processors + // We'll use the standard DistributedMesh trick of dividing up id + // ranges by processor_id to avoid picking duplicate node ids. dof_id_type - next_node_id = first_free_node_id + this->processor_id(), - next_elem_id = first_free_elem_id + this->processor_id(); + next_node_id = first_free_node_id + this->processor_id(); + + // We have to be careful how we select different element ids on + // different processors, because we may be adding sides of refined + // elements and sides of their parents (which are thereby also + // parents of their sides), but libMesh convention requires (and + // some libMesh communication code assumes) that parent elements + // have lower ids than their children. + // + // We'll start with temporary ids in a temporary map stratefied by + // element level, so we can sort by element level after. + // Make sure we're sorting by ids here, not anything that might + // differ from processor to processor, so we can do an + // embarrassingly parallel sort. This is a serialized data + // structure, but it's at worst O(N^2/3) so we'll hold off on doing + // this distributed until someone's benchmark screams at us. + std::vector + >> + side_id_set_by_level; // For avoiding extraneous element side construction ElemSideBuilder side_builder; @@ -3338,14 +3355,19 @@ void BoundaryInfo::_find_id_maps(const std::set & requested_bo // Join up the local results from other processors if (side_id_map) - this->comm().set_union(*side_id_map); + { + std::size_t n_levels = side_id_set_by_level.size(); + this->comm().max(n_levels); + side_id_set_by_level.resize(n_levels); + for (auto l : make_range(n_levels)) + this->comm().set_union(side_id_set_by_level[l]); + } if (node_id_map) this->comm().set_union(*node_id_map); // Finally we'll pass through any unpartitioned elements to add them // to the maps and counts. next_node_id = first_free_node_id + this->n_processors(); - next_elem_id = first_free_elem_id + this->n_processors(); el = _mesh->pid_elements_begin(DofObject::invalid_processor_id); if (el == end_unpartitioned_el) @@ -3402,9 +3424,12 @@ void BoundaryInfo::_find_id_maps(const std::set & requested_bo DofObject::invalid_processor_id))) { std::pair side_pair(elem->id(), s); - libmesh_assert (!side_id_map->count(side_pair)); - (*side_id_map)[side_pair] = next_elem_id; - next_elem_id += this->n_processors() + 1; + auto level = elem->level(); + if (side_id_set_by_level.size() <= level) + side_id_set_by_level.resize(level+1); + auto & level_side_id_set = side_id_set_by_level[level]; + libmesh_assert (!level_side_id_set.count(side_pair)); + level_side_id_set.insert(side_pair); } side = &side_builder(*elem, s); @@ -3429,8 +3454,21 @@ void BoundaryInfo::_find_id_maps(const std::set & requested_bo } } - // FIXME: ought to renumber side/node_id_map image to be contiguous - // to save memory, also ought to reserve memory + // FIXME: should we renumber node_id_map's image to be contiguous + // here, rather than waiting for renumbering in mesh preparation to + // do it later? + + // We do need to build up side_id_map here, to handle proper sorting + // by level. + if (side_id_map) + { + dof_id_type next_elem_id = first_free_elem_id; + for (auto level : make_range(side_id_set_by_level.size())) + { + for (auto side_pair : side_id_set_by_level[level]) + (*side_id_map)[side_pair] = next_elem_id++; + } + } } void BoundaryInfo::clear_stitched_boundary_side_ids (const boundary_id_type sideset_id, From 1e4cd8b93db13ba0e3232edb594a6cff6819ec8e Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 10 Dec 2025 13:24:40 -0600 Subject: [PATCH 09/16] Update subdomain sets after subdomains --- examples/fem_system/fem_system_ex4/fem_system_ex4.C | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/fem_system/fem_system_ex4/fem_system_ex4.C b/examples/fem_system/fem_system_ex4/fem_system_ex4.C index 90782ac7539..c8c4716a73c 100644 --- a/examples/fem_system/fem_system_ex4/fem_system_ex4.C +++ b/examples/fem_system/fem_system_ex4/fem_system_ex4.C @@ -156,6 +156,7 @@ int main (int argc, char ** argv) for (auto & elem : mesh.element_ptr_range()) if (elem->dim() < dim) elem->subdomain_id() = 1; + mesh.cache_elem_data(); mesh_refinement.uniformly_refine(coarserefinements); From 4563ecbb790b529ab1a5c4cdc0d7a4364d1bb298 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 10 Dec 2025 18:09:34 -0600 Subject: [PATCH 10/16] Add clone() to our test GhostingFunctor subclasses This was supposed to be a pure virtual function eventually, and it's time to start moving that way. --- tests/base/overlapping_coupling_test.C | 11 +++++++++++ tests/systems/systems_test.C | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/tests/base/overlapping_coupling_test.C b/tests/base/overlapping_coupling_test.C index 2b71345a077..23d9f71a718 100644 --- a/tests/base/overlapping_coupling_test.C +++ b/tests/base/overlapping_coupling_test.C @@ -60,6 +60,17 @@ class OverlappingCouplingFunctor : public GhostingFunctor virtual ~OverlappingCouplingFunctor(){}; + virtual std::unique_ptr clone () const + { + auto clone_functor = + std::make_unique(_system); + + auto coupling_matrix = + std::make_unique(*_coupling_matrix); + clone_functor->set_coupling_matrix(coupling_matrix); + return clone_functor; + } + void set_coupling_matrix (std::unique_ptr & coupling_matrix) { _coupling_matrix = std::move(coupling_matrix); } diff --git a/tests/systems/systems_test.C b/tests/systems/systems_test.C index a181cd628b6..275ce9360e2 100644 --- a/tests/systems/systems_test.C +++ b/tests/systems/systems_test.C @@ -62,6 +62,11 @@ public: _mesh(mesh) {} + virtual std::unique_ptr clone () const + { + return std::make_unique(_mesh); + } + /** * User-defined function to augment the sparsity pattern. */ From 6c7f52edf1705cf9c7fe839a8d2bcfbea6ceb0a0 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 10 Dec 2025 23:19:23 -0600 Subject: [PATCH 11/16] More updates of mesh caches --- examples/fem_system/fem_system_ex3/fem_system_ex3.C | 2 ++ examples/fem_system/fem_system_ex4/fem_system_ex4.C | 2 ++ examples/fem_system/fem_system_ex5/fem_system_ex5.C | 3 +++ examples/subdomains/subdomains_ex1/subdomains_ex1.C | 3 +++ examples/subdomains/subdomains_ex2/subdomains_ex2.C | 3 +++ .../systems_of_equations_ex6/systems_of_equations_ex6.C | 4 ++++ 6 files changed, 17 insertions(+) diff --git a/examples/fem_system/fem_system_ex3/fem_system_ex3.C b/examples/fem_system/fem_system_ex3/fem_system_ex3.C index 1ca8f370592..7afeadb1b1c 100644 --- a/examples/fem_system/fem_system_ex3/fem_system_ex3.C +++ b/examples/fem_system/fem_system_ex3/fem_system_ex3.C @@ -173,6 +173,8 @@ int main (int argc, char ** argv) mesh.get_boundary_info().add_edge(elem, e, edge_boundary_id); } + // We're all done adding to the boundary_info; let's make sure its + // caches know about it. mesh.get_boundary_info().regenerate_id_sets(); // Create an equation systems object. diff --git a/examples/fem_system/fem_system_ex4/fem_system_ex4.C b/examples/fem_system/fem_system_ex4/fem_system_ex4.C index c8c4716a73c..ba93de15483 100644 --- a/examples/fem_system/fem_system_ex4/fem_system_ex4.C +++ b/examples/fem_system/fem_system_ex4/fem_system_ex4.C @@ -156,6 +156,8 @@ int main (int argc, char ** argv) for (auto & elem : mesh.element_ptr_range()) if (elem->dim() < dim) elem->subdomain_id() = 1; + + // Make sure the mesh knows we added new subdomains. mesh.cache_elem_data(); mesh_refinement.uniformly_refine(coarserefinements); diff --git a/examples/fem_system/fem_system_ex5/fem_system_ex5.C b/examples/fem_system/fem_system_ex5/fem_system_ex5.C index fc6e33faa6b..d2e566b5a28 100644 --- a/examples/fem_system/fem_system_ex5/fem_system_ex5.C +++ b/examples/fem_system/fem_system_ex5/fem_system_ex5.C @@ -225,6 +225,9 @@ int main (int argc, char ** argv) } } + // We're done modifying the BoundaryInfo; get its caches up to date. + mesh.get_boundary_info().regenerate_id_sets(); + // Create an equation systems object. EquationSystems equation_systems (mesh); diff --git a/examples/subdomains/subdomains_ex1/subdomains_ex1.C b/examples/subdomains/subdomains_ex1/subdomains_ex1.C index 8b5e121f4ac..10e85dfe585 100644 --- a/examples/subdomains/subdomains_ex1/subdomains_ex1.C +++ b/examples/subdomains/subdomains_ex1/subdomains_ex1.C @@ -243,6 +243,9 @@ int main (int argc, char ** argv) elem->subdomain_id() = 1; } + // Make sure the mesh knows we added new subdomains. + mesh.cache_elem_data(); + // Create an equation systems object. EquationSystems equation_systems (mesh); diff --git a/examples/subdomains/subdomains_ex2/subdomains_ex2.C b/examples/subdomains/subdomains_ex2/subdomains_ex2.C index abe827d038a..47966c45126 100644 --- a/examples/subdomains/subdomains_ex2/subdomains_ex2.C +++ b/examples/subdomains/subdomains_ex2/subdomains_ex2.C @@ -195,6 +195,9 @@ int main (int argc, char ** argv) elem->subdomain_id() = 1; } + // Make sure the mesh knows we added new subdomains. + mesh.cache_elem_data(); + // Print information about the mesh to the screen. mesh.print_info(); diff --git a/examples/systems_of_equations/systems_of_equations_ex6/systems_of_equations_ex6.C b/examples/systems_of_equations/systems_of_equations_ex6/systems_of_equations_ex6.C index f0fc5c9ba80..61cc9073804 100644 --- a/examples/systems_of_equations/systems_of_equations_ex6/systems_of_equations_ex6.C +++ b/examples/systems_of_equations/systems_of_equations_ex6/systems_of_equations_ex6.C @@ -507,6 +507,10 @@ int main (int argc, char ** argv) mesh.get_boundary_info().add_edge(elem, e, EDGE_BOUNDARY_ID); } + // We're all done adding to the boundary_info; let's make sure its + // caches know about it. + mesh.get_boundary_info().regenerate_id_sets(); + // Create an equation systems object. EquationSystems equation_systems (mesh); From 23af1bb2be163f658e412670ce0c2a719561e42a Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Wed, 10 Dec 2025 23:19:53 -0600 Subject: [PATCH 12/16] Implement clone() for misc_ex9 ghosting functor We're making that pure virtual in non-deprecated builds now. --- .../miscellaneous_ex9/augment_sparsity_on_interface.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h b/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h index 6b2e1ca5d60..08fe3d91518 100644 --- a/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h +++ b/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h @@ -56,6 +56,13 @@ class AugmentSparsityOnInterface : public GhostingFunctor boundary_id_type crack_boundary_lower, boundary_id_type crack_boundary_upper); + + virtual std::unique_ptr clone () const + { + return std::make_unique + (_mesh, _crack_boundary_lower, _crack_boundary_upper); + } + /** * @return a const reference to the lower-to-upper element ID map. */ From ac000580065b3cdc57e55f2427acc68dd4f15a97 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Thu, 11 Dec 2025 11:49:45 -0600 Subject: [PATCH 13/16] Mark overriding functions with override --- .../miscellaneous_ex9/augment_sparsity_on_interface.h | 2 +- tests/base/overlapping_coupling_test.C | 2 +- tests/systems/systems_test.C | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h b/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h index 08fe3d91518..229c6c6efb8 100644 --- a/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h +++ b/examples/miscellaneous/miscellaneous_ex9/augment_sparsity_on_interface.h @@ -57,7 +57,7 @@ class AugmentSparsityOnInterface : public GhostingFunctor boundary_id_type crack_boundary_upper); - virtual std::unique_ptr clone () const + virtual std::unique_ptr clone () const override { return std::make_unique (_mesh, _crack_boundary_lower, _crack_boundary_upper); diff --git a/tests/base/overlapping_coupling_test.C b/tests/base/overlapping_coupling_test.C index 23d9f71a718..29f29802324 100644 --- a/tests/base/overlapping_coupling_test.C +++ b/tests/base/overlapping_coupling_test.C @@ -60,7 +60,7 @@ class OverlappingCouplingFunctor : public GhostingFunctor virtual ~OverlappingCouplingFunctor(){}; - virtual std::unique_ptr clone () const + virtual std::unique_ptr clone () const override { auto clone_functor = std::make_unique(_system); diff --git a/tests/systems/systems_test.C b/tests/systems/systems_test.C index 275ce9360e2..edd31f31ecc 100644 --- a/tests/systems/systems_test.C +++ b/tests/systems/systems_test.C @@ -62,7 +62,7 @@ public: _mesh(mesh) {} - virtual std::unique_ptr clone () const + virtual std::unique_ptr clone () const override { return std::make_unique(_mesh); } From 9cad6d160b91894f1f0ae7caebef50ba59c7c0f8 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Fri, 12 Dec 2025 11:26:49 -0600 Subject: [PATCH 14/16] Fix InfQuad::is_child_on_side() 2008. This has been buggy since I wrote it in 2008. And yet it took a double-refinement test on a distributed mesh plus an extra layer of dbg-mode internal consistency checks to catch the bug. --- src/geom/face_inf_quad.C | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/geom/face_inf_quad.C b/src/geom/face_inf_quad.C index 6070eed4fa9..6abfb1f21a6 100644 --- a/src/geom/face_inf_quad.C +++ b/src/geom/face_inf_quad.C @@ -184,7 +184,9 @@ bool InfQuad::is_child_on_side(const unsigned int c, libmesh_assert_less (c, this->n_children()); libmesh_assert_less (s, this->n_sides()); - return (s == 0 || s == c+1); + return (s == 0 || + (c == 0 && s == 2) || + (c == 1 && s == 1)); } From fb158929a4155064b9a5f07d29dcaabfe7b384ca Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Fri, 12 Dec 2025 11:28:19 -0600 Subject: [PATCH 15/16] More InfElem test coverage for is_child_on_* --- tests/geom/elem_test.C | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/geom/elem_test.C b/tests/geom/elem_test.C index 578ff186835..23d23480f64 100644 --- a/tests/geom/elem_test.C +++ b/tests/geom/elem_test.C @@ -679,6 +679,21 @@ public: for (const Node & node : child_side->node_ref_range()) CPPUNIT_ASSERT(parent_side->contains_point(node)); + // We can at least check for sharing some node with + // a side, on both finite and infinite elements, for + // those children that aren't just the middle elements + // of triangular sides. + bool shares_a_side_node = false; + bool shares_a_vertex = false; + for (const Node & node : child_side->node_ref_range()) + if (parent_side->local_node(node.id()) != invalid_uint) + shares_a_side_node = true; + for (const Node & node : elem->node_ref_range()) + if (parent->local_node(node.id()) < parent->n_vertices()) + shares_a_vertex = true; + + CPPUNIT_ASSERT(shares_a_side_node || !shares_a_vertex); + if (elem->neighbor_ptr(s) && !elem->neighbor_ptr(s)->is_remote()) CPPUNIT_ASSERT_EQUAL(parent->child_neighbor(elem->neighbor_ptr(s)), elem); } @@ -699,6 +714,15 @@ public: if (!parent_edge->infinite()) for (const Node & node : child_edge->node_ref_range()) CPPUNIT_ASSERT(parent_edge->contains_point(node)); + + // We can at least check for sharing some node with + // an edge, on both finite and infinite elements + bool shares_an_edge_node = false; + for (const Node & node : child_edge->node_ref_range()) + if (parent_edge->local_node(node.id()) != invalid_uint) + shares_an_edge_node = true; + + CPPUNIT_ASSERT(shares_an_edge_node); } } From da90cb8b8dd2d0d34db913ee1d15bb0b7bdd8df2 Mon Sep 17 00:00:00 2001 From: Roy Stogner Date: Thu, 18 Dec 2025 14:25:40 -0600 Subject: [PATCH 16/16] Fix libmesh_dbg_var() comment I went to double-check that this was the macro I wanted, and the behavior did match my recollection but didn't match my comment --- include/base/libmesh_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/base/libmesh_common.h b/include/base/libmesh_common.h index e2fe143a244..4d61e95b3d7 100644 --- a/include/base/libmesh_common.h +++ b/include/base/libmesh_common.h @@ -271,7 +271,7 @@ extern bool warned_about_auto_ptr; } while (0) // The libmesh_dbg_var() macro indicates that an argument to a function -// is used only in debug mode (i.e., when NDEBUG is not defined). +// is used only in debug and devel modes (i.e., when NDEBUG is not defined). #ifndef NDEBUG #define libmesh_dbg_var(var) var #else