Skip to content

sources.py: simplify_fault always absorbs a short interior plane into its right neighbour #114

Closed

Description

@lispandfound

Location: source_modelling/sources.py:1812-1830 (simplify_fault, interior-plane branch)

What is wrong

The comment says the code picks the neighbour to absorb into "by total deviation". The two numbers it compares are:

geo.point_to_segment_distance(plane.bounds[0],  left.bounds[0],  right.bounds[0])   # top-left of short plane vs top-left(left)->top-left(right)
geo.point_to_segment_distance(plane.bounds[-1], left.bounds[-1], right.bounds[-1])  # bottom-left of short plane vs bottom-left(left)->bottom-left(right)

Both measure the same thing: how far the short plane's left edge is from the line through the left edges of its neighbours. One is taken at the top of the fault and the other at the bottom. A Fault has constant dip and dip direction, so the bottom edge is the top edge translated, and the two distances are equal up to floating-point noise. The < test is therefore effectively always False, and the short plane is always merged into its right neighbour. The deviation of that choice is never measured, so the result ignores the fault's shape.

Reproducer (.ai/simp.py)

The fault trace runs north: P0=(0,0) → P1=(10000,0) → P2=(10500,0) → P3=(20500,1000), in NZTM offsets (m). The middle plane is 0.5 km long. P1 lies on the line P0–P2, so merging the short plane into the left plane changes nothing. P2 is a corner, so merging into the right plane moves the trace.

fault = sources.Fault([sources.Plane.from_nztm_trace(pts[i:i+2].copy(), 0, 10, dip, dip_dir_nztm=ddir) for i in range(3)])
s = sources.simplify_fault(fault, 1.0)

Output (the same for dip 90 and dip 60):

lengths before: [10.          0.5        10.04987562]
lengths after: [10.         10.54751155]
trace after (relative NZTM):
 [[    0.     0.]
 [10000.     0.]
 [10000.     0.]
 [20500.  1000.]]
kept corner P2 (correct: absorb into left plane)? False

The corner at P2 is removed, but the removable collinear vertex P1 is kept.

Suggested fix

Compare the deviation each merge would actually cause, i.e. the distance from the vertex that disappears to the new edge:

# absorb into left: vertex plane.bounds[0] disappears, new top edge is left.bounds[0] -> plane.bounds[1]
dev_left = geo.point_to_segment_distance(plane.bounds[0], left.bounds[0], plane.bounds[1])
# absorb into right: vertex plane.bounds[1] disappears, new top edge is plane.bounds[0] -> right.bounds[1]
dev_right = geo.point_to_segment_distance(plane.bounds[1], plane.bounds[0], right.bounds[1])
if dev_left < dev_right: ...absorb left... else: ...absorb right...

Add a deterministic test using the kinked trace above. test_simplify_fault only checks the resulting lengths and plane counts, so it cannot catch a wrong merge direction.

Filed by the weekly issue sweep. Kind: bug, severity: medium.

Activity

  1. added
    ai-okVetted: the AI issue solver may attempt a fix
    ai-suggestedFiled by the scheduled Claude jobs
    on Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-okVetted: the AI issue solver may attempt a fixai-suggestedFiled by the scheduled Claude jobs

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions