Skip to content

Conversation

@ChengHauYang
Copy link

@ChengHauYang ChengHauYang commented Oct 21, 2025

Motivation

Currently, libMesh assumes physical continuity (base on node ID) between neighboring elements, which limits its ability to handle cases where two regions share coincident boundaries but are geometrically disconnected -- for instance, across interfaces with discontinuous fields or temperature jumps.
This PR introduces a mechanism to explicitly register disconnected boundary pairs, enabling the framework to reconstruct logical neighbor relationships even when elements are not directly connected in the mesh topology (base on node ID).
This functionality is particularly relevant for methods such as the Cohesive Zone Method (CZM), where field continuity is enforced weakly across interfaces.

This work is motivated by long-standing MOOSE discussions about “fake” or “pseudo” neighbors -- element pairs that were once topological neighbors but become disconnected due to node replacement (e.g., in BreakMeshByBlockGenerator).
See the following issues for historical context:

  • MOOSE #12033: Support for non-geometric neighbor connections -- initial proposal to allow interface coupling between geometrically separated regions.
  • MOOSE #21161: BreakMeshByBlock connectivity -- highlighted that after node replacement, elements that were formerly neighbors lose topological connectivity, motivating the need for a libMesh-level mechanism to handle such cases.

This PR effectively formalizes that capability at the libMesh level, turning what was previously a MOOSE-side workaround into a supported feature.

Design

  1. Disconnected Boundary Framework: Introduced an API in MeshBase (add_disconnected_boundaries() and get_disconnected_boundaries()) that allows users to register and access disconnected boundary pairs. These pairs are stored as PeriodicBoundaries objects and used during neighbor search to establish logical connections between geometrically separated regions.
  2. Neighbor Reconstruction and Validation: Extended UnstructuredMesh::find_neighbors() to detect and link virtual (fake, or disconnected) neighbors across disconnected interfaces (boundaries). A new regression test (disconnected_neighbor_test.C) verifies correct neighbor mapping and demonstrates a temperature-jump interface problem that matches the analytical solution.

Impact

Facilitates coupling between subdomains separated by non-matching meshes or duplicated boundaries.

Copy link
Member

@roystgnr roystgnr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this, in principle, and the MOOSE test failures should be trivial to fix. Hopefully the distributed unit test sweep failures won't be much harder.

@ChengHauYang ChengHauYang force-pushed the break_mesh_libmesh_pr branch from e5f64ed to 120b7c1 Compare October 21, 2025 19:37
@roystgnr
Copy link
Member

Do we have discussion of the problem motivating this anywhere on Github that we can link to, for future reference? I know there's been multiple discussions of the "MOOSE can abuse neighbor pointers" issue somewhere, but I can't recall whether any were here rather than on Slack or over video.

I'm not sure how relevant the discussions would be, because IIRC in most of them my opinion had been "we should come up with a different mechanism for MOOSE to use instead of neighbor_ptr" and I think you've managed to convert me to "we should make it so that misuse of neighbor_ptr is actually a supported use", but it'd still be good to be able to look up the history.

@roystgnr
Copy link
Member

Ooh. One thing I missed in the review: this has a good chance of failing in interesting ways as soon as we use it in the presence of refined elements on either side of the pseudo-neighbor links, doesn't it? And we don't have any coverage of that, do we? We need another unit test or two; it'd probably be adequate to just refactor your existing test but refine both elements in the second test and refine just one element in the third.

@ChengHauYang
Copy link
Author

Thank you, @roystgnr ! I tried to add MOOSE #12033 and MOOSE #21161 to the Motivation section of the PR so that others can easily look up the historical context.

@roystgnr
Copy link
Member

Gah, I forgot to re-activate CI for you. Sorry! I'm going to have you sent an Associates team invite so any future pushes can auto-activate CI.

@roystgnr
Copy link
Member

Jobs manually activated, hopefully the last time that's necessary; let me know if the invite doesn't get to you or gives you any problems.

@roystgnr
Copy link
Member

I'm also adding a few optional recipes that I'm not 100% sure will pass; I don't want to merge if this passes here only to find we have to fix something up for our devel->master merges.

Copy link
Member

@roystgnr roystgnr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only definite fix we need is for that random whitespace futzing that got left around.

Oh, but we might also need to #ifdef LIBMESH_HAVE_SOLVER around those tests, since they involve a solve(); I'm not sure any of our regular recipes use such a decimated libMesh, but better safe than sorry.

(e->processor_id() == mesh.processor_id()),
"Periodic boundary neighbor not found");
(e->processor_id() == mesh.processor_id()),
"Periodic boundary neighbor not found");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any substantive changes left in periodic_boundaries.C? You can revert the whitespace regressions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I will revert it! Thanks, @roystgnr!

BTW, I have received the Associates team invite. Thanks!

element->neighbor_ptr(ms) != remote_elem)
continue;

for (const auto & [id, boundary_ptr] : *db)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, wow - speaking of whitespace issues. The first build failure I looked at (and probably most of the rest) was from a -Werror=misleading-indentation warning:

