What happens
place_sheet(..., setback=d) clips the sheet against the boundary offset inward and returns the would-be intersection as info["surface_trace"] — the locator a damage region keys to. It builds that trace as a single open chain, and refuses anything else:
NotImplementedError: the sheet's intersection with the boundary is not one
open chain; multiple or closed traces are not built.
That is fine for a sheet crossing one wall, which is the case the feature was built and tested for. It rules out a common real geometry: a fault that runs out of the top and both along-strike ends of a box intersects the inward-offset boundary in three disjoint chains.
Where it bit
Every major SCEC CFM fault in a Southern California box (lmoresi/socal-fault-box). Measured with the San Andreas zone, a box of 690 x 520 x 30 km, cells 3 km: refused at every setback tried (1, 1.5, 2 and 3 background cells) with the message above, in ~23 s each — the sheet spans the full length of the domain and reaches above its top, so it leaves through three walls.
The workaround, and why it is not obviously the right fix
Clip the sheet to the shrunk box yourself and place a strictly interior sheet — the placement's ordinary case, no setback needed. That works (cfm_sheets.clip_to_inner_box in the repo above) and it also gives per-axis control, which turned out to matter: vertically the margin is physics (it removes seismogenic depth from a shallow box) while laterally it is only a meshing constraint. But it hands the caller two things place_sheet already knows how to do — the clip, and the surface trace the damage region needs.
Possible resolutions
- Build the trace as a list of chains rather than one, and return them all in
info["surface_trace"]. The refusal is in the trace construction, not in the carve, so the placement itself may already be indifferent.
- Accept a per-axis setback (a scalar or a
(dim,) array), which is independently useful — see above.
- Document the single-wall restriction on the
setback parameter and point at the clip-it-yourself route, if (1) is more than it is worth.
Either way the current error is a good one: it names the real condition and stops rather than producing a quietly wrong trace.
Underworld development team with AI support from Claude Code
What happens
place_sheet(..., setback=d)clips the sheet against the boundary offset inward and returns the would-be intersection asinfo["surface_trace"]— the locator a damage region keys to. It builds that trace as a single open chain, and refuses anything else:That is fine for a sheet crossing one wall, which is the case the feature was built and tested for. It rules out a common real geometry: a fault that runs out of the top and both along-strike ends of a box intersects the inward-offset boundary in three disjoint chains.
Where it bit
Every major SCEC CFM fault in a Southern California box (
lmoresi/socal-fault-box). Measured with the San Andreas zone, a box of 690 x 520 x 30 km, cells 3 km: refused at every setback tried (1, 1.5, 2 and 3 background cells) with the message above, in ~23 s each — the sheet spans the full length of the domain and reaches above its top, so it leaves through three walls.The workaround, and why it is not obviously the right fix
Clip the sheet to the shrunk box yourself and place a strictly interior sheet — the placement's ordinary case, no setback needed. That works (
cfm_sheets.clip_to_inner_boxin the repo above) and it also gives per-axis control, which turned out to matter: vertically the margin is physics (it removes seismogenic depth from a shallow box) while laterally it is only a meshing constraint. But it hands the caller two thingsplace_sheetalready knows how to do — the clip, and the surface trace the damage region needs.Possible resolutions
info["surface_trace"]. The refusal is in the trace construction, not in the carve, so the placement itself may already be indifferent.(dim,)array), which is independently useful — see above.setbackparameter and point at the clip-it-yourself route, if (1) is more than it is worth.Either way the current error is a good one: it names the real condition and stops rather than producing a quietly wrong trace.
Underworld development team with AI support from Claude Code