StructureFunction2DStack.fit_spiral / calculate_modal_power has no per-annulus fault tolerance

Open
#39 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
python
Domain
data

Research direction

Start in structurefunction.py at StructureFunction2D.fit_spiral around lines 2066-2101, then inspect StructureFunction2DStack.fit_spiral and calculate_modal_power around lines 2852-2882. Use test_structurefunction.py as the test entry point and add a case with insufficient azimuthal coverage. Done means an unfit ring contributes a NaN row without aborting the stack, while fully covered results remain unchanged.

Written by the indexing model from the issue text.

Description

Problem

StructureFunction2D.fit_spiral (structurefunction.py:2066) raises ValueError
when a ring has fewer than len(modes) + 1 finite azimuthal S_2 points
(line ~2101: "Not enough finite points to fit {} modes."). StructureFunction2DStack.fit_spiral
(structurefunction.py:2852) loops over self.results calling this per-ring with no
try/except, so one masked/incomplete annulus aborts the whole stack fit rather
than just that ring. calculate_modal_power (structurefunction.py:2882) inherits
the same gap, since it calls self.fit_spiral directly.

This is exactly the kind of thing real interferometric data hits: masked pixels,
partial azimuthal coverage at the inner/outer edge of the analysis annulus, etc.
Clean, full-coverage synthetic fields (the only case in test_structurefunction.py
today) never trigger it, so it went unnoticed.

Context: calculate_modal_power had no in-repo caller until now (see
a00a695's commit message on fix-modal-power-unpack, which fixed an unrelated
tuple-unpacking bug in the same method). It now has a real one -- Paper 2's
"Modal Power" analysis (StructureFunction/paper2/notebooks/run_s2_modal_power.py)
-- which is what surfaced this.

Suggested fix

Catch the per-ring failure in StructureFunction2DStack.fit_spiral (and by
extension calculate_modal_power) and fill a NaN row instead of propagating,
matching the censoring-free convention already used elsewhere in this project
(e.g. noise_mc._accumulate_null, spiral_null._SpiralNull.build_null in
StructureFunction/paper2/notebooks): a draw/ring that can't be fit contributes
NaN, not a crash.

Reference implementation

run_s2_modal_power.robust_modal_power in the StructureFunction repo
reproduces calculate_modal_power's reduction with this fix (per-ring
try/except around r.fit_spiral), and has been checked bit-identical to
calculate_modal_power on a fully-covered synthetic stack
(max abs diff frac_of_data == 0.0). Worth pulling that fault-tolerance
into eddy directly rather than leaving it as a local workaround, along with
a test case that exercises a ring with insufficient azimuthal coverage
(currently untested).

Dominant language
Python
Stars
23
Forks
10
Avg merge
28m
Merged PRs (30d)
1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from PlanetFormationLab/eddy

All issues in PlanetFormationLab/eddy

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.