sp_validation.sacc_io

SACC_IO.

Name:

sacc_io.py

Description:

Read/write the standard SACC data-product layout for the weak-lensing validation package. One file describes each catalogue version:

  • {version}.sacc — NZ tracers, reporting-grid ξ±, pseudo-Cℓ (EE/BB/EB) with bandpower windows, COSEBIs, pure E/B, ρ/τ PSF diagnostics, and the fine ξ± integration input for COSEBIs / pure-EB (grid='integration' tagged points). The covariance is assembled block-diagonally from the per-statistic covariances (zero cross-blocks, never materialized): the analysis blocks first, then a dense per-pair integration-ξ block (the analytic integration-binning covariance when it exists — it feeds derived-statistic error propagation — or the TreeCorr varxip/varxim diagonal as degraded fallback). assemble_covariance hands sacc a list of blocks, which stores a BlockDiagonalCovariance (one FITS table per block, Σ block² on disk rather than a dense N²) — cost scales with the integration grid’s size, a parameter set by the caller, not baked into the layout.

Insertion order is load-bearing. A Sacc is a flat list of data points in the order add_data_point was called, and row/column i of the covariance refers to the i-th inserted point — there is no other linkage between a point and its covariance entry. SACC preserves that order bitwise through FITS save/load, so writers define the covariance layout by their insertion sequence. Writers below insert in the canonical order — ξ+ then ξ−, Cℓ (ee, bb, eb), COSEBIs (all Eₙ then all Bₙ), pure E/B (xip_E, xim_E, xip_B, xim_B, xip_amb, xim_amb — matching b_modes._EB_KEYS), ρ, then τ — but readers never assume global order: they resolve indices through Sacc.indices(dtype, tracers, **tags).

Tag filters are plain keyword arguments to indices / get_data_points / get_tag; the tags={...} form silently selects nothing and must never be used.

Tomographic ξ ordering: each add_xi call inserts one tracer pair as [xip; xim], so a multi-pair vector is pair-major[pair_0 xip; pair_0 xim; pair_1 xip; …] — not type-major ([all xip; all xim]). A tomographic ξ covariance with cross-pair correlations is therefore supplied to assemble_covariance as ONE contiguous block spanning the consecutive add_xi calls, ordered pair-by-pair to match insertion. Writers must call add_xi in the same pair order the covariance was built in. Converters that need a type-major layout (e.g. the DES 2pt-FITS convention) permute explicitly via s.indices rather than assuming global order.

Optionality: a file’s contents are flexible about which components of

a statistic it actually has. add_pseudo_cl requires EE (the bandpower window’s reference series) but BB and EB are optional — an analysis that never computed EB simply omits it. add_cosebis requires Eₙ but Bₙ is optional. add_pure_eb requires xip_E/xim_E but the B and ambiguous-mode blocks are each optional, independently (B and amb are unrelated computations). Everything else a writer takes — θ/ℓ grids, tracer/bin identifiers, the value array for a component you ARE adding — is structurally necessary and has no default; supplying it partially would desynchronise the covariance layout, so it is refused rather than degraded. Readers mirror this split: a composite reader (get_pseudo_cl, get_cosebis, get_pure_eb) returns None (or omits the key) for a component the file doesn’t carry, but a selection naming that component explicitly (s.indices, _mean, extract) still fails loud on no match — silence is reserved for “this file doesn’t have that optional piece”, never for “you asked for something specific and it isn’t there”.

Converters. The tail of this module holds converters between the SACC layout above and external analysis-tool file formats — the CosmoSIS “2pt FITS” (sacc_to_twopoint_fits) and OneCovariance’s redshift / covariance files (write_nz, nz_config_stanza, read_nz, covariance_blocks). The 2pt-FITS converter reproduces today’s hand-assembled product from cosmo_inference/scripts/cosmosis_fitting.py HDU-for-HDU and byte-for-byte (verified against that writer), and is single-bin only today — it fails fast on a multi-bin SACC (tomographic emission lands with the tomographic round). The OneCovariance converters are coupled to SACC by file format only; OneCovariance itself is not a dependency.

source_name(i)[source]

SACC tracer name for source redshift bin i (0-based).

