Skip to content

remove_embedded: the fault-lifecycle removal primitive (no redistribution, ever) - #522

Merged
lmoresi merged 1 commit into
developmentfrom
feature/placement-lifecycle
Aug 11, 2026
Merged

remove_embedded: the fault-lifecycle removal primitive (no redistribution, ever)#522
lmoresi merged 1 commit into
developmentfrom
feature/placement-lifecycle

Conversation

@lmoresi

@lmoresi lmoresi commented Aug 11, 2026

Copy link
Copy Markdown
Member

First half of the lifecycle ruling (2026-08-11): a fault is added to the running distributed mesh and a spent one deleted, with no redistribution. This PR is the delete.

remove_embedded(dm, label, value) — the inverse placement. The object's labels seed the carve (cells = a thin volume, faces = a placed sheet; a zone's _skin companion goes with it); the cavity refills with a plain gmsh fill at background scale, erasing the layer-scale grading; the labels vanish with their points, gated globally empty. Other embedded surfaces are held and count-gated exactly as placements hold them (the interface readers gain exclude= so a removal doesn't hold its own object against itself). Parallel by the same gather-first surgery; the object's geometry is allgathered from its labels first, so removal works whatever the current distribution.

Measured round trip, np=1..4: add zone → FE-exact (9e-16) → remove → FE-exact (4e-11), cells back within ~8% of base → add a sheet in the SAME region → remove → FE-exact. (Refill cell counts are partition-sensitive — gmsh vs shell node ordering — and deliberately not part of the contract; the invariants are.)

Composing the lifecycle surfaced three robustness fixes, each measured before fixing:

  • The sheet carve gets the shell-GROWTH: placing into a refilled region can pinch where the original mesh did not. Now one shared helper (_closed_shell_3d) for every 3-D carve — the unification debt from the Thin-volume fault zones + the 2-D fill delegated to gmsh (walk retired) #519 review, paid.
  • The sheet carve gets the centroid rule: a refill's locally-small cells can straddle a rim EDGE with all vertices outside reach (gmsh: "a segment and a facet intersect"); any cell whose centre is within reach of the bounded sheet drops. The REMOVAL carve deliberately omits this rule — nothing is being embedded, and with it a removal reached the wall its own placement had cleared.
  • Surgery-rank catches broadened to Exception: raw gmsh errors are plain Exceptions, and an uncaught raise on the surgery rank is a hang for its peers.

test_0856 (5 serial; number claimed in the ledger) + ptest_0856 (np=2,4, collective refusals asserted); suites 0853–0855 green against the widened carve. The 2-D parallel family (which retires the 2-D serial-only refusals and brings 2-D removal) is the next PR on this branch line.

Underworld development team with AI support from Claude Code

…d (lifecycle half 1)

The design ruling this serves: a fault is ADDED to the running distributed
mesh and a spent one DELETED, with no redistribution ever. Removal is the
inverse placement: the object's labels seed the carve (cells = a thin
volume, faces = a sheet; a zone's companion _skin label goes with it), the
cavity is refilled by a PLAIN gmsh fill at the background scale — erasing
the layer-scale grading — and the labels vanish with their points (gated:
globally empty afterwards). Every OTHER embedded surface is held and
count-gated exactly as a placement holds it; the interface readers gain an
exclude= parameter so a removal does not hold its own object against
itself. Parallel via the same gather-first surgery; the object's geometry
is allgathered from its labels first, so removal works whatever the current
distribution.

Composing the lifecycle surfaced three robustness fixes, all measured:

- The sheet carve gains the volume carve's shell-growth: a sheet placed
  into a REFILLED region can pinch against the new connectivity where the
  same sheet in the original mesh did not. The growth is now one shared
  helper (_closed_shell_3d) used by every 3-D carve — the flagged
  unification debt, paid.
- The sheet carve gains the centroid rule: the plane-straddle test covers
  triangle interiors, and a refill's locally-small cells can straddle a rim
  EDGE with every vertex outside reach (gmsh: "a segment and a facet
  intersect"). Any cell whose centre is within reach of the bounded sheet
  is dropped.
- Surgery-rank catches broadened to Exception: a raw gmsh error is a plain
  Exception, and an uncaught raise on the surgery rank is a HANG for its
  peers — every failure must become a collective refusal.
- The removal carve does NOT use the centroid rule (nothing is being
  embedded, there are no straddlers to catch); with it, a sheet removal
  reached the domain wall that the sheet's own placement cleared.

Round trip measured at np=1..4: add zone -> FE-exact (9e-16) -> remove ->
FE-exact (4e-11), cell count back within ~8% of base -> add sheet in the
SAME region -> remove again -> FE-exact. Cell counts after a refill are
partition-sensitive (gmsh fill vs shell node ordering) and are not part of
the contract; the invariants are.

tests/test_0856 (5 serial, number claimed in the ledger) and ptest_0856
(np=2,4); suites 0853-0855 green against the widened sheet carve.

Underworld development team with AI support from Claude Code
Copilot AI lite review requested due to automatic review settings August 11, 2026 02:36
@lmoresi

lmoresi commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

Adversarial review (self, posted per project practice). What we attacked:

The compose case is the honest test, and it failed twice before it passed. Removing then re-placing in the SAME region is the lifecycle's actual workload, and the refilled region is a different mesh than the original: locally smaller cells (fill grades 0.3h–1.2h) and different connectivity. First failure: the sheet carve pinched against the new connectivity — the volume carve had growth, the sheet carve had a raise; unified. Second failure: a rim edge crossed a small refill cell whose every vertex was outside reach — gmsh PLC error, escaping as a plain Exception that would have HUNG a parallel run (the catch was too narrow). Both fixes are in the carve, not the fixture.

Asymmetry we defend: the placement carves keep the centroid rule, the removal carve drops it. The rule exists to catch cells straddling an object about to be embedded; a removal embeds nothing, and with the rule a sheet removal reached the domain wall its own placement had cleared. Same reach logic, different purpose, measured both ways.

What we deliberately did not promise: refill cell counts across partitions. gmsh's fill depends on shell node ordering, which the gather sets — counts vary by ±3 at np=1..4 while volume, labels, conformity and FE exactness hold identically. The old cut had the same property; pinning counts would manufacture flakiness.

Residual risks, stated: (1) _labelled_face_soup allgathers the object's skin — fine for fault-scale objects, quadratic-ish if someone removes a domain-filling zone; acceptable and documented by the soup's docstring. (2) A removal's refill quality is gmsh's default — no repair pass is run; reconnect composes after if wanted. (3) 2-D removal refuses with a pointer to the 2-D parallel work, where it lands with the shared machinery rather than twice.

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 adds the mesh “fault lifecycle” deletion half by introducing remove_embedded(dm, label, value) to carve out an embedded sheet/zone and refill the cavity at background resolution (without redistribution), while preserving other embedded interfaces via the same gather-first surgery discipline used by placements.

Changes:

  • Add remove_embedded() plus supporting helpers (_gmsh_fill_plain_3d, _labelled_face_soup) to remove labelled embedded objects and plain-refill their cavity.
  • Unify 3‑D carve shell closure behavior via _closed_shell_3d() and extend the sheet carve to include the centroid-distance drop rule.
  • Add serial and MPI tests for removal behavior and collective refusals.

Reviewed changes

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

File Description
src/underworld3/utilities/place_surface.py Implements remove_embedded(), adds plain gmsh refill, adds exclude support for interface detection, and refactors 3‑D shell closure into a shared helper.
tests/test_0856_remove_embedded.py Adds serial lifecycle tests (remove zone/sheet, preserve unrelated surface, refusal messaging, add→remove→add composition).
tests/parallel/ptest_0856_remove_embedded_parallel.py Adds MPI tests for distributed removal correctness and collective refusal behavior.
Suppressed comments (1)

src/underworld3/utilities/place_surface.py:2725

  • In remove_embedded(), the surgery-rank selection uses n_region = (d_skin < reach_v).sum() + seed.sum(). If clearance is 0 (or very small), reach_v can be 0 and d_skin is 0 on the labelled surface, so d_skin < reach_v is false everywhere; this can select the wrong target rank and make the removal fail even though the object exists. Use the same superset radius as the gather mask (or add an explicit collective guard when owners.sum()==0).
    n_region = int((d_skin < reach_v).sum() + seed.sum())
    owners = np.asarray(comm.allgather(n_region))
    target = int(np.argmax(owners))


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

Comment on lines +2647 to +2651
# The object's global geometry, and which cells are its own.
soup_pts, soup_tris = _labelled_face_soup(dm, removed_pairs)
n_soup = int(comm.allreduce(len(soup_tris), op=MPI.MAX))
if n_soup == 0:
raise ValueError(
Comment on lines 2229 to 2233
raise RuntimeError(
"the thin volume's cavity needs a cell that belongs to a surface "
"already embedded. Zones and surfaces must be separated by at "
"least a cell.")
if not drop.any():
@lmoresi
lmoresi merged commit c39a2bd into development Aug 11, 2026
3 checks passed
@lmoresi
lmoresi deleted the feature/placement-lifecycle branch August 11, 2026 02:56
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