Repository navigation
Merge develop into the tomography branch - #394
Merged
cailmdaley merged 91 commits intoOct 5, 2026
Merged
cailmdaley merged 91 commits into
cailmdaley merged 91 commits into
Conversation
* chore: add Renovate config (ported from shapepipe) * chore: gate python bumps behind dashboard approval
The metacal estimator and calibrate_comprehensive_cat.py carried a workaround that overwrote the metacal no-shear reconvolution-PSF size with the 1P value — a real fix for an older ShapePipe catalogue whose no-shear PSF was wrong, now a no-op under the current stack: ngmix builds one magnitude-dilated reconv PSF and reuses it across all metacal types, so NOSHEAR and 1P are bit-identical per object. - calibration.py: drop the overwrite; the no-shear branch simply uses the no-shear reconv-PSF column it already reads. - calibrate_comprehensive_cat.py: drop the override and the now-redundant NGMIX_T_PSF_RECONV_NOSHEAR_orig column. - test_calibration.py: update the estimator-stdout note (no hack line now). No output changes under the current stack. test_calibration.py: 8 passed. Claude-Session: https://claude.ai/code/session_01Xthu9uZC17TsaeXh899fhc Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The provisional SHA pin predated the fork's merged protocol+packaging PRs (and its commit is now orphaned). The library is changing quickly, so pyproject tracks the fork's main and uv.lock carries the exact commit; updates land via uv lock --upgrade-package smokescreen. The DRAW_SCHEME custody assertion fails closed if an install's draw semantics ever drift from the committed blind. Claude-Session: https://claude.ai/code/session_01G9MahwJEQ1t9EuvUXijmy3 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
check.txt/format.txt/report.md are written by lint.yml during each run; the autofix step's 'git add -A' kept committing them back. Gitignore them (plus the residual-pass variants) and drop the tracked copies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012b34pAS3bXRxN5hVdyq5Sw
* additive bias calculation for paper updated to v1.4.6.3 * added config * added fill_photoz script * fill photoz bands fixes * adding mag errors to fill_photoz * added Z_ML to fill_photoz * added more flags to fill_photoz * added 0p7 and 1p0 aperture magnitudes to PhotoPipe + SP output * Fixed fill photoz script with new MP_NAME type * ruff autofix (format + safe lint fixes) Pushed by the lint gate. * removed leftover git marker * fill_photoz_bands: spot-check FITS/HDF5 row order before filling a tile The fill pairs FITS row k with the k-th HDF5 row of the tile (sorted-index order) and only ever verified the row *count*, so a PhotoPipe tile ordered differently from the comprehensive catalogue would be filled with silently mismatched photo-z. After the size check, compare RA/Dec for a small sample of rows -- up to five at each end plus evenly spaced interior rows, --n_check_rows (default 10), --check_tol_arcsec (default 0.5). Only the sampled HDF5 rows are read (one fancy-index into the already-sorted index array), so the cost is a handful of point reads per tile rather than a full per-row match. A failing tile is warned about, counted as a row-order mismatch in the end-of-run summary, counted towards the consecutive-failure abort, and skipped without being added to done_tiles -- exactly as a size mismatch is, so a resume retries it. --n_check_rows 0 disables the check. HDF5 columns are RA/Dec (cat_config.yaml ra_col/dec_col for SP_v1.4.x). The FITS names are resolved at runtime from a candidate list; ALPHA_J2000 / DELTA_J2000 is first, verified against a real DR6 tile (/n17data/UNIONS/WL/photometry/UNIONS_DR6/UNIONS.001.227_SP_ugriz_photoz_ext.cat). If no candidate pair matches, the check disables itself with a warning rather than skipping tiles. Test: synthetic 3-tile HDF5 + FITS pair, tiles interleaved so the non-contiguous write path runs too; the two aligned tiles fill, the tile with reversed FITS rows is skipped and left empty, and --n_check_rows 0 reproduces the old unchecked behaviour. * fill_photoz_bands: row-order check ignores invalid positions, fails if none A sampled row with a non-finite or sentinel (|Dec| > 90) coordinate on either side carries no information about the FITS/HDF5 pairing. Before, such a row made `sep <= tol` False (a false row-order failure), and an all-NaN sample raised a RuntimeWarning from np.nanmax. Now those rows are excluded from the comparison, n_checked reports only the rows actually compared, and a tile whose sample has no comparable row is treated as unverifiable and skipped (distinct warning), so it is retried on resume rather than written blind. Adds a direct unit test covering partial/all-invalid samples, a reversed remainder, and a single-row tile (one-element h5py fancy index). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: martinkilbinger <martinkilbinger@cea.fr> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Cail Daley <cail.daley@cea.fr> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…vivor, guard against recurrence (#320) * cat_config: drop duplicate SP_v1.4.6 / SP_v1.3.6 blocks, repair the survivor `cosmo_val/cat_config.yaml` carried two top-level `SP_v1.4.6:` keys and two `SP_v1.3.6:` keys. PyYAML keeps the last, so the second block of each pair was what every consumer saw — and both were stale. They arrived in merge c22f075, which resolved a conflict by keeping both sides (and renamed the dead `SSP_v1.4.6_msel` key to `SP_v1.4.6` in the process). Delete the shadowing blocks and repair the surviving `SP_v1.4.6` to the post-aa774b65d convention: * `cov_th` stays at A = 2894.03 deg^2 / n_e = 5.0935 (the nside-4096 footprint mask every other live entry uses), not the 2405 / 6.128 pair the shadowing block held. * `shear.redshift_distr` -> `shear.redshift_path`: the code only ever reads `redshift_path`, so the v1.4.6 n(z) was under a key nothing reads while the winning block pointed at the v1.0-era `dndz_SP_A.txt`. * `shear.mask` (read nowhere) -> entry-level `mask:` pointing at `mask_map_footprint_nside_4096.fits`, matching SP_v1.4.5 / SP_v1.4.6.3 and the mask `A` was measured from. * `shear.R: 1.0` — the delivered e1/e2 columns already have the response applied; the shadowing block's `R: 0.92` double-counted it and inflated xi_pm by 1/R^2 = 18% against a covariance that never sees R. `SP_v1.3.6`'s surviving block already carries the nside-4096 footprint mask; its cov_th still holds the old 2405 / 6.128 pair and needs regenerating separately. * tests: reject duplicate keys in cat_config.yaml `yaml.safe_load` silently keeps the last of a repeated key, so a merge that resolves a conflict by keeping both sides leaves the shadowed block invisible to every consumer *and* to every test. That is how two `SP_v1.4.6` and two `SP_v1.3.6` entries survived on develop. Add a `yaml.SafeLoader` subclass whose `construct_mapping` raises on a repeated key, and assert `cosmo_val/cat_config.yaml` parses clean with it. Fails on the pre-fix config with `duplicate key 'SP_v1.3.6' at line 522 (first seen at line 148)`. * cat_config: recompute SP_v1.3.6 cov_th (A, n_e, sigma_e) for the nside-4096 footprint aa774b6 repointed SP_v1.3.6's mask to the 2894 deg² footprint but left its cov_th holding v1.4.6's old numbers. Recomputed from v1.3.6's own catalogue with sp_validation.survey; the same code path reproduces SP_v1.4.6.3's committed A / n_e / sigma_e bit-for-bit. n_psf left as it was.
* Fixes from the snakemake inference run report (#300) Rules: drop the /automnt prefix from the hardcoded paths in xi_highres (twopoint.smk) and covariance_glass_mock (covariance.smk). /automnt/nXXdataN does not exist on the node that owns that disk, so a job landing there fails immediately, before any log is written. Every canonical path in common.py already uses the plain /nXXdataN form. Docs: workflow/README.md gains a note on the /automnt trap and one on host ~/.local shadowing the container's pinned Snakemake; cosmo_inference/README.md recommends CosmoSIS --mpi over the fragile upstream --smp process pool (cosmosis#170) and cosmosis >= 3.16.1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MS3u2QuVSK1Q2CVaxcPNxU * workflow: profile-driven apptainer containerization (set A) candide profile now owns the container: software-deployment-method: apptainer + apptainer-args carries the bind mounts (matching the app bash-function binds in the top-level UNIONS CLAUDE.md), replacing the old rationale for leaving containerization to each rule. Rewrites the profile's doc comment to describe the new model and its two documented exceptions (xi_highres MPI, covariance_cosmocov host toolchain). Adds a container_smoke rule (workflow/rules/container_smoke.smk + scripts/container_smoke.py) as a cheap end-to-end check of the profile-driven container path (editable sp_validation import, numpy + OMP_NUM_THREADS, git provenance) via `script:`, wired unconditionally into workflow/Snakefile. Reconciles image_sims/Snakefile's container: None comment: it now documents that the two-image (SIF/SIF_PIPELINE) chain is a per-rule container: choice in image_sims.smk, still wrapped by the profile's apptainer deployment -- not a rule-owned apptainer exec call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MS3u2QuVSK1Q2CVaxcPNxU * workflow: profile-driven apptainer containerization (set B: image_sims) Strip every rule's explicit apptainer exec wrapper (_EXEC_PREFIX/EXEC/ EXEC_PIPELINE) from image_sims.smk. Each compute rule now carries a plain per-rule container: SIF / container: SIF_PIPELINE directive; Snakemake wraps the shell: command via the profile's software-deployment-method: apptainer + apptainer-args (set A). PYTHONPATH/PSF_DICT/OMP_NUM_THREADS injection and the SLURM_* env strip move from apptainer --env/-u flags to plain shell VAR=value / env -u syntax at the front of each shell: string (_ENV_PREFIX) -- identical effect, no apptainer-specific mechanism, works the same whether or not the command is container-wrapped. im_mbias split into im_mbias_config (run:, host-side git/provenance introspection + yaml write -- Snakemake never containerizes run: regardless of container:, so this must stay a driver-side step) and im_mbias (shell:, container: SIF, runs the actual m-bias compute). This was the one rule whose apptainer call lived inside a run: block's trailing shell() -- splitting it out is what makes container: apply to it at all. binds: dropped from image_sims config/schema -- it's now the profile's apptainer-args (one bind list for the whole workflow), not a per-run-config value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MS3u2QuVSK1Q2CVaxcPNxU * workflow: profile-driven apptainer containerization (set C: remaining rules + docs) Completes the pivot for the rules outside image_sims: xi_highres and covariance_cosmocov keep their container: None + inline apptainer exec / host-toolchain call (multi-node MPI and a host-compiled binary respectively, both genuinely incompatible with Snakemake's own container wrapping), now documented in their rule docstrings as deliberate exceptions rather than leftovers. No other rule outside image_sims called apptainer directly. While touching these files, retired the stale /pure_eb/ absolute paths left from the old repo layout: added workflow.common.WORKFLOW_SCRIPTS (Path(__file__)-based, correct under both standalone and module-composed runs) for the handful of shell: rules that call a workflow script directly, and reused the existing COSMO_INFERENCE constant elsewhere. Removed the run_cosmo_val rule in twopoint.smk, dead since cosmo_val.smk decomposed it into per-diagnostic rules (its own docstring says so) and still pointing at a stale path plus a nonsensical host .local PYTHONPATH injection. Flagged (not fixed) covariance_process: it calls cosmo_inference/scripts/ cosmocov_process.py, deleted in the #236 cleanup and never restored, so the rule fails on the default covariance target -- pre-existing, unrelated to this pivot. Rewrote workflow/README.md and cosmo_inference/README.md to the new model: snakemake is a thin host-side tool pinned via `uv tool install snakemake snakemake-executor-plugin-slurm`, run directly on the host, never from inside an apptainer shell; the candide profile's software-deployment-method puts each job in the container instead. Added a short pointer from the top-level README's dev-shell instructions to workflow/README.md so the two don't get conflated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MS3u2QuVSK1Q2CVaxcPNxU * workflow: fix script: directive under the profile-driven container container_smoke failed for real (SLURM jobs 839818/839819) with ModuleNotFoundError: snakemake.iocontainers -- the container image had its own snakemake==9.16.3 pip-installed directly (leftover from the old apptainer-shell-then-snakemake-inside pattern this pivot retires), shadowing the host-mounted 9.23.1 orchestrator that script: bind-mounts in and sys.path.extends (appended, not prepended). Removed it and its snakemake-executor-plugin-slurm/-slurm-jobstep/-interface-* family from the image (verified Required-by: none outside the family itself). Separately, apptainer-args never actually isolated host tooling: the image's own /.singularity.d/env/50-bashrc.sh unconditionally sourced the host ~/.bashrc for every apptainer action, not just an interactive `apptainer shell` -- so a host dotfile (asdf init) ran on every exec too, pushing host PATH entries (~/.local/bin) ahead of the image's own /usr/local/bin. A bare `python` in any shell:/script: rule was silently running the host's interpreter, invisibly, surviving --cleanenv. Gated the bashrc sourcing on APPTAINER_COMMAND=shell (set by apptainer itself before these scripts run). Fixing both surfaced a third, previously-masked bug: every script: rule (19 files) imports `from snakemake.script import snakemake`, which is IDE-hint-only in this snakemake version -- snakemake.script exposes no such runtime attribute (only the Snakemake class), and the preamble that actually gets pickled in already provides `snakemake` as a plain global before the rest of the file executes. Removed the broken import repo-wide; the object resolves via normal global lookup exactly as before, including inside the functions/branches a few scripts defer it into. Verified end to end: container_smoke now completes for real through SLURM (jobid 839822, python 3.12.12, sp_validation editable install resolved, correct HEAD commit read from inside the job) with no apptainer exec left in any rule. Dry-run coverage for every rule that owns an edited script (masks_only, cv_weights, and friends) shows clean DAGs. The container-image invariants (no in-image snakemake, exec/run must not source host dotfiles) aren't reproducible from this repo -- the sandbox at /n17data/cdaley/containers/containers has no tracked build recipe -- so they're now documented in workflow/README.md for the next rebuild. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MS3u2QuVSK1Q2CVaxcPNxU * workflow: remove last live apptainer-exec shell call + stale sweep-script docs presentation_pte_cosebis was the one rule left with an inline apptainer exec in its shell: block (container: None override); every sibling presentation_* rule already relies on the module-level container default. Drop the override and the raw call so it's wrapped like the rest. Also update the three sweep-script docstrings (run_xi_sweep, run_cosebis_ptes_sweep, run_cl_sweep) whose example invocations still showed the retired apptainer-exec-then-python pattern, to match the plain `python script.py ...` convention already used by their sibling CLI scripts. * profile: add --cleanenv to apptainer-args Per-node smoke tests showed default env passthrough makes container python resolution nondeterministic (host ~/.local shadowing — the #302 mechanism). --cleanenv makes every job's environment container-defined. Verified: container_smoke green through the profile with it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MS3u2QuVSK1Q2CVaxcPNxU * tests: adapt pure-E/B and glass-mock tests to the tomographic API calculate_pure_eb now returns one results dict per tomographic bin pair (#297); the pure-E/B integration test still indexed the flat mode keys. Unwrap the non-tomographic "tomo_bin_all_tomo_bin_all" entry and correct the docstring that still advertised the flat return. glass_mock's map path imports cosmology.compat.camb, which is absent in the image, so the xfail's raises=AttributeError no longer matched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QndBZicN3QvyZG4XDDPmGs * docs: sweep comment bloat across the pivot diff Remove the 'snakemake is injected' comment repeated in 19 script: files (one note in workflow/README.md instead), trim the candide profile header to the operational lessons, and cut re-narrations of the container model in Snakefile/common.py/README. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * image_sims: one image, drop dead SLURM env strip, im_mbias_config as script Collapse sif/sif_pipeline to the single sp_validation image (it ships shapepipe; must be rebuilt from uv.lock — the current 2026-07-04 image predates the lock and its numpy 2.5 breaks numba/ngmix). Remove the env -u SLURM_* prefix: shapepipe#744 gates mpi4py on OMPI/PMI vars, and --cleanenv strips the host env anyway (verified in-container). Convert im_mbias_config from a run: block to script:, drop the redundant os.makedirs, and trim pivot re-narration from comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * covariance: restore cosmocov_process.py as a containerized script: rule Deleted in the #236 cleanup with no replacement; restored from history to workflow/scripts/ (the rule is its only caller) and converted the rule from shell: to script:. Fixes on the way: bare exit() on a non-PD matrix returned 0 (Snakemake saw success) — now sys.exit(1); eigvalsh for the symmetric matrix; Agg backend; plot dpi 2000 -> 300. Verified round-trip on synthetic input in the container. Drop the NOTE and stale comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * tests: container_smoke becomes a real pytest, out of the main workflow The rule asserted nothing and put a non-scientific artifact in every paper's results/. Now: tests/data/container_smoke/{Snakefile,script} driven by test_container_smoke.py (@slow, skipped off-cluster), which submits one tiny SLURM job through the committed candide profile and asserts APPTAINER_CONTAINER is set (the job really ran in the image), the editable install resolved, seed-42 eigh values match, and git works inside the container. OMP_NUM_THREADS is recorded, not asserted — unset is the profile's designed state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * workflow: locate shell-invoked scripts via workflow.source_path Replace the hand-rolled WORKFLOW_SCRIPTS constant with Snakemake's first-class mechanism, which the docs specifically prescribe because manual path construction breaks under module composition. The script goes in input: (not params:, which would cause spurious reruns), so it also becomes an honest dependency. Fixes a live bug on the way: papers/bmodes unblinding_ceremony called 'python workflow/scripts/unblinding_ceremony.py', which from the paper workdir resolves into the paper's own scripts/ dir -- stale since 091bba8 and only detectable at run time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * workflow: use script: for single-process rules, source_path only for MPI script: resolves relative to the .smk defining it, module composition included, so it defeats the paper-workdir trap without making scripts into input files -- and matches what the other 20+ rules already do. Only xi_highres keeps source_path, where script: is structurally impossible (snakemake would wrap the whole mpiexec line in one container). unblinding_ceremony was already written for script: -- its _config_from_snakemake was dead code because the rule invoked it via shell:, so it silently ran _config_from_cli, which re-derives paths from constants that no longer exist (a cosmo_val dir deleted from the referenced checkout, and a _PROJECT_ROOT off by one since the script moved to papers/bmodes/scripts/). The rule declared 6 inputs and 9 params while passing 2 on the command line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * papers/bmodes: delete the unblinding ceremony Never used, and silently broken: the rule invoked the script via shell:, so the script's snakemake branch was dead code and its CLI branch re-derived paths from constants that no longer exist. Nothing imported it and no rule consumed its output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * containers: run the CI-published image from one shared path CI builds ghcr.io/cosmostat/sp_validation on every push from uv.lock; the hand-built SIFs it replaces were stale in ways that only failed at run time (no shapepipe.modules in one, numpy 2.5 breaking numba in the other). Every call site -- the Snakefiles, image_sims, the MPI rule's own apptainer exec, the paper shell drivers, interactive use -- now names one file, refreshed deliberately (see workflow/README.md). Overridable with --config container=<path or docker:// URI>. im_mbias_config read the OCI revision by opening the configured sif path; it now reads APPTAINER_CONTAINER, since the image a job actually ran in may be overridden and current.sif is a moving target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * deps: ship CosmoSIS in the image, drop the vestigial cosmology pin CosmoSIS was an undeclared, user-supplied dependency of the inference step -- which is why #303 was hand-patched in someone's ~/.local. It pip-installs into the image against the base gfortran/GSL/cfitsio in ~2 min, so declare it. MPIFC must be set at build time or the sampler Makefiles silently skip the MPI targets and --mpi fails at load; chains must run under MPI because the upstream --smp pool is still broken at 3.25.2. cosmology 2022.10.9 was vestigial: the cosmology.compat.camb adapter comes from cosmology-compat-camb via glass[examples]. Relocking drops it and nothing else. UV_PYTHON pins uv to the image's own interpreter, since $HOME is bind-mounted and uv would otherwise pick a host CPython carrying none of the stack. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * containers: pull the CI image by tag, park the MPI rule Snakemake pulls docker://ghcr.io/cosmostat/sp_validation:develop into a shared apptainer prefix on first use and never again, so no digest or path is written down. One constant, CONTAINER_URI in workflow/common.py, is the single source of truth; host-side callers that need a concrete file (the paper shell drivers, interactive use) derive it via workflow/scripts/container_path.py. xi_highres is parked as a comment block: it has never been runnable -- its shell is a bare 'python run_2pcf_highres.py' while the script requires --cat-config and --out -- and parking it leaves covariance_cosmocov as the workflow's only container exception. The MPI reasoning is preserved in the block for whoever revives it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * tests: drop the glass map-path xfail, its condition is met The xfail asked for a compatible glass+cosmology pair verified in a fresh image. glass 2026.2 with cosmology-compat-camb is that pair: the map path runs end to end (11 shells, 66 spectra, monotonic kappa accumulation), so the marker now only hides regressions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * docker: install liblapack-dev, pin MPIFC absolutely cosmosis's bundled MultiNest links -llapack and the base image ships only the runtime liblapack.so.3 with no dev symlink, so the build died at 'cannot find -llapack'. It passed on candide only because that sandbox had liblapack-dev installed at some point. MPIFC takes the absolute path: /opt/ompi/bin is not always on PATH, and a miss silently drops the MPI sampler libraries while the install still reports success. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * docker: point UV_PYTHON at the venv, not the base interpreter uv pip honours UV_PYTHON over VIRTUAL_ENV, so naming the system interpreter sent the editable install of sp_validation there instead of /app/.venv -- the image built fine and then failed its own import smoke test. The venv's own python satisfies the original intent (uv can't wander onto a host CPython from the bind-mounted $HOME) while keeping uv pip pointed at the venv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017hCncxwiiBZm9ixGgdzCNA * workflow: run the launched checkout's sp_validation by default Snakemake's `script:` directive already executes the checkout's script files, while `import sp_validation` resolved to the image's baked copy -- the two halves of one commit, split. `common.inject_checkout_pythonpath()` prepends the checkout's src/ to APPTAINERENV_PYTHONPATH (preserving any user-set value), so the image supplies the frozen dependency stack and the launched tree supplies sp_validation. This is what the image-sims chain has always done for both repos (`_ENV_PREFIX` in rules/image_sims.smk); the main workflow now matches it. Opt out with `--config checkout_pythonpath=false` to reproduce from the image alone. The flag is parsed tolerantly because `--config k=false` can arrive as the string "false". Also drops the hardcoded candide OpenMPI path from workflow/Snakefile: a machine path does not belong in generic workflow code, and it moves to the candide profile in a following commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * profiles: add a machine-independent default, make candide the machine layer workflow/profiles/default carries the container model and nothing else, so the workflow runs off candide with `--profile workflow/profiles/default -j N`. Snakemake cannot compose profiles (one --profile, no `inherits:`), so the small machine-independent set -- software-deployment-method, rerun-triggers, latency-wait -- is duplicated verbatim in both files, marked GENERIC and cross-referenced. That is the least-magic arrangement available. apptainer-args and apptainer-prefix stay out of the shared block: every machine has its own disks and image cache. candide's apptainer-args now carries `--env LD_LIBRARY_PATH=/softs/openmpi/...`, previously an os.environ line in workflow/Snakefile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * presentation: containerize the two rules that ran bare python The Moriond talk-figure rules called `python` with no container, so they ran against whatever interpreter the driver happened to have. Let them inherit the module-level `container:` like every other rule. The two ImageMagick `convert` rules keep `container: None`: `convert` is a host tool, absent from the image. Same for covariance_cosmocov, whose docstring says so directly rather than pointing at a list elsewhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * image_sims: default sif to null and fall back to the workflow's image `image_sims: {sif: ...}` was a required structural key, so every run config repeated the image path — a second place for it to drift from what the rest of the workflow runs. Default it to null and resolve it through the same code path as every other entry point; a run config still overrides it to name its own image or a branch tag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * containers: give every user their own image, driven by spv-container The workflow ran out of one shared image directory on candide (/n17data/cdaley/containers/snakemake-sif) with a hand-maintained current.sif symlink pointing at whatever Snakemake last autopulled. That only worked for one person: refreshing the image or repointing the symlink needed write access to another user's directory, and a refresh moved the ground under everyone at once. Everyone now runs their own image file at one canonical per-user path, ~/.cache/sp_validation/sp_validation.sif (SPV_CONTAINER overrides it), owned by a small CLI: spv-container pull # fetch the tag there, atomically spv-container status # revision label vs. this checkout's HEAD spv-container exec <cmd...> # one-off run inside it, candide binds applied sp_validation.container is stdlib-only on purpose: it runs on the host, outside the container, so it must import without the scientific stack — and it works straight from a checkout (`python3 src/sp_validation/container.py status`) with nothing installed. It also holds CONTAINER_URI, which workflow/common.py loads from this checkout by file path, so the CLI and the workflow can never name different images. `container:` now resolves to that local .sif when it exists and to the registry tag otherwise (Snakemake accepts either, and autopulls the tag into .snakemake/singularity). `--config container=...` still overrides both. The candide profile drops apptainer-prefix accordingly, and common.configure() warns — once, never fatally — when the local image predates the checkout. Also drops workflow/scripts/container_path.py, which existed to locate the shared cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: tell the container story once, around the per-user image Rewrites the container sections of workflow/README.md, CLAUDE.md, CONTRIBUTING.md and README.md for the per-user model: one image per person at ~/.cache/sp_validation/sp_validation.sif, `spv-container` to fill and inspect it, and how `container:` resolves to it. The shared-prefix machinery is gone -- current.sif bootstrap, the atomic-mv refresh recipe, the group-writable TODO. Trims the commentary while there. The profile pair says "change one, change the other" once instead of shouting it in three places; off-candide gets a paragraph rather than parallel billing, since candide is where everyone runs; and the enumeration of container exceptions is dropped in favour of the docstring on each rule that opts out. Also repoints the two paper Snakefiles, which resolve `container:` themselves, at common.resolve_container -- and replaces the obsolete `apptainer build --sandbox` recipe in README.md and installation.rst with `apptainer pull`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * containers: add an opt-in writable sandbox, and one resolution order The pristine SIF is read-only, which is what you want almost always -- but it lost the one real advantage of the old hand-built sandbox workflow: `pip install` mid-analysis, when you need a package the image does not carry yet and a CI rebuild is too slow a loop to think in. `spv-container sandbox` unpacks the image into a writable directory at ~/.cache/sp_validation/sandbox/, and `spv-container exec --writable` runs against it so installs persist. Opt-in: nothing builds one for you. Resolution order is now one thing, shared by the CLI, the run_*.sh drivers and the workflow's `container:` -- sandbox if it exists, else SIF if it exists, else the registry tag. Snakemake execs a sandbox directory as happily as a .sif, so a package installed into the sandbox is there for workflow jobs too, with no further wiring. `resolve_image()` in sp_validation.container is the single implementation; common.resolve_container defers to it. The build stages into a sibling directory and swaps it in, as `pull` does, for a sharper reason than pull has: a half-written .sif fails loudly, but a half-unpacked sandbox is still a *directory*, so resolution would elect it and every job would silently run a broken tree. Building before removing also means a `--force` rebuild that fails -- a typo in --source, a network blip -- leaves the sandbox you already had intact, instead of deleting a working environment on the way to not replacing it. The cost of a sandbox is that what runs is no longer fully described by a revision label, so the divergence is made visible rather than left silent: `status` names which layer is live and says the revision only describes what the sandbox was built from (falling back to the SIF's label, marked as inferred, when the sandbox carries none), and the workflow prints one line at launch when a sandbox is in play. `spv-container pull && spv-container sandbox --force` resets. Verified on candide (apptainer 1.5.3): unprivileged `build --sandbox` works through user namespaces with no fakeroot and no subuid mapping; `exec --writable` persists writes while plain `exec` gets a read-only filesystem; `inspect --labels` still reports the source image's OCI labels from a sandbox directory; `--fix-perms` at build time is what keeps the tree removable afterwards (without it apptainer leaves directories that defeat `rm -rf`, which would strand `--force`); and a failed `--force` rebuild leaves the existing sandbox and its contents untouched, with no staging directory left behind. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * image: build the cosmosis-standard-library fork into the container The cosmo_inference .ini templates pointed COSMOSIS_DIR at two different people's home directories (/home/guerrini/... and a scratch path of Lisa's), so running the inference pipeline meant either being one of them or editing the templates by hand. CosmoSIS itself already ships in the image via the `workflow` extra; only the Standard Library — the tree of module files the pipelines name — was missing. Clone and build it at /opt/cosmosis-standard-library, pinned to Sacha Guerrini's fork at b26fa7ff. That fork is 4 commits ahead of upstream and 373 behind; the four are what the UNIONS pipelines need (tau statistics, sample_S8, two z-dependent linear-alignment modules). Carrying them onto current upstream is future work, noted in cosmo_inference/README.md. The templates now read COSMOSIS_DIR from %(CSL_DIR)s, which the image sets — CosmoSIS reads environment variables into an ini's [DEFAULT] section, which is how the existing %(SCRATCH)s references already work. Off-image, export CSL_DIR and the same templates work unchanged. The build follows CSL's documented procedure for a pip-installed cosmosis (`source cosmosis-configure && make`), but targets `shear/` rather than the top-level `make`: the top level also descends into likelihood/, building the Planck, WMAP and ACT likelihoods, which no UNIONS pipeline uses. Of the modules our templates do name, all are pure Python except two under shear/ — `limber`, which project_2d.py links, and cl_to_xi_nicaea's nicaea_interface.so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: prune duplicated and historical comments The container model, the checkout-PYTHONPATH default, the profile GENERIC mirroring and the CSL_DIR resolution were each explained in three to six places. Give every concept one home -- workflow/README.md for the user-facing story, the docstring of the thing itself for mechanism -- and leave pointers elsewhere. Drop comments narrating what the code used to do; git holds that. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * simplify: collapse duplication added by this branch The container model landed the same few lines in several places; fold each into one home. * the four run_*.sh sweep drivers resolved this user's image (and repeated the bind list) inline -- now one sourced papers/bmodes/scripts/container_env.sh, resolving exactly as sp_validation/container.py does (sandbox first, then SPV_CONTAINER/XDG_CACHE_HOME, binds from SPV_APPTAINER_BINDS) * container.py: one _require_apptainer() instead of three copies of the PATH guard, and compare_revision's merge-base calls go through _git * common.py: resolve_container takes the override value, so image_sims.smk no longer wraps IMSIM["sif"] in a synthetic config dict; drop the CONTAINER_URI / local_sif / local_sandbox re-exports, which have no callers * cosmocov_process.py: only Snakemake runs it, so drop the argv entry point and its main() indirection, matching im_mbias_config.py * xip_xim.py: one catalog() builder for the tomographic and non-tomographic paths instead of two near-identical treecorr.Catalog blocks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * simplify: give the sweep drivers one shared preamble; fold a duplicated README section The four papers/bmodes sweep drivers each repeated the worktree path, the derived script/source dirs, and a full `apptainer exec ... /usr/local/bin/python` invocation (six sites). container_env.sh now owns all of it and exposes `spv_python` / `sweep_versions`; argv is byte-identical. workflow/README.md explained `--config container=` twice, once for a local .sif and once for a branch tag. One subsection now covers both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * image: actually build CSL — cosmosis-configure exits 0 under set -u without running make Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C856B4eJ3LwXrj9SCiEuMc * image: drop set -u in the CSL layer — the configure exports append to unset paths The test -f artifact guard is what keeps a no-op build loud; -u had become the thing breaking the build instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C856B4eJ3LwXrj9SCiEuMc * image: point limber's Makefile at Debian's GSL (GSL_INC/GSL_LIB) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C856B4eJ3LwXrj9SCiEuMc * rebase onto develop: shed tomography-branch remnants The container/workflow work is orthogonal to the tomography branch it was accidentally based on. Restore develop's pure_eb docstring, test_cosmo_val call shape, and glass_mock xfail; keep develop's glass==2025.1 pinned set (cosmology 2022.10.9 is load-bearing there, not vestigial) and relock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0143zcEsfSWr13AfSroMteEC * docs: make spv-container the install story README leads with the four-line install (clone, symlink onto PATH, pull, exec-check); container.py gets a shebang + exec bit so the symlink is a real CLI with no packaging. installation.rst carries the depth (subcommands, per-user model, sandbox, raw apptainer/docker); CONTRIBUTING and workflow/README point at the same symlink step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0143zcEsfSWr13AfSroMteEC * CSL runtime coverage: import-test template modules; patch fork for scipy>=1.15; add fast-pt The image build only compiled CSL; pure-Python modules were never loaded until a pipeline ran. Two runtime breaks shipped in a green image: scipy>=1.15 removed scipy.special.lpn (legendre.py, reached by every real-space likelihood via spec_tools -- #316), and project_2d.py imports fastpt, which the image never carried. - test_csl_modules.py imports every .py module the ini templates reference, in-image (skips without CSL_DIR/cosmosis) - Dockerfile applies upstream a8a941d5 (lpn fix) as a patch until the fork absorbs it (#316) - fast-pt>=3.2,<4 joins the workflow extra (4.0 restructured; CSL expects 3.x) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmGA86b7YyQn54JFj78ssM * ruff autofix (format + safe lint fixes) Pushed by the lint gate. * Repoint CSL at the UNIONS-WL org fork; trim fast-pt annotation Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NmGA86b7YyQn54JFj78ssM * ruff autofix (format + safe lint fixes) Pushed by the lint gate. * lint: noqa E402 on the in-section container-smoke import * Drop CSL scipy workaround after fork merge Pin the container to the current UNIONS-WL fork main commit, remove the now-obsolete lpn patch, and update the inference documentation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XTNCvVQNLXZVTiDR1PZaVG --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Concurrent doc-deploy runs on back-to-back pushes to develop both hit peaceiris/actions-gh-pages at once, and the loser fails with "cannot lock ref 'refs/heads/gh-pages'" (seen in run 34250154443). Scope the job to a gh-pages-<ref> concurrency group; cancel-in-progress is fine since develop only ever has one active deploy target and the newest push should win over a stale one still building. Claude-Session: https://claude.ai/code/session_01Wk8SZkCRuKQ5xu5g38zHpx Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Resolve reporting bins from their edges rather than bin means. This keeps the 12--83 arcmin fiducial window at bins 9--15 and preserves the intended degrees of freedom. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The texlive-latex-base/-recommended/-fonts-recommended + cm-super set added in #338 costs ~1.3 GB and still lacks type1cm.sty, so matplotlib's usetex path stays broken. Replace it with TinyTeX (~270 MB measured), pinned to TeX Live 2025 on both halves: bundle v2026.02, and tlmgr pointed at that year's frozen tlnet-final historic mirror. Packages are an explicit tlmgr list covering matplotlib's usetex preamble. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Container: TinyTeX pinned to TeX Live 2025, replacing apt texlive
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Pin peaceiris/actions-gh-pages action to 84c30a8
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Deletes code with no live caller:
- workflow/scripts/run_2pcf_highres.py and the parked, unrunnable
xi_highres block in workflow/rules/twopoint.smk that was its only
reference; the mpi4py comment in pyproject.toml no longer names it.
- papers/bmodes/scripts/run_cov_sweep.sh, which calls
workflow/scripts/run_cosmocov_chain.sh, absent from develop.
Fixes docs that point at paths or tools that do not exist:
- CLAUDE.md, CONTRIBUTING.md: single-test example uses test_cosmo_val.py.
- CLAUDE.md: cosmo_inference runs through Snakemake (inference_fiducial),
not a pipeline.sh driver.
- papers/{bmodes,cosmo_val}/Snakefile: read cat_config through code/
rather than the deprecated pure_eb symlink.
- ecut_spec.md, update_survey_stats.py: paths under papers/bmodes/.
- README badge and installation docs: Python 3.12 floor, matching
requires-python.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeMhD6yCyrgBz5oz87bCtR
calculate_pure_eb_correlation returns a self-describing results dict (theta, bin edges, xi+/- on both grids, n_eff) in place of the gg/gg_int objects, so calculate_eb_statistics, the plot helpers and save_pure_eb_results work from values alone. New seams: bins_from_edges, log_bin_edges, pure_eb_from_xi, pure_eb_covariance_mc and cosebis_scan_from_xi. The pure-E/B .npz stores n_eff (the realisation count behind the covariance) instead of npatch. Callers in cosmo_val, the bmodes calculate_pure_eb_ptes script, its claims rule and PTE sweep driver are adapted. No numbers change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HeMhD6yCyrgBz5oz87bCtR
cs_util's get_theo_xi returns {pair: (xi+, xi-)}, so concatenating its
return value raised TypeError and the semi-analytic pure-E/B covariance
(pure_eb_covariance_mc, and the paper's precompute_pure_eb_chunk.py) could
not run. One n(z) is one tracer pair: unpack that single entry, which also
fails loudly if a tomographic n(z) is passed. The new test stubs the
kernel and checks the draws centre on the binned theory mean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeMhD6yCyrgBz5oz87bCtR
Remove dead scripts and fix stale doc paths
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…riance Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…files Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Seed each draw with its index and use k-means++ so the initialization samples distinct layouts. Rebuild the shared-layout catalogues because TreeCorr caches patch catalogues independently of reassigned centres. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
BE equals EB for the all/all auto-spectrum, so return a separate copy for the supported BE diagnostic. Tomographic cross-pair FITS readback retains its independent BE. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
One supplied xi covariance describes one pair, not every auto/cross pair. Fail before measuring tomography rather than silently assigning the same uncertainties to different samples. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Member
LisaGoh
approved these changes
Oct 5, 2026
sachaguer
approved these changes
Oct 5, 2026
sachaguer
left a comment
Contributor
There was a problem hiding this comment.
Looks good to me. I do not remember why I traded low ell noise bias equal to l_min for zeroes. I think there is no good reason. It does not have much impact I believe but we should go back to the constant noise bias
White shape noise is flat in ell; zeroing it below the first band understated the Gaussian covariance there. Sacha agreed in the #394 review that there was no reason for the zeros. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133mw5vATUB7QfqbhYac7Fz
The fixture passed one cov_path for every bin pair, which COSEBIs now rejects for tomography. Four jackknife patches give each pair its own covariance; the assertion on per-pair results is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0133mw5vATUB7QfqbhYac7Fz
cailmdaley
merged commit Oct 5, 2026
2ff1b50
into
feature/sp_validation-extend-to-tomography
2 checks passed
This was referenced Oct 5, 2026
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.
This merges
developinto the tomography branch, so that the later merge of the tomography branch intodevelop(step 4 of #375) becomes a review rather than a rebuild.The result keeps the tomography branch's API and takes develop's I/O.
Every per-version result stays keyed by bin pair, with non-tomographic results as the
("all","all")pair.Catalogues are read through develop's readers, and the
("all","all")pair is written through develop's SACC writers.Closes #374. Part of #375.
How to review
GitHub's diff against the tomography branch is about 30k lines, and almost all of it is develop's own PRs, each reviewed when it merged. Two smaller surfaces matter:
git show --remerge-diff 0d5f2ad1(2.1k lines) andgit show --remerge-diff 8631f608(0.7k), and the 30 commits after the first merge (git log -p --no-merges 0d5f2ad1..).git diff develop...merge/develop-into-tomo. That is 60 files, +9.0k/−2.2k, of which 1k is n(z) tables. It is the tomographic science, and the part where review moves the science most.Structure
The first commit, 0d5f2ad, is a true merge of
develop(8db2aa5) and only resolves conflicts.Each later change is its own commit:
pol_factor: -1, the B-mode summary and SACC n(z) on the bin-pair shapes, the tests, and this branch's own callers of reshaped helpersuv.lockre-resolved: shear-psf-leakage develop f1a2c071 (includes CosmoStat/shear_psf_leakage#48), glass 2026.2cell_method == "map", in both the SACC bandpower window and the iNKA fiducial("all","all"); the object-wise leakage reader accepts #48's row selection; Hartlap debiasing of simulation ρ/τ covariances restoredcov_type=Nonein the covariance file name; error bars from the configured τ covariancecov_estimate_method='jk') gets its own seeded layout instead of all repeating seed 0; the (all,all) SACC readback provides BE (= EB); tomographic COSEBIs refuse a single covariance file for every bin pairChoices worth a look
NmtFieldCatalog), which have no pixels. This branch already dropped pw² from the iNKA fiducial, and develop applied it. pw² now applies only on the map path.m_nu; fixed in Preserve CCL neutrino masses and species in CAMB predictions cs_util#97._tomo_bin_all, asbasename()builds them. Existing products under the old names are recomputed on the next run.Verification
The audit's merge checklist (Merge develop into the tomography branch #374, tests from
assay/decision-record, epic Scientific audit of sp_validation: decision record, regression tests, defects #377) passes: 19 tests pass, and 2 are strict xfail for per-bin τ centering, whose fix belongs upstream (Centre galaxy ellipticities within the selected tomographic bin shear_psf_leakage#52). Two checklist items needed code changes:plot_cosebisignoredcompute_tomography(a71e5fa).The other items were already right on the merged branch, and their tests now hold them there.
Full container test suite, slow tests included: 516 pass and 1 fails. The failure is a missing configured data path, and it also fails on a769e10. The workflow DAG tests pass, and CI builds the image and runs the unit tests in it.
Tomographic ρ/τ rerun on the GLASS mock (the one catalogue with bins): each bin's galaxy mask selects exactly its
TOM_BIN_IDrows, and the τ theory covariance is computed per bin from the masked catalogue.Parity with develop
Both branches ran the full
papers/cosmo_valsuite on SP_v1.4.6.3 and its_leak_corrvariant in the same image: develop 8db2aa5, and this branch d6356c9. To compare, I recomputed and diffed every product.Identical to float precision:
ρ/τ differ by up to 0.8σ. This comes from #295's star mask, which drops the 947 of 6,554,897 stars with nonzero HSM flags. Switching the mask off reproduces develop to 1e-12. The flagged stars look like failed fits (e ≈ 0, T ≈ 0.07 against a median of 0.18), so the mask stays. Downstream, the SP_v1.4.6.3 fit moves α 0.029→0.033 (σ 0.011) and η 0.93→1.52 (σ 1.36).
Deliberate changes:
Tomographic ρ/τ (GLASS mock): τ₀₊ per bin, with and without the bin mask.

Develop's own suite fails in three places, independent of this merge: the pseudo-Cℓ covariance rule (the dict bug of closed #372), COSEBIs and pure-E/B refusing the unblinded ξ± part the same workflow writes, and the τ plot (CosmoStat/shear_psf_leakage#46).
— Claude Opus 5.5 on behalf of Cail
🤖 Generated with Claude Code