Repository navigation
Weight Q/U by their variance after the Stokes I division, and blank pixels with no Stokes I model - #97
Conversation
Dividing Q/U by a Stokes I model divides their noise too, but the transform kept 1/sigma^2 weights. Channels where the model is faint got magnified noise at full weight, which blows up PI wherever a model falls towards zero. `stokes_i_weighting` makes the noise-based weights follow the division: - "global" (default): one field-wide power law nu**alpha for every pixel, so the map keeps one RMSF and one reference frequency. `stokes_i_weight_alpha` pins alpha, or "auto" fits it to the field's mean Stokes I. - "per_pixel": each pixel's own model. Most SNR, but the RMSF follows the spectral index. - None: the old weights, unchanged. With the weighting on, pixels without a kept fit divide by the same power law instead of a flat model, so PI is continuous across the Stokes I SNR cut. lam_sq_0 "auto" follows the template weights in both modes. The 1D tool takes the same options. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
Match each option error by its message, and split the SNR-cut assertion so a failure says which side of the cut is empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #97 +/- ##
==========================================
+ Coverage 92.38% 92.51% +0.12%
==========================================
Files 14 14
Lines 3114 3207 +93
==========================================
+ Hits 2877 2967 +90
- Misses 237 240 +3 ☔ View full report in Codecov by Harness. |
Each part of the new notebook section now draws what it tests: the channel weights and the peak PI each mode recovers, the RMSF each mode gives two spectral indices, and fractional polarisation across the SNR cut. The section ends with the numbers in one table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
Replace the line plots with maps of one mock source on a RACS low, mid and high band whose spectrum steepens and curves down across it: PI SNR and RMSF width for each option, and PI across the Stokes I SNR cut. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
Label the mock input on its own figure with two example spectra, then show recovered PI SNR and RMSF width as separate figures. Use two compact sources on empty sky, as in the earlier mock: one nearly flat, one steep and curving down at the top of the band. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
WeightType is now built from a NoiseWeightType literal, so the list lives in one place and callers test membership with get_args. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
Below the Stokes I SNR cut, or where a fitted model is rejected, the 3D tool used to divide by a flat (or template) model, so those pixels kept a band-averaged PI next to fitted pixels reporting PI at the reference frequency. The maps stepped at the edge of the fitted region. Such pixels now get a NaN model, so their FDF and every map made from it are NaN, as in RM-Tools. Run without stokes_i for PI at every pixel. This removes fallback_alpha and fallback_model. Blank FDFs are now NaN in every moment map, not mom0 = 0, the 3D RM-CLEAN model is NaN where the clean FDF is, and the theoretical noise of a pixel with no weight is NaN without a divide warning. The 1D tool is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
A blank pixel has no CLEAN components, and zero says that. The dirty and clean maps stay NaN there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7
|
It is not this PR's: Generated by Claude Code |
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
Summary
Dividing Q/U by a Stokes I model divides their noise too. In channel j the corrected data have noise sigma_j * I(nu_0) / I_j, but the transform kept 1/sigma_j^2 weights. Channels where the model is faint got magnified noise at full weight, so PI blew up wherever a model fell towards zero. Only the reported noise (
fractional_theoretical_noise) knew about the division.stokes_i_weightingmakes the noise-based weights (variance,natural,uniform_lsq,briggs) follow the division:"global"(default): w_j = T_j^2 / sigma_j^2 with T = nu^alpha, the same for every pixel. The map keeps one RMSF and one effective frequency.stokes_i_weight_alphapins alpha, or"auto"fits it to the field's mean Stokes I (one extra pass over the Stokes I cube)."per_pixel": w_j = I_j^2 / sigma_j^2 with each pixel's own model. Inverse-variance weighting of the corrected data, so the most SNR, but the RMSF and effective frequency follow the spectral index. Forces the per-pixel RMSF, and cannot be combined withlam_sq_0_m2="per_pixel".None: the old weights.Pixels with no usable Stokes I model are blanked (3D). Below
stokes_i_snr_cut, with no Stokes I error, with Stokes I blank, or with a rejected model, the pixel used to divide by a flat model, i.e. no correction. It then reported a band-averaged PI next to fitted pixels reporting PI at the reference frequency, and the maps stepped at the edge of the fitted region. On the SB56289 cutout, with the cut raised to put that edge on the emission, the step was about 10%. Those pixels now get a NaN model, so their FDF and every map made from it are NaN, as in RM-Tools (rmtools_fitIcubeleaves unfitted pixels NaN). Run withoutstokes_ifor PI at every pixel; flint writes that set asfdf_no_i.fallback_modelandStokesIFitOptions.fallback_alphaare gone.Other changes:
calc_faraday_momentsreturns NaN for an all-NaN FDF rather thanmom0 = 0(a spectrum the threshold empties still gives 0). The 3D RM-CLEAN model stays at zero there, since a blank pixel has no components.compute_theoretical_noisegives NaN for a pixel with no weight, without a divide warning. These also apply to NaN mosaic edges.lam_sq_0_m2="auto"is the weighted mean of lambda^2 under the template weights in both modes, so REFFREQ moves.noise_weightstakes the noise weights separately from the transform weights.RMSynth3DResultsgainsstokes_i_weightingandstokes_i_weight_alpha.run_rmsynthtakes the same weighting options. A single spectrum has no map to step across, so 1D keeps its flat fallback for an unusable model.Why
On SB56289 the very high PI came from order-2 Stokes I fits falling to a few percent of the reference flux at 1.8 GHz. On a 49x49 real-data cutout at
fit_order=2, pixels with model gain above 10 went from 2.99x the order-1 PI to 1.52x (global) and 1.10x (per_pixel). Max PI fell from 4.48 to 2.38 and 2.25 mJy, and median SNR rose from 6.5 to 8.0.Tests
tests/test_stokes_i_weighting.py, new, covers:tests/test_tools_3d_stokes_i.pyandtests/test_tools_3d_dask.pynow check blanking instead: below the cut, no Stokes I error, blank or negative Stokes I, a runaway fit, a rejected model. Each checks that neighbouring pixels keep their fit.uv run pyteston the touched modules: 550 passed. The Stokes I notebook passes.uv run prek run --hook-stage manual --all-filespasses.Docs
docs/examples/rmsynth_3d_stokes_i.ipynb:Please review the prose in those sections.
🤖 Generated with Claude Code
https://claude.ai/code/session_01F6ajgfHAviQKM4UXZ3gNi7