From f1b7258c382775d8d6f5c8aef23f9fe520a4a3e0 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Wed, 28 Oct 2026 19:55:44 +0100 Subject: [PATCH 1/2] Store covariance shape dispersion per component Preserve existing OneCovariance inputs, including the two legacy catalogues with unverified original conventions. Co-Authored-By: Claude Opus 5.5 --- cosmo_val/cat_config.yaml | 52 ++++++++++++++++++----------------- src/sp_validation/survey.py | 13 ++++----- workflow/rules/covariance.smk | 6 ++-- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index 41eb3281f..a7e364a05 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1,5 +1,7 @@ # One entry per catalogue. `blind:` is `none` (public) or the name of the blind # its signal is concealed under (sp_validation.blinding). +# cov_th.sigma_e is the per-component dispersion. DES and SP_axel_v0.0 +# preserve their former OneCovariance inputs; their original convention is unknown. DES: blind: none subdir: /n17data/mkilbing/astro/data/DES @@ -12,7 +14,7 @@ DES: A: 4143 n_e: 5.67 n_psf: 3.8 - sigma_e: 0.29 + sigma_e: 0.20506096654409875 psf: PSF_size: piff_T star_size: obs_T @@ -54,7 +56,7 @@ SP_axel_v0.0: A: 1500 n_e: 6.47 n_psf: 0.47 - sigma_e: 0.3 + sigma_e: 0.21213203435596423 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -169,7 +171,7 @@ SP_v1.3.6: A: 2894.0303815287743 n_e: 4.002931781928292 n_psf: 0.752316232272063 - sigma_e: 0.3785853255980945 + sigma_e: 0.2677002509881296 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -214,7 +216,7 @@ SP_v1.4.5: A: 2894.0303815287743 n_e: 6.477062501504287 n_psf: 0.4973044366748657 - sigma_e: 0.39740419232655594 + sigma_e: 0.2810071992660706 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -259,7 +261,7 @@ SP_v1.4.5.A: A: 2420.2651014497287 n_e: 7.040818382014773 n_psf: 0.752316232272063 - sigma_e: 0.30961528707207325 + sigma_e: 0.21893106904768259 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -303,7 +305,7 @@ SP_v1.4.5_bright: A: 2420.2651014497287 n_e: 7.040818382014773 n_psf: 0.752316232272063 - sigma_e: 0.30961528707207325 + sigma_e: 0.21893106904768259 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -345,7 +347,7 @@ SP_v1.4.5_faint: A: 2420.2651014497287 n_e: 7.040818382014773 n_psf: 0.752316232272063 - sigma_e: 0.30961528707207325 + sigma_e: 0.21893106904768259 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -387,7 +389,7 @@ SP_v1.4.6_glass_mock: A: 2405.3892055695346 n_e: 6.128201234871523 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -431,7 +433,7 @@ SP_v1.4.5_intermediate: A: 2420.2651014497287 n_e: 7.040818382014773 n_psf: 0.752316232272063 - sigma_e: 0.30961528707207325 + sigma_e: 0.21893106904768259 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -473,7 +475,7 @@ SP_v1.4.6: A: 2894.0303815287743 n_e: 5.09348802763124 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -519,7 +521,7 @@ SP_v1.4.6.3: A: 2894.0303815287743 n_e: 4.957279270321334 n_psf: 0.752316232272063 - sigma_e: 0.3783979556382064 + sigma_e: 0.2675677604189021 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -564,7 +566,7 @@ SP_v1.4.6.3_B: A: 2894.0303815287743 n_e: 4.957279270321334 n_psf: 0.752316232272063 - sigma_e: 0.3783979556382064 + sigma_e: 0.2675677604189021 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -609,7 +611,7 @@ SP_v1.4.6.3_C: A: 2894.0303815287743 n_e: 4.957279270321334 n_psf: 0.752316232272063 - sigma_e: 0.3783979556382064 + sigma_e: 0.2675677604189021 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -654,7 +656,7 @@ SP_v1.4.6.3_A: A: 2894.0303815287743 n_e: 4.957279270321334 n_psf: 0.752316232272063 - sigma_e: 0.3783979556382064 + sigma_e: 0.2675677604189021 psf: PSF_flag: HSM_FLAG_PSF PSF_size: HSM_T_PSF @@ -698,7 +700,7 @@ SP_v1.4.6_ecut07: A: 2894.0303815287743 n_e: 4.77419733499695 n_psf: 0.752316232272063 - sigma_e: 0.34187904441900674 + sigma_e: 0.24174499065425653 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -743,7 +745,7 @@ SP_v1.4.6.1: A: 2405.3892055695346 n_e: 6.128201234871523 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -788,7 +790,7 @@ SP_v1.4.7: A: 2050.5970269635545 n_e: 6.121543643587931 n_psf: 0.752316232272063 - sigma_e: 0.3797419081703805 + sigma_e: 0.26851807836799524 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -835,7 +837,7 @@ SP_v1.4.8: A: 2517.021356326454 n_e: 4.983323636055024 n_psf: 0.752316232272063 - sigma_e: 0.3797397825805285 + sigma_e: 0.26851657534899687 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_starhalo_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -880,7 +882,7 @@ SP_v1.4.11.2: A: 2894.0303815287743 n_e: 6.2581259680050625 n_psf: 0.752316232272063 - sigma_e: 0.380499698062307 + sigma_e: 0.2690539167392911 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -925,7 +927,7 @@ SP_v1.4.11.3: A: 2894.0303815287743 n_e: 6.2581259680050625 n_psf: 0.752316232272063 - sigma_e: 0.380499698062307 + sigma_e: 0.2690539167392911 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -970,7 +972,7 @@ SP_v1.4.12.3: A: 2405.3892055695346 n_e: 6.128201234871523 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_v1.4.6_nside_8192.fits psf: PSF_flag: HSM_FLAG_PSF @@ -1015,7 +1017,7 @@ SP_v1.4.13.3: A: 2405.3892055695346 n_e: 6.128201234871523 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_v1.4.6_nside_8192.fits psf: PSF_flag: HSM_FLAG_PSF @@ -1060,7 +1062,7 @@ SP_v1.4.11.3_ecut07: A: 2894.0303815287743 n_e: 5.830114732534133 n_psf: 0.752316232272063 - sigma_e: 0.34058513153783426 + sigma_e: 0.24083005608171487 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -1210,7 +1212,7 @@ SP_v1.6.6: A: 2405.3892055695346 n_e: 6.128201234871523 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits psf: PSF_flag: HSM_FLAG_PSF @@ -1257,7 +1259,7 @@ GLASS_mock_validation: A: 2405.3892055695346 n_e: 6.128201234871523 n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 + sigma_e: 0.26840896706663525 mask: /n09data/guerrini/glass_mock_test/mask_nside_1024.fits psf: PSF_flag: HSM_FLAG_PSF diff --git a/src/sp_validation/survey.py b/src/sp_validation/survey.py index e9d610259..8b2c3b12b 100644 --- a/src/sp_validation/survey.py +++ b/src/sp_validation/survey.py @@ -241,9 +241,8 @@ def ellipticity_dispersion(e1, e2, w): """Per-component weighted ellipticity dispersion. Computes ``sqrt(0.5 · (⟨e1²⟩ + ⟨e2²⟩))`` where each component average is - weighted by ``w²``. This is the *per-component* RMS convention; it differs by - a factor ``√2`` from the summed-component ``sigma_e`` returned by - :func:`effective_survey_stats`. Keep the two conventions distinct. + weighted by ``w²``. This is the same per-component RMS convention as + :func:`effective_survey_stats`. Parameters ---------- @@ -298,10 +297,8 @@ def additive_bias(e1, e2, w, R): def effective_survey_stats(e1, e2, w, area_deg2): """Effective survey statistics for a shear catalogue. - Bundles the area-dependent number density and the summed-component shape - noise. Here ``sigma_e = sqrt(Σ[w²(e1² + e2²)] / Σw²)`` sums over *both* - components (no ``0.5`` factor); this is distinct from the per-component - :func:`ellipticity_dispersion`, differing by a factor ``√2``. + Bundles the area-dependent number density and per-component shape noise: + ``sigma_e = sqrt(Σ[w²(e1² + e2²)] / (2 Σw²))``. Parameters ---------- @@ -325,7 +322,7 @@ def effective_survey_stats(e1, e2, w, area_deg2): sum_w2 = float(np.sum(w**2)) sum_w2_e2 = float(np.sum((w**2) * (e1**2 + e2**2))) - sigma_e = np.sqrt(sum_w2_e2 / sum_w2) if sum_w2 > 0 else 0.0 + sigma_e = np.sqrt(sum_w2_e2 / (2 * sum_w2)) if sum_w2 > 0 else 0.0 return { "n_eff": n_eff_density(w, area_deg2), diff --git a/workflow/rules/covariance.smk b/workflow/rules/covariance.smk index c30797601..7bca4bba2 100644 --- a/workflow/rules/covariance.smk +++ b/workflow/rules/covariance.smk @@ -20,10 +20,8 @@ def get_onecov_cat_params(version): catalogue entry. Tomographic values are comma-separated strings, one entry per bin.""" cov_th = catalogue_entry(version)["cov_th"] - # cov_th.sigma_e sums both components; OneCovariance takes it per component. - sigma_e = ", ".join( - repr(float(s) / 2**0.5) for s in str(cov_th["sigma_e"]).split(",") - ) + # Both cov_th.sigma_e and OneCovariance use per-component dispersion. + sigma_e = ", ".join(repr(float(s)) for s in str(cov_th["sigma_e"]).split(",")) return sigma_e, cov_th["n_e"], cov_th["A"] From 10d7762c1d9431e7d4419e3f004582eea8d4c2d8 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Wed, 28 Oct 2026 19:55:45 +0100 Subject: [PATCH 2/2] Test shape-noise convention at covariance boundary Co-Authored-By: Claude Opus 5.5 --- .../tests/test_sigma_e_convention.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/sp_validation/tests/test_sigma_e_convention.py diff --git a/src/sp_validation/tests/test_sigma_e_convention.py b/src/sp_validation/tests/test_sigma_e_convention.py new file mode 100644 index 000000000..61c8f766f --- /dev/null +++ b/src/sp_validation/tests/test_sigma_e_convention.py @@ -0,0 +1,41 @@ +"""Regression tests for the per-component shape-noise convention.""" + +import ast +from pathlib import Path + +import numpy as np +import pytest +import yaml + +from sp_validation.survey import effective_survey_stats, ellipticity_dispersion + +ROOT = Path(__file__).resolve().parents[3] + + +@pytest.mark.parametrize( + ("version", "old_sigma"), + [("SP_v1.4.6.3", 0.3783979556382064), ("DES", 0.29), ("SP_axel_v0.0", 0.3)], +) +def test_config_preserves_covariance_inputs(version, old_sigma): + config = yaml.safe_load((ROOT / "cosmo_val/cat_config.yaml").read_text()) + entry = config[version] + # Exercise the actual Snakemake parameter function without importing Snakemake. + source = (ROOT / "workflow/rules/covariance.smk").read_text() + function = source[source.index("def get_onecov_cat_params(") :] + function = function[: function.index("\n\ndef ")] + namespace = {"catalogue_entry": lambda _: entry} + exec(compile(ast.parse(function), "covariance.smk", "exec"), namespace) + sigma, density, _ = namespace["get_onecov_cat_params"](version) + sigma = float(sigma) + assert sigma == pytest.approx(old_sigma / np.sqrt(2)) + assert sigma**2 / density == pytest.approx(old_sigma**2 / (2 * density)) + # CosmoCov's summed-component input, when constructed from this value. + assert sigma * np.sqrt(2) == pytest.approx(old_sigma) + + +def test_survey_stats_per_component(): + e1 = np.array([0.1, 0.4, -0.3]) + e2 = np.array([0.2, -0.1, 0.5]) + weights = np.array([1.0, 2.0, 3.0]) + stats = effective_survey_stats(e1, e2, weights, 1.0) + assert stats["sigma_e"] == pytest.approx(ellipticity_dispersion(e1, e2, weights))