Skip to content

Fix strike direction for nstk == 1 SRF segments (degrees used as radians) - #101

Merged
lispandfound merged 1 commit into
mainfrom
fix/84-srf-strike-degrees-as-radians
Sep 9, 2026
Merged

Fix strike direction for nstk == 1 SRF segments (degrees used as radians)#101
lispandfound merged 1 commit into
mainfrom
fix/84-srf-strike-degrees-as-radians

Conversation

@lispandfound

Copy link
Copy Markdown
Contributor

Fixes #84

Problem

SrfFile.planes builds the along-strike offset for single-strike-point segments from a bearing returned by qcore.coordinates.great_circle_bearing_to_nztm_bearing, which is in degrees, but passed it straight to np.cos/np.sin:

# source_modelling/srf.py:615 (before)
* np.array([np.cos(strike_nztm), np.sin(strike_nztm), 0])

The bearing was therefore interpreted as radians and the resulting plane pointed in an essentially arbitrary direction.

Measured impact

Recovered plane.strike from a synthetic nstk == 1 segment, before this change:

header stk recovered error
0 87.16 87.2
20 50.24 30.2
45 44.70 0.06
90 97.33 7.7
200 103.60 96.1
330 343.26 13.7

After the change every bearing tested recovers the header strike to within 0.05 degrees.

Why CI never caught this

test_planes_nstk_1_ndip_gt_1 exercises exactly this branch, but with stk=45 — which turns out to be an accidental near-fixed-point of the degrees/radians confusion (44.758 rad reduces to 44.44 deg mod 2*pi, 0.3 degrees from the true value). The assertion plane.strike == approx(45, abs=1) passed on a coincidence. That blind spot is why the new test is parametrized over eight bearings rather than tightening the existing one.

Changes

  • source_modelling/srf.py — wrap strike_nztm in np.radians, matching the convention already used at fsp.py:124-125.
  • tests/test_srf.py — add test_planes_nstk_1_strike_recovered, parametrized over 0/20/45/90/135/200/270/330 degrees.

The new test needed a fixture whose point column is genuinely consistent with the header strike (the down-dip column generated along stk + 90). The pre-existing fixture hardcodes a southeast-dipping point column, so for strikes like 200 the header and the point geometry contradict each other and Plane reorders the corners, flipping the reported strike by 180 degrees — unrelated to this bug, but it makes the old fixture unusable for a strike sweep.

Verification

  • New test fails on all 8 parameters without the fix, passes with it.
  • pytest tests/test_srf.py — 24 passed.
  • ruff check, ruff format --check, numpydoc lint clean.

Only line 615 was affected; it is the sole trig call in srf.py, and the ndip == 1 branch delegates to Plane.from_centroid_strike_dip, which converts internally.

🤖 Generated with Claude Code

The along-strike offset in SrfFile.planes was built from
np.cos(strike_nztm)/np.sin(strike_nztm), but
great_circle_bearing_to_nztm_bearing returns degrees, so the bearing was
interpreted as radians and the plane pointed in an unrelated direction.

Measured error in the recovered strike before this change: 87 degrees at
stk=0, 30 degrees at stk=20, 96 degrees at stk=200. The existing
test_planes_nstk_1_ndip_gt_1 did not catch it because it uses stk=45,
which is an accidental near-fixed-point of the confusion (0.06 degrees of
error there).

Adds a parametrized regression test over eight bearings, using a fixture
whose point column is genuinely consistent with the header strike.

Fixes #84

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@lispandfound
lispandfound merged commit a6379b1 into main Sep 9, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

srf.py: strike direction for nstk == 1 segments uses degrees as radians

1 participant