Skip to content

Fit the RMSF over a sized window cut at the main lobe - #98

Merged
AlecThomson merged 3 commits into
mainfrom
claude/rmsf-fwhm-fitting-cost-jvcplj
Sep 29, 2026
Merged

AlecThomson merged 3 commits into
mainfrom
claude/rmsf-fwhm-fitting-cost-jvcplj

Conversation

@AlecThomson

Copy link
Copy Markdown
Owner

fit_rmsf fitted its Gaussian over only about sigma/2 either side of the peak, with the window sized from the analytic guess. That measures how sharply the top of the lobe curves, not its width at half maximum. On a lobe that is not Gaussian it read 2% narrow (flat in frequency) to 7% wide (boxcar), and the answer also moved with n_samples and with the guess.

Change

  • fit_rmsf takes fitting_size (default 1.25): the window is that many analytic FWHMs wide, like WSClean's -beam-fitting-size, and stops one sample short of the lobe's first minimum on each side. However wide it is set, no sidelobe gets in.
  • rmsf_fitting_size is exposed on FDFOptions, get_rmsf_nufft and run_rmsynth.
  • rmsynth_3d and rmsynth_3d_from_fits gain do_fit_rmsf and rmsf_fitting_size. With fitting on, fwhm_rmsf_radm2 comes from one fit to the shared rmsf_arr (about 2 ms per cube) instead of 3.8 / (lambda^2 range). Off by default, so 3D output is unchanged unless asked.
  • Multiscale's per-scale restore widths also go through fit_rmsf, so they pick up the new window. test_multiscale.py passes unchanged.

Accuracy against the exact half-max width

Error at n_samples 3 / 5 / 10 / 20 / 100:

RMSF before after
Gaussian 0 / 0 / 0 / 0 / 0% 0 / 0 / 0 / 0 / 0%
boxcar in lambda^2 +4.5 / +3.4 / +5.4 / +6.2 / +6.6% -4.8 / -1.4 / -0.4 / +0.2 / +0.1%
flat in frequency, 800-1800 MHz -2.3 / -2.2 / -2.2 / -2.1 / -2.1% +1.9 / -0.6 / -1.0 / -0.6 / -1.1%
SB56289-like, 44 channels blanked +2.1 / +1.5 / +2.6 / +3.1 / +3.4% +2.1 / +0.6 / +0.6 / +0.7 / +0.6%
same, bottom 15 channels blanked +2.2 / +1.9 / +2.5 / +2.8 / +2.9% +0.3 / +0.7 / +0.9 / +0.8 / +1.0%

Tests

  • New: exact on a Gaussian RMSF, within 1.5% of the boxcar half max at n_samples 10 and 100, a huge window gives the same answer as one that just reaches the first minimum, rmsf_fitting_size <= 0 raises, and the 3D fit matches fit_rmsf on rmsf_arr.
  • test_options_defaults.py covers the new defaults.
  • Locally, test_options_defaults, test_synthesis_utils, test_nufft, test_multiscale, test_rmsynth and test_tools_3d_dask pass (410 tests), and so do the rmclean_1d and rmsynth_1d notebooks. test_rmclean_graph_build_stays_linear_in_chunk_count failed once under four xdist workers on four cores, then passed serially with and without this change. It only times RM-CLEAN graph building, which this change doesn't touch.
  • prek run --hook-stage manual --all-files is clean.

flint-crew/flint will expose do_fit_rmsf and rmsf_fitting_size once this is released.

🤖 Generated with Claude Code

https://claude.ai/code/session_01C5pn26DJht35yoKWbMJ1sW


Generated by Claude Code

`fit_rmsf` fitted its Gaussian over only about sigma/2 either side of the
peak, sized from the analytic guess. That measures how sharply the top of the
lobe curves, not its width at half maximum, so on a lobe that is not Gaussian
it read 2% narrow (flat in frequency) to 7% wide (boxcar), and the answer
moved with `n_samples` and with the guess.

The window is now `rmsf_fitting_size` analytic FWHMs wide (default 1.25, as
WSClean's `-beam-fitting-size`) and stops one sample short of the lobe's
first minimum on each side, so no sidelobe can get in however wide it is set.
Against the exact half-max width that lands within 1.4% on a Gaussian,
boxcar, flat-in-frequency and gapped ASKAP-like band for `n_samples` >= 5.

`rmsynth_3d` and `rmsynth_3d_from_fits` gain `do_fit_rmsf` and
`rmsf_fitting_size`: with fitting on, `fwhm_rmsf_radm2` comes from one fit to
the shared `rmsf_arr` instead of 3.8 / (lambda^2 range).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5pn26DJht35yoKWbMJ1sW
Both sides added keywords to the same `FDFOptions(...)` calls in the 1D and 3D
tools (the RMSF fit window here, Stokes I weighting in #97); both are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5pn26DJht35yoKWbMJ1sW
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.58%. Comparing base (1aa4680) to head (b43fa8e).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #98      +/-   ##
==========================================
+ Coverage   92.51%   92.58%   +0.06%     
==========================================
  Files          14       14              
  Lines        3207     3208       +1     
==========================================
+ Hits         2967     2970       +3     
+ Misses        240      238       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

`nanargmax` raises its own "All-NaN slice" on a blank RMSF, before
`curve_fit` rejects it. The multiscale screen test pins that a blank pixel
fails in the fit, as it did before this branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5pn26DJht35yoKWbMJ1sW
@AlecThomson
AlecThomson merged commit 8feef6c into main Sep 29, 2026
8 checks passed
@AlecThomson
AlecThomson deleted the claude/rmsf-fwhm-fitting-cost-jvcplj branch September 29, 2026 02:21
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.

2 participants