Kept as the single definition of the source_{i} naming contract — external consumers address tracers through it rather than the f-string.

new_sacc(nz, metadata=None)[source]

Create a Sacc with the survey’s NZ (and PSF) tracers.

Parameters:
  • nz (dict or sequence) – Redshift distributions, one per source bin. Either a mapping {i: (z, nz)} keyed by 0-based bin index, or a sequence of (z, nz) array pairs (bin index = position). Tracers are named source_{i}.

  • metadata (dict, optional) – Key/value pairs stored on s.metadata.

Returns:

Sacc holding the source_{i} NZ tracers and the psf_stars Misc tracer (needed by ρ/τ diagnostics).

Return type:

sacc.Sacc

_pair(bins)[source]

Resolve a (i, j) bin pair to the (source_i, source_j) names.

The pair is normalised to i <= j: shear-shear statistics are symmetric in the tracer pair, and SACC stores each pair under one ordering, so (1, 0) must address the same points as (0, 1). Kept as a helper: every add_*/get_* function relies on this normalisation.

_check_ascending(name, values)[source]

Require values to be strictly ascending; else raise ValueError.

Insertion order is the covariance (and bandpower-window) order, and readers return points in insertion order, so an out-of-order grid would silently desynchronise a data vector from its covariance. Enforce monotonicity at write time instead.

_add_theta_series(s, dtype, tracers, theta, values, **tags)[source]

Insert one theta-tagged series, one point per (theta, value) pair.

add_xi(s, bins, theta, xip, xim, *, grid, theta_nom=None, npairs=None, weight=None)[source]

Add a real-space shear 2PCF (ξ+ then ξ−) for one tracer pair.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • bins (tuple of int) – Source bin pair (i, j).

  • theta (array_like) – Angular separations (arcmin) — TreeCorr meanr.

  • xip (array_like) – ξ+ and ξ− at theta.

  • xim (array_like) – ξ+ and ξ− at theta.

  • grid ({'reporting', 'integration'}) – Stored as the grid tag on every point. The reporting (analysis) ξ± and the fine COSEBIs/pure-EB integration input share the same data type and tracer pair, so this tag is the only thing that disambiguates them in the file.

  • theta_nom (array_like, optional) – Nominal bin centres — TreeCorr rnom — stored as theta_nom.

  • npairs (array_like, optional) – TreeCorr pair counts and weights, stored per point.

  • weight (array_like, optional) – TreeCorr pair counts and weights, stored per point.

add_pseudo_cl(s, bins, ell_eff, cl_ee, cl_bb=None, cl_eb=None, *, window_ells, window_weights, grid='reporting')[source]

Add pseudo-Cℓ EE (required) plus whichever of BB/EB were computed.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • bins (tuple of int) – Source bin pair (i, j).

  • ell_eff (array_like) – Effective multipole of each bandpower.

  • cl_ee (array_like) – EE bandpowers at ell_eff.

  • cl_bb (array_like, optional) – BB and/or EB bandpowers at ell_eff. Each defaults to None and is then simply not written — EB in particular is often not computed at all.

  • cl_eb (array_like, optional) – BB and/or EB bandpowers at ell_eff. Each defaults to None and is then simply not written — EB in particular is often not computed at all.

  • window_ells (array_like) – Multipoles spanned by the bandpower window matrix (shape (nell,)).

  • window_weights (array_like) – Window matrix W of shape (nell, nbp) — one column per bandpower — from NaMaster get_bandpower_windows. One sacc.BandpowerWindow is built and shared across every component written.

  • grid (str, optional) – Stored as the grid tag on every point (default 'reporting'), joining merge’s ℓ-consistency group; variant ℓ binnings belong under different tag values.

add_cosebis(s, bins, En, scale_cut, Bn=None)[source]

Add COSEBIs Eₙ (required) and Bₙ (optional) for one scale cut.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • bins (tuple of int) – Source bin pair (i, j).

  • En (array_like) – E-mode COSEBI amplitudes, one per logarithmic mode n (1-based).

  • scale_cut (tuple of float) – (theta_min, theta_max) in arcmin, stored on every point as the theta_min/theta_max tags; multiple cuts coexist in one file, told apart by these tags.

  • Bn (array_like, optional) – B-mode COSEBI amplitudes at the same n. Defaults to None and is then simply not written. The [En; Bn] layout, when both are present, matches the COSEBI covariance.

