Skip to content

place_sheet runs in parallel: gather-first, one rebuild for every rank count - #518

Merged
lmoresi merged 1 commit into
developmentfrom
feature/place-sheet-parallel
Aug 10, 2026
Merged

place_sheet runs in parallel: gather-first, one rebuild for every rank count#518
lmoresi merged 1 commit into
developmentfrom
feature/place-sheet-parallel

Conversation

@lmoresi

@lmoresi lmoresi commented Aug 9, 2026

Copy link
Copy Markdown
Member

Closes the last refusal on the 3-D placed surface, per the maintainer ruling that "planned, ready" parallel gaps fossilize. Serial and parallel share one mechanism — no fork for the serial suite to diverge from.

Mechanism

Gather-first. The sheet's region is marked as an SF-reconciled function of the mesh (per-vertex h reduced over the star-forest, so no rank's verdict depends on the partition), gathered onto one rank by a shell partitioner (mask-driven port of the contact stream's fault-aware redistribution, c8693579/1d487319, recorded in the ledger — the label-driven original is measured at np=2–8), and the serial carve-and-fill runs there as the rank-local step.

The rebuild is the uninterpolated-cells + DMPlexInterpolate pattern (their 3-D splitter's move, same ledger entry): only cell→vertex cones are wired by hand, PETSc derives every face/edge/orientation, and old faces and edges are recovered by joining surviving vertex tuples — labels transfer topologically, no tet orientation hand-encoded, ever. The gathered region is rank-interior, so nothing the surgery touches is shared: the leaf set is unchanged and _rebuild_point_sf renumbers in one exchange. Partition-independent by construction — the fill sees the identical cavity whatever the incoming partition.

Two parallel defects found by the sweep, fixed, regression-tested

  1. np≥4 hang: the gather can empty a rank, and cell_diameters raises on one — a rank-local death while peers entered the next collective (pytest-timeout couldn't even kill it). Empty ranks now contribute the identity of every reduction; the refusal ptest at np=4/5 regression-tests it and simultaneously proves every refusal collective (all ranks raise the identical error, or none does).
  2. Seam faces masquerading as walls: a partition-seam face also has local support 1; wall = support 1 and unshared, vertex mask OR-reconciled.

Verification

  • ptest_0854 at np=2, 3, 4, 5 (version-stable gates: facet count ≡ input triangles, volume conserved, global Euler 1 from owned counts, walls preserved as owned sums, info dict identical on every rank).
  • Serial: 87 tests across 0843/0844×2/0853/0854/0845, unchanged and green on the unified path; 2-D parallel suites at np=2/3 green; style gate clean.
  • Test number 0854 (parallel file) already claimed in the ledger.

Underworld development team with AI support from Claude Code

…k count

The serial refusal is gone, and not by adding a parallel fork: serial and
parallel now share ONE mechanism, so the serial suite exercises the machinery
the parallel path runs.

Gather-first. The sheet's region - victims, crossed cells, and a margin - is
marked as an SF-reconciled function of the mesh (per-vertex h is reduced over
the star forest, so no rank's verdict depends on the partition), gathered
onto the rank that owns most of it with a shell partitioner, and the serial
carve-and-fill runs there as the rank-local step. The gather and its star+1
growth are a mask-driven port of the contact stream's fault-aware
redistribution (feature/fault-split-node c869357 / 1d48731, recorded in the
coordination ledger), whose label-driven original is measured at np=2..8.

The rebuild is the uninterpolated-cells + DMPlexInterpolate pattern (their
3-D splitter's move, same ledger entry): only cell-to-vertex cones are wired
by hand, PETSc derives every face, edge and cone orientation, and old faces
and edges are recovered by joining their surviving vertex tuples - so labels
transfer topologically and no tet orientation is ever hand-encoded. Because
the gathered region is rank-interior, nothing the surgery deletes or adds is
shared: the star-forest's leaf set is unchanged and reconnect._rebuild_point_sf
renumbers it in one exchange. The result is partition-independent by
construction - the fill sees the identical cavity whatever the partition was.

Two parallel defects found by the np sweep and fixed:

- np>=4 HANG: the gather can empty a rank, and cell_diameters raises on one;
  the rank died while its peers entered the next collective. An empty rank
  contributes the identity of every reduction, never a raise - the 2-D
  module's own discipline, now applied at both call sites. The refusal ptest
  at np=4/5 is the regression test, and it also proves every refusal
  collective: all ranks raise the identical error, or none does.
- Seam faces masquerade as walls: on a distributed mesh a partition-seam face
  also has local support 1, so "support 1 = wall" misclassifies the seam and
  protects the wrong vertices. A wall face is support 1 AND unshared, with
  the vertex mask OR-reconciled.

Gates, all global: the labelled facet count equals the input triangle count,
domain volume conserved, global Euler number 1 from owned stratum counts,
every prior surface's interior-face count re-read off the result, and the
info dict asserted identical on every rank. ptest_0854 passes at np=2,3,4,5;
the serial suites are unchanged and green on the unified path.

Underworld development team with AI support from Claude Code
Copilot AI lite review requested due to automatic review settings August 9, 2026 23:55

@lmoresi lmoresi left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Adversarial review (authoring session).

Where we attacked it: (1) The happy path is not evidence — the np sweep went to 5 ranks and the refusal path is a first-class test, which is what caught the np≥4 hang (gather empties a rank; cell_diameters raises on one; the rank died inside a collective chain and even pytest-timeout could not kill the survivors — diagnosed from the partial dump's petsc garbage-cleanup MPI error). Empty ranks now contribute the identity of every reduction, and the refusal ptest doubles as the collectivity proof: all ranks raise the identical message or none does. (2) Seam-vs-wall: local support 1 misclassifies partition seams as domain walls; the test is support 1 AND unshared, OR-reconciled. (3) Partition-dependence: per-vertex h is SF-reduced before any marking decision, so which vertices are victims is a function of the mesh, never the partition; the gathered cavity is therefore identical from any incoming distribution, and the ptest asserts the info dict identical on every rank. (4) No serial/parallel fork: one rebuild path, so 87 serial tests exercise the machinery the parallel path runs.

Taken, with attribution (ledger rule 2): the shell-partition gather + SF-propagate (fault-split-node c869357/1d487319, mask-driven port) and the uninterp+DMPlexInterpolate rebuild with getFullJoin recovery (their 3-D splitter) — the move that means no tet cone orientation is ever hand-encoded.

Known limits, stated: load concentrates on the surgery rank for the duration of the placement (bounded by the fault region; per-rank fault patches are the recorded escalation); edge_split.cell_diameters still raises on an empty rank — guarded at our call sites, latent for other parallel callers (flagged in the ledger rather than drive-by-fixed, Charter S9); gather-mask margin is +2 h with a loud collective assert if it ever under-reaches.

Recommend squash-merge (solo branch).

Underworld development team with AI support from Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR enables underworld3.utilities.place_surface.place_sheet() to run in parallel using a unified “gather-first” mechanism shared with serial execution, ensuring partition-independent results by gathering the sheet’s affected region onto one rank, running the carve/fill there, and then collectively rebuilding the mesh across ranks.

Changes:

  • Added SF-reconciled marking + a shell-partitioner gather step so the sheet region becomes rank-interior before surgery.
  • Reworked the 3-D rebuild path to use an uninterpolated-cells + DMPlexInterpolate-style rebuild and added collective global gates (volume, Euler, label integrity).
  • Added a parallel regression test suite covering deterministic invariants and “collective refusal” behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
tests/parallel/ptest_0854_place_sheet_parallel.py Adds parallel tests asserting deterministic contract checks and collective error/refusal behavior across ranks.
src/underworld3/utilities/place_surface.py Implements gather-first parallel path for place_sheet, SF-reconciled marking, seam/wall handling, and collective rebuild + global validation gates.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1296 to +1305
tmp = chart_values.copy()
sf.reduceBegin(MPI._typedict[chart_values.dtype.char], tmp,
chart_values, mpi_op)
sf.reduceEnd(MPI._typedict[chart_values.dtype.char], tmp,
chart_values, mpi_op)
out = chart_values.copy()
sf.bcastBegin(MPI._typedict[chart_values.dtype.char], chart_values, out,
MPI.REPLACE)
sf.bcastEnd(MPI._typedict[chart_values.dtype.char], chart_values, out,
MPI.REPLACE)
[v_uninterp(v) for v in tet])
new.symmetrize()
new.stratify()
new.interpolate()
@lmoresi
lmoresi merged commit b19745f into development Aug 10, 2026
3 checks passed
@lmoresi

lmoresi commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

We reviewed this against the fault-contact socket (place -> split_fault -> contact -> solve) and pushed fixes for what we found (8ff3659). Verdict: with those fixes, the PR meets the contract and is ready to merge (as a MERGE COMMIT, per the shared-ancestry rule from #510).

Finding 1 (correctness, fixed): the sewn mesh had mixed cell handedness. The parallel rewrite dropped the serial path's signed-volume flip; DMPlexInterpolate derives face cones but never normalises cell vertex order, and gmsh's fill tets arrive opposite-handed to the kept cells. Evidence: 4131 vs 443 signed volumes on the sewn mesh; the first Stokes solve (plain, no fault — the isolation control) never converged while every abs()-based volume gate stayed green; the same mesh with cells re-oriented solves in 22 s. Fix: made cells oriented against the kept cells' own sign at wiring, plus a global orientation census gate — abs() volume checks are structurally blind to this defect class.

Finding 2 (gates, added): PETSc's DMPlex validity battery as the final pass (maintainer request). Full set in serial — check_geometry is exactly the instrument that catches Finding 1 on day one. In parallel only the partition-safe subset (symmetry, skeleton, point-SF): we measured check_geometry false-positive on every rank of a PLAIN distributed UnstructuredSimplexBox, so it cannot gate distributed results on this stack. Gate failures are allgathered before raising, per this module's own collective-refusal discipline.

Finding 3 (contract, our side of the socket): sheet triangulations must have no all-rim triangles — the splitter refuses them (both copies of such a face would carry the same vertex triple), and structured grids produce them at corners. Conditioning by centroid-fanning creates sub-h facets that give the contact a residual floor (measured: rel 1.1e-2 stall persisting under LU, so not solver conditioning); conditioning by flipping the corner-quad diagonals is clean. Recorded on the fault-network side; noted here because sheet providers will hit it.

Acceptance on the fixed build: place -> split -> frictionless contact converges in 18 s (faster than the gmsh-embed control, 22 s), no-opening leak 8e-18, sensible slip taper; serial suites 26 green; ptest_0854 green at np=2 and np=4. One honest gap for the ledger: the parallel gates here are topological — a parallel FE acceptance through the splitter waits on the contact stream's pairing-through-redistribution work, which is ours, not this PR's.

Underworld development team with AI support from Claude Code

@lmoresi

lmoresi commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

Follow-up from the composed acceptance (place -> split -> contact, and its isolation ladder): there is a SECOND parallel defect, real and reproducible, in the rebuilt mesh's higher-strata consistency. We are handing this one back with the evidence rather than fixing it, since it sits in the rebuild's design.

Symptom: plain Stokes (no fault machinery at all) on the placed mesh at np=2 takes 3594 s; the identical mesh in serial solves in 22 s; the plain BASE box at np=2 solves in 13 s. The solve converges eventually — the operator is assembled wrong, not singular.

What it is NOT (each eliminated by measurement): not the contact machinery (plain Stokes grinds); not element quality (radius-ratio distribution matches the base mesh); not cell handedness (the orientation census gate passes); not missing/duplicated points (global owned v/e/f/c identical serial vs np=2: 1138/6444/9881/4574); not vertex SF corruption (coordinate drift exactly 0); not the partition (a FRESH DMPlexDistribute of the placed mesh — canonical SF, healthy seam counts — still takes 2212 s); not the solver config (base box np=2 is fast).

What the evidence points at: DMPlexCheckFaces FAILS on the rebuilt mesh at np=4 (rank 3: face closure 1462/1456/1418 vs cell face 1456/1462/1418 — same vertex set, swapped order = a cone-orientation mismatch), while the plain distributed base box passes CheckFaces cleanly. We must retract part of our earlier gate scoping: only check_geometry false-positives on healthy distributed meshes; check_faces failing on the REBUILT mesh is a true positive. Inconsistent face/edge cone orientations across the point SF disorder the P2 edge/face DOF numbering during assembly — which produces exactly this signature, and which serial cannot see (no seam) and redistribution cannot heal (it faithfully migrates the local cones).

Where to look: the collective uninterpolate -> DMPlexInterpolate rebuild interpolates each rank's chart locally; nothing afterwards reconciles the leaf faces'/edges' cone order against their roots. The 2-D module never met this because its surgery output is consumed rank-locally; the FE assembly across the seam is what demands root/leaf cone agreement. Suggested acceptance for the fix: CheckFaces green on the rebuilt mesh at np=2..5, and plain Stokes on a placed mesh at np=2 within ~2x of the base box (ours: 13 s).

Merge posture: this does not block the serial capability (fully validated, 18 s acceptance) or the parallel topological layer (gates green np=2-5). It DOES block parallel FE work on placed meshes. Our recommendation: merge, with this defect recorded as the ledger item gating "parallel placed-mesh FE", or hold for the cone-reconciliation fix — maintainer's call.

Underworld development team with AI support from Claude Code

@lmoresi

lmoresi commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

The parallel higher-strata defect from the review dossier above is now tracked as #520 (with reproducer and acceptance criteria) — it survives #519 unchanged, measured on development at eb8d2d3, and gates parallel FE on both placed sheets and thin volumes. Serial capability unaffected.

lmoresi added a commit that referenced this pull request Aug 10, 2026
…n test

The interface-cone fix (#521, closing #520) verifies independently on
this branch: CheckFaces green at np=2 on the rebuilt mesh, placed-mesh
plain Stokes 24 s (was 3594 s), and — the acceptance that motivated
the whole ladder — the COMPOSED chain place_sheet -> split_fault ->
frictionless contact at np=2 converges in 18 s with the serial answer
(peak slip 0.1461 vs 0.1462) and machine-zero no-opening leak.
ptest_0852 locks that chain as a regression gate: the solve IS the
gate, because both #518-era defects passed every topological check.

Underworld development team with AI support from Claude Code
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.

2 participants