sacc_io: SACC read/write layer for the standard data-product layout#245
Draft
cailmdaley wants to merge 5 commits into
Draft
sacc_io: SACC read/write layer for the standard data-product layout#245cailmdaley wants to merge 5 commits into
sacc_io: SACC read/write layer for the standard data-product layout#245cailmdaley wants to merge 5 commits into
Conversation
PRD #241 row 1. sacc>=2.4 joins core dependencies (sp_validation.sacc_io will be core library code). firecrown v1.15.1 + smokescreen 1.5.6 + an exact pyccl pin form the new [blinding] extra: the blind must be exactly recomputable from the seed at unblinding time, so the theory stack is pinned as a set. firecrown is not on PyPI and hard-depends on numcosmo-py (conda-forge only) plus the cosmosis/cobaya sampler connectors we never import — the new uv-overrides.txt drops those three from resolution; the Dockerfile and README carry the --overrides invocation. Plain installs without the blinding extra are unaffected. requires-python moves 3.11 → 3.12: Smokescreen and firecrown both set a 3.12 floor, and the container base (shapepipe:develop) already runs python:3.12-slim-bookworm — this aligns pyproject with the actual runtime. ruff target-version follows. Validated: uv resolution of '.[test,glass,blinding]' with overrides (235 packages, py3.12) and the full firecrown-core + smokescreen import chain in a fresh venv on candide. The CI image build on this branch is the container-side proof. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bGhVMAuhTy1gF6DdUc1dQ
Follow-up hardening after empirical validation of the blinding stack on candide. Two facts surfaced that the resolve-only check couldn't see: - firecrown 1.15.1 subclasses npt.NDArray (DataVector); numpy 2.5 made that a non-subclassable typing alias, so firecrown breaks at import on the numpy>=2.0 resolution (2.5.1). The [blinding] extra now carries numpy>=2.2,<2.5 (2.4.3 verified against the full compiled stack + the fast suite), and the Dockerfile requests the bound explicitly to dodge uv #8410 non-movement of an already-installed numpy. - pip-installed firecrown hits NumCosmo (conda-forge-only) at import time through two paths unrelated to cosmic shear: eager re-export of LSST predefined n(z) bins in generators/__init__ (defeating upstream's own lazy __getattr__), and the cluster likelihoods -> lsstdesc-crow -> Ncm.IntegralND C-subclass at module load. scripts/patch_firecrown.py makes the bin re-export lazy, the cluster imports optional, and ships a loud numcosmo_py shim (raises on any real use). Exact-string surgery against pinned v1.15.1, idempotent, fails loudly on a version bump, ends with an import check. Verified: fresh application on a pristine install, idempotent re-run, and the full toy Smokescreen blind end-to-end after patching. CI now also smoke-tests the blinding imports in the built image - the fast suite never imports firecrown, so a broken blinding stack would otherwise ship green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bGhVMAuhTy1gF6DdUc1dQ
Add sp_validation.sacc_io: the writer/reader layer for the two-file SACC
layout that becomes the package's standard data-product format.
{version}.sacc analysis vector — NZ tracers, coarse xi+/-, pseudo-Cl
(EE/BB/EB) with a shared BandpowerWindow, COSEBIs,
pure E/B, rho/tau PSF diagnostics; one FullCovariance
assembled block-diagonally (zero cross-blocks).
{version}_xi_fine COSEBIs/pure-EB integration input — same NZ tracers,
fine-grid xi+/-, DiagonalCovariance from TreeCorr
varxip/varxim.
Covariance order is point-insertion order (SACC preserves it bitwise
through FITS). Writers insert in the canonical order — xi+ then xi-, Cl
(ee, bb, eb), COSEBIs (all En then all Bn), pure E/B in _EB_KEYS order
(xip_E, xim_E, xip_B, xim_B, xip_amb, xim_amb, matching
b_modes.calculate_eb_statistics), rho, then tau — but readers never assume
global order: every getter resolves indices through s.indices(dtype,
tracers, **tags). assemble_covariance validates that blocks are contiguous,
ascending and tile the data vector exactly, failing loud otherwise.
Custom data types (pure E/B, rho, tau) all parse under
sacc.parse_data_type_name. Tag filters are plain kwargs; the tags={...}
form silently selects nothing and is never used.
Test suite (test_sacc_io.py, all synthetic and fast): per-writer
round-trips (arrays/tags/windows/NZ bitwise), covariance block alignment
and zero cross-blocks, assemble_covariance failure modes, DiagonalCovariance
round-trip, extract() sub-covariance alignment, a tomographic multi-pair
case, reader/writer mirroring on a mixed file, and the end-to-end two-file
layout. 20 passed.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
Fresh-eyes review caught a correctness bug: readers re-sorted selections by
theta/ell/n, but covariance blocks and bandpower windows stay in insertion
order. On a non-ascending grid the reader output silently desynchronised
from its covariance, and get_pseudo_cl returned sorted cl arrays against
unsorted window columns — internally inconsistent within one return tuple.
Fix by construction, not by sort:
- Writers validate their grids. add_xi/add_pure_eb/add_rho/add_tau require
strictly ascending theta; add_pseudo_cl requires strictly ascending
ell_eff (add_cosebis is inherently safe — it enumerates the mode index).
Out-of-order grids raise a loud ValueError naming the argument.
- Readers drop the sort entirely and return in s.indices (insertion) order,
so every getter is covariance- and window-aligned for ANY file, and
ascending for canonical files. The _sorted_* helpers are replaced by
plain insertion-order accessors (_mean/_tag).
Also:
- _pair normalises (i, j) -> sorted, so get_xi(s, (1, 0)) addresses the same
symmetric shear-shear pair as (0, 1) instead of a silent empty read.
- Module docstring documents the tomographic ξ covariance ordering:
insertion is pair-major ([pair0 xip; pair0 xim; pair1 xip; …]), supplied
to assemble_covariance as one contiguous block matching add_xi call order;
type-major converters (DES 2pt-FITS) permute explicitly via s.indices.
- extract() docstring states tracers takes SACC names, not integer bins.
New tests (26 total, was 20): writers reject non-ascending theta/ell;
(1, 0) == (0, 1) round-trip; a 3-pair tomographic ξ covariance assembled as
one contiguous pair-major block with per-pair sub-blocks recovered via
extract(); get_pseudo_cl window column j <-> ell_eff[j] via window_ind tags.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
This was referenced Jul 10, 2026
The two-file split (analysis + {version}_xi_fine.sacc) was premised on a
10000-bin fine grid; the production operating point (Paper II B-modes) is
1000 bins, where a dense per-pair fine covariance block is ~32 MB and the
CosmoCov integration-binning covariance — which feeds pure-E/B and COSEBIs
error propagation — has a natural home as a BlockDiagonal block alongside
the analysis blocks. Layout test replaced with the one-file end-to-end
case (dense fine block, extract() sub-covariance alignment, zero
cross-blocks) plus a varxi-diagonal fallback test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019R5eiy11Lihkgn4MKufXSp
1d4826c to
bd15a0b
Compare
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.
Closes #244. Row 2 of the PRD (#241 §7): the
sacc_iomodule — every data product written to and read from SACC, with round-trip tests. This is the layout contract PRs 3 (converter), 4 (migration), and 6 (blinding) build on. Draft, for review against the PRD.What's in the file
galaxy_shear_xi_plus/minus(standard)source_i,source_j)theta(arcmin, TreeCorrmeanr),theta_nom,npairs,weight,gridgalaxy_shear_cl_ee/bb/eb(standard)source_i,source_j)ell+ sharedBandpowerWindowgalaxy_shear_cosebi_ee/bb(standard)source_i,source_j)n,theta_min,theta_maxgalaxy_shear_xiPure{E,B,Amb}_{plus,minus}(custom)source_i,source_j)thetapsf_rho{k}_xi_{plus,minus}(custom)psf_stars,psf_stars)thetagalaxyPsf_tau{k}_xi_{plus,minus}(custom)source_i,psf_stars)thetasource_0, source_1, …Tomography-native throughout: every writer/reader takes a bin pair
(i, j); the current single-bin analysis is the(0, 0)case.One file per catalogue version — the PRD §1 letter
{version}.sacccarries everything: the analysis vector (coarse ξ±, pseudo-Cℓ, COSEBIs, pure-E/B, ρ/τ, n(z)) and the fine-grid ξ± integration input for COSEBIs / pure-E/B, taggedgrid='fine'. The covariance is assembled block-diagonally (zero cross-blocks — the same independence assumption today'scosmosis_fitting.pyassembly makes): the per-statistic analysis blocks first, then a dense per-pair fine-ξ block.The design point that makes this work: the production fine grid is 1000 θ bins (0.5–300′, the B-modes paper's integration binning), where a dense fine block is 2000×2000 ≈ 32 MB per pair — and a real covariance exists at exactly that binning: the Gaussian CosmoCov covariance computed on the integration grid, which is load-bearing downstream (it is the input to the pure-E/B MC error propagation and the COSEBIs covariance transform). When no CosmoCov run exists, the fine block degrades to the TreeCorr
varxip/varximdiagonal. Extreme convergence-check grids (a one-off 10 000-bin run) likewise take the diagonal fallback — they degrade the covariance, not the file layout. (This supersedes an earlier two-file amendment on this PR, which was premised on 10k bins being an operating point; it isn't.)Beyond the PRD's row-2 list, pure-E/B writers are included: §3 derives pure-E/B from blinded ξ± and PR 6's acceptance test measures them, so they need a home in the layout.
Design notes
ValueErrorotherwise) and readers return insertion order, so reader output, covariance sub-blocks, and bandpower-window columns can never silently diverge. Bin pairs normalize to i≤j. Insertion is pair-major ([pair₀ ξ+; pair₀ ξ−; pair₁ ξ+; …]); type-major consumers (DES 2pt-FITS) permute explicitly viaSacc.indices. Analysis points insert before fine points so covariance blocks tile the mean contiguously.assemble_covariance(s, blocks)places per-statistic covariance blocks with zero cross-blocks and fails loud on any gap, overlap, or size mismatch.extract()(copy +keep_selection) pulls a subset with its correctly-aligned covariance sub-block — the building block for PR 3's converter and PR 6's blinding passes (blinding the fine block is its own extract→blind→merge pass inside the same file).get_bandpower_windows()through.Testing
27 tests, all synthetic and fast: per-writer round-trips (values, tags, windows, n(z) bitwise), covariance block alignment incl. zero cross-blocks,
assemble_covariancefailure modes,DiagonalCovarianceround-trip,extractsub-covariance alignment, a 2-bin/3-pair tomographic case, reader/writer mirroring on a mixed file, the end-to-end one-file layout (analysis + dense fine block,extractof the fine selection, zero analysis↔fine cross-blocks), and the varxi-diagonal fine fallback. Fast suite: no regressions.Not in scope here:
cosmo_valmixin migration (PR 4), SACC→2pt-FITS converter and OneCovariance glue (PR 3).— Claude (Fable) on behalf of Cail.