add_pure_eb(s, bins, theta, xip_E, xim_E, xip_B=None, xim_B=None, xip_amb=None, xim_amb=None, *, grid='reporting')[source]

Add pure E-mode (required) plus whichever of B/ambiguous were computed.

Blocks are inserted in PURE_KEYS order (xip_E, xim_E, xip_B, xim_B, xip_amb, xim_amb), matching b_modes._EB_KEYS and the pure-EB covariance layout — whichever subset is present.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • bins (tuple of int) – Source bin pair (i, j).

  • theta (array_like) – Angular separations (arcmin), shared by every block written.

  • xip_E (array_like) – The pure E-mode correlation functions at theta.

  • xim_E (array_like) – The pure E-mode correlation functions at theta.

  • xip_B (array_like, optional) – The pure B-mode correlation functions. Both default to None; the pair is written together or not at all — supply both or neither.

  • xim_B (array_like, optional) – The pure B-mode correlation functions. Both default to None; the pair is written together or not at all — supply both or neither.

  • xip_amb (array_like, optional) – The ambiguous-mode correlation functions. Both default to None; same both-or-neither rule as B.

  • xim_amb (array_like, optional) – The ambiguous-mode correlation functions. Both default to None; same both-or-neither rule as B.

  • grid (str, optional) – Stored as the grid tag on every point (default 'reporting'), joining ξ’s theta-consistency group in merge’s guard.

add_rho(s, k, theta, rho_p, rho_m, *, grid='reporting')[source]

Add a ρ_k PSF statistic (ρ+ then ρ−) on the psf_stars tracer.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • k (int) – ρ index (0…5).

  • theta (array_like) – Angular separations (arcmin).

  • rho_p (array_like) – ρ_k+ and ρ_k− at theta.

  • rho_m (array_like) – ρ_k+ and ρ_k− at theta.

  • grid (str, optional) – Stored as the grid tag on every point (default 'reporting'), joining ξ’s theta-consistency group in merge’s guard.

add_tau(s, bins, k, theta, tau_p, tau_m, *, grid='reporting')[source]

Add a τ_k PSF-leakage statistic (τ+ then τ−).

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • bins (tuple of int) – Source bin i and PSF; the τ tracers are (source_i, psf_stars). Only bins[0] is used.

  • k (int) – τ index (0, 2 or 5).

  • theta (array_like) – Angular separations (arcmin).

  • tau_p (array_like) – τ_k+ and τ_k− at theta.

  • tau_m (array_like) – τ_k+ and τ_k− at theta.

  • grid (str, optional) – Stored as the grid tag on every point (default 'reporting'), joining ξ’s theta-consistency group in merge’s guard.

assemble_covariance(s, blocks)[source]

Assemble a BlockDiagonalCovariance from per-statistic blocks.

Each block is validated against the current insertion order: its indices must be contiguous and ascending, the blocks must tile 0…len(s.mean) exactly (no gap, no overlap), and each block must be square with a size matching its index span. Any violation raises ValueError naming the mismatch. Cross-blocks are zero and implicit — never materialized — because the blocks are passed to add_covariance as a list, which sacc.BaseCovariance.make turns into a BlockDiagonalCovariance (one FITS table per block, Σ block² on disk rather than a dense N² file).

Parameters:
  • s (sacc.Sacc) – Target, mutated in place via add_covariance.

  • blocks (sequence) – Ordered (selector, cov) pairs (or a mapping of the same). Each selector is either an index array, or a (data_type, tracers) / (data_type, tracers, tags) tuple resolved through s.indices; cov is the block’s dense covariance.

Returns:

s, with the assembled BlockDiagonalCovariance attached.

Return type:

sacc.Sacc

_resolve_indices(s, selector)[source]

Resolve a covariance-block selector to a sorted index array.

add_diagonal_covariance(s, variances)[source]

Attach a DiagonalCovariance from a 1-D variance array.

