Skip to content

Outcropping faults: sheets meet the upper surface (specify-long, bowl cavity, remeshed cap) - #525

Merged
lmoresi merged 1 commit into
developmentfrom
feature/fault-outcrop
Aug 11, 2026
Merged

Outcropping faults: sheets meet the upper surface (specify-long, bowl cavity, remeshed cap)#525
lmoresi merged 1 commit into
developmentfrom
feature/fault-outcrop

Conversation

@lmoresi

@lmoresi lmoresi commented Aug 11, 2026

Copy link
Copy Markdown
Member

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 in place_sheet, serial and parallel.

  • Specify-long contract (ruling 2026-08-11): sheets may extend past the domain. _clip_sheet_to_box clips 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_chain extracts 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).
  • The bowl cavity: wall vertices inside the cap become victims; _closed_shell_3d returns 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, and MeshOnlyEmpty does not prevent it. The 3-D fill takes the loop [shell + discrete cap] with the sheet embedded, outcrop nodes shared by tag.
  • Explicit relabelling: the cap's new wall faces carry exactly the (label, value) pairs the replaced faces carried, with 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-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

… 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
Copilot AI lite review requested due to automatic review settings August 11, 2026 11:34
@lmoresi

lmoresi commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

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

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

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_plane can 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_pairs currently 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 = {}
@lmoresi
lmoresi merged commit dba2890 into development Aug 11, 2026
3 checks passed
@lmoresi
lmoresi deleted the feature/fault-outcrop branch August 11, 2026 11:54
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