Skip to content

Add cr_method dial (per-frame deepCR route) and PSF star-pass decoupling (#61, #62) - #66

Merged
Jammy2211 merged 1 commit into
mainfrom
claude/wake-up-q6ajyq
Aug 6, 2026
Merged

Add cr_method dial (per-frame deepCR route) and PSF star-pass decoupling (#61, #62)#66
Jammy2211 merged 1 commit into
mainfrom
claude/wake-up-q6ajyq

Conversation

@Jammy2211

Copy link
Copy Markdown
Contributor

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 no resetbits key 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, idempotent dq_with_cr_flags (bit cleared before rewrite).
  • The driz_cr (median/blot CR rejection) systematically erodes flux at steep-gradient cores — consider LACosmic per-frame masking as the default #61 resetbits trap is pinned by a unit test: the deepcr route must emit resetbits=0 — AstroDrizzle's default (4096) clears exactly the bit the masks live in, silently producing an unmasked drizzle.
  • Naming: the reporter used LACosmic; this repo's established per-frame CR machinery is deepCR (package/cosmic_rays.py, already used by frame products), so that is reused rather than adding an astroscrappy dependency — and the dial is honestly named "deepcr". Same method class (per-frame, no median reference). An actual LACosmic backend would be a follow-up.
  • Fail-fast in reduce_target: deepcr on 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)

  • _psf now 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) and resetbits=0 so the science pass's flags survive in the inputs.
  • Provenance can't silently regress: the reduction.json psf block always records star_source_pass (+ reason or the star-pass kwargs), and the drizzle block records cr_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.md Stage 3 documents the dial as a lensing-motivated deviation-in-waiting: default stays STScI driz_cr; the flip decision is human-gated on SLACS reference-bar validation comparing deepCR against tuned driz_cr (driz_cr_scale/snr 1.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

🤖 Generated with Claude Code

https://claude.ai/code/session_01UBpwwfzbewbMLhE137h231


Generated by Claude Code

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
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