The 1-D array is passed straight to add_covariance (never np.diag), which is what makes SACC store a DiagonalCovariance.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • variances (array_like) – Per-point variances, len == len(s.mean).

Returns:

s, with the DiagonalCovariance attached.

Return type:

sacc.Sacc

get_nz(s, i)[source]

Return (z, nz) for source bin i.

_get_pm(s, dtype_p, dtype_m, tracers, **tags)[source]

Return (theta, plus, minus) for a +/− data-type pair.

get_xi(s, bins, *, grid)[source]

Return (theta, xip, xim) for one tracer pair and grid.

get_pseudo_cl(s, bins)[source]

Return (ell_eff, cl_ee, cl_bb, cl_eb, window) for one tracer pair.

cl_bb/cl_eb come back None if the file doesn’t carry that component (add_pseudo_cl makes both optional). window is the shared sacc.BandpowerWindow recovered via get_bandpower_windows; its columns are in the same insertion order as the returned ell_eff/cl arrays, so window column j corresponds to ell_eff[j].

get_cosebis(s, bins, scale_cut=None)[source]

Return (n, En, Bn) for one tracer pair.

Bn comes back None if the file doesn’t carry it (add_cosebis makes it optional).

Parameters:

scale_cut (tuple of float, optional) – (theta_min, theta_max) to select when several cuts share the file.

get_pure_eb(s, bins)[source]

Return (theta, {key: array}) for whichever pure-EB blocks exist.

xip_E/xim_E are always present (add_pure_eb requires them); the dict holds whichever of the B and ambiguous-mode keys (out of PURE_KEYS: xip_E, xim_E, xip_B, xim_B, xip_amb, xim_amb) the file actually carries — a key absent from the file is simply absent from the dict, not mapped to None.

get_rho(s, k)[source]

Return (theta, rho_p, rho_m) for ρ index k.

get_tau(s, bins, k)[source]

Return (theta, tau_p, tau_m) for τ index k and source bin.

_indices(s, data_type, tracers, **tag_filters)[source]

Sacc.indices that fails loud instead of selecting nothing.

Sacc.indices returns an empty array (warning only) when a selection matches no point — e.g. a typo’d tag value, or a float tag filter that is not bitwise-identical to the stored one. Every reader here funnels through this guard so an unmatched selection raises instead of propagating empty arrays downstream.

_mean(s, data_type, tracers, **tag_filters)[source]

Mean values for a selection, in s.indices (insertion) order.

Never re-sort: insertion order is the covariance and bandpower-window order, so returning in s.indices order keeps every reader aligned with the covariance for any file (and ascending for canonically-written files, which the writers enforce).

_tag(s, data_type, tracers, tag, **tag_filters)[source]

Values of tag for a selection, in insertion order.

_mean_optional(s, data_type, tracers, **tag_filters)[source]

Mean values for a selection, or None if the file has none.

Used by composite readers (get_pseudo_cl, get_cosebis, get_pure_eb) for the components a writer made optional (BB/EB, COSEBI Bₙ, pure B/ambiguous): absence is a legitimate “this file doesn’t have that piece”, not a typo to fail loud on — unlike _mean/ _indices, used for selections that name a component explicitly.

extract(s, data_type=None, tracers=None, **tag_filters)[source]

Extract a sub-Sacc (points + aligned covariance sub-block).

A copy is made and everything not matching the selection is removed, so the covariance sub-block comes out correctly aligned and the original is untouched.

Parameters:
  • s (sacc.Sacc) – Source, left unmodified.

  • data_type (str, optional) – Data type to keep.

  • tracers (tuple, optional) – Tracer pair to keep, as SACC tracer names (e.g. ("source_0", "source_0") or ("source_0", "psf_stars")) — not integer bin indices. This differs deliberately from the add_* / get_* interface, whose bins argument takes integer pairs: extract is the generic selection escape hatch, mirroring Sacc.keep_selection and addressing non-source tracers uniformly.

  • **tag_filters – Tag filters (plain kwargs, e.g. grid='integration').

Returns:

New Sacc holding only the selected points.

Return type:

sacc.Sacc

merge(saccs)[source]

Merge several per-statistic Sacc objects into one file’s worth.

