Fix flux loss, aliasing, and placement errors in image-plane resampling and overlay#958
Fix flux loss, aliasing, and placement errors in image-plane resampling and overlay#958teutoburg wants to merge 2 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #958 +/- ##
==========================================
+ Coverage 75.60% 75.64% +0.04%
==========================================
Files 70 70
Lines 8981 9013 +32
==========================================
+ Hits 6790 6818 +28
- Misses 2191 2195 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Awesome! Thanks @teutoburg, a major step into improving the experience of using ScopeSim. I suggest that we demonstrate the correctness of this with some tests. Maybe even a notebook that demonstrates the now non-existence of these problems. More importantly, we can we use that as a regression test to prevent the problem from returning. I think this is very important, because otherwise someone might optimize your fix away at some point. Even with your great comments. |
I do have notebooks and tests for this in a branch over in ScopeSim-Targets. In fact, the notebooks (or rather plots therein) are how I found out about these bugs in the first place! Agree that these tests should eventually live in ScopeSim itself, but they currently rely on some other code in Targets, so this needs the scopesim-core refactor to be moved. Or we just extract the pure- |
This PR fixes three defects in
scopesim/optics/image_plane_utils.py, all in theImageSourceField→ FOV →ImagePlanepath (point/table sources are unaffected, which is what makes the discrepancies visible in point-vs-extended comparisons).Silent flux loss / aliasing in
rescale_imagehduWhen the input is finer than the instrument (
zoom < 1), resampling usedndi.zoom, which point-samples the output lattice instead of integrating — so structure narrower than the sample spacing is aliased away, and a compact source on an unlucky grid phase vanishes entirely (all flux zeroed, then silently skipped by theif sum_new != 0guard). Broad sources hid this becauseconserve_fluxrenormalises any partial hit exactly. The fix replaces the downsampling direction with an exact area-weighted rebin (cumulative sum differenced at the output bin edges), which conserves flux to machine precision by construction and, unlike a smoothing prefilter, never spreads a compact source beyond its output cell (a prefilter would leak flux into wings that a smaller FOV then clips afterconserve_fluxnormalised the unclipped total — a phase-dependent deficit). It is gated tospline_order > 0:order == 0is nearest-neighbour resampling of categorical data (masks, strehl labels) where integrating would synthesise phantom values, and those keep the stock path. A warning now fires if all flux is lost rather than dropping it silently.Nominal-vs-realised zoom in the WCS update
ndi.zoomreturnsround(N·zoom)pixels, so the realised zoom differs from the nominal wheneverN·zoomisn't integer.CDELTcorrectly keeps the nominal zoom (CDELT' == pixel_scaleexactly), butCRPIXmust use the realised zoom or the reference — and every source — is misplaced by up to half an output pixel.+1px overlay shift (another off-by-one)
Separately,
overlay_imagedidnp.ceil(coords.round(10)), and.round(10)is far too tight for the ~1e-8 px float dust that the deg-space WCS round-trip inextract_fromleaves on integer coordinates; ceil then amplifies it to a full-pixel paste offset (image sources landed +1px in x and y relative to point sources).ceilitself is correct and stays (even-sized fields legitimately hand over half-integer centres); only the dust tolerance is loosened toround(4).Clauded, but would have taken at least a week otherwise.....