place_sheet: the 3-D placed surface (serial), with a gated gmsh cavity fill - #517
Conversation
The 3-D form of the placed surface. The sheet's points become mesh vertices and every sheet triangle becomes a labelled interior face, with the rim free inside the mesh - the case a cut cannot represent and a two-sub-volume trick cannot fake. Carve (clearance plus crossed tets, every victim's star), fill, sew (createFromCellList on a compacted vertex set), and carry every label topologically: vertex numbering through the rebuild is DEFINED, so faces and edges transfer by vertex tuple and coordinates are never consulted. The fill is delegated to gmsh and GATED, not trusted: the cavity shell and the sheet must come through bit-identical, every sheet triangle must be an interior face of the sewn mesh, and conformity, Euler number, degeneracy and the wall labels are checked on every call. Measured basis: the cavity spike, 6/6 across tilt, resolution, clearance and sub-h sheet spacing - including gmsh inserting its own Steiner points without touching either constraint, which retires the plan's constrained-tetrahedralisation STOP gate. The 2-D guards are ported, faces standing where edges stood: cells owning an interior labelled face are held out of the cavity, a sheet needing them is refused, and every earlier surface's interior-face count is re-read off the RESULT mesh - the lesson that a partial corruption satisfies any identity summed over per-placement bookkeeping. Serial by design: the parallel route is gather-first (redistribute so the sheet's star is rank-interior, place locally, renumber), after which this serial operation is the rank-local step and no placed point is ever shared. reconnect.py is untouched; the chart-surgery generalisation belongs to that stage. Deliverable contract for the fault module: a labelled facet patch - every sheet triangle a face under one (name, value), interior with two cells, an open manifold with its rim edges single - asserted as test_0854. Underworld development team with AI support from Claude Code
There was a problem hiding this comment.
Pull request overview
This PR introduces a new 3‑D “placed surface” capability (place_sheet) that embeds a triangulated sheet into an existing 3‑D simplex mesh by carving a cavity, delegating the cavity fill to gmsh, and then sewing the result back while preserving labels topologically. It also adds a dedicated regression test suite establishing the contract expected by downstream fault-splitting functionality.
Changes:
- Add
place_sheet()and supporting 3‑D cavity-carve / gmsh-fill / sew / label-transfer helpers tounderworld3.utilities.place_surface. - Add
test_0854_place_sheet.pyto assert the 3‑D placed-sheet contract (interior labelled faces, open-manifold rim, label preservation, and dimension-dispatch guards). - Expand module “Scope” documentation in
place_surface.pyto reflect 3‑D support and the gated gmsh fill approach.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/underworld3/utilities/place_surface.py |
Implements 3‑D sheet placement with gated gmsh cavity fill, sewing, and topological label transfer. |
tests/test_0854_place_sheet.py |
Adds contract/regression coverage for the new 3‑D place_sheet() behavior and key guards. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| def _interior_face_counts_3d(dm): | ||
| """{(label, value): count of interior faces} — the breach detector.""" | ||
| interface = _interface_faces_3d(dm) | ||
| fS, fE = dm.getHeightStratum(1) |
| sheet_pts = np.asarray(points, dtype=float).reshape(-1, 3) | ||
| sheet_tris = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) |
| keep_cell = np.ones(len(cells), dtype=bool) | ||
| keep_cell[drop_ids] = False | ||
| keep_vertex = np.ones(len(X), dtype=bool) | ||
| keep_vertex[victims] = False | ||
| old_to_new = -np.ones(len(X), dtype=np.int64) | ||
| old_to_new[keep_vertex] = np.arange(int(keep_vertex.sum())) | ||
| n_survivors = int(keep_vertex.sum()) |
lmoresi
left a comment
There was a problem hiding this comment.
Adversarial review (authoring session).
Where we attacked it: (1) The fill's trust boundary — gmsh output is gated per call (constraint surfaces bit-identical, sheet triangles all interior faces, conformity/Euler/degeneracy/wall counts); the inert-DB-write and misattributed-crash lessons from #515/#510 are why nothing here trusts a library return without reading the result back. (2) The silent-corruption class from 2-D — both cells of a labelled facet cleared while every corner is protected — is guarded with faces standing where edges stood, and the too-close-sheet test accepts either refusal path (held-cell or shell-manifold) while asserting the first sheet is intact afterwards, since which guard trips first is geometry-dependent. (3) Label transfer is topological (vertex tuples through a defined renumbering), never coordinate matching — the property the 2-D rebuilds were built on. (4) Serial-only refusal states the gather-first rationale so the parallel stage has its design recorded at the refusal site.
Known limits, stated rather than discovered later: one sheet per cavity (close pairs are one thin volume — the next work item); no cavity-growth retry loop in 3-D yet (a wedged fill raises with guidance rather than growing, unlike 2-D); test_0854 asserts the handoff contract shape only — the wire-up to split_along_label_3d runs on the fault branch, per the ledger.
Recommend merge (squash — single-purpose branch, no shared ancestry).
Underworld development team with AI support from Claude Code
The 3-D single-cut surface on an existing mesh — the capability the fault streams were waiting on. A triangulated sheet's points become mesh vertices and every sheet triangle a labelled interior face, with the rim free inside the mesh: the case a cut cannot represent and a two-sub-volume construction cannot fake.
Mechanism
Carve (clearance + crossed tets, every victim's whole star), fill, sew on a compacted vertex set, carry every label topologically (vertex numbering through the rebuild is defined, so faces and edges transfer by vertex tuple — coordinates are never consulted). The fill is delegated to gmsh — a cavity shell has no total order to walk, and constrained tetrahedralisation with Steiner insertion is what a mesh generator is for — and gated, not trusted: both constraint surfaces bit-identical, every sheet triangle an interior face, conformity / Euler / degeneracy / wall-label preservation checked per call.
Measured basis:
sheet_cavity_spike.py(study dir), 6/6 across tilt, resolution, clearance and sub-h sheet spacing — gmsh inserting its own Steiner points without touching either constraint, which retires the plan's constrained-tetrahedralisation STOP-gate.Guards, ported from the 2-D lessons
Cells owning an interior labelled face are held out of the cavity; a sheet needing them is refused; every earlier surface's interior-face count is re-read off the result mesh (a partial corruption satisfies any identity summed over per-placement bookkeeping — learned the hard way in 2-D).
Scope
Serial by design: the parallel route is gather-first (redistribute so the sheet's star is rank-interior — the contact stream's measured policy — then this operation is the rank-local step, and no placed point is ever shared).
reconnect.pyuntouched; the chart-surgery generalisation belongs to the parallel stage. Test number 0854 claimed in the coordination ledger.Handoff
The deliverable contract for
split_along_label_3d(fault-split-node): a labelled facet patch — every sheet triangle a face under one(name, value), interior with two cells, an open manifold with single rim edges — asserted astest_0854(5 tests). Full surgery regression: 77 tests green (0844 x2, 0853, 0854); style gate clean.Underworld development team with AI support from Claude Code