A thin wrapper around sacc.concatenate_data_sets: data points concatenate in input order, tracers shared by several inputs (the source_i NZ tracers, psf_stars) are stored once, and the covariance combines block-diagonally in the same order — the library requires either all inputs to carry a covariance or none, and raises otherwise (cross-statistic covariance assembly beyond block-diagonal is out of scope here; see assemble_covariance).

Metadata must be consistent: keys present in several inputs must carry equal values (a type: data file cannot merge with a type: mock file), and the union lands on the result. This deliberately replaces the library’s clash behaviour, which mangles clashing keys by appending labels.

Grid consistency follows tagging semantics: the grid tag declares which binning a set of points lives on, so all same-length theta (or ell) arrays under one tag value must be bitwise identical — sacc itself never validates angles across data types/tracers, and a grid that differs only at floating-point level chokes CosmoSIS downstream instead of failing loud here. Different lengths within a tag group pass (scale-cut subsets are legitimate); grids under different tag values are unconstrained (reporting vs integration differ by design); θ and ℓ are separate domains, each checked against itself only. ℓ series sharing a bitwise-equal grid must also share the bandpower window (series without windows skip that check).

Parameters:

saccs (sequence of sacc.Sacc) – The per-statistic data sets, in the insertion order the merged file should have. Inputs are left unmodified.

Returns:

The merged data set.

Return type:

sacc.Sacc

Raises:

ValueError – If metadata conflicts, a shared tracer differs across inputs, two same-length theta or ell arrays under the same grid tag value are not bitwise identical, or two ℓ series sharing a grid carry different bandpower windows.

_grid_groups(s, angle)[source]

Nested map grid-tag-value -> (data_type, tracers) -> point indices.

One entry per (data_type, tracers) series carrying an angle ('theta' or 'ell') tag, in each series’ own insertion order (never re-sorted), nested under the grid tag value it lives on (None for untagged series) — the shape merge’s consistency guard checks within each tag value. Indices (not angle values) are kept so the ℓ check can also recover each series’ bandpower window.

_check_grid_consistency(s, angle)[source]

Raise unless same-length angle arrays under one grid tag match.

Consistency follows tagging semantics: the grid tag declares which binning a series lives on, so all same-length theta (or ell) arrays sharing a tag value must be bitwise identical — sacc never validates angles across data types/tracers, and a grid diverging at floating-point level chokes CosmoSIS downstream instead of failing loud here. Different lengths within a tag value pass (scale-cut subsets are legitimate); series under different tag values are unconstrained (reporting vs integration differ by design). θ and ℓ are separate domains, each checked against itself only. For ℓ, two series on a bitwise-equal grid must also share the bandpower window (equal window ells and weight matrix); series without windows (foreign files) skip the window check.

update_statistic(s, sub)[source]

Overwrite the values of s’s points that match sub’s, in place.

