Summary
cut_along_lines(dm, [line_a, line_b], ...) returns a mesh in which the surfaces
are not complete chains of element edges — there are consecutive pairs of
on-surface vertices with no edge between them. It does not refuse; the returned
mesh looks fine and the labelled facet count looks plausible.
Cutting the same two lines sequentially, via two add_conforming_surface
calls, is exact.
Measurement
Box fault, two parallel flanks separated by W = h (so an edge can reach both),
graded mesh, snap_frac=0.30. Counting collinear vertices and the edges between
them by geometry (a complete open chain on V collinear vertices has V-1
edges):
| path |
flank |
vertices |
edges |
gaps |
both lines, ONE cut_along_lines call |
A |
306 |
294 |
11 |
|
B |
310 |
295 |
14 |
SEQUENTIAL add_conforming_surface x2 |
A |
318 |
317 |
0 |
|
B |
319 |
318 |
0 |
The gap count is not small, and the affected mesh passes the other checks:
conforming (no facet with more than two cells), Euler characteristic 1, nothing
inverted, and no vertex lying in another edge's interior. So the usual validity
tests do not catch it.
Reproducer: ~/+Simulations/mesh_reconnection_study/chain_gap_check.py (builds
both paths on identical geometry and prints the table above).
Why it matters beyond this case
_fault_collect_polylines(surface) returns several polylines for a branched
fault, and add_conforming_surface passes them to cut_along_lines in a single
call. So a fault NETWORK goes through exactly this path. The parallel network
tests assert junction geometry and partition-independence but not chain
completeness, so they would not see this.
Suspected cause (not confirmed)
_crossing_parameters iterates over the segments of ALL lines together and keeps
one t per edge (t = np.where(hit, tk, t)), so a later line overwrites an
earlier one's crossing on the same edge. multiply_crossed is meant to catch
that and did NOT fire here, so the mechanism needs checking rather than assuming.
Suggested fix direction
Either process lines one at a time internally (matching what sequential
add_conforming_surface does and is known to be exact), or make the multi-line
path detect and refuse the configuration rather than returning a broken chain.
Either way the chain-completeness property is worth asserting in the test suite
directly — n_cut_edges == n_split + n_on_surface - 1 is stated in the docstring
but is only checked for a single open chain, and a closed loop of several lines
legitimately violates it, so it cannot be used as the guard on its own.
Summary
cut_along_lines(dm, [line_a, line_b], ...)returns a mesh in which the surfacesare not complete chains of element edges — there are consecutive pairs of
on-surface vertices with no edge between them. It does not refuse; the returned
mesh looks fine and the labelled facet count looks plausible.
Cutting the same two lines sequentially, via two
add_conforming_surfacecalls, is exact.
Measurement
Box fault, two parallel flanks separated by
W = h(so an edge can reach both),graded mesh,
snap_frac=0.30. Counting collinear vertices and the edges betweenthem by geometry (a complete open chain on
Vcollinear vertices hasV-1edges):
cut_along_linescalladd_conforming_surfacex2The gap count is not small, and the affected mesh passes the other checks:
conforming (no facet with more than two cells), Euler characteristic 1, nothing
inverted, and no vertex lying in another edge's interior. So the usual validity
tests do not catch it.
Reproducer:
~/+Simulations/mesh_reconnection_study/chain_gap_check.py(buildsboth paths on identical geometry and prints the table above).
Why it matters beyond this case
_fault_collect_polylines(surface)returns several polylines for a branchedfault, and
add_conforming_surfacepasses them tocut_along_linesin a singlecall. So a fault NETWORK goes through exactly this path. The parallel network
tests assert junction geometry and partition-independence but not chain
completeness, so they would not see this.
Suspected cause (not confirmed)
_crossing_parametersiterates over the segments of ALL lines together and keepsone
tper edge (t = np.where(hit, tk, t)), so a later line overwrites anearlier one's crossing on the same edge.
multiply_crossedis meant to catchthat and did NOT fire here, so the mechanism needs checking rather than assuming.
Suggested fix direction
Either process lines one at a time internally (matching what sequential
add_conforming_surfacedoes and is known to be exact), or make the multi-linepath detect and refuse the configuration rather than returning a broken chain.
Either way the chain-completeness property is worth asserting in the test suite
directly —
n_cut_edges == n_split + n_on_surface - 1is stated in the docstringbut is only checked for a single open chain, and a closed loop of several lines
legitimately violates it, so it cannot be used as the guard on its own.