Skip to content

pull_vertex_onto can take the vertex from a neighbouring cell, so the tip cell has no on-line corner #542

Description

@lmoresi

What happens

cut_along_lines refuses a fault whose tip is correctly placed:

ValueError: 1 triangle(s) are entered but not left, which means a line ends
inside the mesh. A line must cross from boundary to boundary; a terminating
tip needs a vertex placed at the tip and is not supported.

even though add_fault has already called pull_vertex_onto on every control point of the trace, and every control point does have a vertex exactly on it.

Why

pull_vertex_onto moves the globally nearest vertex onto each target. That vertex need not be a corner of the cell that contains the target. When it is not, the containing cell is entered by the line and terminates there with no on-line corner, which is exactly the state cut_along_lines refuses.

Measured on the listric pair at DX=0.30 (~/+Simulations/listric_extension/selfselect.py), fault F2, trace start (0.65, 0.88) inside an embedded ribbon zone of width 0.02:

vertex position distance to the start point corner of the containing cell?
2213 (0.65361, 0.87071) 9.963e-03 — chosen no
1953 (0.64068, 0.87638) 1.000e-02 yes
1889 (0.65932, 0.88362) 1.000e-02 yes

The margin is 0.4%. Both corners of the containing cell sit at exactly width/2 from the trace, because they are the ribbon's own edge vertices; the winning vertex is an interior one that happens to fall a hair closer. All 22 control points ended up with a vertex exactly on them, and the cut still refused.

Why it looks intermittent

  • On a plain unstructured box the globally nearest vertex is usually a corner of the containing cell. A tip swept across a cell at 7 positions succeeded every time.
  • Inside an embedded zone the vertices sit in rows at exactly ±width/2 from the trace, which manufactures near-ties. The same sweep fails at DX=0.30 and passes at DX=0.50 and DX=0.70 — luck, not geometry.

This is what limited the hybrid self-selection sweep to a subset of separations.

The fix we think is right

Choose the nearest vertex of the cell containing the target, falling back to the globally nearest when the target lies outside the mesh. This keeps the existing principle — move the mesh, not the tip, so the tip stays exactly where it was asked for — and makes the choice depend on the containment relation that cut_along_lines actually tests, rather than on a distance comparison that can be decided by 0.4%.

Note that snapping the tip to the nearest vertex, the other obvious fix, is both rejected by pull_vertex_onto's own docstring (measured: tip error 0.0000 against 0.0306, worst angle 8.79 deg against 5.29) and would not address this: the tip does not lack a vertex, the vertex was taken from the wrong cell.

Reproducer

~/+Simulations/listric_extension/selfselect.py 0.30 — fails; 0.50 and 0.70 pass.

Underworld development team with AI support from Claude Code

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