The merge-back half of the extract → conceal → merge blinding flow (PRD #241 §4): each point of sub is matched to exactly one point of s by (data_type, tracers, tags), and that point’s value is replaced. Nothing else changes — insertion order, tags, windows and the covariance are untouched (blinding shifts the mean only), so sub’s own covariance (e.g. the sub-block extract attaches) is deliberately not consulted. A sub point with no match, or with several, raises ValueError.

Parameters:
  • s (sacc.Sacc) – Target, mutated in place.

  • sub (sacc.Sacc) – The replacement block, e.g. extract(s, ...) after concealment.

save(s, path, *, type)[source]

Write s to path (FITS), overwriting any existing file.

Parameters:
  • s (sacc.Sacc) – Data set to write; its metadata is stamped in place.

  • type ({'data', 'mock'}) – Provenance of the underlying catalogue, stored as the required type metadata tag (PRD #241 §4, “Mocks vs data”). The caller — the pipeline computing the data vector — knows whether its input catalogue is a mock; there is deliberately no default. load refuses type='data' files that are not blinded.

load(path, *, allow_unblinded=False)[source]

Load a Sacc from path (FITS), failing closed on unblinded data.

Every sacc_io file carries a type: data|mock metadata tag (stamped by save); blinded files are additionally stamped concealed=True by Smokescreen. A type='data' file without that stamp is real, unblinded data, and loading it raises — skipping the blind can never silently expose the measured vector (PRD #241 §4). Mocks load freely, blinded or not.

Parameters:
  • path (str) – File to load.

  • allow_unblinded (bool, optional) – Escape hatch for the two legitimate consumers of unblinded data: the blinding step itself (which must read the true vector to conceal it) and the unblinding/verification tooling. Nothing else — no analysis, plotting or inference code — may pass True.

Returns:

The loaded data set.

Return type:

sacc.Sacc

_twopoint_hdu(name, values, ang, *, ang_unit=None)[source]

Build one 2pt BinTableHDU (BIN1/BIN2/ANGBIN/VALUE/ANG).

Reproduces cosmosis_fitting.py._create_2pt_hdu /cl_to_fits exactly: same column order and formats, the 2PTDATA marker, the QUANT pair for name, and NZ_SOURCE kernels. ang_unit stamps TUNIT on the ANG column (“arcmin” for real-space ξ/τ; unset for Cℓ, whose ANG is ℓ).

_nz_hdu(s, n_bins)[source]

Build the NZDATA HDU from the SACC source_i NZ tracers.

Reproduces cosmosis_fitting.py.nz_to_fits: Z_MID from the tracer z grid (assumed uniform), Z_LOW/Z_HIGH as ± half a step, one BIN{i+1} column per source bin, and the NZDATA/NBIN/NZ header cards. All source bins are required to share the z grid — the single Z_MID axis of the DES NZDATA table.

_cov_hdu(matrix, block_names, block_starts, extname='COVMAT', name_in_ctor=False)[source]

Build a covariance ImageHDU with NAME_i/STRT_i block headers.

Reproduces the two covariance builders in cosmosis_fitting.py card for card. The blocked ξ/τ covdat_to_fits builds ImageHDU(cov) unnamed and stamps COVDATA then EXTNAME from a dict; the cov_cl_to_fits CELL covariance builds ImageHDU(cov, name="COVMAT_CELL") (so the EXTNAME card is created early, with astropy’s standard comment) before re-stamping. name_in_ctor selects the second form so the card order matches exactly.

_type_major_xi(s, bins)[source]

Return (theta, xip, xim) for one bin pair from the SACC reporting grid.

get_xi already returns each statistic in insertion (= ascending θ) order; the type-major split (all ξ+, then all ξ−) is exactly the two arrays it hands back, so no further permutation is needed for a single pair.

_require_single_bin(s, n_bins)[source]

Fail fast unless the SACC is a valid single-bin ξ product.

The converter emits the single-bin 2pt-FITS today’s CosmoSIS pipeline reads (BIN1/BIN2 all 1, one NZ column). A tomographic SACC would otherwise slip through silently — n_bins alone drives the NZDATA column count while the ξ/covariance are read from bin (0, 0) only, so a 2-bin file would emit a NBIN=2 n(z) beside a data vector holding just the (0, 0) pair. Guards both the empty-ξ case and the single-bin contract; tomographic emission lands with the tomographic round.

sacc_to_twopoint_fits(s, path, *, rho_stats_hdu=None, tau_stats_hdu=None, n_bins=1)[source]

Convert an analysis SACC to a CosmoSIS 2pt-FITS file.

The assembled HDUList matches today’s cosmosis_fitting.py product for the configuration the SACC describes: PRIMARY, NZ_SOURCE, COVMAT, then (if present) COVMAT_CELL, XI_PLUS, XI_MINUS, (if present) CELL_EE / CELL_BB, and (if the rho/tau sidecars are supplied) TAU_0_PLUS, TAU_2_PLUS, RHO_STATS. The data vector and its covariance are laid out type-major (all ξ+, then all ξ−, then the τ blocks), which is the DES twopoint convention CosmoSIS reads.

Parameters:
  • s (sacc.Sacc) – Analysis SACC (reporting ξ±, optional pseudo-Cℓ, covariance, and — for the ρ/τ product — the τ data points; see rho_stats_hdu).

  • path (str) – Output FITS path (overwritten).

  • rho_stats_hdu (astropy.io.fits.BinTableHDU, optional) – The rho-stats / tau-stats sidecar HDUs, copied verbatim as today’s assembly does. Required together to write the ρ/τ product; the SACC alone cannot rebuild the varrho_* columns Sacha’s fork reads. When omitted, a pure ξ (± Cℓ) product is written.

  • tau_stats_hdu (astropy.io.fits.BinTableHDU, optional) – The rho-stats / tau-stats sidecar HDUs, copied verbatim as today’s assembly does. Required together to write the ρ/τ product; the SACC alone cannot rebuild the varrho_* columns Sacha’s fork reads. When omitted, a pure ξ (± Cℓ) product is written.

  • n_bins (int, optional) – Number of source tomographic bins. Must be 1: this converter emits the single-bin 2pt-FITS today’s CosmoSIS pipeline consumes. Tomographic emission (multiple bin pairs, per-pair BIN1/BIN2, one NZ column per bin) lands with the tomographic round; the converter fails fast on anything else rather than silently truncating to bin (0, 0).

Returns:

The assembled list, also written to path.

Return type:

astropy.io.fits.HDUList

Raises:

ValueError – If the SACC has no ξ points; if n_bins != 1 or the SACC’s ξ tracer pairs are anything other than exactly {(source_0, source_0)} (the single-bin contract); or if exactly one of the ρ/τ sidecars is supplied.

_build_cell(s, bins)[source]

Build the CELL_EE 2pt HDU plus the COVMAT_CELL HDU from the SACC pseudo-Cℓ.

Returns (None, None) when the SACC has no pseudo-Cℓ. Only CELL_EE is emitted — the harmonic 2pt_like fits data_sets=CELL_EE, and today’s assembly appends CELL_EE alone (it builds a CELL_BB HDU but discards it). The SACC still carries EE/BB/EB with bandpower windows for the B-mode null-test path; this converter surfaces only the block CosmoSIS reads. The CELL covariance (the EE bandpower covariance) lives in its own COVMAT_CELL ImageHDU, matching today’s product.

_build_covmat(s, bins, *, use_rho_tau)[source]

Assemble the blocked COVMAT (ξ± type-major, then the τ blocks).

The ξ covariance is pulled from the SACC as the contiguous ξ+/ξ− block for the pair and permuted from pair-major (SACC) to type-major (2pt-FITS). Under use_rho_tau the τ_0/τ_2 covariance blocks are appended block-diagonally with zero ξ↔τ cross-blocks, exactly as covdat_to_fits builds them.

_block_diag(*blocks)[source]

Stack square blocks block-diagonally with zero cross-blocks.

_build_rho_tau(rho_stats_hdu, tau_stats_hdu, theta, use_rho_tau)[source]

Build the TAU_{0,2}_PLUS 2pt HDUs and the verbatim RHO_STATS HDU.

Mirrors tau_to_fits / rho_to_fits: τ_0/τ_2 read their tau_k_p columns onto the shared ξ θ grid (consistency step); RHO_STATS is copied verbatim from the sidecar with its θ column forced onto the ξ grid. The varrho_* columns ride along in the copy — they are why the sidecar is required (the SACC cannot supply them).

nz_table(s, n_bins)[source]

Stack the SACC source_i NZ tracers into a OneCovariance n(z) table.

Parameters:
  • s (sacc.Sacc) – SACC holding source_0 source_{n_bins-1} NZ tracers.

  • n_bins (int) – Number of tomographic source bins to write.

Returns:

Array of shape (n_z, n_bins + 1): column 0 the shared redshift grid, columns 1 n_bins the per-bin n(z). This is the OneCovariance combined-file layout (redshift  n_1(z) n_N(z)).

Return type:

numpy.ndarray

Raises:

ValueError – If any source bin is missing, or if the bins do not share one z grid (OneCovariance’s combined file has a single redshift column, so the grids must agree bin-for-bin).

write_nz(s, path, n_bins, *, dir_key='zlens_directory', header=True)[source]

Write the OneCovariance combined n(z) input file from a SACC.

OneCovariance reads the source redshift distribution as a plain whitespace-delimited text file whose column 0 is the shared redshift grid and whose remaining columns are the per-bin n(z) (redshift  n_1(z) n_N(z)) — no z_low/z_high edges. This writes that file from the SACC source_i NZ tracers and returns the [redshift] config stanza that points OneCovariance at it.

Parameters:
  • s (sacc.Sacc) – Analysis SACC with the source_i NZ tracers.

  • path (str or pathlib.Path) – Output text-file path (overwritten). Its directory + basename become the [redshift] directory/file config values.

  • n_bins (int) – Number of tomographic source bins to write.

  • dir_key (str, optional) – Config key for the redshift directory. Default "zlens_directory" (upstream canonical). Pass "z_directory" for the UNIONS template driven by pseudo_cl.py._modify_onecov_config.

  • header (bool, optional) – If True (default) prepend a # redshift  n_1(z) comment header naming the columns; OneCovariance’s genfromtxt-style reader ignores it. Set False for a bare numeric file.

Returns:

The [redshift] config stanza (see nz_config_stanza()), naming the file just written.

Return type:

dict

nz_config_stanza(directory, filename, *, dir_key='zlens_directory', value_loc='mid')[source]

Build the OneCovariance [redshift] config stanza for an n(z) file.

Parameters:
  • directory (str) – Directory holding the n(z) file (OneCovariance *_directory value).

  • filename (str) – n(z) file basename (OneCovariance zlens_file value).

  • dir_key (str, optional) – Directory config key — "zlens_directory" (upstream) or "z_directory" (UNIONS template). Default "zlens_directory".

  • value_loc (str, optional) – value_loc_in_lensbin — where in each histogram bin the tabulated n(z) value sits (mid/left/right). Default "mid", matching the bin-centred grids the SACC stores.

Returns:

The [redshift] key/value pairs: {dir_key: directory, "zlens_file": filename, "value_loc_in_lensbin": value_loc}. Assign these under config["redshift"] of a OneCovariance configparser config.

Return type:

dict

read_nz(path)[source]

Read a OneCovariance combined n(z) file back to (z, nz_columns).

Inverse of write_nz() (the numeric round-trip; the config stanza is not stored in the file). Comment/header lines are skipped.

Parameters:

path (str or pathlib.Path) – n(z) text file (column 0 = z, columns 1… = per-bin n(z)).

Returns:

(z, nz) where z is the shared redshift grid (shape (n_z,)) and nz is the per-bin distributions (shape (n_z, n_bins)).

Return type:

tuple

covariance_blocks(cov_list, selectors, *, gaussian=True)[source]

Reshape a OneCovariance covariance_list table into SACC cov blocks.

OneCovariance emits a flat covariance_list_*.dat table with one row per (i, j) element pair (row-major, k = i·n + j); the covariance value lives in column 10 (Gaussian) or column 9 (Gaussian+non-Gaussian). This reshapes the flat table into dense square block(s) — reusing sp_validation.statistics.cov_from_one_covariance() for the per-block reshape — and pairs each with its SACC selector, ready for sp_validation.assemble_covariance().

Single-statistic case: pass the whole table and one selector; you get one (selector, dense) block. Multi-statistic case (tomography-ready): pass a sequence of (selector, sub_table) pairs — each sub_table a contiguous slice of the flat output for one statistic / bin-pair — and each is reshaped and re-paired with its selector in order. The API is thus shaped to extend to multi-probe blocking without over-fitting the single-bin case.

Parameters:
  • cov_list (numpy.ndarray or sequence) – Either the flat OneCovariance table (2-D array, one row per pair) for a single block, or — for the multi-block form — a sequence of (selector, sub_table) pairs. In the multi-block form selectors must be None (the selectors travel with the sub-tables).

  • selectors (selector or None) – For the single-block form, the SACC selector for the whole table (a (data_type, tracers[, tags]) tuple or an index array, as assemble_covariance() accepts). Must be None for the multi-block form.

  • gaussian (bool, optional) – Select the Gaussian-only column (True, default) or the Gaussian+non-Gaussian column (False); passed straight through to cov_from_one_covariance.

Returns:

Ordered (selector, dense_cov) pairs, directly consumable by assemble_covariance(s, blocks).

Return type:

list