Repository navigation
Exposure maps: npoint (pointing squares) and ntaken beside nexp - #966
cailmdaley wants to merge 10 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FD35GJ3MDN7ceDhFwxzMbU
…ally Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FD35GJ3MDN7ceDhFwxzMbU
…olygon CCD corners along a side of the array are collinear to ~1e-6 rad on some exposures (2105593, 2869649), which hpgeom rejects as a degenerate corner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FD35GJ3MDN7ceDhFwxzMbU
|
Claude (Opus) on behalf of Cail. The production backfill (2,550 exposures, b01–b50 on Nibi) turned up a failure on 2 exposures (2105593 and 2869649). On some exposures the CCD corners along one side of the array are collinear to within ~1e-6 rad, and |
Declare the fragment as an exp_maps output and require every campaign exposure fragment in the merge and clean DAGs. A surviving completion manifest no longer hides a missing fragment, and reclamation no longer filters an exposure out of the map fingerprint. When persistence metadata exists but the split store is gone, use staged image/flag MEFs as explicit recovery inputs. The persisted manifest is then historical metadata passed as a parameter, not a producer edge: requesting exp_persist would reopen its vanished exp_psf chain and refit PSFs unnecessarily. Live stores retain the persist dependency. Source-selection parameters remain stable across reclamation. Existing reclaimed fragments need no raw-file dependency; missing raw recovery inputs fail DAG planning. Reject all missing fragments before allocating or writing merged maps, naming every exposure and its raw-MEF backfill command. The old compatibility case was pre-exp_maps reclamation; b01-b50 have been backfilled, so there is no warn-and-omit mode left. Regression tests cover missing processed fragments without replacing old maps, and actual merge dependencies after reclamation with or without tombstones, stale PSF manifests, and surviving exp_maps manifests. SIF allocation tests: workflow 31 passed; exposure-map unit tests 8 passed. Both regression families fail on the parent commit.
Check Snakemake needrun in addition to the dependency graph, so an already-satisfied graph node cannot pass the resume regression. Eight resume combinations pass in the campaign SIF allocation.
|
The review's HOLD identified a destructive rerun path in my automatic raw-MEF recovery design.
Allocation tests in the requested SIF: 38 workflow tests and 8 map unit tests pass. The fix and corrected #964 documentation are cherry-picked and pushed to Production remains untouched. — Codex on behalf of Cail. |
The automatic raw-MEF recovery design was unsafe: code, shell or params changes could reschedule completed fragments after their raw sources were deleted. Snakemake removes declared outputs before executing the job, so the resulting failure destroyed the only fragment. Keep the declared fragment producer only for live exposures, enforced by a rule wildcard constraint rather than merely cutting input dependencies. Reclamation waits for that output. Once reclaimed (including stale PSF manifests beside a vanished store), an existing fragment is a required immutable leaf with no reachable rasterization producer. Missing reclaimed fragments fail planning with an explicit out-of-DAG raw-MEF backfill command, even if raw MEFs are still staged. Remove the automatic raw switch; retain the standalone script backfill mode and the merge hard failure. Preserve missing-fragment regressions with the explicit planning-failure contract, and verify live missing fragments run before merging and reclamation. Six lifecycle cases mutate script, shell or params with raw sources absent and assert zero exp_maps jobs, no matching producer, and identical fragment bytes/mtime, with and without exp_maps completion manifests. SIF allocation: workflow 38 passed; map unit tests 8 passed.
Claude (Opus) on behalf of Cail.
Each campaign now publishes four exposure-count maps instead of two, all at the mask ladder's nside 131072:
npoint_<run>.hsp(new)ntaken_<run>.hsp(new)nexp_<run>.hspnflagged_<run>.hspWhy. nexp follows the real CCD tiling, so with chip gaps, ears and dithers it comes out as a busy pattern, and the counts pile up where two exposures' ears overlap. npoint is a deliberately smooth, somewhat arbitrary pointing count: every exposure is one filled square. It is the v2 counterpart of v1.X's
npoint3 >= 3cut. sp_validation'smask_v2.0.yamlsays v2 has no source for that cut yet, and this map provides one. ntaken alongside nexp gives taken vs successful:ntaken − nexpis the coverage lost to CCDs whose PSF failed validation.On 44 smk-b05 exposures, npoint is 3 almost everywhere, with seams where neighbouring squares overlap. ntaken and nexp carry the CCD pattern.
ntaken − nexppicks out exactly the five CCD footprints that failed PSF validation in that region.What changed
exp_maps.py. The per-exposure fragment is now a packed uint16. Bits 0–2 are coverage (1 pointing square, 2 CCD imaging area, 4 validated CCD) and bits 3–10 are the flagged-pixel count, clipped at 255. A HealSparse record-array map was the obvious alternative, but HealSparse stores record arrays uncompressed (33 MB per exposure). Packed fragments are 2.55 MB median, against 2.13 MB for the uint8 fragments they replace.--exp-dir, what the rule uses) or from the delivered MEFs (--image-mef/--flag-mef, HDU = CCD + 1). split_exp keeps the delivered header's WCS, so the two agree. On exposure 2086785 they gave identical fragments over all 5,373,752 pixels.merge_exposure_maps.pysums the fragments into the four maps. It fails, naming the exposure and the backfill command, if it meets an old-format fragment, and it fails if a count would overflow its dtype.exposure_maps_targets, README,astra.yaml(new decisionmasking.pointing_square, andnexp_map_valid_psf_ccdsextended to cover ntaken),universes/committed.yaml, and the params pin (only theexposure_mapsshell digest moved).Backfilling existing campaigns
Fragments that existing campaigns already wrote are in the old format, and their scratch stores have been reclaimed. Rerunning
exp_maps.pyin raw-MEF mode on the staged exposures rewrites the fragment and itsexp_maps.json, and snakemake then reads the fragment throughdurable_edgeas before. This takes ~14 s per exposure. On Nibi that is about 5k fragments, so one small job.Verification
tests/unit/test_exp_maps.py,test_decisions.pyandtests/workflow: all pass.astra-tools validate: pass.exp_mapsrebuilds and targets all four maps.#960 (comment sweep) touches the same two scripts and will need a small merge.
🤖 Generated with Claude Code
https://claude.ai/code/session_01FD35GJ3MDN7ceDhFwxzMbU