Outcropping faults: sheets meet the upper surface (specify-long, bowl cavity, remeshed cap) - #525
Conversation
… bowl cavity + remeshed cap) The science case this machinery exists for: plate-boundary faults intersect the free surface. Three pieces, spike-proven first (outcrop_sheet_spike.py): 1. THE SPECIFY-LONG CONTRACT (ruling 2026-08-11): sheets may extend PAST the domain. _clip_sheet_to_box Sutherland-Hodgman-clips each triangle against the box planes — cut points snap exactly onto the plane and are computed in canonical edge order so shared edges dedupe bitwise — and _outcrop_chain extracts the trace as one ordered polyline on ONE wall (box-edge and multi-wall outcrops refuse with the reason). 2. THE BOWL CAVITY: with an outcrop, wall vertices inside the cap become victims and _closed_shell_3d returns the dropped cells' wall faces as CAP faces (any other wall contact still refuses); the manifold check runs on shell UNION cap. The cap over the cavity is PRE-MESHED by the 2-D fill in the wall plane's own coordinates — rim ring verbatim, outcrop chain embedded free-end — because a geo surface bounded by the discrete rim RESAMPLES it (measured: 84 renumbered nodes, volume 1.31, Euler -46; MeshOnlyEmpty does not prevent it). The 3-D fill takes the surface loop [shell + discrete cap] with the sheet embedded, outcrop nodes shared by tag between cap and sheet. 3. EXPLICIT RELABELLING: the cap's new wall faces carry exactly the (label, value) pairs the replaced faces carried, written with their full closures — joins cannot recover new points, and an unlabelled boundary patch silently loses its Dirichlet conditions. Measured, serial and np=2..4: identical facet counts, P2 quadratic FE-exact (2e-11..5e-11) WITH a Dirichlet condition gripping the remeshed Top, full Top-label coverage on the new boundary faces. Interior sheets unchanged (suites 0853-0856, 44 serial + placement ptests green). The outcropping thin-volume BAND is the remaining instalment (plan carries the design: cap-with-hole + interior skin as one closed loop). Underworld development team with AI support from Claude Code
|
Adversarial review (self, posted per project practice): What we attacked: the cap is the first fill piece whose triangulation is OURS rather than gmsh's-under-gates, so its two failure modes got explicit probes. Resampling: the geo-surface route produced 84 renumbered rim nodes / volume 1.31 / Euler −46 before the pre-meshed discrete cap; the moved-node gate now covers rim and chain, and the cap-triangle-count gate refuses a remeshed cap. Label loss: the coverage assertion in test_0854 walks every boundary face on the top plane — an unlabelled patch cannot pass, and the FE oracle with Dirichlet on the remeshed Top would diverge from the quadratic if the condition lost grip anywhere. Scope honesty: one flat wall per sheet; box-edge outcrops, multiple traces, closed traces, and curved/deformed tops refuse with their reasons (the last is the manifold-prep slot and matters for free-surface runs — recorded in the plan). The clip has no projection band yet: a sheet stopping short of the wall is honestly buried, not snapped; the band is a stated refinement. Process note for the record: the working-directory drift bit again mid-landing (the branch was created in the wrong checkout and the main repo briefly sat on a feature branch — restored to development immediately). All git operations in this session now name their repo explicitly. Underworld development team with AI support from Claude Code |
There was a problem hiding this comment.
Pull request overview
Adds “outcropping sheet” support to place_sheet() so a fault surface can extend beyond the domain, be clipped to the box, and (when it intersects a single wall plane) open a cavity onto that wall with a pre-meshed/remeshed cap that preserves boundary labels. This extends the placed-surface tooling to cover the stated plate-boundary science case where faults intersect the free surface.
Changes:
- Clip input sheet triangles to global box bounds and extract a single-wall outcrop chain to drive an “open wall” cavity case.
- Extend the 3-D cavity carve + shell closure + gmsh fill path to support an open-wall cap (pre-meshed in 2-D and embedded into the 3-D fill).
- Add a new regression/contract test asserting the outcropping case preserves Top boundary labelling and retains P2 exactness under Dirichlet BCs.
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 sheet clipping + outcrop detection; supports open-wall cavity with a pre-meshed cap; explicitly re-applies boundary labels to remeshed cap faces. |
tests/test_0854_place_sheet.py |
Adds a new test exercising an outcropping sheet that intersects the Top wall and verifies label preservation and FE exactness. |
Suppressed comments (2)
src/underworld3/utilities/place_surface.py:1704
on_planecan only store one wall code per point, but corner/edge points lie on multiple planes. The current assignment overwrites earlier axes, so_outcrop_chain()may fail to detect multi-wall/box-edge outcrops (which are supposed to be refused) and can misclassify boundary edges.
on_plane = np.full(len(out_pts), -1, dtype=np.int64)
for axis in range(3):
on_plane[out_pts[:, axis] == lo[axis]] = 2 * axis
on_plane[out_pts[:, axis] == hi[axis]] = 2 * axis + 1
return out_pts, np.array(out_tris, dtype=np.int64), on_plane
src/underworld3/utilities/place_surface.py:2468
wall_pairscurrently includes label values even when the value is negative (typically meaning “unlabelled”). When those pairs are later applied to the full transitive closure, this can overwrite existing non-negative label values on shared rim points/edges with the negative value, silently stripping labels that were not meant to be changed. Only propagate non-negative label values from the replaced faces.
for val in values.getIndices():
if all(lab.getValue(p) == int(val)
for p in cap_face_pts):
wall_pairs.append((name, int(val)))
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| pts = np.asarray(pts, dtype=float) | ||
| key_of = {} | ||
| out_pts = [] | ||
| corner_ids = {} |
The science case the fault meshes exist for: plate-boundary faults intersect the free surface. Spike-proven first (
outcrop_sheet_spike.py, two dead ends measured and recorded), then production inplace_sheet, serial and parallel._clip_sheet_to_boxclips each triangle against the box planes — cut points snap exactly onto the plane, computed in canonical edge order so shared edges dedupe bitwise — and_outcrop_chainextracts the trace as one ordered polyline on one wall. Box-edge and multi-wall outcrops refuse with the reason (recorded residuals, with curved/deformed tops — the manifold-prep slot)._closed_shell_3dreturns dropped cells' wall faces as CAP faces (other wall contact still refuses); the manifold check runs on shell ∪ cap. The cap is pre-meshed by the 2-D fill in the wall plane's own coordinates (rim verbatim, outcrop embedded) because a geo surface bounded by the discrete rim RESAMPLES it — measured, andMeshOnlyEmptydoes not prevent it. The 3-D fill takes the loop [shell + discrete cap] with the sheet embedded, outcrop nodes shared by tag.Measured, serial and np=2..4: identical facet counts, P2 quadratic FE-exactness (2e-11–5e-11) with a Dirichlet condition gripping the remeshed Top, and full Top-label coverage asserted face-by-face. Interior sheets byte-for-byte unaffected: suites 0853–0856 (44 serial) and the placement ptests green. New contract test in test_0854. The outcropping thin-volume BAND (cap-with-hole + interior skin as one closed loop) is the remaining instalment; the plan file carries its design.
Underworld development team with AI support from Claude Code