2-D placement goes parallel: one rebuild for every dimension and rank count - #523
Conversation
… count The lifecycle ruling's second half, first instalment. The uninterp + SF-first + DMPlexInterpolate rebuild is generalised to any dimension (_rebuild_sewn: cone size dim+1, the handedness flip reads signed area in 2-D, join recovery over edges alone) and BOTH 2-D serial paths now go through it — reconnect.rebuild_cavities leaves the placement family. The handedness census (_cell_volumes_signed6) is dimension-generic too: signed 2*area in 2-D, same structural point (abs() is blind to inversion). place_along_lines runs distributed via _place_one_parallel: mark by distance, gather the region to one rank, carve and fill there, rebuild collectively, label by the chain's new ids, gate globally (area conserved, inversion, the validity battery with check_faces at every rank count, held surfaces compared per rank — the mesh outside the region is untouched, so per-rank equality is exactly the right gate). Scope, stated not fudged: INTERIOR surfaces — a wall-touching end needs the end-settling machinery (wall-vertex slides, facet splits) whose collective form is not built, and refuses collectively with the reason; the serial path keeps the full feature set. Wall proximity is judged against TRUE wall segments (support 1 AND unshared — a seam edge also has local support 1, the 3-D lesson one dimension down). Measured: same facet count as serial and P2 FE exactness (x^2+y^2 to 1e-11) at np=1..4. ptest_0853 added (FE oracle + collective wall refusal); all serial suites and the 3-D ptests green. Next instalment: the 2-D ribbon and 2-D removal on the same skeleton. Underworld development team with AI support from Claude Code
|
Adversarial review (self, posted per project practice): What broke and taught. The handedness census was 3-D-only in disguise ( The held gate's per-rank form is an argument, not an approximation. A global count of interface edges needs owned-only reduction machinery; per-rank equality is strictly stronger here BECAUSE the surgery region is rank-interior — every other rank's counts must be bit-unchanged, and the target rank's may grow only in the label being written. If a future change lets surgery touch shared points, this gate must be revisited — the gather-contract assertion (no touched point is shared) sits directly upstream and would fire first. Scope honesty. Interior-only in parallel is a real restriction, chosen over a half-collective end-settling that would move shared wall vertices without SF-propagating coordinates. The refusal is collective and names the serial path. Ribbons (interior by construction) and removal inherit no such restriction and arrive next. Underworld development team with AI support from Claude Code |
There was a problem hiding this comment.
Pull request overview
Enables distributed (gather-first) 2‑D surface placement via place_along_lines, reusing the same collective rebuild machinery as the 3‑D placement path to avoid redistribution while preserving parallel FE correctness (Issue #520 context).
Changes:
- Route 2‑D placement rebuilds through a new dimension-generic
_rebuild_sewn()that attaches the vertex SF beforeDMPlexInterpolate. - Add a distributed
place_along_linesimplementation that refuses near-wall endpoints collectively and applies global validity/orientation/area gates. - Add a new parallel regression test (
ptest_0853) validating P2 FE exactness on placed meshes and collective refusal behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/underworld3/utilities/place_surface.py |
Implements distributed 2‑D placement and generalizes the collective rebuild to work in both 2‑D and 3‑D. |
tests/parallel/ptest_0853_place_lines_parallel.py |
Adds a parallel test that detects wrong-operator rebuild failures and checks collective refusal for wall-touching surfaces. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| mark = np.zeros(pEnd - pStart, dtype=np.int32) | ||
| mark[np.flatnonzero(d_line < (clearance + 2.0) * h_vertex) | ||
| + vS - pStart] = 1 | ||
| dm_work, moved = _gather_region(dm, mark) | ||
| if moved: |
Second half of the lifecycle ruling (no redistribution, ever), first instalment: the 2-D placement family joins the 3-D one on the gather-first machinery.
_rebuild_sewnis now dimension-generic (cone size dim+1; the handedness flip reads signed area in 2-D; join recovery over edges alone) and both 2-D serial paths route through it —reconnect.rebuild_cavitiesleaves the placement family. The handedness census generalises with it (signed 2×area; abs() is blind to inversion in any dimension).place_along_linesruns distributed: mark by distance → gather the region to one rank → carve and fill there → collective rebuild (vertex SF attached before interpolate, the place_sheet parallel rebuild: face/edge cone orientations unreconciled across the point SF — parallel FE on placed meshes assembles a wrong operator #520 property) → label by the chain's new ids → global gates (area conserved, inversion, the validity battery with check_faces at every rank count, held surfaces compared per rank — the mesh outside the region is untouched, so per-rank equality is exactly the right gate).Measured: same facet count as serial and P2 FE exactness (x²+y² to 1e-11) at np=1..4. ptest_0853 added (FE oracle + collective wall refusal, since a rank-local raise is a hang at np≥3); all serial suites (42) and the 3-D ptests green.
Next instalment on this line: the 2-D ribbon (
place_thin_volume) and 2-D removal on the same skeleton, then the distributed add–solve–remove–add composition test.Underworld development team with AI support from Claude Code