this 'if' clause does not guard... [-Werror=misleading-indentation]
               if (element->neighbor_ptr(ms) != nullptr &&
               ^~
../src/mesh/unstructured_mesh.C:1018:17: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
                 for (const auto & [id, boundary_ptr] : *db)

Copy link
Author

@ChengHauYang ChengHauYang Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to fix the indent here in the commit 64e4597. Thanks!

@ChengHauYang ChengHauYang force-pushed the break_mesh_libmesh_pr branch from 99faccb to 64e4597 Compare October 24, 2025 15:48
@roystgnr
Copy link
Member

Well, the good news is that the invite went through and CI is autorunning now. The bad news is that we're failing multiple recipes. "Test mac" is a red herring, but the distributed-mesh test looks like a real failure and I can't even imagine what is going on in the --disable-amr and --disable-deprecated tests.

@ChengHauYang
Copy link
Author

Thank you, Roy, for your feedback. I think this issue is related to whether disconnected elements are being added as ghosted elements. I recently implemented a RelationshipManager in MOOSE, which allows me to properly handle disconnected neighbors on distributed meshes. However, this functionality is not yet available on the libMesh side. I plan to add a GhostingFunctor for disconnected neighbors in libMesh to see if that resolves the issue. Thanks again for taking the time to look into this.

@moosebuild
Copy link

moosebuild commented Oct 25, 2025

Job Coverage, step Generate coverage on 5301b91 wanted to post the following:

Coverage

03e0ca #4283 5301b9
Total Total +/- New
Rate 65.01% 65.03% +0.03% 90.62%
Hits 76959 77033 +74 58
Misses 41429 41418 -11 6

Diff coverage report

Full coverage report

This comment will be updated on new commits.

@ChengHauYang ChengHauYang force-pushed the break_mesh_libmesh_pr branch 3 times, most recently from 1a50621 to a8a8602 Compare October 27, 2025 22:06
…n also access them.

(b) add geometry-based detection of disconnected neighbors via paired boundaries
- Introduced `Elem::geometrically_equal()` for tolerance-based geometric comparison of elements.
- Replaced `_has_disconnected_neighbor` with `_boundary_id_pairs` for boundary-pair registration.
- Updated `UnstructuredMesh::find_neighbors()` to auto-detect disconnected neighbors using geometric matching and paired boundaries.
- Introduced PeriodicBoundaries::disconnected_neighbor() to locate geometrically coincident elements across disconnected boundaries using PointLocator.
- Replaced legacy geometry-based neighbor search in UnstructuredMesh::find_neighbors() with unified PeriodicBoundaries-based logic.
- Moved new MeshBase functions out of the header to use forward declarations, reducing unnecessary includes.
- Reverted the PeriodicBoundaries::neighbor() logic to allow self-matching, since the existing boundary ID check already prevents unintended self-detections in periodic boundary cases.
(a) Revert periodic BC source file
(b) Fix indentation in UnstructuredMesh::find_neighbors and wrap tests (c) involving solve() in DisconnectedNeighborTest with #ifdef LIBMESH_HAVE_SOLVER guards to ensure correct conditional compilation.
The DisconnectedNeighborTest::testTempJump fails in certain CI environments (e.g., 'Test No AMR' recipe), potentially due to issues with the default solver/preconditioner selection, numerical errors (0 vs 0.05), or SEGVs observed in logs.
To attempt to bypass these issues and diagnose the failure, this commit explicitly sets the linear solver to GMRES and no preconditioner.
Introduce `include_internal_boundary` switch to
`side_with_boundary_id()` so we can locate boundaries even when a
neighbor exists (e.g. manually disconnected interfaces).

This fixes interface-side lookup in the temperature jump CZM-style test
without requiring AMR fallback. Updated the test to explicitly use
include_internal_boundary=true for interface IDs.
…l interface support

This change introduces correct handling of intentionally disconnected interfaces (e.g., CZM or jump conditions) on distributed meshes:
- Replace ID-based lookup in `MapBasedDisconnectedGhosting` with direct `Elem*` neighbor mapping to avoid invalid lookups post-renumbering.
- Register the disconnected ghosting functor in `find_neighbors()` so ghosting stays consistent with the neighbor graph established by `prepare_for_use()`.
- Extend `BoundaryInfo::side_with_boundary_id()` to optionally allow internal/logical boundaries when explicitly requested by the caller.
- Fix face FE assembly under `--disable-deprecated` by explicitly requesting shape and map computations before reinit() calls.
- Remove older per-test ghosting setup now handled by the mesh.
@ChengHauYang ChengHauYang force-pushed the break_mesh_libmesh_pr branch from a8a8602 to 0bcdbb4 Compare October 27, 2025 22:19
Restore previous behavior in `side_with_boundary_id()`. When AMR is disabled,
internal boundaries incorrectly returned invalid_uint. Add fallback return
for non-external sides.

Add `testInternalBoundary()` to verify correct side detection on a QUAD4
mesh. Update `disconnected_neighbor_test` to use default API.
This commit removes the `map_based_disconnected_ghosting` implementation and fully migrates the build/runtime logic to `disconnected_neighbor_coupling`.

- Remove map_based_disconnected_ghosting.{h,C} and all associated build rules
- Add disconnected_neighbor_coupling.{h,C} into all Makefile targets
- Drop ghosting implementation in UnstructuredMesh::find_neighbors()
- Update tests to use `DisconnectedNeighborCoupling` directly
- Enable fine-mesh test (testTempJumpRefine)
@ChengHauYang ChengHauYang force-pushed the break_mesh_libmesh_pr branch from 0adca9c to 6708c6d Compare October 28, 2025 21:01
@ChengHauYang ChengHauYang force-pushed the break_mesh_libmesh_pr branch from a7b9cfb to 71680c0 Compare October 28, 2025 22:06
- Ensure boundary ID is added to a locally owned elem
- Select valid left-bottom elem with neighbor for internal side
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants