Skip to content

cut_along_lines with several lines in one call returns incomplete chains, silently #494

Description

@lmoresi

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions