Skip to content

Restructuring: top-level layout + workflow/papers/scratch division#197

Draft
cailmdaley wants to merge 93 commits into
developfrom
cleanup/restructuring
Draft

Restructuring: top-level layout + workflow/papers/scratch division#197
cailmdaley wants to merge 93 commits into
developfrom
cleanup/restructuring

Conversation

@cailmdaley

@cailmdaley cailmdaley commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Restructuring sp_validation

Supersedes #188 (closed as a side-effect of a branch rename — all work is intact on this branch; only the PR wrapper was lost).

Status — WIP draft, not yet for review.

  • Foundation folded in. Sacha's sachaguer:develop (Merge Sacha's fork with fiducial sp_validation #192 head, 120 files: paper plots,
    harmonic configs, library changes) merged; the cosmology.py KeyError: 'mnu' blocker fixed
    (one line) — test_cosmology.py 26/26 green. Sacha's broad .gitignore bans
    (*.png *.sh *.fits) were not adopted.
  • Back-pressure guard suite all green — characterization guards that must stay green as
    files move: ① imports + standalone-scripts/ resolution, ② snakemake -n passes,
    ③ config-path existence (Candide-local), ⑤ symlink integrity, ⑥ dangling-reference grep.
    Full suite 86 passed, 0 skipped; the move-map guard is active with five registered moves.
  • Phase 2 — the moves — COMPLETE. The tree now has the target top-level shape:
    • workflow/ + papers/{bmodes,catalog,harmonic} — the bmodes split (generic compute base
      composed via Snakemake module; paper layers on top). pure_eb run dir repointed;
      all_tapestry dry-runs cleanly from both locations.
    • cosmo_val/ promoted from notebooks/ (code + config home beside cosmo_inference/);
      every tracked reference swept notebooks/cosmo_valcosmo_val, on-disk outputs moved
      along so candide-absolute paths stay live.
    • scratch/ added (tracked, per-person; conventions in its README), one top-level
      results/ (contents gitignored, dir kept), root output/ ignored, the dead hand-listed
      notebook block dropped from .gitignore.
  • Cleanup begun: defunct/ deleted; nbstripout + 2 MB large-file pre-commit hooks land
    the bloat discipline (activation: pre-commit install, see CONTRIBUTING).
  • Remaining: fold glass_mock core into src/ (code-level refactor, own pass); curate
    notebooks/ to official demos (which reduction notebooks become scripts/ — review with
    Martin); branch/milestone tidy-up (Restructuring proposal: top-level layout + workflow/paper/scratch division #188/Foundation: merge pending local code into develop #189 closure) with Cail.
  • develop is untouched. Live state is tracked in the sp-validation-restructuring fiber.

— Claude on behalf of Cail


One organizing principle — the things you run live at the top — a clean three-way
split between analysis, papers, and scratch, and a modular workflow built for more than one
person.


The shape

Today cosmo_val is buried inside notebooks/ while cosmo_inference/ is top-level, so
you constantly hunt for where each one lives. The fix: the things a person actually runs
sit side by side at the top, sharing library code in src/ underneath.

sp_validation/
├── src/sp_validation/   library code (+ glass_mock core)
├── cosmo_val/           validation: code + config        (promoted from notebooks/)
├── cosmo_inference/     inference: code + config         (cosmosis / cosmocov)
├── workflow/            ALL analysis — modular Snakemake, multi-person → results/
├── papers/             final-figure assembly only (PDF, colour, layout)
├── scripts/            real reduction scripts (catalog builders, masking)
├── scratch/            per-person — ad hoc work + personal workflows (tracked)
├── notebooks/          curated to official demos / tutorials
├── results/            analysis products + diagnostic plots (contents gitignored)
└── docs/  tests/  config/

Division of labor

The boundary is the inputs to a paper figure: everything up to that point is analysis;
the figure itself is presentation.

  • workflow/ — all analysis. Generic, reusable, modular, organized for multiple people.
    Produces analysis products and diagnostic plots (sp_validation makes many — they go to
    results/). The bulk of the work lives here.
  • papers/<paper>/ — final-figure assembly only. The figure PDF, colours, layout,
    recombining data for presentation. Tied to one paper, and may never touch Snakemake.
  • scratch/<person>/ — personal and ad hoc. Experiments and one-off custom workflows.
    Tracked, because seeing each other's scratch is useful.

How the workflow scales — modular, not monolithic

Nothing in this analysis is computed once: the catalog changed ~20× in the first release
suite, and every paper varies the data vector, covariance, and inference. So the workflow
is parameterized — the rules are shared, the config changes each time. Snakemake's
module directive imports the rules under your own config and an output prefix, and lets
you override any single rule:

module analysis:
    snakefile: "../../workflow/Snakefile"
    config:    config              # this run's catalog, cuts, blind
    prefix:    "results/bmodes"    # products land here — no clobbering

use rule * from analysis
# swap is per-rule: redefine just the data-vector rule to override it

One top-level results/; each run namespaces under results/<name>/ via the prefix, so
people don't clobber each other. A --dry-run on each composition is the safety net that
lets the structure grow without silent breakage.


Cleanup

  • Delete defunct/ (quarantined since 2024) and the exploratory 2021–22 notebooks — it
    all stays in git history.
  • Curate notebooks/ to official demos and tutorials; personal scratchy ones move to
    scratch/.
  • Discipline via tooling, not bans: nbstripout strips notebook outputs on commit (the
    repo's weight today is committed notebook outputs), plus a pre-commit size hook.
  • Path translation — collecting the paper dirs breaks ~35 hardcoded absolute paths; a
    mechanical sweep rewrites them (scripts included) to the single repo-relative results/.

The milestone

A suite of PRs, in sequence:

  1. Foundation — merge pending local code into develop. (Sacha — folded into this branch)
  2. Restructuring — this PR: proposal + implementation behind the guard suite.
  3. Glass mocks → tomography.
  4. Input pipeline → tomography.

— Claude on behalf of Cail

sachaguer and others added 30 commits March 3, 2026 15:55
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold Sacha's pending foundation (PR #192 head, sachaguer:develop @ c22f075)
onto current develop so the restructuring builds on his foundation without
racing his merge gesture (Cail's direction, 2026-06-05).

.gitignore conflict resolved in favour of develop: kept the .felt tracking
block, rejected sacha's broad cluster bans (*.png *.sh *.fits *.out *.err) —
those get narrowed during the restructuring gitignore pass, not adopted
wholesale. cosmo_val.py / cat_config.yaml auto-merged cleanly (origin's
docstring-RST polish + sacha's functional changes did not collide).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cosmology.py get_cosmo read planck_defaults["mnu"] but the dict never
defined the key, so every bare get_cosmo() call (no ccl_params, no mnu
arg) raised KeyError: 'mnu'. Add "mnu": PLANCK18["m_nu"] (0.06 eV).

Verified: test_cosmology.py 26/26 pass (was immediate KeyError before).
This is the one blocker that kept Sacha's foundation from running clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs/source/sp_validation.*.rst are regenerated on every docs build by
sphinx-apidoc (deploy-docs.yml: `sphinx-apidoc -feTMo docs/source
src/sp_validation`), matching the already-ignored fortuna.*/scripts.*
stubs — they should never be committed.

uv.lock: the container is the canonical runtime (CLAUDE.md), the lockfile
has never been tracked, so ignore it rather than make an unowned
pinned-dep commitment. One-line flip to track if we decide to pin.

Establishes a clean base for the restructuring branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sacha's branch removed the cosmosis_pipeline_glass_mock_0*.ini and
_v0*.ini ignore patterns, which un-ignored ~700 generated glass-mock
pipeline configs in cosmo_inference/cosmosis_config/. Restore the two
specific patterns (not broad bans) so the tree returns to develop's
clean state. These are generated artifacts, never tracked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cailmdaley and others added 30 commits June 11, 2026 03:36
…el, conflicting-PR silent docs skip)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cosmo_val diagnostics shared one in-memory `cv` object across ~13 method
calls in a linear driver, linked only by lazy properties. This adds a generic
workflow layer — workflow/rules/cosmo_val.smk plus one thin script per rule —
that turns each diagnostic into a rule and links them by the real data products
each method writes under cosmo_val/output:

  rho/tau FITS, xi data vector (produced by the existing rho_tau_stats / xi
  rules in twopoint.smk, which cosmo_val consumes rather than duplicating) feed
  the PSF-fit, ratio, pure-E/B and COSEBIS rules; the per-version E/B and
  COSEBIS npz products feed cv_summarize_bmodes.

I/O decisions:
- Methods writing durable products own compute rules keyed on those files
  (pure_eb npz, cosebis npz, pseudo_cl FITS).
- Pure-plot methods whose figure paths derive from internal handler state
  (rho/tau plots, rho_tau_fits, objectwise leakage, 2pcf overlay) declare a
  sentinel under output/snakemake_sentinels so they stay DAG-trackable.
- Lazy cv state the original never persists (c1/c2, xi_psf_sys) is materialized
  to JSON (additive bias) or recomputed in the one rule that needs it
  (xi_psf_sys in cv_ratio_xi_sys_xi) — recompute is cheap next to the science.
- Each rule re-instantiates `cv`; the DAG is expressed through files, not the
  shared object. See workflow/scripts/cv_runner.py.

The new rules activate only when the config carries a `cosmo_val` block, so
papers/bmodes (which composes the same workflow module) is untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
papers/cosmo_val/ mirrors the papers/bmodes pattern: it composes the generic
workflow module via the `module` directive and exposes an `all` target
(default) that builds the full CosmologyValidation suite. Individual rules run
in isolation by requesting their output files.

- config/config.yaml carries the run-specific `cosmo_val:` block (constructor
  args, theory cosmology, pure-E/B and COSEBIS binning, fiducial scale cut)
  plus the shared-module scaffolding the composed workflow parses.
- results/cosmology/planck18.json satisfies common.configure().
- run_cosmo_val.py gains a header marking it the interactive scratch path and
  pointing at the workflow as the canonical batch entry.

snakemake -n all builds a valid 19-job DAG; rules run standalone (e.g.
bmode_summary.json drives xi -> pure_eb/cosebis -> summarize).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add smoke-integration tests that exercise the central compute seams of
CosmologyValidation end-to-end on a small, deterministic toy catalog
written to disk, asserting that catalog/config/estimator are wired
together correctly and the chain produces output of the right shape with
finite values. The underlying numerical libraries (treecorr, cosmo_numba)
are not re-tested: no specific numerical values are asserted.

Seams covered:
- calculate_2pcf: xi+/- via treecorr GGCorrelation.
- calculate_scale_dependent_leakage: shear x PSF assembly via treecorr,
  alpha leakage and xi_sys.
- calculate_pure_eb: the headline B-mode seam, xi+/- -> cosmo_numba
  Schneider (2022) pure E/B split; finiteness asserted on interior bins
  (the estimator is undefined at the extreme reporting bins by
  construction, which real analyses drop via a scale cut).

A new _write_synthetic_catalogs helper writes the shear/PSF FITS and a
cs_util-readable dndz file. Tests are environment-independent (synthesize
in a tmp dir) but need the scientific stack, i.e. the container.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reproducibility surface of the UNIONS GLASS mocks — the fixed Planck18 +
HMCode-AGN cosmology, the sigma8-rescaled CAMB power spectrum, and the
lognormal matter/lensing-map generation — was trapped inside
glass_mock/make_unions_glass_sim.py, entangled with argparse and catalogue I/O
and importable by nothing. Lift it into the library as a proper module:

- GlassMockConfig dataclass: every value that must not silently drift
  (cosmology, shell construction, resolution, seed), with Planck18 densities
  read at construction so it round-trips to a numeric reference.
- build_camb_params / camb_sigma8: the GLASS-free cosmological core (runs
  wherever CAMB is installed).
- build_shells / matter_shell_cls / generate_matter_maps: the GLASS map
  generation, with glass imported lazily so the module and the import guard
  resolve without GLASS (the production container ships CAMB but not GLASS).

make_unions_glass_sim.py becomes a thin CLI wrapper over the core, owning only
parsing, mask downgrade, galaxy sampling, and FITS I/O. glass added as an
optional dependency; module added to __init__ __all__.

The glass_mock/ directory survives (CLI + postprocessing scripts), so this is
an extraction-in-place, not a directory move — documented in the move-map
guard rather than registered there (registering the still-live "glass_mock"
string would be a false move).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tructuring

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per Cail: the pseudo-Cl BB column belongs in the standard B-mode summary
and the NaMaster step is no more expensive than the other diagnostics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The COSEBIs output filename copied the pure_eb scheme (_eb_, reporting
nbins, no _nmodes/_scalecut), so cv_cosebis verify_outputs would
FileNotFoundError every run and deadlock cv_summarize_bmodes. Now mirrors
plot_cosebis exactly: _cosebis_ prefix, nbins_int, _nmodes=, _scalecut=.
Verified char-for-char against the config. Caught by the fresh-eyes review.
Also: ruff import-sort on the new cv_* runner scripts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…extra

The published shapepipe base ships cs_util 0.1.9 and `uv pip install -e` does
not upgrade an already-satisfied dep to meet a new lower bound (astral-sh/uv
#8410), so sp_validation's use of cs_util.size (needs >=0.2.1) silently failed.
Upgrade cs_util explicitly, and install the package with the [test,glass]
extra so the image can run the GLASS map-level mock test.

Captures the container-refresh work that was left uncommitted in the worktree.
Image rebuild/republish (ghcr) is deferred — Cail's gesture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dockerfile: add glass + cs_util>=0.2.1, install with [test,glass] extra.
Build config only — image rebuild/republish on ghcr is Cail's gesture
(push of #197 triggers the deploy-image CI). Not yet validated by a real
image build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pin the numeric output of correlation_from_covariance, scale_cut_to_bins,
find_conservative_scale_cut_key, and the headline calculate_eb_statistics
(Hartlap + 2D-PTE) against fixed deterministic inputs. Tight rtol (1e-12 on
the exact correlation math, 1e-9 on the scipy chi2.sf PTEs) with teeth: each
function has a perturbation test confirming a changed input changes the
result, and scaling the B-modes 10x drops every full-range PTE below 0.05.
Environment-independent (seeded RNG + SimpleNamespace gg stubs; no cluster
data). 8 tests, green in-container; adversarially reviewed (teeth confirmed
to bite, literals independently recomputed by hand).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pin metacal response-matrix recovery (injected finite-difference slopes ->
R=[[2,0],[0,3]]; the 2*step normalization is load-bearing), the size/SNR
boolean masks (strict-bound exclusion), and jackknif_weighted_average2
(seeded mean + error). Tight rtol 1e-10 with teeth (injected-slope change
moves R; tightened bounds flip mask elements; a weight change moves the
jackknife mean). Environment-independent (hand-built NGMIX catalog + seeded
RNG). 5 tests, green in-container; adversarially reviewed clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion

Pin the calibration linear algebra in get_calibrated_quantities and
get_calibrated_m_c (g_corr = inv(R)@g_uncorr; c = mean, c_err = population
std; g_corr_mc = g_corr - inv(R)@c) through a lightweight fake gal_metacal,
at rtol 1e-12. Teeth: perturbing R moves g_corr; a constant ellipticity
offset shifts c by exactly that offset; an inv(R)->R refactor breaks the
pins. Environment-independent (in-memory SimpleNamespace; no cluster data).
4 tests, green in-container; adversarially reviewed (teeth confirmed).

c_err pins CURRENT behavior (population std, ddof=0); the source carries a
"use std of mean" TODO that would need re-baselining if acted on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Redo test_calculate_pure_eb_runs_on_synthetic_catalog per the correction that
the real analysis integrates over a BROADER range than it reports: widen the
integration grid to [1,300] arcmin / nbins_int=600 so every reporting bin of
the Schneider E/B decomposition is well-defined, and assert finiteness on ALL
reporting bins (not just the interior). Add tight value-drift pins (rtol 1e-6,
atol 1e-12) on the four deterministic mode vectors xip_E/xim_E/xip_B/xim_B
(reproducible to 1.4e-11 across processes; covariance left shape-only since it
depends on kmeans patch assignment). Teeth: a ~3% drift on any mode value
fails, and coarsening nbins_int back toward ~80 reintroduces edge NaNs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run nbstripout (0.8.1, the version pinned in .pre-commit-config.yaml) over all
51 tracked notebooks. Removes 13k+ lines of embedded output/execution state
that accumulated while the pre-commit nbstripout hook was shadowed by the bd
(beads) hook in .git/hooks/pre-commit. Notebooks are unchanged as code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…I fix

Adding GLASS to the container image ran test_matter_maps_are_seed_deterministic
for the first time, exposing that glass_mock.py's map path is incompatible with
the installed (unpinned) glass+cosmology versions: cosmology.Cosmology.from_camb
returns a CambCosmology without comoving_distance, which glass.distance_grid and
MultiPlaneConvergence require. The map path had never been exercised (GLASS was
absent from the production image until now). xfail (strict=False, raises=
AttributeError) so CI is green and the gap is documented; the real fix is to pin
a compatible glass+cosmology pair (or adapt the calls) and verify in the fresh
image. See fiber glass-cosmology-api-pin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Purge foreign paths from the dormant inference subsystem: the output root now
derives from COSMO_INFERENCE (this repo's cosmo_inference, via common.py) and
the shell rundir from a COSMO_INFERENCE_RUNDIR constant, instead of
/home/guerrini and the deprecated pure_eb symlink; the external chain/mock dirs
(/n09data/guerrini/*) become config keys (inference.chains_dir,
.glass_mock_data_dir, .glass_mock_chains_dir). grep confirms zero
guerrini/pure_eb/n09data paths remain in inference.smk.

Resolve the pseudo-Cℓ filename schism (consumer adopts the producer's tagged
name): pseudo_cl_assets() now requests
pseudo_cl_{version}_blind={blind}_{binning}_nbins={nbins}.fits and the matching
pseudo_cl_cov_ name, sourcing blind/binning/nbins from a new harmonic.fiducial
config block (A/powspace/32); PSEUDO_CL_DIR fixed COSMO_VAL.parent -> COSMO_VAL
so the DAG edge to the producer forms. Verified on disk that the producer's
tagged files exist at exactly these names (the old bare name does not). Keys
added to both papers/bmodes and papers/cosmo_val configs (both load the workflow).

DAG verified: snakemake --list + inference_fiducial -n build cleanly (only the
expected dormant cov_tau MissingInput); dry-run guard passes. Scope: data
products only, no cosmosis chains. The FITS-CONTENT schema reconciliation
(reader vs producer HDUs) remains for when the subsystem is revived.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Integration test for cosmo_inference/scripts/cosmosis_fitting.py (numpy +
astropy only, fully exercisable in-container). Pins the HDU set for both the
plain-xi and --use-rho-tau paths, the xi+ then xi- data-vector ordering, and
the blocked-covariance offsets (STRT_0..3 = 0, N, 2N, 3N with the tau block
truncated 3N->2N). Teeth confirmed via mutation testing (wrong offset/order/
truncation all turn it red). 9 tests, green in-container. Covers the riskiest,
least-tested code in the inference data-product path; the harmonic-Cℓ
augmentation path is left for a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t guard

git rm --cached the 27 catalog-paper plot PDFs (~13M) under papers/catalog/plots
(no .tex in this repo references them — the paper TeX lives in the docs/ repo —
so they are regenerable script/notebook outputs) and the 2 tracked glass_mock
cosmosis_config .ini files (already matched by .gitignore but committed before
the pattern existed). Both now gitignored so they won't reappear.

Add test_no_stray_outputs.py: a structural guard asserting no tracked image/data
outputs (png/pdf/fits/npy/...) live under any */scripts/ dir — back-pressure
against the regression that once committed a 761 KB PNG beside a script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…_mock

Tracks the latent bug surfaced by adding GLASS to the container: glass_mock.py's
map path needs a cosmology with comoving_distance that the installed glass+
cosmology pair doesn't provide. Map test xfail'd; fix = pin compatible versions,
verify in the fresh sandbox, drop the xfail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the deprecated pure_eb/ compat-symlink prefix from COSMO_VAL,
COSMO_INFERENCE, CAT_CONFIG, and CV_RUNDIR. Behavior-identical (same
inode; pure_eb -> analyses/shear_2d/bmodes_2d -> code/sp_validation),
but no longer depends on a symlink slated for removal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolves the glass_mock map-path AttributeError (Cosmology.from_camb ->
CambCosmology lacks comoving_distance). glass 2025.1 is the unique version
with the flat API the map path uses AND the legacy cosmo.dc/xm/ef interface
that cosmology 2022.10.9 (its newest release) provides; matter_cls lives in
the separate glass.ext.camb package. Verified under uv: the full map path
runs and is seed-deterministic. Drop the test_glass_mock xfail once the
container is rebuilt with the [glass] extra (gated on the sandbox swap).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the lightcone reproduction: root unions analysis + 3 paper
sub-analyses (II bmodes/Daley, III cosmic_shear_2d/Goh, IV harmonic/Guerrini),
shared decisions hoisted to root, stop-at-inference. Decisions + claimed
findings extracted from docs/unions_release TeX and adversarially verified;
astra validate passes. Lives in scratch (Cail's), not papers/. See fiber
unions-astra-reproduction; Phase 2 (review) next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants