3D node redistribution: the MMPDE mover drives tetrahedral meshes (capstone round 2)#393
Merged
Conversation
…dra (capstone round 2) The mover core was dimension-general all along (the per-element algebra is written over cdim); 3D existence was blocked by the rim, not the method: - cell list / signed measure / volume factor now dispatch on dimension (_tet_cells, new _signed_volumes = det(E)/6, fact = d!); - the honest 2D-only guards (mover, redistribute_nodes, node_redistribution, mesh_metric_mismatch) flip to capability for 3D simplex meshes, still refusing quad/hex and manifolds; - follow_metric's shape-quality polish gains the tet formula (q = 6*sqrt(2)*V/e_rms^3; regular tet = 1, sliver -> 0); - THE REAL 3D BLOCKER: _pinned_mask stopped its label closure at edges, and 3D gmsh labels tag faces only — so on any 3D mesh NO boundary vertex was classified: nothing pinned, nothing slipped, the boundary drifted freely into the interior. Every tagged non-vertex point now closes down to its vertices (bit-identical mask in 2D, where the closure of an edge is exactly its endpoints). Measured on the dipping-fault-plane box: monotone energy descent, zero folds, boundary nodes held exactly on their faces under slip, grading ratio 1.12 vs 1.30 for the same problem in 2D (the equidistribution exponent compresses the ideal from 4x to 2.5x in 3D; repeated calls hold a stable equilibrium, matching 2D behaviour). follow_metric runs end-to-end on 3D. Full mover/adapt family: 215 passed serially. Underworld development team with AI support from Claude Code
Underworld development team with AI support from Claude Code
…es, honest test guard The adversarial review of this branch cleared the 3D generalisation (no correctness defects) and flagged message rot: fault_metric_tensor and fault_comb_metric refused 3D 'because the MMPDE mover is 2D' — no longer true on this branch. Both builders stay 2D-only for their OWN reason (the polyline-band / comb construction is planar geometry) and now say so, pointing 3D users at building a metric directly for redistribute_nodes. The new 3D capability test dropped its log-capture guard: callback-sync failures raise since the #379 rework, so a clean run is itself the guard. The pre-existing rank-local early-return hazard in the mover (empty-rank hang class, shared with 2D) is marked with a TODO(BUG) at the site per the review's note. Underworld development team with AI support from Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
mesh.redistribute_nodes()— moving a mesh's nodes so cell sizes follow a metric, with the topology fixed — now works on 3D tetrahedral meshes, serial and parallel. This removes the second of the two issues the adaptivity "capstone" set out to retire (the first, 3D refinement, landed in #378). The user API does not change: the same call, the same metric forms (scalar density or full tensor), the same slip-surface options.Why changes are considered minor
The mover's mathematics (the Huang–Kamenski variational method, reference [1]) was implemented dimension-generally from the start — the per-element algebra never assumed two dimensions. What kept 3D from working sat at the rim: the cell list, the signed cell measure, and the factorial volume factor were hard-wired for triangles, and the entry points refused anything else. Those now dispatch on dimension (a ten-line signed-volume routine is the only new numerics), and the guards state what is genuinely unsupported (quad/hex meshes, constrained manifolds).
Measured behaviour
follow_metricruns end-to-end on 3D meshes (its shape-quality polish gained the tetrahedral formula).Review
An independent adversarial review of the branch found no correctness defects (it verified the orientation-sign conventions of the fold guard against DMPlex's det-negative reference tets, the bit-identity of the 2D boundary mask, and the parallel consistency of the label closure). Its findings — stale "the mover is 2D-only" rationale in two metric-builder error messages, and an incorrect log-capture guard in the new test — are fixed in the final commit; a pre-existing empty-rank hazard shared with the 2D path is marked at the site.
Evidence
Full mover/adapt serial family: 215 tests green. New/flipped contract tests in test_0764 (3D capability: moves, no fold, boundary held exactly) and test_0850_mesh_smoothing (the dimension guard now rejects only dimensions with no discretization). Style gate clean, no allowlist additions.
Design note with the round-2 status and measurements:
docs/developer/design/ADAPTIVITY_3D_SPHERICAL_2026-07.md.[1] Huang & Kamenski, J. Comput. Phys. 301 (2015) 322 (arXiv:1410.7872).
Underworld development team with AI support from Claude Code