Add cr_method dial (per-frame deepCR route) and PSF star-pass decoupling (#61, #62) - #66
Merged
Merged
Conversation
Community-reported (#61, #62, @samlange04): driz_cr's blotted-median reference reads systematically low on steep gradients, flagging genuine deflector-core flux as cosmic rays (~37% core flux loss measured on SLACS ACS/WFC F814W), and PSF star finding on that same CR-rejected mosaic holes star cores before DAOStarFinder sees them (344 -> 599 usable stars when rebuilt from a no-CR pass). - TargetSpec.cr_method ("driz_cr" default | "deepcr"): the per-frame route writes deepCR masks (the machinery frame_products already uses -- #61's reporter used LACosmic; deepCR is the same per-frame, no-median-reference class without a new dependency) into each exposure's DQ as the AstroDrizzle CR bit (4096), then plain weighted-mean drizzles with median=blot=driz_cr=False and resetbits=0. resetbits=0 is #61's trap -- AstroDrizzle's default resetbits=4096 clears exactly the bit the masks were written into -- and is pinned by unit test, along with a regression test that the default driz_cr route and the single-exposure branch are unchanged. The default deliberately stays "driz_cr": the flip is human-gated on SLACS validation with a tuned-driz_cr comparison arm (driz_cr_scale/snr 1.5/1.2 per STScI guidance), so #61/#62 stay open. - TargetSpec.psf_star_pass ("auto" default | "science" | "no_cr"): Tier-1/1b star finding + stamp extraction are decoupled from the shipped science mosaic. "auto" never adds a drizzle (the science mosaic is recorded, with the reason it is or is not the least-CR-rejected pass); "no_cr" is the explicit opt-in second AstroDrizzle pass that treats CR DQ flags as good (final_bits | 4096) without clearing them (resetbits=0), preserving the science flags for frame products. The reduction.json psf block records star_source_pass in every case so the coupling cannot silently regress. - Unsupported combinations (non-astrodrizzle backends, instruments without a deepCR model, no_cr + psf_from_frames) fail fast in reduce_target before any download; both dials are documented in docs/design/hst_acs_pipeline.md stages 3 and 5 as deviations-with- justification, default unchanged. Refs #61, #62 (both stay open pending the SLACS validation arms). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBpwwfzbewbMLhE137h231
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the maintainer-triaged scope for community issues #61 and #62 (@samlange04): a user-facing cosmic-ray-method dial and the decoupling of PSF-star measurement from the shipped science CR pass. The science default is deliberately unchanged — flipping it remains gated on the SLACS validation with a tuned-driz_cr comparison arm, so this PR does not close either issue.
1.
TargetSpec.cr_method: "driz_cr" (default) | "deepcr"(#61)"driz_cr"— today's STScI route, byte-identical kwargs to before (pinned by a regression test that also asserts noresetbitskey appears)."deepcr"— per-frame CR masking then plain weighted-mean drizzle (driz_cr=median=blot=False), eliminating the blotted-median reference that reads systematically low at steep gradients (driz_cr (median/blot CR rejection) systematically erodes flux at steep-gradient cores — consider LACosmic per-frame masking as the default #61's ~37% core-flux erosion). Masks are written into each exposure's DQ as the AstroDrizzle CR bit (4096) via a pure, idempotentdq_with_cr_flags(bit cleared before rewrite).resetbitstrap is pinned by a unit test: the deepcr route must emitresetbits=0— AstroDrizzle's default (4096) clears exactly the bit the masks live in, silently producing an unmasked drizzle.package/cosmic_rays.py, already used by frame products), so that is reused rather than adding anastroscrappydependency — and the dial is honestly named"deepcr". Same method class (per-frame, no median reference). An actual LACosmic backend would be a follow-up.reduce_target:deepcron non-astrodrizzle backends or instruments without a registered deepCR model (e.g.wfc3_ir, whose CRs calwf3 already flags per-frame) is rejected before any download.2. PSF star-pass decoupling:
TargetSpec.psf_star_pass: "auto" (default) | "science" | "no_cr"(#62)_psfnow draws star finding and stamp extraction from_star_pass_image(...)instead of unconditionally from the science mosaic."auto"never adds a drizzle: it uses the least-CR-rejected pass that costs nothing extra (which today is the science mosaic — on driz_cr runs AstroDrizzle keeps no less-rejected intermediate, and the median image is the biased reference itself), and always records why."no_cr"is the explicit opt-in second AstroDrizzle pass on the identical grid/kernel/pixfrac with CR DQ flags treated as good (final_bits | 4096) andresetbits=0so the science pass's flags survive in the inputs.reduction.jsonpsfblock always recordsstar_source_pass(+ reason or the star-pass kwargs), and thedrizzleblock recordscr_method(+ per-frame model/threshold/bit/pixel counts when active) — the Tier-1 empirical ePSF is built from the CR-rejected mosaic — field-star cores get masked before DAOStarFinder ever sees them #62 wiring requirement.3. Design doc
docs/design/hst_acs_pipeline.mdStage 3 documents the dial as a lensing-motivated deviation-in-waiting: default stays STScIdriz_cr; the flip decision is human-gated on SLACS reference-bar validation comparing deepCR against tuned driz_cr (driz_cr_scale/snr1.5/1.2 per published STScI reprocessing, vs the 1.2/0.7 pipeline defaults we currently inherit). Stage 5 documents the star-pass decoupling.Tests
19 new unit tests (numpy/astropy-only, house style): kwargs regressions for both routes and the single-exposure branch, the resetbits trap, DQ bit write/idempotence/shape-mismatch, star-pass decision matrix incl. single-exposure never re-drizzling, and the fail-fast set. Full suite: 265 passed, 15 skipped (baseline 246/15) — verified independently of the implementing agent.
Honest gaps
no_crstar pass are exercised through unit-tested pure functions and monkeypatched plumbing — no drizzlepac/deepCR execution or real-data validation in this environment.psf_star_pass="no_cr"+psf_backend="starred"weights stamps with the science-pass noise map (documented approximation; the passes differ only in CR-rejected pixels).outlier_detectionhas the analogous median-reference failure mode;cr_methodthere is rejected, not addressed — out of scope.apply_per_frame_cr_masksmutates cached FLC DQ arrays in place (idempotently, bit cleared first) — the same behaviour class as driz_cr itself, but reviewer attention welcome.🤖 Generated with Claude Code
https://claude.ai/code/session_01UBpwwfzbewbMLhE137h231
Generated by Claude Code