From 27f24afc3ebd73df77c30b077413889c57f0d9ea Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 1 Jul 2026 13:43:28 +0200 Subject: [PATCH 001/107] (ruff) Commit modifications by the ruff pre-commit --- config/glass_mock/config_glass_mock.yaml | 42 ++++++++ config/glass_mock/config_glass_mock_test.yaml | 42 ++++++++ ..._unions_glass_sim.py => make_glass_sim.py} | 99 ++++++------------- src/sp_validation/glass_mock.py | 15 +++ 4 files changed, 129 insertions(+), 69 deletions(-) create mode 100644 config/glass_mock/config_glass_mock.yaml create mode 100644 config/glass_mock/config_glass_mock_test.yaml rename scripts/glass_mock/{make_unions_glass_sim.py => make_glass_sim.py} (82%) diff --git a/config/glass_mock/config_glass_mock.yaml b/config/glass_mock/config_glass_mock.yaml new file mode 100644 index 00000000..62afdeb8 --- /dev/null +++ b/config/glass_mock/config_glass_mock.yaml @@ -0,0 +1,42 @@ +# Config file to generate a GLASS mock. +# Example config file, uncomment and modify the inplace value if needed. + +# To run this configuration run the script `make_glass_sim.py` +# Options: +# -s, --seed: Random seed +# -N, --number: Mock Number for labelling +# -t, --test: Run in test mode +# -cb, --camb: get Camb C_ell +# -c, --config: Path to the configuration file to generate the simulation (Required) + +# --- Cosmological parameters (CAMB) --- +#h: 0.6766 +#Om: 0.30966 +#Ob: 0.04897 +#ns: 0.9665 +#sigma8: 0.8102 +#mnu: 0.06 +#log_T_AGN: 7.8 +#As_init: 2.1e-9 #seed As before sigma8 rescaling +#kmax: 20.0 + +# --- Resolution --- +nside: 4096 +dx: 120.0 +zmax: 3.0 + +# --- Sampling --- +seed: 42 + +# --- galaxy population (downstream of map generation) --- +nbins: 1 +n_arcmin2: 6.0905 +sigma_e: 0.2684 +bias: 1.2 +phz_sigma_0: 0.03 +#ia_bias: null + +# --- Runtime options --- +mask_path: ... +nz_path: ... +output_path: ... \ No newline at end of file diff --git a/config/glass_mock/config_glass_mock_test.yaml b/config/glass_mock/config_glass_mock_test.yaml new file mode 100644 index 00000000..7f908c0c --- /dev/null +++ b/config/glass_mock/config_glass_mock_test.yaml @@ -0,0 +1,42 @@ +# Config file to generate a GLASS mock. +# Example config file, uncomment and modify the inplace value if needed. + +# To run this configuration run the script `make_glass_sim.py` +# Options: +# -s, --seed: Random seed +# -N, --number: Mock Number for labelling +# -t, --test: Run in test mode +# -cb, --camb: get Camb C_ell +# -c, --config: Path to the configuration file to generate the simulation (Required) + +# --- Cosmological parameters (CAMB) --- +#h: 0.6766 +#Om: 0.30966 +#Ob: 0.04897 +#ns: 0.9665 +#sigma8: 0.8102 +#mnu: 0.06 +#log_T_AGN: 7.8 +#As_init: 2.1e-9 #seed As before sigma8 rescaling +#kmax: 20.0 + +# --- Resolution --- +nside: 32 +dx: 200.0 +zmax: 0.5 + +# --- Sampling --- +seed: 42 + +# --- galaxy population (downstream of map generation) --- +nbins: 1 +n_arcmin2: 0.0824 +sigma_e: 0.2684 +bias: 1.2 +phz_sigma_0: 0.03 +#ia_bias: null + +# --- Runtime options --- +mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits +nz_path: /n17data/sguerrini/UNIONS/WL/nz/v1.4.6.3/nz_SP_v1.4.6.3_A.txt +output_path: /n09data/guerrini/glass_mock_test/ \ No newline at end of file diff --git a/scripts/glass_mock/make_unions_glass_sim.py b/scripts/glass_mock/make_glass_sim.py similarity index 82% rename from scripts/glass_mock/make_unions_glass_sim.py rename to scripts/glass_mock/make_glass_sim.py index bc97abae..2402290c 100644 --- a/scripts/glass_mock/make_unions_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -1,4 +1,4 @@ -"""Generate a UNIONS GLASS mock source catalogue. +"""Generate a GLASS mock source catalogue. Thin CLI wrapper around the generation core in ``sp_validation.glass_mock``: this script owns argument parsing, the mask downgrade, galaxy sampling, and FITS @@ -39,68 +39,21 @@ def get_parser(): """Create the argument parser.""" parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description="Creates a UNIONS simulation using GLASS", + description="Creates a weak lensing catalogue simulation using GLASS", fromfile_prefix_chars="@", ) parser.add_argument("-s", "--seed", help="Random seed", type=int, default=42) parser.add_argument("-N", "--number", help="Mock Number", type=int, default=0) - parser.add_argument( - "-n", - "--nside", - help="Nside for the simulation. Nside=Lmax", - type=int, - default=32, - ) - parser.add_argument( - "-ne", - "--neff", - help="Effective number of galaxies per arcmin^2", - type=float, - default=6.0905, - ) - parser.add_argument( - "-p", - "--path", - help="Output path to save the mocks", - type=str, - default="./", - ) - parser.add_argument( - "-c", - "--cls", - help="Pre-compute and saves the matter shell cls", - action="store_true", - ) parser.add_argument("-t", "--test", help="Test run", action="store_true") - parser.add_argument( - "-sg", - "--sigmae", - help="Set sigma of intrinsic ellipticity", - type=float, - default=0.2684, - ) parser.add_argument("-cb", "--camb", help="get Camb C_ell", action="store_true") parser.add_argument( - "-nz", - "--pathnz", - help="Path to the n(z) file", - type=str, - default="/n17data/sguerrini/UNIONS/WL/nz/v1.4.6.3/nz_SP_v1.4.6.3_A.txt", - ) - parser.add_argument( - "-m", - "--mask", - help="Path to the mask", + "-c", + "--config", + help="Path to the configuration file to generate the simulation", type=str, - default="/n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits", - ) - parser.add_argument( - "-ia", - "--ia_bias", - help="Intrinsic alignment bias for CCL", - type=float, - default=None, + required=True, ) + return parser @@ -111,42 +64,50 @@ def __init__(self): parser = get_parser() args = parser.parse_args() + yaml_config = args.config + # Test mode shrinks the box; otherwise CLI overrides config fields. if args.test: - print("[!!!] Running in test mode [!!!]") + print("[!!!] Running in test mode: ignore the configuration file [!!!]") self.config = GlassMockConfig.from_planck18( nside=32, seed=args.seed, n_arcmin2=0.0824, dx=120.0, zmax=0.5, - sigma_e=args.sigmae, - ia_bias=args.ia_bias, + sigma_e=0.26, + ia_bias=None, ) else: - self.config = GlassMockConfig.from_planck18( - nside=args.nside, - seed=args.seed, - n_arcmin2=args.neff, - sigma_e=args.sigmae, - ia_bias=args.ia_bias, + # Load the configuration from the config file in the parser + self.config = GlassMockConfig.from_yaml( + yaml_config=yaml_config, seed=args.seed ) + # Runtime options self.test = args.test self.camb = args.camb - self.pre_cls = args.cls + + # Number label and random seed self.number = args.number self.n_sim = str(args.number).zfill(5) - self.path = args.path + self.rng = np.random.default_rng(self.config.seed) + + # Input paths self.path_mask = args.mask self.path_nz = args.pathnz - self.rng = np.random.default_rng(self.config.seed) + + # Output path + self.path = args.path print("-" * 66) - print(f"Creating a UNIONS GLASS simulation with NSide = {self.config.nside}") + print( + f"Creating a weak lensing catalogue GLASS simulation with NSide = {self.config.nside}" + ) print(f"Mock number: {self.number}") print(f"Random seed: {self.config.seed}") - print(f"Test mode or not: {self.test}") + if self.test: + print("[!] Running in test mode [!]") print("-" * 66) self.root = f"{self.path}/results" @@ -173,7 +134,7 @@ def __init__(self): self.camb_cls = None def galaxies_simulation(self): - """Create a UNIONS source catalogue from the GLASS mock maps.""" + """Create a source catalogue from the GLASS mock maps.""" config = self.config # Read + downgrade the survey mask. diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index 7ba0057b..9c46d033 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -100,6 +100,21 @@ def from_planck18(cls, **overrides) -> "GlassMockConfig": base.update(overrides) return cls(**base) + @classmethod + def from_yaml(cls, yaml_config, seed=42) -> "GlassMockConfig": + """ + Build a config for the GLASS mock generation from a YAML configuration file. + + Reads the dataclass fields from the YAML file. If absent, the field is kept at its default value. + """ + import yaml + + with open(yaml_config, "r") as f: + data = yaml.safe_load(f) + + overrides = {k: v for k, v in data.items() if k in cls.__dataclass_fields__} + return cls(**{**overrides, "seed": seed}) + @property def lmax(self) -> int: """``lmax`` is tied to ``nside`` in the production mocks.""" From f8fb92f1f05492b304891cbc49ef5867e6d76ce0 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 1 Jul 2026 14:27:37 +0200 Subject: [PATCH 002/107] (Test) Add test data for the test run of the GLASS sims. Runs end to end with the new API. Updated the run script to the most recent version of GLASS --- .gitignore | 4 +- config/glass_mock/config_glass_mock_test.yaml | 1 + config/glass_mock/test_data/mask.fits | Bin 0 -> 106560 bytes config/glass_mock/test_data/mask_nside32.fits | Bin 0 -> 106560 bytes .../test_data/redshift_distribution.txt | 3000 +++++++++++++++++ scripts/glass_mock/make_glass_sim.py | 36 +- src/sp_validation/glass_mock.py | 21 +- 7 files changed, 3042 insertions(+), 20 deletions(-) create mode 100644 config/glass_mock/test_data/mask.fits create mode 100644 config/glass_mock/test_data/mask_nside32.fits create mode 100644 config/glass_mock/test_data/redshift_distribution.txt diff --git a/.gitignore b/.gitignore index f8eec899..fa4684ce 100644 --- a/.gitignore +++ b/.gitignore @@ -196,5 +196,5 @@ papers/catalog/plots/*.pdf # SLURM run logs from cosmo_val validation runs papers/cosmo_val/logs/ -# Ignore scratch notebooks -scratch/*/*.ipynb \ No newline at end of file +# Ignore scratch work notebooks +scratch/guerrini/work_notebooks \ No newline at end of file diff --git a/config/glass_mock/config_glass_mock_test.yaml b/config/glass_mock/config_glass_mock_test.yaml index 7f908c0c..0b46260c 100644 --- a/config/glass_mock/config_glass_mock_test.yaml +++ b/config/glass_mock/config_glass_mock_test.yaml @@ -37,6 +37,7 @@ phz_sigma_0: 0.03 #ia_bias: null # --- Runtime options --- +limber: True mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits nz_path: /n17data/sguerrini/UNIONS/WL/nz/v1.4.6.3/nz_SP_v1.4.6.3_A.txt output_path: /n09data/guerrini/glass_mock_test/ \ No newline at end of file diff --git a/config/glass_mock/test_data/mask.fits b/config/glass_mock/test_data/mask.fits new file mode 100644 index 0000000000000000000000000000000000000000..4f86bf2593a962ae18b8a77d66824ab2d28433dc GIT binary patch literal 106560 zcmeI$VQ<<-90%a-o1C;4Y|^BJY|^qxeL)h_dL@9smC~0^aT2ruk#VNv+wW||fEKD$ zlcr94{300V_;dc<`OzKY^m5osLU8zM9 zr;GV=Q)KHbxyq9M#esU+R>K#bHpO1pRx=4mKmrnwfCMBUf&YF2^>T8Y4*IV`>n!bO zowHuj+PbTwa6O&p<#VX=>-!=UPt|I1y47U&^j}>+pM3fHxml05_pP5#+rQTJ?u+?t zbyu&)C+n4Xi0uy@O}&$S^+ISLsu$ZII)>+icm24If5Cp?wp=_ugonJ$XGK+%`}Yg4 zKUc54lV4MBf0a#dvu>L7F2?)&w_9&DWp1X${p4`fEPFppn(=6@cQ4!XZ}Y`3Jj=R+ z(dDXM`}pMNxA9~Ds9xL*);)~(+j^69diCCLyWUY)UwxQ975CF+Ud653XZkuAT_mHl z|2o#IU+R{-^@eqwuqY=*Ii25z<%hdsR{R)>X>}L3iOpvgW#}j4EQ#B|5ch-q&SlfT z^|QHKZyskyJ)5h$>GA~+bHv~xZm0K-?@#r79u>>Q{bO^Ne^0%-{yK;Hj<$|&IvUqI z>VxySJ1v)0XwnPch3}8Ub-pYnKWvrQIqh|ta-a6EeR6X8OYC1S|B`Yc?O$At&);0e z@u+`ENYB%3H@|c~DW2j;{1(>#kAo9qU$x!oTm5Em_Iq;PJio2(yIybn_WeiWJO1N2 z{wDEfQRcUsVO=+km4}^CmUepU!uIFuB)m#M0uuPT68L6yeCxGIKn~_Zoq%~@9+(H7 z0}gP2103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K0 z2ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`2 z9N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8 zaDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0W zzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h z00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70 z103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh1 z4sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4 zIKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G z-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G;K2Xefz{pn TD**{eKmrnwfCMD))hF;5Z!Vcf literal 0 HcmV?d00001 diff --git a/config/glass_mock/test_data/mask_nside32.fits b/config/glass_mock/test_data/mask_nside32.fits new file mode 100644 index 0000000000000000000000000000000000000000..4f86bf2593a962ae18b8a77d66824ab2d28433dc GIT binary patch literal 106560 zcmeI$VQ<<-90%a-o1C;4Y|^BJY|^qxeL)h_dL@9smC~0^aT2ruk#VNv+wW||fEKD$ zlcr94{300V_;dc<`OzKY^m5osLU8zM9 zr;GV=Q)KHbxyq9M#esU+R>K#bHpO1pRx=4mKmrnwfCMBUf&YF2^>T8Y4*IV`>n!bO zowHuj+PbTwa6O&p<#VX=>-!=UPt|I1y47U&^j}>+pM3fHxml05_pP5#+rQTJ?u+?t zbyu&)C+n4Xi0uy@O}&$S^+ISLsu$ZII)>+icm24If5Cp?wp=_ugonJ$XGK+%`}Yg4 zKUc54lV4MBf0a#dvu>L7F2?)&w_9&DWp1X${p4`fEPFppn(=6@cQ4!XZ}Y`3Jj=R+ z(dDXM`}pMNxA9~Ds9xL*);)~(+j^69diCCLyWUY)UwxQ975CF+Ud653XZkuAT_mHl z|2o#IU+R{-^@eqwuqY=*Ii25z<%hdsR{R)>X>}L3iOpvgW#}j4EQ#B|5ch-q&SlfT z^|QHKZyskyJ)5h$>GA~+bHv~xZm0K-?@#r79u>>Q{bO^Ne^0%-{yK;Hj<$|&IvUqI z>VxySJ1v)0XwnPch3}8Ub-pYnKWvrQIqh|ta-a6EeR6X8OYC1S|B`Yc?O$At&);0e z@u+`ENYB%3H@|c~DW2j;{1(>#kAo9qU$x!oTm5Em_Iq;PJio2(yIybn_WeiWJO1N2 z{wDEfQRcUsVO=+km4}^CmUepU!uIFuB)m#M0uuPT68L6yeCxGIKn~_Zoq%~@9+(H7 z0}gP2103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K0 z2ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`2 z9N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8 zaDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0W zzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h z00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70 z103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh1 z4sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4 zIKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G z-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G;K2Xefz{pn TD**{eKmrnwfCMD))hF;5Z!Vcf literal 0 HcmV?d00001 diff --git a/config/glass_mock/test_data/redshift_distribution.txt b/config/glass_mock/test_data/redshift_distribution.txt new file mode 100644 index 00000000..89aa3aa3 --- /dev/null +++ b/config/glass_mock/test_data/redshift_distribution.txt @@ -0,0 +1,3000 @@ +0.000000000000000000e+00 0.000000000000000000e+00 +1.000333444481493880e-03 3.981956545413817446e-03 +2.000666888962987760e-03 6.932064073856508517e-03 +3.001000333444481423e-03 9.586034800257063962e-03 +4.001333777925975520e-03 1.206298496719232158e-02 +5.001667222407469617e-03 1.441481136008560676e-02 +6.002000666888962846e-03 1.667022635241322340e-02 +7.002334111370456943e-03 1.884724323664124443e-02 +8.002667555851951040e-03 2.095808851670286077e-02 +9.003001000333445136e-03 2.301152155292751286e-02 +1.000333444481493923e-02 2.501406914620550592e-02 +1.100366788929643333e-02 2.697074123338756915e-02 +1.200400133377792569e-02 2.888547339187262988e-02 +1.300433477825941979e-02 3.076141459959420496e-02 +1.400466822274091389e-02 3.260112213783077778e-02 +1.500500166722240798e-02 3.440669810560863934e-02 +1.600533511170390208e-02 3.617988776926599204e-02 +1.700566855618539444e-02 3.792215213804438212e-02 +1.800600200066689027e-02 3.963472264091205677e-02 +1.900633544514838263e-02 4.131864306967396316e-02 +2.000666888962987847e-02 4.297480226962233779e-02 +2.100700233411137083e-02 4.460395998086915531e-02 +2.200733577859286666e-02 4.620676752469865534e-02 +2.300766922307435902e-02 4.778378455222663523e-02 +2.400800266755585138e-02 4.933549274479407526e-02 +2.500833611203734722e-02 5.086230712592543918e-02 +2.600866955651883958e-02 5.236458548114646810e-02 +2.700900300100033541e-02 5.384263626366593558e-02 +2.800933644548182777e-02 5.529672527715281255e-02 +2.900966988996332360e-02 5.672708136236397775e-02 +3.001000333444481596e-02 5.813390126589651485e-02 +3.101033677892631180e-02 5.951735383248771966e-02 +3.201067022340780416e-02 6.087758363398869388e-02 +3.301100366788929652e-02 6.221471412620547398e-02 +3.401133711237078888e-02 6.352885040765290925e-02 +3.501167055685228818e-02 6.482008164074994083e-02 +3.601200400133378055e-02 6.608848318524837562e-02 +3.701233744581527291e-02 6.733411848509854303e-02 +3.801267089029676527e-02 6.855704074303921314e-02 +3.901300433477826457e-02 6.975729441159335409e-02 +4.001333777925975693e-02 7.093491652458211449e-02 +4.101367122374124929e-02 7.208993788952340609e-02 +4.201400466822274166e-02 7.322238415819337654e-02 +4.301433811270423402e-02 7.433227679007292643e-02 +4.401467155718573332e-02 7.541963392127196131e-02 +4.501500500166722568e-02 7.648447114974575578e-02 +4.601533844614871804e-02 7.752680224612372439e-02 +4.701567189063021041e-02 7.854663979821148445e-02 +4.801600533511170277e-02 7.954399579616183713e-02 +4.901633877959320207e-02 8.051888216440464263e-02 +5.001667222407469443e-02 8.147131124565461235e-02 +5.101700566855618679e-02 8.240129624165543065e-02 +5.201733911303767915e-02 8.330885161475302947e-02 +5.301767255751917846e-02 8.419399345390343736e-02 +5.401800600200067082e-02 8.505673980829961400e-02 +5.501833944648216318e-02 8.589711099143833295e-02 +5.601867289096365554e-02 8.671512985813152607e-02 +5.701900633544514790e-02 8.751082205669195868e-02 +5.801933977992664720e-02 8.828421625828319907e-02 +5.901967322440813957e-02 8.903534436521409745e-02 +6.002000666888963193e-02 8.976424169977431644e-02 +6.102034011337112429e-02 9.047094717504571004e-02 +6.202067355785262359e-02 9.115550344898334933e-02 +6.302100700233410902e-02 9.181795706293330306e-02 +6.402134044681560832e-02 9.245835856564466049e-02 +6.502167389129710762e-02 9.307676262373537623e-02 +6.602200733577859304e-02 9.367322811948380901e-02 +6.702234078026009234e-02 9.424781823674160952e-02 +6.802267422474157776e-02 9.480060053569451528e-02 +6.902300766922307707e-02 9.533164701713665867e-02 +7.002334111370457637e-02 9.584103417686894166e-02 +7.102367455818606179e-02 9.632884305078404097e-02 +7.202400800266756109e-02 9.679515925115628194e-02 +7.302434144714904651e-02 9.724007299461577547e-02 +7.402467489163054581e-02 9.766367912225072667e-02 +7.502500833611204512e-02 9.806607711225102941e-02 +7.602534178059353054e-02 9.844737108547679805e-02 +7.702567522507502984e-02 9.880766980431070212e-02 +7.802600866955652914e-02 9.914708666512951629e-02 +7.902634211403801456e-02 9.946573968470928673e-02 +8.002667555851951386e-02 9.976375148086022437e-02 +8.102700900300099929e-02 1.000412492475692139e-01 +8.202734244748249859e-02 1.002983647249140325e-01 +8.302767589196399789e-02 1.005352341639970393e-01 +8.402800933644548331e-02 1.007519982871356856e-01 +8.502834278092698261e-02 1.009488022435336141e-01 +8.602867622540846804e-02 1.011257955606465336e-01 +8.702900966988996734e-02 1.012831320914471950e-01 +8.802934311437146664e-02 1.014209699577842405e-01 +8.902967655885295206e-02 1.015394714900229312e-01 +9.003001000333445136e-02 1.016388031631467875e-01 +9.103034344781593679e-02 1.017191355294932709e-01 +9.203067689229743609e-02 1.017806431482901341e-01 +9.303101033677893539e-02 1.018235045121531485e-01 +9.403134378126042081e-02 1.018479019707004690e-01 +9.503167722574192011e-02 1.018540216514340763e-01 +9.603201067022340554e-02 1.018420533780340365e-01 +9.703234411470490484e-02 1.018121905862071075e-01 +9.803267755918640414e-02 1.017646302372268441e-01 +9.903301100366788956e-02 1.016995727292992480e-01 +1.000333444481493889e-01 1.016172218068835259e-01 +1.010336778926308882e-01 1.015177844680955344e-01 +1.020340113371123736e-01 1.014014708703166734e-01 +1.030343447815938729e-01 1.012684942341296745e-01 +1.040346782260753583e-01 1.011190707456988830e-01 +1.050350116705568576e-01 1.009534194577100252e-01 +1.060353451150383569e-01 1.007717621889824972e-01 +1.070356785595198423e-01 1.005743234228641814e-01 +1.080360120040013416e-01 1.003613302045164024e-01 +1.090363454484828271e-01 1.001330120371949073e-01 +1.100366788929643264e-01 9.988960077763010814e-02 +1.110370123374458257e-01 9.963133053060750643e-02 +1.120373457819273111e-01 9.935843754284783003e-02 +1.130376792264088104e-01 9.907116009628372244e-02 +1.140380126708902958e-01 9.876973840082794998e-02 +1.150383461153717951e-01 9.845441448672652418e-02 +1.160386795598532944e-01 9.812543209658800003e-02 +1.170390130043347798e-01 9.778303657717778152e-02 +1.180393464488162791e-01 9.742747477106536436e-02 +1.190396798932977784e-01 9.705899490821023878e-02 +1.200400133377792639e-01 9.667784649756937243e-02 +1.210403467822607632e-01 9.628428021880934551e-02 +1.220406802267422486e-01 9.587854781420244998e-02 +1.230410136712237479e-01 9.546090198078546751e-02 +1.240413471157052472e-01 9.503159626285755113e-02 +1.250416805601867465e-01 9.459088494489202581e-02 +1.260420140046682180e-01 9.413902294493498024e-02 +1.270423474491497173e-01 9.367626570856205093e-02 +1.280426808936312166e-01 9.320286910346256559e-02 +1.290430143381127159e-01 9.271908931471874171e-02 +1.300433477825942152e-01 9.222518274084594292e-02 +1.310436812270756868e-01 9.172140589065790051e-02 +1.320440146715571861e-01 9.120801528101893374e-02 +1.330443481160386854e-01 9.068526733554439778e-02 +1.340446815605201847e-01 9.015341828430720184e-02 +1.350450150050016840e-01 8.961272406460790707e-02 +1.360453484494831555e-01 8.906344022286323547e-02 +1.370456818939646548e-01 8.850582181766637746e-02 +1.380460153384461541e-01 8.794012332407093180e-02 +1.390463487829276534e-01 8.736659853914753582e-02 +1.400466822274091527e-01 8.678550048886200796e-02 +1.410470156718906243e-01 8.619708133632057734e-02 +1.420473491163721236e-01 8.560159229142663695e-02 +1.430476825608536229e-01 8.499928352199190296e-02 +1.440480160053351222e-01 8.439040406634253089e-02 +1.450483494498166215e-01 8.377520174745926862e-02 +1.460486828942980930e-01 8.315392308868906068e-02 +1.470490163387795923e-01 8.252681323106297873e-02 +1.480493497832610916e-01 8.189411585225533918e-02 +1.490496832277425909e-01 8.125607308721458089e-02 +1.500500166722240902e-01 8.061292545049721092e-02 +1.510503501167055895e-01 7.996491176033238002e-02 +1.520506835611870611e-01 7.931226906444495406e-02 +1.530510170056685604e-01 7.865523256766018823e-02 +1.540513504501500597e-01 7.799403556131585813e-02 +1.550516838946315590e-01 7.732890935450072167e-02 +1.560520173391130583e-01 7.666008320714140511e-02 +1.570523507835945298e-01 7.598778426495425564e-02 +1.580526842280760291e-01 7.531223749628007669e-02 +1.590530176725575284e-01 7.463366563081548499e-02 +1.600533511170390277e-01 7.395228910025516966e-02 +1.610536845615205270e-01 7.326832598085566972e-02 +1.620540180060019986e-01 7.258199193793200832e-02 +1.630543514504834979e-01 7.189350017229435841e-02 +1.640546848949649972e-01 7.120306136863255309e-02 +1.650550183394464965e-01 7.051088364585393620e-02 +1.660553517839279958e-01 6.981717250937757857e-02 +1.670556852284094673e-01 6.912213080538835708e-02 +1.680560186728909666e-01 6.842595867705085488e-02 +1.690563521173724659e-01 6.772885352268379067e-02 +1.700566855618539652e-01 6.703100995589154898e-02 +1.710570190063354645e-01 6.633261976765110479e-02 +1.720573524508169361e-01 6.563387189034837477e-02 +1.730576858952984354e-01 6.493495236375901314e-02 +1.740580193397799347e-01 6.423604430296553369e-02 +1.750583527842614340e-01 6.353732786820311118e-02 +1.760586862287429333e-01 6.283898023662291832e-02 +1.770590196732244048e-01 6.214117557596359614e-02 +1.780593531177059041e-01 6.144408502011700773e-02 +1.790596865621874034e-01 6.074787664657599340e-02 +1.800600200066689027e-01 6.005271545574924347e-02 +1.810603534511504020e-01 5.935876335212712795e-02 +1.820606868956318736e-01 5.866617912728296080e-02 +1.830610203401133729e-01 5.797511844469033226e-02 +1.840613537845948722e-01 5.728573382633962013e-02 +1.850616872290763715e-01 5.659817464113196805e-02 +1.860620206735578708e-01 5.591258709503194035e-02 +1.870623541180393423e-01 5.522911422295555262e-02 +1.880626875625208416e-01 5.454789588237272252e-02 +1.890630210070023409e-01 5.386906874859959810e-02 +1.900633544514838402e-01 5.319276631175769154e-02 +1.910636878959653395e-01 5.251911887537379781e-02 +1.920640213404468111e-01 5.184825355659636315e-02 +1.930643547849283104e-01 5.118029428800065228e-02 +1.940646882294098097e-01 5.051536182095666544e-02 +1.950650216738913090e-01 4.985357373053179303e-02 +1.960653551183728083e-01 4.919504442189957805e-02 +1.970656885628543076e-01 4.853988513822649065e-02 +1.980660220073357791e-01 4.788820397000642659e-02 +1.990663554518172784e-01 4.724010586581389726e-02 +2.000666888962987777e-01 4.659569264444474862e-02 +2.010670223407802770e-01 4.595506300841426667e-02 +2.020673557852617763e-01 4.531831255878059073e-02 +2.030676892297432479e-01 4.468553381126293134e-02 +2.040680226742247472e-01 4.405681621362123046e-02 +2.050683561187062465e-01 4.343224616426647616e-02 +2.060686895631877458e-01 4.281190703206767117e-02 +2.070690230076692451e-01 4.219587917732381399e-02 +2.080693564521507166e-01 4.158423997386682947e-02 +2.090696898966322159e-01 4.097706383226299570e-02 +2.100700233411137152e-01 4.037442222407907483e-02 +2.110703567855952145e-01 3.977638370717944544e-02 +2.120706902300767138e-01 3.918301395202103410e-02 +2.130710236745581854e-01 3.859437576891183719e-02 +2.140713571190396847e-01 3.801052913619963614e-02 +2.150716905635211840e-01 3.743153122935680144e-02 +2.160720240080026833e-01 3.685743645092765669e-02 +2.170723574524841826e-01 3.628829646130429798e-02 +2.180726908969656541e-01 3.572416021029763133e-02 +2.190730243414471534e-01 3.516507396946935010e-02 +2.200733577859286527e-01 3.461108136519214923e-02 +2.210736912304101520e-01 3.406222341240397466e-02 +2.220740246748916513e-01 3.351853854902373669e-02 +2.230743581193731229e-01 3.298006267099493766e-02 +2.240746915638546222e-01 3.244682916792471922e-02 +2.250750250083361215e-01 3.191886895928555679e-02 +2.260753584528176208e-01 3.139621053114716176e-02 +2.270756918972991201e-01 3.087887997340664145e-02 +2.280760253417805916e-01 3.036690101748483819e-02 +2.290763587862620909e-01 2.986029507445768505e-02 +2.300766922307435902e-01 2.935908127359098882e-02 +2.310770256752250895e-01 2.886327650124803634e-02 +2.320773591197065888e-01 2.837289544013933337e-02 +2.330776925641880604e-01 2.788795060888466654e-02 +2.340780260086695597e-01 2.740845240185714807e-02 +2.350783594531510590e-01 2.693440912928074527e-02 +2.360786928976325583e-01 2.646582705755158235e-02 +2.370790263421140576e-01 2.600271044975503709e-02 +2.380793597865955569e-01 2.554506160635030068e-02 +2.390796932310770284e-01 2.509288090599527329e-02 +2.400800266755585277e-01 2.464616684648402237e-02 +2.410803601200400270e-01 2.420491608577078624e-02 +2.420806935645215263e-01 2.376912348305424208e-02 +2.430810270090030256e-01 2.333878213989607972e-02 +2.440813604534844972e-01 2.291388344134947727e-02 +2.450816938979659965e-01 2.249441709707195036e-02 +2.460820273424474958e-01 2.208037118239969399e-02 +2.470823607869289951e-01 2.167173217935863816e-02 +2.480826942314104944e-01 2.126848501759042234e-02 +2.490830276758919659e-01 2.087061311516989759e-02 +2.500833611203734930e-01 2.047809841929303792e-02 +2.510836945648549645e-01 2.009092144681351444e-02 +2.520840280093364361e-01 1.970906132460733054e-02 +2.530843614538179631e-01 1.933249582974543371e-02 +2.540846948982994347e-01 1.896120142945458692e-02 +2.550850283427809617e-01 1.859515332084752184e-02 +2.560853617872624333e-01 1.823432547040402044e-02 +2.570856952317439048e-01 1.787869065318491851e-02 +2.580860286762254319e-01 1.752822049176178121e-02 +2.590863621207069034e-01 1.718288549484581901e-02 +2.600866955651884305e-01 1.684265509559929389e-02 +2.610870290096699020e-01 1.650749768961479180e-02 +2.620873624541513736e-01 1.617738067254628070e-02 +2.630876958986329006e-01 1.585227047737868336e-02 +2.640880293431143722e-01 1.553213261132139339e-02 +2.650883627875958992e-01 1.521693169231268759e-02 +2.660886962320773708e-01 1.490663148512258625e-02 +2.670890296765588423e-01 1.460119493704171816e-02 +2.680893631210403694e-01 1.430058421314490263e-02 +2.690896965655218409e-01 1.400476073111841559e-02 +2.700900300100033680e-01 1.371368519564033703e-02 +2.710903634544848395e-01 1.342731763230437490e-02 +2.720906968989663111e-01 1.314561742107763984e-02 +2.730910303434478381e-01 1.286854332928351850e-02 +2.740913637879293097e-01 1.259605354410179255e-02 +2.750916972324108367e-01 1.232810570457750637e-02 +2.760920306768923083e-01 1.206465693313228324e-02 +2.770923641213737798e-01 1.180566386657050729e-02 +2.780926975658553069e-01 1.155108268657487926e-02 +2.790930310103367784e-01 1.130086914968518597e-02 +2.800933644548183055e-01 1.105497861675525106e-02 +2.810936978992997770e-01 1.081336608188333305e-02 +2.820940313437812486e-01 1.057598620081165973e-02 +2.830943647882627756e-01 1.034279331879137641e-02 +2.840946982327442472e-01 1.011374149790954594e-02 +2.850950316772257742e-01 9.888784543875258487e-03 +2.860953651217072458e-01 9.667876032262550864e-03 +2.870956985661887173e-01 9.450969334207961689e-03 +2.880960320106702444e-01 9.238017641561225429e-03 +2.890963654551517159e-01 9.028973991488040141e-03 +2.900966988996332430e-01 8.823791290523746653e-03 +2.910970323441147145e-01 8.622422338078144718e-03 +2.920973657885961861e-01 8.424819849390755705e-03 +2.930976992330777131e-01 8.230936477937581333e-03 +2.940980326775591847e-01 8.040724837289606533e-03 +2.950983661220407117e-01 7.854137522424354395e-03 +2.960986995665221833e-01 7.671127130491982478e-03 +2.970990330110036548e-01 7.491646281037529428e-03 +2.980993664554851819e-01 7.315647635681610424e-03 +2.990996998999666534e-01 7.143083917261813971e-03 +3.001000333444481805e-01 6.973907928437627669e-03 +3.011003667889296520e-01 6.808072569761871445e-03 +3.021007002334111791e-01 6.645530857221920371e-03 +3.031010336778926506e-01 6.486235939254444971e-03 +3.041013671223741222e-01 6.330141113237142819e-03 +3.051017005668556492e-01 6.177199841462057556e-03 +3.061020340113371208e-01 6.027365766594480409e-03 +3.071023674558186478e-01 5.880592726622047711e-03 +3.081027009003001194e-01 5.736834769299013065e-03 +3.091030343447815909e-01 5.596046166090448172e-03 +3.101033677892631180e-01 5.458181425621947726e-03 +3.111037012337445895e-01 5.323195306639871664e-03 +3.121040346782261166e-01 5.191042830488000297e-03 +3.131043681227075881e-01 5.061679293106330357e-03 +3.141047015671890597e-01 4.935060276557910047e-03 +3.151050350116705867e-01 4.811141660089832302e-03 +3.161053684561520583e-01 4.689879630734757922e-03 +3.171057019006335853e-01 4.571230693459184084e-03 +3.181060353451150569e-01 4.455151680865254010e-03 +3.191063687895965284e-01 4.341599762452411776e-03 +3.201067022340780555e-01 4.230532453446064940e-03 +3.211070356785595270e-01 4.121907623199885969e-03 +3.221073691230410541e-01 4.015683503178773747e-03 +3.231077025675225256e-01 3.911818694529623115e-03 +3.241080360120039972e-01 3.810272175246941055e-03 +3.251083694564855242e-01 3.711003306940715511e-03 +3.261087029009669958e-01 3.613971841213595420e-03 +3.271090363454485228e-01 3.519137925654898958e-03 +3.281093697899299944e-01 3.426462109458868095e-03 +3.291097032344114659e-01 3.335905348674498682e-03 +3.301100366788929930e-01 3.247429011094510320e-03 +3.311103701233744645e-01 3.160994880791012044e-03 +3.321107035678559916e-01 3.076565162305286254e-03 +3.331110370123374631e-01 2.994102484499428646e-03 +3.341113704568189346e-01 2.913569904077187214e-03 +3.351117039013004617e-01 2.834930908781813953e-03 +3.361120373457819333e-01 2.758149420278344782e-03 +3.371123707902634603e-01 2.683189796727949994e-03 +3.381127042347449319e-01 2.610016835061861398e-03 +3.391130376792264034e-01 2.538595772962481609e-03 +3.401133711237079305e-01 2.468892290559157772e-03 +3.411137045681894020e-01 2.400872511846096832e-03 +3.421140380126709291e-01 2.334503005829877739e-03 +3.431143714571524006e-01 2.269750787414048961e-03 +3.441147049016338721e-01 2.206583318028085446e-03 +3.451150383461153992e-01 2.144968506008075419e-03 +3.461153717905968707e-01 2.084874706736484454e-03 +3.471157052350783978e-01 2.026270722548067105e-03 +3.481160386795598694e-01 1.969125802409296081e-03 +3.491163721240413409e-01 1.913409641378168487e-03 +3.501167055685228680e-01 1.859092379851652209e-03 +3.511170390130043395e-01 1.806144602607605721e-03 +3.521173724574858666e-01 1.754537337648090500e-03 +3.531177059019673381e-01 1.704242054850913001e-03 +3.541180393464488096e-01 1.655230664436101564e-03 +3.551183727909303367e-01 1.607475515254037490e-03 +3.561187062354118082e-01 1.560949392901732477e-03 +3.571190396798933353e-01 1.515625517673775853e-03 +3.581193731243748068e-01 1.471477542354389378e-03 +3.591197065688562784e-01 1.428479549856815104e-03 +3.601200400133378055e-01 1.386606050716292140e-03 +3.611203734578192770e-01 1.345831980442766494e-03 +3.621207069023008041e-01 1.306132696739277163e-03 +3.631210403467822756e-01 1.267483976592049220e-03 +3.641213737912637471e-01 1.229862013237978174e-03 +3.651217072357452742e-01 1.193243413015370033e-03 +3.661220406802267457e-01 1.157605192103461746e-03 +3.671223741247082728e-01 1.122924773156262724e-03 +3.681227075691897443e-01 1.089179981836140834e-03 +3.691230410136712159e-01 1.056349043252433149e-03 +3.701233744581527429e-01 1.024410578310328782e-03 +3.711237079026342145e-01 9.933435999750594818e-04 +3.721240413471157416e-01 9.631275094564119895e-04 +3.731243747915972131e-01 9.337420923184676412e-04 +3.741247082360786846e-01 9.051675145192915545e-04 +3.751250416805602117e-01 8.773843183852553761e-04 +3.761253751250416832e-01 8.503734185245843177e-04 +3.771257085695232103e-01 8.241160976845041033e-04 +3.781260420140046818e-01 7.985940025564136494e-04 +3.791263754584861534e-01 7.737891395332304433e-04 +3.801267089029676804e-01 7.496838704231076258e-04 +3.811270423474491520e-01 7.262609081235029936e-04 +3.821273757919306790e-01 7.035033122595123721e-04 +3.831277092364121506e-01 6.813944847902941599e-04 +3.841280426808936221e-01 6.599181655872568032e-04 +3.851283761253751492e-01 6.390584279876472410e-04 +3.861287095698566207e-01 6.187996743269864571e-04 +3.871290430143381478e-01 5.991266314537698704e-04 +3.881293764588196193e-01 5.800243462297252845e-04 +3.891297099033011464e-01 5.614781810187848346e-04 +3.901300433477826179e-01 5.434738091678875801e-04 +3.911303767922640895e-01 5.259972104825754325e-04 +3.921307102367456165e-01 5.090346667002874307e-04 +3.931310436812270881e-01 4.925727569641421243e-04 +3.941313771257086152e-01 4.765983532998714070e-04 +3.951317105701900867e-01 4.610986160985657825e-04 +3.961320440146715582e-01 4.460609896076720890e-04 +3.971323774591530853e-01 4.314731974327123032e-04 +3.981327109036345568e-01 4.173232380520077280e-04 +3.991330443481160839e-01 4.035993803466622967e-04 +4.001333777925975554e-01 3.902901591479520922e-04 +4.011337112370790270e-01 3.773843708041693017e-04 +4.021340446815605540e-01 3.648710687688945064e-04 +4.031343781260420256e-01 3.527395592126039833e-04 +4.041347115705235526e-01 3.409793966593979166e-04 +4.051350450150050242e-01 3.295803796505891769e-04 +4.061353784594864957e-01 3.185325464367942021e-04 +4.071357119039680228e-01 3.078261707000994312e-04 +4.081360453484494943e-01 2.974517573078008965e-04 +4.091363787929310214e-01 2.874000380991116404e-04 +4.101367122374124929e-01 2.776619677062211553e-04 +4.111370456818939645e-01 2.682287194109427312e-04 +4.121373791263754915e-01 2.590916810381805788e-04 +4.131377125708569631e-01 2.502424508873330789e-04 +4.141380460153384901e-01 2.416728337027099899e-04 +4.151383794598199617e-01 2.333748366839687226e-04 +4.161387129043014332e-01 2.253406655375015007e-04 +4.171390463487829603e-01 2.175627205696544831e-04 +4.181393797932644318e-01 2.100335928226009180e-04 +4.191397132377459589e-01 2.027460602536173217e-04 +4.201400466822274304e-01 1.956930839584697997e-04 +4.211403801267089020e-01 1.888678044395513279e-04 +4.221407135711904290e-01 1.822635379193639454e-04 +4.231410470156719006e-01 1.758737726998849539e-04 +4.241413804601534276e-01 1.696921655682914591e-04 +4.251417139046348992e-01 1.637125382495006008e-04 +4.261420473491163707e-01 1.579288739058943149e-04 +4.271423807935978978e-01 1.523353136845834058e-04 +4.281427142380793693e-01 1.469261533125019309e-04 +4.291430476825608964e-01 1.416958397395839443e-04 +4.301433811270423679e-01 1.366389678302395506e-04 +4.311437145715238395e-01 1.317502771032898353e-04 +4.321440480160053665e-01 1.270246485204990980e-04 +4.331443814604868381e-01 1.224571013237938343e-04 +4.341447149049683651e-01 1.180427899212197764e-04 +4.351450483494498367e-01 1.137770008216618762e-04 +4.361453817939313082e-01 1.096551496183085394e-04 +4.371457152384128353e-01 1.056727780208162299e-04 +4.381460486828943068e-01 1.018255509360938466e-04 +4.391463821273758339e-01 9.810925359759418291e-05 +4.401467155718573054e-01 9.451978874298304367e-05 +4.411470490163387770e-01 9.105317384000944194e-05 +4.421473824608203040e-01 8.770553836039391793e-05 +4.431477159053017756e-01 8.447312110150836755e-05 +4.441480493497833026e-01 8.135226755561113308e-05 +4.451483827942647742e-01 7.833942732636999179e-05 +4.461487162387462457e-01 7.543115159238238132e-05 +4.471490496832277728e-01 7.262409061738856701e-05 +4.481493831277092443e-01 6.991499130684727286e-05 +4.491497165721907714e-01 6.730069481052405303e-05 +4.501500500166722429e-01 6.477813417073159500e-05 +4.511503834611537145e-01 6.234433201583393206e-05 +4.521507169056352415e-01 5.999639829862087764e-05 +4.531510503501167131e-01 5.773152807913816396e-05 +4.541513837945982401e-01 5.554699935154134568e-05 +4.551517172390797117e-01 5.344017091453988241e-05 +4.561520506835611832e-01 5.140848028497227445e-05 +4.571523841280427103e-01 4.944944165405045617e-05 +4.581527175725241818e-01 4.756064388579687909e-05 +4.591530510170057089e-01 4.573974855718859615e-05 +4.601533844614871804e-01 4.398448803951509592e-05 +4.611537179059686520e-01 4.229266362044548230e-05 +4.621540513504501790e-01 4.066214366629433344e-05 +4.631543847949316506e-01 3.909086182396980072e-05 +4.641547182394131776e-01 3.757681526207855684e-05 +4.651550516838946492e-01 3.611806295065843461e-05 +4.661553851283761207e-01 3.471272397900390606e-05 +4.671557185728576478e-01 3.335897591104560746e-05 +4.681560520173391193e-01 3.205505317774137556e-05 +4.691563854618206464e-01 3.079924550593036493e-05 +4.701567189063021179e-01 2.958989638310512645e-05 +4.711570523507836450e-01 2.842540155754692179e-05 +4.721573857952651165e-01 2.730420757327465094e-05 +4.731577192397465881e-01 2.622481033925121087e-05 +4.741580526842281151e-01 2.518575373229441658e-05 +4.751583861287095867e-01 2.418562823313749163e-05 +4.761587195731911137e-01 2.322306959508404386e-05 +4.771590530176725853e-01 2.229675754470423406e-05 +4.781593864621540568e-01 2.140541451401905908e-05 +4.791597199066355839e-01 2.054780440362209996e-05 +4.801600533511170554e-01 1.972273137618793667e-05 +4.811603867955985825e-01 1.892903867982041893e-05 +4.821607202400800540e-01 1.816560750069652014e-05 +4.831610536845615256e-01 1.743135584446193874e-05 +4.841613871290430526e-01 1.672523744584051986e-05 +4.851617205735245242e-01 1.604624070592162389e-05 +4.861620540180060512e-01 1.539338765659267825e-05 +4.871623874624875228e-01 1.476573295158967525e-05 +4.881627209069689943e-01 1.416236288364007936e-05 +4.891630543514505214e-01 1.358239442718003767e-05 +4.901633877959319929e-01 1.302497430612978958e-05 +4.911637212404135200e-01 1.248927808621690939e-05 +4.921640546848949915e-01 1.197450929134233875e-05 +4.931643881293764631e-01 1.147989854348860364e-05 +4.941647215738579901e-01 1.100470272567595887e-05 +4.951650550183394617e-01 1.054820416747601774e-05 +4.961653884628209887e-01 1.010970985259962279e-05 +4.971657219073024603e-01 9.688550648081097045e-06 +4.981660553517839318e-01 9.284080554585833557e-06 +4.991663887962654589e-01 8.895675977375519151e-06 +5.001667222407469859e-01 8.522735017470760603e-06 +5.011670556852284575e-01 8.164676782556840940e-06 +5.021673891297099290e-01 7.820940717184903215e-06 +5.031677225741914006e-01 7.490985951827106823e-06 +5.041680560186728721e-01 7.174290670350655021e-06 +5.051683894631544547e-01 6.870351495482126308e-06 +5.061687229076359262e-01 6.578682891839673173e-06 +5.071690563521173978e-01 6.298816586117576189e-06 +5.081693897965988693e-01 6.030301004014262694e-06 +5.091697232410803409e-01 5.772700723501136023e-06 +5.101700566855619234e-01 5.525595944036009684e-06 +5.111703901300433950e-01 5.288581971332632799e-06 +5.121707235745248665e-01 5.061268717302693215e-06 +5.131710570190063381e-01 4.843280214795237178e-06 +5.141713904634878096e-01 4.634254146763277629e-06 +5.151717239079693922e-01 4.433841389495400679e-06 +5.161720573524508637e-01 4.241705569555868505e-06 +5.171723907969323353e-01 4.057522634083560070e-06 +5.181727242414138068e-01 3.880980434106854636e-06 +5.191730576858952784e-01 3.711778320537789161e-06 +5.201733911303768609e-01 3.549626752515443185e-06 +5.211737245748583325e-01 3.394246917774928298e-06 +5.221740580193398040e-01 3.245370364724588776e-06 +5.231743914638212756e-01 3.102738645920883484e-06 +5.241747249083027471e-01 2.966102972636140430e-06 +5.251750583527843297e-01 2.835223880220847951e-06 +5.261753917972658012e-01 2.709870903968664442e-06 +5.271757252417472728e-01 2.589822265197782137e-06 +5.281760586862287443e-01 2.474864567269229545e-06 +5.291763921307102159e-01 2.364792501267864013e-06 +5.301767255751917984e-01 2.259408561078454681e-06 +5.311770590196732700e-01 2.158522767594723286e-06 +5.321773924641547415e-01 2.061952401805122113e-06 +5.331777259086362131e-01 1.969521746505025876e-06 +5.341780593531176846e-01 1.881061836390529332e-06 +5.351783927975992672e-01 1.796410216294697446e-06 +5.361787262420807387e-01 1.715410707332647100e-06 +5.371790596865622103e-01 1.637913180727230276e-06 +5.381793931310436818e-01 1.563773339092628898e-06 +5.391797265755251534e-01 1.492852504958282262e-06 +5.401800600200067359e-01 1.425017416320759712e-06 +5.411803934644882075e-01 1.360140029016649863e-06 +5.421807269089696790e-01 1.298097325714050516e-06 +5.431810603534511506e-01 1.238771131325804335e-06 +5.441813937979326221e-01 1.182047934652005619e-06 +5.451817272424142047e-01 1.127818716064465293e-06 +5.461820606868956762e-01 1.075978781050387187e-06 +5.471823941313771478e-01 1.026427599437084551e-06 +5.481827275758586193e-01 9.790686501243298962e-07 +5.491830610203400909e-01 9.338092711552381878e-07 +5.501833944648216734e-01 8.905605149610231607e-07 +5.511837279093031450e-01 8.492370086192936858e-07 +5.521840613537846165e-01 8.097568189697465454e-07 +5.531843947982660881e-01 7.720413224353442841e-07 +5.541847282427475596e-01 7.360150794010282637e-07 +5.551850616872291422e-01 7.016057130060342169e-07 +5.561853951317106137e-01 6.687437922098608657e-07 +5.571857285761920853e-01 6.373627189955882297e-07 +5.581860620206735568e-01 6.073986195782287017e-07 +5.591863954651550284e-01 5.787902394891982969e-07 +5.601867289096366109e-01 5.514788424117954902e-07 +5.611870623541180825e-01 5.254081126459770493e-07 +5.621873957985995540e-01 5.005240610841315992e-07 +5.631877292430810256e-01 4.767749345830085472e-07 +5.641880626875624971e-01 4.541111286201585044e-07 +5.651883961320440797e-01 4.324851031264678962e-07 +5.661887295765255512e-01 4.118513013895196745e-07 +5.671890630210070228e-01 3.921660719255447872e-07 +5.681893964654884943e-01 3.733875932207748900e-07 +5.691897299099699659e-01 3.554758012458595374e-07 +5.701900633544515484e-01 3.383923196498796192e-07 +5.711903967989330200e-01 3.221003925433339410e-07 +5.721907302434144915e-01 3.065648197820600387e-07 +5.731910636878959631e-01 2.917518946668519301e-07 +5.741913971323774346e-01 2.776293439759753741e-07 +5.751917305768590172e-01 2.641662702504049997e-07 +5.761920640213404887e-01 2.513330962540068329e-07 +5.771923974658219603e-01 2.391015115332811362e-07 +5.781927309103034318e-01 2.274444210036393302e-07 +5.791930643547849034e-01 2.163358954914386977e-07 +5.801933977992664859e-01 2.057511241632046411e-07 +5.811937312437479575e-01 1.956663687756361809e-07 +5.821940646882294290e-01 1.860589196820685255e-07 +5.831943981327109006e-01 1.769070535331357893e-07 +5.841947315771923721e-01 1.681899926113244271e-07 +5.851950650216739547e-01 1.598878657410580037e-07 +5.861953984661554262e-01 1.519816707178434140e-07 +5.871957319106368978e-01 1.444532382017966459e-07 +5.881960653551183693e-01 1.372851970226947669e-07 +5.891963987995998409e-01 1.304609408453732525e-07 +5.901967322440814234e-01 1.239645961460043683e-07 +5.911970656885628950e-01 1.177809914514072914e-07 +5.921973991330443665e-01 1.118956277951124432e-07 +5.931977325775258381e-01 1.062946503454694293e-07 +5.941980660220073096e-01 1.009648211625552744e-07 +5.951983994664888922e-01 9.589349304210169202e-08 +5.961987329109703637e-01 9.106858440606664810e-08 +5.971990663554518353e-01 8.647855520083717973e-08 +5.981993997999333068e-01 8.211238376538846122e-08 +5.991997332444148894e-01 7.795954463299409227e-08 +6.002000666888963609e-01 7.400998723134239772e-08 +6.012004001333778325e-01 7.025411544711496562e-08 +6.022007335778593040e-01 6.668276802227189204e-08 +6.032010670223407756e-01 6.328719975040051374e-08 +6.042014004668223581e-01 6.005906344261587435e-08 +6.052017339113038297e-01 5.699039263355009379e-08 +6.062020673557853012e-01 5.407358499900032417e-08 +6.072024008002667728e-01 5.130138645783588081e-08 +6.082027342447482443e-01 4.866687593169840821e-08 +6.092030676892298269e-01 4.616345073699761587e-08 +6.102034011337112984e-01 4.378481258460947204e-08 +6.112037345781927700e-01 4.152495416355149554e-08 +6.122040680226742415e-01 3.937814628579086576e-08 +6.132044014671557131e-01 3.733892557013798958e-08 +6.142047349116372956e-01 3.540208264399671587e-08 +6.152050683561187672e-01 3.356265084251151361e-08 +6.162054018006002387e-01 3.181589538538990081e-08 +6.172057352450817103e-01 3.015730301241842014e-08 +6.182060686895631818e-01 2.858257205937661037e-08 +6.192064021340447644e-01 2.708760295673333200e-08 +6.202067355785262359e-01 2.566848913416477352e-08 +6.212070690230077075e-01 2.432150831456535830e-08 +6.222074024674891790e-01 2.304311418182835398e-08 +6.232077359119706506e-01 2.182992840727008174e-08 +6.242080693564522331e-01 2.067873302013279558e-08 +6.252084028009337047e-01 1.958646310815804352e-08 +6.262087362454151762e-01 1.855019983474982925e-08 +6.272090696898966478e-01 1.756716375976104505e-08 +6.282094031343781193e-01 1.663470845142991100e-08 +6.292097365788597019e-01 1.575031437747474998e-08 +6.302100700233411734e-01 1.491158306381095001e-08 +6.312104034678226450e-01 1.411623150980384536e-08 +6.322107369123041165e-01 1.336208684939775745e-08 +6.332110703567855881e-01 1.264708124787480024e-08 +6.342114038012671706e-01 1.196924702439986980e-08 +6.352117372457486422e-01 1.132671199088810953e-08 +6.362120706902301137e-01 1.071769499810524765e-08 +6.372124041347115853e-01 1.014050168027053430e-08 +6.382127375791930568e-01 9.593520389771960425e-09 +6.392130710236746394e-01 9.075218313939920955e-09 +6.402134044681561109e-01 8.584137766144669038e-09 +6.412137379126375825e-01 8.118892643788538124e-09 +6.422140713571190540e-01 7.678165046066231591e-09 +6.432144048016005256e-01 7.260702044647058443e-09 +6.442147382460821081e-01 6.865312600711889984e-09 +6.452150716905635797e-01 6.490864622042707183e-09 +6.462154051350450512e-01 6.136282154115159951e-09 +6.472157385795265228e-01 5.800542699393906507e-09 +6.482160720240079943e-01 5.482674659265076213e-09 +6.492164054684895769e-01 5.181754893267168501e-09 +6.502167389129710484e-01 4.896906390502052357e-09 +6.512170723574525200e-01 4.627296048317843767e-09 +6.522174058019339915e-01 4.372132553557058059e-09 +6.532177392464154631e-01 4.130664361860029207e-09 +6.542180726908970456e-01 3.902177770698605368e-09 +6.552184061353785172e-01 3.685995081997093889e-09 +6.562187395798599887e-01 3.481472850369440422e-09 +6.572190730243414603e-01 3.288000213168039422e-09 +6.582194064688229318e-01 3.104997298699708781e-09 +6.592197399133045144e-01 2.931913709117727758e-09 +6.602200733577859859e-01 2.768227074646082464e-09 +6.612204068022674575e-01 2.613441675934257633e-09 +6.622207402467489290e-01 2.467087131476612770e-09 +6.632210736912304005e-01 2.328717147161107990e-09 +6.642214071357119831e-01 2.197908325137765893e-09 +6.652217405801934547e-01 2.074259029317522901e-09 +6.662220740246749262e-01 1.957388304927489138e-09 +6.672224074691563978e-01 1.846934849660408492e-09 +6.682227409136378693e-01 1.742556034061480511e-09 +6.692230743581194519e-01 1.643926968898497422e-09 +6.702234078026009234e-01 1.550739617359437576e-09 +6.712237412470823950e-01 1.462701950014610001e-09 +6.722240746915638665e-01 1.379537140572047147e-09 +6.732244081360453380e-01 1.300982800539895670e-09 +6.742247415805269206e-01 1.226790250993216583e-09 +6.752250750250083922e-01 1.156723829722020386e-09 +6.762254084694898637e-01 1.090560232112978898e-09 +6.772257419139713353e-01 1.028087884190841276e-09 +6.782260753584528068e-01 9.691063463152150332e-10 +6.792264088029343894e-01 9.134257460952962016e-10 +6.802267422474158609e-01 8.608662391495389544e-10 +6.812270756918973325e-01 8.112574963986117204e-10 +6.822274091363788040e-01 7.644382166388099283e-10 +6.832277425808602755e-01 7.202556631997266750e-10 +6.842280760253418581e-01 6.785652235436124528e-10 +6.852284094698233297e-01 6.392299907158750146e-10 +6.862287429143048012e-01 6.021203656054500492e-10 +6.872290763587862727e-01 5.671136790212102853e-10 +6.882294098032677443e-01 5.340938326358096406e-10 +6.892297432477493269e-01 5.029509578918920978e-10 +6.902300766922307984e-01 4.735810920068289105e-10 +6.912304101367122700e-01 4.458858702520572919e-10 +6.922307435811937415e-01 4.197722337209197291e-10 +6.932310770256752130e-01 3.951521518352182567e-10 +6.942314104701567956e-01 3.719423588754716636e-10 +6.952317439146382672e-01 3.500641038529355280e-10 +6.962320773591197387e-01 3.294429130732242734e-10 +6.972324108036012102e-01 3.100083647717846984e-10 +6.982327442480826818e-01 2.916938752302315037e-10 +6.992330776925642644e-01 2.744364958104528336e-10 +7.002334111370457359e-01 2.581767203697787891e-10 +7.012337445815272075e-01 2.428583025456827694e-10 +7.022340780260086790e-01 2.284280824228418280e-10 +7.032344114704901505e-01 2.148358221182139937e-10 +7.042347449149717331e-01 2.020340498419605502e-10 +7.052350783594532047e-01 1.899779120130382108e-10 +7.062354118039346762e-01 1.786250330282862973e-10 +7.072357452484161477e-01 1.679353823031035079e-10 +7.082360786928976193e-01 1.578711482200420212e-10 +7.092364121373792019e-01 1.483966186390817820e-10 +7.102367455818606734e-01 1.394780676400597127e-10 +7.112370790263421449e-01 1.310836481835853315e-10 +7.122374124708236165e-01 1.231832903919313646e-10 +7.132377459153050880e-01 1.157486051658863449e-10 +7.142380793597866706e-01 1.087527928672992296e-10 +7.152384128042681422e-01 1.021705568102375679e-10 +7.162387462487496137e-01 9.597802131620932078e-11 +7.172390796932310852e-01 9.015265410085058473e-11 +7.182394131377125568e-01 8.467319277088815202e-11 +7.192397465821941394e-01 7.951957522105358268e-11 +7.202400800266756109e-01 7.467287373095960003e-11 +7.212404134711570824e-01 7.011523257184285616e-11 +7.222407469156385540e-01 6.582980894246047003e-11 +7.232410803601200255e-01 6.180071706238653323e-11 +7.242414138046016081e-01 5.801297525949494002e-11 +7.252417472490830797e-01 5.445245589652854517e-11 +7.262420806935645512e-01 5.110583798939026473e-11 +7.272424141380460227e-01 4.796056237718746047e-11 +7.282427475825274943e-01 4.500478931103394488e-11 +7.292430810270090769e-01 4.222735833532455676e-11 +7.302434144714905484e-01 3.961775034154666963e-11 +7.312437479159720199e-01 3.716605168072446678e-11 +7.322440813604534915e-01 3.486292022638284361e-11 +7.332444148049349630e-01 3.269955328536063974e-11 +7.342447482494165456e-01 3.066765725903739312e-11 +7.352450816938980171e-01 2.875941896249096529e-11 +7.362454151383794887e-01 2.696747851380712113e-11 +7.372457485828609602e-01 2.528490371026476770e-11 +7.382460820273424318e-01 2.370516581237300562e-11 +7.392464154718240144e-01 2.222211666079121158e-11 +7.402467489163054859e-01 2.082996705502427511e-11 +7.412470823607869574e-01 1.952326632644490860e-11 +7.422474158052684290e-01 1.829688304167668858e-11 +7.432477492497499005e-01 1.714598677568693582e-11 +7.442480826942314831e-01 1.606603089707608018e-11 +7.452484161387129546e-01 1.505273631104400362e-11 +7.462487495831944262e-01 1.410207610835049955e-11 +7.472490830276758977e-01 1.321026107128376519e-11 +7.482494164721573693e-01 1.237372599021072997e-11 +7.492497499166389519e-01 1.158911674671867744e-11 +7.502500833611204234e-01 1.085327812166227262e-11 +7.512504168056018949e-01 1.016324228862659892e-11 +7.522507502500833665e-01 9.516217955396830899e-12 +7.532510836945648380e-01 8.909580118000690643e-12 +7.542514171390464206e-01 8.340860393766566725e-12 +7.552517505835278921e-01 7.807737901617893798e-12 +7.562520840280093637e-01 7.308030659513446920e-12 +7.572524174724908352e-01 6.839687470547551382e-12 +7.582527509169723068e-01 6.400780270740095649e-12 +7.592530843614538894e-01 5.989496912990730664e-12 +7.602534178059353609e-01 5.604134363038697705e-12 +7.612537512504168324e-01 5.243092284563466553e-12 +7.622540846948983040e-01 4.904866991795423065e-12 +7.632544181393797755e-01 4.588045749166858673e-12 +7.642547515838613581e-01 4.291301398641030809e-12 +7.652550850283428296e-01 4.013387296404396716e-12 +7.662554184728243012e-01 3.753132541597661342e-12 +7.672557519173057727e-01 3.509437480704637155e-12 +7.682560853617872443e-01 3.281269472107392122e-12 +7.692564188062688268e-01 3.067658896160719995e-12 +7.702567522507502984e-01 2.867695396939694023e-12 +7.712570856952317699e-01 2.680524342571073207e-12 +7.722574191397132415e-01 2.505343491777033787e-12 +7.732577525841948241e-01 2.341399854939602342e-12 +7.742580860286762956e-01 2.187986738637158050e-12 +7.752584194731577671e-01 2.044440963213429012e-12 +7.762587529176392387e-01 1.910140243517240864e-12 +7.772590863621207102e-01 1.784500723495409492e-12 +7.782594198066022928e-01 1.666974655839108912e-12 +7.792597532510837643e-01 1.557048218372613887e-12 +7.802600866955652359e-01 1.454239459335214816e-12 +7.812604201400467074e-01 1.358096364146066154e-12 +7.822607535845281790e-01 1.268195036654385893e-12 +7.832610870290097616e-01 1.184137988269937423e-12 +7.842614204734912331e-01 1.105552528738927771e-12 +7.852617539179727046e-01 1.032089252680279723e-12 +7.862620873624541762e-01 9.634206163290581311e-13 +7.872624208069356477e-01 8.992395992465892009e-13 +7.882627542514172303e-01 8.392584460528017698e-13 +7.892630876958987018e-01 7.832074835162505258e-13 +7.902634211403801734e-01 7.308340086017351845e-13 +7.912637545848616449e-01 6.819012433251967753e-13 +7.922640880293431165e-01 6.361873525019086595e-13 +7.932644214738246990e-01 5.934845206968477205e-13 +7.942647549183061706e-01 5.535980848971328096e-13 +7.952650883627876421e-01 5.163457196253597115e-13 +7.962654218072691137e-01 4.815566714007519873e-13 +7.972657552517505852e-01 4.490710396325925025e-13 +7.982660886962321678e-01 4.187391011981658428e-13 +7.992664221407136393e-01 3.904206761155981078e-13 +8.002667555851951109e-01 3.639845318716342280e-13 +8.012670890296765824e-01 3.393078241053293978e-13 +8.022674224741580540e-01 3.162755714818302201e-13 +8.032677559186396365e-01 2.947801627161787104e-13 +8.042680893631211081e-01 2.747208938254896474e-13 +8.052684228076025796e-01 2.560035337998564231e-13 +8.062687562520840512e-01 2.385399169879261821e-13 +8.072690896965655227e-01 2.222475605925122366e-13 +8.082694231410471053e-01 2.070493057656640567e-13 +8.092697565855285768e-01 1.928729808811578672e-13 +8.102700900300100484e-01 1.796510856458035899e-13 +8.112704234744915199e-01 1.673204947898856291e-13 +8.122707569189729915e-01 1.558221801511286320e-13 +8.132710903634545740e-01 1.451009500366817300e-13 +8.142714238079360456e-01 1.351052048135739832e-13 +8.152717572524175171e-01 1.257867077401976341e-13 +8.162720906968989887e-01 1.171003701100665157e-13 +8.172724241413804602e-01 1.090040498342490202e-13 +8.182727575858620428e-01 1.014583626408892503e-13 +8.192730910303435143e-01 9.442650511925543462e-14 +8.202734244748249859e-01 8.787408888190002299e-14 +8.212737579193064574e-01 8.176898516197440009e-14 +8.222740913637879290e-01 7.608117920371617069e-14 +8.232744248082695115e-01 7.078263384262360767e-14 +8.242747582527509831e-01 6.584716170817620383e-14 +8.252750916972324546e-01 6.125030551609978288e-14 +8.262754251417139262e-01 5.696922594934863886e-14 +8.272757585861953977e-01 5.298259665724562721e-14 +8.282760920306769803e-01 4.927050593071962725e-14 +8.292764254751584518e-01 4.581436463837543167e-14 +8.302767589196399234e-01 4.259682003336826824e-14 +8.312770923641213949e-01 3.960167506478144483e-14 +8.322774258086028665e-01 3.681381284952770860e-14 +8.332777592530844490e-01 3.421912598179973358e-14 +8.342780926975659206e-01 3.180445037683478502e-14 +8.352784261420473921e-01 2.955750336433126659e-14 +8.362787595865288637e-01 2.746682576432815698e-14 +8.372790930310103352e-01 2.552172769475539661e-14 +8.382794264754919178e-01 2.371223787531299409e-14 +8.392797599199733893e-01 2.202905620684018584e-14 +8.402800933644548609e-01 2.046350941896017522e-14 +8.412804268089363324e-01 1.900750959161851969e-14 +8.422807602534178040e-01 1.765351536815332612e-14 +8.432810936978993865e-01 1.639449568886232063e-14 +8.442814271423808581e-01 1.522389588465442398e-14 +8.452817605868623296e-01 1.413560598035286309e-14 +8.462820940313438012e-01 1.312393106660186574e-14 +8.472824274758252727e-01 1.218356360812880187e-14 +8.482827609203068553e-01 1.130955756438425734e-14 +8.492830943647883268e-01 1.049730420634874511e-14 +8.502834278092697984e-01 9.742509520583331782e-15 +8.512837612537512699e-01 9.041173098444486576e-15 +8.522840946982327415e-01 8.389568414807376592e-15 +8.532844281427143240e-01 7.784224406669961835e-15 +8.542847615871957956e-01 7.221908267667171221e-15 +8.552850950316772671e-01 6.699609379834124767e-15 +8.562854284761587387e-01 6.214524308936993235e-15 +8.572857619206402102e-01 5.764042794362781008e-15 +8.582860953651217928e-01 5.345734668943043277e-15 +8.592864288096032643e-01 4.957337648196196803e-15 +8.602867622540847359e-01 4.596745932329191046e-15 +8.612870956985662074e-01 4.261999567954953352e-15 +8.622874291430476790e-01 3.951274519870755381e-15 +8.632877625875292615e-01 3.662873406421364526e-15 +8.642880960320107331e-01 3.395216854950090103e-15 +8.652884294764922046e-01 3.146835436632104013e-15 +8.662887629209736762e-01 2.916362142603629039e-15 +8.672890963654551477e-01 2.702525365750855759e-15 +8.682894298099367303e-01 2.504142354821945881e-15 +8.692897632544182018e-01 2.320113109678933591e-15 +8.702900966988996734e-01 2.149414688521398856e-15 +8.712904301433811449e-01 1.991095899806262762e-15 +8.722907635878626165e-01 1.844272353355151115e-15 +8.732910970323441990e-01 1.708121846799402472e-15 +8.742914304768256706e-01 1.581880065065100320e-15 +8.752917639213071421e-01 1.464836572052342519e-15 +8.762920973657886137e-01 1.356331075025084689e-15 +8.772924308102700852e-01 1.255749943500208923e-15 +8.782927642547516678e-01 1.162522965617271099e-15 +8.792930976992331393e-01 1.076120326085745119e-15 +8.802934311437146109e-01 9.960497908509362930e-16 +8.812937645881960824e-01 9.218540845965112895e-16 +8.822940980326775540e-01 8.531084481159117855e-16 +8.832944314771591365e-01 7.894183634398414038e-16 +8.842947649216406081e-01 7.304174354070844844e-16 +8.852950983661220796e-01 6.757654191141685357e-16 +8.862954318106035512e-01 6.251463833789803476e-16 +8.872957652550850227e-01 5.782670010077743177e-16 +8.882960986995666053e-01 5.348549572670771806e-16 +8.892964321440480768e-01 4.946574685336149583e-16 +8.902967655885295484e-01 4.574399036304429151e-16 +8.912970990330110199e-01 4.229845008571657092e-16 +8.922974324774924915e-01 3.910891741892002432e-16 +8.932977659219740740e-01 3.615664025577837981e-16 +8.942980993664555456e-01 3.342421965302194497e-16 +8.952984328109370171e-01 3.089551370910169206e-16 +8.962987662554184887e-01 2.855554815808391175e-16 +8.972990996998999602e-01 2.639043321825462176e-16 +8.982994331443815428e-01 2.438728626545157475e-16 +8.992997665888630143e-01 2.253415993015181365e-16 +9.003001000333444859e-01 2.081997524443341595e-16 +9.013004334778259574e-01 1.923445949024821455e-16 +9.023007669223074290e-01 1.776808842404335168e-16 +9.033011003667890115e-01 1.641203257484093833e-16 +9.043014338112704831e-01 1.515810733346004844e-16 +9.053017672557519546e-01 1.399872656977982854e-16 +9.063021007002334262e-01 1.292685953287465752e-16 +9.073024341447148977e-01 1.193599080558722152e-16 +9.083027675891964803e-01 1.102008310070729656e-16 +9.093031010336779518e-01 1.017354270050125356e-16 +9.103034344781594234e-01 9.391187354916657875e-17 +9.113037679226408949e-01 8.668216466461487658e-17 +9.123041013671223665e-01 8.000183401581411061e-17 +9.133044348116039490e-01 7.382969779375378191e-17 +9.143047682560854206e-01 6.812761598775092776e-17 +9.153051017005668921e-01 6.286027074892463756e-17 +9.163054351450483637e-01 5.799496064176795431e-17 +9.173057685895298352e-01 5.350140966350287516e-17 +9.183061020340114178e-01 4.935158998854645498e-17 +9.193064354784928893e-01 4.551955746774810367e-17 +9.203067689229743609e-01 4.198129897949597577e-17 +9.213071023674558324e-01 3.871459079259595585e-17 +9.223074358119373040e-01 3.569886715936906617e-17 +9.233077692564188865e-01 3.291509841193407975e-17 +9.243081027009003581e-01 3.034567788542826591e-17 +9.253084361453818296e-01 2.797431703923070308e-17 +9.263087695898633012e-01 2.578594819131632987e-17 +9.273091030343447727e-01 2.376663432187519571e-17 +9.283094364788263553e-01 2.190348544054751710e-17 +9.293097699233078268e-01 2.018458104718804589e-17 +9.303101033677892984e-01 1.859889824916222372e-17 +9.313104368122707699e-01 1.713624512902347130e-17 +9.323107702567522415e-01 1.578719898507047838e-17 +9.333111037012338240e-01 1.454304909399889931e-17 +9.343114371457152956e-01 1.339574366968906195e-17 +9.353117705901967671e-01 1.233784071527791178e-17 +9.363121040346782387e-01 1.136246248717312016e-17 +9.373124374791597102e-01 1.046325330965708260e-17 +9.383127709236412928e-01 9.634340497337008099e-18 +9.393131043681227643e-01 8.870298159995667850e-18 +9.403134378126042359e-01 8.166113680488685338e-18 +9.413137712570857074e-01 7.517156671293122148e-18 +9.423141047015672900e-01 6.919150229223808238e-18 +9.433144381460487615e-01 6.368144320763377369e-18 +9.443147715905302331e-01 5.860491142473349589e-18 +9.453151050350117046e-01 5.392822312128134033e-18 +9.463154384794931762e-01 4.962027756593613944e-18 +9.473157719239747587e-01 4.565236172125282896e-18 +9.483161053684562303e-01 4.199796941725142897e-18 +9.493164388129377018e-01 3.863263402525256771e-18 +9.503167722574191734e-01 3.553377363908521659e-18 +9.513171057019006449e-01 3.268054784261109751e-18 +9.523174391463822275e-01 3.005372520930599639e-18 +9.533177725908636990e-01 2.763556074164053865e-18 +9.543181060353451706e-01 2.540968251556112776e-18 +9.553184394798266421e-01 2.336098684886506133e-18 +9.563187729243081137e-01 2.147554136187760456e-18 +9.573191063687896962e-01 1.974049534492422076e-18 +9.583194398132711678e-01 1.814399688986189880e-18 +9.593197732577526393e-01 1.667511628263328335e-18 +9.603201067022341109e-01 1.532377519063996122e-18 +9.613204401467155824e-01 1.408068121292722564e-18 +9.623207735911971650e-01 1.293726739288205427e-18 +9.633211070356786365e-01 1.188563632257928642e-18 +9.643214404801601081e-01 1.091850849520802572e-18 +9.653217739246415796e-01 1.002917458733076743e-18 +9.663221073691230512e-01 9.211451376211679865e-19 +9.673224408136046337e-01 8.459641019232663306e-19 +9.683227742580861053e-01 7.768493442604036118e-19 +9.693231077025675768e-01 7.133171605308841289e-19 +9.703234411470490484e-01 6.549219421573488453e-19 +9.713237745915305199e-01 6.012532141250525683e-19 +9.723241080360121025e-01 5.519329002416730727e-19 +9.733244414804935740e-01 5.066127984310844229e-19 +9.743247749249750456e-01 4.649722501545741733e-19 +9.753251083694565171e-01 4.267159892404177711e-19 +9.763254418139379887e-01 3.915721565019412151e-19 +9.773257752584195712e-01 3.592904675436036638e-19 +9.783261087029010428e-01 3.296405220982371635e-19 +9.793264421473825143e-01 3.024102441127232919e-19 +9.803267755918639859e-01 2.774044426093474700e-19 +9.813271090363454574e-01 2.544434840994622586e-19 +9.823274424808270400e-01 2.333620680205927025e-19 +9.833277759253085115e-01 2.140080973108868772e-19 +9.843281093697899831e-01 1.962416368297130549e-19 +9.853284428142714546e-01 1.799339528842563388e-19 +9.863287762587529262e-01 1.649666276316692860e-19 +9.873291097032345087e-01 1.512307425981988452e-19 +9.883294431477159803e-01 1.386261259934090218e-19 +9.893297765921974518e-01 1.270606589015972147e-19 +9.903301100366789234e-01 1.164496358063123223e-19 +9.913304434811603949e-01 1.067151752497290046e-19 +9.923307769256419775e-01 9.778567674845039521e-20 +9.933311103701234490e-01 8.959532038322419089e-20 +9.943314438146049206e-01 8.208360575366571853e-20 +9.953317772590863921e-01 7.519492724209850549e-20 +9.963321107035678637e-01 6.887818276457975823e-20 +9.973324441480494462e-01 6.308641340351402451e-20 +9.983327775925309178e-01 5.777647151617174481e-20 +9.993331110370123893e-01 5.290871509832035828e-20 +1.000333444481493972e+00 4.844672635297551606e-20 +1.001333777925975443e+00 4.435705257218893088e-20 +1.002334111370456915e+00 4.060896758567777288e-20 +1.003334444814938387e+00 3.717425216491144050e-20 +1.004334778259419858e+00 3.402699189581789838e-20 +1.005335111703901330e+00 3.114339114833408692e-20 +1.006335445148382801e+00 2.850160187729707294e-20 +1.007335778592864273e+00 2.608156608733453345e-20 +1.008336112037345744e+00 2.386487088507908836e-20 +1.009336445481827216e+00 2.183461512571872067e-20 +1.010336778926308909e+00 1.997528673821045153e-20 +1.011337112370790381e+00 1.827264988484107504e-20 +1.012337445815271852e+00 1.671364117667757197e-20 +1.013337779259753324e+00 1.528627422728888337e-20 +1.014338112704234796e+00 1.397955188321205636e-20 +1.015338446148716267e+00 1.278338552144968313e-20 +1.016338779593197739e+00 1.168852085207523217e-20 +1.017339113037679210e+00 1.068646970811553236e-20 +1.018339446482160682e+00 9.769447345574425484e-21 +1.019339779926642153e+00 8.930314813987603498e-21 +1.020340113371123847e+00 8.162525992519474272e-21 +1.021340446815605318e+00 7.460078918544358256e-21 +1.022340780260086790e+00 6.817471065099686490e-21 +1.023341113704568262e+00 6.229658250753219621e-21 +1.024341447149049733e+00 5.692016890463260664e-21 +1.025341780593531205e+00 5.200309319090720529e-21 +1.026342114038012676e+00 4.750651940502829152e-21 +1.027342447482494148e+00 4.339485974821230408e-21 +1.028342780926975619e+00 3.963550594442242563e-21 +1.029343114371457313e+00 3.619858256118692262e-21 +1.030343447815938784e+00 3.305672051743577321e-21 +1.031343781260420256e+00 3.018484914617251030e-21 +1.032344114704901727e+00 2.756000531016744752e-21 +1.033344448149383199e+00 2.516115818883712463e-21 +1.034344781593864671e+00 2.296904846509653844e-21 +1.035345115038346142e+00 2.096604074278929239e-21 +1.036345448482827614e+00 1.913598811908903497e-21 +1.037345781927309085e+00 1.746410792263414486e-21 +1.038346115371790557e+00 1.593686770767488957e-21 +1.039346448816272250e+00 1.454188066771271210e-21 +1.040346782260753722e+00 1.326780969951483046e-21 +1.041347115705235193e+00 1.210427941041009406e-21 +1.042347449149716665e+00 1.104179541887135736e-21 +1.043347782594198137e+00 1.007167035093146761e-21 +1.044348116038679608e+00 9.185955983321649677e-22 +1.045348449483161080e+00 8.377381028716002485e-22 +1.046348782927642551e+00 7.639294099387304925e-22 +1.047349116372124023e+00 6.965611423228364055e-22 +1.048349449816605494e+00 6.350768920727827749e-22 +1.049349783261087188e+00 5.789678283336843666e-22 +1.050350116705568659e+00 5.277686722957258675e-22 +1.051350450150050131e+00 4.810540089214237454e-22 +1.052350783594531602e+00 4.384349075946171535e-22 +1.053351117039013074e+00 3.995558261108616114e-22 +1.054351450483494546e+00 3.640917745224509712e-22 +1.055351783927976017e+00 3.317457172748167411e-22 +1.056352117372457489e+00 3.022461938395612503e-22 +1.057352450816938960e+00 2.753451396743978856e-22 +1.058352784261420432e+00 2.508158908334171830e-22 +1.059353117705902125e+00 2.284513569232139828e-22 +1.060353451150383597e+00 2.080623483610225415e-22 +1.061353784594865068e+00 1.894760450487058804e-22 +1.062354118039346540e+00 1.725345946404718237e-22 +1.063354451483828012e+00 1.570938295587300636e-22 +1.064354784928309483e+00 1.430220928098948672e-22 +1.065355118372790955e+00 1.301991634756891941e-22 +1.066355451817272426e+00 1.185152735119206210e-22 +1.067355785261753898e+00 1.078702081811967424e-22 +1.068356118706235369e+00 9.817248308356198591e-23 +1.069356452150717063e+00 8.933859133420228708e-23 +1.070356785595198534e+00 8.129231497447875496e-23 +1.071357119039680006e+00 7.396409519543816684e-23 +1.072357452484161477e+00 6.729045640526211244e-23 +1.073357785928642949e+00 6.121347958712966092e-23 +1.074358119373124421e+00 5.568032077469513933e-23 +1.075358452817605892e+00 5.064277082169794380e-23 +1.076358786262087364e+00 4.605685296260617455e-23 +1.077359119706568835e+00 4.188245495503376830e-23 +1.078359453151050307e+00 3.808299286418095220e-23 +1.079359786595532000e+00 3.462510379667790400e-23 +1.080360120040013472e+00 3.147836511778033422e-23 +1.081360453484494943e+00 2.861503789362006778e-23 +1.082360786928976415e+00 2.600983249066972760e-23 +1.083361120373457887e+00 2.363969443911976951e-23 +1.084361453817939358e+00 2.148360882689339329e-23 +1.085361787262420830e+00 1.952242163763485474e-23 +1.086362120706902301e+00 1.773867658038912438e-23 +1.087362454151383773e+00 1.611646608180696466e-23 +1.088362787595865244e+00 1.464129522450177178e-23 +1.089363121040346938e+00 1.329995751852755884e-23 +1.090363454484828409e+00 1.208042148760347369e-23 +1.091363787929309881e+00 1.097172713839228439e-23 +1.092364121373791352e+00 9.963891460561203774e-24 +1.093364454818272824e+00 9.047822178040109036e-24 +1.094364788262754296e+00 8.215239038477141901e-24 +1.095365121707235767e+00 7.458601988844557243e-24 +1.096365455151717239e+00 6.771045640940214432e-24 +1.097365788596198710e+00 6.146319481614230138e-24 +1.098366122040680182e+00 5.578733329298264752e-24 +1.099366455485161875e+00 5.063107581198157015e-24 +1.100366788929643347e+00 4.594727834667592378e-24 +1.101367122374124818e+00 4.169303502102393807e-24 +1.102367455818606290e+00 3.782930071475673948e-24 +1.103367789263087761e+00 3.432054694618087624e-24 +1.104368122707569233e+00 3.113444812777409080e-24 +1.105368456152050705e+00 2.824159554080536647e-24 +1.106368789596532176e+00 2.561523660466021140e-24 +1.107369123041013648e+00 2.323103722636793724e-24 +1.108369456485495119e+00 2.106686520770277669e-24 +1.109369789929976813e+00 1.910259286263975469e-24 +1.110370123374458284e+00 1.731991715831327667e-24 +1.111370456818939756e+00 1.570219583922230271e-24 +1.112370790263421227e+00 1.423429812842025100e-24 +1.113371123707902699e+00 1.290246872187734900e-24 +1.114371457152384171e+00 1.169420390412196404e-24 +1.115371790596865642e+00 1.059813871551298713e-24 +1.116372124041347114e+00 9.603944194933694463e-25 +1.117372457485828585e+00 8.702233807047560302e-25 +1.118372790930310057e+00 7.884478241222855413e-25 +1.119373124374791750e+00 7.142927840456912290e-25 +1.120373457819273222e+00 6.470541983671621578e-25 +1.121373791263754693e+00 5.860924804145372365e-25 +1.122374124708236165e+00 5.308266681097510506e-25 +1.123374458152717636e+00 4.807290990950683943e-25 +1.124374791597199108e+00 4.353205650016166949e-25 +1.125375125041680580e+00 3.941659021616610537e-25 +1.126375458486162051e+00 3.568699798330811271e-25 +1.127375791930643523e+00 3.230740504429553544e-25 +1.128376125375124994e+00 2.924524294947585425e-25 +1.129376458819606688e+00 2.647094756468150236e-25 +1.130376792264088159e+00 2.395768440821085185e-25 +1.131377125708569631e+00 2.168109886728579295e-25 +1.132377459153051102e+00 1.961908906174810748e-25 +1.133377792597532574e+00 1.775159932107447116e-25 +1.134378126042014046e+00 1.606043242166320543e-25 +1.135378459486495517e+00 1.452907889630340272e-25 +1.136378792930976989e+00 1.314256187815934461e-25 +1.137379126375458460e+00 1.188729607874517209e-25 +1.138379459819939932e+00 1.075095962441132394e-25 +1.139379793264421625e+00 9.722377589854303086e-26 +1.140380126708903097e+00 8.791416171057836447e-26 +1.141380460153384568e+00 7.948886534780233601e-26 +1.142380793597866040e+00 7.186457468007931569e-26 +1.143381127042347511e+00 6.496576029438705648e-26 +1.144381460486828983e+00 5.872395476723436220e-26 +1.145381793931310455e+00 5.307709808477758329e-26 +1.146382127375791926e+00 4.796894319557212461e-26 +1.147382460820273398e+00 4.334851622265735347e-26 +1.148382794264754869e+00 3.916962635511727521e-26 +1.149383127709236563e+00 3.539042088868829699e-26 +1.150383461153718034e+00 3.197298129423358098e-26 +1.151383794598199506e+00 2.888295656550279064e-26 +1.152384128042680977e+00 2.608923043693552193e-26 +1.153384461487162449e+00 2.356361937105970418e-26 +1.154384794931643921e+00 2.128059849623950912e-26 +1.155385128376125392e+00 1.921705293140775461e-26 +1.156385461820606864e+00 1.735205216732190635e-26 +1.157385795265088335e+00 1.566664538582461855e-26 +1.158386128709569807e+00 1.414367579144717253e-26 +1.159386462154051500e+00 1.276761220514829226e-26 +1.160386795598532972e+00 1.152439632962141199e-26 +1.161387129043014443e+00 1.040130424080267290e-26 +1.162387462487495915e+00 9.386820792297507675e-27 +1.163387795931977386e+00 8.470525739563391922e-27 +1.164388129376458858e+00 7.642990499925254936e-27 +1.165388462820940330e+00 6.895684563835116654e-27 +1.166388796265421801e+00 6.220890663104789914e-27 +1.167389129709903273e+00 5.611627883943428947e-27 +1.168389463154384744e+00 5.061581987279027942e-27 +1.169389796598866438e+00 4.565042266684534267e-27 +1.170390130043347909e+00 4.116844335891978883e-27 +1.171390463487829381e+00 3.712318293915979570e-27 +1.172390796932310852e+00 3.347241766729822445e-27 +1.173391130376792324e+00 3.017797370702089436e-27 +1.174391463821273796e+00 2.720534185038451270e-27 +1.175391797265755267e+00 2.452332858656190249e-27 +1.176392130710236739e+00 2.210374011605737750e-27 +1.177392464154718210e+00 1.992109622655628198e-27 +1.178392797599199682e+00 1.795237123266818899e-27 +1.179393131043681375e+00 1.617675944162153989e-27 +1.180393464488162847e+00 1.457546284287197218e-27 +1.181393797932644318e+00 1.313149893372787743e-27 +1.182394131377125790e+00 1.182952678755651775e-27 +1.183394464821607261e+00 1.065568964758635706e-27 +1.184394798266088733e+00 9.597472489520710006e-28 +1.185395131710570205e+00 8.643573141549816297e-28 +1.186395465155051676e+00 7.783785682264610357e-28 +1.187395798599533148e+00 7.008894956681175302e-28 +1.188396132044014619e+00 6.310581159189709137e-28 +1.189396465488496313e+00 5.681333530761883240e-28 +1.190396798932977784e+00 5.114372307128471219e-28 +1.191397132377459256e+00 4.603578135699838908e-28 +1.192397465821940727e+00 4.143428252521915617e-28 +1.193397799266422199e+00 3.728938777231961125e-28 +1.194398132710903671e+00 3.355612544429946647e-28 +1.195398466155385142e+00 3.019391944696705232e-28 +1.196398799599866614e+00 2.716616298180747930e-28 +1.197399133044348085e+00 2.443983328720295075e-28 +1.198399466488829779e+00 2.198514347298182937e-28 +1.199399799933311250e+00 1.977522790632352107e-28 +1.200400133377792722e+00 1.778585794237283730e-28 +1.201400466822274193e+00 1.599518509683630011e-28 +1.202400800266755665e+00 1.438350903312412831e-28 +1.203401133711237136e+00 1.293306798606658224e-28 +1.204401467155718608e+00 1.162784947019631850e-28 +1.205401800600200080e+00 1.045341932526125012e-28 +1.206402134044681551e+00 9.396767337021181358e-29 +1.207402467489163023e+00 8.446167839258367185e-29 +1.208402800933644716e+00 7.591053854964533601e-29 +1.209403134378126188e+00 6.821903472314509118e-29 +1.210403467822607659e+00 6.130137275657896260e-29 +1.211403801267089131e+00 5.508025764591196961e-29 +1.212404134711570602e+00 4.948605796276776632e-29 +1.213404468156052074e+00 4.445605178623002669e-29 +1.214404801600533546e+00 3.993374625579913234e-29 +1.215405135045015017e+00 3.586826361507736730e-29 +1.216405468489496489e+00 3.221378730068342934e-29 +1.217405801933977960e+00 2.892906225060779895e-29 +1.218406135378459654e+00 2.597694416682594964e-29 +1.219406468822941125e+00 2.332399297414326363e-29 +1.220406802267422597e+00 2.094010617590304930e-29 +1.221407135711904068e+00 1.879818822205985622e-29 +1.222407469156385540e+00 1.687385238025312931e-29 +1.223407802600867011e+00 1.514515193972999771e-29 +1.224408136045348483e+00 1.359233788468552294e-29 +1.225408469489829955e+00 1.219764045085160397e-29 +1.226408802934311426e+00 1.094507222982065268e-29 +1.227409136378792898e+00 9.820250712140151056e-30 +1.228409469823274591e+00 8.810238364987536565e-30 +1.229409803267756063e+00 7.903398525263457716e-30 +1.230410136712237534e+00 7.089265556158149269e-30 +1.231410470156719006e+00 6.358427866330501671e-30 +1.232410803601200477e+00 5.702422527319602399e-30 +1.233411137045681949e+00 5.113640348120209865e-30 +1.234411470490163421e+00 4.585240377223149329e-30 +1.235411803934644892e+00 4.111072903016325538e-30 +1.236412137379126364e+00 3.685610114288282291e-30 +1.237412470823607835e+00 3.303883664607842071e-30 +1.238412804268089529e+00 2.961428458426315548e-30 +1.239413137712571000e+00 2.654232043625223576e-30 +1.240413471157052472e+00 2.378689055597149256e-30 +1.241413804601533943e+00 2.131560212450631173e-30 +1.242414138046015415e+00 1.909935410109887377e-30 +1.243414471490496886e+00 1.711200510473037143e-30 +1.244414804934978358e+00 1.533007455850887261e-30 +1.245415138379459830e+00 1.373247379051755856e-30 +1.246415471823941301e+00 1.230026411091291294e-30 +1.247415805268422773e+00 1.101643917925421729e-30 +1.248416138712904466e+00 9.865729241440103487e-31 +1.249416472157385938e+00 8.834425055002690338e-31 +1.250416805601867409e+00 7.910219537399693239e-31 +1.251417139046348881e+00 7.082065366635154787e-31 +1.252417472490830352e+00 6.340046939095171062e-31 +1.253417805935311824e+00 5.675265247769021560e-31 +1.254418139379793296e+00 5.079734386729985734e-31 +1.255418472824274767e+00 4.546288516389858839e-31 +1.256418806268756239e+00 4.068498239987638016e-31 +1.257419139713237710e+00 3.640595446286756491e-31 +1.258419473157719404e+00 3.257405767627880642e-31 +1.259419806602200875e+00 2.914287887356657866e-31 +1.260420140046682347e+00 2.607079007109158940e-31 +1.261420473491163818e+00 2.332045853333577198e-31 +1.262420806935645290e+00 2.085840664486038312e-31 +1.263421140380126761e+00 1.865461656243776903e-31 +1.264421473824608233e+00 1.668217512429044701e-31 +1.265421807269089705e+00 1.491695494683719911e-31 +1.266422140713571176e+00 1.333732804769160969e-31 +1.267422474158052648e+00 1.192390870133470197e-31 +1.268422807602534341e+00 1.065932256493404547e-31 +1.269423141047015813e+00 9.527999409793954265e-32 +1.270423474491497284e+00 8.515987062182163419e-32 +1.271423807935978756e+00 7.610784398755356326e-32 +1.272424141380460227e+00 6.801191459097520736e-32 +1.273424474824941699e+00 6.077174933463482039e-32 +1.274424808269423171e+00 5.429747459781767949e-32 +1.275425141713904642e+00 4.850859322300155381e-32 +1.276425475158386114e+00 4.333301286695010085e-32 +1.277425808602867585e+00 3.870617434599553442e-32 +1.278426142047349279e+00 3.457026975755848904e-32 +1.279426475491830750e+00 3.087354119656024930e-32 +1.280426808936312222e+00 2.756965181752916351e-32 +1.281427142380793693e+00 2.461712183150773340e-32 +1.282427475825275165e+00 2.197882278051721635e-32 +1.283427809269756636e+00 1.962152410996913061e-32 +1.284428142714238108e+00 1.751548666855653912e-32 +1.285428476158719580e+00 1.563409831271270704e-32 +1.286428809603201051e+00 1.395354728484500390e-32 +1.287429143047682523e+00 1.245252947684215126e-32 +1.288429476492164216e+00 1.111198608780275993e-32 +1.289429809936645688e+00 9.914868542032649852e-33 +1.290430143381127159e+00 8.845927854225258496e-33 +1.291430476825608631e+00 7.891525916978885358e-33 +1.292430810270090102e+00 7.039466444716067062e-33 +1.293431143714571574e+00 6.278843540644691286e-33 +1.294431477159053046e+00 5.599906062250107314e-33 +1.295431810603534517e+00 4.993936148384995586e-33 +1.296432144048015989e+00 4.453140439437537102e-33 +1.297432477492497460e+00 3.970552673276507958e-33 +1.298432810936979154e+00 3.539946475430377764e-33 +1.299433144381460625e+00 3.155757283824397595e-33 +1.300433477825942097e+00 2.813012457781508095e-33 +1.301433811270423568e+00 2.507268719175848220e-33 +1.302434144714905040e+00 2.234556161725450975e-33 +1.303434478159386511e+00 1.991328143474670433e-33 +1.304434811603867983e+00 1.774416448452622170e-33 +1.305435145048349455e+00 1.580991167133977291e-33 +1.306435478492830926e+00 1.408524802423730390e-33 +1.307435811937312398e+00 1.254760159095974691e-33 +1.308436145381794091e+00 1.117681620550834963e-33 +1.309436478826275563e+00 9.954894579452276613e-34 +1.310436812270757034e+00 8.865768536938617072e-34 +1.311437145715238506e+00 7.895093544578284661e-34 +1.312437479159719977e+00 7.030064984349535820e-34 +1.313437812604201449e+00 6.259253883881491933e-34 +1.314438146048682921e+00 5.572460057116877835e-34 +1.315438479493164392e+00 4.960580822258616690e-34 +1.316438812937645864e+00 4.415493655597066234e-34 +1.317439146382127335e+00 3.929951311614161328e-34 +1.318439479826609029e+00 3.497488093692385637e-34 +1.319439813271090500e+00 3.112336097680308844e-34 +1.320440146715571972e+00 2.769350374129534420e-34 +1.321440480160053443e+00 2.463942065706329332e-34 +1.322440813604534915e+00 2.192018675426966609e-34 +1.323441147049016386e+00 1.949930710164136811e-34 +1.324441480493497858e+00 1.734424023395013415e-34 +1.325441813937979330e+00 1.542597252371583432e-34 +1.326442147382460801e+00 1.371863808654493838e-34 +1.327442480826942273e+00 1.219917938034791802e-34 +1.328442814271423966e+00 1.084704416971095449e-34 +1.329443147715905438e+00 9.643914984118178763e-35 +1.330443481160386909e+00 8.573467608137164962e-35 +1.331443814604868381e+00 7.621155508096265839e-35 +1.332444148049349852e+00 6.774017427658511615e-35 +1.333444481493831324e+00 6.020505678094207919e-35 +1.334444814938312796e+00 5.350332911548664828e-35 +1.335445148382794267e+00 4.754335400429883857e-35 +1.336445481827275739e+00 4.224351056108214995e-35 +1.337445815271757210e+00 3.753110608013314590e-35 +1.338446148716238904e+00 3.334140532249459032e-35 +1.339446482160720375e+00 2.961676469134818454e-35 +1.340446815605201847e+00 2.630586003439600819e-35 +1.341447149049683318e+00 2.336299801248568078e-35 +1.342447482494164790e+00 2.074750204785556926e-35 +1.343447815938646261e+00 1.842316482556572816e-35 +1.344448149383127733e+00 1.635776018000005939e-35 +1.345448482827609205e+00 1.452260796540991421e-35 +1.346448816272090676e+00 1.289218619502514539e-35 +1.347449149716572148e+00 1.144378534587118022e-35 +1.348449483161053841e+00 1.015720027380346810e-35 +1.349449816605535313e+00 9.014455672304611636e-36 +1.350450150050016784e+00 7.999561445477330640e-36 +1.351450483494498256e+00 7.098294755915397168e-36 +1.352450816938979727e+00 6.298005856690849845e-36 +1.353451150383461199e+00 5.587445127999066357e-36 +1.354451483827942671e+00 4.956609016984705752e-36 +1.355451817272424142e+00 4.396602827506061443e-36 +1.356452150716905614e+00 3.899518528209462556e-36 +1.357452484161387085e+00 3.458325945147419864e-36 +1.358452817605868779e+00 3.066775881787889996e-36 +1.359453151050350250e+00 2.719313866916521691e-36 +1.360453484494831722e+00 2.411003371625601533e-36 +1.361453817939313193e+00 2.137457462156114881e-36 +1.362454151383794665e+00 1.894777967396832722e-36 +1.363454484828276136e+00 1.679501339818950483e-36 +1.364454818272757608e+00 1.488550477814670870e-36 +1.365455151717239080e+00 1.319191856972438044e-36 +1.366455485161720551e+00 1.168997388794472979e-36 +1.367455818606202245e+00 1.035810488661546961e-36 +1.368456152050683716e+00 9.177158913031621420e-37 +1.369456485495165188e+00 8.130128023726127495e-37 +1.370456818939646659e+00 7.201910196136307374e-37 +1.371457152384128131e+00 6.379096971256004547e-37 +1.372457485828609602e+00 5.649784619119449291e-37 +1.373457819273091074e+00 5.003406236995738053e-37 +1.374458152717572545e+00 4.430582473638753585e-37 +1.375458486162054017e+00 3.922988825581035651e-37 +1.376458819606535489e+00 3.473237676590471796e-37 +1.377459153051017182e+00 3.074773452030092307e-37 +1.378459486495498654e+00 2.721779438600786942e-37 +1.379459819939980125e+00 2.409094979190620273e-37 +1.380460153384461597e+00 2.132141894412580522e-37 +1.381460486828943068e+00 1.886859108765150860e-37 +1.382460820273424540e+00 1.669644571891080562e-37 +1.383461153717906011e+00 1.477303665632338438e-37 +1.384461487162387483e+00 1.307003376824605766e-37 +1.385461820606868955e+00 1.156231595240160571e-37 +1.386462154051350426e+00 1.022760966836930745e-37 +1.387462487495832120e+00 9.046167954518277004e-38 +1.388462820940313591e+00 8.000485421419575559e-38 +1.389463154384795063e+00 7.075045212751858826e-38 +1.390463487829276534e+00 6.256094368822115586e-38 +1.391463821273758006e+00 5.531444423016567542e-38 +1.392464154718239477e+00 4.890294413141903534e-38 +1.393464488162720949e+00 4.323073802496212002e-38 +1.394464821607202420e+00 3.821303083848754193e-38 +1.395465155051683892e+00 3.377470087109506552e-38 +1.396465488496165364e+00 2.984920231705978621e-38 +1.397465821940647057e+00 2.637759160561525505e-38 +1.398466155385128529e+00 2.330766366759819067e-38 +1.399466488829610000e+00 2.059318578877715094e-38 +1.400466822274091472e+00 1.819321808694956669e-38 +1.401467155718572943e+00 1.607151087429789831e-38 +1.402467489163054415e+00 1.419597025502788186e-38 +1.403467822607535886e+00 1.253818427582855995e-38 +1.404468156052017358e+00 1.107300280668552696e-38 +1.405468489496498830e+00 9.778165093819035708e-39 +1.406468822940980301e+00 8.633969605689111444e-39 +1.407469156385461995e+00 7.622981396459039183e-39 +1.408469489829943466e+00 6.729772747484180449e-39 +1.409469823274424938e+00 5.940693323686364425e-39 +1.410470156718906409e+00 5.243666504808870502e-39 +1.411470490163387881e+00 4.628008927346165479e-39 +1.412470823607869352e+00 4.084270606723785693e-39 +1.413471157052350824e+00 3.604093305715091192e-39 +1.414471490496832295e+00 3.180085078276303996e-39 +1.415471823941313767e+00 2.805709151681245158e-39 +1.416472157385795239e+00 2.475185517305076750e-39 +1.417472490830276932e+00 2.183403784584716958e-39 +1.418472824274758404e+00 1.925846016164921036e-39 +1.419473157719239875e+00 1.698518407337072143e-39 +1.420473491163721347e+00 1.497890801647435492e-39 +1.421473824608202818e+00 1.320843148819872999e-39 +1.422474158052684290e+00 1.164618112527656787e-39 +1.423474491497165761e+00 1.026779125504526849e-39 +1.424474824941647233e+00 9.051732692870783502e-40 +1.425475158386128705e+00 7.978984266681881451e-40 +1.426475491830610176e+00 7.032742177292546577e-40 +1.427475825275091870e+00 6.198162860026916351e-40 +1.428476158719573341e+00 5.462135506979876086e-40 +1.429476492164054813e+00 4.813080847110575062e-40 +1.430476825608536284e+00 4.240773169603126063e-40 +1.431477159053017756e+00 3.736182920109467064e-40 +1.432477492497499227e+00 3.291337504602079479e-40 +1.433477825941980699e+00 2.899198206007548773e-40 +1.434478159386462170e+00 2.553551358484282411e-40 +1.435478492830943642e+00 2.248912136633643670e-40 +1.436478826275425114e+00 1.980439505161778153e-40 +1.437479159719906807e+00 1.743861041299074819e-40 +1.438479493164388279e+00 1.535406490047502766e-40 +1.439479826608869750e+00 1.351749043229060924e-40 +1.440480160053351222e+00 1.189953449265601576e-40 +1.441480493497832693e+00 1.047430163319753253e-40 +1.442480826942314165e+00 9.218948383867317199e-41 +1.443481160386795636e+00 8.113325384728552013e-41 +1.444481493831277108e+00 7.139661263192220275e-41 +1.445481827275758580e+00 6.282283412764932199e-41 +1.446482160720240051e+00 5.527371388419876583e-41 +1.447482494164721745e+00 4.862739128575654528e-41 +1.448482827609203216e+00 4.277642651713725183e-41 +1.449483161053684688e+00 3.762610263342654680e-41 +1.450483494498166159e+00 3.309292652115370851e-41 +1.451483827942647631e+00 2.910330557499070892e-41 +1.452484161387129102e+00 2.559237960020219795e-41 +1.453484494831610574e+00 2.250298982767435531e-41 +1.454484828276092045e+00 1.978476903075231525e-41 +1.455485161720573517e+00 1.739333859280235268e-41 +1.456485495165054989e+00 1.528960001928648011e-41 +1.457485828609536682e+00 1.343910984282640350e-41 +1.458486162054018154e+00 1.181152815611878581e-41 +1.459486495498499625e+00 1.038013214503908157e-41 +1.460486828942981097e+00 9.121386999948873556e-42 +1.461487162387462568e+00 8.014567472288856400e-42 +1.462487495831944040e+00 7.041424129479961143e-42 +1.463487829276425511e+00 6.185889055810790949e-42 +1.464488162720906983e+00 5.433816360963907771e-42 +1.465488496165388455e+00 4.772753400397134725e-42 +1.466488829609869926e+00 4.191739091240922054e-42 +1.467489163054351620e+00 3.681126130987727784e-42 +1.468489496498833091e+00 3.232424300509485323e-42 +1.469489829943314563e+00 2.838162363548285108e-42 +1.470490163387796034e+00 2.491766366874130307e-42 +1.471490496832277506e+00 2.187452403232599548e-42 +1.472490830276758977e+00 1.920132127006517578e-42 +1.473491163721240449e+00 1.685329513675624615e-42 +1.474491497165721920e+00 1.479107531777157276e-42 +1.475491830610203392e+00 1.298003552893648456e-42 +1.476492164054684864e+00 1.138972463638740636e-42 +1.477492497499166557e+00 9.993365658212641305e-43 +1.478492830943648029e+00 8.767414588382858101e-43 +1.479493164388129500e+00 7.691171935488398096e-43 +1.480493497832610972e+00 6.746440708958415687e-43 +1.481493831277092443e+00 5.917225326779012733e-43 +1.482494164721573915e+00 5.189466572843466604e-43 +1.483494498166055386e+00 4.550808309147876981e-43 +1.484494831610536858e+00 3.990392157124468226e-43 +1.485495165055018330e+00 3.498676811446738322e-43 +1.486495498499499801e+00 3.067279045699821638e-43 +1.487495831943981495e+00 2.688833818588854237e-43 +1.488496165388462966e+00 2.356871197369965559e-43 +1.489496498832944438e+00 2.065708086772182994e-43 +1.490496832277425909e+00 1.810352991125543917e-43 +1.491497165721907381e+00 1.586422248502075183e-43 +1.492497499166388852e+00 1.390066361752968200e-43 +1.493497832610870324e+00 1.217905215336408472e-43 +1.494498166055351795e+00 1.066971111378002311e-43 +1.495498499499833267e+00 9.346586857910626512e-44 +1.496498832944314739e+00 8.186808775272391858e-44 +1.497499166388796432e+00 7.170302229295239120e-44 +1.498499499833277904e+00 6.279448342880612977e-44 +1.499499833277759375e+00 5.498784984539501115e-44 +1.500500166722240847e+00 4.814743989762582861e-44 +1.501500500166722318e+00 4.215420247723090903e-44 +1.502500833611203790e+00 3.690368807843724911e-44 +1.503501167055685261e+00 3.230426622543135533e-44 +1.504501500500166733e+00 2.827555949098917784e-44 +1.505501833944648205e+00 2.474706791554944483e-44 +1.506502167389129676e+00 2.165696078766084261e-44 +1.507502500833611370e+00 1.895101552091231917e-44 +1.508502834278092841e+00 1.658168580435330127e-44 +1.509503167722574313e+00 1.450728335240988214e-44 +1.510503501167055784e+00 1.269125947156441285e-44 +1.511503834611537256e+00 1.110157432516834868e-44 +1.512504168056018727e+00 9.710143241972406328e-45 +1.513504501500500199e+00 8.492350702122041335e-45 +1.514504834944981670e+00 7.426623767539946714e-45 +1.515505168389463142e+00 6.494057720373943802e-45 +1.516505501833944614e+00 5.678087549855358668e-45 +1.517505835278426307e+00 4.964199698899925075e-45 +1.518506168722907779e+00 4.339679159742106584e-45 +1.519506502167389250e+00 3.793387604029055833e-45 +1.520506835611870722e+00 3.315568756905592892e-45 +1.521507169056352193e+00 2.897677685375339000e-45 +1.522507502500833665e+00 2.532231076242452055e-45 +1.523507835945315136e+00 2.212675934925500432e-45 +1.524508169389796608e+00 1.933274449307205037e-45 +1.525508502834278080e+00 1.689003037725805836e-45 +1.526508836278759551e+00 1.475463841811350017e-45 +1.527509169723241245e+00 1.288807137139287079e-45 +1.528509503167722716e+00 1.125663321165680328e-45 +1.529509836612204188e+00 9.830833017275530909e-46 +1.530510170056685659e+00 8.584862532941053194e-46 +1.531510503501167131e+00 7.496138345373219669e-46 +1.532510836945648602e+00 6.544900717843493510e-46 +1.533511170390130074e+00 5.713862103816070664e-46 +1.534511503834611545e+00 4.987899215797780227e-46 +1.535511837279093017e+00 4.353783276871997107e-46 +1.536512170723574489e+00 3.799943742002324841e-46 +1.537512504168056182e+00 3.316261355209930504e-46 +1.538512837612537654e+00 2.893886916943229661e-46 +1.539513171057019125e+00 2.525082581989089768e-46 +1.540513504501500597e+00 2.203082899692814294e-46 +1.541513837945982068e+00 1.921973151714765656e-46 +1.542514171390463540e+00 1.676582843902373818e-46 +1.543514504834945011e+00 1.462392473235265882e-46 +1.544514838279426483e+00 1.275451922724367986e-46 +1.545515171723907955e+00 1.112309040574677190e-46 +1.546515505168389648e+00 9.699471383459353122e-47 +1.547515838612871120e+00 8.457302993187389622e-47 +1.548516172057352591e+00 7.373555254868156467e-47 +1.549516505501834063e+00 6.428108719095170418e-47 +1.550516838946315534e+00 5.603388226391303665e-47 +1.551517172390797006e+00 4.884042549115775936e-47 +1.552517505835278477e+00 4.256664193480126924e-47 +1.553517839279759949e+00 3.709544349629629583e-47 +1.554518172724241420e+00 3.232458600429686666e-47 +1.555518506168722892e+00 2.816479545271514525e-47 +1.556518839613204586e+00 2.453812973358184657e-47 +1.557519173057686057e+00 2.137654639878253229e-47 +1.558519506502167529e+00 1.862065065499773423e-47 +1.559519839946649000e+00 1.621860101142128126e-47 +1.560520173391130472e+00 1.412515281611444821e-47 +1.561520506835611943e+00 1.230082238347751323e-47 +1.562520840280093415e+00 1.071115657553779949e-47 +1.563521173724574886e+00 9.326094591378303954e-48 +1.564521507169056358e+00 8.119410375362294623e-48 +1.565521840613537830e+00 7.068225504951295775e-48 +1.566522174058019523e+00 6.152583688430939389e-48 +1.567522507502500995e+00 5.355079114128557975e-48 +1.568522840946982466e+00 4.660531865382200826e-48 +1.569523174391463938e+00 4.055704466803129747e-48 +1.570523507835945409e+00 3.529054372326813564e-48 +1.571523841280426881e+00 3.070517857426421039e-48 +1.572524174724908352e+00 2.671321348201463196e-48 +1.573524508169389824e+00 2.323816719046428644e-48 +1.574524841613871295e+00 2.021337527101182421e-48 +1.575525175058352767e+00 1.758073533499027046e-48 +1.576525508502834461e+00 1.528961195369921984e-48 +1.577525841947315932e+00 1.329588104605461914e-48 +1.578526175391797404e+00 1.156109604771299554e-48 +1.579526508836278875e+00 1.005176040862226328e-48 +1.580526842280760347e+00 8.738692918234392671e-49 +1.581527175725241818e+00 7.596474064412204561e-49 +1.582527509169723290e+00 6.602963124011137028e-49 +1.583527842614204761e+00 5.738876987154031495e-49 +1.584528176058686233e+00 4.987422856937047603e-49 +1.585528509503167705e+00 4.333977962278240498e-49 +1.586528842947649398e+00 3.765810291924348181e-49 +1.587529176392130870e+00 3.271835118042629214e-49 +1.588529509836612341e+00 2.842402742153423091e-49 +1.589529843281093813e+00 2.469113476503678004e-49 +1.590530176725575284e+00 2.144656380898552132e-49 +1.591530510170056756e+00 1.862668717757650551e-49 +1.592530843614538227e+00 1.617613474824637194e-49 +1.593531177059019699e+00 1.404672642611632006e-49 +1.594531510503501170e+00 1.219654228482852398e-49 +1.595531843947982642e+00 1.058911246690700290e-49 +1.596532177392464336e+00 9.192711483922810797e-50 +1.597532510836945807e+00 7.979743518349660846e-50 +1.598532844281427279e+00 6.926207041148191393e-50 +1.599533177725908750e+00 6.011228553419359662e-50 +1.600533511170390222e+00 5.216656564513929939e-50 +1.601533844614871693e+00 4.526708056882948226e-50 +1.602534178059353165e+00 3.927660680797018032e-50 +1.603534511503834636e+00 3.407584788246793060e-50 +1.604534844948316108e+00 2.956110170960440890e-50 +1.605535178392797580e+00 2.564223026577915937e-50 +1.606535511837279273e+00 2.224089251895231743e-50 +1.607535845281760745e+00 1.928900663438354969e-50 +1.608536178726242216e+00 1.672741182817215141e-50 +1.609536512170723688e+00 1.450470405523173798e-50 +1.610536845615205159e+00 1.257622304191822875e-50 +1.611537179059686631e+00 1.090317107108309060e-50 +1.612537512504168102e+00 9.451846453076226176e-51 +1.613537845948649574e+00 8.192976817762441594e-51 +1.614538179393131045e+00 7.101139281346961199e-51 +1.615538512837612517e+00 6.154256213918696626e-51 +1.616538846282094211e+00 5.333156790658354990e-51 +1.617539179726575682e+00 4.621195779197907566e-51 +1.618539513171057154e+00 4.003922121649168691e-51 +1.619539846615538625e+00 3.468790833346722884e-51 +1.620540180060020097e+00 3.004912579626394577e-51 +1.621540513504501568e+00 2.602836022959635082e-51 +1.622540846948983040e+00 2.254358669390846235e-51 +1.623541180393464511e+00 1.952362497608866079e-51 +1.624541513837945983e+00 1.690671136705982922e-51 +1.625541847282427455e+00 1.463925778966022111e-51 +1.626542180726909148e+00 1.267477379914034808e-51 +1.627542514171390620e+00 1.097293016365779890e-51 +1.628542847615872091e+00 9.498745504494279752e-52 +1.629543181060353563e+00 8.221879888571640760e-52 +1.630543514504835034e+00 7.116021365626548107e-52 +1.631543847949316506e+00 6.158353269602424133e-52 +1.632544181393797977e+00 5.329091693619749353e-52 +1.633544514838279449e+00 4.611083931059440002e-52 +1.634544848282760920e+00 3.989459878541847584e-52 +1.635545181727242392e+00 3.451329443235310405e-52 +1.636545515171724086e+00 2.985519907275741344e-52 +1.637545848616205557e+00 2.582347993797887940e-52 +1.638546182060687029e+00 2.233422067559373031e-52 +1.639546515505168500e+00 1.931470501801959432e-52 +1.640546848949649972e+00 1.670192763491737864e-52 +1.641547182394131443e+00 1.444130221587621641e-52 +1.642547515838612915e+00 1.248554076347536050e-52 +1.643547849283094386e+00 1.079368149587909539e-52 +1.644548182727575858e+00 9.330245729779506678e-53 +1.645548516172057329e+00 8.064506696954952080e-53 +1.646548849616539023e+00 6.969855491800885817e-53 +1.647549183061020495e+00 6.023251297040774619e-53 +1.648549516505501966e+00 5.204744728853681650e-53 +1.649549849949983438e+00 4.497064614311514770e-53 +1.650550183394464909e+00 3.885259792351309096e-53 +1.651550516838946381e+00 3.356388639829173676e-53 +1.652550850283427852e+00 2.899249988491817742e-53 +1.653551183727909324e+00 2.504149936096125452e-53 +1.654551517172390795e+00 2.162699782030323586e-53 +1.655551850616872267e+00 1.867640949115649736e-53 +1.656552184061353961e+00 1.612693301353946785e-53 +1.657552517505835432e+00 1.392423743171933750e-53 +1.658552850950316904e+00 1.202132398695928437e-53 +1.659553184394798375e+00 1.037754028025569928e-53 +1.660553517839279847e+00 8.957726485400052374e-54 +1.661553851283761318e+00 7.731475991993856892e-54 +1.662554184728242790e+00 6.672495200167675203e-54 +1.663554518172724261e+00 5.758049220777337272e-54 +1.664554851617205733e+00 4.968481997673928142e-54 +1.665555185061687204e+00 4.286800897774892877e-54 +1.666555518506168898e+00 3.698317140981510130e-54 +1.667555851950650370e+00 3.190334592265637141e-54 +1.668556185395131841e+00 2.751880435767544648e-54 +1.669556518839613313e+00 2.373472115723145832e-54 +1.670556852284094784e+00 2.046915678990663472e-54 +1.671557185728576256e+00 1.765131304137477079e-54 +1.672557519173057727e+00 1.522002365676236350e-54 +1.673557852617539199e+00 1.312244870591703824e-54 +1.674558186062020670e+00 1.131294527738773046e-54 +1.675558519506502142e+00 9.752090776585913402e-55 +1.676558852950983836e+00 8.405838283564053580e-55 +1.677559186395465307e+00 7.244786181242386446e-55 +1.678559519839946779e+00 6.243546652149494703e-55 +1.679559853284428250e+00 5.380199709869164169e-55 +1.680560186728909722e+00 4.635821222860215300e-55 +1.681560520173391193e+00 3.994074940012817734e-55 +1.682560853617872665e+00 3.440859871204974126e-55 +1.683561187062354136e+00 2.964005539924705320e-55 +1.684561520506835608e+00 2.553008632783152174e-55 +1.685561853951317079e+00 2.198805443300522166e-55 +1.686562187395798773e+00 1.893575262767795939e-55 +1.687562520840280245e+00 1.630570524935728164e-55 +1.688562854284761716e+00 1.403970077340991010e-55 +1.689563187729243188e+00 1.208752442012878394e-55 +1.690563521173724659e+00 1.040586352306668445e-55 +1.691563854618206131e+00 8.957362195289156070e-56 +1.692564188062687602e+00 7.709805005012684561e-56 +1.693564521507169074e+00 6.635412118984681057e-56 +1.694564854951650545e+00 5.710230748305207398e-56 +1.695565188396132017e+00 4.913609787036878671e-56 +1.696565521840613711e+00 4.227746311992183451e-56 +1.697565855285095182e+00 3.637294149877722768e-56 +1.698566188729576654e+00 3.129026047844789331e-56 +1.699566522174058125e+00 2.691542133476030261e-56 +1.700566855618539597e+00 2.315018344534200838e-56 +1.701567189063021068e+00 1.990989368441451634e-56 +1.702567522507502540e+00 1.712161374593639654e-56 +1.703567855951984011e+00 1.472250464980637458e-56 +1.704568189396465483e+00 1.265843323794141694e-56 +1.705568522840946954e+00 1.088277026539570273e-56 +1.706568856285428648e+00 9.355353838214045432e-57 +1.707569189729910120e+00 8.041595532654734656e-57 +1.708569523174391591e+00 6.911709626074598122e-57 +1.709569856618873063e+00 5.940048544171472951e-57 +1.710570190063354534e+00 5.104529939536784880e-57 +1.711570523507836006e+00 4.386142811981295042e-57 +1.712570856952317477e+00 3.768521804580881190e-57 +1.713571190396798949e+00 3.237580297796192976e-57 +1.714571523841280420e+00 2.781194209284901644e-57 +1.715571857285762114e+00 2.388929516785981540e-57 +1.716572190730243586e+00 2.051807479575469193e-57 +1.717572524174725057e+00 1.762102361121054638e-57 +1.718572857619206529e+00 1.513167169550685224e-57 +1.719573191063688000e+00 1.299283548797545245e-57 +1.720573524508169472e+00 1.115532485143968434e-57 +1.721573857952650943e+00 9.576829528562412954e-58 +1.722574191397132415e+00 8.220960186429903939e-58 +1.723574524841613886e+00 7.056422663743809281e-58 +1.724574858286095358e+00 6.056306982989204718e-58 +1.725575191730577052e+00 5.197475232991579386e-58 +1.726575525175058523e+00 4.460034620855692772e-58 +1.727575858619539995e+00 3.826883884185056044e-58 +1.728576192064021466e+00 3.283322886099228290e-58 +1.729576525508502938e+00 2.816716622537691763e-58 +1.730576858952984409e+00 2.416206084506480266e-58 +1.731577192397465881e+00 2.072459463884406780e-58 +1.732577525841947352e+00 1.777458093105561214e-58 +1.733577859286428824e+00 1.524312286321439089e-58 +1.734578192730910295e+00 1.307102919603439434e-58 +1.735578526175391989e+00 1.120745165158841155e-58 +1.736578859619873461e+00 9.608712921236446322e-59 +1.737579193064354932e+00 8.237298752703436155e-59 +1.738579526508836404e+00 7.060991224056785472e-59 +1.739579859953317875e+00 6.052123495137354548e-59 +1.740580193397799347e+00 5.186939068868231624e-59 +1.741580526842280818e+00 4.445040956615849690e-59 +1.742580860286762290e+00 3.808918175964527216e-59 +1.743581193731243761e+00 3.263538761129335715e-59 +1.744581527175725233e+00 2.795999974827810314e-59 +1.745581860620206927e+00 2.395227709422223586e-59 +1.746582194064688398e+00 2.051718183543964122e-59 +1.747582527509169870e+00 1.757316003241155205e-59 +1.748582860953651341e+00 1.505023485517102285e-59 +1.749583194398132813e+00 1.288836855526359450e-59 +1.750583527842614284e+00 1.103605542692365619e-59 +1.751583861287095756e+00 9.449113294074985718e-60 +1.752584194731577227e+00 8.089645606588515752e-60 +1.753584528176058699e+00 6.925150141470898894e-60 +1.754584861620540170e+00 5.927753670490775061e-60 +1.755585195065021864e+00 5.073554851287624679e-60 +1.756585528509503336e+00 4.342060089690189484e-60 +1.757585861953984807e+00 3.715699263231243125e-60 +1.758586195398466279e+00 3.179410038245104171e-60 +1.759586528842947750e+00 2.720281097302615687e-60 +1.760586862287429222e+00 2.327245956087496615e-60 +1.761587195731910693e+00 1.990820220151234473e-60 +1.762587529176392165e+00 1.702876138993271996e-60 +1.763587862620873636e+00 1.456449180566591813e-60 +1.764588196065355108e+00 1.245572093387806914e-60 +1.765588529509836802e+00 1.065132562943415388e-60 +1.766588862954318273e+00 9.107511186780509810e-61 +1.767589196398799745e+00 7.786764201265700156e-61 +1.768589529843281216e+00 6.656954565458958681e-61 +1.769589863287762688e+00 5.690565430452970775e-61 +1.770590196732244159e+00 4.864032957241519705e-61 +1.771590530176725631e+00 4.157180256051430577e-61 +1.772590863621207102e+00 3.552732121334935149e-61 +1.773591197065688574e+00 3.035899068009747225e-61 +1.774591530510170045e+00 2.594020804363947971e-61 +1.775591863954651739e+00 2.216260676553613677e-61 +1.776592197399133211e+00 1.893343821239816890e-61 +1.777592530843614682e+00 1.617332794534272669e-61 +1.778592864288096154e+00 1.381435331017284635e-61 +1.779593197732577625e+00 1.179839646748779218e-61 +1.780593531177059097e+00 1.007573352635774671e-61 +1.781593864621540568e+00 8.603826044446498733e-62 +1.782594198066022040e+00 7.346285962394871941e-62 +1.783594531510503511e+00 6.271989163113649276e-62 +1.784594864954984983e+00 5.354316383952985827e-62 +1.785595198399466677e+00 4.570503244312620173e-62 +1.786595531843948148e+00 3.901083754420917696e-62 +1.787595865288429620e+00 3.329413903798471581e-62 +1.788596198732911091e+00 2.841263842915191077e-62 +1.789596532177392563e+00 2.424468813789372823e-62 +1.790596865621874034e+00 2.068630393496420168e-62 +1.791597199066355506e+00 1.764860822000271057e-62 +1.792597532510836977e+00 1.505564220899854196e-62 +1.793597865955318449e+00 1.284249397112662369e-62 +1.794598199399799920e+00 1.095369686203117758e-62 +1.795598532844281614e+00 9.341859420512246429e-63 +1.796598866288763086e+00 7.966493383280315026e-63 +1.797599199733244557e+00 6.793011260810668699e-63 +1.798599533177726029e+00 5.791869020333461034e-63 +1.799599866622207500e+00 4.937832937398738429e-63 +1.800600200066688972e+00 4.209352689145503689e-63 +1.801600533511170443e+00 3.588025342311115068e-63 +1.802600866955651915e+00 3.058137098848051279e-63 +1.803601200400133386e+00 2.606271554674212138e-63 +1.804601533844614858e+00 2.220974847953787933e-63 +1.805601867289096552e+00 1.892469461278728614e-63 +1.806602200733578023e+00 1.612409630565124518e-63 +1.807602534178059495e+00 1.373672330967039034e-63 +1.808602867622540966e+00 1.170178681172949323e-63 +1.809603201067022438e+00 9.967413530726559385e-64 +1.810603534511503909e+00 8.489342119853220026e-64 +1.811603867955985381e+00 7.229809588389312383e-64 +1.812604201400466852e+00 6.156600131102910333e-64 +1.813604534844948324e+00 5.242232753023243308e-64 +1.814604868289429795e+00 4.463267499480534140e-64 +1.815605201733911489e+00 3.799713029066407369e-64 +1.816605535178392961e+00 3.234520771709956332e-64 +1.817605868622874432e+00 2.753153056393345803e-64 +1.818606202067355904e+00 2.343214425349504436e-64 +1.819606535511837375e+00 1.994136918612824266e-64 +1.820606868956318847e+00 1.696911452823224341e-64 +1.821607202400800318e+00 1.443858563982843827e-64 +1.822607535845281790e+00 1.228432763506358549e-64 +1.823607869289763261e+00 1.045055594399284556e-64 +1.824608202734244733e+00 8.889731903109323893e-65 +1.825608536178726427e+00 7.561347521304619818e-65 +1.826608869623207898e+00 6.430888797828547269e-65 +1.827609203067689370e+00 5.468951438222922756e-65 +1.828609536512170841e+00 4.650486633360761581e-65 +1.829609869956652313e+00 3.954157829912565677e-65 +1.830610203401133784e+00 3.361792208499204239e-65 +1.831610536845615256e+00 2.857912967435114246e-65 +1.832610870290096727e+00 2.429340544372854350e-65 +1.833611203734578199e+00 2.064852645773724462e-65 +1.834611537179059670e+00 1.754894438120505681e-65 +1.835611870623541364e+00 1.491331522065573535e-65 +1.836612204068022836e+00 1.267239392804290461e-65 +1.837612537512504307e+00 1.076724013868326256e-65 +1.838612870956985779e+00 9.147689202947709835e-66 +1.839613204401467250e+00 7.771049404457014443e-66 +1.840613537845948722e+00 6.600992004837483570e-66 +1.841613871290430193e+00 5.606605660321697666e-66 +1.842614204734911665e+00 4.761590941674586924e-66 +1.843614538179393136e+00 4.043574261155632812e-66 +1.844614871623874608e+00 3.433523558250165248e-66 +1.845615205068356302e+00 2.915250696397808604e-66 +1.846615538512837773e+00 2.474987741444309338e-66 +1.847615871957319245e+00 2.101026184951258693e-66 +1.848616205401800716e+00 1.783409789625376365e-66 +1.849616538846282188e+00 1.513673110756018505e-66 +1.850616872290763659e+00 1.284618921516327226e-66 +1.851617205735245131e+00 1.090128771040279672e-66 +1.852617539179726602e+00 9.250017577127240583e-67 +1.853617872624208074e+00 7.848173277866759611e-67 +1.854618206068689545e+00 6.658185297662541054e-67 +1.855618539513171239e+00 5.648126837562119019e-67 +1.856618872957652711e+00 4.790868756500803456e-67 +1.857619206402134182e+00 4.063360701105423664e-67 +1.858619539846615654e+00 3.446019635941618109e-67 +1.859619873291097125e+00 2.922209775567682062e-67 +1.860620206735578597e+00 2.477800295883666562e-67 +1.861620540180060068e+00 2.100789226526284274e-67 +1.862620873624541540e+00 1.780983650444682560e-67 +1.863621207069023011e+00 1.509727805553927439e-67 +1.864621540513504483e+00 1.279671934305642880e-67 +1.865621873957986177e+00 1.084575792330919331e-67 +1.866622207402467648e+00 9.191416344672963718e-68 +1.867622540846949120e+00 7.788722688831859497e-68 +1.868622874291430591e+00 6.599504276260127622e-68 +1.869623207735912063e+00 5.591362617408347377e-68 +1.870623541180393534e+00 4.736802456428525949e-68 +1.871623874624875006e+00 4.012491810277266308e-68 +1.872624208069356477e+00 3.398633357962360603e-68 +1.873624541513837949e+00 2.878430472268594171e-68 +1.874624874958319420e+00 2.437633685892830786e-68 +1.875625208402801114e+00 2.064155510666326403e-68 +1.876625541847282586e+00 1.747743337884377015e-68 +1.877625875291764057e+00 1.479701686923687206e-68 +1.878626208736245529e+00 1.252656378544789011e-68 +1.879626542180727000e+00 1.060354322786327503e-68 +1.880626875625208472e+00 8.974935583415380149e-69 +1.881627209069689943e+00 7.595789855837003547e-69 +1.882627542514171415e+00 6.427999201292027336e-69 +1.883627875958652886e+00 5.439261759794056813e-69 +1.884628209403134580e+00 4.602198821921848088e-69 +1.885628542847616052e+00 3.893606577328864648e-69 +1.886628876292097523e+00 3.293821267373168389e-69 +1.887629209736578995e+00 2.786180603306778045e-69 +1.888629543181060466e+00 2.356566894140856168e-69 +1.889629876625541938e+00 1.993019522928020435e-69 +1.890630210070023409e+00 1.685406274893113029e-69 +1.891630543514504881e+00 1.425144605060530013e-69 +1.892630876958986352e+00 1.204965278830225798e-69 +1.893631210403467824e+00 1.018711962127920405e-69 +1.894631543847949517e+00 8.611713087055034547e-70 +1.895631877292430989e+00 7.279289167795233229e-70 +1.896632210736912461e+00 6.152472274485287403e-70 +1.897632544181393932e+00 5.199620319321373210e-70 +1.898632877625875404e+00 4.393947595155633728e-70 +1.899633211070356875e+00 3.712781466747478957e-70 +1.900633544514838347e+00 3.136932516845252566e-70 +1.901633877959319818e+00 2.650160878303334358e-70 +1.902634211403801290e+00 2.238724104484718600e-70 +1.903634544848282761e+00 1.890994154554111876e-70 +1.904634878292764455e+00 1.597132957804100366e-70 +1.905635211737245926e+00 1.348817622722385237e-70 +1.906635545181727398e+00 1.139007715315157028e-70 +1.907635878626208870e+00 9.617481839336524407e-71 +1.908636212070690341e+00 8.120024856700896798e-71 +1.909636545515171813e+00 6.855112987678033743e-71 +1.910636878959653284e+00 5.786729088904900710e-71 +1.911637212404134756e+00 4.884419536038935690e-71 +1.912637545848616227e+00 4.122437152335570988e-71 +1.913637879293097699e+00 3.479015811279418669e-71 +1.914638212737579392e+00 2.935756539474279431e-71 +1.915638546182060864e+00 2.477108028251925110e-71 +1.916638879626542336e+00 2.089927075090097744e-71 +1.917639213071023807e+00 1.763106690338104029e-71 +1.918639546515505279e+00 1.487261481406538607e-71 +1.919639879959986750e+00 1.254461516897619333e-71 +1.920640213404468222e+00 1.058007220674844305e-71 +1.921640546848949693e+00 8.922389875695645785e-72 +1.922640880293431165e+00 7.523761796461845626e-72 +1.923641213737912636e+00 6.343809812816274642e-72 +1.924641547182394330e+00 5.348432853124621871e-72 +1.925641880626875801e+00 4.508833702790921731e-72 +1.926642214071357273e+00 3.800696265678351875e-72 +1.927642547515838745e+00 3.203490107581987272e-72 +1.928642880960320216e+00 2.699882643787812394e-72 +1.929643214404801688e+00 2.275242354364949832e-72 +1.930643547849283159e+00 1.917218968801313763e-72 +1.931643881293764631e+00 1.615388726872444037e-72 +1.932644214738246102e+00 1.360954655322129292e-72 +1.933644548182727574e+00 1.146493350994786597e-72 +1.934644881627209267e+00 9.657410736389834010e-73 +1.935645215071690739e+00 8.134130622701997681e-73 +1.936645548516172211e+00 6.850509287097504634e-73 +1.937645881960653682e+00 5.768937769447554000e-73 +1.938646215405135154e+00 4.857693694981551218e-73 +1.939646548849616625e+00 4.090022308769885587e-73 +1.940646882294098097e+00 3.443360593173581276e-73 +1.941647215738579568e+00 2.898682249558596779e-73 +1.942647549183061040e+00 2.439944766536905868e-73 +1.943647882627542511e+00 2.053622706515620350e-73 +1.944648216072024205e+00 1.728313802257120765e-73 +1.945648549516505676e+00 1.454406534769822666e-73 +1.946648882960987148e+00 1.223799621785479342e-73 +1.947649216405468620e+00 1.029665331971933908e-73 +1.948649549849950091e+00 8.662497958441600229e-74 +1.949649883294431563e+00 7.287045456218241226e-74 +1.950650216738913034e+00 6.129444130723207915e-74 +1.951650550183394506e+00 5.155276721102243540e-74 +1.952650883627875977e+00 4.335549530986942867e-74 +1.953651217072357449e+00 3.645839966042696370e-74 +1.954651550516839142e+00 3.065577711768487936e-74 +1.955651883961320614e+00 2.577438655687557396e-74 +1.956652217405802086e+00 2.166833916680984629e-74 +1.957652550850283557e+00 1.821479096030721347e-74 +1.958652884294765029e+00 1.531031188342904441e-74 +1.959653217739246500e+00 1.286782552382035662e-74 +1.960653551183727972e+00 1.081402998129335809e-74 +1.961653884628209443e+00 9.087224445427130669e-75 +1.962654218072690915e+00 7.635477826701674760e-75 +1.963654551517172386e+00 6.415085748407962862e-75 +1.964654884961654080e+00 5.389270612760883299e-75 +1.965655218406135551e+00 4.527086548208972102e-75 +1.966655551850617023e+00 3.802497030327066570e-75 +1.967655885295098495e+00 3.193598018552596195e-75 +1.968656218739579966e+00 2.681963711194939865e-75 +1.969656552184061438e+00 2.252095614808692683e-75 +1.970656885628542909e+00 1.890958655146160335e-75 +1.971657219073024381e+00 1.587590613333547001e-75 +1.972657552517505852e+00 1.332773326785330848e-75 +1.973657885961987324e+00 1.118755912276539594e-75 +1.974658219406469017e+00 9.390218013817841245e-76 +1.975658552850950489e+00 7.880926707539887930e-76 +1.976658886295431961e+00 6.613634391011395577e-76 +1.977659219739913432e+00 5.549634209972840741e-76 +1.978659553184394904e+00 4.656395016301622699e-76 +1.979659886628876375e+00 3.906578488714595044e-76 +1.980660220073357847e+00 3.277212287307636799e-76 +1.981660553517839318e+00 2.748994534222040147e-76 +1.982660886962320790e+00 2.305708815128486460e-76 +1.983661220406802261e+00 1.933732183845840367e-76 +1.984661553851283955e+00 1.621621421886397662e-76 +1.985661887295765426e+00 1.359765137476525098e-76 +1.986662220740246898e+00 1.140091253332787556e-76 +1.987662554184728370e+00 9.558210870919041738e-77 +1.988662887629209841e+00 8.012626216146337290e-77 +1.989663221073691313e+00 6.716367355671332536e-77 +1.990663554518172784e+00 5.629311524207960065e-77 +1.991663887962654256e+00 4.717776975386984338e-77 +1.992664221407135727e+00 3.953491529736121989e-77 +1.993664554851617199e+00 3.312725887567311551e-77 +1.994664888296098892e+00 2.775565452982821549e-77 +1.995665221740580364e+00 2.325298587951903982e-77 +1.996665555185061836e+00 1.947902726623215676e-77 +1.997665888629543307e+00 1.631612734344760924e-77 +1.998666222074024779e+00 1.366558381354503928e-77 +1.999666555518506250e+00 1.144459891992841720e-77 +2.000666888962987944e+00 9.583722890575861543e-78 +2.001667222407469193e+00 8.024707321926686870e-78 +2.002667555851950887e+00 6.718702932765383213e-78 +2.003667889296432136e+00 5.624746579564623706e-78 +2.004668222740913830e+00 4.708491221540496132e-78 +2.005668556185395079e+00 3.941139919864279468e-78 +2.006668889629876773e+00 3.298551173365151707e-78 +2.007669223074358023e+00 2.760488120028275330e-78 +2.008669556518839716e+00 2.309988527038997509e-78 +2.009669889963321410e+00 1.932836184717933158e-78 +2.010670223407802659e+00 1.617117422889275905e-78 +2.011670556852284353e+00 1.352849075908989201e-78 +2.012670890296765602e+00 1.131666413644390008e-78 +2.013671223741247296e+00 9.465613965464889754e-79 +2.014671557185728545e+00 7.916631594335522465e-79 +2.015671890630210239e+00 6.620539276482687110e-79 +2.016672224074691488e+00 5.536146603695690491e-79 +2.017672557519173182e+00 4.628956322313532587e-79 +2.018672890963654432e+00 3.870079339581342036e-79 +2.019673224408136125e+00 3.235325189196800201e-79 +2.020673557852617819e+00 2.704439650706208195e-79 +2.021673891297099068e+00 2.260465772465197702e-79 +2.022674224741580762e+00 1.889208371740221318e-79 +2.023674558186062011e+00 1.578785295250074017e-79 +2.024674891630543705e+00 1.319251417420915380e-79 +2.025675225075024954e+00 1.102283614541412151e-79 +2.026675558519506648e+00 9.209168502663970840e-80 +2.027675891963987898e+00 7.693230999052144905e-80 +2.028676225408469591e+00 6.426261766145903384e-80 +2.029676558852951285e+00 5.367466431805140075e-80 +2.030676892297432534e+00 4.482719330609610827e-80 +2.031677225741914228e+00 3.743475928110844210e-80 +2.032677559186395477e+00 3.125862192903867130e-80 +2.033677892630877171e+00 2.609912196151813414e-80 +2.034678226075358420e+00 2.178929868556660371e-80 +2.035678559519840114e+00 1.818954743946088716e-80 +2.036678892964321363e+00 1.518314787749506824e-80 +2.037679226408803057e+00 1.267252149178814617e-80 +2.038679559853284307e+00 1.057609973202717643e-80 +2.039679893297766000e+00 8.825703338915493376e-81 +2.040680226742247694e+00 7.364349644685746963e-81 +2.041680560186728943e+00 6.144418117482233409e-81 +2.042680893631210637e+00 5.126115758356952908e-81 +2.043681227075691886e+00 4.276193454253390791e-81 +2.044681560520173580e+00 3.566872344796342032e-81 +2.045681893964654829e+00 2.974945924314870808e-81 +2.046682227409136523e+00 2.481029182135559269e-81 +2.047682560853617773e+00 2.068930759166742463e-81 +2.048682894298099466e+00 1.725128013975110209e-81 +2.049683227742581160e+00 1.438328170188268060e-81 +2.050683561187062409e+00 1.199101462324696823e-81 +2.051683894631544103e+00 9.995744956652521390e-82 +2.052684228076025352e+00 8.331739600277616853e-82 +2.053684561520507046e+00 6.944124481007979644e-82 +2.054684894964988295e+00 5.787094772739523476e-82 +2.055685228409469989e+00 4.822419423333978280e-82 +2.056685561853951238e+00 4.018191707562179442e-82 +2.057685895298432932e+00 3.347785425682550736e-82 +2.058686228742914626e+00 2.788982979392864131e-82 +2.059686562187395875e+00 2.323247088812599678e-82 +2.060686895631877569e+00 1.935112542002314155e-82 +2.061687229076358818e+00 1.611678239670813304e-82 +2.062687562520840512e+00 1.342183035598815145e-82 +2.063687895965321761e+00 1.117651581283900713e-82 +2.064688229409803455e+00 9.305986478745616005e-83 +2.065688562854284704e+00 7.747822920718725187e-83 +2.066688896298766398e+00 6.449978159296632602e-83 +2.067689229743247648e+00 5.369057941501530215e-83 +2.068689563187729341e+00 4.468885489894760402e-83 +2.069689896632211035e+00 3.719303778123567690e-83 +2.070690230076692284e+00 3.095176113874477334e-83 +2.071690563521173978e+00 2.575552270799264739e-83 +2.072690896965655227e+00 2.142972809826899907e-83 +2.073691230410136921e+00 1.782888741421551659e-83 +2.074691563854618170e+00 1.483177449430572904e-83 +2.075691897299099864e+00 1.233738945925200889e-83 +2.076692230743581113e+00 1.026159156775200109e-83 +2.077692564188062807e+00 8.534291347184711737e-84 +2.078692897632544501e+00 7.097109316550170120e-84 +2.079693231077025750e+00 5.901423942922737687e-84 +2.080693564521507444e+00 4.906744269017067075e-84 +2.081693897965988693e+00 4.079353333930718907e-84 +2.082694231410470387e+00 3.391177429487168219e-84 +2.083694564854951636e+00 2.818843681506845296e-84 +2.084694898299433330e+00 2.342894661504156184e-84 +2.085695231743914579e+00 1.947133922761951464e-84 +2.086695565188396273e+00 1.618080684922486065e-84 +2.087695898632877523e+00 1.344515504850765260e-84 +2.088696232077359216e+00 1.117101786830091583e-84 +2.089696565521840910e+00 9.280705010205964074e-85 +2.090696898966322159e+00 7.709575780426611134e-85 +2.091697232410803853e+00 6.403851985013742317e-85 +2.092697565855285102e+00 5.318796567918404934e-85 +2.093697899299766796e+00 4.417196966785214036e-85 +2.094698232744248045e+00 3.668102320639034518e-85 +2.095698566188729739e+00 3.045772135358525820e-85 +2.096698899633210988e+00 2.528801076852232923e-85 +2.097699233077692682e+00 2.099390451456921254e-85 +2.098699566522174376e+00 1.742741843124786174e-85 +2.099699899966655625e+00 1.446552469953959102e-85 +2.100700233411137319e+00 1.200595234244051902e-85 +2.101700566855618568e+00 9.963692836537491048e-86 +2.102700900300100262e+00 8.268092706514765826e-86 +2.103701233744581511e+00 6.860434720416865173e-86 +2.104701567189063205e+00 5.691925756120529754e-86 +2.105701900633544454e+00 4.722023116969163090e-86 +2.106702234078026148e+00 3.917042493723701909e-86 +2.107702567522507398e+00 3.249000282167803062e-86 +2.108702900966989091e+00 2.694650888502980187e-86 +2.109703234411470785e+00 2.234686253149594390e-86 +2.110703567855952034e+00 1.853070318510087921e-86 +2.111703901300433728e+00 1.536485741863806534e-86 +2.112704234744914977e+00 1.273873964257646216e-86 +2.113704568189396671e+00 1.056052917978434770e-86 +2.114704901633877920e+00 8.753992955361590572e-87 +2.115705235078359614e+00 7.255845008583668418e-87 +2.116705568522840863e+00 6.013552326390684334e-87 +2.117705901967322557e+00 4.983511721319470178e-87 +2.118706235411804251e+00 4.129535145202281834e-87 +2.119706568856285500e+00 3.421591371084296376e-87 +2.120706902300767194e+00 2.834760746140712049e-87 +2.121707235745248443e+00 2.348367014671180533e-87 +2.122707569189730137e+00 1.945256279714640242e-87 +2.123707902634211386e+00 1.611198220301455304e-87 +2.124708236078693080e+00 1.334388880180748757e-87 +2.125708569523174329e+00 1.105037835737839683e-87 +2.126708902967656023e+00 9.150254544800073791e-88 +2.127709236412137273e+00 7.576183698156653665e-88 +2.128709569856618966e+00 6.272333051297389885e-88 +2.129709903301100660e+00 5.192410488710986581e-88 +2.130710236745581909e+00 4.298037694780074207e-88 +2.131710570190063603e+00 3.557400119038565771e-88 +2.132710903634544852e+00 2.944126757070251944e-88 +2.133711237079026546e+00 2.436360709563241184e-88 +2.134711570523507795e+00 2.015988098791322591e-88 +2.135711903967989489e+00 1.667998419443628102e-88 +2.136712237412470738e+00 1.379953968180315299e-88 +2.137712570856952432e+00 1.141549790547564919e-88 +2.138712904301434126e+00 9.442487355902500368e-89 +2.139713237745915375e+00 7.809788261911031819e-89 +2.140713571190397069e+00 6.458823271555494303e-89 +2.141713904634878318e+00 5.341076983784808188e-89 +2.142714238079360012e+00 4.416371194592595948e-89 +2.143714571523841261e+00 3.651435167339890312e-89 +2.144714904968322955e+00 3.018720569457241186e-89 +2.145715238412804204e+00 2.495419294988543626e-89 +2.146715571857285898e+00 2.062649511863506174e-89 +2.147715905301767148e+00 1.704781181702961839e-89 +2.148716238746248841e+00 1.408877203673768609e-89 +2.149716572190730535e+00 1.164230403116342080e-89 +2.150716905635211784e+00 9.619799620193411126e-90 +2.151717239079693478e+00 7.947936896680879008e-90 +2.152717572524174727e+00 6.566048556739825361e-90 +2.153717905968656421e+00 5.423942352750226185e-90 +2.154718239413137670e+00 4.480096156988980970e-90 +2.155718572857619364e+00 3.700163384383437112e-90 +2.156718906302100613e+00 3.055735519770035839e-90 +2.157719239746582307e+00 2.523317613726969317e-90 +2.158719573191064001e+00 2.083480171720591005e-90 +2.159719906635545250e+00 1.720157129508110878e-90 +2.160720240080026944e+00 1.420064804024888976e-90 +2.161720573524508193e+00 1.172221016204118295e-90 +2.162720906968989887e+00 9.675471521565298083e-91 +2.163721240413471136e+00 7.985388877936288382e-91 +2.164721573857952830e+00 6.589937537528802075e-91 +2.165721907302434079e+00 5.437857490566782930e-91 +2.166722240746915773e+00 4.486788951490349747e-91 +2.167722574191397023e+00 3.701730164329299113e-91 +2.168722907635878716e+00 3.053761885833225630e-91 +2.169723241080360410e+00 2.518992527384894894e-91 +2.170723574524841659e+00 2.077685861394151602e-91 +2.171723907969323353e+00 1.713539760791698788e-91 +2.172724241413804602e+00 1.413089874828113575e-91 +2.173724574858286296e+00 1.165216644265979182e-91 +2.174724908302767545e+00 9.607377846417724518e-92 +2.175725241747249239e+00 7.920714505076058752e-92 +2.176725575191730488e+00 6.529578466416487405e-92 +2.177725908636212182e+00 5.382291653977461276e-92 +2.178726242080693876e+00 4.436194783031924891e-92 +2.179726575525175125e+00 3.656076573478885495e-92 +2.180726908969656819e+00 3.012875990502079833e-92 +2.181727242414138068e+00 2.482610153149127858e-92 +2.182727575858619762e+00 2.045488749229208584e-92 +2.183727909303101011e+00 1.685182576961795163e-92 +2.184728242747582705e+00 1.388219443732281992e-92 +2.185728576192063954e+00 1.143485292313676723e-92 +2.186728909636545648e+00 9.418122623001280447e-93 +2.187729243081026897e+00 7.756385678524329619e-93 +2.188729576525508591e+00 6.387276968116969338e-93 +2.189729909969990285e+00 5.259366057405818749e-93 +2.190730243414471534e+00 4.330243790292784000e-93 +2.191730576858953228e+00 3.564943028718164680e-93 +2.192730910303434477e+00 2.934635304542768852e-93 +2.193731243747916171e+00 2.415555276027234095e-93 +2.194731577192397420e+00 1.988113252333108817e-93 +2.195731910636879114e+00 1.636162966775771975e-93 +2.196732244081360363e+00 1.346397495193187381e-93 +2.197732577525842057e+00 1.107850938024141234e-93 +2.198732910970323751e+00 9.114873858720617713e-94 +2.199733244414805000e+00 7.498619108823186060e-94 +2.200733577859286694e+00 6.168409880226782906e-94 +2.201733911303767943e+00 5.073719487030791155e-94 +2.202734244748249637e+00 4.172928846168585885e-94 +2.203734578192730886e+00 3.431759187986394134e-94 +2.204734911637212580e+00 2.821979986663181723e-94 +2.205735245081693829e+00 2.320343877265753546e-94 +2.206735578526175523e+00 1.907708762319582224e-94 +2.207735911970656772e+00 1.568314274390210972e-94 +2.208736245415138466e+00 1.289185508725859741e-94 +2.209736578859620160e+00 1.059641683503074543e-94 +2.210736912304101409e+00 8.708912996428209161e-95 +2.211737245748583103e+00 7.156986021661339481e-95 +2.212737579193064352e+00 5.881088100507884409e-95 +2.213737912637546046e+00 4.832217801652765174e-95 +2.214738246082027295e+00 3.970055845575875850e-95 +2.215738579526508989e+00 3.261429764002328867e-95 +2.216738912970990238e+00 2.679049537842498806e-95 +2.217739246415471932e+00 2.200466481397369888e-95 +2.218739579859953626e+00 1.807216031920313345e-95 +2.219739913304434875e+00 1.484112022652537575e-95 +2.220740246748916569e+00 1.218665722047930045e-95 +2.221740580193397818e+00 1.000607624765603899e-95 +2.222740913637879512e+00 8.214938567003260965e-96 +2.223741247082360761e+00 6.743822517005761074e-96 +2.224741580526842455e+00 5.535657911649371124e-96 +2.225741913971323704e+00 4.543532680262828928e-96 +2.226742247415805398e+00 3.728888250240654938e-96 +2.227742580860287092e+00 3.060034907191948596e-96 +2.228742914304768341e+00 2.510930507292460755e-96 +2.229743247749250035e+00 2.060175917797889115e-96 +2.230743581193731284e+00 1.690188799903446771e-96 +2.231743914638212978e+00 1.386524133574453451e-96 +2.232744248082694227e+00 1.137315472321740961e-96 +2.233744581527175921e+00 9.328155178983827214e-97 +2.234744914971657170e+00 7.650183943343078703e-97 +2.235745248416138864e+00 6.273491207533608432e-97 +2.236745581860620113e+00 5.144083510877249702e-97 +2.237745915305101807e+00 4.217625633295005449e-97 +2.238746248749583501e+00 3.457716214973726917e-97 +2.239746582194064750e+00 2.834470660460383616e-97 +2.240746915638546444e+00 2.323356674103886101e-97 +2.241747249083027693e+00 1.904237475497017992e-97 +2.242747582527509387e+00 1.560585726448435401e-97 +2.243747915971990636e+00 1.278837768988221056e-97 +2.244748249416472330e+00 1.047863177234892556e-97 +2.245748582860953579e+00 8.585290707805027677e-98 +2.246748916305435273e+00 7.033422932420337779e-98 +2.247749249749916967e+00 5.761555665402381646e-98 +2.248749583194398216e+00 4.719262042844601649e-98 +2.249749916638879910e+00 3.865180010665945260e-98 +2.250750250083361159e+00 3.165385864134551091e-98 +2.251750583527842853e+00 2.592059067483461359e-98 +2.252750916972324102e+00 2.122386287548839640e-98 +2.253751250416805796e+00 1.737661864359381822e-98 +2.254751583861287045e+00 1.422549576589289549e-98 +2.255751917305768739e+00 1.164476834646805447e-98 +2.256752250750249988e+00 9.531375907758971064e-99 +2.257752584194731682e+00 7.800844927670247931e-99 +2.258752917639213376e+00 6.383942893410612955e-99 +2.259753251083694625e+00 5.223933554964029446e-99 +2.260753584528176319e+00 4.274325557544620295e-99 +2.261753917972657568e+00 3.497025932649606245e-99 +2.262754251417139262e+00 2.860825779316091342e-99 +2.263754584861620511e+00 2.340158485908269363e-99 +2.264754918306102205e+00 1.914081533519465838e-99 +2.265755251750583454e+00 1.565441700459504232e-99 +2.266755585195065148e+00 1.280190694658933437e-99 +2.267755918639546842e+00 1.046824157843399412e-99 +2.268756252084028091e+00 8.559218425347866677e-100 +2.269756585528509785e+00 6.997707498225885256e-100 +2.270756918972991034e+00 5.720562880442408752e-100 +2.271757252417472728e+00 4.676091978955147386e-100 +2.272757585861953977e+00 3.821981931040057938e-100 +2.273757919306435671e+00 3.123600738921296028e-100 +2.274758252750916920e+00 2.552605539044267556e-100 +2.275758586195398614e+00 2.085802582420000598e-100 +2.276758919639879863e+00 1.704213485241142801e-100 +2.277759253084361557e+00 1.392310497014354930e-100 +2.278759586528843251e+00 1.137390248865562058e-100 +2.279759919973324500e+00 9.290609517726797519e-101 +2.280760253417806194e+00 7.588225303034312351e-101 +2.281760586862287443e+00 6.197228800479933107e-101 +2.282760920306769137e+00 5.060764725220564211e-101 +2.283761253751250386e+00 4.132340198124998988e-101 +2.284761587195732080e+00 3.373939510500502359e-101 +2.285761920640213329e+00 2.754481246833608612e-101 +2.286762254084695023e+00 2.248555707201465781e-101 +2.287762587529176717e+00 1.835391799464604566e-101 +2.288762920973657966e+00 1.498011771929255853e-101 +2.289763254418139660e+00 1.222539695483159936e-101 +2.290763587862620909e+00 9.976357799877623120e-102 +2.291763921307102603e+00 8.140336687785053776e-102 +2.292764254751583852e+00 6.641619990417021873e-102 +2.293764588196065546e+00 5.418349098196770601e-102 +2.294764921640546795e+00 4.419989589306195704e-102 +2.295765255085028489e+00 3.605261861901741734e-102 +2.296765588529509738e+00 2.940449240198204542e-102 +2.297765921973991432e+00 2.398014823914329689e-102 +2.298766255418473126e+00 1.955470842257786407e-102 +2.299766588862954375e+00 1.594454499092735396e-102 +2.300766922307436069e+00 1.299972665611414596e-102 +2.301767255751917318e+00 1.059784626868467928e-102 +2.302767589196399012e+00 8.638976943478431865e-103 +2.303767922640880261e+00 7.041550839087622994e-103 +2.304768256085361955e+00 5.738992117381122931e-103 +2.305768589529843204e+00 4.676966316537771382e-103 +2.306768922974324898e+00 3.811133491416487465e-103 +2.307769256418806592e+00 3.105313023428841701e-103 +2.308769589863287841e+00 2.529984808848038040e-103 +2.309769923307769535e+00 2.061065279820322719e-103 +2.310770256752250784e+00 1.678907952842120974e-103 +2.311770590196732478e+00 1.367487390739908804e-103 +2.312770923641213727e+00 1.113732979194096969e-103 +2.313771257085695421e+00 9.069850626794812486e-104 +2.314771590530176670e+00 7.385510071319395270e-104 +2.315771923974658364e+00 6.013428619615560458e-104 +2.316772257419139613e+00 4.895816494152151875e-104 +2.317772590863621307e+00 3.985560514661533835e-104 +2.318772924308103001e+00 3.244255052550013817e-104 +2.319773257752584250e+00 2.640595491003983453e-104 +2.320773591197065944e+00 2.149067570723692283e-104 +2.321773924641547193e+00 1.748878222637990676e-104 +2.322774258086028887e+00 1.423083472353727118e-104 +2.323774591530510136e+00 1.157877155623941104e-104 +2.324774924974991830e+00 9.420108441467856915e-105 +2.325775258419473079e+00 7.663208199883347379e-105 +2.326775591863954773e+00 6.233423782898674971e-105 +2.327775925308436467e+00 5.069953643467118204e-105 +2.328776258752917716e+00 4.123278118787393245e-105 +2.329776592197399410e+00 3.353069663482449952e-105 +2.330776925641880659e+00 2.726489501698028793e-105 +2.331777259086362353e+00 2.216799370385676941e-105 +2.332777592530843602e+00 1.802230169002135477e-105 +2.333777925975325296e+00 1.465060056341771932e-105 +2.334778259419806545e+00 1.190863287157539853e-105 +2.335778592864288239e+00 9.678982220495815357e-106 +2.336778926308769488e+00 7.866087699196372820e-106 +2.337779259753251182e+00 6.392182747944277147e-106 +2.338779593197732876e+00 5.193987354322890933e-106 +2.339779926642214125e+00 4.220014079619236031e-106 +2.340780260086695819e+00 3.428374204385135160e-106 +2.341780593531177068e+00 2.784991310060186915e-106 +2.342780926975658762e+00 2.262146759816059527e-106 +2.343781260420140011e+00 1.837295521597486203e-106 +2.344781593864621705e+00 1.492102173369624080e-106 +2.345781927309102954e+00 1.211656221231646590e-106 +2.346782260753584648e+00 9.838334338720078347e-107 +2.347782594198066342e+00 7.987760689377635430e-107 +2.348782927642547591e+00 6.484698968642617149e-107 +2.349783261087029285e+00 5.264000265599824621e-107 +2.350783594531510534e+00 4.272708771135470151e-107 +2.351783927975992228e+00 3.467783607186005865e-107 +2.352784261420473477e+00 2.814245587318417141e-107 +2.353784594864955171e+00 2.283669784866917580e-107 +2.354784928309436420e+00 1.852959492616847167e-107 +2.355785261753918114e+00 1.503349136576381156e-107 +2.356785595198399363e+00 1.219593461332835249e-107 +2.357785928642881057e+00 9.893082478450849463e-108 +2.358786262087362751e+00 8.024342921673018235e-108 +2.359786595531844000e+00 6.508016391825448529e-108 +2.360786928976325694e+00 5.277753519902261400e-108 +2.361787262420806943e+00 4.279675868392732128e-108 +2.362787595865288637e+00 3.470035834761427558e-108 +2.363787929309769886e+00 2.813314920967725951e-108 +2.364788262754251580e+00 2.280678389810658737e-108 +2.365788596198732829e+00 1.848719633775273094e-108 +2.366788929643214523e+00 1.498440034504166389e-108 +2.367789263087696217e+00 1.214420221796804043e-108 +2.368789596532177466e+00 9.841468822107121574e-109 +2.369789929976659160e+00 7.974659707780448234e-109 +2.370790263421140409e+00 6.461386314831793701e-109 +2.371790596865622103e+00 5.234805661370064827e-109 +2.372790930310103352e+00 4.240691969562026914e-109 +2.373791263754585046e+00 3.435058999612117658e-109 +2.374791597199066295e+00 2.782229699862363814e-109 +2.375791930643547989e+00 2.253269159381735422e-109 +2.376792264088029238e+00 1.824712602015135347e-109 +2.377792597532510932e+00 1.477532964825019959e-109 +2.378792930976992626e+00 1.196303010238117620e-109 +2.379793264421473875e+00 9.685153781423423402e-110 +2.380793597865955569e+00 7.840308562725993609e-110 +2.381793931310436818e+00 6.346307309591593569e-110 +2.382794264754918512e+00 5.136536168091173195e-110 +2.383794598199399761e+00 4.157008492456737279e-110 +2.384794931643881455e+00 3.363975174415218922e-110 +2.385795265088362704e+00 2.721986466903021235e-110 +2.386795598532844398e+00 2.202320140157653557e-110 +2.387795931977326092e+00 1.781706844870065380e-110 +2.388796265421807341e+00 1.441296578963338634e-110 +2.389796598866289035e+00 1.165820727519069019e-110 +2.390796932310770284e+00 9.429127291254030745e-111 +2.391797265755251978e+00 7.625573901130708212e-111 +2.392797599199733227e+00 6.166445243339086419e-111 +2.393797932644214921e+00 4.986071869170812449e-111 +2.394798266088696170e+00 4.031284961790015769e-111 +2.395798599533177864e+00 3.259040612679783389e-111 +2.396798932977659558e+00 2.634494863848067823e-111 +2.397799266422140807e+00 2.129444128602226496e-111 +2.398799599866622501e+00 1.721061751644739822e-111 +2.399799933311103750e+00 1.390874568436610560e-111 +2.400800266755585444e+00 1.123933948878009400e-111 +2.401800600200066693e+00 9.081444278894098730e-112 +2.402800933644548387e+00 7.337200141081853854e-112 +2.403801267089029636e+00 5.927439350506611267e-112 +2.404801600533511330e+00 4.788121721793301870e-112 +2.405801933977992579e+00 3.867448648370506298e-112 +2.406802267422474273e+00 3.123526822274482214e-112 +2.407802600866955967e+00 2.522477108576725462e-112 +2.408802934311437216e+00 2.036903892430134919e-112 +2.409803267755918910e+00 1.644656299533747015e-112 +2.410803601200400159e+00 1.327825724168840445e-112 +2.411803934644881853e+00 1.071934660145330605e-112 +2.412804268089363102e+00 8.652803871537158553e-113 +2.413804601533844796e+00 6.984039977803815600e-113 +2.414804934978326045e+00 5.636608666819365436e-113 +2.415805268422807739e+00 4.548732126949961017e-113 +2.416805601867289433e+00 3.670490893742309198e-113 +2.417805935311770682e+00 2.961551236131302340e-113 +2.418806268756252376e+00 2.389327386014525590e-113 +2.419806602200733625e+00 1.927495541687449301e-113 +2.420806935645215319e+00 1.554792419002441036e-113 +2.421807269089696568e+00 1.254043950703315783e-113 +2.422807602534178262e+00 1.011380117648880629e-113 +2.423807935978659511e+00 8.156003002791047076e-114 +2.424808269423141205e+00 6.576603410861223408e-114 +2.425808602867622454e+00 5.302580140143492882e-114 +2.426808936312104148e+00 4.274980515793039439e-114 +2.427809269756585842e+00 3.446214851467141270e-114 +2.428809603201067091e+00 2.777869702299302465e-114 +2.429809936645548785e+00 2.238941280506354690e-114 +2.430810270090030034e+00 1.804408431756944666e-114 +2.431810603534511728e+00 1.454080007941822308e-114 +2.432810936978992977e+00 1.171663957559722252e-114 +2.433811270423474671e+00 9.440155520821458489e-115 +2.434811603867955920e+00 7.605303315736228424e-115 +2.435811937312437614e+00 6.126539546549095900e-115 +2.436812270756919308e+00 4.934864753935485367e-115 +2.437812604201400557e+00 3.974628846012168831e-115 +2.438812937645882251e+00 3.200952409339085481e-115 +2.439813271090363500e+00 2.577645363575771600e-115 +2.440813604534845194e+00 2.075527189774890916e-115 +2.441813937979326443e+00 1.671071374858668884e-115 +2.442814271423808137e+00 1.345311593033241907e-115 +2.443814604868289386e+00 1.082959166148876275e-115 +2.444814938312771080e+00 8.716910569969585574e-116 +2.445815271757252329e+00 7.015754951704664961e-116 +2.446815605201734023e+00 5.646086724610827625e-116 +2.447815938646215717e+00 4.543410633478501256e-116 +2.448816272090696966e+00 3.655760599372156312e-116 +2.449816605535178660e+00 2.941269489231173366e-116 +2.450816938979659909e+00 2.366209536144545839e-116 +2.451817272424141603e+00 1.903412403812096869e-116 +2.452817605868622852e+00 1.530995458429996908e-116 +2.453817939313104546e+00 1.231334994260502734e-116 +2.454818272757585795e+00 9.902386073116538568e-117 +2.455818606202067489e+00 7.962781513327311918e-117 +2.456818939646549182e+00 6.402521670826061201e-117 +2.457819273091030432e+00 5.147526930608167192e-117 +2.458819606535512126e+00 4.138162210831523303e-117 +2.459819939979993375e+00 3.326424772805001485e-117 +2.460820273424475069e+00 2.673678692312795436e-117 +2.461820606868956318e+00 2.148829890977186589e-117 +2.462820940313438012e+00 1.726856187507165086e-117 +2.463821273757919261e+00 1.387623411592943975e-117 +2.464821607202400955e+00 1.114931996329940114e-117 +2.465821940646882204e+00 8.957492487379202843e-118 +2.466822274091363898e+00 7.195911927734595674e-118 +2.467822607535845592e+00 5.780248892119114609e-118 +2.468822940980326841e+00 4.642677878614851055e-118 +2.469823274424808535e+00 3.728652227890291972e-118 +2.470823607869289784e+00 2.994308327585717662e-118 +2.471823941313771478e+00 2.404376480751599119e-118 +2.472824274758252727e+00 1.930499695598994899e-118 +2.473824608202734421e+00 1.549880867979525284e-118 +2.474824941647215670e+00 1.244194309153518668e-118 +2.475825275091697364e+00 9.987100435946657535e-119 +2.476825608536179057e+00 8.015893487755505238e-119 +2.477825941980660307e+00 6.433181018278566241e-119 +2.478826275425142001e+00 5.162510162095500051e-119 +2.479826608869623250e+00 4.142451009565903836e-119 +2.480826942314104944e+00 3.323649023493343273e-119 +2.481827275758586193e+00 2.666454916200600717e-119 +2.482827609203067887e+00 2.139019028208720806e-119 +2.483827942647549136e+00 1.715759312303484156e-119 +2.484828276092030830e+00 1.376129786744657750e-119 +2.485828609536512079e+00 1.103630616633383701e-119 +2.486828942980993773e+00 8.850124877852304831e-120 +2.487829276425475467e+00 7.096371965845532673e-120 +2.488829609869956716e+00 5.689638300315398734e-120 +2.489829943314438410e+00 4.561359052198075145e-120 +2.490830276758919659e+00 3.656496607567090643e-120 +2.491830610203401353e+00 2.930875728447265819e-120 +2.492830943647882602e+00 2.349042899884046674e-120 +2.493831277092364296e+00 1.882546907982236459e-120 +2.494831610536845545e+00 1.508557884570745600e-120 +2.495831943981327239e+00 1.208758291356521712e-120 +2.496832277425808932e+00 9.684523729651948283e-121 +2.497832610870290182e+00 7.758511062110448548e-121 +2.498832944314771876e+00 6.214981129266813808e-121 +2.499833277759253125e+00 4.978087885378764120e-121 +2.500833611203734819e+00 3.987003523614804040e-121 +2.501833944648216068e+00 3.192949090872113808e-121 +2.502834278092697762e+00 2.556811337927288703e-121 +2.503834611537179011e+00 2.047230238471954609e-121 +2.504834944981660705e+00 1.639064358519745065e-121 +2.505835278426141954e+00 1.312159534694605323e-121 +2.506835611870623648e+00 1.050361003346296220e-121 +2.507835945315105342e+00 8.407209178512341316e-122 +2.508836278759586591e+00 6.728626659675766156e-122 +2.509836612204068285e+00 5.384710087329216792e-122 +2.510836945648549534e+00 4.308831736350545251e-122 +2.511837279093031228e+00 3.447609422342358955e-122 +2.512837612537512477e+00 2.758277128533096673e-122 +2.513837945981994171e+00 2.206576826287875391e-122 +2.514838279426475420e+00 1.765068330318160580e-122 +2.515838612870957114e+00 1.411774417525548513e-122 +2.516838946315438807e+00 1.129094802923208703e-122 +2.517839279759920057e+00 9.029356999763791761e-123 +2.518839613204401751e+00 7.220122328497131807e-123 +2.519839946648883000e+00 5.772894258773239885e-123 +2.520840280093364694e+00 4.615342818724079365e-123 +2.521840613537845943e+00 3.689569055114199454e-123 +2.522840946982327637e+00 2.949229958233679855e-123 +2.523841280426808886e+00 2.357235354502220318e-123 +2.524841613871290580e+00 1.883903145280272489e-123 +2.525841947315771829e+00 1.505481808267653057e-123 +2.526842280760253523e+00 1.202967151701511042e-123 +2.527842614204735217e+00 9.611548042987983964e-124 +2.528842947649216466e+00 7.678815439574479934e-124 +2.529843281093698160e+00 6.134178842509808081e-124 +2.530843614538179409e+00 4.899818058563903561e-124 +2.531843947982661103e+00 3.913495062801597390e-124 +2.532844281427142352e+00 3.125438391964962799e-124 +2.533844614871624046e+00 2.495849599079219021e-124 +2.534844948316105295e+00 1.992907748062584494e-124 +2.535845281760586989e+00 1.591172617480417190e-124 +2.536845615205068682e+00 1.270307064762617240e-124 +2.537845948649549932e+00 1.014054850387486813e-124 +2.538846282094031626e+00 8.094229169688066414e-125 +2.539846615538512875e+00 6.460272871042732741e-125 +2.540846948982994569e+00 5.155698883291813068e-125 +2.541847282427475818e+00 4.114201360072574578e-125 +2.542847615871957512e+00 3.282803280275426052e-125 +2.543847949316438761e+00 2.619180872769619831e-125 +2.544848282760920455e+00 2.089524361667991692e-125 +2.545848616205401704e+00 1.666827680097553272e-125 +2.546848949649883398e+00 1.329521257540903788e-125 +2.547849283094365092e+00 1.060379160386007528e-125 +2.548849616538846341e+00 8.456456107938282923e-126 +2.549849949983328035e+00 6.743369090760251089e-126 +2.550850283427809284e+00 5.376835869831725173e-126 +2.551850616872290978e+00 4.286846621575138831e-126 +2.552850950316772227e+00 3.417514986517827098e-126 +2.553851283761253921e+00 2.724232860080380558e-126 +2.554851617205735170e+00 2.171397550490039660e-126 +2.555851950650216864e+00 1.730596324581578420e-126 +2.556852284094698557e+00 1.379156426824268015e-126 +2.557852617539179807e+00 1.098987101375781008e-126 +2.558852950983661501e+00 8.756548955118976253e-127 +2.559853284428142750e+00 6.976453148921679538e-127 +2.560853617872624444e+00 5.557733286017169966e-127 +2.561853951317105693e+00 4.427127582504349185e-127 +2.562854284761587387e+00 3.526206094136173328e-127 +2.563854618206068636e+00 2.808372184293210588e-127 +2.564854951650550330e+00 2.236469354182990125e-127 +2.565855285095032023e+00 1.780871392609016814e-127 +2.566855618539513273e+00 1.417958372028869631e-127 +2.567855951983994967e+00 1.128900647796035464e-127 +2.568856285428476216e+00 8.986886999117882676e-128 +2.569856618872957910e+00 7.153591841093375619e-128 +2.570856952317439159e+00 5.693775654551710747e-128 +2.571857285761920853e+00 4.531456995054893192e-128 +2.572857619206402102e+00 3.606091084194067338e-128 +2.573857952650883796e+00 2.869437959597078213e-128 +2.574858286095365045e+00 2.283065147036795244e-128 +2.575858619539846739e+00 1.816356477478012848e-128 +2.576858952984328432e+00 1.444924608978909800e-128 +2.577859286428809682e+00 1.149345517500269951e-128 +2.578859619873291376e+00 9.141497462130089183e-129 +2.579859953317772625e+00 7.270183919455976158e-129 +2.580860286762254319e+00 5.781423326638451756e-129 +2.581860620206735568e+00 4.597115985988600863e-129 +2.582860953651217262e+00 3.655084908417641634e-129 +2.583861287095698511e+00 2.905833975510551943e-129 +2.584861620540180205e+00 2.309965228949930619e-129 +2.585861953984661898e+00 1.836121458998837009e-129 +2.586862287429143148e+00 1.459347398381997666e-129 +2.587862620873624842e+00 1.159784468466928612e-129 +2.588862954318106091e+00 9.216312819972401924e-130 +2.589863287762587785e+00 7.323158673045194095e-130 +2.590863621207069034e+00 5.818365504643055476e-130 +2.591863954651550728e+00 4.622371787595728658e-130 +2.592864288096031977e+00 3.671893415597679290e-130 +2.593864621540513671e+00 2.916598026385666194e-130 +2.594864954984994920e+00 2.316457877398708107e-130 +2.595865288429476614e+00 1.839643015133548184e-130 +2.596865621873958307e+00 1.460844611683666058e-130 +2.597865955318439557e+00 1.159940757905496058e-130 +2.598866288762921251e+00 9.209348621950326486e-131 +2.599866622207402500e+00 7.311110318756319175e-131 +2.600866955651884194e+00 5.803621480821227401e-131 +2.601867289096365443e+00 4.606553726771273085e-131 +2.602867622540847137e+00 3.656070157612851773e-131 +2.603867955985328386e+00 2.901444182974401455e-131 +2.604868289429810080e+00 2.302370639524469192e-131 +2.605868622874291773e+00 1.826827647464339817e-131 +2.606868956318773023e+00 1.449376555352066729e-131 +2.607869289763254717e+00 1.149810333903845468e-131 +2.608869623207735966e+00 9.120790960291125387e-132 +2.609869956652217660e+00 7.234359993067737983e-132 +2.610870290096698909e+00 5.737583900901798919e-132 +2.611870623541180603e+00 4.550082820537880061e-132 +2.612870956985661852e+00 3.608036094502720663e-132 +2.613871290430143546e+00 2.860775413799343197e-132 +2.614871623874624795e+00 2.268077943879005222e-132 +2.615871957319106489e+00 1.798015806346407895e-132 +2.616872290763588182e+00 1.425247719605809120e-132 +2.617872624208069432e+00 1.129662009905596427e-132 +2.618872957652551126e+00 8.952988207391107946e-133 +2.619873291097032375e+00 7.094941528610864225e-133 +2.620873624541514069e+00 5.622001388395757843e-133 +2.621873957985995318e+00 4.454453155017773245e-133 +2.622874291430477012e+00 3.529060984552934354e-133 +2.623874624874958261e+00 2.795665778652054439e-133 +2.624874958319439955e+00 2.214484562854896364e-133 +2.625875291763921648e+00 1.753966861683402948e-133 +2.626875625208402898e+00 1.389093344050097035e-133 +2.627875958652884592e+00 1.100025611532183061e-133 +2.628876292097365841e+00 8.710347533002052900e-134 +2.629876625541847535e+00 6.896511894894643514e-134 +2.630876958986328784e+00 5.459901602951074139e-134 +2.631877292430810478e+00 4.322166197440950469e-134 +2.632877625875291727e+00 3.421207607782626419e-134 +2.633877959319773421e+00 2.707813292533126381e-134 +2.634878292764254670e+00 2.142985945367929800e-134 +2.635878626208736364e+00 1.695825845441400405e-134 +2.636878959653218057e+00 1.341851627592485290e-134 +2.637879293097699307e+00 1.061668825311927130e-134 +2.638879626542181001e+00 8.399142566514860374e-135 +2.639879959986662250e+00 6.644191808090716357e-135 +2.640880293431143944e+00 5.255459458456192953e-135 +2.641880626875625193e+00 4.156622072435015979e-135 +2.642880960320106887e+00 3.287242242792696628e-135 +2.643881293764588136e+00 2.599466347504979679e-135 +2.644881627209069830e+00 2.055407847317409978e-135 +2.645881960653551523e+00 1.625073999002822381e-135 +2.646882294098032773e+00 1.284723279613985181e-135 +2.647882627542514467e+00 1.015564161349976933e-135 +2.648882960986995716e+00 8.027243920779808000e-136 +2.649883294431477410e+00 6.344346206522057673e-136 +2.650883627875958659e+00 5.013818506981239195e-136 +2.651883961320440353e+00 3.961974398293216387e-136 +2.652884294764921602e+00 3.130516819717863052e-136 +2.653884628209403296e+00 2.473328142321984534e-136 +2.654884961653884545e+00 1.953928904870500381e-136 +2.655885295098366239e+00 1.543466106151649998e-136 +2.656885628542847932e+00 1.219120846126125910e-136 +2.657885961987329182e+00 9.628480140577643275e-137 +2.658886295431810876e+00 7.603788730160986154e-137 +2.659886628876292125e+00 6.004317724162786605e-137 +2.660886962320773819e+00 4.740876151479762855e-137 +2.661887295765255068e+00 3.742957330554631879e-137 +2.662887629209736762e+00 2.954829753737852046e-137 +2.663887962654218011e+00 2.332444787181782093e-137 +2.664888296098699705e+00 1.840990731046281732e-137 +2.665888629543181398e+00 1.452958309871342498e-137 +2.666888962987662648e+00 1.146610793735519001e-137 +2.667889296432144342e+00 9.047742244174055874e-138 +2.668889629876625591e+00 7.138808551084133879e-138 +2.669889963321107285e+00 5.632128723283949707e-138 +2.670890296765588534e+00 4.443045191388813375e-138 +2.671890630210070228e+00 3.504694851739610477e-138 +2.672890963654551477e+00 2.764273511926902828e-138 +2.673891297099033171e+00 2.180083537803767384e-138 +2.674891630543514420e+00 1.719200704370813217e-138 +2.675891963987996114e+00 1.355630548975159516e-138 +2.676892297432477807e+00 1.068851658427517404e-138 +2.677892630876959057e+00 8.426647859089773971e-139 +2.678892964321440751e+00 6.642836718145436477e-139 +2.679893297765922000e+00 5.236168713024084264e-139 +2.680893631210403694e+00 4.127005114488226568e-139 +2.681893964654884943e+00 3.252502991834471939e-139 +2.682894298099366637e+00 2.563077418165845405e-139 +2.683894631543847886e+00 2.019607923591800092e-139 +2.684894964988329580e+00 1.591232826500398629e-139 +2.685895298432811273e+00 1.253607885312051582e-139 +2.686895631877292523e+00 9.875316489296740456e-140 +2.687895965321774217e+00 7.778603827083967216e-140 +2.688896298766255466e+00 6.126516465692548880e-140 +2.689896632210737160e+00 4.824884032232051436e-140 +2.690896965655218409e+00 3.799456494061892754e-140 +2.691897299099700103e+00 2.991695545593370031e-140 +2.692897632544181352e+00 2.355454047306528830e-140 +2.693897965988663046e+00 1.854356365777980854e-140 +2.694898299433144295e+00 1.459731854675225262e-140 +2.695898632877625989e+00 1.148984827376993165e-140 +2.696898966322107682e+00 9.043089405535338192e-141 +2.697899299766588932e+00 7.116733086026412251e-141 +2.698899633211070626e+00 5.600229870517193303e-141 +2.699899966655551875e+00 4.406485568203636489e-141 +2.700900300100033569e+00 3.466890884710395145e-141 +2.701900633544514818e+00 2.727403017988792210e-141 +2.702900966988996512e+00 2.145456843735124815e-141 +2.703901300433477761e+00 1.687530271364668912e-141 +2.704901633877959455e+00 1.327225398003061822e-141 +2.705901967322441148e+00 1.043756320464938552e-141 +2.706902300766922398e+00 8.207575297977951025e-142 +2.707902634211404092e+00 6.453450094394031524e-142 +2.708902967655885341e+00 5.073765125635249529e-142 +2.709903301100367035e+00 3.988688169296929951e-142 +2.710903634544848284e+00 3.135386860416309490e-142 +2.711903967989329978e+00 2.464413084428062547e-142 +2.712904301433811227e+00 1.936855409635434596e-142 +2.713904634878292921e+00 1.522096610907598389e-142 +2.714904968322774170e+00 1.196047864800888324e-142 +2.715905301767255864e+00 9.397584173474128910e-143 +2.716905635211737557e+00 7.383209839678278998e-143 +2.717905968656218807e+00 5.800100548261958062e-143 +2.718906302100700501e+00 4.556036096443565814e-143 +2.719906635545181750e+00 3.578492516670603568e-143 +2.720906968989663444e+00 2.810440490445669657e-143 +2.721907302434144693e+00 2.207038949183672829e-143 +2.722907635878626387e+00 1.733033367661619278e-143 +2.723907969323107636e+00 1.360708742721863292e-143 +2.724908302767589330e+00 1.068279142556434685e-143 +2.725908636212071023e+00 8.386208323726374183e-144 +2.726908969656552273e+00 6.582757666845360053e-144 +2.727909303101033966e+00 5.166678189957814350e-144 +2.728909636545515216e+00 4.054863883927328572e-144 +2.729909969989996910e+00 3.182016826193761660e-144 +2.730910303434478159e+00 2.496835825725451660e-144 +2.731910636878959853e+00 1.959019803127730704e-144 +2.732910970323441102e+00 1.536911955302620787e-144 +2.733911303767922796e+00 1.205647848186250721e-144 +2.734911637212404489e+00 9.456997748033160609e-145 +2.735911970656885739e+00 7.417326896944200940e-145 +2.736912304101367432e+00 5.817051082954762234e-145 +2.737912637545848682e+00 4.561625832843312611e-145 +2.738912970990330376e+00 3.576825599521565510e-145 +2.739913304434811625e+00 2.804382154297721437e-145 +2.740913637879293319e+00 2.198558107701526496e-145 +2.741913971323774568e+00 1.723455308893254092e-145 +2.742914304768256262e+00 1.350900703617176538e-145 +2.743914638212737511e+00 1.058785911207403794e-145 +2.744914971657219205e+00 8.297632643598205377e-146 +2.745915305101700898e+00 6.502218774556688534e-146 +2.746915638546182148e+00 5.094836787827673385e-146 +2.747915971990663841e+00 3.991722100329681266e-146 +2.748916305435145091e+00 3.127171110123420597e-146 +2.749916638879626785e+00 2.449651570687566308e-146 +2.750916972324108034e+00 1.918749641584921228e-146 +2.751917305768589728e+00 1.502773857573572275e-146 +2.752917639213070977e+00 1.176874832455582466e-146 +2.753917972657552671e+00 9.215698204975158737e-147 +2.754918306102034364e+00 7.215850670541067055e-147 +2.755918639546515614e+00 5.649475824582005798e-147 +2.756918972990997307e+00 4.422726632072960741e-147 +2.757919306435478557e+00 3.462050217560778371e-147 +2.758919639879960251e+00 2.709804466255050455e-147 +2.759919973324441500e+00 2.120820291651349508e-147 +2.760920306768923194e+00 1.659705795195199636e-147 +2.761920640213404443e+00 1.298732401021546337e-147 +2.762920973657886137e+00 1.016177478329133371e-147 +2.763921307102367386e+00 7.950249898180727791e-148 +2.764921640546849080e+00 6.219469169291773685e-148 +2.765921973991330773e+00 4.865048604830732798e-148 +2.766922307435812023e+00 3.805242796895722398e-148 +2.767922640880293716e+00 2.976040838314227183e-148 +2.768922974324774966e+00 2.327323341968256286e-148 +2.769923307769256660e+00 1.819851240729091491e-148 +2.770923641213737909e+00 1.422906547819345243e-148 +2.771923974658219603e+00 1.112444086285691652e-148 +2.772924308102700852e+00 8.696436507212298028e-149 +2.773924641547182546e+00 6.797759483714429499e-149 +2.774924974991664239e+00 5.313143962122291448e-149 +2.775925308436145489e+00 4.152395365636195992e-149 +2.776925641880627182e+00 3.244943482358312999e-149 +2.777925975325108432e+00 2.535577571817955145e-149 +2.778926308769590126e+00 1.981107268801545747e-149 +2.779926642214071375e+00 1.547748552542028819e-149 +2.780926975658553069e+00 1.209077521106679830e-149 +2.781927309103034318e+00 9.444287791978763590e-150 +2.782927642547516012e+00 7.376419441560255712e-150 +2.783927975991997261e+00 5.760806929883817244e-150 +2.784928309436478955e+00 4.498651620341195007e-150 +2.785928642880960648e+00 3.512713499603059686e-150 +2.786928976325441898e+00 2.742612320829644696e-150 +2.787929309769923591e+00 2.141151715469449563e-150 +2.788929643214404841e+00 1.671443825358458084e-150 +2.789929976658886535e+00 1.304660322755415647e-150 +2.790930310103367784e+00 1.018273519884857271e-150 +2.791930643547849478e+00 7.946808928656758032e-151 +2.792930976992330727e+00 6.201295373503531982e-151 +2.793931310436812421e+00 4.838752292104324635e-151 +2.794931643881294114e+00 3.775249756012159142e-151 +2.795931977325775364e+00 2.945230654726458279e-151 +2.796932310770257057e+00 2.297493343637075527e-151 +2.797932644214738307e+00 1.792051710979573541e-151 +2.798932977659220001e+00 1.397681249210981931e-151 +2.799933311103701250e+00 1.090001426376402192e-151 +2.800933644548182944e+00 8.499772848463762744e-152 +2.801933977992664193e+00 6.627487219686607984e-152 +2.802934311437145887e+00 5.167158746763603234e-152 +2.803934644881627136e+00 4.028246474133483462e-152 +2.804934978326108830e+00 3.140086365069049029e-152 +2.805935311770590523e+00 2.447532552322575111e-152 +2.806935645215071773e+00 1.907553320641714706e-152 +2.807935978659553466e+00 1.486572937020653616e-152 +2.808936312104034716e+00 1.158396093602579469e-152 +2.809936645548516410e+00 9.025874126433446218e-153 +2.810936978992997659e+00 7.032063555049824470e-153 +2.811937312437479353e+00 5.478196776904589237e-153 +2.812937645881960602e+00 4.267306087385944674e-153 +2.813937979326442296e+00 3.323772488885151511e-153 +2.814938312770923989e+00 2.588630767704684698e-153 +2.815938646215405239e+00 2.015905896366419133e-153 +2.816938979659886932e+00 1.569754468316555976e-153 +2.817939313104368182e+00 1.222234468695074862e-153 +2.818939646548849876e+00 9.515654238105653844e-154 +2.819939979993331125e+00 7.407712274896789762e-154 +2.820940313437812819e+00 5.766215701816604217e-154 +2.821940646882294068e+00 4.488063468653140138e-154 +2.822940980326775762e+00 3.492918256526823785e-154 +2.823941313771257011e+00 2.718186060527729038e-154 +2.824941647215738705e+00 2.115101759588001714e-154 +2.825941980660220398e+00 1.645677300696083466e-154 +2.826942314104701648e+00 1.280322618411559408e-154 +2.827942647549183341e+00 9.959911597803155090e-155 +2.828942980993664591e+00 7.747344647984167304e-155 +2.829943314438146285e+00 6.025756749915918519e-155 +2.830943647882627534e+00 4.686316781771001769e-155 +2.831943981327109228e+00 3.644290724410701077e-155 +2.832944314771590477e+00 2.833712043288027151e-155 +2.833944648216072171e+00 2.203229526738583334e-155 +2.834944981660553864e+00 1.712872738895056720e-155 +2.835945315105035114e+00 1.331532523253903454e-155 +2.836945648549516807e+00 1.034998659001308094e-155 +2.837945981993998057e+00 8.044316003249620517e-156 +2.838946315438479751e+00 6.251723835058829521e-156 +2.839946648882961000e+00 4.858159564072451680e-156 +2.840946982327442694e+00 3.774896875499112479e-156 +2.841947315771923943e+00 2.932916751284958014e-156 +2.842947649216405637e+00 2.278535017739755883e-156 +2.843947982660886886e+00 1.769998939436186644e-156 +2.844948316105368580e+00 1.374838331707957016e-156 +2.845948649549850273e+00 1.067804103526586581e-156 +2.846948982994331523e+00 8.292641031514204227e-157 +2.847949316438813216e+00 6.439549328568653941e-157 +2.848949649883294466e+00 5.000108286877143982e-157 +2.849949983327776160e+00 3.882081716817954208e-157 +2.850950316772257409e+00 3.013778035821694021e-157 +2.851950650216739103e+00 2.339479163358209088e-157 +2.852950983661220352e+00 1.815885359140775918e-157 +2.853951317105702046e+00 1.409350455730679826e-157 +2.854951650550183739e+00 1.093731955458248146e-157 +2.855951983994664989e+00 8.487194001728083707e-158 +2.856952317439146682e+00 6.585347326012417381e-158 +2.857952650883627932e+00 5.109219597924551308e-158 +2.858952984328109626e+00 3.963617898694319429e-158 +2.859953317772590875e+00 3.074612016524910395e-158 +2.860953651217072569e+00 2.384790249117230519e-158 +2.861953984661553818e+00 1.849572596403900702e-158 +2.862954318106035512e+00 1.434345928476017543e-158 +2.863954651550516761e+00 1.112238068610193928e-158 +2.864954984994998455e+00 8.623884555596176450e-159 +2.865955318439480148e+00 6.686047238369584767e-159 +2.866955651883961398e+00 5.183191743257221887e-159 +2.867955985328443091e+00 4.017782636644997667e-159 +2.868956318772924341e+00 3.114131360541025176e-159 +2.869956652217406035e+00 2.413508021715106656e-159 +2.870956985661887284e+00 1.870345733206576740e-159 +2.871957319106368978e+00 1.449293578664183348e-159 +2.872957652550850227e+00 1.122928671938549965e-159 +2.873957985995331921e+00 8.699800665960624456e-160 +2.874958319439813614e+00 6.739501297277900156e-160 +2.875958652884294864e+00 5.220445290234225648e-160 +2.876958986328776557e+00 4.043418227488235099e-160 +2.877959319773257807e+00 3.131490572690363686e-160 +2.878959653217739501e+00 2.425017522228467868e-160 +2.879959986662220750e+00 1.877759560027570484e-160 +2.880960320106702444e+00 1.453872797633045460e-160 +2.881960653551183693e+00 1.125574299996763205e-160 +2.882960986995665387e+00 8.713311782050393993e-161 +2.883961320440146636e+00 6.744560718374876287e-161 +2.884961653884628330e+00 5.220179198345184542e-161 +2.885961987329110023e+00 4.039973191089139971e-161 +2.886962320773591273e+00 3.126316071396860687e-161 +2.887962654218072966e+00 2.419070981326483846e-161 +2.888962987662554216e+00 1.871654440672196380e-161 +2.889963321107035910e+00 1.447984973035042450e-161 +2.890963654551517159e+00 1.120117982141492687e-161 +2.891963987995998853e+00 8.664127059143119272e-162 +2.892964321440480102e+00 6.701116750598401850e-162 +2.893964654884961796e+00 5.182399457734027288e-162 +2.894964988329443489e+00 4.007521995750357950e-162 +2.895965321773924739e+00 3.098719556177358975e-162 +2.896965655218406432e+00 2.395796687711700057e-162 +2.897965988662887682e+00 1.852162027437102237e-162 +2.898966322107369376e+00 1.431757019690027675e-162 +2.899966655551850625e+00 1.106677282306347344e-162 +2.900966988996332319e+00 8.553305768309425002e-163 +2.901967322440813568e+00 6.610104562846894504e-163 +2.902967655885295262e+00 5.107918865445254135e-163 +2.903967989329776955e+00 3.946762381842850041e-163 +2.904968322774258205e+00 3.049293998643044035e-163 +2.905968656218739898e+00 2.355694386450505008e-163 +2.906968989663221148e+00 1.819700540843701454e-163 +2.907969323107702841e+00 1.405536831022385934e-163 +2.908969656552184091e+00 1.085540087341896086e-163 +2.909969989996665785e+00 8.383219365874894910e-164 +2.910970323441147034e+00 6.473469773968207633e-164 +2.911970656885628728e+00 4.998327951038382095e-164 +2.912970990330109977e+00 3.858990418027594255e-164 +2.913971323774591671e+00 2.979092457976010303e-164 +2.914971657219073364e+00 2.299617437717884188e-164 +2.915971990663554614e+00 1.774959850305507293e-164 +2.916972324108036307e+00 1.369880878762021069e-164 +2.917972657552517557e+00 1.057154354649017492e-164 +2.918972990996999251e+00 8.157467119553951968e-165 +2.919973324441480500e+00 6.294099299131297255e-165 +2.920973657885962194e+00 4.855938504097389223e-165 +2.921973991330443443e+00 3.746054535474661541e-165 +2.922974324774925137e+00 2.889590777912686909e-165 +2.923974658219406830e+00 2.228742625549125170e-165 +2.924974991663888080e+00 1.718877099057681226e-165 +2.925975325108369773e+00 1.325534578215083436e-165 +2.926975658552851023e+00 1.022112329032631487e-165 +2.927975991997332716e+00 7.880749483851359344e-166 +2.928976325441813966e+00 6.075719945421309360e-166 +2.929976658886295660e+00 4.683702503970844501e-166 +2.930976992330776909e+00 3.610290816848138335e-166 +2.931977325775258603e+00 2.782636010699014389e-166 +2.932977659219739852e+00 2.144529108342913911e-166 +2.933977992664221546e+00 1.652604076125498030e-166 +2.934978326108703239e+00 1.273406386631623143e-166 +2.935978659553184489e+00 9.811300053789825904e-167 +2.936978992997666182e+00 7.558705445729209450e-167 +2.937979326442147432e+00 5.822769730667329971e-167 +2.938979659886629126e+00 4.485110421772116376e-167 +2.939979993331110375e+00 3.454442703479879209e-167 +2.940980326775592069e+00 2.660383084811598690e-167 +2.941980660220073318e+00 2.048668510494618439e-167 +2.942980993664555012e+00 1.577467921444169263e-167 +2.943981327109036705e+00 1.214536891258856186e-167 +2.944981660553517955e+00 9.350228314202278243e-168 +2.945981993997999648e+00 7.197721698027484036e-168 +2.946982327442480898e+00 5.540248127890003415e-168 +2.947982660886962591e+00 4.264073785028619881e-168 +2.948982994331443841e+00 3.281568972544857385e-168 +2.949983327775925535e+00 2.525222747079456977e-168 +2.950983661220406784e+00 1.943028537296963581e-168 +2.951983994664888478e+00 1.494927034709376256e-168 +2.952984328109369727e+00 1.150064354574661360e-168 +2.953984661553851421e+00 8.846788008010412200e-169 +2.954984994998333114e+00 6.804722647891721612e-169 +2.955985328442814364e+00 5.233552282766776734e-169 +2.956985661887296057e+00 4.024797514318450645e-169 +2.957985995331777307e+00 3.094944287578121193e-169 +2.958986328776259001e+00 2.379704137806125412e-169 +2.959986662220740250e+00 1.829592732280697750e-169 +2.960986995665221944e+00 1.406524228905527245e-169 +2.961987329109703193e+00 1.081188305844538469e-169 +2.962987662554184887e+00 8.310301728591374763e-170 +2.963987995998666580e+00 6.386950872765536613e-170 +2.964988329443147830e+00 4.908306671911317610e-170 +2.965988662887629523e+00 3.771647829200338790e-170 +2.966988996332110773e+00 2.897956819579155952e-170 +2.967989329776592466e+00 2.226455485115669116e-170 +2.968989663221073716e+00 1.710399077551897939e-170 +2.969989996665555410e+00 1.313839218965455201e-170 +2.970990330110036659e+00 1.009132796048678851e-170 +2.971990663554518353e+00 7.750250690898670186e-171 +2.972990996998999602e+00 5.951747679345537657e-171 +2.973991330443481296e+00 4.570193655054642841e-171 +2.974991663887962989e+00 3.509021472510415186e-171 +2.975991997332444239e+00 2.694007370229676299e-171 +2.976992330776925932e+00 2.068106332043217357e-171 +2.977992664221407182e+00 1.587480064635196275e-171 +2.978992997665888876e+00 1.218442466618294950e-171 +2.979993331110370125e+00 9.351108696389370184e-172 +2.980993664554851819e+00 7.176001394792727529e-172 +2.981993997999333068e+00 5.506342923489434619e-172 +2.982994331443814762e+00 4.224791944942848789e-172 +2.983994664888296455e+00 3.241221635996652808e-172 +2.984994998332777705e+00 2.486414119567768668e-172 +2.985995331777259398e+00 1.907214450891875975e-172 +2.986995665221740648e+00 1.462806647870458511e-172 +2.987995998666222341e+00 1.121852224294369232e-172 +2.988996332110703591e+00 8.602916599975503884e-173 +2.989996665555185285e+00 6.596553718497416036e-173 +2.990996998999666534e+00 5.057662270003561189e-173 +2.991997332444148228e+00 3.877429223132790916e-173 +2.992997665888629477e+00 2.972345325847491488e-173 +2.993997999333111171e+00 2.278326600069947237e-173 +2.994998332777592864e+00 1.746200180289980749e-173 +2.995998666222074114e+00 1.338238155752033344e-173 +2.996998999666555807e+00 1.025496351143826518e-173 +2.997999333111037057e+00 7.857712996905513712e-174 +2.998999666555518750e+00 6.020319451435505326e-174 +3.000000000000000000e+00 4.612158699305736625e-174 diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index 2402290c..39e4540c 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -25,6 +25,7 @@ from tqdm import tqdm from sp_validation.glass_mock import ( + Cosmology_from_camb, GlassMockConfig, build_camb_params, build_shells, @@ -69,6 +70,21 @@ def __init__(self): # Test mode shrinks the box; otherwise CLI overrides config fields. if args.test: print("[!!!] Running in test mode: ignore the configuration file [!!!]") + if not os.path.exists("config/glass_mock/test_data/"): + raise FileNotFoundError( + "The test must be run from the root of the repository." + ) + if not os.path.exists("config/glass_mock/test_data/mask.fits"): + raise FileNotFoundError( + "Check that you downloaded the test mask from the repository." + ) + if not os.path.exists( + "config/glass_mock/test_data/redshift_distribution.txt" + ): + raise FileNotFoundError( + "Check that you downloaded the test redshift distribution from the repository." + ) + self.config = GlassMockConfig.from_planck18( nside=32, seed=args.seed, @@ -77,6 +93,9 @@ def __init__(self): zmax=0.5, sigma_e=0.26, ia_bias=None, + mask_path="config/glass_mock/test_data/mask.fits", + nz_path="config/glass_mock/test_data/redshift_distribution.txt", + output_path="config/glass_mock/test_data", ) else: # Load the configuration from the config file in the parser @@ -94,11 +113,11 @@ def __init__(self): self.rng = np.random.default_rng(self.config.seed) # Input paths - self.path_mask = args.mask - self.path_nz = args.pathnz + self.path_mask = self.config.mask_path + self.path_nz = self.config.nz_path # Output path - self.path = args.path + self.path = self.config.output_path print("-" * 66) print( @@ -161,7 +180,7 @@ def galaxies_simulation(self): fields = glass.lognormal_fields(shells) gls = glass.solve_gaussian_spectra(fields, cls) matter = glass.generate(fields, gls, config.nside, ncorr=3, rng=self.rng) - convergence = glass.MultiPlaneConvergence(_cosmology(self.pars)) + convergence = glass.MultiPlaneConvergence(Cosmology_from_camb(self.pars)) # n(z) and per-shell galaxy partition. nz = np.loadtxt(self.path_nz) @@ -210,7 +229,7 @@ def galaxies_simulation(self): gal_phz = glass.gaussian_phz(gal_z, config.phz_sigma_0, rng=self.rng) tomo_id = np.digitize(gal_phz, np.unique(zbins)) - 1 gal_ellip = glass.ellipticity_intnorm( - gal_count, config.sigma_e, rng=self.rng + gal_count, config.sigma_e, rng=self.rng, xp=np ) gal_she = glass.galaxy_shear( gal_lon, gal_lat, gal_ellip, kappa_i, gamm1_i, gamm2_i @@ -277,13 +296,6 @@ def get_camb_cls(self, sav=True): return dic -def _cosmology(pars): - """GLASS cosmology wrapper from CAMB params (lazy import).""" - from cosmology import Cosmology - - return Cosmology.from_camb(pars) - - if __name__ == "__main__": print("Starting the simulation") start_time = time.time() diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index 9c46d033..71f6763b 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -81,6 +81,10 @@ class GlassMockConfig: phz_sigma_0: float = 0.03 nbins: int = 1 # number of tomographic bins ia_bias: float | None = None + # --- Runtime options --- + mask_path: str | None = None + nz_path: str | None = None + output_path: str | None = None @classmethod def from_planck18(cls, **overrides) -> "GlassMockConfig": @@ -192,19 +196,21 @@ def build_shells(config: GlassMockConfig, pars): Returns the GLASS shell list (``(z, w, zeff)`` windows). Lazily imports GLASS; only callable where GLASS is installed. """ + import camb import glass - from cosmology import Cosmology + from cosmology.compat.camb import Cosmology - cosmo = Cosmology.from_camb(pars) + results = camb.get_background(pars) + cosmo = Cosmology(results) zb = glass.distance_grid(cosmo, 0.0, config.zmax, dx=config.dx) return glass.linear_windows(zb) -def matter_shell_cls(config: GlassMockConfig, pars, shells): +def matter_shell_cls(config: GlassMockConfig, pars, shells, limber=False): """Matter angular power spectra for the shells, from CAMB via GLASS.""" import glass.ext.camb - return glass.ext.camb.matter_cls(pars, config.lmax, shells) + return glass.ext.camb.matter_cls(pars, config.lmax, shells, limber=limber) def generate_matter_maps(config: GlassMockConfig, pars, shells, cls): @@ -236,9 +242,12 @@ def generate_matter_maps(config: GlassMockConfig, pars, shells, cls): def Cosmology_from_camb(pars): """Thin indirection so the convergence cosmology is built once, lazily.""" - from cosmology import Cosmology + import camb + from cosmology.compat.camb import Cosmology + + results = camb.get_background(pars) - return Cosmology.from_camb(pars) + return Cosmology(results) # --- mask / galaxy-sampling helpers (used by the generation runner) --------- From 2b94f40be0d7808ccb43abcfe6be8cf09e6a913a Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 1 Jul 2026 14:30:43 +0200 Subject: [PATCH 003/107] (feature) Add an option to run the shell cl with limber approximation --- .gitignore | 2 ++ scripts/glass_mock/make_glass_sim.py | 3 ++- src/sp_validation/glass_mock.py | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fa4684ce..f022a2e1 100644 --- a/.gitignore +++ b/.gitignore @@ -173,6 +173,8 @@ cosmo_inference/cosmosis_config/cosmosis_pipeline_glass_mock_0*.ini cosmo_inference/cosmosis_config/cosmosis_pipeline_glass_mock_v0*.ini cosmo_inference/cosmosis_config/glass_mocks_v* +config/glass_mock/test_data/results + # Regenerable catalog-paper plots: the paper TeX lives in the separate docs/ # repo, so these are script/notebook outputs, not LaTeX-tracked figures. papers/catalog/plots/*.pdf diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index 39e4540c..abeb3165 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -106,6 +106,7 @@ def __init__(self): # Runtime options self.test = args.test self.camb = args.camb + self.limber = self.config.limber # Number label and random seed self.number = args.number @@ -173,7 +174,7 @@ def galaxies_simulation(self): cls = np.load(cls_path) except FileNotFoundError: print(f"[!] cls file {cls_path} missing; computing matter cls ...") - cls = matter_shell_cls(config, self.pars, shells) + cls = matter_shell_cls(config, self.pars, shells, limber=self.limber) np.save(cls_path, cls) print("[!] Done.") diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index 71f6763b..ba570f26 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -82,6 +82,7 @@ class GlassMockConfig: nbins: int = 1 # number of tomographic bins ia_bias: float | None = None # --- Runtime options --- + limber: bool = False mask_path: str | None = None nz_path: str | None = None output_path: str | None = None From 50de95ad30aeea718c03631a986c84f7f89aa5d3 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 1 Jul 2026 16:25:54 +0200 Subject: [PATCH 004/107] (ruff) Commit ruff changes --- .gitignore | 3 +- config/glass_mock/config_glass_mock.yaml | 11 +- config/glass_mock/config_glass_mock_test.yaml | 14 +- config/glass_mock/test_data/mask_nside32.fits | Bin 106560 -> 0 bytes .../test_data/redshift_distribution.txt | 3000 ----------------- .../redshift_distribution_non_tomo.txt | 501 +++ .../test_data/redshift_distribution_tomo.txt | 501 +++ scripts/glass_mock/make_glass_sim.py | 177 +- src/sp_validation/glass_mock.py | 1 + 9 files changed, 1136 insertions(+), 3072 deletions(-) delete mode 100644 config/glass_mock/test_data/mask_nside32.fits delete mode 100644 config/glass_mock/test_data/redshift_distribution.txt create mode 100644 config/glass_mock/test_data/redshift_distribution_non_tomo.txt create mode 100644 config/glass_mock/test_data/redshift_distribution_tomo.txt diff --git a/.gitignore b/.gitignore index f022a2e1..38eeaa26 100644 --- a/.gitignore +++ b/.gitignore @@ -199,4 +199,5 @@ papers/catalog/plots/*.pdf papers/cosmo_val/logs/ # Ignore scratch work notebooks -scratch/guerrini/work_notebooks \ No newline at end of file +scratch/guerrini/work_notebooks +scratch/guerrini/launch_scripts \ No newline at end of file diff --git a/config/glass_mock/config_glass_mock.yaml b/config/glass_mock/config_glass_mock.yaml index 62afdeb8..d4b074ea 100644 --- a/config/glass_mock/config_glass_mock.yaml +++ b/config/glass_mock/config_glass_mock.yaml @@ -25,9 +25,6 @@ nside: 4096 dx: 120.0 zmax: 3.0 -# --- Sampling --- -seed: 42 - # --- galaxy population (downstream of map generation) --- nbins: 1 n_arcmin2: 6.0905 @@ -37,6 +34,8 @@ phz_sigma_0: 0.03 #ia_bias: null # --- Runtime options --- -mask_path: ... -nz_path: ... -output_path: ... \ No newline at end of file +limber: True +mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits +nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/redshift_distribution_tomo.txt +output_path: /n09data/guerrini/glass_mock_test/ +output_prefix: tomo_test \ No newline at end of file diff --git a/config/glass_mock/config_glass_mock_test.yaml b/config/glass_mock/config_glass_mock_test.yaml index 0b46260c..73e6d98b 100644 --- a/config/glass_mock/config_glass_mock_test.yaml +++ b/config/glass_mock/config_glass_mock_test.yaml @@ -10,7 +10,7 @@ # -c, --config: Path to the configuration file to generate the simulation (Required) # --- Cosmological parameters (CAMB) --- -#h: 0.6766 +#h: 0.7 #Om: 0.30966 #Ob: 0.04897 #ns: 0.9665 @@ -23,13 +23,10 @@ # --- Resolution --- nside: 32 dx: 200.0 -zmax: 0.5 - -# --- Sampling --- -seed: 42 +zmax: 3 # --- galaxy population (downstream of map generation) --- -nbins: 1 +nbins: 6 n_arcmin2: 0.0824 sigma_e: 0.2684 bias: 1.2 @@ -39,5 +36,6 @@ phz_sigma_0: 0.03 # --- Runtime options --- limber: True mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits -nz_path: /n17data/sguerrini/UNIONS/WL/nz/v1.4.6.3/nz_SP_v1.4.6.3_A.txt -output_path: /n09data/guerrini/glass_mock_test/ \ No newline at end of file +nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/redshift_distribution_tomo.txt +output_path: /n09data/guerrini/glass_mock_test/ +output_prefix: unions \ No newline at end of file diff --git a/config/glass_mock/test_data/mask_nside32.fits b/config/glass_mock/test_data/mask_nside32.fits deleted file mode 100644 index 4f86bf2593a962ae18b8a77d66824ab2d28433dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106560 zcmeI$VQ<<-90%a-o1C;4Y|^BJY|^qxeL)h_dL@9smC~0^aT2ruk#VNv+wW||fEKD$ zlcr94{300V_;dc<`OzKY^m5osLU8zM9 zr;GV=Q)KHbxyq9M#esU+R>K#bHpO1pRx=4mKmrnwfCMBUf&YF2^>T8Y4*IV`>n!bO zowHuj+PbTwa6O&p<#VX=>-!=UPt|I1y47U&^j}>+pM3fHxml05_pP5#+rQTJ?u+?t zbyu&)C+n4Xi0uy@O}&$S^+ISLsu$ZII)>+icm24If5Cp?wp=_ugonJ$XGK+%`}Yg4 zKUc54lV4MBf0a#dvu>L7F2?)&w_9&DWp1X${p4`fEPFppn(=6@cQ4!XZ}Y`3Jj=R+ z(dDXM`}pMNxA9~Ds9xL*);)~(+j^69diCCLyWUY)UwxQ975CF+Ud653XZkuAT_mHl z|2o#IU+R{-^@eqwuqY=*Ii25z<%hdsR{R)>X>}L3iOpvgW#}j4EQ#B|5ch-q&SlfT z^|QHKZyskyJ)5h$>GA~+bHv~xZm0K-?@#r79u>>Q{bO^Ne^0%-{yK;Hj<$|&IvUqI z>VxySJ1v)0XwnPch3}8Ub-pYnKWvrQIqh|ta-a6EeR6X8OYC1S|B`Yc?O$At&);0e z@u+`ENYB%3H@|c~DW2j;{1(>#kAo9qU$x!oTm5Em_Iq;PJio2(yIybn_WeiWJO1N2 z{wDEfQRcUsVO=+km4}^CmUepU!uIFuB)m#M0uuPT68L6yeCxGIKn~_Zoq%~@9+(H7 z0}gP2103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K0 z2ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`2 z9N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8 zaDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0W zzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h z00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70 z103K02ROh14sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh1 z4sd`29N+*4IKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4 zIKTl8aDW3G-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G z-~b0WzyS_$fCC)h00%h00S<70103K02ROh14sd`29N+*4IKTl8aDW3G;K2Xefz{pn TD**{eKmrnwfCMD))hF;5Z!Vcf diff --git a/config/glass_mock/test_data/redshift_distribution.txt b/config/glass_mock/test_data/redshift_distribution.txt deleted file mode 100644 index 89aa3aa3..00000000 --- a/config/glass_mock/test_data/redshift_distribution.txt +++ /dev/null @@ -1,3000 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 -1.000333444481493880e-03 3.981956545413817446e-03 -2.000666888962987760e-03 6.932064073856508517e-03 -3.001000333444481423e-03 9.586034800257063962e-03 -4.001333777925975520e-03 1.206298496719232158e-02 -5.001667222407469617e-03 1.441481136008560676e-02 -6.002000666888962846e-03 1.667022635241322340e-02 -7.002334111370456943e-03 1.884724323664124443e-02 -8.002667555851951040e-03 2.095808851670286077e-02 -9.003001000333445136e-03 2.301152155292751286e-02 -1.000333444481493923e-02 2.501406914620550592e-02 -1.100366788929643333e-02 2.697074123338756915e-02 -1.200400133377792569e-02 2.888547339187262988e-02 -1.300433477825941979e-02 3.076141459959420496e-02 -1.400466822274091389e-02 3.260112213783077778e-02 -1.500500166722240798e-02 3.440669810560863934e-02 -1.600533511170390208e-02 3.617988776926599204e-02 -1.700566855618539444e-02 3.792215213804438212e-02 -1.800600200066689027e-02 3.963472264091205677e-02 -1.900633544514838263e-02 4.131864306967396316e-02 -2.000666888962987847e-02 4.297480226962233779e-02 -2.100700233411137083e-02 4.460395998086915531e-02 -2.200733577859286666e-02 4.620676752469865534e-02 -2.300766922307435902e-02 4.778378455222663523e-02 -2.400800266755585138e-02 4.933549274479407526e-02 -2.500833611203734722e-02 5.086230712592543918e-02 -2.600866955651883958e-02 5.236458548114646810e-02 -2.700900300100033541e-02 5.384263626366593558e-02 -2.800933644548182777e-02 5.529672527715281255e-02 -2.900966988996332360e-02 5.672708136236397775e-02 -3.001000333444481596e-02 5.813390126589651485e-02 -3.101033677892631180e-02 5.951735383248771966e-02 -3.201067022340780416e-02 6.087758363398869388e-02 -3.301100366788929652e-02 6.221471412620547398e-02 -3.401133711237078888e-02 6.352885040765290925e-02 -3.501167055685228818e-02 6.482008164074994083e-02 -3.601200400133378055e-02 6.608848318524837562e-02 -3.701233744581527291e-02 6.733411848509854303e-02 -3.801267089029676527e-02 6.855704074303921314e-02 -3.901300433477826457e-02 6.975729441159335409e-02 -4.001333777925975693e-02 7.093491652458211449e-02 -4.101367122374124929e-02 7.208993788952340609e-02 -4.201400466822274166e-02 7.322238415819337654e-02 -4.301433811270423402e-02 7.433227679007292643e-02 -4.401467155718573332e-02 7.541963392127196131e-02 -4.501500500166722568e-02 7.648447114974575578e-02 -4.601533844614871804e-02 7.752680224612372439e-02 -4.701567189063021041e-02 7.854663979821148445e-02 -4.801600533511170277e-02 7.954399579616183713e-02 -4.901633877959320207e-02 8.051888216440464263e-02 -5.001667222407469443e-02 8.147131124565461235e-02 -5.101700566855618679e-02 8.240129624165543065e-02 -5.201733911303767915e-02 8.330885161475302947e-02 -5.301767255751917846e-02 8.419399345390343736e-02 -5.401800600200067082e-02 8.505673980829961400e-02 -5.501833944648216318e-02 8.589711099143833295e-02 -5.601867289096365554e-02 8.671512985813152607e-02 -5.701900633544514790e-02 8.751082205669195868e-02 -5.801933977992664720e-02 8.828421625828319907e-02 -5.901967322440813957e-02 8.903534436521409745e-02 -6.002000666888963193e-02 8.976424169977431644e-02 -6.102034011337112429e-02 9.047094717504571004e-02 -6.202067355785262359e-02 9.115550344898334933e-02 -6.302100700233410902e-02 9.181795706293330306e-02 -6.402134044681560832e-02 9.245835856564466049e-02 -6.502167389129710762e-02 9.307676262373537623e-02 -6.602200733577859304e-02 9.367322811948380901e-02 -6.702234078026009234e-02 9.424781823674160952e-02 -6.802267422474157776e-02 9.480060053569451528e-02 -6.902300766922307707e-02 9.533164701713665867e-02 -7.002334111370457637e-02 9.584103417686894166e-02 -7.102367455818606179e-02 9.632884305078404097e-02 -7.202400800266756109e-02 9.679515925115628194e-02 -7.302434144714904651e-02 9.724007299461577547e-02 -7.402467489163054581e-02 9.766367912225072667e-02 -7.502500833611204512e-02 9.806607711225102941e-02 -7.602534178059353054e-02 9.844737108547679805e-02 -7.702567522507502984e-02 9.880766980431070212e-02 -7.802600866955652914e-02 9.914708666512951629e-02 -7.902634211403801456e-02 9.946573968470928673e-02 -8.002667555851951386e-02 9.976375148086022437e-02 -8.102700900300099929e-02 1.000412492475692139e-01 -8.202734244748249859e-02 1.002983647249140325e-01 -8.302767589196399789e-02 1.005352341639970393e-01 -8.402800933644548331e-02 1.007519982871356856e-01 -8.502834278092698261e-02 1.009488022435336141e-01 -8.602867622540846804e-02 1.011257955606465336e-01 -8.702900966988996734e-02 1.012831320914471950e-01 -8.802934311437146664e-02 1.014209699577842405e-01 -8.902967655885295206e-02 1.015394714900229312e-01 -9.003001000333445136e-02 1.016388031631467875e-01 -9.103034344781593679e-02 1.017191355294932709e-01 -9.203067689229743609e-02 1.017806431482901341e-01 -9.303101033677893539e-02 1.018235045121531485e-01 -9.403134378126042081e-02 1.018479019707004690e-01 -9.503167722574192011e-02 1.018540216514340763e-01 -9.603201067022340554e-02 1.018420533780340365e-01 -9.703234411470490484e-02 1.018121905862071075e-01 -9.803267755918640414e-02 1.017646302372268441e-01 -9.903301100366788956e-02 1.016995727292992480e-01 -1.000333444481493889e-01 1.016172218068835259e-01 -1.010336778926308882e-01 1.015177844680955344e-01 -1.020340113371123736e-01 1.014014708703166734e-01 -1.030343447815938729e-01 1.012684942341296745e-01 -1.040346782260753583e-01 1.011190707456988830e-01 -1.050350116705568576e-01 1.009534194577100252e-01 -1.060353451150383569e-01 1.007717621889824972e-01 -1.070356785595198423e-01 1.005743234228641814e-01 -1.080360120040013416e-01 1.003613302045164024e-01 -1.090363454484828271e-01 1.001330120371949073e-01 -1.100366788929643264e-01 9.988960077763010814e-02 -1.110370123374458257e-01 9.963133053060750643e-02 -1.120373457819273111e-01 9.935843754284783003e-02 -1.130376792264088104e-01 9.907116009628372244e-02 -1.140380126708902958e-01 9.876973840082794998e-02 -1.150383461153717951e-01 9.845441448672652418e-02 -1.160386795598532944e-01 9.812543209658800003e-02 -1.170390130043347798e-01 9.778303657717778152e-02 -1.180393464488162791e-01 9.742747477106536436e-02 -1.190396798932977784e-01 9.705899490821023878e-02 -1.200400133377792639e-01 9.667784649756937243e-02 -1.210403467822607632e-01 9.628428021880934551e-02 -1.220406802267422486e-01 9.587854781420244998e-02 -1.230410136712237479e-01 9.546090198078546751e-02 -1.240413471157052472e-01 9.503159626285755113e-02 -1.250416805601867465e-01 9.459088494489202581e-02 -1.260420140046682180e-01 9.413902294493498024e-02 -1.270423474491497173e-01 9.367626570856205093e-02 -1.280426808936312166e-01 9.320286910346256559e-02 -1.290430143381127159e-01 9.271908931471874171e-02 -1.300433477825942152e-01 9.222518274084594292e-02 -1.310436812270756868e-01 9.172140589065790051e-02 -1.320440146715571861e-01 9.120801528101893374e-02 -1.330443481160386854e-01 9.068526733554439778e-02 -1.340446815605201847e-01 9.015341828430720184e-02 -1.350450150050016840e-01 8.961272406460790707e-02 -1.360453484494831555e-01 8.906344022286323547e-02 -1.370456818939646548e-01 8.850582181766637746e-02 -1.380460153384461541e-01 8.794012332407093180e-02 -1.390463487829276534e-01 8.736659853914753582e-02 -1.400466822274091527e-01 8.678550048886200796e-02 -1.410470156718906243e-01 8.619708133632057734e-02 -1.420473491163721236e-01 8.560159229142663695e-02 -1.430476825608536229e-01 8.499928352199190296e-02 -1.440480160053351222e-01 8.439040406634253089e-02 -1.450483494498166215e-01 8.377520174745926862e-02 -1.460486828942980930e-01 8.315392308868906068e-02 -1.470490163387795923e-01 8.252681323106297873e-02 -1.480493497832610916e-01 8.189411585225533918e-02 -1.490496832277425909e-01 8.125607308721458089e-02 -1.500500166722240902e-01 8.061292545049721092e-02 -1.510503501167055895e-01 7.996491176033238002e-02 -1.520506835611870611e-01 7.931226906444495406e-02 -1.530510170056685604e-01 7.865523256766018823e-02 -1.540513504501500597e-01 7.799403556131585813e-02 -1.550516838946315590e-01 7.732890935450072167e-02 -1.560520173391130583e-01 7.666008320714140511e-02 -1.570523507835945298e-01 7.598778426495425564e-02 -1.580526842280760291e-01 7.531223749628007669e-02 -1.590530176725575284e-01 7.463366563081548499e-02 -1.600533511170390277e-01 7.395228910025516966e-02 -1.610536845615205270e-01 7.326832598085566972e-02 -1.620540180060019986e-01 7.258199193793200832e-02 -1.630543514504834979e-01 7.189350017229435841e-02 -1.640546848949649972e-01 7.120306136863255309e-02 -1.650550183394464965e-01 7.051088364585393620e-02 -1.660553517839279958e-01 6.981717250937757857e-02 -1.670556852284094673e-01 6.912213080538835708e-02 -1.680560186728909666e-01 6.842595867705085488e-02 -1.690563521173724659e-01 6.772885352268379067e-02 -1.700566855618539652e-01 6.703100995589154898e-02 -1.710570190063354645e-01 6.633261976765110479e-02 -1.720573524508169361e-01 6.563387189034837477e-02 -1.730576858952984354e-01 6.493495236375901314e-02 -1.740580193397799347e-01 6.423604430296553369e-02 -1.750583527842614340e-01 6.353732786820311118e-02 -1.760586862287429333e-01 6.283898023662291832e-02 -1.770590196732244048e-01 6.214117557596359614e-02 -1.780593531177059041e-01 6.144408502011700773e-02 -1.790596865621874034e-01 6.074787664657599340e-02 -1.800600200066689027e-01 6.005271545574924347e-02 -1.810603534511504020e-01 5.935876335212712795e-02 -1.820606868956318736e-01 5.866617912728296080e-02 -1.830610203401133729e-01 5.797511844469033226e-02 -1.840613537845948722e-01 5.728573382633962013e-02 -1.850616872290763715e-01 5.659817464113196805e-02 -1.860620206735578708e-01 5.591258709503194035e-02 -1.870623541180393423e-01 5.522911422295555262e-02 -1.880626875625208416e-01 5.454789588237272252e-02 -1.890630210070023409e-01 5.386906874859959810e-02 -1.900633544514838402e-01 5.319276631175769154e-02 -1.910636878959653395e-01 5.251911887537379781e-02 -1.920640213404468111e-01 5.184825355659636315e-02 -1.930643547849283104e-01 5.118029428800065228e-02 -1.940646882294098097e-01 5.051536182095666544e-02 -1.950650216738913090e-01 4.985357373053179303e-02 -1.960653551183728083e-01 4.919504442189957805e-02 -1.970656885628543076e-01 4.853988513822649065e-02 -1.980660220073357791e-01 4.788820397000642659e-02 -1.990663554518172784e-01 4.724010586581389726e-02 -2.000666888962987777e-01 4.659569264444474862e-02 -2.010670223407802770e-01 4.595506300841426667e-02 -2.020673557852617763e-01 4.531831255878059073e-02 -2.030676892297432479e-01 4.468553381126293134e-02 -2.040680226742247472e-01 4.405681621362123046e-02 -2.050683561187062465e-01 4.343224616426647616e-02 -2.060686895631877458e-01 4.281190703206767117e-02 -2.070690230076692451e-01 4.219587917732381399e-02 -2.080693564521507166e-01 4.158423997386682947e-02 -2.090696898966322159e-01 4.097706383226299570e-02 -2.100700233411137152e-01 4.037442222407907483e-02 -2.110703567855952145e-01 3.977638370717944544e-02 -2.120706902300767138e-01 3.918301395202103410e-02 -2.130710236745581854e-01 3.859437576891183719e-02 -2.140713571190396847e-01 3.801052913619963614e-02 -2.150716905635211840e-01 3.743153122935680144e-02 -2.160720240080026833e-01 3.685743645092765669e-02 -2.170723574524841826e-01 3.628829646130429798e-02 -2.180726908969656541e-01 3.572416021029763133e-02 -2.190730243414471534e-01 3.516507396946935010e-02 -2.200733577859286527e-01 3.461108136519214923e-02 -2.210736912304101520e-01 3.406222341240397466e-02 -2.220740246748916513e-01 3.351853854902373669e-02 -2.230743581193731229e-01 3.298006267099493766e-02 -2.240746915638546222e-01 3.244682916792471922e-02 -2.250750250083361215e-01 3.191886895928555679e-02 -2.260753584528176208e-01 3.139621053114716176e-02 -2.270756918972991201e-01 3.087887997340664145e-02 -2.280760253417805916e-01 3.036690101748483819e-02 -2.290763587862620909e-01 2.986029507445768505e-02 -2.300766922307435902e-01 2.935908127359098882e-02 -2.310770256752250895e-01 2.886327650124803634e-02 -2.320773591197065888e-01 2.837289544013933337e-02 -2.330776925641880604e-01 2.788795060888466654e-02 -2.340780260086695597e-01 2.740845240185714807e-02 -2.350783594531510590e-01 2.693440912928074527e-02 -2.360786928976325583e-01 2.646582705755158235e-02 -2.370790263421140576e-01 2.600271044975503709e-02 -2.380793597865955569e-01 2.554506160635030068e-02 -2.390796932310770284e-01 2.509288090599527329e-02 -2.400800266755585277e-01 2.464616684648402237e-02 -2.410803601200400270e-01 2.420491608577078624e-02 -2.420806935645215263e-01 2.376912348305424208e-02 -2.430810270090030256e-01 2.333878213989607972e-02 -2.440813604534844972e-01 2.291388344134947727e-02 -2.450816938979659965e-01 2.249441709707195036e-02 -2.460820273424474958e-01 2.208037118239969399e-02 -2.470823607869289951e-01 2.167173217935863816e-02 -2.480826942314104944e-01 2.126848501759042234e-02 -2.490830276758919659e-01 2.087061311516989759e-02 -2.500833611203734930e-01 2.047809841929303792e-02 -2.510836945648549645e-01 2.009092144681351444e-02 -2.520840280093364361e-01 1.970906132460733054e-02 -2.530843614538179631e-01 1.933249582974543371e-02 -2.540846948982994347e-01 1.896120142945458692e-02 -2.550850283427809617e-01 1.859515332084752184e-02 -2.560853617872624333e-01 1.823432547040402044e-02 -2.570856952317439048e-01 1.787869065318491851e-02 -2.580860286762254319e-01 1.752822049176178121e-02 -2.590863621207069034e-01 1.718288549484581901e-02 -2.600866955651884305e-01 1.684265509559929389e-02 -2.610870290096699020e-01 1.650749768961479180e-02 -2.620873624541513736e-01 1.617738067254628070e-02 -2.630876958986329006e-01 1.585227047737868336e-02 -2.640880293431143722e-01 1.553213261132139339e-02 -2.650883627875958992e-01 1.521693169231268759e-02 -2.660886962320773708e-01 1.490663148512258625e-02 -2.670890296765588423e-01 1.460119493704171816e-02 -2.680893631210403694e-01 1.430058421314490263e-02 -2.690896965655218409e-01 1.400476073111841559e-02 -2.700900300100033680e-01 1.371368519564033703e-02 -2.710903634544848395e-01 1.342731763230437490e-02 -2.720906968989663111e-01 1.314561742107763984e-02 -2.730910303434478381e-01 1.286854332928351850e-02 -2.740913637879293097e-01 1.259605354410179255e-02 -2.750916972324108367e-01 1.232810570457750637e-02 -2.760920306768923083e-01 1.206465693313228324e-02 -2.770923641213737798e-01 1.180566386657050729e-02 -2.780926975658553069e-01 1.155108268657487926e-02 -2.790930310103367784e-01 1.130086914968518597e-02 -2.800933644548183055e-01 1.105497861675525106e-02 -2.810936978992997770e-01 1.081336608188333305e-02 -2.820940313437812486e-01 1.057598620081165973e-02 -2.830943647882627756e-01 1.034279331879137641e-02 -2.840946982327442472e-01 1.011374149790954594e-02 -2.850950316772257742e-01 9.888784543875258487e-03 -2.860953651217072458e-01 9.667876032262550864e-03 -2.870956985661887173e-01 9.450969334207961689e-03 -2.880960320106702444e-01 9.238017641561225429e-03 -2.890963654551517159e-01 9.028973991488040141e-03 -2.900966988996332430e-01 8.823791290523746653e-03 -2.910970323441147145e-01 8.622422338078144718e-03 -2.920973657885961861e-01 8.424819849390755705e-03 -2.930976992330777131e-01 8.230936477937581333e-03 -2.940980326775591847e-01 8.040724837289606533e-03 -2.950983661220407117e-01 7.854137522424354395e-03 -2.960986995665221833e-01 7.671127130491982478e-03 -2.970990330110036548e-01 7.491646281037529428e-03 -2.980993664554851819e-01 7.315647635681610424e-03 -2.990996998999666534e-01 7.143083917261813971e-03 -3.001000333444481805e-01 6.973907928437627669e-03 -3.011003667889296520e-01 6.808072569761871445e-03 -3.021007002334111791e-01 6.645530857221920371e-03 -3.031010336778926506e-01 6.486235939254444971e-03 -3.041013671223741222e-01 6.330141113237142819e-03 -3.051017005668556492e-01 6.177199841462057556e-03 -3.061020340113371208e-01 6.027365766594480409e-03 -3.071023674558186478e-01 5.880592726622047711e-03 -3.081027009003001194e-01 5.736834769299013065e-03 -3.091030343447815909e-01 5.596046166090448172e-03 -3.101033677892631180e-01 5.458181425621947726e-03 -3.111037012337445895e-01 5.323195306639871664e-03 -3.121040346782261166e-01 5.191042830488000297e-03 -3.131043681227075881e-01 5.061679293106330357e-03 -3.141047015671890597e-01 4.935060276557910047e-03 -3.151050350116705867e-01 4.811141660089832302e-03 -3.161053684561520583e-01 4.689879630734757922e-03 -3.171057019006335853e-01 4.571230693459184084e-03 -3.181060353451150569e-01 4.455151680865254010e-03 -3.191063687895965284e-01 4.341599762452411776e-03 -3.201067022340780555e-01 4.230532453446064940e-03 -3.211070356785595270e-01 4.121907623199885969e-03 -3.221073691230410541e-01 4.015683503178773747e-03 -3.231077025675225256e-01 3.911818694529623115e-03 -3.241080360120039972e-01 3.810272175246941055e-03 -3.251083694564855242e-01 3.711003306940715511e-03 -3.261087029009669958e-01 3.613971841213595420e-03 -3.271090363454485228e-01 3.519137925654898958e-03 -3.281093697899299944e-01 3.426462109458868095e-03 -3.291097032344114659e-01 3.335905348674498682e-03 -3.301100366788929930e-01 3.247429011094510320e-03 -3.311103701233744645e-01 3.160994880791012044e-03 -3.321107035678559916e-01 3.076565162305286254e-03 -3.331110370123374631e-01 2.994102484499428646e-03 -3.341113704568189346e-01 2.913569904077187214e-03 -3.351117039013004617e-01 2.834930908781813953e-03 -3.361120373457819333e-01 2.758149420278344782e-03 -3.371123707902634603e-01 2.683189796727949994e-03 -3.381127042347449319e-01 2.610016835061861398e-03 -3.391130376792264034e-01 2.538595772962481609e-03 -3.401133711237079305e-01 2.468892290559157772e-03 -3.411137045681894020e-01 2.400872511846096832e-03 -3.421140380126709291e-01 2.334503005829877739e-03 -3.431143714571524006e-01 2.269750787414048961e-03 -3.441147049016338721e-01 2.206583318028085446e-03 -3.451150383461153992e-01 2.144968506008075419e-03 -3.461153717905968707e-01 2.084874706736484454e-03 -3.471157052350783978e-01 2.026270722548067105e-03 -3.481160386795598694e-01 1.969125802409296081e-03 -3.491163721240413409e-01 1.913409641378168487e-03 -3.501167055685228680e-01 1.859092379851652209e-03 -3.511170390130043395e-01 1.806144602607605721e-03 -3.521173724574858666e-01 1.754537337648090500e-03 -3.531177059019673381e-01 1.704242054850913001e-03 -3.541180393464488096e-01 1.655230664436101564e-03 -3.551183727909303367e-01 1.607475515254037490e-03 -3.561187062354118082e-01 1.560949392901732477e-03 -3.571190396798933353e-01 1.515625517673775853e-03 -3.581193731243748068e-01 1.471477542354389378e-03 -3.591197065688562784e-01 1.428479549856815104e-03 -3.601200400133378055e-01 1.386606050716292140e-03 -3.611203734578192770e-01 1.345831980442766494e-03 -3.621207069023008041e-01 1.306132696739277163e-03 -3.631210403467822756e-01 1.267483976592049220e-03 -3.641213737912637471e-01 1.229862013237978174e-03 -3.651217072357452742e-01 1.193243413015370033e-03 -3.661220406802267457e-01 1.157605192103461746e-03 -3.671223741247082728e-01 1.122924773156262724e-03 -3.681227075691897443e-01 1.089179981836140834e-03 -3.691230410136712159e-01 1.056349043252433149e-03 -3.701233744581527429e-01 1.024410578310328782e-03 -3.711237079026342145e-01 9.933435999750594818e-04 -3.721240413471157416e-01 9.631275094564119895e-04 -3.731243747915972131e-01 9.337420923184676412e-04 -3.741247082360786846e-01 9.051675145192915545e-04 -3.751250416805602117e-01 8.773843183852553761e-04 -3.761253751250416832e-01 8.503734185245843177e-04 -3.771257085695232103e-01 8.241160976845041033e-04 -3.781260420140046818e-01 7.985940025564136494e-04 -3.791263754584861534e-01 7.737891395332304433e-04 -3.801267089029676804e-01 7.496838704231076258e-04 -3.811270423474491520e-01 7.262609081235029936e-04 -3.821273757919306790e-01 7.035033122595123721e-04 -3.831277092364121506e-01 6.813944847902941599e-04 -3.841280426808936221e-01 6.599181655872568032e-04 -3.851283761253751492e-01 6.390584279876472410e-04 -3.861287095698566207e-01 6.187996743269864571e-04 -3.871290430143381478e-01 5.991266314537698704e-04 -3.881293764588196193e-01 5.800243462297252845e-04 -3.891297099033011464e-01 5.614781810187848346e-04 -3.901300433477826179e-01 5.434738091678875801e-04 -3.911303767922640895e-01 5.259972104825754325e-04 -3.921307102367456165e-01 5.090346667002874307e-04 -3.931310436812270881e-01 4.925727569641421243e-04 -3.941313771257086152e-01 4.765983532998714070e-04 -3.951317105701900867e-01 4.610986160985657825e-04 -3.961320440146715582e-01 4.460609896076720890e-04 -3.971323774591530853e-01 4.314731974327123032e-04 -3.981327109036345568e-01 4.173232380520077280e-04 -3.991330443481160839e-01 4.035993803466622967e-04 -4.001333777925975554e-01 3.902901591479520922e-04 -4.011337112370790270e-01 3.773843708041693017e-04 -4.021340446815605540e-01 3.648710687688945064e-04 -4.031343781260420256e-01 3.527395592126039833e-04 -4.041347115705235526e-01 3.409793966593979166e-04 -4.051350450150050242e-01 3.295803796505891769e-04 -4.061353784594864957e-01 3.185325464367942021e-04 -4.071357119039680228e-01 3.078261707000994312e-04 -4.081360453484494943e-01 2.974517573078008965e-04 -4.091363787929310214e-01 2.874000380991116404e-04 -4.101367122374124929e-01 2.776619677062211553e-04 -4.111370456818939645e-01 2.682287194109427312e-04 -4.121373791263754915e-01 2.590916810381805788e-04 -4.131377125708569631e-01 2.502424508873330789e-04 -4.141380460153384901e-01 2.416728337027099899e-04 -4.151383794598199617e-01 2.333748366839687226e-04 -4.161387129043014332e-01 2.253406655375015007e-04 -4.171390463487829603e-01 2.175627205696544831e-04 -4.181393797932644318e-01 2.100335928226009180e-04 -4.191397132377459589e-01 2.027460602536173217e-04 -4.201400466822274304e-01 1.956930839584697997e-04 -4.211403801267089020e-01 1.888678044395513279e-04 -4.221407135711904290e-01 1.822635379193639454e-04 -4.231410470156719006e-01 1.758737726998849539e-04 -4.241413804601534276e-01 1.696921655682914591e-04 -4.251417139046348992e-01 1.637125382495006008e-04 -4.261420473491163707e-01 1.579288739058943149e-04 -4.271423807935978978e-01 1.523353136845834058e-04 -4.281427142380793693e-01 1.469261533125019309e-04 -4.291430476825608964e-01 1.416958397395839443e-04 -4.301433811270423679e-01 1.366389678302395506e-04 -4.311437145715238395e-01 1.317502771032898353e-04 -4.321440480160053665e-01 1.270246485204990980e-04 -4.331443814604868381e-01 1.224571013237938343e-04 -4.341447149049683651e-01 1.180427899212197764e-04 -4.351450483494498367e-01 1.137770008216618762e-04 -4.361453817939313082e-01 1.096551496183085394e-04 -4.371457152384128353e-01 1.056727780208162299e-04 -4.381460486828943068e-01 1.018255509360938466e-04 -4.391463821273758339e-01 9.810925359759418291e-05 -4.401467155718573054e-01 9.451978874298304367e-05 -4.411470490163387770e-01 9.105317384000944194e-05 -4.421473824608203040e-01 8.770553836039391793e-05 -4.431477159053017756e-01 8.447312110150836755e-05 -4.441480493497833026e-01 8.135226755561113308e-05 -4.451483827942647742e-01 7.833942732636999179e-05 -4.461487162387462457e-01 7.543115159238238132e-05 -4.471490496832277728e-01 7.262409061738856701e-05 -4.481493831277092443e-01 6.991499130684727286e-05 -4.491497165721907714e-01 6.730069481052405303e-05 -4.501500500166722429e-01 6.477813417073159500e-05 -4.511503834611537145e-01 6.234433201583393206e-05 -4.521507169056352415e-01 5.999639829862087764e-05 -4.531510503501167131e-01 5.773152807913816396e-05 -4.541513837945982401e-01 5.554699935154134568e-05 -4.551517172390797117e-01 5.344017091453988241e-05 -4.561520506835611832e-01 5.140848028497227445e-05 -4.571523841280427103e-01 4.944944165405045617e-05 -4.581527175725241818e-01 4.756064388579687909e-05 -4.591530510170057089e-01 4.573974855718859615e-05 -4.601533844614871804e-01 4.398448803951509592e-05 -4.611537179059686520e-01 4.229266362044548230e-05 -4.621540513504501790e-01 4.066214366629433344e-05 -4.631543847949316506e-01 3.909086182396980072e-05 -4.641547182394131776e-01 3.757681526207855684e-05 -4.651550516838946492e-01 3.611806295065843461e-05 -4.661553851283761207e-01 3.471272397900390606e-05 -4.671557185728576478e-01 3.335897591104560746e-05 -4.681560520173391193e-01 3.205505317774137556e-05 -4.691563854618206464e-01 3.079924550593036493e-05 -4.701567189063021179e-01 2.958989638310512645e-05 -4.711570523507836450e-01 2.842540155754692179e-05 -4.721573857952651165e-01 2.730420757327465094e-05 -4.731577192397465881e-01 2.622481033925121087e-05 -4.741580526842281151e-01 2.518575373229441658e-05 -4.751583861287095867e-01 2.418562823313749163e-05 -4.761587195731911137e-01 2.322306959508404386e-05 -4.771590530176725853e-01 2.229675754470423406e-05 -4.781593864621540568e-01 2.140541451401905908e-05 -4.791597199066355839e-01 2.054780440362209996e-05 -4.801600533511170554e-01 1.972273137618793667e-05 -4.811603867955985825e-01 1.892903867982041893e-05 -4.821607202400800540e-01 1.816560750069652014e-05 -4.831610536845615256e-01 1.743135584446193874e-05 -4.841613871290430526e-01 1.672523744584051986e-05 -4.851617205735245242e-01 1.604624070592162389e-05 -4.861620540180060512e-01 1.539338765659267825e-05 -4.871623874624875228e-01 1.476573295158967525e-05 -4.881627209069689943e-01 1.416236288364007936e-05 -4.891630543514505214e-01 1.358239442718003767e-05 -4.901633877959319929e-01 1.302497430612978958e-05 -4.911637212404135200e-01 1.248927808621690939e-05 -4.921640546848949915e-01 1.197450929134233875e-05 -4.931643881293764631e-01 1.147989854348860364e-05 -4.941647215738579901e-01 1.100470272567595887e-05 -4.951650550183394617e-01 1.054820416747601774e-05 -4.961653884628209887e-01 1.010970985259962279e-05 -4.971657219073024603e-01 9.688550648081097045e-06 -4.981660553517839318e-01 9.284080554585833557e-06 -4.991663887962654589e-01 8.895675977375519151e-06 -5.001667222407469859e-01 8.522735017470760603e-06 -5.011670556852284575e-01 8.164676782556840940e-06 -5.021673891297099290e-01 7.820940717184903215e-06 -5.031677225741914006e-01 7.490985951827106823e-06 -5.041680560186728721e-01 7.174290670350655021e-06 -5.051683894631544547e-01 6.870351495482126308e-06 -5.061687229076359262e-01 6.578682891839673173e-06 -5.071690563521173978e-01 6.298816586117576189e-06 -5.081693897965988693e-01 6.030301004014262694e-06 -5.091697232410803409e-01 5.772700723501136023e-06 -5.101700566855619234e-01 5.525595944036009684e-06 -5.111703901300433950e-01 5.288581971332632799e-06 -5.121707235745248665e-01 5.061268717302693215e-06 -5.131710570190063381e-01 4.843280214795237178e-06 -5.141713904634878096e-01 4.634254146763277629e-06 -5.151717239079693922e-01 4.433841389495400679e-06 -5.161720573524508637e-01 4.241705569555868505e-06 -5.171723907969323353e-01 4.057522634083560070e-06 -5.181727242414138068e-01 3.880980434106854636e-06 -5.191730576858952784e-01 3.711778320537789161e-06 -5.201733911303768609e-01 3.549626752515443185e-06 -5.211737245748583325e-01 3.394246917774928298e-06 -5.221740580193398040e-01 3.245370364724588776e-06 -5.231743914638212756e-01 3.102738645920883484e-06 -5.241747249083027471e-01 2.966102972636140430e-06 -5.251750583527843297e-01 2.835223880220847951e-06 -5.261753917972658012e-01 2.709870903968664442e-06 -5.271757252417472728e-01 2.589822265197782137e-06 -5.281760586862287443e-01 2.474864567269229545e-06 -5.291763921307102159e-01 2.364792501267864013e-06 -5.301767255751917984e-01 2.259408561078454681e-06 -5.311770590196732700e-01 2.158522767594723286e-06 -5.321773924641547415e-01 2.061952401805122113e-06 -5.331777259086362131e-01 1.969521746505025876e-06 -5.341780593531176846e-01 1.881061836390529332e-06 -5.351783927975992672e-01 1.796410216294697446e-06 -5.361787262420807387e-01 1.715410707332647100e-06 -5.371790596865622103e-01 1.637913180727230276e-06 -5.381793931310436818e-01 1.563773339092628898e-06 -5.391797265755251534e-01 1.492852504958282262e-06 -5.401800600200067359e-01 1.425017416320759712e-06 -5.411803934644882075e-01 1.360140029016649863e-06 -5.421807269089696790e-01 1.298097325714050516e-06 -5.431810603534511506e-01 1.238771131325804335e-06 -5.441813937979326221e-01 1.182047934652005619e-06 -5.451817272424142047e-01 1.127818716064465293e-06 -5.461820606868956762e-01 1.075978781050387187e-06 -5.471823941313771478e-01 1.026427599437084551e-06 -5.481827275758586193e-01 9.790686501243298962e-07 -5.491830610203400909e-01 9.338092711552381878e-07 -5.501833944648216734e-01 8.905605149610231607e-07 -5.511837279093031450e-01 8.492370086192936858e-07 -5.521840613537846165e-01 8.097568189697465454e-07 -5.531843947982660881e-01 7.720413224353442841e-07 -5.541847282427475596e-01 7.360150794010282637e-07 -5.551850616872291422e-01 7.016057130060342169e-07 -5.561853951317106137e-01 6.687437922098608657e-07 -5.571857285761920853e-01 6.373627189955882297e-07 -5.581860620206735568e-01 6.073986195782287017e-07 -5.591863954651550284e-01 5.787902394891982969e-07 -5.601867289096366109e-01 5.514788424117954902e-07 -5.611870623541180825e-01 5.254081126459770493e-07 -5.621873957985995540e-01 5.005240610841315992e-07 -5.631877292430810256e-01 4.767749345830085472e-07 -5.641880626875624971e-01 4.541111286201585044e-07 -5.651883961320440797e-01 4.324851031264678962e-07 -5.661887295765255512e-01 4.118513013895196745e-07 -5.671890630210070228e-01 3.921660719255447872e-07 -5.681893964654884943e-01 3.733875932207748900e-07 -5.691897299099699659e-01 3.554758012458595374e-07 -5.701900633544515484e-01 3.383923196498796192e-07 -5.711903967989330200e-01 3.221003925433339410e-07 -5.721907302434144915e-01 3.065648197820600387e-07 -5.731910636878959631e-01 2.917518946668519301e-07 -5.741913971323774346e-01 2.776293439759753741e-07 -5.751917305768590172e-01 2.641662702504049997e-07 -5.761920640213404887e-01 2.513330962540068329e-07 -5.771923974658219603e-01 2.391015115332811362e-07 -5.781927309103034318e-01 2.274444210036393302e-07 -5.791930643547849034e-01 2.163358954914386977e-07 -5.801933977992664859e-01 2.057511241632046411e-07 -5.811937312437479575e-01 1.956663687756361809e-07 -5.821940646882294290e-01 1.860589196820685255e-07 -5.831943981327109006e-01 1.769070535331357893e-07 -5.841947315771923721e-01 1.681899926113244271e-07 -5.851950650216739547e-01 1.598878657410580037e-07 -5.861953984661554262e-01 1.519816707178434140e-07 -5.871957319106368978e-01 1.444532382017966459e-07 -5.881960653551183693e-01 1.372851970226947669e-07 -5.891963987995998409e-01 1.304609408453732525e-07 -5.901967322440814234e-01 1.239645961460043683e-07 -5.911970656885628950e-01 1.177809914514072914e-07 -5.921973991330443665e-01 1.118956277951124432e-07 -5.931977325775258381e-01 1.062946503454694293e-07 -5.941980660220073096e-01 1.009648211625552744e-07 -5.951983994664888922e-01 9.589349304210169202e-08 -5.961987329109703637e-01 9.106858440606664810e-08 -5.971990663554518353e-01 8.647855520083717973e-08 -5.981993997999333068e-01 8.211238376538846122e-08 -5.991997332444148894e-01 7.795954463299409227e-08 -6.002000666888963609e-01 7.400998723134239772e-08 -6.012004001333778325e-01 7.025411544711496562e-08 -6.022007335778593040e-01 6.668276802227189204e-08 -6.032010670223407756e-01 6.328719975040051374e-08 -6.042014004668223581e-01 6.005906344261587435e-08 -6.052017339113038297e-01 5.699039263355009379e-08 -6.062020673557853012e-01 5.407358499900032417e-08 -6.072024008002667728e-01 5.130138645783588081e-08 -6.082027342447482443e-01 4.866687593169840821e-08 -6.092030676892298269e-01 4.616345073699761587e-08 -6.102034011337112984e-01 4.378481258460947204e-08 -6.112037345781927700e-01 4.152495416355149554e-08 -6.122040680226742415e-01 3.937814628579086576e-08 -6.132044014671557131e-01 3.733892557013798958e-08 -6.142047349116372956e-01 3.540208264399671587e-08 -6.152050683561187672e-01 3.356265084251151361e-08 -6.162054018006002387e-01 3.181589538538990081e-08 -6.172057352450817103e-01 3.015730301241842014e-08 -6.182060686895631818e-01 2.858257205937661037e-08 -6.192064021340447644e-01 2.708760295673333200e-08 -6.202067355785262359e-01 2.566848913416477352e-08 -6.212070690230077075e-01 2.432150831456535830e-08 -6.222074024674891790e-01 2.304311418182835398e-08 -6.232077359119706506e-01 2.182992840727008174e-08 -6.242080693564522331e-01 2.067873302013279558e-08 -6.252084028009337047e-01 1.958646310815804352e-08 -6.262087362454151762e-01 1.855019983474982925e-08 -6.272090696898966478e-01 1.756716375976104505e-08 -6.282094031343781193e-01 1.663470845142991100e-08 -6.292097365788597019e-01 1.575031437747474998e-08 -6.302100700233411734e-01 1.491158306381095001e-08 -6.312104034678226450e-01 1.411623150980384536e-08 -6.322107369123041165e-01 1.336208684939775745e-08 -6.332110703567855881e-01 1.264708124787480024e-08 -6.342114038012671706e-01 1.196924702439986980e-08 -6.352117372457486422e-01 1.132671199088810953e-08 -6.362120706902301137e-01 1.071769499810524765e-08 -6.372124041347115853e-01 1.014050168027053430e-08 -6.382127375791930568e-01 9.593520389771960425e-09 -6.392130710236746394e-01 9.075218313939920955e-09 -6.402134044681561109e-01 8.584137766144669038e-09 -6.412137379126375825e-01 8.118892643788538124e-09 -6.422140713571190540e-01 7.678165046066231591e-09 -6.432144048016005256e-01 7.260702044647058443e-09 -6.442147382460821081e-01 6.865312600711889984e-09 -6.452150716905635797e-01 6.490864622042707183e-09 -6.462154051350450512e-01 6.136282154115159951e-09 -6.472157385795265228e-01 5.800542699393906507e-09 -6.482160720240079943e-01 5.482674659265076213e-09 -6.492164054684895769e-01 5.181754893267168501e-09 -6.502167389129710484e-01 4.896906390502052357e-09 -6.512170723574525200e-01 4.627296048317843767e-09 -6.522174058019339915e-01 4.372132553557058059e-09 -6.532177392464154631e-01 4.130664361860029207e-09 -6.542180726908970456e-01 3.902177770698605368e-09 -6.552184061353785172e-01 3.685995081997093889e-09 -6.562187395798599887e-01 3.481472850369440422e-09 -6.572190730243414603e-01 3.288000213168039422e-09 -6.582194064688229318e-01 3.104997298699708781e-09 -6.592197399133045144e-01 2.931913709117727758e-09 -6.602200733577859859e-01 2.768227074646082464e-09 -6.612204068022674575e-01 2.613441675934257633e-09 -6.622207402467489290e-01 2.467087131476612770e-09 -6.632210736912304005e-01 2.328717147161107990e-09 -6.642214071357119831e-01 2.197908325137765893e-09 -6.652217405801934547e-01 2.074259029317522901e-09 -6.662220740246749262e-01 1.957388304927489138e-09 -6.672224074691563978e-01 1.846934849660408492e-09 -6.682227409136378693e-01 1.742556034061480511e-09 -6.692230743581194519e-01 1.643926968898497422e-09 -6.702234078026009234e-01 1.550739617359437576e-09 -6.712237412470823950e-01 1.462701950014610001e-09 -6.722240746915638665e-01 1.379537140572047147e-09 -6.732244081360453380e-01 1.300982800539895670e-09 -6.742247415805269206e-01 1.226790250993216583e-09 -6.752250750250083922e-01 1.156723829722020386e-09 -6.762254084694898637e-01 1.090560232112978898e-09 -6.772257419139713353e-01 1.028087884190841276e-09 -6.782260753584528068e-01 9.691063463152150332e-10 -6.792264088029343894e-01 9.134257460952962016e-10 -6.802267422474158609e-01 8.608662391495389544e-10 -6.812270756918973325e-01 8.112574963986117204e-10 -6.822274091363788040e-01 7.644382166388099283e-10 -6.832277425808602755e-01 7.202556631997266750e-10 -6.842280760253418581e-01 6.785652235436124528e-10 -6.852284094698233297e-01 6.392299907158750146e-10 -6.862287429143048012e-01 6.021203656054500492e-10 -6.872290763587862727e-01 5.671136790212102853e-10 -6.882294098032677443e-01 5.340938326358096406e-10 -6.892297432477493269e-01 5.029509578918920978e-10 -6.902300766922307984e-01 4.735810920068289105e-10 -6.912304101367122700e-01 4.458858702520572919e-10 -6.922307435811937415e-01 4.197722337209197291e-10 -6.932310770256752130e-01 3.951521518352182567e-10 -6.942314104701567956e-01 3.719423588754716636e-10 -6.952317439146382672e-01 3.500641038529355280e-10 -6.962320773591197387e-01 3.294429130732242734e-10 -6.972324108036012102e-01 3.100083647717846984e-10 -6.982327442480826818e-01 2.916938752302315037e-10 -6.992330776925642644e-01 2.744364958104528336e-10 -7.002334111370457359e-01 2.581767203697787891e-10 -7.012337445815272075e-01 2.428583025456827694e-10 -7.022340780260086790e-01 2.284280824228418280e-10 -7.032344114704901505e-01 2.148358221182139937e-10 -7.042347449149717331e-01 2.020340498419605502e-10 -7.052350783594532047e-01 1.899779120130382108e-10 -7.062354118039346762e-01 1.786250330282862973e-10 -7.072357452484161477e-01 1.679353823031035079e-10 -7.082360786928976193e-01 1.578711482200420212e-10 -7.092364121373792019e-01 1.483966186390817820e-10 -7.102367455818606734e-01 1.394780676400597127e-10 -7.112370790263421449e-01 1.310836481835853315e-10 -7.122374124708236165e-01 1.231832903919313646e-10 -7.132377459153050880e-01 1.157486051658863449e-10 -7.142380793597866706e-01 1.087527928672992296e-10 -7.152384128042681422e-01 1.021705568102375679e-10 -7.162387462487496137e-01 9.597802131620932078e-11 -7.172390796932310852e-01 9.015265410085058473e-11 -7.182394131377125568e-01 8.467319277088815202e-11 -7.192397465821941394e-01 7.951957522105358268e-11 -7.202400800266756109e-01 7.467287373095960003e-11 -7.212404134711570824e-01 7.011523257184285616e-11 -7.222407469156385540e-01 6.582980894246047003e-11 -7.232410803601200255e-01 6.180071706238653323e-11 -7.242414138046016081e-01 5.801297525949494002e-11 -7.252417472490830797e-01 5.445245589652854517e-11 -7.262420806935645512e-01 5.110583798939026473e-11 -7.272424141380460227e-01 4.796056237718746047e-11 -7.282427475825274943e-01 4.500478931103394488e-11 -7.292430810270090769e-01 4.222735833532455676e-11 -7.302434144714905484e-01 3.961775034154666963e-11 -7.312437479159720199e-01 3.716605168072446678e-11 -7.322440813604534915e-01 3.486292022638284361e-11 -7.332444148049349630e-01 3.269955328536063974e-11 -7.342447482494165456e-01 3.066765725903739312e-11 -7.352450816938980171e-01 2.875941896249096529e-11 -7.362454151383794887e-01 2.696747851380712113e-11 -7.372457485828609602e-01 2.528490371026476770e-11 -7.382460820273424318e-01 2.370516581237300562e-11 -7.392464154718240144e-01 2.222211666079121158e-11 -7.402467489163054859e-01 2.082996705502427511e-11 -7.412470823607869574e-01 1.952326632644490860e-11 -7.422474158052684290e-01 1.829688304167668858e-11 -7.432477492497499005e-01 1.714598677568693582e-11 -7.442480826942314831e-01 1.606603089707608018e-11 -7.452484161387129546e-01 1.505273631104400362e-11 -7.462487495831944262e-01 1.410207610835049955e-11 -7.472490830276758977e-01 1.321026107128376519e-11 -7.482494164721573693e-01 1.237372599021072997e-11 -7.492497499166389519e-01 1.158911674671867744e-11 -7.502500833611204234e-01 1.085327812166227262e-11 -7.512504168056018949e-01 1.016324228862659892e-11 -7.522507502500833665e-01 9.516217955396830899e-12 -7.532510836945648380e-01 8.909580118000690643e-12 -7.542514171390464206e-01 8.340860393766566725e-12 -7.552517505835278921e-01 7.807737901617893798e-12 -7.562520840280093637e-01 7.308030659513446920e-12 -7.572524174724908352e-01 6.839687470547551382e-12 -7.582527509169723068e-01 6.400780270740095649e-12 -7.592530843614538894e-01 5.989496912990730664e-12 -7.602534178059353609e-01 5.604134363038697705e-12 -7.612537512504168324e-01 5.243092284563466553e-12 -7.622540846948983040e-01 4.904866991795423065e-12 -7.632544181393797755e-01 4.588045749166858673e-12 -7.642547515838613581e-01 4.291301398641030809e-12 -7.652550850283428296e-01 4.013387296404396716e-12 -7.662554184728243012e-01 3.753132541597661342e-12 -7.672557519173057727e-01 3.509437480704637155e-12 -7.682560853617872443e-01 3.281269472107392122e-12 -7.692564188062688268e-01 3.067658896160719995e-12 -7.702567522507502984e-01 2.867695396939694023e-12 -7.712570856952317699e-01 2.680524342571073207e-12 -7.722574191397132415e-01 2.505343491777033787e-12 -7.732577525841948241e-01 2.341399854939602342e-12 -7.742580860286762956e-01 2.187986738637158050e-12 -7.752584194731577671e-01 2.044440963213429012e-12 -7.762587529176392387e-01 1.910140243517240864e-12 -7.772590863621207102e-01 1.784500723495409492e-12 -7.782594198066022928e-01 1.666974655839108912e-12 -7.792597532510837643e-01 1.557048218372613887e-12 -7.802600866955652359e-01 1.454239459335214816e-12 -7.812604201400467074e-01 1.358096364146066154e-12 -7.822607535845281790e-01 1.268195036654385893e-12 -7.832610870290097616e-01 1.184137988269937423e-12 -7.842614204734912331e-01 1.105552528738927771e-12 -7.852617539179727046e-01 1.032089252680279723e-12 -7.862620873624541762e-01 9.634206163290581311e-13 -7.872624208069356477e-01 8.992395992465892009e-13 -7.882627542514172303e-01 8.392584460528017698e-13 -7.892630876958987018e-01 7.832074835162505258e-13 -7.902634211403801734e-01 7.308340086017351845e-13 -7.912637545848616449e-01 6.819012433251967753e-13 -7.922640880293431165e-01 6.361873525019086595e-13 -7.932644214738246990e-01 5.934845206968477205e-13 -7.942647549183061706e-01 5.535980848971328096e-13 -7.952650883627876421e-01 5.163457196253597115e-13 -7.962654218072691137e-01 4.815566714007519873e-13 -7.972657552517505852e-01 4.490710396325925025e-13 -7.982660886962321678e-01 4.187391011981658428e-13 -7.992664221407136393e-01 3.904206761155981078e-13 -8.002667555851951109e-01 3.639845318716342280e-13 -8.012670890296765824e-01 3.393078241053293978e-13 -8.022674224741580540e-01 3.162755714818302201e-13 -8.032677559186396365e-01 2.947801627161787104e-13 -8.042680893631211081e-01 2.747208938254896474e-13 -8.052684228076025796e-01 2.560035337998564231e-13 -8.062687562520840512e-01 2.385399169879261821e-13 -8.072690896965655227e-01 2.222475605925122366e-13 -8.082694231410471053e-01 2.070493057656640567e-13 -8.092697565855285768e-01 1.928729808811578672e-13 -8.102700900300100484e-01 1.796510856458035899e-13 -8.112704234744915199e-01 1.673204947898856291e-13 -8.122707569189729915e-01 1.558221801511286320e-13 -8.132710903634545740e-01 1.451009500366817300e-13 -8.142714238079360456e-01 1.351052048135739832e-13 -8.152717572524175171e-01 1.257867077401976341e-13 -8.162720906968989887e-01 1.171003701100665157e-13 -8.172724241413804602e-01 1.090040498342490202e-13 -8.182727575858620428e-01 1.014583626408892503e-13 -8.192730910303435143e-01 9.442650511925543462e-14 -8.202734244748249859e-01 8.787408888190002299e-14 -8.212737579193064574e-01 8.176898516197440009e-14 -8.222740913637879290e-01 7.608117920371617069e-14 -8.232744248082695115e-01 7.078263384262360767e-14 -8.242747582527509831e-01 6.584716170817620383e-14 -8.252750916972324546e-01 6.125030551609978288e-14 -8.262754251417139262e-01 5.696922594934863886e-14 -8.272757585861953977e-01 5.298259665724562721e-14 -8.282760920306769803e-01 4.927050593071962725e-14 -8.292764254751584518e-01 4.581436463837543167e-14 -8.302767589196399234e-01 4.259682003336826824e-14 -8.312770923641213949e-01 3.960167506478144483e-14 -8.322774258086028665e-01 3.681381284952770860e-14 -8.332777592530844490e-01 3.421912598179973358e-14 -8.342780926975659206e-01 3.180445037683478502e-14 -8.352784261420473921e-01 2.955750336433126659e-14 -8.362787595865288637e-01 2.746682576432815698e-14 -8.372790930310103352e-01 2.552172769475539661e-14 -8.382794264754919178e-01 2.371223787531299409e-14 -8.392797599199733893e-01 2.202905620684018584e-14 -8.402800933644548609e-01 2.046350941896017522e-14 -8.412804268089363324e-01 1.900750959161851969e-14 -8.422807602534178040e-01 1.765351536815332612e-14 -8.432810936978993865e-01 1.639449568886232063e-14 -8.442814271423808581e-01 1.522389588465442398e-14 -8.452817605868623296e-01 1.413560598035286309e-14 -8.462820940313438012e-01 1.312393106660186574e-14 -8.472824274758252727e-01 1.218356360812880187e-14 -8.482827609203068553e-01 1.130955756438425734e-14 -8.492830943647883268e-01 1.049730420634874511e-14 -8.502834278092697984e-01 9.742509520583331782e-15 -8.512837612537512699e-01 9.041173098444486576e-15 -8.522840946982327415e-01 8.389568414807376592e-15 -8.532844281427143240e-01 7.784224406669961835e-15 -8.542847615871957956e-01 7.221908267667171221e-15 -8.552850950316772671e-01 6.699609379834124767e-15 -8.562854284761587387e-01 6.214524308936993235e-15 -8.572857619206402102e-01 5.764042794362781008e-15 -8.582860953651217928e-01 5.345734668943043277e-15 -8.592864288096032643e-01 4.957337648196196803e-15 -8.602867622540847359e-01 4.596745932329191046e-15 -8.612870956985662074e-01 4.261999567954953352e-15 -8.622874291430476790e-01 3.951274519870755381e-15 -8.632877625875292615e-01 3.662873406421364526e-15 -8.642880960320107331e-01 3.395216854950090103e-15 -8.652884294764922046e-01 3.146835436632104013e-15 -8.662887629209736762e-01 2.916362142603629039e-15 -8.672890963654551477e-01 2.702525365750855759e-15 -8.682894298099367303e-01 2.504142354821945881e-15 -8.692897632544182018e-01 2.320113109678933591e-15 -8.702900966988996734e-01 2.149414688521398856e-15 -8.712904301433811449e-01 1.991095899806262762e-15 -8.722907635878626165e-01 1.844272353355151115e-15 -8.732910970323441990e-01 1.708121846799402472e-15 -8.742914304768256706e-01 1.581880065065100320e-15 -8.752917639213071421e-01 1.464836572052342519e-15 -8.762920973657886137e-01 1.356331075025084689e-15 -8.772924308102700852e-01 1.255749943500208923e-15 -8.782927642547516678e-01 1.162522965617271099e-15 -8.792930976992331393e-01 1.076120326085745119e-15 -8.802934311437146109e-01 9.960497908509362930e-16 -8.812937645881960824e-01 9.218540845965112895e-16 -8.822940980326775540e-01 8.531084481159117855e-16 -8.832944314771591365e-01 7.894183634398414038e-16 -8.842947649216406081e-01 7.304174354070844844e-16 -8.852950983661220796e-01 6.757654191141685357e-16 -8.862954318106035512e-01 6.251463833789803476e-16 -8.872957652550850227e-01 5.782670010077743177e-16 -8.882960986995666053e-01 5.348549572670771806e-16 -8.892964321440480768e-01 4.946574685336149583e-16 -8.902967655885295484e-01 4.574399036304429151e-16 -8.912970990330110199e-01 4.229845008571657092e-16 -8.922974324774924915e-01 3.910891741892002432e-16 -8.932977659219740740e-01 3.615664025577837981e-16 -8.942980993664555456e-01 3.342421965302194497e-16 -8.952984328109370171e-01 3.089551370910169206e-16 -8.962987662554184887e-01 2.855554815808391175e-16 -8.972990996998999602e-01 2.639043321825462176e-16 -8.982994331443815428e-01 2.438728626545157475e-16 -8.992997665888630143e-01 2.253415993015181365e-16 -9.003001000333444859e-01 2.081997524443341595e-16 -9.013004334778259574e-01 1.923445949024821455e-16 -9.023007669223074290e-01 1.776808842404335168e-16 -9.033011003667890115e-01 1.641203257484093833e-16 -9.043014338112704831e-01 1.515810733346004844e-16 -9.053017672557519546e-01 1.399872656977982854e-16 -9.063021007002334262e-01 1.292685953287465752e-16 -9.073024341447148977e-01 1.193599080558722152e-16 -9.083027675891964803e-01 1.102008310070729656e-16 -9.093031010336779518e-01 1.017354270050125356e-16 -9.103034344781594234e-01 9.391187354916657875e-17 -9.113037679226408949e-01 8.668216466461487658e-17 -9.123041013671223665e-01 8.000183401581411061e-17 -9.133044348116039490e-01 7.382969779375378191e-17 -9.143047682560854206e-01 6.812761598775092776e-17 -9.153051017005668921e-01 6.286027074892463756e-17 -9.163054351450483637e-01 5.799496064176795431e-17 -9.173057685895298352e-01 5.350140966350287516e-17 -9.183061020340114178e-01 4.935158998854645498e-17 -9.193064354784928893e-01 4.551955746774810367e-17 -9.203067689229743609e-01 4.198129897949597577e-17 -9.213071023674558324e-01 3.871459079259595585e-17 -9.223074358119373040e-01 3.569886715936906617e-17 -9.233077692564188865e-01 3.291509841193407975e-17 -9.243081027009003581e-01 3.034567788542826591e-17 -9.253084361453818296e-01 2.797431703923070308e-17 -9.263087695898633012e-01 2.578594819131632987e-17 -9.273091030343447727e-01 2.376663432187519571e-17 -9.283094364788263553e-01 2.190348544054751710e-17 -9.293097699233078268e-01 2.018458104718804589e-17 -9.303101033677892984e-01 1.859889824916222372e-17 -9.313104368122707699e-01 1.713624512902347130e-17 -9.323107702567522415e-01 1.578719898507047838e-17 -9.333111037012338240e-01 1.454304909399889931e-17 -9.343114371457152956e-01 1.339574366968906195e-17 -9.353117705901967671e-01 1.233784071527791178e-17 -9.363121040346782387e-01 1.136246248717312016e-17 -9.373124374791597102e-01 1.046325330965708260e-17 -9.383127709236412928e-01 9.634340497337008099e-18 -9.393131043681227643e-01 8.870298159995667850e-18 -9.403134378126042359e-01 8.166113680488685338e-18 -9.413137712570857074e-01 7.517156671293122148e-18 -9.423141047015672900e-01 6.919150229223808238e-18 -9.433144381460487615e-01 6.368144320763377369e-18 -9.443147715905302331e-01 5.860491142473349589e-18 -9.453151050350117046e-01 5.392822312128134033e-18 -9.463154384794931762e-01 4.962027756593613944e-18 -9.473157719239747587e-01 4.565236172125282896e-18 -9.483161053684562303e-01 4.199796941725142897e-18 -9.493164388129377018e-01 3.863263402525256771e-18 -9.503167722574191734e-01 3.553377363908521659e-18 -9.513171057019006449e-01 3.268054784261109751e-18 -9.523174391463822275e-01 3.005372520930599639e-18 -9.533177725908636990e-01 2.763556074164053865e-18 -9.543181060353451706e-01 2.540968251556112776e-18 -9.553184394798266421e-01 2.336098684886506133e-18 -9.563187729243081137e-01 2.147554136187760456e-18 -9.573191063687896962e-01 1.974049534492422076e-18 -9.583194398132711678e-01 1.814399688986189880e-18 -9.593197732577526393e-01 1.667511628263328335e-18 -9.603201067022341109e-01 1.532377519063996122e-18 -9.613204401467155824e-01 1.408068121292722564e-18 -9.623207735911971650e-01 1.293726739288205427e-18 -9.633211070356786365e-01 1.188563632257928642e-18 -9.643214404801601081e-01 1.091850849520802572e-18 -9.653217739246415796e-01 1.002917458733076743e-18 -9.663221073691230512e-01 9.211451376211679865e-19 -9.673224408136046337e-01 8.459641019232663306e-19 -9.683227742580861053e-01 7.768493442604036118e-19 -9.693231077025675768e-01 7.133171605308841289e-19 -9.703234411470490484e-01 6.549219421573488453e-19 -9.713237745915305199e-01 6.012532141250525683e-19 -9.723241080360121025e-01 5.519329002416730727e-19 -9.733244414804935740e-01 5.066127984310844229e-19 -9.743247749249750456e-01 4.649722501545741733e-19 -9.753251083694565171e-01 4.267159892404177711e-19 -9.763254418139379887e-01 3.915721565019412151e-19 -9.773257752584195712e-01 3.592904675436036638e-19 -9.783261087029010428e-01 3.296405220982371635e-19 -9.793264421473825143e-01 3.024102441127232919e-19 -9.803267755918639859e-01 2.774044426093474700e-19 -9.813271090363454574e-01 2.544434840994622586e-19 -9.823274424808270400e-01 2.333620680205927025e-19 -9.833277759253085115e-01 2.140080973108868772e-19 -9.843281093697899831e-01 1.962416368297130549e-19 -9.853284428142714546e-01 1.799339528842563388e-19 -9.863287762587529262e-01 1.649666276316692860e-19 -9.873291097032345087e-01 1.512307425981988452e-19 -9.883294431477159803e-01 1.386261259934090218e-19 -9.893297765921974518e-01 1.270606589015972147e-19 -9.903301100366789234e-01 1.164496358063123223e-19 -9.913304434811603949e-01 1.067151752497290046e-19 -9.923307769256419775e-01 9.778567674845039521e-20 -9.933311103701234490e-01 8.959532038322419089e-20 -9.943314438146049206e-01 8.208360575366571853e-20 -9.953317772590863921e-01 7.519492724209850549e-20 -9.963321107035678637e-01 6.887818276457975823e-20 -9.973324441480494462e-01 6.308641340351402451e-20 -9.983327775925309178e-01 5.777647151617174481e-20 -9.993331110370123893e-01 5.290871509832035828e-20 -1.000333444481493972e+00 4.844672635297551606e-20 -1.001333777925975443e+00 4.435705257218893088e-20 -1.002334111370456915e+00 4.060896758567777288e-20 -1.003334444814938387e+00 3.717425216491144050e-20 -1.004334778259419858e+00 3.402699189581789838e-20 -1.005335111703901330e+00 3.114339114833408692e-20 -1.006335445148382801e+00 2.850160187729707294e-20 -1.007335778592864273e+00 2.608156608733453345e-20 -1.008336112037345744e+00 2.386487088507908836e-20 -1.009336445481827216e+00 2.183461512571872067e-20 -1.010336778926308909e+00 1.997528673821045153e-20 -1.011337112370790381e+00 1.827264988484107504e-20 -1.012337445815271852e+00 1.671364117667757197e-20 -1.013337779259753324e+00 1.528627422728888337e-20 -1.014338112704234796e+00 1.397955188321205636e-20 -1.015338446148716267e+00 1.278338552144968313e-20 -1.016338779593197739e+00 1.168852085207523217e-20 -1.017339113037679210e+00 1.068646970811553236e-20 -1.018339446482160682e+00 9.769447345574425484e-21 -1.019339779926642153e+00 8.930314813987603498e-21 -1.020340113371123847e+00 8.162525992519474272e-21 -1.021340446815605318e+00 7.460078918544358256e-21 -1.022340780260086790e+00 6.817471065099686490e-21 -1.023341113704568262e+00 6.229658250753219621e-21 -1.024341447149049733e+00 5.692016890463260664e-21 -1.025341780593531205e+00 5.200309319090720529e-21 -1.026342114038012676e+00 4.750651940502829152e-21 -1.027342447482494148e+00 4.339485974821230408e-21 -1.028342780926975619e+00 3.963550594442242563e-21 -1.029343114371457313e+00 3.619858256118692262e-21 -1.030343447815938784e+00 3.305672051743577321e-21 -1.031343781260420256e+00 3.018484914617251030e-21 -1.032344114704901727e+00 2.756000531016744752e-21 -1.033344448149383199e+00 2.516115818883712463e-21 -1.034344781593864671e+00 2.296904846509653844e-21 -1.035345115038346142e+00 2.096604074278929239e-21 -1.036345448482827614e+00 1.913598811908903497e-21 -1.037345781927309085e+00 1.746410792263414486e-21 -1.038346115371790557e+00 1.593686770767488957e-21 -1.039346448816272250e+00 1.454188066771271210e-21 -1.040346782260753722e+00 1.326780969951483046e-21 -1.041347115705235193e+00 1.210427941041009406e-21 -1.042347449149716665e+00 1.104179541887135736e-21 -1.043347782594198137e+00 1.007167035093146761e-21 -1.044348116038679608e+00 9.185955983321649677e-22 -1.045348449483161080e+00 8.377381028716002485e-22 -1.046348782927642551e+00 7.639294099387304925e-22 -1.047349116372124023e+00 6.965611423228364055e-22 -1.048349449816605494e+00 6.350768920727827749e-22 -1.049349783261087188e+00 5.789678283336843666e-22 -1.050350116705568659e+00 5.277686722957258675e-22 -1.051350450150050131e+00 4.810540089214237454e-22 -1.052350783594531602e+00 4.384349075946171535e-22 -1.053351117039013074e+00 3.995558261108616114e-22 -1.054351450483494546e+00 3.640917745224509712e-22 -1.055351783927976017e+00 3.317457172748167411e-22 -1.056352117372457489e+00 3.022461938395612503e-22 -1.057352450816938960e+00 2.753451396743978856e-22 -1.058352784261420432e+00 2.508158908334171830e-22 -1.059353117705902125e+00 2.284513569232139828e-22 -1.060353451150383597e+00 2.080623483610225415e-22 -1.061353784594865068e+00 1.894760450487058804e-22 -1.062354118039346540e+00 1.725345946404718237e-22 -1.063354451483828012e+00 1.570938295587300636e-22 -1.064354784928309483e+00 1.430220928098948672e-22 -1.065355118372790955e+00 1.301991634756891941e-22 -1.066355451817272426e+00 1.185152735119206210e-22 -1.067355785261753898e+00 1.078702081811967424e-22 -1.068356118706235369e+00 9.817248308356198591e-23 -1.069356452150717063e+00 8.933859133420228708e-23 -1.070356785595198534e+00 8.129231497447875496e-23 -1.071357119039680006e+00 7.396409519543816684e-23 -1.072357452484161477e+00 6.729045640526211244e-23 -1.073357785928642949e+00 6.121347958712966092e-23 -1.074358119373124421e+00 5.568032077469513933e-23 -1.075358452817605892e+00 5.064277082169794380e-23 -1.076358786262087364e+00 4.605685296260617455e-23 -1.077359119706568835e+00 4.188245495503376830e-23 -1.078359453151050307e+00 3.808299286418095220e-23 -1.079359786595532000e+00 3.462510379667790400e-23 -1.080360120040013472e+00 3.147836511778033422e-23 -1.081360453484494943e+00 2.861503789362006778e-23 -1.082360786928976415e+00 2.600983249066972760e-23 -1.083361120373457887e+00 2.363969443911976951e-23 -1.084361453817939358e+00 2.148360882689339329e-23 -1.085361787262420830e+00 1.952242163763485474e-23 -1.086362120706902301e+00 1.773867658038912438e-23 -1.087362454151383773e+00 1.611646608180696466e-23 -1.088362787595865244e+00 1.464129522450177178e-23 -1.089363121040346938e+00 1.329995751852755884e-23 -1.090363454484828409e+00 1.208042148760347369e-23 -1.091363787929309881e+00 1.097172713839228439e-23 -1.092364121373791352e+00 9.963891460561203774e-24 -1.093364454818272824e+00 9.047822178040109036e-24 -1.094364788262754296e+00 8.215239038477141901e-24 -1.095365121707235767e+00 7.458601988844557243e-24 -1.096365455151717239e+00 6.771045640940214432e-24 -1.097365788596198710e+00 6.146319481614230138e-24 -1.098366122040680182e+00 5.578733329298264752e-24 -1.099366455485161875e+00 5.063107581198157015e-24 -1.100366788929643347e+00 4.594727834667592378e-24 -1.101367122374124818e+00 4.169303502102393807e-24 -1.102367455818606290e+00 3.782930071475673948e-24 -1.103367789263087761e+00 3.432054694618087624e-24 -1.104368122707569233e+00 3.113444812777409080e-24 -1.105368456152050705e+00 2.824159554080536647e-24 -1.106368789596532176e+00 2.561523660466021140e-24 -1.107369123041013648e+00 2.323103722636793724e-24 -1.108369456485495119e+00 2.106686520770277669e-24 -1.109369789929976813e+00 1.910259286263975469e-24 -1.110370123374458284e+00 1.731991715831327667e-24 -1.111370456818939756e+00 1.570219583922230271e-24 -1.112370790263421227e+00 1.423429812842025100e-24 -1.113371123707902699e+00 1.290246872187734900e-24 -1.114371457152384171e+00 1.169420390412196404e-24 -1.115371790596865642e+00 1.059813871551298713e-24 -1.116372124041347114e+00 9.603944194933694463e-25 -1.117372457485828585e+00 8.702233807047560302e-25 -1.118372790930310057e+00 7.884478241222855413e-25 -1.119373124374791750e+00 7.142927840456912290e-25 -1.120373457819273222e+00 6.470541983671621578e-25 -1.121373791263754693e+00 5.860924804145372365e-25 -1.122374124708236165e+00 5.308266681097510506e-25 -1.123374458152717636e+00 4.807290990950683943e-25 -1.124374791597199108e+00 4.353205650016166949e-25 -1.125375125041680580e+00 3.941659021616610537e-25 -1.126375458486162051e+00 3.568699798330811271e-25 -1.127375791930643523e+00 3.230740504429553544e-25 -1.128376125375124994e+00 2.924524294947585425e-25 -1.129376458819606688e+00 2.647094756468150236e-25 -1.130376792264088159e+00 2.395768440821085185e-25 -1.131377125708569631e+00 2.168109886728579295e-25 -1.132377459153051102e+00 1.961908906174810748e-25 -1.133377792597532574e+00 1.775159932107447116e-25 -1.134378126042014046e+00 1.606043242166320543e-25 -1.135378459486495517e+00 1.452907889630340272e-25 -1.136378792930976989e+00 1.314256187815934461e-25 -1.137379126375458460e+00 1.188729607874517209e-25 -1.138379459819939932e+00 1.075095962441132394e-25 -1.139379793264421625e+00 9.722377589854303086e-26 -1.140380126708903097e+00 8.791416171057836447e-26 -1.141380460153384568e+00 7.948886534780233601e-26 -1.142380793597866040e+00 7.186457468007931569e-26 -1.143381127042347511e+00 6.496576029438705648e-26 -1.144381460486828983e+00 5.872395476723436220e-26 -1.145381793931310455e+00 5.307709808477758329e-26 -1.146382127375791926e+00 4.796894319557212461e-26 -1.147382460820273398e+00 4.334851622265735347e-26 -1.148382794264754869e+00 3.916962635511727521e-26 -1.149383127709236563e+00 3.539042088868829699e-26 -1.150383461153718034e+00 3.197298129423358098e-26 -1.151383794598199506e+00 2.888295656550279064e-26 -1.152384128042680977e+00 2.608923043693552193e-26 -1.153384461487162449e+00 2.356361937105970418e-26 -1.154384794931643921e+00 2.128059849623950912e-26 -1.155385128376125392e+00 1.921705293140775461e-26 -1.156385461820606864e+00 1.735205216732190635e-26 -1.157385795265088335e+00 1.566664538582461855e-26 -1.158386128709569807e+00 1.414367579144717253e-26 -1.159386462154051500e+00 1.276761220514829226e-26 -1.160386795598532972e+00 1.152439632962141199e-26 -1.161387129043014443e+00 1.040130424080267290e-26 -1.162387462487495915e+00 9.386820792297507675e-27 -1.163387795931977386e+00 8.470525739563391922e-27 -1.164388129376458858e+00 7.642990499925254936e-27 -1.165388462820940330e+00 6.895684563835116654e-27 -1.166388796265421801e+00 6.220890663104789914e-27 -1.167389129709903273e+00 5.611627883943428947e-27 -1.168389463154384744e+00 5.061581987279027942e-27 -1.169389796598866438e+00 4.565042266684534267e-27 -1.170390130043347909e+00 4.116844335891978883e-27 -1.171390463487829381e+00 3.712318293915979570e-27 -1.172390796932310852e+00 3.347241766729822445e-27 -1.173391130376792324e+00 3.017797370702089436e-27 -1.174391463821273796e+00 2.720534185038451270e-27 -1.175391797265755267e+00 2.452332858656190249e-27 -1.176392130710236739e+00 2.210374011605737750e-27 -1.177392464154718210e+00 1.992109622655628198e-27 -1.178392797599199682e+00 1.795237123266818899e-27 -1.179393131043681375e+00 1.617675944162153989e-27 -1.180393464488162847e+00 1.457546284287197218e-27 -1.181393797932644318e+00 1.313149893372787743e-27 -1.182394131377125790e+00 1.182952678755651775e-27 -1.183394464821607261e+00 1.065568964758635706e-27 -1.184394798266088733e+00 9.597472489520710006e-28 -1.185395131710570205e+00 8.643573141549816297e-28 -1.186395465155051676e+00 7.783785682264610357e-28 -1.187395798599533148e+00 7.008894956681175302e-28 -1.188396132044014619e+00 6.310581159189709137e-28 -1.189396465488496313e+00 5.681333530761883240e-28 -1.190396798932977784e+00 5.114372307128471219e-28 -1.191397132377459256e+00 4.603578135699838908e-28 -1.192397465821940727e+00 4.143428252521915617e-28 -1.193397799266422199e+00 3.728938777231961125e-28 -1.194398132710903671e+00 3.355612544429946647e-28 -1.195398466155385142e+00 3.019391944696705232e-28 -1.196398799599866614e+00 2.716616298180747930e-28 -1.197399133044348085e+00 2.443983328720295075e-28 -1.198399466488829779e+00 2.198514347298182937e-28 -1.199399799933311250e+00 1.977522790632352107e-28 -1.200400133377792722e+00 1.778585794237283730e-28 -1.201400466822274193e+00 1.599518509683630011e-28 -1.202400800266755665e+00 1.438350903312412831e-28 -1.203401133711237136e+00 1.293306798606658224e-28 -1.204401467155718608e+00 1.162784947019631850e-28 -1.205401800600200080e+00 1.045341932526125012e-28 -1.206402134044681551e+00 9.396767337021181358e-29 -1.207402467489163023e+00 8.446167839258367185e-29 -1.208402800933644716e+00 7.591053854964533601e-29 -1.209403134378126188e+00 6.821903472314509118e-29 -1.210403467822607659e+00 6.130137275657896260e-29 -1.211403801267089131e+00 5.508025764591196961e-29 -1.212404134711570602e+00 4.948605796276776632e-29 -1.213404468156052074e+00 4.445605178623002669e-29 -1.214404801600533546e+00 3.993374625579913234e-29 -1.215405135045015017e+00 3.586826361507736730e-29 -1.216405468489496489e+00 3.221378730068342934e-29 -1.217405801933977960e+00 2.892906225060779895e-29 -1.218406135378459654e+00 2.597694416682594964e-29 -1.219406468822941125e+00 2.332399297414326363e-29 -1.220406802267422597e+00 2.094010617590304930e-29 -1.221407135711904068e+00 1.879818822205985622e-29 -1.222407469156385540e+00 1.687385238025312931e-29 -1.223407802600867011e+00 1.514515193972999771e-29 -1.224408136045348483e+00 1.359233788468552294e-29 -1.225408469489829955e+00 1.219764045085160397e-29 -1.226408802934311426e+00 1.094507222982065268e-29 -1.227409136378792898e+00 9.820250712140151056e-30 -1.228409469823274591e+00 8.810238364987536565e-30 -1.229409803267756063e+00 7.903398525263457716e-30 -1.230410136712237534e+00 7.089265556158149269e-30 -1.231410470156719006e+00 6.358427866330501671e-30 -1.232410803601200477e+00 5.702422527319602399e-30 -1.233411137045681949e+00 5.113640348120209865e-30 -1.234411470490163421e+00 4.585240377223149329e-30 -1.235411803934644892e+00 4.111072903016325538e-30 -1.236412137379126364e+00 3.685610114288282291e-30 -1.237412470823607835e+00 3.303883664607842071e-30 -1.238412804268089529e+00 2.961428458426315548e-30 -1.239413137712571000e+00 2.654232043625223576e-30 -1.240413471157052472e+00 2.378689055597149256e-30 -1.241413804601533943e+00 2.131560212450631173e-30 -1.242414138046015415e+00 1.909935410109887377e-30 -1.243414471490496886e+00 1.711200510473037143e-30 -1.244414804934978358e+00 1.533007455850887261e-30 -1.245415138379459830e+00 1.373247379051755856e-30 -1.246415471823941301e+00 1.230026411091291294e-30 -1.247415805268422773e+00 1.101643917925421729e-30 -1.248416138712904466e+00 9.865729241440103487e-31 -1.249416472157385938e+00 8.834425055002690338e-31 -1.250416805601867409e+00 7.910219537399693239e-31 -1.251417139046348881e+00 7.082065366635154787e-31 -1.252417472490830352e+00 6.340046939095171062e-31 -1.253417805935311824e+00 5.675265247769021560e-31 -1.254418139379793296e+00 5.079734386729985734e-31 -1.255418472824274767e+00 4.546288516389858839e-31 -1.256418806268756239e+00 4.068498239987638016e-31 -1.257419139713237710e+00 3.640595446286756491e-31 -1.258419473157719404e+00 3.257405767627880642e-31 -1.259419806602200875e+00 2.914287887356657866e-31 -1.260420140046682347e+00 2.607079007109158940e-31 -1.261420473491163818e+00 2.332045853333577198e-31 -1.262420806935645290e+00 2.085840664486038312e-31 -1.263421140380126761e+00 1.865461656243776903e-31 -1.264421473824608233e+00 1.668217512429044701e-31 -1.265421807269089705e+00 1.491695494683719911e-31 -1.266422140713571176e+00 1.333732804769160969e-31 -1.267422474158052648e+00 1.192390870133470197e-31 -1.268422807602534341e+00 1.065932256493404547e-31 -1.269423141047015813e+00 9.527999409793954265e-32 -1.270423474491497284e+00 8.515987062182163419e-32 -1.271423807935978756e+00 7.610784398755356326e-32 -1.272424141380460227e+00 6.801191459097520736e-32 -1.273424474824941699e+00 6.077174933463482039e-32 -1.274424808269423171e+00 5.429747459781767949e-32 -1.275425141713904642e+00 4.850859322300155381e-32 -1.276425475158386114e+00 4.333301286695010085e-32 -1.277425808602867585e+00 3.870617434599553442e-32 -1.278426142047349279e+00 3.457026975755848904e-32 -1.279426475491830750e+00 3.087354119656024930e-32 -1.280426808936312222e+00 2.756965181752916351e-32 -1.281427142380793693e+00 2.461712183150773340e-32 -1.282427475825275165e+00 2.197882278051721635e-32 -1.283427809269756636e+00 1.962152410996913061e-32 -1.284428142714238108e+00 1.751548666855653912e-32 -1.285428476158719580e+00 1.563409831271270704e-32 -1.286428809603201051e+00 1.395354728484500390e-32 -1.287429143047682523e+00 1.245252947684215126e-32 -1.288429476492164216e+00 1.111198608780275993e-32 -1.289429809936645688e+00 9.914868542032649852e-33 -1.290430143381127159e+00 8.845927854225258496e-33 -1.291430476825608631e+00 7.891525916978885358e-33 -1.292430810270090102e+00 7.039466444716067062e-33 -1.293431143714571574e+00 6.278843540644691286e-33 -1.294431477159053046e+00 5.599906062250107314e-33 -1.295431810603534517e+00 4.993936148384995586e-33 -1.296432144048015989e+00 4.453140439437537102e-33 -1.297432477492497460e+00 3.970552673276507958e-33 -1.298432810936979154e+00 3.539946475430377764e-33 -1.299433144381460625e+00 3.155757283824397595e-33 -1.300433477825942097e+00 2.813012457781508095e-33 -1.301433811270423568e+00 2.507268719175848220e-33 -1.302434144714905040e+00 2.234556161725450975e-33 -1.303434478159386511e+00 1.991328143474670433e-33 -1.304434811603867983e+00 1.774416448452622170e-33 -1.305435145048349455e+00 1.580991167133977291e-33 -1.306435478492830926e+00 1.408524802423730390e-33 -1.307435811937312398e+00 1.254760159095974691e-33 -1.308436145381794091e+00 1.117681620550834963e-33 -1.309436478826275563e+00 9.954894579452276613e-34 -1.310436812270757034e+00 8.865768536938617072e-34 -1.311437145715238506e+00 7.895093544578284661e-34 -1.312437479159719977e+00 7.030064984349535820e-34 -1.313437812604201449e+00 6.259253883881491933e-34 -1.314438146048682921e+00 5.572460057116877835e-34 -1.315438479493164392e+00 4.960580822258616690e-34 -1.316438812937645864e+00 4.415493655597066234e-34 -1.317439146382127335e+00 3.929951311614161328e-34 -1.318439479826609029e+00 3.497488093692385637e-34 -1.319439813271090500e+00 3.112336097680308844e-34 -1.320440146715571972e+00 2.769350374129534420e-34 -1.321440480160053443e+00 2.463942065706329332e-34 -1.322440813604534915e+00 2.192018675426966609e-34 -1.323441147049016386e+00 1.949930710164136811e-34 -1.324441480493497858e+00 1.734424023395013415e-34 -1.325441813937979330e+00 1.542597252371583432e-34 -1.326442147382460801e+00 1.371863808654493838e-34 -1.327442480826942273e+00 1.219917938034791802e-34 -1.328442814271423966e+00 1.084704416971095449e-34 -1.329443147715905438e+00 9.643914984118178763e-35 -1.330443481160386909e+00 8.573467608137164962e-35 -1.331443814604868381e+00 7.621155508096265839e-35 -1.332444148049349852e+00 6.774017427658511615e-35 -1.333444481493831324e+00 6.020505678094207919e-35 -1.334444814938312796e+00 5.350332911548664828e-35 -1.335445148382794267e+00 4.754335400429883857e-35 -1.336445481827275739e+00 4.224351056108214995e-35 -1.337445815271757210e+00 3.753110608013314590e-35 -1.338446148716238904e+00 3.334140532249459032e-35 -1.339446482160720375e+00 2.961676469134818454e-35 -1.340446815605201847e+00 2.630586003439600819e-35 -1.341447149049683318e+00 2.336299801248568078e-35 -1.342447482494164790e+00 2.074750204785556926e-35 -1.343447815938646261e+00 1.842316482556572816e-35 -1.344448149383127733e+00 1.635776018000005939e-35 -1.345448482827609205e+00 1.452260796540991421e-35 -1.346448816272090676e+00 1.289218619502514539e-35 -1.347449149716572148e+00 1.144378534587118022e-35 -1.348449483161053841e+00 1.015720027380346810e-35 -1.349449816605535313e+00 9.014455672304611636e-36 -1.350450150050016784e+00 7.999561445477330640e-36 -1.351450483494498256e+00 7.098294755915397168e-36 -1.352450816938979727e+00 6.298005856690849845e-36 -1.353451150383461199e+00 5.587445127999066357e-36 -1.354451483827942671e+00 4.956609016984705752e-36 -1.355451817272424142e+00 4.396602827506061443e-36 -1.356452150716905614e+00 3.899518528209462556e-36 -1.357452484161387085e+00 3.458325945147419864e-36 -1.358452817605868779e+00 3.066775881787889996e-36 -1.359453151050350250e+00 2.719313866916521691e-36 -1.360453484494831722e+00 2.411003371625601533e-36 -1.361453817939313193e+00 2.137457462156114881e-36 -1.362454151383794665e+00 1.894777967396832722e-36 -1.363454484828276136e+00 1.679501339818950483e-36 -1.364454818272757608e+00 1.488550477814670870e-36 -1.365455151717239080e+00 1.319191856972438044e-36 -1.366455485161720551e+00 1.168997388794472979e-36 -1.367455818606202245e+00 1.035810488661546961e-36 -1.368456152050683716e+00 9.177158913031621420e-37 -1.369456485495165188e+00 8.130128023726127495e-37 -1.370456818939646659e+00 7.201910196136307374e-37 -1.371457152384128131e+00 6.379096971256004547e-37 -1.372457485828609602e+00 5.649784619119449291e-37 -1.373457819273091074e+00 5.003406236995738053e-37 -1.374458152717572545e+00 4.430582473638753585e-37 -1.375458486162054017e+00 3.922988825581035651e-37 -1.376458819606535489e+00 3.473237676590471796e-37 -1.377459153051017182e+00 3.074773452030092307e-37 -1.378459486495498654e+00 2.721779438600786942e-37 -1.379459819939980125e+00 2.409094979190620273e-37 -1.380460153384461597e+00 2.132141894412580522e-37 -1.381460486828943068e+00 1.886859108765150860e-37 -1.382460820273424540e+00 1.669644571891080562e-37 -1.383461153717906011e+00 1.477303665632338438e-37 -1.384461487162387483e+00 1.307003376824605766e-37 -1.385461820606868955e+00 1.156231595240160571e-37 -1.386462154051350426e+00 1.022760966836930745e-37 -1.387462487495832120e+00 9.046167954518277004e-38 -1.388462820940313591e+00 8.000485421419575559e-38 -1.389463154384795063e+00 7.075045212751858826e-38 -1.390463487829276534e+00 6.256094368822115586e-38 -1.391463821273758006e+00 5.531444423016567542e-38 -1.392464154718239477e+00 4.890294413141903534e-38 -1.393464488162720949e+00 4.323073802496212002e-38 -1.394464821607202420e+00 3.821303083848754193e-38 -1.395465155051683892e+00 3.377470087109506552e-38 -1.396465488496165364e+00 2.984920231705978621e-38 -1.397465821940647057e+00 2.637759160561525505e-38 -1.398466155385128529e+00 2.330766366759819067e-38 -1.399466488829610000e+00 2.059318578877715094e-38 -1.400466822274091472e+00 1.819321808694956669e-38 -1.401467155718572943e+00 1.607151087429789831e-38 -1.402467489163054415e+00 1.419597025502788186e-38 -1.403467822607535886e+00 1.253818427582855995e-38 -1.404468156052017358e+00 1.107300280668552696e-38 -1.405468489496498830e+00 9.778165093819035708e-39 -1.406468822940980301e+00 8.633969605689111444e-39 -1.407469156385461995e+00 7.622981396459039183e-39 -1.408469489829943466e+00 6.729772747484180449e-39 -1.409469823274424938e+00 5.940693323686364425e-39 -1.410470156718906409e+00 5.243666504808870502e-39 -1.411470490163387881e+00 4.628008927346165479e-39 -1.412470823607869352e+00 4.084270606723785693e-39 -1.413471157052350824e+00 3.604093305715091192e-39 -1.414471490496832295e+00 3.180085078276303996e-39 -1.415471823941313767e+00 2.805709151681245158e-39 -1.416472157385795239e+00 2.475185517305076750e-39 -1.417472490830276932e+00 2.183403784584716958e-39 -1.418472824274758404e+00 1.925846016164921036e-39 -1.419473157719239875e+00 1.698518407337072143e-39 -1.420473491163721347e+00 1.497890801647435492e-39 -1.421473824608202818e+00 1.320843148819872999e-39 -1.422474158052684290e+00 1.164618112527656787e-39 -1.423474491497165761e+00 1.026779125504526849e-39 -1.424474824941647233e+00 9.051732692870783502e-40 -1.425475158386128705e+00 7.978984266681881451e-40 -1.426475491830610176e+00 7.032742177292546577e-40 -1.427475825275091870e+00 6.198162860026916351e-40 -1.428476158719573341e+00 5.462135506979876086e-40 -1.429476492164054813e+00 4.813080847110575062e-40 -1.430476825608536284e+00 4.240773169603126063e-40 -1.431477159053017756e+00 3.736182920109467064e-40 -1.432477492497499227e+00 3.291337504602079479e-40 -1.433477825941980699e+00 2.899198206007548773e-40 -1.434478159386462170e+00 2.553551358484282411e-40 -1.435478492830943642e+00 2.248912136633643670e-40 -1.436478826275425114e+00 1.980439505161778153e-40 -1.437479159719906807e+00 1.743861041299074819e-40 -1.438479493164388279e+00 1.535406490047502766e-40 -1.439479826608869750e+00 1.351749043229060924e-40 -1.440480160053351222e+00 1.189953449265601576e-40 -1.441480493497832693e+00 1.047430163319753253e-40 -1.442480826942314165e+00 9.218948383867317199e-41 -1.443481160386795636e+00 8.113325384728552013e-41 -1.444481493831277108e+00 7.139661263192220275e-41 -1.445481827275758580e+00 6.282283412764932199e-41 -1.446482160720240051e+00 5.527371388419876583e-41 -1.447482494164721745e+00 4.862739128575654528e-41 -1.448482827609203216e+00 4.277642651713725183e-41 -1.449483161053684688e+00 3.762610263342654680e-41 -1.450483494498166159e+00 3.309292652115370851e-41 -1.451483827942647631e+00 2.910330557499070892e-41 -1.452484161387129102e+00 2.559237960020219795e-41 -1.453484494831610574e+00 2.250298982767435531e-41 -1.454484828276092045e+00 1.978476903075231525e-41 -1.455485161720573517e+00 1.739333859280235268e-41 -1.456485495165054989e+00 1.528960001928648011e-41 -1.457485828609536682e+00 1.343910984282640350e-41 -1.458486162054018154e+00 1.181152815611878581e-41 -1.459486495498499625e+00 1.038013214503908157e-41 -1.460486828942981097e+00 9.121386999948873556e-42 -1.461487162387462568e+00 8.014567472288856400e-42 -1.462487495831944040e+00 7.041424129479961143e-42 -1.463487829276425511e+00 6.185889055810790949e-42 -1.464488162720906983e+00 5.433816360963907771e-42 -1.465488496165388455e+00 4.772753400397134725e-42 -1.466488829609869926e+00 4.191739091240922054e-42 -1.467489163054351620e+00 3.681126130987727784e-42 -1.468489496498833091e+00 3.232424300509485323e-42 -1.469489829943314563e+00 2.838162363548285108e-42 -1.470490163387796034e+00 2.491766366874130307e-42 -1.471490496832277506e+00 2.187452403232599548e-42 -1.472490830276758977e+00 1.920132127006517578e-42 -1.473491163721240449e+00 1.685329513675624615e-42 -1.474491497165721920e+00 1.479107531777157276e-42 -1.475491830610203392e+00 1.298003552893648456e-42 -1.476492164054684864e+00 1.138972463638740636e-42 -1.477492497499166557e+00 9.993365658212641305e-43 -1.478492830943648029e+00 8.767414588382858101e-43 -1.479493164388129500e+00 7.691171935488398096e-43 -1.480493497832610972e+00 6.746440708958415687e-43 -1.481493831277092443e+00 5.917225326779012733e-43 -1.482494164721573915e+00 5.189466572843466604e-43 -1.483494498166055386e+00 4.550808309147876981e-43 -1.484494831610536858e+00 3.990392157124468226e-43 -1.485495165055018330e+00 3.498676811446738322e-43 -1.486495498499499801e+00 3.067279045699821638e-43 -1.487495831943981495e+00 2.688833818588854237e-43 -1.488496165388462966e+00 2.356871197369965559e-43 -1.489496498832944438e+00 2.065708086772182994e-43 -1.490496832277425909e+00 1.810352991125543917e-43 -1.491497165721907381e+00 1.586422248502075183e-43 -1.492497499166388852e+00 1.390066361752968200e-43 -1.493497832610870324e+00 1.217905215336408472e-43 -1.494498166055351795e+00 1.066971111378002311e-43 -1.495498499499833267e+00 9.346586857910626512e-44 -1.496498832944314739e+00 8.186808775272391858e-44 -1.497499166388796432e+00 7.170302229295239120e-44 -1.498499499833277904e+00 6.279448342880612977e-44 -1.499499833277759375e+00 5.498784984539501115e-44 -1.500500166722240847e+00 4.814743989762582861e-44 -1.501500500166722318e+00 4.215420247723090903e-44 -1.502500833611203790e+00 3.690368807843724911e-44 -1.503501167055685261e+00 3.230426622543135533e-44 -1.504501500500166733e+00 2.827555949098917784e-44 -1.505501833944648205e+00 2.474706791554944483e-44 -1.506502167389129676e+00 2.165696078766084261e-44 -1.507502500833611370e+00 1.895101552091231917e-44 -1.508502834278092841e+00 1.658168580435330127e-44 -1.509503167722574313e+00 1.450728335240988214e-44 -1.510503501167055784e+00 1.269125947156441285e-44 -1.511503834611537256e+00 1.110157432516834868e-44 -1.512504168056018727e+00 9.710143241972406328e-45 -1.513504501500500199e+00 8.492350702122041335e-45 -1.514504834944981670e+00 7.426623767539946714e-45 -1.515505168389463142e+00 6.494057720373943802e-45 -1.516505501833944614e+00 5.678087549855358668e-45 -1.517505835278426307e+00 4.964199698899925075e-45 -1.518506168722907779e+00 4.339679159742106584e-45 -1.519506502167389250e+00 3.793387604029055833e-45 -1.520506835611870722e+00 3.315568756905592892e-45 -1.521507169056352193e+00 2.897677685375339000e-45 -1.522507502500833665e+00 2.532231076242452055e-45 -1.523507835945315136e+00 2.212675934925500432e-45 -1.524508169389796608e+00 1.933274449307205037e-45 -1.525508502834278080e+00 1.689003037725805836e-45 -1.526508836278759551e+00 1.475463841811350017e-45 -1.527509169723241245e+00 1.288807137139287079e-45 -1.528509503167722716e+00 1.125663321165680328e-45 -1.529509836612204188e+00 9.830833017275530909e-46 -1.530510170056685659e+00 8.584862532941053194e-46 -1.531510503501167131e+00 7.496138345373219669e-46 -1.532510836945648602e+00 6.544900717843493510e-46 -1.533511170390130074e+00 5.713862103816070664e-46 -1.534511503834611545e+00 4.987899215797780227e-46 -1.535511837279093017e+00 4.353783276871997107e-46 -1.536512170723574489e+00 3.799943742002324841e-46 -1.537512504168056182e+00 3.316261355209930504e-46 -1.538512837612537654e+00 2.893886916943229661e-46 -1.539513171057019125e+00 2.525082581989089768e-46 -1.540513504501500597e+00 2.203082899692814294e-46 -1.541513837945982068e+00 1.921973151714765656e-46 -1.542514171390463540e+00 1.676582843902373818e-46 -1.543514504834945011e+00 1.462392473235265882e-46 -1.544514838279426483e+00 1.275451922724367986e-46 -1.545515171723907955e+00 1.112309040574677190e-46 -1.546515505168389648e+00 9.699471383459353122e-47 -1.547515838612871120e+00 8.457302993187389622e-47 -1.548516172057352591e+00 7.373555254868156467e-47 -1.549516505501834063e+00 6.428108719095170418e-47 -1.550516838946315534e+00 5.603388226391303665e-47 -1.551517172390797006e+00 4.884042549115775936e-47 -1.552517505835278477e+00 4.256664193480126924e-47 -1.553517839279759949e+00 3.709544349629629583e-47 -1.554518172724241420e+00 3.232458600429686666e-47 -1.555518506168722892e+00 2.816479545271514525e-47 -1.556518839613204586e+00 2.453812973358184657e-47 -1.557519173057686057e+00 2.137654639878253229e-47 -1.558519506502167529e+00 1.862065065499773423e-47 -1.559519839946649000e+00 1.621860101142128126e-47 -1.560520173391130472e+00 1.412515281611444821e-47 -1.561520506835611943e+00 1.230082238347751323e-47 -1.562520840280093415e+00 1.071115657553779949e-47 -1.563521173724574886e+00 9.326094591378303954e-48 -1.564521507169056358e+00 8.119410375362294623e-48 -1.565521840613537830e+00 7.068225504951295775e-48 -1.566522174058019523e+00 6.152583688430939389e-48 -1.567522507502500995e+00 5.355079114128557975e-48 -1.568522840946982466e+00 4.660531865382200826e-48 -1.569523174391463938e+00 4.055704466803129747e-48 -1.570523507835945409e+00 3.529054372326813564e-48 -1.571523841280426881e+00 3.070517857426421039e-48 -1.572524174724908352e+00 2.671321348201463196e-48 -1.573524508169389824e+00 2.323816719046428644e-48 -1.574524841613871295e+00 2.021337527101182421e-48 -1.575525175058352767e+00 1.758073533499027046e-48 -1.576525508502834461e+00 1.528961195369921984e-48 -1.577525841947315932e+00 1.329588104605461914e-48 -1.578526175391797404e+00 1.156109604771299554e-48 -1.579526508836278875e+00 1.005176040862226328e-48 -1.580526842280760347e+00 8.738692918234392671e-49 -1.581527175725241818e+00 7.596474064412204561e-49 -1.582527509169723290e+00 6.602963124011137028e-49 -1.583527842614204761e+00 5.738876987154031495e-49 -1.584528176058686233e+00 4.987422856937047603e-49 -1.585528509503167705e+00 4.333977962278240498e-49 -1.586528842947649398e+00 3.765810291924348181e-49 -1.587529176392130870e+00 3.271835118042629214e-49 -1.588529509836612341e+00 2.842402742153423091e-49 -1.589529843281093813e+00 2.469113476503678004e-49 -1.590530176725575284e+00 2.144656380898552132e-49 -1.591530510170056756e+00 1.862668717757650551e-49 -1.592530843614538227e+00 1.617613474824637194e-49 -1.593531177059019699e+00 1.404672642611632006e-49 -1.594531510503501170e+00 1.219654228482852398e-49 -1.595531843947982642e+00 1.058911246690700290e-49 -1.596532177392464336e+00 9.192711483922810797e-50 -1.597532510836945807e+00 7.979743518349660846e-50 -1.598532844281427279e+00 6.926207041148191393e-50 -1.599533177725908750e+00 6.011228553419359662e-50 -1.600533511170390222e+00 5.216656564513929939e-50 -1.601533844614871693e+00 4.526708056882948226e-50 -1.602534178059353165e+00 3.927660680797018032e-50 -1.603534511503834636e+00 3.407584788246793060e-50 -1.604534844948316108e+00 2.956110170960440890e-50 -1.605535178392797580e+00 2.564223026577915937e-50 -1.606535511837279273e+00 2.224089251895231743e-50 -1.607535845281760745e+00 1.928900663438354969e-50 -1.608536178726242216e+00 1.672741182817215141e-50 -1.609536512170723688e+00 1.450470405523173798e-50 -1.610536845615205159e+00 1.257622304191822875e-50 -1.611537179059686631e+00 1.090317107108309060e-50 -1.612537512504168102e+00 9.451846453076226176e-51 -1.613537845948649574e+00 8.192976817762441594e-51 -1.614538179393131045e+00 7.101139281346961199e-51 -1.615538512837612517e+00 6.154256213918696626e-51 -1.616538846282094211e+00 5.333156790658354990e-51 -1.617539179726575682e+00 4.621195779197907566e-51 -1.618539513171057154e+00 4.003922121649168691e-51 -1.619539846615538625e+00 3.468790833346722884e-51 -1.620540180060020097e+00 3.004912579626394577e-51 -1.621540513504501568e+00 2.602836022959635082e-51 -1.622540846948983040e+00 2.254358669390846235e-51 -1.623541180393464511e+00 1.952362497608866079e-51 -1.624541513837945983e+00 1.690671136705982922e-51 -1.625541847282427455e+00 1.463925778966022111e-51 -1.626542180726909148e+00 1.267477379914034808e-51 -1.627542514171390620e+00 1.097293016365779890e-51 -1.628542847615872091e+00 9.498745504494279752e-52 -1.629543181060353563e+00 8.221879888571640760e-52 -1.630543514504835034e+00 7.116021365626548107e-52 -1.631543847949316506e+00 6.158353269602424133e-52 -1.632544181393797977e+00 5.329091693619749353e-52 -1.633544514838279449e+00 4.611083931059440002e-52 -1.634544848282760920e+00 3.989459878541847584e-52 -1.635545181727242392e+00 3.451329443235310405e-52 -1.636545515171724086e+00 2.985519907275741344e-52 -1.637545848616205557e+00 2.582347993797887940e-52 -1.638546182060687029e+00 2.233422067559373031e-52 -1.639546515505168500e+00 1.931470501801959432e-52 -1.640546848949649972e+00 1.670192763491737864e-52 -1.641547182394131443e+00 1.444130221587621641e-52 -1.642547515838612915e+00 1.248554076347536050e-52 -1.643547849283094386e+00 1.079368149587909539e-52 -1.644548182727575858e+00 9.330245729779506678e-53 -1.645548516172057329e+00 8.064506696954952080e-53 -1.646548849616539023e+00 6.969855491800885817e-53 -1.647549183061020495e+00 6.023251297040774619e-53 -1.648549516505501966e+00 5.204744728853681650e-53 -1.649549849949983438e+00 4.497064614311514770e-53 -1.650550183394464909e+00 3.885259792351309096e-53 -1.651550516838946381e+00 3.356388639829173676e-53 -1.652550850283427852e+00 2.899249988491817742e-53 -1.653551183727909324e+00 2.504149936096125452e-53 -1.654551517172390795e+00 2.162699782030323586e-53 -1.655551850616872267e+00 1.867640949115649736e-53 -1.656552184061353961e+00 1.612693301353946785e-53 -1.657552517505835432e+00 1.392423743171933750e-53 -1.658552850950316904e+00 1.202132398695928437e-53 -1.659553184394798375e+00 1.037754028025569928e-53 -1.660553517839279847e+00 8.957726485400052374e-54 -1.661553851283761318e+00 7.731475991993856892e-54 -1.662554184728242790e+00 6.672495200167675203e-54 -1.663554518172724261e+00 5.758049220777337272e-54 -1.664554851617205733e+00 4.968481997673928142e-54 -1.665555185061687204e+00 4.286800897774892877e-54 -1.666555518506168898e+00 3.698317140981510130e-54 -1.667555851950650370e+00 3.190334592265637141e-54 -1.668556185395131841e+00 2.751880435767544648e-54 -1.669556518839613313e+00 2.373472115723145832e-54 -1.670556852284094784e+00 2.046915678990663472e-54 -1.671557185728576256e+00 1.765131304137477079e-54 -1.672557519173057727e+00 1.522002365676236350e-54 -1.673557852617539199e+00 1.312244870591703824e-54 -1.674558186062020670e+00 1.131294527738773046e-54 -1.675558519506502142e+00 9.752090776585913402e-55 -1.676558852950983836e+00 8.405838283564053580e-55 -1.677559186395465307e+00 7.244786181242386446e-55 -1.678559519839946779e+00 6.243546652149494703e-55 -1.679559853284428250e+00 5.380199709869164169e-55 -1.680560186728909722e+00 4.635821222860215300e-55 -1.681560520173391193e+00 3.994074940012817734e-55 -1.682560853617872665e+00 3.440859871204974126e-55 -1.683561187062354136e+00 2.964005539924705320e-55 -1.684561520506835608e+00 2.553008632783152174e-55 -1.685561853951317079e+00 2.198805443300522166e-55 -1.686562187395798773e+00 1.893575262767795939e-55 -1.687562520840280245e+00 1.630570524935728164e-55 -1.688562854284761716e+00 1.403970077340991010e-55 -1.689563187729243188e+00 1.208752442012878394e-55 -1.690563521173724659e+00 1.040586352306668445e-55 -1.691563854618206131e+00 8.957362195289156070e-56 -1.692564188062687602e+00 7.709805005012684561e-56 -1.693564521507169074e+00 6.635412118984681057e-56 -1.694564854951650545e+00 5.710230748305207398e-56 -1.695565188396132017e+00 4.913609787036878671e-56 -1.696565521840613711e+00 4.227746311992183451e-56 -1.697565855285095182e+00 3.637294149877722768e-56 -1.698566188729576654e+00 3.129026047844789331e-56 -1.699566522174058125e+00 2.691542133476030261e-56 -1.700566855618539597e+00 2.315018344534200838e-56 -1.701567189063021068e+00 1.990989368441451634e-56 -1.702567522507502540e+00 1.712161374593639654e-56 -1.703567855951984011e+00 1.472250464980637458e-56 -1.704568189396465483e+00 1.265843323794141694e-56 -1.705568522840946954e+00 1.088277026539570273e-56 -1.706568856285428648e+00 9.355353838214045432e-57 -1.707569189729910120e+00 8.041595532654734656e-57 -1.708569523174391591e+00 6.911709626074598122e-57 -1.709569856618873063e+00 5.940048544171472951e-57 -1.710570190063354534e+00 5.104529939536784880e-57 -1.711570523507836006e+00 4.386142811981295042e-57 -1.712570856952317477e+00 3.768521804580881190e-57 -1.713571190396798949e+00 3.237580297796192976e-57 -1.714571523841280420e+00 2.781194209284901644e-57 -1.715571857285762114e+00 2.388929516785981540e-57 -1.716572190730243586e+00 2.051807479575469193e-57 -1.717572524174725057e+00 1.762102361121054638e-57 -1.718572857619206529e+00 1.513167169550685224e-57 -1.719573191063688000e+00 1.299283548797545245e-57 -1.720573524508169472e+00 1.115532485143968434e-57 -1.721573857952650943e+00 9.576829528562412954e-58 -1.722574191397132415e+00 8.220960186429903939e-58 -1.723574524841613886e+00 7.056422663743809281e-58 -1.724574858286095358e+00 6.056306982989204718e-58 -1.725575191730577052e+00 5.197475232991579386e-58 -1.726575525175058523e+00 4.460034620855692772e-58 -1.727575858619539995e+00 3.826883884185056044e-58 -1.728576192064021466e+00 3.283322886099228290e-58 -1.729576525508502938e+00 2.816716622537691763e-58 -1.730576858952984409e+00 2.416206084506480266e-58 -1.731577192397465881e+00 2.072459463884406780e-58 -1.732577525841947352e+00 1.777458093105561214e-58 -1.733577859286428824e+00 1.524312286321439089e-58 -1.734578192730910295e+00 1.307102919603439434e-58 -1.735578526175391989e+00 1.120745165158841155e-58 -1.736578859619873461e+00 9.608712921236446322e-59 -1.737579193064354932e+00 8.237298752703436155e-59 -1.738579526508836404e+00 7.060991224056785472e-59 -1.739579859953317875e+00 6.052123495137354548e-59 -1.740580193397799347e+00 5.186939068868231624e-59 -1.741580526842280818e+00 4.445040956615849690e-59 -1.742580860286762290e+00 3.808918175964527216e-59 -1.743581193731243761e+00 3.263538761129335715e-59 -1.744581527175725233e+00 2.795999974827810314e-59 -1.745581860620206927e+00 2.395227709422223586e-59 -1.746582194064688398e+00 2.051718183543964122e-59 -1.747582527509169870e+00 1.757316003241155205e-59 -1.748582860953651341e+00 1.505023485517102285e-59 -1.749583194398132813e+00 1.288836855526359450e-59 -1.750583527842614284e+00 1.103605542692365619e-59 -1.751583861287095756e+00 9.449113294074985718e-60 -1.752584194731577227e+00 8.089645606588515752e-60 -1.753584528176058699e+00 6.925150141470898894e-60 -1.754584861620540170e+00 5.927753670490775061e-60 -1.755585195065021864e+00 5.073554851287624679e-60 -1.756585528509503336e+00 4.342060089690189484e-60 -1.757585861953984807e+00 3.715699263231243125e-60 -1.758586195398466279e+00 3.179410038245104171e-60 -1.759586528842947750e+00 2.720281097302615687e-60 -1.760586862287429222e+00 2.327245956087496615e-60 -1.761587195731910693e+00 1.990820220151234473e-60 -1.762587529176392165e+00 1.702876138993271996e-60 -1.763587862620873636e+00 1.456449180566591813e-60 -1.764588196065355108e+00 1.245572093387806914e-60 -1.765588529509836802e+00 1.065132562943415388e-60 -1.766588862954318273e+00 9.107511186780509810e-61 -1.767589196398799745e+00 7.786764201265700156e-61 -1.768589529843281216e+00 6.656954565458958681e-61 -1.769589863287762688e+00 5.690565430452970775e-61 -1.770590196732244159e+00 4.864032957241519705e-61 -1.771590530176725631e+00 4.157180256051430577e-61 -1.772590863621207102e+00 3.552732121334935149e-61 -1.773591197065688574e+00 3.035899068009747225e-61 -1.774591530510170045e+00 2.594020804363947971e-61 -1.775591863954651739e+00 2.216260676553613677e-61 -1.776592197399133211e+00 1.893343821239816890e-61 -1.777592530843614682e+00 1.617332794534272669e-61 -1.778592864288096154e+00 1.381435331017284635e-61 -1.779593197732577625e+00 1.179839646748779218e-61 -1.780593531177059097e+00 1.007573352635774671e-61 -1.781593864621540568e+00 8.603826044446498733e-62 -1.782594198066022040e+00 7.346285962394871941e-62 -1.783594531510503511e+00 6.271989163113649276e-62 -1.784594864954984983e+00 5.354316383952985827e-62 -1.785595198399466677e+00 4.570503244312620173e-62 -1.786595531843948148e+00 3.901083754420917696e-62 -1.787595865288429620e+00 3.329413903798471581e-62 -1.788596198732911091e+00 2.841263842915191077e-62 -1.789596532177392563e+00 2.424468813789372823e-62 -1.790596865621874034e+00 2.068630393496420168e-62 -1.791597199066355506e+00 1.764860822000271057e-62 -1.792597532510836977e+00 1.505564220899854196e-62 -1.793597865955318449e+00 1.284249397112662369e-62 -1.794598199399799920e+00 1.095369686203117758e-62 -1.795598532844281614e+00 9.341859420512246429e-63 -1.796598866288763086e+00 7.966493383280315026e-63 -1.797599199733244557e+00 6.793011260810668699e-63 -1.798599533177726029e+00 5.791869020333461034e-63 -1.799599866622207500e+00 4.937832937398738429e-63 -1.800600200066688972e+00 4.209352689145503689e-63 -1.801600533511170443e+00 3.588025342311115068e-63 -1.802600866955651915e+00 3.058137098848051279e-63 -1.803601200400133386e+00 2.606271554674212138e-63 -1.804601533844614858e+00 2.220974847953787933e-63 -1.805601867289096552e+00 1.892469461278728614e-63 -1.806602200733578023e+00 1.612409630565124518e-63 -1.807602534178059495e+00 1.373672330967039034e-63 -1.808602867622540966e+00 1.170178681172949323e-63 -1.809603201067022438e+00 9.967413530726559385e-64 -1.810603534511503909e+00 8.489342119853220026e-64 -1.811603867955985381e+00 7.229809588389312383e-64 -1.812604201400466852e+00 6.156600131102910333e-64 -1.813604534844948324e+00 5.242232753023243308e-64 -1.814604868289429795e+00 4.463267499480534140e-64 -1.815605201733911489e+00 3.799713029066407369e-64 -1.816605535178392961e+00 3.234520771709956332e-64 -1.817605868622874432e+00 2.753153056393345803e-64 -1.818606202067355904e+00 2.343214425349504436e-64 -1.819606535511837375e+00 1.994136918612824266e-64 -1.820606868956318847e+00 1.696911452823224341e-64 -1.821607202400800318e+00 1.443858563982843827e-64 -1.822607535845281790e+00 1.228432763506358549e-64 -1.823607869289763261e+00 1.045055594399284556e-64 -1.824608202734244733e+00 8.889731903109323893e-65 -1.825608536178726427e+00 7.561347521304619818e-65 -1.826608869623207898e+00 6.430888797828547269e-65 -1.827609203067689370e+00 5.468951438222922756e-65 -1.828609536512170841e+00 4.650486633360761581e-65 -1.829609869956652313e+00 3.954157829912565677e-65 -1.830610203401133784e+00 3.361792208499204239e-65 -1.831610536845615256e+00 2.857912967435114246e-65 -1.832610870290096727e+00 2.429340544372854350e-65 -1.833611203734578199e+00 2.064852645773724462e-65 -1.834611537179059670e+00 1.754894438120505681e-65 -1.835611870623541364e+00 1.491331522065573535e-65 -1.836612204068022836e+00 1.267239392804290461e-65 -1.837612537512504307e+00 1.076724013868326256e-65 -1.838612870956985779e+00 9.147689202947709835e-66 -1.839613204401467250e+00 7.771049404457014443e-66 -1.840613537845948722e+00 6.600992004837483570e-66 -1.841613871290430193e+00 5.606605660321697666e-66 -1.842614204734911665e+00 4.761590941674586924e-66 -1.843614538179393136e+00 4.043574261155632812e-66 -1.844614871623874608e+00 3.433523558250165248e-66 -1.845615205068356302e+00 2.915250696397808604e-66 -1.846615538512837773e+00 2.474987741444309338e-66 -1.847615871957319245e+00 2.101026184951258693e-66 -1.848616205401800716e+00 1.783409789625376365e-66 -1.849616538846282188e+00 1.513673110756018505e-66 -1.850616872290763659e+00 1.284618921516327226e-66 -1.851617205735245131e+00 1.090128771040279672e-66 -1.852617539179726602e+00 9.250017577127240583e-67 -1.853617872624208074e+00 7.848173277866759611e-67 -1.854618206068689545e+00 6.658185297662541054e-67 -1.855618539513171239e+00 5.648126837562119019e-67 -1.856618872957652711e+00 4.790868756500803456e-67 -1.857619206402134182e+00 4.063360701105423664e-67 -1.858619539846615654e+00 3.446019635941618109e-67 -1.859619873291097125e+00 2.922209775567682062e-67 -1.860620206735578597e+00 2.477800295883666562e-67 -1.861620540180060068e+00 2.100789226526284274e-67 -1.862620873624541540e+00 1.780983650444682560e-67 -1.863621207069023011e+00 1.509727805553927439e-67 -1.864621540513504483e+00 1.279671934305642880e-67 -1.865621873957986177e+00 1.084575792330919331e-67 -1.866622207402467648e+00 9.191416344672963718e-68 -1.867622540846949120e+00 7.788722688831859497e-68 -1.868622874291430591e+00 6.599504276260127622e-68 -1.869623207735912063e+00 5.591362617408347377e-68 -1.870623541180393534e+00 4.736802456428525949e-68 -1.871623874624875006e+00 4.012491810277266308e-68 -1.872624208069356477e+00 3.398633357962360603e-68 -1.873624541513837949e+00 2.878430472268594171e-68 -1.874624874958319420e+00 2.437633685892830786e-68 -1.875625208402801114e+00 2.064155510666326403e-68 -1.876625541847282586e+00 1.747743337884377015e-68 -1.877625875291764057e+00 1.479701686923687206e-68 -1.878626208736245529e+00 1.252656378544789011e-68 -1.879626542180727000e+00 1.060354322786327503e-68 -1.880626875625208472e+00 8.974935583415380149e-69 -1.881627209069689943e+00 7.595789855837003547e-69 -1.882627542514171415e+00 6.427999201292027336e-69 -1.883627875958652886e+00 5.439261759794056813e-69 -1.884628209403134580e+00 4.602198821921848088e-69 -1.885628542847616052e+00 3.893606577328864648e-69 -1.886628876292097523e+00 3.293821267373168389e-69 -1.887629209736578995e+00 2.786180603306778045e-69 -1.888629543181060466e+00 2.356566894140856168e-69 -1.889629876625541938e+00 1.993019522928020435e-69 -1.890630210070023409e+00 1.685406274893113029e-69 -1.891630543514504881e+00 1.425144605060530013e-69 -1.892630876958986352e+00 1.204965278830225798e-69 -1.893631210403467824e+00 1.018711962127920405e-69 -1.894631543847949517e+00 8.611713087055034547e-70 -1.895631877292430989e+00 7.279289167795233229e-70 -1.896632210736912461e+00 6.152472274485287403e-70 -1.897632544181393932e+00 5.199620319321373210e-70 -1.898632877625875404e+00 4.393947595155633728e-70 -1.899633211070356875e+00 3.712781466747478957e-70 -1.900633544514838347e+00 3.136932516845252566e-70 -1.901633877959319818e+00 2.650160878303334358e-70 -1.902634211403801290e+00 2.238724104484718600e-70 -1.903634544848282761e+00 1.890994154554111876e-70 -1.904634878292764455e+00 1.597132957804100366e-70 -1.905635211737245926e+00 1.348817622722385237e-70 -1.906635545181727398e+00 1.139007715315157028e-70 -1.907635878626208870e+00 9.617481839336524407e-71 -1.908636212070690341e+00 8.120024856700896798e-71 -1.909636545515171813e+00 6.855112987678033743e-71 -1.910636878959653284e+00 5.786729088904900710e-71 -1.911637212404134756e+00 4.884419536038935690e-71 -1.912637545848616227e+00 4.122437152335570988e-71 -1.913637879293097699e+00 3.479015811279418669e-71 -1.914638212737579392e+00 2.935756539474279431e-71 -1.915638546182060864e+00 2.477108028251925110e-71 -1.916638879626542336e+00 2.089927075090097744e-71 -1.917639213071023807e+00 1.763106690338104029e-71 -1.918639546515505279e+00 1.487261481406538607e-71 -1.919639879959986750e+00 1.254461516897619333e-71 -1.920640213404468222e+00 1.058007220674844305e-71 -1.921640546848949693e+00 8.922389875695645785e-72 -1.922640880293431165e+00 7.523761796461845626e-72 -1.923641213737912636e+00 6.343809812816274642e-72 -1.924641547182394330e+00 5.348432853124621871e-72 -1.925641880626875801e+00 4.508833702790921731e-72 -1.926642214071357273e+00 3.800696265678351875e-72 -1.927642547515838745e+00 3.203490107581987272e-72 -1.928642880960320216e+00 2.699882643787812394e-72 -1.929643214404801688e+00 2.275242354364949832e-72 -1.930643547849283159e+00 1.917218968801313763e-72 -1.931643881293764631e+00 1.615388726872444037e-72 -1.932644214738246102e+00 1.360954655322129292e-72 -1.933644548182727574e+00 1.146493350994786597e-72 -1.934644881627209267e+00 9.657410736389834010e-73 -1.935645215071690739e+00 8.134130622701997681e-73 -1.936645548516172211e+00 6.850509287097504634e-73 -1.937645881960653682e+00 5.768937769447554000e-73 -1.938646215405135154e+00 4.857693694981551218e-73 -1.939646548849616625e+00 4.090022308769885587e-73 -1.940646882294098097e+00 3.443360593173581276e-73 -1.941647215738579568e+00 2.898682249558596779e-73 -1.942647549183061040e+00 2.439944766536905868e-73 -1.943647882627542511e+00 2.053622706515620350e-73 -1.944648216072024205e+00 1.728313802257120765e-73 -1.945648549516505676e+00 1.454406534769822666e-73 -1.946648882960987148e+00 1.223799621785479342e-73 -1.947649216405468620e+00 1.029665331971933908e-73 -1.948649549849950091e+00 8.662497958441600229e-74 -1.949649883294431563e+00 7.287045456218241226e-74 -1.950650216738913034e+00 6.129444130723207915e-74 -1.951650550183394506e+00 5.155276721102243540e-74 -1.952650883627875977e+00 4.335549530986942867e-74 -1.953651217072357449e+00 3.645839966042696370e-74 -1.954651550516839142e+00 3.065577711768487936e-74 -1.955651883961320614e+00 2.577438655687557396e-74 -1.956652217405802086e+00 2.166833916680984629e-74 -1.957652550850283557e+00 1.821479096030721347e-74 -1.958652884294765029e+00 1.531031188342904441e-74 -1.959653217739246500e+00 1.286782552382035662e-74 -1.960653551183727972e+00 1.081402998129335809e-74 -1.961653884628209443e+00 9.087224445427130669e-75 -1.962654218072690915e+00 7.635477826701674760e-75 -1.963654551517172386e+00 6.415085748407962862e-75 -1.964654884961654080e+00 5.389270612760883299e-75 -1.965655218406135551e+00 4.527086548208972102e-75 -1.966655551850617023e+00 3.802497030327066570e-75 -1.967655885295098495e+00 3.193598018552596195e-75 -1.968656218739579966e+00 2.681963711194939865e-75 -1.969656552184061438e+00 2.252095614808692683e-75 -1.970656885628542909e+00 1.890958655146160335e-75 -1.971657219073024381e+00 1.587590613333547001e-75 -1.972657552517505852e+00 1.332773326785330848e-75 -1.973657885961987324e+00 1.118755912276539594e-75 -1.974658219406469017e+00 9.390218013817841245e-76 -1.975658552850950489e+00 7.880926707539887930e-76 -1.976658886295431961e+00 6.613634391011395577e-76 -1.977659219739913432e+00 5.549634209972840741e-76 -1.978659553184394904e+00 4.656395016301622699e-76 -1.979659886628876375e+00 3.906578488714595044e-76 -1.980660220073357847e+00 3.277212287307636799e-76 -1.981660553517839318e+00 2.748994534222040147e-76 -1.982660886962320790e+00 2.305708815128486460e-76 -1.983661220406802261e+00 1.933732183845840367e-76 -1.984661553851283955e+00 1.621621421886397662e-76 -1.985661887295765426e+00 1.359765137476525098e-76 -1.986662220740246898e+00 1.140091253332787556e-76 -1.987662554184728370e+00 9.558210870919041738e-77 -1.988662887629209841e+00 8.012626216146337290e-77 -1.989663221073691313e+00 6.716367355671332536e-77 -1.990663554518172784e+00 5.629311524207960065e-77 -1.991663887962654256e+00 4.717776975386984338e-77 -1.992664221407135727e+00 3.953491529736121989e-77 -1.993664554851617199e+00 3.312725887567311551e-77 -1.994664888296098892e+00 2.775565452982821549e-77 -1.995665221740580364e+00 2.325298587951903982e-77 -1.996665555185061836e+00 1.947902726623215676e-77 -1.997665888629543307e+00 1.631612734344760924e-77 -1.998666222074024779e+00 1.366558381354503928e-77 -1.999666555518506250e+00 1.144459891992841720e-77 -2.000666888962987944e+00 9.583722890575861543e-78 -2.001667222407469193e+00 8.024707321926686870e-78 -2.002667555851950887e+00 6.718702932765383213e-78 -2.003667889296432136e+00 5.624746579564623706e-78 -2.004668222740913830e+00 4.708491221540496132e-78 -2.005668556185395079e+00 3.941139919864279468e-78 -2.006668889629876773e+00 3.298551173365151707e-78 -2.007669223074358023e+00 2.760488120028275330e-78 -2.008669556518839716e+00 2.309988527038997509e-78 -2.009669889963321410e+00 1.932836184717933158e-78 -2.010670223407802659e+00 1.617117422889275905e-78 -2.011670556852284353e+00 1.352849075908989201e-78 -2.012670890296765602e+00 1.131666413644390008e-78 -2.013671223741247296e+00 9.465613965464889754e-79 -2.014671557185728545e+00 7.916631594335522465e-79 -2.015671890630210239e+00 6.620539276482687110e-79 -2.016672224074691488e+00 5.536146603695690491e-79 -2.017672557519173182e+00 4.628956322313532587e-79 -2.018672890963654432e+00 3.870079339581342036e-79 -2.019673224408136125e+00 3.235325189196800201e-79 -2.020673557852617819e+00 2.704439650706208195e-79 -2.021673891297099068e+00 2.260465772465197702e-79 -2.022674224741580762e+00 1.889208371740221318e-79 -2.023674558186062011e+00 1.578785295250074017e-79 -2.024674891630543705e+00 1.319251417420915380e-79 -2.025675225075024954e+00 1.102283614541412151e-79 -2.026675558519506648e+00 9.209168502663970840e-80 -2.027675891963987898e+00 7.693230999052144905e-80 -2.028676225408469591e+00 6.426261766145903384e-80 -2.029676558852951285e+00 5.367466431805140075e-80 -2.030676892297432534e+00 4.482719330609610827e-80 -2.031677225741914228e+00 3.743475928110844210e-80 -2.032677559186395477e+00 3.125862192903867130e-80 -2.033677892630877171e+00 2.609912196151813414e-80 -2.034678226075358420e+00 2.178929868556660371e-80 -2.035678559519840114e+00 1.818954743946088716e-80 -2.036678892964321363e+00 1.518314787749506824e-80 -2.037679226408803057e+00 1.267252149178814617e-80 -2.038679559853284307e+00 1.057609973202717643e-80 -2.039679893297766000e+00 8.825703338915493376e-81 -2.040680226742247694e+00 7.364349644685746963e-81 -2.041680560186728943e+00 6.144418117482233409e-81 -2.042680893631210637e+00 5.126115758356952908e-81 -2.043681227075691886e+00 4.276193454253390791e-81 -2.044681560520173580e+00 3.566872344796342032e-81 -2.045681893964654829e+00 2.974945924314870808e-81 -2.046682227409136523e+00 2.481029182135559269e-81 -2.047682560853617773e+00 2.068930759166742463e-81 -2.048682894298099466e+00 1.725128013975110209e-81 -2.049683227742581160e+00 1.438328170188268060e-81 -2.050683561187062409e+00 1.199101462324696823e-81 -2.051683894631544103e+00 9.995744956652521390e-82 -2.052684228076025352e+00 8.331739600277616853e-82 -2.053684561520507046e+00 6.944124481007979644e-82 -2.054684894964988295e+00 5.787094772739523476e-82 -2.055685228409469989e+00 4.822419423333978280e-82 -2.056685561853951238e+00 4.018191707562179442e-82 -2.057685895298432932e+00 3.347785425682550736e-82 -2.058686228742914626e+00 2.788982979392864131e-82 -2.059686562187395875e+00 2.323247088812599678e-82 -2.060686895631877569e+00 1.935112542002314155e-82 -2.061687229076358818e+00 1.611678239670813304e-82 -2.062687562520840512e+00 1.342183035598815145e-82 -2.063687895965321761e+00 1.117651581283900713e-82 -2.064688229409803455e+00 9.305986478745616005e-83 -2.065688562854284704e+00 7.747822920718725187e-83 -2.066688896298766398e+00 6.449978159296632602e-83 -2.067689229743247648e+00 5.369057941501530215e-83 -2.068689563187729341e+00 4.468885489894760402e-83 -2.069689896632211035e+00 3.719303778123567690e-83 -2.070690230076692284e+00 3.095176113874477334e-83 -2.071690563521173978e+00 2.575552270799264739e-83 -2.072690896965655227e+00 2.142972809826899907e-83 -2.073691230410136921e+00 1.782888741421551659e-83 -2.074691563854618170e+00 1.483177449430572904e-83 -2.075691897299099864e+00 1.233738945925200889e-83 -2.076692230743581113e+00 1.026159156775200109e-83 -2.077692564188062807e+00 8.534291347184711737e-84 -2.078692897632544501e+00 7.097109316550170120e-84 -2.079693231077025750e+00 5.901423942922737687e-84 -2.080693564521507444e+00 4.906744269017067075e-84 -2.081693897965988693e+00 4.079353333930718907e-84 -2.082694231410470387e+00 3.391177429487168219e-84 -2.083694564854951636e+00 2.818843681506845296e-84 -2.084694898299433330e+00 2.342894661504156184e-84 -2.085695231743914579e+00 1.947133922761951464e-84 -2.086695565188396273e+00 1.618080684922486065e-84 -2.087695898632877523e+00 1.344515504850765260e-84 -2.088696232077359216e+00 1.117101786830091583e-84 -2.089696565521840910e+00 9.280705010205964074e-85 -2.090696898966322159e+00 7.709575780426611134e-85 -2.091697232410803853e+00 6.403851985013742317e-85 -2.092697565855285102e+00 5.318796567918404934e-85 -2.093697899299766796e+00 4.417196966785214036e-85 -2.094698232744248045e+00 3.668102320639034518e-85 -2.095698566188729739e+00 3.045772135358525820e-85 -2.096698899633210988e+00 2.528801076852232923e-85 -2.097699233077692682e+00 2.099390451456921254e-85 -2.098699566522174376e+00 1.742741843124786174e-85 -2.099699899966655625e+00 1.446552469953959102e-85 -2.100700233411137319e+00 1.200595234244051902e-85 -2.101700566855618568e+00 9.963692836537491048e-86 -2.102700900300100262e+00 8.268092706514765826e-86 -2.103701233744581511e+00 6.860434720416865173e-86 -2.104701567189063205e+00 5.691925756120529754e-86 -2.105701900633544454e+00 4.722023116969163090e-86 -2.106702234078026148e+00 3.917042493723701909e-86 -2.107702567522507398e+00 3.249000282167803062e-86 -2.108702900966989091e+00 2.694650888502980187e-86 -2.109703234411470785e+00 2.234686253149594390e-86 -2.110703567855952034e+00 1.853070318510087921e-86 -2.111703901300433728e+00 1.536485741863806534e-86 -2.112704234744914977e+00 1.273873964257646216e-86 -2.113704568189396671e+00 1.056052917978434770e-86 -2.114704901633877920e+00 8.753992955361590572e-87 -2.115705235078359614e+00 7.255845008583668418e-87 -2.116705568522840863e+00 6.013552326390684334e-87 -2.117705901967322557e+00 4.983511721319470178e-87 -2.118706235411804251e+00 4.129535145202281834e-87 -2.119706568856285500e+00 3.421591371084296376e-87 -2.120706902300767194e+00 2.834760746140712049e-87 -2.121707235745248443e+00 2.348367014671180533e-87 -2.122707569189730137e+00 1.945256279714640242e-87 -2.123707902634211386e+00 1.611198220301455304e-87 -2.124708236078693080e+00 1.334388880180748757e-87 -2.125708569523174329e+00 1.105037835737839683e-87 -2.126708902967656023e+00 9.150254544800073791e-88 -2.127709236412137273e+00 7.576183698156653665e-88 -2.128709569856618966e+00 6.272333051297389885e-88 -2.129709903301100660e+00 5.192410488710986581e-88 -2.130710236745581909e+00 4.298037694780074207e-88 -2.131710570190063603e+00 3.557400119038565771e-88 -2.132710903634544852e+00 2.944126757070251944e-88 -2.133711237079026546e+00 2.436360709563241184e-88 -2.134711570523507795e+00 2.015988098791322591e-88 -2.135711903967989489e+00 1.667998419443628102e-88 -2.136712237412470738e+00 1.379953968180315299e-88 -2.137712570856952432e+00 1.141549790547564919e-88 -2.138712904301434126e+00 9.442487355902500368e-89 -2.139713237745915375e+00 7.809788261911031819e-89 -2.140713571190397069e+00 6.458823271555494303e-89 -2.141713904634878318e+00 5.341076983784808188e-89 -2.142714238079360012e+00 4.416371194592595948e-89 -2.143714571523841261e+00 3.651435167339890312e-89 -2.144714904968322955e+00 3.018720569457241186e-89 -2.145715238412804204e+00 2.495419294988543626e-89 -2.146715571857285898e+00 2.062649511863506174e-89 -2.147715905301767148e+00 1.704781181702961839e-89 -2.148716238746248841e+00 1.408877203673768609e-89 -2.149716572190730535e+00 1.164230403116342080e-89 -2.150716905635211784e+00 9.619799620193411126e-90 -2.151717239079693478e+00 7.947936896680879008e-90 -2.152717572524174727e+00 6.566048556739825361e-90 -2.153717905968656421e+00 5.423942352750226185e-90 -2.154718239413137670e+00 4.480096156988980970e-90 -2.155718572857619364e+00 3.700163384383437112e-90 -2.156718906302100613e+00 3.055735519770035839e-90 -2.157719239746582307e+00 2.523317613726969317e-90 -2.158719573191064001e+00 2.083480171720591005e-90 -2.159719906635545250e+00 1.720157129508110878e-90 -2.160720240080026944e+00 1.420064804024888976e-90 -2.161720573524508193e+00 1.172221016204118295e-90 -2.162720906968989887e+00 9.675471521565298083e-91 -2.163721240413471136e+00 7.985388877936288382e-91 -2.164721573857952830e+00 6.589937537528802075e-91 -2.165721907302434079e+00 5.437857490566782930e-91 -2.166722240746915773e+00 4.486788951490349747e-91 -2.167722574191397023e+00 3.701730164329299113e-91 -2.168722907635878716e+00 3.053761885833225630e-91 -2.169723241080360410e+00 2.518992527384894894e-91 -2.170723574524841659e+00 2.077685861394151602e-91 -2.171723907969323353e+00 1.713539760791698788e-91 -2.172724241413804602e+00 1.413089874828113575e-91 -2.173724574858286296e+00 1.165216644265979182e-91 -2.174724908302767545e+00 9.607377846417724518e-92 -2.175725241747249239e+00 7.920714505076058752e-92 -2.176725575191730488e+00 6.529578466416487405e-92 -2.177725908636212182e+00 5.382291653977461276e-92 -2.178726242080693876e+00 4.436194783031924891e-92 -2.179726575525175125e+00 3.656076573478885495e-92 -2.180726908969656819e+00 3.012875990502079833e-92 -2.181727242414138068e+00 2.482610153149127858e-92 -2.182727575858619762e+00 2.045488749229208584e-92 -2.183727909303101011e+00 1.685182576961795163e-92 -2.184728242747582705e+00 1.388219443732281992e-92 -2.185728576192063954e+00 1.143485292313676723e-92 -2.186728909636545648e+00 9.418122623001280447e-93 -2.187729243081026897e+00 7.756385678524329619e-93 -2.188729576525508591e+00 6.387276968116969338e-93 -2.189729909969990285e+00 5.259366057405818749e-93 -2.190730243414471534e+00 4.330243790292784000e-93 -2.191730576858953228e+00 3.564943028718164680e-93 -2.192730910303434477e+00 2.934635304542768852e-93 -2.193731243747916171e+00 2.415555276027234095e-93 -2.194731577192397420e+00 1.988113252333108817e-93 -2.195731910636879114e+00 1.636162966775771975e-93 -2.196732244081360363e+00 1.346397495193187381e-93 -2.197732577525842057e+00 1.107850938024141234e-93 -2.198732910970323751e+00 9.114873858720617713e-94 -2.199733244414805000e+00 7.498619108823186060e-94 -2.200733577859286694e+00 6.168409880226782906e-94 -2.201733911303767943e+00 5.073719487030791155e-94 -2.202734244748249637e+00 4.172928846168585885e-94 -2.203734578192730886e+00 3.431759187986394134e-94 -2.204734911637212580e+00 2.821979986663181723e-94 -2.205735245081693829e+00 2.320343877265753546e-94 -2.206735578526175523e+00 1.907708762319582224e-94 -2.207735911970656772e+00 1.568314274390210972e-94 -2.208736245415138466e+00 1.289185508725859741e-94 -2.209736578859620160e+00 1.059641683503074543e-94 -2.210736912304101409e+00 8.708912996428209161e-95 -2.211737245748583103e+00 7.156986021661339481e-95 -2.212737579193064352e+00 5.881088100507884409e-95 -2.213737912637546046e+00 4.832217801652765174e-95 -2.214738246082027295e+00 3.970055845575875850e-95 -2.215738579526508989e+00 3.261429764002328867e-95 -2.216738912970990238e+00 2.679049537842498806e-95 -2.217739246415471932e+00 2.200466481397369888e-95 -2.218739579859953626e+00 1.807216031920313345e-95 -2.219739913304434875e+00 1.484112022652537575e-95 -2.220740246748916569e+00 1.218665722047930045e-95 -2.221740580193397818e+00 1.000607624765603899e-95 -2.222740913637879512e+00 8.214938567003260965e-96 -2.223741247082360761e+00 6.743822517005761074e-96 -2.224741580526842455e+00 5.535657911649371124e-96 -2.225741913971323704e+00 4.543532680262828928e-96 -2.226742247415805398e+00 3.728888250240654938e-96 -2.227742580860287092e+00 3.060034907191948596e-96 -2.228742914304768341e+00 2.510930507292460755e-96 -2.229743247749250035e+00 2.060175917797889115e-96 -2.230743581193731284e+00 1.690188799903446771e-96 -2.231743914638212978e+00 1.386524133574453451e-96 -2.232744248082694227e+00 1.137315472321740961e-96 -2.233744581527175921e+00 9.328155178983827214e-97 -2.234744914971657170e+00 7.650183943343078703e-97 -2.235745248416138864e+00 6.273491207533608432e-97 -2.236745581860620113e+00 5.144083510877249702e-97 -2.237745915305101807e+00 4.217625633295005449e-97 -2.238746248749583501e+00 3.457716214973726917e-97 -2.239746582194064750e+00 2.834470660460383616e-97 -2.240746915638546444e+00 2.323356674103886101e-97 -2.241747249083027693e+00 1.904237475497017992e-97 -2.242747582527509387e+00 1.560585726448435401e-97 -2.243747915971990636e+00 1.278837768988221056e-97 -2.244748249416472330e+00 1.047863177234892556e-97 -2.245748582860953579e+00 8.585290707805027677e-98 -2.246748916305435273e+00 7.033422932420337779e-98 -2.247749249749916967e+00 5.761555665402381646e-98 -2.248749583194398216e+00 4.719262042844601649e-98 -2.249749916638879910e+00 3.865180010665945260e-98 -2.250750250083361159e+00 3.165385864134551091e-98 -2.251750583527842853e+00 2.592059067483461359e-98 -2.252750916972324102e+00 2.122386287548839640e-98 -2.253751250416805796e+00 1.737661864359381822e-98 -2.254751583861287045e+00 1.422549576589289549e-98 -2.255751917305768739e+00 1.164476834646805447e-98 -2.256752250750249988e+00 9.531375907758971064e-99 -2.257752584194731682e+00 7.800844927670247931e-99 -2.258752917639213376e+00 6.383942893410612955e-99 -2.259753251083694625e+00 5.223933554964029446e-99 -2.260753584528176319e+00 4.274325557544620295e-99 -2.261753917972657568e+00 3.497025932649606245e-99 -2.262754251417139262e+00 2.860825779316091342e-99 -2.263754584861620511e+00 2.340158485908269363e-99 -2.264754918306102205e+00 1.914081533519465838e-99 -2.265755251750583454e+00 1.565441700459504232e-99 -2.266755585195065148e+00 1.280190694658933437e-99 -2.267755918639546842e+00 1.046824157843399412e-99 -2.268756252084028091e+00 8.559218425347866677e-100 -2.269756585528509785e+00 6.997707498225885256e-100 -2.270756918972991034e+00 5.720562880442408752e-100 -2.271757252417472728e+00 4.676091978955147386e-100 -2.272757585861953977e+00 3.821981931040057938e-100 -2.273757919306435671e+00 3.123600738921296028e-100 -2.274758252750916920e+00 2.552605539044267556e-100 -2.275758586195398614e+00 2.085802582420000598e-100 -2.276758919639879863e+00 1.704213485241142801e-100 -2.277759253084361557e+00 1.392310497014354930e-100 -2.278759586528843251e+00 1.137390248865562058e-100 -2.279759919973324500e+00 9.290609517726797519e-101 -2.280760253417806194e+00 7.588225303034312351e-101 -2.281760586862287443e+00 6.197228800479933107e-101 -2.282760920306769137e+00 5.060764725220564211e-101 -2.283761253751250386e+00 4.132340198124998988e-101 -2.284761587195732080e+00 3.373939510500502359e-101 -2.285761920640213329e+00 2.754481246833608612e-101 -2.286762254084695023e+00 2.248555707201465781e-101 -2.287762587529176717e+00 1.835391799464604566e-101 -2.288762920973657966e+00 1.498011771929255853e-101 -2.289763254418139660e+00 1.222539695483159936e-101 -2.290763587862620909e+00 9.976357799877623120e-102 -2.291763921307102603e+00 8.140336687785053776e-102 -2.292764254751583852e+00 6.641619990417021873e-102 -2.293764588196065546e+00 5.418349098196770601e-102 -2.294764921640546795e+00 4.419989589306195704e-102 -2.295765255085028489e+00 3.605261861901741734e-102 -2.296765588529509738e+00 2.940449240198204542e-102 -2.297765921973991432e+00 2.398014823914329689e-102 -2.298766255418473126e+00 1.955470842257786407e-102 -2.299766588862954375e+00 1.594454499092735396e-102 -2.300766922307436069e+00 1.299972665611414596e-102 -2.301767255751917318e+00 1.059784626868467928e-102 -2.302767589196399012e+00 8.638976943478431865e-103 -2.303767922640880261e+00 7.041550839087622994e-103 -2.304768256085361955e+00 5.738992117381122931e-103 -2.305768589529843204e+00 4.676966316537771382e-103 -2.306768922974324898e+00 3.811133491416487465e-103 -2.307769256418806592e+00 3.105313023428841701e-103 -2.308769589863287841e+00 2.529984808848038040e-103 -2.309769923307769535e+00 2.061065279820322719e-103 -2.310770256752250784e+00 1.678907952842120974e-103 -2.311770590196732478e+00 1.367487390739908804e-103 -2.312770923641213727e+00 1.113732979194096969e-103 -2.313771257085695421e+00 9.069850626794812486e-104 -2.314771590530176670e+00 7.385510071319395270e-104 -2.315771923974658364e+00 6.013428619615560458e-104 -2.316772257419139613e+00 4.895816494152151875e-104 -2.317772590863621307e+00 3.985560514661533835e-104 -2.318772924308103001e+00 3.244255052550013817e-104 -2.319773257752584250e+00 2.640595491003983453e-104 -2.320773591197065944e+00 2.149067570723692283e-104 -2.321773924641547193e+00 1.748878222637990676e-104 -2.322774258086028887e+00 1.423083472353727118e-104 -2.323774591530510136e+00 1.157877155623941104e-104 -2.324774924974991830e+00 9.420108441467856915e-105 -2.325775258419473079e+00 7.663208199883347379e-105 -2.326775591863954773e+00 6.233423782898674971e-105 -2.327775925308436467e+00 5.069953643467118204e-105 -2.328776258752917716e+00 4.123278118787393245e-105 -2.329776592197399410e+00 3.353069663482449952e-105 -2.330776925641880659e+00 2.726489501698028793e-105 -2.331777259086362353e+00 2.216799370385676941e-105 -2.332777592530843602e+00 1.802230169002135477e-105 -2.333777925975325296e+00 1.465060056341771932e-105 -2.334778259419806545e+00 1.190863287157539853e-105 -2.335778592864288239e+00 9.678982220495815357e-106 -2.336778926308769488e+00 7.866087699196372820e-106 -2.337779259753251182e+00 6.392182747944277147e-106 -2.338779593197732876e+00 5.193987354322890933e-106 -2.339779926642214125e+00 4.220014079619236031e-106 -2.340780260086695819e+00 3.428374204385135160e-106 -2.341780593531177068e+00 2.784991310060186915e-106 -2.342780926975658762e+00 2.262146759816059527e-106 -2.343781260420140011e+00 1.837295521597486203e-106 -2.344781593864621705e+00 1.492102173369624080e-106 -2.345781927309102954e+00 1.211656221231646590e-106 -2.346782260753584648e+00 9.838334338720078347e-107 -2.347782594198066342e+00 7.987760689377635430e-107 -2.348782927642547591e+00 6.484698968642617149e-107 -2.349783261087029285e+00 5.264000265599824621e-107 -2.350783594531510534e+00 4.272708771135470151e-107 -2.351783927975992228e+00 3.467783607186005865e-107 -2.352784261420473477e+00 2.814245587318417141e-107 -2.353784594864955171e+00 2.283669784866917580e-107 -2.354784928309436420e+00 1.852959492616847167e-107 -2.355785261753918114e+00 1.503349136576381156e-107 -2.356785595198399363e+00 1.219593461332835249e-107 -2.357785928642881057e+00 9.893082478450849463e-108 -2.358786262087362751e+00 8.024342921673018235e-108 -2.359786595531844000e+00 6.508016391825448529e-108 -2.360786928976325694e+00 5.277753519902261400e-108 -2.361787262420806943e+00 4.279675868392732128e-108 -2.362787595865288637e+00 3.470035834761427558e-108 -2.363787929309769886e+00 2.813314920967725951e-108 -2.364788262754251580e+00 2.280678389810658737e-108 -2.365788596198732829e+00 1.848719633775273094e-108 -2.366788929643214523e+00 1.498440034504166389e-108 -2.367789263087696217e+00 1.214420221796804043e-108 -2.368789596532177466e+00 9.841468822107121574e-109 -2.369789929976659160e+00 7.974659707780448234e-109 -2.370790263421140409e+00 6.461386314831793701e-109 -2.371790596865622103e+00 5.234805661370064827e-109 -2.372790930310103352e+00 4.240691969562026914e-109 -2.373791263754585046e+00 3.435058999612117658e-109 -2.374791597199066295e+00 2.782229699862363814e-109 -2.375791930643547989e+00 2.253269159381735422e-109 -2.376792264088029238e+00 1.824712602015135347e-109 -2.377792597532510932e+00 1.477532964825019959e-109 -2.378792930976992626e+00 1.196303010238117620e-109 -2.379793264421473875e+00 9.685153781423423402e-110 -2.380793597865955569e+00 7.840308562725993609e-110 -2.381793931310436818e+00 6.346307309591593569e-110 -2.382794264754918512e+00 5.136536168091173195e-110 -2.383794598199399761e+00 4.157008492456737279e-110 -2.384794931643881455e+00 3.363975174415218922e-110 -2.385795265088362704e+00 2.721986466903021235e-110 -2.386795598532844398e+00 2.202320140157653557e-110 -2.387795931977326092e+00 1.781706844870065380e-110 -2.388796265421807341e+00 1.441296578963338634e-110 -2.389796598866289035e+00 1.165820727519069019e-110 -2.390796932310770284e+00 9.429127291254030745e-111 -2.391797265755251978e+00 7.625573901130708212e-111 -2.392797599199733227e+00 6.166445243339086419e-111 -2.393797932644214921e+00 4.986071869170812449e-111 -2.394798266088696170e+00 4.031284961790015769e-111 -2.395798599533177864e+00 3.259040612679783389e-111 -2.396798932977659558e+00 2.634494863848067823e-111 -2.397799266422140807e+00 2.129444128602226496e-111 -2.398799599866622501e+00 1.721061751644739822e-111 -2.399799933311103750e+00 1.390874568436610560e-111 -2.400800266755585444e+00 1.123933948878009400e-111 -2.401800600200066693e+00 9.081444278894098730e-112 -2.402800933644548387e+00 7.337200141081853854e-112 -2.403801267089029636e+00 5.927439350506611267e-112 -2.404801600533511330e+00 4.788121721793301870e-112 -2.405801933977992579e+00 3.867448648370506298e-112 -2.406802267422474273e+00 3.123526822274482214e-112 -2.407802600866955967e+00 2.522477108576725462e-112 -2.408802934311437216e+00 2.036903892430134919e-112 -2.409803267755918910e+00 1.644656299533747015e-112 -2.410803601200400159e+00 1.327825724168840445e-112 -2.411803934644881853e+00 1.071934660145330605e-112 -2.412804268089363102e+00 8.652803871537158553e-113 -2.413804601533844796e+00 6.984039977803815600e-113 -2.414804934978326045e+00 5.636608666819365436e-113 -2.415805268422807739e+00 4.548732126949961017e-113 -2.416805601867289433e+00 3.670490893742309198e-113 -2.417805935311770682e+00 2.961551236131302340e-113 -2.418806268756252376e+00 2.389327386014525590e-113 -2.419806602200733625e+00 1.927495541687449301e-113 -2.420806935645215319e+00 1.554792419002441036e-113 -2.421807269089696568e+00 1.254043950703315783e-113 -2.422807602534178262e+00 1.011380117648880629e-113 -2.423807935978659511e+00 8.156003002791047076e-114 -2.424808269423141205e+00 6.576603410861223408e-114 -2.425808602867622454e+00 5.302580140143492882e-114 -2.426808936312104148e+00 4.274980515793039439e-114 -2.427809269756585842e+00 3.446214851467141270e-114 -2.428809603201067091e+00 2.777869702299302465e-114 -2.429809936645548785e+00 2.238941280506354690e-114 -2.430810270090030034e+00 1.804408431756944666e-114 -2.431810603534511728e+00 1.454080007941822308e-114 -2.432810936978992977e+00 1.171663957559722252e-114 -2.433811270423474671e+00 9.440155520821458489e-115 -2.434811603867955920e+00 7.605303315736228424e-115 -2.435811937312437614e+00 6.126539546549095900e-115 -2.436812270756919308e+00 4.934864753935485367e-115 -2.437812604201400557e+00 3.974628846012168831e-115 -2.438812937645882251e+00 3.200952409339085481e-115 -2.439813271090363500e+00 2.577645363575771600e-115 -2.440813604534845194e+00 2.075527189774890916e-115 -2.441813937979326443e+00 1.671071374858668884e-115 -2.442814271423808137e+00 1.345311593033241907e-115 -2.443814604868289386e+00 1.082959166148876275e-115 -2.444814938312771080e+00 8.716910569969585574e-116 -2.445815271757252329e+00 7.015754951704664961e-116 -2.446815605201734023e+00 5.646086724610827625e-116 -2.447815938646215717e+00 4.543410633478501256e-116 -2.448816272090696966e+00 3.655760599372156312e-116 -2.449816605535178660e+00 2.941269489231173366e-116 -2.450816938979659909e+00 2.366209536144545839e-116 -2.451817272424141603e+00 1.903412403812096869e-116 -2.452817605868622852e+00 1.530995458429996908e-116 -2.453817939313104546e+00 1.231334994260502734e-116 -2.454818272757585795e+00 9.902386073116538568e-117 -2.455818606202067489e+00 7.962781513327311918e-117 -2.456818939646549182e+00 6.402521670826061201e-117 -2.457819273091030432e+00 5.147526930608167192e-117 -2.458819606535512126e+00 4.138162210831523303e-117 -2.459819939979993375e+00 3.326424772805001485e-117 -2.460820273424475069e+00 2.673678692312795436e-117 -2.461820606868956318e+00 2.148829890977186589e-117 -2.462820940313438012e+00 1.726856187507165086e-117 -2.463821273757919261e+00 1.387623411592943975e-117 -2.464821607202400955e+00 1.114931996329940114e-117 -2.465821940646882204e+00 8.957492487379202843e-118 -2.466822274091363898e+00 7.195911927734595674e-118 -2.467822607535845592e+00 5.780248892119114609e-118 -2.468822940980326841e+00 4.642677878614851055e-118 -2.469823274424808535e+00 3.728652227890291972e-118 -2.470823607869289784e+00 2.994308327585717662e-118 -2.471823941313771478e+00 2.404376480751599119e-118 -2.472824274758252727e+00 1.930499695598994899e-118 -2.473824608202734421e+00 1.549880867979525284e-118 -2.474824941647215670e+00 1.244194309153518668e-118 -2.475825275091697364e+00 9.987100435946657535e-119 -2.476825608536179057e+00 8.015893487755505238e-119 -2.477825941980660307e+00 6.433181018278566241e-119 -2.478826275425142001e+00 5.162510162095500051e-119 -2.479826608869623250e+00 4.142451009565903836e-119 -2.480826942314104944e+00 3.323649023493343273e-119 -2.481827275758586193e+00 2.666454916200600717e-119 -2.482827609203067887e+00 2.139019028208720806e-119 -2.483827942647549136e+00 1.715759312303484156e-119 -2.484828276092030830e+00 1.376129786744657750e-119 -2.485828609536512079e+00 1.103630616633383701e-119 -2.486828942980993773e+00 8.850124877852304831e-120 -2.487829276425475467e+00 7.096371965845532673e-120 -2.488829609869956716e+00 5.689638300315398734e-120 -2.489829943314438410e+00 4.561359052198075145e-120 -2.490830276758919659e+00 3.656496607567090643e-120 -2.491830610203401353e+00 2.930875728447265819e-120 -2.492830943647882602e+00 2.349042899884046674e-120 -2.493831277092364296e+00 1.882546907982236459e-120 -2.494831610536845545e+00 1.508557884570745600e-120 -2.495831943981327239e+00 1.208758291356521712e-120 -2.496832277425808932e+00 9.684523729651948283e-121 -2.497832610870290182e+00 7.758511062110448548e-121 -2.498832944314771876e+00 6.214981129266813808e-121 -2.499833277759253125e+00 4.978087885378764120e-121 -2.500833611203734819e+00 3.987003523614804040e-121 -2.501833944648216068e+00 3.192949090872113808e-121 -2.502834278092697762e+00 2.556811337927288703e-121 -2.503834611537179011e+00 2.047230238471954609e-121 -2.504834944981660705e+00 1.639064358519745065e-121 -2.505835278426141954e+00 1.312159534694605323e-121 -2.506835611870623648e+00 1.050361003346296220e-121 -2.507835945315105342e+00 8.407209178512341316e-122 -2.508836278759586591e+00 6.728626659675766156e-122 -2.509836612204068285e+00 5.384710087329216792e-122 -2.510836945648549534e+00 4.308831736350545251e-122 -2.511837279093031228e+00 3.447609422342358955e-122 -2.512837612537512477e+00 2.758277128533096673e-122 -2.513837945981994171e+00 2.206576826287875391e-122 -2.514838279426475420e+00 1.765068330318160580e-122 -2.515838612870957114e+00 1.411774417525548513e-122 -2.516838946315438807e+00 1.129094802923208703e-122 -2.517839279759920057e+00 9.029356999763791761e-123 -2.518839613204401751e+00 7.220122328497131807e-123 -2.519839946648883000e+00 5.772894258773239885e-123 -2.520840280093364694e+00 4.615342818724079365e-123 -2.521840613537845943e+00 3.689569055114199454e-123 -2.522840946982327637e+00 2.949229958233679855e-123 -2.523841280426808886e+00 2.357235354502220318e-123 -2.524841613871290580e+00 1.883903145280272489e-123 -2.525841947315771829e+00 1.505481808267653057e-123 -2.526842280760253523e+00 1.202967151701511042e-123 -2.527842614204735217e+00 9.611548042987983964e-124 -2.528842947649216466e+00 7.678815439574479934e-124 -2.529843281093698160e+00 6.134178842509808081e-124 -2.530843614538179409e+00 4.899818058563903561e-124 -2.531843947982661103e+00 3.913495062801597390e-124 -2.532844281427142352e+00 3.125438391964962799e-124 -2.533844614871624046e+00 2.495849599079219021e-124 -2.534844948316105295e+00 1.992907748062584494e-124 -2.535845281760586989e+00 1.591172617480417190e-124 -2.536845615205068682e+00 1.270307064762617240e-124 -2.537845948649549932e+00 1.014054850387486813e-124 -2.538846282094031626e+00 8.094229169688066414e-125 -2.539846615538512875e+00 6.460272871042732741e-125 -2.540846948982994569e+00 5.155698883291813068e-125 -2.541847282427475818e+00 4.114201360072574578e-125 -2.542847615871957512e+00 3.282803280275426052e-125 -2.543847949316438761e+00 2.619180872769619831e-125 -2.544848282760920455e+00 2.089524361667991692e-125 -2.545848616205401704e+00 1.666827680097553272e-125 -2.546848949649883398e+00 1.329521257540903788e-125 -2.547849283094365092e+00 1.060379160386007528e-125 -2.548849616538846341e+00 8.456456107938282923e-126 -2.549849949983328035e+00 6.743369090760251089e-126 -2.550850283427809284e+00 5.376835869831725173e-126 -2.551850616872290978e+00 4.286846621575138831e-126 -2.552850950316772227e+00 3.417514986517827098e-126 -2.553851283761253921e+00 2.724232860080380558e-126 -2.554851617205735170e+00 2.171397550490039660e-126 -2.555851950650216864e+00 1.730596324581578420e-126 -2.556852284094698557e+00 1.379156426824268015e-126 -2.557852617539179807e+00 1.098987101375781008e-126 -2.558852950983661501e+00 8.756548955118976253e-127 -2.559853284428142750e+00 6.976453148921679538e-127 -2.560853617872624444e+00 5.557733286017169966e-127 -2.561853951317105693e+00 4.427127582504349185e-127 -2.562854284761587387e+00 3.526206094136173328e-127 -2.563854618206068636e+00 2.808372184293210588e-127 -2.564854951650550330e+00 2.236469354182990125e-127 -2.565855285095032023e+00 1.780871392609016814e-127 -2.566855618539513273e+00 1.417958372028869631e-127 -2.567855951983994967e+00 1.128900647796035464e-127 -2.568856285428476216e+00 8.986886999117882676e-128 -2.569856618872957910e+00 7.153591841093375619e-128 -2.570856952317439159e+00 5.693775654551710747e-128 -2.571857285761920853e+00 4.531456995054893192e-128 -2.572857619206402102e+00 3.606091084194067338e-128 -2.573857952650883796e+00 2.869437959597078213e-128 -2.574858286095365045e+00 2.283065147036795244e-128 -2.575858619539846739e+00 1.816356477478012848e-128 -2.576858952984328432e+00 1.444924608978909800e-128 -2.577859286428809682e+00 1.149345517500269951e-128 -2.578859619873291376e+00 9.141497462130089183e-129 -2.579859953317772625e+00 7.270183919455976158e-129 -2.580860286762254319e+00 5.781423326638451756e-129 -2.581860620206735568e+00 4.597115985988600863e-129 -2.582860953651217262e+00 3.655084908417641634e-129 -2.583861287095698511e+00 2.905833975510551943e-129 -2.584861620540180205e+00 2.309965228949930619e-129 -2.585861953984661898e+00 1.836121458998837009e-129 -2.586862287429143148e+00 1.459347398381997666e-129 -2.587862620873624842e+00 1.159784468466928612e-129 -2.588862954318106091e+00 9.216312819972401924e-130 -2.589863287762587785e+00 7.323158673045194095e-130 -2.590863621207069034e+00 5.818365504643055476e-130 -2.591863954651550728e+00 4.622371787595728658e-130 -2.592864288096031977e+00 3.671893415597679290e-130 -2.593864621540513671e+00 2.916598026385666194e-130 -2.594864954984994920e+00 2.316457877398708107e-130 -2.595865288429476614e+00 1.839643015133548184e-130 -2.596865621873958307e+00 1.460844611683666058e-130 -2.597865955318439557e+00 1.159940757905496058e-130 -2.598866288762921251e+00 9.209348621950326486e-131 -2.599866622207402500e+00 7.311110318756319175e-131 -2.600866955651884194e+00 5.803621480821227401e-131 -2.601867289096365443e+00 4.606553726771273085e-131 -2.602867622540847137e+00 3.656070157612851773e-131 -2.603867955985328386e+00 2.901444182974401455e-131 -2.604868289429810080e+00 2.302370639524469192e-131 -2.605868622874291773e+00 1.826827647464339817e-131 -2.606868956318773023e+00 1.449376555352066729e-131 -2.607869289763254717e+00 1.149810333903845468e-131 -2.608869623207735966e+00 9.120790960291125387e-132 -2.609869956652217660e+00 7.234359993067737983e-132 -2.610870290096698909e+00 5.737583900901798919e-132 -2.611870623541180603e+00 4.550082820537880061e-132 -2.612870956985661852e+00 3.608036094502720663e-132 -2.613871290430143546e+00 2.860775413799343197e-132 -2.614871623874624795e+00 2.268077943879005222e-132 -2.615871957319106489e+00 1.798015806346407895e-132 -2.616872290763588182e+00 1.425247719605809120e-132 -2.617872624208069432e+00 1.129662009905596427e-132 -2.618872957652551126e+00 8.952988207391107946e-133 -2.619873291097032375e+00 7.094941528610864225e-133 -2.620873624541514069e+00 5.622001388395757843e-133 -2.621873957985995318e+00 4.454453155017773245e-133 -2.622874291430477012e+00 3.529060984552934354e-133 -2.623874624874958261e+00 2.795665778652054439e-133 -2.624874958319439955e+00 2.214484562854896364e-133 -2.625875291763921648e+00 1.753966861683402948e-133 -2.626875625208402898e+00 1.389093344050097035e-133 -2.627875958652884592e+00 1.100025611532183061e-133 -2.628876292097365841e+00 8.710347533002052900e-134 -2.629876625541847535e+00 6.896511894894643514e-134 -2.630876958986328784e+00 5.459901602951074139e-134 -2.631877292430810478e+00 4.322166197440950469e-134 -2.632877625875291727e+00 3.421207607782626419e-134 -2.633877959319773421e+00 2.707813292533126381e-134 -2.634878292764254670e+00 2.142985945367929800e-134 -2.635878626208736364e+00 1.695825845441400405e-134 -2.636878959653218057e+00 1.341851627592485290e-134 -2.637879293097699307e+00 1.061668825311927130e-134 -2.638879626542181001e+00 8.399142566514860374e-135 -2.639879959986662250e+00 6.644191808090716357e-135 -2.640880293431143944e+00 5.255459458456192953e-135 -2.641880626875625193e+00 4.156622072435015979e-135 -2.642880960320106887e+00 3.287242242792696628e-135 -2.643881293764588136e+00 2.599466347504979679e-135 -2.644881627209069830e+00 2.055407847317409978e-135 -2.645881960653551523e+00 1.625073999002822381e-135 -2.646882294098032773e+00 1.284723279613985181e-135 -2.647882627542514467e+00 1.015564161349976933e-135 -2.648882960986995716e+00 8.027243920779808000e-136 -2.649883294431477410e+00 6.344346206522057673e-136 -2.650883627875958659e+00 5.013818506981239195e-136 -2.651883961320440353e+00 3.961974398293216387e-136 -2.652884294764921602e+00 3.130516819717863052e-136 -2.653884628209403296e+00 2.473328142321984534e-136 -2.654884961653884545e+00 1.953928904870500381e-136 -2.655885295098366239e+00 1.543466106151649998e-136 -2.656885628542847932e+00 1.219120846126125910e-136 -2.657885961987329182e+00 9.628480140577643275e-137 -2.658886295431810876e+00 7.603788730160986154e-137 -2.659886628876292125e+00 6.004317724162786605e-137 -2.660886962320773819e+00 4.740876151479762855e-137 -2.661887295765255068e+00 3.742957330554631879e-137 -2.662887629209736762e+00 2.954829753737852046e-137 -2.663887962654218011e+00 2.332444787181782093e-137 -2.664888296098699705e+00 1.840990731046281732e-137 -2.665888629543181398e+00 1.452958309871342498e-137 -2.666888962987662648e+00 1.146610793735519001e-137 -2.667889296432144342e+00 9.047742244174055874e-138 -2.668889629876625591e+00 7.138808551084133879e-138 -2.669889963321107285e+00 5.632128723283949707e-138 -2.670890296765588534e+00 4.443045191388813375e-138 -2.671890630210070228e+00 3.504694851739610477e-138 -2.672890963654551477e+00 2.764273511926902828e-138 -2.673891297099033171e+00 2.180083537803767384e-138 -2.674891630543514420e+00 1.719200704370813217e-138 -2.675891963987996114e+00 1.355630548975159516e-138 -2.676892297432477807e+00 1.068851658427517404e-138 -2.677892630876959057e+00 8.426647859089773971e-139 -2.678892964321440751e+00 6.642836718145436477e-139 -2.679893297765922000e+00 5.236168713024084264e-139 -2.680893631210403694e+00 4.127005114488226568e-139 -2.681893964654884943e+00 3.252502991834471939e-139 -2.682894298099366637e+00 2.563077418165845405e-139 -2.683894631543847886e+00 2.019607923591800092e-139 -2.684894964988329580e+00 1.591232826500398629e-139 -2.685895298432811273e+00 1.253607885312051582e-139 -2.686895631877292523e+00 9.875316489296740456e-140 -2.687895965321774217e+00 7.778603827083967216e-140 -2.688896298766255466e+00 6.126516465692548880e-140 -2.689896632210737160e+00 4.824884032232051436e-140 -2.690896965655218409e+00 3.799456494061892754e-140 -2.691897299099700103e+00 2.991695545593370031e-140 -2.692897632544181352e+00 2.355454047306528830e-140 -2.693897965988663046e+00 1.854356365777980854e-140 -2.694898299433144295e+00 1.459731854675225262e-140 -2.695898632877625989e+00 1.148984827376993165e-140 -2.696898966322107682e+00 9.043089405535338192e-141 -2.697899299766588932e+00 7.116733086026412251e-141 -2.698899633211070626e+00 5.600229870517193303e-141 -2.699899966655551875e+00 4.406485568203636489e-141 -2.700900300100033569e+00 3.466890884710395145e-141 -2.701900633544514818e+00 2.727403017988792210e-141 -2.702900966988996512e+00 2.145456843735124815e-141 -2.703901300433477761e+00 1.687530271364668912e-141 -2.704901633877959455e+00 1.327225398003061822e-141 -2.705901967322441148e+00 1.043756320464938552e-141 -2.706902300766922398e+00 8.207575297977951025e-142 -2.707902634211404092e+00 6.453450094394031524e-142 -2.708902967655885341e+00 5.073765125635249529e-142 -2.709903301100367035e+00 3.988688169296929951e-142 -2.710903634544848284e+00 3.135386860416309490e-142 -2.711903967989329978e+00 2.464413084428062547e-142 -2.712904301433811227e+00 1.936855409635434596e-142 -2.713904634878292921e+00 1.522096610907598389e-142 -2.714904968322774170e+00 1.196047864800888324e-142 -2.715905301767255864e+00 9.397584173474128910e-143 -2.716905635211737557e+00 7.383209839678278998e-143 -2.717905968656218807e+00 5.800100548261958062e-143 -2.718906302100700501e+00 4.556036096443565814e-143 -2.719906635545181750e+00 3.578492516670603568e-143 -2.720906968989663444e+00 2.810440490445669657e-143 -2.721907302434144693e+00 2.207038949183672829e-143 -2.722907635878626387e+00 1.733033367661619278e-143 -2.723907969323107636e+00 1.360708742721863292e-143 -2.724908302767589330e+00 1.068279142556434685e-143 -2.725908636212071023e+00 8.386208323726374183e-144 -2.726908969656552273e+00 6.582757666845360053e-144 -2.727909303101033966e+00 5.166678189957814350e-144 -2.728909636545515216e+00 4.054863883927328572e-144 -2.729909969989996910e+00 3.182016826193761660e-144 -2.730910303434478159e+00 2.496835825725451660e-144 -2.731910636878959853e+00 1.959019803127730704e-144 -2.732910970323441102e+00 1.536911955302620787e-144 -2.733911303767922796e+00 1.205647848186250721e-144 -2.734911637212404489e+00 9.456997748033160609e-145 -2.735911970656885739e+00 7.417326896944200940e-145 -2.736912304101367432e+00 5.817051082954762234e-145 -2.737912637545848682e+00 4.561625832843312611e-145 -2.738912970990330376e+00 3.576825599521565510e-145 -2.739913304434811625e+00 2.804382154297721437e-145 -2.740913637879293319e+00 2.198558107701526496e-145 -2.741913971323774568e+00 1.723455308893254092e-145 -2.742914304768256262e+00 1.350900703617176538e-145 -2.743914638212737511e+00 1.058785911207403794e-145 -2.744914971657219205e+00 8.297632643598205377e-146 -2.745915305101700898e+00 6.502218774556688534e-146 -2.746915638546182148e+00 5.094836787827673385e-146 -2.747915971990663841e+00 3.991722100329681266e-146 -2.748916305435145091e+00 3.127171110123420597e-146 -2.749916638879626785e+00 2.449651570687566308e-146 -2.750916972324108034e+00 1.918749641584921228e-146 -2.751917305768589728e+00 1.502773857573572275e-146 -2.752917639213070977e+00 1.176874832455582466e-146 -2.753917972657552671e+00 9.215698204975158737e-147 -2.754918306102034364e+00 7.215850670541067055e-147 -2.755918639546515614e+00 5.649475824582005798e-147 -2.756918972990997307e+00 4.422726632072960741e-147 -2.757919306435478557e+00 3.462050217560778371e-147 -2.758919639879960251e+00 2.709804466255050455e-147 -2.759919973324441500e+00 2.120820291651349508e-147 -2.760920306768923194e+00 1.659705795195199636e-147 -2.761920640213404443e+00 1.298732401021546337e-147 -2.762920973657886137e+00 1.016177478329133371e-147 -2.763921307102367386e+00 7.950249898180727791e-148 -2.764921640546849080e+00 6.219469169291773685e-148 -2.765921973991330773e+00 4.865048604830732798e-148 -2.766922307435812023e+00 3.805242796895722398e-148 -2.767922640880293716e+00 2.976040838314227183e-148 -2.768922974324774966e+00 2.327323341968256286e-148 -2.769923307769256660e+00 1.819851240729091491e-148 -2.770923641213737909e+00 1.422906547819345243e-148 -2.771923974658219603e+00 1.112444086285691652e-148 -2.772924308102700852e+00 8.696436507212298028e-149 -2.773924641547182546e+00 6.797759483714429499e-149 -2.774924974991664239e+00 5.313143962122291448e-149 -2.775925308436145489e+00 4.152395365636195992e-149 -2.776925641880627182e+00 3.244943482358312999e-149 -2.777925975325108432e+00 2.535577571817955145e-149 -2.778926308769590126e+00 1.981107268801545747e-149 -2.779926642214071375e+00 1.547748552542028819e-149 -2.780926975658553069e+00 1.209077521106679830e-149 -2.781927309103034318e+00 9.444287791978763590e-150 -2.782927642547516012e+00 7.376419441560255712e-150 -2.783927975991997261e+00 5.760806929883817244e-150 -2.784928309436478955e+00 4.498651620341195007e-150 -2.785928642880960648e+00 3.512713499603059686e-150 -2.786928976325441898e+00 2.742612320829644696e-150 -2.787929309769923591e+00 2.141151715469449563e-150 -2.788929643214404841e+00 1.671443825358458084e-150 -2.789929976658886535e+00 1.304660322755415647e-150 -2.790930310103367784e+00 1.018273519884857271e-150 -2.791930643547849478e+00 7.946808928656758032e-151 -2.792930976992330727e+00 6.201295373503531982e-151 -2.793931310436812421e+00 4.838752292104324635e-151 -2.794931643881294114e+00 3.775249756012159142e-151 -2.795931977325775364e+00 2.945230654726458279e-151 -2.796932310770257057e+00 2.297493343637075527e-151 -2.797932644214738307e+00 1.792051710979573541e-151 -2.798932977659220001e+00 1.397681249210981931e-151 -2.799933311103701250e+00 1.090001426376402192e-151 -2.800933644548182944e+00 8.499772848463762744e-152 -2.801933977992664193e+00 6.627487219686607984e-152 -2.802934311437145887e+00 5.167158746763603234e-152 -2.803934644881627136e+00 4.028246474133483462e-152 -2.804934978326108830e+00 3.140086365069049029e-152 -2.805935311770590523e+00 2.447532552322575111e-152 -2.806935645215071773e+00 1.907553320641714706e-152 -2.807935978659553466e+00 1.486572937020653616e-152 -2.808936312104034716e+00 1.158396093602579469e-152 -2.809936645548516410e+00 9.025874126433446218e-153 -2.810936978992997659e+00 7.032063555049824470e-153 -2.811937312437479353e+00 5.478196776904589237e-153 -2.812937645881960602e+00 4.267306087385944674e-153 -2.813937979326442296e+00 3.323772488885151511e-153 -2.814938312770923989e+00 2.588630767704684698e-153 -2.815938646215405239e+00 2.015905896366419133e-153 -2.816938979659886932e+00 1.569754468316555976e-153 -2.817939313104368182e+00 1.222234468695074862e-153 -2.818939646548849876e+00 9.515654238105653844e-154 -2.819939979993331125e+00 7.407712274896789762e-154 -2.820940313437812819e+00 5.766215701816604217e-154 -2.821940646882294068e+00 4.488063468653140138e-154 -2.822940980326775762e+00 3.492918256526823785e-154 -2.823941313771257011e+00 2.718186060527729038e-154 -2.824941647215738705e+00 2.115101759588001714e-154 -2.825941980660220398e+00 1.645677300696083466e-154 -2.826942314104701648e+00 1.280322618411559408e-154 -2.827942647549183341e+00 9.959911597803155090e-155 -2.828942980993664591e+00 7.747344647984167304e-155 -2.829943314438146285e+00 6.025756749915918519e-155 -2.830943647882627534e+00 4.686316781771001769e-155 -2.831943981327109228e+00 3.644290724410701077e-155 -2.832944314771590477e+00 2.833712043288027151e-155 -2.833944648216072171e+00 2.203229526738583334e-155 -2.834944981660553864e+00 1.712872738895056720e-155 -2.835945315105035114e+00 1.331532523253903454e-155 -2.836945648549516807e+00 1.034998659001308094e-155 -2.837945981993998057e+00 8.044316003249620517e-156 -2.838946315438479751e+00 6.251723835058829521e-156 -2.839946648882961000e+00 4.858159564072451680e-156 -2.840946982327442694e+00 3.774896875499112479e-156 -2.841947315771923943e+00 2.932916751284958014e-156 -2.842947649216405637e+00 2.278535017739755883e-156 -2.843947982660886886e+00 1.769998939436186644e-156 -2.844948316105368580e+00 1.374838331707957016e-156 -2.845948649549850273e+00 1.067804103526586581e-156 -2.846948982994331523e+00 8.292641031514204227e-157 -2.847949316438813216e+00 6.439549328568653941e-157 -2.848949649883294466e+00 5.000108286877143982e-157 -2.849949983327776160e+00 3.882081716817954208e-157 -2.850950316772257409e+00 3.013778035821694021e-157 -2.851950650216739103e+00 2.339479163358209088e-157 -2.852950983661220352e+00 1.815885359140775918e-157 -2.853951317105702046e+00 1.409350455730679826e-157 -2.854951650550183739e+00 1.093731955458248146e-157 -2.855951983994664989e+00 8.487194001728083707e-158 -2.856952317439146682e+00 6.585347326012417381e-158 -2.857952650883627932e+00 5.109219597924551308e-158 -2.858952984328109626e+00 3.963617898694319429e-158 -2.859953317772590875e+00 3.074612016524910395e-158 -2.860953651217072569e+00 2.384790249117230519e-158 -2.861953984661553818e+00 1.849572596403900702e-158 -2.862954318106035512e+00 1.434345928476017543e-158 -2.863954651550516761e+00 1.112238068610193928e-158 -2.864954984994998455e+00 8.623884555596176450e-159 -2.865955318439480148e+00 6.686047238369584767e-159 -2.866955651883961398e+00 5.183191743257221887e-159 -2.867955985328443091e+00 4.017782636644997667e-159 -2.868956318772924341e+00 3.114131360541025176e-159 -2.869956652217406035e+00 2.413508021715106656e-159 -2.870956985661887284e+00 1.870345733206576740e-159 -2.871957319106368978e+00 1.449293578664183348e-159 -2.872957652550850227e+00 1.122928671938549965e-159 -2.873957985995331921e+00 8.699800665960624456e-160 -2.874958319439813614e+00 6.739501297277900156e-160 -2.875958652884294864e+00 5.220445290234225648e-160 -2.876958986328776557e+00 4.043418227488235099e-160 -2.877959319773257807e+00 3.131490572690363686e-160 -2.878959653217739501e+00 2.425017522228467868e-160 -2.879959986662220750e+00 1.877759560027570484e-160 -2.880960320106702444e+00 1.453872797633045460e-160 -2.881960653551183693e+00 1.125574299996763205e-160 -2.882960986995665387e+00 8.713311782050393993e-161 -2.883961320440146636e+00 6.744560718374876287e-161 -2.884961653884628330e+00 5.220179198345184542e-161 -2.885961987329110023e+00 4.039973191089139971e-161 -2.886962320773591273e+00 3.126316071396860687e-161 -2.887962654218072966e+00 2.419070981326483846e-161 -2.888962987662554216e+00 1.871654440672196380e-161 -2.889963321107035910e+00 1.447984973035042450e-161 -2.890963654551517159e+00 1.120117982141492687e-161 -2.891963987995998853e+00 8.664127059143119272e-162 -2.892964321440480102e+00 6.701116750598401850e-162 -2.893964654884961796e+00 5.182399457734027288e-162 -2.894964988329443489e+00 4.007521995750357950e-162 -2.895965321773924739e+00 3.098719556177358975e-162 -2.896965655218406432e+00 2.395796687711700057e-162 -2.897965988662887682e+00 1.852162027437102237e-162 -2.898966322107369376e+00 1.431757019690027675e-162 -2.899966655551850625e+00 1.106677282306347344e-162 -2.900966988996332319e+00 8.553305768309425002e-163 -2.901967322440813568e+00 6.610104562846894504e-163 -2.902967655885295262e+00 5.107918865445254135e-163 -2.903967989329776955e+00 3.946762381842850041e-163 -2.904968322774258205e+00 3.049293998643044035e-163 -2.905968656218739898e+00 2.355694386450505008e-163 -2.906968989663221148e+00 1.819700540843701454e-163 -2.907969323107702841e+00 1.405536831022385934e-163 -2.908969656552184091e+00 1.085540087341896086e-163 -2.909969989996665785e+00 8.383219365874894910e-164 -2.910970323441147034e+00 6.473469773968207633e-164 -2.911970656885628728e+00 4.998327951038382095e-164 -2.912970990330109977e+00 3.858990418027594255e-164 -2.913971323774591671e+00 2.979092457976010303e-164 -2.914971657219073364e+00 2.299617437717884188e-164 -2.915971990663554614e+00 1.774959850305507293e-164 -2.916972324108036307e+00 1.369880878762021069e-164 -2.917972657552517557e+00 1.057154354649017492e-164 -2.918972990996999251e+00 8.157467119553951968e-165 -2.919973324441480500e+00 6.294099299131297255e-165 -2.920973657885962194e+00 4.855938504097389223e-165 -2.921973991330443443e+00 3.746054535474661541e-165 -2.922974324774925137e+00 2.889590777912686909e-165 -2.923974658219406830e+00 2.228742625549125170e-165 -2.924974991663888080e+00 1.718877099057681226e-165 -2.925975325108369773e+00 1.325534578215083436e-165 -2.926975658552851023e+00 1.022112329032631487e-165 -2.927975991997332716e+00 7.880749483851359344e-166 -2.928976325441813966e+00 6.075719945421309360e-166 -2.929976658886295660e+00 4.683702503970844501e-166 -2.930976992330776909e+00 3.610290816848138335e-166 -2.931977325775258603e+00 2.782636010699014389e-166 -2.932977659219739852e+00 2.144529108342913911e-166 -2.933977992664221546e+00 1.652604076125498030e-166 -2.934978326108703239e+00 1.273406386631623143e-166 -2.935978659553184489e+00 9.811300053789825904e-167 -2.936978992997666182e+00 7.558705445729209450e-167 -2.937979326442147432e+00 5.822769730667329971e-167 -2.938979659886629126e+00 4.485110421772116376e-167 -2.939979993331110375e+00 3.454442703479879209e-167 -2.940980326775592069e+00 2.660383084811598690e-167 -2.941980660220073318e+00 2.048668510494618439e-167 -2.942980993664555012e+00 1.577467921444169263e-167 -2.943981327109036705e+00 1.214536891258856186e-167 -2.944981660553517955e+00 9.350228314202278243e-168 -2.945981993997999648e+00 7.197721698027484036e-168 -2.946982327442480898e+00 5.540248127890003415e-168 -2.947982660886962591e+00 4.264073785028619881e-168 -2.948982994331443841e+00 3.281568972544857385e-168 -2.949983327775925535e+00 2.525222747079456977e-168 -2.950983661220406784e+00 1.943028537296963581e-168 -2.951983994664888478e+00 1.494927034709376256e-168 -2.952984328109369727e+00 1.150064354574661360e-168 -2.953984661553851421e+00 8.846788008010412200e-169 -2.954984994998333114e+00 6.804722647891721612e-169 -2.955985328442814364e+00 5.233552282766776734e-169 -2.956985661887296057e+00 4.024797514318450645e-169 -2.957985995331777307e+00 3.094944287578121193e-169 -2.958986328776259001e+00 2.379704137806125412e-169 -2.959986662220740250e+00 1.829592732280697750e-169 -2.960986995665221944e+00 1.406524228905527245e-169 -2.961987329109703193e+00 1.081188305844538469e-169 -2.962987662554184887e+00 8.310301728591374763e-170 -2.963987995998666580e+00 6.386950872765536613e-170 -2.964988329443147830e+00 4.908306671911317610e-170 -2.965988662887629523e+00 3.771647829200338790e-170 -2.966988996332110773e+00 2.897956819579155952e-170 -2.967989329776592466e+00 2.226455485115669116e-170 -2.968989663221073716e+00 1.710399077551897939e-170 -2.969989996665555410e+00 1.313839218965455201e-170 -2.970990330110036659e+00 1.009132796048678851e-170 -2.971990663554518353e+00 7.750250690898670186e-171 -2.972990996998999602e+00 5.951747679345537657e-171 -2.973991330443481296e+00 4.570193655054642841e-171 -2.974991663887962989e+00 3.509021472510415186e-171 -2.975991997332444239e+00 2.694007370229676299e-171 -2.976992330776925932e+00 2.068106332043217357e-171 -2.977992664221407182e+00 1.587480064635196275e-171 -2.978992997665888876e+00 1.218442466618294950e-171 -2.979993331110370125e+00 9.351108696389370184e-172 -2.980993664554851819e+00 7.176001394792727529e-172 -2.981993997999333068e+00 5.506342923489434619e-172 -2.982994331443814762e+00 4.224791944942848789e-172 -2.983994664888296455e+00 3.241221635996652808e-172 -2.984994998332777705e+00 2.486414119567768668e-172 -2.985995331777259398e+00 1.907214450891875975e-172 -2.986995665221740648e+00 1.462806647870458511e-172 -2.987995998666222341e+00 1.121852224294369232e-172 -2.988996332110703591e+00 8.602916599975503884e-173 -2.989996665555185285e+00 6.596553718497416036e-173 -2.990996998999666534e+00 5.057662270003561189e-173 -2.991997332444148228e+00 3.877429223132790916e-173 -2.992997665888629477e+00 2.972345325847491488e-173 -2.993997999333111171e+00 2.278326600069947237e-173 -2.994998332777592864e+00 1.746200180289980749e-173 -2.995998666222074114e+00 1.338238155752033344e-173 -2.996998999666555807e+00 1.025496351143826518e-173 -2.997999333111037057e+00 7.857712996905513712e-174 -2.998999666555518750e+00 6.020319451435505326e-174 -3.000000000000000000e+00 4.612158699305736625e-174 diff --git a/config/glass_mock/test_data/redshift_distribution_non_tomo.txt b/config/glass_mock/test_data/redshift_distribution_non_tomo.txt new file mode 100644 index 00000000..15c4a47b --- /dev/null +++ b/config/glass_mock/test_data/redshift_distribution_non_tomo.txt @@ -0,0 +1,501 @@ +0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000021e-02 1.539937506863479477e-03 +2.000000000000000042e-02 4.354621114841233237e-03 +2.999999999999999889e-02 7.996950397128197580e-03 +4.000000000000000083e-02 1.230564841035703891e-02 +5.000000000000000278e-02 1.718606196257605606e-02 +5.999999999999999778e-02 2.257303471016786772e-02 +7.000000000000000666e-02 2.841755418221013352e-02 +8.000000000000000167e-02 3.468055475441410296e-02 +8.999999999999999667e-02 4.132958972425367777e-02 +1.000000000000000056e-01 4.833686282899146008e-02 +1.100000000000000006e-01 5.567798045703171994e-02 +1.199999999999999956e-01 6.333111840482133492e-02 +1.300000000000000044e-01 7.127644240940279874e-02 +1.400000000000000133e-01 7.949569188500757444e-02 +1.499999999999999944e-01 8.797187290406095761e-02 +1.600000000000000033e-01 9.668902677243641774e-02 +1.700000000000000122e-01 1.056320523994966204e-01 +1.799999999999999933e-01 1.147865678773978826e-01 +1.900000000000000022e-01 1.241388012363979498e-01 +2.000000000000000111e-01 1.336755033059782716e-01 +2.099999999999999922e-01 1.433838775930178222e-01 +2.200000000000000011e-01 1.532515234450602115e-01 +2.300000000000000100e-01 1.632663897155546551e-01 +2.399999999999999911e-01 1.734167368240674412e-01 +2.500000000000000000e-01 1.836911055944393523e-01 +2.600000000000000089e-01 1.940782916144088155e-01 +2.700000000000000178e-01 2.045673241292221278e-01 +2.800000000000000266e-01 2.151474486850073475e-01 +2.899999999999999800e-01 2.258081128930605475e-01 +2.999999999999999889e-01 2.365389548062355740e-01 +3.099999999999999978e-01 2.473297934922943364e-01 +3.200000000000000067e-01 2.581706214628362583e-01 +3.300000000000000155e-01 2.690515986750057764e-01 +3.400000000000000244e-01 2.799630478700771419e-01 +3.500000000000000333e-01 2.908954510508430791e-01 +3.599999999999999867e-01 3.018394469304660488e-01 +3.699999999999999956e-01 3.127858292105780147e-01 +3.800000000000000044e-01 3.237255455670945414e-01 +3.900000000000000133e-01 3.346496972393329106e-01 +4.000000000000000222e-01 3.455495391322799814e-01 +4.100000000000000311e-01 3.564164803537959170e-01 +4.199999999999999845e-01 3.672420851185922985e-01 +4.299999999999999933e-01 3.780180739593260730e-01 +4.400000000000000022e-01 3.887363251923902085e-01 +4.500000000000000111e-01 3.993888765921613793e-01 +4.600000000000000200e-01 4.099679272327816371e-01 +4.700000000000000289e-01 4.204658394611290873e-01 +4.799999999999999822e-01 4.308751409686109590e-01 +4.899999999999999911e-01 4.411885269328758574e-01 +5.000000000000000000e-01 4.513988622035659537e-01 +5.100000000000000089e-01 4.614991835089001793e-01 +5.200000000000000178e-01 4.714827016622296085e-01 +5.300000000000000266e-01 4.813428037497889944e-01 +5.400000000000000355e-01 4.910730552827363704e-01 +5.500000000000000444e-01 5.006672022982299186e-01 +5.600000000000000533e-01 5.101191733957912700e-01 +5.700000000000000622e-01 5.194230816965546005e-01 +5.799999999999999600e-01 5.285732267142277951e-01 +5.899999999999999689e-01 5.375640961277033947e-01 +5.999999999999999778e-01 5.463903674462829985e-01 +6.099999999999999867e-01 5.550469095594072755e-01 +6.199999999999999956e-01 5.635287841636549278e-01 +6.300000000000000044e-01 5.718312470605623199e-01 +6.400000000000000133e-01 5.799497493195616693e-01 +6.500000000000000222e-01 5.878799383010221424e-01 +6.600000000000000311e-01 5.956176585350180241e-01 +6.700000000000000400e-01 6.031589524520577505e-01 +6.800000000000000488e-01 6.105000609625610419e-01 +6.900000000000000577e-01 6.176374238824169360e-01 +7.000000000000000666e-01 6.245676802024405783e-01 +7.099999999999999645e-01 6.312876682000376771e-01 +7.199999999999999734e-01 6.377944253918177431e-01 +7.299999999999999822e-01 6.440851883263314370e-01 +7.399999999999999911e-01 6.501573922165090336e-01 +7.500000000000000000e-01 6.560086704117557010e-01 +7.600000000000000089e-01 6.616368537100251190e-01 +7.700000000000000178e-01 6.670399695105416749e-01 +7.800000000000000266e-01 6.722162408081657770e-01 +7.900000000000000355e-01 6.771640850307087955e-01 +8.000000000000000444e-01 6.818821127208092303e-01 +8.100000000000000533e-01 6.863691260642560410e-01 +8.200000000000000622e-01 6.906241172669157491e-01 +8.300000000000000711e-01 6.946462667826814874e-01 +8.399999999999999689e-01 6.984349413950913243e-01 +8.499999999999999778e-01 7.019896921555075497e-01 +8.599999999999999867e-01 7.053102521809501146e-01 +8.699999999999999956e-01 7.083965343148931337e-01 +8.800000000000000044e-01 7.112486286545204317e-01 +8.900000000000000133e-01 7.138667999481170812e-01 +9.000000000000000222e-01 7.162514848664459644e-01 +9.100000000000000311e-01 7.184032891521118236e-01 +9.200000000000000400e-01 7.203229846510679213e-01 +9.300000000000000488e-01 7.220115062305524356e-01 +9.400000000000000577e-01 7.234699485878687275e-01 +9.500000000000000666e-01 7.246995629545379680e-01 +9.599999999999999645e-01 7.257017537004600838e-01 +9.699999999999999734e-01 7.264780748428069090e-01 +9.799999999999999822e-01 7.270302264644648016e-01 +9.899999999999999911e-01 7.273600510469091507e-01 +1.000000000000000000e+00 7.274695297224667012e-01 +1.010000000000000009e+00 7.273607784509685814e-01 +1.020000000000000018e+00 7.270360441258496564e-01 +1.030000000000000027e+00 7.264977006147820271e-01 +1.040000000000000036e+00 7.257482447399601355e-01 +1.050000000000000044e+00 7.247902922031738138e-01 +1.060000000000000053e+00 7.236265734608122724e-01 +1.070000000000000062e+00 7.222599295539492426e-01 +1.080000000000000071e+00 7.206933078986481611e-01 +1.090000000000000080e+00 7.189297580416124100e-01 +1.100000000000000089e+00 7.169724273862850827e-01 +1.110000000000000098e+00 7.148245568944672224e-01 +1.120000000000000107e+00 7.124894767684958330e-01 +1.130000000000000115e+00 7.099706021189617910e-01 +1.140000000000000124e+00 7.072714286229138114e-01 +1.150000000000000133e+00 7.043955281774211263e-01 +1.159999999999999920e+00 7.013465445533126896e-01 +1.169999999999999929e+00 6.981281890538297841e-01 +1.179999999999999938e+00 6.947442361828605195e-01 +1.189999999999999947e+00 6.911985193273350037e-01 +1.199999999999999956e+00 6.874949264582724817e-01 +1.209999999999999964e+00 6.836373958548797036e-01 +1.219999999999999973e+00 6.796299118559959718e-01 +1.229999999999999982e+00 6.754765006430851759e-01 +1.239999999999999991e+00 6.711812260588544410e-01 +1.250000000000000000e+00 6.667481854654786488e-01 +1.260000000000000009e+00 6.621815056462829752e-01 +1.270000000000000018e+00 6.574853387546280015e-01 +1.280000000000000027e+00 6.526638583136016303e-01 +1.290000000000000036e+00 6.477212552700154502e-01 +1.300000000000000044e+00 6.426617341060522071e-01 +1.310000000000000053e+00 6.374895090118015695e-01 +1.320000000000000062e+00 6.322088001217657238e-01 +1.330000000000000071e+00 6.268238298182954304e-01 +1.340000000000000080e+00 6.213388191047718445e-01 +1.350000000000000089e+00 6.157579840512120706e-01 +1.360000000000000098e+00 6.100855323148330900e-01 +1.370000000000000107e+00 6.043256597379692563e-01 +1.380000000000000115e+00 5.984825470255971114e-01 +1.390000000000000124e+00 5.925603565045712839e-01 +1.400000000000000133e+00 5.865632289665424492e-01 +1.409999999999999920e+00 5.804952805963723428e-01 +1.419999999999999929e+00 5.743605999877305912e-01 +1.429999999999999938e+00 5.681632452474034700e-01 +1.439999999999999947e+00 5.619072411897095831e-01 +1.449999999999999956e+00 5.555965766222765723e-01 +1.459999999999999964e+00 5.492352017242854156e-01 +1.469999999999999973e+00 5.428270255181580906e-01 +1.479999999999999982e+00 5.363759134355238212e-01 +1.489999999999999991e+00 5.298856849781603540e-01 +1.500000000000000000e+00 5.233601114744772520e-01 +1.510000000000000009e+00 5.168029139319725296e-01 +1.520000000000000018e+00 5.102177609859670504e-01 +1.530000000000000027e+00 5.036082669447908833e-01 +1.540000000000000036e+00 4.969779899314723526e-01 +1.550000000000000044e+00 4.903304301218576189e-01 +1.560000000000000053e+00 4.836690280789701091e-01 +1.570000000000000062e+00 4.769971631832984893e-01 +1.580000000000000071e+00 4.703181521585940161e-01 +1.590000000000000080e+00 4.636352476926445809e-01 +1.600000000000000089e+00 4.569516371523797416e-01 +1.610000000000000098e+00 4.502704413925717186e-01 +1.620000000000000107e+00 4.435947136572779836e-01 +1.630000000000000115e+00 4.369274385730903543e-01 +1.640000000000000124e+00 4.302715312331513720e-01 +1.650000000000000133e+00 4.236298363708130843e-01 +1.660000000000000142e+00 4.170051276217281999e-01 +1.669999999999999929e+00 4.104001068730749879e-01 +1.679999999999999938e+00 4.038174036985497928e-01 +1.689999999999999947e+00 3.972595748776704960e-01 +1.699999999999999956e+00 3.907291039978806890e-01 +1.709999999999999964e+00 3.842284011378613306e-01 +1.719999999999999973e+00 3.777598026304040957e-01 +1.729999999999999982e+00 3.713255709031357821e-01 +1.739999999999999991e+00 3.649278943953298548e-01 +1.750000000000000000e+00 3.585688875489879690e-01 +1.760000000000000009e+00 3.522505908723272405e-01 +1.770000000000000018e+00 3.459749710737644590e-01 +1.780000000000000027e+00 3.397439212644507434e-01 +1.790000000000000036e+00 3.335592612273660107e-01 +1.800000000000000044e+00 3.274227377509619230e-01 +1.810000000000000053e+00 3.213360250253015638e-01 +1.820000000000000062e+00 3.153007250986233356e-01 +1.830000000000000071e+00 3.093183683922376415e-01 +1.840000000000000080e+00 3.033904142716395969e-01 +1.850000000000000089e+00 2.975182516717121417e-01 +1.860000000000000098e+00 2.917031997738803728e-01 +1.870000000000000107e+00 2.859465087330672062e-01 +1.880000000000000115e+00 2.802493604523003667e-01 +1.890000000000000124e+00 2.746128694028142636e-01 +1.900000000000000133e+00 2.690380834874948612e-01 +1.910000000000000142e+00 2.635259849455179881e-01 +1.919999999999999929e+00 2.580774912960386858e-01 +1.929999999999999938e+00 2.526934563188009686e-01 +1.939999999999999947e+00 2.473746710695480799e-01 +1.949999999999999956e+00 2.421218649281299262e-01 +1.959999999999999964e+00 2.369357066772201093e-01 +1.969999999999999973e+00 2.318168056095795404e-01 +1.979999999999999982e+00 2.267657126618208285e-01 +1.989999999999999991e+00 2.217829215726565273e-01 +2.000000000000000000e+00 2.168688700636401956e-01 +2.010000000000000231e+00 2.120239410404359814e-01 +2.020000000000000018e+00 2.072484638126886325e-01 +2.030000000000000249e+00 2.025427153305902617e-01 +2.040000000000000036e+00 1.979069214362860762e-01 +2.049999999999999822e+00 1.933412581282832887e-01 +2.060000000000000053e+00 1.888458528370793865e-01 +2.069999999999999840e+00 1.844207857102533321e-01 +2.080000000000000071e+00 1.800660909053100611e-01 +2.089999999999999858e+00 1.757817578886092247e-01 +2.100000000000000089e+00 1.715677327387462869e-01 +2.109999999999999876e+00 1.674239194528092944e-01 +2.120000000000000107e+00 1.633501812539643328e-01 +2.129999999999999893e+00 1.593463418988812497e-01 +2.140000000000000124e+00 1.554121869835467795e-01 +2.149999999999999911e+00 1.515474652460686023e-01 +2.160000000000000142e+00 1.477518898651102308e-01 +2.169999999999999929e+00 1.440251397526542965e-01 +2.180000000000000160e+00 1.403668608398331852e-01 +2.189999999999999947e+00 1.367766673546163547e-01 +2.200000000000000178e+00 1.332541430901921786e-01 +2.209999999999999964e+00 1.297988426629307357e-01 +2.220000000000000195e+00 1.264102927588607039e-01 +2.229999999999999982e+00 1.230879933676455457e-01 +2.240000000000000213e+00 1.198314190030877746e-01 +2.250000000000000000e+00 1.166400199092426510e-01 +2.260000000000000231e+00 1.135132232512673367e-01 +2.270000000000000018e+00 1.104504342901816843e-01 +2.280000000000000249e+00 1.074510375407597695e-01 +2.290000000000000036e+00 1.045143979118249022e-01 +2.300000000000000266e+00 1.016398618282587235e-01 +2.310000000000000053e+00 9.882675833408750832e-02 +2.319999999999999840e+00 9.607440017604933213e-02 +2.330000000000000071e+00 9.338208486709335954e-02 +2.339999999999999858e+00 9.074909572930309198e-02 +2.350000000000000089e+00 8.817470291577877983e-02 +2.359999999999999876e+00 8.565816441106151313e-02 +2.370000000000000107e+00 8.319872700971145396e-02 +2.379999999999999893e+00 8.079562727270708788e-02 +2.390000000000000124e+00 7.844809246135860115e-02 +2.399999999999999911e+00 7.615534144847727061e-02 +2.410000000000000142e+00 7.391658560657310417e-02 +2.419999999999999929e+00 7.173102967289454734e-02 +2.430000000000000160e+00 6.959787259115582381e-02 +2.439999999999999947e+00 6.751630832983530894e-02 +2.450000000000000178e+00 6.548552667695832497e-02 +2.459999999999999964e+00 6.350471401131446703e-02 +2.470000000000000195e+00 6.157305405008499377e-02 +2.479999999999999982e+00 5.968972857289397971e-02 +2.490000000000000213e+00 5.785391812231730629e-02 +2.500000000000000000e+00 5.606480268091939417e-02 +2.510000000000000231e+00 5.432156232490855524e-02 +2.520000000000000018e+00 5.262337785453282540e-02 +2.530000000000000249e+00 5.096943140135768574e-02 +2.540000000000000036e+00 4.935890701259598029e-02 +2.550000000000000266e+00 4.779099121267828265e-02 +2.560000000000000053e+00 4.626487354227725585e-02 +2.569999999999999840e+00 4.477974707501646695e-02 +2.580000000000000071e+00 4.333480891211646813e-02 +2.589999999999999858e+00 4.192926065524795604e-02 +2.600000000000000089e+00 4.056230885787701079e-02 +2.609999999999999876e+00 3.923316545540828093e-02 +2.620000000000000107e+00 3.794104817444162586e-02 +2.629999999999999893e+00 3.668518092147692727e-02 +2.640000000000000124e+00 3.546479415140973979e-02 +2.649999999999999911e+00 3.427912521617817315e-02 +2.660000000000000142e+00 3.312741869392702571e-02 +2.669999999999999929e+00 3.200892669907012822e-02 +2.680000000000000160e+00 3.092290917363746394e-02 +2.689999999999999947e+00 2.986863416030518040e-02 +2.700000000000000178e+00 2.884537805751076489e-02 +2.709999999999999964e+00 2.785242585706639018e-02 +2.720000000000000195e+00 2.688907136468463369e-02 +2.729999999999999982e+00 2.595461740384056790e-02 +2.740000000000000213e+00 2.504837600339402184e-02 +2.750000000000000000e+00 2.416966856940315489e-02 +2.760000000000000231e+00 2.331782604155990465e-02 +2.770000000000000018e+00 2.249218903468288774e-02 +2.780000000000000249e+00 2.169210796570213151e-02 +2.790000000000000036e+00 2.091694316657254429e-02 +2.800000000000000266e+00 2.016606498355184157e-02 +2.810000000000000053e+00 1.943885386327943651e-02 +2.819999999999999840e+00 1.873470042608972944e-02 +2.830000000000000071e+00 1.805300552699367136e-02 +2.839999999999999858e+00 1.739318030475876797e-02 +2.850000000000000089e+00 1.675464621951530675e-02 +2.859999999999999876e+00 1.613683507931390829e-02 +2.870000000000000107e+00 1.553918905605487094e-02 +2.879999999999999893e+00 1.496116069120686544e-02 +2.890000000000000124e+00 1.440221289172637954e-02 +2.899999999999999911e+00 1.386181891658643146e-02 +2.910000000000000142e+00 1.333946235431587163e-02 +2.919999999999999929e+00 1.283463709194639958e-02 +2.930000000000000160e+00 1.234684727575733817e-02 +2.939999999999999947e+00 1.187560726420353215e-02 +2.950000000000000178e+00 1.142044157340304719e-02 +2.959999999999999964e+00 1.098088481555707825e-02 +2.970000000000000195e+00 1.055648163066528859e-02 +2.979999999999999982e+00 1.014678661189444622e-02 +2.990000000000000213e+00 9.751364224949137655e-03 +3.000000000000000000e+00 9.369788721787701211e-03 +3.010000000000000231e+00 9.001644049016828408e-03 +3.020000000000000018e+00 8.646523751292257282e-03 +3.030000000000000249e+00 8.304030870043598370e-03 +3.040000000000000036e+00 7.973777837834540930e-03 +3.050000000000000266e+00 7.655386368660446923e-03 +3.060000000000000053e+00 7.348487344478121276e-03 +3.070000000000000284e+00 7.052720698253536757e-03 +3.080000000000000071e+00 6.767735293805762255e-03 +3.089999999999999858e+00 6.493188802716336541e-03 +3.100000000000000089e+00 6.228747578565676894e-03 +3.109999999999999876e+00 5.974086528749391473e-03 +3.120000000000000107e+00 5.728888984119206822e-03 +3.129999999999999893e+00 5.492846566685419957e-03 +3.140000000000000124e+00 5.265659055608580032e-03 +3.149999999999999911e+00 5.047034251701308823e-03 +3.160000000000000142e+00 4.836687840651790471e-03 +3.169999999999999929e+00 4.634343255173532447e-03 +3.180000000000000160e+00 4.439731536277212454e-03 +3.189999999999999947e+00 4.252591193853345712e-03 +3.200000000000000178e+00 4.072668066746101476e-03 +3.209999999999999964e+00 3.899715182491525142e-03 +3.220000000000000195e+00 3.733492616885461263e-03 +3.229999999999999982e+00 3.573767353539470965e-03 +3.240000000000000213e+00 3.420313143575352040e-03 +3.250000000000000000e+00 3.272910365602150953e-03 +3.260000000000000231e+00 3.131345886112087574e-03 +3.270000000000000018e+00 2.995412920425440974e-03 +3.280000000000000249e+00 2.864910894307190828e-03 +3.290000000000000036e+00 2.739645306372137483e-03 +3.300000000000000266e+00 2.619427591388258370e-03 +3.310000000000000053e+00 2.504074984582378965e-03 +3.320000000000000284e+00 2.393410387045433359e-03 +3.330000000000000071e+00 2.287262232329256937e-03 +3.339999999999999858e+00 2.185464354320594375e-03 +3.350000000000000089e+00 2.087855856472531791e-03 +3.359999999999999876e+00 1.994280982468088839e-03 +3.370000000000000107e+00 1.904588988385240433e-03 +3.379999999999999893e+00 1.818634016427714837e-03 +3.390000000000000124e+00 1.736274970280524870e-03 +3.399999999999999911e+00 1.657375392144809457e-03 +3.410000000000000142e+00 1.581803341501400190e-03 +3.419999999999999929e+00 1.509431275648455547e-03 +3.430000000000000160e+00 1.440135932053693713e-03 +3.439999999999999947e+00 1.373798212557982359e-03 +3.450000000000000178e+00 1.310303069462591136e-03 +3.459999999999999964e+00 1.249539393528863824e-03 +3.470000000000000195e+00 1.191399903915032771e-03 +3.479999999999999982e+00 1.135781040071535703e-03 +3.490000000000000213e+00 1.082582855612596300e-03 +3.500000000000000000e+00 1.031708914178621796e-03 +3.510000000000000231e+00 9.830661873007860967e-04 +3.520000000000000018e+00 9.365649542761766066e-04 +3.530000000000000249e+00 8.921187040589807250e-04 +3.540000000000000036e+00 8.496440391705326880e-04 +3.550000000000000266e+00 8.090605816283328536e-04 +3.560000000000000053e+00 7.702908808918079286e-04 +3.570000000000000284e+00 7.332603238201528852e-04 +3.580000000000000071e+00 6.978970466354378647e-04 +3.589999999999999858e+00 6.641318488820351822e-04 +3.600000000000000089e+00 6.318981093715066296e-04 +3.609999999999999876e+00 6.011317041001773337e-04 +3.620000000000000107e+00 5.717709261249223933e-04 +3.629999999999999893e+00 5.437564073810660040e-04 +3.640000000000000124e+00 5.170310424247296808e-04 +3.649999999999999911e+00 4.915399140806066009e-04 +3.660000000000000142e+00 4.672302209747489265e-04 +3.669999999999999929e+00 4.440512069308050593e-04 +3.680000000000000160e+00 4.219540922069490454e-04 +3.689999999999999947e+00 4.008920065497597273e-04 +3.700000000000000178e+00 3.808199240403083684e-04 +3.709999999999999964e+00 3.616945997068966878e-04 +3.720000000000000195e+00 3.434745078780324068e-04 +3.729999999999999982e+00 3.261197822486135807e-04 +3.740000000000000213e+00 3.095921576315514476e-04 +3.750000000000000000e+00 2.938549133666026425e-04 +3.760000000000000231e+00 2.788728183575983708e-04 +3.770000000000000018e+00 2.646120777089062113e-04 +3.780000000000000249e+00 2.510402809315314646e-04 +3.790000000000000036e+00 2.381263516890092305e-04 +3.800000000000000266e+00 2.258404990529570735e-04 +3.810000000000000053e+00 2.141541702379862069e-04 +3.820000000000000284e+00 2.030400047855086668e-04 +3.830000000000000071e+00 1.924717901659137053e-04 +3.839999999999999858e+00 1.824244187684995189e-04 +3.850000000000000089e+00 1.728738462485919904e-04 +3.859999999999999876e+00 1.637970512012828822e-04 +3.870000000000000107e+00 1.551719961313237036e-04 +3.879999999999999893e+00 1.469775896888190277e-04 +3.890000000000000124e+00 1.391936501405120579e-04 +3.899999999999999911e+00 1.318008700466514831e-04 +3.910000000000000142e+00 1.247807821136226263e-04 +3.919999999999999929e+00 1.181157261927979286e-04 +3.930000000000000160e+00 1.117888173962968014e-04 +3.939999999999999947e+00 1.057839153006771295e-04 +3.950000000000000178e+00 1.000855942098637422e-04 +3.959999999999999964e+00 9.467911444898297101e-05 +3.970000000000000195e+00 8.955039466111365304e-05 +3.979999999999999982e+00 8.468598507935508899e-05 +3.990000000000000213e+00 8.007304174698640564e-05 +4.000000000000000000e+00 7.569930165891772917e-05 +4.009999999999999787e+00 7.155305879803737284e-05 +4.020000000000000462e+00 6.762314104049842590e-05 +4.030000000000000249e+00 6.389888790443169124e-05 +4.040000000000000036e+00 6.037012911701270163e-05 +4.049999999999999822e+00 5.702716397528318892e-05 +4.060000000000000497e+00 5.386074147658470357e-05 +4.070000000000000284e+00 5.086204119493443527e-05 +4.080000000000000071e+00 4.802265488015101832e-05 +4.089999999999999858e+00 4.533456875702039282e-05 +4.099999999999999645e+00 4.279014650226993922e-05 +4.110000000000000320e+00 4.038211287760699484e-05 +4.120000000000000107e+00 3.810353799756363775e-05 +4.129999999999999893e+00 3.594782221137101584e-05 +4.139999999999999680e+00 3.390868157858218285e-05 +4.150000000000000355e+00 3.198013391863285836e-05 +4.160000000000000142e+00 3.015648541502528071e-05 +4.169999999999999929e+00 2.843231775528890664e-05 +4.179999999999999716e+00 2.680247578835809533e-05 +4.190000000000000391e+00 2.526205568147361550e-05 +4.200000000000000178e+00 2.380639355918723205e-05 +4.209999999999999964e+00 2.243105460751029207e-05 +4.219999999999999751e+00 2.113182262671278963e-05 +4.230000000000000426e+00 1.990469001672597201e-05 +4.240000000000000213e+00 1.874584817955670109e-05 +4.250000000000000000e+00 1.765167832355950015e-05 +4.259999999999999787e+00 1.661874265485266630e-05 +4.270000000000000462e+00 1.564377594159051401e-05 +4.280000000000000249e+00 1.472367743722979956e-05 +4.290000000000000036e+00 1.385550314934019049e-05 +4.299999999999999822e+00 1.303645844092226011e-05 +4.310000000000000497e+00 1.226389095159203316e-05 +4.320000000000000284e+00 1.153528382638809769e-05 +4.330000000000000071e+00 1.084824924034087195e-05 +4.339999999999999858e+00 1.020052220732327752e-05 +4.350000000000000533e+00 9.589954662070093405e-06 +4.360000000000000320e+00 9.014509804616692749e-06 +4.370000000000000107e+00 8.472256696760831896e-06 +4.379999999999999893e+00 7.961365100498308370e-06 +4.389999999999999680e+00 7.480100548719846339e-06 +4.400000000000000355e+00 7.026819638787182111e-06 +4.410000000000000142e+00 6.599965539928366694e-06 +4.419999999999999929e+00 6.198063705704921943e-06 +4.429999999999999716e+00 5.819717783110912374e-06 +4.440000000000000391e+00 5.463605710160176731e-06 +4.450000000000000178e+00 5.128475994109442647e-06 +4.459999999999999964e+00 4.813144162745563934e-06 +4.469999999999999751e+00 4.516489381441409406e-06 +4.480000000000000426e+00 4.237451228949907836e-06 +4.490000000000000213e+00 3.975026625166211130e-06 +4.500000000000000000e+00 3.728266904338776927e-06 +4.509999999999999787e+00 3.496275027455002836e-06 +4.520000000000000462e+00 3.278202927764108358e-06 +4.530000000000000249e+00 3.073248983629502589e-06 +4.540000000000000036e+00 2.880655613125952564e-06 +4.549999999999999822e+00 2.699706985013199309e-06 +4.560000000000000497e+00 2.529726840926145037e-06 +4.570000000000000284e+00 2.370076423825027983e-06 +4.580000000000000071e+00 2.220152507944157265e-06 +4.589999999999999858e+00 2.079385525668439710e-06 +4.600000000000000533e+00 1.947237786949184512e-06 +4.610000000000000320e+00 1.823201787048896874e-06 +4.620000000000000107e+00 1.706798598575101179e-06 +4.629999999999999893e+00 1.597576343929440794e-06 +4.639999999999999680e+00 1.495108744457528071e-06 +4.650000000000000355e+00 1.398993742739143427e-06 +4.660000000000000142e+00 1.308852194607379469e-06 +4.669999999999999929e+00 1.224326627628362285e-06 +4.679999999999999716e+00 1.145080062911691148e-06 +4.690000000000000391e+00 1.070794897254885976e-06 +4.700000000000000178e+00 1.001171842753453140e-06 +4.709999999999999964e+00 9.359289211317424164e-07 +4.719999999999999751e+00 8.748005101689583192e-07 +4.730000000000000426e+00 8.175364397089691711e-07 +4.740000000000000213e+00 7.639011348529320970e-07 +4.750000000000000000e+00 7.136728040394321014e-07 +4.759999999999999787e+00 6.666426698190985282e-07 +4.770000000000000462e+00 6.226142402281157827e-07 +4.780000000000000249e+00 5.814026187594347311e-07 +4.790000000000000036e+00 5.428338510206137025e-07 +4.799999999999999822e+00 5.067443062540728789e-07 +4.810000000000000497e+00 4.729800919787301626e-07 +4.820000000000000284e+00 4.413965000918793889e-07 +4.830000000000000071e+00 4.118574828467606342e-07 +4.839999999999999858e+00 3.842351571948298727e-07 +4.850000000000000533e+00 3.584093360520481808e-07 +4.860000000000000320e+00 3.342670851160997905e-07 +4.870000000000000107e+00 3.117023039260469944e-07 +4.879999999999999893e+00 2.906153299179266810e-07 +4.889999999999999680e+00 2.709125642890161304e-07 +4.900000000000000355e+00 2.525061185403120229e-07 +4.910000000000000142e+00 2.353134806210512852e-07 +4.919999999999999929e+00 2.192571996510261051e-07 +4.929999999999999716e+00 2.042645882462201667e-07 +4.940000000000000391e+00 1.902674415206736977e-07 +4.950000000000000178e+00 1.772017718829930070e-07 +4.959999999999999964e+00 1.650075587892168629e-07 +4.969999999999999751e+00 1.536285126552568616e-07 +4.980000000000000426e+00 1.430118521716543266e-07 +4.990000000000000213e+00 1.331080943011983516e-07 +5.000000000000000000e+00 1.238708562759898487e-07 diff --git a/config/glass_mock/test_data/redshift_distribution_tomo.txt b/config/glass_mock/test_data/redshift_distribution_tomo.txt new file mode 100644 index 00000000..92fd76f6 --- /dev/null +++ b/config/glass_mock/test_data/redshift_distribution_tomo.txt @@ -0,0 +1,501 @@ +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000021e-02 1.539937506863479477e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.000000000000000042e-02 4.354621114841233237e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.999999999999999889e-02 7.996950397128197580e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.000000000000000083e-02 1.230564841035703891e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +5.000000000000000278e-02 1.718606196257605606e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +5.999999999999999778e-02 2.257303471016786772e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +7.000000000000000666e-02 2.841755418221013352e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +8.000000000000000167e-02 3.468055475441410296e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +8.999999999999999667e-02 4.132958972425367777e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000056e-01 4.833686282899146008e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.100000000000000006e-01 5.567798045703171994e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.199999999999999956e-01 6.333111840482133492e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.300000000000000044e-01 7.127644240940279874e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.400000000000000133e-01 7.949569188500757444e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.499999999999999944e-01 8.797187290406095761e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.600000000000000033e-01 9.668902677243640387e-02 1.614882619331313001e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.700000000000000122e-01 1.056320523994964816e-01 1.351137338877444904e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.799999999999999933e-01 1.147865678773969250e-01 9.505027516849669190e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.900000000000000022e-01 1.241388012363918020e-01 6.151176652093682308e-15 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.000000000000000111e-01 1.336755033059418007e-01 3.646504877060481157e-14 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.099999999999999922e-01 1.433838775928187315e-01 1.991003829296580357e-13 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.200000000000000011e-01 1.532515234440552376e-01 1.004988630970547006e-12 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.300000000000000100e-01 1.632663897108496964e-01 4.704946108657897756e-12 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.399999999999999911e-01 1.734167368035731960e-01 2.049424340658864430e-11 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.500000000000000000e-01 1.836911055111285218e-01 8.331084045868062068e-11 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.600000000000000089e-01 1.940782912974445551e-01 3.169642814338128490e-10 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.700000000000000178e-01 2.045673229974847573e-01 1.131737369315146494e-09 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.800000000000000266e-01 2.151474448827670471e-01 3.802240322614322598e-09 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.899999999999999800e-01 2.258081008434757453e-01 1.204958480370055000e-08 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.999999999999999889e-01 2.365389187006463334e-01 3.610558922273093705e-08 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.099999999999999978e-01 2.473296909667828114e-01 1.025255115058832154e-07 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.200000000000000067e-01 2.581703449681812534e-01 2.764946549727501638e-07 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.300000000000000155e-01 2.690508890313944024e-01 7.096436114082356453e-07 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.400000000000000244e-01 2.799613110462015886e-01 1.736823875468063563e-06 4.662322231933824855e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.500000000000000333e-01 2.908913897960314077e-01 4.061254811421941901e-06 2.583670898901443416e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.599999999999999867e-01 3.018303572909819543e-01 9.089639482710861433e-06 1.373947407418390805e-15 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.699999999999999956e-01 3.127663228662022066e-01 1.950634436891043497e-05 6.893151510737389432e-15 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.800000000000000044e-01 3.236853407172435304e-01 4.020484981860280042e-05 3.241856199299520783e-14 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +3.900000000000000133e-01 3.345699792701520847e-01 7.971796903761345344e-05 1.432642055538383969e-13 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.000000000000000222e-01 3.453972446681233266e-01 1.522944635599302987e-04 5.966898959928456991e-13 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.100000000000000311e-01 3.561357356872097979e-01 2.807446642384667278e-04 2.347659114530768397e-12 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.199999999999999845e-01 3.667419771453656052e-01 5.001079644842026418e-04 8.742488218771877294e-12 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.299999999999999933e-01 3.771560018219017318e-01 8.620721065502708535e-04 3.087410164958152419e-11 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.400000000000000022e-01 3.872964251093485299e-01 1.439899979450073690e-03 1.035915583661646512e-10 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.500000000000000111e-01 3.970554653825547065e-01 2.333410878777936875e-03 3.308287653551427720e-10 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.600000000000000200e-01 4.062945736079904835e-01 3.673352617441839798e-03 1.007349317623349535e-09 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.700000000000000289e-01 4.148415023345984332e-01 5.624334197152194674e-03 2.929378468558717274e-09 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.799999999999999822e-01 4.224897163924213883e-01 8.385416427587607385e-03 8.148601966637974086e-09 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +4.899999999999999911e-01 4.290009818473208281e-01 1.218752337000364276e-02 2.171555134073154624e-08 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +5.000000000000000000e-01 4.341117422412649196e-01 1.728706443813065111e-02 5.552417039207953005e-08 5.011534101145543459e-17 0.000000000000000000e+00 0.000000000000000000e+00 +5.100000000000000089e-01 4.375435079817874828e-01 2.395553912012464079e-02 1.364069878953243777e-07 2.049468077523805556e-16 0.000000000000000000e+00 0.000000000000000000e+00 +5.200000000000000178e-01 4.390169859943412467e-01 3.246539324048713232e-02 3.224274003695541761e-07 8.636940693148735105e-16 0.000000000000000000e+00 0.000000000000000000e+00 +5.300000000000000266e-01 4.382691325285715256e-01 4.307293697093857177e-02 7.342502750767433476e-07 3.767504937405573431e-15 0.000000000000000000e+00 0.000000000000000000e+00 +5.400000000000000355e-01 4.350718106991963707e-01 5.599963160420910746e-02 1.612979315532863648e-06 1.537465727950917325e-14 0.000000000000000000e+00 0.000000000000000000e+00 +5.500000000000000444e-01 4.292503654041376904e-01 7.141341456206214133e-02 3.422331970336960685e-06 5.975411748414992075e-14 0.000000000000000000e+00 0.000000000000000000e+00 +5.600000000000000533e-01 4.207002636678696672e-01 8.941188803754447634e-02 7.021690155691773514e-06 2.214129888757403200e-13 0.000000000000000000e+00 0.000000000000000000e+00 +5.700000000000000622e-01 4.094000272965073739e-01 1.100091071956577626e-01 1.394720360637749612e-05 7.830387799077097932e-13 0.000000000000000000e+00 0.000000000000000000e+00 +5.799999999999999600e-01 3.954190047130465846e-01 1.331273721792688158e-01 2.684981926551858141e-05 2.646886166283657862e-12 0.000000000000000000e+00 0.000000000000000000e+00 +5.899999999999999689e-01 3.789190465915061590e-01 1.585948995274572282e-01 5.015000017562216392e-05 8.564369808719667552e-12 0.000000000000000000e+00 0.000000000000000000e+00 +5.999999999999999778e-01 3.601497897086659572e-01 1.861496016761532424e-01 9.097603490253185112e-05 2.656128200553507587e-11 0.000000000000000000e+00 0.000000000000000000e+00 +6.099999999999999867e-01 3.394379220485379545e-01 2.154485351236863355e-01 1.604523081202781538e-04 7.906276363770337246e-11 0.000000000000000000e+00 0.000000000000000000e+00 +6.199999999999999956e-01 3.171714061995755674e-01 2.460819864629658904e-01 2.753912749512043212e-04 2.261623034735574779e-10 0.000000000000000000e+00 0.000000000000000000e+00 +6.300000000000000044e-01 2.937800998367049998e-01 2.775907325507723233e-01 4.604140505979135815e-04 6.224870767506286974e-10 0.000000000000000000e+00 0.000000000000000000e+00 +6.400000000000000133e-01 2.697144812642079392e-01 3.094847895376878766e-01 7.504768671437603636e-04 1.650522107997988509e-09 0.000000000000000000e+00 0.000000000000000000e+00 +6.500000000000000222e-01 2.454242469633750034e-01 3.412619728228676319e-01 1.193714293979784543e-03 4.220799766222615784e-09 0.000000000000000000e+00 0.000000000000000000e+00 +6.600000000000000311e-01 2.213384108026694164e-01 3.724248082580806640e-01 1.854429052641198795e-03 1.042162672717165301e-08 0.000000000000000000e+00 0.000000000000000000e+00 +6.700000000000000400e-01 1.978482402854722932e-01 4.024947164274449429e-01 2.815970867028899302e-03 2.487211160489419456e-08 3.348204782605323907e-17 0.000000000000000000e+00 +6.800000000000000488e-01 1.752939689160500913e-01 4.310228690679417940e-01 4.183165543133970064e-03 5.743543506937962249e-08 6.777912242156971001e-17 0.000000000000000000e+00 +6.900000000000000577e-01 1.539557856234219924e-01 4.575976142393253232e-01 6.083895557180169812e-03 1.284624891195774969e-07 3.085718799889921458e-16 0.000000000000000000e+00 +7.000000000000000666e-01 1.340491780397383981e-01 4.818488131742932890e-01 8.669410423207461053e-03 2.785652001697901804e-07 1.248136954195448217e-15 0.000000000000000000e+00 +7.099999999999999645e-01 1.157243407682299330e-01 5.034497692372071898e-01 1.211297199850875674e-02 5.861960871976178571e-07 4.520612173364571743e-15 0.000000000000000000e+00 +7.199999999999999734e-01 9.906908180325493807e-02 5.221176213873008898e-01 1.660652401890975668e-02 1.198182336259976755e-06 1.586130685546551663e-14 0.000000000000000000e+00 +7.299999999999999822e-01 8.411448308133587237e-02 5.376131084736023302e-01 2.235521580753587964e-02 2.380963803790221850e-06 5.355935762190425332e-14 0.000000000000000000e+00 +7.399999999999999911e-01 7.084249342384477122e-02 5.497404978324285096e-01 2.956979726537413769e-02 4.603694687655575934e-06 1.739585492590330683e-13 0.000000000000000000e+00 +7.500000000000000000e-01 5.919464147614116045e-02 5.583482432664836903e-01 3.845711711715178033e-02 8.668551435002927783e-06 5.440519998929155855e-13 0.000000000000000000e+00 +7.600000000000000089e-01 4.908113325887875078e-02 5.633306385784786485e-01 4.920917355332636578e-02 1.590831770127603740e-05 1.640098818002154181e-12 0.000000000000000000e+00 +7.700000000000000178e-01 4.038972126963992493e-02 5.646304145716388723e-01 6.199135756487455379e-02 2.847609961672602441e-05 4.771559347140737470e-12 0.000000000000000000e+00 +7.800000000000000266e-01 3.299387779642509039e-02 5.622419392805708682e-01 7.693066766536615919e-02 4.975605239389383909e-05 1.340984640895080407e-11 0.000000000000000000e+00 +7.900000000000000355e-01 2.675995522130884818e-02 5.562144640312042254e-01 9.410473960353642198e-02 8.492613821850217477e-05 3.644076367341346881e-11 0.000000000000000000e+00 +8.000000000000000444e-01 2.155315592808926956e-02 5.466547389784724986e-01 1.135325149284951984e-01 1.417027899099369457e-04 9.584228925483725884e-11 0.000000000000000000e+00 +8.100000000000000533e-01 1.724225413625518621e-02 5.337283120040172690e-01 1.351672682569173556e-01 2.312914228756922049e-04 2.441905895621031500e-10 0.000000000000000000e+00 +8.200000000000000622e-01 1.370310631446661383e-02 5.176589189564108073e-01 1.588925367334704242e-01 3.695546593339714457e-04 6.032339399339098914e-10 0.000000000000000000e+00 +8.300000000000000711e-01 1.082105440739568325e-02 4.987255519052841701e-01 1.845212680151044615e-01 5.783910087925287023e-04 1.446104618044234327e-09 0.000000000000000000e+00 +8.399999999999999689e-01 8.492368483615756955e-03 4.772570262102269756e-01 2.117982528241139084e-01 8.872905102245751520e-04 3.366910160836069286e-09 0.000000000000000000e+00 +8.499999999999999778e-01 6.624896028738718780e-03 4.536241228125140745e-01 2.404056641564599750e-01 1.335001538209432690e-03 7.619585293097197538e-09 0.000000000000000000e+00 +8.599999999999999867e-01 5.138088405274474427e-03 4.282296256229109477e-01 2.699713143923895275e-01 1.971206986286263250e-03 1.677408889534854048e-08 0.000000000000000000e+00 +8.699999999999999956e-01 3.962565662767755965e-03 4.014967764109840798e-01 3.000790842252494817e-01 2.858072067107956474e-03 3.594878393675884972e-08 0.000000000000000000e+00 +8.800000000000000044e-01 3.039363303081291188e-03 3.738568104795802483e-01 3.302808850321978240e-01 4.071494783317450121e-03 7.505634360454145076e-08 0.000000000000000000e+00 +8.900000000000000133e-01 2.318982557617298070e-03 3.457363046782563343e-01 3.601094908753040835e-01 5.701869059379470125e-03 1.527775598766901995e-07 0.000000000000000000e+00 +9.000000000000000222e-01 1.760342204033970084e-03 3.175450639159804189e-01 3.890916106036985123e-01 7.854164751648755674e-03 3.033910842837618412e-07 0.000000000000000000e+00 +9.100000000000000311e-01 1.329707117851893893e-03 2.896652009065096012e-01 4.167606505186060573e-01 1.064714242860430662e-02 5.881805399220220047e-07 3.987939362915592498e-17 +9.200000000000000400e-01 9.996480514146239768e-04 2.624419405635543545e-01 4.426687258288088955e-01 1.421155625060968597e-02 1.113956680258438677e-06 1.599438325452657675e-16 +9.300000000000000488e-01 7.480693616013980992e-04 2.361765232463523267e-01 4.663975960230017925e-01 1.868725529415628597e-02 2.062305440089852803e-06 4.809562789568694136e-16 +9.400000000000000577e-01 5.573268799382214865e-04 2.111214090595887882e-01 4.875683089034889561e-01 2.421916922979048745e-02 3.734515060670443504e-06 1.526104689638597213e-15 +9.500000000000000666e-01 4.134467924763931949e-04 1.874778164993770035e-01 5.058494283747770881e-01 3.095225256719253207e-02 6.618720710238419233e-06 4.706782123258309453e-15 +9.599999999999999645e-01 3.054480002419670843e-04 1.653954777146330324e-01 5.209637857912046144e-01 3.902555464459427775e-02 1.148754977200705858e-05 1.409958892945934097e-14 +9.699999999999999734e-01 2.247645639621065523e-04 1.449743701667425277e-01 5.326937331039534040e-01 4.856567071110100275e-02 1.953629700688028575e-05 4.097287642725277212e-14 +9.799999999999999822e-01 1.647610477458523871e-04 1.262680966095084123e-01 5.408848924670576208e-01 5.967990317940288780e-02 3.257316063442919438e-05 1.156264860980133771e-13 +9.899999999999999911e-01 1.203314172321944019e-04 1.092885338187701977e-01 5.454483989827731971e-01 7.244951283445183465e-02 5.327399336475601528e-05 3.169966378707302841e-13 +1.000000000000000000e+00 8.757120797261259363e-05 9.401135343377249598e-02 5.463616299897278150e-01 8.692346113349824765e-02 8.551395665096365236e-05 8.445631829787452869e-13 +1.010000000000000009e+00 6.351260558210720388e-05 8.038203093412973899e-02 5.436674144371487216e-01 1.031130335113659702e-01 1.347869605537307329e-04 2.188292442234903755e-12 +1.020000000000000018e+00 4.591258022744503978e-05 6.832199474986185017e-02 5.374717262206131219e-01 1.209876901816309969e-01 2.087203879984701250e-04 5.517788499267541666e-12 +1.030000000000000027e+00 3.308506691439644051e-05 5.773461963661813412e-02 5.279398896906133398e-01 1.404724192159305385e-01 3.176869911565202687e-04 1.354907999599729549e-11 +1.040000000000000036e+00 2.376920947954347198e-05 4.851082969953900148e-02 5.152913643308603131e-01 1.614467736341936299e-01 4.755078334668812450e-04 3.242073798140482060e-11 +1.050000000000000044e+00 1.702676875958501860e-05 4.053414051274251140e-02 4.997932261228877904e-01 1.837456689052915371e-01 7.002298178478492518e-04 7.564456142563883764e-11 +1.060000000000000053e+00 1.216285246587368811e-05 3.368503150020914910e-02 4.817525201847516758e-01 2.071619096456787934e-01 1.014949105503201027e-03 1.722036485676948522e-10 +1.070000000000000062e+00 8.665105188461925210e-06 2.784459530287804185e-02 4.615077162574321989e-01 2.314503141294012023e-01 1.448638376333029982e-03 3.827163644781810072e-10 +1.080000000000000071e+00 6.157374138353131114e-06 2.289745779829197372e-02 4.394195485514988397e-01 2.563332207838229237e-01 2.036922560021247729e-03 8.308747909735498940e-10 +1.090000000000000080e+00 4.364628584036829025e-06 1.873399954403896436e-02 4.158615581335344813e-01 2.815070981489081747e-01 2.822735823495265729e-03 1.763051501130930938e-09 +1.100000000000000089e+00 3.086558380169819119e-06 1.525193649955695376e-02 3.912106747804782181e-01 3.066499420314597324e-01 3.856783857886315284e-03 3.658523730880318455e-09 +1.110000000000000098e+00 2.177816382631798547e-06 1.235733554530114506e-02 3.658381735243608768e-01 3.314291310907604426e-01 5.197731489308821465e-03 7.428353216209023155e-09 +1.120000000000000107e+00 1.533311608890686663e-06 9.965149701945600139e-03 3.401013187109006575e-01 3.555094225198551228e-01 6.912037758511026773e-03 1.476567456409793133e-08 +1.130000000000000115e+00 1.077317174939318544e-06 7.999360496998732598e-03 3.143359672256667015e-01 3.785607984628082123e-01 9.073369868121048537e-03 2.874819218854707111e-08 +1.140000000000000124e+00 7.554420044525390091e-07 6.392812182772026133e-03 2.888503462911807262e-01 4.002659156940988616e-01 1.176154416230164659e-02 5.485055610683904830e-08 +1.150000000000000133e+00 5.287401273633696096e-07 5.086815988022990419e-03 2.639201548017506838e-01 4.203269598298132181e-01 1.506096621093475953e-02 1.026067721427023607e-07 +1.159999999999999920e+00 3.694069600603872627e-07 4.030593650930917524e-03 2.397850660033362113e-01 4.384717552720807654e-01 1.905857194120165812e-02 1.882788031126817605e-07 +1.169999999999999929e+00 2.576478256172417289e-07 3.180619291396995056e-03 2.166466388580143010e-01 4.544590285607395530e-01 2.384130565034060789e-02 3.390455127107025517e-07 +1.179999999999999938e+00 1.794082257805426176e-07 2.499908159783769887e-03 1.946675804952582300e-01 4.680827626086341287e-01 2.949320607678780973e-02 5.994341707921660711e-07 +1.189999999999999947e+00 1.247347553751315127e-07 1.957290636282447288e-03 1.739722466174895843e-01 4.791756108173245710e-01 3.609220554043009871e-02 1.040981052976354072e-06 +1.199999999999999956e+00 8.659556137888371261e-08 1.526700523074696380e-03 1.546482232702307003e-01 4.876113632164771339e-01 4.370677642250402062e-02 1.776430424561485100e-06 +1.209999999999999964e+00 6.003431641129191042e-08 1.186498456821617666e-03 1.367488034117998463e-01 4.933064726950170398e-01 5.239258109886594378e-02 2.980158058868660208e-06 +1.219999999999999973e+00 4.156531527405939585e-08 9.188443069215798805e-04 1.202961554213368978e-01 4.962206600834439540e-01 6.218929355632565187e-02 4.916922652659156695e-06 +1.229999999999999982e+00 2.874226537364183563e-08 7.091267350689117992e-04 1.052849772272313883e-01 4.963566246648489222e-01 7.311776179357656891e-02 7.981480093998342935e-06 +1.239999999999999991e+00 1.985176979539427896e-08 5.454536114729735975e-04 9.168643748116768466e-02 4.937588939219547668e-01 8.517766919146980975e-02 1.275200101938270604e-05 +1.250000000000000000e+00 1.369603324259006201e-08 4.182035971692599718e-04 7.945222197468297665e-02 4.885118548082269530e-01 9.834583083402068104e-02 2.006055534552998338e-05 +1.260000000000000009e+00 9.439214314960034364e-09 3.196367654093523682e-04 6.851852685803538900e-02 4.807370197047131088e-01 1.125752287815075109e-01 3.108409740326714259e-05 +1.270000000000000018e+00 6.499046789844780571e-09 2.435604924737936846e-04 5.880986771019022091e-02 4.705895938211596130e-01 1.277948509127032062e-01 4.745931905434854099e-05 +1.280000000000000027e+00 4.470568134183769465e-09 1.850458460820321276e-04 5.024260282436234654e-02 4.582544266593162052e-01 1.439103541662635211e-01 7.142434700938892834e-05 +1.290000000000000036e+00 3.072562195846065087e-09 1.401891957253903313e-04 4.272809822220174913e-02 4.439414471605867551e-01 1.608055281600602460e-01 1.059894588790527889e-04 +1.300000000000000044e+00 2.110029059468151354e-09 1.059136380165031282e-04 3.617548928941841102e-02 4.278806987493339320e-01 1.783444926099220507e-01 1.551377093322584737e-04 +1.310000000000000053e+00 1.447938509207935114e-09 7.980496463201170300e-05 3.049401836180056641e-02 4.103171049105429069e-01 1.963745247767608171e-01 2.240545501267006835e-04 +1.320000000000000062e+00 9.929082461189637924e-10 5.997721296292581636e-05 2.559494832978258944e-02 3.915051065295243049e-01 2.147293838621967266e-01 3.193831943909459549e-04 +1.330000000000000071e+00 6.804373241412314845e-10 4.496326070311629057e-05 2.139306900207567017e-02 3.717033175897766495e-01 2.332329796163886138e-01 4.494996689140518214e-04 +1.340000000000000080e+00 4.660249166082903513e-10 3.362640219784691930e-05 1.780782552932241164e-02 3.511693450524774884e-01 2.517032233212468939e-01 6.247983335022933580e-04 +1.350000000000000089e+00 3.190020323059575847e-10 2.508933740362713548e-05 1.476410684746647280e-02 3.301549114641024740e-01 2.699559007785266584e-01 8.579753047108228418e-04 +1.360000000000000098e+00 2.182537620060257098e-10 1.867748643680915176e-05 1.219273725074603715e-02 3.089014052730736370e-01 2.878084181409205633e-01 1.164293945402312264e-03 +1.370000000000000107e+00 1.492567491773983311e-10 1.387399592588429562e-05 1.003071635392241250e-02 2.876359647305094525e-01 3.050832908439197744e-01 1.561813664434939146e-03 +1.380000000000000115e+00 1.020304123234632149e-10 1.028421700879462491e-05 8.221252415421138535e-03 2.665681777852352274e-01 3.216112714711567566e-01 2.071561034744768669e-03 +1.390000000000000124e+00 6.972163412658023648e-11 7.607803539589589622e-06 6.713631836315738302e-03 2.458874540373246487e-01 3.372340417499066212e-01 2.717621007763046641e-03 +1.400000000000000133e+00 4.762837548229303302e-11 5.616901495429025980e-06 5.462964162213741412e-03 2.257610972106653402e-01 3.518064245725837647e-01 3.527126071955804382e-03 +1.409999999999999920e+00 3.252686654653270964e-11 4.139177976849373141e-06 4.429837577211066107e-03 2.063330793613646352e-01 3.651981016438504146e-01 4.530122803442502774e-03 +1.419999999999999929e+00 2.220820359598883994e-11 3.044673545841167993e-06 3.579915100585794388e-03 1.877234926355634825e-01 3.772948490350265804e-01 5.759298520800725830e-03 +1.429999999999999938e+00 1.515991523648943325e-11 2.235658889103976142e-06 2.883496809241144730e-03 1.700286320654501859e-01 3.879993250034428631e-01 7.249555695220221210e-03 +1.439999999999999947e+00 1.034683098626099816e-11 1.638840311522065044e-06 2.315068665998265638e-03 1.533216445716258092e-01 3.972314608487446108e-01 9.037428252682576379e-03 +1.449999999999999956e+00 7.060922971419358754e-12 1.199392806151284170e-06 1.852854117469739364e-03 1.376536656025731664e-01 4.049285158375934279e-01 1.116034166477316084e-02 +1.459999999999999964e+00 4.818095350094647536e-12 8.764086843136990109e-07 1.478380653643590918e-03 1.230553559195996793e-01 4.110448613842956900e-01 1.365572735324401508e-02 +1.469999999999999973e+00 3.287475050906292697e-12 6.394379240018318150e-07 1.176070059350544858e-03 1.095387468405975423e-01 4.155515582622911763e-01 1.656001091470735501e-02 +1.479999999999999982e+00 2.243028356765281869e-12 4.658670434431361876e-07 9.328581577592964628e-04 9.709930243004447070e-02 4.184357845621698435e-01 1.990750241626376357e-02 +1.489999999999999991e+00 1.530410370860696685e-12 3.389395278792606130e-07 7.378474379748105241e-04 8.571811109958503427e-02 4.197001625968400651e-01 2.372922490270214929e-02 +1.500000000000000000e+00 1.044227620301937164e-12 2.462652922192765168e-07 5.819940399045956444e-04 7.536412615730746223e-02 4.193620213150490716e-01 2.805172369587968437e-02 +1.510000000000000009e+00 7.125319208727499156e-13 1.787016081457706795e-07 4.578290922701090714e-04 6.599638423629203277e-02 4.174526183660571266e-01 3.289590353503257791e-02 +1.520000000000000018e+00 4.862454061953004069e-13 1.295152431329936622e-07 3.592133083616361297e-04 5.756614145515785014e-02 4.140163340064137443e-01 3.827594270030442586e-02 +1.530000000000000027e+00 3.318641881800761590e-13 9.375680550089078047e-08 2.811229803902905123e-04 5.001887886674720451e-02 4.091098386070023962e-01 4.419833273351361780e-02 +1.540000000000000036e+00 2.265235929570130444e-13 6.779473330279948552e-08 2.194650195892666104e-04 4.329614056732959682e-02 4.028012273977749635e-01 5.066108915181868205e-02 +1.550000000000000044e+00 1.546572594408821060e-13 4.896903398857068421e-08 1.709184117419885595e-04 3.733717920593891682e-02 3.951691107749291110e-01 5.765317276005898356e-02 +1.560000000000000053e+00 1.055972136085097858e-13 3.533460021916003750e-08 1.327993480889430187e-04 3.208039411088316195e-02 3.863016461815973468e-01 6.515415310369479518e-02 +1.570000000000000062e+00 7.212787436823881470e-14 2.547135196869689815e-08 1.029473073417947572e-04 2.746455652191652150e-02 3.762954981625536433e-01 7.313413572006240149e-02 +1.580000000000000071e+00 4.926561120850808183e-14 1.834410667434065353e-08 7.962947006486899347e-05 2.342982428523900443e-02 3.652547163437112610e-01 8.155396371542289469e-02 +1.590000000000000080e+00 3.366389966685696226e-14 1.319936283749637620e-08 6.146101257826301601e-05 1.991855474509214227e-02 3.532895262658639624e-01 9.036569246971370983e-02 +1.600000000000000089e+00 2.300688167185030099e-14 9.489422346087647066e-09 4.733903027706718483e-05 1.687592945220024732e-02 3.405150345524147748e-01 9.951332462804229895e-02 +1.610000000000000098e+00 1.574686925727820699e-14 6.816714197451711120e-09 3.638806161764795288e-05 1.425040778032774305e-02 3.270498570999844645e-01 1.089337816339119580e-01 +1.620000000000000107e+00 1.076088606378924714e-14 4.893014710945852010e-09 2.791541030034892261e-05 1.199402872609029759e-02 3.130146861450396889e-01 1.185580784828221823e-01 +1.630000000000000115e+00 7.373320916393947496e-15 3.509629230139760943e-09 2.137468452168460057e-05 1.006258122764231919e-02 2.985308185410810999e-01 1.283126606102086287e-01 +1.640000000000000124e+00 5.039707156606339832e-15 2.515625287883645411e-09 1.633618184121517184e-05 8.415663457889582463e-03 2.837186728516735146e-01 1.381208562261167228e-01 +1.650000000000000133e+00 3.456878447189422070e-15 1.801967357392948972e-09 1.246294195874585876e-05 7.016650915740045569e-03 2.686963265375161281e-01 1.479043941736273293e-01 +1.660000000000000142e+00 2.361140339769211921e-15 1.289971153793733803e-09 9.491465192571795135e-06 5.832591937290134212e-03 2.535781063595277107e-01 1.575849365697442606e-01 +1.669999999999999929e+00 1.617506550254850546e-15 9.229126500328865171e-10 7.216250793272702641e-06 4.834047646692864360e-03 2.384732650603055559e-01 1.670855769923690337e-01 +1.679999999999999938e+00 1.120818448326204615e-15 6.599389111110727231e-10 5.477446552559663205e-06 3.994891513508192112e-03 2.234847754947852649e-01 1.763322585837637235e-01 +1.689999999999999947e+00 7.718317718684845388e-16 4.716545757365531850e-10 4.151020319190942675e-06 3.292081707036350561e-03 2.087082698534345193e-01 1.852550718252251039e-01 +1.699999999999999956e+00 5.205557371795253563e-16 3.369269462820051816e-10 3.140966303118730650e-06 2.705417442489946919e-03 1.942311467503382849e-01 1.937893985018218634e-01 +1.709999999999999964e+00 3.625923350094396720e-16 2.405762177237260559e-10 2.373145988632483469e-06 2.217288579870357445e-03 1.801318630912998042e-01 2.018768760801259443e-01 +1.719999999999999973e+00 2.516385783948776405e-16 1.717071357389781900e-10 1.790436743033715327e-06 1.812425923627207806e-03 1.664794211831775173e-01 2.094661649151489391e-01 +1.729999999999999982e+00 1.649016793794969713e-16 1.225055812872878406e-10 1.348922463496005526e-06 1.477658020001481991e-03 1.533330548865708964e-01 2.165135089515941580e-01 +1.739999999999999991e+00 1.215454052057018530e-16 8.737113186617575940e-11 1.014911453478866216e-06 1.201678783392800143e-03 1.407421121164291467e-01 2.229830884966831628e-01 +1.750000000000000000e+00 7.961828697422301127e-17 6.229269087776454313e-11 7.626086451560616175e-07 9.748290136706607139e-04 1.287461249800961882e-01 2.288471708842831631e-01 +1.760000000000000009e+00 5.866150746364105615e-17 4.439923292318511293e-11 5.723036423002075206e-07 7.888937929441010430e-04 1.173750535697218461e-01 2.340860711616196688e-01 +1.770000000000000018e+00 3.841093788301158207e-17 3.163695772718413247e-11 4.289640513595782200e-07 6.369168648361575071e-04 1.066496850861715084e-01 2.386879401270684264e-01 +1.780000000000000027e+00 1.885957619321147712e-17 2.253753302325919247e-11 3.211462555028557282e-07 5.130323880938954164e-04 9.658216668239595626e-02 2.426484010251678258e-01 +1.790000000000000036e+00 1.851625859457894989e-17 1.605170754312330251e-11 2.401540797277149262e-07 4.123139021731565281e-04 8.717664822342960362e-02 2.459700589316318187e-01 +1.800000000000000044e+00 1.817561311184611855e-17 1.143011599325978046e-11 1.793904769781704556e-07 3.306399261469550560e-04 7.843001005207206333e-02 2.486619083708357936e-01 +1.810000000000000053e+00 0.000000000000000000e+00 8.137769864236968103e-12 1.338590958179008190e-07 2.645753142235484604e-04 7.033265075436209712e-02 2.507386650894823377e-01 +1.820000000000000062e+00 0.000000000000000000e+00 5.792888185064208087e-12 9.978192334122564621e-08 2.112672920838356422e-04 6.286931072545985655e-02 2.522200472933634474e-01 +1.830000000000000071e+00 0.000000000000000000e+00 4.123163504158291035e-12 7.430659412046685338e-08 1.683549800624304976e-04 5.601990890292645603e-02 2.531300301985314749e-01 +1.840000000000000080e+00 0.000000000000000000e+00 2.934404509437793404e-12 5.528279528550251697e-08 1.338911554219225338e-04 4.976037220137410205e-02 2.534960956291138490e-01 +1.850000000000000089e+00 0.000000000000000000e+00 2.088196987818810075e-12 4.109179192465435618e-08 1.062750019901083088e-04 4.406343978443494347e-02 2.533484957914069469e-01 +1.860000000000000098e+00 0.000000000000000000e+00 1.485930496827888161e-12 3.051669906914862113e-08 8.419462861343407956e-05 3.889942718470752042e-02 2.527195474423744037e-01 +1.870000000000000107e+00 0.000000000000000000e+00 1.057331050114870163e-12 2.264394104089137764e-08 6.657819755548245592e-05 3.423693827886707675e-02 2.516429696116462833e-01 +1.880000000000000115e+00 0.000000000000000000e+00 7.523348095324758579e-13 1.678853196886138342e-08 5.255258132893701560e-05 3.004351610978756720e-02 2.501532749718995974e-01 +1.890000000000000124e+00 0.000000000000000000e+00 5.353109911981471400e-13 1.243751461026292355e-08 4.140855440294176831e-05 2.628622640779162967e-02 2.482852220025697776e-01 +1.900000000000000133e+00 0.000000000000000000e+00 3.809073234244706527e-13 9.207214654667112586e-09 3.257161919934678870e-05 2.293217030971014814e-02 2.460733323509897985e-01 +1.910000000000000142e+00 0.000000000000000000e+00 2.710392715305241659e-13 6.810986302454714229e-09 2.557765954269661335e-05 1.994892514159541178e-02 2.435514753331225191e-01 +1.919999999999999929e+00 0.000000000000000000e+00 1.928876693263395471e-13 5.034911002652640679e-09 2.005270613143748552e-05 1.730491417568628953e-02 2.407525193791170692e-01 +1.929999999999999938e+00 0.000000000000000000e+00 1.372712034894855376e-13 3.719515090093346339e-09 1.569618546753373967e-05 1.496970797228164285e-02 2.377080484413994221e-01 +1.939999999999999947e+00 0.000000000000000000e+00 9.770355550497070407e-14 2.746034365719055889e-09 1.226710458016934130e-05 1.291426126829763044e-02 2.344481399505382035e-01 +1.949999999999999956e+00 0.000000000000000000e+00 6.954095794421101587e-14 2.026109074851652458e-09 9.572697970727701990e-06 1.111109038716246757e-02 2.310011998168181035e-01 +1.959999999999999964e+00 0.000000000000000000e+00 4.950628795425162859e-14 1.494062259318707004e-09 7.459130114271636743e-06 9.534396841737206515e-03 2.273938492112568543e-01 +1.969999999999999973e+00 0.000000000000000000e+00 3.524659622859073820e-14 1.101121192966155883e-09 5.803906544438261252e-06 8.160143214006465237e-03 2.236508573878721873e-01 +1.979999999999999982e+00 0.000000000000000000e+00 2.510052338465078198e-14 8.110980483030642230e-10 4.509699239154103763e-06 6.966087558734339324e-03 2.197951145927241889e-01 +1.989999999999999991e+00 0.000000000000000000e+00 1.787618953549825793e-14 5.971650809132615970e-10 3.499338098546060994e-06 5.931782532330697778e-03 2.158476391050443144e-01 +2.000000000000000000e+00 0.000000000000000000e+00 1.273688180077510876e-14 4.394495211068296458e-10 2.711760254268619611e-06 5.038545232619322418e-03 2.118276126313043406e-01 +2.010000000000000231e+00 0.000000000000000000e+00 9.074433345977802960e-15 3.232418849680934038e-10 2.098743320960148623e-06 4.269403388610665089e-03 2.077524385852534128e-01 +2.020000000000000018e+00 0.000000000000000000e+00 6.465585659239437314e-15 2.376624233325534827e-10 1.622278067577374789e-06 3.609023097719445215e-03 2.036378181992327119e-01 +2.030000000000000249e+00 0.000000000000000000e+00 4.609785513617459268e-15 1.746697314412375089e-10 1.252460916077393156e-06 3.043622803520505942e-03 1.994978398914793449e-01 +2.040000000000000036e+00 0.000000000000000000e+00 3.284826272623138873e-15 1.283242761155044399e-10 9.658077117091639874e-07 2.560877668325015153e-03 1.953450778319217873e-01 +2.049999999999999822e+00 0.000000000000000000e+00 2.339705888585772235e-15 9.424179696584112349e-11 7.439078636058203301e-07 2.149817945345193666e-03 1.911906961808303718e-01 +2.060000000000000053e+00 0.000000000000000000e+00 1.666805060701360910e-15 6.918805073074525467e-11 5.723526929591603475e-07 1.800724414306093413e-03 1.870445560008906216e-01 +2.069999999999999840e+00 0.000000000000000000e+00 1.187539574586914804e-15 5.077857796472893261e-11 4.398840709930952418e-07 1.505023427061821968e-03 1.829153223483407364e-01 +2.080000000000000071e+00 0.000000000000000000e+00 8.496324603286173785e-16 3.725638338540987369e-11 3.377195572034839594e-07 1.255183627141797037e-03 1.788105695213538238e-01 +2.089999999999999858e+00 0.000000000000000000e+00 6.049865602427579646e-16 2.732752590375000800e-11 2.590185875063445521e-07 1.044615966719344155e-03 1.747368828759742199e-01 +2.100000000000000089e+00 0.000000000000000000e+00 4.381004284893701990e-16 2.003953265642662089e-11 1.984611026675992799e-07 8.675782507414362202e-04 1.706999560068622190e-01 +2.109999999999999876e+00 0.000000000000000000e+00 3.066985189116692570e-16 1.469167691858605536e-11 1.519155959001551035e-07 7.190850928289851170e-04 1.667046824296924135e-01 +2.120000000000000107e+00 0.000000000000000000e+00 2.176261587658125932e-16 1.076859572356316944e-11 1.161781079219726925e-07 5.948238709419178356e-04 1.627552411941456667e-01 +2.129999999999999893e+00 0.000000000000000000e+00 1.592189798993372198e-16 7.891476446737450005e-12 8.876738851909710868e-08 4.910770210104920940e-04 1.588551761025905895e-01 +2.140000000000000124e+00 0.000000000000000000e+00 1.121524223927143901e-16 5.781992255128146365e-12 6.776442766957509553e-08 4.046508009077945086e-04 1.550074684124292113e-01 +2.149999999999999911e+00 0.000000000000000000e+00 8.412574261988304456e-17 4.235680665465538987e-12 5.168696472056352329e-08 3.328104916289441047e-04 1.512146030632391669e-01 +2.160000000000000142e+00 0.000000000000000000e+00 5.741314252104399250e-17 3.102450386164660077e-12 3.939151671961303739e-08 2.732218732214534030e-04 1.474786285972695399e-01 +2.169999999999999929e+00 0.000000000000000000e+00 3.997500656956318079e-17 2.272099423400832132e-12 2.999701609687260286e-08 2.238987155308227062e-04 1.438012110378352193e-01 +2.180000000000000160e+00 0.000000000000000000e+00 3.116770415974760792e-17 1.663786591529606892e-12 2.282538529447591215e-08 1.831559538207494378e-04 1.401836820589633104e-01 +2.189999999999999947e+00 0.000000000000000000e+00 2.277789079928866168e-17 1.218207155727556169e-12 1.735536244938146358e-08 1.495681725784303486e-04 1.366270818254572417e-01 +2.200000000000000178e+00 0.000000000000000000e+00 1.479418177854265644e-17 8.918894397921118508e-13 1.318667633675012942e-08 1.219329933526162521e-04 1.331321969092713331e-01 +2.209999999999999964e+00 0.000000000000000000e+00 1.441056636940837695e-17 6.529283516315241764e-13 1.001229077707459566e-08 9.923895066120309329e-05 1.296995936993258214e-01 +2.220000000000000195e+00 0.000000000000000000e+00 7.017180878524692740e-18 4.779682583598309175e-13 7.596930531637991973e-09 8.063744058267207813e-05 1.263296477208695201e-01 +2.229999999999999982e+00 0.000000000000000000e+00 6.832756214583432278e-18 3.498712819677446630e-13 5.760489451858984676e-09 6.541833674581619830e-05 1.230225692700604029e-01 +2.240000000000000213e+00 0.000000000000000000e+00 6.651980022536628490e-18 2.560945788876376436e-13 4.365227426665486635e-09 5.298888558314323672e-05 1.197784257520210921e-01 +2.250000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.874590403134975516e-13 3.305903318390574361e-09 4.285551482304234656e-05 1.165971610883288351e-01 +2.260000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.372097122751739994e-13 2.502179923832708894e-09 3.460821452940601739e-05 1.134786125344207919e-01 +2.270000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.004356911031548712e-13 1.892784486300859568e-09 2.790717714340951842e-05 1.104225252201533475e-01 +2.280000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.351530704690434846e-14 1.431024237201164453e-09 2.247141081932049278e-05 1.074285646988426962e-01 +2.290000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.381670415028282227e-14 1.081346184203956386e-09 1.806906800624292621e-05 1.044963277624186559e-01 +2.300000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.939346156416903352e-14 8.167008950539859823e-10 1.450925806032794156e-05 1.016253517534581041e-01 +2.310000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.883983432968017991e-14 6.165243236779424460e-10 1.163513820918934384e-05 9.881512257932514864e-02 +2.319999999999999840e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.111414100763760632e-14 4.651944504886763807e-10 9.318101116486660250e-06 9.606508160971728716e-02 +2.330000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.545793365940586551e-14 3.508520326818904922e-10 7.452899416447807511e-06 9.337463161680942259e-02 +2.339999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.131945748820939697e-14 2.645005037302947090e-10 5.953567809983981724e-06 9.074314189698128030e-02 +2.350000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.291586680549370729e-15 1.993194796579811860e-10 4.750021615578641666e-06 8.816995269483543507e-02 +2.359999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.072113698125966354e-15 1.501416795207547126e-10 3.785227085942417415e-06 8.565437903382781826e-02 +2.370000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.447574203944711422e-15 1.130546621776036549e-10 3.012853412633119830e-06 8.319571404323970742e-02 +2.379999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.256152314445057105e-15 8.509784987104835391e-11 2.395329251639868255e-06 8.079323185835433907e-02 +2.390000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.386399670585543391e-15 6.403222869540918126e-11 1.902237935776897606e-06 7.844619015938820994e-02 +2.399999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.750172859952802725e-15 4.816544195615067664e-11 1.508995428454159384e-06 7.615383240488161898e-02 +2.410000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.280196765776664886e-15 3.621897812579862607e-11 1.195763653279049312e-06 7.391538980669956627e-02 +2.419999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.397218005304305507e-16 2.722745564797629526e-11 9.465592401268478653e-07 7.173008308642601916e-02 +2.430000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.876955294791007758e-16 2.046231480086412083e-11 7.485240902358236558e-07 6.959712404660257923e-02 +2.439999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.059675803069522445e-16 1.537395235649882171e-11 5.913296031096073117e-07 6.751571698485774042e-02 +2.450000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.707880514319666554e-16 1.154797211605313668e-11 4.666910401398046071e-07 6.548505997436984694e-02 +2.459999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.714419233202488701e-16 8.672040667114443861e-12 3.679724282153279499e-07 6.350434603021394564e-02 +2.470000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.982434846783914486e-16 6.510859497425683577e-12 2.898657250745560432e-07 6.157276417784886269e-02 +2.479999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.457916041895565560e-16 4.887226155642315448e-12 2.281307618698030984e-07 5.968950043724473298e-02 +2.490000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.059807407408892020e-16 3.667749360184711401e-12 1.793848216311360020e-07 5.785373873382781973e-02 +2.500000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.780554350927864103e-17 2.752041206136252714e-12 1.409326717600859481e-07 5.606466174549552123e-02 +2.510000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.729359676538773614e-17 2.064592852155514961e-12 1.106295018367351923e-07 5.432145169334207102e-02 +2.520000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.381776429573893385e-17 1.548610346924291867e-12 8.677057519319773001e-08 5.262329108240897807e-02 +2.530000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.112308996161187046e-17 1.161411859982025926e-12 6.800252739898963702e-08 5.096936339766884594e-02 +2.540000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.191975801428646355e-17 8.709021255748708052e-13 5.325217421360005887e-08 4.935885375955084697e-02 +2.550000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.591759764419218979e-17 6.529743434929926783e-13 4.166945754729868309e-08 4.779094954256773997e-02 +2.560000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.284108195950185917e-17 4.895225900273460528e-13 3.258178784067368374e-08 4.626484095999987972e-02 +2.569999999999999840e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.943101247914857710e-18 3.669451800036738461e-13 2.545755778150753704e-08 4.477972161709173338e-02 +2.580000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.216695393294470206e-18 2.750378836989766458e-13 1.987702485011119497e-08 4.333478903481657180e-02 +2.589999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.655083058496595875e-18 2.061317329132877640e-13 1.550910544222397100e-08 4.192924514593637797e-02 +2.600000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.503320922597199382e-18 1.544774176078517278e-13 1.209290411957089347e-08 4.056229676481840768e-02 +2.609999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.177878180876129440e-18 1.157607589481089058e-13 9.423030381865093257e-09 3.923315603226213560e-02 +2.620000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.106151263083867790e-18 8.674183977010909617e-14 7.337940480433001405e-09 3.794104083641439834e-02 +2.629999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.036436626078160077e-18 6.499491135791056170e-14 5.710692101569937623e-09 3.668517521071983090e-02 +2.640000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.968691551524334159e-18 4.869755421850592770e-14 4.441620975357477385e-09 3.546478970974006883e-02 +2.649999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.648760327325065136e-14 3.452546424430209179e-09 3.427912176359525914e-02 +2.660000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.733769912132806097e-14 2.682191632828153098e-09 3.312741601170805794e-02 +2.669999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.048177727754681947e-14 2.082567857831818741e-09 3.200892461648179033e-02 +2.680000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.534610261157525913e-14 1.616122635238265706e-09 3.092290755749948356e-02 +2.689999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.149686508592240903e-14 1.253492815999396109e-09 2.986863290680086616e-02 +2.700000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.614671972962280436e-15 9.717358808334643381e-10 2.884537708576626752e-02 +2.709999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.455051934820109232e-15 7.529390734337769394e-10 2.785242510412086481e-02 +2.720000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.836164314657621046e-15 5.831266290921146968e-10 2.688907078155316696e-02 +2.729999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.623538290012886062e-15 4.514019957649875984e-10 2.595461695243495015e-02 +2.740000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.715576559987804673e-15 3.492751169944023655e-10 2.504837565411618949e-02 +2.750000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.035337854910090020e-15 2.701352699961908659e-10 2.416966829926584653e-02 +2.760000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.524802455241534417e-15 2.088383228052072319e-10 2.331782583272005646e-02 +2.770000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.143687653236109857e-15 1.613838394573600148e-10 2.249218887329790598e-02 +2.780000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.573575666959108612e-16 1.246626669212186277e-10 2.169210784103860895e-02 +2.790000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.421013482646241153e-16 9.625976207138332541e-11 2.091694307031213840e-02 +2.800000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.813598377077672525e-16 7.430029327161647599e-11 2.016606490925106770e-02 +2.810000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.614895074498864502e-16 5.732960078490365620e-11 1.943885380594947629e-02 +2.819999999999999840e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.703960450424941502e-16 4.421963321993972999e-11 1.873470038186982739e-02 +2.830000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.034350533574988660e-16 3.409606368852256177e-11 1.805300549289740389e-02 +2.839999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.525514430418198638e-16 2.628156646930657304e-11 1.739318027847704812e-02 +2.850000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.143985731144815808e-16 2.025167991601323594e-11 1.675464619926350998e-02 +2.859999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.599433207823943862e-17 1.560049693150399015e-11 1.613683506371332496e-02 +2.870000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.469487052763263171e-17 1.201401342702921619e-11 1.553918904404079197e-02 +2.879999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.816965271603458441e-17 9.249424595513707806e-12 1.496116068195739099e-02 +2.890000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.597675380538273801e-17 7.119062454373942420e-12 1.440221288460728083e-02 +2.899999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.693199341766289624e-17 5.477933603789479502e-12 1.386181891110846963e-02 +2.910000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.073368953863476848e-17 4.214062419125637238e-12 1.333946235010178769e-02 +2.919999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.496177509279630431e-17 3.241006183757403507e-12 1.283463708870537928e-02 +2.930000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.165159100801475770e-17 2.492053936385204407e-12 1.234684727326527259e-02 +2.939999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.569972200480942165e-18 1.915746088725387656e-12 1.187560726228777640e-02 +2.950000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.339618593089206769e-18 1.472398606910043953e-12 1.142044157193064340e-02 +2.959999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.876492461195293449e-18 1.131420007945783711e-12 1.098088481442565344e-02 +2.970000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.516014689619136218e-18 8.692350115921256453e-13 1.055648162979604988e-02 +2.979999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.816299030620873925e-18 6.676808518021171944e-13 1.014678661122676330e-02 +2.990000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.165237816808915395e-18 5.127705371577789142e-13 9.751364224436365538e-03 +3.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.560383276220273058e-18 3.937346328552139812e-13 9.369788721393964084e-03 +3.010000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.993832482698504114e-19 3.022829514125574972e-13 9.001644048714544169e-03 +3.020000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.599569751652944662e-19 2.320364802305667169e-13 8.646523751060220669e-03 +3.030000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.609663134560237002e-19 1.780888036079732782e-13 8.304030869865508188e-03 +3.040000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.426335874404852505e-19 1.366648906565995881e-13 7.973777837697875945e-03 +3.050000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.249593104444197535e-19 1.048625344859545797e-13 7.655386368555583756e-03 +3.060000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.079229923003092215e-19 8.045057254146697992e-14 7.348487344397670873e-03 +3.070000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.915046452725743785e-19 6.171483475754226614e-14 7.052720698191821368e-03 +3.080000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.733740900516633485e-14 6.767735293758424253e-03 +3.089999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.630576073963015200e-14 6.493188802680031381e-03 +3.100000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.784203097343751499e-14 6.228747578537834582e-03 +3.109999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.134990610049608915e-14 5.974086528728041363e-03 +3.120000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.636993654283359404e-14 5.728888984102836236e-03 +3.129999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.255087980089360287e-14 5.492846566672869232e-03 +3.140000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.622023444262508414e-15 5.265659055598958388e-03 +3.149999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.376228393573661235e-15 5.047034251693932778e-03 +3.160000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.654401720407803443e-15 4.836687840646136140e-03 +3.169999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.334020965441248550e-15 4.634343255169198240e-03 +3.180000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.321961671158865602e-15 4.439731536273890458e-03 +3.189999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.546210387916036112e-15 4.252591193850799138e-03 +3.200000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.951509551298544049e-15 4.072668066744149912e-03 +3.209999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.495644285966709991e-15 3.899715182490029811e-03 +3.220000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.146302367693501740e-15 3.733492616884314611e-03 +3.229999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.784440864424856951e-16 3.573767353538592328e-03 +3.240000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.732631345273242546e-16 3.420313143574678534e-03 +3.250000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.159797832504702935e-16 3.272910365601634439e-03 +3.260000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.954509992190602088e-16 3.131345886111691623e-03 +3.270000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.031262881778339936e-16 2.995412920425136964e-03 +3.280000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.321903728002282965e-16 2.864910894306957508e-03 +3.290000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.779346119587744121e-16 2.739645306371957505e-03 +3.300000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.363921798126602872e-16 2.619427591388118291e-03 +3.310000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.045310720429986645e-16 2.504074984582268810e-03 +3.320000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.011516247010664999e-17 2.393410387045344454e-03 +3.330000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.145278288723690110e-17 2.287262232329181477e-03 +3.339999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.707124520588693767e-17 2.185464354320525420e-03 +3.350000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.604467676348906764e-17 2.087855856472461535e-03 +3.359999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.767620830385188103e-17 1.994280982468007741e-03 +3.370000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.125091140089592956e-17 1.904588988385136784e-03 +3.379999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.625366933515682138e-17 1.818634016427572372e-03 +3.390000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.243335829660339818e-17 1.736274970280319739e-03 +3.399999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.568292868192838719e-18 1.657375392144508049e-03 +3.410000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.288041134180035406e-18 1.581803341500953282e-03 +3.419999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.613947943652692736e-18 1.509431275647791799e-03 +3.430000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.316954589961256297e-18 1.440135932052709908e-03 +3.439999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.279228174557127406e-18 1.373798212556529962e-03 +3.450000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.545775114670173031e-18 1.310303069460456776e-03 +3.459999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.942174366820658675e-18 1.249539393525743490e-03 +3.470000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.521127545592068874e-18 1.191399903910495819e-03 +3.479999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.134873235458113793e-18 1.135781040064974545e-03 +3.490000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.014313092741668182e-19 1.082582855603159621e-03 +3.500000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.872561947392753250e-19 1.031708914165122178e-03 +3.510000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.911402221423096914e-19 9.830661872815783710e-04 +3.520000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.159183905177672920e-19 9.365649542489919719e-04 +3.530000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.971352177835109493e-19 8.921187040207091473e-04 +3.540000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.358235937556609512e-19 8.496440391169327684e-04 +3.550000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.796475372080792118e-19 8.090605815536522491e-04 +3.560000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.282792007437318676e-19 7.702908807882861367e-04 +3.570000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.221123741823847783e-19 7.332603236773752768e-04 +3.580000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.748213699925597550e-20 6.978970464395008481e-04 +3.589999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.373344700157104883e-20 6.641318486144732764e-04 +3.600000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.507739151206760329e-20 6.318981090079212742e-04 +3.609999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.336951293620867474e-20 6.011317036084937084e-04 +3.620000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.173966234975691376e-20 5.717709254631863193e-04 +3.629999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.018454416309579469e-20 5.437564064946738074e-04 +3.640000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.870098838729405082e-20 5.170310412429644916e-04 +3.649999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.915399125123312519e-04 +3.660000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.672302189030774246e-04 +3.669999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.440512042065474993e-04 +3.680000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.219540886405891643e-04 +3.689999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.008920019016896041e-04 +3.700000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.808199180090044399e-04 +3.709999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.616945919146784282e-04 +3.720000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.434744978540011599e-04 +3.729999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.261197694083200821e-04 +3.740000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.095921412528926560e-04 +3.750000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.938548925614003549e-04 +3.760000000000000231e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.788727920382623915e-04 +3.770000000000000018e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.646120445495396285e-04 +3.780000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.510402393227918988e-04 +3.790000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.381262996860046804e-04 +3.800000000000000266e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.258404343154230280e-04 +3.810000000000000053e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.141540899619723433e-04 +3.820000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.030399056257952133e-04 +3.830000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.924716681483835924e-04 +3.839999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.824242691917173792e-04 +3.850000000000000089e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.728736635738833786e-04 +3.859999999999999876e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.637968289306732496e-04 +3.870000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.551717266727870568e-04 +3.879999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.469772642084021851e-04 +3.890000000000000124e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.391932584010457258e-04 +3.899999999999999911e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.318004002329337956e-04 +3.910000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.247802206441653273e-04 +3.919999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.181150575184606466e-04 +3.930000000000000160e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.117880237863992903e-04 +3.939999999999999947e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.057829766174721661e-04 +3.950000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000844876725719612e-04 +3.959999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.467781438893157803e-05 +3.970000000000000195e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.954887226988028496e-05 +3.979999999999999982e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.468420815218909300e-05 +3.990000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.007097442415881782e-05 +4.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.569690416802829188e-05 +4.009999999999999787e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.155028720067656181e-05 +4.020000000000000462e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.761994698701451201e-05 +4.030000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.389521840087908475e-05 +4.040000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.036592630864834542e-05 +4.049999999999999822e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.702236495122367109e-05 +4.060000000000000497e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.385527810042758625e-05 +4.070000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.085583996627522527e-05 +4.080000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.801563683198268288e-05 +4.089999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.532664939397855914e-05 +4.099999999999999645e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.278123578457674117e-05 +4.110000000000000320e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.037211525536551257e-05 +4.120000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.809235249975894268e-05 +4.129999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.593534259354158979e-05 +4.139999999999999680e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.389479653263693828e-05 +4.150000000000000355e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.196472734770647897e-05 +4.160000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.013943677559264344e-05 +4.169999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.841350246800420975e-05 +4.179999999999999716e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.678176571825482426e-05 +4.190000000000000391e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.523931968726843476e-05 +4.200000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.378149811048649358e-05 +4.209999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.240386446773675930e-05 +4.219999999999999751e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.110220159856760464e-05 +4.230000000000000426e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.987250174599398784e-05 +4.240000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.871095701206949778e-05 +4.250000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.761395020916761803e-05 +4.259999999999999787e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.657804609134507862e-05 +4.270000000000000462e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.559998295065271757e-05 +4.280000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.467666456376918968e-05 +4.290000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.380515247484436792e-05 +4.299999999999999822e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.298265860096709476e-05 +4.310000000000000497e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.220653814719408694e-05 +4.320000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.147428281861089934e-05 +4.330000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.078351431742450022e-05 +4.339999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.013197811361725266e-05 +4.350000000000000533e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.517537478213390192e-06 +4.360000000000000320e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.938167768725686373e-06 +4.370000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.391950956853344026e-06 +4.379999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.877070388993910244e-06 +4.389999999999999680e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.391805770604876890e-06 +4.400000000000000355e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.934528365906822180e-06 +4.410000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.503696404854364192e-06 +4.419999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.097850689711229687e-06 +4.429999999999999716e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.715610393953746256e-06 +4.440000000000000391e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.355669046585743586e-06 +4.450000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.016790695282744467e-06 +4.459999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.697806242087593931e-06 +4.469999999999999751e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.397609945659861551e-06 +4.480000000000000426e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.115156084330508385e-06 +4.490000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.849455774438885070e-06 +4.500000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.599573938626780587e-06 +4.509999999999999787e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.364626418939012099e-06 +4.520000000000000462e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.143777229731475377e-06 +4.530000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.936235945517863021e-06 +4.540000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.741255218998309102e-06 +4.549999999999999822e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.558128424608961101e-06 +4.560000000000000497e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.386187423011975700e-06 +4.570000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.224800442015688300e-06 +4.580000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.073370069474093740e-06 +4.589999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.931331353769111588e-06 +4.600000000000000533e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.798150007526828464e-06 +4.610000000000000320e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.673320710266274216e-06 +4.620000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.556365505724561042e-06 +4.629999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.446832289650692389e-06 +4.639999999999999680e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.344293383910860287e-06 +4.650000000000000355e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.248344192803720109e-06 +4.660000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.158601937545942537e-06 +4.669999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.074704464956369815e-06 +4.679999999999999716e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.963091264432861814e-07 +4.690000000000000391e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.230917234831150279e-07 +4.700000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.547455158709785526e-07 +4.709999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.909802891238337773e-07 +4.719999999999999751e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.315214775254283935e-07 +4.730000000000000426e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.761093394180026812e-07 +4.740000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.244981814688013757e-07 +4.750000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.764556287684500852e-07 +4.759999999999999787e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.317619377533198992e-07 +4.770000000000000462e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.902093490828477776e-07 +4.780000000000000249e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.516014777458562430e-07 +4.790000000000000036e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.157527378149979787e-07 +4.799999999999999822e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.824877994152667324e-07 +4.810000000000000497e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.516410756195147884e-07 +4.820000000000000284e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.230562371303328443e-07 +4.830000000000000071e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.965857527523306205e-07 +4.839999999999999858e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.720904538010278337e-07 +4.850000000000000533e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.494391207330565905e-07 +4.860000000000000320e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.285080904167570243e-07 +4.870000000000000107e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.091808825914865425e-07 +4.879999999999999893e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.913478441877041063e-07 +4.889999999999999680e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.749058102973752587e-07 +4.900000000000000355e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597577806952962489e-07 +4.910000000000000142e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.458126109160604397e-07 +4.919999999999999929e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.329847169884304582e-07 +4.929999999999999716e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.211937930188092765e-07 +4.940000000000000391e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.103645408980406442e-07 +4.950000000000000178e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.004264114812765874e-07 +4.959999999999999964e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.131335665895433652e-08 +4.969999999999999751e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.296359179845285449e-08 +4.980000000000000426e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.531936809080894562e-08 +4.990000000000000213e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.832675438539998569e-08 +5.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.193542813799492433e-08 diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index abeb3165..60d680c5 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -96,6 +96,7 @@ def __init__(self): mask_path="config/glass_mock/test_data/mask.fits", nz_path="config/glass_mock/test_data/redshift_distribution.txt", output_path="config/glass_mock/test_data", + output_prefix="test", ) else: # Load the configuration from the config file in the parser @@ -119,6 +120,7 @@ def __init__(self): # Output path self.path = self.config.output_path + self.prefix = self.config.output_prefix print("-" * 66) print( @@ -150,48 +152,95 @@ def __init__(self): print("-" * 66) self.z = None - self.bin_nz = None + self.bin_nz = [] + self.ngal_per_bin = [] self.camb_cls = None - def galaxies_simulation(self): - """Create a source catalogue from the GLASS mock maps.""" - config = self.config - - # Read + downgrade the survey mask. - unions_mask = hp.read_map(self.path_mask) - mask_file_name = f"{self.path}/mask_nside{config.nside}.fits" + def read_mask(self): + """Read the survey mask from the specified path.""" + mask = hp.read_map(self.path_mask) + mask_file_name = f"{self.path}/mask_nside_{self.config.nside}.fits" if not os.path.isfile(mask_file_name): os.makedirs(self.path, exist_ok=True) - unions_mask = downgrade_mask(unions_mask, config.nside) - hp.write_map(mask_file_name, unions_mask, overwrite=True) + mask = downgrade_mask(mask, self.config.nside) + hp.write_map(mask_file_name, mask, overwrite=True) else: - unions_mask = hp.read_map(mask_file_name).astype(np.float64) + mask = hp.read_map(mask_file_name).astype(np.float64) + return mask - # Shells + matter spectra from the library core (cached cls on disk). - shells = build_shells(config, self.pars) - cls_path = f"{self.root}/cls_{config.nside}.npy" + def get_cl_matter_shells(self, shells): + """Get the matter power spectrum for the simulation.""" + cls_path = f"{self.root}/cls_{self.config.nside}_limber_{self.limber}.npy" try: cls = np.load(cls_path) except FileNotFoundError: print(f"[!] cls file {cls_path} missing; computing matter cls ...") - cls = matter_shell_cls(config, self.pars, shells, limber=self.limber) + cls = matter_shell_cls(self.config, self.pars, shells, limber=self.limber) np.save(cls_path, cls) print("[!] Done.") + return cls + + def read_redshift_distributions(self, shells): + """Read the redshift distributions from the specified path. + The convention used here is that the non-tomographic redshift distribution sums to one. + All the per bin distributions will sum to a lower value than 1. + """ + nz = np.loadtxt(self.path_nz) + nz = nz[nz[:, 0] <= self.config.zmax] + self.z = nz[:, 0] + + # Check that the number of nz columns corresponds to what is expected. + dndz_cols = nz[:, 1:] + assert self.config.nbins == dndz_cols.shape[1], ( + f"Number of bins ({self.config.nbins}) does not match n(z) columns ({dndz_cols.shape[1]})" + ) + + # Check that the per bin integrals sum together to one + per_bin_integral = np.trapezoid(dndz_cols, self.z.T, axis=0) + assert np.isclose(per_bin_integral.sum(), 1.0, atol=1e-2), ( + f"Per bin integrals do not sum to one: {per_bin_integral.sum()}. Integrals per bin: {per_bin_integral}" + ) + + for b in range(self.config.nbins): + dndz_b = ( + dndz_cols[:, b] * self.config.n_arcmin2 + ) # TODO: Implement different densities per tomo bin + ngal_b = glass.partition(self.z, dndz_b, shells) + self.ngal_per_bin.append(ngal_b) + self.bin_nz.append(dndz_b) + + def galaxies_simulation(self): + """Create a source catalogue from the GLASS mock maps.""" + config = self.config + + # Read + downgrade the survey mask. + mask = self.read_mask() + + # Shells + matter spectra from the library core (cached cls on disk). + shells = build_shells(config, self.pars) + + # Generate the file for the matter shells if not done already + cls = self.get_cl_matter_shells(shells) + + # Apply discretisation to the full set of spectra + cls = glass.discretized_cls(cls, nside=config.nside, lmax=config.lmax, ncorr=3) + + # Generate the matter maps fields = glass.lognormal_fields(shells) gls = glass.solve_gaussian_spectra(fields, cls) matter = glass.generate(fields, gls, config.nside, ncorr=3, rng=self.rng) convergence = glass.MultiPlaneConvergence(Cosmology_from_camb(self.pars)) # n(z) and per-shell galaxy partition. - nz = np.loadtxt(self.path_nz) - nz = nz[nz[:, 0] <= config.zmax] - self.z = nz[:, 0] - dndz = nz[:, 1] * config.n_arcmin2 - ngal = glass.partition(self.z, dndz, shells) - zbins = glass.equal_dens_zbins(self.z, dndz, nbins=config.nbins) + self.read_redshift_distributions(shells) + + # Name of the output file + out_file = ( + f"{self.root}/{self.prefix}_glass_sim_{self.n_sim}_{config.nside}.fits" + ) - out_file = f"{self.root}/unions_glass_sim_{self.n_sim}_{config.nside}.fits" + # Open the FITS file to save the catalogue fits = fitsio.FITS(out_file, "rw", clobber=True) fits.write(None) fits.create_table_hdu( @@ -222,41 +271,52 @@ def galaxies_simulation(self): kappa_i = kappa_i + ia_convergence(delta_i, shells[i], config) gamm1_i, gamm2_i = glass.shear_from_convergence(kappa_i) - for gal_lon, gal_lat, gal_count in glass.points.positions_from_delta( - ngal[i], delta_i, config.bias, unions_mask, rng=self.rng - ): - ngal_tot += gal_count - gal_z = glass.redshifts(gal_count, shells[i], rng=self.rng) - gal_phz = glass.gaussian_phz(gal_z, config.phz_sigma_0, rng=self.rng) - tomo_id = np.digitize(gal_phz, np.unique(zbins)) - 1 - gal_ellip = glass.ellipticity_intnorm( - gal_count, config.sigma_e, rng=self.rng, xp=np - ) - gal_she = glass.galaxy_shear( - gal_lon, gal_lat, gal_ellip, kappa_i, gamm1_i, gamm2_i - ) - noise_she = glass.galaxies.galaxy_shear( - gal_lon, - gal_lat, - gal_ellip, - np.zeros(np.shape(kappa_i)), - np.zeros(np.shape(gamm1_i)), - np.zeros(np.shape(gamm2_i)), - ) - - catalogue = np.empty(gal_count, dtype=cat_dtype) - catalogue["RA"] = gal_lon - catalogue["Dec"] = gal_lat - catalogue["e1"] = gal_she.real - catalogue["e2"] = -gal_she.imag - catalogue["w"] = np.ones_like(gal_lon) - catalogue["n1"] = noise_she.real - catalogue["n2"] = noise_she.imag - catalogue["TOM_BIN_ID"] = tomo_id - catalogue["TRUE_Z"] = gal_z - catalogue["PHOTO_Z"] = gal_phz - fits["SOURCE_CATALOGUE"].append(catalogue) - c += 1 + # Sample each tomographic bin against the same matter/convergence + # realisation for this shell + for b in range(config.nbins): + bias_b = config.bias # TODO: implement a per bin bias + + for gal_lon, gal_lat, gal_count in glass.points.positions_from_delta( + self.ngal_per_bin[b][i], delta_i, bias_b, mask, rng=self.rng + ): + ngal_tot += gal_count + gal_z = glass.redshifts(gal_count, shells[i], rng=self.rng) + gal_phz = glass.gaussian_phz( + gal_z, config.phz_sigma_0, rng=self.rng + ) + + gal_ellip = glass.ellipticity_intnorm( + gal_count, + config.sigma_e, + rng=self.rng, + xp=np, # TODO: Implement different shape noise per tomo bin + ) + gal_she = glass.galaxy_shear( + gal_lon, gal_lat, gal_ellip, kappa_i, gamm1_i, gamm2_i + ) + noise_she = glass.galaxies.galaxy_shear( + gal_lon, + gal_lat, + gal_ellip, + np.zeros(np.shape(kappa_i)), + np.zeros(np.shape(gamm1_i)), + np.zeros(np.shape(gamm2_i)), + ) + + catalogue = np.empty(gal_count, dtype=cat_dtype) + catalogue["RA"] = gal_lon + catalogue["Dec"] = gal_lat + catalogue["e1"] = gal_she.real + catalogue["e2"] = -gal_she.imag + catalogue["w"] = np.ones_like(gal_lon) + catalogue["n1"] = noise_she.real + catalogue["n2"] = noise_she.imag + catalogue["TOM_BIN_ID"] = b + catalogue["TRUE_Z"] = gal_z + catalogue["PHOTO_Z"] = gal_phz + fits["SOURCE_CATALOGUE"].append(catalogue) + + c += 1 print("[DONE] \n") print(f"Total number of galaxies sampled: {ngal_tot} using {c} z-shells") @@ -266,6 +326,9 @@ def galaxies_simulation(self): if self.camb: self.get_camb_cls() + # TODO: Add a script to perform some final validation tests that are printed + # TODO: This would essentially include computing shape noise and number density. + def get_camb_cls(self, sav=True): """Lensing C_ell from CAMB source windows for the mock n(z).""" if self.bin_nz is None or self.z is None: diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index ba570f26..6c6f6277 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -86,6 +86,7 @@ class GlassMockConfig: mask_path: str | None = None nz_path: str | None = None output_path: str | None = None + output_prefix: str | None = None @classmethod def from_planck18(cls, **overrides) -> "GlassMockConfig": From 49f4cced56328c37fefd71c4c024ec2daeff5acc Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 1 Jul 2026 16:42:05 +0200 Subject: [PATCH 005/107] Update the installation of glass to the most recent version --- pyproject.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b8f1ed6..593d38f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,10 +96,13 @@ docs = [ # ``comoving_distance``, which no ``cosmology`` release exposes. ``matter_cls`` # lives in the separate ``glass.ext.camb`` package (absent from glass core # >=2024.2), so it is pinned explicitly. +#glass = [ +# "glass==2025.1", +# "glass.ext.camb==2023.6", +# "cosmology==2022.10.9", +#] glass = [ - "glass==2025.1", - "glass.ext.camb==2023.6", - "cosmology==2022.10.9", + "glass[examples]==2026.2" ] develop = ["sp_validation[test,docs]"] From b9e97229fb70db4cba5b0ee33c5dd469aabce3d4 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 1 Jul 2026 17:10:58 +0200 Subject: [PATCH 006/107] Add the possibility to input a list of bias, shape noise, and number density. --- config/glass_mock/config_glass_mock.yaml | 6 ++-- config/glass_mock/config_glass_mock_test.yaml | 4 +-- scripts/glass_mock/make_glass_sim.py | 26 ++++++++++---- src/sp_validation/glass_mock.py | 34 +++++++++++++++++-- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/config/glass_mock/config_glass_mock.yaml b/config/glass_mock/config_glass_mock.yaml index d4b074ea..02edd5c2 100644 --- a/config/glass_mock/config_glass_mock.yaml +++ b/config/glass_mock/config_glass_mock.yaml @@ -26,8 +26,8 @@ dx: 120.0 zmax: 3.0 # --- galaxy population (downstream of map generation) --- -nbins: 1 -n_arcmin2: 6.0905 +nbins: 6 +n_arcmin2: [1, 1.02, 1.05, 0.85, 0.9, 1.2] sigma_e: 0.2684 bias: 1.2 phz_sigma_0: 0.03 @@ -36,6 +36,6 @@ phz_sigma_0: 0.03 # --- Runtime options --- limber: True mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits -nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/redshift_distribution_tomo.txt +nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/test_data/redshift_distribution_tomo.txt output_path: /n09data/guerrini/glass_mock_test/ output_prefix: tomo_test \ No newline at end of file diff --git a/config/glass_mock/config_glass_mock_test.yaml b/config/glass_mock/config_glass_mock_test.yaml index 73e6d98b..bb5c8b3f 100644 --- a/config/glass_mock/config_glass_mock_test.yaml +++ b/config/glass_mock/config_glass_mock_test.yaml @@ -27,7 +27,7 @@ zmax: 3 # --- galaxy population (downstream of map generation) --- nbins: 6 -n_arcmin2: 0.0824 +n_arcmin2: [4.0, 3.8, 4.2, 3.95, 4.05, 4.0] sigma_e: 0.2684 bias: 1.2 phz_sigma_0: 0.03 @@ -36,6 +36,6 @@ phz_sigma_0: 0.03 # --- Runtime options --- limber: True mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits -nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/redshift_distribution_tomo.txt +nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/test_data/redshift_distribution_tomo.txt output_path: /n09data/guerrini/glass_mock_test/ output_prefix: unions \ No newline at end of file diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index 60d680c5..028ece71 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -104,6 +104,9 @@ def __init__(self): yaml_config=yaml_config, seed=args.seed ) + # Check consistency of the config for the tomographic bins + self.config.check_consistency() + # Runtime options self.test = args.test self.camb = args.camb @@ -192,9 +195,6 @@ def read_redshift_distributions(self, shells): # Check that the number of nz columns corresponds to what is expected. dndz_cols = nz[:, 1:] - assert self.config.nbins == dndz_cols.shape[1], ( - f"Number of bins ({self.config.nbins}) does not match n(z) columns ({dndz_cols.shape[1]})" - ) # Check that the per bin integrals sum together to one per_bin_integral = np.trapezoid(dndz_cols, self.z.T, axis=0) @@ -203,8 +203,13 @@ def read_redshift_distributions(self, shells): ) for b in range(self.config.nbins): + n_arcmin2 = ( + self.config.n_arcmin2[b] + if isinstance(self.config.n_arcmin2, list) + else self.config.n_arcmin2 + ) dndz_b = ( - dndz_cols[:, b] * self.config.n_arcmin2 + dndz_cols[:, b] * n_arcmin2 ) # TODO: Implement different densities per tomo bin ngal_b = glass.partition(self.z, dndz_b, shells) self.ngal_per_bin.append(ngal_b) @@ -274,7 +279,14 @@ def galaxies_simulation(self): # Sample each tomographic bin against the same matter/convergence # realisation for this shell for b in range(config.nbins): - bias_b = config.bias # TODO: implement a per bin bias + bias_b = ( + config.bias[b] if isinstance(config.bias, list) else config.bias + ) + sigma_e = ( + config.sigma_e[b] + if isinstance(config.sigma_e, list) + else config.sigma_e + ) for gal_lon, gal_lat, gal_count in glass.points.positions_from_delta( self.ngal_per_bin[b][i], delta_i, bias_b, mask, rng=self.rng @@ -287,9 +299,9 @@ def galaxies_simulation(self): gal_ellip = glass.ellipticity_intnorm( gal_count, - config.sigma_e, + sigma_e, rng=self.rng, - xp=np, # TODO: Implement different shape noise per tomo bin + xp=np, ) gal_she = glass.galaxy_shear( gal_lon, gal_lat, gal_ellip, kappa_i, gamm1_i, gamm2_i diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index 6c6f6277..aa345ff2 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -75,9 +75,9 @@ class GlassMockConfig: As_init: float = 2.1e-9 # seed As before sigma8 rescaling kmax: float = 20.0 # --- galaxy population (downstream of map generation) --- - n_arcmin2: float = 6.0905 - sigma_e: float = 0.2684 - bias: float = 1.2 # constant linear galaxy bias b(z) + n_arcmin2: float | list = 6.0905 + sigma_e: float | list = 0.2684 + bias: float | list = 1.2 # constant linear galaxy bias b(z) phz_sigma_0: float = 0.03 nbins: int = 1 # number of tomographic bins ia_bias: float | None = None @@ -131,6 +131,34 @@ def Oc(self) -> float: """Cold dark matter density (CDM = matter - baryons), pre-neutrino.""" return self.Om - self.Ob + def check_consistency(self): + """Check that the configuration in terms of bins is internally consistent""" + redshift_distributions = np.loadtxt(self.nz_path) + if redshift_distributions.shape[1] - 1 != self.nbins: + raise ValueError( + f"The number of tomographic bins is inconsistent with the redshift distribution: {redshift_distributions.ndim - 1}." + ) + + if isinstance(self.n_arcmin2, list) and len(self.n_arcmin2) != self.nbins: + raise ValueError( + f"Number of elements in n_arcmin2 {len(self.n_arcmin2)} " + f"does not match number of bins {self.nbins}" + ) + + if isinstance(self.sigma_e, list) and len(self.sigma_e) != self.nbins: + raise ValueError( + f"Number of elements in sigma_e {len(self.sigma_e)} " + f"does not match number of bins {self.nbins}" + ) + + if isinstance(self.bias, list) and len(self.bias) != self.nbins: + raise ValueError( + f"Number of elements in bias {len(self.bias)} " + f"does not match number of bins {self.nbins}" + ) + + print("The number of tomographic bins is consistent across inputs...") + def build_camb_params(config: GlassMockConfig): """Build the CAMB parameters for a mock, with ``As`` rescaled to ``sigma8``. From 3d91e1e1c20b0ce64c7f0f55ee41ce55dc4cef3a Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 2 Jul 2026 15:09:40 +0200 Subject: [PATCH 007/107] Add functions to perform a rapid validation of shape noise and number density at runtime. --- scripts/glass_mock/make_glass_sim.py | 39 +++++++++++++++++++--- src/sp_validation/glass_mock.py | 49 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index 028ece71..6af8fc69 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -33,6 +33,8 @@ downgrade_mask, ia_convergence, matter_shell_cls, + validate_number_density, + validate_shape_noise, ) @@ -47,6 +49,9 @@ def get_parser(): parser.add_argument("-N", "--number", help="Mock Number", type=int, default=0) parser.add_argument("-t", "--test", help="Test run", action="store_true") parser.add_argument("-cb", "--camb", help="get Camb C_ell", action="store_true") + parser.add_argument( + "-v", "--validation", help="Run some validation checks", action="store_true" + ) parser.add_argument( "-c", "--config", @@ -110,6 +115,7 @@ def __init__(self): # Runtime options self.test = args.test self.camb = args.camb + self.validation = args.validation self.limber = self.config.limber # Number label and random seed @@ -153,6 +159,10 @@ def __init__(self): print(f"Omega_m: {self.pars.omegam}") print(f"ia_bias: {self.config.ia_bias}") print("-" * 66) + print(f"number density: {self.config.n_arcmin2}") + print(f"shape noise: {self.config.sigma_e}") + print(f"galaxy bias: {self.config.bias}") + print("-" * 66) self.z = None self.bin_nz = [] @@ -209,8 +219,12 @@ def read_redshift_distributions(self, shells): else self.config.n_arcmin2 ) dndz_b = ( - dndz_cols[:, b] * n_arcmin2 - ) # TODO: Implement different densities per tomo bin + dndz_cols[:, b] + * n_arcmin2 + / per_bin_integral[ + b + ] # Normalize the per bin redshift distribution to have the correct number density + ) ngal_b = glass.partition(self.z, dndz_b, shells) self.ngal_per_bin.append(ngal_b) self.bin_nz.append(dndz_b) @@ -323,7 +337,7 @@ def galaxies_simulation(self): catalogue["w"] = np.ones_like(gal_lon) catalogue["n1"] = noise_she.real catalogue["n2"] = noise_she.imag - catalogue["TOM_BIN_ID"] = b + catalogue["TOM_BIN_ID"] = b + 1 catalogue["TRUE_Z"] = gal_z catalogue["PHOTO_Z"] = gal_phz fits["SOURCE_CATALOGUE"].append(catalogue) @@ -338,8 +352,10 @@ def galaxies_simulation(self): if self.camb: self.get_camb_cls() - # TODO: Add a script to perform some final validation tests that are printed - # TODO: This would essentially include computing shape noise and number density. + if self.validation: + print("-" * 66) + self.run_validation_checks(out_file, mask) + print("-" * 66) def get_camb_cls(self, sav=True): """Lensing C_ell from CAMB source windows for the mock n(z).""" @@ -371,6 +387,19 @@ def get_camb_cls(self, sav=True): self.camb_cls = dic return dic + def run_validation_checks(self, out_file, mask): + print("Running validation checks...") + + cat_glass = fitsio.FITS(out_file)["SOURCE_CATALOGUE"].read() + + # First estimate the non-tomographic and tomographic shape noise. + validate_shape_noise(cat_glass) + + # Next, validate the number density. + validate_number_density(cat_glass, mask) + + print("Validation checks completed.") + if __name__ == "__main__": print("Starting the simulation") diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index aa345ff2..2f385d1a 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -375,6 +375,55 @@ def create_mask_from_catalogue(nside, path, output, ra_col="RA", dec_col="DEC"): hp.write_map(output, mask, dtype=np.float32, overwrite=True) +# --- shape noise and number density on mock catalogues ---------------------- +def shape_noise(e1, e2, w): + return np.sqrt( + 0.5 + * (np.sum(e1**2 * w**2) / np.sum(w**2) + np.sum(e2**2 * w**2) / np.sum(w**2)) + ) + + +def number_density(w, area): + return np.sum(w) ** 2 / np.sum(w**2) / area + + +def validate_shape_noise(cat, tomo=True, e1_col="e1", e2_col="e2", w_col="w"): + # First compute and print the non-tomographic shape noise + e1 = cat[e1_col] + e2 = cat[e2_col] + w = cat[w_col] + sigma_e = shape_noise(e1, e2, w) + print(f"Non-tomographic shape noise: {sigma_e}") + + if tomo: + # Now compute and print the tomographic shape noise + for b in range(1, cat["TOM_BIN_ID"].max() + 1): + e1_bin = e1[cat["TOM_BIN_ID"] == b] + e2_bin = e2[cat["TOM_BIN_ID"] == b] + w_bin = w[cat["TOM_BIN_ID"] == b] + sigma_e_bin = shape_noise(e1_bin, e2_bin, w_bin) + print(f"Tomographic shape noise (bin {b}): {sigma_e_bin}") + + +def validate_number_density(cat, mask, tomo=True, w_col="w"): + import healpy as hp + + # First compute and print the non-tomographic shape noise + w = cat[w_col] + nside = hp.npix2nside(mask.shape[0]) + area = ( + np.sum(mask) * hp.nside2pixarea(nside, degrees=True) * 60 * 60 + ) # area in arcmin^2 + n_gal = number_density(w, area) + print(f"Non-tomographic number density: {n_gal}") + + if tomo: + for b in range(1, cat["TOM_BIN_ID"].max() + 1): + w_bin = w[cat["TOM_BIN_ID"] == b] + n_gal_bin = number_density(w_bin, area) + print(f"Tomographic number density (bin {b}): {n_gal_bin}") + + # --- two-point statistics on mock catalogues -------------------------------- # Configuration-space treecorr defaults for the GLASS-mock 2PCF. From 7dd5a861635306fdd6b145cdd28985743438a51d Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 2 Jul 2026 15:37:41 +0200 Subject: [PATCH 008/107] Fix bugs in the CAMB angular power spectra estimation in --- config/glass_mock/config_glass_mock.yaml | 4 ++-- config/glass_mock/config_glass_mock_test.yaml | 9 +++++---- scripts/glass_mock/make_glass_sim.py | 8 ++++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/config/glass_mock/config_glass_mock.yaml b/config/glass_mock/config_glass_mock.yaml index 02edd5c2..366a0e5a 100644 --- a/config/glass_mock/config_glass_mock.yaml +++ b/config/glass_mock/config_glass_mock.yaml @@ -21,7 +21,7 @@ #kmax: 20.0 # --- Resolution --- -nside: 4096 +nside: 1024 dx: 120.0 zmax: 3.0 @@ -38,4 +38,4 @@ limber: True mask_path: /n09data/guerrini/glass_mock_v1.4.6_rerun/mask_nside4096.fits nz_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/test_data/redshift_distribution_tomo.txt output_path: /n09data/guerrini/glass_mock_test/ -output_prefix: tomo_test \ No newline at end of file +output_prefix: tomo_test_2 \ No newline at end of file diff --git a/config/glass_mock/config_glass_mock_test.yaml b/config/glass_mock/config_glass_mock_test.yaml index bb5c8b3f..702e0568 100644 --- a/config/glass_mock/config_glass_mock_test.yaml +++ b/config/glass_mock/config_glass_mock_test.yaml @@ -7,6 +7,7 @@ # -N, --number: Mock Number for labelling # -t, --test: Run in test mode # -cb, --camb: get Camb C_ell +# -v, --validation: Run some validation checks # -c, --config: Path to the configuration file to generate the simulation (Required) # --- Cosmological parameters (CAMB) --- @@ -27,11 +28,11 @@ zmax: 3 # --- galaxy population (downstream of map generation) --- nbins: 6 -n_arcmin2: [4.0, 3.8, 4.2, 3.95, 4.05, 4.0] -sigma_e: 0.2684 -bias: 1.2 +n_arcmin2: [4.0, 3.8, 4.2, 3.95, 4.05, 4.0] # number density in arcmin^-2. Input can be a float or a list (a number density for each bin) +sigma_e: 0.2684 # intrinsic ellipticity dispersion. Input can be a float or a list (a shape noise for each bin) +bias: 1.2 # galaxy bias. Input can be a float or a list (a galaxy bias for each bin) phz_sigma_0: 0.03 -#ia_bias: null +#ia_bias: null # Intrinsic alignment amplitude. Warning: this feature has not been robustly tested. # --- Runtime options --- limber: True diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index 6af8fc69..4db41735 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -350,7 +350,10 @@ def galaxies_simulation(self): print("Saved simulation to: ", out_file) if self.camb: + print("-" * 66) + print("Compute the associated theory power spectra...") self.get_camb_cls() + print("-" * 66) if self.validation: print("-" * 66) @@ -370,7 +373,7 @@ def get_camb_cls(self, sav=True): for i in range(self.config.nbins) ] self.pars.SourceWindows = sources - self.pars.InitPower.set_params(As=2.1e-9, ns=0.965) + results = camb.get_results(self.pars) cl_camb = results.get_source_cls_dict(lmax=lmax, raw_cl=True) @@ -380,9 +383,10 @@ def get_camb_cls(self, sav=True): a, b = key.replace("W", "").replace("x", "-").split("-") dic[f"{int(a) - 1}-{int(b) - 1}"] = cl_camb[key] if sav: - out = f"{self.root}/camb_cls.fits" + out = f"{self.root}/{self.prefix}_camb_cls_{self.n_sim}_{self.config.nside}.fits" fits = fitsio.FITS(out, "rw", clobber=True) fits.write(dic) + print("Saved CAMB power spectra to: ", out) fits.close() self.camb_cls = dic return dic From 90c3bdaaf668730582df3451903279594c4a7733 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 2 Jul 2026 16:49:04 +0200 Subject: [PATCH 009/107] Add functions from namaster_util to prepare the upgrade to tomography for harmonic space estimators --- .gitignore | 4 +- src/sp_validation/pseudo_cl.py | 188 +++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f8eec899..a568c1ca 100644 --- a/.gitignore +++ b/.gitignore @@ -197,4 +197,6 @@ papers/catalog/plots/*.pdf papers/cosmo_val/logs/ # Ignore scratch notebooks -scratch/*/*.ipynb \ No newline at end of file +scratch/*/*.ipynb +scratch/guerrini/work_notebooks +scratch/guerrini/launch_scripts \ No newline at end of file diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index c9355ec9..e43ac244 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -140,6 +140,194 @@ def apply_random_rotation(e1, e2, rng=None): return e1_out, e2_out +def get_field_and_workspace_from_map( + b, + mask_a, + e1_map_a=None, + e2_map_a=None, + mask_b=None, + e1_map_b=None, + e2_map_b=None, + pol_factor=-1, +): + """Compute a NaMaster field and workspace object from the input maps. + + If the shear maps are None, returns field objects but only the workspace objects is relevant and contains the mixing matrix. + If the second mask and shear maps (indexed b) are provided, the mixing matrix is computed between the two fields. + + Parameters + ---------- + b : nmt.NmtBin + NaMaster binning object. + mask_a : np.ndarray + Field mask for the first map. + e1_map_a : np.ndarray, optional + E1 map for the first field. + e2_map_a : np.ndarray, optional + E2 map for the first field. + mask_b : np.ndarray, optional + Field mask for the second map. + e1_map_b : np.ndarray, optional + E1 map for the second field. + e2_map_b : np.ndarray, optional + E2 map for the second field. + pol_factor : float, optional + Polarization factor to apply to the E2 map. + + Returns + ------- + field_a : nmt.NmtField + NaMaster field object for the first map. + field_b : nmt.NmtField + NaMaster field object for the second map (if provided, same than the first map otherwise). + wsp : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix. + + """ + nside = hp.npix2nside(len(mask_a)) + lmax = b.get_ell_max() + if e1_map_a is None or e2_map_a is None: + e1_map_a = np.zeros(hp.nside2npix(nside)) + e2_map_a = np.zeros(hp.nside2npix(nside)) + + # Create NaMaster field + field_a = nmt.NmtField( + mask=mask_a, maps=[e1_map_a, pol_factor * e2_map_a], lmax=lmax + ) + + if mask_b is not None: + if e1_map_b is None or e2_map_b is None: + e1_map_b = np.zeros(hp.nside2npix(nside)) + e2_map_b = np.zeros(hp.nside2npix(nside)) + + field_b = nmt.NmtField( + mask=mask_b, maps=[e1_map_b, pol_factor * e2_map_b], lmax=lmax + ) + else: + field_b = field_a + # Create NaMaster workspace + wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) + + return field_a, field_b, wsp + + +def get_field_and_workspace_from_catalog( + b, + ra_a, + dec_a, + e1_a, + e2_a, + w_a, + ra_b=None, + dec_b=None, + e1_b=None, + e2_b=None, + w_b=None, + pol_factor=-1, +): + """Create a NaMaster field and workspace from the input catalog. + + If the second catalog is provided, the mixing matrix is computed between the two fields. + + Parameters + ---------- + b : nmt.NmtBin + NaMaster binning object. + ra_a : np.ndarray + Right ascension of sources in the first catalog. + dec_a : np.ndarray + Declination of sources in the first catalog. + e1_a : np.ndarray + E1 shear component of sources in the first catalog. + e2_a : np.ndarray + E2 shear component of sources in the first catalog. + w_a : np.ndarray + Weights of sources in the first catalog. + ra_b : np.ndarray, optional + Right ascension of sources in the second catalog. + dec_b : np.ndarray, optional + Declination of sources in the second catalog. + e1_b : np.ndarray, optional + E1 shear component of sources in the second catalog. + e2_b : np.ndarray, optional + E2 shear component of sources in the second catalog. + w_b : np.ndarray, optional + Weights of sources in the second catalog. + pol_factor : float, optional + Polarization factor to apply to the E2 component. + + Returns + ------- + field_a : nmt.NmtFieldCatalog + NaMaster field object for the first catalog. + field_b : nmt.NmtFieldCatalog + NaMaster field object for the second catalog (if provided, same as the first catalog otherwise). + wsp : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix. + + """ + lmax = b.get_ell_max() + # Get field for input catalog a + field_a = nmt.NmtFieldCatalog( + positions=[ra_a, dec_a], + weights=w_a, + field=[e1_a, pol_factor * e2_a], + lmax=lmax, + lmax_mask=lmax, + spin=2, + lonlat=True, + ) + + if ( + ra_b is not None + and dec_b is not None + and e1_b is not None + and e2_b is not None + and w_b is not None + ): + field_b = nmt.NmtFieldCatalog( + positions=[ra_b, dec_b], + weights=w_b, + field=[e1_b, pol_factor * e2_b], + lmax=lmax, + lmax_mask=lmax, + spin=2, + lonlat=True, + ) + else: + field_b = field_a + + wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) + return field_a, field_b, wsp + + +def compute_cl_from_field_and_workspace(field_a, field_b, wsp, b): + """Compute the angular power spectrum from the input NaMaster field and workspace + + Parameters + ---------- + field_a : nmt.NmtField + NaMaster field object for the first catalog. + field_b : nmt.NmtField + NaMaster field object for the second catalog. + wsp : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix. + b : nmt.NmtBin + NaMaster binning object. + + Returns + ------- + cl_coupled : np.ndarray + Coupled angular power spectrum. + cl_decoupled : np.ndarray + Decoupled angular power spectrum. + """ + cl_coupled = nmt.compute_coupled_cell(field_a, field_b) + cl_decoupled = wsp.decouple_cell(cl_coupled) + + return cl_coupled, cl_decoupled + + def get_pseudo_cls_map( shear_map, mask, From 875f65d83c1c32186cc004536b62888515bc1250 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 3 Jul 2026 11:14:39 +0200 Subject: [PATCH 010/107] Add tomography pseudo-cl DV measurement. --- src/sp_validation/cosmo_val/core.py | 43 +++ src/sp_validation/cosmo_val/pseudo_cl.py | 440 +++++++++++++++++------ src/sp_validation/pseudo_cl.py | 368 ++++++++++++++++--- 3 files changed, 689 insertions(+), 162 deletions(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index ee65f658..6f81d1bf 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -1,5 +1,6 @@ # %% import copy +import itertools import os import re from pathlib import Path @@ -7,6 +8,7 @@ import colorama import numpy as np import yaml +from astropy.io import fits from shear_psf_leakage import run_object, run_scale from ..b_modes import ( @@ -96,6 +98,10 @@ class CosmologyValidation( noise debiasing, making those realizations reproducible run-to-run. cosmo_params : dict, optional Cosmological parameters to pass to get_cosmo(). If None, uses Planck 2018. + compute_tomography : bool, default False + Whether to compute tomographic correlation functions and pseudo-C_ell. + force_run : bool, default False + If True, forces re-computation of results even if cached outputs exist. Attributes ---------- @@ -222,6 +228,8 @@ def __init__( path_onecovariance=None, cosmo_params=None, blind=None, + compute_tomography=False, + force_run=False, ): self.rho_tau_method = rho_tau_method self.cov_estimate_method = cov_estimate_method @@ -251,6 +259,8 @@ def __init__( self.nside_mask = nside_mask self.path_onecovariance = path_onecovariance self.blind = blind + self.compute_tomography = compute_tomography + self.force_run = force_run assert self.cell_method in ["map", "catalog"], ( "cell_method must be 'map' or 'catalog'" @@ -631,3 +641,36 @@ def summarize_bmodes(self, fiducial_scale_cut=(12, 83), versions=None): print() return summary + + def _get_tomo_bins(self, version): + """ + Return the tomo_bin_ids for a given version. If the version does not have tomography, return None. + + Returns + ------- + tomo_bin_ids : list or None + List of unique tomographic bin IDs for the version, or None if no tomography is available + tomo_bin_pairs : list of tuples or None + List of unique pairs of tomographic bin IDs (including self-pairs) for the version, or None if no tomography is available + """ + if "tomo_bin_ids" in self.cc[version]["shear"]: + self.print_cyan( + f"Extracting tomography information from version {version}." + ) + cat_gal = fits.getdata(self.cc[version]["shear"]["path"]) + tomo_bin = cat_gal[self.cc[version]["shear"]["tomo_bin_ids"]] + tomo_bin_ids = np.unique(tomo_bin) + tomo_bin_ids = tomo_bin_ids[ + tomo_bin_ids > 0 + ] # Exclude zero or negative bins + self.print_cyan( + f"Found {len(tomo_bin_ids)} tomographic bins for version {version}: {tomo_bin_ids}." + ) + + tomo_bin_pairs = list( + itertools.combinations_with_replacement(tomo_bin_ids, 2) + ) + return tomo_bin_ids, tomo_bin_pairs + else: + self.print_cyan(f"Version {version} does not have tomography information.") + return None, None diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index e4733eea..f74bd606 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -9,6 +9,7 @@ import configparser import os +import warnings import healpy as hp import matplotlib.pyplot as plt @@ -18,10 +19,12 @@ from ..cosmology import get_theo_c_ell from ..pseudo_cl import ( - apply_random_rotation, get_n_gal_map, + get_noise_realisation, + get_pixels, get_pseudo_cls_catalog, get_pseudo_cls_map, + get_shear_map, make_namaster_bin, ) from ..rho_tau import get_params_rho_tau @@ -29,11 +32,14 @@ class PseudoClMixin: + # ---------------- Pseudo-Cl properties ---------------- # @property def pseudo_cls(self): if not hasattr(self, "_pseudo_cls"): self.calculate_pseudo_cl() self.calculate_pseudo_cl_eb_cov() + if self.compute_tomography: + self.calculate_pseudo_cl_tomo() return self._pseudo_cls @property @@ -42,18 +48,8 @@ def pseudo_cls_onecov(self): self.calculate_pseudo_cl_onecovariance() return self._pseudo_cls_onecov - def get_namaster_bin(self, lmin, lmax, b_lmax): - """Build NaMaster binning object (thin wrapper, state -> primitive).""" - return make_namaster_bin( - lmin, - lmax, - b_lmax, - self.binning, - ell_step=self.ell_step, - n_ell_bins=self.n_ell_bins, - power=self.power, - ) - + # ---------------- Pseudo-Cl calculation methods ---------------- # + # TODO: some cleaning to clearly separate DV, covariance, and utility functions. def get_variance_map(self, nside, e1, e2, w, unique_pix, idx_rep): """ Create a variance map from the input catalog. @@ -94,10 +90,7 @@ def calculate_pseudo_cl_eb_cov(self): nside = self.nside - try: - self._pseudo_cls - except AttributeError: - self._pseudo_cls = {} + self._pseudo_cls = getattr(self, "_pseudo_cls", {}) for ver in self.versions: self.print_magenta(ver) @@ -105,7 +98,7 @@ def calculate_pseudo_cl_eb_cov(self): self._pseudo_cls[ver] = {} out_path = self._output_path(f"pseudo_cl_cov_{ver}.fits") - if os.path.exists(out_path): + if os.path.exists(out_path) and not self.force_run: self.print_done( f"Skipping Pseudo-Cl covariance calculation, {out_path} exists" ) @@ -299,8 +292,11 @@ def calculate_pseudo_cl_onecovariance(self): out_dir = self._output_path(f"pseudo_cl_cov_onecov_{ver}/") os.makedirs(out_dir, exist_ok=True) - if os.path.exists( - os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") + if ( + os.path.exists( + os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") + ) + and not self.force_run ): self.print_done(f"Skipping OneCovariance calculation, {out_dir} exists") self._load_onecovariance_cov(out_dir, ver) @@ -417,7 +413,7 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): out_file = self._output_path( f"pseudo_cl_cov_g_ng_{gaussian_part}_{ver}.fits" ) - if os.path.exists(out_file): + if os.path.exists(out_file) and not self.force_run: self.print_done( f"Skipping Gaussian and Non-Gaussian covariance calculation, {out_file} exists" ) @@ -459,20 +455,21 @@ def calculate_pseudo_cl(self): nside = self.nside - try: - self._pseudo_cls - except AttributeError: - self._pseudo_cls = {} + self._pseudo_cls = getattr(self, "_pseudo_cls", {}) for ver in self.versions: self.print_magenta(ver) - self._pseudo_cls[ver] = {} + if ver not in self._pseudo_cls.keys(): + self._pseudo_cls[ver] = {} + + if "non_tomo" not in self._pseudo_cls[ver].keys(): + self._pseudo_cls[ver].update({"non_tomo": {}}) - out_path = self._output_path(f"pseudo_cl_{ver}.fits") - if os.path.exists(out_path): + out_path = self._output_path_pseudo_cl(ver, tomo_bin_pair=None) + if os.path.exists(out_path) and not self.force_run: self.print_done(f"Skipping Pseudo-Cl's calculation, {out_path} exists") cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver]["pseudo_cl"] = cl_shear + self._pseudo_cls[ver]["non_tomo"]["pseudo_cl"] = cl_shear elif self.cell_method == "map": self.calculate_pseudo_cl_map(ver, nside, out_path) elif self.cell_method == "catalog": @@ -482,31 +479,88 @@ def calculate_pseudo_cl(self): self.print_done("Done pseudo-Cl's") + def calculate_pseudo_cl_tomo(self): + """ + Compute the pseudo-Cl of a `CosmologyValidation` inputs with tomography. + """ + if not self.compute_tomography: + warnings.warn( + "``compute_tomography`` is set to False but tomography will be computed. Check that this is intentional." + ) + + self.print_start("Computing tomographic pseudo-Cl's") + + self._pseudo_cls = getattr(self, "_pseudo_cls", {}) + + for ver in self.versions: + self.print_magenta(ver) + + if ver not in self.pseudo_cls.keys(): + self._pseudo_cls[ver] = {} + + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError(f"Version {ver} does not have tomography information.") + + # Loop on the different tomographic bin pairs + for bin_key1, bin_key2 in tomo_bin_pairs: + self.print_cyan(f"Tomo Bin Pair: ({bin_key1}, {bin_key2})") + + if ( + f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + not in self._pseudo_cls[ver].keys() + ): + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + ] = {} + + out_path = self._output_path_pseudo_cl( + ver, tomo_bin_pair=(bin_key1, bin_key2) + ) + if os.path.exists(out_path) and not self.force_run: + self.print_done( + f"Skipping Pseudo-Cl's calculation, {out_path} exists" + ) + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ + "pseudo_cl" + ] = cl_shear + continue + + if self.cell_method == "map": + self.calculate_pseudo_cl_map_tomo( + ver, self.nside, out_path, bin_key1, bin_key2 + ) + elif self.cell_method == "catalog": + ... + else: + raise ValueError(f"Unknown cell method: {self.cell_method}") + def calculate_pseudo_cl_map(self, ver, nside, out_path): params = get_params_rho_tau(self.cc[ver], survey=ver) # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - w = cat_gal[params["w_col"]] self.print_cyan("Creating maps and computing Cl's...") - n_gal_map, unique_pix, _idx, idx_rep = self.get_n_gal_map( - params, nside, cat_gal - ) - mask = n_gal_map != 0 - - shear_map_e1 = np.zeros(hp.nside2npix(nside)) - shear_map_e2 = np.zeros(hp.nside2npix(nside)) - e1 = cat_gal[params["e1_col"]] - e2 = cat_gal[params["e2_col"]] + # Get the pixels and indices for the catalog + unique_pix, idx, idx_rep = self.get_pixels(params, nside, cat_gal) - del cat_gal + n_gal_map = self.get_n_gal_map( + params, nside, cat_gal, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) - shear_map_e1[unique_pix] += np.bincount(idx_rep, weights=e1 * w) - shear_map_e2[unique_pix] += np.bincount(idx_rep, weights=e2 * w) - shear_map_e1[mask] /= n_gal_map[mask] - shear_map_e2[mask] /= n_gal_map[mask] + shear_map_e1, shear_map_e2 = self.get_shear_map( + params, + nside, + cat_gal, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + n_gal_map=n_gal_map, + ) shear_map = shear_map_e1 + 1j * shear_map_e2 @@ -514,119 +568,274 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path): ell_eff, cl_shear, wsp = self.get_pseudo_cls_map(shear_map, n_gal_map) - cl_noise = np.zeros_like(cl_shear) - rng = np.random.default_rng(self.cell_seed) + # Estimate the noise bias component and subtract + cl_noise = self.get_noise_bias_from_gaussian_real( + params, + nside, + cat_gal, + n_gal_map, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + wsp=wsp, + ) - for i in range(self.nrandom_cell): - noise_map_e1 = np.zeros(hp.nside2npix(nside)) - noise_map_e2 = np.zeros(hp.nside2npix(nside)) + # Noise realizations are now reproducible (seeded rng from self.cell_seed). + cl_shear = cl_shear - cl_noise - e1_rot, e2_rot = self.apply_random_rotation(e1, e2, rng) + self.print_cyan("Saving pseudo-Cl's...") + self.save_pseudo_cl(ell_eff, cl_shear, out_path) + + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver]["non_tomo"]["pseudo_cl"] = cl_shear - noise_map_e1[unique_pix] += np.bincount(idx_rep, weights=e1_rot * w) - noise_map_e2[unique_pix] += np.bincount(idx_rep, weights=e2_rot * w) + def calculate_pseudo_cl_catalog(self, ver, out_path): + params = get_params_rho_tau(self.cc[ver], survey=ver) - noise_map_e1[mask] /= n_gal_map[mask] - noise_map_e2[mask] /= n_gal_map[mask] + # Load data and create shear and noise maps + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - noise_map = noise_map_e1 + 1j * noise_map_e2 - del noise_map_e1, noise_map_e2 + ell_eff, cl_shear, _ = self.get_pseudo_cls_catalog( + catalog=cat_gal, params=params + ) - _, cl_noise_, _ = self.get_pseudo_cls_map(noise_map, n_gal_map, wsp) - cl_noise += cl_noise_ + self.print_cyan("Saving pseudo-Cl's...") + self.save_pseudo_cl(ell_eff, cl_shear, out_path) - cl_noise /= self.nrandom_cell - del e1, e2, w - try: - del e1_rot, e2_rot - except NameError: # Continue if the random generation has been skipped. - pass - del n_gal_map + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver]["non_tomo"]["pseudo_cl"] = cl_shear - # Noise realizations are now reproducible (seeded rng from self.cell_seed). - cl_shear = cl_shear - cl_noise + def calculate_pseudo_cl_map_tomo( + self, ver, nside, out_path, tomo_bin_a, tomo_bin_b + ): + params = get_params_rho_tau(self.cc[ver]) + + # Load data and create shear and noise maps + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + + tomo_bin_id = cat_gal[self.cc[ver]["shear"]["tomo_bin_ids"]] + mask_a = tomo_bin_id == tomo_bin_a + mask_b = tomo_bin_id == tomo_bin_b + cat_gal_a = cat_gal[mask_a] + cat_gal_b = cat_gal[mask_b] + + del cat_gal + + print("Creating maps and computing Cl's...") + # Get the pixels and indices for the catalogs + unique_pix_a, idx_a, idx_rep_a = self.get_pixels(params, nside, cat_gal_a) + unique_pix_b, idx_b, idx_rep_b = self.get_pixels(params, nside, cat_gal_b) + + # Create number density maps for each tomographic bin + n_gal_map_a = self.get_n_gal_map( + params, + nside, + cat_gal_a, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + ) + n_gal_map_b = self.get_n_gal_map( + params, + nside, + cat_gal_b, + unique_pix=unique_pix_b, + idx=idx_b, + idx_rep=idx_rep_b, + ) + + # Create shear maps for each tomographic bin + shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( + nside, + cat_gal_a, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + n_gal_map=n_gal_map_a, + ) + shear_map_a = shear_map_a_e1 + 1j * shear_map_a_e2 + del shear_map_a_e1, shear_map_a_e2 + + shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( + nside, + cat_gal_b, + unique_pix=unique_pix_b, + idx=idx_b, + idx_rep=idx_rep_b, + n_gal_map=n_gal_map_b, + ) + shear_map_b = shear_map_b_e1 + 1j * shear_map_b_e2 + del shear_map_b_e1, shear_map_b_e2 + + # Compute the pseudo-Cl's + ell_eff, cl_shear, wsp = self.get_pseudo_cls_map( + shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, n_gal_map_b=n_gal_map_b + ) + + # Remove the noise bias for auto-correlations. + if tomo_bin_a == tomo_bin_b: + # Compute the noise bias using noise_bias_method + cl_noise = self.get_noise_bias_from_gaussian_real( + params, + nside, + cat_gal_a, + n_gal_map_a, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + wsp=wsp, + ) + + # Subtract the noise bias from the pseudo-Cl's + cl_shear = cl_shear - cl_noise self.print_cyan("Saving pseudo-Cl's...") self.save_pseudo_cl(ell_eff, cl_shear, out_path) cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver]["pseudo_cl"] = cl_shear + self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ] = cl_shear - def calculate_pseudo_cl_catalog(self, ver, out_path): - params = get_params_rho_tau(self.cc[ver], survey=ver) + def calculate_pseudo_cl_catalog_tomo(self, ver, out_path, tomo_bin_a, tomo_bin_b): + params = get_params_rho_tau(self.cc[ver]) # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) ell_eff, cl_shear, wsp = self.get_pseudo_cls_catalog( - catalog=cat_gal, params=params + catalog=cat_gal, params=params, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b ) self.print_cyan("Saving pseudo-Cl's...") self.save_pseudo_cl(ell_eff, cl_shear, out_path) cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver]["pseudo_cl"] = cl_shear + self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ] = cl_shear - def get_n_gal_map(self, params, nside, cat_gal): + # ---------------- Utility functions for pseudo-Cl calculations ---------------- # + def get_namaster_bin(self, lmin, lmax, b_lmax): + """Build NaMaster binning object (thin wrapper, state -> primitive).""" + return make_namaster_bin( + lmin, + lmax, + b_lmax, + self.binning, + ell_step=self.ell_step, + n_ell_bins=self.n_ell_bins, + power=self.power, + ) + + def get_pixels(self, params, nside, cat_gal): + """Get unique pixels and indices for a catalog (thin wrapper -> primitive).""" + return get_pixels(cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], nside) + + def get_n_gal_map( + self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None + ): """Weighted galaxy number-density map (thin wrapper -> primitive).""" return get_n_gal_map( nside, cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], weights=cat_gal[params["w_col"]], + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, ) - def get_gaussian_real( - self, params, nside, lmax, cat_gal, n_gal, mask, unique_pix, idx_rep, rng=None + def get_shear_map( + self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None ): - e1_rot, e2_rot = self.apply_random_rotation( - cat_gal[params["e1_col"]], cat_gal[params["e2_col"]], rng + """Weighted shear map (thin wrapper -> primitive).""" + return get_shear_map( + cat_gal[params["ra_col"]], + cat_gal[params["dec_col"]], + cat_gal[params["e1_col"]], + cat_gal[params["e2_col"]], + cat_gal[params["w_col"]], + nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, ) - noise_map_e1 = np.zeros(hp.nside2npix(nside)) - noise_map_e2 = np.zeros(hp.nside2npix(nside)) - - w = cat_gal[params["w_col"]] - noise_map_e1[unique_pix] += np.bincount(idx_rep, weights=e1_rot * w) - noise_map_e2[unique_pix] += np.bincount(idx_rep, weights=e2_rot * w) - noise_map_e1[mask] /= n_gal[mask] - noise_map_e2[mask] /= n_gal[mask] - return noise_map_e1 + 1j * noise_map_e2 - - def get_sample( + def get_noise_realisation( self, params, nside, - lmax, - b, cat_gal, - n_gal, - mask, - unique_pix, - idx_rep, + n_gal=None, + unique_pix=None, + idx=None, + idx_rep=None, rng=None, ): - noise_map = self.get_gaussian_real( - params, nside, lmax, cat_gal, n_gal, mask, unique_pix, idx_rep, rng + """ + Get a single Gaussian noise realization (thin wrapper -> primitive). + """ + return get_noise_realisation( + cat_gal[params["ra_col"]], + cat_gal[params["dec_col"]], + cat_gal[params["e1_col"]], + cat_gal[params["e2_col"]], + cat_gal[params["w_col"]], + nside, + n_gal=n_gal, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + rng=rng, ) - f = nmt.NmtField(mask=mask, maps=[noise_map.real, noise_map.imag], lmax=lmax) + def get_noise_bias_from_gaussian_real( + self, + params, + nside, + cat_gal, + n_gal_map, + unique_pix=None, + idx=None, + idx_rep=None, + wsp=None, + ): + cl_noise = np.zeros_like() + rng = np.random.default_rng(self.cell_seed) - wsp = nmt.NmtWorkspace.from_fields(f, f, b) + for _ in range(self.nrandom_cell): + noise_map_e1, noise_map_e2 = self.get_noise_realisation( + params, + nside, + cat_gal, + n_gal=n_gal_map, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + rng=rng, + ) - cl_noise = nmt.compute_coupled_cell(f, f) - cl_noise = wsp.decouple_cell(cl_noise) + noise_map = noise_map_e1 + 1j * noise_map_e2 + del noise_map_e1, noise_map_e2 - return cl_noise, f, wsp + _, cl_noise_, _ = self.get_pseudo_cls_map(noise_map, n_gal_map, wsp) + cl_noise += cl_noise_ - def get_pseudo_cls_map(self, map, mask, wsp=None): + cl_noise /= self.nrandom_cell + return cl_noise + + def get_pseudo_cls_map( + self, map_a, mask_a, wsp=None, shear_map_b=None, mask_b=None + ): """Map-based pseudo-cl (thin wrapper, state -> primitive).""" return get_pseudo_cls_map( - map, - mask, + map_a, + mask_a, self.nside, self.binning, + shear_map_b=shear_map_b, + mask_b=mask_b, pol_factor=self.pol_factor, wsp=wsp, ell_step=self.ell_step, @@ -634,13 +843,17 @@ def get_pseudo_cls_map(self, map, mask, wsp=None): power=self.power, ) - def get_pseudo_cls_catalog(self, catalog, params, wsp=None): + def get_pseudo_cls_catalog( + self, catalog, params, wsp=None, tomo_bin_a=None, tomo_bin_b=None + ): """Catalog-based pseudo-cl (thin wrapper, state -> primitive).""" return get_pseudo_cls_catalog( catalog, params, self.nside, self.binning, + tomo_bin_a=tomo_bin_a, + tomo_bin_b=tomo_bin_b, pol_factor=self.pol_factor, wsp=wsp, ell_step=self.ell_step, @@ -648,12 +861,16 @@ def get_pseudo_cls_catalog(self, catalog, params, wsp=None): power=self.power, ) - def apply_random_rotation(self, e1, e2, rng=None): - """Random ellipticity rotation (thin wrapper -> primitive). - - Pass a seeded ``rng`` for reproducible noise realizations. - """ - return apply_random_rotation(e1, e2, rng) + def _output_path_pseudo_cl(self, ver, tomo_bin_pair=None): + if tomo_bin_pair is None: + return self._output_path( + f"pseudo_cl_from_{self.cell_method}_non_tomo_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" + ) + else: + bin_key1, bin_key2 = tomo_bin_pair + return self._output_path( + f"pseudo_cl_from_{self.cell_method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" + ) def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): """ @@ -676,6 +893,7 @@ def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): cell_hdu.writeto(out_path, overwrite=True) + # ---------------- Plotting functions for pseudo-Cl's ---------------- # def plot_pseudo_cl(self): """ Plot pseudo-Cl's for given catalogs. diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index e43ac244..483e71c5 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -21,6 +21,7 @@ LMIN = 8 +# ---------------------- Binning utility functions ---------------------- def pseudo_cl_geometry(nside): """Return ``(lmin, lmax, b_lmax)`` for the pseudo-Cl estimator at ``nside``. @@ -87,7 +88,38 @@ def make_namaster_bin( return b -def get_n_gal_map(nside, ra, dec, weights=None): +# ---------------------- Map computation utility functions ---------------------- +def get_pixels(ra, dec, nside): + """ + Get the HEALPix pixel indices for given RA and Dec. + + Parameters + ---------- + ra : np.ndarray + Right ascension in degrees. + dec : np.ndarray + Declination in degrees. + nside : int + HEALPix nside parameter. + + Returns + ------- + unique_pix : np.ndarray + Sorted unique pixel indices. + idx : np.ndarray + First-occurrence indices into the input from ``np.unique``. + idx_rep : np.ndarray + Inverse map: pixel-group index for each input object. + """ + pixels = hp.ang2pix(nside, theta=np.radians(90 - dec), phi=np.radians(ra)) + + unique_pix, idx, idx_rep = np.unique(pixels, return_index=True, return_inverse=True) + return unique_pix, idx, idx_rep + + +def get_n_gal_map( + nside, ra, dec, weights=None, unique_pix=None, idx=None, idx_rep=None +): """Weighted galaxy number-density HEALPix map plus pixel bookkeeping. Bins ``(ra, dec)`` (degrees) onto an ``nside`` HEALPix grid. With @@ -98,21 +130,106 @@ def get_n_gal_map(nside, ra, dec, weights=None): ------- n_gal : np.ndarray Map of summed weights (or counts) per pixel, shape ``(npix,)``. - unique_pix : np.ndarray - Sorted unique occupied pixel indices. - idx : np.ndarray - First-occurrence indices into the input from ``np.unique``. - idx_rep : np.ndarray - Inverse map: pixel-group index for each input object. """ - theta = (90.0 - dec) * np.pi / 180.0 - phi = ra * np.pi / 180.0 - pix = hp.ang2pix(nside, theta, phi) + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) - unique_pix, idx, idx_rep = np.unique(pix, return_index=True, return_inverse=True) n_gal = np.zeros(hp.nside2npix(nside)) n_gal[unique_pix] = np.bincount(idx_rep, weights=weights) - return n_gal, unique_pix, idx, idx_rep + return n_gal + + +def get_shear_map( + ra, dec, e1, e2, w, nside, unique_pix=None, idx=None, idx_rep=None, n_gal_map=None +): + """Weighted shear HEALPix maps plus pixel bookkeeping. + + Bins ``(ra, dec)`` (degrees) onto an ``nside`` HEALPix grid. The shear + components ``(e1, e2)`` are weighted by ``w`` and summed per pixel. If + ``unique_pix``, ``idx``, and ``idx_rep`` are provided, they are used to + avoid recomputing the pixel indices. + If ``n_gal_map`` is provided, it is used to normalize the shear maps by the galaxy density. + + Returns + ------- + e1_map : np.ndarray + Weighted sum of E1 per pixel, shape ``(npix,)``. + e2_map : np.ndarray + Weighted sum of E2 per pixel, shape ``(npix,)``. + """ + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + if n_gal_map is None: + n_gal_map = get_n_gal_map( + nside, ra, dec, weights=w, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) + + npix = hp.nside2npix(nside) + e1_map = np.zeros(npix) + e2_map = np.zeros(npix) + + e1_map[unique_pix] = np.bincount(idx_rep, weights=e1 * w) + e2_map[unique_pix] = np.bincount(idx_rep, weights=e2 * w) + + non_zero = n_gal_map > 0 + e1_map[non_zero] /= n_gal_map[non_zero] + e2_map[non_zero] /= n_gal_map[non_zero] + + return e1_map, e2_map + + +def get_variance_map( + nside, ra, dec, e1, e2, w, unique_pix=None, idx=None, idx_rep=None +): + """Compute the variance map of the shear components. + + The variance is computed as the weighted variance of the shear components in each pixel. + + Returns + ------- + variance_map : np.ndarray + Variance map of the shear components, shape ``(npix,)``. + """ + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + npix = hp.nside2npix(nside) + variance_map = np.zeros(npix) + + variance_map[unique_pix] = np.bincount( + idx_rep, weights=0.5 * (e1**2 + e2**2) * w**2 + ) + + return variance_map + + +def get_noise_bias_analytical( + ra, dec, e1, e2, w, lmax, nside=1024, unique_pix=None, idx=None, idx_rep=None +): + """ + Compute the analytical noise bias for shear power spectrum. + """ + variance_map = get_variance_map( + nside=nside, + ra=ra, + dec=dec, + e1=e1, + e2=e2, + w=w, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + ) + + noise_bias = hp.nside2pixarea(nside) * np.mean(variance_map) + + noise_bias_cl = np.zeros((4, lmax)) + + noise_bias_cl[0, :] = noise_bias # EE + noise_bias_cl[3, :] = noise_bias # BB + + return noise_bias_cl def apply_random_rotation(e1, e2, rng=None): @@ -140,6 +257,63 @@ def apply_random_rotation(e1, e2, rng=None): return e1_out, e2_out +def get_noise_realisation( + ra, + dec, + e1, + e2, + w, + nside, + unique_pix=None, + idx=None, + idx_rep=None, + n_gal_map=None, + rng=None, +): + """ + Generate a random noise realisation of the shear maps by applying a random rotation to the ellipticity components. + + Parameters + ---------- + ra, dec : np.ndarray + Right ascension and declination of the sources. + e1, e2 : np.ndarray + Ellipticity components. + w : np.ndarray + Weights of the sources. + nside : int + HEALPix resolution. + unique_pix, idx, idx_rep : np.ndarray, optional + Pixel indices and bookkeeping arrays. If not provided, they will be computed. + n_gal_map : np.ndarray, optional + Galaxy number density map. If not provided, it will be computed. + + Returns + ------- + noise_map_e1, noise_map_e2 : np.ndarray + Noise map for ellipticity components. + """ + # Apply random rotation to the ellipticity components + e1_rot, e2_rot = apply_random_rotation(e1, e2, rng=rng) + + # Compute the noise maps using the rotated ellipticity components + noise_map_e1, noise_map_e2 = get_shear_map( + ra=ra, + dec=dec, + e1=e1_rot, + e2=e2_rot, + w=w, + nside=nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + n_gal_map=n_gal_map, + ) + + return noise_map_e1, noise_map_e2 + + +# ---------------------- Cl computation functions ---------------------- def get_field_and_workspace_from_map( b, mask_a, @@ -149,6 +323,7 @@ def get_field_and_workspace_from_map( e1_map_b=None, e2_map_b=None, pol_factor=-1, + return_wsp=True, ): """Compute a NaMaster field and workspace object from the input maps. @@ -173,6 +348,8 @@ def get_field_and_workspace_from_map( E2 map for the second field. pol_factor : float, optional Polarization factor to apply to the E2 map. + return_wsp : bool, optional + If True, return the NaMaster workspace object containing the mixing matrix. Returns ------- @@ -205,10 +382,14 @@ def get_field_and_workspace_from_map( ) else: field_b = field_a - # Create NaMaster workspace - wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) - return field_a, field_b, wsp + if return_wsp: + # Create NaMaster workspace + wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) + + return field_a, field_b, wsp + else: + return field_a, field_b, None def get_field_and_workspace_from_catalog( @@ -224,6 +405,7 @@ def get_field_and_workspace_from_catalog( e2_b=None, w_b=None, pol_factor=-1, + return_wsp=True, ): """Create a NaMaster field and workspace from the input catalog. @@ -255,6 +437,8 @@ def get_field_and_workspace_from_catalog( Weights of sources in the second catalog. pol_factor : float, optional Polarization factor to apply to the E2 component. + return_wsp : bool, optional + If True, return the NaMaster workspace object containing the mixing matrix. Returns ------- @@ -297,8 +481,11 @@ def get_field_and_workspace_from_catalog( else: field_b = field_a - wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) - return field_a, field_b, wsp + if return_wsp: + wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) + return field_a, field_b, wsp + else: + return field_a, field_b, None def compute_cl_from_field_and_workspace(field_a, field_b, wsp, b): @@ -329,12 +516,14 @@ def compute_cl_from_field_and_workspace(field_a, field_b, wsp, b): def get_pseudo_cls_map( - shear_map, - mask, + shear_map_a, + mask_a, nside, binning, *, - pol_factor=True, + shear_map_b=None, + mask_b=None, + pol_factor=-1, wsp=None, ell_step=10, n_ell_bins=32, @@ -344,16 +533,20 @@ def get_pseudo_cls_map( Parameters ---------- - shear_map : np.ndarray + shear_map_a : np.ndarray Complex shear map (``e1 + 1j * e2``). - mask : np.ndarray + mask_a : np.ndarray Field mask (the galaxy number-density map). nside : int HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. - pol_factor : bool, optional - If ``True`` flip the sign of the imaginary (e2) component. + shear_map_b : np.ndarray, optional + Complex shear map for the second field (``e1 + 1j * e2``). + mask_b : np.ndarray, optional + Field mask for the second field (the galaxy number-density map). + pol_factor : float, optional + Polarization factor to apply to the E2 component. wsp : nmt.NmtWorkspace, optional Reuse a coupling workspace; built from the field if ``None``. ell_step, n_ell_bins, power : optional @@ -368,6 +561,27 @@ def get_pseudo_cls_map( wsp : nmt.NmtWorkspace The coupling workspace (newly built or the one passed in). """ + # First do some assertion checks + if shear_map_b is not None: + assert mask_b is not None, "mask_b must be provided if shear_map_b is provided" + assert shear_map_a.shape == shear_map_b.shape, ( + "shear_map_a and shear_map_b must have the same shape" + ) + assert mask_a.shape == mask_b.shape, ( + "mask_a and mask_b must have the same shape" + ) + + if mask_b is not None: + assert shear_map_b is not None, ( + "shear_map_b must be provided if mask_b is provided" + ) + assert shear_map_a.shape == shear_map_b.shape, ( + "shear_map_a and shear_map_b must have the same shape" + ) + assert mask_a.shape == mask_b.shape, ( + "mask_a and mask_b must have the same shape" + ) + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) b = make_namaster_bin( @@ -381,18 +595,36 @@ def get_pseudo_cls_map( ) ell_eff = b.get_effective_ells() - factor = -1 if pol_factor else 1 - - f_all = nmt.NmtField( - mask=mask, maps=[shear_map.real, factor * shear_map.imag], lmax=b_lmax - ) if wsp is None: - wsp = nmt.NmtWorkspace.from_fields(f_all, f_all, b) + field_a, field_b, wsp = get_field_and_workspace_from_map( + b, + mask_a, + e1_map_a=shear_map_a.real, + e2_map_a=shear_map_a.imag, + mask_b=mask_b, + e1_map_b=shear_map_b.real if shear_map_b is not None else None, + e2_map_b=shear_map_b.imag if shear_map_b is not None else None, + pol_factor=pol_factor, + return_wsp=True, + ) + else: + field_a, field_b, _ = get_field_and_workspace_from_map( + b, + mask_a, + e1_map_a=shear_map_a.real, + e2_map_a=shear_map_a.imag, + mask_b=mask_b, + e1_map_b=shear_map_b.real if shear_map_b is not None else None, + e2_map_b=shear_map_b.imag if shear_map_b is not None else None, + pol_factor=pol_factor, + return_wsp=False, + ) - cl_coupled = nmt.compute_coupled_cell(f_all, f_all) - cl_all = wsp.decouple_cell(cl_coupled) + cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( + field_a, field_b, wsp, b + ) - return ell_eff, cl_all, wsp + return ell_eff, cl_decoupled, wsp def get_pseudo_cls_catalog( @@ -401,7 +633,9 @@ def get_pseudo_cls_catalog( nside, binning, *, - pol_factor=True, + tomo_bin_a=None, + tomo_bin_b=None, + pol_factor=-1, wsp=None, ell_step=10, n_ell_bins=32, @@ -420,8 +654,8 @@ def get_pseudo_cls_catalog( HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. - pol_factor : bool, optional - If ``True`` flip the sign of the e2 component. + pol_factor : int, optional + Polarization factor to apply to the E2 component. wsp : nmt.NmtWorkspace, optional Reuse a coupling workspace; built from the field if ``None``. ell_step, n_ell_bins, power : optional @@ -436,6 +670,11 @@ def get_pseudo_cls_catalog( wsp : nmt.NmtWorkspace The coupling workspace (newly built or the one passed in). """ + # First make some assertion checks reagarding the run mode + assert (tomo_bin_a is None and tomo_bin_b is None) or ( + tomo_bin_a is not None and tomo_bin_b is not None + ), "Both tomo_bin_a and tomo_bin_b must be provided or both must be None" + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) b = make_namaster_bin( @@ -449,22 +688,49 @@ def get_pseudo_cls_catalog( ) ell_eff = b.get_effective_ells() - factor = -1 if pol_factor else 1 - - f_all = nmt.NmtFieldCatalog( - positions=[catalog[params["ra_col"]], catalog[params["dec_col"]]], - weights=catalog[params["w_col"]], - field=[catalog[params["e1_col"]], factor * catalog[params["e2_col"]]], - lmax=b_lmax, - lmax_mask=b_lmax, - spin=2, - lonlat=True, - ) + is_tomography = tomo_bin_a is not None and tomo_bin_b is not None + if is_tomography: + mask_tomo_a = catalog[params["tomo_bin_col"]] == tomo_bin_a + mask_tomo_b = catalog[params["tomo_bin_col"]] == tomo_bin_b + else: + mask_tomo_a = np.ones(len(catalog), dtype=bool) + mask_tomo_b = np.ones(len(catalog), dtype=bool) if wsp is None: - wsp = nmt.NmtWorkspace.from_fields(f_all, f_all, b) + field_a, field_b, wsp = get_field_and_workspace_from_catalog( + b, + ra_a=catalog[params["ra_col"]][mask_tomo_a], + dec_a=catalog[params["dec_col"]][mask_tomo_a], + e1_a=catalog[params["e1_col"]][mask_tomo_a], + e2_a=catalog[params["e2_col"]][mask_tomo_a], + w_a=catalog[params["w_col"]][mask_tomo_a], + ra_b=catalog[params["ra_col"]][mask_tomo_b], + dec_b=catalog[params["dec_col"]][mask_tomo_b], + e1_b=catalog[params["e1_col"]][mask_tomo_b], + e2_b=catalog[params["e2_col"]][mask_tomo_b], + w_b=catalog[params["w_col"]][mask_tomo_b], + pol_factor=pol_factor, + return_wsp=True, + ) + else: + field_a, field_b, _ = get_field_and_workspace_from_catalog( + b, + ra_a=catalog[params["ra_col"]][mask_tomo_a], + dec_a=catalog[params["dec_col"]][mask_tomo_a], + e1_a=catalog[params["e1_col"]][mask_tomo_a], + e2_a=catalog[params["e2_col"]][mask_tomo_a], + w_a=catalog[params["w_col"]][mask_tomo_a], + ra_b=catalog[params["ra_col"]][mask_tomo_b], + dec_b=catalog[params["dec_col"]][mask_tomo_b], + e1_b=catalog[params["e1_col"]][mask_tomo_b], + e2_b=catalog[params["e2_col"]][mask_tomo_b], + w_b=catalog[params["w_col"]][mask_tomo_b], + pol_factor=pol_factor, + return_wsp=False, + ) - cl_coupled = nmt.compute_coupled_cell(f_all, f_all) - cl_all = wsp.decouple_cell(cl_coupled) + cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( + field_a, field_b, wsp, b + ) - return ell_eff, cl_all, wsp + return ell_eff, cl_decoupled, wsp From 57980ddeefc3af51750e4b1e1a03d32806db342d Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 3 Jul 2026 12:13:14 +0200 Subject: [PATCH 011/107] Fix bugs and tests to the new API to pass the CI --- src/sp_validation/cosmo_val/core.py | 2 +- src/sp_validation/pseudo_cl.py | 56 +++++++++++++---------- src/sp_validation/tests/test_pseudo_cl.py | 50 ++++++++++++++------ 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 6f81d1bf..389699d6 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -89,7 +89,7 @@ class CosmologyValidation( Number of ell bins for pseudo-C_ell analysis (used with binning='powspace'). ell_step : int, default 10 Bin width in ell for linear binning (used with binning='linear'). - pol_factor : bool, default True + pol_factor : int, default -1 Apply polarization correction factor in pseudo-C_ell calculations. nrandom_cell : int, default 10 Number of random realizations for C_ell error estimation. diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index 483e71c5..b96525fa 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -362,7 +362,7 @@ def get_field_and_workspace_from_map( """ nside = hp.npix2nside(len(mask_a)) - lmax = b.get_ell_max() + lmax = b.lmax if e1_map_a is None or e2_map_a is None: e1_map_a = np.zeros(hp.nside2npix(nside)) e2_map_a = np.zeros(hp.nside2npix(nside)) @@ -406,6 +406,7 @@ def get_field_and_workspace_from_catalog( w_b=None, pol_factor=-1, return_wsp=True, + same_bin=False, ): """Create a NaMaster field and workspace from the input catalog. @@ -450,7 +451,7 @@ def get_field_and_workspace_from_catalog( NaMaster workspace object containing the mixing matrix. """ - lmax = b.get_ell_max() + lmax = b.lmax # Get field for input catalog a field_a = nmt.NmtFieldCatalog( positions=[ra_a, dec_a], @@ -468,6 +469,7 @@ def get_field_and_workspace_from_catalog( and e1_b is not None and e2_b is not None and w_b is not None + and not same_bin ): field_b = nmt.NmtFieldCatalog( positions=[ra_b, dec_b], @@ -692,41 +694,47 @@ def get_pseudo_cls_catalog( if is_tomography: mask_tomo_a = catalog[params["tomo_bin_col"]] == tomo_bin_a mask_tomo_b = catalog[params["tomo_bin_col"]] == tomo_bin_b + catalog_a = catalog[mask_tomo_a] + catalog_b = catalog[mask_tomo_b] + same_bin = tomo_bin_a == tomo_bin_b else: - mask_tomo_a = np.ones(len(catalog), dtype=bool) - mask_tomo_b = np.ones(len(catalog), dtype=bool) + catalog_a = catalog + catalog_b = catalog + same_bin = True if wsp is None: field_a, field_b, wsp = get_field_and_workspace_from_catalog( b, - ra_a=catalog[params["ra_col"]][mask_tomo_a], - dec_a=catalog[params["dec_col"]][mask_tomo_a], - e1_a=catalog[params["e1_col"]][mask_tomo_a], - e2_a=catalog[params["e2_col"]][mask_tomo_a], - w_a=catalog[params["w_col"]][mask_tomo_a], - ra_b=catalog[params["ra_col"]][mask_tomo_b], - dec_b=catalog[params["dec_col"]][mask_tomo_b], - e1_b=catalog[params["e1_col"]][mask_tomo_b], - e2_b=catalog[params["e2_col"]][mask_tomo_b], - w_b=catalog[params["w_col"]][mask_tomo_b], + ra_a=catalog_a[params["ra_col"]], + dec_a=catalog_a[params["dec_col"]], + e1_a=catalog_a[params["e1_col"]], + e2_a=catalog_a[params["e2_col"]], + w_a=catalog_a[params["w_col"]], + ra_b=catalog_b[params["ra_col"]], + dec_b=catalog_b[params["dec_col"]], + e1_b=catalog_b[params["e1_col"]], + e2_b=catalog_b[params["e2_col"]], + w_b=catalog_b[params["w_col"]], pol_factor=pol_factor, return_wsp=True, + same_bin=same_bin, ) else: field_a, field_b, _ = get_field_and_workspace_from_catalog( b, - ra_a=catalog[params["ra_col"]][mask_tomo_a], - dec_a=catalog[params["dec_col"]][mask_tomo_a], - e1_a=catalog[params["e1_col"]][mask_tomo_a], - e2_a=catalog[params["e2_col"]][mask_tomo_a], - w_a=catalog[params["w_col"]][mask_tomo_a], - ra_b=catalog[params["ra_col"]][mask_tomo_b], - dec_b=catalog[params["dec_col"]][mask_tomo_b], - e1_b=catalog[params["e1_col"]][mask_tomo_b], - e2_b=catalog[params["e2_col"]][mask_tomo_b], - w_b=catalog[params["w_col"]][mask_tomo_b], + ra_a=catalog_a[params["ra_col"]], + dec_a=catalog_a[params["dec_col"]], + e1_a=catalog_a[params["e1_col"]], + e2_a=catalog_a[params["e2_col"]], + w_a=catalog_a[params["w_col"]], + ra_b=catalog_b[params["ra_col"]], + dec_b=catalog_b[params["dec_col"]], + e1_b=catalog_b[params["e1_col"]], + e2_b=catalog_b[params["e2_col"]], + w_b=catalog_b[params["w_col"]], pol_factor=pol_factor, return_wsp=False, + same_bin=same_bin, ) cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index 45a2366d..be5c6d7d 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -58,6 +58,7 @@ import yaml from sp_validation.cosmo_val import CosmologyValidation +from sp_validation.pseudo_cl import apply_random_rotation from sp_validation.rho_tau import get_params_rho_tau # These tests need the full harmonic-space stack (pymaster/NaMaster + healpy), @@ -165,7 +166,7 @@ def cv(tmp_path): binning="powspace", power=0.5, n_ell_bins=N_ELL_BINS, - pol_factor=True, + pol_factor=-1, ) cv._test_version = version return cv @@ -265,18 +266,38 @@ def test_unknown_binning_raises(self, cv): cv.get_namaster_bin(LMIN, LMAX, B_LMAX) +# ========================================================================== +# get_pixels -- fetch the pixels from ra, dec, and nside (HEALPix) +# ========================================================================== +def test_get_pixels(cv, cat_and_params): + """Pin the HEALPix pixelization of the synthetic catalog.""" + cat_gal, params = cat_and_params + unique_pix, idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) + + # Structural invariants of the HEALPix occupancy map. + assert unique_pix.size == 485 + assert idx_rep.size == 5000 # one entry per galaxy + + # Pinned scalar summaries: total weight is conserved (sum of weights), + # peak occupancy, and the pixel index bookkeeping. + npt.assert_array_equal(unique_pix[:5], [12167, 12168, 12169, 12170, 12171]) + assert int(unique_pix.sum()) == 7849989 + + # =========================================================================== # get_n_gal_map -- weighted galaxy number-density map # =========================================================================== def test_get_n_gal_map(cv, cat_and_params): cat_gal, params = cat_and_params - n_gal, unique_pix, idx, idx_rep = cv.get_n_gal_map(params, NSIDE, cat_gal) + + unique_pix, idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) + n_gal = cv.get_n_gal_map( + params, NSIDE, cat_gal, unique_pix=unique_pix, idx=None, idx_rep=idx_rep + ) # Structural invariants of the HEALPix occupancy map. assert n_gal.shape == (healpy.nside2npix(NSIDE),) assert int(np.count_nonzero(n_gal)) == 485 - assert unique_pix.size == 485 - assert idx_rep.size == 5000 # one entry per galaxy # The map is supported exactly on the occupied pixels. npt.assert_array_equal(np.nonzero(n_gal)[0], np.sort(unique_pix)) @@ -284,8 +305,6 @@ def test_get_n_gal_map(cv, cat_and_params): # peak occupancy, and the pixel index bookkeeping. npt.assert_allclose(n_gal.sum(), 3760.657282591494, rtol=RTOL_DET) npt.assert_allclose(n_gal.max(), 16.063410433711447, rtol=RTOL_DET) - npt.assert_array_equal(unique_pix[:5], [12167, 12168, 12169, 12170, 12171]) - assert int(unique_pix.sum()) == 7849989 npt.assert_allclose( n_gal[unique_pix][:5], np.array( @@ -307,7 +326,10 @@ def test_get_n_gal_map(cv, cat_and_params): # =========================================================================== def _build_shear_map(cv, cat_gal, params): """Replicate calculate_pseudo_cl_map's weighted shear-map construction.""" - n_gal, unique_pix, _idx, idx_rep = cv.get_n_gal_map(params, NSIDE, cat_gal) + unique_pix, _idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) + n_gal = cv.get_n_gal_map( + params, NSIDE, cat_gal, unique_pix=unique_pix, idx=None, idx_rep=idx_rep + ) w = cat_gal[params["w_col"]] e1 = cat_gal[params["e1_col"]] e2 = cat_gal[params["e2_col"]] @@ -471,7 +493,7 @@ def test_apply_random_rotation_preserves_magnitude(cv, cat_and_params): e1 = np.asarray(cat_gal[params["e1_col"]], dtype=np.float64) e2 = np.asarray(cat_gal[params["e2_col"]], dtype=np.float64) - e1_rot, e2_rot = cv.apply_random_rotation(e1, e2) + e1_rot, e2_rot = apply_random_rotation(e1, e2) assert e1_rot.shape == e1.shape assert e2_rot.shape == e2.shape @@ -491,16 +513,16 @@ def test_apply_random_rotation_reproducible_with_seed(cv, cat_and_params): e1 = np.asarray(cat_gal[params["e1_col"]], dtype=np.float64) e2 = np.asarray(cat_gal[params["e2_col"]], dtype=np.float64) - a1, a2 = cv.apply_random_rotation(e1, e2, np.random.default_rng(42)) - b1, b2 = cv.apply_random_rotation(e1, e2, np.random.default_rng(42)) + a1, a2 = apply_random_rotation(e1, e2, np.random.default_rng(42)) + b1, b2 = apply_random_rotation(e1, e2, np.random.default_rng(42)) npt.assert_array_equal(a1, b1) npt.assert_array_equal(a2, b2) - c1, _ = cv.apply_random_rotation(e1, e2, np.random.default_rng(7)) + c1, _ = apply_random_rotation(e1, e2, np.random.default_rng(7)) assert not np.allclose(a1, c1) - d1, _ = cv.apply_random_rotation(e1, e2) - f1, _ = cv.apply_random_rotation(e1, e2) + d1, _ = apply_random_rotation(e1, e2, None) + f1, _ = apply_random_rotation(e1, e2, None) assert not np.allclose(d1, f1) @@ -515,7 +537,7 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): (it drops the BE row); we pin the round-tripped table. """ ver = cv._test_version - cv._pseudo_cls = {ver: {}} + cv._pseudo_cls = {ver: {"non_tomo": {}}} out_path = cv._output_path(f"pseudo_cl_cat_{ver}.fits") cv.calculate_pseudo_cl_catalog(ver, out_path) From a941f5424c2558a552707883e4b380ba52b82f9f Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 3 Jul 2026 19:43:31 +0200 Subject: [PATCH 012/107] Account for Martin's comment. Merged non-tomo and tomo functions into one API. --- src/sp_validation/cosmo_val/pseudo_cl.py | 177 +++++++--------------- src/sp_validation/pseudo_cl.py | 98 +++++++++++- src/sp_validation/tests/test_pseudo_cl.py | 16 +- 3 files changed, 156 insertions(+), 135 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index f74bd606..f43f125d 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -9,7 +9,6 @@ import configparser import os -import warnings import healpy as hp import matplotlib.pyplot as plt @@ -97,12 +96,15 @@ def calculate_pseudo_cl_eb_cov(self): if ver not in self._pseudo_cls.keys(): self._pseudo_cls[ver] = {} - out_path = self._output_path(f"pseudo_cl_cov_{ver}.fits") + if "non_tomo" not in self._pseudo_cls[ver].keys(): + self._pseudo_cls[ver].update({"non_tomo": {}}) + + out_path = self._output_path_pseudo_cl_cov(ver, "iNKA") if os.path.exists(out_path) and not self.force_run: self.print_done( f"Skipping Pseudo-Cl covariance calculation, {out_path} exists" ) - self._pseudo_cls[ver]["cov"] = fits.open(out_path) + self._pseudo_cls[ver]["non_tomo"]["cov"] = fits.open(out_path) else: params = get_params_rho_tau(self.cc[ver], survey=ver) @@ -447,48 +449,14 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): f"Done Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" ) - def calculate_pseudo_cl(self): - """ - Compute the pseudo-Cl of given catalogs. - """ - self.print_start("Computing pseudo-Cl's") - - nside = self.nside - - self._pseudo_cls = getattr(self, "_pseudo_cls", {}) - for ver in self.versions: - self.print_magenta(ver) - - if ver not in self._pseudo_cls.keys(): - self._pseudo_cls[ver] = {} - - if "non_tomo" not in self._pseudo_cls[ver].keys(): - self._pseudo_cls[ver].update({"non_tomo": {}}) - - out_path = self._output_path_pseudo_cl(ver, tomo_bin_pair=None) - if os.path.exists(out_path) and not self.force_run: - self.print_done(f"Skipping Pseudo-Cl's calculation, {out_path} exists") - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver]["non_tomo"]["pseudo_cl"] = cl_shear - elif self.cell_method == "map": - self.calculate_pseudo_cl_map(ver, nside, out_path) - elif self.cell_method == "catalog": - self.calculate_pseudo_cl_catalog(ver, out_path) - else: - raise ValueError(f"Unknown cell method: {self.cell_method}") - - self.print_done("Done pseudo-Cl's") - - def calculate_pseudo_cl_tomo(self): + def calculate_pseudo_cl(self, compute_tomography=True): """ Compute the pseudo-Cl of a `CosmologyValidation` inputs with tomography. """ - if not self.compute_tomography: - warnings.warn( - "``compute_tomography`` is set to False but tomography will be computed. Check that this is intentional." - ) - - self.print_start("Computing tomographic pseudo-Cl's") + if compute_tomography: + self.print_start("Computing tomographic pseudo-Cl's") + else: + self.print_start("Computing non-tomographic pseudo-Cl's") self._pseudo_cls = getattr(self, "_pseudo_cls", {}) @@ -498,10 +466,16 @@ def calculate_pseudo_cl_tomo(self): if ver not in self.pseudo_cls.keys(): self._pseudo_cls[ver] = {} - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + if compute_tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - if tomo_bin_ids is None or tomo_bin_pairs is None: - raise ValueError(f"Version {ver} does not have tomography information.") + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) + + else: + tomo_bin_pairs = [("all", "all")] # Loop on the different tomographic bin pairs for bin_key1, bin_key2 in tomo_bin_pairs: @@ -529,99 +503,41 @@ def calculate_pseudo_cl_tomo(self): continue if self.cell_method == "map": - self.calculate_pseudo_cl_map_tomo( + self.calculate_pseudo_cl_map( ver, self.nside, out_path, bin_key1, bin_key2 ) elif self.cell_method == "catalog": - ... + self.calculate_pseudo_cl_catalog(ver, out_path, bin_key1, bin_key2) else: raise ValueError(f"Unknown cell method: {self.cell_method}") - def calculate_pseudo_cl_map(self, ver, nside, out_path): - params = get_params_rho_tau(self.cc[ver], survey=ver) - - # Load data and create shear and noise maps - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - - self.print_cyan("Creating maps and computing Cl's...") - - # Get the pixels and indices for the catalog - unique_pix, idx, idx_rep = self.get_pixels(params, nside, cat_gal) - - n_gal_map = self.get_n_gal_map( - params, nside, cat_gal, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep - ) - - shear_map_e1, shear_map_e2 = self.get_shear_map( - params, - nside, - cat_gal, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - n_gal_map=n_gal_map, - ) - - shear_map = shear_map_e1 + 1j * shear_map_e2 + def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): + assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( + isinstance(tomo_bin_a, int) and isinstance(tomo_bin_b, int) + ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." - del shear_map_e1, shear_map_e2 - - ell_eff, cl_shear, wsp = self.get_pseudo_cls_map(shear_map, n_gal_map) - - # Estimate the noise bias component and subtract - cl_noise = self.get_noise_bias_from_gaussian_real( - params, - nside, - cat_gal, - n_gal_map, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - wsp=wsp, - ) - - # Noise realizations are now reproducible (seeded rng from self.cell_seed). - cl_shear = cl_shear - cl_noise - - self.print_cyan("Saving pseudo-Cl's...") - self.save_pseudo_cl(ell_eff, cl_shear, out_path) - - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver]["non_tomo"]["pseudo_cl"] = cl_shear - - def calculate_pseudo_cl_catalog(self, ver, out_path): - params = get_params_rho_tau(self.cc[ver], survey=ver) - - # Load data and create shear and noise maps - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + params = get_params_rho_tau(self.cc[ver]) - ell_eff, cl_shear, _ = self.get_pseudo_cls_catalog( - catalog=cat_gal, params=params + self.print_cyan( + f"Computing pseudo-Cl's for tomographic bins {tomo_bin_a} and {tomo_bin_b}..." ) - self.print_cyan("Saving pseudo-Cl's...") - self.save_pseudo_cl(ell_eff, cl_shear, out_path) - - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver]["non_tomo"]["pseudo_cl"] = cl_shear - - def calculate_pseudo_cl_map_tomo( - self, ver, nside, out_path, tomo_bin_a, tomo_bin_b - ): - params = get_params_rho_tau(self.cc[ver]) - # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - tomo_bin_id = cat_gal[self.cc[ver]["shear"]["tomo_bin_ids"]] - mask_a = tomo_bin_id == tomo_bin_a - mask_b = tomo_bin_id == tomo_bin_b - cat_gal_a = cat_gal[mask_a] - cat_gal_b = cat_gal[mask_b] + if tomo_bin_a != "all" and tomo_bin_b != "all": + cat_gal_a = cat_gal + cat_gal_b = cat_gal + else: + tomo_bin_id = cat_gal[self.cc[ver]["shear"]["tomo_bin_ids"]] + mask_a = tomo_bin_id == tomo_bin_a + mask_b = tomo_bin_id == tomo_bin_b + cat_gal_a = cat_gal[mask_a] + cat_gal_b = cat_gal[mask_b] del cat_gal - print("Creating maps and computing Cl's...") + self.print_cyan("Creating maps and computing Cl's...") # Get the pixels and indices for the catalogs unique_pix_a, idx_a, idx_rep_a = self.get_pixels(params, nside, cat_gal_a) unique_pix_b, idx_b, idx_rep_b = self.get_pixels(params, nside, cat_gal_b) @@ -697,7 +613,11 @@ def calculate_pseudo_cl_map_tomo( "pseudo_cl" ] = cl_shear - def calculate_pseudo_cl_catalog_tomo(self, ver, out_path, tomo_bin_a, tomo_bin_b): + def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): + assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( + isinstance(tomo_bin_a, int) and isinstance(tomo_bin_b, int) + ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." + params = get_params_rho_tau(self.cc[ver]) # Load data and create shear and noise maps @@ -872,6 +792,17 @@ def _output_path_pseudo_cl(self, ver, tomo_bin_pair=None): f"pseudo_cl_from_{self.cell_method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" ) + def _output_path_pseudo_cl_cov(self, ver, method, tomo_bin_pair=None): + if tomo_bin_pair is None: + return self._output_path( + f"pseudo_cl_cov_from_{method}_non_tomo_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" + ) + else: + bin_key1, bin_key2 = tomo_bin_pair + return self._output_path( + f"pseudo_cl_cov_from_{method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" + ) + def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): """ Save pseudo-Cl's to a FITS file. diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index b96525fa..279c0be6 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -17,6 +17,8 @@ import numpy as np import pymaster as nmt +from sp_validation.cosmology import get_theo_c_ell + # Lowest multipole retained by the pseudo-Cl estimators. LMIN = 8 @@ -635,8 +637,8 @@ def get_pseudo_cls_catalog( nside, binning, *, - tomo_bin_a=None, - tomo_bin_b=None, + tomo_bin_a="all", + tomo_bin_b="all", pol_factor=-1, wsp=None, ell_step=10, @@ -656,6 +658,8 @@ def get_pseudo_cls_catalog( HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. + tomo_bin_a, tomo_bin_b : str or int or None, optional + Tomographic bin IDs for the two fields. pol_factor : int, optional Polarization factor to apply to the E2 component. wsp : nmt.NmtWorkspace, optional @@ -673,9 +677,9 @@ def get_pseudo_cls_catalog( The coupling workspace (newly built or the one passed in). """ # First make some assertion checks reagarding the run mode - assert (tomo_bin_a is None and tomo_bin_b is None) or ( - tomo_bin_a is not None and tomo_bin_b is not None - ), "Both tomo_bin_a and tomo_bin_b must be provided or both must be None" + assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( + tomo_bin_a != "all" and tomo_bin_b != "all" + ), "Both tomo_bin_a and tomo_bin_b must be provided or both must be 'all'" lmin, lmax, b_lmax = pseudo_cl_geometry(nside) @@ -690,7 +694,7 @@ def get_pseudo_cls_catalog( ) ell_eff = b.get_effective_ells() - is_tomography = tomo_bin_a is not None and tomo_bin_b is not None + is_tomography = tomo_bin_a != "all" and tomo_bin_b != "all" if is_tomography: mask_tomo_a = catalog[params["tomo_bin_col"]] == tomo_bin_a mask_tomo_b = catalog[params["tomo_bin_col"]] == tomo_bin_b @@ -742,3 +746,85 @@ def get_pseudo_cls_catalog( ) return ell_eff, cl_decoupled, wsp + + +# ---------------------- Covariance computation functions ---------------------- +def get_fiducial_cl(z, dndz, lmax, cosmo): + """ + Get the fiducial Cl's using the redshift distribution. + Cosmology is determined by the input cosmo object. + """ + ell = np.arange(1, lmax + 1) + + fiducial_cl = get_theo_c_ell(ell=ell, z=z, dndz=dndz, backend="camb", cosmo=cosmo) + + return fiducial_cl + + +def get_pseudo_cl_iNKA_covariance( + input_cl_a1_b1, + input_cl_a1_b2, + input_cl_a2_b1, + input_cl_a2_b2, + field_a1, + field_a2, + field_b1, + field_b2, + wsp_a, + wsp_b, + b, +): + """Compute the iNKA covariance for pseudo-Cl. + + Parameters + ---------- + input_cl_a1_b1 : np.ndarray + Input Cl for field a1 and b1. + input_cl_a1_b2 : np.ndarray + Input Cl for field a1 and b2. + input_cl_a2_b1 : np.ndarray + Input Cl for field a2 and b1. + input_cl_a2_b2 : np.ndarray + Input Cl for field a2 and b2. + field_a1 : nmt.NmtField + NaMaster field object for the first catalog (a1). + field_a2 : nmt.NmtField + NaMaster field object for the second catalog (a2). + field_b1 : nmt.NmtField + NaMaster field object for the first catalog (b1). + field_b2 : nmt.NmtField + NaMaster field object for the second catalog (b2). + wsp_a : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix for fields a. + wsp_b : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix for fields b. + b : nmt.NmtBin + NaMaster binning object. + + Returns + ------- + cov_matrix : np.ndarray + Covariance matrix of the pseudo-Cl, shape ``(n_bins, n_bins)``. + """ + # Compute the coupling coefficients for the covariance + cw = nmt.NmtCovarianceWorkspace.from_fields(field_a1, field_a2, field_b1, field_b2) + + # Get actual number of ell bins from binning scheme + n_ell_actual = b.get_n_bands() + + # Compute the covariance using NaMaster's built-in function + cov_matrix = nmt.compute_covariance( + cw, + 2, + 2, + 2, + 2, + input_cl_a1_b1, + input_cl_a1_b2, + input_cl_a2_b1, + input_cl_a2_b2, + wsp_a, + wb=wsp_b, + ).reshape([n_ell_actual, 4, n_ell_actual, 4]) + + return cov_matrix diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index be5c6d7d..cdaef9d4 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -415,7 +415,9 @@ def test_get_pseudo_cls_map(cv, cat_and_params): # =========================================================================== def test_get_pseudo_cls_catalog(cv, cat_and_params): cat_gal, params = cat_and_params - ell_eff, cl_all, wsp = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) + ell_eff, cl_all, wsp = cv.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" + ) assert cl_all.shape == (4, N_ELL_BINS) # Effective ells share the binning math with the map path: bitwise-stable. @@ -521,8 +523,8 @@ def test_apply_random_rotation_reproducible_with_seed(cv, cat_and_params): c1, _ = apply_random_rotation(e1, e2, np.random.default_rng(7)) assert not np.allclose(a1, c1) - d1, _ = apply_random_rotation(e1, e2, None) - f1, _ = apply_random_rotation(e1, e2, None) + d1, _ = apply_random_rotation(e1, e2) + f1, _ = apply_random_rotation(e1, e2) assert not np.allclose(d1, f1) @@ -537,9 +539,9 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): (it drops the BE row); we pin the round-tripped table. """ ver = cv._test_version - cv._pseudo_cls = {ver: {"non_tomo": {}}} + cv._pseudo_cls = {ver: {"tomo_bin_all_tomo_bin_all": {}}} out_path = cv._output_path(f"pseudo_cl_cat_{ver}.fits") - cv.calculate_pseudo_cl_catalog(ver, out_path) + cv.calculate_pseudo_cl_catalog(ver, out_path, tomo_bin_a="all", tomo_bin_b="all") assert os.path.exists(out_path) d = fits.getdata(out_path) @@ -610,5 +612,7 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): # (same computation, FITS round-trip) -- consistency, not an independent pin. cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) params = get_params_rho_tau(cv.cc[ver], survey=ver) - _, cl_prim, _ = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) + _, cl_prim, _ = cv.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" + ) npt.assert_allclose(ee, cl_prim[0], rtol=RTOL_CAT, atol=ATOL_CAT) From a1d28a4fd504930d8d3c254454586c3d90b919fa Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 6 Jul 2026 09:04:06 +0200 Subject: [PATCH 013/107] Account for Fable review --- src/sp_validation/cosmo_val/core.py | 5 ++++- src/sp_validation/cosmo_val/pseudo_cl.py | 11 +++++++---- src/sp_validation/pseudo_cl.py | 3 +++ src/sp_validation/rho_tau.py | 1 + 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 389699d6..9fe76d90 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -219,7 +219,7 @@ def __init__( power=1 / 2, n_ell_bins=32, ell_step=10, - pol_factor=True, + pol_factor=-1, cell_method="map", noise_bias_method="analytic", fiducial_input_inka="coupled", @@ -250,7 +250,10 @@ def __init__( self.power = power self.n_ell_bins = n_ell_bins self.ell_step = ell_step + + assert pol_factor in (-1, 1), "The polarisatio factor must be -1 or 1." self.pol_factor = pol_factor + self.nrandom_cell = nrandom_cell self.cell_seed = cell_seed self.cell_method = cell_method diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index f43f125d..c405282e 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -513,7 +513,8 @@ def calculate_pseudo_cl(self, compute_tomography=True): def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - isinstance(tomo_bin_a, int) and isinstance(tomo_bin_b, int) + isinstance(tomo_bin_a, (int, np.integer)) + and isinstance(tomo_bin_b, (int, np.integer)) ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." params = get_params_rho_tau(self.cc[ver]) @@ -525,11 +526,11 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - if tomo_bin_a != "all" and tomo_bin_b != "all": + if tomo_bin_a == "all" and tomo_bin_b == "all": cat_gal_a = cat_gal cat_gal_b = cat_gal else: - tomo_bin_id = cat_gal[self.cc[ver]["shear"]["tomo_bin_ids"]] + tomo_bin_id = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] mask_a = tomo_bin_id == tomo_bin_a mask_b = tomo_bin_id == tomo_bin_b cat_gal_a = cat_gal[mask_a] @@ -600,6 +601,7 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): idx=idx_a, idx_rep=idx_rep_a, wsp=wsp, + n_el_bins=cl_shear.shape[1], ) # Subtract the noise bias from the pseudo-Cl's @@ -716,12 +718,13 @@ def get_noise_bias_from_gaussian_real( nside, cat_gal, n_gal_map, + n_ell_bins, unique_pix=None, idx=None, idx_rep=None, wsp=None, ): - cl_noise = np.zeros_like() + cl_noise = np.zeros_like(n_ell_bins) rng = np.random.default_rng(self.cell_seed) for _ in range(self.nrandom_cell): diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index 279c0be6..7a35e57b 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -696,6 +696,9 @@ def get_pseudo_cls_catalog( is_tomography = tomo_bin_a != "all" and tomo_bin_b != "all" if is_tomography: + assert params["tomo_bin_col"] is not None, ( + "The column of tomographic bin ids is not specified." + ) mask_tomo_a = catalog[params["tomo_bin_col"]] == tomo_bin_a mask_tomo_b = catalog[params["tomo_bin_col"]] == tomo_bin_b catalog_a = catalog[mask_tomo_a] diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 0a311432..d89f67fb 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -61,6 +61,7 @@ def get_params_rho_tau(cat, survey="other"): params["w_col"] = cat["shear"]["w_col"] params["e1_col"] = cat["shear"]["e1_col"] params["e2_col"] = cat["shear"]["e2_col"] + params["tomo_bin_col"] = cat["shear"]["tomo_bin_col"] params["R11"] = cat["shear"].get("R11") params["R22"] = cat["shear"].get("R22") From 2b84eb6a7d8ca3c9e38d801a5aa753a1eb20ea60 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 6 Jul 2026 09:12:44 +0200 Subject: [PATCH 014/107] (fix) KeyError for the unspecified tomo_bin_col --- src/sp_validation/rho_tau.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index d89f67fb..b8bb523b 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -61,7 +61,10 @@ def get_params_rho_tau(cat, survey="other"): params["w_col"] = cat["shear"]["w_col"] params["e1_col"] = cat["shear"]["e1_col"] params["e2_col"] = cat["shear"]["e2_col"] - params["tomo_bin_col"] = cat["shear"]["tomo_bin_col"] + try: + params["tomo_bin_col"] = cat["shear"]["tomo_bin_col"] + except KeyError: + params["tomo_bin_col"] = None params["R11"] = cat["shear"].get("R11") params["R22"] = cat["shear"].get("R22") From 753abc13d6a67583ec7bf2ad0a13ff73d98d3ecf Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 6 Jul 2026 14:49:26 +0200 Subject: [PATCH 015/107] Upgrade the iNKA covariance estimation to tomography --- src/sp_validation/cosmo_val/pseudo_cl.py | 493 +++++++++++++---------- src/sp_validation/pseudo_cl.py | 217 +++++++++- 2 files changed, 487 insertions(+), 223 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index c405282e..3769e6f8 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -13,19 +13,10 @@ import healpy as hp import matplotlib.pyplot as plt import numpy as np -import pymaster as nmt from astropy.io import fits -from ..cosmology import get_theo_c_ell -from ..pseudo_cl import ( - get_n_gal_map, - get_noise_realisation, - get_pixels, - get_pseudo_cls_catalog, - get_pseudo_cls_map, - get_shear_map, - make_namaster_bin, -) +import sp_validation.pseudo_cl as spv_pseudo_cl + from ..rho_tau import get_params_rho_tau from ..statistics import chi2_and_pte, cov_from_one_covariance @@ -35,10 +26,10 @@ class PseudoClMixin: @property def pseudo_cls(self): if not hasattr(self, "_pseudo_cls"): - self.calculate_pseudo_cl() - self.calculate_pseudo_cl_eb_cov() + self.calculate_pseudo_cl(compute_tomography=False) + self.calculate_pseudo_cl_eb_cov(compute_tomography=False) if self.compute_tomography: - self.calculate_pseudo_cl_tomo() + self.calculate_pseudo_cl_tomo(compute_tomography=True) return self._pseudo_cls @property @@ -49,38 +40,6 @@ def pseudo_cls_onecov(self): # ---------------- Pseudo-Cl calculation methods ---------------- # # TODO: some cleaning to clearly separate DV, covariance, and utility functions. - def get_variance_map(self, nside, e1, e2, w, unique_pix, idx_rep): - """ - Create a variance map from the input catalog. - """ - - variance_map = np.zeros(hp.nside2npix(nside)) - - variance_map[unique_pix] = np.bincount( - idx_rep, weights=(e1**2 + e2**2) / 2 * w**2 - ) - - return variance_map - - def get_field_and_workspace_from_map(self, mask, lmax, b): - """ - Create a NaMaster field and workspace from the input map. - """ - - nside = hp.npix2nside(len(mask)) - - # Create NaMaster field - f = nmt.NmtField( - mask=mask, - maps=[np.zeros(hp.nside2npix(nside)), np.zeros(hp.nside2npix(nside))], - lmax=lmax, - ) - - # Create NaMaster workspace - wsp = nmt.NmtWorkspace.from_fields(f, f, b) - - return f, wsp - def calculate_pseudo_cl_eb_cov(self): """ Compute a theoretical Gaussian covariance of the Pseudo-Cl for EE, EB and BB. @@ -96,174 +55,191 @@ def calculate_pseudo_cl_eb_cov(self): if ver not in self._pseudo_cls.keys(): self._pseudo_cls[ver] = {} - if "non_tomo" not in self._pseudo_cls[ver].keys(): - self._pseudo_cls[ver].update({"non_tomo": {}}) - - out_path = self._output_path_pseudo_cl_cov(ver, "iNKA") - if os.path.exists(out_path) and not self.force_run: - self.print_done( - f"Skipping Pseudo-Cl covariance calculation, {out_path} exists" - ) - self._pseudo_cls[ver]["non_tomo"]["cov"] = fits.open(out_path) - else: - params = get_params_rho_tau(self.cc[ver], survey=ver) - - self.print_cyan(f"Extracting the fiducial power spectrum for {ver}") + if compute_tomography: + tom_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - lmax = 2 * self.nside - ell = np.arange(1, lmax + 1) - pw = hp.pixwin(nside, lmax=lmax) - if pw.shape[0] != len(ell) + 1: + if tomo_bin_ids is None or tomo_bin_pairs is None: raise ValueError( - "Unexpected pixwin length for lmax=" - f"{lmax}: got {pw.shape[0]}, expected {len(ell) + 1}" - ) - pw = pw[1 : len(ell) + 1] - - # Load redshift distribution and calculate theory C_ell - path_redshift_distr = self.cc[ver]["shear"]["redshift_path"] - z, dndz = np.loadtxt(path_redshift_distr, unpack=True) - fiducial_cl = ( - get_theo_c_ell( - ell=ell, - z=z, - nz=dndz, - backend="ccl", - cosmo=self.cosmo, + f"Version {ver} does not have tomography information." ) - * pw**2 - ) - - self.print_cyan("Getting a binning, n_gal_map, field and workspace.") - lmin = 8 - lmax = 2 * self.nside - b_lmax = lmax - 1 - - b = self.get_namaster_bin(lmin, lmax, b_lmax) - - # Load data and create shear and noise maps - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - - n_gal, unique_pix, _idx, idx_rep = self.get_n_gal_map( - params, nside, cat_gal - ) + else: + tomo_bin_pairs = [("all", "all")] - f, wsp = self.get_field_and_workspace_from_map(n_gal, b_lmax, b) - - if self.noise_bias_method == "randoms": - self.print_cyan("Getting a sample of Cls with noise bias.") - - cl_noise, f, wsp = self.get_sample( - params, - self.nside, - b_lmax, - b, - cat_gal, - n_gal, - n_gal, - unique_pix, - idx_rep, - np.random.default_rng(self.cell_seed), - ) + # Initialise dictionnary to store field and workspace + n_gal_map_dict = {} + field_dict = {} + wsp_dict = {} - noise_bias_cl = np.mean(cl_noise, axis=0) + self.print_cyan(f"Extracting the fiducial power spectrum for {ver}") - elif self.noise_bias_method == "analytic": - self.print_cyan("Getting analytic noise bias.") + fiducial_cl = self.get_fiducial_cl(ver, compute_tomography) - e1, e2, w = ( - cat_gal[self.cc[ver]["shear"]["e1_col"]], - cat_gal[self.cc[ver]["shear"]["e2_col"]], - cat_gal[self.cc[ver]["shear"]["w_col"]], - ) - variance_map = self.get_variance_map( - self.nside, e1, e2, w, unique_pix, idx_rep - ) + self.print_cyan( + "Estimating and adding the noise bias to the fiducial power spectra" + ) - noise_bias = hp.nside2pixarea(self.nside) * np.mean(variance_map) + params = get_params_rho_tau(self.cc[ver]) + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - noise_bias_cl = np.zeros((4, lmax)) - noise_bias_cl[0, :] = noise_bias - noise_bias_cl[3, :] = noise_bias + for bin_key1, bin_key2 in tomo_bin_pairs: + if bin_key1 == bin_key2: + cat_gal_ = self._get_tomographic_bin(params, cat_gal, bin_key1) - noise_bias_cl = wsp.decouple_cell(noise_bias_cl) # Decouple + noise_bias_cl = self.get_noise_bias(params, nside, cat_gal_) else: - raise ValueError( - f"Noise bias method {self.noise_bias_method} not recognized. It should be 'randoms' or 'analytic'." - ) + noise_bias_cl = np.zeros_like((4, 2 * nside)) - # Unbin, then fill the data vector below lmin with the lowest-ell value - noise_bias_cl = b.unbin_cell(noise_bias_cl) - lowest_ell = b.get_ell_list(0)[0] - noise_bias_cl[:, :lowest_ell] = noise_bias_cl[:, [lowest_ell]] - - self.print_cyan("Adding noise bias to the fiducial Cls.") - - fiducial_cl = ( + # Update the fiducial_cl dictionnary + fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = ( np.array( [ - fiducial_cl, - 0.0 * fiducial_cl, - 0.0 * fiducial_cl, - 0.0 * fiducial_cl, + fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], ] ) + noise_bias_cl ) + # Compute the fields and workspaces + for bin_key1, bin_key2 in tomo_bin_pairs: + self.print_cyan( + f"Computing fields and workspaces for {bin_key1}, {bin_key2}" + ) + lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(self.nside) + b = get_namaster_bin(lmin, lmax, b_lmax) + + # Get the tomographic bins + cat_gal_a = self._get_tomographic_bin(params, cat_gal, bin_key1) + cat_gal_b = self._get_tomographic_bin(params, cat_gal, bin_key2) + + # Compute the n_gal_maps and the wsp object + unique_pix_a, idx_a, idx_rep_a = self.get_pixels( + params, nside, cat_gal_a + ) + unique_pix_b, idx_b, idx_rep_b = self.get_pixels( + params, nside, cat_gal_b + ) + + # Compute the number density maps + n_gal_map_a = self.get_n_gal_map(params, nside, cat_gal_a) + n_gal_map_b = self.get_n_gal_map(params, nside, cat_gal_b) + + # Get the shear maps + shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( + params, + nside, + cat_gal_a, + self.nside, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + ) + shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( + params, + nside, + cat_gal_b, + self.nside, + unique_pix=unique_pix_b, + idx=idx_b, + idx_rep=idx_rep_b, + ) + + # Get the fields and workspaces + field_a, field_b, wsp = spv_pseudo_cl.get_field_and_workspace_from_map( + b, + mask_a=n_gal_map_a, + e1_map_a=shear_map_a_e1, + e2_map_a=shear_map_a_e2, + mask_b=n_gal_map_b, + e1_map_b=shear_map_b_e1, + e2_map_b=shear_map_b_e2, + pol_factor=self.pol_factor, + return_wsp=True, + ) + + # Save in the dictionnaries + if not hasattr(n_gal_map_dict, f"W{bin_key1}"): + n_gal_map_dict[f"W{bin_key1}"] = n_gal_map_a + if not hasattr(n_gal_map_dict, f"W{bin_key2}"): + n_gal_map_dict[f"W{bin_key2}"] = n_gal_map_b + if not hasattr(field_dict, f"W{bin_key1}"): + field_dict[f"W{bin_key1}"] = field_a + if not hasattr(field_dict, f"W{bin_key2}"): + field_dict[f"W{bin_key2}"] = field_b + if bin_key1 <= bin_key2 and not hasattr( + wsp_dict, f"W{bin_key1}xW{bin_key2}" + ): + wsp_dict[f"W{bin_key1}xW{bin_key2}"] = wsp + + for bin_key1, bin_key2 in tomo_bin_pairs: + # Couple the cell if required if self.fiducial_input_inka == "coupled": self.print_cyan("Coupling the fiducial Cls.") + # Get the wsp object + n_gal_map_a = n_gal_map_dict[f"W{bin_key1}"] + n_gal_map_b = n_gal_map_dict[f"W{bin_key2}"] + wsp = wsp_dict[f"W{bin_key1}xW{bin_key2}"] coupling_mat = wsp.get_coupling_matrix() coupling_mat_re = np.reshape( coupling_mat, (4, lmax, 4, lmax), order="F" ) - fiducial_cl = np.tensordot(coupling_mat_re, fiducial_cl) / np.mean( - n_gal**2 - ) # couple and divide by the mean of the mask squared + fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = np.tensordot( + coupling_mat_re, fiducial_cl[f"W{bin_key1}xW{bin_key2}"] + ) / np.mean( + n_gal_map_a * n_gal_map_b + ) # couple and divide by the product of the mask - self.print_cyan("Computing the Pseudo-Cl covariance") + # Loop on the different tomographic bin pairs to compute the covariance + for bin_key1, bin_key2 in tomo_bin_pairs: + self.print_cyan(f"Tomo Bin Pair: ({bin_key1}, {bin_key2})") - cw = nmt.NmtCovarianceWorkspace.from_fields(f, f, f, f) - - # Get actual number of ell bins from binning scheme - n_ell_actual = b.get_n_bands() - - covar_22_22 = nmt.gaussian_covariance( - cw, - 2, - 2, - 2, - 2, - fiducial_cl, - fiducial_cl, - fiducial_cl, - fiducial_cl, - wsp, - wb=wsp, - ).reshape([n_ell_actual, 4, n_ell_actual, 4]) + if ( + f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + not in self._pseudo_cls[ver].keys() + ): + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + ] = {} - self.print_cyan("Saving Pseudo-Cl covariance") + out_path = self._output_path_pseudo_cl_cov( + ver, "iNKA", tomo_bin_pair=(bin_key1, bin_key2) + ) - # covar_22_22 is indexed [ell, pol_a, ell, pol_b]; store each of the - # 16 EE/EB/BE/BB cross-blocks as a named HDU (row-major pol order). - # Append rather than construct from a list so astropy promotes the - # first HDU to a PrimaryHDU on write. - pols = ["EE", "EB", "BE", "BB"] - hdu = fits.HDUList() - for i, pa in enumerate(pols): - for j, pb in enumerate(pols): - hdu.append( - fits.ImageHDU( - covar_22_22[:, i, :, j], name=f"COVAR_{pa}_{pb}" - ) - ) + if os.path.exists(out_path) and not self.force_run: + self.print_done( + f"Skipping Pseudo-Cl covariance calculation, {out_path} exists" + ) + self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ + "cov" + ] = fits.open(out_path) + continue - hdu.writeto(out_path, overwrite=True) + self.print_cyan("Computing the Pseudo-Cl covariance") - self._pseudo_cls[ver]["cov"] = hdu + covar_22_22 = spv_pseudo_cl.get_pseudo_cl_iNKA_covariance( + fiducial_cl[f"W{bin_key1}xW{bin_key1}"], + fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + fiducial_cl[f"W{bin_key2}xW{bin_key1}"], + fiducial_cl[f"W{bin_key2}xW{bin_key2}"], + field_dict[f"W{bin_key1}"], + field_dict[f"W{bin_key2}"], + field_dict[f"W{bin_key1}"], + field_dict[f"W{bin_key2}"], + wsp_a=wsp_dict[f"W{bin_key1}xW{bin_key2}"], + wsp_b=wsp_dict[f"W{bin_key2}xW{bin_key1}"], + b=b, + ) + + self.print_cyan("Saving Pseudo-Cl covariance") + + self._pseudo_cls[ver]["cov"] = self._save_iNKA_covariance( + covar_22_22, out_path + ) self.print_done("Done Pseudo-Cl covariance") @@ -526,15 +502,9 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - if tomo_bin_a == "all" and tomo_bin_b == "all": - cat_gal_a = cat_gal - cat_gal_b = cat_gal - else: - tomo_bin_id = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] - mask_a = tomo_bin_id == tomo_bin_a - mask_b = tomo_bin_id == tomo_bin_b - cat_gal_a = cat_gal[mask_a] - cat_gal_b = cat_gal[mask_b] + # Get the tomographic bin + cat_gal_a = self._get_tomographic_bin(params, cat_gal, tomo_bin_a) + cat_gal_b = self._get_tomographic_bin(params, cat_gal, tomo_bin_b) del cat_gal @@ -596,12 +566,11 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): params, nside, cat_gal_a, - n_gal_map_a, unique_pix=unique_pix_a, idx=idx_a, idx_rep=idx_rep_a, + n_gal_map=n_gal_map_a, wsp=wsp, - n_el_bins=cl_shear.shape[1], ) # Subtract the noise bias from the pseudo-Cl's @@ -640,7 +609,7 @@ def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): # ---------------- Utility functions for pseudo-Cl calculations ---------------- # def get_namaster_bin(self, lmin, lmax, b_lmax): """Build NaMaster binning object (thin wrapper, state -> primitive).""" - return make_namaster_bin( + return spv_pseudo_cl.make_namaster_bin( lmin, lmax, b_lmax, @@ -652,13 +621,15 @@ def get_namaster_bin(self, lmin, lmax, b_lmax): def get_pixels(self, params, nside, cat_gal): """Get unique pixels and indices for a catalog (thin wrapper -> primitive).""" - return get_pixels(cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], nside) + return spv_pseudo_cl.get_pixels( + cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], nside + ) def get_n_gal_map( self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None ): """Weighted galaxy number-density map (thin wrapper -> primitive).""" - return get_n_gal_map( + return spv_pseudo_cl.get_n_gal_map( nside, cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], @@ -672,7 +643,7 @@ def get_shear_map( self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None ): """Weighted shear map (thin wrapper -> primitive).""" - return get_shear_map( + return spv_pseudo_cl.get_shear_map( cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], cat_gal[params["e1_col"]], @@ -698,7 +669,7 @@ def get_noise_realisation( """ Get a single Gaussian noise realization (thin wrapper -> primitive). """ - return get_noise_realisation( + return spv_pseudo_cl.get_noise_realisation( cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], cat_gal[params["e1_col"]], @@ -717,42 +688,73 @@ def get_noise_bias_from_gaussian_real( params, nside, cat_gal, - n_gal_map, - n_ell_bins, unique_pix=None, idx=None, idx_rep=None, + n_gal_map=None, wsp=None, ): - cl_noise = np.zeros_like(n_ell_bins) - rng = np.random.default_rng(self.cell_seed) - - for _ in range(self.nrandom_cell): - noise_map_e1, noise_map_e2 = self.get_noise_realisation( - params, - nside, - cat_gal, - n_gal=n_gal_map, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - rng=rng, - ) - - noise_map = noise_map_e1 + 1j * noise_map_e2 - del noise_map_e1, noise_map_e2 + """Noise-bias from Gaussian realisations (thin wrapper, state -> primitive)""" + return spv_pseudo_cl.get_noise_bias_from_gaussian_real( + cat_gal[params["ra_col"]], + cat_gal[params["dec_col"]], + cat_gal[params["e1_col"]], + cat_gal[params["e2_col"]], + cat_gal[params["w_col"]], + nside, + nrandom_cell=self.nrandom_cell, + binning=self.binning, + ell_step=self.ell_step, + n_ell_bins=self.n_ell_bins, + power=self.power, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + n_gal_map=n_gal_map, + wsp=wsp, + seed=self.cell_seed, + ) - _, cl_noise_, _ = self.get_pseudo_cls_map(noise_map, n_gal_map, wsp) - cl_noise += cl_noise_ + def get_noise_bias_analytical( + self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None + ): + """Noise-bias from analytical prescription (thin wrapper, state -> primitive)""" + return spv_pseudo_cl.get_noise_bias_analytical( + cat_gal[params["ra_col"]], + cat_gal[params["dec_col"]], + cat_gal[params["e1_col"]], + cat_gal[params["e2_col"]], + cat_gal[params["w_col"]], + lmax=2 * nside, + nside=nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + ) - cl_noise /= self.nrandom_cell - return cl_noise + def get_noise_bias(self, params, nside, cat_gal): + """Noise-bias estimation (thin wrapper, state -> primitive)""" + return spv_pseudo_cl.get_noise_bias( + cat_gal[params["ra_col"]], + cat_gal[params["dec_col"]], + cat_gal[params["e1_col"]], + cat_gal[params["e2_col"]], + cat_gal[params["w_col"]], + nside, + noise_bias_method=self.noise_bias_method, + binning=self.binning, + ell_step=self.ell_step, + n_ell_bins=self.n_ell_bins, + power=self.power, + nrandom_cell=self.nrandom_cell, + seed=self.cell_seed, + ) def get_pseudo_cls_map( self, map_a, mask_a, wsp=None, shear_map_b=None, mask_b=None ): """Map-based pseudo-cl (thin wrapper, state -> primitive).""" - return get_pseudo_cls_map( + return spv_pseudo_cl.get_pseudo_cls_map( map_a, mask_a, self.nside, @@ -770,7 +772,7 @@ def get_pseudo_cls_catalog( self, catalog, params, wsp=None, tomo_bin_a=None, tomo_bin_b=None ): """Catalog-based pseudo-cl (thin wrapper, state -> primitive).""" - return get_pseudo_cls_catalog( + return spv_pseudo_cl.get_pseudo_cls_catalog( catalog, params, self.nside, @@ -784,6 +786,38 @@ def get_pseudo_cls_catalog( power=self.power, ) + def read_redshift_distribution(ver, is_tomography): + path_redshift_distr = self.cc[ver]["shear"]["redshift_path"] + z, dndz = np.loadtxt(path_redshift_distr, unpack=True) + + # Here it is assumed that the tomographic redshift distribution sum to the non-tomographic one and that the latter is normalised + if not is_tomography: + dndz = np.sum(dndz, axis=1) + + return z, dndz + + def get_fiducial_cl(ver, is_tomography): + lmax = 2 * self.nside + ell = np.arange(1, lmax + 1) + + z, dndz = self.read_redshift_distribution(ver, is_tomography) + + fiducial_cl = spv_pseudo_cl.get_fiducial_cl(z, dndz, lmax, self.cosmo) + + # If non-tomographic, change the key to 'WallxWall' + if not is_tomography: + fiducial_cl = {"WallxWall": fiducial_cl["W1xW1"]} + + return fiducial_cl + + def _get_tomographic_bin(self, params, cat_gal, tomo_bin): + if tomo_bin == "all": + return cat_gal + else: + tomo_bin_id = cat_gal[params["tomo_bin_col"]] + mask = tomo_bin_id == tomo_bin + return cat_gal[mask] + def _output_path_pseudo_cl(self, ver, tomo_bin_pair=None): if tomo_bin_pair is None: return self._output_path( @@ -827,6 +861,23 @@ def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): cell_hdu.writeto(out_path, overwrite=True) + def _save_iNKA_covariance(covar, out_path): + # covar_22_22 is indexed [ell, pol_a, ell, pol_b]; store each of the + # 16 EE/EB/BE/BB cross-blocks as a named HDU (row-major pol order). + # Append rather than construct from a list so astropy promotes the + # first HDU to a PrimaryHDU on write. + pols = ["EE", "EB", "BE", "BB"] + hdu = fits.HDUList() + for i, pa in enumerate(pols): + for j, pb in enumerate(pols): + hdu.append( + fits.ImageHDU(covar_22_22[:, i, :, j], name=f"COVAR_{pa}_{pb}") + ) + + hdu.writeto(out_path, overwrite=True) + + return hdu + # ---------------- Plotting functions for pseudo-Cl's ---------------- # def plot_pseudo_cl(self): """ diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index 7a35e57b..ef0a6875 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -315,6 +315,219 @@ def get_noise_realisation( return noise_map_e1, noise_map_e2 +def get_noise_bias_from_gaussian_real( + ra, + dec, + e1, + e2, + w, + nside, + nrandom_cell, + binning, + ell_step=10, + n_ell_bins=32, + power=0.5, + unique_pix=None, + idx=None, + idx_rep=None, + n_gal_map=None, + wsp=None, + seed=42, +): + """ + Compute the power spectrum of the noise bias from random realisations. + + Parameters + ---------- + ra, dec : np.ndarray + Right ascension and declination of the sources. + e1, e2 : np.ndarray + Ellipticity components. + w : np.ndarray + Weights of the sources. + nside : int + HEALPix resolution. + nrandom_cell : int + Number of random cells to use for the noise estimation. + binning, ell_step, n_ell_bins, power : str, int, int, float + Binning scheme and parameters. + unique_pix, idx, idx_rep : np.ndarray, optional + Pixel indices and bookkeeping arrays. If not provided, they will be computed. + n_gal_map : np.ndarray, optional + Galaxy number density map. If not provided, it will be computed. + + Returns + ------- + noise_bias_cl : np.ndarray + Power spectrum of the noise bias. + """ + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) + + b = make_namaster_bin( + lmin, + lmax, + b_lmax, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + ) + + ell_eff = b.get_effective_ells() + noise_bias_cl = np.zeros_like(ell_eff) + + rng = np.random.default_rng(seed) + + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + if n_gal_map is None: + n_gal_map = get_n_gal_map( + nside, ra, dec, weights=w, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) + + if wsp is None: + _, _, wsp = get_field_and_workspace_from_map(b, mask_a=n_gal_map_a) + + for _ in range(nrandom_cell): + noise_map_e1, noise_map_e2 = get_noise_realisation( + ra, + dec, + e1, + e2, + w, + nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + n_gal_map=n_gal_map, + rng=rng, + ) + + noise_map = noise_map_e1 + 1j * noise_map_e2 + del noise_map_e1, noise_map_e2 + + _, cl_noise_, _ = get_pseudo_cls_map( + noise_map, + n_gal_map, + nside, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + wsp=wsp, + ) + + noise_bias_cl += cl_noise_ + + noise_bias_cl /= nrandom_cell + + return noise_bias_cl + + +def get_noise_bias( + ra, + dec, + e1, + e2, + w, + nside, + noise_bias_method, + binnning, + *, + ell_step=10, + n_ell_bins=32, + power=0.5, + nrandom_cell=100, + seed=42, +): + """ + Compute the noise bias from object positions and ellipticities. + + Parameters + ---------- + ra, dec : np.ndarray + Right ascension and declination of the sources. + e1, e2 : np.ndarray + Ellipticity components. + w : np.ndarray + Weights of the sources. + nside : int + HEALPix resolution. + noise_bias_method : {'randoms', 'analytic'} + Method used to estimate the noise bias. + binning : {'linear', 'logspace', 'powspace'} + Binning scheme. + ell_step : int, optional + Bin width in ell for ``'linear'`` binning. + n_ell_bins : int, optional + Number of ell bins for ``'logspace'`` / ``'powspace'`` binning. + power : float, optional + Exponent for ``'powspace'`` binning. + + Returns + ------- + noise_bias_cl : np.ndarray + Power spectrum of the noise bias. + """ + assert noise_bias_method in ["randoms", "analytic"] + + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) + + b = make_namaster_bin( + lmin, + lmax, + b_lmax, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + ) + + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + if noise_bias_method == "analytic": + noise_bias_cl = get_noise_bias_analytical( + ra, + dec, + e1, + e2, + w, + lmax, + nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + ) + + elif noise_bias_method == "randoms": + noise_bias_cl = get_noise_bias_from_gaussian_real( + ra, + dec, + e1, + e2, + w, + nside, + nrandom_cell, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + seed=42, + ) + + noise_bias_cl = b.unbin_cell(noise_bias_cl) + else: + raise ValueError( + f"Invalid noise bias method `{noise_bias_method}`. Must be 'analytic' or 'randoms'." + ) + + return noise_bias_cl + + # ---------------------- Cl computation functions ---------------------- def get_field_and_workspace_from_map( b, @@ -752,14 +965,14 @@ def get_pseudo_cls_catalog( # ---------------------- Covariance computation functions ---------------------- -def get_fiducial_cl(z, dndz, lmax, cosmo): +def get_fiducial_cl(z, dndz, lmax, cosmo, backend="camb"): """ Get the fiducial Cl's using the redshift distribution. Cosmology is determined by the input cosmo object. """ ell = np.arange(1, lmax + 1) - fiducial_cl = get_theo_c_ell(ell=ell, z=z, dndz=dndz, backend="camb", cosmo=cosmo) + fiducial_cl = get_theo_c_ell(ell=ell, z=z, dndz=dndz, backend=backend, cosmo=cosmo) return fiducial_cl From ac31d60c40264ebdb048c41ca160c7f53db9ac69 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 6 Jul 2026 14:54:22 +0200 Subject: [PATCH 016/107] (ruff) Fix bugs flagged by Ruff at the previous commit step --- src/sp_validation/cosmo_val/pseudo_cl.py | 15 ++++++--------- src/sp_validation/pseudo_cl.py | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 3769e6f8..6830e72f 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -40,7 +40,7 @@ def pseudo_cls_onecov(self): # ---------------- Pseudo-Cl calculation methods ---------------- # # TODO: some cleaning to clearly separate DV, covariance, and utility functions. - def calculate_pseudo_cl_eb_cov(self): + def calculate_pseudo_cl_eb_cov(self, compute_tomography=True): """ Compute a theoretical Gaussian covariance of the Pseudo-Cl for EE, EB and BB. """ @@ -56,7 +56,7 @@ def calculate_pseudo_cl_eb_cov(self): self._pseudo_cls[ver] = {} if compute_tomography: - tom_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) if tomo_bin_ids is None or tomo_bin_pairs is None: raise ValueError( @@ -110,7 +110,7 @@ def calculate_pseudo_cl_eb_cov(self): f"Computing fields and workspaces for {bin_key1}, {bin_key2}" ) lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(self.nside) - b = get_namaster_bin(lmin, lmax, b_lmax) + b = self.get_namaster_bin(lmin, lmax, b_lmax) # Get the tomographic bins cat_gal_a = self._get_tomographic_bin(params, cat_gal, bin_key1) @@ -786,7 +786,7 @@ def get_pseudo_cls_catalog( power=self.power, ) - def read_redshift_distribution(ver, is_tomography): + def read_redshift_distribution(self, ver, is_tomography): path_redshift_distr = self.cc[ver]["shear"]["redshift_path"] z, dndz = np.loadtxt(path_redshift_distr, unpack=True) @@ -796,9 +796,8 @@ def read_redshift_distribution(ver, is_tomography): return z, dndz - def get_fiducial_cl(ver, is_tomography): + def get_fiducial_cl(self, ver, is_tomography): lmax = 2 * self.nside - ell = np.arange(1, lmax + 1) z, dndz = self.read_redshift_distribution(ver, is_tomography) @@ -870,9 +869,7 @@ def _save_iNKA_covariance(covar, out_path): hdu = fits.HDUList() for i, pa in enumerate(pols): for j, pb in enumerate(pols): - hdu.append( - fits.ImageHDU(covar_22_22[:, i, :, j], name=f"COVAR_{pa}_{pb}") - ) + hdu.append(fits.ImageHDU(covar[:, i, :, j], name=f"COVAR_{pa}_{pb}")) hdu.writeto(out_path, overwrite=True) diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index ef0a6875..dd40eea1 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -387,7 +387,7 @@ def get_noise_bias_from_gaussian_real( ) if wsp is None: - _, _, wsp = get_field_and_workspace_from_map(b, mask_a=n_gal_map_a) + _, _, wsp = get_field_and_workspace_from_map(b, mask_a=n_gal_map) for _ in range(nrandom_cell): noise_map_e1, noise_map_e2 = get_noise_realisation( @@ -433,7 +433,7 @@ def get_noise_bias( w, nside, noise_bias_method, - binnning, + binning, *, ell_step=10, n_ell_bins=32, From 12842c89450df6972258a7073e66f7ed1f4b87af Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 6 Jul 2026 15:15:44 +0200 Subject: [PATCH 017/107] (fix) Implement the bug fix detected by Copilot --- src/sp_validation/cosmo_val/core.py | 4 +- src/sp_validation/cosmo_val/pseudo_cl.py | 51 ++++++++++++++---------- src/sp_validation/pseudo_cl.py | 11 +++-- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 9fe76d90..5b88aacf 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -656,12 +656,12 @@ def _get_tomo_bins(self, version): tomo_bin_pairs : list of tuples or None List of unique pairs of tomographic bin IDs (including self-pairs) for the version, or None if no tomography is available """ - if "tomo_bin_ids" in self.cc[version]["shear"]: + if "tomo_bin_col" in self.cc[version]["shear"]: self.print_cyan( f"Extracting tomography information from version {version}." ) cat_gal = fits.getdata(self.cc[version]["shear"]["path"]) - tomo_bin = cat_gal[self.cc[version]["shear"]["tomo_bin_ids"]] + tomo_bin = cat_gal[self.cc[version]["shear"]["tomo_bin_col"]] tomo_bin_ids = np.unique(tomo_bin) tomo_bin_ids = tomo_bin_ids[ tomo_bin_ids > 0 diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 6830e72f..0dd62d8e 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -27,9 +27,10 @@ class PseudoClMixin: def pseudo_cls(self): if not hasattr(self, "_pseudo_cls"): self.calculate_pseudo_cl(compute_tomography=False) - self.calculate_pseudo_cl_eb_cov(compute_tomography=False) + self.calculate_pseudo_cl_inka_cov(compute_tomography=False) if self.compute_tomography: - self.calculate_pseudo_cl_tomo(compute_tomography=True) + self.calculate_pseudo_cl(compute_tomography=True) + self.calculate_pseudo_cl_inka_cov(compute_tomography=True) return self._pseudo_cls @property @@ -40,7 +41,7 @@ def pseudo_cls_onecov(self): # ---------------- Pseudo-Cl calculation methods ---------------- # # TODO: some cleaning to clearly separate DV, covariance, and utility functions. - def calculate_pseudo_cl_eb_cov(self, compute_tomography=True): + def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): """ Compute a theoretical Gaussian covariance of the Pseudo-Cl for EE, EB and BB. """ @@ -89,7 +90,7 @@ def calculate_pseudo_cl_eb_cov(self, compute_tomography=True): noise_bias_cl = self.get_noise_bias(params, nside, cat_gal_) else: - noise_bias_cl = np.zeros_like((4, 2 * nside)) + noise_bias_cl = np.zeros((4, 2 * nside)) # Update the fiducial_cl dictionnary fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = ( @@ -131,18 +132,16 @@ def calculate_pseudo_cl_eb_cov(self, compute_tomography=True): # Get the shear maps shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( params, - nside, - cat_gal_a, self.nside, + cat_gal_a, unique_pix=unique_pix_a, idx=idx_a, idx_rep=idx_rep_a, ) shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( params, - nside, - cat_gal_b, self.nside, + cat_gal_b, unique_pix=unique_pix_b, idx=idx_b, idx_rep=idx_rep_b, @@ -162,17 +161,15 @@ def calculate_pseudo_cl_eb_cov(self, compute_tomography=True): ) # Save in the dictionnaries - if not hasattr(n_gal_map_dict, f"W{bin_key1}"): + if not f"W{bin_key1}" not in n_gal_map_dict: n_gal_map_dict[f"W{bin_key1}"] = n_gal_map_a - if not hasattr(n_gal_map_dict, f"W{bin_key2}"): + if f"W{bin_key2}" not in n_gal_map_dict: n_gal_map_dict[f"W{bin_key2}"] = n_gal_map_b - if not hasattr(field_dict, f"W{bin_key1}"): + if f"W{bin_key1}" not in field_dict: field_dict[f"W{bin_key1}"] = field_a - if not hasattr(field_dict, f"W{bin_key2}"): + if f"W{bin_key2}" not in field_dict: field_dict[f"W{bin_key2}"] = field_b - if bin_key1 <= bin_key2 and not hasattr( - wsp_dict, f"W{bin_key1}xW{bin_key2}" - ): + if bin_key1 <= bin_key2 and f"W{bin_key1}xW{bin_key2}" not in wsp_dict: wsp_dict[f"W{bin_key1}xW{bin_key2}"] = wsp for bin_key1, bin_key2 in tomo_bin_pairs: @@ -237,9 +234,9 @@ def calculate_pseudo_cl_eb_cov(self, compute_tomography=True): self.print_cyan("Saving Pseudo-Cl covariance") - self._pseudo_cls[ver]["cov"] = self._save_iNKA_covariance( - covar_22_22, out_path - ) + self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ + "cov" + ] = self._save_iNKA_covariance(covar_22_22, out_path) self.print_done("Done Pseudo-Cl covariance") @@ -533,6 +530,7 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): # Create shear maps for each tomographic bin shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( + params, nside, cat_gal_a, unique_pix=unique_pix_a, @@ -544,6 +542,7 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): del shear_map_a_e1, shear_map_a_e2 shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( + params, nside, cat_gal_b, unique_pix=unique_pix_b, @@ -556,7 +555,7 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): # Compute the pseudo-Cl's ell_eff, cl_shear, wsp = self.get_pseudo_cls_map( - shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, n_gal_map_b=n_gal_map_b + shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, mask_b=n_gal_map_b ) # Remove the noise bias for auto-correlations. @@ -640,7 +639,14 @@ def get_n_gal_map( ) def get_shear_map( - self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None + self, + params, + nside, + cat_gal, + unique_pix=None, + idx=None, + idx_rep=None, + n_gal_map=None, ): """Weighted shear map (thin wrapper -> primitive).""" return spv_pseudo_cl.get_shear_map( @@ -653,6 +659,7 @@ def get_shear_map( unique_pix=unique_pix, idx=idx, idx_rep=idx_rep, + n_gal_map=n_gal_map, ) def get_noise_realisation( @@ -676,7 +683,7 @@ def get_noise_realisation( cat_gal[params["e2_col"]], cat_gal[params["w_col"]], nside, - n_gal=n_gal, + n_gal_map=n_gal, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep, @@ -860,7 +867,7 @@ def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): cell_hdu.writeto(out_path, overwrite=True) - def _save_iNKA_covariance(covar, out_path): + def _save_iNKA_covariance(self, covar, out_path): # covar_22_22 is indexed [ell, pol_a, ell, pol_b]; store each of the # 16 EE/EB/BE/BB cross-blocks as a named HDU (row-major pol order). # Append rather than construct from a list so astropy promotes the diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index dd40eea1..fb574527 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -374,7 +374,7 @@ def get_noise_bias_from_gaussian_real( ) ell_eff = b.get_effective_ells() - noise_bias_cl = np.zeros_like(ell_eff) + noise_bias_cl = np.zeros((4, ell_eff.size)) rng = np.random.default_rng(seed) @@ -464,13 +464,18 @@ def get_noise_bias( Number of ell bins for ``'logspace'`` / ``'powspace'`` binning. power : float, optional Exponent for ``'powspace'`` binning. + nrandom_cell : int, optional + Number of random cells to use for the noise estimation. (only for the `randoms` method) + seed : int, optional + Random seed for reproducibility. (only for the `randoms` method) Returns ------- noise_bias_cl : np.ndarray Power spectrum of the noise bias. """ - assert noise_bias_method in ["randoms", "analytic"] + if noise_bias_method not in ["randoms", "analytic"]: + raise ValueError("noise_bias_method must be 'randoms' or 'analytic'") lmin, lmax, b_lmax = pseudo_cl_geometry(nside) @@ -516,7 +521,7 @@ def get_noise_bias( unique_pix=unique_pix, idx=idx, idx_rep=idx_rep, - seed=42, + seed=seed, ) noise_bias_cl = b.unbin_cell(noise_bias_cl) From 0f6daa32e3afa116f3d98a07c31866d8f599e36e Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 6 Jul 2026 16:21:10 +0200 Subject: [PATCH 018/107] (fix) GLASS mock allowing negative right ascension conflicting with NaMaster --- scripts/glass_mock/make_glass_sim.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/glass_mock/make_glass_sim.py b/scripts/glass_mock/make_glass_sim.py index 4db41735..2cbb5d16 100644 --- a/scripts/glass_mock/make_glass_sim.py +++ b/scripts/glass_mock/make_glass_sim.py @@ -330,6 +330,7 @@ def galaxies_simulation(self): ) catalogue = np.empty(gal_count, dtype=cat_dtype) + gal_lon[gal_lon < 0] += 360 catalogue["RA"] = gal_lon catalogue["Dec"] = gal_lat catalogue["e1"] = gal_she.real From 38289f0e4de3edbc57c33841bdfb93d53ad119f1 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 8 Jul 2026 11:13:55 +0200 Subject: [PATCH 019/107] Finalise the loop to compute iNKA pseudo-cl covariance. Add GLASS mock to the config file. --- cosmo_val/cat_config.yaml | 46 +++++ src/sp_validation/cosmo_val/pseudo_cl.py | 251 +++++++++++++++++++---- src/sp_validation/pseudo_cl.py | 7 +- 3 files changed, 262 insertions(+), 42 deletions(-) diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index 0b949372..07eb84d3 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1286,3 +1286,49 @@ SP_v1.6.6: e1_col: e1 e2_col: e2 path: unions_shapepipe_star_2024_v1.6.a.fits + +GLASS_mock_validation: + subdir: /n09data/guerrini/glass_mock_test/results/ + pipeline: SP + colour: violet + getdist_colour: 0.0, 0.5, 1.0 + ls: dashed + marker: '*' + cov_th: + A: 2405.3892055695346 + n_e: 6.128201234871523 + n_psf: 0.752316232272063 + sigma_e: 0.379587601488189 + mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits + psf: + PSF_flag: FLAG_PSF_HSM + PSF_size: SIGMA_PSF_HSM + square_size: true + star_flag: FLAG_STAR_HSM + star_size: SIGMA_STAR_HSM + hdu: 1 + path: unions_shapepipe_psf_2024_v1.6.a.fits + ra_col: RA + dec_col: Dec + e1_PSF_col: E1_PSF_HSM + e1_star_col: E1_STAR_HSM + e2_PSF_col: E2_PSF_HSM + e2_star_col: E2_STAR_HSM + shear: + R: 1.0 + path: tomo_test_2_glass_sim_00001_1024.fits + redshift_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/test_data/redshift_distribution_tomo.txt + w_col: w + e1_col: e1 + e1_PSF_col: e1_PSF + e2_col: e2 + e2_PSF_col: e2_PSF + cols: RA,Dec + tomo_bin_col: tom_bin_id + star: + ra_col: RA + dec_col: Dec + e1_col: e1 + e2_col: e2 + path: unions_shapepipe_star_2024_v1.6.a.fits + diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 0dd62d8e..4bbb5626 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -8,6 +8,7 @@ """ import configparser +import itertools import os import healpy as hp @@ -53,9 +54,23 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): for ver in self.versions: self.print_magenta(ver) + out_dir_block = self._output_path(f"pseudo_cl/iNKA_block_{ver}/") + os.makedirs(out_dir_block, exist_ok=True) + if ver not in self._pseudo_cls.keys(): self._pseudo_cls[ver] = {} + out_path_merged = self._output_path_pseudo_cl_cov( + ver, "iNKA", tomography=compute_tomography + ) + + if os.path.exists(out_path_merged) and not self.force_run: + self.print_done( + f"Skipping Pseudo-Cl iNKA covariance calculation, {out_path_merged} exists" + ) + self._pseudo_cls[ver]["cov_iNKA"] = fits.open(out_path_merged) + continue + if compute_tomography: tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) @@ -79,6 +94,7 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): self.print_cyan( "Estimating and adding the noise bias to the fiducial power spectra" ) + self.print_cyan(f"Method used: {self.noise_bias_method}") params = get_params_rho_tau(self.cc[ver]) cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) @@ -172,10 +188,10 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): if bin_key1 <= bin_key2 and f"W{bin_key1}xW{bin_key2}" not in wsp_dict: wsp_dict[f"W{bin_key1}xW{bin_key2}"] = wsp - for bin_key1, bin_key2 in tomo_bin_pairs: + if self.fiducial_input_inka == "coupled": # Couple the cell if required - if self.fiducial_input_inka == "coupled": - self.print_cyan("Coupling the fiducial Cls.") + self.print_cyan("Coupling the fiducial Cls.") + for bin_key1, bin_key2 in tomo_bin_pairs: # Get the wsp object n_gal_map_a = n_gal_map_dict[f"W{bin_key1}"] n_gal_map_b = n_gal_map_dict[f"W{bin_key2}"] @@ -191,53 +207,118 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): n_gal_map_a * n_gal_map_b ) # couple and divide by the product of the mask + # To compute all the blocks in the covariance, + # we must compute all the (i, j, k, l) bin combinations + # Or equivalently, compute the covariance for all spectra pairs + n_spectra = len(tomo_bin_pairs) + + # indices into tomo_bin_pairs for all unique covariance blocks + block_indices = list( + itertools.combinations_with_replacement(range(n_spectra), 2) + ) # Loop on the different tomographic bin pairs to compute the covariance - for bin_key1, bin_key2 in tomo_bin_pairs: - self.print_cyan(f"Tomo Bin Pair: ({bin_key1}, {bin_key2})") + for index_a, index_b in block_indices: + bin_key_a1, bin_key_a2 = tomo_bin_pairs[index_a] + bin_key_b1, bin_key_b2 = tomo_bin_pairs[index_b] + self.print_cyan( + f"Tomo Bin Quad: ({bin_key_a1}, {bin_key_a2}, {bin_key_b1}, {bin_key_b2})" + ) if ( - f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" not in self._pseudo_cls[ver].keys() ): self._pseudo_cls[ver][ - f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" ] = {} - out_path = self._output_path_pseudo_cl_cov( - ver, "iNKA", tomo_bin_pair=(bin_key1, bin_key2) + if ( + bin_key_a1 == bin_key_b1 + and bin_key_a2 == bin_key_b2 + and ( + f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" + not in self._pseudo_cls[ver].keys() + ) + ): + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" + ] = {} + + out_path = self._output_path_iNKA_block_cov( + ver, tomo_bin_quad=(bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2) ) if os.path.exists(out_path) and not self.force_run: self.print_done( - f"Skipping Pseudo-Cl covariance calculation, {out_path} exists" + f"Skipping Pseudo-Cl covariance block Cl ({bin_key_a1, bin_key_a2}), Cl ({bin_key_b1, bin_key_b2}) calculation, {out_path} exists" ) - self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ - "cov" + + self._pseudo_cls[ver][ + f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" ] = fits.open(out_path) + + if bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2: + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" + ]["cov"] = fits.open(out_path) + continue self.print_cyan("Computing the Pseudo-Cl covariance") + input_cl_a1_b1 = ( + fiducial_cl[f"W{bin_key_a1}xW{bin_key_b1}"] + if bin_key_a1 <= bin_key_b1 + else fiducial_cl[f"W{bin_key_b1}xW{bin_key_a1}"] + ) + input_cl_a1_b2 = ( + fiducial_cl[f"W{bin_key_a1}xW{bin_key_b2}"] + if bin_key_a1 <= bin_key_a2 + else fiducial_cl[f"W{bin_key_b2}xW{bin_key_a1}"] + ) + input_cl_a2_b1 = ( + fiducial_cl[f"W{bin_key_a2}xW{bin_key_b1}"] + if bin_key_a2 <= bin_key_b1 + else fiducial_cl[f"W{bin_key_b1}xW{bin_key_a2}"] + ) + input_cl_a2_b2 = ( + fiducial_cl[f"W{bin_key_a2}xW{bin_key_b2}"] + if bin_key_a2 <= bin_key_b2 + else fiducial_cl[f"W{bin_key_b2}xW{bin_key_a2}"] + ) + covar_22_22 = spv_pseudo_cl.get_pseudo_cl_iNKA_covariance( - fiducial_cl[f"W{bin_key1}xW{bin_key1}"], - fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - fiducial_cl[f"W{bin_key2}xW{bin_key1}"], - fiducial_cl[f"W{bin_key2}xW{bin_key2}"], - field_dict[f"W{bin_key1}"], - field_dict[f"W{bin_key2}"], - field_dict[f"W{bin_key1}"], - field_dict[f"W{bin_key2}"], - wsp_a=wsp_dict[f"W{bin_key1}xW{bin_key2}"], - wsp_b=wsp_dict[f"W{bin_key2}xW{bin_key1}"], + input_cl_a1_b1, + input_cl_a1_b2, + input_cl_a2_b1, + input_cl_a2_b2, + field_dict[f"W{bin_key_a1}"], + field_dict[f"W{bin_key_a2}"], + field_dict[f"W{bin_key_b1}"], + field_dict[f"W{bin_key_b2}"], + wsp_a=wsp_dict[f"W{bin_key_a1}xW{bin_key_a2}"], + wsp_b=wsp_dict[f"W{bin_key_b1}xW{bin_key_b2}"], b=b, ) self.print_cyan("Saving Pseudo-Cl covariance") - self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ - "cov" + self._pseudo_cls[ver][ + f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" ] = self._save_iNKA_covariance(covar_22_22, out_path) + if bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2: + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" + ]["cov"] = self._pseudo_cls[ver][ + f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" + ] + + # Merge the covariance blocks + self._pseudo_cls[ver]["cov_iNKA"] = self._merge_iNKA_covariance( + ver, tomography=compute_tomography + ) + self.print_done(f"Done Pseudo-Cl covariance calculation for {ver}") self.print_done("Done Pseudo-Cl covariance") def calculate_pseudo_cl_onecovariance(self): @@ -426,6 +507,9 @@ def calculate_pseudo_cl(self, compute_tomography=True): """ Compute the pseudo-Cl of a `CosmologyValidation` inputs with tomography. """ + out_dir = self._output_path("pseudo_cl") + os.makedirs(out_dir, exist_ok=True) + if compute_tomography: self.print_start("Computing tomographic pseudo-Cl's") else: @@ -585,7 +669,8 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - isinstance(tomo_bin_a, int) and isinstance(tomo_bin_b, int) + isinstance(tomo_bin_a, (int, np.integer)) + and isinstance(tomo_bin_b, (int, np.integer)) ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." params = get_params_rho_tau(self.cc[ver]) @@ -795,7 +880,9 @@ def get_pseudo_cls_catalog( def read_redshift_distribution(self, ver, is_tomography): path_redshift_distr = self.cc[ver]["shear"]["redshift_path"] - z, dndz = np.loadtxt(path_redshift_distr, unpack=True) + redshift_distribution = np.loadtxt(path_redshift_distr) + z = redshift_distribution[:, 0] + dndz = redshift_distribution[:, 1:] # Here it is assumed that the tomographic redshift distribution sum to the non-tomographic one and that the latter is normalised if not is_tomography: @@ -804,6 +891,7 @@ def read_redshift_distribution(self, ver, is_tomography): return z, dndz def get_fiducial_cl(self, ver, is_tomography): + """Get a theory prediction for the angular power spectra (thin wrapper, state -> primitive).""" lmax = 2 * self.nside z, dndz = self.read_redshift_distribution(ver, is_tomography) @@ -827,24 +915,30 @@ def _get_tomographic_bin(self, params, cat_gal, tomo_bin): def _output_path_pseudo_cl(self, ver, tomo_bin_pair=None): if tomo_bin_pair is None: return self._output_path( - f"pseudo_cl_from_{self.cell_method}_non_tomo_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" + "pseudo_cl", + f"pseudo_cl_from_{self.cell_method}_non_tomo_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", ) else: bin_key1, bin_key2 = tomo_bin_pair return self._output_path( - f"pseudo_cl_from_{self.cell_method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" + "pseudo_cl", + f"pseudo_cl_from_{self.cell_method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", ) - def _output_path_pseudo_cl_cov(self, ver, method, tomo_bin_pair=None): - if tomo_bin_pair is None: - return self._output_path( - f"pseudo_cl_cov_from_{method}_non_tomo_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" - ) - else: - bin_key1, bin_key2 = tomo_bin_pair - return self._output_path( - f"pseudo_cl_cov_from_{method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits" - ) + def _output_path_pseudo_cl_cov(self, ver, method, tomography): + is_tomo = "tomo" if tomography else "non_tomo" + return self._output_path( + "pseudo_cl", + f"pseudo_cl_cov_{is_tomo}_{ver}_from_{method}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", + ) + + def _output_path_iNKA_block_cov(self, ver, tomo_bin_quad): + bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2 = tomo_bin_quad + return self._output_path( + "pseudo_cl", + f"iNKA_block_{ver}", + f"pseudo_cl_cov_from_iNKA_tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}_tomo_bin_{bin_key_b1}_tomo_bin_{bin_key_b2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", + ) def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): """ @@ -882,6 +976,75 @@ def _save_iNKA_covariance(self, covar, out_path): return hdu + def _merge_iNKA_covariance(self, ver, tomography): + """ + Merge the iNKA covariance matrices for a given version to get the data vector covariance. + """ + out_path = self._output_path_pseudo_cl_cov( + ver, method="iNKA", tomography=tomography + ) + + if tomography: + # Merge the tomographic covariance matrices + tomo_bin_ids, tomo_bin_pairs = self._get_tomographic_bin(ver) + + # Get the number of bins from the pseudo_cls attribute + # The non-tomographic pseudo-cl are computed from the call + # to this attribute. + n_ell = self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"][ + "pseudo_cl" + ].shape[1] + + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise AssertionError( + f"Tomographic bin IDs of version {ver} is not available." + ) + + n_spectra = len(tomo_bin_pairs) + block_indices = list( + itertools.combinations_with_replacement(range(n_spectra), 2) + ) + + pols = ["EE", "EB", "BE", "BB"] + covar = fits.HDUList() + for pa in pols: + for pb in pols: + full_cov = np.zeros((n_spectra * n_ell, n_spectra * n_ell)) + for index_a, index_b in block_indices: + bin_key_a1, bin_key_a2 = tomo_bin_pairs[index_a] + bin_key_b1, bin_key_b2 = tomo_bin_pairs[index_b] + + block_path = self._output_path_iNKA_block_cov( + ver, + tomo_bin_quad=( + bin_key_a1, + bin_key_a2, + bin_key_b1, + bin_key_b2, + ), + ) + block = fits.open(block_path)[f"COVAR_{pa}_{pb}"].data + + sl_a = slice(index_a * n_ell, (index_a + 1) * n_ell) + sl_b = slice(index_b * n_ell, (index_b + 1) * n_ell) + + full_cov[sl_a, sl_b] = block + + if index_a != index_b: + full_cov[sl_b, sl_a] = block.T + + covar.append(fits.ImageHDU(full_cov, name=f"COVAR_{pa}_{pb}")) + + else: + block_path = self._output_path_iNKA_block_cov( + ver, tomo_bin_quad=(bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2) + ) + covar = fits.open(block_path) + + covar.writeto(out_path, overwrite=True) + + return covar + # ---------------- Plotting functions for pseudo-Cl's ---------------- # def plot_pseudo_cl(self): """ @@ -1082,3 +1245,15 @@ def plot_pseudo_cl(self): pte_bb=np.array(pte_bb), ) print(f" Saved BB data to {bb_out}") + + def plot_pseudo_cl_ee(self, tomography=True): + """ + Plot the pseudo-Cl for EE power spectrum. + """ + pass + + def plot_pseudo_cl_eb_bb(self, tomography=True, plot_eb=False, plot_bb=True): + """ + Plot the pseudo-Cl for EB and BB power spectra. + """ + pass diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index fb574527..3508e804 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -16,8 +16,7 @@ import healpy as hp import numpy as np import pymaster as nmt - -from sp_validation.cosmology import get_theo_c_ell +from cs_util.cosmo import get_theo_c_ell # Lowest multipole retained by the pseudo-Cl estimators. LMIN = 8 @@ -977,7 +976,7 @@ def get_fiducial_cl(z, dndz, lmax, cosmo, backend="camb"): """ ell = np.arange(1, lmax + 1) - fiducial_cl = get_theo_c_ell(ell=ell, z=z, dndz=dndz, backend=backend, cosmo=cosmo) + fiducial_cl = get_theo_c_ell(ell=ell, z=z, nz=dndz, backend=backend, cosmo=cosmo) return fiducial_cl @@ -1034,7 +1033,7 @@ def get_pseudo_cl_iNKA_covariance( n_ell_actual = b.get_n_bands() # Compute the covariance using NaMaster's built-in function - cov_matrix = nmt.compute_covariance( + cov_matrix = nmt.gaussian_covariance( cw, 2, 2, From 81a010a750566e307e53112c3a04a7408a75f1c2 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 9 Jul 2026 09:48:22 +0200 Subject: [PATCH 020/107] Clean bugs and add function to plot nicely the pseudo-cl. --- src/sp_validation/cosmo_val/pseudo_cl.py | 574 +++++++++++++++-------- 1 file changed, 374 insertions(+), 200 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 4bbb5626..d8c1caf8 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -7,6 +7,7 @@ on pymaster (NaMaster), healpy, and OneCovariance. """ +import colorsys import configparser import itertools import os @@ -15,11 +16,12 @@ import matplotlib.pyplot as plt import numpy as np from astropy.io import fits +from matplotlib.colors import to_rgb import sp_validation.pseudo_cl as spv_pseudo_cl from ..rho_tau import get_params_rho_tau -from ..statistics import chi2_and_pte, cov_from_one_covariance +from ..statistics import cov_from_one_covariance class PseudoClMixin: @@ -28,10 +30,14 @@ class PseudoClMixin: def pseudo_cls(self): if not hasattr(self, "_pseudo_cls"): self.calculate_pseudo_cl(compute_tomography=False) - self.calculate_pseudo_cl_inka_cov(compute_tomography=False) + self.calculate_pseudo_cl_inka_cov( + compute_tomography=False, load_all_block=True + ) if self.compute_tomography: self.calculate_pseudo_cl(compute_tomography=True) - self.calculate_pseudo_cl_inka_cov(compute_tomography=True) + self.calculate_pseudo_cl_inka_cov( + compute_tomography=True, load_all_block=True + ) return self._pseudo_cls @property @@ -42,7 +48,9 @@ def pseudo_cls_onecov(self): # ---------------- Pseudo-Cl calculation methods ---------------- # # TODO: some cleaning to clearly separate DV, covariance, and utility functions. - def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): + def calculate_pseudo_cl_inka_cov( + self, compute_tomography=True, load_all_block=False + ): """ Compute a theoretical Gaussian covariance of the Pseudo-Cl for EE, EB and BB. """ @@ -64,13 +72,6 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): ver, "iNKA", tomography=compute_tomography ) - if os.path.exists(out_path_merged) and not self.force_run: - self.print_done( - f"Skipping Pseudo-Cl iNKA covariance calculation, {out_path_merged} exists" - ) - self._pseudo_cls[ver]["cov_iNKA"] = fits.open(out_path_merged) - continue - if compute_tomography: tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) @@ -82,6 +83,51 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): else: tomo_bin_pairs = [("all", "all")] + if os.path.exists(out_path_merged) and not self.force_run: + self.print_done( + f"Skipping Pseudo-Cl iNKA covariance calculation, {out_path_merged} exists" + ) + self._pseudo_cls[ver]["cov_iNKA"] = fits.open(out_path_merged) + + if load_all_block: + self.print_done("Loading all the iNKA covariance blocks") + n_spectra = len(tomo_bin_pairs) + + # indices into tomo_bin_pairs for all unique covariance blocks + block_indices = list( + itertools.combinations_with_replacement(range(n_spectra), 2) + ) + + for index_a, index_b in block_indices: + bin_key_a1, bin_key_a2 = tomo_bin_pairs[index_a] + bin_key_b1, bin_key_b2 = tomo_bin_pairs[index_b] + + load_path = self._output_path_iNKA_block_cov( + ver, + tomo_bin_quad=( + bin_key_a1, + bin_key_a2, + bin_key_b1, + bin_key_b2, + ), + ) + + if os.path.exists(load_path): + self._pseudo_cls[ver][ + f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" + ] = fits.open(load_path) + + if bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2: + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" + ]["cov"] = fits.open(load_path) + else: + raise FileNotFoundError( + "The file does not exist, please run `cosmo_val` with `force_run` to True." + ) + + continue + # Initialise dictionnary to store field and workspace n_gal_map_dict = {} field_dict = {} @@ -224,14 +270,6 @@ def calculate_pseudo_cl_inka_cov(self, compute_tomography=True): f"Tomo Bin Quad: ({bin_key_a1}, {bin_key_a2}, {bin_key_b1}, {bin_key_b2})" ) - if ( - f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" - not in self._pseudo_cls[ver].keys() - ): - self._pseudo_cls[ver][ - f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" - ] = {} - if ( bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2 @@ -905,6 +943,7 @@ def get_fiducial_cl(self, ver, is_tomography): return fiducial_cl def _get_tomographic_bin(self, params, cat_gal, tomo_bin): + """Extract tomographic bin from a given catalogue""" if tomo_bin == "all": return cat_gal else: @@ -955,8 +994,9 @@ def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): col1 = fits.Column(name="ELL", format="D", array=ell_eff) col2 = fits.Column(name="EE", format="D", array=pseudo_cl[0]) col3 = fits.Column(name="EB", format="D", array=pseudo_cl[1]) - col4 = fits.Column(name="BB", format="D", array=pseudo_cl[3]) - coldefs = fits.ColDefs([col1, col2, col3, col4]) + col4 = fits.Column(name="BE", format="D", array=pseudo_cl[2]) + col5 = fits.Column(name="BB", format="D", array=pseudo_cl[3]) + coldefs = fits.ColDefs([col1, col2, col3, col4, col5]) cell_hdu = fits.BinTableHDU.from_columns(coldefs, name="PSEUDO_CELL") cell_hdu.writeto(out_path, overwrite=True) @@ -984,16 +1024,18 @@ def _merge_iNKA_covariance(self, ver, tomography): ver, method="iNKA", tomography=tomography ) + print(self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"]) + if tomography: # Merge the tomographic covariance matrices - tomo_bin_ids, tomo_bin_pairs = self._get_tomographic_bin(ver) + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) # Get the number of bins from the pseudo_cls attribute # The non-tomographic pseudo-cl are computed from the call # to this attribute. - n_ell = self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"][ - "pseudo_cl" - ].shape[1] + n_ell = self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"]["pseudo_cl"][ + "ELL" + ].shape[0] if tomo_bin_ids is None or tomo_bin_pairs is None: raise AssertionError( @@ -1046,214 +1088,346 @@ def _merge_iNKA_covariance(self, ver, tomography): return covar # ---------------- Plotting functions for pseudo-Cl's ---------------- # - def plot_pseudo_cl(self): - """ - Plot pseudo-Cl's for given catalogs. + def plot_pseudo_cl( + self, + pol_list, + versions=None, + ell_factor="ell", + cov_type="iNKA", + offset=0.15, + tomography=True, + savefig=None, + show=True, + ): """ - self.print_cyan("Plotting pseudo-Cl's") + Plot the pseudo-Cl for EE power spectrum. - # Plotting EE - out_path = self._output_path("cell_ee.png") - fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(8, 8)) + Parameters + ---------- + pol_list : list + List of polarization types to plot (e.g., ["EE", "BB"]). + ell_factor : {"None", "ell", "ell(ell+1)"} + Factor to multiply the ell values by. + cov_type : {"iNKA"} + Type of covariance to use. + tomography : bool, optional + Whether to plot tomographic power spectra. + savefig : str, optional + Path to save the figure. + show : bool, optional + Whether to show the figure. + + Returns + ------- + fig, ax : matplotlib.figure.Figure, matplotlib.axes.Axes + Figure and axes objects for the plot. + """ + if versions is None: + versions = self.versions + else: + for ver in versions: + if ver not in self.versions: + raise ValueError( + f"Version {ver} is not available. Available versions: {self.versions}" + ) + # Check that the method for the covariance is valid + if cov_type not in ["iNKA"]: + raise ValueError( + f"Invalid covariance type: {cov_type}. Valid options are: ['iNKA']" + ) - for ver in self.versions: - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - cov = self.pseudo_cls[ver]["cov"]["COVAR_EE_EE"].data - ax[0].errorbar( - ell, - ell * self.pseudo_cls[ver]["pseudo_cl"]["EE"], - yerr=ell * np.sqrt(np.diag(cov)), - fmt=self.cc[ver]["marker"], - label=ver + " EE", - color=self.cc[ver]["colour"], - capsize=2, + # Check that the ell_factor is valid + if ell_factor not in ["None", "ell", "ell(ell+1)"]: + raise ValueError( + f"Invalid ell_factor: {ell_factor}. Valid options are: ['None', 'ell', 'ell(ell+1)']" ) - ax[0].set_ylabel(r"$\ell C_\ell$") + def get_ell_factor(ell): + """Given some ell, return the ell_factor for the plot.""" + if ell_factor == "None": + return 1 + elif ell_factor == "ell": + return ell + elif ell_factor == "ell(ell+1)": + return ell * (ell + 1) + + # Check that all items in the list are valid polarisation + valid_pols = ["EE", "BB", "EB", "BE"] + for pol in pol_list: + if pol not in valid_pols: + raise ValueError( + f"Invalid polarization type: {pol}. Valid options are: {valid_pols}" + ) - ax[0].set_xlim(ell.min() - 10, ell.max() + 100) - ax[0].set_xscale("squareroot") - ax[0].set_xticks(np.array([100, 400, 900, 1600])) - ax[0].minorticks_on() - ax[0].tick_params(axis="x", which="minor", length=2, width=0.8) - minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] - ax[0].xaxis.set_ticks(minor_ticks, minor=True) + fmt_dict = {"EE": "o", "BB": "s", "EB": "^", "BE": "v"} + # From all the versions, get the maximum number of tomo_bin_ids + tomo_bins = {} + for ver in versions: + if tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + else: + tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] - for ver in self.versions: - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - cov = self.pseudo_cls[ver]["cov"]["COVAR_EE_EE"].data - ax[1].errorbar( - ell, - self.pseudo_cls[ver]["pseudo_cl"]["EE"], - yerr=np.sqrt(np.diag(cov)), - fmt=self.cc[ver]["marker"], - label=ver + " EE", - color=self.cc[ver]["colour"], - ) + tomo_bins[ver] = {"ids": tomo_bin_ids, "pairs": tomo_bin_pairs} - ax[1].set_xlabel(r"$\ell$") - ax[1].set_ylabel(r"$C_\ell$") + n_tomo_bins_plot = max(len(bins["ids"]) for bins in tomo_bins.values()) - ax[1].set_xlim(ell.min() - 10, ell.max() + 100) - ax[1].set_xscale("squareroot") - ax[1].set_yscale("log") - ax[1].set_xticks(np.array([100, 400, 900, 1600])) - ax[1].minorticks_on() - ax[1].tick_params(axis="x", which="minor", length=2, width=0.8) - minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] - ax[1].xaxis.set_ticks(minor_ticks, minor=True) + fig, axs = plt.subplots( + n_tomo_bins_plot, + n_tomo_bins_plot, + figsize=(12, 12), + sharex=True, + sharey=True, + ) - plt.suptitle("Pseudo-Cl EE (Gaussian covariance)") - plt.legend() - plt.savefig(out_path) + for j, ver in enumerate(versions): + # Plot the pseudo-cl for each tomo bin of the considered version + for tomo_bin_a, tomo_bin_b in tomo_bins[ver]["pairs"]: + if tomography: + ax = axs[tomo_bin_b - 1, tomo_bin_a - 1] + else: + ax = axs + ver_tomo_info = self._pseudo_cls[ver][ + f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}" + ] + ver_label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver + ver_color = ( + self.cc[ver]["colour"] if "colour" in self.cc[ver] else "black" + ) - # Plotting EB - out_path = self._output_path("cell_eb.png") + pseudo_cls = ver_tomo_info["pseudo_cl"] + cov = ver_tomo_info["cov"] + + ell = pseudo_cls["ell"] + + ell_widths = np.diff(ell) + ell_widths = np.append( + ell_widths, ell_widths[-1] + ) # Assume last bin width is same as second last + + # Better jittering: symmetric around original ell values + jitter_fraction = (j - (len(versions) - 1) / 2) * offset + jittered_ell = ell + jitter_fraction * ell_widths + ell_factor_ = get_ell_factor(jittered_ell) + + for pol in pol_list: + pol_color = self.get_pol_color(ver_color, pol, pol_list) + ax.errorbar( + jittered_ell, + ell_factor_ * pseudo_cls[pol], + yerr=np.sqrt(np.diag(cov[f"COVAR_{pol}_{pol}"].data)) + * ell_factor_, + fmt=fmt_dict[pol], + label=ver_label + f" {pol}", + color=pol_color, + capsize=2, + ) - fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(8, 8)) + # Draw to extract the yaxis text offset + fig.canvas.draw() - for ver in self.versions: - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - cov = self.pseudo_cls[ver]["cov"]["COVAR_EB_EB"].data - ax[0].errorbar( - ell, - ell * self.pseudo_cls[ver]["pseudo_cl"]["EB"], - yerr=ell * np.sqrt(np.diag(cov)), - fmt=self.cc[ver]["marker"], - label=ver + " EB", - color=self.cc[ver]["colour"], - capsize=2, + if ell_factor == "None": + ell_label = r"$C_\ell$" + elif ell_factor == "ell": + ell_label = r"$\ell C_\ell$" + else: + ell_label = r"$\ell(\ell+1) C_\ell$" + + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + if tomography: + ax = axs[tomo_bin_b - 1, tomo_bin_a - 1] + else: + ax = axs + ax.text( + 0.8, + 0.95, + f"{tomo_bin_a}-{tomo_bin_b}", + transform=ax.transAxes, + verticalalignment="top", ) + ax.axhline(0, color="black", ls="--") + ax.set_xlim(ell.min(), ell.max()) + ax.set_xscale("squareroot") + ax.set_xticks(np.array([100, 400, 900, 1600])) + ax.minorticks_on() + minor_ticks = [i * 10 for i in range(1, 10)] + [ + i * 100 for i in range(1, 21) + ] + ax.xaxis.set_ticks(minor_ticks, minor=True) + ax.tick_params(axis="both", which="both", direction="in") + if tomo_bin_b == 6 or tomo_bin_b == "all": + ax.set_xlabel(r"$\ell$") + if tomo_bin_a == 1 or tomo_bin_a == "all": + text_offset = ax.yaxis.get_offset_text().get_text() + ax.yaxis.get_offset_text().set_visible(False) + ax.set_ylabel(f"{ell_label}{text_offset}") + else: + ax.yaxis.get_offset_text().set_visible(False) + if tomo_bin_a != tomo_bin_b: + ax = axs[tomo_bin_a - 1, tomo_bin_b - 1] + ax.set_visible(False) - ax[0].axhline(0, color="black", linestyle="--") - ax[0].set_ylabel(r"$\ell C_\ell$") + # Setup the legend + plt.subplots_adjust(hspace=0.0, wspace=0.0) # Remove space between subplots - ax[0].set_xlim(ell.min() - 10, ell.max() + 100) - ax[0].set_xscale("squareroot") - ax[0].set_xticks(np.array([100, 400, 900, 1600])) - ax[0].minorticks_on() - ax[0].tick_params(axis="x", which="minor", length=2, width=0.8) - minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] - ax[0].xaxis.set_ticks(minor_ticks, minor=True) + legend_ax = self._add_grouped_legend(fig, versions, self.cc, pol_list, fmt_dict) - for ver in self.versions: - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - cov = self.pseudo_cls[ver]["cov"]["COVAR_EB_EB"].data - ax[1].errorbar( - ell, - self.pseudo_cls[ver]["pseudo_cl"]["EB"], - yerr=np.sqrt(np.diag(cov)), - fmt=self.cc[ver]["marker"], - label=ver + " EB", - color=self.cc[ver]["colour"], - ) + if savefig is not None: + plt.savefig(savefig, dpi=300, bbox_inches="tight") + if show: + plt.show() - ax[1].set_xlabel(r"$\ell$") - ax[1].set_ylabel(r"$C_\ell$") + return fig, axs, legend_ax - ax[1].set_xlim(ell.min() - 10, ell.max() + 100) - ax[1].set_xscale("squareroot") - ax[1].set_yscale("log") - ax[1].set_xticks(np.array([100, 400, 900, 1600])) - ax[1].minorticks_on() - ax[1].tick_params(axis="x", which="minor", length=2, width=0.8) - minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] - ax[1].xaxis.set_ticks(minor_ticks, minor=True) + def _add_grouped_legend( + self, + fig, + versions, + cc, + pol_list, + fmt_dict, + row_height=0.35, + label_width=2, + col_width=0.9, + gap_below=0.10, + capsize=2, + fontsize=10, + ): + """ + Add a custom legend below the figure with one row per version: + ... - plt.suptitle("Pseudo-Cl EB (Gaussian covariance)") - plt.legend() - plt.savefig(out_path) + The box size (in inches) scales with the number of versions (rows) + and polarizations (columns), then gets converted to figure-fraction + coordinates so it works for any figsize. - # Plotting BB - out_path = self._output_path("cell_bb.png") + Parameters + ---------- + row_height : float + Height per row (per version), in inches. + label_width : float + Width reserved for the version label column, in inches. + col_width : float + Width per polarization column (marker + pol label), in inches. + gap_below : float + Vertical gap between the bottom of the subplot grid and the + top of the legend box, in inches. + """ + n_rows = len(versions) + n_pol = len(pol_list) - fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(8, 8)) + # Desired box size in inches + box_width_in = label_width + n_pol * col_width + box_height_in = n_rows * row_height - for ver in self.versions: - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - cov = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data - ax[0].errorbar( - ell, - ell * self.pseudo_cls[ver]["pseudo_cl"]["BB"], - yerr=ell * np.sqrt(np.diag(cov)), - fmt=self.cc[ver]["marker"], - label=ver + " BB", - color=self.cc[ver]["colour"], - capsize=2, - ) + fig_w_in, fig_h_in = fig.get_size_inches() - ax[0].axhline(0, color="black", linestyle="--") - ax[0].set_ylabel(r"$\ell C_\ell$") + # Convert to figure-fraction + width_frac = box_width_in / fig_w_in + height_frac = box_height_in / fig_h_in + gap_frac = gap_below / fig_h_in - ax[0].set_xlim(ell.min() - 10, ell.max() + 100) - ax[0].set_xscale("squareroot") - ax[0].set_xticks(np.array([100, 400, 900, 1600])) - ax[0].minorticks_on() - ax[0].tick_params(axis="x", which="minor", length=2, width=0.8) - minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] - ax[0].xaxis.set_ticks(minor_ticks, minor=True) + left = 0.5 - width_frac / 2 # centered horizontally + bottom = -gap_frac - height_frac # just below the subplot grid (y=0) - for ver in self.versions: - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - cov = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data - ax[1].errorbar( - ell, - self.pseudo_cls[ver]["pseudo_cl"]["BB"], - yerr=np.sqrt(np.diag(cov)), - fmt=self.cc[ver]["marker"], - label=ver + " BB", - color=self.cc[ver]["colour"], - ) + legend_ax = fig.add_axes([left, bottom, width_frac, height_frac]) + legend_ax.axis("off") + legend_ax.set_xlim(0, 1) + legend_ax.set_ylim(0, 1) - ax[1].set_xlabel(r"$\ell$") - ax[1].set_ylabel(r"$C_\ell$") + # Fractions *within* legend_ax's own 0-1 coordinate system, derived + # from the same inch-based proportions so columns stay consistent + label_frac = label_width / box_width_in + col_frac = col_width / box_width_in - ax[1].set_xlim(ell.min() - 10, ell.max() + 100) - ax[1].set_xscale("squareroot") - ax[1].set_yscale("log") - ax[1].set_xticks(np.array([100, 400, 900, 1600])) - ax[1].minorticks_on() - ax[1].tick_params(axis="x", which="minor", length=2, width=0.8) - minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] - ax[1].xaxis.set_ticks(minor_ticks, minor=True) + dummy_yerr = 0.15 / n_rows - plt.suptitle("Pseudo-Cl BB (Gaussian covariance)") - plt.legend() - plt.savefig(out_path) + for i, ver in enumerate(versions): + y = 1.0 - (i + 0.5) / n_rows - # Print C_l^BB PTE for each version and save BB data - print("\nC_l^BB PTE summary:") - for ver in self.versions: - cl_bb = self.pseudo_cls[ver]["pseudo_cl"]["BB"] - cov_bb = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data - chi2_bb, _, pte_bb = chi2_and_pte(cl_bb, cov_bb) - chi2_bb = float(chi2_bb) - print( - f" {ver}: C_l^BB PTE = {pte_bb:.4f} " - f"(chi2/dof = {chi2_bb:.1f}/{len(cl_bb)})" - ) + ver_label = cc[ver]["label"] if "label" in cc[ver] else ver + ver_color = cc[ver]["colour"] if "colour" in cc[ver] else "black" - # Save BB data + covariance to .npz - ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] - bb_out = self._output_path(f"{ver}_cell_bb_data.npz") - np.savez( - bb_out, - ell=ell, - cl_bb=cl_bb, - cov_bb=cov_bb, - chi2_bb=np.array(chi2_bb), - pte_bb=np.array(pte_bb), + legend_ax.text( + 0.02 * label_frac, + y, + ver_label, + ha="left", + va="center", + fontweight="bold", + fontsize=fontsize, ) - print(f" Saved BB data to {bb_out}") - def plot_pseudo_cl_ee(self, tomography=True): - """ - Plot the pseudo-Cl for EE power spectrum. - """ - pass + for k, pol in enumerate(pol_list): + pol_color = self.get_pol_color(ver_color, pol, pol_list) + x_marker = label_frac + k * col_frac + 0.15 * col_frac + x_text = x_marker + 0.15 * col_frac + + legend_ax.errorbar( + [x_marker], + [y], + yerr=dummy_yerr, + fmt=fmt_dict[pol], + color=pol_color, + markersize=6, + capsize=capsize, + clip_on=False, + ) + legend_ax.text( + x_text, + y, + pol, + ha="left", + va="center", + fontsize=fontsize - 1, + ) + + return legend_ax - def plot_pseudo_cl_eb_bb(self, tomography=True, plot_eb=False, plot_bb=True): + def get_pol_color(self, base_color, pol, pol_list, lightness_range=(-0.25, 0.25)): """ - Plot the pseudo-Cl for EB and BB power spectra. + Given a base version color, return a shade variant for a given + polarization, by adjusting lightness in HLS space while keeping + hue and saturation fixed. + + Parameters + ---------- + base_color : str or tuple + Any matplotlib-recognized color (hex, name, RGB tuple, etc.) + pol : str + The polarization this color is for (e.g. "EE"). + pol_list : list + Full list of polarizations being plotted, used to compute + this pol's relative position in the lightness range. + lightness_range : tuple of float + (min_offset, max_offset) added to the base lightness, spread + evenly across pol_list. Negative = darker, positive = lighter. + Values are in HLS lightness units (0-1 scale), so keep these + modest (e.g. +/-0.25) to avoid washing out to white or black. + + Returns + ------- + tuple + RGB color tuple in [0, 1] range, usable directly in matplotlib. """ - pass + r, g, b = to_rgb(base_color) + h, lightness, s = colorsys.rgb_to_hls(r, g, b) + + n_pol = len(pol_list) + idx = pol_list.index(pol) + + if n_pol == 1: + l_offset = 0.0 + else: + # spread idx evenly across [lightness_range[0], lightness_range[1]] + frac = idx / (n_pol - 1) # 0 to 1 + l_offset = lightness_range[0] + frac * ( + lightness_range[1] - lightness_range[0] + ) + + lightness_new = min( + max(lightness + l_offset, 0.05), 0.95 + ) # clamp to avoid pure black/white + + r_new, g_new, b_new = colorsys.hls_to_rgb(h, lightness_new, s) + return (r_new, g_new, b_new) From ab93109124af173d00c5900d4b34659138bb977b Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 9 Jul 2026 10:18:33 +0200 Subject: [PATCH 021/107] Update the import in core comos_val of cs_util get_cosmo function. --- src/sp_validation/cosmo_val/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 5b88aacf..780fade1 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -9,13 +9,13 @@ import numpy as np import yaml from astropy.io import fits +from cs_util.cosmo import get_cosmo from shear_psf_leakage import run_object, run_scale from ..b_modes import ( _get_pte_from_scale_cut, find_conservative_scale_cut_key, ) -from ..cosmology import get_cosmo from ..statistics import chi2_and_pte from .catalog_characterization import CatalogCharacterizationMixin from .cosebis import CosebisMixin From f499ac8ddba01c8c5694d38805c75c104233fb0e Mon Sep 17 00:00:00 2001 From: Sacha Guerrini <85949041+sachaguer@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:51:59 +0200 Subject: [PATCH 022/107] Revert "Tomographic pseudo-cl" --- .gitignore | 4 +- cosmo_val/cat_config.yaml | 46 - src/sp_validation/cosmo_val/core.py | 51 +- src/sp_validation/cosmo_val/pseudo_cl.py | 1504 +++++++-------------- src/sp_validation/pseudo_cl.py | 858 +----------- src/sp_validation/rho_tau.py | 4 - src/sp_validation/tests/test_pseudo_cl.py | 60 +- 7 files changed, 540 insertions(+), 1987 deletions(-) diff --git a/.gitignore b/.gitignore index a568c1ca..f8eec899 100644 --- a/.gitignore +++ b/.gitignore @@ -197,6 +197,4 @@ papers/catalog/plots/*.pdf papers/cosmo_val/logs/ # Ignore scratch notebooks -scratch/*/*.ipynb -scratch/guerrini/work_notebooks -scratch/guerrini/launch_scripts \ No newline at end of file +scratch/*/*.ipynb \ No newline at end of file diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index ea36a81d..cc1326df 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1259,49 +1259,3 @@ SP_v1.6.6: e1_col: e1 e2_col: e2 path: unions_shapepipe_star_2024_v1.6.a.fits - -GLASS_mock_validation: - subdir: /n09data/guerrini/glass_mock_test/results/ - pipeline: SP - colour: violet - getdist_colour: 0.0, 0.5, 1.0 - ls: dashed - marker: '*' - cov_th: - A: 2405.3892055695346 - n_e: 6.128201234871523 - n_psf: 0.752316232272063 - sigma_e: 0.379587601488189 - mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits - psf: - PSF_flag: FLAG_PSF_HSM - PSF_size: SIGMA_PSF_HSM - square_size: true - star_flag: FLAG_STAR_HSM - star_size: SIGMA_STAR_HSM - hdu: 1 - path: unions_shapepipe_psf_2024_v1.6.a.fits - ra_col: RA - dec_col: Dec - e1_PSF_col: E1_PSF_HSM - e1_star_col: E1_STAR_HSM - e2_PSF_col: E2_PSF_HSM - e2_star_col: E2_STAR_HSM - shear: - R: 1.0 - path: tomo_test_2_glass_sim_00001_1024.fits - redshift_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/test_data/redshift_distribution_tomo.txt - w_col: w - e1_col: e1 - e1_PSF_col: e1_PSF - e2_col: e2 - e2_PSF_col: e2_PSF - cols: RA,Dec - tomo_bin_col: tom_bin_id - star: - ra_col: RA - dec_col: Dec - e1_col: e1 - e2_col: e2 - path: unions_shapepipe_star_2024_v1.6.a.fits - diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 066bf4a1..48251bbb 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -1,6 +1,5 @@ # %% import copy -import itertools import os import re from pathlib import Path @@ -8,7 +7,7 @@ import colorama import numpy as np import yaml -from astropy.io import fits +from cs_util.cosmo import get_cosmo from shear_psf_leakage import run_object, run_scale from ..b_modes import ( @@ -89,7 +88,7 @@ class CosmologyValidation( Number of ell bins for pseudo-C_ell analysis (used with binning='powspace'). ell_step : int, default 10 Bin width in ell for linear binning (used with binning='linear'). - pol_factor : int, default -1 + pol_factor : bool, default True Apply polarization correction factor in pseudo-C_ell calculations. nrandom_cell : int, default 10 Number of random realizations for C_ell error estimation. @@ -98,10 +97,6 @@ class CosmologyValidation( noise debiasing, making those realizations reproducible run-to-run. cosmo_params : dict, optional Cosmological parameters to pass to get_cosmo(). If None, uses Planck 2018. - compute_tomography : bool, default False - Whether to compute tomographic correlation functions and pseudo-C_ell. - force_run : bool, default False - If True, forces re-computation of results even if cached outputs exist. Attributes ---------- @@ -219,7 +214,7 @@ def __init__( power=1 / 2, n_ell_bins=32, ell_step=10, - pol_factor=-1, + pol_factor=True, cell_method="map", noise_bias_method="analytic", fiducial_input_inka="coupled", @@ -228,8 +223,6 @@ def __init__( path_onecovariance=None, cosmo_params=None, blind=None, - compute_tomography=False, - force_run=False, ): self.rho_tau_method = rho_tau_method self.cov_estimate_method = cov_estimate_method @@ -250,10 +243,7 @@ def __init__( self.power = power self.n_ell_bins = n_ell_bins self.ell_step = ell_step - - assert pol_factor in (-1, 1), "The polarisatio factor must be -1 or 1." self.pol_factor = pol_factor - self.nrandom_cell = nrandom_cell self.cell_seed = cell_seed self.cell_method = cell_method @@ -262,8 +252,6 @@ def __init__( self.nside_mask = nside_mask self.path_onecovariance = path_onecovariance self.blind = blind - self.compute_tomography = compute_tomography - self.force_run = force_run assert self.cell_method in ["map", "catalog"], ( "cell_method must be 'map' or 'catalog'" @@ -648,36 +636,3 @@ def summarize_bmodes(self, fiducial_scale_cut=(12, 83), versions=None): print() return summary - - def _get_tomo_bins(self, version): - """ - Return the tomo_bin_ids for a given version. If the version does not have tomography, return None. - - Returns - ------- - tomo_bin_ids : list or None - List of unique tomographic bin IDs for the version, or None if no tomography is available - tomo_bin_pairs : list of tuples or None - List of unique pairs of tomographic bin IDs (including self-pairs) for the version, or None if no tomography is available - """ - if "tomo_bin_col" in self.cc[version]["shear"]: - self.print_cyan( - f"Extracting tomography information from version {version}." - ) - cat_gal = fits.getdata(self.cc[version]["shear"]["path"]) - tomo_bin = cat_gal[self.cc[version]["shear"]["tomo_bin_col"]] - tomo_bin_ids = np.unique(tomo_bin) - tomo_bin_ids = tomo_bin_ids[ - tomo_bin_ids > 0 - ] # Exclude zero or negative bins - self.print_cyan( - f"Found {len(tomo_bin_ids)} tomographic bins for version {version}: {tomo_bin_ids}." - ) - - tomo_bin_pairs = list( - itertools.combinations_with_replacement(tomo_bin_ids, 2) - ) - return tomo_bin_ids, tomo_bin_pairs - else: - self.print_cyan(f"Version {version} does not have tomography information.") - return None, None diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index d8c1caf8..514049a6 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -7,37 +7,33 @@ on pymaster (NaMaster), healpy, and OneCovariance. """ -import colorsys import configparser -import itertools import os import healpy as hp import matplotlib.pyplot as plt import numpy as np +import pymaster as nmt from astropy.io import fits -from matplotlib.colors import to_rgb - -import sp_validation.pseudo_cl as spv_pseudo_cl - +from cs_util.cosmo import get_theo_c_ell + +from ..pseudo_cl import ( + apply_random_rotation, + get_n_gal_map, + get_pseudo_cls_catalog, + get_pseudo_cls_map, + make_namaster_bin, +) from ..rho_tau import get_params_rho_tau -from ..statistics import cov_from_one_covariance +from ..statistics import chi2_and_pte, cov_from_one_covariance class PseudoClMixin: - # ---------------- Pseudo-Cl properties ---------------- # @property def pseudo_cls(self): if not hasattr(self, "_pseudo_cls"): - self.calculate_pseudo_cl(compute_tomography=False) - self.calculate_pseudo_cl_inka_cov( - compute_tomography=False, load_all_block=True - ) - if self.compute_tomography: - self.calculate_pseudo_cl(compute_tomography=True) - self.calculate_pseudo_cl_inka_cov( - compute_tomography=True, load_all_block=True - ) + self.calculate_pseudo_cl() + self.calculate_pseudo_cl_eb_cov() return self._pseudo_cls @property @@ -46,11 +42,51 @@ def pseudo_cls_onecov(self): self.calculate_pseudo_cl_onecovariance() return self._pseudo_cls_onecov - # ---------------- Pseudo-Cl calculation methods ---------------- # - # TODO: some cleaning to clearly separate DV, covariance, and utility functions. - def calculate_pseudo_cl_inka_cov( - self, compute_tomography=True, load_all_block=False - ): + def get_namaster_bin(self, lmin, lmax, b_lmax): + """Build NaMaster binning object (thin wrapper, state -> primitive).""" + return make_namaster_bin( + lmin, + lmax, + b_lmax, + self.binning, + ell_step=self.ell_step, + n_ell_bins=self.n_ell_bins, + power=self.power, + ) + + def get_variance_map(self, nside, e1, e2, w, unique_pix, idx_rep): + """ + Create a variance map from the input catalog. + """ + + variance_map = np.zeros(hp.nside2npix(nside)) + + variance_map[unique_pix] = np.bincount( + idx_rep, weights=(e1**2 + e2**2) / 2 * w**2 + ) + + return variance_map + + def get_field_and_workspace_from_map(self, mask, lmax, b): + """ + Create a NaMaster field and workspace from the input map. + """ + + nside = hp.npix2nside(len(mask)) + + # Create NaMaster field + f = nmt.NmtField( + mask=mask, + maps=[np.zeros(hp.nside2npix(nside)), np.zeros(hp.nside2npix(nside))], + lmax=lmax, + ) + + # Create NaMaster workspace + wsp = nmt.NmtWorkspace.from_fields(f, f, b) + + return f, wsp + + def calculate_pseudo_cl_eb_cov(self): """ Compute a theoretical Gaussian covariance of the Pseudo-Cl for EE, EB and BB. """ @@ -58,305 +94,182 @@ def calculate_pseudo_cl_inka_cov( nside = self.nside - self._pseudo_cls = getattr(self, "_pseudo_cls", {}) + try: + self._pseudo_cls + except AttributeError: + self._pseudo_cls = {} for ver in self.versions: self.print_magenta(ver) - out_dir_block = self._output_path(f"pseudo_cl/iNKA_block_{ver}/") - os.makedirs(out_dir_block, exist_ok=True) - if ver not in self._pseudo_cls.keys(): self._pseudo_cls[ver] = {} - out_path_merged = self._output_path_pseudo_cl_cov( - ver, "iNKA", tomography=compute_tomography - ) + out_path = self._output_path(f"pseudo_cl_cov_{ver}.fits") + if os.path.exists(out_path): + self.print_done( + f"Skipping Pseudo-Cl covariance calculation, {out_path} exists" + ) + self._pseudo_cls[ver]["cov"] = fits.open(out_path) + else: + params = get_params_rho_tau(self.cc[ver], survey=ver) - if compute_tomography: - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + self.print_cyan(f"Extracting the fiducial power spectrum for {ver}") - if tomo_bin_ids is None or tomo_bin_pairs is None: + lmax = 2 * self.nside + ell = np.arange(1, lmax + 1) + pw = hp.pixwin(nside, lmax=lmax) + if pw.shape[0] != len(ell) + 1: raise ValueError( - f"Version {ver} does not have tomography information." + "Unexpected pixwin length for lmax=" + f"{lmax}: got {pw.shape[0]}, expected {len(ell) + 1}" ) - - else: - tomo_bin_pairs = [("all", "all")] - - if os.path.exists(out_path_merged) and not self.force_run: - self.print_done( - f"Skipping Pseudo-Cl iNKA covariance calculation, {out_path_merged} exists" + pw = pw[1 : len(ell) + 1] + + # Load redshift distribution and calculate theory C_ell + path_redshift_distr = self.cc[ver]["shear"]["redshift_path"] + z, dndz = np.loadtxt(path_redshift_distr, unpack=True) + fiducial_cl = ( + get_theo_c_ell( + ell=ell, + z=z, + nz=dndz, + backend="ccl", + cosmo=self.cosmo, + ) + * pw**2 ) - self._pseudo_cls[ver]["cov_iNKA"] = fits.open(out_path_merged) - if load_all_block: - self.print_done("Loading all the iNKA covariance blocks") - n_spectra = len(tomo_bin_pairs) + self.print_cyan("Getting a binning, n_gal_map, field and workspace.") - # indices into tomo_bin_pairs for all unique covariance blocks - block_indices = list( - itertools.combinations_with_replacement(range(n_spectra), 2) - ) + lmin = 8 + lmax = 2 * self.nside + b_lmax = lmax - 1 - for index_a, index_b in block_indices: - bin_key_a1, bin_key_a2 = tomo_bin_pairs[index_a] - bin_key_b1, bin_key_b2 = tomo_bin_pairs[index_b] - - load_path = self._output_path_iNKA_block_cov( - ver, - tomo_bin_quad=( - bin_key_a1, - bin_key_a2, - bin_key_b1, - bin_key_b2, - ), - ) + b = self.get_namaster_bin(lmin, lmax, b_lmax) - if os.path.exists(load_path): - self._pseudo_cls[ver][ - f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" - ] = fits.open(load_path) - - if bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2: - self._pseudo_cls[ver][ - f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" - ]["cov"] = fits.open(load_path) - else: - raise FileNotFoundError( - "The file does not exist, please run `cosmo_val` with `force_run` to True." - ) + # Load data and create shear and noise maps + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - continue + n_gal, unique_pix, _idx, idx_rep = self.get_n_gal_map( + params, nside, cat_gal + ) - # Initialise dictionnary to store field and workspace - n_gal_map_dict = {} - field_dict = {} - wsp_dict = {} + f, wsp = self.get_field_and_workspace_from_map(n_gal, b_lmax, b) + + if self.noise_bias_method == "randoms": + self.print_cyan("Getting a sample of Cls with noise bias.") + + cl_noise, f, wsp = self.get_sample( + params, + self.nside, + b_lmax, + b, + cat_gal, + n_gal, + n_gal, + unique_pix, + idx_rep, + np.random.default_rng(self.cell_seed), + ) - self.print_cyan(f"Extracting the fiducial power spectrum for {ver}") + noise_bias_cl = np.mean(cl_noise, axis=0) - fiducial_cl = self.get_fiducial_cl(ver, compute_tomography) + elif self.noise_bias_method == "analytic": + self.print_cyan("Getting analytic noise bias.") - self.print_cyan( - "Estimating and adding the noise bias to the fiducial power spectra" - ) - self.print_cyan(f"Method used: {self.noise_bias_method}") + e1, e2, w = ( + cat_gal[self.cc[ver]["shear"]["e1_col"]], + cat_gal[self.cc[ver]["shear"]["e2_col"]], + cat_gal[self.cc[ver]["shear"]["w_col"]], + ) + variance_map = self.get_variance_map( + self.nside, e1, e2, w, unique_pix, idx_rep + ) - params = get_params_rho_tau(self.cc[ver]) - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + noise_bias = hp.nside2pixarea(self.nside) * np.mean(variance_map) - for bin_key1, bin_key2 in tomo_bin_pairs: - if bin_key1 == bin_key2: - cat_gal_ = self._get_tomographic_bin(params, cat_gal, bin_key1) + noise_bias_cl = np.zeros((4, lmax)) + noise_bias_cl[0, :] = noise_bias + noise_bias_cl[3, :] = noise_bias - noise_bias_cl = self.get_noise_bias(params, nside, cat_gal_) + noise_bias_cl = wsp.decouple_cell(noise_bias_cl) # Decouple else: - noise_bias_cl = np.zeros((4, 2 * nside)) + raise ValueError( + f"Noise bias method {self.noise_bias_method} not recognized. It should be 'randoms' or 'analytic'." + ) + + # Unbin, then fill the data vector below lmin with the lowest-ell value + noise_bias_cl = b.unbin_cell(noise_bias_cl) + lowest_ell = b.get_ell_list(0)[0] + noise_bias_cl[:, :lowest_ell] = noise_bias_cl[:, [lowest_ell]] + + self.print_cyan("Adding noise bias to the fiducial Cls.") - # Update the fiducial_cl dictionnary - fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = ( + fiducial_cl = ( np.array( [ - fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + fiducial_cl, + 0.0 * fiducial_cl, + 0.0 * fiducial_cl, + 0.0 * fiducial_cl, ] ) + noise_bias_cl ) - # Compute the fields and workspaces - for bin_key1, bin_key2 in tomo_bin_pairs: - self.print_cyan( - f"Computing fields and workspaces for {bin_key1}, {bin_key2}" - ) - lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(self.nside) - b = self.get_namaster_bin(lmin, lmax, b_lmax) - - # Get the tomographic bins - cat_gal_a = self._get_tomographic_bin(params, cat_gal, bin_key1) - cat_gal_b = self._get_tomographic_bin(params, cat_gal, bin_key2) - - # Compute the n_gal_maps and the wsp object - unique_pix_a, idx_a, idx_rep_a = self.get_pixels( - params, nside, cat_gal_a - ) - unique_pix_b, idx_b, idx_rep_b = self.get_pixels( - params, nside, cat_gal_b - ) - - # Compute the number density maps - n_gal_map_a = self.get_n_gal_map(params, nside, cat_gal_a) - n_gal_map_b = self.get_n_gal_map(params, nside, cat_gal_b) - - # Get the shear maps - shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( - params, - self.nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - ) - shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( - params, - self.nside, - cat_gal_b, - unique_pix=unique_pix_b, - idx=idx_b, - idx_rep=idx_rep_b, - ) - - # Get the fields and workspaces - field_a, field_b, wsp = spv_pseudo_cl.get_field_and_workspace_from_map( - b, - mask_a=n_gal_map_a, - e1_map_a=shear_map_a_e1, - e2_map_a=shear_map_a_e2, - mask_b=n_gal_map_b, - e1_map_b=shear_map_b_e1, - e2_map_b=shear_map_b_e2, - pol_factor=self.pol_factor, - return_wsp=True, - ) - - # Save in the dictionnaries - if not f"W{bin_key1}" not in n_gal_map_dict: - n_gal_map_dict[f"W{bin_key1}"] = n_gal_map_a - if f"W{bin_key2}" not in n_gal_map_dict: - n_gal_map_dict[f"W{bin_key2}"] = n_gal_map_b - if f"W{bin_key1}" not in field_dict: - field_dict[f"W{bin_key1}"] = field_a - if f"W{bin_key2}" not in field_dict: - field_dict[f"W{bin_key2}"] = field_b - if bin_key1 <= bin_key2 and f"W{bin_key1}xW{bin_key2}" not in wsp_dict: - wsp_dict[f"W{bin_key1}xW{bin_key2}"] = wsp - - if self.fiducial_input_inka == "coupled": - # Couple the cell if required - self.print_cyan("Coupling the fiducial Cls.") - for bin_key1, bin_key2 in tomo_bin_pairs: - # Get the wsp object - n_gal_map_a = n_gal_map_dict[f"W{bin_key1}"] - n_gal_map_b = n_gal_map_dict[f"W{bin_key2}"] - wsp = wsp_dict[f"W{bin_key1}xW{bin_key2}"] + if self.fiducial_input_inka == "coupled": + self.print_cyan("Coupling the fiducial Cls.") coupling_mat = wsp.get_coupling_matrix() coupling_mat_re = np.reshape( coupling_mat, (4, lmax, 4, lmax), order="F" ) - fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = np.tensordot( - coupling_mat_re, fiducial_cl[f"W{bin_key1}xW{bin_key2}"] - ) / np.mean( - n_gal_map_a * n_gal_map_b - ) # couple and divide by the product of the mask - - # To compute all the blocks in the covariance, - # we must compute all the (i, j, k, l) bin combinations - # Or equivalently, compute the covariance for all spectra pairs - n_spectra = len(tomo_bin_pairs) - - # indices into tomo_bin_pairs for all unique covariance blocks - block_indices = list( - itertools.combinations_with_replacement(range(n_spectra), 2) - ) - # Loop on the different tomographic bin pairs to compute the covariance - for index_a, index_b in block_indices: - bin_key_a1, bin_key_a2 = tomo_bin_pairs[index_a] - bin_key_b1, bin_key_b2 = tomo_bin_pairs[index_b] - self.print_cyan( - f"Tomo Bin Quad: ({bin_key_a1}, {bin_key_a2}, {bin_key_b1}, {bin_key_b2})" - ) - - if ( - bin_key_a1 == bin_key_b1 - and bin_key_a2 == bin_key_b2 - and ( - f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" - not in self._pseudo_cls[ver].keys() - ) - ): - self._pseudo_cls[ver][ - f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" - ] = {} - - out_path = self._output_path_iNKA_block_cov( - ver, tomo_bin_quad=(bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2) - ) - - if os.path.exists(out_path) and not self.force_run: - self.print_done( - f"Skipping Pseudo-Cl covariance block Cl ({bin_key_a1, bin_key_a2}), Cl ({bin_key_b1, bin_key_b2}) calculation, {out_path} exists" - ) - - self._pseudo_cls[ver][ - f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" - ] = fits.open(out_path) - - if bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2: - self._pseudo_cls[ver][ - f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" - ]["cov"] = fits.open(out_path) - - continue + fiducial_cl = np.tensordot(coupling_mat_re, fiducial_cl) / np.mean( + n_gal**2 + ) # couple and divide by the mean of the mask squared self.print_cyan("Computing the Pseudo-Cl covariance") - input_cl_a1_b1 = ( - fiducial_cl[f"W{bin_key_a1}xW{bin_key_b1}"] - if bin_key_a1 <= bin_key_b1 - else fiducial_cl[f"W{bin_key_b1}xW{bin_key_a1}"] - ) - input_cl_a1_b2 = ( - fiducial_cl[f"W{bin_key_a1}xW{bin_key_b2}"] - if bin_key_a1 <= bin_key_a2 - else fiducial_cl[f"W{bin_key_b2}xW{bin_key_a1}"] - ) - input_cl_a2_b1 = ( - fiducial_cl[f"W{bin_key_a2}xW{bin_key_b1}"] - if bin_key_a2 <= bin_key_b1 - else fiducial_cl[f"W{bin_key_b1}xW{bin_key_a2}"] - ) - input_cl_a2_b2 = ( - fiducial_cl[f"W{bin_key_a2}xW{bin_key_b2}"] - if bin_key_a2 <= bin_key_b2 - else fiducial_cl[f"W{bin_key_b2}xW{bin_key_a2}"] - ) - - covar_22_22 = spv_pseudo_cl.get_pseudo_cl_iNKA_covariance( - input_cl_a1_b1, - input_cl_a1_b2, - input_cl_a2_b1, - input_cl_a2_b2, - field_dict[f"W{bin_key_a1}"], - field_dict[f"W{bin_key_a2}"], - field_dict[f"W{bin_key_b1}"], - field_dict[f"W{bin_key_b2}"], - wsp_a=wsp_dict[f"W{bin_key_a1}xW{bin_key_a2}"], - wsp_b=wsp_dict[f"W{bin_key_b1}xW{bin_key_b2}"], - b=b, - ) + cw = nmt.NmtCovarianceWorkspace.from_fields(f, f, f, f) + + # Get actual number of ell bins from binning scheme + n_ell_actual = b.get_n_bands() + + covar_22_22 = nmt.gaussian_covariance( + cw, + 2, + 2, + 2, + 2, + fiducial_cl, + fiducial_cl, + fiducial_cl, + fiducial_cl, + wsp, + wb=wsp, + ).reshape([n_ell_actual, 4, n_ell_actual, 4]) self.print_cyan("Saving Pseudo-Cl covariance") - self._pseudo_cls[ver][ - f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" - ] = self._save_iNKA_covariance(covar_22_22, out_path) + # covar_22_22 is indexed [ell, pol_a, ell, pol_b]; store each of the + # 16 EE/EB/BE/BB cross-blocks as a named HDU (row-major pol order). + # Append rather than construct from a list so astropy promotes the + # first HDU to a PrimaryHDU on write. + pols = ["EE", "EB", "BE", "BB"] + hdu = fits.HDUList() + for i, pa in enumerate(pols): + for j, pb in enumerate(pols): + hdu.append( + fits.ImageHDU( + covar_22_22[:, i, :, j], name=f"COVAR_{pa}_{pb}" + ) + ) - if bin_key_a1 == bin_key_b1 and bin_key_a2 == bin_key_b2: - self._pseudo_cls[ver][ - f"tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}" - ]["cov"] = self._pseudo_cls[ver][ - f"spectra_{bin_key_a1}{bin_key_a2}_spectra_{bin_key_b1}{bin_key_b2}" - ] + hdu.writeto(out_path, overwrite=True) + + self._pseudo_cls[ver]["cov"] = hdu - # Merge the covariance blocks - self._pseudo_cls[ver]["cov_iNKA"] = self._merge_iNKA_covariance( - ver, tomography=compute_tomography - ) - self.print_done(f"Done Pseudo-Cl covariance calculation for {ver}") self.print_done("Done Pseudo-Cl covariance") def calculate_pseudo_cl_onecovariance(self): @@ -386,11 +299,8 @@ def calculate_pseudo_cl_onecovariance(self): out_dir = self._output_path(f"pseudo_cl_cov_onecov_{ver}/") os.makedirs(out_dir, exist_ok=True) - if ( - os.path.exists( - os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") - ) - and not self.force_run + if os.path.exists( + os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") ): self.print_done(f"Skipping OneCovariance calculation, {out_dir} exists") self._load_onecovariance_cov(out_dir, ver) @@ -507,7 +417,7 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): out_file = self._output_path( f"pseudo_cl_cov_g_ng_{gaussian_part}_{ver}.fits" ) - if os.path.exists(out_file) and not self.force_run: + if os.path.exists(out_file): self.print_done( f"Skipping Gaussian and Non-Gaussian covariance calculation, {out_file} exists" ) @@ -541,356 +451,182 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): f"Done Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" ) - def calculate_pseudo_cl(self, compute_tomography=True): + def calculate_pseudo_cl(self): """ - Compute the pseudo-Cl of a `CosmologyValidation` inputs with tomography. + Compute the pseudo-Cl of given catalogs. """ - out_dir = self._output_path("pseudo_cl") - os.makedirs(out_dir, exist_ok=True) + self.print_start("Computing pseudo-Cl's") - if compute_tomography: - self.print_start("Computing tomographic pseudo-Cl's") - else: - self.print_start("Computing non-tomographic pseudo-Cl's") - - self._pseudo_cls = getattr(self, "_pseudo_cls", {}) + nside = self.nside + try: + self._pseudo_cls + except AttributeError: + self._pseudo_cls = {} for ver in self.versions: self.print_magenta(ver) - if ver not in self.pseudo_cls.keys(): - self._pseudo_cls[ver] = {} - - if compute_tomography: - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - - if tomo_bin_ids is None or tomo_bin_pairs is None: - raise ValueError( - f"Version {ver} does not have tomography information." - ) - + self._pseudo_cls[ver] = {} + + out_path = self._output_path(f"pseudo_cl_{ver}.fits") + if os.path.exists(out_path): + self.print_done(f"Skipping Pseudo-Cl's calculation, {out_path} exists") + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver]["pseudo_cl"] = cl_shear + elif self.cell_method == "map": + self.calculate_pseudo_cl_map(ver, nside, out_path) + elif self.cell_method == "catalog": + self.calculate_pseudo_cl_catalog(ver, out_path) else: - tomo_bin_pairs = [("all", "all")] - - # Loop on the different tomographic bin pairs - for bin_key1, bin_key2 in tomo_bin_pairs: - self.print_cyan(f"Tomo Bin Pair: ({bin_key1}, {bin_key2})") - - if ( - f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" - not in self._pseudo_cls[ver].keys() - ): - self._pseudo_cls[ver][ - f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" - ] = {} - - out_path = self._output_path_pseudo_cl( - ver, tomo_bin_pair=(bin_key1, bin_key2) - ) - if os.path.exists(out_path) and not self.force_run: - self.print_done( - f"Skipping Pseudo-Cl's calculation, {out_path} exists" - ) - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ - "pseudo_cl" - ] = cl_shear - continue - - if self.cell_method == "map": - self.calculate_pseudo_cl_map( - ver, self.nside, out_path, bin_key1, bin_key2 - ) - elif self.cell_method == "catalog": - self.calculate_pseudo_cl_catalog(ver, out_path, bin_key1, bin_key2) - else: - raise ValueError(f"Unknown cell method: {self.cell_method}") + raise ValueError(f"Unknown cell method: {self.cell_method}") - def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): - assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - isinstance(tomo_bin_a, (int, np.integer)) - and isinstance(tomo_bin_b, (int, np.integer)) - ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." + self.print_done("Done pseudo-Cl's") - params = get_params_rho_tau(self.cc[ver]) - - self.print_cyan( - f"Computing pseudo-Cl's for tomographic bins {tomo_bin_a} and {tomo_bin_b}..." - ) + def calculate_pseudo_cl_map(self, ver, nside, out_path): + params = get_params_rho_tau(self.cc[ver], survey=ver) # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - # Get the tomographic bin - cat_gal_a = self._get_tomographic_bin(params, cat_gal, tomo_bin_a) - cat_gal_b = self._get_tomographic_bin(params, cat_gal, tomo_bin_b) + w = cat_gal[params["w_col"]] + self.print_cyan("Creating maps and computing Cl's...") + n_gal_map, unique_pix, _idx, idx_rep = self.get_n_gal_map( + params, nside, cat_gal + ) + mask = n_gal_map != 0 + + shear_map_e1 = np.zeros(hp.nside2npix(nside)) + shear_map_e2 = np.zeros(hp.nside2npix(nside)) + + e1 = cat_gal[params["e1_col"]] + e2 = cat_gal[params["e2_col"]] del cat_gal - self.print_cyan("Creating maps and computing Cl's...") - # Get the pixels and indices for the catalogs - unique_pix_a, idx_a, idx_rep_a = self.get_pixels(params, nside, cat_gal_a) - unique_pix_b, idx_b, idx_rep_b = self.get_pixels(params, nside, cat_gal_b) + shear_map_e1[unique_pix] += np.bincount(idx_rep, weights=e1 * w) + shear_map_e2[unique_pix] += np.bincount(idx_rep, weights=e2 * w) + shear_map_e1[mask] /= n_gal_map[mask] + shear_map_e2[mask] /= n_gal_map[mask] - # Create number density maps for each tomographic bin - n_gal_map_a = self.get_n_gal_map( - params, - nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - ) - n_gal_map_b = self.get_n_gal_map( - params, - nside, - cat_gal_b, - unique_pix=unique_pix_b, - idx=idx_b, - idx_rep=idx_rep_b, - ) + shear_map = shear_map_e1 + 1j * shear_map_e2 - # Create shear maps for each tomographic bin - shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( - params, - nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - n_gal_map=n_gal_map_a, - ) - shear_map_a = shear_map_a_e1 + 1j * shear_map_a_e2 - del shear_map_a_e1, shear_map_a_e2 + del shear_map_e1, shear_map_e2 - shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( - params, - nside, - cat_gal_b, - unique_pix=unique_pix_b, - idx=idx_b, - idx_rep=idx_rep_b, - n_gal_map=n_gal_map_b, - ) - shear_map_b = shear_map_b_e1 + 1j * shear_map_b_e2 - del shear_map_b_e1, shear_map_b_e2 + ell_eff, cl_shear, wsp = self.get_pseudo_cls_map(shear_map, n_gal_map) - # Compute the pseudo-Cl's - ell_eff, cl_shear, wsp = self.get_pseudo_cls_map( - shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, mask_b=n_gal_map_b - ) + cl_noise = np.zeros_like(cl_shear) + rng = np.random.default_rng(self.cell_seed) - # Remove the noise bias for auto-correlations. - if tomo_bin_a == tomo_bin_b: - # Compute the noise bias using noise_bias_method - cl_noise = self.get_noise_bias_from_gaussian_real( - params, - nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - n_gal_map=n_gal_map_a, - wsp=wsp, - ) + for i in range(self.nrandom_cell): + noise_map_e1 = np.zeros(hp.nside2npix(nside)) + noise_map_e2 = np.zeros(hp.nside2npix(nside)) + + e1_rot, e2_rot = self.apply_random_rotation(e1, e2, rng) + + noise_map_e1[unique_pix] += np.bincount(idx_rep, weights=e1_rot * w) + noise_map_e2[unique_pix] += np.bincount(idx_rep, weights=e2_rot * w) - # Subtract the noise bias from the pseudo-Cl's - cl_shear = cl_shear - cl_noise + noise_map_e1[mask] /= n_gal_map[mask] + noise_map_e2[mask] /= n_gal_map[mask] + + noise_map = noise_map_e1 + 1j * noise_map_e2 + del noise_map_e1, noise_map_e2 + + _, cl_noise_, _ = self.get_pseudo_cls_map(noise_map, n_gal_map, wsp) + cl_noise += cl_noise_ + + cl_noise /= self.nrandom_cell + del e1, e2, w + try: + del e1_rot, e2_rot + except NameError: # Continue if the random generation has been skipped. + pass + del n_gal_map + + # Noise realizations are now reproducible (seeded rng from self.cell_seed). + cl_shear = cl_shear - cl_noise self.print_cyan("Saving pseudo-Cl's...") self.save_pseudo_cl(ell_eff, cl_shear, out_path) cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ - "pseudo_cl" - ] = cl_shear + self._pseudo_cls[ver]["pseudo_cl"] = cl_shear - def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): - assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - isinstance(tomo_bin_a, (int, np.integer)) - and isinstance(tomo_bin_b, (int, np.integer)) - ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." - - params = get_params_rho_tau(self.cc[ver]) + def calculate_pseudo_cl_catalog(self, ver, out_path): + params = get_params_rho_tau(self.cc[ver], survey=ver) # Load data and create shear and noise maps cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) ell_eff, cl_shear, wsp = self.get_pseudo_cls_catalog( - catalog=cat_gal, params=params, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b + catalog=cat_gal, params=params ) self.print_cyan("Saving pseudo-Cl's...") self.save_pseudo_cl(ell_eff, cl_shear, out_path) cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ - "pseudo_cl" - ] = cl_shear - - # ---------------- Utility functions for pseudo-Cl calculations ---------------- # - def get_namaster_bin(self, lmin, lmax, b_lmax): - """Build NaMaster binning object (thin wrapper, state -> primitive).""" - return spv_pseudo_cl.make_namaster_bin( - lmin, - lmax, - b_lmax, - self.binning, - ell_step=self.ell_step, - n_ell_bins=self.n_ell_bins, - power=self.power, - ) + self._pseudo_cls[ver]["pseudo_cl"] = cl_shear - def get_pixels(self, params, nside, cat_gal): - """Get unique pixels and indices for a catalog (thin wrapper -> primitive).""" - return spv_pseudo_cl.get_pixels( - cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], nside - ) - - def get_n_gal_map( - self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None - ): + def get_n_gal_map(self, params, nside, cat_gal): """Weighted galaxy number-density map (thin wrapper -> primitive).""" - return spv_pseudo_cl.get_n_gal_map( + return get_n_gal_map( nside, cat_gal[params["ra_col"]], cat_gal[params["dec_col"]], weights=cat_gal[params["w_col"]], - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, ) - def get_shear_map( - self, - params, - nside, - cat_gal, - unique_pix=None, - idx=None, - idx_rep=None, - n_gal_map=None, + def get_gaussian_real( + self, params, nside, lmax, cat_gal, n_gal, mask, unique_pix, idx_rep, rng=None ): - """Weighted shear map (thin wrapper -> primitive).""" - return spv_pseudo_cl.get_shear_map( - cat_gal[params["ra_col"]], - cat_gal[params["dec_col"]], - cat_gal[params["e1_col"]], - cat_gal[params["e2_col"]], - cat_gal[params["w_col"]], - nside, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - n_gal_map=n_gal_map, + e1_rot, e2_rot = self.apply_random_rotation( + cat_gal[params["e1_col"]], cat_gal[params["e2_col"]], rng ) + noise_map_e1 = np.zeros(hp.nside2npix(nside)) + noise_map_e2 = np.zeros(hp.nside2npix(nside)) + + w = cat_gal[params["w_col"]] + noise_map_e1[unique_pix] += np.bincount(idx_rep, weights=e1_rot * w) + noise_map_e2[unique_pix] += np.bincount(idx_rep, weights=e2_rot * w) + noise_map_e1[mask] /= n_gal[mask] + noise_map_e2[mask] /= n_gal[mask] + + return noise_map_e1 + 1j * noise_map_e2 - def get_noise_realisation( + def get_sample( self, params, nside, + lmax, + b, cat_gal, - n_gal=None, - unique_pix=None, - idx=None, - idx_rep=None, + n_gal, + mask, + unique_pix, + idx_rep, rng=None, ): - """ - Get a single Gaussian noise realization (thin wrapper -> primitive). - """ - return spv_pseudo_cl.get_noise_realisation( - cat_gal[params["ra_col"]], - cat_gal[params["dec_col"]], - cat_gal[params["e1_col"]], - cat_gal[params["e2_col"]], - cat_gal[params["w_col"]], - nside, - n_gal_map=n_gal, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - rng=rng, + noise_map = self.get_gaussian_real( + params, nside, lmax, cat_gal, n_gal, mask, unique_pix, idx_rep, rng ) - def get_noise_bias_from_gaussian_real( - self, - params, - nside, - cat_gal, - unique_pix=None, - idx=None, - idx_rep=None, - n_gal_map=None, - wsp=None, - ): - """Noise-bias from Gaussian realisations (thin wrapper, state -> primitive)""" - return spv_pseudo_cl.get_noise_bias_from_gaussian_real( - cat_gal[params["ra_col"]], - cat_gal[params["dec_col"]], - cat_gal[params["e1_col"]], - cat_gal[params["e2_col"]], - cat_gal[params["w_col"]], - nside, - nrandom_cell=self.nrandom_cell, - binning=self.binning, - ell_step=self.ell_step, - n_ell_bins=self.n_ell_bins, - power=self.power, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - n_gal_map=n_gal_map, - wsp=wsp, - seed=self.cell_seed, - ) + f = nmt.NmtField(mask=mask, maps=[noise_map.real, noise_map.imag], lmax=lmax) - def get_noise_bias_analytical( - self, params, nside, cat_gal, unique_pix=None, idx=None, idx_rep=None - ): - """Noise-bias from analytical prescription (thin wrapper, state -> primitive)""" - return spv_pseudo_cl.get_noise_bias_analytical( - cat_gal[params["ra_col"]], - cat_gal[params["dec_col"]], - cat_gal[params["e1_col"]], - cat_gal[params["e2_col"]], - cat_gal[params["w_col"]], - lmax=2 * nside, - nside=nside, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - ) + wsp = nmt.NmtWorkspace.from_fields(f, f, b) - def get_noise_bias(self, params, nside, cat_gal): - """Noise-bias estimation (thin wrapper, state -> primitive)""" - return spv_pseudo_cl.get_noise_bias( - cat_gal[params["ra_col"]], - cat_gal[params["dec_col"]], - cat_gal[params["e1_col"]], - cat_gal[params["e2_col"]], - cat_gal[params["w_col"]], - nside, - noise_bias_method=self.noise_bias_method, - binning=self.binning, - ell_step=self.ell_step, - n_ell_bins=self.n_ell_bins, - power=self.power, - nrandom_cell=self.nrandom_cell, - seed=self.cell_seed, - ) + cl_noise = nmt.compute_coupled_cell(f, f) + cl_noise = wsp.decouple_cell(cl_noise) - def get_pseudo_cls_map( - self, map_a, mask_a, wsp=None, shear_map_b=None, mask_b=None - ): + return cl_noise, f, wsp + + def get_pseudo_cls_map(self, map, mask, wsp=None): """Map-based pseudo-cl (thin wrapper, state -> primitive).""" - return spv_pseudo_cl.get_pseudo_cls_map( - map_a, - mask_a, + return get_pseudo_cls_map( + map, + mask, self.nside, self.binning, - shear_map_b=shear_map_b, - mask_b=mask_b, pol_factor=self.pol_factor, wsp=wsp, ell_step=self.ell_step, @@ -898,17 +634,13 @@ def get_pseudo_cls_map( power=self.power, ) - def get_pseudo_cls_catalog( - self, catalog, params, wsp=None, tomo_bin_a=None, tomo_bin_b=None - ): + def get_pseudo_cls_catalog(self, catalog, params, wsp=None): """Catalog-based pseudo-cl (thin wrapper, state -> primitive).""" - return spv_pseudo_cl.get_pseudo_cls_catalog( + return get_pseudo_cls_catalog( catalog, params, self.nside, self.binning, - tomo_bin_a=tomo_bin_a, - tomo_bin_b=tomo_bin_b, pol_factor=self.pol_factor, wsp=wsp, ell_step=self.ell_step, @@ -916,68 +648,12 @@ def get_pseudo_cls_catalog( power=self.power, ) - def read_redshift_distribution(self, ver, is_tomography): - path_redshift_distr = self.cc[ver]["shear"]["redshift_path"] - redshift_distribution = np.loadtxt(path_redshift_distr) - z = redshift_distribution[:, 0] - dndz = redshift_distribution[:, 1:] - - # Here it is assumed that the tomographic redshift distribution sum to the non-tomographic one and that the latter is normalised - if not is_tomography: - dndz = np.sum(dndz, axis=1) - - return z, dndz - - def get_fiducial_cl(self, ver, is_tomography): - """Get a theory prediction for the angular power spectra (thin wrapper, state -> primitive).""" - lmax = 2 * self.nside - - z, dndz = self.read_redshift_distribution(ver, is_tomography) - - fiducial_cl = spv_pseudo_cl.get_fiducial_cl(z, dndz, lmax, self.cosmo) - - # If non-tomographic, change the key to 'WallxWall' - if not is_tomography: - fiducial_cl = {"WallxWall": fiducial_cl["W1xW1"]} - - return fiducial_cl + def apply_random_rotation(self, e1, e2, rng=None): + """Random ellipticity rotation (thin wrapper -> primitive). - def _get_tomographic_bin(self, params, cat_gal, tomo_bin): - """Extract tomographic bin from a given catalogue""" - if tomo_bin == "all": - return cat_gal - else: - tomo_bin_id = cat_gal[params["tomo_bin_col"]] - mask = tomo_bin_id == tomo_bin - return cat_gal[mask] - - def _output_path_pseudo_cl(self, ver, tomo_bin_pair=None): - if tomo_bin_pair is None: - return self._output_path( - "pseudo_cl", - f"pseudo_cl_from_{self.cell_method}_non_tomo_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", - ) - else: - bin_key1, bin_key2 = tomo_bin_pair - return self._output_path( - "pseudo_cl", - f"pseudo_cl_from_{self.cell_method}_tomo_bin_{bin_key1}_tomo_bin_{bin_key2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", - ) - - def _output_path_pseudo_cl_cov(self, ver, method, tomography): - is_tomo = "tomo" if tomography else "non_tomo" - return self._output_path( - "pseudo_cl", - f"pseudo_cl_cov_{is_tomo}_{ver}_from_{method}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", - ) - - def _output_path_iNKA_block_cov(self, ver, tomo_bin_quad): - bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2 = tomo_bin_quad - return self._output_path( - "pseudo_cl", - f"iNKA_block_{ver}", - f"pseudo_cl_cov_from_iNKA_tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}_tomo_bin_{bin_key_b1}_tomo_bin_{bin_key_b2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", - ) + Pass a seeded ``rng`` for reproducible noise realizations. + """ + return apply_random_rotation(e1, e2, rng) def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): """ @@ -994,440 +670,208 @@ def save_pseudo_cl(self, ell_eff, pseudo_cl, out_path): col1 = fits.Column(name="ELL", format="D", array=ell_eff) col2 = fits.Column(name="EE", format="D", array=pseudo_cl[0]) col3 = fits.Column(name="EB", format="D", array=pseudo_cl[1]) - col4 = fits.Column(name="BE", format="D", array=pseudo_cl[2]) - col5 = fits.Column(name="BB", format="D", array=pseudo_cl[3]) - coldefs = fits.ColDefs([col1, col2, col3, col4, col5]) + col4 = fits.Column(name="BB", format="D", array=pseudo_cl[3]) + coldefs = fits.ColDefs([col1, col2, col3, col4]) cell_hdu = fits.BinTableHDU.from_columns(coldefs, name="PSEUDO_CELL") cell_hdu.writeto(out_path, overwrite=True) - def _save_iNKA_covariance(self, covar, out_path): - # covar_22_22 is indexed [ell, pol_a, ell, pol_b]; store each of the - # 16 EE/EB/BE/BB cross-blocks as a named HDU (row-major pol order). - # Append rather than construct from a list so astropy promotes the - # first HDU to a PrimaryHDU on write. - pols = ["EE", "EB", "BE", "BB"] - hdu = fits.HDUList() - for i, pa in enumerate(pols): - for j, pb in enumerate(pols): - hdu.append(fits.ImageHDU(covar[:, i, :, j], name=f"COVAR_{pa}_{pb}")) - - hdu.writeto(out_path, overwrite=True) - - return hdu - - def _merge_iNKA_covariance(self, ver, tomography): + def plot_pseudo_cl(self): """ - Merge the iNKA covariance matrices for a given version to get the data vector covariance. + Plot pseudo-Cl's for given catalogs. """ - out_path = self._output_path_pseudo_cl_cov( - ver, method="iNKA", tomography=tomography - ) + self.print_cyan("Plotting pseudo-Cl's") - print(self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"]) + # Plotting EE + out_path = self._output_path("cell_ee.png") + fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(8, 8)) - if tomography: - # Merge the tomographic covariance matrices - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + for ver in self.versions: + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + cov = self.pseudo_cls[ver]["cov"]["COVAR_EE_EE"].data + ax[0].errorbar( + ell, + ell * self.pseudo_cls[ver]["pseudo_cl"]["EE"], + yerr=ell * np.sqrt(np.diag(cov)), + fmt=self.cc[ver]["marker"], + label=ver + " EE", + color=self.cc[ver]["colour"], + capsize=2, + ) - # Get the number of bins from the pseudo_cls attribute - # The non-tomographic pseudo-cl are computed from the call - # to this attribute. - n_ell = self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"]["pseudo_cl"][ - "ELL" - ].shape[0] + ax[0].set_ylabel(r"$\ell C_\ell$") - if tomo_bin_ids is None or tomo_bin_pairs is None: - raise AssertionError( - f"Tomographic bin IDs of version {ver} is not available." - ) + ax[0].set_xlim(ell.min() - 10, ell.max() + 100) + ax[0].set_xscale("squareroot") + ax[0].set_xticks(np.array([100, 400, 900, 1600])) + ax[0].minorticks_on() + ax[0].tick_params(axis="x", which="minor", length=2, width=0.8) + minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] + ax[0].xaxis.set_ticks(minor_ticks, minor=True) - n_spectra = len(tomo_bin_pairs) - block_indices = list( - itertools.combinations_with_replacement(range(n_spectra), 2) + for ver in self.versions: + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + cov = self.pseudo_cls[ver]["cov"]["COVAR_EE_EE"].data + ax[1].errorbar( + ell, + self.pseudo_cls[ver]["pseudo_cl"]["EE"], + yerr=np.sqrt(np.diag(cov)), + fmt=self.cc[ver]["marker"], + label=ver + " EE", + color=self.cc[ver]["colour"], ) - pols = ["EE", "EB", "BE", "BB"] - covar = fits.HDUList() - for pa in pols: - for pb in pols: - full_cov = np.zeros((n_spectra * n_ell, n_spectra * n_ell)) - for index_a, index_b in block_indices: - bin_key_a1, bin_key_a2 = tomo_bin_pairs[index_a] - bin_key_b1, bin_key_b2 = tomo_bin_pairs[index_b] - - block_path = self._output_path_iNKA_block_cov( - ver, - tomo_bin_quad=( - bin_key_a1, - bin_key_a2, - bin_key_b1, - bin_key_b2, - ), - ) - block = fits.open(block_path)[f"COVAR_{pa}_{pb}"].data + ax[1].set_xlabel(r"$\ell$") + ax[1].set_ylabel(r"$C_\ell$") - sl_a = slice(index_a * n_ell, (index_a + 1) * n_ell) - sl_b = slice(index_b * n_ell, (index_b + 1) * n_ell) + ax[1].set_xlim(ell.min() - 10, ell.max() + 100) + ax[1].set_xscale("squareroot") + ax[1].set_yscale("log") + ax[1].set_xticks(np.array([100, 400, 900, 1600])) + ax[1].minorticks_on() + ax[1].tick_params(axis="x", which="minor", length=2, width=0.8) + minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] + ax[1].xaxis.set_ticks(minor_ticks, minor=True) - full_cov[sl_a, sl_b] = block + plt.suptitle("Pseudo-Cl EE (Gaussian covariance)") + plt.legend() + plt.savefig(out_path) - if index_a != index_b: - full_cov[sl_b, sl_a] = block.T + # Plotting EB + out_path = self._output_path("cell_eb.png") - covar.append(fits.ImageHDU(full_cov, name=f"COVAR_{pa}_{pb}")) + fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(8, 8)) - else: - block_path = self._output_path_iNKA_block_cov( - ver, tomo_bin_quad=(bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2) + for ver in self.versions: + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + cov = self.pseudo_cls[ver]["cov"]["COVAR_EB_EB"].data + ax[0].errorbar( + ell, + ell * self.pseudo_cls[ver]["pseudo_cl"]["EB"], + yerr=ell * np.sqrt(np.diag(cov)), + fmt=self.cc[ver]["marker"], + label=ver + " EB", + color=self.cc[ver]["colour"], + capsize=2, ) - covar = fits.open(block_path) - - covar.writeto(out_path, overwrite=True) - return covar - - # ---------------- Plotting functions for pseudo-Cl's ---------------- # - def plot_pseudo_cl( - self, - pol_list, - versions=None, - ell_factor="ell", - cov_type="iNKA", - offset=0.15, - tomography=True, - savefig=None, - show=True, - ): - """ - Plot the pseudo-Cl for EE power spectrum. + ax[0].axhline(0, color="black", linestyle="--") + ax[0].set_ylabel(r"$\ell C_\ell$") - Parameters - ---------- - pol_list : list - List of polarization types to plot (e.g., ["EE", "BB"]). - ell_factor : {"None", "ell", "ell(ell+1)"} - Factor to multiply the ell values by. - cov_type : {"iNKA"} - Type of covariance to use. - tomography : bool, optional - Whether to plot tomographic power spectra. - savefig : str, optional - Path to save the figure. - show : bool, optional - Whether to show the figure. - - Returns - ------- - fig, ax : matplotlib.figure.Figure, matplotlib.axes.Axes - Figure and axes objects for the plot. - """ - if versions is None: - versions = self.versions - else: - for ver in versions: - if ver not in self.versions: - raise ValueError( - f"Version {ver} is not available. Available versions: {self.versions}" - ) - # Check that the method for the covariance is valid - if cov_type not in ["iNKA"]: - raise ValueError( - f"Invalid covariance type: {cov_type}. Valid options are: ['iNKA']" - ) + ax[0].set_xlim(ell.min() - 10, ell.max() + 100) + ax[0].set_xscale("squareroot") + ax[0].set_xticks(np.array([100, 400, 900, 1600])) + ax[0].minorticks_on() + ax[0].tick_params(axis="x", which="minor", length=2, width=0.8) + minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] + ax[0].xaxis.set_ticks(minor_ticks, minor=True) - # Check that the ell_factor is valid - if ell_factor not in ["None", "ell", "ell(ell+1)"]: - raise ValueError( - f"Invalid ell_factor: {ell_factor}. Valid options are: ['None', 'ell', 'ell(ell+1)']" + for ver in self.versions: + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + cov = self.pseudo_cls[ver]["cov"]["COVAR_EB_EB"].data + ax[1].errorbar( + ell, + self.pseudo_cls[ver]["pseudo_cl"]["EB"], + yerr=np.sqrt(np.diag(cov)), + fmt=self.cc[ver]["marker"], + label=ver + " EB", + color=self.cc[ver]["colour"], ) - def get_ell_factor(ell): - """Given some ell, return the ell_factor for the plot.""" - if ell_factor == "None": - return 1 - elif ell_factor == "ell": - return ell - elif ell_factor == "ell(ell+1)": - return ell * (ell + 1) - - # Check that all items in the list are valid polarisation - valid_pols = ["EE", "BB", "EB", "BE"] - for pol in pol_list: - if pol not in valid_pols: - raise ValueError( - f"Invalid polarization type: {pol}. Valid options are: {valid_pols}" - ) + ax[1].set_xlabel(r"$\ell$") + ax[1].set_ylabel(r"$C_\ell$") - fmt_dict = {"EE": "o", "BB": "s", "EB": "^", "BE": "v"} - # From all the versions, get the maximum number of tomo_bin_ids - tomo_bins = {} - for ver in versions: - if tomography: - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - else: - tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] + ax[1].set_xlim(ell.min() - 10, ell.max() + 100) + ax[1].set_xscale("squareroot") + ax[1].set_yscale("log") + ax[1].set_xticks(np.array([100, 400, 900, 1600])) + ax[1].minorticks_on() + ax[1].tick_params(axis="x", which="minor", length=2, width=0.8) + minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] + ax[1].xaxis.set_ticks(minor_ticks, minor=True) - tomo_bins[ver] = {"ids": tomo_bin_ids, "pairs": tomo_bin_pairs} - - n_tomo_bins_plot = max(len(bins["ids"]) for bins in tomo_bins.values()) - - fig, axs = plt.subplots( - n_tomo_bins_plot, - n_tomo_bins_plot, - figsize=(12, 12), - sharex=True, - sharey=True, - ) - - for j, ver in enumerate(versions): - # Plot the pseudo-cl for each tomo bin of the considered version - for tomo_bin_a, tomo_bin_b in tomo_bins[ver]["pairs"]: - if tomography: - ax = axs[tomo_bin_b - 1, tomo_bin_a - 1] - else: - ax = axs - ver_tomo_info = self._pseudo_cls[ver][ - f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}" - ] - ver_label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver - ver_color = ( - self.cc[ver]["colour"] if "colour" in self.cc[ver] else "black" - ) - - pseudo_cls = ver_tomo_info["pseudo_cl"] - cov = ver_tomo_info["cov"] - - ell = pseudo_cls["ell"] - - ell_widths = np.diff(ell) - ell_widths = np.append( - ell_widths, ell_widths[-1] - ) # Assume last bin width is same as second last - - # Better jittering: symmetric around original ell values - jitter_fraction = (j - (len(versions) - 1) / 2) * offset - jittered_ell = ell + jitter_fraction * ell_widths - ell_factor_ = get_ell_factor(jittered_ell) - - for pol in pol_list: - pol_color = self.get_pol_color(ver_color, pol, pol_list) - ax.errorbar( - jittered_ell, - ell_factor_ * pseudo_cls[pol], - yerr=np.sqrt(np.diag(cov[f"COVAR_{pol}_{pol}"].data)) - * ell_factor_, - fmt=fmt_dict[pol], - label=ver_label + f" {pol}", - color=pol_color, - capsize=2, - ) + plt.suptitle("Pseudo-Cl EB (Gaussian covariance)") + plt.legend() + plt.savefig(out_path) - # Draw to extract the yaxis text offset - fig.canvas.draw() + # Plotting BB + out_path = self._output_path("cell_bb.png") - if ell_factor == "None": - ell_label = r"$C_\ell$" - elif ell_factor == "ell": - ell_label = r"$\ell C_\ell$" - else: - ell_label = r"$\ell(\ell+1) C_\ell$" + fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(8, 8)) - for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: - if tomography: - ax = axs[tomo_bin_b - 1, tomo_bin_a - 1] - else: - ax = axs - ax.text( - 0.8, - 0.95, - f"{tomo_bin_a}-{tomo_bin_b}", - transform=ax.transAxes, - verticalalignment="top", + for ver in self.versions: + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + cov = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data + ax[0].errorbar( + ell, + ell * self.pseudo_cls[ver]["pseudo_cl"]["BB"], + yerr=ell * np.sqrt(np.diag(cov)), + fmt=self.cc[ver]["marker"], + label=ver + " BB", + color=self.cc[ver]["colour"], + capsize=2, ) - ax.axhline(0, color="black", ls="--") - ax.set_xlim(ell.min(), ell.max()) - ax.set_xscale("squareroot") - ax.set_xticks(np.array([100, 400, 900, 1600])) - ax.minorticks_on() - minor_ticks = [i * 10 for i in range(1, 10)] + [ - i * 100 for i in range(1, 21) - ] - ax.xaxis.set_ticks(minor_ticks, minor=True) - ax.tick_params(axis="both", which="both", direction="in") - if tomo_bin_b == 6 or tomo_bin_b == "all": - ax.set_xlabel(r"$\ell$") - if tomo_bin_a == 1 or tomo_bin_a == "all": - text_offset = ax.yaxis.get_offset_text().get_text() - ax.yaxis.get_offset_text().set_visible(False) - ax.set_ylabel(f"{ell_label}{text_offset}") - else: - ax.yaxis.get_offset_text().set_visible(False) - if tomo_bin_a != tomo_bin_b: - ax = axs[tomo_bin_a - 1, tomo_bin_b - 1] - ax.set_visible(False) - - # Setup the legend - plt.subplots_adjust(hspace=0.0, wspace=0.0) # Remove space between subplots - - legend_ax = self._add_grouped_legend(fig, versions, self.cc, pol_list, fmt_dict) - - if savefig is not None: - plt.savefig(savefig, dpi=300, bbox_inches="tight") - if show: - plt.show() - - return fig, axs, legend_ax - - def _add_grouped_legend( - self, - fig, - versions, - cc, - pol_list, - fmt_dict, - row_height=0.35, - label_width=2, - col_width=0.9, - gap_below=0.10, - capsize=2, - fontsize=10, - ): - """ - Add a custom legend below the figure with one row per version: - ... - - The box size (in inches) scales with the number of versions (rows) - and polarizations (columns), then gets converted to figure-fraction - coordinates so it works for any figsize. - - Parameters - ---------- - row_height : float - Height per row (per version), in inches. - label_width : float - Width reserved for the version label column, in inches. - col_width : float - Width per polarization column (marker + pol label), in inches. - gap_below : float - Vertical gap between the bottom of the subplot grid and the - top of the legend box, in inches. - """ - n_rows = len(versions) - n_pol = len(pol_list) - - # Desired box size in inches - box_width_in = label_width + n_pol * col_width - box_height_in = n_rows * row_height - fig_w_in, fig_h_in = fig.get_size_inches() + ax[0].axhline(0, color="black", linestyle="--") + ax[0].set_ylabel(r"$\ell C_\ell$") - # Convert to figure-fraction - width_frac = box_width_in / fig_w_in - height_frac = box_height_in / fig_h_in - gap_frac = gap_below / fig_h_in + ax[0].set_xlim(ell.min() - 10, ell.max() + 100) + ax[0].set_xscale("squareroot") + ax[0].set_xticks(np.array([100, 400, 900, 1600])) + ax[0].minorticks_on() + ax[0].tick_params(axis="x", which="minor", length=2, width=0.8) + minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] + ax[0].xaxis.set_ticks(minor_ticks, minor=True) - left = 0.5 - width_frac / 2 # centered horizontally - bottom = -gap_frac - height_frac # just below the subplot grid (y=0) - - legend_ax = fig.add_axes([left, bottom, width_frac, height_frac]) - legend_ax.axis("off") - legend_ax.set_xlim(0, 1) - legend_ax.set_ylim(0, 1) - - # Fractions *within* legend_ax's own 0-1 coordinate system, derived - # from the same inch-based proportions so columns stay consistent - label_frac = label_width / box_width_in - col_frac = col_width / box_width_in - - dummy_yerr = 0.15 / n_rows - - for i, ver in enumerate(versions): - y = 1.0 - (i + 0.5) / n_rows - - ver_label = cc[ver]["label"] if "label" in cc[ver] else ver - ver_color = cc[ver]["colour"] if "colour" in cc[ver] else "black" - - legend_ax.text( - 0.02 * label_frac, - y, - ver_label, - ha="left", - va="center", - fontweight="bold", - fontsize=fontsize, + for ver in self.versions: + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + cov = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data + ax[1].errorbar( + ell, + self.pseudo_cls[ver]["pseudo_cl"]["BB"], + yerr=np.sqrt(np.diag(cov)), + fmt=self.cc[ver]["marker"], + label=ver + " BB", + color=self.cc[ver]["colour"], ) - for k, pol in enumerate(pol_list): - pol_color = self.get_pol_color(ver_color, pol, pol_list) - x_marker = label_frac + k * col_frac + 0.15 * col_frac - x_text = x_marker + 0.15 * col_frac - - legend_ax.errorbar( - [x_marker], - [y], - yerr=dummy_yerr, - fmt=fmt_dict[pol], - color=pol_color, - markersize=6, - capsize=capsize, - clip_on=False, - ) - legend_ax.text( - x_text, - y, - pol, - ha="left", - va="center", - fontsize=fontsize - 1, - ) + ax[1].set_xlabel(r"$\ell$") + ax[1].set_ylabel(r"$C_\ell$") - return legend_ax + ax[1].set_xlim(ell.min() - 10, ell.max() + 100) + ax[1].set_xscale("squareroot") + ax[1].set_yscale("log") + ax[1].set_xticks(np.array([100, 400, 900, 1600])) + ax[1].minorticks_on() + ax[1].tick_params(axis="x", which="minor", length=2, width=0.8) + minor_ticks = [i * 10 for i in range(1, 10)] + [i * 100 for i in range(1, 21)] + ax[1].xaxis.set_ticks(minor_ticks, minor=True) - def get_pol_color(self, base_color, pol, pol_list, lightness_range=(-0.25, 0.25)): - """ - Given a base version color, return a shade variant for a given - polarization, by adjusting lightness in HLS space while keeping - hue and saturation fixed. + plt.suptitle("Pseudo-Cl BB (Gaussian covariance)") + plt.legend() + plt.savefig(out_path) - Parameters - ---------- - base_color : str or tuple - Any matplotlib-recognized color (hex, name, RGB tuple, etc.) - pol : str - The polarization this color is for (e.g. "EE"). - pol_list : list - Full list of polarizations being plotted, used to compute - this pol's relative position in the lightness range. - lightness_range : tuple of float - (min_offset, max_offset) added to the base lightness, spread - evenly across pol_list. Negative = darker, positive = lighter. - Values are in HLS lightness units (0-1 scale), so keep these - modest (e.g. +/-0.25) to avoid washing out to white or black. - - Returns - ------- - tuple - RGB color tuple in [0, 1] range, usable directly in matplotlib. - """ - r, g, b = to_rgb(base_color) - h, lightness, s = colorsys.rgb_to_hls(r, g, b) - - n_pol = len(pol_list) - idx = pol_list.index(pol) - - if n_pol == 1: - l_offset = 0.0 - else: - # spread idx evenly across [lightness_range[0], lightness_range[1]] - frac = idx / (n_pol - 1) # 0 to 1 - l_offset = lightness_range[0] + frac * ( - lightness_range[1] - lightness_range[0] + # Print C_l^BB PTE for each version and save BB data + print("\nC_l^BB PTE summary:") + for ver in self.versions: + cl_bb = self.pseudo_cls[ver]["pseudo_cl"]["BB"] + cov_bb = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data + chi2_bb, _, pte_bb = chi2_and_pte(cl_bb, cov_bb) + chi2_bb = float(chi2_bb) + print( + f" {ver}: C_l^BB PTE = {pte_bb:.4f} " + f"(chi2/dof = {chi2_bb:.1f}/{len(cl_bb)})" ) - lightness_new = min( - max(lightness + l_offset, 0.05), 0.95 - ) # clamp to avoid pure black/white - - r_new, g_new, b_new = colorsys.hls_to_rgb(h, lightness_new, s) - return (r_new, g_new, b_new) + # Save BB data + covariance to .npz + ell = self.pseudo_cls[ver]["pseudo_cl"]["ELL"] + bb_out = self._output_path(f"{ver}_cell_bb_data.npz") + np.savez( + bb_out, + ell=ell, + cl_bb=cl_bb, + cov_bb=cov_bb, + chi2_bb=np.array(chi2_bb), + pte_bb=np.array(pte_bb), + ) + print(f" Saved BB data to {bb_out}") diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index 3508e804..c9355ec9 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -16,13 +16,11 @@ import healpy as hp import numpy as np import pymaster as nmt -from cs_util.cosmo import get_theo_c_ell # Lowest multipole retained by the pseudo-Cl estimators. LMIN = 8 -# ---------------------- Binning utility functions ---------------------- def pseudo_cl_geometry(nside): """Return ``(lmin, lmax, b_lmax)`` for the pseudo-Cl estimator at ``nside``. @@ -89,38 +87,7 @@ def make_namaster_bin( return b -# ---------------------- Map computation utility functions ---------------------- -def get_pixels(ra, dec, nside): - """ - Get the HEALPix pixel indices for given RA and Dec. - - Parameters - ---------- - ra : np.ndarray - Right ascension in degrees. - dec : np.ndarray - Declination in degrees. - nside : int - HEALPix nside parameter. - - Returns - ------- - unique_pix : np.ndarray - Sorted unique pixel indices. - idx : np.ndarray - First-occurrence indices into the input from ``np.unique``. - idx_rep : np.ndarray - Inverse map: pixel-group index for each input object. - """ - pixels = hp.ang2pix(nside, theta=np.radians(90 - dec), phi=np.radians(ra)) - - unique_pix, idx, idx_rep = np.unique(pixels, return_index=True, return_inverse=True) - return unique_pix, idx, idx_rep - - -def get_n_gal_map( - nside, ra, dec, weights=None, unique_pix=None, idx=None, idx_rep=None -): +def get_n_gal_map(nside, ra, dec, weights=None): """Weighted galaxy number-density HEALPix map plus pixel bookkeeping. Bins ``(ra, dec)`` (degrees) onto an ``nside`` HEALPix grid. With @@ -131,106 +98,21 @@ def get_n_gal_map( ------- n_gal : np.ndarray Map of summed weights (or counts) per pixel, shape ``(npix,)``. + unique_pix : np.ndarray + Sorted unique occupied pixel indices. + idx : np.ndarray + First-occurrence indices into the input from ``np.unique``. + idx_rep : np.ndarray + Inverse map: pixel-group index for each input object. """ - if unique_pix is None or idx is None or idx_rep is None: - unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + theta = (90.0 - dec) * np.pi / 180.0 + phi = ra * np.pi / 180.0 + pix = hp.ang2pix(nside, theta, phi) + unique_pix, idx, idx_rep = np.unique(pix, return_index=True, return_inverse=True) n_gal = np.zeros(hp.nside2npix(nside)) n_gal[unique_pix] = np.bincount(idx_rep, weights=weights) - return n_gal - - -def get_shear_map( - ra, dec, e1, e2, w, nside, unique_pix=None, idx=None, idx_rep=None, n_gal_map=None -): - """Weighted shear HEALPix maps plus pixel bookkeeping. - - Bins ``(ra, dec)`` (degrees) onto an ``nside`` HEALPix grid. The shear - components ``(e1, e2)`` are weighted by ``w`` and summed per pixel. If - ``unique_pix``, ``idx``, and ``idx_rep`` are provided, they are used to - avoid recomputing the pixel indices. - If ``n_gal_map`` is provided, it is used to normalize the shear maps by the galaxy density. - - Returns - ------- - e1_map : np.ndarray - Weighted sum of E1 per pixel, shape ``(npix,)``. - e2_map : np.ndarray - Weighted sum of E2 per pixel, shape ``(npix,)``. - """ - if unique_pix is None or idx is None or idx_rep is None: - unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) - - if n_gal_map is None: - n_gal_map = get_n_gal_map( - nside, ra, dec, weights=w, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep - ) - - npix = hp.nside2npix(nside) - e1_map = np.zeros(npix) - e2_map = np.zeros(npix) - - e1_map[unique_pix] = np.bincount(idx_rep, weights=e1 * w) - e2_map[unique_pix] = np.bincount(idx_rep, weights=e2 * w) - - non_zero = n_gal_map > 0 - e1_map[non_zero] /= n_gal_map[non_zero] - e2_map[non_zero] /= n_gal_map[non_zero] - - return e1_map, e2_map - - -def get_variance_map( - nside, ra, dec, e1, e2, w, unique_pix=None, idx=None, idx_rep=None -): - """Compute the variance map of the shear components. - - The variance is computed as the weighted variance of the shear components in each pixel. - - Returns - ------- - variance_map : np.ndarray - Variance map of the shear components, shape ``(npix,)``. - """ - if unique_pix is None or idx is None or idx_rep is None: - unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) - - npix = hp.nside2npix(nside) - variance_map = np.zeros(npix) - - variance_map[unique_pix] = np.bincount( - idx_rep, weights=0.5 * (e1**2 + e2**2) * w**2 - ) - - return variance_map - - -def get_noise_bias_analytical( - ra, dec, e1, e2, w, lmax, nside=1024, unique_pix=None, idx=None, idx_rep=None -): - """ - Compute the analytical noise bias for shear power spectrum. - """ - variance_map = get_variance_map( - nside=nside, - ra=ra, - dec=dec, - e1=e1, - e2=e2, - w=w, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - ) - - noise_bias = hp.nside2pixarea(nside) * np.mean(variance_map) - - noise_bias_cl = np.zeros((4, lmax)) - - noise_bias_cl[0, :] = noise_bias # EE - noise_bias_cl[3, :] = noise_bias # BB - - return noise_bias_cl + return n_gal, unique_pix, idx, idx_rep def apply_random_rotation(e1, e2, rng=None): @@ -258,493 +140,13 @@ def apply_random_rotation(e1, e2, rng=None): return e1_out, e2_out -def get_noise_realisation( - ra, - dec, - e1, - e2, - w, - nside, - unique_pix=None, - idx=None, - idx_rep=None, - n_gal_map=None, - rng=None, -): - """ - Generate a random noise realisation of the shear maps by applying a random rotation to the ellipticity components. - - Parameters - ---------- - ra, dec : np.ndarray - Right ascension and declination of the sources. - e1, e2 : np.ndarray - Ellipticity components. - w : np.ndarray - Weights of the sources. - nside : int - HEALPix resolution. - unique_pix, idx, idx_rep : np.ndarray, optional - Pixel indices and bookkeeping arrays. If not provided, they will be computed. - n_gal_map : np.ndarray, optional - Galaxy number density map. If not provided, it will be computed. - - Returns - ------- - noise_map_e1, noise_map_e2 : np.ndarray - Noise map for ellipticity components. - """ - # Apply random rotation to the ellipticity components - e1_rot, e2_rot = apply_random_rotation(e1, e2, rng=rng) - - # Compute the noise maps using the rotated ellipticity components - noise_map_e1, noise_map_e2 = get_shear_map( - ra=ra, - dec=dec, - e1=e1_rot, - e2=e2_rot, - w=w, - nside=nside, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - n_gal_map=n_gal_map, - ) - - return noise_map_e1, noise_map_e2 - - -def get_noise_bias_from_gaussian_real( - ra, - dec, - e1, - e2, - w, - nside, - nrandom_cell, - binning, - ell_step=10, - n_ell_bins=32, - power=0.5, - unique_pix=None, - idx=None, - idx_rep=None, - n_gal_map=None, - wsp=None, - seed=42, -): - """ - Compute the power spectrum of the noise bias from random realisations. - - Parameters - ---------- - ra, dec : np.ndarray - Right ascension and declination of the sources. - e1, e2 : np.ndarray - Ellipticity components. - w : np.ndarray - Weights of the sources. - nside : int - HEALPix resolution. - nrandom_cell : int - Number of random cells to use for the noise estimation. - binning, ell_step, n_ell_bins, power : str, int, int, float - Binning scheme and parameters. - unique_pix, idx, idx_rep : np.ndarray, optional - Pixel indices and bookkeeping arrays. If not provided, they will be computed. - n_gal_map : np.ndarray, optional - Galaxy number density map. If not provided, it will be computed. - - Returns - ------- - noise_bias_cl : np.ndarray - Power spectrum of the noise bias. - """ - lmin, lmax, b_lmax = pseudo_cl_geometry(nside) - - b = make_namaster_bin( - lmin, - lmax, - b_lmax, - binning, - ell_step=ell_step, - n_ell_bins=n_ell_bins, - power=power, - ) - - ell_eff = b.get_effective_ells() - noise_bias_cl = np.zeros((4, ell_eff.size)) - - rng = np.random.default_rng(seed) - - if unique_pix is None or idx is None or idx_rep is None: - unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) - - if n_gal_map is None: - n_gal_map = get_n_gal_map( - nside, ra, dec, weights=w, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep - ) - - if wsp is None: - _, _, wsp = get_field_and_workspace_from_map(b, mask_a=n_gal_map) - - for _ in range(nrandom_cell): - noise_map_e1, noise_map_e2 = get_noise_realisation( - ra, - dec, - e1, - e2, - w, - nside, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - n_gal_map=n_gal_map, - rng=rng, - ) - - noise_map = noise_map_e1 + 1j * noise_map_e2 - del noise_map_e1, noise_map_e2 - - _, cl_noise_, _ = get_pseudo_cls_map( - noise_map, - n_gal_map, - nside, - binning, - ell_step=ell_step, - n_ell_bins=n_ell_bins, - power=power, - wsp=wsp, - ) - - noise_bias_cl += cl_noise_ - - noise_bias_cl /= nrandom_cell - - return noise_bias_cl - - -def get_noise_bias( - ra, - dec, - e1, - e2, - w, - nside, - noise_bias_method, - binning, - *, - ell_step=10, - n_ell_bins=32, - power=0.5, - nrandom_cell=100, - seed=42, -): - """ - Compute the noise bias from object positions and ellipticities. - - Parameters - ---------- - ra, dec : np.ndarray - Right ascension and declination of the sources. - e1, e2 : np.ndarray - Ellipticity components. - w : np.ndarray - Weights of the sources. - nside : int - HEALPix resolution. - noise_bias_method : {'randoms', 'analytic'} - Method used to estimate the noise bias. - binning : {'linear', 'logspace', 'powspace'} - Binning scheme. - ell_step : int, optional - Bin width in ell for ``'linear'`` binning. - n_ell_bins : int, optional - Number of ell bins for ``'logspace'`` / ``'powspace'`` binning. - power : float, optional - Exponent for ``'powspace'`` binning. - nrandom_cell : int, optional - Number of random cells to use for the noise estimation. (only for the `randoms` method) - seed : int, optional - Random seed for reproducibility. (only for the `randoms` method) - - Returns - ------- - noise_bias_cl : np.ndarray - Power spectrum of the noise bias. - """ - if noise_bias_method not in ["randoms", "analytic"]: - raise ValueError("noise_bias_method must be 'randoms' or 'analytic'") - - lmin, lmax, b_lmax = pseudo_cl_geometry(nside) - - b = make_namaster_bin( - lmin, - lmax, - b_lmax, - binning, - ell_step=ell_step, - n_ell_bins=n_ell_bins, - power=power, - ) - - unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) - - if noise_bias_method == "analytic": - noise_bias_cl = get_noise_bias_analytical( - ra, - dec, - e1, - e2, - w, - lmax, - nside, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - ) - - elif noise_bias_method == "randoms": - noise_bias_cl = get_noise_bias_from_gaussian_real( - ra, - dec, - e1, - e2, - w, - nside, - nrandom_cell, - binning, - ell_step=ell_step, - n_ell_bins=n_ell_bins, - power=power, - unique_pix=unique_pix, - idx=idx, - idx_rep=idx_rep, - seed=seed, - ) - - noise_bias_cl = b.unbin_cell(noise_bias_cl) - else: - raise ValueError( - f"Invalid noise bias method `{noise_bias_method}`. Must be 'analytic' or 'randoms'." - ) - - return noise_bias_cl - - -# ---------------------- Cl computation functions ---------------------- -def get_field_and_workspace_from_map( - b, - mask_a, - e1_map_a=None, - e2_map_a=None, - mask_b=None, - e1_map_b=None, - e2_map_b=None, - pol_factor=-1, - return_wsp=True, -): - """Compute a NaMaster field and workspace object from the input maps. - - If the shear maps are None, returns field objects but only the workspace objects is relevant and contains the mixing matrix. - If the second mask and shear maps (indexed b) are provided, the mixing matrix is computed between the two fields. - - Parameters - ---------- - b : nmt.NmtBin - NaMaster binning object. - mask_a : np.ndarray - Field mask for the first map. - e1_map_a : np.ndarray, optional - E1 map for the first field. - e2_map_a : np.ndarray, optional - E2 map for the first field. - mask_b : np.ndarray, optional - Field mask for the second map. - e1_map_b : np.ndarray, optional - E1 map for the second field. - e2_map_b : np.ndarray, optional - E2 map for the second field. - pol_factor : float, optional - Polarization factor to apply to the E2 map. - return_wsp : bool, optional - If True, return the NaMaster workspace object containing the mixing matrix. - - Returns - ------- - field_a : nmt.NmtField - NaMaster field object for the first map. - field_b : nmt.NmtField - NaMaster field object for the second map (if provided, same than the first map otherwise). - wsp : nmt.NmtWorkspace - NaMaster workspace object containing the mixing matrix. - - """ - nside = hp.npix2nside(len(mask_a)) - lmax = b.lmax - if e1_map_a is None or e2_map_a is None: - e1_map_a = np.zeros(hp.nside2npix(nside)) - e2_map_a = np.zeros(hp.nside2npix(nside)) - - # Create NaMaster field - field_a = nmt.NmtField( - mask=mask_a, maps=[e1_map_a, pol_factor * e2_map_a], lmax=lmax - ) - - if mask_b is not None: - if e1_map_b is None or e2_map_b is None: - e1_map_b = np.zeros(hp.nside2npix(nside)) - e2_map_b = np.zeros(hp.nside2npix(nside)) - - field_b = nmt.NmtField( - mask=mask_b, maps=[e1_map_b, pol_factor * e2_map_b], lmax=lmax - ) - else: - field_b = field_a - - if return_wsp: - # Create NaMaster workspace - wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) - - return field_a, field_b, wsp - else: - return field_a, field_b, None - - -def get_field_and_workspace_from_catalog( - b, - ra_a, - dec_a, - e1_a, - e2_a, - w_a, - ra_b=None, - dec_b=None, - e1_b=None, - e2_b=None, - w_b=None, - pol_factor=-1, - return_wsp=True, - same_bin=False, -): - """Create a NaMaster field and workspace from the input catalog. - - If the second catalog is provided, the mixing matrix is computed between the two fields. - - Parameters - ---------- - b : nmt.NmtBin - NaMaster binning object. - ra_a : np.ndarray - Right ascension of sources in the first catalog. - dec_a : np.ndarray - Declination of sources in the first catalog. - e1_a : np.ndarray - E1 shear component of sources in the first catalog. - e2_a : np.ndarray - E2 shear component of sources in the first catalog. - w_a : np.ndarray - Weights of sources in the first catalog. - ra_b : np.ndarray, optional - Right ascension of sources in the second catalog. - dec_b : np.ndarray, optional - Declination of sources in the second catalog. - e1_b : np.ndarray, optional - E1 shear component of sources in the second catalog. - e2_b : np.ndarray, optional - E2 shear component of sources in the second catalog. - w_b : np.ndarray, optional - Weights of sources in the second catalog. - pol_factor : float, optional - Polarization factor to apply to the E2 component. - return_wsp : bool, optional - If True, return the NaMaster workspace object containing the mixing matrix. - - Returns - ------- - field_a : nmt.NmtFieldCatalog - NaMaster field object for the first catalog. - field_b : nmt.NmtFieldCatalog - NaMaster field object for the second catalog (if provided, same as the first catalog otherwise). - wsp : nmt.NmtWorkspace - NaMaster workspace object containing the mixing matrix. - - """ - lmax = b.lmax - # Get field for input catalog a - field_a = nmt.NmtFieldCatalog( - positions=[ra_a, dec_a], - weights=w_a, - field=[e1_a, pol_factor * e2_a], - lmax=lmax, - lmax_mask=lmax, - spin=2, - lonlat=True, - ) - - if ( - ra_b is not None - and dec_b is not None - and e1_b is not None - and e2_b is not None - and w_b is not None - and not same_bin - ): - field_b = nmt.NmtFieldCatalog( - positions=[ra_b, dec_b], - weights=w_b, - field=[e1_b, pol_factor * e2_b], - lmax=lmax, - lmax_mask=lmax, - spin=2, - lonlat=True, - ) - else: - field_b = field_a - - if return_wsp: - wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) - return field_a, field_b, wsp - else: - return field_a, field_b, None - - -def compute_cl_from_field_and_workspace(field_a, field_b, wsp, b): - """Compute the angular power spectrum from the input NaMaster field and workspace - - Parameters - ---------- - field_a : nmt.NmtField - NaMaster field object for the first catalog. - field_b : nmt.NmtField - NaMaster field object for the second catalog. - wsp : nmt.NmtWorkspace - NaMaster workspace object containing the mixing matrix. - b : nmt.NmtBin - NaMaster binning object. - - Returns - ------- - cl_coupled : np.ndarray - Coupled angular power spectrum. - cl_decoupled : np.ndarray - Decoupled angular power spectrum. - """ - cl_coupled = nmt.compute_coupled_cell(field_a, field_b) - cl_decoupled = wsp.decouple_cell(cl_coupled) - - return cl_coupled, cl_decoupled - - def get_pseudo_cls_map( - shear_map_a, - mask_a, + shear_map, + mask, nside, binning, *, - shear_map_b=None, - mask_b=None, - pol_factor=-1, + pol_factor=True, wsp=None, ell_step=10, n_ell_bins=32, @@ -754,20 +156,16 @@ def get_pseudo_cls_map( Parameters ---------- - shear_map_a : np.ndarray + shear_map : np.ndarray Complex shear map (``e1 + 1j * e2``). - mask_a : np.ndarray + mask : np.ndarray Field mask (the galaxy number-density map). nside : int HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. - shear_map_b : np.ndarray, optional - Complex shear map for the second field (``e1 + 1j * e2``). - mask_b : np.ndarray, optional - Field mask for the second field (the galaxy number-density map). - pol_factor : float, optional - Polarization factor to apply to the E2 component. + pol_factor : bool, optional + If ``True`` flip the sign of the imaginary (e2) component. wsp : nmt.NmtWorkspace, optional Reuse a coupling workspace; built from the field if ``None``. ell_step, n_ell_bins, power : optional @@ -782,27 +180,6 @@ def get_pseudo_cls_map( wsp : nmt.NmtWorkspace The coupling workspace (newly built or the one passed in). """ - # First do some assertion checks - if shear_map_b is not None: - assert mask_b is not None, "mask_b must be provided if shear_map_b is provided" - assert shear_map_a.shape == shear_map_b.shape, ( - "shear_map_a and shear_map_b must have the same shape" - ) - assert mask_a.shape == mask_b.shape, ( - "mask_a and mask_b must have the same shape" - ) - - if mask_b is not None: - assert shear_map_b is not None, ( - "shear_map_b must be provided if mask_b is provided" - ) - assert shear_map_a.shape == shear_map_b.shape, ( - "shear_map_a and shear_map_b must have the same shape" - ) - assert mask_a.shape == mask_b.shape, ( - "mask_a and mask_b must have the same shape" - ) - lmin, lmax, b_lmax = pseudo_cl_geometry(nside) b = make_namaster_bin( @@ -816,36 +193,18 @@ def get_pseudo_cls_map( ) ell_eff = b.get_effective_ells() - if wsp is None: - field_a, field_b, wsp = get_field_and_workspace_from_map( - b, - mask_a, - e1_map_a=shear_map_a.real, - e2_map_a=shear_map_a.imag, - mask_b=mask_b, - e1_map_b=shear_map_b.real if shear_map_b is not None else None, - e2_map_b=shear_map_b.imag if shear_map_b is not None else None, - pol_factor=pol_factor, - return_wsp=True, - ) - else: - field_a, field_b, _ = get_field_and_workspace_from_map( - b, - mask_a, - e1_map_a=shear_map_a.real, - e2_map_a=shear_map_a.imag, - mask_b=mask_b, - e1_map_b=shear_map_b.real if shear_map_b is not None else None, - e2_map_b=shear_map_b.imag if shear_map_b is not None else None, - pol_factor=pol_factor, - return_wsp=False, - ) + factor = -1 if pol_factor else 1 - cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( - field_a, field_b, wsp, b + f_all = nmt.NmtField( + mask=mask, maps=[shear_map.real, factor * shear_map.imag], lmax=b_lmax ) + if wsp is None: + wsp = nmt.NmtWorkspace.from_fields(f_all, f_all, b) + + cl_coupled = nmt.compute_coupled_cell(f_all, f_all) + cl_all = wsp.decouple_cell(cl_coupled) - return ell_eff, cl_decoupled, wsp + return ell_eff, cl_all, wsp def get_pseudo_cls_catalog( @@ -854,9 +213,7 @@ def get_pseudo_cls_catalog( nside, binning, *, - tomo_bin_a="all", - tomo_bin_b="all", - pol_factor=-1, + pol_factor=True, wsp=None, ell_step=10, n_ell_bins=32, @@ -875,10 +232,8 @@ def get_pseudo_cls_catalog( HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. - tomo_bin_a, tomo_bin_b : str or int or None, optional - Tomographic bin IDs for the two fields. - pol_factor : int, optional - Polarization factor to apply to the E2 component. + pol_factor : bool, optional + If ``True`` flip the sign of the e2 component. wsp : nmt.NmtWorkspace, optional Reuse a coupling workspace; built from the field if ``None``. ell_step, n_ell_bins, power : optional @@ -893,11 +248,6 @@ def get_pseudo_cls_catalog( wsp : nmt.NmtWorkspace The coupling workspace (newly built or the one passed in). """ - # First make some assertion checks reagarding the run mode - assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - tomo_bin_a != "all" and tomo_bin_b != "all" - ), "Both tomo_bin_a and tomo_bin_b must be provided or both must be 'all'" - lmin, lmax, b_lmax = pseudo_cl_geometry(nside) b = make_namaster_bin( @@ -911,140 +261,22 @@ def get_pseudo_cls_catalog( ) ell_eff = b.get_effective_ells() - is_tomography = tomo_bin_a != "all" and tomo_bin_b != "all" - if is_tomography: - assert params["tomo_bin_col"] is not None, ( - "The column of tomographic bin ids is not specified." - ) - mask_tomo_a = catalog[params["tomo_bin_col"]] == tomo_bin_a - mask_tomo_b = catalog[params["tomo_bin_col"]] == tomo_bin_b - catalog_a = catalog[mask_tomo_a] - catalog_b = catalog[mask_tomo_b] - same_bin = tomo_bin_a == tomo_bin_b - else: - catalog_a = catalog - catalog_b = catalog - same_bin = True + factor = -1 if pol_factor else 1 - if wsp is None: - field_a, field_b, wsp = get_field_and_workspace_from_catalog( - b, - ra_a=catalog_a[params["ra_col"]], - dec_a=catalog_a[params["dec_col"]], - e1_a=catalog_a[params["e1_col"]], - e2_a=catalog_a[params["e2_col"]], - w_a=catalog_a[params["w_col"]], - ra_b=catalog_b[params["ra_col"]], - dec_b=catalog_b[params["dec_col"]], - e1_b=catalog_b[params["e1_col"]], - e2_b=catalog_b[params["e2_col"]], - w_b=catalog_b[params["w_col"]], - pol_factor=pol_factor, - return_wsp=True, - same_bin=same_bin, - ) - else: - field_a, field_b, _ = get_field_and_workspace_from_catalog( - b, - ra_a=catalog_a[params["ra_col"]], - dec_a=catalog_a[params["dec_col"]], - e1_a=catalog_a[params["e1_col"]], - e2_a=catalog_a[params["e2_col"]], - w_a=catalog_a[params["w_col"]], - ra_b=catalog_b[params["ra_col"]], - dec_b=catalog_b[params["dec_col"]], - e1_b=catalog_b[params["e1_col"]], - e2_b=catalog_b[params["e2_col"]], - w_b=catalog_b[params["w_col"]], - pol_factor=pol_factor, - return_wsp=False, - same_bin=same_bin, - ) - - cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( - field_a, field_b, wsp, b + f_all = nmt.NmtFieldCatalog( + positions=[catalog[params["ra_col"]], catalog[params["dec_col"]]], + weights=catalog[params["w_col"]], + field=[catalog[params["e1_col"]], factor * catalog[params["e2_col"]]], + lmax=b_lmax, + lmax_mask=b_lmax, + spin=2, + lonlat=True, ) - return ell_eff, cl_decoupled, wsp - - -# ---------------------- Covariance computation functions ---------------------- -def get_fiducial_cl(z, dndz, lmax, cosmo, backend="camb"): - """ - Get the fiducial Cl's using the redshift distribution. - Cosmology is determined by the input cosmo object. - """ - ell = np.arange(1, lmax + 1) - - fiducial_cl = get_theo_c_ell(ell=ell, z=z, nz=dndz, backend=backend, cosmo=cosmo) - - return fiducial_cl - - -def get_pseudo_cl_iNKA_covariance( - input_cl_a1_b1, - input_cl_a1_b2, - input_cl_a2_b1, - input_cl_a2_b2, - field_a1, - field_a2, - field_b1, - field_b2, - wsp_a, - wsp_b, - b, -): - """Compute the iNKA covariance for pseudo-Cl. - - Parameters - ---------- - input_cl_a1_b1 : np.ndarray - Input Cl for field a1 and b1. - input_cl_a1_b2 : np.ndarray - Input Cl for field a1 and b2. - input_cl_a2_b1 : np.ndarray - Input Cl for field a2 and b1. - input_cl_a2_b2 : np.ndarray - Input Cl for field a2 and b2. - field_a1 : nmt.NmtField - NaMaster field object for the first catalog (a1). - field_a2 : nmt.NmtField - NaMaster field object for the second catalog (a2). - field_b1 : nmt.NmtField - NaMaster field object for the first catalog (b1). - field_b2 : nmt.NmtField - NaMaster field object for the second catalog (b2). - wsp_a : nmt.NmtWorkspace - NaMaster workspace object containing the mixing matrix for fields a. - wsp_b : nmt.NmtWorkspace - NaMaster workspace object containing the mixing matrix for fields b. - b : nmt.NmtBin - NaMaster binning object. - - Returns - ------- - cov_matrix : np.ndarray - Covariance matrix of the pseudo-Cl, shape ``(n_bins, n_bins)``. - """ - # Compute the coupling coefficients for the covariance - cw = nmt.NmtCovarianceWorkspace.from_fields(field_a1, field_a2, field_b1, field_b2) - - # Get actual number of ell bins from binning scheme - n_ell_actual = b.get_n_bands() + if wsp is None: + wsp = nmt.NmtWorkspace.from_fields(f_all, f_all, b) - # Compute the covariance using NaMaster's built-in function - cov_matrix = nmt.gaussian_covariance( - cw, - 2, - 2, - 2, - 2, - input_cl_a1_b1, - input_cl_a1_b2, - input_cl_a2_b1, - input_cl_a2_b2, - wsp_a, - wb=wsp_b, - ).reshape([n_ell_actual, 4, n_ell_actual, 4]) + cl_coupled = nmt.compute_coupled_cell(f_all, f_all) + cl_all = wsp.decouple_cell(cl_coupled) - return cov_matrix + return ell_eff, cl_all, wsp diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 95a720f9..6874a08c 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -51,10 +51,6 @@ def get_params_rho_tau(cat, survey="other"): params["w_col"] = cat["shear"]["w_col"] params["e1_col"] = cat["shear"]["e1_col"] params["e2_col"] = cat["shear"]["e2_col"] - try: - params["tomo_bin_col"] = cat["shear"]["tomo_bin_col"] - except KeyError: - params["tomo_bin_col"] = None params["R11"] = cat["shear"].get("R11") params["R22"] = cat["shear"].get("R22") diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index cdaef9d4..45a2366d 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -58,7 +58,6 @@ import yaml from sp_validation.cosmo_val import CosmologyValidation -from sp_validation.pseudo_cl import apply_random_rotation from sp_validation.rho_tau import get_params_rho_tau # These tests need the full harmonic-space stack (pymaster/NaMaster + healpy), @@ -166,7 +165,7 @@ def cv(tmp_path): binning="powspace", power=0.5, n_ell_bins=N_ELL_BINS, - pol_factor=-1, + pol_factor=True, ) cv._test_version = version return cv @@ -266,38 +265,18 @@ def test_unknown_binning_raises(self, cv): cv.get_namaster_bin(LMIN, LMAX, B_LMAX) -# ========================================================================== -# get_pixels -- fetch the pixels from ra, dec, and nside (HEALPix) -# ========================================================================== -def test_get_pixels(cv, cat_and_params): - """Pin the HEALPix pixelization of the synthetic catalog.""" - cat_gal, params = cat_and_params - unique_pix, idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) - - # Structural invariants of the HEALPix occupancy map. - assert unique_pix.size == 485 - assert idx_rep.size == 5000 # one entry per galaxy - - # Pinned scalar summaries: total weight is conserved (sum of weights), - # peak occupancy, and the pixel index bookkeeping. - npt.assert_array_equal(unique_pix[:5], [12167, 12168, 12169, 12170, 12171]) - assert int(unique_pix.sum()) == 7849989 - - # =========================================================================== # get_n_gal_map -- weighted galaxy number-density map # =========================================================================== def test_get_n_gal_map(cv, cat_and_params): cat_gal, params = cat_and_params - - unique_pix, idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) - n_gal = cv.get_n_gal_map( - params, NSIDE, cat_gal, unique_pix=unique_pix, idx=None, idx_rep=idx_rep - ) + n_gal, unique_pix, idx, idx_rep = cv.get_n_gal_map(params, NSIDE, cat_gal) # Structural invariants of the HEALPix occupancy map. assert n_gal.shape == (healpy.nside2npix(NSIDE),) assert int(np.count_nonzero(n_gal)) == 485 + assert unique_pix.size == 485 + assert idx_rep.size == 5000 # one entry per galaxy # The map is supported exactly on the occupied pixels. npt.assert_array_equal(np.nonzero(n_gal)[0], np.sort(unique_pix)) @@ -305,6 +284,8 @@ def test_get_n_gal_map(cv, cat_and_params): # peak occupancy, and the pixel index bookkeeping. npt.assert_allclose(n_gal.sum(), 3760.657282591494, rtol=RTOL_DET) npt.assert_allclose(n_gal.max(), 16.063410433711447, rtol=RTOL_DET) + npt.assert_array_equal(unique_pix[:5], [12167, 12168, 12169, 12170, 12171]) + assert int(unique_pix.sum()) == 7849989 npt.assert_allclose( n_gal[unique_pix][:5], np.array( @@ -326,10 +307,7 @@ def test_get_n_gal_map(cv, cat_and_params): # =========================================================================== def _build_shear_map(cv, cat_gal, params): """Replicate calculate_pseudo_cl_map's weighted shear-map construction.""" - unique_pix, _idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) - n_gal = cv.get_n_gal_map( - params, NSIDE, cat_gal, unique_pix=unique_pix, idx=None, idx_rep=idx_rep - ) + n_gal, unique_pix, _idx, idx_rep = cv.get_n_gal_map(params, NSIDE, cat_gal) w = cat_gal[params["w_col"]] e1 = cat_gal[params["e1_col"]] e2 = cat_gal[params["e2_col"]] @@ -415,9 +393,7 @@ def test_get_pseudo_cls_map(cv, cat_and_params): # =========================================================================== def test_get_pseudo_cls_catalog(cv, cat_and_params): cat_gal, params = cat_and_params - ell_eff, cl_all, wsp = cv.get_pseudo_cls_catalog( - catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" - ) + ell_eff, cl_all, wsp = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) assert cl_all.shape == (4, N_ELL_BINS) # Effective ells share the binning math with the map path: bitwise-stable. @@ -495,7 +471,7 @@ def test_apply_random_rotation_preserves_magnitude(cv, cat_and_params): e1 = np.asarray(cat_gal[params["e1_col"]], dtype=np.float64) e2 = np.asarray(cat_gal[params["e2_col"]], dtype=np.float64) - e1_rot, e2_rot = apply_random_rotation(e1, e2) + e1_rot, e2_rot = cv.apply_random_rotation(e1, e2) assert e1_rot.shape == e1.shape assert e2_rot.shape == e2.shape @@ -515,16 +491,16 @@ def test_apply_random_rotation_reproducible_with_seed(cv, cat_and_params): e1 = np.asarray(cat_gal[params["e1_col"]], dtype=np.float64) e2 = np.asarray(cat_gal[params["e2_col"]], dtype=np.float64) - a1, a2 = apply_random_rotation(e1, e2, np.random.default_rng(42)) - b1, b2 = apply_random_rotation(e1, e2, np.random.default_rng(42)) + a1, a2 = cv.apply_random_rotation(e1, e2, np.random.default_rng(42)) + b1, b2 = cv.apply_random_rotation(e1, e2, np.random.default_rng(42)) npt.assert_array_equal(a1, b1) npt.assert_array_equal(a2, b2) - c1, _ = apply_random_rotation(e1, e2, np.random.default_rng(7)) + c1, _ = cv.apply_random_rotation(e1, e2, np.random.default_rng(7)) assert not np.allclose(a1, c1) - d1, _ = apply_random_rotation(e1, e2) - f1, _ = apply_random_rotation(e1, e2) + d1, _ = cv.apply_random_rotation(e1, e2) + f1, _ = cv.apply_random_rotation(e1, e2) assert not np.allclose(d1, f1) @@ -539,9 +515,9 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): (it drops the BE row); we pin the round-tripped table. """ ver = cv._test_version - cv._pseudo_cls = {ver: {"tomo_bin_all_tomo_bin_all": {}}} + cv._pseudo_cls = {ver: {}} out_path = cv._output_path(f"pseudo_cl_cat_{ver}.fits") - cv.calculate_pseudo_cl_catalog(ver, out_path, tomo_bin_a="all", tomo_bin_b="all") + cv.calculate_pseudo_cl_catalog(ver, out_path) assert os.path.exists(out_path) d = fits.getdata(out_path) @@ -612,7 +588,5 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): # (same computation, FITS round-trip) -- consistency, not an independent pin. cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) params = get_params_rho_tau(cv.cc[ver], survey=ver) - _, cl_prim, _ = cv.get_pseudo_cls_catalog( - catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" - ) + _, cl_prim, _ = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) npt.assert_allclose(ee, cl_prim[0], rtol=RTOL_CAT, atol=ATOL_CAT) From 6eabbc6b466c944ffe33dc83de5a1f68a9a9a96e Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 16 Jul 2026 15:49:31 +0200 Subject: [PATCH 023/107] Add unit tests for the tomographic pseudo cls --- .../tests/data/test_cl_catalog.npz | Bin 0 -> 6903 bytes src/sp_validation/tests/test_pseudo_cl.py | 195 ++++++++++++++++-- 2 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 src/sp_validation/tests/data/test_cl_catalog.npz diff --git a/src/sp_validation/tests/data/test_cl_catalog.npz b/src/sp_validation/tests/data/test_cl_catalog.npz new file mode 100644 index 0000000000000000000000000000000000000000..fdba323b63704311ff63bd9048ff9fad12c63c33 GIT binary patch literal 6903 zcmd5>3v?9K8J^8MP!NkCc!-9FB)n3BP&~pJmXJivhA|}|SRTVZW^;Gh%q%;z^duoB z6ha70wILJ0pgbBA{(ijo|HZ1*KSmYB5isig6VNlwY9_^cu?`5XeC&a`@D0nN+6>J`v+@|f|7 z#fkshr%!2Ab;nQxk~&!Q=X(oMc)R2iQu6&C#f6|-3pDjdyic%8qOAD*c15ew)M(vO zQ)5MkVN#`8m@;ja$)=TSbF^fOX$*)|qa8|tSHSiWPd%zttTS#!s-~41$8lLWMEb!p zL(yy%i?w1cSskQE`4Vq)iF_*Gb&77O@m^W*J0#xj(LjWHzZId85L%a%;&dspen2QS zH7@hXxw#lHS{Q=@u>_JO4qfy`xMJ{bd=7L!fC=2gC99Dc+1UtdMg~5lp+g!zOH0!f z3=Iqiebor(Z5v^GY=l7@u`p{jFrud|760myPxNR~s8<-;LaCa%@| z7L+Q>037}_vx#BMNd^_z!)OJ9Ak zbTsVv@r}cctzQPl6JOW0l^?4D!s178d;b1RA;I&DEF8IRoWQiel#yy^92nIS7Uip`17rb1~S!tykVDV%%DnY zO4q1MRK~mlva^`f81e|dt;R@R#U&wvF{l`NsW(oIM`e%N;W|d^bI)R-nx+RSYihjR zDoUcuZuRIW{5Cb3_lqt?yo$~K<@ekC-0S9B7U;_+zPA5kC*C0>|CN9UG?J?SH6G*06!$7 ziD;v?Nsmib z1X1wmD_CW_%cWKLZ9p{g36>)X@ByV;zCeM+1Pbv4SP6>3vm7V^rC?Rh=!3F5g#wi0 z$B_354y@LvE5sFoPZ1m%deMMj z^-4~sEGVdz(ywG!p@39~S8T>v94|Uug2y3i=>5#IdQpMk{rIg@=fEH&G(+x`vA*EJ@6~FZzvHb4);{4z>(h`daii59k|l1mE`(%>8?BCzEbr22 z!K+mz1GU{l*xEH4E12guz{90a*Ol(S#a{c#=tVcROH}jObBT`^b^K!=Jd~AN!mM7- zwzc-3zVq~3@WT^*I_H^B(w+de9@%MMTH<8wS@+wd!Ikh7J2$myLN45QvvB>pdp6RZ zfqf(Cv#cGqU2N{Dfk*%S`W5yyX2j6B+poe+)t9enoAYQ-0c#gdnmjwPojoukJ<~t+ z4E)`aH%k})Wg`2V&hy&9{iU?$U{|h=S5M4;9ahhLbm^|VpRq3<*_c{b^)x%bdHb@Q z`|@Z{0$+J^)W4j+5ZJoo**8<8%3*y{Lta8-4lKX&@%7wsOK8sm3z^ApzkFjMESs4X zF*a@>4~+zsES;exMUaD46If^T4O zd>6t6-_YRrI>H6tL*4KR-~)G*acy)0;7{NLI0@RoDexgU4L<6r;gh<)X!$>CxH}(X z=<$t7&~HJIjJw59jggYOrBIELn7h598Y4Y-FAvrDZj%1Vh_B%3iodcQ^X`4CxuKbi zE$oRuULAWn^(>Wi*m}IA?-Xz^eD7NMGtPIT$QZ*O9Fp)&Ovyb|(%BDM*VcXgQUh$- zY4?944<+MZSo%@I{x6HR1SGxsO69Hkiv_R|bk-+$&yw*B+_%}Jv~b%4l3vHO4@-Et znLTja|M->XR5EU0k3E#}x6c;zr;-kL700=ow#BgZ_333?{5U$oO`WyNsMh+EQ{XriclVxBpg!e~QBYe@C literal 0 HcmV?d00001 diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index cdaef9d4..61677827 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -51,6 +51,7 @@ """ import os +from pathlib import Path import numpy as np import numpy.testing as npt @@ -74,6 +75,8 @@ SEED = 1234 N_ELL_BINS = 8 +REFERENCE_TOMO = Path(__file__).parent / "data" / "test_cl_catalog.npz" + # --------------------------------------------------------------------------- # Synthetic-catalog fixture (deterministic; no cluster data) @@ -102,9 +105,10 @@ def _write_synthetic_config(tmp_path): e1 = rng.normal(0, 0.25, n_gal) e2 = rng.normal(0, 0.25, n_gal) w = rng.uniform(0.5, 1.0, n_gal) - Table({"RA": ra, "Dec": dec, "e1": e1, "e2": e2, "w": w}).write( - cat_dir / "shear.fits", overwrite=True - ) + tomo_bin_id = rng.integers(1, 3, n_gal) # Create a two bin catalogue + Table( + {"RA": ra, "Dec": dec, "e1": e1, "e2": e2, "w": w, "tomo_bin_id": tomo_bin_id} + ).write(cat_dir / "shear.fits", overwrite=True) z_edges = np.linspace(0.05, 3.0, 31) dndz = np.exp(-(((z_edges - 0.7) / 0.3) ** 2)) @@ -121,6 +125,7 @@ def _write_synthetic_config(tmp_path): "e2_col_corrected": "e2", "ra_col": "RA", "dec_col": "Dec", + "tomo_bin_col": "tomo_bin_id", } # Minimal psf block so get_params_rho_tau() succeeds; the pseudo-Cl # primitives only read the shear-side keys, but the production call path @@ -181,6 +186,11 @@ def cat_and_params(cv): return cat_gal, params +def test_reference_exists(): + """Guard the guard: a missing reference must fail loudly, not skip.""" + assert REFERENCE_TOMO.exists(), f"committed reference missing: {REFERENCE_TOMO}" + + # Tolerances ---------------------------------------------------------------- # Bitwise-stable primitives (binning math, n_gal map, map-based pseudo-Cl). RTOL_DET = 1e-9 @@ -328,18 +338,11 @@ def _build_shear_map(cv, cat_gal, params): """Replicate calculate_pseudo_cl_map's weighted shear-map construction.""" unique_pix, _idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) n_gal = cv.get_n_gal_map( - params, NSIDE, cat_gal, unique_pix=unique_pix, idx=None, idx_rep=idx_rep + params, NSIDE, cat_gal, unique_pix=unique_pix, idx=_idx, idx_rep=idx_rep + ) + m1, m2 = cv.get_shear_map( + params, NSIDE, cat_gal, unique_pix=unique_pix, idx=_idx, idx_rep=idx_rep ) - w = cat_gal[params["w_col"]] - e1 = cat_gal[params["e1_col"]] - e2 = cat_gal[params["e2_col"]] - mask = n_gal != 0 - m1 = np.zeros(n_gal.size) - m2 = np.zeros(n_gal.size) - m1[unique_pix] += np.bincount(idx_rep, weights=e1 * w) - m2[unique_pix] += np.bincount(idx_rep, weights=e2 * w) - m1[mask] /= n_gal[mask] - m2[mask] /= n_gal[mask] return m1 + 1j * m2, n_gal @@ -347,6 +350,9 @@ def test_get_pseudo_cls_map(cv, cat_and_params): cat_gal, params = cat_and_params shear_map, n_gal = _build_shear_map(cv, cat_gal, params) ell_eff, cl_all, wsp = cv.get_pseudo_cls_map(shear_map, n_gal) + ell_eff_2, cl_all_2, wsp_2 = cv.get_pseudo_cls_map( + shear_map, n_gal, shear_map_b=shear_map, mask_b=n_gal + ) assert cl_all.shape == (4, N_ELL_BINS) npt.assert_allclose( @@ -409,6 +415,80 @@ def test_get_pseudo_cls_map(cv, cat_and_params): # BE (index 2) is the transpose-symmetric partner of EB for an auto-spectrum. npt.assert_allclose(cl_all[2], cl_all[1], rtol=RTOL_DET, atol=1e-18) + # Assert that running the same map against itself and against itself as a second map gives the same result. + npt.assert_allclose(cl_all, cl_all_2, rtol=RTOL_DET, atol=ATOL_DET) + npt.assert_allclose(ell_eff, ell_eff_2, rtol=RTOL_DET, atol=ATOL_DET) + + +def test_get_pseudo_cls_map_with_tomo(cv, cat_and_params): + cat_gal, params = cat_and_params + cat_gal_tomo1 = cat_gal[cat_gal[params["tomo_bin_col"]] == 1] + cat_gal_tomo2 = cat_gal[cat_gal[params["tomo_bin_col"]] == 2] + shear_map_1, n_gal_1 = _build_shear_map(cv, cat_gal_tomo1, params) + shear_map_2, n_gal_2 = _build_shear_map(cv, cat_gal_tomo2, params) + ell_eff, cl_all, wsp = cv.get_pseudo_cls_map( + shear_map_1, n_gal_1, shear_map_b=shear_map_2, mask_b=n_gal_2 + ) + + assert cl_all.shape == (4, N_ELL_BINS) + npt.assert_allclose( + ell_eff, + np.array([11.5, 20.0, 30.5, 43.5, 58.5, 75.5, 95.0, 116.5]), + rtol=RTOL_DET, + atol=ATOL_DET, + ) + npt.assert_allclose( + cl_all[0], # EE + np.array( + [ + -8.648947250571888e-06, + 4.386882223005456e-06, + -2.3193526251107696e-06, + 1.0651397314115168e-06, + -2.722591256637468e-07, + -3.862270431501105e-07, + -1.40420405782524e-06, + 2.1531429566476774e-07, + ] + ), + rtol=RTOL_DET, + atol=ATOL_DET, + ) + npt.assert_allclose( + cl_all[1], # EB + np.array( + [ + -2.8032134416262087e-07, + -1.271298363785575e-06, + 1.7112863041724342e-08, + -2.899403854283714e-07, + 1.360217254538336e-06, + 7.682614612656511e-08, + 6.620365648170535e-07, + -3.164479179586416e-07, + ] + ), + rtol=RTOL_DET, + atol=ATOL_DET, + ) + npt.assert_allclose( + cl_all[3], # BB + np.array( + [ + 9.387700364892905e-06, + -2.9045253374872504e-06, + -1.7006849005134948e-06, + -3.6869001010423035e-07, + 4.4986878855942184e-07, + 6.021221048891767e-07, + 8.413198154662088e-08, + -5.782957119801217e-07, + ] + ), + rtol=RTOL_DET, + atol=ATOL_DET, + ) + # =========================================================================== # get_pseudo_cls_catalog -- NmtFieldCatalog path (drifts ~2e-12) @@ -616,3 +696,90 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" ) npt.assert_allclose(ee, cl_prim[0], rtol=RTOL_CAT, atol=ATOL_CAT) + + +def test_calculate_pseudo_cl_catalog_end_to_end_tomo(cv, tmp_path): + """End-to-end catalog path: FITS round-trip of ell + EE/EB/BB. + + The catalog method has no random noise debiasing, so it is reproducible to + the same ~2e-12 catalog-path float noise. save_pseudo_cl stores ELL/EE/EB/BB + (it drops the BE row); we pin the round-tripped table. + """ + ver = cv._test_version + cv._pseudo_cls = { + ver: { + "tomo_bin_1_tomo_bin_1": {}, + "tomo_bin_1_tomo_bin_2": {}, + "tomo_bin_2_tomo_bin_2": {}, + } + } + out_path = cv._output_path(f"pseudo_cl_cat_{ver}.fits") + tomo_bin_ids, tomo_bin_pairs = cv._get_tomo_bins(ver) + + result_to_compare = np.load(REFERENCE_TOMO, allow_pickle=True)["arr_0"].item() + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + out_path = cv._output_path( + f"pseudo_cl_cat_{ver}_{tomo_bin_a}_{tomo_bin_b}.fits" + ) + cv.calculate_pseudo_cl_catalog( + ver, out_path, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b + ) + + assert os.path.exists(out_path) + d = fits.getdata(out_path) + # FITS gives big-endian f8; normalize for value comparison. + ell = np.asarray(d["ELL"], dtype=np.float64) + ee = np.asarray(d["EE"], dtype=np.float64) + eb = np.asarray(d["EB"], dtype=np.float64) + be = np.asarray(d["BE"], dtype=np.float64) + bb = np.asarray(d["BB"], dtype=np.float64) + + npt.assert_allclose( + ell, + result_to_compare[f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ]["ELL"], + rtol=RTOL_DET, + atol=ATOL_DET, + ) + npt.assert_allclose( + ee, + result_to_compare[f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ]["EE"], + rtol=RTOL_CAT, + atol=ATOL_CAT, + ) + npt.assert_allclose( + eb, + result_to_compare[f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ]["EB"], + rtol=RTOL_CAT, + atol=ATOL_CAT, + ) + npt.assert_allclose( + be, + result_to_compare[f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ]["BE"], + rtol=RTOL_CAT, + atol=ATOL_CAT, + ) + npt.assert_allclose( + bb, + result_to_compare[f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ]["BB"], + rtol=RTOL_CAT, + atol=ATOL_CAT, + ) + + # The end-to-end catalog EE matches the primitive get_pseudo_cls_catalog EE + # (same computation, FITS round-trip) -- consistency, not an independent pin. + cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) + params = get_params_rho_tau(cv.cc[ver], survey=ver) + _, cl_prim, _ = cv.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b + ) + npt.assert_allclose(ee, cl_prim[0], rtol=RTOL_CAT, atol=ATOL_CAT) From dee0141259278d70c370811326de8c6f23c60e60 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 08:46:43 +0200 Subject: [PATCH 024/107] Fix non-tomo branch bug in the _merge_iNKA_covariance_function --- src/sp_validation/cosmo_val/pseudo_cl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index d8c1caf8..11c6572b 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1079,7 +1079,7 @@ def _merge_iNKA_covariance(self, ver, tomography): else: block_path = self._output_path_iNKA_block_cov( - ver, tomo_bin_quad=(bin_key_a1, bin_key_a2, bin_key_b1, bin_key_b2) + ver, tomo_bin_quad=("all", "all", "all", "all") ) covar = fits.open(block_path) From 21863bc15c113a8caec2b758384b1dfbc62ea146 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 08:50:36 +0200 Subject: [PATCH 025/107] Fix small bugs identified by Fable --- src/sp_validation/cosmo_val/pseudo_cl.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 11c6572b..13f747ad 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -223,7 +223,7 @@ def calculate_pseudo_cl_inka_cov( ) # Save in the dictionnaries - if not f"W{bin_key1}" not in n_gal_map_dict: + if f"W{bin_key1}" not in n_gal_map_dict: n_gal_map_dict[f"W{bin_key1}"] = n_gal_map_a if f"W{bin_key2}" not in n_gal_map_dict: n_gal_map_dict[f"W{bin_key2}"] = n_gal_map_b @@ -311,7 +311,7 @@ def calculate_pseudo_cl_inka_cov( ) input_cl_a1_b2 = ( fiducial_cl[f"W{bin_key_a1}xW{bin_key_b2}"] - if bin_key_a1 <= bin_key_a2 + if bin_key_a1 <= bin_key_b2 else fiducial_cl[f"W{bin_key_b2}xW{bin_key_a1}"] ) input_cl_a2_b1 = ( @@ -1024,15 +1024,13 @@ def _merge_iNKA_covariance(self, ver, tomography): ver, method="iNKA", tomography=tomography ) - print(self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"]) - if tomography: # Merge the tomographic covariance matrices tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) # Get the number of bins from the pseudo_cls attribute # The non-tomographic pseudo-cl are computed from the call - # to this attribute. + # to this attribute if not already computed. n_ell = self._pseudo_cls[ver]["tomo_bin_all_tomo_bin_all"]["pseudo_cl"][ "ELL" ].shape[0] From 8996879d9d60dcf4548898fa125feba7b2683315 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 09:08:20 +0200 Subject: [PATCH 026/107] Update the cl tests accounting for Fable comments --- .../generate_test_cl_catalog_reference.py | 137 ++++++++++++++++++ src/sp_validation/tests/test_pseudo_cl.py | 45 +++++- 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/sp_validation/tests/data/generate_test_cl_catalog_reference.py diff --git a/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py b/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py new file mode 100644 index 00000000..7c41d564 --- /dev/null +++ b/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py @@ -0,0 +1,137 @@ +# %% +from pathlib import Path + +import IPython +import numpy as np +import yaml +from astropy.io import fits +from astropy.table import Table + +from sp_validation.cosmo_val import CosmologyValidation +from sp_validation.rho_tau import get_params_rho_tau + +ipython = IPython.get_ipython() + +# %% +NSIDE = 64 +SEED = 1234 +N_ELL_BINS = 8 + +rng = np.random.default_rng(SEED) +version = "TestCatalog" + +cat_dir = Path("./catalog") +nz_dir = Path("./nz") +output_dir = Path("./output_test") + +for d in (cat_dir, nz_dir, output_dir): + d.mkdir(exist_ok=True) + +n_gal = 5000 +ra = rng.uniform(10.0, 30.0, n_gal) +dec = rng.uniform(10.0, 30.0, n_gal) +e1 = rng.normal(0, 0.25, n_gal) +e2 = rng.normal(0, 0.25, n_gal) +w = rng.uniform(0.5, 1.0, n_gal) +tomo_bin_id = rng.integers(1, 3, n_gal) # Create a two bin catalogue +Table( + {"RA": ra, "Dec": dec, "e1": e1, "e2": e2, "w": w, "tomo_bin_id": tomo_bin_id} +).write(cat_dir / "shear.fits", overwrite=True) + +shear_cfg = { + "path": "shear.fits", + "w_col": "w", + "e1_col": "e1", + "e2_col": "e2", + "R": 1.0, + "e1_col_corrected": "e1", + "e2_col_corrected": "e2", + "ra_col": "RA", + "dec_col": "Dec", + "tomo_bin_col": "tomo_bin_id", +} + +psf_cfg = { + "path": "shear.fits", + "ra_col": "RA", + "dec_col": "Dec", + "e1_PSF_col": "e1", + "e2_PSF_col": "e2", + "e1_star_col": "e1", + "e2_star_col": "e2", + "PSF_size": "w", + "star_size": "w", + "PSF_flag": "w", + "star_flag": "w", +} +config_data = { + "nz": {"subdir": str(nz_dir), "dndz": {"blind": "A", "path": "dndz"}}, + "paths": {"output": str(output_dir)}, + version: { + "subdir": str(cat_dir), + "pipeline": "SP", + "shear": shear_cfg, + "star": {**psf_cfg}, + "psf": psf_cfg, + }, +} + +config_path = Path("./config.yaml") +config_path.write_text(yaml.dump(config_data, sort_keys=False)) + +# %% +cv = CosmologyValidation( + versions=[version], + catalog_config=config_path, + output_dir=str(output_dir), + nside=NSIDE, + binning="powspace", + power=0.5, + n_ell_bins=N_ELL_BINS, + pol_factor=-1, +) +cv._test_version = version + +ver = cv._test_version +params = get_params_rho_tau(cv.cc[ver], survey=ver) +cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) + +cat_gal_tomo_bin_1 = cat_gal[cat_gal[params["tomo_bin_col"]] == 1] +cat_gal_tomo_bin_2 = cat_gal[cat_gal[params["tomo_bin_col"]] == 2] +n_gal_map_a = cv.get_n_gal_map(params, NSIDE, cat_gal_tomo_bin_1) +shear_map_a_e1, shear_map_a_e2 = cv.get_shear_map(params, NSIDE, cat_gal_tomo_bin_1) +n_gal_map_b = cv.get_n_gal_map(params, NSIDE, cat_gal_tomo_bin_2) +shear_map_b_e1, shear_map_b_e2 = cv.get_shear_map(params, NSIDE, cat_gal_tomo_bin_2) + +shear_map_a = shear_map_a_e1 + 1j * shear_map_a_e2 +shear_map_b = shear_map_b_e1 + 1j * shear_map_b_e2 + + +# %% +ell_eff, cl_all, wsp = cv.get_pseudo_cls_map( + shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, mask_b=n_gal_map_b +) + +# %% +# Get the catalogue output +ver = cv._test_version +cv._pseudo_cls = { + ver: { + "tomo_bin_1_tomo_bin_1": {}, + "tomo_bin_1_tomo_bin_2": {}, + "tomo_bin_2_tomo_bin_2": {}, + } +} +out_path = cv._output_path(f"pseudo_cl_cat_{ver}.fits") +tomo_bin_ids, tomo_bin_pairs = cv._get_tomo_bins(ver) +for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + out_path = cv._output_path(f"pseudo_cl_cat_{ver}_{tomo_bin_a}_{tomo_bin_b}.fits") + cv.calculate_pseudo_cl_catalog( + ver, out_path, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b + ) + + +# %% +np.savez("./test_cl_catalog", cv._pseudo_cls[ver]) +# %% +test_result = np.load("./test_cl_catalog.npz", allow_pickle=True) diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index 61677827..c07afd00 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -340,10 +340,36 @@ def _build_shear_map(cv, cat_gal, params): n_gal = cv.get_n_gal_map( params, NSIDE, cat_gal, unique_pix=unique_pix, idx=_idx, idx_rep=idx_rep ) + w = cat_gal[params["w_col"]] + e1 = cat_gal[params["e1_col"]] + e2 = cat_gal[params["e2_col"]] + mask = n_gal != 0 + m1 = np.zeros(n_gal.size) + m2 = np.zeros(n_gal.size) + m1[unique_pix] += np.bincount(idx_rep, weights=e1 * w) + m2[unique_pix] += np.bincount(idx_rep, weights=e2 * w) + m1[mask] /= n_gal[mask] + m2[mask] /= n_gal[mask] + return m1 + 1j * m2, n_gal + + +def test_build_shear_map(cv, cat_and_params): + """Pin the weighted shear map construction from the synthetic catalog.""" + cat_gal, params = cat_and_params + unique_pix, _idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) + n_gal = cv.get_n_gal_map( + params, NSIDE, cat_gal, unique_pix=unique_pix, idx=_idx, idx_rep=idx_rep + ) m1, m2 = cv.get_shear_map( params, NSIDE, cat_gal, unique_pix=unique_pix, idx=_idx, idx_rep=idx_rep ) - return m1 + 1j * m2, n_gal + + m = m1 + 1j * m2 + + m_replicate, n_gal_replicate = _build_shear_map(cv, cat_gal, params) + + npt.assert_allclose(m, m_replicate, rtol=RTOL_DET, atol=ATOL_DET) + npt.assert_allclose(n_gal, n_gal_replicate, rtol=RTOL_DET, atol=ATOL_DET) def test_get_pseudo_cls_map(cv, cat_and_params): @@ -471,6 +497,23 @@ def test_get_pseudo_cls_map_with_tomo(cv, cat_and_params): rtol=RTOL_DET, atol=ATOL_DET, ) + npt.assert_allclose( + cl_all[2], # BE + np.array( + [ + -3.83934537153214e-06, + 6.218772676611559e-06, + -5.542920986484232e-06, + 8.810792449470091e-07, + -3.2973293732012423e-07, + -1.3155110757359602e-08, + -1.420039280183438e-07, + 3.09464700816748e-07, + ] + ), + rtol=RTOL_DET, + atol=ATOL_DET, + ) npt.assert_allclose( cl_all[3], # BB np.array( From 9c948cd15ce92d5828a18152ebb09558d2e34bb9 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 09:27:46 +0200 Subject: [PATCH 027/107] Reapply "Tomographic pseudo-cl" This reverts commit f499ac8ddba01c8c5694d38805c75c104233fb0e. --- .gitignore | 4 +- cosmo_val/cat_config.yaml | 46 ++ src/sp_validation/cosmo_val/core.py | 51 +- src/sp_validation/cosmo_val/pseudo_cl.py | 4 - src/sp_validation/pseudo_cl.py | 858 ++++++++++++++++++++-- src/sp_validation/rho_tau.py | 4 + src/sp_validation/tests/test_pseudo_cl.py | 55 +- 7 files changed, 953 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index f8eec899..a568c1ca 100644 --- a/.gitignore +++ b/.gitignore @@ -197,4 +197,6 @@ papers/catalog/plots/*.pdf papers/cosmo_val/logs/ # Ignore scratch notebooks -scratch/*/*.ipynb \ No newline at end of file +scratch/*/*.ipynb +scratch/guerrini/work_notebooks +scratch/guerrini/launch_scripts \ No newline at end of file diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index cc1326df..ea36a81d 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1259,3 +1259,49 @@ SP_v1.6.6: e1_col: e1 e2_col: e2 path: unions_shapepipe_star_2024_v1.6.a.fits + +GLASS_mock_validation: + subdir: /n09data/guerrini/glass_mock_test/results/ + pipeline: SP + colour: violet + getdist_colour: 0.0, 0.5, 1.0 + ls: dashed + marker: '*' + cov_th: + A: 2405.3892055695346 + n_e: 6.128201234871523 + n_psf: 0.752316232272063 + sigma_e: 0.379587601488189 + mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits + psf: + PSF_flag: FLAG_PSF_HSM + PSF_size: SIGMA_PSF_HSM + square_size: true + star_flag: FLAG_STAR_HSM + star_size: SIGMA_STAR_HSM + hdu: 1 + path: unions_shapepipe_psf_2024_v1.6.a.fits + ra_col: RA + dec_col: Dec + e1_PSF_col: E1_PSF_HSM + e1_star_col: E1_STAR_HSM + e2_PSF_col: E2_PSF_HSM + e2_star_col: E2_STAR_HSM + shear: + R: 1.0 + path: tomo_test_2_glass_sim_00001_1024.fits + redshift_path: /home/guerrini/sp_validation_cosmostat/config/glass_mock/test_data/redshift_distribution_tomo.txt + w_col: w + e1_col: e1 + e1_PSF_col: e1_PSF + e2_col: e2 + e2_PSF_col: e2_PSF + cols: RA,Dec + tomo_bin_col: tom_bin_id + star: + ra_col: RA + dec_col: Dec + e1_col: e1 + e2_col: e2 + path: unions_shapepipe_star_2024_v1.6.a.fits + diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 48251bbb..066bf4a1 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -1,5 +1,6 @@ # %% import copy +import itertools import os import re from pathlib import Path @@ -7,7 +8,7 @@ import colorama import numpy as np import yaml -from cs_util.cosmo import get_cosmo +from astropy.io import fits from shear_psf_leakage import run_object, run_scale from ..b_modes import ( @@ -88,7 +89,7 @@ class CosmologyValidation( Number of ell bins for pseudo-C_ell analysis (used with binning='powspace'). ell_step : int, default 10 Bin width in ell for linear binning (used with binning='linear'). - pol_factor : bool, default True + pol_factor : int, default -1 Apply polarization correction factor in pseudo-C_ell calculations. nrandom_cell : int, default 10 Number of random realizations for C_ell error estimation. @@ -97,6 +98,10 @@ class CosmologyValidation( noise debiasing, making those realizations reproducible run-to-run. cosmo_params : dict, optional Cosmological parameters to pass to get_cosmo(). If None, uses Planck 2018. + compute_tomography : bool, default False + Whether to compute tomographic correlation functions and pseudo-C_ell. + force_run : bool, default False + If True, forces re-computation of results even if cached outputs exist. Attributes ---------- @@ -214,7 +219,7 @@ def __init__( power=1 / 2, n_ell_bins=32, ell_step=10, - pol_factor=True, + pol_factor=-1, cell_method="map", noise_bias_method="analytic", fiducial_input_inka="coupled", @@ -223,6 +228,8 @@ def __init__( path_onecovariance=None, cosmo_params=None, blind=None, + compute_tomography=False, + force_run=False, ): self.rho_tau_method = rho_tau_method self.cov_estimate_method = cov_estimate_method @@ -243,7 +250,10 @@ def __init__( self.power = power self.n_ell_bins = n_ell_bins self.ell_step = ell_step + + assert pol_factor in (-1, 1), "The polarisatio factor must be -1 or 1." self.pol_factor = pol_factor + self.nrandom_cell = nrandom_cell self.cell_seed = cell_seed self.cell_method = cell_method @@ -252,6 +262,8 @@ def __init__( self.nside_mask = nside_mask self.path_onecovariance = path_onecovariance self.blind = blind + self.compute_tomography = compute_tomography + self.force_run = force_run assert self.cell_method in ["map", "catalog"], ( "cell_method must be 'map' or 'catalog'" @@ -636,3 +648,36 @@ def summarize_bmodes(self, fiducial_scale_cut=(12, 83), versions=None): print() return summary + + def _get_tomo_bins(self, version): + """ + Return the tomo_bin_ids for a given version. If the version does not have tomography, return None. + + Returns + ------- + tomo_bin_ids : list or None + List of unique tomographic bin IDs for the version, or None if no tomography is available + tomo_bin_pairs : list of tuples or None + List of unique pairs of tomographic bin IDs (including self-pairs) for the version, or None if no tomography is available + """ + if "tomo_bin_col" in self.cc[version]["shear"]: + self.print_cyan( + f"Extracting tomography information from version {version}." + ) + cat_gal = fits.getdata(self.cc[version]["shear"]["path"]) + tomo_bin = cat_gal[self.cc[version]["shear"]["tomo_bin_col"]] + tomo_bin_ids = np.unique(tomo_bin) + tomo_bin_ids = tomo_bin_ids[ + tomo_bin_ids > 0 + ] # Exclude zero or negative bins + self.print_cyan( + f"Found {len(tomo_bin_ids)} tomographic bins for version {version}: {tomo_bin_ids}." + ) + + tomo_bin_pairs = list( + itertools.combinations_with_replacement(tomo_bin_ids, 2) + ) + return tomo_bin_ids, tomo_bin_pairs + else: + self.print_cyan(f"Version {version} does not have tomography information.") + return None, None diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 13f747ad..e2537630 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1085,11 +1085,7 @@ def _merge_iNKA_covariance(self, ver, tomography): return covar - # ---------------- Plotting functions for pseudo-Cl's ---------------- # def plot_pseudo_cl( - self, - pol_list, - versions=None, ell_factor="ell", cov_type="iNKA", offset=0.15, diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index c9355ec9..3508e804 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -16,11 +16,13 @@ import healpy as hp import numpy as np import pymaster as nmt +from cs_util.cosmo import get_theo_c_ell # Lowest multipole retained by the pseudo-Cl estimators. LMIN = 8 +# ---------------------- Binning utility functions ---------------------- def pseudo_cl_geometry(nside): """Return ``(lmin, lmax, b_lmax)`` for the pseudo-Cl estimator at ``nside``. @@ -87,7 +89,38 @@ def make_namaster_bin( return b -def get_n_gal_map(nside, ra, dec, weights=None): +# ---------------------- Map computation utility functions ---------------------- +def get_pixels(ra, dec, nside): + """ + Get the HEALPix pixel indices for given RA and Dec. + + Parameters + ---------- + ra : np.ndarray + Right ascension in degrees. + dec : np.ndarray + Declination in degrees. + nside : int + HEALPix nside parameter. + + Returns + ------- + unique_pix : np.ndarray + Sorted unique pixel indices. + idx : np.ndarray + First-occurrence indices into the input from ``np.unique``. + idx_rep : np.ndarray + Inverse map: pixel-group index for each input object. + """ + pixels = hp.ang2pix(nside, theta=np.radians(90 - dec), phi=np.radians(ra)) + + unique_pix, idx, idx_rep = np.unique(pixels, return_index=True, return_inverse=True) + return unique_pix, idx, idx_rep + + +def get_n_gal_map( + nside, ra, dec, weights=None, unique_pix=None, idx=None, idx_rep=None +): """Weighted galaxy number-density HEALPix map plus pixel bookkeeping. Bins ``(ra, dec)`` (degrees) onto an ``nside`` HEALPix grid. With @@ -98,21 +131,106 @@ def get_n_gal_map(nside, ra, dec, weights=None): ------- n_gal : np.ndarray Map of summed weights (or counts) per pixel, shape ``(npix,)``. - unique_pix : np.ndarray - Sorted unique occupied pixel indices. - idx : np.ndarray - First-occurrence indices into the input from ``np.unique``. - idx_rep : np.ndarray - Inverse map: pixel-group index for each input object. """ - theta = (90.0 - dec) * np.pi / 180.0 - phi = ra * np.pi / 180.0 - pix = hp.ang2pix(nside, theta, phi) + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) - unique_pix, idx, idx_rep = np.unique(pix, return_index=True, return_inverse=True) n_gal = np.zeros(hp.nside2npix(nside)) n_gal[unique_pix] = np.bincount(idx_rep, weights=weights) - return n_gal, unique_pix, idx, idx_rep + return n_gal + + +def get_shear_map( + ra, dec, e1, e2, w, nside, unique_pix=None, idx=None, idx_rep=None, n_gal_map=None +): + """Weighted shear HEALPix maps plus pixel bookkeeping. + + Bins ``(ra, dec)`` (degrees) onto an ``nside`` HEALPix grid. The shear + components ``(e1, e2)`` are weighted by ``w`` and summed per pixel. If + ``unique_pix``, ``idx``, and ``idx_rep`` are provided, they are used to + avoid recomputing the pixel indices. + If ``n_gal_map`` is provided, it is used to normalize the shear maps by the galaxy density. + + Returns + ------- + e1_map : np.ndarray + Weighted sum of E1 per pixel, shape ``(npix,)``. + e2_map : np.ndarray + Weighted sum of E2 per pixel, shape ``(npix,)``. + """ + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + if n_gal_map is None: + n_gal_map = get_n_gal_map( + nside, ra, dec, weights=w, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) + + npix = hp.nside2npix(nside) + e1_map = np.zeros(npix) + e2_map = np.zeros(npix) + + e1_map[unique_pix] = np.bincount(idx_rep, weights=e1 * w) + e2_map[unique_pix] = np.bincount(idx_rep, weights=e2 * w) + + non_zero = n_gal_map > 0 + e1_map[non_zero] /= n_gal_map[non_zero] + e2_map[non_zero] /= n_gal_map[non_zero] + + return e1_map, e2_map + + +def get_variance_map( + nside, ra, dec, e1, e2, w, unique_pix=None, idx=None, idx_rep=None +): + """Compute the variance map of the shear components. + + The variance is computed as the weighted variance of the shear components in each pixel. + + Returns + ------- + variance_map : np.ndarray + Variance map of the shear components, shape ``(npix,)``. + """ + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + npix = hp.nside2npix(nside) + variance_map = np.zeros(npix) + + variance_map[unique_pix] = np.bincount( + idx_rep, weights=0.5 * (e1**2 + e2**2) * w**2 + ) + + return variance_map + + +def get_noise_bias_analytical( + ra, dec, e1, e2, w, lmax, nside=1024, unique_pix=None, idx=None, idx_rep=None +): + """ + Compute the analytical noise bias for shear power spectrum. + """ + variance_map = get_variance_map( + nside=nside, + ra=ra, + dec=dec, + e1=e1, + e2=e2, + w=w, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + ) + + noise_bias = hp.nside2pixarea(nside) * np.mean(variance_map) + + noise_bias_cl = np.zeros((4, lmax)) + + noise_bias_cl[0, :] = noise_bias # EE + noise_bias_cl[3, :] = noise_bias # BB + + return noise_bias_cl def apply_random_rotation(e1, e2, rng=None): @@ -140,13 +258,493 @@ def apply_random_rotation(e1, e2, rng=None): return e1_out, e2_out +def get_noise_realisation( + ra, + dec, + e1, + e2, + w, + nside, + unique_pix=None, + idx=None, + idx_rep=None, + n_gal_map=None, + rng=None, +): + """ + Generate a random noise realisation of the shear maps by applying a random rotation to the ellipticity components. + + Parameters + ---------- + ra, dec : np.ndarray + Right ascension and declination of the sources. + e1, e2 : np.ndarray + Ellipticity components. + w : np.ndarray + Weights of the sources. + nside : int + HEALPix resolution. + unique_pix, idx, idx_rep : np.ndarray, optional + Pixel indices and bookkeeping arrays. If not provided, they will be computed. + n_gal_map : np.ndarray, optional + Galaxy number density map. If not provided, it will be computed. + + Returns + ------- + noise_map_e1, noise_map_e2 : np.ndarray + Noise map for ellipticity components. + """ + # Apply random rotation to the ellipticity components + e1_rot, e2_rot = apply_random_rotation(e1, e2, rng=rng) + + # Compute the noise maps using the rotated ellipticity components + noise_map_e1, noise_map_e2 = get_shear_map( + ra=ra, + dec=dec, + e1=e1_rot, + e2=e2_rot, + w=w, + nside=nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + n_gal_map=n_gal_map, + ) + + return noise_map_e1, noise_map_e2 + + +def get_noise_bias_from_gaussian_real( + ra, + dec, + e1, + e2, + w, + nside, + nrandom_cell, + binning, + ell_step=10, + n_ell_bins=32, + power=0.5, + unique_pix=None, + idx=None, + idx_rep=None, + n_gal_map=None, + wsp=None, + seed=42, +): + """ + Compute the power spectrum of the noise bias from random realisations. + + Parameters + ---------- + ra, dec : np.ndarray + Right ascension and declination of the sources. + e1, e2 : np.ndarray + Ellipticity components. + w : np.ndarray + Weights of the sources. + nside : int + HEALPix resolution. + nrandom_cell : int + Number of random cells to use for the noise estimation. + binning, ell_step, n_ell_bins, power : str, int, int, float + Binning scheme and parameters. + unique_pix, idx, idx_rep : np.ndarray, optional + Pixel indices and bookkeeping arrays. If not provided, they will be computed. + n_gal_map : np.ndarray, optional + Galaxy number density map. If not provided, it will be computed. + + Returns + ------- + noise_bias_cl : np.ndarray + Power spectrum of the noise bias. + """ + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) + + b = make_namaster_bin( + lmin, + lmax, + b_lmax, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + ) + + ell_eff = b.get_effective_ells() + noise_bias_cl = np.zeros((4, ell_eff.size)) + + rng = np.random.default_rng(seed) + + if unique_pix is None or idx is None or idx_rep is None: + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + if n_gal_map is None: + n_gal_map = get_n_gal_map( + nside, ra, dec, weights=w, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) + + if wsp is None: + _, _, wsp = get_field_and_workspace_from_map(b, mask_a=n_gal_map) + + for _ in range(nrandom_cell): + noise_map_e1, noise_map_e2 = get_noise_realisation( + ra, + dec, + e1, + e2, + w, + nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + n_gal_map=n_gal_map, + rng=rng, + ) + + noise_map = noise_map_e1 + 1j * noise_map_e2 + del noise_map_e1, noise_map_e2 + + _, cl_noise_, _ = get_pseudo_cls_map( + noise_map, + n_gal_map, + nside, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + wsp=wsp, + ) + + noise_bias_cl += cl_noise_ + + noise_bias_cl /= nrandom_cell + + return noise_bias_cl + + +def get_noise_bias( + ra, + dec, + e1, + e2, + w, + nside, + noise_bias_method, + binning, + *, + ell_step=10, + n_ell_bins=32, + power=0.5, + nrandom_cell=100, + seed=42, +): + """ + Compute the noise bias from object positions and ellipticities. + + Parameters + ---------- + ra, dec : np.ndarray + Right ascension and declination of the sources. + e1, e2 : np.ndarray + Ellipticity components. + w : np.ndarray + Weights of the sources. + nside : int + HEALPix resolution. + noise_bias_method : {'randoms', 'analytic'} + Method used to estimate the noise bias. + binning : {'linear', 'logspace', 'powspace'} + Binning scheme. + ell_step : int, optional + Bin width in ell for ``'linear'`` binning. + n_ell_bins : int, optional + Number of ell bins for ``'logspace'`` / ``'powspace'`` binning. + power : float, optional + Exponent for ``'powspace'`` binning. + nrandom_cell : int, optional + Number of random cells to use for the noise estimation. (only for the `randoms` method) + seed : int, optional + Random seed for reproducibility. (only for the `randoms` method) + + Returns + ------- + noise_bias_cl : np.ndarray + Power spectrum of the noise bias. + """ + if noise_bias_method not in ["randoms", "analytic"]: + raise ValueError("noise_bias_method must be 'randoms' or 'analytic'") + + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) + + b = make_namaster_bin( + lmin, + lmax, + b_lmax, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + ) + + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + + if noise_bias_method == "analytic": + noise_bias_cl = get_noise_bias_analytical( + ra, + dec, + e1, + e2, + w, + lmax, + nside, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + ) + + elif noise_bias_method == "randoms": + noise_bias_cl = get_noise_bias_from_gaussian_real( + ra, + dec, + e1, + e2, + w, + nside, + nrandom_cell, + binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + seed=seed, + ) + + noise_bias_cl = b.unbin_cell(noise_bias_cl) + else: + raise ValueError( + f"Invalid noise bias method `{noise_bias_method}`. Must be 'analytic' or 'randoms'." + ) + + return noise_bias_cl + + +# ---------------------- Cl computation functions ---------------------- +def get_field_and_workspace_from_map( + b, + mask_a, + e1_map_a=None, + e2_map_a=None, + mask_b=None, + e1_map_b=None, + e2_map_b=None, + pol_factor=-1, + return_wsp=True, +): + """Compute a NaMaster field and workspace object from the input maps. + + If the shear maps are None, returns field objects but only the workspace objects is relevant and contains the mixing matrix. + If the second mask and shear maps (indexed b) are provided, the mixing matrix is computed between the two fields. + + Parameters + ---------- + b : nmt.NmtBin + NaMaster binning object. + mask_a : np.ndarray + Field mask for the first map. + e1_map_a : np.ndarray, optional + E1 map for the first field. + e2_map_a : np.ndarray, optional + E2 map for the first field. + mask_b : np.ndarray, optional + Field mask for the second map. + e1_map_b : np.ndarray, optional + E1 map for the second field. + e2_map_b : np.ndarray, optional + E2 map for the second field. + pol_factor : float, optional + Polarization factor to apply to the E2 map. + return_wsp : bool, optional + If True, return the NaMaster workspace object containing the mixing matrix. + + Returns + ------- + field_a : nmt.NmtField + NaMaster field object for the first map. + field_b : nmt.NmtField + NaMaster field object for the second map (if provided, same than the first map otherwise). + wsp : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix. + + """ + nside = hp.npix2nside(len(mask_a)) + lmax = b.lmax + if e1_map_a is None or e2_map_a is None: + e1_map_a = np.zeros(hp.nside2npix(nside)) + e2_map_a = np.zeros(hp.nside2npix(nside)) + + # Create NaMaster field + field_a = nmt.NmtField( + mask=mask_a, maps=[e1_map_a, pol_factor * e2_map_a], lmax=lmax + ) + + if mask_b is not None: + if e1_map_b is None or e2_map_b is None: + e1_map_b = np.zeros(hp.nside2npix(nside)) + e2_map_b = np.zeros(hp.nside2npix(nside)) + + field_b = nmt.NmtField( + mask=mask_b, maps=[e1_map_b, pol_factor * e2_map_b], lmax=lmax + ) + else: + field_b = field_a + + if return_wsp: + # Create NaMaster workspace + wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) + + return field_a, field_b, wsp + else: + return field_a, field_b, None + + +def get_field_and_workspace_from_catalog( + b, + ra_a, + dec_a, + e1_a, + e2_a, + w_a, + ra_b=None, + dec_b=None, + e1_b=None, + e2_b=None, + w_b=None, + pol_factor=-1, + return_wsp=True, + same_bin=False, +): + """Create a NaMaster field and workspace from the input catalog. + + If the second catalog is provided, the mixing matrix is computed between the two fields. + + Parameters + ---------- + b : nmt.NmtBin + NaMaster binning object. + ra_a : np.ndarray + Right ascension of sources in the first catalog. + dec_a : np.ndarray + Declination of sources in the first catalog. + e1_a : np.ndarray + E1 shear component of sources in the first catalog. + e2_a : np.ndarray + E2 shear component of sources in the first catalog. + w_a : np.ndarray + Weights of sources in the first catalog. + ra_b : np.ndarray, optional + Right ascension of sources in the second catalog. + dec_b : np.ndarray, optional + Declination of sources in the second catalog. + e1_b : np.ndarray, optional + E1 shear component of sources in the second catalog. + e2_b : np.ndarray, optional + E2 shear component of sources in the second catalog. + w_b : np.ndarray, optional + Weights of sources in the second catalog. + pol_factor : float, optional + Polarization factor to apply to the E2 component. + return_wsp : bool, optional + If True, return the NaMaster workspace object containing the mixing matrix. + + Returns + ------- + field_a : nmt.NmtFieldCatalog + NaMaster field object for the first catalog. + field_b : nmt.NmtFieldCatalog + NaMaster field object for the second catalog (if provided, same as the first catalog otherwise). + wsp : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix. + + """ + lmax = b.lmax + # Get field for input catalog a + field_a = nmt.NmtFieldCatalog( + positions=[ra_a, dec_a], + weights=w_a, + field=[e1_a, pol_factor * e2_a], + lmax=lmax, + lmax_mask=lmax, + spin=2, + lonlat=True, + ) + + if ( + ra_b is not None + and dec_b is not None + and e1_b is not None + and e2_b is not None + and w_b is not None + and not same_bin + ): + field_b = nmt.NmtFieldCatalog( + positions=[ra_b, dec_b], + weights=w_b, + field=[e1_b, pol_factor * e2_b], + lmax=lmax, + lmax_mask=lmax, + spin=2, + lonlat=True, + ) + else: + field_b = field_a + + if return_wsp: + wsp = nmt.NmtWorkspace.from_fields(field_a, field_b, b) + return field_a, field_b, wsp + else: + return field_a, field_b, None + + +def compute_cl_from_field_and_workspace(field_a, field_b, wsp, b): + """Compute the angular power spectrum from the input NaMaster field and workspace + + Parameters + ---------- + field_a : nmt.NmtField + NaMaster field object for the first catalog. + field_b : nmt.NmtField + NaMaster field object for the second catalog. + wsp : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix. + b : nmt.NmtBin + NaMaster binning object. + + Returns + ------- + cl_coupled : np.ndarray + Coupled angular power spectrum. + cl_decoupled : np.ndarray + Decoupled angular power spectrum. + """ + cl_coupled = nmt.compute_coupled_cell(field_a, field_b) + cl_decoupled = wsp.decouple_cell(cl_coupled) + + return cl_coupled, cl_decoupled + + def get_pseudo_cls_map( - shear_map, - mask, + shear_map_a, + mask_a, nside, binning, *, - pol_factor=True, + shear_map_b=None, + mask_b=None, + pol_factor=-1, wsp=None, ell_step=10, n_ell_bins=32, @@ -156,16 +754,20 @@ def get_pseudo_cls_map( Parameters ---------- - shear_map : np.ndarray + shear_map_a : np.ndarray Complex shear map (``e1 + 1j * e2``). - mask : np.ndarray + mask_a : np.ndarray Field mask (the galaxy number-density map). nside : int HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. - pol_factor : bool, optional - If ``True`` flip the sign of the imaginary (e2) component. + shear_map_b : np.ndarray, optional + Complex shear map for the second field (``e1 + 1j * e2``). + mask_b : np.ndarray, optional + Field mask for the second field (the galaxy number-density map). + pol_factor : float, optional + Polarization factor to apply to the E2 component. wsp : nmt.NmtWorkspace, optional Reuse a coupling workspace; built from the field if ``None``. ell_step, n_ell_bins, power : optional @@ -180,6 +782,27 @@ def get_pseudo_cls_map( wsp : nmt.NmtWorkspace The coupling workspace (newly built or the one passed in). """ + # First do some assertion checks + if shear_map_b is not None: + assert mask_b is not None, "mask_b must be provided if shear_map_b is provided" + assert shear_map_a.shape == shear_map_b.shape, ( + "shear_map_a and shear_map_b must have the same shape" + ) + assert mask_a.shape == mask_b.shape, ( + "mask_a and mask_b must have the same shape" + ) + + if mask_b is not None: + assert shear_map_b is not None, ( + "shear_map_b must be provided if mask_b is provided" + ) + assert shear_map_a.shape == shear_map_b.shape, ( + "shear_map_a and shear_map_b must have the same shape" + ) + assert mask_a.shape == mask_b.shape, ( + "mask_a and mask_b must have the same shape" + ) + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) b = make_namaster_bin( @@ -193,18 +816,36 @@ def get_pseudo_cls_map( ) ell_eff = b.get_effective_ells() - factor = -1 if pol_factor else 1 - - f_all = nmt.NmtField( - mask=mask, maps=[shear_map.real, factor * shear_map.imag], lmax=b_lmax - ) if wsp is None: - wsp = nmt.NmtWorkspace.from_fields(f_all, f_all, b) + field_a, field_b, wsp = get_field_and_workspace_from_map( + b, + mask_a, + e1_map_a=shear_map_a.real, + e2_map_a=shear_map_a.imag, + mask_b=mask_b, + e1_map_b=shear_map_b.real if shear_map_b is not None else None, + e2_map_b=shear_map_b.imag if shear_map_b is not None else None, + pol_factor=pol_factor, + return_wsp=True, + ) + else: + field_a, field_b, _ = get_field_and_workspace_from_map( + b, + mask_a, + e1_map_a=shear_map_a.real, + e2_map_a=shear_map_a.imag, + mask_b=mask_b, + e1_map_b=shear_map_b.real if shear_map_b is not None else None, + e2_map_b=shear_map_b.imag if shear_map_b is not None else None, + pol_factor=pol_factor, + return_wsp=False, + ) - cl_coupled = nmt.compute_coupled_cell(f_all, f_all) - cl_all = wsp.decouple_cell(cl_coupled) + cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( + field_a, field_b, wsp, b + ) - return ell_eff, cl_all, wsp + return ell_eff, cl_decoupled, wsp def get_pseudo_cls_catalog( @@ -213,7 +854,9 @@ def get_pseudo_cls_catalog( nside, binning, *, - pol_factor=True, + tomo_bin_a="all", + tomo_bin_b="all", + pol_factor=-1, wsp=None, ell_step=10, n_ell_bins=32, @@ -232,8 +875,10 @@ def get_pseudo_cls_catalog( HEALPix resolution; fixes the harmonic geometry. binning : str Binning scheme passed to :func:`make_namaster_bin`. - pol_factor : bool, optional - If ``True`` flip the sign of the e2 component. + tomo_bin_a, tomo_bin_b : str or int or None, optional + Tomographic bin IDs for the two fields. + pol_factor : int, optional + Polarization factor to apply to the E2 component. wsp : nmt.NmtWorkspace, optional Reuse a coupling workspace; built from the field if ``None``. ell_step, n_ell_bins, power : optional @@ -248,6 +893,11 @@ def get_pseudo_cls_catalog( wsp : nmt.NmtWorkspace The coupling workspace (newly built or the one passed in). """ + # First make some assertion checks reagarding the run mode + assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( + tomo_bin_a != "all" and tomo_bin_b != "all" + ), "Both tomo_bin_a and tomo_bin_b must be provided or both must be 'all'" + lmin, lmax, b_lmax = pseudo_cl_geometry(nside) b = make_namaster_bin( @@ -261,22 +911,140 @@ def get_pseudo_cls_catalog( ) ell_eff = b.get_effective_ells() - factor = -1 if pol_factor else 1 + is_tomography = tomo_bin_a != "all" and tomo_bin_b != "all" + if is_tomography: + assert params["tomo_bin_col"] is not None, ( + "The column of tomographic bin ids is not specified." + ) + mask_tomo_a = catalog[params["tomo_bin_col"]] == tomo_bin_a + mask_tomo_b = catalog[params["tomo_bin_col"]] == tomo_bin_b + catalog_a = catalog[mask_tomo_a] + catalog_b = catalog[mask_tomo_b] + same_bin = tomo_bin_a == tomo_bin_b + else: + catalog_a = catalog + catalog_b = catalog + same_bin = True - f_all = nmt.NmtFieldCatalog( - positions=[catalog[params["ra_col"]], catalog[params["dec_col"]]], - weights=catalog[params["w_col"]], - field=[catalog[params["e1_col"]], factor * catalog[params["e2_col"]]], - lmax=b_lmax, - lmax_mask=b_lmax, - spin=2, - lonlat=True, + if wsp is None: + field_a, field_b, wsp = get_field_and_workspace_from_catalog( + b, + ra_a=catalog_a[params["ra_col"]], + dec_a=catalog_a[params["dec_col"]], + e1_a=catalog_a[params["e1_col"]], + e2_a=catalog_a[params["e2_col"]], + w_a=catalog_a[params["w_col"]], + ra_b=catalog_b[params["ra_col"]], + dec_b=catalog_b[params["dec_col"]], + e1_b=catalog_b[params["e1_col"]], + e2_b=catalog_b[params["e2_col"]], + w_b=catalog_b[params["w_col"]], + pol_factor=pol_factor, + return_wsp=True, + same_bin=same_bin, + ) + else: + field_a, field_b, _ = get_field_and_workspace_from_catalog( + b, + ra_a=catalog_a[params["ra_col"]], + dec_a=catalog_a[params["dec_col"]], + e1_a=catalog_a[params["e1_col"]], + e2_a=catalog_a[params["e2_col"]], + w_a=catalog_a[params["w_col"]], + ra_b=catalog_b[params["ra_col"]], + dec_b=catalog_b[params["dec_col"]], + e1_b=catalog_b[params["e1_col"]], + e2_b=catalog_b[params["e2_col"]], + w_b=catalog_b[params["w_col"]], + pol_factor=pol_factor, + return_wsp=False, + same_bin=same_bin, + ) + + cl_coupled, cl_decoupled = compute_cl_from_field_and_workspace( + field_a, field_b, wsp, b ) - if wsp is None: - wsp = nmt.NmtWorkspace.from_fields(f_all, f_all, b) + return ell_eff, cl_decoupled, wsp + + +# ---------------------- Covariance computation functions ---------------------- +def get_fiducial_cl(z, dndz, lmax, cosmo, backend="camb"): + """ + Get the fiducial Cl's using the redshift distribution. + Cosmology is determined by the input cosmo object. + """ + ell = np.arange(1, lmax + 1) + + fiducial_cl = get_theo_c_ell(ell=ell, z=z, nz=dndz, backend=backend, cosmo=cosmo) + + return fiducial_cl + + +def get_pseudo_cl_iNKA_covariance( + input_cl_a1_b1, + input_cl_a1_b2, + input_cl_a2_b1, + input_cl_a2_b2, + field_a1, + field_a2, + field_b1, + field_b2, + wsp_a, + wsp_b, + b, +): + """Compute the iNKA covariance for pseudo-Cl. + + Parameters + ---------- + input_cl_a1_b1 : np.ndarray + Input Cl for field a1 and b1. + input_cl_a1_b2 : np.ndarray + Input Cl for field a1 and b2. + input_cl_a2_b1 : np.ndarray + Input Cl for field a2 and b1. + input_cl_a2_b2 : np.ndarray + Input Cl for field a2 and b2. + field_a1 : nmt.NmtField + NaMaster field object for the first catalog (a1). + field_a2 : nmt.NmtField + NaMaster field object for the second catalog (a2). + field_b1 : nmt.NmtField + NaMaster field object for the first catalog (b1). + field_b2 : nmt.NmtField + NaMaster field object for the second catalog (b2). + wsp_a : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix for fields a. + wsp_b : nmt.NmtWorkspace + NaMaster workspace object containing the mixing matrix for fields b. + b : nmt.NmtBin + NaMaster binning object. + + Returns + ------- + cov_matrix : np.ndarray + Covariance matrix of the pseudo-Cl, shape ``(n_bins, n_bins)``. + """ + # Compute the coupling coefficients for the covariance + cw = nmt.NmtCovarianceWorkspace.from_fields(field_a1, field_a2, field_b1, field_b2) + + # Get actual number of ell bins from binning scheme + n_ell_actual = b.get_n_bands() - cl_coupled = nmt.compute_coupled_cell(f_all, f_all) - cl_all = wsp.decouple_cell(cl_coupled) + # Compute the covariance using NaMaster's built-in function + cov_matrix = nmt.gaussian_covariance( + cw, + 2, + 2, + 2, + 2, + input_cl_a1_b1, + input_cl_a1_b2, + input_cl_a2_b1, + input_cl_a2_b2, + wsp_a, + wb=wsp_b, + ).reshape([n_ell_actual, 4, n_ell_actual, 4]) - return ell_eff, cl_all, wsp + return cov_matrix diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 6874a08c..95a720f9 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -51,6 +51,10 @@ def get_params_rho_tau(cat, survey="other"): params["w_col"] = cat["shear"]["w_col"] params["e1_col"] = cat["shear"]["e1_col"] params["e2_col"] = cat["shear"]["e2_col"] + try: + params["tomo_bin_col"] = cat["shear"]["tomo_bin_col"] + except KeyError: + params["tomo_bin_col"] = None params["R11"] = cat["shear"].get("R11") params["R22"] = cat["shear"].get("R22") diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index 0fed63d1..c07afd00 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -59,6 +59,7 @@ import yaml from sp_validation.cosmo_val import CosmologyValidation +from sp_validation.pseudo_cl import apply_random_rotation from sp_validation.rho_tau import get_params_rho_tau # These tests need the full harmonic-space stack (pymaster/NaMaster + healpy), @@ -170,7 +171,7 @@ def cv(tmp_path): binning="powspace", power=0.5, n_ell_bins=N_ELL_BINS, - pol_factor=True, + pol_factor=-1, ) cv._test_version = version return cv @@ -275,18 +276,38 @@ def test_unknown_binning_raises(self, cv): cv.get_namaster_bin(LMIN, LMAX, B_LMAX) +# ========================================================================== +# get_pixels -- fetch the pixels from ra, dec, and nside (HEALPix) +# ========================================================================== +def test_get_pixels(cv, cat_and_params): + """Pin the HEALPix pixelization of the synthetic catalog.""" + cat_gal, params = cat_and_params + unique_pix, idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) + + # Structural invariants of the HEALPix occupancy map. + assert unique_pix.size == 485 + assert idx_rep.size == 5000 # one entry per galaxy + + # Pinned scalar summaries: total weight is conserved (sum of weights), + # peak occupancy, and the pixel index bookkeeping. + npt.assert_array_equal(unique_pix[:5], [12167, 12168, 12169, 12170, 12171]) + assert int(unique_pix.sum()) == 7849989 + + # =========================================================================== # get_n_gal_map -- weighted galaxy number-density map # =========================================================================== def test_get_n_gal_map(cv, cat_and_params): cat_gal, params = cat_and_params - n_gal, unique_pix, idx, idx_rep = cv.get_n_gal_map(params, NSIDE, cat_gal) + + unique_pix, idx, idx_rep = cv.get_pixels(params, NSIDE, cat_gal) + n_gal = cv.get_n_gal_map( + params, NSIDE, cat_gal, unique_pix=unique_pix, idx=None, idx_rep=idx_rep + ) # Structural invariants of the HEALPix occupancy map. assert n_gal.shape == (healpy.nside2npix(NSIDE),) assert int(np.count_nonzero(n_gal)) == 485 - assert unique_pix.size == 485 - assert idx_rep.size == 5000 # one entry per galaxy # The map is supported exactly on the occupied pixels. npt.assert_array_equal(np.nonzero(n_gal)[0], np.sort(unique_pix)) @@ -294,8 +315,6 @@ def test_get_n_gal_map(cv, cat_and_params): # peak occupancy, and the pixel index bookkeeping. npt.assert_allclose(n_gal.sum(), 3760.657282591494, rtol=RTOL_DET) npt.assert_allclose(n_gal.max(), 16.063410433711447, rtol=RTOL_DET) - npt.assert_array_equal(unique_pix[:5], [12167, 12168, 12169, 12170, 12171]) - assert int(unique_pix.sum()) == 7849989 npt.assert_allclose( n_gal[unique_pix][:5], np.array( @@ -519,7 +538,9 @@ def test_get_pseudo_cls_map_with_tomo(cv, cat_and_params): # =========================================================================== def test_get_pseudo_cls_catalog(cv, cat_and_params): cat_gal, params = cat_and_params - ell_eff, cl_all, wsp = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) + ell_eff, cl_all, wsp = cv.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" + ) assert cl_all.shape == (4, N_ELL_BINS) # Effective ells share the binning math with the map path: bitwise-stable. @@ -597,7 +618,7 @@ def test_apply_random_rotation_preserves_magnitude(cv, cat_and_params): e1 = np.asarray(cat_gal[params["e1_col"]], dtype=np.float64) e2 = np.asarray(cat_gal[params["e2_col"]], dtype=np.float64) - e1_rot, e2_rot = cv.apply_random_rotation(e1, e2) + e1_rot, e2_rot = apply_random_rotation(e1, e2) assert e1_rot.shape == e1.shape assert e2_rot.shape == e2.shape @@ -617,16 +638,16 @@ def test_apply_random_rotation_reproducible_with_seed(cv, cat_and_params): e1 = np.asarray(cat_gal[params["e1_col"]], dtype=np.float64) e2 = np.asarray(cat_gal[params["e2_col"]], dtype=np.float64) - a1, a2 = cv.apply_random_rotation(e1, e2, np.random.default_rng(42)) - b1, b2 = cv.apply_random_rotation(e1, e2, np.random.default_rng(42)) + a1, a2 = apply_random_rotation(e1, e2, np.random.default_rng(42)) + b1, b2 = apply_random_rotation(e1, e2, np.random.default_rng(42)) npt.assert_array_equal(a1, b1) npt.assert_array_equal(a2, b2) - c1, _ = cv.apply_random_rotation(e1, e2, np.random.default_rng(7)) + c1, _ = apply_random_rotation(e1, e2, np.random.default_rng(7)) assert not np.allclose(a1, c1) - d1, _ = cv.apply_random_rotation(e1, e2) - f1, _ = cv.apply_random_rotation(e1, e2) + d1, _ = apply_random_rotation(e1, e2) + f1, _ = apply_random_rotation(e1, e2) assert not np.allclose(d1, f1) @@ -641,9 +662,9 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): (it drops the BE row); we pin the round-tripped table. """ ver = cv._test_version - cv._pseudo_cls = {ver: {}} + cv._pseudo_cls = {ver: {"tomo_bin_all_tomo_bin_all": {}}} out_path = cv._output_path(f"pseudo_cl_cat_{ver}.fits") - cv.calculate_pseudo_cl_catalog(ver, out_path) + cv.calculate_pseudo_cl_catalog(ver, out_path, tomo_bin_a="all", tomo_bin_b="all") assert os.path.exists(out_path) d = fits.getdata(out_path) @@ -714,7 +735,9 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): # (same computation, FITS round-trip) -- consistency, not an independent pin. cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) params = get_params_rho_tau(cv.cc[ver], survey=ver) - _, cl_prim, _ = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) + _, cl_prim, _ = cv.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" + ) npt.assert_allclose(ee, cl_prim[0], rtol=RTOL_CAT, atol=ATOL_CAT) From 3146790653076f9da1cb5fb74b74a069c4b1e8e5 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 09:30:28 +0200 Subject: [PATCH 028/107] Fix Ruff errors --- src/sp_validation/cosmo_val/pseudo_cl.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index e2537630..13f747ad 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1085,7 +1085,11 @@ def _merge_iNKA_covariance(self, ver, tomography): return covar + # ---------------- Plotting functions for pseudo-Cl's ---------------- # def plot_pseudo_cl( + self, + pol_list, + versions=None, ell_factor="ell", cov_type="iNKA", offset=0.15, From 7eca12e874b06b0f6045036cb285c0fdcb1d1e6a Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 09:35:19 +0200 Subject: [PATCH 029/107] Add get_cosmo import in core CosmoValidation that disappeared in the merging process. --- src/sp_validation/cosmo_val/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 066bf4a1..c6204c65 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -9,6 +9,7 @@ import numpy as np import yaml from astropy.io import fits +from cs_util.cosmo import get_cosmo from shear_psf_leakage import run_object, run_scale from ..b_modes import ( From 335961ea1a6255cb77540b7e57bdb43f2a8914a7 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 16:51:54 +0200 Subject: [PATCH 030/107] add a script to run gaussian simulations for covariance estimation --- config/cosmo_val/cosmo_gaussian_sims.yaml | 14 + scripts/cosmo_val/run_cl_gaussian_sims.py | 622 ++++++++++++++++++++++ 2 files changed, 636 insertions(+) create mode 100644 config/cosmo_val/cosmo_gaussian_sims.yaml create mode 100644 scripts/cosmo_val/run_cl_gaussian_sims.py diff --git a/config/cosmo_val/cosmo_gaussian_sims.yaml b/config/cosmo_val/cosmo_gaussian_sims.yaml new file mode 100644 index 00000000..99e333c8 --- /dev/null +++ b/config/cosmo_val/cosmo_gaussian_sims.yaml @@ -0,0 +1,14 @@ +# --- Cosmological parameters (CAMB) --- +h: 0.6766 +Omega_m: 0.30966 +Omega_b: 0.04897 +ns: 0.9665 +sig8: 0.8102 +mnu: 0.06 +extra_params: + camb: + nonlinear: True + halofit_version: "mead2020_feedback" + HMCode_log_T_AGN: 7.8 + kmax: 20.0 + kmax_extrapolate: 500 \ No newline at end of file diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py new file mode 100644 index 00000000..97546e65 --- /dev/null +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -0,0 +1,622 @@ +import argparse +import itertools +import os +import warnings +from pathlib import Path + +import healpy as hp +import numpy as np +import pymaster as nmt +import yaml +from astropy.io import fits +from cs_util.cosmo import get_cosmo +from mpi4py import MPI +from tqdm import tqdm + +import sp_validation.pseudo_cl as spv_pseudo_cl + + +def get_parser(): + """Create the argument parser.""" + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description="Generate Gaussian simulations of shear maps and compute their power spectra.", + fromfile_prefix_chars="@", + ) + parser.add_argument( + "-c", + "--config", + help="Path to the configuration file to generate the simulation", + type=str, + required=True, + ) + parser.add_argument( + "-o", + "--output", + help="Output directory for the simulation results (Ideally the same than cosmo_val)", + type=str, + required=True, + ) + parser.add_argument( + "-v", "--version", help="Run some validation checks", type=str, required=True + ) + parser.add_argument( + "-t", + "--tomo", + help="Whether to run the simulation in tomographic mode (default: False)", + action="store_true", + ) + parser.add_argument( + "-iw", + "--ignore-warnings", + help="Ignore warnings during the simulation", + action="store_true", + ) + parser.add_argument( + "-n", + "--n_sims", + help="Number of simulations to run (default: 100)", + type=int, + default=100, + ) + parser.add_argument( + "-ns", + "--nside", + help="Nside for the HEALPix maps (default: 1024)", + type=int, + default=1024, + ) + parser.add_argument( + "-b", + "--binning", + help="Binning scheme for the power spectra (default: powspace)", + type=str, + default="powspace", + ) + parser.add_argument( + "-es", + "--ell_step", + help="Step size for the linear binning (default: 10)", + type=int, + default=10, + ) + parser.add_argument( + "-eb", + "--ell_bins", + help="Number of bins for the power spectra (default: 32)", + type=int, + default=32, + ) + parser.add_argument( + "-p", + "--power", + help="Power for the powspace binning (default: 0.5)", + type=float, + default=0.5, + ) + parser.add_argument( + "-cc", + "--cosmo-config", + help="Path to the cosmology configuration file (default: None)", + type=str, + default=None, + ) + parser.add_argument( + "-f", + "--force", + help="Force overwrite of existing output files (default: False)", + action="store_true", + ) + return parser + + +# --- Helper function for reading configuration and extracting useful data --- +def load_version_config(config, version, tomography): + if not os.path.exists(config): + raise FileNotFoundError(f"Config file {config} does not exist.") + else: + # Load useful information from the config file + with open(config, "r") as file: + cc = yaml.load(file, Loader=yaml.FullLoader) + + # Check that the version exists in the config file + if version not in cc: + raise ValueError(f"Version {version} not found in config file.") + else: + subdir = Path(cc[version]["subdir"]) + cat_path = str(subdir / Path(cc[version]["shear"]["path"])) + + ra_col = cc[version]["shear"]["cols"].split(",")[0] + dec_col = cc[version]["shear"]["cols"].split(",")[1] + e1_col = cc[version]["shear"]["e1_col"] + e2_col = cc[version]["shear"]["e2_col"] + w_col = cc[version]["shear"]["w_col"] + tomo_bin_col = None + if tomography: + tomo_bin_col = cc[version]["shear"]["tomo_bin_col"] + + redshift_path = Path(cc[version]["shear"]["redshift_path"]) + return ( + cat_path, + ra_col, + dec_col, + e1_col, + e2_col, + w_col, + tomo_bin_col, + redshift_path, + ) + + +def load_catalog_slices(cat_path, columns, tomography, tomo_bin_col): + cat_gal = fits.getdata(cat_path) + + ra, dec, e1, e2, w = {}, {}, {}, {}, {} + + ra_col, dec_col, e1_col, e2_col, w_col = columns + + if tomography: + tomo_bin_ids = np.unique(cat_gal[tomo_bin_col]) + else: + tomo_bin_ids = ["all"] + + for bin_id in tomo_bin_ids: + if bin_id == "all": + mask_bin = np.ones(len(cat_gal), dtype=bool) + else: + mask_bin = cat_gal[tomo_bin_col] == bin_id + ra[bin_id] = cat_gal[ra_col][mask_bin] + dec[bin_id] = cat_gal[dec_col][mask_bin] + e1[bin_id] = cat_gal[e1_col][mask_bin] + e2[bin_id] = cat_gal[e2_col][mask_bin] + w[bin_id] = cat_gal[w_col][mask_bin] + + del cat_gal # Free memory + + return ra, dec, e1, e2, w, tomo_bin_ids + + +def build_pre_computed_maps(ra, dec, w, tomo_bin_ids, nside): + n_gal, unique_pix, idx, idx_rep = {}, {}, {}, {} + + for bin_id in tomo_bin_ids: + unique_pix[bin_id], idx[bin_id], idx_rep[bin_id] = spv_pseudo_cl.get_pixels( + ra[bin_id], dec[bin_id], nside + ) + n_gal[bin_id] = spv_pseudo_cl.get_n_gal_map( + nside, + ra[bin_id], + dec[bin_id], + weights=w[bin_id], + unique_pix=unique_pix[bin_id], + idx=idx[bin_id], + idx_rep=idx_rep[bin_id], + ) + + return n_gal, unique_pix, idx, idx_rep + + +def build_fiducial_cl(cosmo_params, redshift_path, lmax, tomography, tomo_bin_ids): + cosmo = get_cosmo(**cosmo_params) + + # --- Load the redshift distribution --- + redshift_distr = np.loadtxt(redshift_path) + z = redshift_distr[:, 0] + dndz = redshift_distr[:, 1:] + + if not tomography and dndz.shape[1] > 1: + # Sum the redshift distributions across all bins to get the total distribution + dndz = np.sum(dndz, axis=1, keepdims=True) + + # Normalize the summed distribution + dndz /= np.trapezoid(dndz[:, 0], z) + + if tomography and dndz.shape[1] != len(tomo_bin_ids): + raise ValueError( + "Mismatch between the number of tomographic bins and the redshift distributions provided." + ) + + fiducial_cl = spv_pseudo_cl.get_fiducial_cl(z, dndz, lmax, cosmo) + + return cosmo, fiducial_cl + + +def prepare_workspace(n_gal, tomo_bin_ids, nside, b, b_lmax, out_dir, force_run): + """Precomputes the workspace objects for each pair of tomographic bins and saves them to disk.""" + f = {} + for bin_id in tqdm(tomo_bin_ids, desc="Computing fields"): + f[bin_id] = nmt.NmtField( + mask=n_gal[bin_id], + maps=[np.zeros(hp.nside2npix(nside)), np.zeros(hp.nside2npix(nside))], + lmax=b_lmax, + ) + + # Compute wsp object for each pair of tomographic bins + tomo_bin_pairs = list(itertools.combinations_with_replacement(tomo_bin_ids, 2)) + for tomo_bin_a, tomo_bin_b in tqdm(tomo_bin_pairs, desc="Computing workspaces"): + wsp_file = os.path.join(out_dir, f"wsp_{tomo_bin_a}_{tomo_bin_b}.npz") + if os.path.exists(wsp_file) and not force_run: + print( + f"Workspace for bins {tomo_bin_a} and {tomo_bin_b} already exists. Skipping." + ) + continue + wsp = nmt.NmtWorkspace(f[tomo_bin_a], f[tomo_bin_b], b) + wsp.write_to(wsp_file) + + +# --- Function to generate Gaussian simulation, add noise and extract the spectra --- +def get_gaussian_simulation( + nside, + lmax, + fiducial_cl, + n_gal, + unique_pix, + idx, + idx_rep, + ra, + dec, + e1, + e2, + w, + tomo_bin_ids, +): + final_maps = {} + for bin_id in tomo_bin_ids: + noise_map_e1, noise_map_e2 = spv_pseudo_cl.get_noise_realisation( + ra[bin_id], + dec[bin_id], + e1[bin_id], + e2[bin_id], + w[bin_id], + nside, + unique_pix[bin_id], + idx[bin_id], + idx_rep[bin_id], + n_gal_map=n_gal[bin_id], + ) + final_maps[bin_id] = noise_map_e1 + 1j * noise_map_e2 + + # Generate a Gaussian field with the fiducial power spectrum + tomo_bin_pairs = list(itertools.combinations_with_replacement(tomo_bin_ids, 2)) + # Organise the cls in a list to feed the helpy function + cl_list = [ + fiducial_cl[f"W{tomo_bin_a}xW{tomo_bin_b}"] + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs + ] + # Generate alms with the correct cross-bin correlation + alm_shear = hp.synalm(cl_list, lmax=lmax) + + for bin_id in tomo_bin_ids: + if bin_id != "all": + gauss_map = hp.alm2map( + [ + np.zeros_like(alm_shear[bin_id - 1]), + alm_shear[bin_id - 1], + np.zeros_like(alm_shear[bin_id - 1]), + ], + nside=nside, + verbose=False, + ) + else: + gauss_map = hp.alm2map( + [np.zeros_like(alm_shear), alm_shear, np.zeros_like(alm_shear)], + nside=nside, + verbose=False, + ) + + mask = n_gal[bin_id] > 0 + final_maps[bin_id][mask] += gauss_map[1][mask] + 1j * gauss_map[2][mask] + + return final_maps + + +def extract_spectra(noisy_gaussian_maps, n_gal, tomo_bin_ids, lmax, out_dir): + # Create the NmtField for each tomo bin + f = {} + for bin_id in tomo_bin_ids: + # Compute the power spectrum + f[bin_id] = nmt.NmtField( + mask=n_gal[bin_id], + maps=[noisy_gaussian_maps[bin_id].real, noisy_gaussian_maps[bin_id].imag], + lmax=lmax, + ) + + # Compute the power spectrum for each bin pair + cl_final = {} + tomo_bin_pairs = list(itertools.combinations_with_replacement(tomo_bin_ids, 2)) + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + cl_coupled = nmt.compute_coupled_cell(f[tomo_bin_a], f[tomo_bin_b]) + + wsp = nmt.NmtWorkspace() + wsp_file = os.path.join(out_dir, f"wsp_{tomo_bin_a}_{tomo_bin_b}.npz") + wsp.read_from(wsp_file) + cl_decoupled = wsp.decouple_cell(cl_coupled) + cl_final[f"W{tomo_bin_a}xW{tomo_bin_b}"] = cl_decoupled + + +# --- single unit of work distributed to the MPI processes --- +def run_one_simulation(sim_id, nside, version, tomography, out_dir, force_run): + """Worker executes this simulation and saves results""" + try: + out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}.npz" + if os.path.exists(out_file) and not force_run: + print(f"Rank {rank} skipping {sim_id} (already exists)") + return + + print(f"Rank {rank} starting {sim_id}") + setup_file = os.path.join( + out_dir, f"precomputed_setup_{version}_tomography_{tomography}.npz" + ) + data = np.load(setup_file, allow_pickle=True) + n_gal = data["n_gal"].item() + unique_pix = data["unique_pix"].item() + idx = data["idx"].item() + idx_rep = data["idx_rep"].item() + ra_col = data["ra_col"].item() + dec_col = data["dec_col"].item() + e1_col = data["e1_col"].item() + e2_col = data["e2_col"].item() + w = data["w"].item() + fiducial_cl = data["fiducial_cl"].item() + tomo_bin_ids = data["tomo_bin_ids"] + + lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(nside) + + # --- call the simulation --- + noisy_gaussian_maps = get_gaussian_simulation( + nside, + lmax, + fiducial_cl, + n_gal, + unique_pix, + idx, + idx_rep, + ra_col, + dec_col, + e1_col, + e2_col, + w, + tomo_bin_ids, + ) + + # --- Compute the power spectra --- + cl_final = extract_spectra( + noisy_gaussian_maps, n_gal, tomo_bin_ids, lmax, out_dir + ) + + # Save the results + np.savez(out_file, cl_decoupled=cl_final) + + print(f"Rank {rank} finished {sim_id} for version {version}") + + except Exception as e: + print(f"Rank {rank} failed {sim_id} with error {e}") + + +# --- Distribution of the MPI processes in a master-worker scheme --- +def distribute_work(comm, n_sims, worker_fn, worker_args=()): + rank = comm.Get_rank() + size = comm.Get_size() + + work_tag = 1 + done_tag = 2 + stop_tag = 0 + + if rank == 0: + next_sim = 0 + closed_workers = 0 + + for worker in range(1, size): + if next_sim < n_sims: + comm.send(next_sim, dest=worker, tag=work_tag) + next_sim += 1 + else: + comm.send("STOP", dest=worker, tag=stop_tag) + closed_workers += 1 + + while closed_workers < size - 1: + status = MPI.Status() + _ = comm.recv(source=MPI.ANY_SOURCE, tag=done_tag, status=status) + worker = status.Get_source() + + if next_sim < n_sims: + comm.send(next_sim, dest=worker, tag=work_tag) + next_sim += 1 + else: + comm.send("STOP", dest=worker, tag=stop_tag) + closed_workers += 1 + + else: + while True: + sim_id = comm.recv(source=0, tag=MPI.ANY_TAG, status=MPI.Status()) + if sim_id == "STOP": + break + worker_fn(sim_id, *worker_args) + comm.send(sim_id, dest=0, tag=done_tag) + + +# --- Final function to read the computed spectra and extract a covariance matrix --- +def concatenate_spectra(cl_sample, tomo_bin_ids): + """Concatenate the spectra from different tomographic bins into a single array.""" + concatenated_cl = [] + tomo_bin_pairs = itertools.combinations_with_replacement(tomo_bin_ids, 2) + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + concatenated_cl.append(cl_sample[f"W{tomo_bin_a}xW{tomo_bin_b}"][0]) + return np.concatenate(concatenated_cl) + + +def get_covariance_from_simulated_spectra( + n_sims, version, tomography, tomo_bin_ids, out_dir +): + """Compute the covariance of the EE signal from the simulated spectra.""" + cl_samples = [] + for sim_id in range(n_sims): + out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}.npz" + if not os.path.exists(out_file): + raise FileNotFoundError(f"Simulation output file {out_file} not found.") + data = np.load(out_file, allow_pickle=True) + cl_samples.append( + concatenate_spectra(data["cl_decoupled"].item(), tomo_bin_ids) + ) + + cl_samples = np.array(cl_samples) + + covariance_matrix = np.cov(cl_samples, rowvar=False) + + return covariance_matrix + + +if __name__ == "__main__": + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + size = comm.Get_size() + + # --- Get the parser and extract arguments --- + parser = get_parser() + args = parser.parse_args() + + out_dir = args.output + version = args.version + out_dir = str(Path(out_dir) / Path(f"gaussian_simulations_{version}")) + + config = args.config + tomography = args.tomo + ignore_warnings = args.ignore_warnings + + if ignore_warnings: + warnings.filterwarnings("ignore") + print("Ignoring warnings during the simulation.") + + nside = args.nside + binning = args.binning + ell_step = args.ell_step + n_ell_bins = args.ell_bins + power = args.power + + force_run = args.force + + if rank == 0: + # Check that the config file exists (same than cosmo_val) + ( + cat_path, + ra_col, + dec_col, + e1_col, + e2_col, + w_col, + tomo_bin_col, + redshift_path, + ) = load_version_config(config, version, tomography) + columns = (ra_col, dec_col, e1_col, e2_col, w_col) + + os.makedirs(out_dir, exist_ok=True) + print(f"Output directory: {out_dir}") + + print("Pre-compute setup for the simulations and workspace...") + + print("Reading catalog...") + ra, dec, e1, e2, w, tomo_bin_ids = load_catalog_slices( + cat_path, columns, tomography, tomo_bin_col + ) + + # Setup the binning + nside = args.nside + lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(nside) + ells = np.arange(lmin, lmax + 1) + b = spv_pseudo_cl.make_namaster_bin( + lmin, + lmax, + b_lmax, + binning=binning, + ell_step=ell_step, + n_ell_bins=n_ell_bins, + power=power, + ) + ell_eff = b.get_effective_ells() + + print("Getting n_gal map...") + n_gal, unique_pix, idx, idx_rep = build_pre_computed_maps( + ra, dec, w, tomo_bin_ids, nside + ) + + print("Getting fiducial Cl...") + # --- Get the cosmology object to compute the fiducial --- + if args.cosmo_config is None: + cosmo_params = {} + else: + with open(args.cosmo_config, "r") as f: + cosmo_params = yaml.safe_load(f) + + cosmo, fiducial_cl = build_fiducial_cl( + cosmo_params, redshift_path, lmax, tomography, tomo_bin_ids + ) + + print("Saving precomputed setup...") + setup_file = os.path.join( + out_dir, f"precomputed_setup_{version}_tomography_{tomography}.npz" + ) + + np.savez( + setup_file, + n_gal=n_gal, + unique_pix=unique_pix, + idx=idx, + idx_rep=idx_rep, + ra_col=ra, + dec_col=dec, + e1_col=e1, + e2_col=e2, + w=w, + fiducial_cl=fiducial_cl, + tomo_bin_ids=tomo_bin_ids, + ) + + print("Setting up workspace...") + prepare_workspace(n_gal, tomo_bin_ids, nside, b, b_lmax, out_dir, force_run) + + print( + f"Running Gaussian simulations for version {version} with the following parameters:\n" + f" - Number of simulations: {args.n_sims}\n" + f" - Nside: {nside}\n" + f" - Binning: {binning}\n" + f" - Ell step: {ell_step}\n" + f" - Number of ell bins: {n_ell_bins}\n" + f" - Power for powspace: {power}\n" + f" \n" + f" --- Cosmological parameters ---\n" + f" - H0: {cosmo['H0']}\n" + f" - Omega_m: {cosmo['Omega_m']}\n" + f" - Omega_b: {cosmo['Omega_b']}\n" + f" - Omega_c: {cosmo['Omega_c']}\n" + f" - Omega_nu: {cosmo['Omega_nu_mass'] + cosmo['Omega_nu_rel']}\n" + f" - n_s: {cosmo['n_s']}\n" + f" - sigma_8: {cosmo.sigma8()}\n" + f" - w0: {cosmo['w0']}\n" + f" - wa: {cosmo['wa']}\n" + ) + + comm.Barrier() + + n_sims = args.n_sims + distribute_work( + comm, + n_sims, + run_one_simulation, + worker_args=(nside, version, tomography, out_dir, force_run), + ) + + comm.Barrier() + if rank == 0: + print("All simulations completed ✅") + + print(f"Merging into a covariance matrix for version {version}...") + outpath_cov = os.path.join( + out_dir, f"covariance_matrix_{version}_tomography_{tomography}.npy" + ) + + covariance_matrix = get_covariance_from_simulated_spectra( + n_sims, version, tomography, tomo_bin_ids, out_dir + ) + + np.save(outpath_cov, covariance_matrix) + print(f"Covariance matrix saved to {outpath_cov}") From 07a1dcbe9d091145dcc57a473fc2b469c0a00a57 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 17 Jul 2026 16:58:57 +0200 Subject: [PATCH 031/107] Add docstring --- scripts/cosmo_val/run_cl_gaussian_sims.py | 31 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index 97546e65..a85598d8 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -1,3 +1,28 @@ +""" +Executable script to run Gaussian simulations of shear maps and compute their power spectra using the NaMaster library. +The execution is distributed across multiple MPI processes to speed up the computation. + +To run the script, use the following command: + mpirun -np python run_cl_gaussian_sims.py --config --output --version [options] + +Options: + -c, --config: Path to the configuration file to generate the simulation (required). + -o, --output: Output directory for the simulation results (required, ideally the same as cosmo_val). + -v, --version: Version name for the simulation (required). + -t, --tomo: Whether to run the simulation in tomographic mode (default: False). + -iw, --ignore-warnings: Ignore warnings during the simulation. + -n, --n_sims: Number of simulations to run (default: 100). + -ns, --nside: Nside for the HEALPix maps (default: 1024). + -b, --binning: Binning scheme for the power spectra (default: powspace). + -es, --ell_step: Step size for the linear binning (default: 10). + -eb, --ell_bins: Number of bins for the power spectra (default: 32). + -p, --power: Power for the powspace binning (default: 0.5). + -cc, --cosmo-config: Path to the cosmology configuration file (default: None, use Planck18 cosmology by default). + -f, --force: Force overwrite of existing output files (default: False). + +Author: Sacha Guerrini +""" + import argparse import itertools import os @@ -97,7 +122,7 @@ def get_parser(): parser.add_argument( "-cc", "--cosmo-config", - help="Path to the cosmology configuration file (default: None)", + help="Path to the cosmology configuration file (default: None, use Planck18 cosmology by default)", type=str, default=None, ) @@ -387,10 +412,10 @@ def run_one_simulation(sim_id, nside, version, tomography, out_dir, force_run): # Save the results np.savez(out_file, cl_decoupled=cl_final) - print(f"Rank {rank} finished {sim_id} for version {version}") + print(f"Rank {rank} finished simulation {sim_id} for version {version}") except Exception as e: - print(f"Rank {rank} failed {sim_id} with error {e}") + print(f"Rank {rank} failed simulation {sim_id} with error {e}") # --- Distribution of the MPI processes in a master-worker scheme --- From 9a940d4f38f396bacd7ef5202cf5316404d479a3 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 20 Jul 2026 13:53:33 +0200 Subject: [PATCH 032/107] Fix multiple bugs in the script. --- scripts/cosmo_val/run_cl_gaussian_sims.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index a85598d8..c914eb0e 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -243,6 +243,14 @@ def build_fiducial_cl(cosmo_params, redshift_path, lmax, tomography, tomo_bin_id fiducial_cl = spv_pseudo_cl.get_fiducial_cl(z, dndz, lmax, cosmo) + # healpy.synalm expects spectra arrays with an explicit ell=0 slot. + # The theory helper returns ell=1..lmax, so prepend a zero monopole here + # to keep the Gaussian map synthesis numerically stable. + fiducial_cl = { + key: np.concatenate(([0.0], np.asarray(value, dtype=float))) + for key, value in fiducial_cl.items() + } + return cosmo, fiducial_cl @@ -358,6 +366,8 @@ def extract_spectra(noisy_gaussian_maps, n_gal, tomo_bin_ids, lmax, out_dir): cl_decoupled = wsp.decouple_cell(cl_coupled) cl_final[f"W{tomo_bin_a}xW{tomo_bin_b}"] = cl_decoupled + return cl_final + # --- single unit of work distributed to the MPI processes --- def run_one_simulation(sim_id, nside, version, tomography, out_dir, force_run): From fb40fa7362145b335782db318107ac0b875f5c36 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 20 Jul 2026 14:00:37 +0200 Subject: [PATCH 033/107] Fix bug related to noise bias decoupling --- src/sp_validation/cosmo_val/pseudo_cl.py | 64 +++++++++++++++--------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 13f747ad..3bb22c2f 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -145,35 +145,14 @@ def calculate_pseudo_cl_inka_cov( params = get_params_rho_tau(self.cc[ver]) cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - for bin_key1, bin_key2 in tomo_bin_pairs: - if bin_key1 == bin_key2: - cat_gal_ = self._get_tomographic_bin(params, cat_gal, bin_key1) - - noise_bias_cl = self.get_noise_bias(params, nside, cat_gal_) - - else: - noise_bias_cl = np.zeros((4, 2 * nside)) - - # Update the fiducial_cl dictionnary - fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = ( - np.array( - [ - fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], - ] - ) - + noise_bias_cl - ) + lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(self.nside) + b = self.get_namaster_bin(lmin, lmax, b_lmax) # Compute the fields and workspaces for bin_key1, bin_key2 in tomo_bin_pairs: self.print_cyan( f"Computing fields and workspaces for {bin_key1}, {bin_key2}" ) - lmin, lmax, b_lmax = spv_pseudo_cl.pseudo_cl_geometry(self.nside) - b = self.get_namaster_bin(lmin, lmax, b_lmax) # Get the tomographic bins cat_gal_a = self._get_tomographic_bin(params, cat_gal, bin_key1) @@ -234,6 +213,45 @@ def calculate_pseudo_cl_inka_cov( if bin_key1 <= bin_key2 and f"W{bin_key1}xW{bin_key2}" not in wsp_dict: wsp_dict[f"W{bin_key1}xW{bin_key2}"] = wsp + for bin_key1, bin_key2 in tomo_bin_pairs: + if bin_key1 == bin_key2: + self.print_cyan( + f"Adding the noise bias for tomographic bins {bin_key1}, {bin_key2}" + ) + + cat_gal_ = self._get_tomographic_bin(params, cat_gal, bin_key1) + + noise_bias_cl = self.get_noise_bias(params, nside, cat_gal_) + + # If using the analytical noise bias, we need to decouple it + if self.noise_bias_method == "analytical": + wsp = wsp_dict[f"W{bin_key1}xW{bin_key2}"] + noise_bias_cl = wsp.decouple_cell(noise_bias_cl) + + # And then unbin it + noise_bias_cl = b.unbin_cell(noise_bias_cl) + + # Force the bins not part of the mask to be zero + lowest_ell = b.get_ell_list(0)[0] + for i in range(4): + noise_bias_cl[i, :lowest_ell] = 0 + + else: + noise_bias_cl = np.zeros((4, 2 * nside)) + + # Update the fiducial_cl dictionnary + fiducial_cl[f"W{bin_key1}xW{bin_key2}"] = ( + np.array( + [ + fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + 0.0 * fiducial_cl[f"W{bin_key1}xW{bin_key2}"], + ] + ) + + noise_bias_cl + ) + if self.fiducial_input_inka == "coupled": # Couple the cell if required self.print_cyan("Coupling the fiducial Cls.") From 6ec19cf246f20cb7a397ff0c0b72c797558af0c3 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 20 Jul 2026 17:25:23 +0200 Subject: [PATCH 034/107] Fix typo in the value of --- src/sp_validation/cosmo_val/pseudo_cl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 3bb22c2f..0eb17da4 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -224,7 +224,7 @@ def calculate_pseudo_cl_inka_cov( noise_bias_cl = self.get_noise_bias(params, nside, cat_gal_) # If using the analytical noise bias, we need to decouple it - if self.noise_bias_method == "analytical": + if self.noise_bias_method == "analytic": wsp = wsp_dict[f"W{bin_key1}xW{bin_key2}"] noise_bias_cl = wsp.decouple_cell(noise_bias_cl) From 59d6098a57996d6c127e605080c9e00bc50440df Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 21 Jul 2026 09:44:56 +0200 Subject: [PATCH 035/107] Add print of the decoupling of the noise bias --- src/sp_validation/cosmo_val/pseudo_cl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 0eb17da4..f2311914 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -225,6 +225,9 @@ def calculate_pseudo_cl_inka_cov( # If using the analytical noise bias, we need to decouple it if self.noise_bias_method == "analytic": + self.print_cyan( + "Decoupling the noise bias for the analytic method..." + ) wsp = wsp_dict[f"W{bin_key1}xW{bin_key2}"] noise_bias_cl = wsp.decouple_cell(noise_bias_cl) From 39bc6db28030b35bfd573097827ba26c5dad6ee5 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 21 Jul 2026 09:57:58 +0200 Subject: [PATCH 036/107] Fix bugs in the non-tomographic branch --- scripts/cosmo_val/run_cl_gaussian_sims.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index c914eb0e..f5bf729c 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -251,6 +251,9 @@ def build_fiducial_cl(cosmo_params, redshift_path, lmax, tomography, tomo_bin_id for key, value in fiducial_cl.items() } + if not tomography: + fiducial_cl = {"WallxWall": fiducial_cl["W1xW1"]} + return cosmo, fiducial_cl @@ -332,7 +335,11 @@ def get_gaussian_simulation( ) else: gauss_map = hp.alm2map( - [np.zeros_like(alm_shear), alm_shear, np.zeros_like(alm_shear)], + [ + np.zeros_like(alm_shear[0]), + alm_shear[0], + np.zeros_like(alm_shear[0]), + ], nside=nside, verbose=False, ) From 89435712db51de8f12d515cbb13470986098dcc0 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 21 Jul 2026 15:23:56 +0200 Subject: [PATCH 037/107] Align namaster and fiducial binning for the covariance --- src/sp_validation/cosmo_val/pseudo_cl.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index f2311914..b0351d07 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -137,6 +137,13 @@ def calculate_pseudo_cl_inka_cov( fiducial_cl = self.get_fiducial_cl(ver, compute_tomography) + # Add a zero multipole and drop the last value to align + # Namaster and fiducial binning + fiducial_cl = { + key: np.concatenate(([0.0], np.asarray(value[:-1], dtype=float))) + for key, value in fiducial_cl.items() + } + self.print_cyan( "Estimating and adding the noise bias to the fiducial power spectra" ) From 45e938d6f98c204322c763d919fc3eb6cc17edd2 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 21 Jul 2026 15:48:19 +0200 Subject: [PATCH 038/107] Add options to save EB and BB covariances from Gaussian simulations --- scripts/cosmo_val/run_cl_gaussian_sims.py | 60 ++++++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index f5bf729c..44a0f6e9 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -40,6 +40,8 @@ import sp_validation.pseudo_cl as spv_pseudo_cl +pol_to_pol_index_dict = {"EE": 0, "EB": 1, "BE": 2, "BB": 3} + def get_parser(): """Create the argument parser.""" @@ -132,6 +134,18 @@ def get_parser(): help="Force overwrite of existing output files (default: False)", action="store_true", ) + parser.add_argument( + "-seb", + "--save-eb", + help="Save the EB covariance matrix (default: False)", + action="store_true", + ) + parser.add_argument( + "-sbb", + "--save-bb", + help="Save the BB covariance matrix (default: False)", + action="store_true", + ) return parser @@ -478,27 +492,28 @@ def distribute_work(comm, n_sims, worker_fn, worker_args=()): # --- Final function to read the computed spectra and extract a covariance matrix --- -def concatenate_spectra(cl_sample, tomo_bin_ids): +def concatenate_spectra(cl_sample, tomo_bin_ids, pol_index): """Concatenate the spectra from different tomographic bins into a single array.""" concatenated_cl = [] tomo_bin_pairs = itertools.combinations_with_replacement(tomo_bin_ids, 2) for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: - concatenated_cl.append(cl_sample[f"W{tomo_bin_a}xW{tomo_bin_b}"][0]) + concatenated_cl.append(cl_sample[f"W{tomo_bin_a}xW{tomo_bin_b}"][pol_index]) return np.concatenate(concatenated_cl) def get_covariance_from_simulated_spectra( - n_sims, version, tomography, tomo_bin_ids, out_dir + n_sims, version, tomography, tomo_bin_ids, pol, out_dir ): """Compute the covariance of the EE signal from the simulated spectra.""" cl_samples = [] + pol_index = pol_to_pol_index_dict[pol] for sim_id in range(n_sims): out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}.npz" if not os.path.exists(out_file): raise FileNotFoundError(f"Simulation output file {out_file} not found.") data = np.load(out_file, allow_pickle=True) cl_samples.append( - concatenate_spectra(data["cl_decoupled"].item(), tomo_bin_ids) + concatenate_spectra(data["cl_decoupled"].item(), tomo_bin_ids, pol_index) ) cl_samples = np.array(cl_samples) @@ -537,6 +552,9 @@ def get_covariance_from_simulated_spectra( force_run = args.force + save_eb = args.save_eb + save_bb = args.save_bb + if rank == 0: # Check that the config file exists (same than cosmo_val) ( @@ -651,14 +669,40 @@ def get_covariance_from_simulated_spectra( if rank == 0: print("All simulations completed ✅") - print(f"Merging into a covariance matrix for version {version}...") + print(f"Merging into a EE covariance matrix for version {version}...") outpath_cov = os.path.join( - out_dir, f"covariance_matrix_{version}_tomography_{tomography}.npy" + out_dir, f"covariance_matrix_EE_{version}_tomography_{tomography}.npy" ) covariance_matrix = get_covariance_from_simulated_spectra( - n_sims, version, tomography, tomo_bin_ids, out_dir + n_sims, version, tomography, tomo_bin_ids, "EE", out_dir ) np.save(outpath_cov, covariance_matrix) - print(f"Covariance matrix saved to {outpath_cov}") + print(f"EE covariance matrix saved to {outpath_cov}") + + if save_eb: + print(f"Merging into a EB covariance matrix for version {version}...") + outpath_cov = os.path.join( + out_dir, f"covariance_matrix_EB_{version}_tomography_{tomography}.npy" + ) + + covariance_matrix = get_covariance_from_simulated_spectra( + n_sims, version, tomography, tomo_bin_ids, "EB", out_dir + ) + + np.save(outpath_cov, covariance_matrix) + print(f"EB covariance matrix saved to {outpath_cov}") + + if save_bb: + print(f"Merging into a BB covariance matrix for version {version}...") + outpath_cov = os.path.join( + out_dir, f"covariance_matrix_BB_{version}_tomography_{tomography}.npy" + ) + + covariance_matrix = get_covariance_from_simulated_spectra( + n_sims, version, tomography, tomo_bin_ids, "BB", out_dir + ) + + np.save(outpath_cov, covariance_matrix) + print(f"BB covariance matrix saved to {outpath_cov}") From acb779806969f5ee04e910e495baaa936a7bdfce Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 21 Jul 2026 16:04:39 +0200 Subject: [PATCH 039/107] Fix the small things from Claude --- config/cosmo_val/cosmo_gaussian_sims.yaml | 2 +- scripts/cosmo_val/run_cl_gaussian_sims.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/cosmo_val/cosmo_gaussian_sims.yaml b/config/cosmo_val/cosmo_gaussian_sims.yaml index 99e333c8..da63e08d 100644 --- a/config/cosmo_val/cosmo_gaussian_sims.yaml +++ b/config/cosmo_val/cosmo_gaussian_sims.yaml @@ -9,6 +9,6 @@ extra_params: camb: nonlinear: True halofit_version: "mead2020_feedback" - HMCode_log_T_AGN: 7.8 + HMCode_logT_AGN: 7.8 kmax: 20.0 kmax_extrapolate: 500 \ No newline at end of file diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index 44a0f6e9..584dfbbf 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -437,7 +437,7 @@ def run_one_simulation(sim_id, nside, version, tomography, out_dir, force_run): # --- Compute the power spectra --- cl_final = extract_spectra( - noisy_gaussian_maps, n_gal, tomo_bin_ids, lmax, out_dir + noisy_gaussian_maps, n_gal, tomo_bin_ids, b_lmax, out_dir ) # Save the results From 618bddd98c1e8c36a4f32e64e309bb37d4d47a84 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 21 Jul 2026 16:32:08 +0200 Subject: [PATCH 040/107] Add seed to the gaussian simulations run script --- scripts/cosmo_val/run_cl_gaussian_sims.py | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index 584dfbbf..6db6f1ee 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -11,6 +11,7 @@ -v, --version: Version name for the simulation (required). -t, --tomo: Whether to run the simulation in tomographic mode (default: False). -iw, --ignore-warnings: Ignore warnings during the simulation. + -s, --seed: Random seed for the simulations (default: 42). -n, --n_sims: Number of simulations to run (default: 100). -ns, --nside: Nside for the HEALPix maps (default: 1024). -b, --binning: Binning scheme for the power spectra (default: powspace). @@ -19,6 +20,8 @@ -p, --power: Power for the powspace binning (default: 0.5). -cc, --cosmo-config: Path to the cosmology configuration file (default: None, use Planck18 cosmology by default). -f, --force: Force overwrite of existing output files (default: False). + -seb, --save-eb: Save the EB power spectrum (default: False). + -sbb, --save-bb: Save the BB power spectrum (default: False). Author: Sacha Guerrini """ @@ -79,6 +82,13 @@ def get_parser(): help="Ignore warnings during the simulation", action="store_true", ) + parser.add_argument( + "-s", + "--seed", + help="Random seed for the simulations (default: 42)", + type=int, + default=42, + ) parser.add_argument( "-n", "--n_sims", @@ -294,6 +304,12 @@ def prepare_workspace(n_gal, tomo_bin_ids, nside, b, b_lmax, out_dir, force_run) wsp.write_to(wsp_file) +def get_legacy_seed(base_seed, sim_id): + """Derive a well-mixed, reproducible legacy seed for a given simulation index.""" + ss = np.random.SeedSequence(entropy=base_seed, spawn_key=(sim_id,)) + return int(ss.generate_state(1, dtype=np.uint32)[0]) + + # --- Function to generate Gaussian simulation, add noise and extract the spectra --- def get_gaussian_simulation( nside, @@ -391,14 +407,17 @@ def extract_spectra(noisy_gaussian_maps, n_gal, tomo_bin_ids, lmax, out_dir): # --- single unit of work distributed to the MPI processes --- -def run_one_simulation(sim_id, nside, version, tomography, out_dir, force_run): +def run_one_simulation(sim_id, nside, version, tomography, out_dir, force_run, seed): """Worker executes this simulation and saves results""" try: - out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}.npz" + out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}_seed_{seed}.npz" if os.path.exists(out_file) and not force_run: print(f"Rank {rank} skipping {sim_id} (already exists)") return + legacy_seed = get_legacy_seed(seed, sim_id) + np.random.seed(legacy_seed) + print(f"Rank {rank} starting {sim_id}") setup_file = os.path.join( out_dir, f"precomputed_setup_{version}_tomography_{tomography}.npz" @@ -554,6 +573,7 @@ def get_covariance_from_simulated_spectra( save_eb = args.save_eb save_bb = args.save_bb + seed = args.seed if rank == 0: # Check that the config file exists (same than cosmo_val) @@ -662,7 +682,7 @@ def get_covariance_from_simulated_spectra( comm, n_sims, run_one_simulation, - worker_args=(nside, version, tomography, out_dir, force_run), + worker_args=(nside, version, tomography, out_dir, force_run, seed), ) comm.Barrier() From 3862e4da41ddf6f3b9b7a7f59adea4784bf75541 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Thu, 23 Jul 2026 20:03:16 +0200 Subject: [PATCH 041/107] Start modifying the onecovariance routine to account for tomography --- .../cosmo_val/catalog_characterization.py | 76 ++++++++++++++++--- src/sp_validation/cosmo_val/pseudo_cl.py | 66 ++++++++++++---- 2 files changed, 116 insertions(+), 26 deletions(-) diff --git a/src/sp_validation/cosmo_val/catalog_characterization.py b/src/sp_validation/cosmo_val/catalog_characterization.py index bb5f503d..f2507120 100644 --- a/src/sp_validation/cosmo_val/catalog_characterization.py +++ b/src/sp_validation/cosmo_val/catalog_characterization.py @@ -156,7 +156,9 @@ def n_eff_gal(self): @property def ellipticity_dispersion(self): if not hasattr(self, "_ellipticity_dispersion"): - self.calculate_ellipticity_dispersion() + self.calculate_ellipticity_dispersion(tomography=False) + if self.compute_tomography: + self.calculate_ellipticity_dispersion(tomography=True) return self._ellipticity_dispersion def _get_binned_catalog_mask(self, ver): @@ -202,29 +204,81 @@ def calculate_area_from_binned_catalog(self, ver): return area - def calculate_n_eff_gal(self): + def calculate_n_eff_gal(self, tomography=False): self.print_start("Calculating effective number of galaxy") - n_eff_gal = {} + if not hasattr(self, "_n_eff_gal"): + self._n_eff_gal = {} for ver in self.versions: self.print_magenta(ver) + if ver not in self._n_eff_gal: + self._n_eff_gal[ver] = {} + + if tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) + + else: + tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] + with self.results[ver].temporarily_read_data(): w = self._read_shear_cols(ver, "w_col") - n_eff_gal[ver] = n_eff_density(w, self.area[ver]) - print(f"n_eff_gal = {n_eff_gal[ver]:.2f} gal./arcmin^-2") + for tomo_bin_id in tomo_bin_ids: + if tomo_bin_id == "all": + self._n_eff_gal[ver][f"tomo_bin_{tomo_bin_id}"] = n_eff_density( + w, self.area[ver] + ) + else: + tomo_bin = self._read_shear_cols(ver, "tomo_bin_col") + mask = tomo_bin == tomo_bin_id + self._n_eff_gal[ver][f"tomo_bin_{tomo_bin_id}"] = n_eff_density( + w[mask], self.area[ver] + ) + print( + f"n_eff_gal for tomo_bin_{tomo_bin_id} = {self._n_eff_gal[ver][f'tomo_bin_{tomo_bin_id}']:.2f} gal./arcmin^-2" + ) - self._n_eff_gal = n_eff_gal self.print_done("Effective number of galaxy calculation finished") - def calculate_ellipticity_dispersion(self): + def calculate_ellipticity_dispersion(self, tomography=False): self.print_start("Calculating ellipticity dispersion") - ellipticity_dispersion = {} + if not hasattr(self, "_ellipticity_dispersion"): + self._ellipticity_dispersion = {} for ver in self.versions: self.print_magenta(ver) + if ver not in self._ellipticity_dispersion: + self._ellipticity_dispersion[ver] = {} + + if tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) + + else: + tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] + with self.results[ver].temporarily_read_data(): e1, e2, w = self._read_shear_cols(ver, "e1_col", "e2_col", "w_col") - ellipticity_dispersion[ver] = ellipticity_dispersion_stat(e1, e2, w) - print(f"Ellipticity dispersion = {ellipticity_dispersion[ver]:.4f}") - self._ellipticity_dispersion = ellipticity_dispersion + for tomo_bin_id in tomo_bin_ids: + if tomo_bin_id == "all": + self._ellipticity_dispersion[ver][f"tomo_bin_{tomo_bin_id}"] = ( + ellipticity_dispersion_stat(e1, e2, w) + ) + else: + tomo_bin = self._read_shear_cols(ver, "tomo_bin_col") + mask = tomo_bin == tomo_bin_id + self._ellipticity_dispersion[ver][f"tomo_bin_{tomo_bin_id}"] = ( + ellipticity_dispersion_stat(e1[mask], e2[mask], w[mask]) + ) + print( + f"Ellipticity dispersion for tomo_bin_{tomo_bin_id} = {self._ellipticity_dispersion[ver][f'tomo_bin_{tomo_bin_id}']:.4f}" + ) def plot_footprints(self): self.print_start("Plotting footprints:") diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index b0351d07..c467392e 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -387,7 +387,7 @@ def calculate_pseudo_cl_inka_cov( self.print_done(f"Done Pseudo-Cl covariance calculation for {ver}") self.print_done("Done Pseudo-Cl covariance") - def calculate_pseudo_cl_onecovariance(self): + def calculate_pseudo_cl_onecovariance(self, tomography=False): """ Compute the pseudo-Cl covariance using OneCovariance. """ @@ -407,11 +407,14 @@ def calculate_pseudo_cl_onecovariance(self): if not os.path.exists(template_config): raise ValueError(f"Template config file {template_config} does not exist") - self._pseudo_cls_onecov = {} + if not hasattr(self, "_pseudo_cls_onecov"): + self._pseudo_cls_onecov = {} for ver in self.versions: self.print_magenta(ver) - out_dir = self._output_path(f"pseudo_cl_cov_onecov_{ver}/") + out_dir = self._output_path( + "pseudo_cl/", f"pseudo_cl_cov_onecov_{ver}_tomography_{tomography}" + ) os.makedirs(out_dir, exist_ok=True) if ( @@ -421,7 +424,7 @@ def calculate_pseudo_cl_onecovariance(self): and not self.force_run ): self.print_done(f"Skipping OneCovariance calculation, {out_dir} exists") - self._load_onecovariance_cov(out_dir, ver) + self._load_onecovariance_cov(out_dir, ver, tomography) else: mask_path = self.cc[ver]["mask"] if not os.path.exists(mask_path): @@ -434,7 +437,9 @@ def calculate_pseudo_cl_onecovariance(self): self.cc[ver]["shear"]["redshift_path"] ) - config_path = os.path.join(out_dir, f"config_onecov_{ver}.ini") + config_path = os.path.join( + out_dir, f"config_onecov_{ver}_tomography_{tomography}.ini" + ) self.print_cyan( f"Modifying OneCovariance config file and saving it to {config_path}" @@ -446,6 +451,7 @@ def calculate_pseudo_cl_onecovariance(self): mask_path, redshift_distr_path, ver, + tomography, ) self.print_cyan("Running OneCovariance...") @@ -457,12 +463,19 @@ def calculate_pseudo_cl_onecovariance(self): f"OneCovariance command failed with return code {ret}" ) self.print_cyan("OneCovariance completed successfully.") - self._load_onecovariance_cov(out_dir, ver) + self._load_onecovariance_cov(out_dir, ver, tomography) self.print_done("Done Pseudo-Cl covariance with OneCovariance") def _modify_onecov_config( - self, template_config, config_path, out_dir, mask_path, redshift_distr_path, ver + self, + template_config, + config_path, + out_dir, + mask_path, + redshift_distr_path, + ver, + tomography, ): """ Modify OneCovariance configuration file with correct mask, redshift distribution, @@ -478,6 +491,10 @@ def _modify_onecov_config( Path to the mask file redshift_distr_path : str Path to the redshift distribution file + ver : str + Version identifier for the current analysis + tomography : bool + Whether to compute tomography or not """ config = configparser.ConfigParser() # Load the template configuration @@ -489,10 +506,24 @@ def _modify_onecov_config( config["survey specs"]["mask_directory"] = mask_folder config["survey specs"]["mask_file_lensing"] = mask_base config["survey specs"]["survey_area_lensing_in_deg2"] = str(self.area[ver]) - config["survey specs"]["ellipticity_dispersion"] = str( - self.ellipticity_dispersion[ver] + + # Update ellipticity dispersion and effective number density + # Account for tomography if needed + if tomography: + tomo_bin_ids, _ = self._get_tomo_bins(ver) + else: + tomo_bin_ids = ["all"] + input_ellipticity_distribution = ", ".join( + str(self.ellipticity_dispersion[ver][f"tomo_bin_{bin_id}"]) + for bin_id in tomo_bin_ids + ) + input_n_eff_gal = ", ".join( + str(self.n_eff_gal[ver][f"tomo_bin_{bin_id}"]) for bin_id in tomo_bin_ids + ) + config["survey specs"]["ellipticity_dispersion"] = ( + input_ellipticity_distribution ) - config["survey specs"]["n_eff_lensing"] = str(self.n_eff_gal[ver]) + config["survey specs"]["n_eff_lensing"] = input_n_eff_gal # Update redshift distribution path redshift_distr_base = os.path.basename(os.path.abspath(redshift_distr_path)) @@ -507,7 +538,7 @@ def _modify_onecov_config( with open(config_path, "w") as f: config.write(f) - def _load_onecovariance_cov(self, out_dir, ver): + def _load_onecovariance_cov(self, out_dir, ver, tomography): self.print_cyan(f"Loading OneCovariance results from {out_dir}") cov_one_cov = np.genfromtxt( os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") @@ -515,10 +546,15 @@ def _load_onecovariance_cov(self, out_dir, ver): gaussian_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=True) all_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=False) - self._pseudo_cls_onecov[ver] = { - "gaussian_cov": gaussian_one_cov, - "all_cov": all_one_cov, - } + key_to_update = "tomo" if tomography else "non_tomo" + self._pseudo_cls_onecov[ver].update( + { + key_to_update: { + "gaussian_cov": gaussian_one_cov, + "all_cov": all_one_cov, + } + } + ) def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): assert gaussian_part in ["iNKA", "OneCovariance"], ( From bbf52150d5fc2c83c08728857a138c63d56843cf Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:19:09 +0200 Subject: [PATCH 042/107] Fix bugs identified in Claude first review --- src/sp_validation/cosmo_val/catalog_characterization.py | 4 +++- src/sp_validation/cosmo_val/pseudo_cl.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sp_validation/cosmo_val/catalog_characterization.py b/src/sp_validation/cosmo_val/catalog_characterization.py index f2507120..e8117011 100644 --- a/src/sp_validation/cosmo_val/catalog_characterization.py +++ b/src/sp_validation/cosmo_val/catalog_characterization.py @@ -150,7 +150,9 @@ def area(self): @property def n_eff_gal(self): if not hasattr(self, "_n_eff_gal"): - self.calculate_n_eff_gal() + self.calculate_n_eff_gal(tomography=False) + if self.compute_tomography: + self.calculate_n_eff_gal(tomography=True) return self._n_eff_gal @property diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index c467392e..53936164 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -547,7 +547,7 @@ def _load_onecovariance_cov(self, out_dir, ver, tomography): all_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=False) key_to_update = "tomo" if tomography else "non_tomo" - self._pseudo_cls_onecov[ver].update( + self._pseudo_cls_onecov.setdefault(ver, {}).update( { key_to_update: { "gaussian_cov": gaussian_one_cov, From c7e3275fabd9f54ba231eecb99a499e33ffb2dbc Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:21:52 +0200 Subject: [PATCH 043/107] Reorganise the functions --- src/sp_validation/cosmo_val/pseudo_cl.py | 565 +++++++++++------------ 1 file changed, 282 insertions(+), 283 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 53936164..46a596f6 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -47,7 +47,193 @@ def pseudo_cls_onecov(self): return self._pseudo_cls_onecov # ---------------- Pseudo-Cl calculation methods ---------------- # - # TODO: some cleaning to clearly separate DV, covariance, and utility functions. + def calculate_pseudo_cl(self, compute_tomography=True): + """ + Compute the pseudo-Cl of a `CosmologyValidation` inputs with tomography. + """ + out_dir = self._output_path("pseudo_cl") + os.makedirs(out_dir, exist_ok=True) + + if compute_tomography: + self.print_start("Computing tomographic pseudo-Cl's") + else: + self.print_start("Computing non-tomographic pseudo-Cl's") + + self._pseudo_cls = getattr(self, "_pseudo_cls", {}) + + for ver in self.versions: + self.print_magenta(ver) + + if ver not in self.pseudo_cls.keys(): + self._pseudo_cls[ver] = {} + + if compute_tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) + + else: + tomo_bin_pairs = [("all", "all")] + + # Loop on the different tomographic bin pairs + for bin_key1, bin_key2 in tomo_bin_pairs: + self.print_cyan(f"Tomo Bin Pair: ({bin_key1}, {bin_key2})") + + if ( + f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + not in self._pseudo_cls[ver].keys() + ): + self._pseudo_cls[ver][ + f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" + ] = {} + + out_path = self._output_path_pseudo_cl( + ver, tomo_bin_pair=(bin_key1, bin_key2) + ) + if os.path.exists(out_path) and not self.force_run: + self.print_done( + f"Skipping Pseudo-Cl's calculation, {out_path} exists" + ) + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ + "pseudo_cl" + ] = cl_shear + continue + + if self.cell_method == "map": + self.calculate_pseudo_cl_map( + ver, self.nside, out_path, bin_key1, bin_key2 + ) + elif self.cell_method == "catalog": + self.calculate_pseudo_cl_catalog(ver, out_path, bin_key1, bin_key2) + else: + raise ValueError(f"Unknown cell method: {self.cell_method}") + + def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): + assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( + isinstance(tomo_bin_a, (int, np.integer)) + and isinstance(tomo_bin_b, (int, np.integer)) + ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." + + params = get_params_rho_tau(self.cc[ver]) + + self.print_cyan( + f"Computing pseudo-Cl's for tomographic bins {tomo_bin_a} and {tomo_bin_b}..." + ) + + # Load data and create shear and noise maps + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + + # Get the tomographic bin + cat_gal_a = self._get_tomographic_bin(params, cat_gal, tomo_bin_a) + cat_gal_b = self._get_tomographic_bin(params, cat_gal, tomo_bin_b) + + del cat_gal + + self.print_cyan("Creating maps and computing Cl's...") + # Get the pixels and indices for the catalogs + unique_pix_a, idx_a, idx_rep_a = self.get_pixels(params, nside, cat_gal_a) + unique_pix_b, idx_b, idx_rep_b = self.get_pixels(params, nside, cat_gal_b) + + # Create number density maps for each tomographic bin + n_gal_map_a = self.get_n_gal_map( + params, + nside, + cat_gal_a, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + ) + n_gal_map_b = self.get_n_gal_map( + params, + nside, + cat_gal_b, + unique_pix=unique_pix_b, + idx=idx_b, + idx_rep=idx_rep_b, + ) + + # Create shear maps for each tomographic bin + shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( + params, + nside, + cat_gal_a, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + n_gal_map=n_gal_map_a, + ) + shear_map_a = shear_map_a_e1 + 1j * shear_map_a_e2 + del shear_map_a_e1, shear_map_a_e2 + + shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( + params, + nside, + cat_gal_b, + unique_pix=unique_pix_b, + idx=idx_b, + idx_rep=idx_rep_b, + n_gal_map=n_gal_map_b, + ) + shear_map_b = shear_map_b_e1 + 1j * shear_map_b_e2 + del shear_map_b_e1, shear_map_b_e2 + + # Compute the pseudo-Cl's + ell_eff, cl_shear, wsp = self.get_pseudo_cls_map( + shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, mask_b=n_gal_map_b + ) + + # Remove the noise bias for auto-correlations. + if tomo_bin_a == tomo_bin_b: + # Compute the noise bias using noise_bias_method + cl_noise = self.get_noise_bias_from_gaussian_real( + params, + nside, + cat_gal_a, + unique_pix=unique_pix_a, + idx=idx_a, + idx_rep=idx_rep_a, + n_gal_map=n_gal_map_a, + wsp=wsp, + ) + + # Subtract the noise bias from the pseudo-Cl's + cl_shear = cl_shear - cl_noise + + self.print_cyan("Saving pseudo-Cl's...") + self.save_pseudo_cl(ell_eff, cl_shear, out_path) + + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ] = cl_shear + + def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): + assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( + isinstance(tomo_bin_a, (int, np.integer)) + and isinstance(tomo_bin_b, (int, np.integer)) + ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." + + params = get_params_rho_tau(self.cc[ver]) + + # Load data and create shear and noise maps + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + + ell_eff, cl_shear, wsp = self.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b + ) + + self.print_cyan("Saving pseudo-Cl's...") + self.save_pseudo_cl(ell_eff, cl_shear, out_path) + + cl_shear = fits.getdata(out_path) + self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ + "pseudo_cl" + ] = cl_shear + def calculate_pseudo_cl_inka_cov( self, compute_tomography=True, load_all_block=False ): @@ -467,104 +653,15 @@ def calculate_pseudo_cl_onecovariance(self, tomography=False): self.print_done("Done Pseudo-Cl covariance with OneCovariance") - def _modify_onecov_config( - self, - template_config, - config_path, - out_dir, - mask_path, - redshift_distr_path, - ver, - tomography, - ): - """ - Modify OneCovariance configuration file with correct mask, redshift distribution, - and ellipticity dispersion parameters. - - Parameters - ---------- - template_config : str - Path to the template configuration file - config_path : str - Path where the modified configuration will be saved - mask_path : str - Path to the mask file - redshift_distr_path : str - Path to the redshift distribution file - ver : str - Version identifier for the current analysis - tomography : bool - Whether to compute tomography or not - """ - config = configparser.ConfigParser() - # Load the template configuration - config.read(template_config) - - # Update mask path - mask_base = os.path.basename(os.path.abspath(mask_path)) - mask_folder = os.path.dirname(os.path.abspath(mask_path)) - config["survey specs"]["mask_directory"] = mask_folder - config["survey specs"]["mask_file_lensing"] = mask_base - config["survey specs"]["survey_area_lensing_in_deg2"] = str(self.area[ver]) - - # Update ellipticity dispersion and effective number density - # Account for tomography if needed - if tomography: - tomo_bin_ids, _ = self._get_tomo_bins(ver) - else: - tomo_bin_ids = ["all"] - input_ellipticity_distribution = ", ".join( - str(self.ellipticity_dispersion[ver][f"tomo_bin_{bin_id}"]) - for bin_id in tomo_bin_ids - ) - input_n_eff_gal = ", ".join( - str(self.n_eff_gal[ver][f"tomo_bin_{bin_id}"]) for bin_id in tomo_bin_ids + def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): + assert gaussian_part in ["iNKA", "OneCovariance"], ( + "gaussian_part must be 'iNKA' or 'OneCovariance'" ) - config["survey specs"]["ellipticity_dispersion"] = ( - input_ellipticity_distribution + self.print_start( + f"Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" ) - config["survey specs"]["n_eff_lensing"] = input_n_eff_gal - - # Update redshift distribution path - redshift_distr_base = os.path.basename(os.path.abspath(redshift_distr_path)) - redshift_distr_folder = os.path.dirname(os.path.abspath(redshift_distr_path)) - config["redshift"]["z_directory"] = redshift_distr_folder - config["redshift"]["zlens_file"] = redshift_distr_base - # Update output directory - config["output settings"]["directory"] = out_dir - - # Save the modified configuration - with open(config_path, "w") as f: - config.write(f) - - def _load_onecovariance_cov(self, out_dir, ver, tomography): - self.print_cyan(f"Loading OneCovariance results from {out_dir}") - cov_one_cov = np.genfromtxt( - os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") - ) - gaussian_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=True) - all_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=False) - - key_to_update = "tomo" if tomography else "non_tomo" - self._pseudo_cls_onecov.setdefault(ver, {}).update( - { - key_to_update: { - "gaussian_cov": gaussian_one_cov, - "all_cov": all_one_cov, - } - } - ) - - def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): - assert gaussian_part in ["iNKA", "OneCovariance"], ( - "gaussian_part must be 'iNKA' or 'OneCovariance'" - ) - self.print_start( - f"Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" - ) - - self._pseudo_cls_cov_g_ng = {} + self._pseudo_cls_cov_g_ng = {} for ver in self.versions: self.print_magenta(ver) @@ -605,193 +702,6 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): f"Done Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" ) - def calculate_pseudo_cl(self, compute_tomography=True): - """ - Compute the pseudo-Cl of a `CosmologyValidation` inputs with tomography. - """ - out_dir = self._output_path("pseudo_cl") - os.makedirs(out_dir, exist_ok=True) - - if compute_tomography: - self.print_start("Computing tomographic pseudo-Cl's") - else: - self.print_start("Computing non-tomographic pseudo-Cl's") - - self._pseudo_cls = getattr(self, "_pseudo_cls", {}) - - for ver in self.versions: - self.print_magenta(ver) - - if ver not in self.pseudo_cls.keys(): - self._pseudo_cls[ver] = {} - - if compute_tomography: - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - - if tomo_bin_ids is None or tomo_bin_pairs is None: - raise ValueError( - f"Version {ver} does not have tomography information." - ) - - else: - tomo_bin_pairs = [("all", "all")] - - # Loop on the different tomographic bin pairs - for bin_key1, bin_key2 in tomo_bin_pairs: - self.print_cyan(f"Tomo Bin Pair: ({bin_key1}, {bin_key2})") - - if ( - f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" - not in self._pseudo_cls[ver].keys() - ): - self._pseudo_cls[ver][ - f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}" - ] = {} - - out_path = self._output_path_pseudo_cl( - ver, tomo_bin_pair=(bin_key1, bin_key2) - ) - if os.path.exists(out_path) and not self.force_run: - self.print_done( - f"Skipping Pseudo-Cl's calculation, {out_path} exists" - ) - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver][f"tomo_bin_{bin_key1}_tomo_bin_{bin_key2}"][ - "pseudo_cl" - ] = cl_shear - continue - - if self.cell_method == "map": - self.calculate_pseudo_cl_map( - ver, self.nside, out_path, bin_key1, bin_key2 - ) - elif self.cell_method == "catalog": - self.calculate_pseudo_cl_catalog(ver, out_path, bin_key1, bin_key2) - else: - raise ValueError(f"Unknown cell method: {self.cell_method}") - - def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): - assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - isinstance(tomo_bin_a, (int, np.integer)) - and isinstance(tomo_bin_b, (int, np.integer)) - ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." - - params = get_params_rho_tau(self.cc[ver]) - - self.print_cyan( - f"Computing pseudo-Cl's for tomographic bins {tomo_bin_a} and {tomo_bin_b}..." - ) - - # Load data and create shear and noise maps - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - - # Get the tomographic bin - cat_gal_a = self._get_tomographic_bin(params, cat_gal, tomo_bin_a) - cat_gal_b = self._get_tomographic_bin(params, cat_gal, tomo_bin_b) - - del cat_gal - - self.print_cyan("Creating maps and computing Cl's...") - # Get the pixels and indices for the catalogs - unique_pix_a, idx_a, idx_rep_a = self.get_pixels(params, nside, cat_gal_a) - unique_pix_b, idx_b, idx_rep_b = self.get_pixels(params, nside, cat_gal_b) - - # Create number density maps for each tomographic bin - n_gal_map_a = self.get_n_gal_map( - params, - nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - ) - n_gal_map_b = self.get_n_gal_map( - params, - nside, - cat_gal_b, - unique_pix=unique_pix_b, - idx=idx_b, - idx_rep=idx_rep_b, - ) - - # Create shear maps for each tomographic bin - shear_map_a_e1, shear_map_a_e2 = self.get_shear_map( - params, - nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - n_gal_map=n_gal_map_a, - ) - shear_map_a = shear_map_a_e1 + 1j * shear_map_a_e2 - del shear_map_a_e1, shear_map_a_e2 - - shear_map_b_e1, shear_map_b_e2 = self.get_shear_map( - params, - nside, - cat_gal_b, - unique_pix=unique_pix_b, - idx=idx_b, - idx_rep=idx_rep_b, - n_gal_map=n_gal_map_b, - ) - shear_map_b = shear_map_b_e1 + 1j * shear_map_b_e2 - del shear_map_b_e1, shear_map_b_e2 - - # Compute the pseudo-Cl's - ell_eff, cl_shear, wsp = self.get_pseudo_cls_map( - shear_map_a, n_gal_map_a, shear_map_b=shear_map_b, mask_b=n_gal_map_b - ) - - # Remove the noise bias for auto-correlations. - if tomo_bin_a == tomo_bin_b: - # Compute the noise bias using noise_bias_method - cl_noise = self.get_noise_bias_from_gaussian_real( - params, - nside, - cat_gal_a, - unique_pix=unique_pix_a, - idx=idx_a, - idx_rep=idx_rep_a, - n_gal_map=n_gal_map_a, - wsp=wsp, - ) - - # Subtract the noise bias from the pseudo-Cl's - cl_shear = cl_shear - cl_noise - - self.print_cyan("Saving pseudo-Cl's...") - self.save_pseudo_cl(ell_eff, cl_shear, out_path) - - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ - "pseudo_cl" - ] = cl_shear - - def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): - assert (tomo_bin_a == "all" and tomo_bin_b == "all") or ( - isinstance(tomo_bin_a, (int, np.integer)) - and isinstance(tomo_bin_b, (int, np.integer)) - ), "tomo_bin_a and tomo_bin_b must be either both 'all' or both integers." - - params = get_params_rho_tau(self.cc[ver]) - - # Load data and create shear and noise maps - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) - - ell_eff, cl_shear, wsp = self.get_pseudo_cls_catalog( - catalog=cat_gal, params=params, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b - ) - - self.print_cyan("Saving pseudo-Cl's...") - self.save_pseudo_cl(ell_eff, cl_shear, out_path) - - cl_shear = fits.getdata(out_path) - self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ - "pseudo_cl" - ] = cl_shear - # ---------------- Utility functions for pseudo-Cl calculations ---------------- # def get_namaster_bin(self, lmin, lmax, b_lmax): """Build NaMaster binning object (thin wrapper, state -> primitive).""" @@ -1006,6 +916,95 @@ def get_fiducial_cl(self, ver, is_tomography): return fiducial_cl + def _modify_onecov_config( + self, + template_config, + config_path, + out_dir, + mask_path, + redshift_distr_path, + ver, + tomography, + ): + """ + Modify OneCovariance configuration file with correct mask, redshift distribution, + and ellipticity dispersion parameters. + + Parameters + ---------- + template_config : str + Path to the template configuration file + config_path : str + Path where the modified configuration will be saved + mask_path : str + Path to the mask file + redshift_distr_path : str + Path to the redshift distribution file + ver : str + Version identifier for the current analysis + tomography : bool + Whether to compute tomography or not + """ + config = configparser.ConfigParser() + # Load the template configuration + config.read(template_config) + + # Update mask path + mask_base = os.path.basename(os.path.abspath(mask_path)) + mask_folder = os.path.dirname(os.path.abspath(mask_path)) + config["survey specs"]["mask_directory"] = mask_folder + config["survey specs"]["mask_file_lensing"] = mask_base + config["survey specs"]["survey_area_lensing_in_deg2"] = str(self.area[ver]) + + # Update ellipticity dispersion and effective number density + # Account for tomography if needed + if tomography: + tomo_bin_ids, _ = self._get_tomo_bins(ver) + else: + tomo_bin_ids = ["all"] + input_ellipticity_distribution = ", ".join( + str(self.ellipticity_dispersion[ver][f"tomo_bin_{bin_id}"]) + for bin_id in tomo_bin_ids + ) + input_n_eff_gal = ", ".join( + str(self.n_eff_gal[ver][f"tomo_bin_{bin_id}"]) for bin_id in tomo_bin_ids + ) + config["survey specs"]["ellipticity_dispersion"] = ( + input_ellipticity_distribution + ) + config["survey specs"]["n_eff_lensing"] = input_n_eff_gal + + # Update redshift distribution path + redshift_distr_base = os.path.basename(os.path.abspath(redshift_distr_path)) + redshift_distr_folder = os.path.dirname(os.path.abspath(redshift_distr_path)) + config["redshift"]["z_directory"] = redshift_distr_folder + config["redshift"]["zlens_file"] = redshift_distr_base + + # Update output directory + config["output settings"]["directory"] = out_dir + + # Save the modified configuration + with open(config_path, "w") as f: + config.write(f) + + def _load_onecovariance_cov(self, out_dir, ver, tomography): + self.print_cyan(f"Loading OneCovariance results from {out_dir}") + cov_one_cov = np.genfromtxt( + os.path.join(out_dir, "covariance_list_3x2pt_pure_Cell.dat") + ) + gaussian_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=True) + all_one_cov = cov_from_one_covariance(cov_one_cov, gaussian=False) + + key_to_update = "tomo" if tomography else "non_tomo" + self._pseudo_cls_onecov.setdefault(ver, {}).update( + { + key_to_update: { + "gaussian_cov": gaussian_one_cov, + "all_cov": all_one_cov, + } + } + ) + def _get_tomographic_bin(self, params, cat_gal, tomo_bin): """Extract tomographic bin from a given catalogue""" if tomo_bin == "all": From ff3be19943ecfe178373e791aa2ddf858734529a Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:28:37 +0200 Subject: [PATCH 044/107] Update the merge of Gaussian and non-Gaussian part of the covariance --- src/sp_validation/cosmo_val/pseudo_cl.py | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 46a596f6..9ebe59f7 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -567,7 +567,8 @@ def calculate_pseudo_cl_inka_cov( ] # Merge the covariance blocks - self._pseudo_cls[ver]["cov_iNKA"] = self._merge_iNKA_covariance( + tomo_str = "tomo" if compute_tomography else "non_tomo" + self._pseudo_cls[ver][f"cov_iNKA_{tomo_str}"] = self._merge_iNKA_covariance( ver, tomography=compute_tomography ) self.print_done(f"Done Pseudo-Cl covariance calculation for {ver}") @@ -653,7 +654,7 @@ def calculate_pseudo_cl_onecovariance(self, tomography=False): self.print_done("Done Pseudo-Cl covariance with OneCovariance") - def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): + def calculate_pseudo_cl_g_ng_cov(self, tomography=False, gaussian_part="iNKA"): assert gaussian_part in ["iNKA", "OneCovariance"], ( "gaussian_part must be 'iNKA' or 'OneCovariance'" ) @@ -661,34 +662,37 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): f"Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" ) - self._pseudo_cls_cov_g_ng = {} + if not hasattr(self, "_pseudo_cls_cov_g_ng"): + self._pseudo_cls_cov_g_ng = {} for ver in self.versions: self.print_magenta(ver) + self._pseudo_cls_cov_g_ng.setdefault(ver, {}) + key_to_use = "tomo" if tomography else "non_tomo" out_file = self._output_path( - f"pseudo_cl_cov_g_ng_{gaussian_part}_{ver}.fits" + f"pseudo_cl_cov_g_ng_{gaussian_part}_{ver}_tomography_{tomography}.fits" ) if os.path.exists(out_file) and not self.force_run: self.print_done( f"Skipping Gaussian and Non-Gaussian covariance calculation, {out_file} exists" ) cov_hdu = fits.open(out_file) - self._pseudo_cls_cov_g_ng[ver] = cov_hdu + self._pseudo_cls_cov_g_ng[ver][key_to_use] = cov_hdu continue if gaussian_part == "iNKA": - gaussian_cov = self.pseudo_cls[ver]["cov"]["COVAR_EE_EE"].data + gaussian_cov = self.pseudo_cls[ver][f"cov_iNKA_{tomo_str}"].data non_gaussian_cov = ( - self.pseudo_cls_onecov[ver]["all_cov"] - - self.pseudo_cls_onecov[ver]["gaussian_cov"] + self.pseudo_cls_onecov[ver][key_to_use]["all_cov"] + - self.pseudo_cls_onecov[ver][key_to_use]["gaussian_cov"] ) full_cov = gaussian_cov + non_gaussian_cov elif gaussian_part == "OneCovariance": - gaussian_cov = self.pseudo_cls_onecov[ver]["gaussian_cov"] + gaussian_cov = self.pseudo_cls_onecov[ver][key_to_use]["gaussian_cov"] non_gaussian_cov = ( - self.pseudo_cls_onecov[ver]["all_cov"] - - self.pseudo_cls_onecov[ver]["gaussian_cov"] + self.pseudo_cls_onecov[ver][key_to_use]["all_cov"] + - self.pseudo_cls_onecov[ver][key_to_use]["gaussian_cov"] ) - full_cov = self.pseudo_cls_onecov[ver]["all_cov"] + full_cov = self.pseudo_cls_onecov[ver][key_to_use]["all_cov"] else: raise ValueError(f"Unknown gaussian_part: {gaussian_part}") self.print_cyan("Saving Gaussian and Non-Gaussian covariance...") @@ -697,7 +701,7 @@ def calculate_pseudo_cl_g_ng_cov(self, gaussian_part="iNKA"): hdu.append(fits.ImageHDU(non_gaussian_cov, name="COVAR_NON_GAUSSIAN")) hdu.append(fits.ImageHDU(full_cov, name="COVAR_FULL")) hdu.writeto(out_file, overwrite=True) - self._pseudo_cls_cov_g_ng[ver] = hdu + self._pseudo_cls_cov_g_ng[ver][key_to_use] = hdu self.print_done( f"Done Gaussian and Non-Gaussian covariance of the Pseudo-Cl's using {gaussian_part} for the Gaussian part" ) From 17886932637f8ffa9e5046e5d3f47f772acb5f9a Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:29:02 +0200 Subject: [PATCH 045/107] Update the merge of Gaussian and non-Gaussian part of the covariance --- src/sp_validation/cosmo_val/pseudo_cl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 9ebe59f7..69e0b0cd 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -680,7 +680,7 @@ def calculate_pseudo_cl_g_ng_cov(self, tomography=False, gaussian_part="iNKA"): self._pseudo_cls_cov_g_ng[ver][key_to_use] = cov_hdu continue if gaussian_part == "iNKA": - gaussian_cov = self.pseudo_cls[ver][f"cov_iNKA_{tomo_str}"].data + gaussian_cov = self.pseudo_cls[ver][f"cov_iNKA_{key_to_use}"].data non_gaussian_cov = ( self.pseudo_cls_onecov[ver][key_to_use]["all_cov"] - self.pseudo_cls_onecov[ver][key_to_use]["gaussian_cov"] From d1fa65c34b44bba3e46169fda5a0d50ecef0e42f Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:50:58 +0200 Subject: [PATCH 046/107] Modify the onecov config to match the cosmo object parameters --- src/sp_validation/cosmo_val/pseudo_cl.py | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 69e0b0cd..1e570543 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -984,6 +984,9 @@ def _modify_onecov_config( config["redshift"]["z_directory"] = redshift_distr_folder config["redshift"]["zlens_file"] = redshift_distr_base + # TODO: add an update of the config file cosmological parameters using the cosmo object + self._update_onecov_cosmo_params(config, self.cosmo) + # Update output directory config["output settings"]["directory"] = out_dir @@ -991,6 +994,41 @@ def _modify_onecov_config( with open(config_path, "w") as f: config.write(f) + def _update_onecov_cosmo_params(self, config, cosmo): + """ + Update the cosmological parameters in the OneCovariance configuration. + + Parameters + ---------- + config : configparser.ConfigParser + The configuration object to update. + cosmo + The cosmology object containing the parameters to set. + """ + # Update cosmo params section + config["cosmo"]["h"] = str(cosmo["H0"] / 100.0) + config["cosmo"]["omega_m"] = str(cosmo["Omega_m"]) + config["cosmo"]["omega_b"] = str(cosmo["Omega_b"]) + config["cosmo"]["omega_de"] = str(cosmo["Omega_de"]) + config["cosmo"]["sigma8"] = str(cosmo.sigma8()) + config["cosmo"]["ns"] = str(cosmo["n_s"]) + config["cosmo"]["w0"] = str(cosmo["w0"]) + config["cosmo"]["wa"] = str(cosmo["wa"]) + config["cosmo"]["neff"] = str(cosmo["Neff"]) + config["cosmo"]["m_nu"] = str(cosmo["m_nu"]) + + # Update powspec evaluation section + cosmo_dict = cosmo.to_dict() + + config["powspec evaluation"]["non_linear_model"] = str( + cosmo_dict.get("matter_power_spectrum") + ) + config["powspec evaluation"]["HMCode_logT_AGN"] = str( + cosmo_dict.get("extra_parameters", {}) + .get("camb", {}) + .get("HMCode_logT_AGN") + ) + def _load_onecovariance_cov(self, out_dir, ver, tomography): self.print_cyan(f"Loading OneCovariance results from {out_dir}") cov_one_cov = np.genfromtxt( From 4dfa2926d4892f4a924dee299b44e032a6366271 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:55:46 +0200 Subject: [PATCH 047/107] Fix KeyErro in iNKA covariance --- src/sp_validation/cosmo_val/pseudo_cl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 1e570543..ca7daf16 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -257,6 +257,7 @@ def calculate_pseudo_cl_inka_cov( out_path_merged = self._output_path_pseudo_cl_cov( ver, "iNKA", tomography=compute_tomography ) + tomo_str = "tomo" if compute_tomography else "non_tomo" if compute_tomography: tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) @@ -273,7 +274,9 @@ def calculate_pseudo_cl_inka_cov( self.print_done( f"Skipping Pseudo-Cl iNKA covariance calculation, {out_path_merged} exists" ) - self._pseudo_cls[ver]["cov_iNKA"] = fits.open(out_path_merged) + self._pseudo_cls[ver][f"cov_iNKA_{tomo_str}"] = fits.open( + out_path_merged + ) if load_all_block: self.print_done("Loading all the iNKA covariance blocks") @@ -567,7 +570,6 @@ def calculate_pseudo_cl_inka_cov( ] # Merge the covariance blocks - tomo_str = "tomo" if compute_tomography else "non_tomo" self._pseudo_cls[ver][f"cov_iNKA_{tomo_str}"] = self._merge_iNKA_covariance( ver, tomography=compute_tomography ) From 615426d0e4e6c2c9173b265eded05db217f29c26 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 09:59:45 +0200 Subject: [PATCH 048/107] Fix wrong load of COVAR_EE_EE iNKA covariance --- src/sp_validation/cosmo_val/pseudo_cl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index ca7daf16..8d7a0286 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -682,7 +682,9 @@ def calculate_pseudo_cl_g_ng_cov(self, tomography=False, gaussian_part="iNKA"): self._pseudo_cls_cov_g_ng[ver][key_to_use] = cov_hdu continue if gaussian_part == "iNKA": - gaussian_cov = self.pseudo_cls[ver][f"cov_iNKA_{key_to_use}"].data + gaussian_cov = self.pseudo_cls[ver][f"cov_iNKA_{key_to_use}"][ + "COVAR_EE_EE" + ].data non_gaussian_cov = ( self.pseudo_cls_onecov[ver][key_to_use]["all_cov"] - self.pseudo_cls_onecov[ver][key_to_use]["gaussian_cov"] From 194857e31cb1e351aebcb4c10675c38ba6c9f57b Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 10:09:32 +0200 Subject: [PATCH 049/107] Fix the update of cosmo params --- src/sp_validation/cosmo_val/pseudo_cl.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 8d7a0286..24a01898 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1013,7 +1013,7 @@ def _update_onecov_cosmo_params(self, config, cosmo): config["cosmo"]["h"] = str(cosmo["H0"] / 100.0) config["cosmo"]["omega_m"] = str(cosmo["Omega_m"]) config["cosmo"]["omega_b"] = str(cosmo["Omega_b"]) - config["cosmo"]["omega_de"] = str(cosmo["Omega_de"]) + config["cosmo"]["omega_de"] = str(cosmo["Omega_l"]) config["cosmo"]["sigma8"] = str(cosmo.sigma8()) config["cosmo"]["ns"] = str(cosmo["n_s"]) config["cosmo"]["w0"] = str(cosmo["w0"]) @@ -1025,12 +1025,14 @@ def _update_onecov_cosmo_params(self, config, cosmo): cosmo_dict = cosmo.to_dict() config["powspec evaluation"]["non_linear_model"] = str( - cosmo_dict.get("matter_power_spectrum") - ) + cosmo_dict.get("extra_parameters", {}) + .get("camb", {}) + .get("halofit_version", "mead2020_feedback") # Default to mead2020 feedback + ) # TODO: check what would be the default if not provided as input to align with this config["powspec evaluation"]["HMCode_logT_AGN"] = str( cosmo_dict.get("extra_parameters", {}) .get("camb", {}) - .get("HMCode_logT_AGN") + .get("HMCode_logT_AGN", 7.8) # OneCovariance default ) def _load_onecovariance_cov(self, out_dir, ver, tomography): From 9c13693d619f365c0b6110a2f166941adceceb52 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 10:20:51 +0200 Subject: [PATCH 050/107] Fetch the sum of neutrino mass from the cosmo object for OneCovariance --- src/sp_validation/cosmo_val/pseudo_cl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 24a01898..4253dffc 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1019,7 +1019,11 @@ def _update_onecov_cosmo_params(self, config, cosmo): config["cosmo"]["w0"] = str(cosmo["w0"]) config["cosmo"]["wa"] = str(cosmo["wa"]) config["cosmo"]["neff"] = str(cosmo["Neff"]) - config["cosmo"]["m_nu"] = str(cosmo["m_nu"]) + config["cosmo"]["m_nu"] = str( + np.sum( + cosmo["m_nu"] + ) # cosmo["m_nu"] is an array of neutrino masses in eV. OneCovariance expects the sum of neutrino masses in eV. + ) # Update powspec evaluation section cosmo_dict = cosmo.to_dict() From a8052d8209ad05785c10f7bf00a5fffd5cf1c6bb Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 11:00:00 +0200 Subject: [PATCH 051/107] Handle the redshift distribution in the non-tomographic case --- src/sp_validation/cosmo_val/pseudo_cl.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 4253dffc..f0b7e306 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -982,13 +982,30 @@ def _modify_onecov_config( ) config["survey specs"]["n_eff_lensing"] = input_n_eff_gal + # Handle the case where the redshift distribution file is tomographic + # Converts it to a non-tomographic distribution if needed + if not tomography: + # Load the redshift distribution + z, dndz = self.read_redshift_distribution(ver, is_tomography=True) + # Sum over tomographic bins to get the non-tomographic distribution + dndz_non_tomo = np.sum(dndz, axis=1) + # Save the non-tomographic distribution to a new file + non_tomo_redshift_distr_path = os.path.join( + out_dir, f"redshift_distribution_{ver}_non_tomo.txt" + ) + np.savetxt( + non_tomo_redshift_distr_path, + np.column_stack((z, dndz_non_tomo)), + header="z dN/dz", + ) + redshift_distr_path = non_tomo_redshift_distr_path + # Update redshift distribution path redshift_distr_base = os.path.basename(os.path.abspath(redshift_distr_path)) redshift_distr_folder = os.path.dirname(os.path.abspath(redshift_distr_path)) config["redshift"]["z_directory"] = redshift_distr_folder config["redshift"]["zlens_file"] = redshift_distr_base - # TODO: add an update of the config file cosmological parameters using the cosmo object self._update_onecov_cosmo_params(config, self.cosmo) # Update output directory From 0d10d543bfa6bd72c87987b40cc721f70b689dec Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 16:09:29 +0200 Subject: [PATCH 052/107] Update the load onecovariance script to handle tomography --- cosmo_val/cat_config.yaml | 6 +-- src/sp_validation/cosmo_val/pseudo_cl.py | 9 ++-- src/sp_validation/statistics.py | 64 +++++++++++++++++++++--- 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index ea36a81d..7fc6122f 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1272,7 +1272,7 @@ GLASS_mock_validation: n_e: 6.128201234871523 n_psf: 0.752316232272063 sigma_e: 0.379587601488189 - mask: /home/guerrini/sp_validation/cosmo_inference/data/mask/mask_map_footprint_nside_4096.fits + mask: /n09data/guerrini/glass_mock_test/mask_nside_1024.fits psf: PSF_flag: FLAG_PSF_HSM PSF_size: SIGMA_PSF_HSM @@ -1280,7 +1280,7 @@ GLASS_mock_validation: star_flag: FLAG_STAR_HSM star_size: SIGMA_STAR_HSM hdu: 1 - path: unions_shapepipe_psf_2024_v1.6.a.fits + path: ../../../../n17data/UNIONS/WL/v1.6.x/unions_shapepipe_psf_2024_v1.6.a.fits ra_col: RA dec_col: Dec e1_PSF_col: E1_PSF_HSM @@ -1303,5 +1303,5 @@ GLASS_mock_validation: dec_col: Dec e1_col: e1 e2_col: e2 - path: unions_shapepipe_star_2024_v1.6.a.fits + path: ../../../../n17data/UNIONS/WL/v1.6.x/unions_shapepipe_star_2024_v1.6.a.fits diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index f0b7e306..a0ceae5d 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -43,7 +43,9 @@ def pseudo_cls(self): @property def pseudo_cls_onecov(self): if not hasattr(self, "_pseudo_cls_onecov"): - self.calculate_pseudo_cl_onecovariance() + self.calculate_pseudo_cl_onecovariance(tomography=False) + if self.compute_tomography: + self.calculate_pseudo_cl_onecovariance(tomography=True) return self._pseudo_cls_onecov # ---------------- Pseudo-Cl calculation methods ---------------- # @@ -600,7 +602,7 @@ def calculate_pseudo_cl_onecovariance(self, tomography=False): self._pseudo_cls_onecov = {} for ver in self.versions: self.print_magenta(ver) - + self._pseudo_cls_onecov.setdefault(ver, {}) out_dir = self._output_path( "pseudo_cl/", f"pseudo_cl_cov_onecov_{ver}_tomography_{tomography}" ) @@ -672,7 +674,8 @@ def calculate_pseudo_cl_g_ng_cov(self, tomography=False, gaussian_part="iNKA"): self._pseudo_cls_cov_g_ng.setdefault(ver, {}) key_to_use = "tomo" if tomography else "non_tomo" out_file = self._output_path( - f"pseudo_cl_cov_g_ng_{gaussian_part}_{ver}_tomography_{tomography}.fits" + "pseudo_cl", + f"pseudo_cl_cov_g_ng_{gaussian_part}_{ver}_tomography_{tomography}.fits", ) if os.path.exists(out_file) and not self.force_run: self.print_done( diff --git a/src/sp_validation/statistics.py b/src/sp_validation/statistics.py index b8c6d1d5..b1df76b5 100644 --- a/src/sp_validation/statistics.py +++ b/src/sp_validation/statistics.py @@ -7,6 +7,8 @@ Extracted verbatim from the former basic.py. """ +import itertools + import numpy as np from scipy import stats @@ -119,10 +121,60 @@ def cov_from_one_covariance(cov_one_cov, gaussian=True): Square covariance matrix. """ - n_bins = np.sqrt(cov_one_cov.shape[0]).astype(int) - cov = np.zeros((n_bins, n_bins)) - index_value = 10 if gaussian else 9 - for i in range(n_bins): - for j in range(n_bins): - cov[i, j] = cov_one_cov[i * n_bins + j, index_value] + # Get the ell_bins and tomo_bins for each covariance entry + ell1 = cov_one_cov[:, 1] + ell2 = cov_one_cov[:, 2] + tomoi = cov_one_cov[:, 5].astype(int) + tomoj = cov_one_cov[:, 6].astype(int) + tomok = cov_one_cov[:, 7].astype(int) + tomol = cov_one_cov[:, 8].astype(int) + + # Get the values to save in the covariance + cov_col = 10 if gaussian else 9 + values = cov_one_cov[:, cov_col] + + # Map the ell bins to and index + ell_bins = np.unique(ell1) + n_ell_bins = len(ell_bins) + ell_to_idx = {ell: idx for idx, ell in enumerate(ell_bins)} + + # Map the tomo bin pairs to an index + tomo_bin_ids = np.unique(tomoi) + tomo_bin_pairs = list(itertools.combinations_with_replacement(tomo_bin_ids, 2)) + n_spectra = len(tomo_bin_pairs) + pair_to_idx = {pair: idx for idx, pair in enumerate(tomo_bin_pairs)} + + # Initialize the covariance matrix + cov_size = n_ell_bins * n_spectra + cov = np.zeros((cov_size, cov_size)) + + # Get the tomo bin pair indices + # the ordering of OneCovariance is the same than itertools + a_idx = np.fromiter( + (pair_to_idx[(bin_i, bin_j)] for bin_i, bin_j in zip(tomoi, tomoj)), + dtype=int, + count=len(tomoi), + ) + b_idx = np.fromiter( + (pair_to_idx[(bin_k, bin_l)] for bin_k, bin_l in zip(tomok, tomol)), + dtype=int, + count=len(tomok), + ) + + # Get the ell bin indices + ell1_idx = np.fromiter( + (ell_to_idx[ell] for ell in ell1), dtype=int, count=len(ell1) + ) + ell2_idx = np.fromiter( + (ell_to_idx[ell] for ell in ell2), dtype=int, count=len(ell2) + ) + + # Get the row and col + row = a_idx * n_ell_bins + ell1_idx + col = b_idx * n_ell_bins + ell2_idx + + # Assign the values + cov[row, col] = values + cov[col, row] = values # Symmetrize + return cov From debf0007f6d89ffed3511920af939424ba5a8a7e Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Fri, 24 Jul 2026 16:53:49 +0200 Subject: [PATCH 053/107] Update the tests for the onecovariance merge --- src/sp_validation/tests/test_statistics.py | 150 ++++++++++++++++----- 1 file changed, 116 insertions(+), 34 deletions(-) diff --git a/src/sp_validation/tests/test_statistics.py b/src/sp_validation/tests/test_statistics.py index 8e56d040..1b52d87b 100644 --- a/src/sp_validation/tests/test_statistics.py +++ b/src/sp_validation/tests/test_statistics.py @@ -150,47 +150,129 @@ def test_chi2_and_pte_diagonal_reduces_to_sum_of_squares(): def test_cov_from_one_covariance_selects_gaussian_column(): - """Pin the reshaped matrix and prove the gaussian column selection. - - WHAT IS PINNED: ``cov_from_one_covariance`` reads a flat OneCovariance - table with one row per ``(i, j)`` pair (row-major, ``k = i*n_bins + j``) - and lays the chosen column into a square matrix. Column 10 carries the - Gaussian-only term (``gaussian=True``); column 9 the Gaussian+non-Gaussian - term (``gaussian=False``). With a deterministic table whose entries encode - their row and column, both reshaped matrices are pinned as literals. - - WHY TEETH: the only difference between the two calls is the column index - (10 vs 9), so the two pinned matrices differ by exactly 1 in every entry, - proving the gaussian flag selects the right column. A companion check - places a unique tag ``10*i + j`` in column 10 and asserts the reshape is - row-major (``cov[i, j]`` lands at row ``i*n_bins + j``), so a transposed - or column-major refactor would change the recovered matrix. - """ - # Two-bin table -> 4 rows; entry (row k, col c) = 10*k + c, so the - # chosen-column values are distinct and self-documenting. Row k carries - # the (i, j) pair with k = i*n_bins + j, so rows 0..3 -> (0,0),(0,1), - # (1,0),(1,1). Column 10 holds values 10, 20, 30, 40; column 9 holds - # 9, 19, 29, 39. - one_cov = np.array([np.arange(11.0) + 10.0 * k for k in range(4)]) + """Pin the reshaped matrix and prove the gaussian column selection.""" + # obs, ell1, ell2, s1, s2, tomoi, tomoj, tomok, tomol, cov, covg, covng, covssc + one_cov = np.array( + [ + [0.0, 10.0, 10.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 99.0, 100.0, 0.0, 0.0], + [0.0, 10.0, 20.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 100.0, 101.0, 0.0, 0.0], + [0.0, 20.0, 20.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 110.0, 111.0, 0.0, 0.0], + ] + ) cov_gauss = cov_from_one_covariance(one_cov, gaussian=True) cov_nongauss = cov_from_one_covariance(one_cov, gaussian=False) - npt.assert_allclose(cov_gauss, [[10.0, 20.0], [30.0, 40.0]], rtol=1e-12) - npt.assert_allclose(cov_nongauss, [[9.0, 19.0], [29.0, 39.0]], rtol=1e-12) + npt.assert_allclose(cov_gauss, [[100.0, 101.0], [101.0, 111.0]], rtol=1e-12) + npt.assert_allclose(cov_nongauss, [[99.0, 100.0], [100.0, 110.0]], rtol=1e-12) - # TEETH: the gaussian flag shifts the column by one, so every entry of the - # gaussian matrix exceeds its non-gaussian counterpart by exactly 1. + # TEETH: the gaussian flag shifts the column by one, so every entry of + # the gaussian matrix exceeds its non-gaussian counterpart by exactly 1. npt.assert_allclose(cov_gauss - cov_nongauss, 1.0, rtol=1e-12) - # TEETH: the (i, j) -> row k = i*n_bins + j layout is row-major. Tag - # column 10 with 10*i + j and check it lands at cov[i, j], not cov[j, i]. - tagged = np.zeros((4, 11)) - for i in range(2): - for j in range(2): - tagged[i * 2 + j, 10] = 10.0 * i + j + +def test_cov_from_one_covariance_orders_tomo_blocks(): + """Pin the tomo-block ordering against ``combinations_with_replacement``.""" + # obs, ell1, ell2, s1, s2, tomoi, tomoj, tomok, tomol, cov, covg, covng, covssc + one_cov = np.array( + [ + [ + 0.0, + 10.0, + 10.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + -1.0, + 0.0, + 0.0, + 0.0, + ], # (a,b)=(0,0) + [ + 0.0, + 10.0, + 10.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 2.0, + 0.0, + 1.0, + 0.0, + 0.0, + ], # (0,1) + [ + 0.0, + 10.0, + 10.0, + 1.0, + 1.0, + 1.0, + 1.0, + 2.0, + 2.0, + 1.0, + 2.0, + 0.0, + 0.0, + ], # (0,2) + [ + 0.0, + 10.0, + 10.0, + 1.0, + 1.0, + 1.0, + 2.0, + 1.0, + 2.0, + 10.0, + 11.0, + 0.0, + 0.0, + ], # (1,1) + [ + 0.0, + 10.0, + 10.0, + 1.0, + 1.0, + 1.0, + 2.0, + 2.0, + 2.0, + 11.0, + 12.0, + 0.0, + 0.0, + ], # (1,2) + [ + 0.0, + 10.0, + 10.0, + 1.0, + 1.0, + 2.0, + 2.0, + 2.0, + 2.0, + 21.0, + 22.0, + 0.0, + 0.0, + ], # (2,2) + ] + ) + + cov_gauss = cov_from_one_covariance(one_cov, gaussian=True) + npt.assert_allclose( - cov_from_one_covariance(tagged, gaussian=True), - [[0.0, 1.0], [10.0, 11.0]], + cov_gauss, + [[0.0, 1.0, 2.0], [1.0, 11.0, 12.0], [2.0, 12.0, 22.0]], rtol=1e-12, ) From ee75f539d60eb1525ce0966f977238fa0c202a0d Mon Sep 17 00:00:00 2001 From: Sacha Guerrini <85949041+sachaguer@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:41:02 +0200 Subject: [PATCH 054/107] Tomographic rho-/tau-statistics (#295) * Start updating rho-tau-stats file for tomography * Bring parent branch sp_validation-extend-to-tomography to the child branch for rho-tau-statistics * Update calculation function to work in tomographic context. * Update plot scripts for tau-statistics to handle tomography. Validate the code running and GLASS mocks. Upgrade the sampling of error parameters. * Fix typo in tomo bins col name * Account for Copilot comments * Clean pseudo-cl test with the modified parameter initialisation * Reorganise functions in psf_systematics.py * Update the rho_tau_fits script to compute the tomographic PSF additive bias. * Update the rho_tau_fits script to compute the tomographic PSF additive bias. * Update the plotting scripts for xi_sys. * Fix bugs identified by Claude --- cosmo_val/cat_config.yaml | 2 +- src/sp_validation/cosmo_val/core.py | 41 +- src/sp_validation/cosmo_val/pseudo_cl.py | 17 +- .../cosmo_val/psf_systematics.py | 1467 +++++++++++++---- src/sp_validation/rho_tau.py | 254 +-- src/sp_validation/tests/test_pseudo_cl.py | 6 +- 6 files changed, 1360 insertions(+), 427 deletions(-) diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index ea36a81d..5f0676bf 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1280,7 +1280,7 @@ GLASS_mock_validation: star_flag: FLAG_STAR_HSM star_size: SIGMA_STAR_HSM hdu: 1 - path: unions_shapepipe_psf_2024_v1.6.a.fits + path: ../../../../n17data/UNIONS/WL/v1.6.x/unions_shapepipe_psf_2024_v1.6.a.fits ra_col: RA dec_col: Dec e1_PSF_col: E1_PSF_HSM diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index c6204c65..9c041788 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -483,11 +483,20 @@ def results_objectwise(self): self._results_objectwise = self.init_results(objectwise=True) return self._results_objectwise - def basename(self, version, treecorr_config=None, npatch=None): + def basename( + self, + version, + tomo_bin_a="all", + tomo_bin_b=None, + treecorr_config=None, + npatch=None, + ): cfg = treecorr_config or self.treecorr_config patches = npatch or self.npatch + tomo_bin_a_str = f"tomo_bin_{tomo_bin_a}" + tomo_bin_b_str = f"_tomo_bin_{tomo_bin_b}" if tomo_bin_b is not None else "" return ( - f"{version}_minsep={cfg['min_sep']}" + f"{version}_{tomo_bin_a_str}{tomo_bin_b_str}_minsep={cfg['min_sep']}" f"_maxsep={cfg['max_sep']}" f"_nbins={cfg['nbins']}" f"_npatch={patches}" @@ -682,3 +691,31 @@ def _get_tomo_bins(self, version): else: self.print_cyan(f"Version {version} does not have tomography information.") return None, None + + def _get_tomo_bins_for_versions(self, versions, tomography): + """ + Return a dictionary of tomo_bin_ids and tomo_bin_pairs for each version in versions. + + Parameters + ---------- + versions : list of str + List of catalog version identifiers + tomography : bool + If True, assumes tomography else returns the format for non-tomographic versions. + + Returns + ------- + dict + Dictionary with version as key and a dictionary containing 'ids' and 'pairs' as values. + Example: {version1: {'ids': tomo_bin_ids1, 'pairs': tomo_bin_pairs1}, ...} + """ + tomo_bins = {} + for ver in versions: + if tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + else: + tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] + + tomo_bins[ver] = {"ids": tomo_bin_ids, "pairs": tomo_bin_pairs} + + return tomo_bins diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index b0351d07..b99625de 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1187,16 +1187,11 @@ def get_ell_factor(ell): fmt_dict = {"EE": "o", "BB": "s", "EB": "^", "BE": "v"} # From all the versions, get the maximum number of tomo_bin_ids - tomo_bins = {} - for ver in versions: - if tomography: - tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - else: - tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] - - tomo_bins[ver] = {"ids": tomo_bin_ids, "pairs": tomo_bin_pairs} + tomo_bins = self._get_tomo_bins_for_versions(versions, tomography=tomography) - n_tomo_bins_plot = max(len(bins["ids"]) for bins in tomo_bins.values()) + max_key = max(tomo_bins, key=lambda k: len(tomo_bins[k]["ids"])) + n_tomo_bins_plot = len(tomo_bins[max_key]["ids"]) + reference_tomo_bin_pairs = tomo_bins[max_key]["pairs"] fig, axs = plt.subplots( n_tomo_bins_plot, @@ -1234,7 +1229,7 @@ def get_ell_factor(ell): # Better jittering: symmetric around original ell values jitter_fraction = (j - (len(versions) - 1) / 2) * offset jittered_ell = ell + jitter_fraction * ell_widths - ell_factor_ = get_ell_factor(jittered_ell) + ell_factor_ = get_ell_factor(ell) for pol in pol_list: pol_color = self.get_pol_color(ver_color, pol, pol_list) @@ -1259,7 +1254,7 @@ def get_ell_factor(ell): else: ell_label = r"$\ell(\ell+1) C_\ell$" - for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + for tomo_bin_a, tomo_bin_b in reference_tomo_bin_pairs: if tomography: ax = axs[tomo_bin_b - 1, tomo_bin_a - 1] else: diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 9f8a247e..6765123e 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -6,10 +6,13 @@ """ import os +from pathlib import Path import matplotlib.pyplot as plt +import matplotlib.ticker as mticker import numpy as np import treecorr +from astropy.io import fits from cs_util import plots as cs_plots from shear_psf_leakage import leakage from shear_psf_leakage import plots as psfleak_plots @@ -22,111 +25,25 @@ ) +# TODO: Reorganise the order of functions so it is more readable class PSFSystematicsMixin: - def calculate_rho_tau_stats(self): - out_dir = f"{self.cc['paths']['output']}/rho_tau_stats" - if not os.path.exists(out_dir): - os.mkdir(out_dir) - - self.print_start("Rho stats") - for ver in self.versions: - base = self.basename(ver) - rho_stat_handler, tau_stat_handler = get_rho_tau_w_cov( - self.cc, - ver, - self.treecorr_config, - out_dir, - base, - method=self.cov_estimate_method, - cov_rho=self.compute_cov_rho, - npatch=self.npatch, - ) - self.print_done("Rho stats finished") - - self._rho_stat_handler = rho_stat_handler - self._tau_stat_handler = tau_stat_handler - + # --- property definitions --- @property def rho_stat_handler(self): if not hasattr(self, "_rho_stat_handler"): - self.calculate_rho_tau_stats() + self.calculate_rho_tau_stats(tomography=False) + if self.compute_tomography: + self.calculate_rho_tau_stats(tomography=True) return self._rho_stat_handler @property def tau_stat_handler(self): if not hasattr(self, "_tau_stat_handler"): - self.calculate_rho_tau_stats() + self.calculate_rho_tau_stats(tomography=False) + if self.compute_tomography: + self.calculate_rho_tau_stats(tomography=True) return self._tau_stat_handler - def plot_rho_stats(self, abs=False): - filenames = [f"rho_stats_{self.basename(ver)}.fits" for ver in self.versions] - - savefig = "rho_stats.png" - self.rho_stat_handler.plot_rho_stats( - filenames, - self.colors, - self.versions, - savefig=savefig, - legend="outside", - abs=abs, - show=True, - close=True, - ) - - self.print_done( - "Rho stats plot saved to " - + f"{os.path.abspath(self.rho_stat_handler.catalogs._output)}/{savefig}", - ) - - def plot_tau_stats(self, plot_tau_m=False): - filenames = [f"tau_stats_{self.basename(ver)}.fits" for ver in self.versions] - - savefig = "tau_stats.png" - self.tau_stat_handler.plot_tau_stats( - filenames, - self.colors, - self.versions, - savefig=savefig, - legend="outside", - plot_tau_m=plot_tau_m, - show=True, - close=True, - ) - - self.print_done( - "Tau stats plot saved to " - + f"{os.path.abspath(self.tau_stat_handler.catalogs._output)}/{savefig}", - ) - - def set_params_rho_tau(self, params, params_psf, survey="other"): - params = {**params} - if survey in ("DES", "SP_axel_v0.0", "SP_axel_v0.0_repr"): - params["patch_number"] = 120 - print("DES, jackknife patch number = 120") - elif survey in ("SP_v1.4-P3", "SP_v1.4-P3_LFmask"): - params["patch_number"] = 120 - print("SP_v1.4, jackknife patch number =120") - else: - params["patch_number"] = 150 - - params["ra_PSF_col"] = params_psf["ra_col"] - params["dec_PSF_col"] = params_psf["dec_col"] - params["e1_PSF_col"] = params_psf["e1_PSF_col"] - params["e2_PSF_col"] = params_psf["e2_PSF_col"] - params["e1_star_col"] = params_psf["e1_star_col"] - params["e2_star_col"] = params_psf["e2_star_col"] - params["PSF_size"] = params_psf["PSF_size"] - params["star_size"] = params_psf["star_size"] - if survey != "DES": - params["PSF_flag"] = params_psf["PSF_flag"] - params["star_flag"] = params_psf["star_flag"] - params["ra_units"] = "deg" - params["dec_units"] = "deg" - - params["w_col"] = self.cc[survey]["shear"]["w_col"] - - return params - @property def psf_fitter(self): if not hasattr(self, "_psf_fitter"): @@ -137,150 +54,269 @@ def psf_fitter(self): ) return self._psf_fitter - def calculate_rho_tau_fits(self): + @property + def rho_tau_fits(self): + if not hasattr(self, "_rho_tau_fits"): + self.calculate_rho_tau_fits(tomography=False) + if self.compute_tomography: + self.calculate_rho_tau_fits(tomography=True) + return self._rho_tau_fits + + @property + def xi_psf_sys(self): + if not hasattr(self, "_xi_psf_sys"): + self.calculate_rho_tau_fits() + return self._xi_psf_sys + + # --- calculate functions --- + def calculate_rho_tau_stats(self, tomography=True): + out_dir = f"{self.cc['paths']['output']}/rho_tau_stats" + if not os.path.exists(out_dir): + os.mkdir(out_dir) + + self.print_start("Rho stats") + for ver in self.versions: + # Get the tomographic bins + if tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) + + else: + tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] + + # Get the selection for stars + mask_star = self._get_star_mask(ver) + + for tomo_bin_id in tomo_bin_ids: + self.print_cyan(f"Computing for the tomographic bin: {tomo_bin_id}") + base_rho = self.basename(ver) + base_tau = self.basename(ver, tomo_bin_a=tomo_bin_id) + + # Get the selection for galaxies + mask_gal = self._get_galaxy_mask(ver, tomo_bin_id) + + # Compute rho and tau statistics + rho_stat_handler, tau_stat_handler = get_rho_tau_w_cov( + self.cc, + ver, + self.treecorr_config, + out_dir, + base_rho, + base_tau, + mask_star=mask_star, + mask_gal=mask_gal, + method=self.cov_estimate_method, + cov_rho=self.compute_cov_rho, + npatch=self.npatch, + ) + self.print_done("Rho stats finished") + + self._rho_stat_handler = rho_stat_handler + self._tau_stat_handler = tau_stat_handler + + def calculate_rho_tau_fits(self, tomography=True, track_result=True): assert self.rho_tau_method != "none" # this initializes the rho_tau_fits attribute - self._rho_tau_fits = {"flat_sample_list": [], "result_list": [], "q_list": []} + if not hasattr(self, "_rho_tau_fits"): + self._rho_tau_fits = {} quantiles = [1 - self.quantile, self.quantile] - self._xi_psf_sys = {} + if not hasattr(self, "_xi_psf_sys"): + self._xi_psf_sys = {} for ver in self.versions: params = self.set_params_rho_tau( - self.results[ver]._params, - self.cc[ver]["psf"], - survey=ver, + ver, self.results[ver]._params, self.cc[ver]["psf"] ) - npatch = {"sim": 300, "jk": params["patch_number"]}.get( - self.cov_estimate_method, None - ) + # Get the tomographic bins + if tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) - base = self.basename(ver) + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) - flat_samples, result, q = get_samples( - self.psf_fitter, - ver, - base, - cov_type=self.cov_estimate_method, - apply_debias=npatch, - sampler=self.rho_tau_method, - ) + else: + tomo_bin_ids, tomo_bin_pairs = ["all"], [("all", "all")] - self.rho_tau_fits["flat_sample_list"].append(flat_samples) - self.rho_tau_fits["result_list"].append(result) - self.rho_tau_fits["q_list"].append(q) + # Get the samples for each tomographic bin + for tomo_bin_id in tomo_bin_ids: + self.print_cyan( + f"Sample PSF error parameters for tomographic bin {tomo_bin_id}" + ) + _ = self.get_samples( + ver, params, tomo_bin_id, track_result=track_result + ) - self.psf_fitter.load_rho_stat(f"rho_stats_{self.basename(ver)}.fits") - nbins = self.psf_fitter.rho_stat_handler._treecorr_config["nbins"] - xi_psf_sys_samples = np.array( - [self.psf_fitter.compute_xi_psf_sys(sample) for sample in flat_samples] - ).reshape(-1, nbins) - - self._xi_psf_sys[ver] = { - "mean": np.mean(xi_psf_sys_samples, axis=0), - "var": np.var(xi_psf_sys_samples, axis=0), - "quantiles": np.quantile(xi_psf_sys_samples, quantiles, axis=0), - } + # Get the xi_psf_sys for each tomographic bin pairs + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + xi_psf_sys_samples_plus, xi_psf_sys_samples_minus = ( + self.get_xi_psf_sys_samples(ver, params, tomo_bin_a, tomo_bin_b) + ) - @property - def rho_tau_fits(self): - if not hasattr(self, "_rho_tau_fits"): - self.calculate_rho_tau_fits() - return self._rho_tau_fits + if ver not in self._xi_psf_sys.keys(): + self._xi_psf_sys[ver] = {} + + self._xi_psf_sys[ver][ + f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}" + ] = { + "mean_plus": np.mean(xi_psf_sys_samples_plus, axis=0), + "var_plus": np.var(xi_psf_sys_samples_plus, axis=0), + "quantiles_plus": np.quantile( + xi_psf_sys_samples_plus, quantiles, axis=0 + ), + "mean_minus": np.mean(xi_psf_sys_samples_minus, axis=0), + "var_minus": np.var(xi_psf_sys_samples_minus, axis=0), + "quantiles_minus": np.quantile( + xi_psf_sys_samples_minus, quantiles, axis=0 + ), + } - def plot_rho_tau_fits(self): - out_dir = self.rho_stat_handler.catalogs._output + def calculate_scale_dependent_leakage(self): + # TODO: Upgrade for tomography + self.print_start("Calculating scale-dependent leakage:") + for ver in self.versions: + self.print_magenta(ver) + results = self.results[ver] - savefig = f"{out_dir}/contours_tau_stat.png" - psfleak_plots.plot_contours( - self.rho_tau_fits["flat_sample_list"], - names=["x0", "x1", "x2"], - labels=[r"\alpha", r"\beta", r"\eta"], - savefig=savefig, - legend_labels=self.versions, - legend_loc="upper right", - contour_colors=self.colors, - markers={"x0": 0, "x1": 1, "x2": 1}, - show=True, - close=True, - ) - self.print_done(f"Tau contours plot saved to {os.path.abspath(savefig)}") + output_base_path = self._output_path(f"leakage_{ver}/xi_for_leak_scale") + output_path_ab = f"{output_base_path}_a_b.txt" + output_path_aa = f"{output_base_path}_a_a.txt" + with self.results[ver].temporarily_read_data(): + if os.path.exists(output_path_ab) and os.path.exists(output_path_aa): + self.print_green( + f"Skipping computation, reading {output_path_ab} and " + f"{output_path_aa} instead" + ) - plt.figure(figsize=(15, 6)) - for mcmc_result, ver, color, flat_sample in zip( - self.rho_tau_fits["result_list"], - self.versions, - self.colors, - self.rho_tau_fits["flat_sample_list"], - ): - self.psf_fitter.load_rho_stat(f"rho_stats_{self.basename(ver)}.fits") - for i in range(100): - self.psf_fitter.plot_xi_psf_sys( - flat_sample[-i + 1], ver, color, alpha=0.1 - ) - self.psf_fitter.plot_xi_psf_sys(mcmc_result[1], ver, color) - plt.legend() - out_path = os.path.abspath(f"{out_dir}/xi_psf_sys_samples.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_psf_sys samples plot saved to {out_path}") + results.r_corr_gp = treecorr.GGCorrelation(self.treecorr_config) + results.r_corr_gp.read(output_path_ab) - plt.figure(figsize=(15, 6)) - for mcmc_result, ver, color, flat_sample in zip( - self.rho_tau_fits["result_list"], - self.versions, - self.colors, - self.rho_tau_fits["flat_sample_list"], - ): - ls = self.cc[ver]["ls"] - theta = self.psf_fitter.rho_stat_handler.rho_stats["theta"] - xi_psf_sys = self.xi_psf_sys[ver] - plt.plot(theta, xi_psf_sys["mean"], linestyle=ls, color=color) - plt.plot(theta, xi_psf_sys["quantiles"][0], linestyle=ls, color=color) - plt.plot(theta, xi_psf_sys["quantiles"][1], linestyle=ls, color=color) - plt.fill_between( - theta, - xi_psf_sys["quantiles"][0], - xi_psf_sys["quantiles"][1], - color=color, - alpha=0.25, - label=ver, - ) + results.r_corr_pp = treecorr.GGCorrelation(self.treecorr_config) + results.r_corr_pp.read(output_path_aa) - plt.xscale("log") - plt.yscale("log") - plt.xlabel(r"$\theta$ [arcmin]") - plt.ylabel(r"$\xi^{\rm PSF}_{\rm sys}$") - plt.title(f"{1 - self.quantile:.1%}, {self.quantile:.1%} quantiles") - plt.legend() - out_path = os.path.abspath(f"{out_dir}/xi_psf_sys_quantiles.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_psf_sys quantiles plot saved to {out_path}") + else: + results.compute_corr_gp_pp_alpha(output_base_path=output_base_path) - for mcmc_result, ver, flat_sample in zip( - self.rho_tau_fits["result_list"], - self.versions, - self.rho_tau_fits["flat_sample_list"], - ): - self.psf_fitter.load_rho_stat(f"rho_stats_{self.basename(ver)}.fits") - for yscale in ("linear", "log"): - out_path = os.path.abspath( - f"{out_dir}/xi_psf_sys_terms_{yscale}_{ver}.png" - ) - self.psf_fitter.plot_xi_psf_sys_terms( - ver, mcmc_result[1], out_path, yscale=yscale, show=True - ) - self.print_done( - f"{yscale}-scale xi_psf_sys terms plot saved to {out_path}" + results.do_alpha(fast=True) + results.do_xi_sys() + + self.print_done("Finished scale-dependent leakage calculation.") + + def calculate_objectwise_leakage(self): + # TODO: Upgrade for tomography + if not hasattr(self.results[self.versions[0]], "alpha_leak_mean"): + self.calculate_scale_dependent_leakage() + + self.print_start("Object-wise leakage:") + mix = True + order = "lin" + for ver in self.versions: + self.print_magenta(ver) + + results_obj = self.results_objectwise[ver] + results_obj.check_params() + results_obj.update_params() + results_obj.prepare_output() + + # Skip read_data() and copy catalogue from scale leakage instance instead + # results_obj._dat = self.results[ver].dat_shear + + out_base = results_obj.get_out_base(mix, order) + out_path = f"{out_base}.pkl" + if os.path.exists(out_path): + self.print_green( + f"Skipping object-wise leakage, file {out_path} exists" ) + results_obj.par_best_fit = leakage.read_from_file(out_path) + else: + self.print_cyan("Computing object-wise leakage regression") - @property - def xi_psf_sys(self): - if not hasattr(self, "_xi_psf_sys"): - self.calculate_rho_tau_fits() - return self._xi_psf_sys + # Run + with results_obj.temporarily_read_data(): + try: + results_obj.PSF_leakage() + except KeyError as e: + print(f"{e}\nExpected key is missing from catalog.") + # remove the results object for this version + self.results_objectwise.pop(ver) + + # Gather coefficients + leakage_coeff = {} + for ver in self.results_objectwise: + results = self.results[ver] + par_best_fit = self.results_objectwise[ver].par_best_fit + + # Object-wise leakage + a11 = ufloat(par_best_fit["a11"].value, par_best_fit["a11"].stderr) + a22 = ufloat(par_best_fit["a22"].value, par_best_fit["a22"].stderr) + leakage_coeff[ver] = { + "a11": a11, + "a22": a22, + "aii_mean": 0.5 * (a11 + a22), + # Scale-dependent leakage: mean + "alpha_mean": ufloat(results.alpha_leak_mean, results.alpha_leak_std), + # Scale-dependent leakage: value at smallest scale + "alpha_1": ufloat(results.alpha_leak[0], results.sig_alpha_leak[0]), + # Scale-dependent leakage: value extrapolated to 0 using affine model + "alpha_0": ufloat( + results.alpha_affine_best_fit["c"].value, + results.alpha_affine_best_fit["c"].stderr, + ), + } + + self.leakage_coeff = leakage_coeff + + # --- utility functions --- + def _get_galaxy_mask(self, ver, tomo_bin_id): + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + if tomo_bin_id != "all": + gal_mask = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] == tomo_bin_id + else: + gal_mask = np.ones(len(cat_gal), dtype=bool) + return gal_mask + + def _get_star_mask(self, ver): + cat_star = fits.getdata( + self.cc[ver]["psf"]["path"], hdu=self.cc[ver]["psf"]["hdu"] + ) + PSF_flag = self.cc[ver]["psf"].get("PSF_flag") + star_flag = self.cc[ver]["psf"].get("star_flag") + if PSF_flag is not None: + if star_flag is not None: + star_mask = (cat_star[PSF_flag] == 0) & (cat_star[star_flag] == 0) + else: + star_mask = cat_star[PSF_flag] == 0 + else: + star_mask = np.ones(len(cat_star), dtype=bool) + return star_mask + + def set_params_rho_tau(self, ver, params, params_psf): + params = {**params} + + params["ra_PSF_col"] = params_psf["ra_col"] + params["dec_PSF_col"] = params_psf["dec_col"] + params["e1_PSF_col"] = params_psf["e1_PSF_col"] + params["e2_PSF_col"] = params_psf["e2_PSF_col"] + params["e1_star_col"] = params_psf["e1_star_col"] + params["e2_star_col"] = params_psf["e2_star_col"] + params["PSF_size"] = params_psf["PSF_size"] + params["star_size"] = params_psf["star_size"] + params["PSF_flag"] = params_psf.get("PSF_flag") + params["star_flag"] = params_psf.get("star_flag") + params["ra_units"] = "deg" + params["dec_units"] = "deg" + + params["w_col"] = self.cc[ver]["shear"]["w_col"] + params["patch_number"] = self.cc[ver].get("patch_number", 100) + + return params def set_params_leakage_scale(self, ver): params_in = {} @@ -341,35 +377,548 @@ def set_params_leakage_object(self, ver): return params_in - def calculate_scale_dependent_leakage(self): - self.print_start("Calculating scale-dependent leakage:") - for ver in self.versions: - self.print_magenta(ver) - results = self.results[ver] + def set_psf_parameter_sampling_method(self, rho_tau_method): + if rho_tau_method not in ["emcee", "lsq"]: + raise ValueError("Invalid PSF parameter sampling method.") + self.rho_tau_method = rho_tau_method - output_base_path = self._output_path(f"leakage_{ver}/xi_for_leak_scale") - output_path_ab = f"{output_base_path}_a_b.txt" - output_path_aa = f"{output_base_path}_a_a.txt" - with self.results[ver].temporarily_read_data(): - if os.path.exists(output_path_ab) and os.path.exists(output_path_aa): - self.print_green( - f"Skipping computation, reading {output_path_ab} and " - f"{output_path_aa} instead" + def set_psf_parameter_nsamples(self, nsamples): + self.psf_error_nsamples = nsamples + + def set_psf_parameter_nwalkers(self, nwalkers): + self.psf_error_nwalkers = nwalkers + + def get_samples(self, version, params, tomo_bin_id, track_result=False): + npatch = params["patch_number"] if self.cov_estimate_method == "jk" else None + + base_rho = self.basename(version) + base_tau = self.basename(version, tomo_bin_a=tomo_bin_id) + + # Set the number of samples and walkers and fallback to defaults if not attributed. + n_samples = ( + self.psf_error_nsamples if hasattr(self, "psf_error_nsamples") else 10_000 + ) + n_walkers = ( + self.psf_error_nwalkers if hasattr(self, "psf_error_nwalkers") else 124 + ) + + flat_samples, result, q = get_samples( + self.psf_fitter, + base_rho, + base_tau, + cov_type=self.cov_estimate_method, + apply_debias=npatch, + sampler=self.rho_tau_method, + nsamples=n_samples, + nwalkers=n_walkers, + ) + + if track_result: + if version not in self.rho_tau_fits.keys(): + self.rho_tau_fits[version] = { + "flat_samples": {}, + "result": {}, + "quantile": {}, + } + self.rho_tau_fits[version]["flat_samples"][f"tomo_bin_{tomo_bin_id}"] = ( + flat_samples + ) + self.rho_tau_fits[version]["result"][f"tomo_bin_{tomo_bin_id}"] = result + self.rho_tau_fits[version]["quantile"][f"tomo_bin_{tomo_bin_id}"] = q + + return flat_samples + + def get_xi_psf_sys_samples(self, ver, params, tomo_bin_a, tomo_bin_b): + base_rho = self.basename(ver) + self.psf_fitter.load_rho_stat(f"rho_stats_{base_rho}.fits") + nbins = self.psf_fitter.rho_stat_handler._treecorr_config["nbins"] + + # Get the samples for the given tomographic bins + flat_samples_a = self.get_samples(ver, params, tomo_bin_a, track_result=False) + flat_samples_b = self.get_samples(ver, params, tomo_bin_b, track_result=False) + + xi_psf_sys_samples_plus = np.array( + [ + self.psf_fitter.compute_xi_psf_sys(sample_a, sample_b, p_or_m="p") + for (sample_a, sample_b) in zip(flat_samples_a, flat_samples_b) + ] + ).reshape(-1, nbins) + + xi_psf_sys_samples_minus = np.array( + [ + self.psf_fitter.compute_xi_psf_sys(sample_a, sample_b, p_or_m="m") + for (sample_a, sample_b) in zip(flat_samples_a, flat_samples_b) + ] + ).reshape(-1, nbins) + + return xi_psf_sys_samples_plus, xi_psf_sys_samples_minus + + # --- plotting functions --- + def plot_rho_stats( + self, + versions=None, + colors=None, + abs=False, + offset=0, + savefig=None, + show=True, + close=True, + ): + """ + Plot the Rho statistics. + + Parameters + ---------- + versions : list, optional + List of versions to plot. If None, all versions are plotted. + abs : bool, optional + If True, plot the absolute values of the Rho statistics. + offset : float, optional + Offset to apply to the versions for better visualisation. + savefig : str, optional + If provided, save the figure to this file. + show : bool, optional + If True, show the figure. + close : bool, optional + If True, close the figure after saving or showing. + """ + if versions is None: + versions = self.versions + + filenames = [f"rho_stats_{self.basename(ver)}.fits" for ver in versions] + + if colors is None: + colors = [self.cc[ver]["colour"] for ver in versions] + + if len(colors) != len(versions): + raise ValueError("Colors and versions must have the same length.") + + self.rho_stat_handler.plot_rho_stats( + filenames, + colors, + versions, + offset=offset, + savefig=savefig, + legend="outside", + abs=abs, + show=show, + close=close, + ) + + if savefig is not None: + self.print_done( + "Rho stats plot saved to " + + f"{os.path.abspath(self.rho_stat_handler.catalogs._output)}/{savefig}", + ) + + def plot_tau_stats( + self, + tomography=False, + cov_type=None, + versions=None, + colors=None, + offset=0, + savefig=None, + show=True, + close=True, + plot_tau_m=False, + plot_theta_times_tau=False, + fmt="", + capsize=2, + ): + if versions is None: + versions = self.versions + + if colors is None: + colors = [self.cc[ver]["colour"] for ver in versions] + + if len(colors) != len(versions): + raise ValueError("Colors and versions must have the same length.") + + if cov_type is None: + self.print_cyan("Using the error bars from the tau-statistics files") + else: + self.print_cyan( + f"Using the error bars from the covariance files of type: {cov_type}" + ) + + out_dir = f"{self.cc['paths']['output']}/rho_tau_stats" + + if tomography: + # Write the whole script for the tomography. It does not exist in shear_psf_leakage + e_obs = r"e^\mathrm{obs}" + e_psf = r"e^\mathrm{PSF}" + delta_e_psf = r"\delta e^\mathrm{PSF}" + delta_T_psf = r"\delta T^\mathrm{PSF}" + + factor_theta_label = r"\theta" if plot_theta_times_tau else r"" + + titles = [ + rf"$\tau_0 = \langle {e_obs} {e_psf} \rangle$", + rf"${factor_theta_label} \tau_2 = {factor_theta_label} \langle {e_obs} {delta_e_psf} \rangle$", + rf"${factor_theta_label} \tau_5 = {factor_theta_label} \langle {e_obs} {delta_T_psf} \rangle$", + ] + + dict_index_tau = { + 0: "0", + 1: "2", + 2: "5", + } + + # From all the versions, get the maximum number of tomo_bin_ids + tomo_bins = self._get_tomo_bins_for_versions( + versions, tomography=tomography + ) + + n_tomo_bins_plot = max(len(bins["ids"]) for bins in tomo_bins.values()) + + n_rows = n_tomo_bins_plot * (1 + plot_tau_m) + + fig = plt.figure(figsize=(20 * (1 + plot_tau_m), 10 * (1 + plot_tau_m))) + gs = fig.add_gridspec(n_rows, 3, wspace=0.1, hspace=0) + all_axs = gs.subplots(sharex="col") + + for k in range(n_rows): + axs = all_axs[k] + + tomo_bin_id = k // 2 + 1 if plot_tau_m else k + 1 + is_m_component = k % 2 if plot_tau_m else 0 + + for file_idx, (ver, color) in enumerate(zip(versions, colors)): + # Check if the tomo bin is valid for this version + if tomo_bin_id not in tomo_bins[ver]["ids"]: + continue + + # Load the tau-stats in the tau_stat_handler for easier read + base_tau = self.basename(ver, tomo_bin_a=tomo_bin_id) + + self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau}.fits") + + if cov_type is not None: + cov_tau_path = ( + Path(out_dir) / f"cov_tau_{base_tau}_{cov_type}.npy" + ) + cov_tau = np.load(cov_tau_path) + + # Plot the different tau-stats per row + for i in range(3): + p_or_m = "m" if is_m_component else "p" + p_or_m_label = "-" if is_m_component else "+" + + # Get the jittered angular scale for the x-axis + theta = self.tau_stat_handler.tau_stats["theta"] + num_theta_bins = theta.shape[0] + + jittered_theta = self._get_jittered_theta( + theta, file_idx, len(versions), offset + ) + + factor_theta = ( + np.ones_like(jittered_theta) + if (i == 0) or not plot_theta_times_tau + else theta + ) + + y_plot = ( + self.tau_stat_handler.tau_stats[ + f"tau_{dict_index_tau[i]}_{p_or_m}" + ] + * factor_theta + ) + + if cov_type is None or p_or_m == "m": + cov_diag = self.tau_stat_handler.tau_stats[ + "vartau_" + dict_index_tau[i] + "_" + p_or_m + ] + else: + cov_diag = np.diag( + cov_tau[ + i * num_theta_bins : (i + 1) * num_theta_bins, + i * num_theta_bins : (i + 1) * num_theta_bins, + ] + ) + + yerr_plot = np.sqrt(cov_diag) * factor_theta + + ver_label = ( + self.cc[ver]["label"] if "label" in self.cc[ver] else ver + ) + axs[i].errorbar( + jittered_theta, + y_plot, + yerr=yerr_plot, + fmt=fmt, + label=ver_label, + capsize=capsize, + color=color, + ) + + # Set the style of the plot + for i in range(3): + axs[i].set_xscale("log") + axs[i].set_xlim(theta.min() * 0.9, theta.max() * 1.1) + if i == 0: + axs[i].set_ylabel(f"Bin {tomo_bin_id}\n `{p_or_m_label}' comp.") + if k == n_rows - 1: + axs[i].set_xlabel(r"$\theta$ [arcmin]") + if k == 0: + axs[i].set_title(titles[i]) + + # --- Force scientific notation and scaling --- + axs[i].ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + axs[i].yaxis.offsetText.set_visible(True) + + # --- Make it look clean --- + axs[i].yaxis.set_major_locator( + mticker.MaxNLocator(nbins=5) + ) # Fewer, rounded ticks + # axs[i].yaxis.get_offset_text().set_fontsize(10) # Smaller ×10⁻⁴ label + axs[i].yaxis.get_offset_text().set_position( + (0, 1.02) + ) # Move scale factor slightly above axis + axs[i].yaxis.get_offset_text().set_ha("left") + + if k == n_rows - 1: + axs[1].legend( + loc="upper center", + bbox_to_anchor=(0.5, -0.5), # (x, y) relative to the axes + ncol=3, # number of columns + frameon=False, ) - results.r_corr_gp = treecorr.GGCorrelation(self.treecorr_config) - results.r_corr_gp.read(output_path_ab) + plt.tight_layout() - results.r_corr_pp = treecorr.GGCorrelation(self.treecorr_config) - results.r_corr_pp.read(output_path_aa) + if savefig is not None: + plt.savefig(savefig, dpi=300, bbox_inches="tight") - else: - results.compute_corr_gp_pp_alpha(output_base_path=output_base_path) + if show: + plt.show() - results.do_alpha(fast=True) - results.do_xi_sys() + if close: + plt.close() - self.print_done("Finished scale-dependent leakage calculation.") + else: + filenames = [f"tau_stats_{self.basename(ver)}.fits" for ver in versions] + cov_paths = [ + f"cov_tau_{self.basename(ver)}_{cov_type}.npy" for ver in versions + ] + self.tau_stat_handler.plot_tau_stats( + filenames, + colors, + versions, + cov_paths=cov_paths, + offset=offset, + savefig=savefig, + legend="outside", + plot_tau_m=plot_tau_m, + plot_theta_times_tau=plot_theta_times_tau, + show=show, + close=close, + fmt=fmt, + capsize=capsize, + ) + + if savefig is not None: + self.print_done( + "Tau stats plot saved to " + + f"{os.path.abspath(self.tau_stat_handler.catalogs._output)}/{savefig}", + ) + + def plot_rho_tau_fits( + self, + tomography=False, + versions=None, + colors=None, + savefig_contours=None, + savefig_xi_psf_sys=None, + savefig_format="png", + tomo_bin_label_position=None, + nsamples_to_plot=100, + offset=0, + alpha=0.3, + times_theta=False, + show=True, + close=True, + ): + """ + Plot the Rho/Tau fits and the xi_psf_sys samples. + + Parameters + ---------- + versions : list, optional + List of versions to plot. If None, all versions are plotted. + colors : list, optional + List of colors for each version. If None, default colors are used. + savefig_contours : str, optional + If provided, save the contour plot to this file. + savefig_xi_psf_sys : str, optional + If provided, save the xi_psf_sys samples plot to this file. + nsamples_to_plot : int, optional + Number of xi_psf_sys samples to plot. Default is 100. + offset : float, optional + Offset to apply to the versions for better visualisation. + alpha : float, optional + Alpha value for the xi_psf_sys samples plot. Default is 0.3. + times_theta : bool, optional + If True, plot the xi_psf_sys multiplied by theta. + show : bool, optional + If True, show the figure. + close : bool, optional + If True, close the figure after saving or showing. + """ + if savefig_format not in ["png", "pdf", "jpg", "jpeg", "svg"]: + raise ValueError( + "Invalid savefig_format. Must be one of: 'png', 'pdf', 'jpg', 'jpeg', 'svg'." + ) + + out_dir = self.rho_stat_handler.catalogs._output + + versions = versions if versions is not None else self.versions + colors = ( + colors + if colors is not None + else [self.cc[ver]["colour"] for ver in versions] + ) + + # Print the tau-statistics constraints on the PSF error model parameters. + savefig = ( + f"{out_dir}/{savefig_contours}" if savefig_contours is not None else None + ) + self.print_cyan( + "Only plot contours for the non-tomographic case. For tomography, the contours are not plotted." + ) + sample_list = [ + self.rho_tau_fits[ver]["flat_samples"]["tomo_bin_all"] for ver in versions + ] + psfleak_plots.plot_contours( + sample_list, + names=["x0", "x1", "x2"], + labels=[r"\alpha", r"\beta", r"\eta"], + savefig=savefig_contours, + legend_labels=versions, + legend_loc="upper right", + contour_colors=colors, + markers={"x0": 0, "x1": 1, "x2": 1}, + show=show, + close=close, + ) + if savefig_contours is not None: + self.print_done(f"Tau contours plot saved to {os.path.abspath(savefig)}") + + # Plot the xi_psf_sys samples and quantiles. + if tomography: + self.print_cyan("Plot the xi_psf_sys for the tomographic case.") + else: + self.print_cyan("Plot the xi_psf_sys for the non-tomographic case.") + + x_label = r"$\theta$ [arcmin]" + y_label_plus = ( + r"$\theta$" if times_theta else "" + ) + r"$\xi^{\rm PSF, sys}_+(\theta)$" + y_label_minus = ( + r"$\theta$" if times_theta else "" + ) + r"$\xi^{\rm PSF, sys}_-(\theta)$" + + if tomo_bin_label_position is None: + tomo_bin_label_position = (0.05, 0.9) if times_theta else (0.8, 0.95) + + y_scale = "linear" if times_theta else "log" + + out_path = ( + f"{out_dir}/{savefig_xi_psf_sys}_tomography_{tomography}_sample.{savefig_format}" + if savefig_xi_psf_sys is not None + else None + ) + + kwargs_x_y_plot_function = { + "nsamples_to_plot": nsamples_to_plot, + "alpha": alpha, + "offset": offset, + "times_theta": times_theta, + } + + self.plot_2pcf_tomography( + self._xi_psf_sys_sample_x_y_plot_function, + x_label, + y_label_plus, + y_label_minus, + tomo_bin_label_position, + extract_text_offset=times_theta, + add_index_version_to_kwargs=True, + x_scale="log", + y_scale=y_scale, + tomography=tomography, + versions=versions, + colors=colors, + savefig=out_path, + show=show, + close=close, + **kwargs_x_y_plot_function, + ) + + # Plot the xi_psf_sys mean and std. + x_label = r"$\theta$ [arcmin]" + y_label_plus = ( + r"$\theta$" if times_theta else "" + ) + r"$\xi^{\rm PSF, sys}_+(\theta)$" + y_label_minus = ( + r"$\theta$" if times_theta else "" + ) + r"$\xi^{\rm PSF, sys}_-(\theta)$" + + if tomo_bin_label_position is None: + tomo_bin_label_position = (0.05, 0.9) if times_theta else (0.8, 0.95) + + y_scale = "linear" if times_theta else "log" + + out_path = ( + f"{out_dir}/{savefig_xi_psf_sys}_tomography_{tomography}_mean_and_std.{savefig_format}" + if savefig_xi_psf_sys is not None + else None + ) + + kwargs_x_y_plot_function = { + "offset": offset, + "times_theta": times_theta, + "alpha": alpha, + } + + self.plot_2pcf_tomography( + self._xi_psf_sys_mean_and_std_x_y_plot_function, + x_label, + y_label_plus, + y_label_minus, + tomo_bin_label_position, + extract_text_offset=times_theta, + add_index_version_to_kwargs=True, + x_scale="log", + y_scale=y_scale, + tomography=tomography, + versions=versions, + colors=colors, + savefig=out_path, + show=show, + close=close, + **kwargs_x_y_plot_function, + ) + + """ + for mcmc_result, ver, flat_sample in zip( + self.rho_tau_fits["result_list"], + self.versions, + self.rho_tau_fits["flat_samples"], + ): + self.psf_fitter.load_rho_stat(f"rho_stats_{self.basename(ver)}.fits") + for yscale in ("linear", "log"): + out_path = os.path.abspath( + f"{out_dir}/xi_psf_sys_terms_{yscale}_{ver}.png" + ) + self.psf_fitter.plot_xi_psf_sys_terms( + ver, mcmc_result[1], out_path, yscale=yscale, show=True + ) + self.print_done( + f"{yscale}-scale xi_psf_sys terms plot saved to {out_path}" + ) + """ def plot_scale_dependent_leakage(self): if not hasattr(self.results[self.versions[0]], "r_corr_gp"): @@ -516,69 +1065,6 @@ def plot_scale_dependent_leakage(self): cs_plots.show() self.print_done(f"xi_sys_minus plot saved to {out_path}") - def calculate_objectwise_leakage(self): - if not hasattr(self.results[self.versions[0]], "alpha_leak_mean"): - self.calculate_scale_dependent_leakage() - - self.print_start("Object-wise leakage:") - mix = True - order = "lin" - for ver in self.versions: - self.print_magenta(ver) - - results_obj = self.results_objectwise[ver] - results_obj.check_params() - results_obj.update_params() - results_obj.prepare_output() - - # Skip read_data() and copy catalogue from scale leakage instance instead - # results_obj._dat = self.results[ver].dat_shear - - out_base = results_obj.get_out_base(mix, order) - out_path = f"{out_base}.pkl" - if os.path.exists(out_path): - self.print_green( - f"Skipping object-wise leakage, file {out_path} exists" - ) - results_obj.par_best_fit = leakage.read_from_file(out_path) - else: - self.print_cyan("Computing object-wise leakage regression") - - # Run - with results_obj.temporarily_read_data(): - try: - results_obj.PSF_leakage() - except KeyError as e: - print(f"{e}\nExpected key is missing from catalog.") - # remove the results object for this version - self.results_objectwise.pop(ver) - - # Gather coefficients - leakage_coeff = {} - for ver in self.results_objectwise: - results = self.results[ver] - par_best_fit = self.results_objectwise[ver].par_best_fit - - # Object-wise leakage - a11 = ufloat(par_best_fit["a11"].value, par_best_fit["a11"].stderr) - a22 = ufloat(par_best_fit["a22"].value, par_best_fit["a22"].stderr) - leakage_coeff[ver] = { - "a11": a11, - "a22": a22, - "aii_mean": 0.5 * (a11 + a22), - # Scale-dependent leakage: mean - "alpha_mean": ufloat(results.alpha_leak_mean, results.alpha_leak_std), - # Scale-dependent leakage: value at smallest scale - "alpha_1": ufloat(results.alpha_leak[0], results.sig_alpha_leak[0]), - # Scale-dependent leakage: value extrapolated to 0 using affine model - "alpha_0": ufloat( - results.alpha_affine_best_fit["c"].value, - results.alpha_affine_best_fit["c"].stderr, - ), - } - - self.leakage_coeff = leakage_coeff - def plot_objectwise_leakage(self): if not hasattr(self, "leakage_coeff"): self.calculate_objectwise_leakage() @@ -625,3 +1111,382 @@ def plot_objectwise_leakage(self): cs_plots.savefig(out_path, close_fig=False) cs_plots.show() self.print_done(f"Object-wise leakage coefficients plot saved to {out_path}") + + # --- utlility functions for plotting --- + def plot_2pcf_tomography( + self, + x_y_plot_function, + x_label, + y_label_plus, + y_label_minus, + tomo_bin_label_position, + extract_text_offset, + add_index_version_to_kwargs, + x_scale=None, + y_scale=None, + tomography=False, + versions=None, + colors=None, + savefig=None, + show=True, + close=True, + **kwargs, + ): + """ + Standard plot function for 2-point correlation functions with tomographic bins. + + Parameters + ---------- + x_y_plot_function : callable + Function to plot the x and y data. Should accept axes for `+' and `-' components, version, tomo_bin_indices, and kwargs. + x_label : str + Label for the x-axis. + y_label_plus : str + Label for the y-axis of the `+' component. + y_label_minus : str + Label for the y-axis of the `-' component. + tomo_bin_label_position : tuple + Position to place the tomographic bin labels in axes coordinates (x, y). + extract_text_offset : bool + If True, extract the y-axis offset text and include it in the y-label. + add_index_version_to_kwargs : bool + If True, add the index of the version to the kwargs for plotting. + x_scale : str, optional + Scale for the x-axis ('linear', 'log', etc.). If None, default scale is used. + y_scale : str, optional + Scale for the y-axis ('linear', 'log', etc.). If None, default scale is used. + tomography : bool, optional + If True, plot the tomographic bins. Default is False. + versions : list, optional + List of versions to plot. If None, all versions are plotted. + colors : list, optional + List of colors for each version. If None, default colors are used. + savefig : str, optional + If provided, save the figure to this file. + show : bool, optional + If True, show the figure. + close : bool, optional + If True, close the figure after saving or showing. + kwargs : dict + Additional keyword arguments to pass to the plotting function. + """ + versions = versions if versions is not None else self.versions + colors = ( + colors + if colors is not None + else [self.cc[ver]["colour"] for ver in versions] + ) + + # First get the max number of tomo bins among the versions. + tomo_bins = self._get_tomo_bins_for_versions(versions, tomography=tomography) + + max_key = max(tomo_bins, key=lambda k: len(tomo_bins[k]["ids"])) + n_tomo_bins_plot = len(tomo_bins[max_key]["ids"]) + reference_tomo_bin_pairs = tomo_bins[max_key]["pairs"] + + n_rows = n_tomo_bins_plot + n_cols = n_tomo_bins_plot + 2 + + # First start with the quantiles plot + fig, axs = plt.subplots( + n_rows, + n_cols, + figsize=(5 * n_cols, 5 * n_rows), + sharex=True, + sharey=True, + gridspec_kw={"wspace": 0, "hspace": 0}, + ) + + for idx, (ver, color) in enumerate(zip(versions, colors)): + tomo_bin_pairs = tomo_bins[ver]["pairs"] + + kwargs["color"] = color + if add_index_version_to_kwargs: + kwargs["idx"] = idx + kwargs["versions"] = versions + + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + # Plot the nsamples last samples + ax_plus = self._get_ax_plus(axs, tomo_bin_a, tomo_bin_b) + ax_minus = self._get_ax_minus(axs, tomo_bin_a, tomo_bin_b) + + # Apply the x_y_plot_function to plot the data + x_y_plot_function( + ax_plus, ax_minus, ver, tomo_bin_a, tomo_bin_b, **kwargs + ) + + # Draw to extract the y-axis text offset + fig.canvas.draw() + + # Set the visibility to false where necessary + self._set_ax_visibility_to_false(axs, n_tomo_bins_plot) + + # Set the labels and scales for the plots + for tomo_bin_a, tomo_bin_b in reference_tomo_bin_pairs: + ax_plus = self._get_ax_plus(axs, tomo_bin_a, tomo_bin_b) + ax_minus = self._get_ax_minus(axs, tomo_bin_a, tomo_bin_b) + + ax_plus.tick_params( + axis="both", + which="both", + direction="in", + bottom=True, + top=False, + labelbottom=tomo_bin_b == 1 or tomo_bin_b == "all", + left=True, + right=False, + labelleft=tomo_bin_a == 1 or tomo_bin_a == "all", + ) + if x_scale is not None: + ax_plus.set_xscale(x_scale) + + ax_plus.text( + tomo_bin_label_position[0], + tomo_bin_label_position[1], + f"{tomo_bin_a}-{tomo_bin_b}", + transform=ax_plus.transAxes, + verticalalignment="top", + bbox=dict( + boxstyle="square", + facecolor="white", + edgecolor="black", + alpha=0.8, + ), + ) + ax_plus.axhline(0, color="k", linestyle="--") + + if y_scale is not None: + ax_plus.set_yscale(y_scale) + if tomo_bin_b == 1 or tomo_bin_b == "all": + ax_plus.set_xlabel(r"$\theta$ [arcmin]") + if tomo_bin_a == 1 or tomo_bin_a == "all": + text_offset = ( + ax_plus.yaxis.get_offset_text().get_text() + if extract_text_offset + else "" + ) + ax_plus.set_ylabel(y_label_plus + text_offset) + ax_plus.yaxis.get_offset_text().set_visible( + False + ) # Hide the offset text for the plus ax + + # Move the ticks to the right for the minus ax + ax_minus.yaxis.tick_right() + ax_minus.yaxis.set_label_position("right") + ax_minus.tick_params( + axis="both", + which="both", + direction="in", + bottom=True, + top=False, + labelbottom=tomo_bin_b == n_tomo_bins_plot or tomo_bin_b == "all", + left=False, + right=True, + labelleft=False, + labelright=tomo_bin_a == 1 or tomo_bin_a == "all", + ) + if x_scale is not None: + ax_minus.set_xscale(x_scale) + ax_minus.text( + tomo_bin_label_position[0], + tomo_bin_label_position[1], + f"{tomo_bin_a}-{tomo_bin_b}", + transform=ax_minus.transAxes, + verticalalignment="top", + bbox=dict( + boxstyle="square", + facecolor="white", + edgecolor="black", + alpha=0.8, + ), + ) + ax_minus.axhline(0, color="k", linestyle="--") + if y_scale is not None: + ax_minus.set_yscale(y_scale) + if tomo_bin_b == n_tomo_bins_plot or tomo_bin_b == "all": + ax_minus.set_xlabel(x_label) + if tomo_bin_a == 1 or tomo_bin_a == "all": + text_offset = ( + ax_minus.yaxis.get_offset_text().get_text() + if extract_text_offset + else "" + ) + ax_minus.set_ylabel(y_label_minus + text_offset) + ax_minus.yaxis.get_offset_text().set_visible( + False + ) # Hide the offset text for the minus ax + + # Build the legend + handles = [] + for ver, color in zip(versions, colors): + label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver + handles.append(plt.Line2D([0], [0], color=color, lw=2, label=label)) + fig.legend( + handles=handles, + loc="upper center", + ncol=3, + frameon=False, + bbox_to_anchor=(0.5, 0.0), + ) + + if savefig is not None: + plt.savefig(savefig, dpi=300, bbox_inches="tight") + self.print_done(f"Plot saved to {os.path.abspath(savefig)}") + + if show: + plt.show() + + if close: + plt.close() + + def _xi_psf_sys_sample_x_y_plot_function( + self, + ax_plus, + ax_minus, + version, + tomo_bin_a, + tomo_bin_b, + idx, + versions, + color, + offset, + nsamples_to_plot, + times_theta, + alpha, + ): + # Load the rho-stats to compute the xi_psf_sys samples + base_rho = self.basename(version) + self.psf_fitter.load_rho_stat(f"rho_stats_{base_rho}.fits") + + # Get the angular scales for the xi_psf_sys plots + theta = self.psf_fitter.rho_stat_handler.rho_stats["theta"] + + # Add the offset to the theta values for better visualisation + jittered_theta = self._get_jittered_theta(theta, idx, len(versions), offset) + + # Get the parameters for the rho-tau fit + params = self.set_params_rho_tau( + version, self.results[version]._params, self.cc[version]["psf"] + ) + + # Get the xi_psf_sys samples + xi_psf_sys_samples_plus, xi_psf_sys_samples_minus = self.get_xi_psf_sys_samples( + version, params, tomo_bin_a, tomo_bin_b + ) + + y_plus = xi_psf_sys_samples_plus[-nsamples_to_plot:] * ( + theta if times_theta else 1 + ) + y_minus = xi_psf_sys_samples_minus[-nsamples_to_plot:] * ( + theta if times_theta else 1 + ) + + ax_plus.plot( + jittered_theta, + y_plus.T, + color=color, + alpha=alpha, + ) + + ax_minus.plot(jittered_theta, y_minus.T, color=color, alpha=alpha) + + def _xi_psf_sys_mean_and_std_x_y_plot_function( + self, + ax_plus, + ax_minus, + version, + tomo_bin_a, + tomo_bin_b, + idx, + versions, + color, + offset, + times_theta, + alpha, + ): + # Load the rho-stats to compute the xi_psf_sys mean and std. + base_rho = self.basename(version) + self.psf_fitter.load_rho_stat(f"rho_stats_{base_rho}.fits") + + # Get the angular scales for the xi_psf_sys plots + theta = self.psf_fitter.rho_stat_handler.rho_stats["theta"] + + # Add the offset to the theta values for better visualisation + jittered_theta = self._get_jittered_theta(theta, idx, len(versions), offset) + + xi_psf_sys = self.xi_psf_sys[version][ + f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}" + ] + + def plot_axis(ax, ax_type): + """Plot depending on the axis type ('plus' or 'minus').""" + ax.plot( + jittered_theta, + xi_psf_sys[f"mean_{ax_type}"] * (theta if times_theta else 1), + color=color, + ) + ax.plot( + jittered_theta, + xi_psf_sys[f"quantiles_{ax_type}"][0] * (theta if times_theta else 1), + color=color, + ) + ax.plot( + jittered_theta, + xi_psf_sys[f"quantiles_{ax_type}"][1] * (theta if times_theta else 1), + color=color, + ) + ax.fill_between( + jittered_theta, + xi_psf_sys[f"quantiles_{ax_type}"][0] * (theta if times_theta else 1), + xi_psf_sys[f"quantiles_{ax_type}"][1] * (theta if times_theta else 1), + color=color, + alpha=alpha, + ) + + # Plot the plus axis + plot_axis(ax_plus, "plus") + + # Plot the minus axis + plot_axis(ax_minus, "minus") + + def _get_jittered_theta(self, theta, idx, n_versions, offset): + """Get the jittered theta values for better visualisation.""" + theta_widths = np.diff(theta) + theta_widths = np.append(theta_widths, theta_widths[-1]) + jitter_fraction = (idx - (n_versions - 1) / 2) * offset + jittered_theta = theta + jitter_fraction * theta_widths + return jittered_theta + + def _get_ax_plus(self, axs, tomo_bin_a, tomo_bin_b): + if (tomo_bin_a == "all") ^ (tomo_bin_b == "all"): + raise ValueError( + "Invalid combination of tomographic bins: 'all' and a specific bin." + ) + + if tomo_bin_a == "all" and tomo_bin_b == "all": + return axs[0] + + else: + nrows = axs.shape[0] + return axs[nrows - tomo_bin_b, tomo_bin_a - 1] + + def _get_ax_minus(self, axs, tomo_bin_a, tomo_bin_b): + if (tomo_bin_a == "all") ^ (tomo_bin_b == "all"): + raise ValueError( + "Invalid combination of tomographic bins: 'all' and a specific bin." + ) + + if tomo_bin_a == "all" and tomo_bin_b == "all": + return axs[2] + + else: + ncols = axs.shape[1] + return axs[tomo_bin_b - 1, ncols - tomo_bin_a] + + def _set_ax_visibility_to_false(self, axs, n_tomo_bins_plot): + """Set the visibility of empty axes to False for better visualization.""" + if n_tomo_bins_plot == 1: + axs[1].set_visible(False) + else: + for i in range(n_tomo_bins_plot): + axs[i, n_tomo_bins_plot - i].set_visible(False) diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 95a720f9..9bc975cc 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -1,3 +1,4 @@ +import gc import os import time from pathlib import Path @@ -16,22 +17,10 @@ def _extract_xip(correlations): return np.array([corr.xip for corr in correlations]).flatten() -def get_params_rho_tau(cat, survey="other"): +def get_params_rho_tau(cat): # Set parameters params = {} - # TODO to yaml file - if survey == "DES": - params["patch_number"] = 120 - print("DES, jackknife patch number = 120") - elif survey == "SP_axel_v0.0": - params["patch_number"] = 120 - print("SP_Axel_v0.0, jackknife patch number =120") - elif survey == "SP_v1.4-P3" or survey == "SP_v1.4-P3_LFmask": - params["patch_number"] = 120 - print("SP_v1.4, jackknife patch number =120") - else: - params["patch_number"] = 150 params["ra_PSF_col"] = cat["psf"]["ra_col"] params["dec_PSF_col"] = cat["psf"]["dec_col"] params["e1_PSF_col"] = cat["psf"]["e1_PSF_col"] @@ -40,21 +29,18 @@ def get_params_rho_tau(cat, survey="other"): params["e2_star_col"] = cat["psf"]["e2_star_col"] params["PSF_size"] = cat["psf"]["PSF_size"] params["star_size"] = cat["psf"]["star_size"] - if survey != "DES": - params["PSF_flag"] = cat["psf"]["PSF_flag"] - params["star_flag"] = cat["psf"]["star_flag"] + params["PSF_flag"] = cat["psf"].get("PSF_flag") + params["star_flag"] = cat["psf"].get("star_flag") params["ra_units"] = "deg" params["dec_units"] = "deg" + params["patch_number"] = cat.get("patch_number", 100) # Default patch number to 100 params["ra_col"] = cat["shear"].get("ra_col", "RA") params["dec_col"] = cat["shear"].get("dec_col", "Dec") params["w_col"] = cat["shear"]["w_col"] params["e1_col"] = cat["shear"]["e1_col"] params["e2_col"] = cat["shear"]["e2_col"] - try: - params["tomo_bin_col"] = cat["shear"]["tomo_bin_col"] - except KeyError: - params["tomo_bin_col"] = None + params["tomo_bin_col"] = cat["shear"].get("tomo_bin_col") params["R11"] = cat["shear"].get("R11") params["R22"] = cat["shear"].get("R22") @@ -66,43 +52,59 @@ def get_rho_tau_w_cov( version, treecorr_config, outdir, - base, + base_rho, + base_tau, method, + mask_star=None, + mask_gal=None, cov_rho=False, - npatch=None, + ncov=100, + **kwargs, ): """Compute rho/tau statistics and, if requested, their covariance.""" if method == "th": - nbin_ang, nbin_rad = 100, 200 + nbin_ang, nbin_rad = kwargs.get("nbin_ang", 100), kwargs.get("nbin_rad", 200) + compute_minus = kwargs.get("compute_minus", True) rho_stat_handler, tau_stat_handler = get_rho_tau( config, version, treecorr_config, outdir, - base, + base_rho, + base_tau, cov_rho=cov_rho, + mask_star=mask_star, + mask_gal=mask_gal, ) get_theory_cov( config, version, treecorr_config, outdir, - base, + base_tau, nbin_ang=nbin_ang, nbin_rad=nbin_rad, + compute_minus=compute_minus, + mask_star=mask_star, + mask_gal=mask_gal, ) return rho_stat_handler, tau_stat_handler elif method == "jk": + npatch = kwargs.get("npatch", 100) return get_jackknife_cov( config, version, treecorr_config, outdir, - base, + base_rho, + base_tau, npatch=npatch, + ncov=ncov, + mask_star=mask_star, + mask_gal=mask_gal, ) elif method == "sim": - tau_cov_path = Path(outdir) / f"cov_tau_{base}_th.npy" + tau_cov_path = Path(outdir) / f"cov_tau_{base_tau}_th.npy" if tau_cov_path.exists(): print(f"Found existing covariance at {tau_cov_path}") @@ -112,8 +114,11 @@ def get_rho_tau_w_cov( version, treecorr_config, outdir, - base, + base_rho, + base_tau, cov_rho=cov_rho, + mask_star=mask_star, + mask_gal=mask_gal, ) else: raise ValueError( @@ -128,9 +133,12 @@ def get_rho_tau( version, treecorr_config, outdir, - base, + base_rho, + base_tau, + mask_star=None, + mask_gal=None, cov_rho=False, - npatch=None, + force_run=False, ): """ Compute rho and tau statistics for a given version of the catalogue. @@ -145,16 +153,28 @@ def get_rho_tau( TreeCorr configuration (must include 'min_sep', 'max_sep', and 'nbins'). outdir : str Output directory. + base_rho : str + Base name for the rho output files. + base_tau : str + Base name for the tau output files. + mask_star : array-like, optional + Boolean mask for stars. If None, no masking is applied. + mask_gal : array-like, optional + Boolean mask for galaxies. If None, no masking is applied. + cov_rho : bool, optional + If True, compute the covariance of rho statistics. + force_run : bool, optional + If True, force the computation even if output files already exist. """ - params = get_params_rho_tau(config[version], survey=version) + params = get_params_rho_tau(config[version]) print("Compute Rho and Tau statistics for the version: ", version) start_time = time.time() outdir_path = Path(outdir) - rho_path = outdir_path / f"rho_stats_{base}.fits" - catalog_id = f"{base}_jk" if cov_rho else base + rho_path = outdir_path / f"rho_stats_{base_rho}.fits" + catalog_id = f"{base_rho}_jk" if cov_rho else base_rho cov_rho_path = outdir_path / f"cov_rho_{catalog_id}.npy" if cov_rho else None rho_stat_handler = RhoStat( @@ -163,17 +183,15 @@ def get_rho_tau( rho_stats_exists = rho_path.exists() cov_exists = True if not cov_rho else cov_rho_path.exists() - need_compute = (not rho_stats_exists) or (not cov_exists) + need_compute = (not rho_stats_exists) or (not cov_exists) or force_run if need_compute: rho_stat_handler.catalogs.set_params(params, outdir) - mask = version != "DES" - rho_stat_handler.build_cat_to_compute_rho( config[version]["psf"]["path"], catalog_id=catalog_id, - mask=mask, + mask=mask_star, hdu=( config[version]["psf"]["hdu"] if config[version]["psf"]["hdu"] is not None @@ -193,7 +211,7 @@ def get_rho_tau( print(f"Skipping rho statistics computation, file {rho_path} already exists.") rho_stat_handler.load_rho_stats(rho_path.name) - tau_path = outdir_path / f"tau_stats_{base}.fits" + tau_path = outdir_path / f"tau_stats_{base_tau}.fits" tau_stat_handler = TauStat( catalogs=rho_stat_handler.catalogs, @@ -202,21 +220,19 @@ def get_rho_tau( verbose=True, ) - if tau_path.exists(): + if tau_path.exists() and not force_run: print(f"Skipping tau statistics computation, file {tau_path} already exists.") tau_stat_handler.load_tau_stats(tau_path.name) else: tau_stat_handler.catalogs.set_params(params, outdir) - mask = version != "DES" - # Build the different catalogs if necessary if f"psf_{version}" not in tau_stat_handler.catalogs.catalogs_dict.keys(): tau_stat_handler.build_cat_to_compute_tau( config[version]["psf"]["path"], cat_type="psf", catalog_id=version, - mask=mask, + mask=mask_star, hdu=( config[version]["psf"]["hdu"] if config[version]["psf"]["hdu"] is not None @@ -229,7 +245,7 @@ def get_rho_tau( config[version]["shear"]["path"], cat_type="gal", catalog_id=version, - mask=mask, + mask=mask_gal, ) # function to extract the tau_+ @@ -247,17 +263,17 @@ def get_theory_cov( base, nbin_ang=100, nbin_rad=100, + compute_minus=True, + mask_star=None, # TODO add the masking in the theory covariance + mask_gal=None, ): """ Compute an analytical estimate of the covariance matrix of rho and tau-statistics. """ - params = get_params_rho_tau(config[version], survey=version) + params = get_params_rho_tau(config[version]) info = config[version] - A = info["cov_th"]["A"] * 60 * 60 - n_e = info["cov_th"]["n_e"] - n_psf = info["cov_th"]["n_psf"] path_gal = info["shear"]["path"] path_psf = info["psf"]["path"] @@ -277,21 +293,23 @@ def get_theory_cov( path_psf=path_psf, hdu_psf=hdu_psf, treecorr_config=treecorr_config, - A=A, - n_e=n_e, - n_psf=n_psf, params=params, + mask_star=mask_star, + mask_gal=mask_gal, ) elapsed = time.time() - start_time print(f"--- Rho/tau statistics for covariance computed in {elapsed:.2f}s ---") - cov = cov_tau_th.build_cov(nbin_ang=nbin_ang, nbin_rad=nbin_rad) + cov = cov_tau_th.build_cov( + nbin_ang=nbin_ang, nbin_rad=nbin_rad, compute_minus=compute_minus + ) print(f"--- Covariance matrix assembled in {time.time() - start_time:.2f}s ---") target_cov.parent.mkdir(parents=True, exist_ok=True) np.save(target_cov, cov) print("Saved covariance matrix of version: ", version) del cov_tau_th + gc.collect() return @@ -300,20 +318,24 @@ def get_jackknife_cov( version, treecorr_config, outdir, - base, + base_rho, + base_tau, npatch, ncov=100, + mask_star=None, + mask_gal=None, + force_run=False, ): """ Compute the covariance matrix of rho and tau-statistics using the jackknife method. Also compute rho and tau-statistics. """ + # TODO: Reorganise this to avoid recomputing unnecessarily the rho-stats multiple times. + rho_filename = f"rho_stats_{base_rho}.fits" + tau_filename = f"tau_stats_{base_tau}.fits" + tau_cov_path = Path(outdir) / f"cov_tau_{base_tau}_jk.npy" - rho_filename = f"rho_stats_{base}.fits" - tau_filename = f"tau_stats_{base}.fits" - tau_cov_path = Path(outdir) / f"cov_tau_{base}_jk.npy" - - if tau_cov_path.exists(): + if tau_cov_path.exists() and not force_run: print(f"Skipping covariance computation, file {tau_cov_path} already exists.") rho_stat_handler = RhoStat( output=outdir, treecorr_config=treecorr_config, verbose=False @@ -328,13 +350,13 @@ def get_jackknife_cov( rho_path = Path(outdir) / rho_filename tau_path = Path(outdir) / tau_filename - if rho_path.exists(): + if rho_path.exists() and not force_run: rho_stat_handler.load_rho_stats(rho_path.name) - if tau_path.exists(): + if tau_path.exists() and not force_run: tau_stat_handler.load_tau_stats(tau_path.name) return rho_stat_handler, tau_stat_handler - params = get_params_rho_tau(config[version], survey=version) + params = get_params_rho_tau(config[version]) rho_stat_handler = RhoStat( output=outdir, treecorr_config=treecorr_config, verbose=False @@ -352,8 +374,8 @@ def get_jackknife_cov( tau_stat_handler.catalogs.set_params(params, outdir) for i in range(ncov): - tau_chunk = outdir + f"/cov_tau_{version}{i}.npy" - rho_chunk = outdir + f"/cov_rho_{version}{i}.npy" + tau_chunk = outdir + f"/cov_tau_{base_tau}{i}.npy" + rho_chunk = outdir + f"/cov_rho_{base_rho}{i}.npy" if not (os.path.exists(tau_chunk) and os.path.exists(rho_chunk)): print(f"Computing rho-statistics for {version} (patch {i + 1}/{ncov})") @@ -362,7 +384,7 @@ def get_jackknife_cov( rho_stat_handler.build_cat_to_compute_rho( config[version]["psf"]["path"], catalog_id=version + str(i), - mask=False, + mask=mask_star, hdu=config[version]["psf"]["hdu"], ) @@ -375,7 +397,7 @@ def get_jackknife_cov( config[version]["shear"]["path"], cat_type="gal", catalog_id=version + str(i), - mask=False, + mask=mask_gal, ) else: @@ -425,13 +447,13 @@ def get_jackknife_cov( ) tau_dict[f"gal_{version}{i + 1}"] = tau_dict.pop(f"gal_{version}{i}") - cov_tau_loc = np.zeros_like(np.load(outdir + f"/cov_tau_{version}0.npy")) - cov_rho_loc = np.zeros_like(np.load(outdir + f"/cov_rho_{version}0.npy")) + cov_tau_loc = np.zeros_like(np.load(outdir + f"/cov_tau_{base_tau}0.npy")) + cov_rho_loc = np.zeros_like(np.load(outdir + f"/cov_rho_{base_rho}0.npy")) for i in range(ncov): - cov_tau_loc += np.load(outdir + f"/cov_tau_{version}{i}.npy") - cov_rho_loc += np.load(outdir + f"/cov_rho_{version}{i}.npy") - os.remove(outdir + f"/cov_tau_{version}{i}.npy") - os.remove(outdir + f"/cov_rho_{version}{i}.npy") + cov_tau_loc += np.load(outdir + f"/cov_tau_{base_tau}{i}.npy") + cov_rho_loc += np.load(outdir + f"/cov_rho_{base_rho}{i}.npy") + os.remove(outdir + f"/cov_tau_{base_tau}{i}.npy") + os.remove(outdir + f"/cov_rho_{base_rho}{i}.npy") cov_tau = cov_tau_loc / ncov cov_rho = cov_rho_loc / ncov @@ -439,7 +461,7 @@ def get_jackknife_cov( tau_cov_path.parent.mkdir(parents=True, exist_ok=True) np.save(tau_cov_path, cov_tau) - cov_rho_path = Path(outdir) / f"cov_rho_{base}_jk.npy" + cov_rho_path = Path(outdir) / f"cov_rho_{base_rho}_jk.npy" cov_rho_path.parent.mkdir(parents=True, exist_ok=True) np.save(cov_rho_path, cov_rho) @@ -448,11 +470,13 @@ def get_jackknife_cov( def get_samples( psf_fitter, - version, - base, + base_rho, + base_tau, cov_type="jk", apply_debias=None, sampler="emcee", + nsamples=10000, + nwalkers=124, ): """Return (alpha, beta, eta) samples using ``emcee`` or least squares. @@ -460,10 +484,10 @@ def get_samples( ---------- psf_fitter : PSFFitter PSF fitter instance that provides ``load_*`` helpers. - version : str - Catalog identifier whose rho/tau statistics are sampled. - base : str - Precomputed basename (e.g. ``SP_v1.4_minsep=…``) used for filenames. + base_rho : str + Precomputed basename (e.g. ``SP_v1.4_minsep=…``) used for rho-stat filenames. + base_tau : str + Precomputed basename (e.g. ``SP_v1.4_minsep=…``) used for tau-stat filenames. cov_type : str, optional Covariance label (``'jk'``, ``'th'``, or ``'sim'``). Defaults to ``'jk'``. apply_debias : int or None, optional @@ -471,22 +495,29 @@ def get_samples( sampler : str, optional ``'emcee'`` for MCMC sampling, ``'lsq'`` for least squares (default ``'emcee'``). + nsamples : int, optional + Number of samples to draw (default ``10000``). + nwalkers : int, optional + Number of walkers for the MCMC run (default ``124``). """ if sampler == "emcee": return get_samples_emcee( psf_fitter, - version, - base, + base_rho, + base_tau, cov_type=cov_type, apply_debias=apply_debias, + nsamples=nsamples, + nwalkers=nwalkers, ) elif sampler == "lsq": return get_samples_lsq( psf_fitter, - version, - base, + base_rho, + base_tau, cov_type=cov_type, apply_debias=apply_debias, + nsamples=nsamples, ) else: raise ValueError("Sampler must be either 'emcee' or 'lsq'.") @@ -494,8 +525,8 @@ def get_samples( def get_samples_emcee( psf_fitter, - version, - base, + base_rho, + base_tau, nwalkers=124, nsamples=10000, cov_type="jk", @@ -507,10 +538,10 @@ def get_samples_emcee( ---------- psf_fitter : PSFFitter PSF fitter instance managing rho/tau statistics and covariances. - version : str - Catalog identifier whose rho/tau statistics are sampled. - base : str - Precomputed basename for locating statistics/covariance files. + base_rho : str + Precomputed basename for locating rho statistics/covariance files. + base_tau : str + Precomputed basename for locating tau statistics/covariance files. nwalkers : int, optional Number of walkers for the MCMC run (default ``124``). nsamples : int, optional @@ -521,20 +552,21 @@ def get_samples_emcee( Jackknife patch count applied during debiasing. Disabled when ``None``. """ # Load rho and tau stats - psf_fitter.load_rho_stat(f"rho_stats_{base}.fits") - psf_fitter.load_tau_stat(f"tau_stats_{base}.fits") + psf_fitter.load_rho_stat(f"rho_stats_{base_rho}.fits") + psf_fitter.load_tau_stat(f"tau_stats_{base_tau}.fits") # Check if the path exists (use base for cache key to account for different TreeCorr configs) - sample_file_path = psf_fitter.get_sample_file_path(base) + base_sample = f"{base_tau}_sampler_emcee_cov_tau_type_{cov_type}" + sample_file_path = psf_fitter.get_sample_path(base_sample) if os.path.exists(sample_file_path): print(f"Skipping sampling; {sample_file_path} exists.") - flat_samples = psf_fitter.load_samples(base) - mcmc_result, q = psf_fitter.get_mcmc_from_samples(base) + flat_samples = psf_fitter.load_samples(base_sample) + mcmc_result, q = psf_fitter.get_mcmc_from_samples(flat_samples) print(mcmc_result) # Or run MCMC else: print("MCMC sampling") - cov_filename = f"cov_tau_{base}_{cov_type}.npy" + cov_filename = f"cov_tau_{base_tau}_{cov_type}.npy" psf_fitter.load_covariance(cov_filename, cov_type="tau") debias_npatch = apply_debias if (apply_debias is not None) else None @@ -543,16 +575,17 @@ def get_samples_emcee( nsamples=nsamples, npatch=debias_npatch, apply_debias=debias_npatch is not None, - savefig="mcmc_samples_" + version + ".png", + savefig="mcmc_samples_" + base_tau + ".png", ) - psf_fitter.save_samples(flat_samples, base) + psf_fitter.save_samples(flat_samples, base_sample) return flat_samples, mcmc_result, q def get_samples_lsq( psf_fitter, - version, - base, + base_rho, + base_tau, + nsamples=10000, apply_debias=None, cov_type="jk", ): @@ -562,36 +595,39 @@ def get_samples_lsq( ---------- psf_fitter : PSFFitter PSF fitter instance managing rho/tau statistics and covariances. - version : str - Catalog identifier whose rho/tau statistics are sampled. - base : str - Precomputed basename for locating statistics/covariance files. + base_rho : str + Precomputed basename for locating rho statistics/covariance files. + base_tau : str + Precomputed basename for locating tau statistics/covariance files. apply_debias : int or None, optional Jackknife patch count applied during debiasing. Disabled when ``None``. cov_type : str, optional Covariance label (defaults to ``'jk'``). """ # Load rho and tau stats - psf_fitter.load_rho_stat(f"rho_stats_{base}.fits") - psf_fitter.load_tau_stat(f"tau_stats_{base}.fits") + psf_fitter.load_rho_stat(f"rho_stats_{base_rho}.fits") + psf_fitter.load_tau_stat(f"tau_stats_{base_tau}.fits") + base_sample = f"{base_tau}_sampler_lsq_cov_tau_type_{cov_type}" # Check if the path exists (use base for cache key to account for different TreeCorr configs) - sample_file_path = psf_fitter.get_sample_path(base) + sample_file_path = psf_fitter.get_sample_path(base_sample) if os.path.exists(sample_file_path): print(f"Skipping sampling; {sample_file_path} exists.") - flat_samples = psf_fitter.load_samples(base) + flat_samples = psf_fitter.load_samples(base_sample) mcmc_result, q = psf_fitter.get_mcmc_from_samples(flat_samples) print(mcmc_result) # Or run MCMC else: print("Least square sampling") - tau_covariance = f"cov_tau_{base}_{cov_type}.npy" - rho_covariance = f"cov_rho_{base}_jk.npy" + tau_covariance = f"cov_tau_{base_tau}_{cov_type}.npy" + rho_covariance = f"cov_rho_{base_rho}_jk.npy" psf_fitter.load_covariance(tau_covariance, cov_type="tau") psf_fitter.load_covariance(rho_covariance, cov_type="rho") debias_npatch = apply_debias if (apply_debias is not None) else None flat_samples, mcmc_result, q = psf_fitter.get_least_squares_params_samples( - npatch=debias_npatch, apply_debias=(debias_npatch is not None) + npatch=debias_npatch, + apply_debias=(debias_npatch is not None), + n_samples=nsamples, ) - psf_fitter.save_samples(flat_samples, base) + psf_fitter.save_samples(flat_samples, base_sample) return flat_samples, mcmc_result, q diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index c07afd00..eb4eb533 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -181,7 +181,7 @@ def cv(tmp_path): def cat_and_params(cv): """(catalog ndarray, params dict) for the synthetic version.""" ver = cv._test_version - params = get_params_rho_tau(cv.cc[ver], survey=ver) + params = get_params_rho_tau(cv.cc[ver]) cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) return cat_gal, params @@ -734,7 +734,7 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): # The end-to-end catalog EE matches the primitive get_pseudo_cls_catalog EE # (same computation, FITS round-trip) -- consistency, not an independent pin. cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) - params = get_params_rho_tau(cv.cc[ver], survey=ver) + params = get_params_rho_tau(cv.cc[ver]) _, cl_prim, _ = cv.get_pseudo_cls_catalog( catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" ) @@ -821,7 +821,7 @@ def test_calculate_pseudo_cl_catalog_end_to_end_tomo(cv, tmp_path): # The end-to-end catalog EE matches the primitive get_pseudo_cls_catalog EE # (same computation, FITS round-trip) -- consistency, not an independent pin. cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) - params = get_params_rho_tau(cv.cc[ver], survey=ver) + params = get_params_rho_tau(cv.cc[ver]) _, cl_prim, _ = cv.get_pseudo_cls_catalog( catalog=cat_gal, params=params, tomo_bin_a=tomo_bin_a, tomo_bin_b=tomo_bin_b ) From 18b90dbe0a06f4b583dff37f5746550b0c1404c5 Mon Sep 17 00:00:00 2001 From: Lisa Goh Date: Mon, 27 Jul 2026 15:42:48 +0100 Subject: [PATCH 055/107] Tomographic 2pcf calculation with treecorr (#297) * added tomographic 2pcf implementation * ruff pass * edits after review * parent-child functio reconciliation * pass CI tests * updated 2pcf plotting functions --- scripts/xip_xim.py | 70 +- src/sp_validation/cosmo_val/cosebis.py | 102 +- .../cosmo_val/psf_systematics.py | 225 ---- src/sp_validation/cosmo_val/pure_eb.py | 43 +- src/sp_validation/cosmo_val/real_space.py | 992 ++++++++++-------- src/sp_validation/tests/test_cosmo_val.py | 11 +- 6 files changed, 717 insertions(+), 726 deletions(-) diff --git a/scripts/xip_xim.py b/scripts/xip_xim.py index 1345f01a..763752ce 100755 --- a/scripts/xip_xim.py +++ b/scripts/xip_xim.py @@ -20,6 +20,8 @@ def params_default(): params = { "input_path": "shape_catalog_ngmix.fits", + "tomo_bin1": None, + "tomo_bin2": None, "key_ra": "RA", "key_dec": "DEC", "key_e1": "e1", @@ -30,20 +32,27 @@ def params_default(): "theta_max": 200, "n_theta": 20, "output_path": "./xip_xim.txt", + "key_tomo_bin_col": "tomo_bin_id", } short_options = { "input_path": "-i", "output_path": "-o", + "tomo_bin1": "-b1", + "tomo_bin2": "-b2", } types = { "sign_e1": "int", "sign_e2": "int", + "tomo_bin1": "int", + "tomo_bin2": "int", } help_strings = { "input_path": "shear catalogue input path, default={}", + "tomo_bin1": "First tomographic bin, default={} (non-tomographic)", + "tomo_bin2": "Second tomographic bin, default={} (non-tomographic)", "key_ra": "column name for right ascension, default={}", "key_dec": "column name for declination, default={}", "key_e1": "column name for ellipticity component 1, default={}", @@ -54,6 +63,7 @@ def params_default(): "theta_max": "maximum angular scale [arcmin], default={}", "n_theta": "number of angular scales, default={}", "output_path": "output path, default={}", + "key_tomo_bin_col": "column name for tomography bin, default={}", } return params, short_options, types, help_strings @@ -132,18 +142,52 @@ def main(argv=None): "Signs for ellipticity components =" + f" ({params['sign_e1']:+d}, {params['sign_e2']:+d})" ) - g1 = data[params["key_e1"]] * params["sign_e1"] - g2 = data[params["key_e2"]] * params["sign_e2"] - cat = treecorr.Catalog( - ra=data[params["key_ra"]], - dec=data[params["key_dec"]], - g1=g1, - g2=g2, - w=data["w"], - ra_units=coord_units, - dec_units=coord_units, - ) - + tomographic = params["tomo_bin1"] is not None and params["tomo_bin2"] is not None + cat2 = None + if tomographic: + if params["verbose"]: + print( + f"Calculating 2PCF for tomographic bins {params['tomo_bin1']} and {params['tomo_bin2']}" + ) + tomo_bin1_idx = data[params["key_tomo_bin_col"]] == params["tomo_bin1"] + tomo_bin2_idx = data[params["key_tomo_bin_col"]] == params["tomo_bin2"] + + g1 = data[params["key_e1"]] * params["sign_e1"] + g2 = data[params["key_e2"]] * params["sign_e2"] + + cat1 = treecorr.Catalog( + ra=data[params["key_ra"]][tomo_bin1_idx], + dec=data[params["key_dec"]][tomo_bin1_idx], + g1=g1[tomo_bin1_idx], + g2=g2[tomo_bin1_idx], + w=data["w"][tomo_bin1_idx], + ra_units=coord_units, + dec_units=coord_units, + ) + if params["tomo_bin1"] != params["tomo_bin2"]: + cat2 = treecorr.Catalog( + ra=data[params["key_ra"]][tomo_bin2_idx], + dec=data[params["key_dec"]][tomo_bin2_idx], + g1=g1[tomo_bin2_idx], + g2=g2[tomo_bin2_idx], + w=data["w"][tomo_bin2_idx], + ra_units=coord_units, + dec_units=coord_units, + ) + else: + if params["verbose"]: + print("Calculating non-tomographic 2PCF") + g1 = data[params["key_e1"]] * params["sign_e1"] + g2 = data[params["key_e2"]] * params["sign_e2"] + cat1 = treecorr.Catalog( + ra=data[params["key_ra"]], + dec=data[params["key_dec"]], + g1=g1, + g2=g2, + w=data["w"], + ra_units=coord_units, + dec_units=coord_units, + ) # Set treecorr config info for correlation sep_units = "arcmin" TreeCorrConfig = { @@ -159,7 +203,7 @@ def main(argv=None): # Compute correlation if params["verbose"]: print("Correlating...") - gg.process(cat, cat) + gg.process(cat1, cat2=cat2) # Write to file if params["verbose"]: diff --git a/src/sp_validation/cosmo_val/cosebis.py b/src/sp_validation/cosmo_val/cosebis.py index aa4657ba..87c36ac2 100644 --- a/src/sp_validation/cosmo_val/cosebis.py +++ b/src/sp_validation/cosmo_val/cosebis.py @@ -29,6 +29,7 @@ def calculate_cosebis( cov_path=None, scale_cuts=None, evaluate_all_scale_cuts=False, + compute_tomography=False, min_sep=None, max_sep=None, nbins=None, @@ -68,6 +69,8 @@ def calculate_cosebis( If True, evaluates COSEBIs for all possible scale cut combinations using the reporting binning parameters. Ignored when scale_cuts is provided. Defaults to False. + compute_tomography : bool, optional + If True, computes COSEBIs for all tomographic bin combinations. Defaults to False.s min_sep : float, optional Minimum separation for reporting binning (only used when evaluate_all_scale_cuts=True). Defaults to self.treecorr_config["min_sep"]. @@ -99,44 +102,61 @@ def calculate_cosebis( f"Computing fine-binned 2PCF with {nbins_int} bins from {min_sep_int} to " f"{max_sep_int} arcmin" ) - gg = self.calculate_2pcf(version, npatch=npatch, **treecorr_config) - if scale_cuts is not None: - # Explicit scale cuts provided - print(f"Evaluating {len(scale_cuts)} explicit scale cuts") - results = calculate_cosebis( - gg=gg, nmodes=nmodes, scale_cuts=scale_cuts, cov_path=cov_path - ) - elif evaluate_all_scale_cuts: - # Use reporting binning parameters or inherit from class config - binning = self._binning(min_sep, max_sep, nbins) - min_sep, max_sep, nbins = ( - binning["min_sep"], - binning["max_sep"], - binning["nbins"], - ) + ggs = self.calculate_2pcf_version( + version, + npatch=npatch, + compute_tomography=compute_tomography, + **treecorr_config, + ) + results = { + bin_key: None for bin_key in ggs.keys() + } # Initialize results dictionary - # Generate scale cuts using np.geomspace (no TreeCorr needed) - bin_edges = np.geomspace(min_sep, max_sep, nbins + 1) - generated_cuts = [ - (bin_edges[start], bin_edges[stop]) - for start in range(nbins) - for stop in range(start + 1, nbins + 1) - ] + for bin_key in ggs.keys(): + # LG TO-DO: account for tomographic scale-cuts + if scale_cuts is not None: + # Explicit scale cuts provided + print(f"Evaluating {len(scale_cuts)} explicit scale cuts") + results[bin_key] = calculate_cosebis( + gg=ggs[bin_key], + nmodes=nmodes, + scale_cuts=scale_cuts, + cov_path=cov_path, + ) + elif evaluate_all_scale_cuts: + # Use reporting binning parameters or inherit from class config + binning = self._binning(min_sep, max_sep, nbins) + min_sep, max_sep, nbins = ( + binning["min_sep"], + binning["max_sep"], + binning["nbins"], + ) - print(f"Evaluating {len(generated_cuts)} scale cut combinations") + # Generate scale cuts using np.geomspace (no TreeCorr needed) + bin_edges = np.geomspace(min_sep, max_sep, nbins + 1) + generated_cuts = [ + (bin_edges[start], bin_edges[stop]) + for start in range(nbins) + for stop in range(start + 1, nbins + 1) + ] - # Call b_modes function with scale cuts list - results = calculate_cosebis( - gg=gg, nmodes=nmodes, scale_cuts=generated_cuts, cov_path=cov_path - ) - else: - # Single scale cut behavior: use full range - results = calculate_cosebis( - gg=gg, nmodes=nmodes, scale_cuts=None, cov_path=cov_path - ) - # Extract single results dict from scale_cuts dictionary - results = next(iter(results.values())) + print(f"Evaluating {len(generated_cuts)} scale cut combinations") + + # Call b_modes function with scale cuts list + results[bin_key] = calculate_cosebis( + gg=ggs[bin_key], + nmodes=nmodes, + scale_cuts=generated_cuts, + cov_path=cov_path, + ) + else: + # Single scale cut behavior: use full range + results[bin_key] = calculate_cosebis( + gg=ggs[bin_key], nmodes=nmodes, scale_cuts=None, cov_path=cov_path + ) + # Extract single results dict from scale_cuts dictionary + results[bin_key] = next(iter(results[bin_key].values())) return results @@ -152,6 +172,7 @@ def plot_cosebis( cov_path=None, scale_cuts=None, # Explicit scale cuts evaluate_all_scale_cuts=False, # Grid-based scale cuts + compute_tomography=False, min_sep=None, max_sep=None, nbins=None, # Reporting binning @@ -218,7 +239,7 @@ def plot_cosebis( # Get or calculate results for this version if results is None: # Calculate COSEBIs using instance method - results = self.calculate_cosebis( + results_tomo = self.calculate_cosebis( version, min_sep_int=min_sep_int, max_sep_int=max_sep_int, @@ -228,10 +249,16 @@ def plot_cosebis( cov_path=cov_path, scale_cuts=scale_cuts, evaluate_all_scale_cuts=evaluate_all_scale_cuts, + compute_tomography=False, # LG: Hardcoded to False for now min_sep=min_sep, max_sep=max_sep, nbins=nbins, ) + results = results_tomo[ + "tomo_bin_all_tomo_bin_all" + ] # LG: Extract non-tomographic results + elif isinstance(results, dict) and "tomo_bin_all_tomo_bin_all" in results: + results = results["tomo_bin_all_tomo_bin_all"] # Generate plots using specialized plotting functions # Extract single result for plotting if multiple scale cuts were evaluated @@ -265,9 +292,12 @@ def plot_cosebis( if multiple_scale_cuts and len(results) > 1: # Create temporary gg object with correct binning for mapping treecorr_config_temp = self._binning(min_sep, max_sep, nbins) - gg_temp = self.calculate_2pcf( + ggs_temp = self.calculate_2pcf_version( version, npatch=npatch, **treecorr_config_temp ) + gg_temp = ggs_temp[ + "tomo_bin_all_tomo_bin_all" + ] # LG: Extract non-tomographic result plot_cosebis_scale_cut_heatmap( results, diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 6765123e..465aa937 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -1113,231 +1113,6 @@ def plot_objectwise_leakage(self): self.print_done(f"Object-wise leakage coefficients plot saved to {out_path}") # --- utlility functions for plotting --- - def plot_2pcf_tomography( - self, - x_y_plot_function, - x_label, - y_label_plus, - y_label_minus, - tomo_bin_label_position, - extract_text_offset, - add_index_version_to_kwargs, - x_scale=None, - y_scale=None, - tomography=False, - versions=None, - colors=None, - savefig=None, - show=True, - close=True, - **kwargs, - ): - """ - Standard plot function for 2-point correlation functions with tomographic bins. - - Parameters - ---------- - x_y_plot_function : callable - Function to plot the x and y data. Should accept axes for `+' and `-' components, version, tomo_bin_indices, and kwargs. - x_label : str - Label for the x-axis. - y_label_plus : str - Label for the y-axis of the `+' component. - y_label_minus : str - Label for the y-axis of the `-' component. - tomo_bin_label_position : tuple - Position to place the tomographic bin labels in axes coordinates (x, y). - extract_text_offset : bool - If True, extract the y-axis offset text and include it in the y-label. - add_index_version_to_kwargs : bool - If True, add the index of the version to the kwargs for plotting. - x_scale : str, optional - Scale for the x-axis ('linear', 'log', etc.). If None, default scale is used. - y_scale : str, optional - Scale for the y-axis ('linear', 'log', etc.). If None, default scale is used. - tomography : bool, optional - If True, plot the tomographic bins. Default is False. - versions : list, optional - List of versions to plot. If None, all versions are plotted. - colors : list, optional - List of colors for each version. If None, default colors are used. - savefig : str, optional - If provided, save the figure to this file. - show : bool, optional - If True, show the figure. - close : bool, optional - If True, close the figure after saving or showing. - kwargs : dict - Additional keyword arguments to pass to the plotting function. - """ - versions = versions if versions is not None else self.versions - colors = ( - colors - if colors is not None - else [self.cc[ver]["colour"] for ver in versions] - ) - - # First get the max number of tomo bins among the versions. - tomo_bins = self._get_tomo_bins_for_versions(versions, tomography=tomography) - - max_key = max(tomo_bins, key=lambda k: len(tomo_bins[k]["ids"])) - n_tomo_bins_plot = len(tomo_bins[max_key]["ids"]) - reference_tomo_bin_pairs = tomo_bins[max_key]["pairs"] - - n_rows = n_tomo_bins_plot - n_cols = n_tomo_bins_plot + 2 - - # First start with the quantiles plot - fig, axs = plt.subplots( - n_rows, - n_cols, - figsize=(5 * n_cols, 5 * n_rows), - sharex=True, - sharey=True, - gridspec_kw={"wspace": 0, "hspace": 0}, - ) - - for idx, (ver, color) in enumerate(zip(versions, colors)): - tomo_bin_pairs = tomo_bins[ver]["pairs"] - - kwargs["color"] = color - if add_index_version_to_kwargs: - kwargs["idx"] = idx - kwargs["versions"] = versions - - for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: - # Plot the nsamples last samples - ax_plus = self._get_ax_plus(axs, tomo_bin_a, tomo_bin_b) - ax_minus = self._get_ax_minus(axs, tomo_bin_a, tomo_bin_b) - - # Apply the x_y_plot_function to plot the data - x_y_plot_function( - ax_plus, ax_minus, ver, tomo_bin_a, tomo_bin_b, **kwargs - ) - - # Draw to extract the y-axis text offset - fig.canvas.draw() - - # Set the visibility to false where necessary - self._set_ax_visibility_to_false(axs, n_tomo_bins_plot) - - # Set the labels and scales for the plots - for tomo_bin_a, tomo_bin_b in reference_tomo_bin_pairs: - ax_plus = self._get_ax_plus(axs, tomo_bin_a, tomo_bin_b) - ax_minus = self._get_ax_minus(axs, tomo_bin_a, tomo_bin_b) - - ax_plus.tick_params( - axis="both", - which="both", - direction="in", - bottom=True, - top=False, - labelbottom=tomo_bin_b == 1 or tomo_bin_b == "all", - left=True, - right=False, - labelleft=tomo_bin_a == 1 or tomo_bin_a == "all", - ) - if x_scale is not None: - ax_plus.set_xscale(x_scale) - - ax_plus.text( - tomo_bin_label_position[0], - tomo_bin_label_position[1], - f"{tomo_bin_a}-{tomo_bin_b}", - transform=ax_plus.transAxes, - verticalalignment="top", - bbox=dict( - boxstyle="square", - facecolor="white", - edgecolor="black", - alpha=0.8, - ), - ) - ax_plus.axhline(0, color="k", linestyle="--") - - if y_scale is not None: - ax_plus.set_yscale(y_scale) - if tomo_bin_b == 1 or tomo_bin_b == "all": - ax_plus.set_xlabel(r"$\theta$ [arcmin]") - if tomo_bin_a == 1 or tomo_bin_a == "all": - text_offset = ( - ax_plus.yaxis.get_offset_text().get_text() - if extract_text_offset - else "" - ) - ax_plus.set_ylabel(y_label_plus + text_offset) - ax_plus.yaxis.get_offset_text().set_visible( - False - ) # Hide the offset text for the plus ax - - # Move the ticks to the right for the minus ax - ax_minus.yaxis.tick_right() - ax_minus.yaxis.set_label_position("right") - ax_minus.tick_params( - axis="both", - which="both", - direction="in", - bottom=True, - top=False, - labelbottom=tomo_bin_b == n_tomo_bins_plot or tomo_bin_b == "all", - left=False, - right=True, - labelleft=False, - labelright=tomo_bin_a == 1 or tomo_bin_a == "all", - ) - if x_scale is not None: - ax_minus.set_xscale(x_scale) - ax_minus.text( - tomo_bin_label_position[0], - tomo_bin_label_position[1], - f"{tomo_bin_a}-{tomo_bin_b}", - transform=ax_minus.transAxes, - verticalalignment="top", - bbox=dict( - boxstyle="square", - facecolor="white", - edgecolor="black", - alpha=0.8, - ), - ) - ax_minus.axhline(0, color="k", linestyle="--") - if y_scale is not None: - ax_minus.set_yscale(y_scale) - if tomo_bin_b == n_tomo_bins_plot or tomo_bin_b == "all": - ax_minus.set_xlabel(x_label) - if tomo_bin_a == 1 or tomo_bin_a == "all": - text_offset = ( - ax_minus.yaxis.get_offset_text().get_text() - if extract_text_offset - else "" - ) - ax_minus.set_ylabel(y_label_minus + text_offset) - ax_minus.yaxis.get_offset_text().set_visible( - False - ) # Hide the offset text for the minus ax - - # Build the legend - handles = [] - for ver, color in zip(versions, colors): - label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver - handles.append(plt.Line2D([0], [0], color=color, lw=2, label=label)) - fig.legend( - handles=handles, - loc="upper center", - ncol=3, - frameon=False, - bbox_to_anchor=(0.5, 0.0), - ) - - if savefig is not None: - plt.savefig(savefig, dpi=300, bbox_inches="tight") - self.print_done(f"Plot saved to {os.path.abspath(savefig)}") - - if show: - plt.show() - - if close: - plt.close() def _xi_psf_sys_sample_x_y_plot_function( self, diff --git a/src/sp_validation/cosmo_val/pure_eb.py b/src/sp_validation/cosmo_val/pure_eb.py index 7524074e..f585b0e3 100644 --- a/src/sp_validation/cosmo_val/pure_eb.py +++ b/src/sp_validation/cosmo_val/pure_eb.py @@ -29,6 +29,7 @@ def calculate_pure_eb( max_sep_int=300, nbins_int=100, npatch=256, + compute_tomography=False, var_method="jackknife", cov_path_int=None, cosmo_cov=None, @@ -61,13 +62,15 @@ def calculate_pure_eb( npatch : int, optional Number of patches for the jackknife or bootstrap resampling. Defaults to the value in self.npatch if not provided. + compute_tomography : bool, optional + Whether to compute tomographic (cross-bin) pure E/B modes. Defaults to False. var_method : str, optional Variance estimation method. Defaults to "jackknife". cov_path_int : str, optional Path to the covariance matrix for the reporting binning. Replaces the treecorr covariance matrix if provided, meaning that var_method has no effect on the results although it is still passed to - CosmologyValidation.calculate_2pcf. + CosmologyValidation.calculate_2pcf_version. cosmo_cov : pyccl.Cosmology, optional Cosmology object to use for theoretical xi+/xi- predictions in the semi-analytical covariance calculation. Defaults to self.cosmo if not @@ -109,10 +112,21 @@ def calculate_pure_eb( treecorr_config_int = self._binning(min_sep_int, max_sep_int, nbins_int) # Calculate correlation functions - gg = self.calculate_2pcf(version, npatch=npatch, **treecorr_config) - gg_int = self.calculate_2pcf(version, npatch=npatch, **treecorr_config_int) + ggs = self.calculate_2pcf_version( + version, + npatch=npatch, + compute_tomography=compute_tomography, + **treecorr_config, + ) + ggs_int = self.calculate_2pcf_version( + version, + npatch=npatch, + compute_tomography=compute_tomography, + **treecorr_config_int, + ) # Get redshift distribution if using analytic covariance + # LG TO-DO: check tomographic redshift distribution handling z_dist = ( np.column_stack(self.get_redshift(version)) if cov_path_int is not None @@ -120,15 +134,20 @@ def calculate_pure_eb( ) # Delegate to b_modes module - results = calculate_pure_eb_correlation( - gg=gg, - gg_int=gg_int, - var_method=var_method, - cov_path_int=cov_path_int, - cosmo_cov=cosmo_cov, - n_samples=n_samples, - z_dist=z_dist, - ) + results = { + bin_key: None for bin_key in ggs.keys() + } # Initialize results dictionary + + for bin_key in ggs.keys(): + results[bin_key] = calculate_pure_eb_correlation( + gg=ggs[bin_key], + gg_int=ggs_int[bin_key], + var_method=var_method, + cov_path_int=cov_path_int, + cosmo_cov=cosmo_cov, + n_samples=n_samples, + z_dist=z_dist, + ) return results diff --git a/src/sp_validation/cosmo_val/real_space.py b/src/sp_validation/cosmo_val/real_space.py index 76d05d0a..96b7a077 100644 --- a/src/sp_validation/cosmo_val/real_space.py +++ b/src/sp_validation/cosmo_val/real_space.py @@ -9,394 +9,174 @@ import os import matplotlib.pyplot as plt -import matplotlib.ticker as mticker import numpy as np import treecorr from astropy.io import fits -from cs_util import plots as cs_plots class RealSpaceMixin: - def calculate_2pcf(self, ver, npatch=None, save_fits=False, **treecorr_config): + def calculate_2pcf_version( + self, + ver, + npatch=None, + compute_tomography=False, + **treecorr_config, + ): """ - Calculate the two-point correlation function (2PCF) ξ± for a given catalog + Calculate the two-point correlation function (2PCF) ξ± for a single catalog version with TreeCorr. + This is the per-version child function. Use :meth:`calculate_2pcf` to run over every + version in ``self.versions`` in one call. + By default the class instance's `npatch` and `treecorr_config` entries are - used to - initialize the TreeCorr Catalog and GGCorrelation objects, but may be - overridden - by passing keyword arguments. + used to initialize the TreeCorr Catalog and GGCorrelation objects, but may + be overridden by passing keyword arguments. Parameters: ver (str): The catalog version to process. - npatch (int, optional): The number of patches to use for the calculation. - Defaults to the instance's `npatch` attribute. + npatch (int, optional): The number of patches to use for the + calculation. Defaults to the instance's `npatch` attribute. - save_fits (bool, optional): Whether to save the ξ± results to FITS files. - Defaults to False. + compute_tomography (bool, optional): Whether to compute tomographic + correlations. Defaults to False. - **treecorr_config: Additional TreeCorr configuration parameters that will - override the instance's default `treecorr_config`. For example, `min_sep=1`. + **treecorr_config: Additional TreeCorr configuration parameters that + will override the instance's default `treecorr_config`. For example, + `min_sep=1`. Returns: - treecorr.GGCorrelation: The TreeCorr GGCorrelation object containing the - computed 2PCF results. - - Notes: - - If the output file for the given configuration already exists, the - calculation is skipped, and the results are loaded from the file. - - If a patch file for the given configuration does not exist, it is - created during the process. - - FITS files for ξ+ and ξ− are saved with additional metadata in their - headers if `save_fits` is True. + dict: Mapping of ``"tomo_bin_{b1}_tomo_bin_{b2}"`` to the corresponding + treecorr.GGCorrelation object. For the non-tomographic case the single + key is ``"tomo_bin_all_tomo_bin_all"``. """ - self.print_magenta(f"Computing {ver} ξ±") - npatch = npatch or self.npatch treecorr_config = { **self._binning(**treecorr_config), "var_method": "jackknife" if int(npatch) > 1 else "shot", } - gg = treecorr.GGCorrelation(treecorr_config) + if compute_tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError(f"Version {ver} does not have tomography information.") + self.print_magenta( + f"Computing tomographic ξ± for {ver} with {len(tomo_bin_pairs)} bins." + ) + else: + self.print_magenta(f"Computing non-tomographic ξ± for {ver}.") + tomo_bin_pairs = [("all", "all")] - # If the output file already exists, skip the calculation - out_fname = self._output_path( - f"{ver}_xi_minsep={treecorr_config['min_sep']}_maxsep={treecorr_config['max_sep']}_nbins={treecorr_config['nbins']}_npatch={npatch}.txt" - ) + ggs = {f"tomo_bin_{b1}_tomo_bin_{b2}": None for b1, b2 in tomo_bin_pairs} - if os.path.exists(out_fname): - self.print_done(f"Skipping 2PCF calculation, {out_fname} exists") - gg.read(out_fname) + # LG TO-DO: Change to sacc_io method - else: - # Load data and create a catalog - with self.results[ver].temporarily_read_data(): - g1, g2 = self._calibrated_g(ver) - w = self._read_shear_cols(ver, "w_col") + patch_file = self._output_path(f"{ver}_patches_npatch={npatch}.dat") - # Use patch file if it exists - patch_file = self._output_path(f"{ver}_patches_npatch={npatch}.dat") + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + with self.results[ver].temporarily_read_data(): + g1, g2 = self._calibrated_g(ver) + w = self._read_shear_cols(ver, "w_col") - cat_gal = treecorr.Catalog( - ra=self.results[ver].dat_shear["RA"], - dec=self.results[ver].dat_shear["Dec"], - g1=g1, - g2=g2, - w=w, + for bin1, bin2 in tomo_bin_pairs: + gg = treecorr.GGCorrelation(treecorr_config) + + # Load data and create a catalog + if bin1 == "all" and bin2 == "all": + mask_bin1 = np.ones(len(g1), dtype=bool) + else: + mask_bin1 = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] == bin1 + + cat_gal1 = treecorr.Catalog( + ra=cat_gal["RA"][mask_bin1], + dec=cat_gal["Dec"][mask_bin1], + g1=g1[mask_bin1], + g2=g2[mask_bin1], + w=w[mask_bin1], + ra_units=self.treecorr_config["ra_units"], + dec_units=self.treecorr_config["dec_units"], + npatch=npatch, + patch_centers=patch_file if os.path.exists(patch_file) else None, + ) + cat_gal2 = None + + if bin1 != bin2: + mask_bin2 = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] == bin2 + cat_gal2 = treecorr.Catalog( + ra=cat_gal["RA"][mask_bin2], + dec=cat_gal["Dec"][mask_bin2], + g1=g1[mask_bin2], + g2=g2[mask_bin2], + w=w[mask_bin2], ra_units=self.treecorr_config["ra_units"], dec_units=self.treecorr_config["dec_units"], npatch=npatch, patch_centers=patch_file if os.path.exists(patch_file) else None, ) - # If no patch file exists, save the current patches - if not os.path.exists(patch_file): - cat_gal.write_patch_centers(patch_file) + # If no patch file exists, save the current patches + if not os.path.exists(patch_file): + cat_gal1.write_patch_centers(patch_file) # Process the catalog & write the correlation functions - gg.process(cat_gal) - gg.write(out_fname, write_patch_results=True, write_cov=True) - - # Save xi_p and xi_m results to fits file - # (moved outside so it runs even if txt exists) - if save_fits: - lst = np.arange(1, treecorr_config["nbins"] + 1) - - col1 = fits.Column(name="BIN1", format="K", array=np.ones(len(lst))) - col2 = fits.Column(name="BIN2", format="K", array=np.ones(len(lst))) - col3 = fits.Column(name="ANGBIN", format="K", array=lst) - col4 = fits.Column(name="VALUE", format="D", array=gg.xip) - col5 = fits.Column(name="ANG", format="D", unit="arcmin", array=gg.meanr) - coldefs = fits.ColDefs([col1, col2, col3, col4, col5]) - xiplus_hdu = fits.BinTableHDU.from_columns(coldefs, name="XI_PLUS") - - col4 = fits.Column(name="VALUE", format="D", array=gg.xim) - coldefs = fits.ColDefs([col1, col2, col3, col4, col5]) - ximinus_hdu = fits.BinTableHDU.from_columns(coldefs, name="XI_MINUS") - - # append xi_plus header info - xiplus_dict = { - "2PTDATA": "T", - "QUANT1": "G+R", - "QUANT2": "G+R", - "KERNEL_1": "NZ_SOURCE", - "KERNEL_2": "NZ_SOURCE", - "WINDOWS": "SAMPLE", - } - for key in xiplus_dict: - xiplus_hdu.header[key] = xiplus_dict[key] - - col1 = fits.Column(name="BIN1", format="K", array=np.ones(len(lst))) - col2 = fits.Column(name="BIN2", format="K", array=np.ones(len(lst))) - col3 = fits.Column(name="ANGBIN", format="K", array=lst) - col4 = fits.Column(name="VALUE", format="D", array=gg.xip) - col5 = fits.Column(name="ANG", format="D", unit="arcmin", array=gg.rnom) - coldefs = fits.ColDefs([col1, col2, col3, col4, col5]) - xiplus_hdu = fits.BinTableHDU.from_columns(coldefs, name="XI_PLUS") - - col4 = fits.Column(name="VALUE", format="D", array=gg.xim) - coldefs = fits.ColDefs([col1, col2, col3, col4, col5]) - ximinus_hdu = fits.BinTableHDU.from_columns(coldefs, name="XI_MINUS") - - # append xi_plus header info - xiplus_dict = { - "2PTDATA": "T", - "QUANT1": "G+R", - "QUANT2": "G+R", - "KERNEL_1": "NZ_SOURCE", - "KERNEL_2": "NZ_SOURCE", - "WINDOWS": "SAMPLE", - } - for key in xiplus_dict: - xiplus_hdu.header[key] = xiplus_dict[key] - # Use same naming format as txt output - fits_base = out_fname.replace(".txt", "").replace("_xi_", "_") - xiplus_hdu.writeto( - f"{fits_base.replace(ver, f'xi_plus_{ver}')}.fits", - overwrite=True, - ) + gg.process(cat_gal1, cat2=cat_gal2) + ggs[f"tomo_bin_{bin1}_tomo_bin_{bin2}"] = gg - # append xi_minus header info - ximinus_dict = {**xiplus_dict, "QUANT1": "G-R", "QUANT2": "G-R"} - for key in ximinus_dict: - ximinus_hdu.header[key] = ximinus_dict[key] - ximinus_hdu.writeto( - f"{fits_base.replace(ver, f'xi_minus_{ver}')}.fits", - overwrite=True, - ) + self.print_done(f"Done 2PCF for {ver}.") - # Add correlation object to class - if not hasattr(self, "cat_ggs"): - self.cat_ggs = {} - self.cat_ggs[ver] = gg + return ggs - self.print_done("Done 2PCF") + def calculate_2pcf( + self, + npatch=None, + compute_tomography=False, + **treecorr_config, + ): + """ + Calculate the 2PCF ξ± for every catalog version in ``self.versions``. - return gg + Parent function that iterates over ``self.versions`` and delegates the + per-version computation to :meth:`calculate_2pcf_version`. Results are stored + in ``self.cat_ggs`` keyed by version. - def plot_2pcf(self): - # Plot of n_pairs - plt.subplots(ncols=1, nrows=1) - for ver in self.versions: - self.calculate_2pcf(ver) - plt.plot( - self.cat_ggs[ver].meanr, - self.cat_ggs[ver].npairs, - label=ver, - ls=self.cc[ver]["ls"], - color=self.cc[ver]["colour"], - ) - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.ylabel(r"$n_{\rm pair}$") - plt.legend() - out_path = self._output_path("n_pair.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"n_pair plot saved to {out_path}") - - # Plot of xi_+ - plt.subplots(ncols=1, nrows=1, figsize=(7, 7)) - for idx, ver in enumerate(self.versions): - plt.errorbar( - self.cat_ggs[ver].meanr * cs_plots.dx(idx, fx=1.05, nx=len(ver)), - self.cat_ggs[ver].xip, - yerr=np.sqrt(self.cat_ggs[ver].varxip), - label=ver, - ls=self.cc[ver]["ls"], - color=self.cc[ver]["colour"], - ) - plt.xscale("log") - plt.yscale("log") - plt.legend() - plt.ticklabel_format(axis="y") - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.xlim([self.theta_min_plot, self.theta_max_plot]) - plt.ylabel(r"$\xi_+(\theta)$") - out_path = self._output_path("xi_p.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_plus plot saved to {out_path}") - - # Plot of xi_- - plt.subplots(ncols=1, nrows=1, figsize=(7, 7)) - for idx, ver in enumerate(self.versions): - plt.errorbar( - self.cat_ggs[ver].meanr * cs_plots.dx(idx, fx=1.05, nx=len(ver)), - self.cat_ggs[ver].xim, - yerr=np.sqrt(self.cat_ggs[ver].varxim), - label=ver, - ls=self.cc[ver]["ls"], - color=self.cc[ver]["colour"], - ) - plt.xscale("log") - plt.yscale("log") - plt.legend() - plt.ticklabel_format(axis="y") - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.xlim([self.theta_min_plot, self.theta_max_plot]) - plt.ylabel(r"$\xi_-(\theta)$") - out_path = self._output_path("xi_m.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_minus plot saved to {out_path}") - - # Plot of xi_+(theta) * theta - plt.subplots(ncols=1, nrows=1, figsize=(7, 7)) - for idx, ver in enumerate(self.versions): - plt.errorbar( - self.cat_ggs[ver].meanr, - self.cat_ggs[ver].xip * self.cat_ggs[ver].meanr, - yerr=np.sqrt(self.cat_ggs[ver].varxip) * self.cat_ggs[ver].meanr, - label=ver, - ls=self.cc[ver]["ls"], - color=self.cc[ver]["colour"], - ) - plt.xscale("log") - plt.legend() - plt.ticklabel_format(axis="y") - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.xlim([self.theta_min_plot, self.theta_max_plot]) - plt.ylabel(r"$\theta \xi_+(\theta)$") - out_path = self._output_path("xi_p_theta.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_plus_theta plot saved to {out_path}") - - # Plot of xi_- * theta - plt.subplots(ncols=1, nrows=1, figsize=(7, 7)) - for idx, ver in enumerate(self.versions): - plt.errorbar( - self.cat_ggs[ver].meanr * cs_plots.dx(idx, len(ver)), - self.cat_ggs[ver].xim * self.cat_ggs[ver].meanr, - yerr=np.sqrt(self.cat_ggs[ver].varxim) * self.cat_ggs[ver].meanr, - label=ver, - ls=self.cc[ver]["ls"], - color=self.cc[ver]["colour"], - ) - plt.xscale("log") - plt.legend() - plt.ticklabel_format(axis="y") - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.xlim([self.theta_min_plot, self.theta_max_plot]) - plt.ylabel(r"$\theta \xi_-(\theta)$") - out_path = self._output_path("xi_m_theta.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_minus_theta plot saved to {out_path}") - - # Plot of xi_+ with and without xi_psf_sys - # but skip if xi_psf_sys is not calculated since that takes forever - if hasattr(self, "_xi_psf_sys"): - for idx, ver in enumerate(self.versions): - plt.subplots(ncols=1, nrows=1, figsize=(7, 7)) - plt.errorbar( - self.cat_ggs[ver].meanr * cs_plots.dx(idx, len(ver)), - self.cat_ggs[ver].xip, - yerr=np.sqrt(self.cat_ggs[ver].varxim), - label=r"$\xi_+$", - ls="solid", - color="green", - ) - plt.errorbar( - self.cat_ggs[ver].meanr * cs_plots.dx(idx, len(ver)), - self.xi_psf_sys[ver]["mean"], - yerr=np.sqrt(self.xi_psf_sys[ver]["var"]), - label=r"$\xi^{\rm psf}_{+, {\rm sys}}$", - ls="dotted", - color="red", - ) - plt.errorbar( - self.cat_ggs[ver].meanr * cs_plots.dx(idx, len(ver)), - self.cat_ggs[ver].xip + self.xi_psf_sys[ver]["mean"], - yerr=np.sqrt( - self.cat_ggs[ver].varxip + self.xi_psf_sys[ver]["var"] - ), - label=r"$\xi_+ + \xi^{\rm psf}_{+, {\rm sys}}$", - ls="dashdot", - color="magenta", - ) + Parameters: + npatch (int, optional): Number of patches to use; defaults to the + instance's `npatch` attribute. - plt.xscale("log") - plt.yscale("log") - plt.legend() - plt.ticklabel_format(axis="y") - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.xlim([self.theta_min_plot, self.theta_max_plot]) - plt.ylim(1e-8, 5e-4) - plt.ylabel(r"$\xi_+(\theta)$") - out_path = self._output_path(f"xi_p_xi_psf_sys_{ver}.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_plus_xi_psf_sys {ver} plot saved to {out_path}") - - def plot_ratio_xi_sys_xi(self, threshold=0.1, offset=0.02): - - plt.subplots(ncols=1, nrows=1, figsize=(10, 7)) - - for idx, ver in enumerate(self.versions): - self.calculate_2pcf(ver) - xi_psf_sys = self.xi_psf_sys[ver] - gg = self.cat_ggs[ver] - - ratio = xi_psf_sys["mean"] / gg.xip - ratio_err = np.sqrt( - (np.sqrt(xi_psf_sys["var"]) / gg.xip) ** 2 - + (xi_psf_sys["mean"] * np.sqrt(gg.varxip) / gg.xip**2) ** 2 - ) + compute_tomography (bool, optional): Whether to compute tomographic + correlations. Defaults to False. - theta = gg.meanr - jittered_theta = theta * (1 + idx * offset) - - plt.errorbar( - jittered_theta, - ratio, - yerr=ratio_err, - label=ver, - ls=self.cc[ver]["ls"], - color=self.cc[ver]["colour"], - fmt=self.cc[ver].get("marker", None), - capsize=5, + **treecorr_config: Additional TreeCorr configuration parameters passed + through to each per-version call. + + Returns: + dict: ``self.cat_ggs``, mapping each version to its + ``{"tomo_bin_{b1}_tomo_bin_{b2}": treecorr.GGCorrelation}`` dict. + """ + self.cat_ggs = {} + for ver in self.versions: + self.cat_ggs[ver] = self.calculate_2pcf_version( + ver, + npatch=npatch, + compute_tomography=compute_tomography, + **treecorr_config, ) - plt.fill_between( - [self.theta_min_plot, self.theta_max_plot], - -threshold, - +threshold, - color="black", - alpha=0.1, - label=f"{threshold:.0%} threshold", - ) - plt.plot( - [self.theta_min_plot, self.theta_max_plot], - [threshold, threshold], - ls="dashed", - color="black", - ) - plt.plot( - [self.theta_min_plot, self.theta_max_plot], - [-threshold, -threshold], - ls="dashed", - color="black", - ) - plt.xscale("log") - plt.xlabel(rf"$\theta$ [{self.treecorr_config['sep_units']}]") - plt.ylabel(r"$\xi^{\rm psf}_{+, {\rm sys}} / \xi_+$") - plt.gca().yaxis.set_major_formatter(mticker.PercentFormatter(xmax=1)) - plt.legend() - plt.title("Ratio of PSF systematics to cosmic shear signal") - out_path = self._output_path("ratio_xi_sys_xi.png") - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - print(f"Ratio of xi_psf_sys to xi plot saved to {out_path}") + # LG TO-DO: No longer writing out text file, change to sacc_io method + + return self.cat_ggs def calculate_aperture_mass_dispersion( - self, theta_min=0.3, theta_max=200, nbins=500, nbins_map=15, npatch=25 + self, + theta_min=0.3, + theta_max=200, + nbins=500, + nbins_map=15, + npatch=25, + compute_tomography=False, ): - self.print_start("Computing aperture-mass dispersion") self._map2 = {} theta_map = np.geomspace(theta_min * 5, theta_max / 2, nbins_map) @@ -405,53 +185,76 @@ def calculate_aperture_mass_dispersion( treecorr_config = self._binning(theta_min, theta_max, nbins) for ver in self.versions: - self.print_magenta(ver) + if compute_tomography: + tomo_bin_ids, tomo_bin_pairs = self._get_tomo_bins(ver) + if tomo_bin_ids is None or tomo_bin_pairs is None: + raise ValueError( + f"Version {ver} does not have tomography information." + ) + self.print_magenta( + f"Computing MAP for {ver} with {len(tomo_bin_pairs)} bins." + ) + else: + self.print_magenta(f"Computing non-tomographic MAP for {ver}.") - gg = treecorr.GGCorrelation(treecorr_config) + tomo_bin_pairs = [("all", "all")] - out_fname = self._output_path(f"xi_for_map2_{ver}.txt") - if os.path.exists(out_fname): - self.print_green(f"Skipping xi for Map2, {out_fname} exists") - gg.read(out_fname) - else: - with self.results[ver].temporarily_read_data(): - g1, g2 = self._calibrated_g(ver) - cat_gal = treecorr.Catalog( - ra=self.results[ver].dat_shear["RA"], - dec=self.results[ver].dat_shear["Dec"], - g1=g1, - g2=g2, - w=self._read_shear_cols(ver, "w_col"), + self._map2.setdefault(ver, {}) + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + + # LG TO-DO: Change to sacc_io method + with self.results[ver].temporarily_read_data(): + g1, g2 = self._calibrated_g(ver) + w = self._read_shear_cols(ver, "w_col") + + for bin1, bin2 in tomo_bin_pairs: + gg = treecorr.GGCorrelation(treecorr_config) + + # Load data and create a catalog + if bin1 == "all" and bin2 == "all": + mask_bin1 = np.ones(len(cat_gal), dtype=bool) + else: + mask_bin1 = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] == bin1 + + cat_gal1 = treecorr.Catalog( + ra=cat_gal["RA"][mask_bin1], + dec=cat_gal["Dec"][mask_bin1], + g1=g1[mask_bin1], + g2=g2[mask_bin1], + w=w[mask_bin1], + ra_units=self.treecorr_config["ra_units"], + dec_units=self.treecorr_config["dec_units"], + npatch=npatch, + ) + cat_gal2 = None + + if bin1 != bin2: + mask_bin2 = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] == bin2 + cat_gal2 = treecorr.Catalog( + ra=cat_gal["RA"][mask_bin2], + dec=cat_gal["Dec"][mask_bin2], + g1=g1[mask_bin2], + g2=g2[mask_bin2], + w=w[mask_bin2], ra_units=self.treecorr_config["ra_units"], dec_units=self.treecorr_config["dec_units"], npatch=npatch, ) - gg.process(cat_gal) - gg.write(out_fname) - del cat_gal - del g1 - del g2 + gg.process(cat_gal1, cat2=cat_gal2) - mapsq, mapsq_im, mxsq, mxsq_im, varmapsq = gg.calculateMapSq( - R=theta_map, - m2_uform="Schneider", - ) - out_fname_map2 = self._output_path(f"map2_{ver}.txt") - if os.path.exists(out_fname_map2): - self.print_green(f"Skipping Map2, {out_fname_map2} exists") - else: - print(f"Writing Map2 to output file {out_fname_map2} ") - gg.writeMapSq(out_fname_map2, R=theta_map, m2_uform="Schneider") - self._map2[ver] = { - "mapsq": mapsq, - "mapsq_im": mapsq_im, - "mxsq": mxsq, - "mxsq_im": mxsq_im, - "varmapsq": varmapsq, - } - - self.print_done("Done aperture-mass dispersion") + mapsq, mapsq_im, mxsq, mxsq_im, varmapsq = gg.calculateMapSq( + R=theta_map, + m2_uform="Schneider", + ) + self._map2[ver][f"tomo_bin_{bin1}_tomo_bin_{bin2}"] = { + "mapsq": mapsq, + "mapsq_im": mapsq_im, + "mxsq": mxsq, + "mxsq_im": mxsq_im, + "varmapsq": varmapsq, + } + self.print_done(f"Done aperture-mass dispersion for {ver}.") @property def map2(self): @@ -459,64 +262,383 @@ def map2(self): self.calculate_aperture_mass_dispersion() return self._map2 - def plot_aperture_mass_dispersion(self): - for mode in ["mapsq", "mapsq_im", "mxsq", "mxsq_im"]: - x = [self.map2["theta_map"] for ver in self.versions] - y = [self.map2[ver][mode] for ver in self.versions] - yerr = [np.sqrt(self.map2[ver]["varmapsq"]) for ver in self.versions] - labels = list(self.versions) - colors = [self.cc[ver]["colour"] for ver in self.versions] - linestyles = [self.cc[ver]["ls"] for ver in self.versions] - - xlabel = r"$\theta$ [arcmin]" - ylabel = "dispersion" - title = f"Aperture-mass dispersion {mode}" - out_path = self._output_path(f"{mode}.png") - cs_plots.plot_data_1d( - x, - y, - yerr, - title, - xlabel, - ylabel, - out_path=None, - labels=labels, - xlog=True, - xlim=[self.theta_min_plot, self.theta_max_plot], - ylim=[-2e-6, 5e-6], - colors=colors, - linestyles=linestyles, - shift_x=True, + # LG: plotting functions removed, perhaps can use Sacha's implementation in psf_systematics.py instead + def plot_2pcf_tomography( + self, + x_y_plot_function, + x_label, + y_label_plus, + y_label_minus, + tomo_bin_label_position, + extract_text_offset, + add_index_version_to_kwargs, + x_scale=None, + y_scale=None, + tomography=False, + versions=None, + colors=None, + savefig=None, + show=True, + close=True, + **kwargs, + ): + """ + Standard plot function for 2-point correlation functions with tomographic bins. + + Parameters + ---------- + x_y_plot_function : callable + Function to plot the x and y data. Should accept axes for `+' and `-' components, version, tomo_bin_indices, and kwargs. + x_label : str + Label for the x-axis. + y_label_plus : str + Label for the y-axis of the `+' component. + y_label_minus : str + Label for the y-axis of the `-' component. + tomo_bin_label_position : tuple + Position to place the tomographic bin labels in axes coordinates (x, y). + extract_text_offset : bool + If True, extract the y-axis offset text and include it in the y-label. + add_index_version_to_kwargs : bool + If True, add the index of the version to the kwargs for plotting. + x_scale : str, optional + Scale for the x-axis ('linear', 'log', etc.). If None, default scale is used. + y_scale : str, optional + Scale for the y-axis ('linear', 'log', etc.). If None, default scale is used. + tomography : bool, optional + If True, plot the tomographic bins. Default is False. + versions : list, optional + List of versions to plot. If None, all versions are plotted. + colors : list, optional + List of colors for each version. If None, default colors are used. + savefig : str, optional + If provided, save the figure to this file. + show : bool, optional + If True, show the figure. + close : bool, optional + If True, close the figure after saving or showing. + kwargs : dict + Additional keyword arguments to pass to the plotting function. + """ + versions = versions if versions is not None else self.versions + colors = ( + colors + if colors is not None + else [self.cc[ver]["colour"] for ver in versions] + ) + + # First get the max number of tomo bins among the versions. + tomo_bins = self._get_tomo_bins_for_versions(versions, tomography=tomography) + + max_key = max(tomo_bins, key=lambda k: len(tomo_bins[k]["ids"])) + n_tomo_bins_plot = len(tomo_bins[max_key]["ids"]) + reference_tomo_bin_pairs = tomo_bins[max_key]["pairs"] + + n_rows = n_tomo_bins_plot + n_cols = n_tomo_bins_plot + 2 + + # First start with the quantiles plot + fig, axs = plt.subplots( + n_rows, + n_cols, + figsize=(5 * n_cols, 5 * n_rows), + sharex=True, + sharey=True, + gridspec_kw={"wspace": 0, "hspace": 0}, + ) + + for idx, (ver, color) in enumerate(zip(versions, colors)): + tomo_bin_pairs = tomo_bins[ver]["pairs"] + + kwargs["color"] = color + if add_index_version_to_kwargs: + kwargs["idx"] = idx + kwargs["versions"] = versions + + for tomo_bin_a, tomo_bin_b in tomo_bin_pairs: + # Plot the nsamples last samples + ax_plus = self._get_ax_plus(axs, tomo_bin_a, tomo_bin_b) + ax_minus = self._get_ax_minus(axs, tomo_bin_a, tomo_bin_b) + + # Apply the x_y_plot_function to plot the data + x_y_plot_function( + ax_plus, ax_minus, ver, tomo_bin_a, tomo_bin_b, **kwargs + ) + + # Draw to extract the y-axis text offset + fig.canvas.draw() + + # Set the visibility to false where necessary + self._set_ax_visibility_to_false(axs, n_tomo_bins_plot) + + # Set the labels and scales for the plots + for tomo_bin_a, tomo_bin_b in reference_tomo_bin_pairs: + ax_plus = self._get_ax_plus(axs, tomo_bin_a, tomo_bin_b) + ax_minus = self._get_ax_minus(axs, tomo_bin_a, tomo_bin_b) + + ax_plus.tick_params( + axis="both", + which="both", + direction="in", + bottom=True, + top=False, + labelbottom=tomo_bin_b == 1 or tomo_bin_b == "all", + left=True, + right=False, + labelleft=tomo_bin_a == 1 or tomo_bin_a == "all", ) - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"linear-scale {mode} plot saved to {out_path}") - - for mode in ["mapsq", "mapsq_im", "mxsq", "mxsq_im"]: - x = [self.map2["theta_map"] for ver in self.versions] - y = [np.abs(self.map2[ver][mode]) for ver in self.versions] - yerr = [np.sqrt(self.map2[ver]["varmapsq"]) for ver in self.versions] - xlabel = r"$\theta$ [arcmin]" - ylabel = "dispersion" - title = f"Aperture-mass dispersion mode {mode}" - out_path = self._output_path(f"{mode}_log.png") - cs_plots.plot_data_1d( - x, - y, - yerr, - title, - xlabel, - ylabel, - out_path=None, - labels=labels, - xlog=True, - ylog=True, - xlim=[self.theta_min_plot, self.theta_max_plot], - ylim=[1e-8, 1e-5], - colors=colors, - linestyles=linestyles, - shift_x=True, + if x_scale is not None: + ax_plus.set_xscale(x_scale) + + ax_plus.text( + tomo_bin_label_position[0], + tomo_bin_label_position[1], + f"{tomo_bin_a}-{tomo_bin_b}", + transform=ax_plus.transAxes, + verticalalignment="top", + bbox=dict( + boxstyle="square", + facecolor="white", + edgecolor="black", + alpha=0.8, + ), ) - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"log-scale {mode} plot saved to {out_path}") + ax_plus.axhline(0, color="k", linestyle="--") + + if y_scale is not None: + ax_plus.set_yscale(y_scale) + if tomo_bin_b == 1 or tomo_bin_b == "all": + ax_plus.set_xlabel(r"$\theta$ [arcmin]") + if tomo_bin_a == 1 or tomo_bin_a == "all": + text_offset = ( + ax_plus.yaxis.get_offset_text().get_text() + if extract_text_offset + else "" + ) + ax_plus.set_ylabel(y_label_plus + text_offset) + ax_plus.yaxis.get_offset_text().set_visible( + False + ) # Hide the offset text for the plus ax + + # Move the ticks to the right for the minus ax + ax_minus.yaxis.tick_right() + ax_minus.yaxis.set_label_position("right") + ax_minus.tick_params( + axis="both", + which="both", + direction="in", + bottom=True, + top=False, + labelbottom=tomo_bin_b == n_tomo_bins_plot or tomo_bin_b == "all", + left=False, + right=True, + labelleft=False, + labelright=tomo_bin_a == 1 or tomo_bin_a == "all", + ) + if x_scale is not None: + ax_minus.set_xscale(x_scale) + ax_minus.text( + tomo_bin_label_position[0], + tomo_bin_label_position[1], + f"{tomo_bin_a}-{tomo_bin_b}", + transform=ax_minus.transAxes, + verticalalignment="top", + bbox=dict( + boxstyle="square", + facecolor="white", + edgecolor="black", + alpha=0.8, + ), + ) + ax_minus.axhline(0, color="k", linestyle="--") + if y_scale is not None: + ax_minus.set_yscale(y_scale) + if tomo_bin_b == n_tomo_bins_plot or tomo_bin_b == "all": + ax_minus.set_xlabel(x_label) + if tomo_bin_a == 1 or tomo_bin_a == "all": + text_offset = ( + ax_minus.yaxis.get_offset_text().get_text() + if extract_text_offset + else "" + ) + ax_minus.set_ylabel(y_label_minus + text_offset) + ax_minus.yaxis.get_offset_text().set_visible( + False + ) # Hide the offset text for the minus ax + + # Build the legend + handles = [] + for ver, color in zip(versions, colors): + label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver + handles.append(plt.Line2D([0], [0], color=color, lw=2, label=label)) + fig.legend( + handles=handles, + loc="upper center", + ncol=3, + frameon=False, + bbox_to_anchor=(0.5, 0.0), + ) + + if savefig is not None: + plt.savefig(savefig, dpi=300, bbox_inches="tight") + self.print_done(f"Plot saved to {os.path.abspath(savefig)}") + + if show: + plt.show() + + if close: + plt.close() + + def _xiplus_ximinus_sample_x_y_plot_function( + self, + ax_plus, + ax_minus, + version, + tomo_bin_a, + tomo_bin_b, + idx, + versions, + color, + offset, + times_theta, + alpha, + ): + """Plot the measured ξ± 2PCF for one version/tomographic-bin pair. + + Uses the jackknife covariance from TreeCorr to plot the error bars. This function is fed into :meth:`plot_2pcf_tomography` as the ``x_y_plot_function`` argument. + + Parameters + ---------- + ax_plus, ax_minus : matplotlib.axes.Axes + Axes for the ξ+ and ξ- components. + version : str + Catalog version to plot. + tomo_bin_a, tomo_bin_b : int or str + Tomographic bin pair (``"all"`` for the non-tomographic case). + idx : int + Index of ``version`` within ``versions`` (used for the x-jitter). + versions : list + Full list of versions being plotted. + color : str + Colour for this version. + offset : float + Fractional jitter applied to θ for readability. + times_theta : bool + If True, plot θ·ξ± rather than ξ±. + alpha : float + Opacity of the plotted points/error bars. + """ + # Get the measured 2PCF for this version and tomographic-bin pair. + gg = self.cat_ggs[version][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"] + + # Angular scales of the measurement. + theta = gg.meanr + + # Add the offset to the theta values for better visualisation. + jittered_theta = self._get_jittered_theta(theta, idx, len(versions), offset) + + scale = theta if times_theta else 1 + + y_plus = gg.xip * scale + y_minus = gg.xim * scale + yerr_plus = np.sqrt(gg.varxip) * scale + yerr_minus = np.sqrt(gg.varxim) * scale + + ax_plus.errorbar( + jittered_theta, + y_plus, + yerr=yerr_plus, + color=color, + alpha=alpha, + fmt="o", + markersize=3, + capsize=2, + ) + + ax_minus.errorbar( + jittered_theta, + y_minus, + yerr=yerr_minus, + color=color, + alpha=alpha, + fmt="o", + markersize=3, + capsize=2, + ) + + def _mapsq_mxsq_sample_x_y_plot_function( + self, + ax_plus, + ax_minus, + version, + tomo_bin_a, + tomo_bin_b, + idx, + versions, + color, + offset, + times_theta, + alpha, + ): + """Plot the aperture-mass dispersion ⟨M_ap²⟩ / ⟨M_ײ⟩ for one bin pair. + + Uses the jackknife covariance from TreeCorr to plot the error bars. This function is fed into :meth:`plot_2pcf_tomography` as the ``x_y_plot_function`` argument. The E-mode ⟨M_ap²⟩ is placed on the ``plus`` axis and the B-mode ⟨M_ײ⟩ on the ``minus`` axis. + + Parameters + ---------- + ax_plus, ax_minus : matplotlib.axes.Axes + Axes for the E-mode (⟨M_ap²⟩) and B-mode (⟨M_ײ⟩) components. + version : str + Catalog version to plot. + tomo_bin_a, tomo_bin_b : int or str + Tomographic bin pair (``"all"`` for the non-tomographic case). + idx : int + Index of ``version`` within ``versions`` (used for the x-jitter). + versions : list + Full list of versions being plotted. + color : str + Colour for this version. + offset : float + Fractional jitter applied to θ for readability. + times_theta : bool + If True, plot θ·⟨M²⟩ rather than ⟨M²⟩. + alpha : float + Opacity of the plotted points/error bars. + """ + # Get the aperture-mass dispersion for this version and bin pair. + map2 = self.map2[version][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"] + + # Angular scales of the measurement. + theta = self.map2["theta_map"] + + # Add the offset to the theta values for better visualisation. + jittered_theta = self._get_jittered_theta(theta, idx, len(versions), offset) + + scale = theta if times_theta else 1 + + y_plus = map2["mapsq"] * scale + y_minus = map2["mxsq"] * scale + # Both E- and B-mode share the same variance estimate. + yerr = np.sqrt(map2["varmapsq"]) * scale + + ax_plus.errorbar( + jittered_theta, + y_plus, + yerr=yerr, + color=color, + alpha=alpha, + fmt="o", + markersize=3, + capsize=2, + ) + + ax_minus.errorbar( + jittered_theta, + y_minus, + yerr=yerr, + color=color, + alpha=alpha, + fmt="o", + markersize=3, + capsize=2, + ) diff --git a/src/sp_validation/tests/test_cosmo_val.py b/src/sp_validation/tests/test_cosmo_val.py index f50992d4..9536cbee 100644 --- a/src/sp_validation/tests/test_cosmo_val.py +++ b/src/sp_validation/tests/test_cosmo_val.py @@ -512,13 +512,14 @@ def test_calculate_2pcf_runs_on_synthetic_catalog(self, tmp_path): **params, ) - gg = cv.calculate_2pcf(version) + ggs = cv.calculate_2pcf_version(version) # treecorr GGCorrelation with xi+/- on the configured angular grid - assert gg.xip.shape == (nbins,) - assert gg.xim.shape == (nbins,) - assert np.all(np.isfinite(gg.xip)) - assert np.all(np.isfinite(gg.xim)) + # LG:Hardcoded to be non-tomographic for now + assert ggs["tomo_bin_all_tomo_bin_all"].xip.shape == (nbins,) + assert ggs["tomo_bin_all_tomo_bin_all"].xim.shape == (nbins,) + assert np.all(np.isfinite(ggs["tomo_bin_all_tomo_bin_all"].xip)) + assert np.all(np.isfinite(ggs["tomo_bin_all_tomo_bin_all"].xim)) # The additive-bias subtraction in the pipeline must have run. assert version in cv.c1 and version in cv.c2 From 2fc8ee0087b2e2df7df9b26c8651901fec30017a Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Mon, 24 Aug 2026 14:38:34 +0200 Subject: [PATCH 056/107] Update de scale dependent plotting script to read the computed rho-tau-stats and plot tomography --- .../cosmo_val/psf_systematics.py | 379 +++++++++++------- 1 file changed, 240 insertions(+), 139 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 6765123e..1a91a3d7 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -209,8 +209,10 @@ def calculate_scale_dependent_leakage(self): self.print_done("Finished scale-dependent leakage calculation.") - def calculate_objectwise_leakage(self): + def calculate_objectwise_leakage(self, tomography=False): # TODO: Upgrade for tomography + + # TODO: remove and save the results of the scale-dependent leakage independently if not hasattr(self.results[self.versions[0]], "alpha_leak_mean"): self.calculate_scale_dependent_leakage() @@ -453,6 +455,62 @@ def get_xi_psf_sys_samples(self, ver, params, tomo_bin_a, tomo_bin_b): return xi_psf_sys_samples_plus, xi_psf_sys_samples_minus + def _get_alpha_leakage( + self, + rho_stat_handler, + tau_stat_handler, + cov_rho=None, + cov_tau=None, + n_samples=10_000, + ): + """ + Compute the alpha leakage parameter from the rho and tau statistics. + + Parameters + ---------- + rho_stat_handler : RhoStatHandler + The handler for the rho statistics. + tau_stat_handler : TauStatHandler + The handler for the tau statistics. + cov_rho : np.ndarray, optional + The covariance matrix for the rho statistics. If None, it will be computed. + cov_tau : np.ndarray, optional + The covariance matrix for the tau statistics. If None, it will be computed. + + Returns + ------- + alpha_leak : float + The estimated alpha leakage parameter. + """ + if cov_rho is None: + cov_rho = np.diag(rho_stat_handler.rho_stats["varrho_0_p"]) + if cov_tau is None: + cov_tau = np.diag(tau_stat_handler.tau_stats["vartau_0_p"]) + + theta = rho_stat_handler.rho_stats["theta"] + n_bins = len(theta) + alpha = ( + tau_stat_handler.tau_stats["tau_0_p"] + / rho_stat_handler.rho_stats["rho_0_p"] + ) + + # Derive alpha_err by sampling from the covariance matrices of rho and tau statistics + rho_samples = np.random.multivariate_normal( + mean=rho_stat_handler.rho_stats["rho_0_p"], + cov=cov_rho[:n_bins, :n_bins], + size=n_samples, + ) + tau_samples = np.random.multivariate_normal( + mean=tau_stat_handler.tau_stats["tau_0_p"], + cov=cov_tau[:n_bins, :n_bins], + size=n_samples, + ) + + alpha_samples = tau_samples / rho_samples + alpha_err = np.std(alpha_samples, axis=0) + + return theta, alpha, alpha_err + # --- plotting functions --- def plot_rho_stats( self, @@ -920,150 +978,190 @@ def plot_rho_tau_fits( ) """ - def plot_scale_dependent_leakage(self): - if not hasattr(self.results[self.versions[0]], "r_corr_gp"): - self.calculate_scale_dependent_leakage() + def plot_scale_dependent_leakage( + self, + tomography=False, + cov_type=None, + versions=None, + colors=None, + offset=0, + savefig=None, + show=True, + close=True, + plot_theta_times_tau=False, + ylim_alpha=False, + fmt="", + capsize=2, + ): + # First plot alpha leakage + self.plot_scale_dependent_alpha( + tomography=tomography, + cov_type=cov_type, + versions=versions, + colors=colors, + offset=offset, + savefig=savefig, + show=show, + close=close, + fmt=fmt, + capsize=capsize, + ylim_alpha=ylim_alpha, + ) - theta = [] - y = [] - yerr = [] - labels = [] - colors = [] - linestyles = [] - markers = [] + # Second plot xi_sys + self.plot_xi_sys_from_scale_dependent_leakage( + tomography=tomography, + cov_type=cov_type, + versions=versions, + colors=colors, + offset=offset, + savefig=savefig, + show=show, + close=close, + plot_theta_times_tau=plot_theta_times_tau, + fmt=fmt, + capsize=capsize, + ) - for ver in self.versions: - if hasattr(self.results[ver], "r_corr_gp"): - theta.append(self.results[ver].r_corr_gp.meanr) - y.append(self.results[ver].alpha_leak) - yerr.append(self.results[ver].sig_alpha_leak) - labels.append(ver) - colors.append(self.cc[ver]["colour"]) - linestyles.append(self.cc[ver]["ls"]) - markers.append(self.cc[ver]["marker"]) - - if len(theta) > 0: - # Log x - out_path = self._output_path("alpha_leak_log.png") - - title = r"$\alpha$ leakage" - xlabel = r"$\theta$ [arcmin]" - ylabel = r"$\alpha(\theta)$" - cs_plots.plot_data_1d( - theta, - y, - yerr, - title, - xlabel, - ylabel, - out_path=None, - xlog=True, - xlim=[self.theta_min_plot, self.theta_max_plot], - ylim=self.ylim_alpha, - labels=labels, - colors=colors, - linestyles=linestyles, - shift_x=True, - ) - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"Log-scale alpha leakage plot saved to {out_path}") - - # Lin x - out_path = self._output_path("alpha_leak_lin.png") - - title = r"$\alpha$ leakage" - xlabel = r"$\theta$ [arcmin]" - ylabel = r"$\alpha(\theta)$" - cs_plots.plot_data_1d( - theta, - y, - yerr, - title, - xlabel, - ylabel, - out_path=None, - xlog=False, - xlim=[-10, self.theta_max_plot], - ylim=self.ylim_alpha, - labels=labels, - colors=colors, - linestyles=linestyles, - shift_x=False, - ) - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"Lin-scale alpha leakage plot saved to {out_path}") + def plot_scale_dependent_alpha( + self, + tomography=False, + cov_type=None, + versions=None, + colors=None, + offset=0, + savefig=None, + show=True, + close=True, + fmt="", + capsize=2, + ylim_alpha=False, + ): + if versions is None: + versions = self.versions - # Plot xi_sys - y = [] - yerr = [] - colors = [] - linestyles = [] + if colors is None: + colors = [self.cc[ver]["colour"] for ver in versions] - for ver in self.versions: - if hasattr(self.results[ver], "C_sys_p"): - y.append(self.results[ver].C_sys_p) - yerr.append(self.results[ver].C_sys_std_p) - labels.append(ver) - colors.append(self.cc[ver]["colour"]) - linestyles.append(self.cc[ver]["ls"]) - - if len(y) > 0: - xlabel = r"$\theta$ [arcmin]" - ylabel = r"$\xi^{\rm sys}_+(\theta)$" - title = "Cross-correlation leakage" - out_path = self._output_path("xi_sys_p.png") - cs_plots.plot_data_1d( - theta, - y, - yerr, - title, - xlabel, - ylabel, - out_path=None, - labels=labels, - xlog=True, - xlim=[self.theta_min_plot, self.theta_max_plot], - colors=colors, - linestyles=linestyles, - # shift_x=True, - ) - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_sys_plus plot saved to {out_path}") + if len(colors) != len(versions): + raise ValueError("Colors and versions must have the same length.") - y = [] - yerr = [] - for ver in self.versions: - if hasattr(self.results[ver], "C_sys_m"): - y.append(self.results[ver].C_sys_m) - yerr.append(self.results[ver].C_sys_std_m) - - if len(y) > 0: - xlabel = r"$\theta$ [arcmin]" - ylabel = r"$\xi^{\rm sys}_-(\theta)$" - title = "Cross-correlation leakage" - out_path = self._output_path("xi_sys_m.png") - cs_plots.plot_data_1d( - theta, - y, - yerr, - title, - xlabel, - ylabel, - out_path=None, - labels=labels, - xlog=True, - xlim=[self.theta_min_plot, self.theta_max_plot], - ylim=[-1e-7, 1e-6], - colors=colors, - linestyles=linestyles, - # shift_x=True, + if cov_type is None: + self.print_cyan("Using the error bars from the tau-statistics files") + else: + self.print_cyan( + f"Using the error bars from the covariance files of type: {cov_type}" ) - cs_plots.savefig(out_path, close_fig=False) - cs_plots.show() - self.print_done(f"xi_sys_minus plot saved to {out_path}") + + tomo_bins = self._get_tomo_bins_for_versions(versions, tomography=tomography) + + n_tomo_bins_plot = max(len(bins["ids"]) for bins in tomo_bins.values()) + + out_dir = f"{self.cc['paths']['output']}/rho_tau_stats" + + fig, axs = plt.subplots( + n_tomo_bins_plot, 1, figsize=(8, 3 * n_tomo_bins_plot), sharex=True + ) + + # Iterate upon each version + for ver, color in zip(versions, colors): + label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver + # Iterate upon each tomographic bin + for tomo_bin_id in tomo_bins[ver]["ids"]: + base_rho = self.basename(ver) + base_tau = self.basename(ver, tomo_bin_a=tomo_bin_id) + self.rho_stat_handler.load_rho_stats(f"rho_stats_{base_rho}.fits") + self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau}.fits") + + if cov_type is not None: + cov_tau_path = Path(out_dir) / f"cov_tau_{base_tau}_{cov_type}.npy" + cov_tau = np.load(cov_tau_path) + cov_rho_path = Path(out_dir) / f"cov_rho_{base_rho}_jk.npy" + cov_rho = np.load(cov_rho_path) + else: + cov_tau = None + cov_rho = None + + # Get the error bar sampling from the covariance matrices + theta, alpha, alpha_err = self._get_alpha_leakage( + self.rho_stat_handler, self.tau_stat_handler, cov_rho, cov_tau + ) + + jittered_theta = self._get_jittered_theta( + theta, versions.index(ver), len(versions), offset + ) + + if tomo_bin_id == "all": + ax = axs + else: + ax = axs[tomo_bin_id - 1] + + ax.errorbar( + jittered_theta, + alpha, + yerr=alpha_err, + fmt=fmt, + label=f"{label}", + capsize=capsize, + color=color, + ) + + if tomography: + for i, ax in enumerate(axs): + ax.set_xscale("log") + ax.set_xlim(self.theta_min_plot, self.theta_max_plot) + if ylim_alpha: + ax.set_ylim(self.ylim_alpha) + ax.set_ylabel(rf"$\alpha_{i + 1}(\theta)$") + if i == len(axs) - 1: + ax.set_xlabel(r"$\theta$ [arcmin]") + else: + axs.set_xscale("log") + axs.set_xlim(self.theta_min_plot, self.theta_max_plot) + if ylim_alpha: + axs.set_ylim(self.ylim_alpha) + axs.set_ylabel(r"$\alpha_{\rm all}(\theta)$") + axs.set_xlabel(r"$\theta$ [arcmin]") + + if tomography: + handles, labels = axs[0].get_legend_handles_labels() + else: + handles, labels = axs.get_legend_handles_labels() + + fig.legend( + handles, + labels, + loc="upper center", + bbox_to_anchor=(0.5, -0.02), + ncol=3, + frameon=False, + ) + + if savefig is not None: + plt.savefig(savefig, dpi=300, bbox_inches="tight") + self.print_done(f"Scale-dependent alpha leakage plot saved to {savefig}") + + if show: + plt.show() + + if close: + plt.close() + + def plot_xi_sys_from_scale_dependent_leakage( + self, + tomography=False, + cov_type=None, + versions=None, + colors=None, + offset=0, + savefig=None, + show=True, + close=True, + plot_theta_times_tau=False, + fmt="", + capsize=2, + ): + pass def plot_objectwise_leakage(self): if not hasattr(self, "leakage_coeff"): @@ -1490,3 +1588,6 @@ def _set_ax_visibility_to_false(self, axs, n_tomo_bins_plot): else: for i in range(n_tomo_bins_plot): axs[i, n_tomo_bins_plot - i].set_visible(False) + + +# %% From 35795aba67f63a5416505f7db05ecc8f39c7a361 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 25 Aug 2026 09:39:52 +0200 Subject: [PATCH 057/107] Update the plotting script for the scale dependent xi_sys --- .../cosmo_val/psf_systematics.py | 186 ++++++++++++++---- 1 file changed, 146 insertions(+), 40 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 1a91a3d7..9d0dd658 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -511,6 +511,51 @@ def _get_alpha_leakage( return theta, alpha, alpha_err + def _compute_scale_dependent_xi_psf_sys( + self, rho_0, tau_0_a, tau_0_b, cov_rho, cov_tau_a, cov_tau_b, n_samples=10_000 + ): + """ + Compute the scale-dependent xi_psf_sys from the rho and tau statistics. + + Parameters + ---------- + rho_0 : np.ndarray + The rho_0 statistics. + tau_0_a : np.ndarray + The tau_0 statistics for tomographic bin a. + tau_0_b : np.ndarray + The tau_0 statistics for tomographic bin b. + cov_rho : np.ndarray + The covariance matrix for the rho statistics. + cov_tau_a : np.ndarray + The covariance matrix for the tau statistics for tomographic bin a. + cov_tau_b : np.ndarray + The covariance matrix for the tau statistics for tomographic bin b. + """ + # Compute xi_psf_sys for each scale using the formula: + xi_psf_sys = (tau_0_a * tau_0_b) / rho_0 + + # Derive the error bars by sampling the statistics + rho_samples = np.random.multivariate_normal( + mean=rho_0, + cov=cov_rho, + size=n_samples, + ) + tau_samples_a = np.random.multivariate_normal( + mean=tau_0_a, + cov=cov_tau_a, + size=n_samples, + ) + tau_samples_b = np.random.multivariate_normal( + mean=tau_0_b, + cov=cov_tau_b, + size=n_samples, + ) + xi_psf_sys_samples = (tau_samples_a * tau_samples_b) / rho_samples + xi_psf_sys_err = np.std(xi_psf_sys_samples, axis=0) + + return xi_psf_sys, xi_psf_sys_err + # --- plotting functions --- def plot_rho_stats( self, @@ -959,25 +1004,6 @@ def plot_rho_tau_fits( **kwargs_x_y_plot_function, ) - """ - for mcmc_result, ver, flat_sample in zip( - self.rho_tau_fits["result_list"], - self.versions, - self.rho_tau_fits["flat_samples"], - ): - self.psf_fitter.load_rho_stat(f"rho_stats_{self.basename(ver)}.fits") - for yscale in ("linear", "log"): - out_path = os.path.abspath( - f"{out_dir}/xi_psf_sys_terms_{yscale}_{ver}.png" - ) - self.psf_fitter.plot_xi_psf_sys_terms( - ver, mcmc_result[1], out_path, yscale=yscale, show=True - ) - self.print_done( - f"{yscale}-scale xi_psf_sys terms plot saved to {out_path}" - ) - """ - def plot_scale_dependent_leakage( self, tomography=False, @@ -1009,16 +1035,31 @@ def plot_scale_dependent_leakage( ) # Second plot xi_sys - self.plot_xi_sys_from_scale_dependent_leakage( + self.plot_2pcf_tomography( + self._scale_dependent_xi_psf_sys_x_y_plot_function, + x_label=r"$\theta$ [arcmin]", + y_label_plus=(r"$\theta$" if plot_theta_times_tau else "") + + r"$\xi^{\rm PSF, sys}_+(\theta)$", + y_label_minus=(r"$\theta$" if plot_theta_times_tau else "") + + r"$\xi^{\rm PSF, sys}_-(\theta)$", + tomo_bin_label_position=(0.05, 0.9) + if not plot_theta_times_tau + else (0.8, 0.95), + extract_text_offset=plot_theta_times_tau, + add_index_version_to_kwargs=True, + x_scale="log", + y_scale="linear" if plot_theta_times_tau else "log", tomography=tomography, - cov_type=cov_type, versions=versions, colors=colors, - offset=offset, - savefig=savefig, + savefig=savefig.replace(".png", "_xi_psf_sys.png") + if savefig is not None + else None, show=show, close=close, - plot_theta_times_tau=plot_theta_times_tau, + offset=offset, + cov_type=cov_type, + times_theta=plot_theta_times_tau, fmt=fmt, capsize=capsize, ) @@ -1115,6 +1156,7 @@ def plot_scale_dependent_alpha( ax.set_ylabel(rf"$\alpha_{i + 1}(\theta)$") if i == len(axs) - 1: ax.set_xlabel(r"$\theta$ [arcmin]") + ax.text(0.05, 0.9, f"Tomo bin {i + 1}", transform=ax.transAxes) else: axs.set_xscale("log") axs.set_xlim(self.theta_min_plot, self.theta_max_plot) @@ -1122,6 +1164,7 @@ def plot_scale_dependent_alpha( axs.set_ylim(self.ylim_alpha) axs.set_ylabel(r"$\alpha_{\rm all}(\theta)$") axs.set_xlabel(r"$\theta$ [arcmin]") + axs.text(0.05, 0.9, "All tomographic bins", transform=axs.transAxes) if tomography: handles, labels = axs[0].get_legend_handles_labels() @@ -1147,22 +1190,6 @@ def plot_scale_dependent_alpha( if close: plt.close() - def plot_xi_sys_from_scale_dependent_leakage( - self, - tomography=False, - cov_type=None, - versions=None, - colors=None, - offset=0, - savefig=None, - show=True, - close=True, - plot_theta_times_tau=False, - fmt="", - capsize=2, - ): - pass - def plot_objectwise_leakage(self): if not hasattr(self, "leakage_coeff"): self.calculate_objectwise_leakage() @@ -1547,6 +1574,85 @@ def plot_axis(ax, ax_type): # Plot the minus axis plot_axis(ax_minus, "minus") + def _scale_dependent_xi_psf_sys_x_y_plot_function( + self, + ax_plus, + ax_minus, + version, + tomo_bin_a, + tomo_bin_b, + idx, + versions, + color, + offset, + cov_type, + times_theta, + fmt, + capsize, + ): + # Load the rho-stats and the tau-stats + base_rho = self.basename(version) + base_tau_a = self.basename(version, tomo_bin_a=tomo_bin_a) + base_tau_b = self.basename(version, tomo_bin_a=tomo_bin_b) + self.rho_stat_handler.load_rho_stats(f"rho_stats_{base_rho}.fits") + + theta = self.rho_stat_handler.rho_stats["theta"] + n_bins = len(theta) + rho_0_p = self.rho_stat_handler.rho_stats["rho_0_p"] + rho_0_m = self.rho_stat_handler.rho_stats["rho_0_m"] + + self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau_a}.fits") + + tau_0_p_a = self.tau_stat_handler.tau_stats["tau_0_p"] + tau_0_m_a = self.tau_stat_handler.tau_stats["tau_0_m"] + + self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau_b}.fits") + + tau_0_p_b = self.tau_stat_handler.tau_stats["tau_0_p"] + tau_0_m_b = self.tau_stat_handler.tau_stats["tau_0_m"] + + if cov_type is not None: + outdir = f"{self.cc['paths']['output']}/rho_tau_stats" + cov_tau_path = Path(outdir) / f"cov_tau_{base_tau_a}_{cov_type}.npy" + cov_tau_p_a = np.load(cov_tau_path)[:n_bins, :n_bins] + cov_tau_path = Path(outdir) / f"cov_tau_{base_tau_b}_{cov_type}.npy" + cov_tau_p_b = np.load(cov_tau_path)[:n_bins, :n_bins] + cov_rho_path = Path(outdir) / f"cov_rho_{base_rho}_jk.npy" + cov_rho_p = np.load(cov_rho_path)[:n_bins, :n_bins] + else: + cov_tau_p_a = np.diag(self.tau_stat_handler.tau_stats["vartau_0_p"]) + cov_tau_p_b = np.diag(self.tau_stat_handler.tau_stats["vartau_0_p"]) + cov_rho_p = np.diag(self.rho_stat_handler.rho_stats["varrho_0_p"]) + + cov_tau_m_a = np.diag(self.tau_stat_handler.tau_stats["vartau_0_m"]) + cov_tau_m_b = np.diag(self.tau_stat_handler.tau_stats["vartau_0_m"]) + cov_rho_m = np.diag(self.rho_stat_handler.rho_stats["varrho_0_m"]) + + # Compute the scale-dependent xi_psf_sys and its error bars + xi_psf_sys_plus, xi_psf_sys_plus_err = self._compute_scale_dependent_xi_psf_sys( + rho_0_p, tau_0_p_a, tau_0_p_b, cov_rho_p, cov_tau_p_a, cov_tau_p_b + ) + xi_psf_sys_minus, xi_psf_sys_minus_err = ( + self._compute_scale_dependent_xi_psf_sys( + rho_0_m, tau_0_m_a, tau_0_m_b, cov_rho_m, cov_tau_m_a, cov_tau_m_b + ) + ) + + jittered_theta = self._get_jittered_theta(theta, idx, len(versions), offset) + + y_plus = xi_psf_sys_plus * (theta if times_theta else 1) + y_plus_err = xi_psf_sys_plus_err * (theta if times_theta else 1) + y_minus = xi_psf_sys_minus * (theta if times_theta else 1) + y_minus_err = xi_psf_sys_minus_err * (theta if times_theta else 1) + + ax_plus.errorbar( + jittered_theta, y_plus, yerr=y_plus_err, fmt=fmt, capsize=capsize + ) + + ax_minus.errorbar( + jittered_theta, y_minus, yerr=y_minus_err, fmt=fmt, capsize=capsize + ) + def _get_jittered_theta(self, theta, idx, n_versions, offset): """Get the jittered theta values for better visualisation.""" theta_widths = np.diff(theta) From ed942aadcd37b3981a78cc597a141d329536e646 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 25 Aug 2026 09:43:26 +0200 Subject: [PATCH 058/107] Update the pyproject.toml --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 57707a2a..5ca8b163 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,8 @@ dependencies = [ "lmfit", "numexpr", "numpy>=2.0", + # Sacha's fork of OneCovariance with the bug fix for numpy>2 + "onecovariance @ git+https://github.com/sachaguer/OneCovariance@main", "opencv-python-headless", "pyccl", "pyarrow", From 7eed6961198de16843074f815843091e7cf32cda Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 25 Aug 2026 10:12:03 +0200 Subject: [PATCH 059/107] Fix the pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5ca8b163..939c5663 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ dependencies = [ "numexpr", "numpy>=2.0", # Sacha's fork of OneCovariance with the bug fix for numpy>2 - "onecovariance @ git+https://github.com/sachaguer/OneCovariance@main", + "onecovariance @ git+https://github.com/sachaguer/OneCovariance.git@main", "opencv-python-headless", "pyccl", "pyarrow", From 744614ef3f54bfa25f2d8e035f6401904c1c2a9e Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Tue, 25 Aug 2026 10:23:00 +0200 Subject: [PATCH 060/107] Fix the pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 939c5663..dd834466 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,11 +43,11 @@ dependencies = [ "jupyterlab", "jupytext>=1.15", "lenspack", + # Sacha's fork of OneCovariance with the bug fix for numpy>2 + "levin @ git+https://github.com/sachaguer/OneCovariance.git@main", "lmfit", "numexpr", "numpy>=2.0", - # Sacha's fork of OneCovariance with the bug fix for numpy>2 - "onecovariance @ git+https://github.com/sachaguer/OneCovariance.git@main", "opencv-python-headless", "pyccl", "pyarrow", From a66b48bfcdf342b8d11414eaae4c2bee8bde09e5 Mon Sep 17 00:00:00 2001 From: Sacha Guerrini Date: Wed, 26 Aug 2026 18:23:59 +0200 Subject: [PATCH 061/107] Update the objectwise leakage to run in the tomographic case. --- .../cosmo_val/psf_systematics.py | 96 ++++++++++--------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 9d0dd658..a05d7eb9 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -211,17 +211,21 @@ def calculate_scale_dependent_leakage(self): def calculate_objectwise_leakage(self, tomography=False): # TODO: Upgrade for tomography - - # TODO: remove and save the results of the scale-dependent leakage independently - if not hasattr(self.results[self.versions[0]], "alpha_leak_mean"): - self.calculate_scale_dependent_leakage() + # Get the tomographic bins + tomo_bins = self._get_tomo_bins_for_versions( + self.versions, tomography=tomography + ) self.print_start("Object-wise leakage:") mix = True order = "lin" + if not hasattr(self, "leakage_coeff"): + self.leakage_coeff = {} for ver in self.versions: self.print_magenta(ver) + self.leakage_coeff.setdefault(ver, {}) + results_obj = self.results_objectwise[ver] results_obj.check_params() results_obj.update_params() @@ -230,50 +234,48 @@ def calculate_objectwise_leakage(self, tomography=False): # Skip read_data() and copy catalogue from scale leakage instance instead # results_obj._dat = self.results[ver].dat_shear - out_base = results_obj.get_out_base(mix, order) - out_path = f"{out_base}.pkl" - if os.path.exists(out_path): - self.print_green( - f"Skipping object-wise leakage, file {out_path} exists" - ) - results_obj.par_best_fit = leakage.read_from_file(out_path) - else: - self.print_cyan("Computing object-wise leakage regression") - - # Run - with results_obj.temporarily_read_data(): - try: - results_obj.PSF_leakage() - except KeyError as e: - print(f"{e}\nExpected key is missing from catalog.") - # remove the results object for this version - self.results_objectwise.pop(ver) - - # Gather coefficients - leakage_coeff = {} - for ver in self.results_objectwise: - results = self.results[ver] - par_best_fit = self.results_objectwise[ver].par_best_fit - - # Object-wise leakage - a11 = ufloat(par_best_fit["a11"].value, par_best_fit["a11"].stderr) - a22 = ufloat(par_best_fit["a22"].value, par_best_fit["a22"].stderr) - leakage_coeff[ver] = { - "a11": a11, - "a22": a22, - "aii_mean": 0.5 * (a11 + a22), - # Scale-dependent leakage: mean - "alpha_mean": ufloat(results.alpha_leak_mean, results.alpha_leak_std), - # Scale-dependent leakage: value at smallest scale - "alpha_1": ufloat(results.alpha_leak[0], results.sig_alpha_leak[0]), - # Scale-dependent leakage: value extrapolated to 0 using affine model - "alpha_0": ufloat( - results.alpha_affine_best_fit["c"].value, - results.alpha_affine_best_fit["c"].stderr, - ), - } + # Iterate on the tomographic bins for this version + for tomo_bin_id in tomo_bins[ver]["ids"]: + if tomo_bin_id == "all": + selection = None + else: + selection = self._get_galaxy_mask(ver, tomo_bin_id) + + suffix = f"tomo_bin_{tomo_bin_id}" - self.leakage_coeff = leakage_coeff + out_base = results_obj.get_out_base(mix, order, suffix=suffix) + out_path = f"{out_base}.pkl" + if os.path.exists(out_path): + self.print_green( + f"Skipping object-wise leakage, file {out_path} exists" + ) + results_obj.par_best_fit = leakage.read_from_file(out_path) + else: + self.print_cyan("Computing object-wise leakage regression") + + # Run + with results_obj.temporarily_read_data(selection=selection): + try: + results_obj.PSF_leakage(suffix=suffix) + + # Gather coefficients + + except KeyError as e: + print(f"{e}\nExpected key is missing from catalog.") + # remove the results object for this version + self.results_objectwise.pop(ver) + continue + + par_best_fit = results_obj.par_best_fit + + # Object-wise leakage + a11 = ufloat(par_best_fit["a11"].value, par_best_fit["a11"].stderr) + a22 = ufloat(par_best_fit["a22"].value, par_best_fit["a22"].stderr) + self.leakage_coeff[ver][f"tomo_bin_{tomo_bin_id}"] = { + "a11": a11, + "a22": a22, + "aii_mean": 0.5 * (a11 + a22), + } # --- utility functions --- def _get_galaxy_mask(self, ver, tomo_bin_id): From 4d4ea33d76d8ba64e2c87139a5a29cb05871353b Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 22:16:53 +0200 Subject: [PATCH 062/107] psf_systematics: seeded Monte-Carlo errors for alpha and xi_psf_sys _get_alpha_leakage and _compute_scale_dependent_xi_psf_sys draw from np.random.default_rng(seed) (seed=0 by default) instead of the global unseeded state, so the plotted error bars are reproducible run to run. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- .../cosmo_val/psf_systematics.py | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index d868a8da..27eb00e5 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -464,10 +464,14 @@ def _get_alpha_leakage( cov_rho=None, cov_tau=None, n_samples=10_000, + seed=0, ): """ Compute the alpha leakage parameter from the rho and tau statistics. + alpha(theta) = tau_0,+(theta) / rho_0,+(theta); its error is the standard + deviation of that ratio over Gaussian draws of rho_0,+ and tau_0,+. + Parameters ---------- rho_stat_handler : RhoStatHandler @@ -475,14 +479,22 @@ def _get_alpha_leakage( tau_stat_handler : TauStatHandler The handler for the tau statistics. cov_rho : np.ndarray, optional - The covariance matrix for the rho statistics. If None, it will be computed. + The covariance matrix for the rho statistics; its leading block is + the rho_0,+ covariance. If None, the variances in the rho-statistics + table are used. cov_tau : np.ndarray, optional - The covariance matrix for the tau statistics. If None, it will be computed. + The covariance matrix for the tau statistics; its leading block is + the tau_0,+ covariance. If None, the variances in the tau-statistics + table are used. + n_samples : int, optional + Number of Gaussian draws for the error. + seed : int or np.random.Generator, optional + Seed (or generator) for the draws, so that errors are reproducible. Returns ------- - alpha_leak : float - The estimated alpha leakage parameter. + theta, alpha, alpha_err : np.ndarray + Angular scales, alpha leakage and its error. """ if cov_rho is None: cov_rho = np.diag(rho_stat_handler.rho_stats["varrho_0_p"]) @@ -496,13 +508,13 @@ def _get_alpha_leakage( / rho_stat_handler.rho_stats["rho_0_p"] ) - # Derive alpha_err by sampling from the covariance matrices of rho and tau statistics - rho_samples = np.random.multivariate_normal( + rng = np.random.default_rng(seed) + rho_samples = rng.multivariate_normal( mean=rho_stat_handler.rho_stats["rho_0_p"], cov=cov_rho[:n_bins, :n_bins], size=n_samples, ) - tau_samples = np.random.multivariate_normal( + tau_samples = rng.multivariate_normal( mean=tau_stat_handler.tau_stats["tau_0_p"], cov=cov_tau[:n_bins, :n_bins], size=n_samples, @@ -514,11 +526,22 @@ def _get_alpha_leakage( return theta, alpha, alpha_err def _compute_scale_dependent_xi_psf_sys( - self, rho_0, tau_0_a, tau_0_b, cov_rho, cov_tau_a, cov_tau_b, n_samples=10_000 + self, + rho_0, + tau_0_a, + tau_0_b, + cov_rho, + cov_tau_a, + cov_tau_b, + n_samples=10_000, + seed=0, ): """ Compute the scale-dependent xi_psf_sys from the rho and tau statistics. + xi_psf_sys = tau_0_a * tau_0_b / rho_0; its error is the standard + deviation over Gaussian draws of rho_0, tau_0_a and tau_0_b. + Parameters ---------- rho_0 : np.ndarray @@ -533,25 +556,20 @@ def _compute_scale_dependent_xi_psf_sys( The covariance matrix for the tau statistics for tomographic bin a. cov_tau_b : np.ndarray The covariance matrix for the tau statistics for tomographic bin b. + n_samples : int, optional + Number of Gaussian draws for the error. + seed : int or np.random.Generator, optional + Seed (or generator) for the draws, so that errors are reproducible. """ - # Compute xi_psf_sys for each scale using the formula: xi_psf_sys = (tau_0_a * tau_0_b) / rho_0 - # Derive the error bars by sampling the statistics - rho_samples = np.random.multivariate_normal( - mean=rho_0, - cov=cov_rho, - size=n_samples, - ) - tau_samples_a = np.random.multivariate_normal( - mean=tau_0_a, - cov=cov_tau_a, - size=n_samples, + rng = np.random.default_rng(seed) + rho_samples = rng.multivariate_normal(mean=rho_0, cov=cov_rho, size=n_samples) + tau_samples_a = rng.multivariate_normal( + mean=tau_0_a, cov=cov_tau_a, size=n_samples ) - tau_samples_b = np.random.multivariate_normal( - mean=tau_0_b, - cov=cov_tau_b, - size=n_samples, + tau_samples_b = rng.multivariate_normal( + mean=tau_0_b, cov=cov_tau_b, size=n_samples ) xi_psf_sys_samples = (tau_samples_a * tau_samples_b) / rho_samples xi_psf_sys_err = np.std(xi_psf_sys_samples, axis=0) From 76f04a2be6d0c26ab5a32484bc8636c7b7a1fd6b Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 22:17:03 +0200 Subject: [PATCH 063/107] psf_systematics: auto-bin xi_psf_sys errors reuse one tau draw For a == b, xi_psf_sys = tau^2 / rho is one measurement squared; drawing two independent tau samples underestimated the tau contribution to the error by sqrt(2). The callback passes same_bin to the helper. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- .../cosmo_val/psf_systematics.py | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 27eb00e5..835f9c9e 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -534,6 +534,7 @@ def _compute_scale_dependent_xi_psf_sys( cov_tau_a, cov_tau_b, n_samples=10_000, + same_bin=False, seed=0, ): """ @@ -558,6 +559,9 @@ def _compute_scale_dependent_xi_psf_sys( The covariance matrix for the tau statistics for tomographic bin b. n_samples : int, optional Number of Gaussian draws for the error. + same_bin : bool, optional + True when a and b are the same bin: tau_0_a and tau_0_b are then one + measurement, and each draw uses a single tau sample (tau^2 / rho). seed : int or np.random.Generator, optional Seed (or generator) for the draws, so that errors are reproducible. """ @@ -568,9 +572,12 @@ def _compute_scale_dependent_xi_psf_sys( tau_samples_a = rng.multivariate_normal( mean=tau_0_a, cov=cov_tau_a, size=n_samples ) - tau_samples_b = rng.multivariate_normal( - mean=tau_0_b, cov=cov_tau_b, size=n_samples - ) + if same_bin: + tau_samples_b = tau_samples_a + else: + tau_samples_b = rng.multivariate_normal( + mean=tau_0_b, cov=cov_tau_b, size=n_samples + ) xi_psf_sys_samples = (tau_samples_a * tau_samples_b) / rho_samples xi_psf_sys_err = np.std(xi_psf_sys_samples, axis=0) @@ -1424,12 +1431,25 @@ def _scale_dependent_xi_psf_sys_x_y_plot_function( cov_rho_m = np.diag(self.rho_stat_handler.rho_stats["varrho_0_m"]) # Compute the scale-dependent xi_psf_sys and its error bars + same_bin = tomo_bin_a == tomo_bin_b xi_psf_sys_plus, xi_psf_sys_plus_err = self._compute_scale_dependent_xi_psf_sys( - rho_0_p, tau_0_p_a, tau_0_p_b, cov_rho_p, cov_tau_p_a, cov_tau_p_b + rho_0_p, + tau_0_p_a, + tau_0_p_b, + cov_rho_p, + cov_tau_p_a, + cov_tau_p_b, + same_bin=same_bin, ) xi_psf_sys_minus, xi_psf_sys_minus_err = ( self._compute_scale_dependent_xi_psf_sys( - rho_0_m, tau_0_m_a, tau_0_m_b, cov_rho_m, cov_tau_m_a, cov_tau_m_b + rho_0_m, + tau_0_m_a, + tau_0_m_b, + cov_rho_m, + cov_tau_m_a, + cov_tau_m_b, + same_bin=same_bin, ) ) From ed7bfbf722eab1917163d6f551285976f276eb4a Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 22:17:13 +0200 Subject: [PATCH 064/107] psf_systematics: cross-bin xi_psf_sys errors use each bin's tau variance The tau variances are saved while each bin's table is loaded, so bin a's draw uses bin a's variance and bin b's uses bin b's, for + and -. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- .../cosmo_val/psf_systematics.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 835f9c9e..1823a82e 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -1403,15 +1403,17 @@ def _scale_dependent_xi_psf_sys_x_y_plot_function( rho_0_p = self.rho_stat_handler.rho_stats["rho_0_p"] rho_0_m = self.rho_stat_handler.rho_stats["rho_0_m"] - self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau_a}.fits") - - tau_0_p_a = self.tau_stat_handler.tau_stats["tau_0_p"] - tau_0_m_a = self.tau_stat_handler.tau_stats["tau_0_m"] - - self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau_b}.fits") - - tau_0_p_b = self.tau_stat_handler.tau_stats["tau_0_p"] - tau_0_m_b = self.tau_stat_handler.tau_stats["tau_0_m"] + tau_stats = {} + for key, base_tau in (("a", base_tau_a), ("b", base_tau_b)): + self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau}.fits") + tau_stats[key] = { + col: self.tau_stat_handler.tau_stats[col] + for col in ("tau_0_p", "tau_0_m", "vartau_0_p", "vartau_0_m") + } + tau_0_p_a = tau_stats["a"]["tau_0_p"] + tau_0_m_a = tau_stats["a"]["tau_0_m"] + tau_0_p_b = tau_stats["b"]["tau_0_p"] + tau_0_m_b = tau_stats["b"]["tau_0_m"] if cov_type is not None: outdir = f"{self.cc['paths']['output']}/rho_tau_stats" @@ -1422,12 +1424,12 @@ def _scale_dependent_xi_psf_sys_x_y_plot_function( cov_rho_path = Path(outdir) / f"cov_rho_{base_rho}_jk.npy" cov_rho_p = np.load(cov_rho_path)[:n_bins, :n_bins] else: - cov_tau_p_a = np.diag(self.tau_stat_handler.tau_stats["vartau_0_p"]) - cov_tau_p_b = np.diag(self.tau_stat_handler.tau_stats["vartau_0_p"]) + cov_tau_p_a = np.diag(tau_stats["a"]["vartau_0_p"]) + cov_tau_p_b = np.diag(tau_stats["b"]["vartau_0_p"]) cov_rho_p = np.diag(self.rho_stat_handler.rho_stats["varrho_0_p"]) - cov_tau_m_a = np.diag(self.tau_stat_handler.tau_stats["vartau_0_m"]) - cov_tau_m_b = np.diag(self.tau_stat_handler.tau_stats["vartau_0_m"]) + cov_tau_m_a = np.diag(tau_stats["a"]["vartau_0_m"]) + cov_tau_m_b = np.diag(tau_stats["b"]["vartau_0_m"]) cov_rho_m = np.diag(self.rho_stat_handler.rho_stats["varrho_0_m"]) # Compute the scale-dependent xi_psf_sys and its error bars From a1a61986ab19c2fa046942c69dc6f6e75d905c67 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 22:17:20 +0200 Subject: [PATCH 065/107] psf_systematics: xi_psf_sys errorbars in the legend's version colour Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- src/sp_validation/cosmo_val/psf_systematics.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 1823a82e..e33784c2 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -1463,11 +1463,21 @@ def _scale_dependent_xi_psf_sys_x_y_plot_function( y_minus_err = xi_psf_sys_minus_err * (theta if times_theta else 1) ax_plus.errorbar( - jittered_theta, y_plus, yerr=y_plus_err, fmt=fmt, capsize=capsize + jittered_theta, + y_plus, + yerr=y_plus_err, + fmt=fmt, + capsize=capsize, + color=color, ) ax_minus.errorbar( - jittered_theta, y_minus, yerr=y_minus_err, fmt=fmt, capsize=capsize + jittered_theta, + y_minus, + yerr=y_minus_err, + fmt=fmt, + capsize=capsize, + color=color, ) def _get_jittered_theta(self, theta, idx, n_versions, offset): From 0f63bfd58bccd1884235248cff9a95de0f77f978 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 22:17:31 +0200 Subject: [PATCH 066/107] Object-wise leakage plot reads per-bin coefficients; alpha summaries from rho/tau plot_objectwise_leakage(tomography=False, cov_type=None) compares the object-wise of each version and tomographic bin with the scale-dependent alpha(theta) = tau_0 / rho_0, summarised as alpha_mean (inverse-variance weighted mean and std), alpha_1 (smallest theta) and alpha_0 (intercept of a weighted affine fit). calculate_alpha_leakage_summaries stores them in leakage_coeff[ver]["tomo_bin_"]; _load_alpha_leakage is shared with plot_scale_dependent_alpha. Marker is the version, colour the version (non-tomographic) or the bin. A version missing a catalogue column is dropped cleanly from the bin loop. The galaxy mask reads HDU 1, the table LeakageObject.read_data selects rows from. cv_objectwise_leakage takes the rho/tau products as inputs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- .../cosmo_val/psf_systematics.py | 291 ++++++++++++------ workflow/rules/cosmo_val.smk | 3 + workflow/scripts/cv_objectwise_leakage.py | 8 +- 3 files changed, 198 insertions(+), 104 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index e33784c2..2f7c8a46 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -210,8 +210,13 @@ def calculate_scale_dependent_leakage(self): self.print_done("Finished scale-dependent leakage calculation.") def calculate_objectwise_leakage(self, tomography=False): - # TODO: Upgrade for tomography - # Get the tomographic bins + """Fit the object-wise PSF leakage, per version and tomographic bin. + + Stores ``a11``, ``a22`` and ``aii_mean`` (as ufloats) in + ``self.leakage_coeff[ver]["tomo_bin_"]``, with ```` ``all`` when + ``tomography`` is False. A version whose catalogue lacks a required + column is dropped from ``results_objectwise`` and ``leakage_coeff``. + """ tomo_bins = self._get_tomo_bins_for_versions( self.versions, tomography=tomography ) @@ -224,62 +229,67 @@ def calculate_objectwise_leakage(self, tomography=False): for ver in self.versions: self.print_magenta(ver) - self.leakage_coeff.setdefault(ver, {}) - results_obj = self.results_objectwise[ver] results_obj.check_params() results_obj.update_params() results_obj.prepare_output() - # Skip read_data() and copy catalogue from scale leakage instance instead - # results_obj._dat = self.results[ver].dat_shear + coeff_ver = self.leakage_coeff.setdefault(ver, {}) + try: + for tomo_bin_id in tomo_bins[ver]["ids"]: + coeff_ver[f"tomo_bin_{tomo_bin_id}"] = self._objectwise_leakage_bin( + results_obj, ver, tomo_bin_id, mix, order + ) + except KeyError as e: + print(f"{e}\nExpected key is missing from catalog.") + self.results_objectwise.pop(ver) + self.leakage_coeff.pop(ver) + + def _objectwise_leakage_bin(self, results_obj, ver, tomo_bin_id, mix, order): + """Object-wise leakage coefficients of one tomographic bin.""" + selection = ( + None if tomo_bin_id == "all" else self._get_galaxy_mask(ver, tomo_bin_id) + ) + suffix = f"tomo_bin_{tomo_bin_id}" - # Iterate on the tomographic bins for this version + out_path = f"{results_obj.get_out_base(mix, order, suffix=suffix)}.pkl" + if os.path.exists(out_path): + self.print_green(f"Skipping object-wise leakage, file {out_path} exists") + results_obj.par_best_fit = leakage.read_from_file(out_path) + else: + self.print_cyan(f"Computing object-wise leakage regression, {suffix}") + with results_obj.temporarily_read_data(selection=selection): + results_obj.PSF_leakage(mix=mix, order=order, suffix=suffix) + + par_best_fit = results_obj.par_best_fit + a11 = ufloat(par_best_fit["a11"].value, par_best_fit["a11"].stderr) + a22 = ufloat(par_best_fit["a22"].value, par_best_fit["a22"].stderr) + return {"a11": a11, "a22": a22, "aii_mean": 0.5 * (a11 + a22)} + + def calculate_alpha_leakage_summaries(self, tomography=False, cov_type=None): + """Summarise the scale-dependent leakage alpha(theta) = tau_0 / rho_0. + + For each version in ``leakage_coeff`` and each of its tomographic bins, + stores ``alpha_mean``, ``alpha_1`` and ``alpha_0`` (see + `_alpha_summaries`) next to the object-wise coefficients in + ``self.leakage_coeff[ver]["tomo_bin_"]``. + """ + tomo_bins = self._get_tomo_bins_for_versions( + list(self.leakage_coeff), tomography=tomography + ) + for ver, coeff_ver in self.leakage_coeff.items(): for tomo_bin_id in tomo_bins[ver]["ids"]: - if tomo_bin_id == "all": - selection = None - else: - selection = self._get_galaxy_mask(ver, tomo_bin_id) - - suffix = f"tomo_bin_{tomo_bin_id}" - - out_base = results_obj.get_out_base(mix, order, suffix=suffix) - out_path = f"{out_base}.pkl" - if os.path.exists(out_path): - self.print_green( - f"Skipping object-wise leakage, file {out_path} exists" - ) - results_obj.par_best_fit = leakage.read_from_file(out_path) - else: - self.print_cyan("Computing object-wise leakage regression") - - # Run - with results_obj.temporarily_read_data(selection=selection): - try: - results_obj.PSF_leakage(suffix=suffix) - - # Gather coefficients - - except KeyError as e: - print(f"{e}\nExpected key is missing from catalog.") - # remove the results object for this version - self.results_objectwise.pop(ver) - continue - - par_best_fit = results_obj.par_best_fit - - # Object-wise leakage - a11 = ufloat(par_best_fit["a11"].value, par_best_fit["a11"].stderr) - a22 = ufloat(par_best_fit["a22"].value, par_best_fit["a22"].stderr) - self.leakage_coeff[ver][f"tomo_bin_{tomo_bin_id}"] = { - "a11": a11, - "a22": a22, - "aii_mean": 0.5 * (a11 + a22), - } + theta, alpha, alpha_err = self._load_alpha_leakage( + ver, tomo_bin_id, cov_type + ) + coeff_ver.setdefault(f"tomo_bin_{tomo_bin_id}", {}).update( + self._alpha_summaries(theta, alpha, alpha_err) + ) # --- utility functions --- def _get_galaxy_mask(self, ver, tomo_bin_id): - cat_gal = fits.getdata(self.cc[ver]["shear"]["path"]) + # HDU 1 is the table LeakageObject.read_data reads, so masks align by row + cat_gal = fits.getdata(self.cc[ver]["shear"]["path"], ext=1) if tomo_bin_id != "all": gal_mask = cat_gal[self.cc[ver]["shear"]["tomo_bin_col"]] == tomo_bin_id else: @@ -457,6 +467,34 @@ def get_xi_psf_sys_samples(self, ver, params, tomo_bin_a, tomo_bin_b): return xi_psf_sys_samples_plus, xi_psf_sys_samples_minus + def _load_alpha_leakage(self, ver, tomo_bin_id, cov_type=None, seed=0): + """Alpha(theta) of one version and tomographic bin from its rho/tau files. + + With ``cov_type`` None the errors use the variances in the rho/tau + files; otherwise the ``cov_tau_*_.npy`` and jackknife + ``cov_rho_*_jk.npy`` covariances under ``rho_tau_stats/``. + """ + base_rho = self.basename(ver) + base_tau = self.basename(ver, tomo_bin_a=tomo_bin_id) + self.rho_stat_handler.load_rho_stats(f"rho_stats_{base_rho}.fits") + self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau}.fits") + + if cov_type is not None: + out_dir = Path(self.cc["paths"]["output"]) / "rho_tau_stats" + cov_tau = np.load(out_dir / f"cov_tau_{base_tau}_{cov_type}.npy") + cov_rho = np.load(out_dir / f"cov_rho_{base_rho}_jk.npy") + else: + cov_tau = None + cov_rho = None + + return self._get_alpha_leakage( + self.rho_stat_handler, + self.tau_stat_handler, + cov_rho, + cov_tau, + seed=seed, + ) + def _get_alpha_leakage( self, rho_stat_handler, @@ -525,6 +563,37 @@ def _get_alpha_leakage( return theta, alpha, alpha_err + @staticmethod + def _alpha_summaries(theta, alpha, alpha_err): + """Scalar summaries of alpha(theta), as ufloats. + + Returns + ------- + dict + ``alpha_mean``: inverse-variance weighted mean of alpha(theta), with + the weighted standard deviation; ``alpha_1``: alpha and its error at + the smallest theta; ``alpha_0``: intercept c of the weighted + least-squares affine fit alpha(theta) = c + m theta, with its error + from the fit covariance (errors taken as absolute). + """ + theta = np.asarray(theta, dtype=float) + alpha = np.asarray(alpha, dtype=float) + alpha_err = np.asarray(alpha_err, dtype=float) + + weights = 1 / alpha_err**2 + mean = np.average(alpha, weights=weights) + std = np.sqrt(np.average((alpha - mean) ** 2, weights=weights)) + + i_min = np.argmin(theta) + + (_, c), cov = np.polyfit(theta, alpha, 1, w=1 / alpha_err, cov="unscaled") + + return { + "alpha_mean": ufloat(mean, std), + "alpha_1": ufloat(alpha[i_min], alpha_err[i_min]), + "alpha_0": ufloat(c, np.sqrt(cov[1, 1])), + } + def _compute_scale_dependent_xi_psf_sys( self, rho_0, @@ -1125,8 +1194,6 @@ def plot_scale_dependent_alpha( n_tomo_bins_plot = max(len(bins["ids"]) for bins in tomo_bins.values()) - out_dir = f"{self.cc['paths']['output']}/rho_tau_stats" - fig, axs = plt.subplots( n_tomo_bins_plot, 1, figsize=(8, 3 * n_tomo_bins_plot), sharex=True ) @@ -1136,23 +1203,8 @@ def plot_scale_dependent_alpha( label = self.cc[ver]["label"] if "label" in self.cc[ver] else ver # Iterate upon each tomographic bin for tomo_bin_id in tomo_bins[ver]["ids"]: - base_rho = self.basename(ver) - base_tau = self.basename(ver, tomo_bin_a=tomo_bin_id) - self.rho_stat_handler.load_rho_stats(f"rho_stats_{base_rho}.fits") - self.tau_stat_handler.load_tau_stats(f"tau_stats_{base_tau}.fits") - - if cov_type is not None: - cov_tau_path = Path(out_dir) / f"cov_tau_{base_tau}_{cov_type}.npy" - cov_tau = np.load(cov_tau_path) - cov_rho_path = Path(out_dir) / f"cov_rho_{base_rho}_jk.npy" - cov_rho = np.load(cov_rho_path) - else: - cov_tau = None - cov_rho = None - - # Get the error bar sampling from the covariance matrices - theta, alpha, alpha_err = self._get_alpha_leakage( - self.rho_stat_handler, self.tau_stat_handler, cov_rho, cov_tau + theta, alpha, alpha_err = self._load_alpha_leakage( + ver, tomo_bin_id, cov_type ) jittered_theta = self._get_jittered_theta( @@ -1217,49 +1269,88 @@ def plot_scale_dependent_alpha( if close: plt.close() - def plot_objectwise_leakage(self): - if not hasattr(self, "leakage_coeff"): - self.calculate_objectwise_leakage() + def plot_objectwise_leakage(self, tomography=False, cov_type=None): + """Plot object-wise leakage against the scale-dependent alpha. + + x is the object-wise , y the alpha(theta) summaries from the + rho/tau products (fill style: mean, smallest scale, affine intercept). + Marker shape is the version; colour is the version (non-tomographic) or + the tomographic bin. Saved to ``leakage_coefficients.png``, or + ``leakage_coefficients_tomo.png`` when ``tomography`` is True. + """ + tomo_bins = self._get_tomo_bins_for_versions( + self.versions, tomography=tomography + ) + existing = getattr(self, "leakage_coeff", {}) + if any( + ver in self.results_objectwise + and any(f"tomo_bin_{i}" not in existing.get(ver, {}) for i in bins["ids"]) + for ver, bins in tomo_bins.items() + ): + self.calculate_objectwise_leakage(tomography=tomography) + self.calculate_alpha_leakage_summaries(tomography=tomography, cov_type=cov_type) self.print_start("Plotting object-wise leakage:") cs_plots.figure(figsize=(15, 15)) - linestyles = ["-", "--", ":"] - fillstyles = ["full", "none", "left", "right", "bottom", "top"] - - for ver in self.results_objectwise: - label = ver - for key, ls, fs in zip( - ["alpha_mean", "alpha_1", "alpha_0"], linestyles, fillstyles - ): - x = self.leakage_coeff[ver]["aii_mean"].nominal_value - dx = self.leakage_coeff[ver]["aii_mean"].std_dev - y = self.leakage_coeff[ver][key].nominal_value - dy = self.leakage_coeff[ver][key].std_dev - - eb = plt.errorbar( - x, - y, - xerr=dx, - yerr=dy, - fmt=self.cc[ver]["marker"], - color=self.cc[ver]["colour"], - fillstyle=fs, - label=label, + summaries = { + "alpha_mean": (r"$\bar\alpha$", "-", "full"), + "alpha_1": (r"$\alpha(\theta_{\rm min})$", "--", "none"), + "alpha_0": (r"$\alpha(0)$", ":", "left"), + } + max_bins = max(len(bins["ids"]) for bins in tomo_bins.values()) + bin_colours = plt.get_cmap("viridis")(np.linspace(0, 0.9, max_bins)) + + handles = [] + for ver in self.leakage_coeff: + marker = self.cc[ver]["marker"] + ver_colour = "k" if tomography else self.cc[ver]["colour"] + for i_bin, tomo_bin_id in enumerate(tomo_bins[ver]["ids"]): + coeff = self.leakage_coeff[ver][f"tomo_bin_{tomo_bin_id}"] + colour = bin_colours[i_bin] if tomography else ver_colour + for key, (_, ls, fs) in summaries.items(): + eb = plt.errorbar( + coeff["aii_mean"].nominal_value, + coeff[key].nominal_value, + xerr=coeff["aii_mean"].std_dev, + yerr=coeff[key].std_dev, + fmt=marker, + color=colour, + fillstyle=fs, + ) + eb[-1][0].set_linestyle(ls) + handles.append( + plt.Line2D( + [], + [], + marker=marker, + ls="", + color=ver_colour, + label=self.cc[ver].get("label", ver), ) - label = None - eb[-1][0].set_linestyle(ls) + ) + + if tomography: + handles += [ + plt.Line2D([], [], marker="s", ls="", color=c, label=f"bin {i + 1}") + for i, c in enumerate(bin_colours) + ] + handles += [ + plt.Line2D([], [], marker="o", ls=ls, color="grey", fillstyle=fs, label=lab) + for lab, ls, fs in summaries.values() + ] # y=x line xlim = 0.02 x = [-xlim, xlim] - y = x - plt.plot(x, y, "k:", linewidth=0.5) + plt.plot(x, x, "k:", linewidth=0.5) - plt.legend() - plt.xlabel(r"tr $a$ (object-wise)") - plt.ylabel(r"$\alpha$ (scale-dependent)") - out_path = self._output_path("leakage_coefficients.png") + plt.legend(handles=handles) + plt.xlabel(r"$\langle a_{ii} \rangle$ (object-wise)") + plt.ylabel(r"$\alpha$ (scale-dependent, $\tau_0 / \rho_0$)") + out_path = self._output_path( + f"leakage_coefficients{'_tomo' if tomography else ''}.png" + ) cs_plots.savefig(out_path, close_fig=False) cs_plots.show() self.print_done(f"Object-wise leakage coefficients plot saved to {out_path}") diff --git a/workflow/rules/cosmo_val.smk b/workflow/rules/cosmo_val.smk index 704b4fb0..478c24cd 100644 --- a/workflow/rules/cosmo_val.smk +++ b/workflow/rules/cosmo_val.smk @@ -191,6 +191,9 @@ rule cv_footprints: rule cv_objectwise_leakage: """Object-wise PSF-leakage regression vs scale-dependent alpha (all versions).""" + input: + rho=[cv_rho_stats(v) for v in CV_VERSIONS], + tau=[cv_tau_stats(v) for v in CV_VERSIONS], output: sentinel=str(CV_SENTINELS / "objectwise_leakage.done"), params: diff --git a/workflow/scripts/cv_objectwise_leakage.py b/workflow/scripts/cv_objectwise_leakage.py index 8b6d5694..5edd95ae 100644 --- a/workflow/scripts/cv_objectwise_leakage.py +++ b/workflow/scripts/cv_objectwise_leakage.py @@ -1,9 +1,9 @@ """Rule cv_objectwise_leakage: object-wise vs scale-dependent PSF leakage. -plot_objectwise_leakage triggers calculate_objectwise_leakage, which itself -triggers calculate_scale_dependent_leakage — the whole leakage chain runs here. -Writes leakage_{version}/ products (the object-wise regression .pkl is the -durable artifact) and leakage_coefficients.png. Sentinel-tracked because the +plot_objectwise_leakage runs calculate_objectwise_leakage (the per-version +regression) and summarises the scale-dependent alpha(theta) = tau_0 / rho_0 from +the rho/tau products. Writes leakage_{version}/ products (the object-wise +regression .pkl is the durable artifact) and leakage_coefficients.png. Sentinel-tracked because the per-version leakage product paths are built inside shear_psf_leakage. """ From fedde756efc8ecf0f1c152bdf4ef81f0883f06cf Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 22:18:12 +0200 Subject: [PATCH 067/107] Test PSF-leakage helpers and the object-wise plot on synthetic data xi_psf_sys: central value, auto-pair error 2|tau|sigma/rho from one tau draw, cross-pair error from both bins' variances, seed reproducibility. alpha: central value tau/rho and reproducibility. Summaries recover the affine intercept. Smoke test: object-wise regression per bin (real shear_psf_leakage) and the comparison plot, with alpha(theta) stubbed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- src/sp_validation/tests/test_psf_leakage.py | 206 ++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 src/sp_validation/tests/test_psf_leakage.py diff --git a/src/sp_validation/tests/test_psf_leakage.py b/src/sp_validation/tests/test_psf_leakage.py new file mode 100644 index 00000000..2aea5364 --- /dev/null +++ b/src/sp_validation/tests/test_psf_leakage.py @@ -0,0 +1,206 @@ +"""PSF-leakage helpers of the cosmology validation: alpha, xi_psf_sys, summaries. + +Synthetic inputs only. The object-wise smoke test runs the real +shear_psf_leakage regression on a toy catalogue and therefore needs the +scientific stack (the container). +""" + +from types import SimpleNamespace + +import numpy as np +import pytest +import yaml + +from sp_validation.cosmo_val.psf_systematics import PSFSystematicsMixin + +mixin = PSFSystematicsMixin() + +N_THETA = 5 +RHO = np.full(N_THETA, 2.0) +TINY = np.diag(np.full(N_THETA, 1e-16)) + + +def _xi_sys(tau_a, tau_b, sig_a, sig_b, same_bin, seed=0, n_samples=40_000): + return mixin._compute_scale_dependent_xi_psf_sys( + RHO, + tau_a, + tau_b, + TINY, + np.diag(np.full(N_THETA, sig_a**2)), + np.diag(np.full(N_THETA, sig_b**2)), + n_samples=n_samples, + same_bin=same_bin, + seed=seed, + ) + + +def test_xi_sys_central_value(): + tau_a = np.linspace(1.0, 2.0, N_THETA) + tau_b = np.linspace(0.5, 3.0, N_THETA) + xi, _ = _xi_sys(tau_a, tau_b, 0.01, 0.02, same_bin=False) + np.testing.assert_allclose(xi, tau_a * tau_b / RHO) + + +def test_xi_sys_auto_pair_reuses_tau_draw(): + mu, sig = 1.0, 0.01 + tau = np.full(N_THETA, mu) + _, err = _xi_sys(tau, tau, sig, sig, same_bin=True) + np.testing.assert_allclose(err, 2 * mu * sig / RHO, rtol=0.03) + + +def test_xi_sys_cross_pair_uses_both_variances(): + mu_a, mu_b, sig_a, sig_b = 1.0, 3.0, 0.01, 0.03 + _, err = _xi_sys( + np.full(N_THETA, mu_a), np.full(N_THETA, mu_b), sig_a, sig_b, same_bin=False + ) + expected = np.sqrt(mu_b**2 * sig_a**2 + mu_a**2 * sig_b**2) / RHO + np.testing.assert_allclose(err, expected, rtol=0.03) + + +def test_xi_sys_errors_reproducible(): + tau = np.full(N_THETA, 1.0) + _, err_1 = _xi_sys(tau, tau, 0.1, 0.1, same_bin=False, seed=7) + _, err_2 = _xi_sys(tau, tau, 0.1, 0.1, same_bin=False, seed=7) + _, err_3 = _xi_sys(tau, tau, 0.1, 0.1, same_bin=False, seed=8) + np.testing.assert_array_equal(err_1, err_2) + assert not np.array_equal(err_1, err_3) + + +def _handlers(rho, tau, var_rho, var_tau): + theta = np.geomspace(1, 100, len(rho)) + rho_h = SimpleNamespace( + rho_stats={"theta": theta, "rho_0_p": rho, "varrho_0_p": var_rho} + ) + tau_h = SimpleNamespace(tau_stats={"tau_0_p": tau, "vartau_0_p": var_tau}) + return rho_h, tau_h + + +def test_alpha_leakage_central_value_and_reproducible(): + rho = np.linspace(1.0, 2.0, N_THETA) + tau = np.linspace(0.01, 0.03, N_THETA) + rho_h, tau_h = _handlers(rho, tau, np.full(N_THETA, 1e-4), np.full(N_THETA, 1e-6)) + theta, alpha, err = mixin._get_alpha_leakage(rho_h, tau_h, seed=3) + np.testing.assert_array_equal(theta, rho_h.rho_stats["theta"]) + np.testing.assert_allclose(alpha, tau / rho) + _, _, err_again = mixin._get_alpha_leakage(rho_h, tau_h, seed=3) + np.testing.assert_array_equal(err, err_again) + assert np.all(err > 0) + + +def test_alpha_summaries_recover_affine_model(): + c, m = 0.01, 2e-4 + theta = np.geomspace(1, 100, 12) + alpha_err = np.linspace(1e-3, 3e-3, theta.size) + alpha = c + m * theta + + out = PSFSystematicsMixin._alpha_summaries(theta, alpha, alpha_err) + + assert out["alpha_0"].nominal_value == pytest.approx(c, rel=1e-8) + assert 0 < out["alpha_0"].std_dev < alpha_err.max() + assert out["alpha_1"].nominal_value == pytest.approx(alpha[0]) + assert out["alpha_1"].std_dev == pytest.approx(alpha_err[0]) + w = 1 / alpha_err**2 + mean = np.sum(w * alpha) / np.sum(w) + assert out["alpha_mean"].nominal_value == pytest.approx(mean) + std = np.sqrt(np.sum(w * (alpha - mean) ** 2) / np.sum(w)) + assert out["alpha_mean"].std_dev == pytest.approx(std) + + +def _objectwise_config(tmp_path, n_gal=3000, seed=11): + """Toy shear catalogue with PSF columns and two tomographic bins.""" + from astropy.table import Table + + rng = np.random.default_rng(seed) + cat_dir = tmp_path / "catalog" + cat_dir.mkdir() + output_dir = tmp_path / "output" + output_dir.mkdir() + + e1_psf = rng.normal(0, 0.03, n_gal) + e2_psf = rng.normal(0, 0.03, n_gal) + tomo_bin = rng.integers(1, 3, n_gal) + leak = np.where(tomo_bin == 1, 0.05, 0.15) + Table( + { + "RA": rng.uniform(10, 12, n_gal), + "Dec": rng.uniform(10, 12, n_gal), + "e1": leak * e1_psf + rng.normal(0, 0.02, n_gal), + "e2": leak * e2_psf + rng.normal(0, 0.02, n_gal), + "w": np.ones(n_gal), + "e1_PSF": e1_psf, + "e2_PSF": e2_psf, + "fwhm_PSF": rng.uniform(0.6, 0.8, n_gal), + "tomo_bin": tomo_bin, + } + ).write(cat_dir / "shear.fits") + + version = "TestCatalog" + config = { + "nz": {"subdir": str(cat_dir), "dndz": {"blind": "A", "path": "dndz"}}, + "paths": {"output": str(output_dir)}, + version: { + "subdir": str(cat_dir), + "pipeline": "SP", + "colour": "C0", + "marker": "o", + "shear": { + "path": "shear.fits", + "w_col": "w", + "e1_col": "e1", + "e2_col": "e2", + "e1_PSF_col": "e1_PSF", + "e2_PSF_col": "e2_PSF", + "tomo_bin_col": "tomo_bin", + "R": 1.0, + }, + }, + } + config_path = tmp_path / "config.yaml" + config_path.write_text(yaml.dump(config, sort_keys=False)) + return version, { + "catalog_config": str(config_path), + "output_dir": str(output_dir), + } + + +def test_objectwise_leakage_and_plot_smoke(tmp_path, monkeypatch): + """Object-wise fit per bin and the comparison plot, alpha(theta) stubbed. + + The rho/tau products are replaced by a synthetic alpha(theta), so this + covers the object-wise regression, the per-bin storage and the plot, not + the rho/tau loading. + """ + pytest.importorskip("shear_psf_leakage") + import matplotlib + + matplotlib.use("Agg") + # TeX text rendering dominates the run time of the ~20 diagnostic figures + monkeypatch.setitem(matplotlib.rcParams, "text.usetex", False) + from sp_validation.cosmo_val import CosmologyValidation + + version, params = _objectwise_config(tmp_path) + cv = CosmologyValidation(versions=[version], npatch=1, **params) + + theta = np.geomspace(1, 100, 6) + monkeypatch.setattr( + cv, + "_load_alpha_leakage", + lambda ver, tomo_bin_id, cov_type=None: ( + theta, + 0.01 + 1e-4 * theta, + np.full(theta.size, 1e-3), + ), + ) + + cv.plot_objectwise_leakage() + coeff = cv.leakage_coeff[version]["tomo_bin_all"] + assert {"a11", "a22", "aii_mean", "alpha_mean", "alpha_1", "alpha_0"} <= set(coeff) + assert coeff["alpha_0"].nominal_value == pytest.approx(0.01) + assert (tmp_path / "output" / "leakage_coefficients.png").exists() + + cv.plot_objectwise_leakage(tomography=True) + for tomo_bin_id, expected in ((1, 0.05), (2, 0.15)): + bin_coeff = cv.leakage_coeff[version][f"tomo_bin_{tomo_bin_id}"] + assert bin_coeff["aii_mean"].nominal_value == pytest.approx(expected, abs=0.03) + assert "alpha_mean" in bin_coeff + assert (tmp_path / "output" / "leakage_coefficients_tomo.png").exists() From 03b37a39b6c7eaedb79b63660b05ddb75aeb3efa Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:03:18 +0200 Subject: [PATCH 068/107] Object-wise leakage: a version dropped for a missing column stays dropped Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- .../cosmo_val/psf_systematics.py | 3 ++ src/sp_validation/tests/test_psf_leakage.py | 36 +++++++++++++++++++ workflow/rules/cosmo_val.smk | 3 -- workflow/scripts/cv_objectwise_leakage.py | 8 ++--- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 2f7c8a46..3fee6b33 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -227,6 +227,9 @@ def calculate_objectwise_leakage(self, tomography=False): if not hasattr(self, "leakage_coeff"): self.leakage_coeff = {} for ver in self.versions: + if ver not in self.results_objectwise: + # Dropped earlier for a missing catalogue column + continue self.print_magenta(ver) results_obj = self.results_objectwise[ver] diff --git a/src/sp_validation/tests/test_psf_leakage.py b/src/sp_validation/tests/test_psf_leakage.py index 2aea5364..bef99e32 100644 --- a/src/sp_validation/tests/test_psf_leakage.py +++ b/src/sp_validation/tests/test_psf_leakage.py @@ -204,3 +204,39 @@ def test_objectwise_leakage_and_plot_smoke(tmp_path, monkeypatch): assert bin_coeff["aii_mean"].nominal_value == pytest.approx(expected, abs=0.03) assert "alpha_mean" in bin_coeff assert (tmp_path / "output" / "leakage_coefficients_tomo.png").exists() + + +class _ObjectwiseControlFlow(PSFSystematicsMixin): + """Two versions, one of which lacks a catalogue column.""" + + def __init__(self): + self.versions = ["good", "bad"] + obj = SimpleNamespace( + check_params=lambda: None, + update_params=lambda: None, + prepare_output=lambda: None, + ) + self.results_objectwise = {"good": obj, "bad": obj} + + def _get_tomo_bins_for_versions(self, versions, tomography): + return {v: {"ids": [1, 2] if tomography else ["all"]} for v in versions} + + def _objectwise_leakage_bin(self, obj, ver, *args): + if ver == "bad": + raise KeyError("fwhm_PSF") + return {"aii_mean": 0.1} + + def print_start(self, *args): + pass + + def print_magenta(self, *args): + pass + + +def test_version_missing_a_column_stays_dropped(): + cv = _ObjectwiseControlFlow() + cv.calculate_objectwise_leakage() + assert set(cv.results_objectwise) == {"good"} + assert set(cv.leakage_coeff) == {"good"} + cv.calculate_objectwise_leakage(tomography=True) + assert "tomo_bin_1" in cv.leakage_coeff["good"] diff --git a/workflow/rules/cosmo_val.smk b/workflow/rules/cosmo_val.smk index 478c24cd..704b4fb0 100644 --- a/workflow/rules/cosmo_val.smk +++ b/workflow/rules/cosmo_val.smk @@ -191,9 +191,6 @@ rule cv_footprints: rule cv_objectwise_leakage: """Object-wise PSF-leakage regression vs scale-dependent alpha (all versions).""" - input: - rho=[cv_rho_stats(v) for v in CV_VERSIONS], - tau=[cv_tau_stats(v) for v in CV_VERSIONS], output: sentinel=str(CV_SENTINELS / "objectwise_leakage.done"), params: diff --git a/workflow/scripts/cv_objectwise_leakage.py b/workflow/scripts/cv_objectwise_leakage.py index 5edd95ae..8b6d5694 100644 --- a/workflow/scripts/cv_objectwise_leakage.py +++ b/workflow/scripts/cv_objectwise_leakage.py @@ -1,9 +1,9 @@ """Rule cv_objectwise_leakage: object-wise vs scale-dependent PSF leakage. -plot_objectwise_leakage runs calculate_objectwise_leakage (the per-version -regression) and summarises the scale-dependent alpha(theta) = tau_0 / rho_0 from -the rho/tau products. Writes leakage_{version}/ products (the object-wise -regression .pkl is the durable artifact) and leakage_coefficients.png. Sentinel-tracked because the +plot_objectwise_leakage triggers calculate_objectwise_leakage, which itself +triggers calculate_scale_dependent_leakage — the whole leakage chain runs here. +Writes leakage_{version}/ products (the object-wise regression .pkl is the +durable artifact) and leakage_coefficients.png. Sentinel-tracked because the per-version leakage product paths are built inside shear_psf_leakage. """ From 5c94bbcb9b13d53b4b2c3853adb954ce5afac05a Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:03:41 +0200 Subject: [PATCH 069/107] uv.lock: shear_psf_leakage develop@f1a2c071 (object-wise row selection and per-bin suffix) Re-locking also brings the lock in line with this branch's pyproject (glass 2026.2 and the OneCovariance dependencies). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmsgTjEULk4a3t5rppLpbZ --- uv.lock | 1369 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 739 insertions(+), 630 deletions(-) diff --git a/uv.lock b/uv.lock index bef8072e..0c7fa3bc 100644 --- a/uv.lock +++ b/uv.lock @@ -14,9 +14,9 @@ name = "adjusttext" version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "scipy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/5c/496e506ad3313664df24a79f801719cadcd62af5999fcb299c9b08ff0d4b/adjusttext-1.4.0.tar.gz", hash = "sha256:1f73860ced8cccce3f85ee6989ca133c2579b67a7453f63dbeb38f39bf123154", size = 15852, upload-time = "2026-06-08T16:48:32.726Z" } wheels = [ @@ -55,8 +55,8 @@ name = "anyio" version = "4.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "idna", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13' and sys_platform == 'linux'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } wheels = [ @@ -68,7 +68,7 @@ name = "argon2-cffi" version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "argon2-cffi-bindings", marker = "sys_platform == 'linux'" }, + { name = "argon2-cffi-bindings" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } wheels = [ @@ -80,7 +80,7 @@ name = "argon2-cffi-bindings" version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "sys_platform == 'linux'" }, + { name = "cffi" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } wheels = [ @@ -112,13 +112,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/16/1a8fd2b19544b84575cf84ef7aa3ad4c173b756d5f087c91f85d1b295777/array_api_compat-1.15.0-py3-none-any.whl", hash = "sha256:7b1b9c53269061403fd5f45a8de349f16e7887653328bfa0c5f2d45299ff0a8e", size = 79113, upload-time = "2026-06-07T20:53:23.621Z" }, ] +[[package]] +name = "array-api-extra" +version = "0.11.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "array-api-compat" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/63/eb18bc7d039c954da3cfb11293c7e461b03eb9682bb0b22733be43f75501/array_api_extra-0.11.4.tar.gz", hash = "sha256:66236f72aaa4cdf97a3c5581696e3a9c89513714612a9bc6010e19fad1a22012", size = 112368, upload-time = "2026-09-07T18:50:34.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/b3/fcf70b2ed935f26fc15dfd81f72f781d395b09e302af5c86ec285d63524e/array_api_extra-0.11.4-py3-none-any.whl", hash = "sha256:b2d5bd31b3072249fd3f90a2b1f7b398c78e3ccb7575e04e9e3348ae894da60f", size = 120196, upload-time = "2026-09-07T18:50:32.88Z" }, +] + [[package]] name = "arrow" version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, - { name = "tzdata", marker = "sys_platform == 'linux'" }, + { name = "python-dateutil" }, + { name = "tzdata" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } wheels = [ @@ -139,11 +151,11 @@ name = "astropy" version = "8.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy-iers-data", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pyerfa", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "astropy-iers-data" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyerfa" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0e/c4/21be4313ddfde5f60e0607fd307f367b9e0f0bf153a89b10cbd036dd8cfd/astropy-8.0.1.tar.gz", hash = "sha256:45ca31d5b91fa294cd590a4791a32db94de7f9c8a343155f4d5877baa82351da", size = 7152500, upload-time = "2026-07-05T07:24:48.482Z" } wheels = [ @@ -157,8 +169,8 @@ name = "astropy-healpix" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/bb/fb3dcca212b1d57ed26b33eabb9e1cc18fcadbfc2f2976b20530b7d80b90/astropy_healpix-2.0.0.tar.gz", hash = "sha256:6ce7646dc5b1c09a7ed24b1f23d3e2fce8e3570387220d26a24487ab001b3b0b", size = 112064, upload-time = "2026-06-29T20:02:46.407Z" } wheels = [ @@ -220,8 +232,8 @@ name = "beautifulsoup4" version = "4.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "soupsieve", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "soupsieve" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" } wheels = [ @@ -233,7 +245,7 @@ name = "bleach" version = "6.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "webencodings", marker = "sys_platform == 'linux'" }, + { name = "webencodings" }, ] sdist = { url = "https://files.pythonhosted.org/packages/48/3c/e12ac860709702bd5ebeb9b56a4fe334f1001246ee1b8f2b7ee28912df7d/bleach-6.4.0.tar.gz", hash = "sha256:4202482733d85cedd04e59fcb2f89f4e4c7c385a78d3c3c23c30446843a37452", size = 204857, upload-time = "2026-06-05T13:01:13.734Z" } wheels = [ @@ -242,7 +254,7 @@ wheels = [ [package.optional-dependencies] css = [ - { name = "tinycss2", marker = "sys_platform == 'linux'" }, + { name = "tinycss2" }, ] [[package]] @@ -250,14 +262,14 @@ name = "blosc2" version = "4.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "httpx", extra = ["http2"], marker = "sys_platform == 'linux'" }, - { name = "msgpack", marker = "sys_platform == 'linux'" }, - { name = "ndindex", marker = "sys_platform == 'linux'" }, - { name = "numexpr", marker = "platform_machine != 'wasm32' and sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pydantic", marker = "sys_platform == 'linux'" }, - { name = "rich", marker = "sys_platform == 'linux'" }, - { name = "threadpoolctl", marker = "platform_machine != 'wasm32' and sys_platform == 'linux'" }, + { name = "httpx", extra = ["http2"] }, + { name = "msgpack" }, + { name = "ndindex" }, + { name = "numexpr", marker = "platform_machine != 'wasm32'" }, + { name = "numpy" }, + { name = "pydantic" }, + { name = "rich" }, + { name = "threadpoolctl", marker = "platform_machine != 'wasm32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c1/26/c5b32a553f002e91886213ec91ff56a2dd8555e5e78eb4c289addb208642/blosc2-4.8.0.tar.gz", hash = "sha256:9789c3248052be59e63d6debafec4cd59d0eb4a6fd1cbaced36865d5d40ffa63", size = 5681856, upload-time = "2026-07-10T09:38:33.592Z" } wheels = [ @@ -273,17 +285,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/5a/012f58a518bebf0d11931a5a12ba08955aa959cca59764ad38a8d82c9a1b/blosc2-4.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f8b459e22059a3a794480df2c72ff008ab6c95b9799fa2979c1dd7830cf44e2", size = 7186485, upload-time = "2026-07-10T09:38:29.411Z" }, ] +[[package]] +name = "cached-property" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/4b/3d870836119dbe9a5e3c9a61af8cc1a8b69d75aea564572e385882d5aefb/cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641", size = 10574, upload-time = "2024-10-25T15:43:55.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/0e/7d8225aab3bc1a0f5811f8e1b557aa034ac04bdf641925b30d3caf586b28/cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb", size = 7428, upload-time = "2024-10-25T15:43:54.711Z" }, +] + [[package]] name = "cadcutils" version = "1.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "distro", marker = "sys_platform == 'linux'" }, - { name = "lxml", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pyopenssl", marker = "sys_platform == 'linux'" }, - { name = "requests", marker = "sys_platform == 'linux'" }, - { name = "setuptools", marker = "sys_platform == 'linux'" }, + { name = "distro" }, + { name = "lxml" }, + { name = "packaging" }, + { name = "pyopenssl" }, + { name = "requests" }, + { name = "setuptools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fe/44/4964823c72c9e6a93e26de0118cead37fa3c3600b4677f0c06788c903c80/cadcutils-1.6.2.tar.gz", hash = "sha256:6e2d7822756d48a363bc5f857cd717f80aab760554df86518b957f8826a7a906", size = 94673, upload-time = "2026-06-22T17:35:47.303Z" } wheels = [ @@ -295,10 +316,10 @@ name = "camb" version = "1.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "sympy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "scipy" }, + { name = "sympy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6d/ad/f2e6446fbd94f7adc1474e1f4e40d2e7fa7e5e7143640cb8557d492f0a73/camb-1.6.6.tar.gz", hash = "sha256:9856202a5c05570256e52377b20431891c7b08b2e9c334e141fd08d2a085516f", size = 799344, upload-time = "2026-03-11T16:46:55.819Z" } wheels = [ @@ -320,7 +341,7 @@ name = "cffi" version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy' and sys_platform == 'linux'" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } wheels = [ @@ -429,12 +450,12 @@ name = "clmm" version = "1.16.10" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "healpy", marker = "sys_platform == 'linux'" }, - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "qp-prob", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "healpy" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "qp-prob" }, + { name = "scipy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/73/7ff76d487cdbcddb9c80ec03313db1b11f48fe4fcb5e1609b6eca5a73159/clmm-1.16.10.tar.gz", hash = "sha256:48273784419c038a89619b798a140153ad1e451390ff56074f3b6825c91ebe32", size = 72475, upload-time = "2025-12-04T17:30:11.47Z" } wheels = [ @@ -473,7 +494,7 @@ name = "conda-inject" version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/a8/8dc86113c65c949cc72d651461d6e4c544b3302a85ed14a5298829e6a419/conda_inject-1.3.2.tar.gz", hash = "sha256:0b8cde8c47998c118d8ff285a04977a3abcf734caf579c520fca469df1cd0aac", size = 3635, upload-time = "2024-05-27T12:20:58.873Z" } wheels = [ @@ -500,7 +521,7 @@ name = "contourpy" version = "1.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } wheels = [ @@ -541,12 +562,12 @@ name = "cosmo-numba" version = "1.0.1.dev6+g188d272c6" source = { git = "https://github.com/aguinot/cosmo-numba.git?rev=main#188d272c67d6d699d7cff7a7a53ded8dec759bb0" } dependencies = [ - { name = "mpmath", marker = "sys_platform == 'linux'" }, - { name = "numba", marker = "sys_platform == 'linux'" }, - { name = "numbaquadpack", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "rocket-fft", marker = "sys_platform == 'linux'" }, - { name = "sympy", marker = "sys_platform == 'linux'" }, + { name = "mpmath" }, + { name = "numba" }, + { name = "numbaquadpack" }, + { name = "numpy" }, + { name = "rocket-fft" }, + { name = "sympy" }, ] [[package]] @@ -554,13 +575,34 @@ name = "cosmology" version = "2022.10.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c9/38/04099bb2b6a626bd69a3f117afc60f2c47c08899e0730e0e48c898bf4745/cosmology-2022.10.9.tar.gz", hash = "sha256:0c2857c9bf1fdd09f1f11ab5765df0389a4101f3a900fa11251c3f37696f02d4", size = 8488, upload-time = "2022-10-10T10:18:16.229Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2e/2c/c80db37593ad593e3f6c60705fbdf690b33f22c89edd5507d2731346cd87/cosmology-2022.10.9-py3-none-any.whl", hash = "sha256:3903658c2474177a1a1c75771ae14458d93200c516f8fc6c3f4d776f3287b288", size = 9341, upload-time = "2022-10-10T10:18:14.928Z" }, ] +[[package]] +name = "cosmology-api" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/f4/801cab91dab5b2e8102ed54cc4e2bd257f319c9047db182fcaf53706df6a/cosmology_api-0.3.2.tar.gz", hash = "sha256:7ccdfdf20f91dfc2282aee059adf2530bea6e194a0f8d01487e15a7497cf4694", size = 17338, upload-time = "2025-05-15T13:29:52.355Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/8c/6cc73fafff9f4d75e77a9c3cf5b9c34aa42841cfc7da5c89737daa2c3bd7/cosmology_api-0.3.2-py3-none-any.whl", hash = "sha256:9391ef0b2616bbf4217fefdb94a72370766d59a2f0558b5ade224a92ea093a61", size = 18685, upload-time = "2025-05-15T13:29:50.833Z" }, +] + +[[package]] +name = "cosmology-compat-camb" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/2e/a637aae1677d02337be371c9a7b7e9d5051199bb58a2386781230adcee45/cosmology_compat_camb-0.2.0.tar.gz", hash = "sha256:e36fda04a78e16fc1a5c4aa22c30ec1bf64bbe0b092aa1b894ca746ce5e5551b", size = 4562, upload-time = "2025-05-08T10:31:43.226Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/42/1a657f9b3540f355f34af735d8643aae1480620689a72423fe69aeeb78e5/cosmology_compat_camb-0.2.0-py3-none-any.whl", hash = "sha256:eb6e74290bb6a6a60a47d1338fc233ba1697058ed7f01a01de1180dbee1bd75d", size = 3903, upload-time = "2025-05-08T10:31:41.997Z" }, +] + [[package]] name = "coverage" version = "7.15.0" @@ -615,7 +657,7 @@ name = "cryptography" version = "49.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'linux'" }, + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" } wheels = [ @@ -659,18 +701,18 @@ name = "cs-util" version = "0.2.2" source = { git = "https://github.com/CosmoStat/cs_util.git?rev=develop#bedcdbf50ac57fc4d0b72a62b251373cd1807824" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "camb", marker = "sys_platform == 'linux'" }, - { name = "datetime", marker = "sys_platform == 'linux'" }, - { name = "healpy", marker = "sys_platform == 'linux'" }, - { name = "healsparse", marker = "sys_platform == 'linux'" }, - { name = "keyring", marker = "sys_platform == 'linux'" }, - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pyccl", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "swig", marker = "sys_platform == 'linux'" }, - { name = "vos", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "camb" }, + { name = "datetime" }, + { name = "healpy" }, + { name = "healsparse" }, + { name = "keyring" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pyccl" }, + { name = "scipy" }, + { name = "swig" }, + { name = "vos" }, ] [[package]] @@ -687,13 +729,13 @@ name = "dask" version = "2026.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'linux'" }, - { name = "cloudpickle", marker = "sys_platform == 'linux'" }, - { name = "fsspec", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "partd", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "toolz", marker = "sys_platform == 'linux'" }, + { name = "click" }, + { name = "cloudpickle" }, + { name = "fsspec" }, + { name = "packaging" }, + { name = "partd" }, + { name = "pyyaml" }, + { name = "toolz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/b5/aa50877159f1efd65a744107e0662a15d2b80fa0c427d980313367f49493/dask-2026.7.0.tar.gz", hash = "sha256:b039970642ce97a063070bae2763dada8aae27e3cbd8ff6d894072f920a1e252", size = 11548914, upload-time = "2026-07-06T16:58:36.496Z" } wheels = [ @@ -702,7 +744,7 @@ wheels = [ [package.optional-dependencies] array = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] [[package]] @@ -710,11 +752,11 @@ name = "dask-image" version = "2026.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "dask", extra = ["array"], marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pims", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "tifffile", marker = "sys_platform == 'linux'" }, + { name = "dask", extra = ["array"] }, + { name = "numpy" }, + { name = "pims" }, + { name = "scipy" }, + { name = "tifffile" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/49/e592a13a5e1efdcdb8f1faab7c4e309c61648792e276f9ced5fb79381b33/dask_image-2026.5.0.tar.gz", hash = "sha256:ed6b462277e691b2c12b0890ba801a0f9a00cc1894b0aa71b195a7a1419b2b00", size = 80457, upload-time = "2026-05-27T14:05:57.383Z" } wheels = [ @@ -726,8 +768,8 @@ name = "datetime" version = "6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pytz", marker = "sys_platform == 'linux'" }, - { name = "zope-interface", marker = "sys_platform == 'linux'" }, + { name = "pytz" }, + { name = "zope-interface" }, ] sdist = { url = "https://files.pythonhosted.org/packages/77/32/decbfd165e9985ba9d8c2d34a39afe5aeba2fc3fe390eb6e9ef1aab98fa8/datetime-6.0.tar.gz", hash = "sha256:c1514936d2f901e10c8e08d83bf04e6c9dbd7ca4f244da94fec980980a3bc4d5", size = 64167, upload-time = "2025-11-25T08:00:34.586Z" } wheels = [ @@ -769,13 +811,25 @@ name = "deprecated" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "wrapt", marker = "sys_platform == 'linux'" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] +[[package]] +name = "deprecation" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788, upload-time = "2020-04-20T14:23:38.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178, upload-time = "2020-04-20T14:23:36.581Z" }, +] + [[package]] name = "dill" version = "0.4.1" @@ -817,7 +871,7 @@ name = "donfig" version = "0.8.1.post1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/71/80cc718ff6d7abfbabacb1f57aaa42e9c1552bfdd01e64ddd704e4a03638/donfig-0.8.1.post1.tar.gz", hash = "sha256:3bef3413a4c1c601b585e8d297256d0c1470ea012afa6e8461dc28bfb7c23f52", size = 19506, upload-time = "2024-05-23T14:14:31.513Z" } wheels = [ @@ -838,7 +892,7 @@ name = "ducc0" version = "0.41.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cf/e7/849dd1a079b994184fa53905d47278f86d0dd3dcfb6ff2b0e3eb7d6a1c1b/ducc0-0.41.0.tar.gz", hash = "sha256:bac084745bbdb243482a4aec3ecc857bdd46faec298c67e48bc0bb90350dbabd", size = 353129, upload-time = "2026-03-26T18:56:04.669Z" } wheels = [ @@ -857,7 +911,7 @@ name = "emcee" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/53/1045ee878cb24281387079f8ee4f0ade1622c6aae1ed1fd91a53e4fa5b19/emcee-3.1.6.tar.gz", hash = "sha256:11af4daf6ab8f9ca69681e3c29054665db7bbd87fd4eb8e437d2c3a1248c637d", size = 2871117, upload-time = "2024-04-19T10:03:19.555Z" } wheels = [ @@ -887,7 +941,7 @@ name = "fitsio" version = "1.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/6b/5658778e070faaa194f1a1133527cedfa948d173daed29d61f25c5487ab6/fitsio-1.4.1.tar.gz", hash = "sha256:2b2bf0bfd37790114b44283931c59a14b1fec25a653bf65c4fc555b8d3b24de9", size = 4148217, upload-time = "2026-07-10T21:21:31.585Z" } wheels = [ @@ -906,7 +960,7 @@ name = "flt" version = "2026.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/80/66/538256582e373d81a944c0ee3a12fd0e4dd0f39d10136353c4ce914d7061/flt-2026.1.tar.gz", hash = "sha256:4bf2818d4a9137f5f715fd2ff13acfbf0175639373155cd89ea2b788edf00093", size = 12817, upload-time = "2026-03-20T22:47:29.754Z" } wheels = [ @@ -977,11 +1031,11 @@ name = "getdist" version = "1.6.1" source = { git = "https://github.com/benabed/getdist.git?rev=113cd22a9a0d013b6f72fe734be81f260f3d3be5#113cd22a9a0d013b6f72fe734be81f260f3d3be5" } dependencies = [ - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "scipy" }, ] [[package]] @@ -989,7 +1043,7 @@ name = "gitdb" version = "4.0.12" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "smmap", marker = "sys_platform == 'linux'" }, + { name = "smmap" }, ] sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } wheels = [ @@ -1001,7 +1055,7 @@ name = "gitpython" version = "3.1.50" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "gitdb", marker = "sys_platform == 'linux'" }, + { name = "gitdb" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" } wheels = [ @@ -1010,18 +1064,28 @@ wheels = [ [[package]] name = "glass" -version = "2025.1" +version = "2026.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cosmology", marker = "sys_platform == 'linux'" }, - { name = "healpix", marker = "sys_platform == 'linux'" }, - { name = "healpy", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "transformcl", marker = "sys_platform == 'linux'" }, + { name = "array-api-compat" }, + { name = "array-api-extra" }, + { name = "healpix" }, + { name = "healpy" }, + { name = "transformcl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/d6/26cd58e75834671259f26b9287a584d726e6023da35ddf7ca3b7f2c393fb/glass-2025.1.tar.gz", hash = "sha256:7b1aa2394e16010f7f1b4243f49e7e12d7a4dd28fcbf3e3f7cf25ce4905a8615", size = 48533, upload-time = "2025-02-21T18:43:48.046Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/c3/3b7d18cca8d0fa41dbb646ce16935804b380dcf766e7385799f44829b419/glass-2026.2.tar.gz", hash = "sha256:cf5ef6cb76b4738f8dc05ddfc18c359c558bb36e9cd09ca0115fa3ca3deb491f", size = 66236, upload-time = "2026-06-04T15:18:39.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/e7/3e89bc5147a3e84245c783098afffbbacbcdf5678ba1aacc2c9c035434da/glass-2025.1-py3-none-any.whl", hash = "sha256:d7919a7d19e05ab8da4e52dbfbcfb5bd1ed2b72e0cc3afde39ec94ced22883c5", size = 47185, upload-time = "2025-02-21T18:43:46.649Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d1/55ea5e63e81db82806e92ba9f9c2b7277c0c2afe57bf8c1d3763e18005b2/glass-2026.2-py3-none-any.whl", hash = "sha256:0af0b5cd8708040925699c307076fb1c79bbb7e6cba4522764bcc21dd3bdadb8", size = 63746, upload-time = "2026-06-04T15:18:37.895Z" }, +] + +[package.optional-dependencies] +examples = [ + { name = "camb" }, + { name = "cosmology-api" }, + { name = "cosmology-compat-camb" }, + { name = "glass-ext-camb" }, + { name = "jupyter" }, + { name = "matplotlib" }, ] [[package]] @@ -1029,9 +1093,9 @@ name = "glass-ext-camb" version = "2023.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "camb", marker = "sys_platform == 'linux'" }, - { name = "glass", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "camb" }, + { name = "glass" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b7/1a/18bb47c0f7cc267c2f8c95273055b82b1975e4dccbfea6051e82a4d1f513/glass.ext.camb-2023.6.tar.gz", hash = "sha256:f7b6e521b2d45d3b68400353ce6ab1747c772631832a257567de028aa2060509", size = 2989, upload-time = "2023-06-30T22:27:31.511Z" } wheels = [ @@ -1113,8 +1177,8 @@ name = "h2" version = "4.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "hpack", marker = "sys_platform == 'linux'" }, - { name = "hyperframe", marker = "sys_platform == 'linux'" }, + { name = "hpack" }, + { name = "hyperframe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } wheels = [ @@ -1126,7 +1190,7 @@ name = "h5py" version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } wheels = [ @@ -1153,7 +1217,7 @@ name = "healpix" version = "2025.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/1a/c1af658a2d06cdd2734b085b0eee34d51646f091ed13b1669d3a596de57c/healpix-2025.1.tar.gz", hash = "sha256:733f16f30c15d911c8209f4045f0b0934e5fcafa63b8b39d62e425a036adccea", size = 22744, upload-time = "2025-07-07T16:38:42.402Z" } wheels = [ @@ -1168,8 +1232,8 @@ name = "healpy" version = "1.19.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e3/4e/6f5328f375f88c8c38314f24f82b7e1486ec0358c557779500382db1503c/healpy-1.19.0.tar.gz", hash = "sha256:28e839cb885a23d36c77fc3423a3cb9271a07fda94085bd12fc329f941130ec5", size = 4075006, upload-time = "2025-12-02T08:27:19.766Z" } wheels = [ @@ -1192,14 +1256,34 @@ name = "healsparse" version = "1.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "hpgeom", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "hpgeom" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e3/33/36520faabae128fe9a4a5cbeb0916be864d2eb34cf7410d52dc532af044e/healsparse-1.13.0.tar.gz", hash = "sha256:c21e4eb075c686bd306bdc1b40e039134c4ca60ccfa4e7e73845c107a5b16544", size = 133589, upload-time = "2026-07-07T18:33:05.481Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b4/2f/7674b91031a12a9b3cd69f3bb675d3b1eac59657139e23bc528ed6d33d0a/healsparse-1.13.0-py3-none-any.whl", hash = "sha256:3b475a61d7701526246a592ed980758f94764caba4355429bf470a2f9b812964", size = 70673, upload-time = "2026-07-07T18:33:04.514Z" }, ] +[[package]] +name = "hmf" +version = "3.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astropy" }, + { name = "cached-property" }, + { name = "camb" }, + { name = "click" }, + { name = "deprecation" }, + { name = "numpy" }, + { name = "rich" }, + { name = "scipy" }, + { name = "toml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/3a/31fb85e524d07871f5083154d8a248aba508d6ebb811cc0058e608a32d81/hmf-3.6.3.tar.gz", hash = "sha256:c53b53a9c15a3671d84491c39a09b1d0b280f41724bd71901b7b627fb4ac18d1", size = 2382901, upload-time = "2026-09-11T13:59:23.145Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/34/f825393c1f13d38c0857c50605ad517b71b201988d415be715b5f95db61a/hmf-3.6.3-py3-none-any.whl", hash = "sha256:1b0881ea27c41660640669e80d021d894ab2c14feee9d37e1e21cbd27567e630", size = 80447, upload-time = "2026-09-11T13:59:21.958Z" }, +] + [[package]] name = "hpack" version = "4.2.0" @@ -1214,7 +1298,7 @@ name = "hpgeom" version = "1.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/07/72/27640de7d85a566b8ecd27e7894f371a19669c56c268756e18111a137df8/hpgeom-1.5.4.tar.gz", hash = "sha256:85ac73e267c11f3f248920d3541a0e6ffab821cae54f0d44414d1939dfb91200", size = 153906, upload-time = "2026-02-24T19:03:01.954Z" } wheels = [ @@ -1240,8 +1324,8 @@ name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "certifi", marker = "sys_platform == 'linux'" }, - { name = "h11", marker = "sys_platform == 'linux'" }, + { name = "certifi" }, + { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } wheels = [ @@ -1253,10 +1337,10 @@ name = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'linux'" }, - { name = "certifi", marker = "sys_platform == 'linux'" }, - { name = "httpcore", marker = "sys_platform == 'linux'" }, - { name = "idna", marker = "sys_platform == 'linux'" }, + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ @@ -1265,7 +1349,7 @@ wheels = [ [package.optional-dependencies] http2 = [ - { name = "h2", marker = "sys_platform == 'linux'" }, + { name = "h2" }, ] [[package]] @@ -1300,8 +1384,8 @@ name = "imageio" version = "2.37.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pillow", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, + { name = "pillow" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/84/93bcd1300216ea50811cee96873b84a1bebf8d0489ffaf7f2a3756bab866/imageio-2.37.3.tar.gz", hash = "sha256:bbb37efbfc4c400fcd534b367b91fcd66d5da639aaa138034431a1c5e0a41451", size = 389673, upload-time = "2026-03-09T11:31:12.573Z" } wheels = [ @@ -1338,7 +1422,7 @@ name = "importlib-metadata" version = "9.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "zipp", marker = "sys_platform == 'linux'" }, + { name = "zipp" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" } wheels = [ @@ -1368,18 +1452,18 @@ name = "ipykernel" version = "7.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "comm", marker = "sys_platform == 'linux'" }, - { name = "debugpy", marker = "sys_platform == 'linux'" }, - { name = "ipython", marker = "sys_platform == 'linux'" }, - { name = "jupyter-client", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "matplotlib-inline", marker = "sys_platform == 'linux'" }, - { name = "nest-asyncio2", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "psutil", marker = "sys_platform == 'linux'" }, - { name = "pyzmq", marker = "sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio2" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/c4/e4a38f579de4225a561305666f7541cdabb30075def2aa1ac17bd73c1fb5/ipykernel-7.3.0.tar.gz", hash = "sha256:9acaaaf97d16355166e4085afe9d225bfbdf2b7ef520f9df3be8f2b248275e09", size = 184899, upload-time = "2026-06-10T08:41:25.481Z" } wheels = [ @@ -1391,16 +1475,16 @@ name = "ipython" version = "9.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "decorator", marker = "sys_platform == 'linux'" }, - { name = "ipython-pygments-lexers", marker = "sys_platform == 'linux'" }, - { name = "jedi", marker = "sys_platform == 'linux'" }, - { name = "matplotlib-inline", marker = "sys_platform == 'linux'" }, - { name = "pexpect", marker = "sys_platform == 'linux'" }, - { name = "prompt-toolkit", marker = "sys_platform == 'linux'" }, - { name = "psutil", marker = "sys_platform == 'linux'" }, - { name = "pygments", marker = "sys_platform == 'linux'" }, - { name = "stack-data", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect" }, + { name = "prompt-toolkit" }, + { name = "psutil" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/53/59/165d3b4d75cc34add3122c4417ecb229085140ac573103c223cd01dde96f/ipython-9.15.0.tar.gz", hash = "sha256:da2819ce2aa83135257df830660b1176d986c3d2876db24df01974fa955b2756", size = 4442580, upload-time = "2026-06-26T11:03:35.913Z" } wheels = [ @@ -1421,7 +1505,7 @@ name = "ipython-pygments-lexers" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pygments", marker = "sys_platform == 'linux'" }, + { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } wheels = [ @@ -1433,11 +1517,11 @@ name = "ipywidgets" version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "comm", marker = "sys_platform == 'linux'" }, - { name = "ipython", marker = "sys_platform == 'linux'" }, - { name = "jupyterlab-widgets", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, - { name = "widgetsnbextension", marker = "sys_platform == 'linux'" }, + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } wheels = [ @@ -1449,7 +1533,7 @@ name = "isoduration" version = "20.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "arrow", marker = "sys_platform == 'linux'" }, + { name = "arrow" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } wheels = [ @@ -1461,7 +1545,7 @@ name = "jaraco-classes" version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "more-itertools", marker = "sys_platform == 'linux'" }, + { name = "more-itertools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } wheels = [ @@ -1482,7 +1566,7 @@ name = "jaraco-functools" version = "4.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "more-itertools", marker = "sys_platform == 'linux'" }, + { name = "more-itertools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/cf/ea4ef2920830dea3f5ab2ea4da6fb67724e6dca80ee2553788c3607243d0/jaraco_functools-4.5.0.tar.gz", hash = "sha256:3bb5665ea4a020cf78a7040e89154c77edadb3ca74f366479669c5999aa70b03", size = 20272, upload-time = "2026-05-15T21:34:10.025Z" } wheels = [ @@ -1494,7 +1578,7 @@ name = "jedi" version = "0.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "parso", marker = "sys_platform == 'linux'" }, + { name = "parso" }, ] sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" } wheels = [ @@ -1515,7 +1599,7 @@ name = "jinja2" version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markupsafe", marker = "sys_platform == 'linux'" }, + { name = "markupsafe" }, ] sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ @@ -1545,7 +1629,7 @@ name = "jsonargparse" version = "4.49.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/87/0b/f012466077d803b546955bf827ab30a320ad1550e3167195432cc2d6bfad/jsonargparse-4.49.0.tar.gz", hash = "sha256:9e691a09d937fb4c6bc1f6d3bf6c3546efa03381ea1e64ee10759095f1b14da7", size = 125237, upload-time = "2026-05-15T06:48:44.807Z" } wheels = [ @@ -1554,8 +1638,8 @@ wheels = [ [package.optional-dependencies] signatures = [ - { name = "docstring-parser", marker = "sys_platform == 'linux'" }, - { name = "typeshed-client", marker = "sys_platform == 'linux'" }, + { name = "docstring-parser" }, + { name = "typeshed-client" }, ] [[package]] @@ -1572,10 +1656,10 @@ name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs", marker = "sys_platform == 'linux'" }, - { name = "jsonschema-specifications", marker = "sys_platform == 'linux'" }, - { name = "referencing", marker = "sys_platform == 'linux'" }, - { name = "rpds-py", marker = "sys_platform == 'linux'" }, + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -1584,15 +1668,15 @@ wheels = [ [package.optional-dependencies] format-nongpl = [ - { name = "fqdn", marker = "sys_platform == 'linux'" }, - { name = "idna", marker = "sys_platform == 'linux'" }, - { name = "isoduration", marker = "sys_platform == 'linux'" }, - { name = "jsonpointer", marker = "sys_platform == 'linux'" }, - { name = "rfc3339-validator", marker = "sys_platform == 'linux'" }, - { name = "rfc3986-validator", marker = "sys_platform == 'linux'" }, - { name = "rfc3987-syntax", marker = "sys_platform == 'linux'" }, - { name = "uri-template", marker = "sys_platform == 'linux'" }, - { name = "webcolors", marker = "sys_platform == 'linux'" }, + { name = "fqdn" }, + { name = "idna" }, + { name = "isoduration" }, + { name = "jsonpointer" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "rfc3987-syntax" }, + { name = "uri-template" }, + { name = "webcolors" }, ] [[package]] @@ -1600,7 +1684,7 @@ name = "jsonschema-specifications" version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "referencing", marker = "sys_platform == 'linux'" }, + { name = "referencing" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ @@ -1612,12 +1696,12 @@ name = "jupyter" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ipykernel", marker = "sys_platform == 'linux'" }, - { name = "ipywidgets", marker = "sys_platform == 'linux'" }, - { name = "jupyter-console", marker = "sys_platform == 'linux'" }, - { name = "jupyterlab", marker = "sys_platform == 'linux'" }, - { name = "nbconvert", marker = "sys_platform == 'linux'" }, - { name = "notebook", marker = "sys_platform == 'linux'" }, + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyter-console" }, + { name = "jupyterlab" }, + { name = "nbconvert" }, + { name = "notebook" }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } wheels = [ @@ -1629,8 +1713,8 @@ name = "jupyter-builder" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "jupyter-core" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/df/db5efc4e28803a1421350445e53d85ec571a4e6928e3524ccc39f4e16584/jupyter_builder-1.1.0.tar.gz", hash = "sha256:b996e8af616900f18724fa34883169d869be2205497940bec78a3a1031eb897d", size = 971142, upload-time = "2026-07-11T07:24:02.4Z" } wheels = [ @@ -1642,12 +1726,12 @@ name = "jupyter-client" version = "8.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, - { name = "pyzmq", marker = "sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7d/dc/5512503b088997c2250b8bf18258fba9d9ce5ead641183700960d3c9d342/jupyter_client-8.9.1.tar.gz", hash = "sha256:a58f730dd9e728ba16ba1d62ebccf7ffe1ebbdbce4e95cfae941b7321ae1f4fa", size = 359256, upload-time = "2026-06-09T13:15:01.033Z" } wheels = [ @@ -1659,14 +1743,14 @@ name = "jupyter-console" version = "6.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ipykernel", marker = "sys_platform == 'linux'" }, - { name = "ipython", marker = "sys_platform == 'linux'" }, - { name = "jupyter-client", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "prompt-toolkit", marker = "sys_platform == 'linux'" }, - { name = "pygments", marker = "sys_platform == 'linux'" }, - { name = "pyzmq", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "ipykernel" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "pyzmq" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } wheels = [ @@ -1678,8 +1762,8 @@ name = "jupyter-core" version = "5.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "platformdirs", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "platformdirs" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } wheels = [ @@ -1691,14 +1775,14 @@ name = "jupyter-events" version = "0.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jsonschema", extra = ["format-nongpl"], marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "python-json-logger", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "referencing", marker = "sys_platform == 'linux'" }, - { name = "rfc3339-validator", marker = "sys_platform == 'linux'" }, - { name = "rfc3986-validator", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "packaging" }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/f8/475c4241b2b75af0deaae453ed003c6c851766dbc44d332d8baf245dc931/jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3", size = 62854, upload-time = "2026-04-20T23:17:50.66Z" } wheels = [ @@ -1710,7 +1794,7 @@ name = "jupyter-lsp" version = "2.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jupyter-server", marker = "sys_platform == 'linux'" }, + { name = "jupyter-server" }, ] sdist = { url = "https://files.pythonhosted.org/packages/36/ff/1e4a61f5170a9a1d978f3ac3872449de6c01fc71eaf89657824c878b1549/jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6", size = 55677, upload-time = "2026-04-02T08:10:06.749Z" } wheels = [ @@ -1722,23 +1806,23 @@ name = "jupyter-server" version = "2.20.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "anyio", marker = "sys_platform == 'linux'" }, - { name = "argon2-cffi", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "jupyter-client", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "jupyter-events", marker = "sys_platform == 'linux'" }, - { name = "jupyter-server-terminals", marker = "sys_platform == 'linux'" }, - { name = "nbconvert", marker = "sys_platform == 'linux'" }, - { name = "nbformat", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "prometheus-client", marker = "sys_platform == 'linux'" }, - { name = "pyzmq", marker = "sys_platform == 'linux'" }, - { name = "send2trash", marker = "sys_platform == 'linux'" }, - { name = "terminado", marker = "sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, - { name = "websocket-client", marker = "sys_platform == 'linux'" }, + { name = "anyio" }, + { name = "argon2-cffi" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "jupyter-events" }, + { name = "jupyter-server-terminals" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "prometheus-client" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "websocket-client" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6b/dc/db3a582633170186f8c8b31298d7eb26ad0eb031a1f53476c258b64eed05/jupyter_server-2.20.0.tar.gz", hash = "sha256:b5778ba337d8015a3dc2b80803ecdd5ac18d3797fddf61a50ea5fb472b4ebe14", size = 756523, upload-time = "2026-06-17T12:09:09.435Z" } wheels = [ @@ -1750,7 +1834,7 @@ name = "jupyter-server-terminals" version = "0.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "terminado", marker = "sys_platform == 'linux'" }, + { name = "terminado" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770, upload-time = "2026-01-14T16:53:20.213Z" } wheels = [ @@ -1762,19 +1846,19 @@ name = "jupyterlab" version = "4.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "async-lru", marker = "sys_platform == 'linux'" }, - { name = "httpx", marker = "sys_platform == 'linux'" }, - { name = "ipykernel", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "jupyter-builder", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "jupyter-lsp", marker = "sys_platform == 'linux'" }, - { name = "jupyter-server", marker = "sys_platform == 'linux'" }, - { name = "jupyterlab-server", marker = "sys_platform == 'linux'" }, - { name = "notebook-shim", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "async-lru" }, + { name = "httpx" }, + { name = "ipykernel" }, + { name = "jinja2" }, + { name = "jupyter-builder" }, + { name = "jupyter-core" }, + { name = "jupyter-lsp" }, + { name = "jupyter-server" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "packaging" }, + { name = "tornado" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bc/2a/d6af53bfd45a43a5bfe7e40ba47ee7a8921a807daf4bb708e3a295bbb54d/jupyterlab-4.6.1.tar.gz", hash = "sha256:75315982ed28427edaa62bb85eadb5105e4043a757643c910efd787fe6ed0837", size = 28179125, upload-time = "2026-06-29T12:48:45.402Z" } wheels = [ @@ -1795,13 +1879,13 @@ name = "jupyterlab-server" version = "2.28.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "babel", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "json5", marker = "sys_platform == 'linux'" }, - { name = "jsonschema", marker = "sys_platform == 'linux'" }, - { name = "jupyter-server", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "requests", marker = "sys_platform == 'linux'" }, + { name = "babel" }, + { name = "jinja2" }, + { name = "json5" }, + { name = "jsonschema" }, + { name = "jupyter-server" }, + { name = "packaging" }, + { name = "requests" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } wheels = [ @@ -1822,11 +1906,11 @@ name = "jupytext" version = "1.19.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", marker = "sys_platform == 'linux'" }, - { name = "mdit-py-plugins", marker = "sys_platform == 'linux'" }, - { name = "nbformat", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3b/52/e014296ac8f40ca783aeb73dae52e65edbb0eaae0dcdc1ea41bfaa8aebf7/jupytext-1.19.4.tar.gz", hash = "sha256:739bcd4bc12aa4fe298a38017cdb5ae27b08a6ba3a5470728d2fe9e04b155db1", size = 4581977, upload-time = "2026-06-21T21:48:58.32Z" } wheels = [ @@ -1838,11 +1922,11 @@ name = "keyring" version = "25.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jaraco-classes", marker = "sys_platform == 'linux'" }, - { name = "jaraco-context", marker = "sys_platform == 'linux'" }, - { name = "jaraco-functools", marker = "sys_platform == 'linux'" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney" }, + { name = "secretstorage" }, ] sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } wheels = [ @@ -1931,15 +2015,33 @@ name = "lenspack" version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "numpy" }, + { name = "scipy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/73/de/18cfc64028334a1907d44ba2b40e233a2c7a06d7c1e827acfd1785c0b293/lenspack-1.0.0.tar.gz", hash = "sha256:e528a5409473eb996b39695cccb744db067341eb5fac6f240cd8d0aa669c85ec", size = 32632, upload-time = "2020-09-04T08:33:53.267Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1b/3d/a93a1332b1412301ddb454f206507b5e0b3679e012471fbbb075ca3b0845/lenspack-1.0.0-py3-none-any.whl", hash = "sha256:f9c632697516bb681e16b3eff66a7210d6355926179b95aa7060c8936c2259cc", size = 37746, upload-time = "2020-09-04T08:33:50.114Z" }, ] +[[package]] +name = "levin" +version = "0.0.1" +source = { git = "https://github.com/sachaguer/OneCovariance.git?rev=main#70265be3a4bffcfded7b9251a3c4cbe8159d793f" } +dependencies = [ + { name = "astropy" }, + { name = "future" }, + { name = "gsl" }, + { name = "healpy" }, + { name = "hmf" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pip" }, + { name = "pyyaml" }, + { name = "scipy" }, + { name = "wget" }, +] + [[package]] name = "llvmlite" version = "0.48.0" @@ -1961,11 +2063,11 @@ name = "lmfit" version = "1.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "asteval", marker = "sys_platform == 'linux'" }, - { name = "dill", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "uncertainties", marker = "sys_platform == 'linux'" }, + { name = "asteval" }, + { name = "dill" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "uncertainties" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/e5/a35942aed2de95e228728c34609b51fe3ec9182398eac50d288eef313aa2/lmfit-1.3.4.tar.gz", hash = "sha256:3c22c28c43f717f6c5b4a3bd81e893a2149739c26a592c046f2e33c23cfbe497", size = 630720, upload-time = "2025-07-19T20:09:01.876Z" } wheels = [ @@ -1986,8 +2088,8 @@ name = "lsstdesc-coord" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "future", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "future" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b8/e0/6806df4cfa54927a2d8e425407611b4fc031dd125bc952b4a31deb743305/lsstdesc_coord-1.3.1.tar.gz", hash = "sha256:60f878c29e1f30a9b50bf60dca3c466dc9cfb2cbc71f0a27c575ced969de57ab", size = 41647, upload-time = "2026-02-13T18:37:32.611Z" } wheels = [ @@ -2059,7 +2161,7 @@ name = "markdown-it-py" version = "4.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdurl", marker = "sys_platform == 'linux'" }, + { name = "mdurl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } wheels = [ @@ -2109,15 +2211,15 @@ name = "matplotlib" version = "3.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "contourpy", marker = "sys_platform == 'linux'" }, - { name = "cycler", marker = "sys_platform == 'linux'" }, - { name = "fonttools", marker = "sys_platform == 'linux'" }, - { name = "kiwisolver", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pillow", marker = "sys_platform == 'linux'" }, - { name = "pyparsing", marker = "sys_platform == 'linux'" }, - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } wheels = [ @@ -2143,7 +2245,7 @@ name = "matplotlib-inline" version = "0.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" } wheels = [ @@ -2155,7 +2257,7 @@ name = "mdit-py-plugins" version = "0.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", marker = "sys_platform == 'linux'" }, + { name = "markdown-it-py" }, ] sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" } wheels = [ @@ -2255,12 +2357,12 @@ name = "myst-parser" version = "5.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "markdown-it-py", marker = "sys_platform == 'linux'" }, - { name = "mdit-py-plugins", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "sphinx", marker = "sys_platform == 'linux'" }, + { name = "docutils" }, + { name = "jinja2" }, + { name = "markdown-it-py" }, + { name = "mdit-py-plugins" }, + { name = "pyyaml" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/21/dc/603751677fff302f34396e206b610f556a59d7fe58b9a2145f54e96b48e8/myst_parser-5.1.0.tar.gz", hash = "sha256:ab69322dc6719dcc7f296479dbb70181b66df6ed315064f92dbc85c0e1bf2f02", size = 101182, upload-time = "2026-05-13T09:38:19.361Z" } wheels = [ @@ -2272,10 +2374,10 @@ name = "nbclient" version = "0.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jupyter-client", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "nbformat", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "nbformat" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/a5/b3bae4b590c0cbcada2c63a34f7580024e834a8ba213e949a2f906705787/nbclient-0.11.0.tar.gz", hash = "sha256:04a134a5b087f2c5887f228aca155db50169b8cd9334dee6942c8e927e56081a", size = 62535, upload-time = "2026-06-05T07:52:41.746Z" } wheels = [ @@ -2287,20 +2389,20 @@ name = "nbconvert" version = "7.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "beautifulsoup4", marker = "sys_platform == 'linux'" }, - { name = "bleach", extra = ["css"], marker = "sys_platform == 'linux'" }, - { name = "defusedxml", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "jupyterlab-pygments", marker = "sys_platform == 'linux'" }, - { name = "markupsafe", marker = "sys_platform == 'linux'" }, - { name = "mistune", marker = "sys_platform == 'linux'" }, - { name = "nbclient", marker = "sys_platform == 'linux'" }, - { name = "nbformat", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pandocfilters", marker = "sys_platform == 'linux'" }, - { name = "pygments", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "beautifulsoup4" }, + { name = "bleach", extra = ["css"] }, + { name = "defusedxml" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyterlab-pygments" }, + { name = "markupsafe" }, + { name = "mistune" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pandocfilters" }, + { name = "pygments" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/01/b1/708e53fe2e429c103c6e6e159106bcf0357ac41aa4c28772bd8402339051/nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2", size = 865311, upload-time = "2026-04-08T00:44:14.914Z" } wheels = [ @@ -2312,10 +2414,10 @@ name = "nbformat" version = "5.10.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "fastjsonschema", marker = "sys_platform == 'linux'" }, - { name = "jsonschema", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } wheels = [ @@ -2364,19 +2466,19 @@ name = "notebook" version = "5.7.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ipykernel", marker = "sys_platform == 'linux'" }, - { name = "ipython-genutils", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "jupyter-client", marker = "sys_platform == 'linux'" }, - { name = "jupyter-core", marker = "sys_platform == 'linux'" }, - { name = "nbconvert", marker = "sys_platform == 'linux'" }, - { name = "nbformat", marker = "sys_platform == 'linux'" }, - { name = "prometheus-client", marker = "sys_platform == 'linux'" }, - { name = "pyzmq", marker = "sys_platform == 'linux'" }, - { name = "send2trash", marker = "sys_platform == 'linux'" }, - { name = "terminado", marker = "sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, - { name = "traitlets", marker = "sys_platform == 'linux'" }, + { name = "ipykernel" }, + { name = "ipython-genutils" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "prometheus-client" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6e/22/b5dcce67559d63d0f22e46d806305710808c698a1b91c07eb09e389785e0/notebook-5.7.4.tar.gz", hash = "sha256:d908673a4010787625c8952e91a22adf737db031f2aa0793ad92f6558918a74a", size = 13389469, upload-time = "2018-12-17T10:06:00.805Z" } wheels = [ @@ -2388,7 +2490,7 @@ name = "notebook-shim" version = "0.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jupyter-server", marker = "sys_platform == 'linux'" }, + { name = "jupyter-server" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } wheels = [ @@ -2400,8 +2502,8 @@ name = "numba" version = "0.66.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "llvmlite", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "llvmlite" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ae/a0/570e3dc53e5602b49108f62a13e529f1eec8bfc7ef37d49c825924dcf546/numba-0.66.0.tar.gz", hash = "sha256:b900e63a0e26c05ea9a6d5a3a5a0a177cb64c5011887bf43edb8c3ed2c38d363", size = 2806181, upload-time = "2026-07-01T23:12:46.36Z" } wheels = [ @@ -2425,8 +2527,8 @@ name = "numcodecs" version = "0.16.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/bd/8a391e7c356366224734efd24da929cc4796fff468bfb179fe1af6548535/numcodecs-0.16.5.tar.gz", hash = "sha256:0d0fb60852f84c0bd9543cc4d2ab9eefd37fc8efcc410acd4777e62a1d300318", size = 6276387, upload-time = "2025-11-21T02:49:48.986Z" } wheels = [ @@ -2443,7 +2545,7 @@ name = "numexpr" version = "2.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cb/2f/fdba158c9dbe5caca9c3eca3eaffffb251f2fb8674bf8e2d0aed5f38d319/numexpr-2.14.1.tar.gz", hash = "sha256:4be00b1086c7b7a5c32e31558122b7b80243fe098579b170967da83f3152b48b", size = 119400, upload-time = "2025-10-13T16:17:27.351Z" } wheels = [ @@ -2502,7 +2604,7 @@ name = "numpydoc" version = "1.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "sphinx", marker = "sys_platform == 'linux'" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/3c/dfccc9e7dee357fb2aa13c3890d952a370dd0ed071e0f7ed62ed0df567c1/numpydoc-1.10.0.tar.gz", hash = "sha256:3f7970f6eee30912260a6b31ac72bba2432830cd6722569ec17ee8d3ef5ffa01", size = 94027, upload-time = "2025-12-02T16:39:12.937Z" } wheels = [ @@ -2514,7 +2616,7 @@ name = "opencv-python-headless" version = "5.0.0.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1d/99/76b7c80252aa83c1af16393454aafd125a0287101afe8deb0a6821af0e30/opencv_python_headless-5.0.0.93.tar.gz", hash = "sha256:b82f9831daab90b725c7c1ee1b36cb5732c367096ac76d119e64e14eb70d5f3c", size = 81817738, upload-time = "2026-07-02T07:01:06.039Z" } wheels = [ @@ -2538,10 +2640,10 @@ name = "pandas" version = "2.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "python-dateutil", marker = "sys_platform == 'linux'" }, - { name = "pytz", marker = "sys_platform == 'linux'" }, - { name = "tzdata", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -2590,8 +2692,8 @@ name = "partd" version = "1.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "locket", marker = "sys_platform == 'linux'" }, - { name = "toolz", marker = "sys_platform == 'linux'" }, + { name = "locket" }, + { name = "toolz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b2/3a/3f06f34820a31257ddcabdfafc2672c5816be79c7e353b02c1f318daa7d4/partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c", size = 21029, upload-time = "2024-05-06T19:51:41.945Z" } wheels = [ @@ -2603,7 +2705,7 @@ name = "patsy" version = "1.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/be/44/ed13eccdd0519eff265f44b670d46fbb0ec813e2274932dc1c0e48520f7d/patsy-1.0.2.tar.gz", hash = "sha256:cdc995455f6233e90e22de72c37fcadb344e7586fb83f06696f54d92f8ce74c0", size = 399942, upload-time = "2025-10-20T16:17:37.535Z" } wheels = [ @@ -2615,7 +2717,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess", marker = "sys_platform == 'linux'" }, + { name = "ptyprocess" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -2668,14 +2770,23 @@ name = "pims" version = "0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "imageio", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "slicerator", marker = "sys_platform == 'linux'" }, - { name = "tifffile", marker = "sys_platform == 'linux'" }, + { name = "imageio" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "slicerator" }, + { name = "tifffile" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz", hash = "sha256:55907a4c301256086d2aa4e34a5361b9109f24e375c2071e1117b9491e82946b", size = 87779, upload-time = "2024-06-10T19:20:42.842Z" } +[[package]] +name = "pip" +version = "26.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/15/4500e320e6b101ec3b719ae85b697d9940b6cda672bc555bd6016fc60c6f/pip-26.2.1.tar.gz", hash = "sha256:f6ad667e89a1fe78046c8f13232b247200f5258d7828f3f7883d660878e0813f", size = 1848877, upload-time = "2026-08-04T22:51:14.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl", hash = "sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e", size = 1816632, upload-time = "2026-08-04T22:51:12.472Z" }, +] + [[package]] name = "platformdirs" version = "4.10.0" @@ -2708,7 +2819,7 @@ name = "prompt-toolkit" version = "3.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "wcwidth", marker = "sys_platform == 'linux'" }, + { name = "wcwidth" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } wheels = [ @@ -2814,8 +2925,8 @@ name = "pybtex" version = "0.26.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "latexcodec", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "latexcodec" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/f5/f30da9c93f0fa6d619332b2f69597219b625f35780473a05164a9981fd9a/pybtex-0.26.1.tar.gz", hash = "sha256:2e5543bea424e60e9e42eef70bff597be48649d8f68ba061a7a092b2477d5464", size = 692991, upload-time = "2026-04-03T13:05:39.014Z" } wheels = [ @@ -2827,8 +2938,8 @@ name = "pybtex-docutils" version = "1.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils", marker = "sys_platform == 'linux'" }, - { name = "pybtex", marker = "sys_platform == 'linux'" }, + { name = "docutils" }, + { name = "pybtex" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7e/84/796ea94d26188a853660f81bded39f8de4cfe595130aef0dea1088705a11/pybtex-docutils-1.0.3.tar.gz", hash = "sha256:3a7ebdf92b593e00e8c1c538aa9a20bca5d92d84231124715acc964d51d93c6b", size = 18348, upload-time = "2023-08-22T18:47:54.833Z" } wheels = [ @@ -2840,10 +2951,10 @@ name = "pyccl" version = "3.3.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "scipy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/40/ff357d61a20c21420f1fe7417ca6d889be2e07a67b3564775aeaf6c3f366/pyccl-3.3.4.tar.gz", hash = "sha256:a9d22557354fd161df909e8f4f199e4f1c434c080fd6cddb2246609ddbd70e7c", size = 16783719, upload-time = "2026-05-14T16:26:27.327Z" } wheels = [ @@ -2866,10 +2977,10 @@ name = "pydantic" version = "2.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "annotated-types", marker = "sys_platform == 'linux'" }, - { name = "pydantic-core", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, - { name = "typing-inspection", marker = "sys_platform == 'linux'" }, + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ @@ -2881,7 +2992,7 @@ name = "pydantic-core" version = "2.46.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } wheels = [ @@ -2934,7 +3045,7 @@ name = "pyerfa" version = "2.0.1.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/39/63cc8291b0cf324ae710df41527faf7d331bce573899199d926b3e492260/pyerfa-2.0.1.5.tar.gz", hash = "sha256:17d6b24fe4846c65d5e7d8c362dcb08199dc63b30a236aedd73875cc83e1f6c0", size = 818430, upload-time = "2024-11-11T15:22:30.852Z" } wheels = [ @@ -2957,8 +3068,8 @@ name = "pymaster" version = "2.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ducc0", marker = "sys_platform == 'linux'" }, - { name = "healpy", marker = "sys_platform == 'linux'" }, + { name = "ducc0" }, + { name = "healpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/95/45/470858f2a6a3f5133955bb87aed978a5e921d4052aea1c19718e6b72f0ae/pymaster-2.6.tar.gz", hash = "sha256:056a09bc8631cf338e93ef14d8c70272ad9554ef10498d919e920994531c57a2", size = 33707181, upload-time = "2025-12-16T09:18:28.905Z" } @@ -2967,8 +3078,8 @@ name = "pyopenssl" version = "26.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13' and sys_platform == 'linux'" }, + { name = "cryptography" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/74/b7/da07bae88f5a9506b4def6f2f4903cf4c3b8831e560dba8fa18ca08f758f/pyopenssl-26.3.0.tar.gz", hash = "sha256:589de7fae1c9ea670d18422ed00fc04da787bbde8e1454aea872aa57b49ad341", size = 182024, upload-time = "2026-06-12T20:28:07.458Z" } wheels = [ @@ -2989,10 +3100,10 @@ name = "pytest" version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "iniconfig", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pluggy", marker = "sys_platform == 'linux'" }, - { name = "pygments", marker = "sys_platform == 'linux'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ @@ -3004,9 +3115,9 @@ name = "pytest-cov" version = "7.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage", marker = "sys_platform == 'linux'" }, - { name = "pluggy", marker = "sys_platform == 'linux'" }, - { name = "pytest", marker = "sys_platform == 'linux'" }, + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } wheels = [ @@ -3018,7 +3129,7 @@ name = "python-dateutil" version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", marker = "sys_platform == 'linux'" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ @@ -3076,7 +3187,7 @@ name = "pyzmq" version = "27.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cffi", marker = "implementation_name == 'pypy' and sys_platform == 'linux'" }, + { name = "cffi", marker = "implementation_name == 'pypy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } wheels = [ @@ -3105,10 +3216,10 @@ name = "qp-prob" version = "1.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "deprecated", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "tables-io", marker = "sys_platform == 'linux'" }, + { name = "deprecated" }, + { name = "numpy" }, + { name = "scipy" }, + { name = "tables-io" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b0/65/c54639ac63667800ee00d74c8851bd104ccaefcae95138073d6e77832d17/qp_prob-1.1.4.tar.gz", hash = "sha256:a68a3a3785d3ca57a2246c0524398338b7b7dffef0aef0722c231c64dde30107", size = 2460490, upload-time = "2026-06-25T20:08:34.63Z" } wheels = [ @@ -3120,9 +3231,9 @@ name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs", marker = "sys_platform == 'linux'" }, - { name = "rpds-py", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "python_full_version < '3.13' and sys_platform == 'linux'" }, + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ @@ -3134,8 +3245,8 @@ name = "regions" version = "0.12" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d3/86/44ff464efa2777247d6addc74416059f1872ab05c88588bbe1aadca27a7c/regions-0.12.tar.gz", hash = "sha256:1c9460770f250ef299e90a9d5c0b35941f7d05bbf879f6ffaa0538250c018ef9", size = 273609, upload-time = "2026-06-24T20:10:17.78Z" } wheels = [ @@ -3148,16 +3259,16 @@ name = "reproject" version = "0.21.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "astropy-healpix", marker = "sys_platform == 'linux'" }, - { name = "dask", extra = ["array"], marker = "sys_platform == 'linux'" }, - { name = "dask-image", marker = "sys_platform == 'linux'" }, - { name = "fsspec", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pillow", marker = "sys_platform == 'linux'" }, - { name = "pyavm", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "zarr", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "astropy-healpix" }, + { name = "dask", extra = ["array"] }, + { name = "dask-image" }, + { name = "fsspec" }, + { name = "numpy" }, + { name = "pillow" }, + { name = "pyavm" }, + { name = "scipy" }, + { name = "zarr" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/44/6fd820ba336484277a91a2f4808b60d6ec0b9f033f588c237e778f20fe89/reproject-0.21.0.tar.gz", hash = "sha256:01ede715a1993c29431f52ff74189ef30f5e7b2e8b4dc88c1b002145a971dc1c", size = 1622661, upload-time = "2026-06-25T15:11:34.886Z" } wheels = [ @@ -3170,10 +3281,10 @@ name = "requests" version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "certifi", marker = "sys_platform == 'linux'" }, - { name = "charset-normalizer", marker = "sys_platform == 'linux'" }, - { name = "idna", marker = "sys_platform == 'linux'" }, - { name = "urllib3", marker = "sys_platform == 'linux'" }, + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ @@ -3185,7 +3296,7 @@ name = "rfc3339-validator" version = "0.1.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", marker = "sys_platform == 'linux'" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } wheels = [ @@ -3206,7 +3317,7 @@ name = "rfc3987-syntax" version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "lark", marker = "sys_platform == 'linux'" }, + { name = "lark" }, ] sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } wheels = [ @@ -3218,8 +3329,8 @@ name = "rich" version = "15.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py", marker = "sys_platform == 'linux'" }, - { name = "pygments", marker = "sys_platform == 'linux'" }, + { name = "markdown-it-py" }, + { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } wheels = [ @@ -3231,7 +3342,7 @@ name = "rocket-fft" version = "0.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numba", marker = "sys_platform == 'linux'" }, + { name = "numba" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/b0/09cbec3177ecf56e5ceb789a69fbcdcbabe758cbb682e5d36b14aa8c05fb/rocket_fft-0.3.1.tar.gz", hash = "sha256:ddc902a361099aff9fc02fe9ea3b0d036c2f67f93df356c2b754f7627d7fa4f4", size = 74276, upload-time = "2025-12-26T00:30:54.083Z" } wheels = [ @@ -3345,9 +3456,9 @@ name = "sacc" version = "2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "numpy" }, + { name = "scipy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b6/82/5b603d960279b8eb7fac677ba3550c0903a8390f1bff2eee6fbfa59765da/sacc-2.4.tar.gz", hash = "sha256:a98fb4e59b3c742ddb492a15a10e1f8a4279ba36d56c0d3c35e1cd13cca1167b", size = 424905, upload-time = "2026-07-02T11:29:27.458Z" } wheels = [ @@ -3359,7 +3470,7 @@ name = "scipy" version = "1.17.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -3390,9 +3501,9 @@ name = "seaborn" version = "0.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pandas", marker = "sys_platform == 'linux'" }, + { name = "matplotlib" }, + { name = "numpy" }, + { name = "pandas" }, ] sdist = { url = "https://files.pythonhosted.org/packages/86/59/a451d7420a77ab0b98f7affa3a1d78a313d2f7281a57afb1a34bae8ab412/seaborn-0.13.2.tar.gz", hash = "sha256:93e60a40988f4d65e9f4885df477e2fdaff6b73a9ded434c1ab356dd57eefff7", size = 1457696, upload-time = "2024-01-25T13:21:52.551Z" } wheels = [ @@ -3404,8 +3515,8 @@ name = "secretstorage" version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography", marker = "sys_platform == 'linux'" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "cryptography" }, + { name = "jeepney" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } wheels = [ @@ -3433,31 +3544,18 @@ wheels = [ [[package]] name = "shear-psf-leakage" version = "0.2.1" -source = { git = "https://github.com/CosmoStat/shear_psf_leakage.git?rev=develop#0b3c17523b77760c9259be15ba5bbba99d575d0a" } -dependencies = [ - { name = "camb", marker = "sys_platform == 'linux'" }, - { name = "cs-util", marker = "sys_platform == 'linux'" }, - { name = "emcee", marker = "sys_platform == 'linux'" }, - { name = "getdist", marker = "sys_platform == 'linux'" }, - { name = "gsl", marker = "sys_platform == 'linux'" }, - { name = "jupyter", marker = "sys_platform == 'linux'" }, - { name = "jupyter-server", marker = "sys_platform == 'linux'" }, - { name = "jupyterlab", marker = "sys_platform == 'linux'" }, - { name = "jupytext", marker = "sys_platform == 'linux'" }, - { name = "lenspack", marker = "sys_platform == 'linux'" }, - { name = "lmfit", marker = "sys_platform == 'linux'" }, - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "notebook", marker = "sys_platform == 'linux'" }, - { name = "pandas", marker = "sys_platform == 'linux'" }, - { name = "pyccl", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "stats", marker = "sys_platform == 'linux'" }, - { name = "swig", marker = "sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, - { name = "tqdm", marker = "sys_platform == 'linux'" }, - { name = "treecorr", marker = "sys_platform == 'linux'" }, - { name = "uncertainties", marker = "sys_platform == 'linux'" }, +source = { git = "https://github.com/CosmoStat/shear_psf_leakage.git?rev=develop#f1a2c071872e8633010f9af69c7004df168ebb5a" } +dependencies = [ + { name = "cs-util" }, + { name = "emcee" }, + { name = "getdist" }, + { name = "lmfit" }, + { name = "matplotlib" }, + { name = "pandas" }, + { name = "scipy" }, + { name = "tqdm" }, + { name = "treecorr" }, + { name = "uncertainties" }, ] [[package]] @@ -3474,11 +3572,11 @@ name = "skyproj" version = "2.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "healsparse", marker = "sys_platform == 'linux'" }, - { name = "hpgeom", marker = "sys_platform == 'linux'" }, - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "healsparse" }, + { name = "hpgeom" }, + { name = "matplotlib" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/cf/5158151ae6fc60459f430451b612a4bf2da07ebff2a9f52a37e73715a5ee/skyproj-2.5.0.tar.gz", hash = "sha256:cb8d5115927ca43cacdb6d92f00bb4fcc8563ee71028f755203ce1752e67c140", size = 8521587, upload-time = "2026-06-26T19:51:15.378Z" } wheels = [ @@ -3500,7 +3598,7 @@ name = "smart-open" version = "7.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "wrapt", marker = "sys_platform == 'linux'" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/c6/22e7a2acd5d27941e85e0d7ede398da5abe2e4677d2265c924157247c32e/smart_open-7.7.1.tar.gz", hash = "sha256:9414ba5733e28309f29b28a303b0f1054ad23fe0275f1a1b600c80a724f4bd1a", size = 54952, upload-time = "2026-06-26T07:56:35.309Z" } wheels = [ @@ -3521,13 +3619,13 @@ name = "smokescreen" version = "1.5.6" source = { git = "https://github.com/UNIONS-WL/Smokescreen?rev=588a6b9b26560bd5ba3dd5ba342f3c40152644f9#588a6b9b26560bd5ba3dd5ba342f3c40152644f9" } dependencies = [ - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "cryptography", marker = "sys_platform == 'linux'" }, - { name = "jsonargparse", extra = ["signatures"], marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pyccl", marker = "sys_platform == 'linux'" }, - { name = "sacc", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "astropy" }, + { name = "cryptography" }, + { name = "jsonargparse", extra = ["signatures"] }, + { name = "numpy" }, + { name = "pyccl" }, + { name = "sacc" }, + { name = "scipy" }, ] [[package]] @@ -3535,37 +3633,37 @@ name = "snakemake" version = "9.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "conda-inject", marker = "sys_platform == 'linux'" }, - { name = "configargparse", marker = "sys_platform == 'linux'" }, - { name = "connection-pool", marker = "sys_platform == 'linux'" }, - { name = "docutils", marker = "sys_platform == 'linux'" }, - { name = "dpath", marker = "sys_platform == 'linux'" }, - { name = "gitpython", marker = "sys_platform == 'linux'" }, - { name = "humanfriendly", marker = "sys_platform == 'linux'" }, - { name = "immutables", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "jsonschema", marker = "sys_platform == 'linux'" }, - { name = "nbformat", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "platformdirs", marker = "sys_platform == 'linux'" }, - { name = "psutil", marker = "sys_platform == 'linux'" }, - { name = "pulp", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "referencing", marker = "sys_platform == 'linux'" }, - { name = "requests", marker = "sys_platform == 'linux'" }, - { name = "smart-open", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-common", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-executor-plugins", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-logger-plugins", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-report-plugins", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-scheduler-plugins", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-storage-plugins", marker = "sys_platform == 'linux'" }, - { name = "sqlmodel", marker = "sys_platform == 'linux'" }, - { name = "tabulate", marker = "sys_platform == 'linux'" }, - { name = "tenacity", marker = "sys_platform == 'linux'" }, - { name = "throttler", marker = "sys_platform == 'linux'" }, - { name = "wrapt", marker = "sys_platform == 'linux'" }, - { name = "yte", marker = "sys_platform == 'linux'" }, + { name = "conda-inject" }, + { name = "configargparse" }, + { name = "connection-pool" }, + { name = "docutils" }, + { name = "dpath" }, + { name = "gitpython" }, + { name = "humanfriendly" }, + { name = "immutables" }, + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "psutil" }, + { name = "pulp" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "requests" }, + { name = "smart-open" }, + { name = "snakemake-interface-common" }, + { name = "snakemake-interface-executor-plugins" }, + { name = "snakemake-interface-logger-plugins" }, + { name = "snakemake-interface-report-plugins" }, + { name = "snakemake-interface-scheduler-plugins" }, + { name = "snakemake-interface-storage-plugins" }, + { name = "sqlmodel" }, + { name = "tabulate" }, + { name = "tenacity" }, + { name = "throttler" }, + { name = "wrapt" }, + { name = "yte" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/c2/45aa858e55edbb7dbc243ed60859f4f3d92d63ecdd67a80632aa3659fe6b/snakemake-9.23.1.tar.gz", hash = "sha256:ef8d698bfce66a6669cc29df7e344b0b367fd90956c725e5ae0a79a556e8e93f", size = 6802587, upload-time = "2026-06-18T09:42:57.653Z" } wheels = [ @@ -3577,9 +3675,9 @@ name = "snakemake-interface-common" version = "1.23.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "argparse-dataclass", marker = "sys_platform == 'linux'" }, - { name = "configargparse", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, + { name = "argparse-dataclass" }, + { name = "configargparse" }, + { name = "packaging" }, ] sdist = { url = "https://files.pythonhosted.org/packages/89/c3/592f832f6e5d2d31f749392e48e8401b7625dec668d3d365d8d28f2b6c30/snakemake_interface_common-1.23.0.tar.gz", hash = "sha256:6ed14531a461417659364a0dd0acc51b786af4e26fc15cc5e00ff3d9fcaffacc", size = 13960, upload-time = "2026-03-08T21:54:29.251Z" } wheels = [ @@ -3591,9 +3689,9 @@ name = "snakemake-interface-executor-plugins" version = "9.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "argparse-dataclass", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-common", marker = "sys_platform == 'linux'" }, - { name = "throttler", marker = "sys_platform == 'linux'" }, + { name = "argparse-dataclass" }, + { name = "snakemake-interface-common" }, + { name = "throttler" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/50/de06b284c45a8e94fb8e4a12d5235065e78b49b8f84329dc10fe39f4b7dd/snakemake_interface_executor_plugins-9.4.0.tar.gz", hash = "sha256:9d4138897beacbaadaedad94b63f948eaeb604b7fc78f9cf65ac57f090f2c066", size = 16549, upload-time = "2026-03-08T17:04:02.644Z" } wheels = [ @@ -3605,7 +3703,7 @@ name = "snakemake-interface-logger-plugins" version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "snakemake-interface-common", marker = "sys_platform == 'linux'" }, + { name = "snakemake-interface-common" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/0c/3fa5d592663c65669a867526604aadc6fbc235fb9284e94b49c0ef59aa41/snakemake_interface_logger_plugins-2.1.0.tar.gz", hash = "sha256:c89a00d2a398490cecd91b6dc6db8049cba93712d82e1d8f3000f3040bf3791c", size = 15917, upload-time = "2026-05-20T15:12:35.259Z" } wheels = [ @@ -3617,7 +3715,7 @@ name = "snakemake-interface-report-plugins" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "snakemake-interface-common", marker = "sys_platform == 'linux'" }, + { name = "snakemake-interface-common" }, ] sdist = { url = "https://files.pythonhosted.org/packages/18/d6/6160ed98de665d6871dd356597dbf726688cc786e88668359ca37b7d9f54/snakemake_interface_report_plugins-1.3.0.tar.gz", hash = "sha256:fc9495298bec4e69721ab8afe6d6d88a86966fda2eeb003db56b9a88b86d5934", size = 4283, upload-time = "2025-10-31T10:52:36.55Z" } wheels = [ @@ -3629,7 +3727,7 @@ name = "snakemake-interface-scheduler-plugins" version = "2.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "snakemake-interface-common", marker = "sys_platform == 'linux'" }, + { name = "snakemake-interface-common" }, ] sdist = { url = "https://files.pythonhosted.org/packages/88/d9/d480807d2cfc2d132bc760d877d45ec8fbe620a24200ec4d2697c4a26031/snakemake_interface_scheduler_plugins-2.0.2.tar.gz", hash = "sha256:2797e8fa9019d983132c2b403f14d6fcd3c5ad4c8d8a66b984b4740a71cacc46", size = 8642, upload-time = "2025-10-20T13:58:12.988Z" } wheels = [ @@ -3641,11 +3739,11 @@ name = "snakemake-interface-storage-plugins" version = "4.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "humanfriendly", marker = "sys_platform == 'linux'" }, - { name = "snakemake-interface-common", marker = "sys_platform == 'linux'" }, - { name = "tenacity", marker = "sys_platform == 'linux'" }, - { name = "throttler", marker = "sys_platform == 'linux'" }, - { name = "wrapt", marker = "sys_platform == 'linux'" }, + { name = "humanfriendly" }, + { name = "snakemake-interface-common" }, + { name = "tenacity" }, + { name = "throttler" }, + { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/6e/f3c5b2d621fd6a6b78d8cfc01fef6b926fe2c277f5ed77c5e4deeacb94eb/snakemake_interface_storage_plugins-4.4.1.tar.gz", hash = "sha256:b2b5bf05318af36955ebf2ce76c921c0fb06904ca98fb30e1657d88b7b7b6945", size = 14924, upload-time = "2026-03-16T11:16:01.075Z" } wheels = [ @@ -3675,81 +3773,82 @@ name = "sp-validation" version = "0.6.0" source = { virtual = "." } dependencies = [ - { name = "adjusttext", marker = "sys_platform == 'linux'" }, - { name = "astropy", marker = "sys_platform == 'linux'" }, - { name = "camb", marker = "sys_platform == 'linux'" }, - { name = "clmm", marker = "sys_platform == 'linux'" }, - { name = "colorama", marker = "sys_platform == 'linux'" }, - { name = "cosmo-numba", marker = "sys_platform == 'linux'" }, - { name = "cryptography", marker = "sys_platform == 'linux'" }, - { name = "cs-util", marker = "sys_platform == 'linux'" }, - { name = "emcee", marker = "sys_platform == 'linux'" }, - { name = "getdist", marker = "sys_platform == 'linux'" }, - { name = "h5py", marker = "sys_platform == 'linux'" }, - { name = "healpy", marker = "sys_platform == 'linux'" }, - { name = "healsparse", marker = "sys_platform == 'linux'" }, - { name = "importlib-metadata", marker = "sys_platform == 'linux'" }, - { name = "joblib", marker = "sys_platform == 'linux'" }, - { name = "jupyter", marker = "sys_platform == 'linux'" }, - { name = "jupyterlab", marker = "sys_platform == 'linux'" }, - { name = "jupytext", marker = "sys_platform == 'linux'" }, - { name = "lenspack", marker = "sys_platform == 'linux'" }, - { name = "lmfit", marker = "sys_platform == 'linux'" }, - { name = "matplotlib", marker = "sys_platform == 'linux'" }, - { name = "numba", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "opencv-python-headless", marker = "sys_platform == 'linux'" }, - { name = "pandas", marker = "sys_platform == 'linux'" }, - { name = "pyarrow", marker = "sys_platform == 'linux'" }, - { name = "pyccl", marker = "sys_platform == 'linux'" }, - { name = "pymaster", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "regions", marker = "sys_platform == 'linux'" }, - { name = "reproject", marker = "sys_platform == 'linux'" }, - { name = "sacc", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, - { name = "seaborn", marker = "sys_platform == 'linux'" }, - { name = "shear-psf-leakage", marker = "sys_platform == 'linux'" }, - { name = "skyproj", marker = "sys_platform == 'linux'" }, - { name = "smokescreen", marker = "sys_platform == 'linux'" }, - { name = "statsmodels", marker = "sys_platform == 'linux'" }, - { name = "tqdm", marker = "sys_platform == 'linux'" }, - { name = "treecorr", marker = "sys_platform == 'linux'" }, - { name = "uncertainties", marker = "sys_platform == 'linux'" }, + { name = "adjusttext" }, + { name = "astropy" }, + { name = "camb" }, + { name = "clmm" }, + { name = "colorama" }, + { name = "cosmo-numba" }, + { name = "cryptography" }, + { name = "cs-util" }, + { name = "emcee" }, + { name = "getdist" }, + { name = "h5py" }, + { name = "healpy" }, + { name = "healsparse" }, + { name = "importlib-metadata" }, + { name = "joblib" }, + { name = "jupyter" }, + { name = "jupyterlab" }, + { name = "jupytext" }, + { name = "lenspack" }, + { name = "levin" }, + { name = "lmfit" }, + { name = "matplotlib" }, + { name = "numba" }, + { name = "numpy" }, + { name = "opencv-python-headless" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyccl" }, + { name = "pymaster" }, + { name = "pyyaml" }, + { name = "regions" }, + { name = "reproject" }, + { name = "sacc" }, + { name = "scipy" }, + { name = "seaborn" }, + { name = "shear-psf-leakage" }, + { name = "skyproj" }, + { name = "smokescreen" }, + { name = "statsmodels" }, + { name = "tqdm" }, + { name = "treecorr" }, + { name = "uncertainties" }, ] [package.optional-dependencies] develop = [ - { name = "myst-parser", marker = "sys_platform == 'linux'" }, - { name = "numpydoc", marker = "sys_platform == 'linux'" }, - { name = "pytest", marker = "sys_platform == 'linux'" }, - { name = "pytest-cov", marker = "sys_platform == 'linux'" }, - { name = "ruff", marker = "sys_platform == 'linux'" }, - { name = "sphinx", marker = "sys_platform == 'linux'" }, - { name = "sphinxawesome-theme", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-bibtex", marker = "sys_platform == 'linux'" }, + { name = "myst-parser" }, + { name = "numpydoc" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, + { name = "sphinx" }, + { name = "sphinxawesome-theme" }, + { name = "sphinxcontrib-bibtex" }, ] docs = [ - { name = "myst-parser", marker = "sys_platform == 'linux'" }, - { name = "numpydoc", marker = "sys_platform == 'linux'" }, - { name = "sphinx", marker = "sys_platform == 'linux'" }, - { name = "sphinxawesome-theme", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-bibtex", marker = "sys_platform == 'linux'" }, + { name = "myst-parser" }, + { name = "numpydoc" }, + { name = "sphinx" }, + { name = "sphinxawesome-theme" }, + { name = "sphinxcontrib-bibtex" }, ] glass = [ - { name = "cosmology", marker = "sys_platform == 'linux'" }, - { name = "fitsio", marker = "sys_platform == 'linux'" }, - { name = "glass", marker = "sys_platform == 'linux'" }, - { name = "glass-ext-camb", marker = "sys_platform == 'linux'" }, + { name = "cosmology" }, + { name = "fitsio" }, + { name = "glass", extra = ["examples"] }, + { name = "glass-ext-camb" }, ] test = [ - { name = "pytest", marker = "sys_platform == 'linux'" }, - { name = "pytest-cov", marker = "sys_platform == 'linux'" }, - { name = "ruff", marker = "sys_platform == 'linux'" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, ] workflow = [ - { name = "mpi4py", marker = "sys_platform == 'linux'" }, - { name = "snakemake", marker = "sys_platform == 'linux'" }, + { name = "mpi4py" }, + { name = "snakemake" }, ] [package.metadata] @@ -3766,7 +3865,7 @@ requires-dist = [ { name = "emcee" }, { name = "fitsio", marker = "extra == 'glass'" }, { name = "getdist", git = "https://github.com/benabed/getdist.git?rev=113cd22a9a0d013b6f72fe734be81f260f3d3be5" }, - { name = "glass", marker = "extra == 'glass'", specifier = "==2025.1" }, + { name = "glass", extras = ["examples"], marker = "extra == 'glass'", specifier = "==2026.2" }, { name = "glass-ext-camb", marker = "extra == 'glass'", specifier = "==2023.6" }, { name = "h5py" }, { name = "healpy" }, @@ -3777,6 +3876,7 @@ requires-dist = [ { name = "jupyterlab" }, { name = "jupytext", specifier = ">=1.15" }, { name = "lenspack" }, + { name = "levin", git = "https://github.com/sachaguer/OneCovariance.git?rev=main" }, { name = "lmfit" }, { name = "matplotlib" }, { name = "mpi4py", marker = "extra == 'workflow'" }, @@ -3818,22 +3918,22 @@ name = "sphinx" version = "9.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "alabaster", marker = "sys_platform == 'linux'" }, - { name = "babel", marker = "sys_platform == 'linux'" }, - { name = "docutils", marker = "sys_platform == 'linux'" }, - { name = "imagesize", marker = "sys_platform == 'linux'" }, - { name = "jinja2", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pygments", marker = "sys_platform == 'linux'" }, - { name = "requests", marker = "sys_platform == 'linux'" }, - { name = "roman-numerals", marker = "sys_platform == 'linux'" }, - { name = "snowballstemmer", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-applehelp", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-devhelp", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-htmlhelp", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-jsmath", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-qthelp", marker = "sys_platform == 'linux'" }, - { name = "sphinxcontrib-serializinghtml", marker = "sys_platform == 'linux'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ @@ -3845,8 +3945,8 @@ name = "sphinxawesome-theme" version = "6.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "beautifulsoup4", marker = "sys_platform == 'linux'" }, - { name = "sphinx", marker = "sys_platform == 'linux'" }, + { name = "beautifulsoup4" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9a/3f/ba7f8dc2837c0ac362127c6cd491578c23ac4d4e8cc6a37726531be29256/sphinxawesome_theme-6.0.5.tar.gz", hash = "sha256:ed3d82b7f0e30e3d6f38f5245d89775e9472a80a45ffd73d6a0a4b9d05ac5736", size = 343775, upload-time = "2026-06-15T15:09:36.094Z" } wheels = [ @@ -3867,10 +3967,10 @@ name = "sphinxcontrib-bibtex" version = "2.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "docutils", marker = "sys_platform == 'linux'" }, - { name = "pybtex", marker = "sys_platform == 'linux'" }, - { name = "pybtex-docutils", marker = "sys_platform == 'linux'" }, - { name = "sphinx", marker = "sys_platform == 'linux'" }, + { name = "docutils" }, + { name = "pybtex" }, + { name = "pybtex-docutils" }, + { name = "sphinx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/15/6a/8e0b2c2420286389e7fed78ff361ec30e2f1d58c8560af8d64df5e7b61e0/sphinxcontrib_bibtex-2.7.0.tar.gz", hash = "sha256:fee700f7aae29bb8f654c62913f00d34ac44fc0b8ca0fa67ac922ff4453addee", size = 120669, upload-time = "2026-05-06T09:29:24.935Z" } wheels = [ @@ -3927,8 +4027,8 @@ name = "sqlalchemy" version = "2.0.51" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "greenlet", marker = "(platform_machine == 'AMD64' and sys_platform == 'linux') or (platform_machine == 'WIN32' and sys_platform == 'linux') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'amd64' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and sys_platform == 'linux') or (platform_machine == 'win32' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" } wheels = [ @@ -3956,8 +4056,8 @@ name = "sqlmodel" version = "0.0.37" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pydantic", marker = "sys_platform == 'linux'" }, - { name = "sqlalchemy", marker = "sys_platform == 'linux'" }, + { name = "pydantic" }, + { name = "sqlalchemy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fb/26/1d2faa0fd5a765267f49751de533adac6b9ff9366c7c6e7692df4f32230f/sqlmodel-0.0.37.tar.gz", hash = "sha256:d2c19327175794faf50b1ee31cc966764f55b1dedefc046450bc5741a3d68352", size = 85527, upload-time = "2026-02-21T16:39:47.038Z" } wheels = [ @@ -3969,31 +4069,25 @@ name = "stack-data" version = "0.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "asttokens", marker = "sys_platform == 'linux'" }, - { name = "executing", marker = "sys_platform == 'linux'" }, - { name = "pure-eval", marker = "sys_platform == 'linux'" }, + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, ] sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] -[[package]] -name = "stats" -version = "0.1.2a0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/43/f2d8d8e2660740ecca9c0fab85a07bc02da27d1fcc5071ee481082888720/stats-0.1.2a.tar.gz", hash = "sha256:854e59fa99f92ce8319c16149ef8e9939dba3e2237bbb0fb6a6094c60155bd4c", size = 127625, upload-time = "2010-12-31T09:02:15.131Z" } - [[package]] name = "statsmodels" version = "0.14.6" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "pandas", marker = "sys_platform == 'linux'" }, - { name = "patsy", marker = "sys_platform == 'linux'" }, - { name = "scipy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "patsy" }, + { name = "scipy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0d/81/e8d74b34f85285f7335d30c5e3c2d7c0346997af9f3debf9a0a9a63de184/statsmodels-0.14.6.tar.gz", hash = "sha256:4d17873d3e607d398b85126cd4ed7aad89e4e9d89fc744cdab1af3189a996c2a", size = 20689085, upload-time = "2025-12-05T23:08:39.522Z" } wheels = [ @@ -4035,7 +4129,7 @@ name = "sympy" version = "1.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mpmath", marker = "sys_platform == 'linux'" }, + { name = "mpmath" }, ] sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } wheels = [ @@ -4047,11 +4141,11 @@ name = "tables" version = "3.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "blosc2", marker = "sys_platform == 'linux'" }, - { name = "numexpr", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "py-cpuinfo", marker = "sys_platform == 'linux'" }, + { name = "blosc2" }, + { name = "numexpr" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "py-cpuinfo" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cc/a3/d213ebe7376d48055bd55a29cd9f99061afa0dcece608f94a5025d797b0a/tables-3.11.1.tar.gz", hash = "sha256:78abcf413091bc7c1e4e8c10fbbb438d1ac0b5a87436c5b972c3e8253871b6fb", size = 4790533, upload-time = "2026-03-01T11:43:36.036Z" } wheels = [ @@ -4066,12 +4160,12 @@ name = "tables-io" version = "1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'linux'" }, - { name = "deprecated", marker = "sys_platform == 'linux'" }, - { name = "h5py", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pandas", marker = "sys_platform == 'linux'" }, - { name = "tables", marker = "sys_platform == 'linux'" }, + { name = "click" }, + { name = "deprecated" }, + { name = "h5py" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "tables" }, ] sdist = { url = "https://files.pythonhosted.org/packages/12/1b/61bed3c55ce4602be5379362ce939b71a2e707e856ee497c1d1e72f8ff3d/tables_io-1.1.2.tar.gz", hash = "sha256:ea27eac8b22ff4e3500afd92140794bd41161f768a2c84520b91b3b58e945182", size = 91394, upload-time = "2026-06-18T16:21:20.896Z" } wheels = [ @@ -4101,8 +4195,8 @@ name = "terminado" version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess", marker = "os_name != 'nt' and sys_platform == 'linux'" }, - { name = "tornado", marker = "sys_platform == 'linux'" }, + { name = "ptyprocess", marker = "os_name != 'nt'" }, + { name = "tornado" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } wheels = [ @@ -4132,7 +4226,7 @@ name = "tifffile" version = "2026.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy", marker = "sys_platform == 'linux'" }, + { name = "numpy" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b7/38/5e2ecef5af2f4fd4a89bb8d6240de9458bab4d51a4cbd97aeb3a0cd618e2/tifffile-2026.6.1.tar.gz", hash = "sha256:626c892c0e899d959b9438e7c0e1491dc154a7fead1f1f37a991724a50eceba9", size = 429694, upload-time = "2026-05-31T23:57:12.165Z" } wheels = [ @@ -4144,13 +4238,22 @@ name = "tinycss2" version = "1.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "webencodings", marker = "sys_platform == 'linux'" }, + { name = "webencodings" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/ae/2ca4913e5c0f09781d75482874c3a95db9105462a92ddd303c7d285d3df2/tinycss2-1.5.1.tar.gz", hash = "sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957", size = 88195, upload-time = "2025-11-23T10:29:10.082Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/60/45/c7b5c3168458db837e8ceab06dc77824e18202679d0463f0e8f002143a97/tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661", size = 28404, upload-time = "2025-11-23T10:29:08.676Z" }, ] +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + [[package]] name = "toolz" version = "1.1.0" @@ -4195,8 +4298,8 @@ name = "transformcl" version = "2026.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "array-api-compat", marker = "sys_platform == 'linux'" }, - { name = "flt", marker = "sys_platform == 'linux'" }, + { name = "array-api-compat" }, + { name = "flt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9d/ce/ae8373b67259c3ea6033425161f7d195387b1e68b17d4d00eee1b68e5edd/transformcl-2026.1.tar.gz", hash = "sha256:14551c33d164f3db24b9141f1f77553182a4b9c789f883476eb33d87ea4d8f13", size = 4850, upload-time = "2026-03-02T19:18:21.221Z" } wheels = [ @@ -4208,11 +4311,11 @@ name = "treecorr" version = "5.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "lsstdesc-coord", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "pybind11", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, - { name = "setuptools", marker = "sys_platform == 'linux'" }, + { name = "lsstdesc-coord" }, + { name = "numpy" }, + { name = "pybind11" }, + { name = "pyyaml" }, + { name = "setuptools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5d/95/ff399cb37b9e3d7c758f8cf0d5757f06a38093f223f2a65d7cc5b50313d5/treecorr-5.1.3.tar.gz", hash = "sha256:d921ccace064325a8ebb93f371c624f13fcc9559373e3b37cc216cb94ae81ed2", size = 1146147, upload-time = "2026-03-18T18:38:03.626Z" } wheels = [ @@ -4227,8 +4330,8 @@ name = "typeshed-client" version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-resources", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "importlib-resources" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/54/5d/97d6fa8c204b0d42be931e7a568b306cda43f93ff45f8b47537ee61390de/typeshed_client-2.12.0.tar.gz", hash = "sha256:54dcfa25fcff82cedcb1b51c03c1b3c51a614097aab8fe49e4316aff6f79d9c7", size = 529457, upload-time = "2026-06-02T04:00:51.51Z" } wheels = [ @@ -4249,7 +4352,7 @@ name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ @@ -4297,9 +4400,9 @@ name = "vos" version = "3.6.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aenum", marker = "sys_platform == 'linux'" }, - { name = "cadcutils", marker = "sys_platform == 'linux'" }, - { name = "html2text", marker = "sys_platform == 'linux'" }, + { name = "aenum" }, + { name = "cadcutils" }, + { name = "html2text" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3e/6c/445bbedf2f90e71c09ff890aa8b409af5d074b1578eef383117cbcdfd09a/vos-3.6.4.tar.gz", hash = "sha256:67e4e24689ee881124b6670d7d2fc6527cc404687fc527fffc20cf5b1bd4c0bd", size = 103445, upload-time = "2025-12-12T00:20:56.398Z" } wheels = [ @@ -4342,6 +4445,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, ] +[[package]] +name = "wget" +version = "3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/6a/62e288da7bcda82b935ff0c6cfe542970f04e29c756b0e147251b2fb251f/wget-3.2.zip", hash = "sha256:35e630eca2aa50ce998b9b1a127bb26b30dfee573702782aa982f875e3f16061", size = 10857, upload-time = "2015-10-22T15:26:37.51Z" } + [[package]] name = "widgetsnbextension" version = "4.0.15" @@ -4395,9 +4504,9 @@ name = "yte" version = "1.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "argparse-dataclass", marker = "sys_platform == 'linux'" }, - { name = "dpath", marker = "sys_platform == 'linux'" }, - { name = "pyyaml", marker = "sys_platform == 'linux'" }, + { name = "argparse-dataclass" }, + { name = "dpath" }, + { name = "pyyaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/44/f5/7e44620e6e077bfe624b9a17c329b8e0d0159e176e1f1a93c2790428ab2c/yte-1.9.4.tar.gz", hash = "sha256:86a47e6d722cec9419a7ac88be57d0d6c4ce28f02860393b71a66f2c674069f6", size = 8101, upload-time = "2025-11-27T12:55:00.85Z" } wheels = [ @@ -4409,12 +4518,12 @@ name = "zarr" version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "donfig", marker = "sys_platform == 'linux'" }, - { name = "google-crc32c", marker = "sys_platform == 'linux'" }, - { name = "numcodecs", marker = "sys_platform == 'linux'" }, - { name = "numpy", marker = "sys_platform == 'linux'" }, - { name = "packaging", marker = "sys_platform == 'linux'" }, - { name = "typing-extensions", marker = "sys_platform == 'linux'" }, + { name = "donfig" }, + { name = "google-crc32c" }, + { name = "numcodecs" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/8d/aeb164004f87543b06ef54f885d02c342c31ceb274e2bbec470a98927621/zarr-3.2.1.tar.gz", hash = "sha256:71565b738a0e7e8ed226f0516eba8c6bb53440ad7669a8c48ebb3534a161d035", size = 675161, upload-time = "2026-05-05T12:37:22.383Z" } wheels = [ From 7781c872a5ceb8941c0adb83e43fb3b2ec3e9bda Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:27:31 +0200 Subject: [PATCH 070/107] Point the non-tomographic workflow at the merged tomographic API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflow scripts call the ("all", "all") pair explicitly (calculate_2pcf_version(...)["tomo_bin_all_tomo_bin_all"], calculate_pseudo_cl(compute_tomography=False, out_path=...), calculate_pseudo_cl_inka_cov(compute_tomography=False), calculate_rho_tau_stats(tomography=False)), and the rules declare the names the methods write: cv_basename and the rho/tau outputs carry _tomo_bin_all, the ξ± dump is xi_{basename}.txt (also in papers/bmodes, which imports these rules). The pseudo-Cl covariance job computes in a private directory, since the iNKA blocks are cached under binning-only names. plot_2pcf and plot_ratio_xi_sys_xi come back as wrappers over plot_2pcf_tomography, so the cv_plot_2pcf / cv_ratio_xi_sys_xi rules and run_cosmo_val.py run; xi_psf_sys computes the non-tomographic fit first, as rho_tau_fits does. The ρ/τ plot rules pass the figure names the methods no longer default to, and plot_rho_tau_fits saves its contours into the output directory. pol_factor: true in the paper config passed the ±1 assert and meant no e2 flip; it is -1. GLASS_mock_validation's PSF columns take the v2 names its file presents (as SP_v1.6.6, same file) and its bin column the file's TOM_BIN_ID. compute_tomography and force_run are exempt from the cv_init_params forwarding: rule scripts choose the pair per call, and Snakemake decides what reruns. Co-Authored-By: Claude Opus 5.5 --- cosmo_val/cat_config.yaml | 21 ++- cosmo_val/run_cosmo_val.py | 1 - papers/bmodes/rules/figures.smk | 2 +- .../scripts/bb_covariance_nz_independence.py | 2 +- .../scripts/cosebis_version_comparison.py | 2 +- .../harmonic_config_cosebis_comparison.py | 4 +- papers/cosmo_val/config/config.yaml | 3 +- .../cosmo_val/psf_systematics.py | 6 +- src/sp_validation/cosmo_val/real_space.py | 120 ++++++++++++++++++ .../tests/test_cv_init_params.py | 10 +- workflow/common.py | 9 +- workflow/rules/cosmo_val.smk | 8 +- workflow/rules/twopoint.smk | 8 +- workflow/scripts/cv_plot_2pcf.py | 12 +- workflow/scripts/cv_plot_rho_stats.py | 2 +- workflow/scripts/cv_plot_tau_stats.py | 2 +- workflow/scripts/cv_ratio_xi_sys_xi.py | 15 ++- workflow/scripts/cv_rho_tau_fits.py | 7 +- workflow/scripts/generate_pseudo_cl.py | 2 +- workflow/scripts/generate_pseudo_cl_cov.py | 67 +++++----- workflow/scripts/run_2pcf.py | 10 +- workflow/scripts/run_glass_mock_pseudo_cl.py | 4 +- workflow/scripts/run_rho_tau.py | 2 +- 23 files changed, 233 insertions(+), 86 deletions(-) diff --git a/cosmo_val/cat_config.yaml b/cosmo_val/cat_config.yaml index 1a55b8d7..5dc90cdd 100644 --- a/cosmo_val/cat_config.yaml +++ b/cosmo_val/cat_config.yaml @@ -1223,19 +1223,18 @@ GLASS_mock_validation: sigma_e: 0.379587601488189 mask: /n09data/guerrini/glass_mock_test/mask_nside_1024.fits psf: - PSF_flag: FLAG_PSF_HSM - PSF_size: SIGMA_PSF_HSM - square_size: true - star_flag: FLAG_STAR_HSM - star_size: SIGMA_STAR_HSM + PSF_flag: HSM_FLAG_PSF + PSF_size: HSM_T_PSF + star_flag: HSM_FLAG_STAR + star_size: HSM_T_STAR hdu: 1 path: /n17data/UNIONS/WL/v1.6.x/unions_shapepipe_psf_2024_v1.6.a.fits ra_col: RA - dec_col: Dec - e1_PSF_col: E1_PSF_HSM - e1_star_col: E1_STAR_HSM - e2_PSF_col: E2_PSF_HSM - e2_star_col: E2_STAR_HSM + dec_col: DEC + e1_PSF_col: HSM_G1_PSF + e1_star_col: HSM_G1_STAR + e2_PSF_col: HSM_G2_PSF + e2_star_col: HSM_G2_STAR shear: R: 1.0 path: tomo_test_2_glass_sim_00001_1024.fits @@ -1246,7 +1245,7 @@ GLASS_mock_validation: e2_col: e2 e2_PSF_col: e2_PSF cols: RA,Dec - tomo_bin_col: tom_bin_id + tomo_bin_col: TOM_BIN_ID star: ra_col: RA dec_col: Dec diff --git a/cosmo_val/run_cosmo_val.py b/cosmo_val/run_cosmo_val.py index de19ebc3..f7c34fb0 100644 --- a/cosmo_val/run_cosmo_val.py +++ b/cosmo_val/run_cosmo_val.py @@ -134,7 +134,6 @@ # max_sep_int=300, # nbins_int=100, # npatch=256, -# var_method="jackknife", # ) # %% diff --git a/papers/bmodes/rules/figures.smk b/papers/bmodes/rules/figures.smk index 31b65ea0..b9095aaf 100644 --- a/papers/bmodes/rules/figures.smk +++ b/papers/bmodes/rules/figures.smk @@ -84,7 +84,7 @@ def _reporting_cov_path(version): def _xi_integration_path(version): """Path to fine-binned 2PCF integration file. Unpatched: values only, no covariance.""" return ( - f"{COSMO_VAL_OUTPUT}/{version}_xi_minsep={FIDUCIAL['min_sep_int']}" + f"{COSMO_VAL_OUTPUT}/xi_{version}_tomo_bin_all_minsep={FIDUCIAL['min_sep_int']}" f"_maxsep={FIDUCIAL['max_sep_int']}_nbins={FIDUCIAL['nbins_int']}_npatch=1.txt" ) diff --git a/papers/bmodes/scripts/bb_covariance_nz_independence.py b/papers/bmodes/scripts/bb_covariance_nz_independence.py index 7f8cae02..a11f9916 100644 --- a/papers/bmodes/scripts/bb_covariance_nz_independence.py +++ b/papers/bmodes/scripts/bb_covariance_nz_independence.py @@ -511,7 +511,7 @@ def _from_cli(argv=None): } xi_integration_path = os.path.join( a.cosmo_val_dir, - f"{version}_xi_minsep={min_sep_int}_maxsep={max_sep_int}" + f"xi_{version}_tomo_bin_all_minsep={min_sep_int}_maxsep={max_sep_int}" f"_nbins={nbins_int}_npatch={npatch}.txt", ) pseudo_cl_path = os.path.join( diff --git a/papers/bmodes/scripts/cosebis_version_comparison.py b/papers/bmodes/scripts/cosebis_version_comparison.py index 464cca70..51fa9d53 100644 --- a/papers/bmodes/scripts/cosebis_version_comparison.py +++ b/papers/bmodes/scripts/cosebis_version_comparison.py @@ -148,7 +148,7 @@ def _create_stacked_bmode_figure( def _xi_integration(results_dir, ver): - return f"{results_dir}/{ver}_xi_minsep=0.5_maxsep=300.0_nbins=1000_npatch=1.txt" + return f"{results_dir}/xi_{ver}_tomo_bin_all_minsep=0.5_maxsep=300.0_nbins=1000_npatch=1.txt" def _cov_integration(cov_dir, ver): diff --git a/papers/bmodes/scripts/harmonic_config_cosebis_comparison.py b/papers/bmodes/scripts/harmonic_config_cosebis_comparison.py index 5e4e4e3e..2fc52dae 100644 --- a/papers/bmodes/scripts/harmonic_config_cosebis_comparison.py +++ b/papers/bmodes/scripts/harmonic_config_cosebis_comparison.py @@ -889,7 +889,7 @@ def _from_cli(argv=None): default=None, help=( "Explicit path to the fiducial 1000-bin integration xi_pm text file " - "reproduced by lc (e.g. SP_v1.4.6.3_leak_corr_xi_minsep=0.5_maxsep=300.0_" + "reproduced by lc (e.g. xi_SP_v1.4.6.3_leak_corr_tomo_bin_all_minsep=0.5_maxsep=300.0_" "nbins=1000_npatch=1.txt), overriding the --cosmo-val-dir pattern lookup " "for --fiducial-version." ), @@ -946,7 +946,7 @@ def _from_cli(argv=None): if is_fiducial and a.fiducial_xi_path else os.path.join( a.cosmo_val_dir, - f"{ver}_xi_minsep={min_sep_int}_maxsep={max_sep_int}" + f"xi_{ver}_tomo_bin_all_minsep={min_sep_int}_maxsep={max_sep_int}" f"_nbins={nbins_int}_npatch={npatch}.txt", ) ) diff --git a/papers/cosmo_val/config/config.yaml b/papers/cosmo_val/config/config.yaml index 5b33999f..7dd0e091 100644 --- a/papers/cosmo_val/config/config.yaml +++ b/papers/cosmo_val/config/config.yaml @@ -43,7 +43,8 @@ cosmo_val: power: 0.5 n_ell_bins: 32 ell_step: 10 - pol_factor: true + # e2 multiplier (±1) applied before NaMaster; -1 flips the e2 sign + pol_factor: -1 noise_bias_method: analytic cell_seed: 8192 path_onecovariance: "/home/guerrini/OneCovariance/" diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 63c1b73a..44476061 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -67,7 +67,9 @@ def rho_tau_fits(self): @property def xi_psf_sys(self): if not hasattr(self, "_xi_psf_sys"): - self.calculate_rho_tau_fits() + self.calculate_rho_tau_fits(tomography=False) + if self.compute_tomography: + self.calculate_rho_tau_fits(tomography=True) return self._xi_psf_sys # --- calculate functions --- @@ -833,7 +835,7 @@ def plot_rho_tau_fits( sample_list, names=["x0", "x1", "x2"], labels=[r"\alpha", r"\beta", r"\eta"], - savefig=savefig_contours, + savefig=savefig, legend_labels=versions, legend_loc="upper right", contour_colors=colors, diff --git a/src/sp_validation/cosmo_val/real_space.py b/src/sp_validation/cosmo_val/real_space.py index da5cfdd8..fd1f2bbd 100644 --- a/src/sp_validation/cosmo_val/real_space.py +++ b/src/sp_validation/cosmo_val/real_space.py @@ -269,6 +269,81 @@ def map2(self): self.calculate_aperture_mass_dispersion() return self._map2 + def plot_2pcf( + self, tomography=False, offset=0.02, alpha=1.0, show=True, close=True + ): + """Plot ξ± of every version, one panel per bin pair. + + Measures, or reads back, the 2PCF with :meth:`calculate_2pcf` and draws + it with :meth:`plot_2pcf_tomography`, as ξ± (log-log) and as θ·ξ±. + Writes ``xi_pm_tomography_{tomography}.png`` and + ``xi_pm_theta_tomography_{tomography}.png`` under the output directory. + """ + self.calculate_2pcf(compute_tomography=tomography) + + for times_theta in (False, True): + prefix = r"$\theta\,$" if times_theta else "" + suffix = "_theta" if times_theta else "" + self.plot_2pcf_tomography( + self._xiplus_ximinus_sample_x_y_plot_function, + r"$\theta$ [arcmin]", + prefix + r"$\xi_+(\theta)$", + prefix + r"$\xi_-(\theta)$", + (0.05, 0.9) if times_theta else (0.8, 0.95), + extract_text_offset=times_theta, + add_index_version_to_kwargs=True, + x_scale="log", + y_scale="linear" if times_theta else "log", + tomography=tomography, + savefig=self._output_path(f"xi_pm{suffix}_tomography_{tomography}.png"), + show=show, + close=close, + offset=offset, + times_theta=times_theta, + alpha=alpha, + ) + + def plot_ratio_xi_sys_xi( + self, tomography=False, threshold=0.1, offset=0.02, show=True, close=True + ): + """Plot ξ^{PSF, sys}_± / ξ± of every version, one panel per bin pair. + + The band marks ``±threshold``. ξ± comes from :meth:`calculate_2pcf` and + ξ^{PSF, sys} from the ``xi_psf_sys`` property, both on the instance's + ``treecorr_config`` binning. Writes ``ratio_xi_sys_xi.png`` (non- + tomographic) or ``ratio_xi_sys_xi_tomography.png`` under the output + directory. + """ + if tomography and not self.compute_tomography: + raise ValueError( + "plot_ratio_xi_sys_xi(tomography=True) needs the tomographic " + "xi_psf_sys; construct CosmologyValidation with " + "compute_tomography=True" + ) + self.calculate_2pcf(compute_tomography=tomography) + + y_label = r"$\xi^{{\rm PSF, sys}}_{0} / \xi_{0}$" + self.plot_2pcf_tomography( + self._ratio_xi_sys_xi_x_y_plot_function, + r"$\theta$ [arcmin]", + y_label.format("+"), + y_label.format("-"), + (0.8, 0.95), + extract_text_offset=False, + add_index_version_to_kwargs=True, + x_scale="log", + tomography=tomography, + savefig=self._output_path( + "ratio_xi_sys_xi_tomography.png" + if tomography + else "ratio_xi_sys_xi.png" + ), + show=show, + close=close, + offset=offset, + threshold=threshold, + ) + def plot_2pcf_tomography( self, x_y_plot_function, @@ -648,3 +723,48 @@ def _mapsq_mxsq_sample_x_y_plot_function( markersize=3, capsize=2, ) + + def _ratio_xi_sys_xi_x_y_plot_function( + self, + ax_plus, + ax_minus, + version, + tomo_bin_a, + tomo_bin_b, + idx, + versions, + color, + offset, + threshold, + ): + """Plot ξ^{PSF, sys}_± / ξ± for one version/tomographic-bin pair. + + Fed into :meth:`plot_2pcf_tomography` as the ``x_y_plot_function`` + argument. The error bar propagates the variances of both ξ^{PSF, sys} + and ξ±; the first version also draws the ``±threshold`` band. + """ + key = f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}" + gg = self.cat_ggs[version][key] + xi_psf_sys = self.xi_psf_sys[version][key] + + theta = self._get_jittered_theta(gg.meanr, idx, len(versions), offset) + + for ax, xi, var_xi, component in ( + (ax_plus, gg.xip, gg.varxip, "plus"), + (ax_minus, gg.xim, gg.varxim, "minus"), + ): + mean = xi_psf_sys[f"mean_{component}"] + var = xi_psf_sys[f"var_{component}"] + ratio = mean / xi + ratio_err = np.sqrt(var / xi**2 + mean**2 * var_xi / xi**4) + ax.errorbar( + theta, + ratio, + yerr=ratio_err, + color=color, + fmt=self.cc[version].get("marker", "o"), + markersize=3, + capsize=2, + ) + if idx == 0: + ax.axhspan(-threshold, threshold, color="black", alpha=0.1) diff --git a/src/sp_validation/tests/test_cv_init_params.py b/src/sp_validation/tests/test_cv_init_params.py index ffc87d8b..7ec743cc 100644 --- a/src/sp_validation/tests/test_cv_init_params.py +++ b/src/sp_validation/tests/test_cv_init_params.py @@ -16,7 +16,15 @@ REPO = Path(__file__).resolve().parents[3] -EXEMPT = {} +EXEMPT = { + # The workflow is the non-tomographic DAG: every rule script names the bin + # pair it computes, and the flag only switches the lazy properties on to + # tomographic products no rule declares. + "compute_tomography": "rule scripts pass tomography per call", + # Snakemake owns staleness: it removes a job's declared outputs before + # running it, so the methods' skip-if-exists only ever reuses intermediates. + "force_run": "Snakemake decides what reruns", +} def _load_common(): diff --git a/workflow/common.py b/workflow/common.py index de0f71b2..b250399c 100644 --- a/workflow/common.py +++ b/workflow/common.py @@ -375,14 +375,15 @@ def get_shear_catalog(wildcards): def cv_basename(version, fiducial=None): - """Reproduce CosmologyValidation.basename() for a version. + """Reproduce CosmologyValidation.basename() for a version's ("all", "all") pair. - Mirrors the f-string in cosmo_val.py so rule outputs match exactly what the - method writes. Uses fiducial binning (min_sep/max_sep/nbins/npatch). + Mirrors the f-string of ``CosmologyValidation.basename(version)`` so rule + outputs match exactly what the method writes. Uses fiducial binning + (min_sep/max_sep/nbins/npatch). """ fiducial = fiducial or FIDUCIAL return ( - f"{version}_minsep={fiducial['min_sep']}" + f"{version}_tomo_bin_all_minsep={fiducial['min_sep']}" f"_maxsep={fiducial['max_sep']}" f"_nbins={fiducial['nbins']}" f"_npatch={fiducial['npatch']}" diff --git a/workflow/rules/cosmo_val.smk b/workflow/rules/cosmo_val.smk index 8d9839c6..699bc9de 100644 --- a/workflow/rules/cosmo_val.smk +++ b/workflow/rules/cosmo_val.smk @@ -50,12 +50,12 @@ CV_BINNING = ( def cv_xi_txt(version): - """Path to the 2pcf data vector calculate_2pcf writes for a version. + """Path to the ξ± TreeCorr dump calculate_2pcf_version writes for a version. - Mirrors the out_fname f-string in cosmo_val.calculate_2pcf: - {ver}_xi_minsep=..._maxsep=..._nbins=..._npatch=...txt + Mirrors RealSpaceMixin._xi_txt_path for the ("all", "all") pair: + xi_{ver}_tomo_bin_all_minsep=..._maxsep=..._nbins=..._npatch=...txt """ - return str(COSMO_VAL / f"{version}_xi_{xi_binning('reporting')}.txt") + return str(COSMO_VAL / f"xi_{version}_tomo_bin_all_{xi_binning('reporting')}.txt") def cv_rho_stats(version): diff --git a/workflow/rules/twopoint.smk b/workflow/rules/twopoint.smk index 15174fa2..93abebe4 100644 --- a/workflow/rules/twopoint.smk +++ b/workflow/rules/twopoint.smk @@ -22,7 +22,7 @@ rule xi: input: catalog=get_shear_catalog, output: - txt=str(COSMO_VAL / "{version}_xi_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.txt"), + txt=str(COSMO_VAL / "xi_{version}_tomo_bin_all_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.txt"), sacc=str(COSMO_VAL / "{version}_xi_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.sacc"), threads: 24 params: @@ -46,10 +46,10 @@ rule xi: rule rho_tau_stats: output: - rho_stats=str(COSMO_VAL / "rho_tau_stats/rho_stats_{version}_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.fits"), - tau_stats=str(COSMO_VAL / "rho_tau_stats/tau_stats_{version}_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.fits"), + rho_stats=str(COSMO_VAL / "rho_tau_stats/rho_stats_{version}_tomo_bin_all_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.fits"), + tau_stats=str(COSMO_VAL / "rho_tau_stats/tau_stats_{version}_tomo_bin_all_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.fits"), # Born-as-SACC ρ/τ part, written alongside the FITS. - rho_tau=str(COSMO_VAL / "rho_tau_stats/rho_tau_{version}_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.sacc"), + rho_tau=str(COSMO_VAL / "rho_tau_stats/rho_tau_{version}_tomo_bin_all_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.sacc"), threads: 48 params: ver="{version}", diff --git a/workflow/scripts/cv_plot_2pcf.py b/workflow/scripts/cv_plot_2pcf.py index 5251e7a5..c589edb3 100644 --- a/workflow/scripts/cv_plot_2pcf.py +++ b/workflow/scripts/cv_plot_2pcf.py @@ -1,14 +1,14 @@ -"""Rule cv_plot_2pcf: n_pairs / xi± overlay across versions. +"""Rule cv_plot_2pcf: xi± overlay across versions. -Reads each version's xi txt (declared inputs, produced by cv_2pcf); calls -plot_2pcf, which re-reads the existing txt files rather than recomputing. -Writes figures under the output dir. Sentinel-tracked: plot_2pcf emits several -figures whose names are internal. +Reads each version's xi txt (declared inputs, produced by the xi rule); calls +plot_2pcf, which reads the existing txt files back rather than recomputing, for +the non-tomographic ("all", "all") pair. Writes figures under the output dir. +Sentinel-tracked: plot_2pcf emits several figures whose names are internal. """ from cv_runner import _unbuffer_streams, make_cv, touch_sentinels _unbuffer_streams() cv = make_cv(snakemake) -cv.plot_2pcf() +cv.plot_2pcf(tomography=False, show=False) touch_sentinels(snakemake) diff --git a/workflow/scripts/cv_plot_rho_stats.py b/workflow/scripts/cv_plot_rho_stats.py index c99bd646..a55a1377 100644 --- a/workflow/scripts/cv_plot_rho_stats.py +++ b/workflow/scripts/cv_plot_rho_stats.py @@ -9,5 +9,5 @@ _unbuffer_streams() cv = make_cv(snakemake) -cv.plot_rho_stats() +cv.plot_rho_stats(savefig="rho_stats.png", show=False) touch_sentinels(snakemake) diff --git a/workflow/scripts/cv_plot_tau_stats.py b/workflow/scripts/cv_plot_tau_stats.py index 2a00b3a5..8975ed81 100644 --- a/workflow/scripts/cv_plot_tau_stats.py +++ b/workflow/scripts/cv_plot_tau_stats.py @@ -8,5 +8,5 @@ _unbuffer_streams() cv = make_cv(snakemake) -cv.plot_tau_stats() +cv.plot_tau_stats(tomography=False, savefig="tau_stats.png", show=False) touch_sentinels(snakemake) diff --git a/workflow/scripts/cv_ratio_xi_sys_xi.py b/workflow/scripts/cv_ratio_xi_sys_xi.py index 79afd89a..3aebd8b8 100644 --- a/workflow/scripts/cv_ratio_xi_sys_xi.py +++ b/workflow/scripts/cv_ratio_xi_sys_xi.py @@ -1,15 +1,18 @@ """Rule cv_ratio_xi_sys_xi: ratio of PSF systematics to cosmic-shear signal. -Joins two upstream chains: the 2pcf data vector (xi txt, reloaded via -calculate_2pcf) and xi_psf_sys (recomputed in memory from the rho/tau FITS via -the lazy cv.xi_psf_sys property — the PSF-error fit is not persisted by -cosmo_val.py). Both are declared as inputs so the DAG shows the join. Writes -ratio_xi_sys_xi.png at a fixed path (declared output). +Joins two upstream chains for the non-tomographic ("all", "all") pair: the +2pcf data vector (xi txt, read back by calculate_2pcf) and xi_psf_sys +(recomputed in memory from the rho/tau FITS via the lazy cv.xi_psf_sys +property — the PSF-error fit is not persisted). Both are declared as inputs so +the DAG shows the join. Writes ratio_xi_sys_xi.png at a fixed path (declared +output). """ from cv_runner import _unbuffer_streams, make_cv, verify_outputs _unbuffer_streams() cv = make_cv(snakemake) -cv.plot_ratio_xi_sys_xi(offset=snakemake.params.get("offset", 0.1)) +cv.plot_ratio_xi_sys_xi( + tomography=False, offset=snakemake.params.get("offset", 0.1), show=False +) verify_outputs(snakemake) diff --git a/workflow/scripts/cv_rho_tau_fits.py b/workflow/scripts/cv_rho_tau_fits.py index 64b24b67..6e02ba10 100644 --- a/workflow/scripts/cv_rho_tau_fits.py +++ b/workflow/scripts/cv_rho_tau_fits.py @@ -13,5 +13,10 @@ _unbuffer_streams() cv = make_cv(snakemake) if cv.rho_tau_method != "none": - cv.plot_rho_tau_fits() + cv.plot_rho_tau_fits( + tomography=False, + savefig_contours="contours_tau_stat.png", + savefig_xi_psf_sys="xi_psf_sys", + show=False, + ) touch_sentinels(snakemake) diff --git a/workflow/scripts/generate_pseudo_cl.py b/workflow/scripts/generate_pseudo_cl.py index e1b96da6..53679944 100644 --- a/workflow/scripts/generate_pseudo_cl.py +++ b/workflow/scripts/generate_pseudo_cl.py @@ -126,7 +126,7 @@ def generate_pseudo_cl( cv = CosmologyValidation(**cv_kwargs) # Pseudo-Cls only (no covariance), born directly at the final out_path. - cv.calculate_pseudo_cl(out_path=out_path) + cv.calculate_pseudo_cl(compute_tomography=False, out_path=out_path) if os.path.exists(out_path): # Readback of the part just written — a legitimate pre-blind consumer. diff --git a/workflow/scripts/generate_pseudo_cl_cov.py b/workflow/scripts/generate_pseudo_cl_cov.py index 247c5f33..4f00f7f9 100644 --- a/workflow/scripts/generate_pseudo_cl_cov.py +++ b/workflow/scripts/generate_pseudo_cl_cov.py @@ -1,13 +1,13 @@ """Generate pseudo-Cl covariances (no data vector). Dual-mode. Under Snakemake (``script:`` directive) the injected ``snakemake`` -object supplies the parameters and the native product is renamed to the tagged -output filename the rule declares; as a standalone CLI (argparse) the same -compute runs from explicit flags and the primitive's native -``pseudo_cl_cov_{ver}.fits`` is left in place under ``--out`` (no rename — each -lc/ASTRA recipe gets its own output directory, so the untagged native name is -unambiguous and the primitives' skip-if-exists never collides across nbins -runs). The CLI form is what the lightcone/ASTRA recipe calls, so the +object supplies the parameters, the covariance is computed in a private +working directory and moved to the tagged output filename the rule declares; +as a standalone CLI (argparse) the same compute runs from explicit flags and +the native ``pseudo_cl/pseudo_cl_cov_non_tomo_{ver}_from_iNKA_…fits`` (with its +``iNKA_block_{ver}/`` block) is left in place under ``--out`` (each lc/ASTRA +recipe gets its own output directory, so the methods' skip-if-exists never +reuses another configuration's covariance). The CLI form is what the lightcone/ASTRA recipe calls, so the measurement is driven directly (no nested Snakemake) with lc handling orchestration: @@ -27,6 +27,8 @@ import argparse import json import os +import shutil +import tempfile from astropy.io import fits @@ -51,9 +53,10 @@ def generate_pseudo_cl_cov( version : str Catalog version (e.g., "SP_v1.4.6_leak_corr") output_dir : str - Directory the covariance FITS file is written into. The primitive writes - its native ``pseudo_cl_cov_{version}.fits`` here; callers that need a - tagged filename rename it themselves (see ``_from_snakemake``). + Output directory of the ``CosmologyValidation`` run. The covariance is + written to its native path under ``output_dir/pseudo_cl/``; callers + that need a tagged filename move it themselves (see + ``_from_snakemake``). cat_config : str Path to catalog configuration YAML nside : int @@ -73,7 +76,7 @@ def generate_pseudo_cl_cov( Returns ------- str - Path to the primitive's native ``pseudo_cl_cov_{version}.fits`` product. + Path to the native non-tomographic iNKA covariance FITS. """ os.makedirs(output_dir, exist_ok=True) @@ -129,12 +132,12 @@ def generate_pseudo_cl_cov( cv = CosmologyValidation(**cv_kwargs) - # Calculate covariance only + # Covariance of the ("all", "all") pair only print("Calculating covariance...") - cv.calculate_pseudo_cl_eb_cov() + cv.calculate_pseudo_cl_inka_cov(compute_tomography=False) - # Report on the native product (renamed by the Snakemake caller, if any) - src_cov = os.path.join(output_dir, f"pseudo_cl_cov_{version}.fits") + # Report on the native product (moved by the Snakemake caller, if any) + src_cov = cv._output_path_pseudo_cl_cov(version, "iNKA", tomography=False) if os.path.exists(src_cov): with fits.open(src_cov) as hdul: # CV outputs covariance blocks as COVAR_XX_YY extensions @@ -147,21 +150,25 @@ def generate_pseudo_cl_cov( def _from_snakemake(smk): p = smk.params output_cov = smk.output.pseudo_cl_cov - src_cov = generate_pseudo_cl_cov( - version=p["version"], - output_dir=os.path.dirname(output_cov), - cat_config=p["cat_config"], - nside=int(p["nside"]), - npatch=int(p["npatch"]), - cosmo_params=p.get("cosmo_params", None), - binning=p["binning"], - nbins=int(p["nbins"]), - power=float(p.get("power", 0.5)), - ) - # Snakemake declares a tagged output filename; rename the native product to it. - if os.path.exists(src_cov) and src_cov != output_cov: - os.rename(src_cov, output_cov) - print(f"Saved to: {output_cov}") + out_dir = os.path.dirname(output_cov) + os.makedirs(out_dir, exist_ok=True) + # The iNKA blocks and the merged covariance are cached under names that + # carry the binning only, so the job computes in a directory of its own and + # never picks up a block another configuration left behind. + with tempfile.TemporaryDirectory(dir=out_dir, prefix=".pseudo_cl_cov_") as work: + src_cov = generate_pseudo_cl_cov( + version=p["version"], + output_dir=work, + cat_config=p["cat_config"], + nside=int(p["nside"]), + npatch=int(p["npatch"]), + cosmo_params=p.get("cosmo_params", None), + binning=p["binning"], + nbins=int(p["nbins"]), + power=float(p.get("power", 0.5)), + ) + shutil.move(src_cov, output_cov) + print(f"Saved to: {output_cov}") def _from_cli(argv=None): diff --git a/workflow/scripts/run_2pcf.py b/workflow/scripts/run_2pcf.py index 28c8c521..1fcd9a0c 100644 --- a/workflow/scripts/run_2pcf.py +++ b/workflow/scripts/run_2pcf.py @@ -14,7 +14,8 @@ The measurement is binning-agnostic: the reporting and the fine integration grids are the same compute with different ``--min-sep/--max-sep/--nbins``. -``CosmologyValidation.calculate_2pcf`` writes the ``.txt`` dump (a raw +``CosmologyValidation.calculate_2pcf_version`` measures the non-tomographic +``("all", "all")`` pair and writes its ``xi_{basename}.txt`` dump (a raw byproduct); the ξ± data product is born as SACC here, a *part* named by its binning and tagged with its ``--grid``. The part carries the covariance the measurement estimated: the dense jackknife covariance when it had patches, the @@ -68,13 +69,14 @@ def run_2pcf( # so the SACC provenance metadata stamps the npatch actually measured npatch=npatch, ) - gg = cv.calculate_2pcf( - ver=ver, + gg = cv.calculate_2pcf_version( + ver, npatch=npatch, + compute_tomography=False, min_sep=min_sep, max_sep=max_sep, nbins=nbins, - ) + )["tomo_bin_all_tomo_bin_all"] # Born-as-SACC ξ± part. theta = meanr; theta_nom = rnom. jackknife = gg.var_method == "jackknife" diff --git a/workflow/scripts/run_glass_mock_pseudo_cl.py b/workflow/scripts/run_glass_mock_pseudo_cl.py index 9d90c080..9f9e9371 100644 --- a/workflow/scripts/run_glass_mock_pseudo_cl.py +++ b/workflow/scripts/run_glass_mock_pseudo_cl.py @@ -4,7 +4,7 @@ NaMaster estimator. Matches the real data pipeline binning exactly. Mock catalogs have columns: RA, Dec, e1, e2, w. -Polarization convention: pol_factor=True → field=[e1, -e2] (matches pipeline). +Polarization convention: pol_factor=-1 → field=[e1, -e2] (matches pipeline). """ import numpy as np @@ -49,7 +49,7 @@ print(f"Binning: {nbins} powspace bins, ell=[{ell_eff[0]:.1f}, {ell_eff[-1]:.1f}]") # Create NaMaster spin-2 field from catalog -# pol_factor=True: e2 sign flip to match IAU polarization convention +# pol_factor=-1: e2 sign flip to match IAU polarization convention print("Creating NaMaster field...") f_all = nmt.NmtFieldCatalog( positions=[ra, dec], diff --git a/workflow/scripts/run_rho_tau.py b/workflow/scripts/run_rho_tau.py index a798a1b6..d04da3f0 100644 --- a/workflow/scripts/run_rho_tau.py +++ b/workflow/scripts/run_rho_tau.py @@ -19,5 +19,5 @@ catalog_config=params["cat_config"], output_dir=params["output_dir"], ) -cv.calculate_rho_tau_stats() +cv.calculate_rho_tau_stats(tomography=False) verify_outputs(snakemake) From 2ae102abd5bd8278d88a36fc1b3f32e2a1434c14 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:28:13 +0200 Subject: [PATCH 071/107] Read the ("all", "all") pair in the B-mode summary and the SACC n(z) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit summarize_bmodes looked up the pre-tomography shapes inside try/except KeyError, so pure E/B, COSEBIs and C_ℓ^BB all dropped out of the summary silently. It now reads the pair entry of each result (plot_cosebis stores its result under the pair key, as plot_pure_eb does) and catches only the RuntimeError of a scale cut that selects no bin. get_redshift, hence sacc_nz and every SACC writer, unpacked a multi-column tomographic n(z) file into (z, nz1, ..., nzN); it now returns the summed whole-survey n(z) through read_redshift_distribution, the n(z) the pseudo-Cl iNKA fiducial uses for the same pair. pol_factor=True passed the ±1 assert and meant no e2 flip; bools are rejected. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/cosmo_val/core.py | 72 ++++++++++++++------------ src/sp_validation/cosmo_val/cosebis.py | 2 +- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 2a66cd9c..017b5c74 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -316,7 +316,9 @@ def __init__( self.n_ell_bins = n_ell_bins self.ell_step = ell_step - assert pol_factor in (-1, 1), "The polarisatio factor must be -1 or 1." + # bool is an int subclass: True would pass `in (-1, 1)` and mean "no flip" + if isinstance(pol_factor, bool) or pol_factor not in (-1, 1): + raise ValueError(f"pol_factor must be -1 or 1, got {pol_factor!r}") self.pol_factor = pol_factor self.nrandom_cell = nrandom_cell @@ -462,7 +464,8 @@ def _output_path(self, *parts): def sacc_nz(self, version): """Single-bin ``nz`` mapping ``{0: (z, nz)}`` for the SACC writers. - The round is single-bin, so the whole survey n(z) is bin 0. + The SACC parts carry the ("all", "all") pair only, so the whole-survey + n(z) of :meth:`get_redshift` is bin 0. """ return {0: tuple(self.get_redshift(version))} @@ -475,7 +478,12 @@ def sacc_metadata(self, version): } def get_redshift(self, version): - """Load redshift distribution for a catalog version. + """Load the whole-survey redshift distribution of a catalog version. + + The ``redshift_path`` file holds z in column 0 and one n(z) column per + tomographic bin (a single column for a non-tomographic version); the + whole-survey n(z) is their sum, as + ``read_redshift_distribution(version, is_tomography=False)`` returns it. Parameters ---------- @@ -489,7 +497,7 @@ def get_redshift(self, version): nz : ndarray n(z) probability density """ - return np.loadtxt(self.cc[version]["shear"]["redshift_path"], unpack=True) + return self.read_redshift_distribution(version, is_tomography=False) def _write_catalog_config(self): with self.catalog_config_path.open("w") as file: @@ -630,8 +638,9 @@ def colors(self): def summarize_bmodes(self, fiducial_scale_cut=(12, 83), versions=None): """Print and return B-mode PTE summary across all statistics. - Collects PTEs from pure E/B, COSEBIs, and pseudo-Cl at the specified - fiducial scale cut. Statistics that haven't been computed show '--'. + Collects the PTEs of the non-tomographic ``("all", "all")`` pair from + pure E/B, COSEBIs, and pseudo-Cl at the specified fiducial scale cut. + Statistics that haven't been computed show '--'. Parameters ---------- @@ -648,45 +657,42 @@ def summarize_bmodes(self, fiducial_scale_cut=(12, 83), versions=None): versions = versions or self.versions summary = {} cov_methods = set() + pair = "tomo_bin_all_tomo_bin_all" for ver in versions: row = {} # Pure E/B PTEs from stored results - if ver in self._pure_eb_results: - res = self._pure_eb_results[ver] - edges = (res["left_edges"], res["right_edges"]) + pure_eb = self._pure_eb_results.get(ver, {}).get(pair) + if pure_eb is not None: + edges = (pure_eb["left_edges"], pure_eb["right_edges"]) try: for stat in ("xip_B", "xim_B", "combined"): row[stat] = _get_pte_from_scale_cut( - res["pte_matrices"][stat], edges, fiducial_scale_cut + pure_eb["pte_matrices"][stat], edges, fiducial_scale_cut ) - except (KeyError, RuntimeError): + except RuntimeError: + # The scale cut selects no bin of this grid. pass - cov_methods.add(covariance_label(res["npatch"])) + cov_methods.add(covariance_label(pure_eb["npatch"])) # COSEBIs PTE from stored results - if ver in self._cosebis_results: - cosebis_res = self._cosebis_results[ver] - has_multi_scale_cuts = all(isinstance(k, tuple) for k in cosebis_res) - if has_multi_scale_cuts: - key = find_conservative_scale_cut_key( - cosebis_res, fiducial_scale_cut - ) - row["COSEBIS"] = cosebis_res[key]["pte_B"] - elif "pte_B" in cosebis_res: - row["COSEBIS"] = cosebis_res["pte_B"] - - # Pseudo-Cl BB PTE (_pseudo_cls is lazy; check existence without - # triggering computation) - if hasattr(self, "_pseudo_cls") and ver in self._pseudo_cls: - try: - cl_bb = self.pseudo_cls[ver]["pseudo_cl"]["BB"] - cov_bb = self.pseudo_cls[ver]["cov"]["COVAR_BB_BB"].data - _, _, row["C_l_BB"] = chi2_and_pte(cl_bb, cov_bb) - cov_methods.add("Gaussian (NaMaster)") - except (KeyError, AttributeError): - pass + cosebis = self._cosebis_results.get(ver, {}).get(pair) + if cosebis is not None: + if all(isinstance(k, tuple) for k in cosebis): + key = find_conservative_scale_cut_key(cosebis, fiducial_scale_cut) + row["COSEBIS"] = cosebis[key]["pte_B"] + else: + row["COSEBIS"] = cosebis["pte_B"] + + # Pseudo-Cl BB PTE, once both the spectrum and its covariance are + # loaded (_pseudo_cls is lazy; read it without triggering computation) + pseudo_cl = getattr(self, "_pseudo_cls", {}).get(ver, {}).get(pair, {}) + if "pseudo_cl" in pseudo_cl and "cov" in pseudo_cl: + cl_bb = pseudo_cl["pseudo_cl"]["BB"] + cov_bb = pseudo_cl["cov"]["COVAR_BB_BB"].data + _, _, row["C_l_BB"] = chi2_and_pte(cl_bb, cov_bb) + cov_methods.add("Gaussian (NaMaster)") summary[ver] = row diff --git a/src/sp_validation/cosmo_val/cosebis.py b/src/sp_validation/cosmo_val/cosebis.py index 1f7c5470..1d33c767 100644 --- a/src/sp_validation/cosmo_val/cosebis.py +++ b/src/sp_validation/cosmo_val/cosebis.py @@ -309,4 +309,4 @@ def plot_cosebis( # Save data products and store on instance save_cosebis_results(results, out_stub + "_data.npz", fiducial_scale_cut) - self._cosebis_results[version] = results + self._cosebis_results[version] = {"tomo_bin_all_tomo_bin_all": results} From 613e87150f5940c4c4476b7a981e19e5c142080f Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:31:44 +0200 Subject: [PATCH 072/107] Move the tests onto the merged API and pin the repaired seams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2PCF tests call calculate_2pcf_version(...)["tomo_bin_all_tomo_bin_all"] and look for the xi_{basename}.txt dump; the pure-E/B test reads its pair and re-measures the integration-grid ξ± on the same patches for the jackknife check, since cat_ggs is only filled by calculate_2pcf. get_rho_tau takes base_tau. The OneCovariance fixture carries the ℓ and tomographic-bin columns the tomographic reshape reads (one triangle, as the reshape mirrors the other). The column-schema scan learns the non-catalogue keys the tomographic code reads (W{}, H0) and the GLASS mock's TOM_BIN_ID. New: plot_2pcf and plot_ratio_xi_sys_xi write their figures; summarize_bmodes reads every statistic's pair and raises on a shape it cannot read; sacc_nz is the summed n(z) of a multi-column file; pol_factor rejects bools; cv_basename spells CosmologyValidation.basename. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/tests/test_column_schema.py | 5 + src/sp_validation/tests/test_cosmo_val.py | 181 ++++++++++++++++-- .../tests/test_cv_init_params.py | 17 +- src/sp_validation/tests/test_grammar.py | 2 +- .../tests/test_sacc_io_one_covariance.py | 40 ++-- 5 files changed, 210 insertions(+), 35 deletions(-) diff --git a/src/sp_validation/tests/test_column_schema.py b/src/sp_validation/tests/test_column_schema.py index 850de5b6..d4ad4cfd 100644 --- a/src/sp_validation/tests/test_column_schema.py +++ b/src/sp_validation/tests/test_column_schema.py @@ -61,6 +61,11 @@ "C22", "SNR", "T", + # NaMaster field/workspace dicts keyed by bin; cosmology parameter dicts + "W{}", + "H0", + # GLASS mock catalogues, which are not ShapePipe products + "TOM_BIN_ID", } #: An f-string template needs this many literal characters to count as diff --git a/src/sp_validation/tests/test_cosmo_val.py b/src/sp_validation/tests/test_cosmo_val.py index edecb797..daa33a30 100644 --- a/src/sp_validation/tests/test_cosmo_val.py +++ b/src/sp_validation/tests/test_cosmo_val.py @@ -372,6 +372,7 @@ def _write_synthetic_catalogs( version_cfg = { "subdir": str(cat_dir), "pipeline": "SP", + "colour": "tab:blue", "shear": shear_cfg, "star": star_cfg, } @@ -484,19 +485,22 @@ def test_xi_part_carries_the_covariance_the_measurement_estimated( ) def test_a_patched_xi_dump_reads_back(self, tmp_path): - """calculate_2pcf reads back the text dump a patched measurement wrote. + """calculate_2pcf_version reads back the dump a patched measurement wrote. The ξ± figure rules re-enter calculate_2pcf on the reporting grid, which has patches, and are handed the dump rule xi wrote (to its precision). """ params, version = self._write_synthetic_catalogs(tmp_path) binning = dict(npatch=4, min_sep=5.0, max_sep=100.0, nbins=6) - measured = CosmologyValidation(versions=[version], **params).calculate_2pcf( + pair = "tomo_bin_all_tomo_bin_all" + measured = CosmologyValidation( + versions=[version], **params + ).calculate_2pcf_version(version, **binning)[pair] + dumps = list(Path(params["output_dir"]).glob(f"xi_{version}_*.txt")) + assert len(dumps) == 1 + read = CosmologyValidation(versions=[version], **params).calculate_2pcf_version( version, **binning - ) - read = CosmologyValidation(versions=[version], **params).calculate_2pcf( - version, **binning - ) + )[pair] for column in ("meanr", "npairs", "xip", "xim", "varxip", "varxim"): np.testing.assert_allclose( getattr(read, column), getattr(measured, column), rtol=1e-4 @@ -525,10 +529,13 @@ def test_calculate_2pcf_does_not_depend_on_thread_count(self, tmp_path): xi = {} for n_threads in (4, 48): - # calculate_2pcf reads back an existing text dump instead of measuring. - for dump in Path(params["output_dir"]).glob(f"{version}_xi_*.txt"): + # calculate_2pcf_version reads back an existing text dump instead of + # measuring. + for dump in Path(params["output_dir"]).glob(f"xi_{version}_*.txt"): dump.unlink() - gg = cv.calculate_2pcf(version, num_threads=n_threads) + gg = cv.calculate_2pcf_version(version, num_threads=n_threads)[ + "tomo_bin_all_tomo_bin_all" + ] assert treecorr.get_omp_threads() == n_threads # the count took effect xi[n_threads] = np.concatenate([gg.xip, gg.xim]) sigma = np.sqrt(np.concatenate([gg.varxip, gg.varxim])) @@ -541,9 +548,9 @@ def test_treecorr_runs_on_the_cpus_the_process_holds(self, tmp_path): import treecorr params, version = self._write_synthetic_catalogs(tmp_path) - CosmologyValidation(versions=[version], npatch=1, **params).calculate_2pcf( - version - ) + CosmologyValidation( + versions=[version], npatch=1, **params + ).calculate_2pcf_version(version) assert treecorr.get_omp_threads() == len(os.sched_getaffinity(0)) def test_calculate_scale_dependent_leakage_runs_on_synthetic_catalog( @@ -617,13 +624,10 @@ def test_calculate_pure_eb_runs_on_synthetic_catalog(self, tmp_path, pure_eb_xi) ) cv.treecorr_config.update(bin_slop=0, angle_slop=0) - results = cv.calculate_pure_eb( - version, - npatch=npatch, - min_sep_int=1.0, - max_sep_int=300.0, - nbins_int=600, - ) + integration = dict(min_sep_int=1.0, max_sep_int=300.0, nbins_int=600) + results = cv.calculate_pure_eb(version, npatch=npatch, **integration)[ + "tomo_bin_all_tomo_bin_all" + ] measured = { key: results[key] @@ -652,7 +656,17 @@ def test_calculate_pure_eb_runs_on_synthetic_catalog(self, tmp_path, pure_eb_xi) cov = np.asarray(results["cov"]) assert cov.shape == (6 * nbins, 6 * nbins) assert results["npatch"] == npatch - gg_int = cv.cat_ggs[version] + # The integration-grid ξ± again, measured on the jackknife patches the + # first measurement wrote (its columns-only dump carries no patches). + for dump in Path(params["output_dir"]).glob(f"xi_{version}_*.txt"): + dump.unlink() + gg_int = cv.calculate_2pcf_version( + version, + npatch=npatch, + min_sep=integration["min_sep_int"], + max_sep=integration["max_sep_int"], + nbins=integration["nbins_int"], + )["tomo_bin_all_tomo_bin_all"] jackknife_of_modes = treecorr.estimate_multi_cov( [gg_int], "jackknife", @@ -662,3 +676,130 @@ def test_calculate_pure_eb_runs_on_synthetic_catalog(self, tmp_path, pure_eb_xi) np.testing.assert_allclose( cov, jackknife_of_modes, rtol=0, atol=1e-10 * np.abs(cov).max() ) + + def test_plot_2pcf_writes_the_non_tomographic_figures(self, tmp_path): + """plot_2pcf draws the ("all", "all") ξ± through plot_2pcf_tomography.""" + params, version = self._write_synthetic_catalogs(tmp_path) + cv = CosmologyValidation( + versions=[version], + npatch=1, + theta_min=5.0, + theta_max=100.0, + nbins=6, + **params, + ) + cv.plot_2pcf(show=False) + + out = Path(params["output_dir"]) + for name in ("xi_pm_tomography_False", "xi_pm_theta_tomography_False"): + assert (out / f"{name}.png").is_file(), name + + def test_plot_ratio_xi_sys_xi_writes_the_declared_figure(self, tmp_path): + """plot_ratio_xi_sys_xi divides the pair's ξ^{PSF, sys}± by its ξ±. + + ξ^{PSF, sys} is set on the instance, so the test needs no ρ/τ fit; the + figure lands where the cv_ratio_xi_sys_xi rule declares it. + """ + params, version = self._write_synthetic_catalogs(tmp_path) + nbins = 6 + cv = CosmologyValidation( + versions=[version], + npatch=1, + theta_min=5.0, + theta_max=100.0, + nbins=nbins, + **params, + ) + sys = np.full(nbins, 1e-6) + cv._xi_psf_sys = { + version: { + "tomo_bin_all_tomo_bin_all": { + "mean_plus": sys, + "var_plus": sys**2, + "mean_minus": sys, + "var_minus": sys**2, + } + } + } + cv.plot_ratio_xi_sys_xi(show=False) + + assert (Path(params["output_dir"]) / "ratio_xi_sys_xi.png").is_file() + with pytest.raises(ValueError, match="compute_tomography"): + cv.plot_ratio_xi_sys_xi(tomography=True, show=False) + + def test_summarize_bmodes_reads_the_all_pair(self, tmp_path): + """Each statistic's ("all", "all") result reaches the summary. + + A result whose shape the summary cannot read raises rather than + silently leaving its column empty. + """ + from types import SimpleNamespace + + from sp_validation.statistics import chi2_and_pte + + params, version = self._write_synthetic_catalogs(tmp_path) + cv = CosmologyValidation(versions=[version], **params) + pair = "tomo_bin_all_tomo_bin_all" + + edges = np.geomspace(1.0, 100.0, 6) + cv._pure_eb_results[version] = { + pair: { + "left_edges": edges[:-1], + "right_edges": edges[1:], + "pte_matrices": { + stat: np.full((5, 5), pte) + for stat, pte in (("xip_B", 0.1), ("xim_B", 0.2), ("combined", 0.3)) + }, + "npatch": 8, + } + } + cv._cosebis_results[version] = {pair: {"pte_B": 0.4}} + cl_bb = np.array([1.0, -0.5, 0.25]) + cov_bb = np.diag([2.0, 1.0, 0.5]) + cv._pseudo_cls = { + version: { + pair: { + "pseudo_cl": {"BB": cl_bb}, + "cov": {"COVAR_BB_BB": SimpleNamespace(data=cov_bb)}, + } + } + } + + row = cv.summarize_bmodes(fiducial_scale_cut=(1.0, 100.0))[version] + + assert row == pytest.approx( + { + "xip_B": 0.1, + "xim_B": 0.2, + "combined": 0.3, + "COSEBIS": 0.4, + "C_l_BB": chi2_and_pte(cl_bb, cov_bb)[2], + } + ) + + del cv._pure_eb_results[version][pair]["pte_matrices"] + with pytest.raises(KeyError): + cv.summarize_bmodes(fiducial_scale_cut=(1.0, 100.0)) + + def test_sacc_nz_is_the_summed_tomographic_nz(self, tmp_path): + """A multi-column n(z) file gives the SACC parts its whole-survey sum.""" + params, version = self._write_synthetic_catalogs(tmp_path) + cv = CosmologyValidation(versions=[version], **params) + + z = np.linspace(0.0, 2.0, 11) + nz_bins = np.stack([np.exp(-((z - mu) ** 2)) for mu in (0.5, 1.0, 1.5)]) + nz_path = tmp_path / "nz_tomo.txt" + np.savetxt(nz_path, np.column_stack([z, *nz_bins])) + cv.cc[version]["shear"]["redshift_path"] = str(nz_path) + + [(bin_id, (z_read, nz_read))] = cv.sacc_nz(version).items() + assert bin_id == 0 + np.testing.assert_allclose(z_read, z) + np.testing.assert_allclose(nz_read, nz_bins.sum(axis=0)) + + @pytest.mark.parametrize("pol_factor", [True, False, 0, 2]) + def test_pol_factor_must_be_plus_or_minus_one(self, tmp_path, pol_factor): + """pol_factor is a ±1 e2 multiplier; a bool would pass `in (-1, 1)`.""" + params, version = self._write_synthetic_catalogs(tmp_path) + with pytest.raises(ValueError, match="pol_factor"): + CosmologyValidation(versions=[version], pol_factor=pol_factor, **params) diff --git a/src/sp_validation/tests/test_cv_init_params.py b/src/sp_validation/tests/test_cv_init_params.py index 7ec743cc..5d1a045d 100644 --- a/src/sp_validation/tests/test_cv_init_params.py +++ b/src/sp_validation/tests/test_cv_init_params.py @@ -1,14 +1,16 @@ -"""Guard: the workflow sets every CosmologyValidation constructor default. +"""Guard: the workflow builds ``cv`` and names its products as the class does. ``workflow/common.py::cv_init_params`` builds the constructor kwargs for every cosmo_val rule. A keyword it does not forward falls back to the constructor default without any trace in the run config, so each keyword with a default is -either forwarded or exempted below with its reason. +either forwarded or exempted below with its reason. ``cv_basename`` names the +rule outputs, so it must spell ``CosmologyValidation.basename`` exactly. """ import importlib.util import inspect from pathlib import Path +from types import SimpleNamespace import yaml @@ -57,3 +59,14 @@ def test_every_default_is_forwarded_or_exempt(): def test_exemptions_are_live_keywords(): stale = set(EXEMPT) - _defaulted_keywords() assert not stale, f"EXEMPT names keywords the constructor lacks: {sorted(stale)}" + + +def test_cv_basename_is_the_class_basename(): + fiducial = {"min_sep": 1.0, "max_sep": 250.0, "nbins": 20, "npatch": 100} + cv = SimpleNamespace( + treecorr_config={k: fiducial[k] for k in ("min_sep", "max_sep", "nbins")}, + npatch=fiducial["npatch"], + ) + assert _load_common().cv_basename( + "SP_v1.4.6.3", fiducial + ) == CosmologyValidation.basename(cv, "SP_v1.4.6.3") diff --git a/src/sp_validation/tests/test_grammar.py b/src/sp_validation/tests/test_grammar.py index 08ed6742..60e23b75 100644 --- a/src/sp_validation/tests/test_grammar.py +++ b/src/sp_validation/tests/test_grammar.py @@ -273,7 +273,7 @@ def test_get_rho_tau_identical_for_v1_and_v2_psf_catalogues(tmp_path): "max_sep": 600, "nbins": 4, } - get_rho_tau(config, label, treecorr_config, str(outdir), label) + get_rho_tau(config, label, treecorr_config, str(outdir), label, label) stats[label] = [ fits.getdata(outdir / f"{kind}_stats_{label}.fits") for kind in ("rho", "tau") diff --git a/src/sp_validation/tests/test_sacc_io_one_covariance.py b/src/sp_validation/tests/test_sacc_io_one_covariance.py index bbe90ca3..776475b3 100644 --- a/src/sp_validation/tests/test_sacc_io_one_covariance.py +++ b/src/sp_validation/tests/test_sacc_io_one_covariance.py @@ -28,26 +28,40 @@ # --------------------------------------------------------------------------- # # Synthetic OneCovariance-shaped fixtures # --------------------------------------------------------------------------- # +def _ell(i): + """The multipole of ℓ-bin ``i`` in the synthetic tables.""" + return 10.0 * (i + 1) + + def _one_cov_table(cov_gauss, cov_all): """Flatten two n x n matrices into a OneCovariance ``covariance_list`` table. - Reproduces the real flat output: one row per ``(i, j)`` element pair in - row-major order ``k = i·n + j``, with the Gaussian value in column 10 and - the Gaussian+non-Gaussian value in column 9. Columns 0-8 and the index - columns are filled with self-documenting placeholder values (the reshape - only reads cols 9/10, but a realistic width proves it does not spill). + Reproduces the real flat output of a single tomographic bin: one row per + ``(i, j)`` ℓ-bin pair with ``i <= j`` (the reshape mirrors the other + triangle), ℓ_i and ℓ_j in columns 1 and 2, the bin indices of the four + fields (all ``1``) in columns 5-8, the Gaussian value in column 10 and the + Gaussian+non-Gaussian value in column 9. Columns 0, 3 and 4 hold + placeholders the reshape does not read. """ n = cov_gauss.shape[0] rows = [] for i in range(n): - for j in range(n): - row = np.arange(11.0) # placeholder cols 0-8 (+ overwritten 9,10) + for j in range(i, n): + row = np.arange(11.0) # placeholder cols 0, 3, 4 + row[1], row[2] = _ell(i), _ell(j) + row[5:9] = 1 row[9] = cov_all[i, j] row[10] = cov_gauss[i, j] rows.append(row) return np.array(rows) +def _row_of(table, i, j): + """Index of the table row holding ℓ-bin pair ``(i, j)``.""" + [k] = np.flatnonzero((table[:, 1] == _ell(i)) & (table[:, 2] == _ell(j))) + return k + + def _spd(n, seed): """Symmetric positive-definite matrix of size ``n`` (a valid covariance).""" a = np.random.default_rng(seed).normal(size=(n, n)) @@ -75,7 +89,8 @@ def test_covariance_blocks_reshapes_to_hand_built_matrix(): WHY TEETH: (a) ``gaussian=True`` vs ``False`` must return the two *different* matrices, proving the column flag is load-bearing; (b) perturbing a single entry of the flat input must change exactly that entry of the reshaped - block, proving the reshape actually reads the table (not a constant). + block and its mirror, proving the reshape actually reads the table (not a + constant). """ cov_gauss = _spd(4, seed=1) cov_all = _spd(4, seed=2) @@ -94,12 +109,13 @@ def test_covariance_blocks_reshapes_to_hand_built_matrix(): assert not np.allclose(block_g, block_a) # TEETH: a perturbation of one flat-table entry moves exactly that block - # entry (row k = i·n + j, col 10 for gaussian). + # entry and its mirror (col 10 for gaussian). perturbed = table.copy() - perturbed[2 * 4 + 1, 10] += 5.0 # element (i=2, j=1) + perturbed[_row_of(table, 1, 2), 10] += 5.0 [(_, block_p)] = sio.covariance_blocks(perturbed, selector, gaussian=True) - npt.assert_allclose(block_p[2, 1] - block_g[2, 1], 5.0, rtol=1e-12) - block_p[2, 1] = block_g[2, 1] + for i, j in ((1, 2), (2, 1)): + npt.assert_allclose(block_p[i, j] - block_g[i, j], 5.0, rtol=1e-12) + block_p[i, j] = block_g[i, j] npt.assert_allclose(block_p, block_g, rtol=1e-12) # nothing else moved From b9a9e9bdc95d9340368dd7f4c61ea46a6e7d603c Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:33:22 +0200 Subject: [PATCH 073/107] Repair the tomography branch's own callers of reshaped helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glass_mock.compute_two_point_cl_map unpacked four values from get_n_gal_map, which returns the count map only; it takes the pixel bookkeeping from get_pixels and passes it on. Both mock pseudo-Cℓ helpers label the coupled spectrum ℓ = 0..lmax-1, the range compute_coupled_cell returns (checked at nside 32: 64 entries, labels 0..63). The test-reference generator and the namaster covariance paper script call get_params_rho_tau without survey=, the generator reads its catalogue through io.open_entry and carries the mandatory patch_number, and the paper script takes the pixel bookkeeping from get_pixels. Co-Authored-By: Claude Opus 5.5 --- .../2025_09_11_namaster_covariance.py | 7 +++-- src/sp_validation/glass_mock.py | 26 +++++++++++++------ .../generate_test_cl_catalog_reference.py | 9 ++++--- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/papers/harmonic/2025_09_11_namaster_covariance.py b/papers/harmonic/2025_09_11_namaster_covariance.py index aea3fc7e..47a77a92 100644 --- a/papers/harmonic/2025_09_11_namaster_covariance.py +++ b/papers/harmonic/2025_09_11_namaster_covariance.py @@ -112,11 +112,14 @@ catalog_config="/home/guerrini/sp_validation/cosmo_val/cat_config.yaml", ) # %% -params = get_params_rho_tau(cv.cc["SP_v1.4.5_leak_corr"], survey="SP_v1.4.5_leak_corr") +params = get_params_rho_tau(cv.cc["SP_v1.4.5_leak_corr"]) # %% print("Collecting maps...") -n_gal, unique_pix, idx, idx_rep = cv.get_n_gal_map(params, nside, cat_gal) +unique_pix, idx, idx_rep = cv.get_pixels(params, nside, cat_gal) +n_gal = cv.get_n_gal_map( + params, nside, cat_gal, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep +) mask = n_gal > 0 print("Computing noise...") diff --git a/src/sp_validation/glass_mock.py b/src/sp_validation/glass_mock.py index d09ffc55..b6347aed 100644 --- a/src/sp_validation/glass_mock.py +++ b/src/sp_validation/glass_mock.py @@ -475,16 +475,19 @@ def compute_two_point_xi(cat, config=None): return gg -def get_n_gal_map(nside, ra, dec): - """Galaxy-count HEALPix map plus the unique-pixel bookkeeping arrays. +def get_n_gal_map(nside, ra, dec, unique_pix=None, idx=None, idx_rep=None): + """Galaxy-count HEALPix map, shape ``(npix,)``. The unweighted twin of the pseudo-Cl ``get_n_gal_map`` primitive: delegates - to it with ``weights=None`` (counts). Imported lazily so this module keeps - resolving in CAMB-only environments without the harmonic stack. + to it with ``weights=None`` (counts), reusing the ``get_pixels`` bookkeeping + when given. Imported lazily so this module keeps resolving in CAMB-only + environments without the harmonic stack. """ from sp_validation.pseudo_cl import get_n_gal_map as _get_n_gal_map - return _get_n_gal_map(nside, ra, dec) + return _get_n_gal_map( + nside, ra, dec, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) def compute_two_point_cl(cat, nside=1024, lmin=8, n_bins=32): @@ -527,7 +530,8 @@ def compute_two_point_cl(cat, nside=1024, lmin=8, n_bins=32): cl_coupled = nmt.compute_coupled_cell(f_all, f_all) cl_all = wsp.decouple_cell(cl_coupled) - cl_coupled = np.concatenate([np.arange(1, lmax + 1)[np.newaxis, :], cl_coupled]) + # compute_coupled_cell runs over ell = 0..b_lmax, index = ell + cl_coupled = np.concatenate([np.arange(lmax)[np.newaxis, :], cl_coupled]) cl_all = np.concatenate([ell_eff[np.newaxis, ...], cl_all]) return cl_coupled, cl_all @@ -551,6 +555,8 @@ def compute_two_point_cl_map(cat, nside=1024, lmin=8, n_bins=32): import healpy as hp import pymaster as nmt + from sp_validation.pseudo_cl import get_pixels + e1 = cat["e1"] e2 = cat["e2"] ra = cat["ra"] @@ -562,7 +568,10 @@ def compute_two_point_cl_map(cat, nside=1024, lmin=8, n_bins=32): b, ell_eff, lmax, b_lmax = _mock_powspace_bin(nside, lmin, n_bins) factor = -1 - n_gal_map, unique_pix, _idx, idx_rep = get_n_gal_map(nside, ra, dec) + unique_pix, idx, idx_rep = get_pixels(ra, dec, nside) + n_gal_map = get_n_gal_map( + nside, ra, dec, unique_pix=unique_pix, idx=idx, idx_rep=idx_rep + ) mask = n_gal_map > 0 shear_map_e1 = np.zeros(hp.nside2npix(nside)) @@ -582,7 +591,8 @@ def compute_two_point_cl_map(cat, nside=1024, lmin=8, n_bins=32): cl_coupled = nmt.compute_coupled_cell(f_all, f_all) cl_all = wsp.decouple_cell(cl_coupled) - cl_coupled = np.concatenate([np.arange(1, lmax + 1)[np.newaxis, :], cl_coupled]) + # compute_coupled_cell runs over ell = 0..b_lmax, index = ell + cl_coupled = np.concatenate([np.arange(lmax)[np.newaxis, :], cl_coupled]) cl_all = np.concatenate([ell_eff[np.newaxis, ...], cl_all]) return cl_coupled, cl_all diff --git a/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py b/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py index 7c41d564..dbed4a60 100644 --- a/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py +++ b/src/sp_validation/tests/data/generate_test_cl_catalog_reference.py @@ -4,10 +4,10 @@ import IPython import numpy as np import yaml -from astropy.io import fits from astropy.table import Table from sp_validation.cosmo_val import CosmologyValidation +from sp_validation.io import open_entry from sp_validation.rho_tau import get_params_rho_tau ipython = IPython.get_ipython() @@ -65,7 +65,7 @@ "star_flag": "w", } config_data = { - "nz": {"subdir": str(nz_dir), "dndz": {"blind": "A", "path": "dndz"}}, + "nz": {"subdir": str(nz_dir), "dndz": {"path": "dndz_{pipeline}_A.txt"}}, "paths": {"output": str(output_dir)}, version: { "subdir": str(cat_dir), @@ -73,6 +73,7 @@ "shear": shear_cfg, "star": {**psf_cfg}, "psf": psf_cfg, + "patch_number": 150, }, } @@ -93,8 +94,8 @@ cv._test_version = version ver = cv._test_version -params = get_params_rho_tau(cv.cc[ver], survey=ver) -cat_gal = fits.getdata(cv.cc[ver]["shear"]["path"]) +params = get_params_rho_tau(cv.cc[ver]) +cat_gal = open_entry(cv.cc[ver]["shear"]) cat_gal_tomo_bin_1 = cat_gal[cat_gal[params["tomo_bin_col"]] == 1] cat_gal_tomo_bin_2 = cat_gal[cat_gal[params["tomo_bin_col"]] == 2] From 45c51e597c795ab99dc948cbe421bb3416331094 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:34:56 +0200 Subject: [PATCH 074/107] Apply pixel windows only to map-based pseudo-Cl spectra Catalogue spectra are not pixelized. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/cosmo_val/pseudo_cl.py | 55 +++++++++--- src/sp_validation/cosmo_val/sacc_writers.py | 10 ++- src/sp_validation/pseudo_cl.py | 12 ++- src/sp_validation/tests/test_pseudo_cl.py | 92 ++++++++++++++++++-- src/sp_validation/tests/test_sacc_writers.py | 18 +++- 5 files changed, 160 insertions(+), 27 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index ce6676bc..78eab1ba 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -28,6 +28,23 @@ from .sacc_writers import pseudo_cl_to_sacc +def _apply_pixel_window_to_fiducial_cl(fiducial_cl, nside, cell_method): + """Apply ``pw²(ℓ)`` to fiducial spectra only for HEALPix-map measurements. + + Each spectrum array uses index ``ell``, so the window spans the matching + ``ell = 0..len(spectrum)-1`` grid. Catalogue spectra are point-sampled and + must remain unchanged. + """ + if cell_method != "map" or not fiducial_cl: + return fiducial_cl + + lmax = len(next(iter(fiducial_cl.values()))) - 1 + pixwin = hp.pixwin(nside, lmax=lmax) + return { + key: np.asarray(cl, dtype=float) * pixwin**2 for key, cl in fiducial_cl.items() + } + + def plot_pseudo_cl_spectrum(datasets, spectrum, output_path): """Two-panel ℓC_ℓ / C_ℓ figure for one spectrum across catalogue versions. @@ -272,7 +289,9 @@ def calculate_pseudo_cl_map(self, ver, nside, out_path, tomo_bin_a, tomo_bin_b): self.print_cyan("Saving pseudo-Cl's...") tomo_bin_pair = (tomo_bin_a, tomo_bin_b) - self._save_pseudo_cl(ver, out_path, tomo_bin_pair, ell_eff, cl_shear, wsp) + self._save_pseudo_cl( + ver, out_path, tomo_bin_pair, ell_eff, cl_shear, wsp, nside=nside + ) self._pseudo_cls[ver][f"tomo_bin_{tomo_bin_a}_tomo_bin_{tomo_bin_b}"][ "pseudo_cl" @@ -304,8 +323,11 @@ def calculate_pseudo_cl_catalog(self, ver, out_path, tomo_bin_a, tomo_bin_b): def calculate_pseudo_cl_inka_cov( self, compute_tomography=True, load_all_block=False ): - """ - Compute a theoretical Gaussian covariance of the Pseudo-Cl for EE, EB and BB. + """Compute the Gaussian iNKA covariance for EE, EB, and BB. + + Apply the HEALPix pixel window ``pw²(ℓ)`` to each fiducial spectrum only + when ``cell_method == "map"``. Catalogue-based spectra have no pixel + window and use the unmodified fiducial. """ self.print_start("Computing Pseudo-Cl covariance") @@ -399,6 +421,9 @@ def calculate_pseudo_cl_inka_cov( key: np.concatenate(([0.0], np.asarray(value[:-1], dtype=float))) for key, value in fiducial_cl.items() } + fiducial_cl = _apply_pixel_window_to_fiducial_cl( + fiducial_cl, nside, self.cell_method + ) self.print_cyan( "Estimating and adding the noise bias to the fiducial power spectra" @@ -1180,10 +1205,14 @@ def _output_path_iNKA_block_cov(self, ver, tomo_bin_quad): f"pseudo_cl_cov_from_iNKA_tomo_bin_{bin_key_a1}_tomo_bin_{bin_key_a2}_tomo_bin_{bin_key_b1}_tomo_bin_{bin_key_b2}_{ver}_binning_{self.binning}_nbins_{self.n_ell_bins}.fits", ) - def _save_pseudo_cl(self, ver, out_path, tomo_bin_pair, ell_eff, cl_all, wsp): - """Write one bin pair's pseudo-Cl: SACC for ``("all", "all")``, else FITS.""" + def _save_pseudo_cl( + self, ver, out_path, tomo_bin_pair, ell_eff, cl_all, wsp, nside=None + ): + """Write one pair as SACC or FITS, forwarding ``nside`` for map spectra.""" if tuple(tomo_bin_pair) == ("all", "all"): - self.pseudo_cl_to_sacc_part(ver, out_path, ell_eff, cl_all, wsp) + self.pseudo_cl_to_sacc_part( + ver, out_path, ell_eff, cl_all, wsp, nside=nside + ) else: self.save_pseudo_cl(ell_eff, cl_all, out_path) @@ -1202,12 +1231,15 @@ def _load_pseudo_cl_sacc(out_path): ell, ee, bb, eb, _window = sacc_io.get_pseudo_cl(s, SACC_BIN) return {"ELL": ell, "EE": ee, "EB": eb, "BB": bb} - def pseudo_cl_to_sacc_part(self, version, out_path, ell_eff, cl_all, wsp): - """Write the pseudo-Cl SACC part (EE/BB/EB + shared bandpower window). + def pseudo_cl_to_sacc_part( + self, version, out_path, ell_eff, cl_all, wsp, nside=None + ): + """Write the pseudo-Cl SACC part with its shared bandpower window. - ``cl_all`` is NaMaster's decoupled ``(4, nbp)`` array (EE, EB, BE, BB); - the writer takes the shared bandpower window from ``wsp``. No covariance - is attached here. + ``cl_all`` is NaMaster's decoupled ``(4, nbp)`` array (EE, EB, BE, BB). + Map-based callers pass ``nside`` so the window includes ``pw²(ℓ)``; + catalogue-based callers leave it unset because their spectra have no + HEALPix pixel window. No covariance is attached here. """ s = pseudo_cl_to_sacc( self.sacc_nz(version), @@ -1215,6 +1247,7 @@ def pseudo_cl_to_sacc_part(self, version, out_path, ell_eff, cl_all, wsp): ell_eff, cl_all, wsp, + nside=nside, ) sacc_io.save(s, out_path, type="data") diff --git a/src/sp_validation/cosmo_val/sacc_writers.py b/src/sp_validation/cosmo_val/sacc_writers.py index 8b7f57b7..b10bfe81 100644 --- a/src/sp_validation/cosmo_val/sacc_writers.py +++ b/src/sp_validation/cosmo_val/sacc_writers.py @@ -68,14 +68,16 @@ def xi_to_sacc( return s -def pseudo_cl_to_sacc(nz, metadata, ell_eff, cl_all, wsp, covariance=None): +def pseudo_cl_to_sacc(nz, metadata, ell_eff, cl_all, wsp, covariance=None, nside=None): """One pseudo-Cℓ part: EE/BB/EB with the shared bandpower window. ``cl_all`` is NaMaster's decoupled ``(4, nbp)`` array (EE, EB, BE, BB); the - window comes from :func:`bandpower_window_from_workspace`. ``covariance``, - when given, is the dense ``[EE; BB; EB]``-ordered block matching insertion. + window comes from :func:`bandpower_window_from_workspace`. Supply ``nside`` + only for HEALPix-map spectra, whose measured spectrum includes ``pw²(ℓ)``; + catalogue-based spectra have no pixel window. ``covariance``, when given, + is the dense ``[EE; BB; EB]``-ordered block matching insertion. """ - window_ells, window_weights = bandpower_window_from_workspace(wsp) + window_ells, window_weights = bandpower_window_from_workspace(wsp, nside) s = sio.new_sacc(nz, metadata) sio.add_pseudo_cl( s, diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index 052ef57e..f52ab1d1 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -1054,9 +1054,13 @@ def get_pseudo_cl_iNKA_covariance( _NMT_EE = 0 -def bandpower_window_from_workspace(wsp): +def bandpower_window_from_workspace(wsp, nside=None): """Extract the bandpower window matrix ``W`` for a spin-2×spin-2 workspace. + For HEALPix-map spectra, supply ``nside`` to include the pixel window + ``pw²(ℓ)`` in the measured spectrum. Catalogue-based spectra have no pixel + window and must leave ``nside`` unset. + NaMaster's ``get_bandpower_windows()`` returns a four-index array ``(n_cl_out, n_bpw, n_cl_in, n_ell)`` describing how each output bandpower is built from the input multipoles across the EE/EB/BE/BB spectra. SACC's @@ -1071,9 +1075,13 @@ def bandpower_window_from_workspace(wsp): ``compute_coupled_cell``. window_weights : np.ndarray ``W`` of shape ``(n_ell, n_bpw)`` — one column per bandpower, the layout - :func:`sp_validation.sacc_io.add_pseudo_cl` expects. + :func:`sp_validation.sacc_io.add_pseudo_cl` expects. If ``nside`` is + given, this includes ``pw²(ℓ)`` on the window's multipole grid. """ bpw = wsp.get_bandpower_windows() # (n_cl_out, n_bpw, n_cl_in, n_ell) diagonal = bpw[_NMT_EE, :, _NMT_EE, :] # (n_bpw, n_ell) window_ells = np.arange(diagonal.shape[1], dtype=float) + if nside is not None: + pixwin = hp.pixwin(nside, lmax=diagonal.shape[1] - 1) + diagonal = diagonal * pixwin**2 return window_ells, diagonal.T diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index e9fdab14..80f4d90d 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -60,8 +60,12 @@ from sp_validation import sacc_io from sp_validation.cosmo_val import CosmologyValidation +from sp_validation.cosmo_val.pseudo_cl import _apply_pixel_window_to_fiducial_cl from sp_validation.cosmo_val.sacc_writers import BIN as SACC_BIN -from sp_validation.pseudo_cl import apply_random_rotation +from sp_validation.pseudo_cl import ( + apply_random_rotation, + bandpower_window_from_workspace, +) from sp_validation.rho_tau import get_params_rho_tau # These tests need the full harmonic-space stack (pymaster/NaMaster + healpy), @@ -658,14 +662,24 @@ def test_apply_random_rotation_reproducible_with_seed(cv, cat_and_params): # =========================================================================== # calculate_pseudo_cl_catalog -- deterministic end-to-end catalog path # =========================================================================== -def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): - """End-to-end catalog path: SACC round-trip of ell + EE/EB/BB. - - The catalog method has no random noise debiasing, so it is reproducible to - the same ~2e-12 catalog-path float noise; we pin the round-tripped spectra. - """ +def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path, monkeypatch): + """Catalog SACC output retains the unwindowed workspace bandpower window.""" ver = cv._test_version + cv.cell_method = "catalog" cv._pseudo_cls = {ver: {"tomo_bin_all_tomo_bin_all": {}}} + saved = {} + save_pseudo_cl = cv._save_pseudo_cl + + def capture_workspace( + ver, out_path, tomo_bin_pair, ell_eff, cl_all, wsp, nside=None + ): + saved["workspace"] = wsp + saved["nside"] = nside + return save_pseudo_cl( + ver, out_path, tomo_bin_pair, ell_eff, cl_all, wsp, nside=nside + ) + + monkeypatch.setattr(cv, "_save_pseudo_cl", capture_workspace) out_path = cv._output_path(f"pseudo_cl_{ver}.sacc") cv.calculate_pseudo_cl_catalog(ver, out_path, tomo_bin_a="all", tomo_bin_b="all") @@ -673,6 +687,12 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): s = sacc_io.load(out_path, allow_unblinded=True) ell, ee, bb, eb, window = sacc_io.get_pseudo_cl(s, SACC_BIN) assert window is not None # the shared BandpowerWindow rides the part + assert saved["nside"] is None + window_ells, unwindowed_weights = bandpower_window_from_workspace( + saved["workspace"] + ) + assert len(window_ells) == window.weight.shape[0] + npt.assert_allclose(window.weight, unwindowed_weights, rtol=1e-12, atol=1e-15) npt.assert_allclose( ell, @@ -741,6 +761,64 @@ def test_calculate_pseudo_cl_catalog_end_to_end(cv, tmp_path): npt.assert_allclose(ee, cl_prim[0], rtol=RTOL_CAT, atol=ATOL_CAT) +def test_calculate_pseudo_cl_map_sacc_pixel_window(cv, monkeypatch): + """Map SACC output multiplies its workspace window by pw²(ℓ).""" + ver = cv._test_version + cv.cell_method = "map" + cv.noise_bias_method = "analytic" + cv._pseudo_cls = {ver: {"tomo_bin_all_tomo_bin_all": {}}} + saved = {} + save_pseudo_cl = cv._save_pseudo_cl + + def capture_workspace( + ver, out_path, tomo_bin_pair, ell_eff, cl_all, wsp, nside=None + ): + saved["workspace"] = wsp + saved["nside"] = nside + return save_pseudo_cl( + ver, out_path, tomo_bin_pair, ell_eff, cl_all, wsp, nside=nside + ) + + monkeypatch.setattr(cv, "_save_pseudo_cl", capture_workspace) + out_path = cv._output_path(f"pseudo_cl_map_{ver}.sacc") + cv.calculate_pseudo_cl_map(ver, NSIDE, out_path, "all", "all") + + s = sacc_io.load(out_path, allow_unblinded=True) + _ell, _ee, _bb, _eb, window = sacc_io.get_pseudo_cl(s, SACC_BIN) + assert saved["nside"] == NSIDE + window_ells, unwindowed_weights = bandpower_window_from_workspace( + saved["workspace"] + ) + assert len(window_ells) == window.weight.shape[0] + pw2 = healpy.pixwin(NSIDE, lmax=len(window_ells) - 1) ** 2 + nonzero = np.abs(unwindowed_weights) > 1e-20 + npt.assert_allclose( + window.weight[nonzero] / unwindowed_weights[nonzero], + np.broadcast_to(pw2[:, None], unwindowed_weights.shape)[nonzero], + rtol=1e-12, + ) + + +def test_fiducial_pixel_window_applies_only_to_map(): + """iNKA fiducials carry pw² on maps and remain unchanged for catalogues.""" + nside = 4 + ell_grid = np.arange(12) + fiducial = { + "W1xW1": 1.0 + ell_grid.astype(float), + "W1xW2": 2.0 + 2 * ell_grid.astype(float), + } + original = {key: value.copy() for key, value in fiducial.items()} + + catalog_fiducial = _apply_pixel_window_to_fiducial_cl(fiducial, nside, "catalog") + map_fiducial = _apply_pixel_window_to_fiducial_cl(fiducial, nside, "map") + pw2 = healpy.pixwin(nside, lmax=len(ell_grid) - 1) ** 2 + + for key, cl in original.items(): + npt.assert_array_equal(catalog_fiducial[key], cl) + npt.assert_allclose(map_fiducial[key], cl * pw2, rtol=1e-14, atol=0.0) + npt.assert_array_equal(fiducial[key], cl) + + def test_calculate_pseudo_cl_catalog_end_to_end_tomo(cv, tmp_path): """End-to-end catalog path: FITS round-trip of ell + EE/EB/BB. diff --git a/src/sp_validation/tests/test_sacc_writers.py b/src/sp_validation/tests/test_sacc_writers.py index c4963b33..74181190 100644 --- a/src/sp_validation/tests/test_sacc_writers.py +++ b/src/sp_validation/tests/test_sacc_writers.py @@ -152,9 +152,11 @@ def get_bandpower_windows(self): assert np.array_equal(data["BB"], cl_all[3]) -def test_pseudo_cl_to_sacc_real_namaster(tmp_path): - """A real small-nside NaMaster workspace's window survives the writer.""" +def test_pseudo_cl_to_sacc_real_namaster_pixel_window(tmp_path): + """The SACC window gains pw²(ℓ) only when a map nside is supplied.""" pytest.importorskip("pymaster") + import healpy as hp + from sp_validation.pseudo_cl import get_pseudo_cls_map nside = 32 @@ -169,9 +171,19 @@ def test_pseudo_cl_to_sacc_real_namaster(tmp_path): ell_r, ee, bb, eb, window = sio.get_pseudo_cl(s2, (0, 0)) assert np.array_equal(ell_r, ell_eff) assert np.array_equal(ee, cl_all[0]) and np.array_equal(bb, cl_all[3]) - # window columns correspond to the bandpowers, one per ell_eff + # With nside unset, the workspace window carries no pixel window. assert window.weight.shape[1] == len(ell_eff) + mapped = sw.pseudo_cl_to_sacc({0: _nz()}, META, ell_eff, cl_all, wsp, nside=nside) + mapped_window = mapped.get_bandpower_windows(mapped.indices(sio.CL_EE)) + pw2 = hp.pixwin(nside, lmax=window.weight.shape[0] - 1) ** 2 + nonzero = np.abs(window.weight) > 1e-20 + np.testing.assert_allclose( + mapped_window.weight[nonzero] / window.weight[nonzero], + np.broadcast_to(pw2[:, None], window.weight.shape)[nonzero], + rtol=1e-12, + ) + def test_cosebis_to_sacc(tmp_path): En, Bn = np.arange(1, 11) * 1e-6, np.arange(1, 11) * 1e-7 From 497d7b8a87fc2b735ac420c1d52e74f9a72ffa15 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:37:14 +0200 Subject: [PATCH 075/107] Pin the OneCovariance fixture to the real table; drop the GLASS xfail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The OneCovariance fixture is the full row-major n×n table of one bin, as the file is written, now with the ℓ (cols 1-2) and bin (cols 5-8) columns the tomographic reshape reads; the perturbation teeth keep the table symmetric. The tomographic reshape stays: PseudoClMixin's OneCovariance covariance needs it, and on a single-bin table it returns the matrix the row-major reshape did. test_matter_maps_are_seed_deterministic passes (XPASS) with the merged pins (glass 2026.2, glass.ext.camb 2023.6, cosmology.compat.camb 0.2.0) overlaid on the current image, so its xfail goes. It fails on an image built from the old lock (no cosmology.compat) until uv.lock is re-locked. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/tests/test_glass_mock.py | 13 ----------- .../tests/test_sacc_io_one_covariance.py | 22 +++++++++---------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/sp_validation/tests/test_glass_mock.py b/src/sp_validation/tests/test_glass_mock.py index 233d8aa1..a559658a 100644 --- a/src/sp_validation/tests/test_glass_mock.py +++ b/src/sp_validation/tests/test_glass_mock.py @@ -131,19 +131,6 @@ def test_config_change_breaks_reference(): @pytest.mark.skipif(not HAVE_GLASS, reason="GLASS not installed in this image") -@pytest.mark.xfail( - reason=( - "glass_mock map path is incompatible with the installed glass/cosmology " - "API: cosmology.Cosmology.from_camb returns a CambCosmology lacking " - "comoving_distance, which glass.distance_grid / MultiPlaneConvergence " - "require. The map path was never exercised before GLASS was added to the " - "image. Fix = pin a compatible glass+cosmology pair (or adapt the API " - "calls) and verify in the fresh image; then drop this xfail. " - "See fiber shapepipe/sp_validation glass-cosmology-api-pin." - ), - strict=False, - raises=AttributeError, -) def test_matter_maps_are_seed_deterministic(): """Same config + seed → bit-identical matter/lensing maps. diff --git a/src/sp_validation/tests/test_sacc_io_one_covariance.py b/src/sp_validation/tests/test_sacc_io_one_covariance.py index 776475b3..1454bc77 100644 --- a/src/sp_validation/tests/test_sacc_io_one_covariance.py +++ b/src/sp_validation/tests/test_sacc_io_one_covariance.py @@ -37,16 +37,15 @@ def _one_cov_table(cov_gauss, cov_all): """Flatten two n x n matrices into a OneCovariance ``covariance_list`` table. Reproduces the real flat output of a single tomographic bin: one row per - ``(i, j)`` ℓ-bin pair with ``i <= j`` (the reshape mirrors the other - triangle), ℓ_i and ℓ_j in columns 1 and 2, the bin indices of the four - fields (all ``1``) in columns 5-8, the Gaussian value in column 10 and the - Gaussian+non-Gaussian value in column 9. Columns 0, 3 and 4 hold - placeholders the reshape does not read. + ``(i, j)`` ℓ-bin pair in row-major order ``k = i·n + j``, ℓ_i and ℓ_j in + columns 1 and 2, the bin indices of the four fields (all ``1``) in columns + 5-8, the Gaussian value in column 10 and the Gaussian+non-Gaussian value in + column 9. Columns 0, 3 and 4 hold placeholders the reshape does not read. """ n = cov_gauss.shape[0] rows = [] for i in range(n): - for j in range(i, n): + for j in range(n): row = np.arange(11.0) # placeholder cols 0, 3, 4 row[1], row[2] = _ell(i), _ell(j) row[5:9] = 1 @@ -89,8 +88,9 @@ def test_covariance_blocks_reshapes_to_hand_built_matrix(): WHY TEETH: (a) ``gaussian=True`` vs ``False`` must return the two *different* matrices, proving the column flag is load-bearing; (b) perturbing a single entry of the flat input must change exactly that entry of the reshaped - block and its mirror, proving the reshape actually reads the table (not a - constant). + block, proving the reshape actually reads the table (not a constant). The + table stays symmetric, as a covariance is, so the entry is perturbed with + its mirror. """ cov_gauss = _spd(4, seed=1) cov_all = _spd(4, seed=2) @@ -108,10 +108,10 @@ def test_covariance_blocks_reshapes_to_hand_built_matrix(): # TEETH: gaussian and gauss+ng select different columns -> different blocks. assert not np.allclose(block_g, block_a) - # TEETH: a perturbation of one flat-table entry moves exactly that block - # entry and its mirror (col 10 for gaussian). + # TEETH: a perturbation of one flat-table entry (and its mirror) moves + # exactly that block entry (and its mirror), col 10 for gaussian. perturbed = table.copy() - perturbed[_row_of(table, 1, 2), 10] += 5.0 + perturbed[[_row_of(table, 1, 2), _row_of(table, 2, 1)], 10] += 5.0 [(_, block_p)] = sio.covariance_blocks(perturbed, selector, gaussian=True) for i, j in ((1, 2), (2, 1)): npt.assert_allclose(block_p[i, j] - block_g[i, j], 5.0, rtol=1e-12) From 9010a8cdf163c7f2149bba20a22e5d3e76e4c3ec Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:40:53 +0200 Subject: [PATCH 076/107] Keep the GLASS xfail until the re-locked image; cite the OneCovariance writer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_matter_maps_are_seed_deterministic passes with the locked glass 2026.2 / cosmology-compat-camb 0.2.0 on the path, but the image is still built from the old lock, so the xfail stays (raises=ModuleNotFoundError) until it XPASSes in a rebuilt image. The OneCovariance fixture's full row-major table is what cov_output.__write_cov_list writes: every (ℓ1, ℓ2) pair for every ordered pair of spectra. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/tests/test_glass_mock.py | 10 ++++++++++ .../tests/test_sacc_io_one_covariance.py | 12 +++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sp_validation/tests/test_glass_mock.py b/src/sp_validation/tests/test_glass_mock.py index a559658a..54014403 100644 --- a/src/sp_validation/tests/test_glass_mock.py +++ b/src/sp_validation/tests/test_glass_mock.py @@ -131,6 +131,16 @@ def test_config_change_breaks_reference(): @pytest.mark.skipif(not HAVE_GLASS, reason="GLASS not installed in this image") +@pytest.mark.xfail( + reason=( + "glass_mock builds its cosmology with cosmology.compat.camb, which an image " + "built before the lock carried glass 2026.2 + cosmology-compat-camb 0.2.0 " + "lacks. With those locked pins on the path the test passes; drop this " + "xfail once it XPASSes in the image built from the current uv.lock." + ), + strict=False, + raises=ModuleNotFoundError, +) def test_matter_maps_are_seed_deterministic(): """Same config + seed → bit-identical matter/lensing maps. diff --git a/src/sp_validation/tests/test_sacc_io_one_covariance.py b/src/sp_validation/tests/test_sacc_io_one_covariance.py index 1454bc77..d69fb64c 100644 --- a/src/sp_validation/tests/test_sacc_io_one_covariance.py +++ b/src/sp_validation/tests/test_sacc_io_one_covariance.py @@ -36,11 +36,13 @@ def _ell(i): def _one_cov_table(cov_gauss, cov_all): """Flatten two n x n matrices into a OneCovariance ``covariance_list`` table. - Reproduces the real flat output of a single tomographic bin: one row per - ``(i, j)`` ℓ-bin pair in row-major order ``k = i·n + j``, ℓ_i and ℓ_j in - columns 1 and 2, the bin indices of the four fields (all ``1``) in columns - 5-8, the Gaussian value in column 10 and the Gaussian+non-Gaussian value in - column 9. Columns 0, 3 and 4 hold placeholders the reshape does not read. + Reproduces the real flat output of a single tomographic bin, as + OneCovariance's ``__write_cov_list`` writes it (every ℓ pair, both + orders): one row per ``(i, j)`` ℓ-bin pair in row-major order + ``k = i·n + j``, ℓ_i and ℓ_j in columns 1 and 2, the bin indices of the + four fields (all ``1``) in columns 5-8, the Gaussian value in column 10 and + the total (Gaussian + non-Gaussian) value in column 9. Columns 0, 3 and 4 + hold placeholders the reshape does not read. """ n = cov_gauss.shape[0] rows = [] From f7f2abb835924f353f4512f7504b458c795539ab Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:41:07 +0200 Subject: [PATCH 077/107] Name the jackknife rho/tau draws by the id the library writes them under shear_psf_leakage saves each jackknife draw as cov_{rho,tau}_{catalog_id}.npy. #295 renamed the files get_jackknife_cov reads to cov_{rho,tau}_{base}{i}.npy but kept catalog_id = version + i, so a fresh cov_estimate_method="jk" run computed every draw and then raised FileNotFoundError. The draws now take catalog_id = base_tau + i, which keeps the bins of a tomographic run apart, and the reader looks for exactly those files. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/rho_tau.py | 56 +++++++++++++++---------- src/sp_validation/tests/test_grammar.py | 46 ++++++++++++++++++++ 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 9b2ebf54..614f1589 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -462,17 +462,30 @@ def get_jackknife_cov( tau_stat_handler.catalogs.set_params(params, outdir) + # shear_psf_leakage keys its catalogues by catalog_id and writes each + # draw's covariance to cov_{rho,tau}_{catalog_id}.npy; base_tau names the + # draws so that the bins of a tomographic run never share them. + def catalog_id(i): + return f"{base_tau}{i}" + + def chunks(i): + return [ + os.path.join(outdir, f"cov_{kind}_{catalog_id(i)}.npy") + for kind in ("rho", "tau") + ] + with _CatalogueLoader(config[version], params) as load: for i in range(ncov): - tau_chunk = outdir + f"/cov_tau_{base_tau}{i}.npy" - rho_chunk = outdir + f"/cov_rho_{base_rho}{i}.npy" - if not (os.path.exists(tau_chunk) and os.path.exists(rho_chunk)): + if not all(os.path.exists(chunk) for chunk in chunks(i)): print(f"Computing rho-statistics for {version} (patch {i + 1}/{ncov})") - if f"psf_{version}{i}" not in rho_stat_handler.catalogs.catalogs_dict: + if ( + f"psf_{catalog_id(i)}" + not in rho_stat_handler.catalogs.catalogs_dict + ): # Build catalogues rho_stat_handler.build_cat_to_compute_rho( - load("psf"), catalog_id=version + str(i), mask=mask_star + load("psf"), catalog_id=catalog_id(i), mask=mask_star ) tau_stat_handler.catalogs.catalogs_dict = ( @@ -483,7 +496,7 @@ def get_jackknife_cov( tau_stat_handler.build_cat_to_compute_tau( load("shear"), cat_type="gal", - catalog_id=version + str(i), + catalog_id=catalog_id(i), mask=mask_gal, ) @@ -492,7 +505,7 @@ def get_jackknife_cov( npatch = rho_stat_handler.catalogs._params["patch_number"] field = rho_stat_handler.catalogs.catalogs_dict[ - f"psf_{version}{i}" + f"psf_{catalog_id(i)}" ].getNField(max_top=int.bit_length(npatch) - 1, coords="spherical") patch, centers = field.run_kmeans(npatch) @@ -506,7 +519,7 @@ def get_jackknife_cov( # Compute and save rho stats rho_stat_handler.compute_rho_stats( - version + str(i), + catalog_id(i), rho_filename, save_cov=True, func=_extract_xip, @@ -515,7 +528,7 @@ def get_jackknife_cov( # function to extract the tau_+ tau_stat_handler.compute_tau_stats( - version + str(i), + catalog_id(i), tau_filename, save_cov=True, func=_extract_xip, @@ -525,22 +538,21 @@ def get_jackknife_cov( # Update the keys in the dictionaries rho_dict = rho_stat_handler.catalogs.catalogs_dict tau_dict = tau_stat_handler.catalogs.catalogs_dict - rho_dict[f"psf_{version}{i + 1}"] = rho_dict.pop(f"psf_{version}{i}") - rho_dict[f"psf_error_{version}{i + 1}"] = rho_dict.pop( - f"psf_error_{version}{i}" - ) - rho_dict[f"psf_size_error_{version}{i + 1}"] = rho_dict.pop( - f"psf_size_error_{version}{i}" + for prefix in ("psf", "psf_error", "psf_size_error"): + rho_dict[f"{prefix}_{catalog_id(i + 1)}"] = rho_dict.pop( + f"{prefix}_{catalog_id(i)}" + ) + tau_dict[f"gal_{catalog_id(i + 1)}"] = tau_dict.pop( + f"gal_{catalog_id(i)}" ) - tau_dict[f"gal_{version}{i + 1}"] = tau_dict.pop(f"gal_{version}{i}") - cov_tau_loc = np.zeros_like(np.load(outdir + f"/cov_tau_{base_tau}0.npy")) - cov_rho_loc = np.zeros_like(np.load(outdir + f"/cov_rho_{base_rho}0.npy")) + cov_rho_loc, cov_tau_loc = (np.zeros_like(np.load(c)) for c in chunks(0)) for i in range(ncov): - cov_tau_loc += np.load(outdir + f"/cov_tau_{base_tau}{i}.npy") - cov_rho_loc += np.load(outdir + f"/cov_rho_{base_rho}{i}.npy") - os.remove(outdir + f"/cov_tau_{base_tau}{i}.npy") - os.remove(outdir + f"/cov_rho_{base_rho}{i}.npy") + rho_chunk, tau_chunk = chunks(i) + cov_rho_loc += np.load(rho_chunk) + cov_tau_loc += np.load(tau_chunk) + os.remove(rho_chunk) + os.remove(tau_chunk) cov_tau = cov_tau_loc / ncov cov_rho = cov_rho_loc / ncov diff --git a/src/sp_validation/tests/test_grammar.py b/src/sp_validation/tests/test_grammar.py index 60e23b75..a855e73b 100644 --- a/src/sp_validation/tests/test_grammar.py +++ b/src/sp_validation/tests/test_grammar.py @@ -285,6 +285,52 @@ def test_get_rho_tau_identical_for_v1_and_v2_psf_catalogues(tmp_path): np.testing.assert_allclose(table_v1[name], table_v2[name], rtol=1e-10) +def test_jackknife_cov_reads_the_draws_it_writes(tmp_path): + """The jackknife ρ/τ covariance averages the per-draw files the library saves. + + shear_psf_leakage writes each draw to cov_{rho,tau}_{catalog_id}.npy; the + reader must look for those names, and leave only the averaged products. + """ + from sp_validation.rho_tau import get_jackknife_cov + + _, v2 = _twins() + rng = np.random.default_rng(5) + shear = np.empty(N, dtype=[(n, "f8") for n in ("RA", "Dec", "e1", "e2", "w")]) + shear["RA"], shear["Dec"] = v2["RA"], v2["DEC"] + shear["e1"], shear["e2"] = rng.normal(0, 0.3, (2, N)) + shear["w"] = 1.0 + fits.BinTableHDU(shear).writeto(tmp_path / "shear.fits") + fits.BinTableHDU(np.array(v2[list(PSF_BLOCK.values())])).writeto( + tmp_path / "psf.fits" + ) + config = { + "v": { + "patch_number": 2, + "psf": PSF_BLOCK | {"path": str(tmp_path / "psf.fits"), "hdu": 1}, + "shear": SHEAR_BLOCK | {"path": str(tmp_path / "shear.fits")}, + } + } + treecorr_config = { + "ra_units": "deg", + "dec_units": "deg", + "sep_units": "arcmin", + "min_sep": 10, + "max_sep": 600, + "nbins": 4, + } + outdir = tmp_path / "out" + outdir.mkdir() + get_jackknife_cov( + config, "v", treecorr_config, str(outdir), "v", "v_bin_1", npatch=2, ncov=2 + ) + + cov_tau = np.load(outdir / "cov_tau_v_bin_1_jk.npy") + cov_rho = np.load(outdir / "cov_rho_v_jk.npy") + assert cov_tau.shape == (3 * 4, 3 * 4) + assert cov_rho.shape == (6 * 4, 6 * 4) + assert not list(outdir.glob("cov_*[0-9].npy")) + + # -- mask-bit columns: a rule family of their own --------------------------- From d603c5c1a2521c9c7fb929753d177ca5806dbf5b Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:43:40 +0200 Subject: [PATCH 078/107] Compute the iNKA fiducial C_ell with CCL, from the configured cosmology The pseudo-C_ell covariance's fiducial spectrum is built from self.cosmo, a CCL Cosmology. The tomographic pseudo-C_ell work (#221, reapplied as #240; a941f542, 753abc13) evaluated it with cs_util's CAMB backend, which rebuilds a CAMB cosmology from H0, ombh2, omch2, ns, As/sigma8 and w/wa only: the neutrino mass is dropped (CAMB's 0.06 eV default stands in) and CAMB's mead2020 non-linear model replaces the object's halofit. Against CCL that is -4% at ell=1000 for Planck 2018 and -8.5% with mnu=0.3. develop passed backend="ccl"; the default is CCL again. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/pseudo_cl.py | 8 ++++++-- src/sp_validation/tests/test_pseudo_cl.py | 24 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index f52ab1d1..5bb1b392 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -969,10 +969,14 @@ def get_pseudo_cls_catalog( # ---------------------- Covariance computation functions ---------------------- -def get_fiducial_cl(z, dndz, lmax, cosmo, backend="camb"): +def get_fiducial_cl(z, dndz, lmax, cosmo, backend="ccl"): """ Get the fiducial Cl's using the redshift distribution. - Cosmology is determined by the input cosmo object. + + Cosmology is determined by the input CCL cosmo object. The default CCL + backend evaluates that object as configured; cs_util's CAMB backend + rebuilds a CAMB cosmology from it, dropping the neutrino mass and using + CAMB's own non-linear model. """ ell = np.arange(1, lmax + 1) diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index 80f4d90d..11bcb720 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -819,6 +819,30 @@ def test_fiducial_pixel_window_applies_only_to_map(): npt.assert_array_equal(fiducial[key], cl) +def test_fiducial_cl_follows_the_configured_cosmology(): + """The iNKA fiducial is CCL's prediction for the configured cosmology. + + A non-default neutrino mass makes the check bite: cs_util's CAMB backend + drops it. + """ + import pyccl as ccl + from cs_util.cosmo import get_cosmo + + from sp_validation.pseudo_cl import get_fiducial_cl + + cosmo = get_cosmo(mnu=0.3) + z = np.linspace(0.01, 3.0, 200) + dndz = np.exp(-(((z - 0.7) / 0.3) ** 2)) + lmax = 128 + fiducial = get_fiducial_cl(z, dndz, lmax, cosmo)["W1xW1"] + + ell = np.arange(1, lmax + 1) + tracer = ccl.WeakLensingTracer(cosmo, dndz=(z, dndz)) + npt.assert_allclose( + fiducial, ccl.angular_cl(cosmo, tracer, tracer, ell), rtol=1e-10 + ) + + def test_calculate_pseudo_cl_catalog_end_to_end_tomo(cv, tmp_path): """End-to-end catalog path: FITS round-trip of ell + EE/EB/BB. From 551f5c9c3e8ba82b2ab1b3c4c0ef30c3b8a71ae7 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:45:58 +0200 Subject: [PATCH 079/107] Default the catalogue pseudo-Cl wrapper to the ("all", "all") pair PseudoClMixin.get_pseudo_cls_catalog forwarded tomo_bin_a=tomo_bin_b=None over the primitive's "all" defaults, which the primitive reads as a tomographic selection: a call without bins selected an empty catalogue (NaMaster then fails inside ducc0) or tripped the tomography assertion. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/cosmo_val/pseudo_cl.py | 2 +- src/sp_validation/tests/test_pseudo_cl.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index 78eab1ba..aa25cbdf 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -974,7 +974,7 @@ def get_pseudo_cls_map( ) def get_pseudo_cls_catalog( - self, catalog, params, wsp=None, tomo_bin_a=None, tomo_bin_b=None + self, catalog, params, wsp=None, tomo_bin_a="all", tomo_bin_b="all" ): """Catalog-based pseudo-cl (thin wrapper, state -> primitive).""" return spv_pseudo_cl.get_pseudo_cls_catalog( diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index 11bcb720..9f99a67c 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -613,6 +613,16 @@ def test_get_pseudo_cls_catalog(cv, cat_and_params): npt.assert_allclose(cl_all[2], cl_all[1], rtol=RTOL_CAT, atol=ATOL_CAT) +def test_get_pseudo_cls_catalog_defaults_to_the_whole_catalogue(cv, cat_and_params): + """Called without bins, the wrapper measures the ("all", "all") pair.""" + cat_gal, params = cat_and_params + _, cl_default, _ = cv.get_pseudo_cls_catalog(catalog=cat_gal, params=params) + _, cl_all, _ = cv.get_pseudo_cls_catalog( + catalog=cat_gal, params=params, tomo_bin_a="all", tomo_bin_b="all" + ) + npt.assert_allclose(cl_default, cl_all, rtol=RTOL_CAT, atol=ATOL_CAT) + + # =========================================================================== # apply_random_rotation -- invariant + reproducibility # =========================================================================== From 465ae282c26ab992a12ab3f995c209c6327af751 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Fri, 2 Oct 2026 23:51:40 +0200 Subject: [PATCH 080/107] Let the object-wise leakage reader take a row selection shear_psf_leakage f1a2c071 (#48) has temporarily_read_data(selection=...) call self.read_data(selection=selection). _LeakageObject overrode read_data without that argument, so every tomographic bin of the object-wise leakage raised TypeError. The override now keeps the rows a boolean selection marks, over the rows io.open_entry returns, which are the rows _get_galaxy_mask builds its masks on. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/cosmo_val/core.py | 14 ++++++++++++-- src/sp_validation/tests/test_psf_leakage.py | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index 017b5c74..c6ed7b7b 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -52,8 +52,18 @@ class _LeakageObject(run_object.LeakageObject): entries = None - def read_data(self): - self._dat = io.open_entry(self.entries["shear"]) + def read_data(self, selection=None): + """Read the shear entry, keeping the rows ``selection`` marks. + + ``selection`` is an optional boolean array over the rows of + ``io.open_entry(entries["shear"])``, in that order. + """ + dat = io.open_entry(self.entries["shear"]) + if selection is not None: + if len(selection) != len(dat): + raise ValueError("Selection array has different length than catalogue") + dat = dat[selection] + self._dat = dat # %% diff --git a/src/sp_validation/tests/test_psf_leakage.py b/src/sp_validation/tests/test_psf_leakage.py index bef99e32..0290ad4e 100644 --- a/src/sp_validation/tests/test_psf_leakage.py +++ b/src/sp_validation/tests/test_psf_leakage.py @@ -206,6 +206,26 @@ def test_objectwise_leakage_and_plot_smoke(tmp_path, monkeypatch): assert (tmp_path / "output" / "leakage_coefficients_tomo.png").exists() +def test_leakage_object_reads_the_selected_rows(tmp_path): + """The object-wise reader keeps the rows a galaxy mask selects.""" + pytest.importorskip("shear_psf_leakage") + from sp_validation.cosmo_val import CosmologyValidation + from sp_validation.cosmo_val.core import _LeakageObject + + version, params = _objectwise_config(tmp_path) + cv = CosmologyValidation(versions=[version], npatch=1, **params) + obj = object.__new__(_LeakageObject) + obj.entries = cv.cc[version] + + mask = cv._get_galaxy_mask(version, 2) + obj.read_data(selection=mask) + assert len(obj._dat) == mask.sum() + assert np.all(obj._dat["tomo_bin"] == 2) + + with pytest.raises(ValueError, match="different length"): + obj.read_data(selection=mask[1:]) + + class _ObjectwiseControlFlow(PSFSystematicsMixin): """Two versions, one of which lacks a catalogue column.""" From e3bd484774c8dcde387d89dd3d4eccb99fe64f6c Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 00:00:58 +0200 Subject: [PATCH 081/107] Restore Hartlap debiasing of simulation rho/tau covariances The inverse of a covariance estimated from N realisations is biased high; the PSF-leakage fits correct it with the realisation count. develop passed 300 for cov_estimate_method="sim"; #295 (ee75f539) kept only the jackknife patch count, so simulation covariances went undebiased and the fitted leakage parameters came out too tight (about 12% for 60 tau points). Nothing records the simulation count: no code in this repository's history writes cov_tau_*_sim.npy, and the file holds only the matrix. The count is therefore the constructor parameter n_sim_cov, default 300 as on develop, forwarded by the workflow from config["cosmo_val"]. Co-Authored-By: Claude Opus 5.5 --- papers/cosmo_val/config/config.yaml | 2 ++ src/sp_validation/cosmo_val/core.py | 11 +++++-- .../cosmo_val/psf_systematics.py | 8 +++-- src/sp_validation/tests/test_psf_leakage.py | 33 +++++++++++++++++++ workflow/common.py | 1 + 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/papers/cosmo_val/config/config.yaml b/papers/cosmo_val/config/config.yaml index 7dd0e091..f469cc44 100644 --- a/papers/cosmo_val/config/config.yaml +++ b/papers/cosmo_val/config/config.yaml @@ -33,6 +33,8 @@ cosmo_val: cov_estimate_method: th compute_cov_rho: true n_cov: 100 + # simulations behind a cov_estimate_method: sim rho/tau covariance (Hartlap) + n_sim_cov: 300 var_method: jackknife quantile: 0.1587 ylim_xi_sys_ratio: [-0.02, 0.5] diff --git a/src/sp_validation/cosmo_val/core.py b/src/sp_validation/cosmo_val/core.py index c6ed7b7b..4fbd2a89 100644 --- a/src/sp_validation/cosmo_val/core.py +++ b/src/sp_validation/cosmo_val/core.py @@ -129,8 +129,13 @@ class CosmologyValidation( Output directory. If None, the catalog config's paths.output. rho_tau_method : {'lsq', 'mcmc'}, default 'lsq' Fitting method for PSF leakage systematics parameters. - cov_estimate_method : {'th', 'jk'}, default 'th' - Covariance estimation: 'th' for semi-analytic theory, 'jk' for jackknife. + cov_estimate_method : {'th', 'jk', 'sim'}, default 'th' + Covariance estimation: 'th' for semi-analytic theory, 'jk' for jackknife, + 'sim' for a covariance measured on simulations. + n_sim_cov : int, default 300 + Number of simulations the 'sim' rho/tau covariance was measured on. + The covariance file does not record it; it sets the Hartlap debiasing + of the inverse covariance in the PSF-leakage fits. compute_cov_rho : bool, default True Whether to compute covariance for rho statistics during PSF analysis. n_cov : int, default 100 @@ -279,6 +284,7 @@ def __init__( cov_estimate_method="th", compute_cov_rho=True, n_cov=100, + n_sim_cov=300, theta_min=0.1, theta_max=250, nbins=20, @@ -310,6 +316,7 @@ def __init__( self.cov_estimate_method = cov_estimate_method self.compute_cov_rho = compute_cov_rho self.n_cov = n_cov + self.n_sim_cov = n_sim_cov self.theta_min = theta_min self.theta_max = theta_max self.npatch = npatch diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 58f92d09..7c0cc140 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -446,7 +446,11 @@ def set_psf_parameter_nwalkers(self, nwalkers): self.psf_error_nwalkers = nwalkers def get_samples(self, version, params, tomo_bin_id, track_result=False): - npatch = params["patch_number"] if self.cov_estimate_method == "jk" else None + # Hartlap debiasing of the inverse covariance: the number of jackknife + # patches or of simulations the covariance was estimated from. + n_realisations = {"jk": params["patch_number"], "sim": self.n_sim_cov}.get( + self.cov_estimate_method + ) base_rho = self.basename(version) base_tau = self.basename(version, tomo_bin_a=tomo_bin_id) @@ -464,7 +468,7 @@ def get_samples(self, version, params, tomo_bin_id, track_result=False): base_rho, base_tau, cov_type=self.cov_estimate_method, - apply_debias=npatch, + apply_debias=n_realisations, sampler=self.rho_tau_method, nsamples=n_samples, nwalkers=n_walkers, diff --git a/src/sp_validation/tests/test_psf_leakage.py b/src/sp_validation/tests/test_psf_leakage.py index 0290ad4e..fe91b3bd 100644 --- a/src/sp_validation/tests/test_psf_leakage.py +++ b/src/sp_validation/tests/test_psf_leakage.py @@ -260,3 +260,36 @@ def test_version_missing_a_column_stays_dropped(): assert set(cv.leakage_coeff) == {"good"} cv.calculate_objectwise_leakage(tomography=True) assert "tomo_bin_1" in cv.leakage_coeff["good"] + + +@pytest.mark.parametrize( + "cov_estimate_method, expected", + [("sim", 500), ("jk", 150), ("th", None)], +) +def test_rho_tau_fit_debiases_estimated_covariances( + monkeypatch, cov_estimate_method, expected +): + """Simulation and jackknife covariances get Hartlap-debiased; theory does not. + + The debiasing count is the number of simulations (``n_sim_cov``) or of + jackknife patches the covariance was estimated from. + """ + from sp_validation.cosmo_val import psf_systematics + + seen = {} + + def fake_get_samples(*args, apply_debias, **kwargs): + seen["apply_debias"] = apply_debias + return None, None, None + + monkeypatch.setattr(psf_systematics, "get_samples", fake_get_samples) + cv = SimpleNamespace( + cov_estimate_method=cov_estimate_method, + n_sim_cov=500, + rho_tau_method="lsq", + psf_fitter=None, + basename=lambda version, tomo_bin_a="all": version, + ) + cv.get_samples = PSFSystematicsMixin.get_samples.__get__(cv) + cv.get_samples("v", {"patch_number": 150}, "all") + assert seen["apply_debias"] == expected diff --git a/workflow/common.py b/workflow/common.py index b250399c..25c7da78 100644 --- a/workflow/common.py +++ b/workflow/common.py @@ -398,6 +398,7 @@ def cv_basename(version, fiducial=None): "cov_estimate_method", "compute_cov_rho", "n_cov", + "n_sim_cov", "theta_min", "theta_max", "nbins", From e6406e46313e52a36dad524d71b0fed69fe37afa Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 00:22:22 +0200 Subject: [PATCH 082/107] Seed jackknife patches across tomographic bins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TreeCorr's unseeded k-means and thread-dependent tree depth changed patch layouts, ξ±, and jackknife covariances between runs. Compute seeded, fixed-depth centres once from each full version catalogue and share them across bin pairs; use the same helper for rho/tau covariance patches. Add reproducibility and cross-bin regressions. Co-Authored-By: Claude Opus 5.5 --- src/sp_validation/cosmo_val/real_space.py | 33 ++++-- src/sp_validation/rho_tau.py | 9 +- src/sp_validation/statistics.py | 40 ++++++- src/sp_validation/tests/test_cosmo_val.py | 132 ++++++++++++++++------ 4 files changed, 164 insertions(+), 50 deletions(-) diff --git a/src/sp_validation/cosmo_val/real_space.py b/src/sp_validation/cosmo_val/real_space.py index fd1f2bbd..7dea3542 100644 --- a/src/sp_validation/cosmo_val/real_space.py +++ b/src/sp_validation/cosmo_val/real_space.py @@ -11,6 +11,8 @@ import numpy as np import treecorr +from sp_validation.statistics import jackknife_patch_centers + class RealSpaceMixin: def calculate_2pcf_version( @@ -53,8 +55,8 @@ def calculate_2pcf_version( - The non-tomographic pair is written to the columns-only TreeCorr dump ``xi_{basename}.txt``. If that file already exists, the pair is read back from it instead of being recomputed. - - If a patch file for the given configuration does not exist, it is - created during the process. + - Seeded patch centres are computed once from the full catalogue and + shared by every tomographic bin pair. """ npatch = npatch or self.npatch @@ -88,13 +90,12 @@ def calculate_2pcf_version( to_compute.append((bin1, bin2)) if to_compute: - patch_file = self._output_path(f"{ver}_patches_npatch={npatch}.dat") cols = self._shear_columns(ver, compute_tomography) + patch_centers = self._patch_centers(cols, npatch) for bin1, bin2 in to_compute: gg = treecorr.GGCorrelation(treecorr_config) - patch_centers = patch_file if os.path.exists(patch_file) else None cat_gal1 = self._bin_catalog(cols, bin1, npatch, patch_centers) cat_gal2 = ( self._bin_catalog(cols, bin2, npatch, patch_centers) @@ -102,10 +103,6 @@ def calculate_2pcf_version( else None ) - # If no patch file exists, save the current patches - if not os.path.exists(patch_file): - cat_gal1.write_patch_centers(patch_file) - gg.process(cat_gal1, cat2=cat_gal2) if (bin1, bin2) == ("all", "all"): @@ -154,6 +151,19 @@ def _shear_columns(self, ver, compute_tomography): ), } + def _patch_centers(self, cols, npatch): + """Seeded patch centres from the full catalogue of one version.""" + if int(npatch) <= 1: + return None + cat = treecorr.Catalog( + ra=cols["ra"], + dec=cols["dec"], + w=cols["w"], + ra_units=self.treecorr_config["ra_units"], + dec_units=self.treecorr_config["dec_units"], + ) + return jackknife_patch_centers(cat, int(npatch)) + def _bin_catalog(self, cols, tomo_bin_id, npatch, patch_centers=None): """TreeCorr catalogue of one tomographic bin (``"all"``: every row).""" mask = slice(None) if tomo_bin_id == "all" else cols["tomo_bin"] == tomo_bin_id @@ -239,13 +249,16 @@ def calculate_aperture_mass_dispersion( self._map2.setdefault(ver, {}) cols = self._shear_columns(ver, compute_tomography) + patch_centers = self._patch_centers(cols, npatch) for bin1, bin2 in tomo_bin_pairs: gg = treecorr.GGCorrelation(treecorr_config) - cat_gal1 = self._bin_catalog(cols, bin1, npatch) + cat_gal1 = self._bin_catalog(cols, bin1, npatch, patch_centers) cat_gal2 = ( - self._bin_catalog(cols, bin2, npatch) if bin1 != bin2 else None + self._bin_catalog(cols, bin2, npatch, patch_centers) + if bin1 != bin2 + else None ) gg.process(cat_gal1, cat2=cat_gal2) diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 614f1589..526e02e6 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -14,6 +14,7 @@ # SquareRootScale lives in sp_validation.plots; re-exported here so that # `from sp_validation.rho_tau import SquareRootScale` keeps working. from sp_validation.plots import SquareRootScale # noqa: F401 +from sp_validation.statistics import jackknife_patch_centers def _extract_xip(correlations): @@ -504,10 +505,10 @@ def chunks(i): print(f"Computing the patch centers for patch {i + 1}/{ncov}") npatch = rho_stat_handler.catalogs._params["patch_number"] - field = rho_stat_handler.catalogs.catalogs_dict[ - f"psf_{catalog_id(i)}" - ].getNField(max_top=int.bit_length(npatch) - 1, coords="spherical") - patch, centers = field.run_kmeans(npatch) + centers = jackknife_patch_centers( + rho_stat_handler.catalogs.catalogs_dict[f"psf_{catalog_id(i)}"], + npatch, + ) # Update the patch centers of the catalogs for key, cat in rho_stat_handler.catalogs.catalogs_dict.items(): diff --git a/src/sp_validation/statistics.py b/src/sp_validation/statistics.py index d3919c4b..bd95932b 100644 --- a/src/sp_validation/statistics.py +++ b/src/sp_validation/statistics.py @@ -3,8 +3,8 @@ :Name: statistics.py :Description: Cosmology-independent statistical helpers (jackknife resampling, - chi2/PTE, calibrated min-PTE across many null tests, - covariance<->correlation, OneCovariance reshaping). + jackknife patch centres, chi2/PTE, calibrated min-PTE across + many null tests, covariance<->correlation, OneCovariance reshaping). """ import itertools @@ -13,6 +13,42 @@ import numpy as np from scipy import stats +#: Depth of the ball-tree layers that seed the jackknife k-means. +PATCH_MIN_TOP = 6 + + +def jackknife_patch_centers(cat, npatch, seed=0): + """Seeded k-means jackknife patch centres for a TreeCorr catalogue. + + Seeding the k-means does not by itself fix the patches. TreeCorr starts the + k-means from the top layers of a ball tree whose depth, unless ``min_top`` + is given, grows with the OpenMP thread count (``Field._determine_top``), + and ``Catalog(npatch=..., rng=...)`` offers no way to set it. Pinning that + depth makes the centres a function of the catalogue's positions, weights + and ``seed`` alone, on every machine. + + Parameters + ---------- + cat : treecorr.Catalog + Catalogue with spherical (RA, Dec) positions. + npatch : int + Number of patches. + seed : int, optional + Seed for the k-means initialisation. + + Returns + ------- + numpy.ndarray + Patch centres, to pass as ``patch_centers`` to ``treecorr.Catalog``. + """ + field = cat.getNField( + min_top=PATCH_MIN_TOP, + max_top=int.bit_length(npatch) - 1, + coords="spherical", + ) + _, centers = field.run_kmeans(npatch, rng=np.random.default_rng(seed)) + return centers + def jackknif_weighted_average2( data, diff --git a/src/sp_validation/tests/test_cosmo_val.py b/src/sp_validation/tests/test_cosmo_val.py index daa33a30..7029cdcd 100644 --- a/src/sp_validation/tests/test_cosmo_val.py +++ b/src/sp_validation/tests/test_cosmo_val.py @@ -283,6 +283,7 @@ def _write_synthetic_catalogs( seed=1234, coherent_shear=False, with_psf=False, + with_tomography=False, ): """Write small deterministic FITS catalogs + dndz, return a config dict. @@ -300,6 +301,8 @@ def _write_synthetic_catalogs( with_psf : bool If True, add a ``psf`` config block (rho/tau / pseudo-Cl read it via ``get_params_rho_tau``). + with_tomography : bool + If True, add two tomographic bins to the shear catalogue. """ from astropy.table import Table @@ -325,9 +328,10 @@ def _write_synthetic_catalogs( w = rng.uniform(0.5, 1.0, n_gal) shear_path = cat_dir / "shear.fits" - Table({"RA": ra, "Dec": dec, "e1": e1, "e2": e2, "w": w}).write( - shear_path, overwrite=True - ) + shear_data = {"RA": ra, "Dec": dec, "e1": e1, "e2": e2, "w": w} + if with_tomography: + shear_data["tomo_bin_id"] = rng.integers(1, 3, n_gal) + Table(shear_data).write(shear_path, overwrite=True) star_path = cat_dir / "star.fits" Table( @@ -362,6 +366,8 @@ def _write_synthetic_catalogs( "e1_col_corrected": "e1", "e2_col_corrected": "e2", } + if with_tomography: + shear_cfg["tomo_bin_col"] = "tomo_bin_id" star_cfg = { "path": "star.fits", "ra_col": "RA", @@ -506,42 +512,100 @@ def test_a_patched_xi_dump_reads_back(self, tmp_path): getattr(read, column), getattr(measured, column), rtol=1e-4 ) - def test_calculate_2pcf_does_not_depend_on_thread_count(self, tmp_path): - """calculate_2pcf's ξ± is the same on 4 and on 48 TreeCorr threads. - - Production binning (default bin_slop/angle_slop), both runs on the - jackknife patches the first one writes, each from a fresh Catalog; they - must agree to far below the jackknife σ. - """ + def test_calculate_2pcf_is_reproducible_across_machines( + self, tmp_path, monkeypatch + ): + """Fresh calculate_2pcf_version runs share patches and ξ± across CPU counts.""" import treecorr + import treecorr.field + + patches = [] + process = treecorr.GGCorrelation.process + + def recording_process(gg, cat, *args, **kwargs): + patches.append(np.array(cat.patch)) + return process(gg, cat, *args, **kwargs) + + monkeypatch.setattr(treecorr.GGCorrelation, "process", recording_process) + + xi, var, counts = {}, {}, {} + for tree, n_cpu in (("a", 4), ("b", 16)): + monkeypatch.setattr(treecorr.field, "get_omp_threads", lambda n=n_cpu: n) + run_dir = tmp_path / tree + run_dir.mkdir() + params, version = self._write_synthetic_catalogs( + run_dir, + n_gal=4000, + ra_range=(0.0, 60.0), + dec_range=(-10.0, 30.0), + coherent_shear=True, + ) + gg = CosmologyValidation( + versions=[version], + npatch=100, + theta_min=15.0, + theta_max=70.0, + nbins=6, + **params, + ).calculate_2pcf_version(version, num_threads=n_cpu)[ + "tomo_bin_all_tomo_bin_all" + ] + xi[tree] = np.concatenate([gg.xip, gg.xim]) + var[tree] = np.concatenate([gg.varxip, gg.varxim]) + counts[tree] = {key: result.npairs for key, result in gg.results.items()} + + np.testing.assert_array_equal(patches[0], patches[1]) + assert counts["a"].keys() == counts["b"].keys() + for key in counts["a"]: + np.testing.assert_array_equal(counts["a"][key], counts["b"][key]) + np.testing.assert_allclose(xi["a"], xi["b"], rtol=0, atol=1e-12) + np.testing.assert_allclose(var["a"], var["b"], rtol=1e-10) + + def test_cross_tomographic_pair_shares_patch_centres(self, tmp_path, monkeypatch): + """Both catalogues in a cross-bin pair use the full-sample centres.""" + import treecorr + + from sp_validation.statistics import jackknife_patch_centers params, version = self._write_synthetic_catalogs( - tmp_path, n_gal=4000, coherent_shear=True + tmp_path, n_gal=400, with_tomography=True ) - cv = CosmologyValidation( - versions=[version], - npatch=8, - theta_min=15.0, - theta_max=70.0, - nbins=6, - **params, + cv = CosmologyValidation(versions=[version], npatch=4, **params) + cols = cv._shear_columns(version, compute_tomography=True) + full_catalog = treecorr.Catalog( + ra=cols["ra"], + dec=cols["dec"], + w=cols["w"], + ra_units=cv.treecorr_config["ra_units"], + dec_units=cv.treecorr_config["dec_units"], + ) + expected_centers = jackknife_patch_centers(full_catalog, 4) + catalogs = [] + make_catalog = cv._bin_catalog + + def recording_bin_catalog(cols, bin_id, npatch, patch_centers=None): + catalog = make_catalog(cols, bin_id, npatch, patch_centers) + catalogs.append((bin_id, patch_centers, np.array(catalog._centers))) + return catalog + + monkeypatch.setattr(cv, "_bin_catalog", recording_bin_catalog) + cv.calculate_2pcf_version(version, npatch=4, compute_tomography=True) + + assert len(catalogs) == 4 + cross_pair_catalogs = catalogs[1:3] + assert [entry[0] for entry in cross_pair_catalogs] == [1, 2] + np.testing.assert_array_equal( + cross_pair_catalogs[0][1], cross_pair_catalogs[1][1] + ) + np.testing.assert_array_equal( + cross_pair_catalogs[0][2], cross_pair_catalogs[1][2] + ) + np.testing.assert_allclose( + cross_pair_catalogs[0][2], expected_centers, rtol=0, atol=1e-14 + ) + np.testing.assert_allclose( + cross_pair_catalogs[1][2], expected_centers, rtol=0, atol=1e-14 ) - - xi = {} - for n_threads in (4, 48): - # calculate_2pcf_version reads back an existing text dump instead of - # measuring. - for dump in Path(params["output_dir"]).glob(f"xi_{version}_*.txt"): - dump.unlink() - gg = cv.calculate_2pcf_version(version, num_threads=n_threads)[ - "tomo_bin_all_tomo_bin_all" - ] - assert treecorr.get_omp_threads() == n_threads # the count took effect - xi[n_threads] = np.concatenate([gg.xip, gg.xim]) - sigma = np.sqrt(np.concatenate([gg.varxip, gg.varxim])) - - shift = np.max(np.abs(xi[48] - xi[4]) / sigma) - assert shift < 1e-6, f"ξ± moves by {shift:.3g}σ between 4 and 48 threads" def test_treecorr_runs_on_the_cpus_the_process_holds(self, tmp_path): """By default TreeCorr takes the process's CPU affinity, not the node's count.""" From 989e0eb1899c16cc05f8f286984ee22deb22d94e Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 01:26:31 +0200 Subject: [PATCH 083/107] Expect the rho/tau SACC part under the tomo_bin_all basename in the DAG test Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0138Efdjkma53ysbYhhXDgxW --- workflow/tests/test_dag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/tests/test_dag.py b/workflow/tests/test_dag.py index b11acf3b..438f91a1 100644 --- a/workflow/tests/test_dag.py +++ b/workflow/tests/test_dag.py @@ -33,7 +33,7 @@ def test_assemble_resolves(toy): f"pseudo_cl_cov_{version}_{harmonic}.fits", f"{version}_cosebis.sacc", f"{version}_pure_eb.sacc", - f"rho_tau_{version}_{reporting}.sacc", + f"rho_tau_{version}_tomo_bin_all_{reporting}.sacc", }, job.input From 88fa858f5cce84b42d646fbe13553a892f426b04 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 01:54:28 +0200 Subject: [PATCH 084/107] Route rho/tau workflow paths through cv_basename helpers The tomography branch inserts _tomo_bin_all into CosmologyValidation.basename, but inference.smk and covariance.smk still hard-coded the pre-tomography rho_stats/tau_stats/cov_tau names, so they could never match what rho_tau_stats writes. cv_rho_stats, cv_tau_stats and a new cv_cov_tau now live in common.py (cosmo_val.smk is only included with a cosmo_val config, so its helpers were invisible to the compute rules) and take the binning explicitly: CV_FIDUCIAL in cosmo_val, the wildcards in inference_prep, FIDUCIAL for the glass-mock rules. cv_objectwise_leakage reads every version's rho/tau FITS through _load_alpha_leakage but declared no inputs; it now declares them so it is scheduled after rho_tau_stats. Co-Authored-By: Claude Opus 5.5 --- workflow/common.py | 18 ++++++++++++++++++ workflow/rules/cosmo_val.smk | 27 +++++++++------------------ workflow/rules/covariance.smk | 7 ++----- workflow/rules/inference.smk | 10 +++++----- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/workflow/common.py b/workflow/common.py index 25c7da78..111da5f5 100644 --- a/workflow/common.py +++ b/workflow/common.py @@ -390,6 +390,24 @@ def cv_basename(version, fiducial=None): ) +def cv_rho_stats(version, fiducial=None): + """ρ-statistics FITS that CosmologyValidation writes for a version.""" + base = cv_basename(version, fiducial) + return str(COSMO_VAL / "rho_tau_stats" / f"rho_stats_{base}.fits") + + +def cv_tau_stats(version, fiducial=None): + """τ-statistics FITS that CosmologyValidation writes for a version.""" + base = cv_basename(version, fiducial) + return str(COSMO_VAL / "rho_tau_stats" / f"tau_stats_{base}.fits") + + +def cv_cov_tau(version, fiducial=None): + """Theoretical τ covariance that CosmologyValidation writes for a version.""" + base = cv_basename(version, fiducial) + return str(COSMO_VAL / "rho_tau_stats" / f"cov_tau_{base}_th.npy") + + # CosmologyValidation constructor kwargs read from config["cosmo_val"]. Every # keyword with a default is either here or explicitly exempted in # src/sp_validation/tests/test_cv_init_params.py, so no default applies silently. diff --git a/workflow/rules/cosmo_val.smk b/workflow/rules/cosmo_val.smk index 699bc9de..f4eb0e2a 100644 --- a/workflow/rules/cosmo_val.smk +++ b/workflow/rules/cosmo_val.smk @@ -58,18 +58,6 @@ def cv_xi_txt(version): return str(COSMO_VAL / f"xi_{version}_tomo_bin_all_{xi_binning('reporting')}.txt") -def cv_rho_stats(version): - return str( - COSMO_VAL / "rho_tau_stats" / f"rho_stats_{cv_basename(version, CV_FIDUCIAL)}.fits" - ) - - -def cv_tau_stats(version): - return str( - COSMO_VAL / "rho_tau_stats" / f"tau_stats_{cv_basename(version, CV_FIDUCIAL)}.fits" - ) - - def _pure_eb_stub(version): """Shared stem of the pure-E/B diagnostic products (npz + figures).""" eb = XI_GRIDS["integration"] @@ -220,7 +208,7 @@ CV_INIT = cv_init_params(config) rule cv_plot_rho_stats: """Overlay rho statistics across all versions.""" input: - rho=[cv_rho_stats(v) for v in CV_VERSIONS], + rho=[cv_rho_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], output: sentinel=str(CV_SENTINELS / "plot_rho_stats.done"), params: @@ -234,7 +222,7 @@ rule cv_plot_rho_stats: rule cv_plot_tau_stats: """Overlay tau statistics across all versions.""" input: - tau=[cv_tau_stats(v) for v in CV_VERSIONS], + tau=[cv_tau_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], output: sentinel=str(CV_SENTINELS / "plot_tau_stats.done"), params: @@ -248,8 +236,8 @@ rule cv_plot_tau_stats: rule cv_rho_tau_fits: """Fit the PSF-error model (alpha/beta/eta) and propagate to xi_psf_sys.""" input: - rho=[cv_rho_stats(v) for v in CV_VERSIONS], - tau=[cv_tau_stats(v) for v in CV_VERSIONS], + rho=[cv_rho_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], + tau=[cv_tau_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], output: sentinel=str(CV_SENTINELS / "rho_tau_fits.done"), params: @@ -280,6 +268,9 @@ rule cv_footprints: rule cv_objectwise_leakage: """Object-wise PSF-leakage regression vs scale-dependent alpha (all versions).""" + input: + rho=[cv_rho_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], + tau=[cv_tau_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], output: sentinel=str(CV_SENTINELS / "objectwise_leakage.done"), params: @@ -345,8 +336,8 @@ rule cv_ratio_xi_sys_xi: """Ratio of PSF systematics (xi_psf_sys) to the cosmic-shear signal (xi+).""" input: xi=[cv_xi_txt(v) for v in CV_VERSIONS], - rho=[cv_rho_stats(v) for v in CV_VERSIONS], - tau=[cv_tau_stats(v) for v in CV_VERSIONS], + rho=[cv_rho_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], + tau=[cv_tau_stats(v, CV_FIDUCIAL) for v in CV_VERSIONS], output: ratio=str(COSMO_VAL / "ratio_xi_sys_xi.png"), params: diff --git a/workflow/rules/covariance.smk b/workflow/rules/covariance.smk index 6ca907f9..b1a7faf1 100644 --- a/workflow/rules/covariance.smk +++ b/workflow/rules/covariance.smk @@ -212,17 +212,14 @@ rule covariance_glass_mock: "../scripts/compute_glass_mock_covariance.py" -# fiducial_binning_suffix() defined in Snakefile - - rule generate_glass_mock_rhotau_samples: """Generate sampled tau statistics for glass mocks. Only tau is sampled; inference_prep_glass_mock uses real rho data. """ input: - cov_tau=str(COSMO_VAL / f"rho_tau_stats/cov_tau_{FIDUCIAL['mock_version']}{fiducial_binning_suffix()}_th.npy"), - ref_tau=str(COSMO_VAL / f"rho_tau_stats/tau_stats_{FIDUCIAL['mock_version']}{fiducial_binning_suffix()}.fits"), + cov_tau=cv_cov_tau(FIDUCIAL["mock_version"]), + ref_tau=cv_tau_stats(FIDUCIAL["mock_version"]), output: tau="results/glass_mock_rhotau_samples/{mock_id}/tau_stats_sampled.fits", params: diff --git a/workflow/rules/inference.smk b/workflow/rules/inference.smk index f707e895..00e96137 100644 --- a/workflow/rules/inference.smk +++ b/workflow/rules/inference.smk @@ -59,10 +59,10 @@ rule inference_prep: # n(z) file: the catalogue entry's nz_file=lambda w: redshift_path(w.version), # rho/tau stats - rho_stats=str(COSMO_VAL / "rho_tau_stats/rho_stats_{version}_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.fits"), - tau_stats=str(COSMO_VAL / "rho_tau_stats/tau_stats_{version}_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}.fits"), + rho_stats=lambda w: cv_rho_stats(w.version, w), + tau_stats=lambda w: cv_tau_stats(w.version, w), # tau covariance (tracked as dependency) - tau_cov=str(COSMO_VAL / "rho_tau_stats/cov_tau_{version}_minsep={min_sep}_maxsep={max_sep}_nbins={nbins}_npatch={npatch}_th.npy"), + tau_cov=lambda w: cv_cov_tau(w.version, w), pseudo_cl=lambda w: pseudo_cl_assets(w.version)[0], pseudo_cl_cov=lambda w: pseudo_cl_assets(w.version)[1], output: @@ -131,10 +131,10 @@ rule inference_prep_glass_mock: # n(z) file nz_file=redshift_path(FIDUCIAL["mock_version"]), # Rho/tau stats: rho from real data, tau sampled - rho_stats=str(COSMO_VAL / f"rho_tau_stats/rho_stats_{FIDUCIAL['mock_version']}{fiducial_binning_suffix()}.fits"), + rho_stats=cv_rho_stats(FIDUCIAL["mock_version"]), tau_stats="results/glass_mock_rhotau_samples/{mock_id}/tau_stats_sampled.fits", # Tau covariance (real data) - tau_cov=str(COSMO_VAL / f"rho_tau_stats/cov_tau_{FIDUCIAL['mock_version']}{fiducial_binning_suffix()}_th.npy"), + tau_cov=cv_cov_tau(FIDUCIAL["mock_version"]), # C_ell data for dual config generation cl_file=f"{GLASS_MOCK_DATA_DIR}/cl_glass_mock_{{mock_id}}_4096.npy", cl_cov=pseudo_cl_assets(FIDUCIAL["mock_version"])[1], From f2275a73f7648044993dd3a63cde868e77906b83 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 17:27:51 +0200 Subject: [PATCH 085/107] Keep the iNKA fiducial C_ell on CAMB, as the tomography branch computes it Reverts d603c5c1. The CAMB/CCL difference is mostly the non-linear model (mead2020 vs halofit), not an error, and the fiducial only sets the signal term of a Gaussian covariance. The one real defect, cs_util's CCL-to-CAMB conversion dropping m_nu, belongs in cs_util. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0138Efdjkma53ysbYhhXDgxW --- src/sp_validation/pseudo_cl.py | 8 ++------ src/sp_validation/tests/test_pseudo_cl.py | 24 ----------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/sp_validation/pseudo_cl.py b/src/sp_validation/pseudo_cl.py index 5bb1b392..f52ab1d1 100644 --- a/src/sp_validation/pseudo_cl.py +++ b/src/sp_validation/pseudo_cl.py @@ -969,14 +969,10 @@ def get_pseudo_cls_catalog( # ---------------------- Covariance computation functions ---------------------- -def get_fiducial_cl(z, dndz, lmax, cosmo, backend="ccl"): +def get_fiducial_cl(z, dndz, lmax, cosmo, backend="camb"): """ Get the fiducial Cl's using the redshift distribution. - - Cosmology is determined by the input CCL cosmo object. The default CCL - backend evaluates that object as configured; cs_util's CAMB backend - rebuilds a CAMB cosmology from it, dropping the neutrino mass and using - CAMB's own non-linear model. + Cosmology is determined by the input cosmo object. """ ell = np.arange(1, lmax + 1) diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index 9f99a67c..cd01ebe6 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -829,30 +829,6 @@ def test_fiducial_pixel_window_applies_only_to_map(): npt.assert_array_equal(fiducial[key], cl) -def test_fiducial_cl_follows_the_configured_cosmology(): - """The iNKA fiducial is CCL's prediction for the configured cosmology. - - A non-default neutrino mass makes the check bite: cs_util's CAMB backend - drops it. - """ - import pyccl as ccl - from cs_util.cosmo import get_cosmo - - from sp_validation.pseudo_cl import get_fiducial_cl - - cosmo = get_cosmo(mnu=0.3) - z = np.linspace(0.01, 3.0, 200) - dndz = np.exp(-(((z - 0.7) / 0.3) ** 2)) - lmax = 128 - fiducial = get_fiducial_cl(z, dndz, lmax, cosmo)["W1xW1"] - - ell = np.arange(1, lmax + 1) - tracer = ccl.WeakLensingTracer(cosmo, dndz=(z, dndz)) - npt.assert_allclose( - fiducial, ccl.angular_cl(cosmo, tracer, tracer, ell), rtol=1e-10 - ) - - def test_calculate_pseudo_cl_catalog_end_to_end_tomo(cv, tmp_path): """End-to-end catalog path: FITS round-trip of ell + EE/EB/BB. From d6356c9629826b6974cc19a93de5bb7d9bdd2ec0 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 17:29:34 +0200 Subject: [PATCH 086/107] Tolerate NFS placeholders when the covariance rule clears its work directory The iNKA covariance was computed and moved into place, then the job failed removing its temporary directory: files still open leave .nfs placeholders. Also report the BB block's real shape (it printed 5x5 for a 32-bin block). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0138Efdjkma53ysbYhhXDgxW --- workflow/scripts/generate_pseudo_cl_cov.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/workflow/scripts/generate_pseudo_cl_cov.py b/workflow/scripts/generate_pseudo_cl_cov.py index 4f00f7f9..0364f1bf 100644 --- a/workflow/scripts/generate_pseudo_cl_cov.py +++ b/workflow/scripts/generate_pseudo_cl_cov.py @@ -25,6 +25,7 @@ """ import argparse +import gc import json import os import shutil @@ -141,9 +142,8 @@ def generate_pseudo_cl_cov( if os.path.exists(src_cov): with fits.open(src_cov) as hdul: # CV outputs covariance blocks as COVAR_XX_YY extensions - cov = hdul["COVAR_BB_BB"].data - n_ell = int(len(cov) ** 0.5) - print(f"Generated covariance matrix: {n_ell}x{n_ell}") + n_row, n_col = hdul["COVAR_BB_BB"].data.shape + print(f"Generated BB covariance block: {n_row}x{n_col}") return src_cov @@ -154,8 +154,12 @@ def _from_snakemake(smk): os.makedirs(out_dir, exist_ok=True) # The iNKA blocks and the merged covariance are cached under names that # carry the binning only, so the job computes in a directory of its own and - # never picks up a block another configuration left behind. - with tempfile.TemporaryDirectory(dir=out_dir, prefix=".pseudo_cl_cov_") as work: + # never picks up a block another configuration left behind. On NFS, files + # still held open at exit leave .nfs placeholders that block the removal, + # so a leftover work directory is tolerated rather than failing the job. + with tempfile.TemporaryDirectory( + dir=out_dir, prefix=".pseudo_cl_cov_", ignore_cleanup_errors=True + ) as work: src_cov = generate_pseudo_cl_cov( version=p["version"], output_dir=work, @@ -168,6 +172,7 @@ def _from_snakemake(smk): power=float(p.get("power", 0.5)), ) shutil.move(src_cov, output_cov) + gc.collect() print(f"Saved to: {output_cov}") From 454e802c8906ef557ebc050542a1bd1cb4e61564 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 18:53:04 +0200 Subject: [PATCH 087/107] plot_tau_stats: use the tau-statistics error bars when no covariance type is given MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A cov_type of None was formatted into the covariance file name (cov_tau_…_None.npy), so the workflow's tau plot failed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0138Efdjkma53ysbYhhXDgxW --- src/sp_validation/cosmo_val/psf_systematics.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sp_validation/cosmo_val/psf_systematics.py b/src/sp_validation/cosmo_val/psf_systematics.py index 7c0cc140..b189e622 100644 --- a/src/sp_validation/cosmo_val/psf_systematics.py +++ b/src/sp_validation/cosmo_val/psf_systematics.py @@ -945,9 +945,13 @@ def plot_tau_stats( else: filenames = [f"tau_stats_{self.basename(ver)}.fits" for ver in versions] - cov_paths = [ - f"cov_tau_{self.basename(ver)}_{cov_type}.npy" for ver in versions - ] + cov_paths = ( + None + if cov_type is None + else [ + f"cov_tau_{self.basename(ver)}_{cov_type}.npy" for ver in versions + ] + ) self.tau_stat_handler.plot_tau_stats( filenames, colors, From a769e10b702101c8f7cf519b515f4373e3ac56ed Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Sat, 3 Oct 2026 18:56:04 +0200 Subject: [PATCH 088/107] Plot tau statistics with the configured tau covariance The rho/tau rule writes the covariance of cov_estimate_method next to the tau statistics; the plot now draws its error bars from it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0138Efdjkma53ysbYhhXDgxW --- workflow/scripts/cv_plot_tau_stats.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/workflow/scripts/cv_plot_tau_stats.py b/workflow/scripts/cv_plot_tau_stats.py index 8975ed81..a2c6f365 100644 --- a/workflow/scripts/cv_plot_tau_stats.py +++ b/workflow/scripts/cv_plot_tau_stats.py @@ -1,6 +1,7 @@ """Rule cv_plot_tau_stats: tau-statistics overlay across versions. -Reads tau_stats_{base}.fits for every version (declared inputs); writes +Reads tau_stats_{base}.fits for every version (declared inputs), with error +bars from the version's tau covariance of the configured type; writes tau_stats.png into the leakage output dir. Sentinel-tracked (see rho_stats). """ @@ -8,5 +9,10 @@ _unbuffer_streams() cv = make_cv(snakemake) -cv.plot_tau_stats(tomography=False, savefig="tau_stats.png", show=False) +cv.plot_tau_stats( + tomography=False, + cov_type=cv.cov_estimate_method, + savefig="tau_stats.png", + show=False, +) touch_sentinels(snakemake) From 03bd3add23983da905a68ee64396eba31f2fee9b Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:24:47 +0200 Subject: [PATCH 089/107] Keep the joint pure-B Hartlap correction tied to its full data dimension Co-Authored-By: GPT-6.1 Sol --- ...est_tomo_joint_pure_b_hartlap_dimension.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_joint_pure_b_hartlap_dimension.py diff --git a/src/sp_validation/tests/regression/test_tomo_joint_pure_b_hartlap_dimension.py b/src/sp_validation/tests/regression/test_tomo_joint_pure_b_hartlap_dimension.py new file mode 100644 index 00000000..177e9837 --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_joint_pure_b_hartlap_dimension.py @@ -0,0 +1,39 @@ +"""Joint xi+ B / xi- B statistics must debias the full joint inverse covariance.""" + +from types import SimpleNamespace + +import numpy as np +import pytest +from scipy import stats + +from sp_validation.b_modes import calculate_eb_statistics + + +@pytest.mark.parametrize("start, stop", [(0, 4), (0, 3), (1, 3)]) +def test_joint_b_mode_chi2_hartlap_uses_joint_dimension_2n(start, stop): + """The combined B-mode PTE must apply Hartlap with p = 2n, not p = n. + + An inverse sample covariance from N patches over p entries requires + (N - p - 2)/(N - 1). For identity covariance the raw joint chi2 is the + sum of squared xi+ B and xi- B over the cut, so the PTE is known by hand. + Full and interior cuts check that p is twice the *selected* bin count. + Develop already uses p = 2n; this unmarked test protects that correct + behaviour against the tomography branch's single-block p = n regression. + """ + nbins, npatch = 4, 50 + results = { + # Both API contracts: develop reads theta/npatch; tomography reads gg. + "theta": np.geomspace(1.0, 100.0, nbins), + "npatch": npatch, + "gg": SimpleNamespace(nbins=nbins, npatch1=npatch, npatch2=npatch), + "cov": np.eye(6 * nbins), + "xip_B": np.linspace(0.5, 2.0, nbins), + "xim_B": np.linspace(-1.5, 1.0, nbins), + } + pte = calculate_eb_statistics(results)["pte_matrices"]["combined"] + n = stop - start + raw = np.sum(results["xip_B"][start:stop] ** 2) + np.sum( + results["xim_B"][start:stop] ** 2 + ) + expected = stats.chi2.sf((npatch - 2 * n - 2) / (npatch - 1) * raw, 2 * n) + assert pte[start, stop - 1] == pytest.approx(expected, rel=1e-10) From c38086835f999443046c5e0e9b3fc1d8db144170 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:24:47 +0200 Subject: [PATCH 090/107] Protect the pinned TreeCorr layout from node CPU-count changes Co-Authored-By: GPT-6.1 Sol --- .../regression/test_tomo_drops_min_top_pin.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_drops_min_top_pin.py diff --git a/src/sp_validation/tests/regression/test_tomo_drops_min_top_pin.py b/src/sp_validation/tests/regression/test_tomo_drops_min_top_pin.py new file mode 100644 index 00000000..09cea030 --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_drops_min_top_pin.py @@ -0,0 +1,101 @@ +"""ξ± from CosmologyValidation must not depend on the host's CPU count.""" + +import multiprocessing + +import numpy as np +import pytest +import treecorr +import yaml + +from sp_validation.cosmo_val import CosmologyValidation + + +@pytest.fixture(autouse=True) +def _restore_treecorr_threads(): + previous = treecorr.get_omp_threads() + try: + yield + finally: + treecorr.set_omp_threads(previous) + + +def _cosmology_validation(tmp_path): + """Minimal CosmologyValidation: only the constructor runs, no catalogue is read.""" + cfg = { + "paths": {"output": str(tmp_path / "out")}, + "nz": {"subdir": str(tmp_path), "dndz": {"path": "nz.txt"}}, + "synthetic": {"subdir": str(tmp_path), "shear": {"path": "cat.fits"}}, + } + cfg_path = tmp_path / "cat_config.yaml" + cfg_path.write_text(yaml.safe_dump(cfg)) + return CosmologyValidation( + ["synthetic"], + catalog_config=str(cfg_path), + npatch=8, + theta_min=15.0, + theta_max=70.0, + nbins=6, + ) + + +def _synthetic_catalog(**kwargs): + """4000 galaxies over ~4x4 deg with a spatially correlated shear field.""" + rng = np.random.default_rng(1234) + n = 4000 + ra = rng.uniform(150.0, 154.0, n) + dec = rng.uniform(0.0, 4.0, n) + g1 = 0.02 * np.sin(np.radians(ra) * 90) + 0.25 * rng.standard_normal(n) + g2 = 0.02 * np.cos(np.radians(dec) * 90) + 0.25 * rng.standard_normal(n) + return treecorr.Catalog( + ra=ra, dec=dec, g1=g1, g2=g2, ra_units="deg", dec_units="deg", **kwargs + ) + + +def _xi_on_node(cv, n_cpu, patch_centers, monkeypatch): + """ξ± as calculate_2pcf_version builds it (instance npatch, jackknife, fixed + patch centres, fresh Catalog), on a node with n_cpu CPUs.""" + monkeypatch.setattr(multiprocessing, "cpu_count", lambda: n_cpu) + # Simulate the thread count seen by Field's default min_top calculation, + # without actually launching 48 threads inside a two-core test allocation. + # Leave min_top itself untouched: the missing pin must remain observable. + monkeypatch.setattr(treecorr.field, "get_omp_threads", multiprocessing.cpu_count) + config = {**cv._binning(), "var_method": "jackknife"} + gg = treecorr.GGCorrelation(config) + gg.process(_synthetic_catalog(patch_centers=patch_centers), num_threads=2) + return gg + + +def test_xi_pm_identical_on_16_and_48_cpu_nodes(tmp_path, monkeypatch): + """Protects the machine independence of ξ±. TreeCorr picks the depth of its root + cells (min_top) as max(3, ceil(log2 n_threads)) when the config leaves it unset, + and the thread count defaults to the node's CPU count; min_top decides which pairs + bin_slop approximates, so ξ± changes between a 16-CPU node + (min_top=4) and a 48-CPU node (min_top=6) once the catalogue is split into jackknife + patches. The same catalogue, patch centres and CosmologyValidation settings must + give the same ξ± on either node, to far below the jackknife σ (1e-6σ), which holds + once the config pins min_top (and num_threads). This passes on develop, + whose pin must survive the tomography merge. Only Field's view of the host + thread count is simulated; the actual correlation runs on two cores. + """ + cv = _cosmology_validation(tmp_path) + patch_file = str(tmp_path / "patches.dat") + treecorr.set_omp_threads(2) + _synthetic_catalog( + npatch=cv.npatch, rng=np.random.default_rng(99) + ).write_patch_centers(patch_file) + gg16 = _xi_on_node(cv, 16, patch_file, monkeypatch) + gg48 = _xi_on_node(cv, 48, patch_file, monkeypatch) + sigma = np.sqrt(np.concatenate([gg48.varxip, gg48.varxim])) + shift = ( + np.abs( + np.concatenate([gg16.xip, gg16.xim]) - np.concatenate([gg48.xip, gg48.xim]) + ) + / sigma + ) + + assert shift.max() < 1e-6, ( + f"ξ± moves by {shift.max():.3g}σ between a 16- and a 48-CPU node " + f"(xip {shift[: cv.nbins].max():.3g}σ, xim {shift[cv.nbins :].max():.3g}σ); " + f"treecorr_config min_top={cv.treecorr_config.get('min_top')}, " + f"num_threads={cv.treecorr_config.get('num_threads')}" + ) From 5e5a640bac6e2512486132aa52b2858dc5a9e3c2 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:24:48 +0200 Subject: [PATCH 091/107] Require pure-E/B MC rebinning to use the data pair weights Co-Authored-By: GPT-6.1 Sol --- ...st_tomo_mc_covariance_uniform_rebinning.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_mc_covariance_uniform_rebinning.py diff --git a/src/sp_validation/tests/regression/test_tomo_mc_covariance_uniform_rebinning.py b/src/sp_validation/tests/regression/test_tomo_mc_covariance_uniform_rebinning.py new file mode 100644 index 00000000..aafed418 --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_mc_covariance_uniform_rebinning.py @@ -0,0 +1,105 @@ +"""Pure-E/B MC covariance must describe the data's pair-weighted xi estimator.""" + +import inspect +import sys +import types +from types import SimpleNamespace + +import numpy as np +import pytest + +from sp_validation import b_modes as bm + +# Reporting bins are exact unions of eight fine log bins, so no partial-bin +# interpolation enters the independently constructed pair-weighted reference. +EDGES_INT = np.geomspace(1.0, 100.0, 41) +THETA_INT = np.sqrt(EDGES_INT[:-1] * EDGES_INT[1:]) +REPORT_IDX = np.array([4, 12, 20, 28, 36]) +EDGES_REP = EDGES_INT[REPORT_IDX] +WEIGHT_INT = THETA_INT**2 # annular area, hence pair counts, grows as theta^2 + + +def _pair_weighted_binning(): + matrix = np.zeros((len(EDGES_REP) - 1, len(THETA_INT))) + for i, (lo, hi) in enumerate(zip(REPORT_IDX[:-1], REPORT_IDX[1:])): + matrix[i, lo:hi] = WEIGHT_INT[lo:hi] / WEIGHT_INT[lo:hi].sum() + return matrix + + +def test_mc_reporting_xi_is_pair_weighted_mean_of_fine_bins(monkeypatch, tmp_path): + """Each MC reporting xi must be sum_j w_j xi_j / sum_j w_j, like TreeCorr. + + Reporting-bin xi feeds the local term of the pure-E/B transform. A uniform + average of fine bins would give a covariance for a different estimator + from the pair-weighted data. Exact unions of fine bins make the reference + independent of the package's binning implementation. + Develop's explicit operator already uses pair weights, so this test is + unmarked: it protects against the tomography branch's uniform MC rebinning. + The transform and theory stubs only expose its inputs, not fix the binning. + """ + reference = _pair_weighted_binning() + rng = np.random.default_rng(0) + if "cov_path_int" in inspect.signature(bm.calculate_pure_eb_correlation).parameters: + # Tomography API: observe reporting and fine xi passed to the transform. + n_int = len(THETA_INT) + gg = SimpleNamespace( + meanr=np.sqrt(EDGES_REP[:-1] * EDGES_REP[1:]), + left_edges=EDGES_REP[:-1], + right_edges=EDGES_REP[1:], + xip=np.zeros(4), + xim=np.zeros(4), + ) + gg_int = SimpleNamespace( + meanr=THETA_INT, + left_edges=EDGES_INT[:-1], + right_edges=EDGES_INT[1:], + xip=np.zeros(n_int), + xim=np.zeros(n_int), + weight=WEIGHT_INT, + ) + cov_path = tmp_path / "cov_int.txt" + np.savetxt(cov_path, np.eye(2 * n_int)) + calls = [] + + def observe_pure_eb(theta, xip, xim, theta_int, xip_int, xim_int, **kwargs): + calls.append((np.array([xip, xim]), np.array([xip_int, xim_int]))) + return tuple(np.zeros(len(theta)) for _ in range(6)) + + # Replace only the transform dependency, avoiding its eager JIT import. + # The package's actual MC sampling and rebinning still run unchanged. + module = types.ModuleType("cosmo_numba.B_modes.schneider2022") + module.get_pure_EB_modes = observe_pure_eb + monkeypatch.setitem(sys.modules, "cosmo_numba.B_modes.schneider2022", module) + monkeypatch.setattr( + bm, "get_theo_xi", lambda **kwargs: (np.zeros(n_int), np.zeros(n_int)) + ) + # Keep the package's MC sampling but avoid changing global random state. + monkeypatch.setattr(np.random, "multivariate_normal", rng.multivariate_normal) + with pytest.warns(UserWarning, match="covariance matrix is not positive"): + bm.calculate_pure_eb_correlation( + gg, + gg_int, + cov_path_int=str(cov_path), + cosmo_cov=object(), + n_samples=3, + z_dist=np.ones((5, 2)), + ) + mc = calls[1:] # First call transforms data, not an MC sample. + assert len(mc) == 3 + got = np.array([reporting for reporting, fine in mc]) + want = np.array([fine @ reference.T for reporting, fine in mc]) + else: + # Develop API: rebinning is the explicit linear operator. + matrix, edges = bm._reporting_binning(WEIGHT_INT, EDGES_INT, EDGES_REP) + np.testing.assert_allclose(edges, EDGES_REP) + fine = rng.standard_normal((3, len(THETA_INT))) + got = fine @ matrix.T + want = fine @ reference.T + + np.testing.assert_allclose( + got, + want, + rtol=0, + atol=1e-12, + err_msg="MC reporting xi is not the pair-weighted mean of fine bins", + ) From d1a1c9412178a94a363a4b4d06023b9a30787574 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:17 +0200 Subject: [PATCH 092/107] Keep covariance jobs inside the launched checkout and results tree Co-Authored-By: GPT-6.1 Sol --- ...covariance_rules_call_external_checkout.py | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_covariance_rules_call_external_checkout.py diff --git a/src/sp_validation/tests/regression/test_tomo_covariance_rules_call_external_checkout.py b/src/sp_validation/tests/regression/test_tomo_covariance_rules_call_external_checkout.py new file mode 100644 index 00000000..c5079874 --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_covariance_rules_call_external_checkout.py @@ -0,0 +1,239 @@ +"""Exercise real covariance rules without catalogues or CosmoCov. + +These tests pass on develop: it uses checkout-local scripts and launch-relative +outputs. They guard against the external paths introduced on the tomography +branch. Snakemake >=8 is optional; all generated workflow files stay in tmp_path. +""" + +import json +import os +import shlex +import subprocess +import sys +from pathlib import Path +from types import SimpleNamespace + +import numpy as np +import pytest + +import sp_validation + + +def _run_snakemake(case, *args): + env = dict(os.environ) + env.update( + PYTHONDONTWRITEBYTECODE="1", + XDG_CACHE_HOME=str(case.directory / "cache"), + MPLCONFIGDIR=str(case.directory / "mplconfig"), + TMPDIR=str(case.directory / "tmp"), + # Preserve the dependency location if pytest loaded Snakemake via its + # pythonpath setting rather than the interpreter's site-packages. + PYTHONPATH=str(case.snakemake_root) + os.pathsep + env.get("PYTHONPATH", ""), + ) + return subprocess.run( + [ + sys.executable, + "-m", + "snakemake", + "--snakefile", + str(case.snakefile), + "--cores", + "1", + "--profile", + "none", + "--workflow-profile", + "none", + "--nolock", + "--scheduler", + "greedy", + *args, + ], + cwd=case.directory, + env=env, + text=True, + capture_output=True, + timeout=60, + ) + + +@pytest.fixture +def covariance_rules(tmp_path): + snakemake = pytest.importorskip( + "snakemake", + minversion="8", + reason="Real covariance rules require Snakemake >=8", + ) + # Use the imported package's __file__, not cwd or a hard-coded checkout. + # This also lets PYTHONPATH select tomography while keeping this test file. + root = Path(sp_validation.__file__).resolve().parents[2] + source = root / "workflow/rules/covariance.smk" + if not source.is_file(): + pytest.skip("Checkout does not contain workflow/rules/covariance.smk") + (tmp_path / "tmp").mkdir() + snakefile = tmp_path / "Snakefile" + case = SimpleNamespace( + root=root, + directory=tmp_path, + snakefile=snakefile, + snakemake_root=Path(snakemake.__file__).resolve().parents[1], + ) + # Only globals normally supplied by the workflow are synthetic. Snakemake + # parses the unmodified rules, resolves entry points and executes the job. + snakefile.write_text( + f""" +from pathlib import Path +from snakemake.io import apply_wildcards +import json +COSMO_INFERENCE = Path({str(tmp_path / "cosmo_inference")!r}) +COSMO_VAL = Path({str(tmp_path / "cosmo_val")!r}) +COSMOLOGY_PARAMS = str(COSMO_INFERENCE / "cosmology.json") +GLASS_MOCK_DIR = str(Path({str(tmp_path / "glass_mock")!r})) +GLASS_MOCK_SUITE = "glass_mock_v1.4.6" +PLANCK18 = dict.fromkeys( + ["Omega_m", "Omega_v", "sigma_8", "n_s", "h", "Omega_b"], 0.0 +) +BLOCK_PAIRS = [] +DEFAULT_MASK_SUFFIX = "" +FIDUCIAL = dict.fromkeys([ + "mock_version", "version", "blind", "min_sep", "max_sep", "nbins", + "min_sep_int", "max_sep_int", "nbins_int", +], "synthetic") +config = {{ + "tools": {{"cosmocov_executable": "unused"}}, + "glass_mocks": {{"seed_range": [1, 1]}}, +}} +def fiducial_binning_suffix(): + return "_synthetic" +def covariance_path(*args, **kwargs): + return "unused-fiducial-input" +def covariance_dir(*args, **kwargs): + return "unused-covariance-directory" +def redshift_path(*args, **kwargs): + return "unused-redshift-input" +def build_redshift_path(*args, **kwargs): + return "unused-redshift-input" +def cv_cov_tau(*args, **kwargs): + return "unused-tau-covariance" +def cv_tau_stats(*args, **kwargs): + return "unused-tau-statistics" +wildcard_constraints: + mask_suffix="(?:_masked)?" +include: {str(source)!r} +manifest = {{}} +for name in [ + "covariance_process", "generate_glass_mock_rhotau_samples", + "covariance_glass_mock", +]: + registered = workflow.get_rule(name) + manifest[name] = {{ + "script": registered.script, "shellcmd": registered.shellcmd, + "basedir": str(registered.basedir), + "output": list(map(str, registered.output)), + }} +synthetic_wildcards = dict( + version="synthetic", blind="A", gaussian="ng", min_sep="1", max_sep="2", + nbins="1", mask_suffix="", +) +manifest["covariance_process"]["synthetic_matrix"] = apply_wildcards( + workflow.get_rule("covariance_process").output[0], synthetic_wildcards +) +Path("rules.json").write_text(json.dumps(manifest, indent=2)) +""" + ) + # Upper-triangle dump: columns 8 and 9 contain Gaussian and non-Gaussian + # terms. Their sum is the positive-definite matrix [[3, .25], [.25, 6]]. + raw = np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 2, 1], + [0, 1, 0, 0, 0, 0, 0, 0, 0.25, 0], + [1, 1, 0, 0, 0, 0, 0, 0, 4, 2], + ] + ) + parsed = _run_snakemake(case, "--list-rules") + assert parsed.returncode == 0, parsed.stdout + parsed.stderr + case.manifest = json.loads((tmp_path / "rules.json").read_text()) + case.matrix = Path(case.manifest["covariance_process"]["synthetic_matrix"]) + raw_path = Path(str(case.matrix).replace("_processed.txt", ".txt")) + raw_path.parent.mkdir(parents=True) + np.savetxt(raw_path, raw) + return case + + +@pytest.mark.slow +def test_covariance_postprocessing_runs_from_launched_checkout(covariance_rules): + """Process a synthetic CosmoCov dump without relying on another checkout. + + The three input rows give G+NG = [[3, .25], [.25, 6]], reflecting the supplied + off-diagonal element, and G = [[2, .25], [.25, 4]]. Snakemake executes the + actual rule with existing raw input, so no survey data or expensive + covariance calculation is needed. A missing external entry point is a + workflow defect. Develop passes because its processor is checkout-local; + this guards against the tomography merge reintroducing an external script. + """ + case = covariance_rules + result = _run_snakemake(case, "--printshellcmds", str(case.matrix)) + log = result.stdout + result.stderr + diagnosis = next( + (line for line in log.splitlines() if "can't open file" in line), log[-2500:] + ) + assert result.returncode == 0, ( + f"covariance_process returned {result.returncode}; expected exit 0 and " + f"matrix [[3.0, 0.25], [0.25, 6.0]]; {diagnosis}" + ) + np.testing.assert_allclose( + np.loadtxt(case.matrix), [[3, 0.25], [0.25, 6]], rtol=0, atol=0 + ) + gaussian = Path(str(case.matrix).replace("_processed.txt", "_processed_g.txt")) + np.testing.assert_allclose( + np.loadtxt(gaussian), [[2, 0.25], [0.25, 4]], rtol=0, atol=0 + ) + + +def test_tau_sampling_selects_script_in_launched_checkout(covariance_rules): + """The real tau-sampling rule must select code from the launched checkout. + + Identical script contents today cannot guarantee this: edits to an unrelated + checkout must not change a fixed commit's mock samples. Inspect Snakemake's + registered entry point instead of inferring provenance from random values. + Develop passes with its local script directive; the tomography merge must + not restore an external entry point. No survey inputs are read. + """ + case = covariance_rules + rule = case.manifest["generate_glass_mock_rhotau_samples"] + if rule["script"]: + selected = (Path(rule["basedir"]) / rule["script"]).resolve() + else: + tokens = shlex.split(rule["shellcmd"]) + assert tokens[0] == "python", f"Unexpected tau command: {rule['shellcmd']}" + selected = (case.directory / tokens[1]).resolve() + expected = ( + case.root / "workflow/scripts/generate_glass_mock_rhotau_samples.py" + ).resolve() + assert selected == expected, ( + f"Tau sampling selects {selected}; expected launched-checkout script {expected}" + ) + assert selected.is_file(), f"Selected tau script is missing: {selected}" + + +def test_glass_covariance_outputs_stay_in_launch_directory(covariance_rules): + """All seven GLASS outputs must live in the launch directory's results tree. + + Resolve the real rule's output objects relative to the synthetic launch + directory, the correct base for portable products and independent launches. + No GLASS catalogues are read or external products written. Develop passes + with relative outputs; this guards against the tomography merge restoring + absolute paths that could overwrite another analysis. + """ + case = covariance_rules + expected_dir = case.directory / "results/covariance/glass_mock_v1.4.6" + outputs = case.manifest["covariance_glass_mock"]["output"] + assert len(outputs) == 7 + wrong = [] + for output in outputs: + actual = (case.directory / output).resolve() + if actual.parent != expected_dir: + wrong.append(str(actual)) + assert not wrong, ( + f"GLASS covariance outputs escape launch directory: {wrong}; " + f"expected directory {expected_dir}" + ) From bdfed2ba153b09d16f7b96d2c6de7d9b6007709c Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:18 +0200 Subject: [PATCH 093/107] Keep the rho/tau driver on the launched catalogue configuration Co-Authored-By: GPT-6.1 Sol --- ...t_tomo_rho_tau_driver_external_checkout.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_rho_tau_driver_external_checkout.py diff --git a/src/sp_validation/tests/regression/test_tomo_rho_tau_driver_external_checkout.py b/src/sp_validation/tests/regression/test_tomo_rho_tau_driver_external_checkout.py new file mode 100644 index 00000000..8343238f --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_rho_tau_driver_external_checkout.py @@ -0,0 +1,101 @@ +"""The rho_tau_stats driver must use the checkout that launched the workflow.""" + +import io +import os +import runpy +import sys +import types +from pathlib import Path + +import pytest + +import sp_validation +import sp_validation.cosmo_val as cosmo_val_mod + + +class _Stop(Exception): + """Stop before any catalogue reads or output writes.""" + + +def test_rho_tau_driver_reads_cat_config_from_launched_checkout(monkeypatch, tmp_path): + """Resolve the catalogue config and output inside the launched checkout. + + The constructor stub records cwd and arguments, then stops before reading + survey data. Defaults './cat_config.yaml' and './output' resolve from cwd. + The expected checkout contains the imported sp_validation code; ordinarily + that is the repository containing this test, but a branch comparison can + import a different checkout while retaining this test file. + + Develop passes explicit paths from rule params and does not chdir, so this + test is unmarked: it guards against the tomography branch's regression of + changing cwd to an unrelated, hard-coded checkout and using defaults. + """ + checkout = Path(__file__).resolve().parents[4] + code_checkout = Path(sp_validation.__file__).resolve().parents[2] + if code_checkout != checkout: + checkout = code_checkout + driver = checkout / "workflow" / "scripts" / "run_rho_tau.py" + if not driver.is_file(): + pytest.skip("rho/tau workflow driver from the tomography branch is absent") + + seen = {} + + def fake_cv(*args, **kwargs): + seen["cwd"] = Path.cwd() + seen["kwargs"] = kwargs + raise _Stop + + monkeypatch.setattr(cosmo_val_mod, "CosmologyValidation", fake_cv) + fake_snk = types.SimpleNamespace( + params={ + "ver": "SP_v1.4.6.3", + "min_sep": "1.0", + "max_sep": "250.0", + "nbins": "20", + "npatch": "20", + "cat_config": str(checkout / "cosmo_val" / "cat_config.yaml"), + "output_dir": str(checkout / "cosmo_val" / "output"), + }, + output={}, + input={}, + ) + snk_script = types.ModuleType("snakemake.script") + snk_script.snakemake = fake_snk + monkeypatch.setitem(sys.modules, "snakemake.script", snk_script) + try: + import IPython + except ImportError: + pytest.skip("tomography branch driver requires IPython") + monkeypatch.setattr(IPython, "get_ipython", lambda: None) + # Stream unbuffering is unrelated to path resolution. Avoid closing pytest's + # captured descriptors when either driver generation calls os.fdopen. + monkeypatch.setattr(os, "fdopen", lambda *args, **kwargs: io.StringIO()) + # Register the original streams for restoration after the driver's assignment. + monkeypatch.setattr(sys, "stdout", sys.stdout) + monkeypatch.setattr(sys, "stderr", sys.stderr) + monkeypatch.syspath_prepend(str(driver.parent)) + monkeypatch.chdir(tmp_path) + + try: + with pytest.raises(_Stop): + runpy.run_path(str(driver), init_globals={"snakemake": fake_snk}) + except FileNotFoundError as exc: + # The tomography driver may chdir to a survey checkout absent on CI. + missing = Path(exc.filename) if exc.filename else None + if ( + missing + and missing.is_absolute() + and missing.parts[1] in {"n17data", "n09data", "n23data1", "home"} + ): + pytest.skip( + f"tomography branch external checkout is unavailable: {missing}" + ) + raise + + kw = seen["kwargs"] + cat_cfg = (seen["cwd"] / kw.get("catalog_config", "./cat_config.yaml")).resolve() + out_dir = (seen["cwd"] / (kw.get("output_dir") or "./output")).resolve() + assert cat_cfg.is_relative_to(checkout) and out_dir.is_relative_to(checkout), ( + f"driver used catalog_config={cat_cfg}, output_dir={out_dir}, " + f"cwd={seen['cwd']}; expected paths inside launched checkout {checkout}" + ) From b0a1652b389e4205bdaefdbfd52e7440226010e1 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:18 +0200 Subject: [PATCH 094/107] Require both configured HSM flags to exclude failed star fits Co-Authored-By: GPT-6.1 Sol --- .../test_rho_tau_ignores_hsm_flags.py | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_rho_tau_ignores_hsm_flags.py diff --git a/src/sp_validation/tests/regression/test_rho_tau_ignores_hsm_flags.py b/src/sp_validation/tests/regression/test_rho_tau_ignores_hsm_flags.py new file mode 100644 index 00000000..4562c81a --- /dev/null +++ b/src/sp_validation/tests/regression/test_rho_tau_ignores_hsm_flags.py @@ -0,0 +1,166 @@ +"""Rho/tau must exclude stars whose configured HSM PSF or star fit failed. + +Clean stars are perfectly modelled, making rho_1 and tau_2 zero by construction. +""" + +import inspect + +import numpy as np +from astropy.io import fits + +from sp_validation.cosmo_val.psf_systematics import PSFSystematicsMixin + +VERSION = "SP_synthetic" +N_GOOD = 2000 +N_FLAGGED = 100 +N_GAL = 3000 + + +def _write_catalogues(tmp_path): + rng = np.random.default_rng(1) + n = N_GOOD + N_FLAGGED + ra = rng.uniform(10.0, 12.0, n) + dec = rng.uniform(0.0, 2.0, n) + e1_psf = rng.normal(0.0, 0.02, n) + e2_psf = rng.normal(0.0, 0.02, n) + t_psf = rng.uniform(0.4, 0.6, n) + + # Good stars: q = e_star - e_PSF = 0 and T_star = T_PSF exactly. + e1_star = e1_psf.copy() + e2_star = e2_psf.copy() + t_star = t_psf.copy() + flag_psf = np.zeros(n, dtype=np.int16) + flag_star = np.zeros(n, dtype=np.int16) + + # Failed fits contribute q = (-0.05, 0) if either flag is ignored. + bad = slice(N_GOOD, n) + e1_psf[bad] = 0.05 + e2_psf[bad] = 0.0 + e1_star[bad] = 0.0 + e2_star[bad] = 0.0 + t_star[bad] = 0.069 + flag_star[N_GOOD : N_GOOD + N_FLAGGED // 2] = 1 + flag_psf[N_GOOD + N_FLAGGED // 2 : n] = 1 + + psf_path = tmp_path / "psf.fits" + fits.BinTableHDU.from_columns( + [ + fits.Column("RA", "D", array=ra), + fits.Column("DEC", "D", array=dec), + fits.Column("HSM_G1_PSF", "D", array=e1_psf), + fits.Column("HSM_G2_PSF", "D", array=e2_psf), + fits.Column("HSM_G1_STAR", "D", array=e1_star), + fits.Column("HSM_G2_STAR", "D", array=e2_star), + fits.Column("HSM_T_PSF", "D", array=t_psf), + fits.Column("HSM_T_STAR", "D", array=t_star), + fits.Column("HSM_FLAG_PSF", "I", array=flag_psf), + fits.Column("HSM_FLAG_STAR", "I", array=flag_star), + ] + ).writeto(psf_path) + + shear_path = tmp_path / "shear.fits" + fits.BinTableHDU.from_columns( + [ + fits.Column("RA", "D", array=rng.uniform(10.0, 12.0, N_GAL)), + fits.Column("Dec", "D", array=rng.uniform(0.0, 2.0, N_GAL)), + fits.Column("e1", "D", array=rng.normal(0.0, 0.3, N_GAL)), + fits.Column("e2", "D", array=rng.normal(0.0, 0.3, N_GAL)), + fits.Column("w_des", "D", array=np.ones(N_GAL)), + ] + ).writeto(shear_path) + return psf_path, shear_path + + +class _Validation(PSFSystematicsMixin): + """Minimal host carrying the configuration read by the real entry point.""" + + def __init__(self, cc): + self.cc = cc + self.versions = [VERSION] + self.treecorr_config = { + "ra_units": "deg", + "dec_units": "deg", + "sep_units": "arcmin", + "min_sep": 2.0, + "max_sep": 100.0, + "nbins": 5, + "num_threads": 2, + } + self.cov_estimate_method = "sim" + self.compute_cov_rho = False + self.npatch = 4 + + def basename(self, version, tomo_bin_a="all", **kwargs): + return f"{version}_{tomo_bin_a}" + + def _get_galaxy_mask(self, ver, tomo_bin_id): + return np.ones(N_GAL, dtype=bool) + + def rho_tau_to_sacc_part(self, *args, **kwargs): + pass + + def print_start(self, *args, **kwargs): + pass + + print_done = print_cyan = print_magenta = print_start + + +def test_flagged_stars_excluded_from_rho_1_and_tau_2(tmp_path): + """Both configured HSM flags must exclude failed fits from rho/tau. + + Every unflagged star has e_star == e_PSF exactly, so q is identically zero: + rho_1 = and tau_2 = must be zero in every bin. + Half of the bad stars have only HSM_FLAG_STAR set, and half have only + HSM_FLAG_PSF set; their q = (-0.05, 0) catches ignoring either flag. + This merges the two audit reproductions and exercises the pipeline's + calculate_rho_tau_stats entry point, not a separately masked reference. + """ + psf_path, shear_path = _write_catalogues(tmp_path) + out = tmp_path / "out" + out.mkdir() + cc = { + "paths": {"output": str(out)}, + VERSION: { + "patch_number": 4, + "cov_th": {"A": 4.0, "n_e": 1.0, "n_psf": 1.0, "sigma_e": 0.3}, + "psf": { + "path": str(psf_path), + "hdu": 1, + "ra_col": "RA", + "dec_col": "DEC", + "e1_PSF_col": "HSM_G1_PSF", + "e2_PSF_col": "HSM_G2_PSF", + "e1_star_col": "HSM_G1_STAR", + "e2_star_col": "HSM_G2_STAR", + "PSF_size": "HSM_T_PSF", + "star_size": "HSM_T_STAR", + "PSF_flag": "HSM_FLAG_PSF", + "star_flag": "HSM_FLAG_STAR", + }, + "shear": { + "path": str(shear_path), + "ra_col": "RA", + "dec_col": "Dec", + "w_col": "w_des", + "e1_col": "e1", + "e2_col": "e2", + }, + }, + } + rho_tau_dir = out / "rho_tau_stats" + rho_tau_dir.mkdir() + # The sim method only needs an existing covariance; don't run an estimator. + np.save(rho_tau_dir / f"cov_tau_{VERSION}_all_th.npy", np.eye(3)) + + val = _Validation(cc) + kwargs = ( + {"tomography": False} + if "tomography" in inspect.signature(val.calculate_rho_tau_stats).parameters + else {} + ) + val.calculate_rho_tau_stats(**kwargs) + + rho_1 = np.asarray(val._rho_stat_handler.rho_stats["rho_1_p"]) + tau_2 = np.asarray(val._tau_stat_handler.tau_stats["tau_2_p"]) + np.testing.assert_allclose(rho_1, 0, atol=1e-12, rtol=0) + np.testing.assert_allclose(tau_2, 0, atol=1e-12, rtol=0) From eda87f8f2f8f2770fbfd8580554bc0ad08d58bc5 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:19 +0200 Subject: [PATCH 095/107] Require PSF fit sample widths to follow the selected covariance Co-Authored-By: GPT-6.1 Sol --- .../test_lsq_sample_cache_ignores_cov_type.py | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_lsq_sample_cache_ignores_cov_type.py diff --git a/src/sp_validation/tests/regression/test_lsq_sample_cache_ignores_cov_type.py b/src/sp_validation/tests/regression/test_lsq_sample_cache_ignores_cov_type.py new file mode 100644 index 00000000..88e778df --- /dev/null +++ b/src/sp_validation/tests/regression/test_lsq_sample_cache_ignores_cov_type.py @@ -0,0 +1,80 @@ +"""Least-squares PSF-leakage samples must follow the requested tau covariance.""" + +import inspect + +import numpy as np +import pytest +from astropy.table import Table +from shear_psf_leakage.rho_tau_stat import PSFErrorFit, RhoStat, TauStat + +from sp_validation.rho_tau import get_samples_lsq + +CFG = dict(min_sep=1.0, max_sep=30.0, nbins=1, sep_units="arcmin") +SIGMA_TH = 1e-3 +SIGMA_JK = 1e-1 + + +def _write_fixture(out): + # One theta bin: rho_0 = rho_1 = rho_3 = 1, other rho and all tau = 0. + # Each of alpha/beta/eta has independent unit response to a tau component. + rd = {"theta": [10.0]} + for i in range(6): + rd[f"rho_{i}_p"] = [1.0 if i in (0, 1, 3) else 0.0] + td = {"theta": [10.0]} + for i in (0, 2, 5): + td[f"tau_{i}_p"] = [0.0] + Table(rd).write(out / "rho_stats_fit.fits") + Table(td).write(out / "tau_stats_fit.fits") + np.save(out / "cov_tau_fit_th.npy", np.eye(3) * SIGMA_TH**2) + np.save(out / "cov_tau_fit_jk.npy", np.eye(3) * SIGMA_JK**2) + np.save(out / "cov_rho_fit_jk.npy", np.zeros((6, 6))) + + +def _run(fitter, cov_type): + np.random.seed(23) + if "base_tau" in inspect.signature(get_samples_lsq).parameters: + # Tomography branch takes separate rho/tau basenames and a draw count. + return get_samples_lsq(fitter, "fit", "fit", cov_type=cov_type, nsamples=256) + return get_samples_lsq(fitter, "test", "fit", cov_type=cov_type) + + +@pytest.fixture +def restore_random_state(): + state = np.random.get_state() + yield + np.random.set_state(state) + + +def test_lsq_posterior_width_follows_cov_type_not_cached_th( + tmp_path, monkeypatch, restore_random_state +): + """A jk request after a th request must use jk covariance, not cached th draws. + + This one-bin unit-response model has tau = 0 and diagonal covariance + sigma_tau^2 I, so each parameter's posterior std is sigma_tau by + construction: 1e-3 for th and 1e-1 for jk. Using the same output directory + must not let a basename-only cache silently retain the previous method's + alpha/beta/eta errors and xi_psf_sys uncertainty bands. + """ + _write_fixture(tmp_path) + rho = RhoStat(output=str(tmp_path), treecorr_config=CFG) + tau = TauStat(output=str(tmp_path), treecorr_config=CFG) + fitter = PSFErrorFit(rho, tau, str(tmp_path)) + original = fitter.get_least_squares_params_samples + + def small_sample(**kwargs): + # Only reduce Monte Carlo work; leave loading and cache logic untouched. + kwargs.update(n_samples=256, verbose=False) + return original(**kwargs) + + monkeypatch.setattr(fitter, "get_least_squares_params_samples", small_sample) + s_th, _, _ = _run(fitter, "th") + std_th = s_th.std(axis=0) + assert np.allclose(std_th, SIGMA_TH, rtol=0.3), std_th + + s_jk, _, _ = _run(fitter, "jk") + std_jk = s_jk.std(axis=0) + assert np.allclose(std_jk, SIGMA_JK, rtol=0.3), ( + f"jk after th returned posterior std {std_jk}; expected ~{SIGMA_JK}, " + f"not cached th width ~{SIGMA_TH}" + ) From dd9e4676e14ef9a5481b4b1383c2802d6fc60ce9 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:19 +0200 Subject: [PATCH 096/107] Check pure-E/B statistics through the bin-keyed result API Co-Authored-By: GPT-6.1 Sol --- .../test_tomo_pure_eb_mapping_keyerror.py | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_pure_eb_mapping_keyerror.py diff --git a/src/sp_validation/tests/regression/test_tomo_pure_eb_mapping_keyerror.py b/src/sp_validation/tests/regression/test_tomo_pure_eb_mapping_keyerror.py new file mode 100644 index 00000000..2e6ce4bc --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_pure_eb_mapping_keyerror.py @@ -0,0 +1,127 @@ +"""plot_pure_eb must accept the bin-keyed results from calculate_pure_eb.""" + +import inspect + +import numpy as np +import pytest +import treecorr + +pure_eb = pytest.importorskip( + "sp_validation.cosmo_val.pure_eb", + reason="PureEBMixin is required for the tomography branch's bin-keyed API", +) +if not hasattr(pure_eb, "PureEBMixin"): + pytest.skip( + "PureEBMixin is missing (tomography branch API)", allow_module_level=True + ) +PureEBMixin = pure_eb.PureEBMixin +BIN_KEY = "tomo_bin_all_tomo_bin_all" + + +def _synthetic_gg(min_sep, max_sep, nbins, npatch): + rng = np.random.default_rng(1) + n = 8000 + cat = treecorr.Catalog( + ra=rng.uniform(150.0, 152.0, n), + dec=rng.uniform(1.0, 3.0, n), + g1=rng.normal(0, 0.2, n), + g2=rng.normal(0, 0.2, n), + ra_units="deg", + dec_units="deg", + npatch=npatch, + rng=rng, + ) + gg = treecorr.GGCorrelation( + min_sep=min_sep, + max_sep=max_sep, + nbins=nbins, + var_method="jackknife", + sep_units="arcmin", + bin_slop=0.0, + ) + gg.process(cat, num_threads=2) + return gg + + +class _TomoCV(PureEBMixin): + """Minimal host supplying the tomography branch's documented 2PCF mapping.""" + + versions = ["v"] + npatch = 32 + treecorr_config = {"min_sep": 2.0, "max_sep": 60.0, "nbins": 3} + + def __init__(self, output): + self.cc = {"paths": {"output": str(output)}} + self._pure_eb_results = {} + + def print_start(self, *args, **kwargs): + pass + + def _binning(self, min_sep=None, max_sep=None, nbins=None): + return { + "min_sep": min_sep or self.treecorr_config["min_sep"], + "max_sep": max_sep or self.treecorr_config["max_sep"], + "nbins": nbins or self.treecorr_config["nbins"], + } + + def calculate_2pcf_version(self, version, npatch, compute_tomography, **config): + return { + BIN_KEY: _synthetic_gg( + config["min_sep"], config["max_sep"], config["nbins"], npatch + ) + } + + +@pytest.mark.slow +def test_plot_pure_eb_passes_one_bin_result_to_eb_statistics(tmp_path, monkeypatch): + """The statistics step needs a per-bin result, not the outer bin mapping. + + On the tomography branch calculate_pure_eb returns {bin_key: result}, even + for the single (all, all) bin. plot_pure_eb must pass that inner result + (carrying gg, cov and xip_B) to calculate_eb_statistics. A finite combined + B-mode PTE is the expected outcome of this random-shear TreeCorr fixture. + The real calculation and statistics run; only the 2PCF source and figure + writers are substituted. Develop's single-result API is unaffected and + skips because it lacks the tomography branch's compute_tomography argument. + The real transform's eager JIT and jackknife resampling take over 20 seconds. + """ + if ( + "compute_tomography" + not in inspect.signature(PureEBMixin.calculate_pure_eb).parameters + ): + pytest.skip("Bin-keyed calculate_pure_eb API exists only on tomography branch") + + for name in ( + "plot_integration_vs_reporting", + "plot_pure_eb_correlations", + "plot_pte_2d_heatmaps", + "plot_eb_covariance_matrix", + "save_pure_eb_results", + ): + if hasattr(pure_eb, name): + monkeypatch.setattr(pure_eb, name, lambda *args, **kwargs: None) + + seen = [] + real_statistics = pure_eb.calculate_eb_statistics + + def observe_statistics(results, **kwargs): + seen.append(sorted(results)[:6]) + return real_statistics(results, **kwargs) + + monkeypatch.setattr(pure_eb, "calculate_eb_statistics", observe_statistics) + cv = _TomoCV(tmp_path) + try: + cv.plot_pure_eb( + versions=["v"], + min_sep_int=0.5, + max_sep_int=200.0, + nbins_int=60, + npatch=32, + ) + except KeyError as error: + pytest.fail( + f"Statistics received keys {seen} rather than one per-bin result: {error}" + ) + assert set(cv._pure_eb_results["v"]) == {BIN_KEY} + pte = cv._pure_eb_results["v"][BIN_KEY]["pte_matrices"]["combined"] + assert np.isfinite(pte).any(), "No finite combined B-mode PTE produced" From a71e5fa5b446bd658e994875e9868cf82992e80f Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:19 +0200 Subject: [PATCH 097/107] Honor tomography when plotting and saving each COSEBIs bin pair Co-Authored-By: GPT-6.1 Sol --- src/sp_validation/cosmo_val/cosebis.py | 102 +++++++------ ...st_tomo_plot_cosebis_ignores_tomography.py | 142 ++++++++++++++++++ 2 files changed, 195 insertions(+), 49 deletions(-) create mode 100644 src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py diff --git a/src/sp_validation/cosmo_val/cosebis.py b/src/sp_validation/cosmo_val/cosebis.py index 1d33c767..b1d478e1 100644 --- a/src/sp_validation/cosmo_val/cosebis.py +++ b/src/sp_validation/cosmo_val/cosebis.py @@ -208,6 +208,8 @@ def plot_cosebis( evaluate_all_scale_cuts : bool Whether to evaluate all scale cuts from reporting binning grid (default: False). Ignored when scale_cuts is provided. + compute_tomography : bool + Compute and plot each tomographic bin pair instead of the all-galaxy pair. min_sep, max_sep, nbins : float, float, int, optional Reporting binning parameters. Only used when evaluate_all_scale_cuts=True. fiducial_scale_cut : tuple, optional @@ -249,64 +251,66 @@ def plot_cosebis( cov_path=cov_path, scale_cuts=scale_cuts, evaluate_all_scale_cuts=evaluate_all_scale_cuts, - compute_tomography=False, # LG: Hardcoded to False for now + compute_tomography=compute_tomography, min_sep=min_sep, max_sep=max_sep, nbins=nbins, ) - results = results_tomo[ - "tomo_bin_all_tomo_bin_all" - ] # LG: Extract non-tomographic results - elif isinstance(results, dict) and "tomo_bin_all_tomo_bin_all" in results: - results = results["tomo_bin_all_tomo_bin_all"] - - # Generate plots using specialized plotting functions - # Extract single result for plotting if multiple scale cuts were evaluated - multiple_scale_cuts = isinstance(results, dict) and all( - isinstance(k, tuple) for k in results - ) - if multiple_scale_cuts: - # Multiple scale cuts: use fiducial_scale_cut if provided, otherwise use - # full range (largest scale cut) - plot_results = results[ - find_conservative_scale_cut_key(results, fiducial_scale_cut) - if fiducial_scale_cut is not None - else max(results, key=lambda x: x[1] - x[0]) - ] + elif results and all( + isinstance(key, str) and key.startswith("tomo_bin_") for key in results + ): + results_tomo = results else: - # Single result - plot_results = results - - plot_cosebis_modes( - plot_results, - version, - out_stub + "_cosebis.png", - fiducial_scale_cut=fiducial_scale_cut, - ) - - plot_cosebis_covariance_matrix( - plot_results, version, var_method, out_stub + "_covariance.png" - ) + results_tomo = {"tomo_bin_all_tomo_bin_all": results} - # Generate scale cut heatmap if we have multiple scale cuts - if multiple_scale_cuts and len(results) > 1: - # Create temporary gg object with correct binning for mapping - treecorr_config_temp = self._binning(min_sep, max_sep, nbins) - ggs_temp = self.calculate_2pcf_version( - version, npatch=npatch, **treecorr_config_temp + ggs_temp = None + for bin_key, bin_results in results_tomo.items(): + bin_stub = ( + out_stub + if bin_key == "tomo_bin_all_tomo_bin_all" + else f"{out_stub}_{bin_key}" ) - gg_temp = ggs_temp[ - "tomo_bin_all_tomo_bin_all" - ] # LG: Extract non-tomographic result + multiple_scale_cuts = isinstance(bin_results, dict) and all( + isinstance(k, tuple) for k in bin_results + ) + if multiple_scale_cuts: + plot_results = bin_results[ + find_conservative_scale_cut_key(bin_results, fiducial_scale_cut) + if fiducial_scale_cut is not None + else max(bin_results, key=lambda x: x[1] - x[0]) + ] + else: + plot_results = bin_results - plot_cosebis_scale_cut_heatmap( - results, - (gg_temp.left_edges, gg_temp.right_edges), + plot_cosebis_modes( + plot_results, version, - out_stub + "_scalecut_ptes.png", + bin_stub + "_cosebis.png", fiducial_scale_cut=fiducial_scale_cut, ) + plot_cosebis_covariance_matrix( + plot_results, version, var_method, bin_stub + "_covariance.png" + ) - # Save data products and store on instance - save_cosebis_results(results, out_stub + "_data.npz", fiducial_scale_cut) - self._cosebis_results[version] = {"tomo_bin_all_tomo_bin_all": results} + if multiple_scale_cuts and len(bin_results) > 1: + if ggs_temp is None: + treecorr_config_temp = self._binning(min_sep, max_sep, nbins) + ggs_temp = self.calculate_2pcf_version( + version, + npatch=npatch, + compute_tomography=compute_tomography, + **treecorr_config_temp, + ) + gg_temp = ggs_temp[bin_key] + plot_cosebis_scale_cut_heatmap( + bin_results, + (gg_temp.left_edges, gg_temp.right_edges), + version, + bin_stub + "_scalecut_ptes.png", + fiducial_scale_cut=fiducial_scale_cut, + ) + + save_cosebis_results( + bin_results, bin_stub + "_data.npz", fiducial_scale_cut + ) + self._cosebis_results[version] = results_tomo diff --git a/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py b/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py new file mode 100644 index 00000000..7e779f00 --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py @@ -0,0 +1,142 @@ +"""plot_cosebis(compute_tomography=True) must produce per-bin-pair COSEBIs.""" + +import inspect +from contextlib import nullcontext +from types import SimpleNamespace + +import numpy as np +import pytest +import yaml +from astropy.io import fits + +cosebis_mod = pytest.importorskip( + "sp_validation.cosmo_val.cosebis", + reason="COSEBIs module required by the tomography branch", +) +from sp_validation.cosmo_val import CosmologyValidation # noqa: E402 + +VER = "SP_toy" +NBINS = 60 + + +def _make_cv(tmp_path): + rng = np.random.default_rng(11) + n = 600 + t = np.zeros( + n, + dtype=[ + ("RA", "f8"), + ("Dec", "f8"), + ("e1", "f8"), + ("e2", "f8"), + ("w", "f8"), + ("tomo", "i4"), + ], + ) + t["RA"] = 10 + rng.uniform(-0.5, 0.5, n) + t["Dec"] = rng.uniform(-0.5, 0.5, n) + t["tomo"] = np.repeat([1, 2], n // 2) + # Opposite coherent e1 in the two bins: the merged catalogue nearly cancels, + # so the all-galaxy COSEBIs differ strongly from any single bin pair. + t["e1"] = np.where(t["tomo"] == 1, 0.1, -0.1) + rng.normal(0, 0.01, n) + t["e2"] = rng.normal(0, 0.01, n) + t["w"] = 1.0 + cat = tmp_path / "toy.fits" + fits.BinTableHDU(t).writeto(cat) + out = tmp_path / "out" + out.mkdir() + cfg = { + VER: { + "subdir": str(tmp_path), + "shear": { + "path": str(cat), + "e1_col": "e1", + "e2_col": "e2", + "w_col": "w", + "R": 1.0, + "tomo_bin_col": "tomo", + }, + }, + "nz": {"subdir": str(tmp_path)}, + "paths": {"output": str(out)}, + } + cfg_path = tmp_path / "cfg.yaml" + cfg_path.write_text(yaml.safe_dump(cfg)) + cv = CosmologyValidation( + [VER], + catalog_config=str(cfg_path), + output_dir=str(out), + compute_tomography=True, + npatch=1, + ) + cv.treecorr_config["num_threads"] = 2 + # Hand the table straight in, bypassing the leakage-correction reader. + cv._results = { + VER: SimpleNamespace(dat_shear=t, temporarily_read_data=lambda: nullcontext()) + } + cv._c1 = {VER: 0.0} + cv._c2 = {VER: 0.0} + cov = tmp_path / "cov.txt" + np.savetxt(cov, np.eye(2 * NBINS) * 1e-6) + params = dict( + version=VER, + min_sep_int=1.0, + max_sep_int=30.0, + nbins_int=NBINS, + npatch=1, + nmodes=1, + cov_path=str(cov), + ) + return cv, params + + +@pytest.mark.slow +def test_plot_cosebis_compute_tomography_true_stores_bin_pair_results( + tmp_path, monkeypatch +): + """plot_cosebis exposes a ``compute_tomography`` switch and saves/stores the + COSEBIs data vector and PTEs it computes. With ``compute_tomography=True`` on + a two-bin catalogue the stored results must be the three tomographic bin + pairs (1,1), (1,2), (2,2), identical to what ``calculate_cosebis(..., + compute_tomography=True)`` returns for the same inputs; the merged + all-galaxy E_n is the wrong answer. The fixture gives the two bins opposite + coherent e1 so the merged catalogue's E_1 differs from every bin pair's by + a large factor, making the substitution unmistakable.""" + if ( + "compute_tomography" + not in inspect.signature(CosmologyValidation.plot_cosebis).parameters + ): + pytest.skip("Tomography branch adds plot_cosebis(compute_tomography=...)") + # The first COSEBIs call can compile numba kernels; keep their cache local. + monkeypatch.setenv("NUMBA_CACHE_DIR", str(tmp_path / "numba-cache")) + # Plot rendering is irrelevant; keep genuine computation and result storage. + monkeypatch.setattr(cosebis_mod, "plot_cosebis_modes", lambda *a, **k: None) + monkeypatch.setattr( + cosebis_mod, "plot_cosebis_covariance_matrix", lambda *a, **k: None + ) + cv, params = _make_cv(tmp_path) + + expected = cv.calculate_cosebis(**params, compute_tomography=True) + assert set(expected) == { + "tomo_bin_1_tomo_bin_1", + "tomo_bin_1_tomo_bin_2", + "tomo_bin_2_tomo_bin_2", + } + + cv.plot_cosebis(**params, compute_tomography=True) + stored = cv._cosebis_results[VER] + + stored_keys = sorted(stored) if isinstance(stored, dict) else None + assert isinstance(stored, dict) and set(expected) <= set(stored), ( + "plot_cosebis(compute_tomography=True) stored non-tomographic COSEBIs: " + f"keys={stored_keys}, En={np.asarray(stored.get('En')).tolist()}; " + "expected bin pairs with En=" + f"{ {k: v['En'].tolist() for k, v in expected.items()} }" + ) + for k, v in expected.items(): + np.testing.assert_allclose(stored[k]["En"], v["En"], rtol=1e-10) + products = list((tmp_path / "out").glob(f"*_{k}_data.npz")) + assert len(products) == 1 + with np.load(products[0]) as product: + np.testing.assert_allclose(product["En"], v["En"], rtol=1e-10) + np.testing.assert_allclose(product["Bn"], v["Bn"], rtol=1e-10) From 02504936d6006b01f49b326374e4b2d9b84166af Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:20 +0200 Subject: [PATCH 098/107] Transpose the opposite polarization block to preserve joint iNKA covariance Co-Authored-By: GPT-6.1 Sol --- src/sp_validation/cosmo_val/pseudo_cl.py | 13 +-- ...test_tomo_inka_merge_crosspol_transpose.py | 96 +++++++++++++++++++ 2 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 src/sp_validation/tests/regression/test_tomo_inka_merge_crosspol_transpose.py diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index aa25cbdf..b9cc7f8b 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1335,15 +1335,16 @@ def _merge_iNKA_covariance(self, ver, tomography): bin_key_b2, ), ) - block = fits.open(block_path)[f"COVAR_{pa}_{pb}"].data - sl_a = slice(index_a * n_ell, (index_a + 1) * n_ell) sl_b = slice(index_b * n_ell, (index_b + 1) * n_ell) - full_cov[sl_a, sl_b] = block - - if index_a != index_b: - full_cov[sl_b, sl_a] = block.T + with fits.open(block_path) as block_cov: + full_cov[sl_a, sl_b] = block_cov[f"COVAR_{pa}_{pb}"].data + if index_a != index_b: + # Cov(P_b, Q_a) = Cov(Q_a, P_b).T. + full_cov[sl_b, sl_a] = block_cov[ + f"COVAR_{pb}_{pa}" + ].data.T covar.append(fits.ImageHDU(full_cov, name=f"COVAR_{pa}_{pb}")) diff --git a/src/sp_validation/tests/regression/test_tomo_inka_merge_crosspol_transpose.py b/src/sp_validation/tests/regression/test_tomo_inka_merge_crosspol_transpose.py new file mode 100644 index 00000000..4d7be5db --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_inka_merge_crosspol_transpose.py @@ -0,0 +1,96 @@ +"""Tomographic iNKA merging must transpose the (Q,P) block, not (P,Q). + +Develop lacks this tomography-branch merger, so the test skips there. +""" + +import itertools + +import numpy as np +import pytest + +pseudo_cl = pytest.importorskip( + "sp_validation.cosmo_val.pseudo_cl", + reason="Tomographic iNKA merger module is only on the tomography branch", +) +PseudoClMixin = getattr(pseudo_cl, "PseudoClMixin", None) + +POLS = ["EE", "EB", "BE", "BB"] + + +def test_tomo_inka_merge_lower_triangle_uses_transposed_qp_block(tmp_path): + """The merged tomographic iNKA covariance must equal the joint covariance. + + Draw a symmetric positive-definite covariance C over the data vector indexed + (polarisation P, spectrum pair s, ell). The correct COVAR_P_Q is therefore + C[P, :, :, Q, :, :] by construction. Write only upper-triangle spectrum-pair + blocks (a <= b), as the pipeline does, in its [ell, pol, ell, pol] layout + using the real _save_iNKA_covariance method. + + The lower block (b, a) of COVAR_P_Q is Cov(C_b^P, C_a^Q), which equals + block_{Q,P}(a, b).T. Using block_{P,Q}(a, b).T is only right when P == Q. + This protects all 16 polarisation blocks, including EE_BB and EB_BE, and + the symmetry COVAR_P_Q == COVAR_Q_P.T. It consolidates both audit reproductions + of the same incorrect cross-polarisation transpose. + """ + if PseudoClMixin is None or not hasattr(PseudoClMixin, "_merge_iNKA_covariance"): + pytest.skip("Tomographic iNKA merger is only on the tomography branch") + + n_bins_src = [1, 2] + pairs = list(itertools.combinations_with_replacement(n_bins_src, 2)) + n_s, n_ell, n_pol = len(pairs), 3, 4 + + rng = np.random.default_rng(7) + dim = n_pol * n_s * n_ell + draws = rng.standard_normal((dim, dim)) + covariance = (draws @ draws.T / dim + np.eye(dim)).reshape( + n_pol, n_s, n_ell, n_pol, n_s, n_ell + ) + + class Tiny(PseudoClMixin): + def _get_tomo_bins(self, ver): + return n_bins_src, pairs + + def _output_path_pseudo_cl_cov(self, ver, method, tomography): + return str(tmp_path / "merged.fits") + + def _output_path_iNKA_block_cov(self, ver, tomo_bin_quad): + return str(tmp_path / ("_".join(map(str, tomo_bin_quad)) + ".fits")) + + instance = Tiny() + instance._pseudo_cls = { + "v": {"tomo_bin_all_tomo_bin_all": {"pseudo_cl": {"ELL": np.arange(n_ell)}}} + } + for ia, ib in itertools.combinations_with_replacement(range(n_s), 2): + # [ell_a, pol_a, ell_b, pol_b] + block = np.transpose(covariance[:, ia, :, :, ib, :], (1, 0, 3, 2)) + quad = (*pairs[ia], *pairs[ib]) + with instance._save_iNKA_covariance( + block, instance._output_path_iNKA_block_cov("v", quad) + ): + pass + + with instance._merge_iNKA_covariance("v", True) as merged: + errors = {} + for i, pa in enumerate(POLS): + for j, pb in enumerate(POLS): + expected = covariance[i, :, :, j, :, :].reshape( + n_s * n_ell, n_s * n_ell + ) + got = merged[f"COVAR_{pa}_{pb}"].data + errors[f"{pa}_{pb}"] = np.max(np.abs(got - expected)) + bad = {k: f"{v:.3g}" for k, v in errors.items() if v > 1e-12} + asym = np.max(np.abs(merged["COVAR_EE_BB"].data - merged["COVAR_BB_EE"].data.T)) + assert not bad and asym < 1e-12, ( + f"Merged blocks differ from the joint covariance: {bad}; " + f"max|COVAR_EE_BB - COVAR_BB_EE.T| = {asym:.3g}" + ) + joint = np.block( + [[merged[f"COVAR_{pa}_{pb}"].data for pb in POLS] for pa in POLS] + ) + np.testing.assert_allclose(joint, joint.T, rtol=0, atol=1e-12) + eigenvalues = np.linalg.eigvalsh(joint) + assert eigenvalues.min() > 0 + print( + f"iNKA joint covariance: symmetry error={np.max(np.abs(joint - joint.T)):.3g}, " + f"min eigenvalue={eigenvalues.min():.12g}" + ) From 426b94086f44d1a12040f0172c5801cc8bad6646 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:20 +0200 Subject: [PATCH 099/107] Aggregate Gaussian spectra from the requested seed rather than stale files Co-Authored-By: GPT-6.1 Sol --- scripts/cosmo_val/run_cl_gaussian_sims.py | 12 +-- ...mo_gaussian_sims_seed_filename_mismatch.py | 100 ++++++++++++++++++ 2 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 src/sp_validation/tests/regression/test_tomo_gaussian_sims_seed_filename_mismatch.py diff --git a/scripts/cosmo_val/run_cl_gaussian_sims.py b/scripts/cosmo_val/run_cl_gaussian_sims.py index 6db6f1ee..3e0f2efa 100644 --- a/scripts/cosmo_val/run_cl_gaussian_sims.py +++ b/scripts/cosmo_val/run_cl_gaussian_sims.py @@ -521,13 +521,13 @@ def concatenate_spectra(cl_sample, tomo_bin_ids, pol_index): def get_covariance_from_simulated_spectra( - n_sims, version, tomography, tomo_bin_ids, pol, out_dir + n_sims, version, tomography, tomo_bin_ids, pol, out_dir, seed ): - """Compute the covariance of the EE signal from the simulated spectra.""" + """Compute the covariance of one polarization from the seed's spectra.""" cl_samples = [] pol_index = pol_to_pol_index_dict[pol] for sim_id in range(n_sims): - out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}.npz" + out_file = f"{out_dir}/cl_sample_{sim_id}_{version}_tomography_{tomography}_seed_{seed}.npz" if not os.path.exists(out_file): raise FileNotFoundError(f"Simulation output file {out_file} not found.") data = np.load(out_file, allow_pickle=True) @@ -695,7 +695,7 @@ def get_covariance_from_simulated_spectra( ) covariance_matrix = get_covariance_from_simulated_spectra( - n_sims, version, tomography, tomo_bin_ids, "EE", out_dir + n_sims, version, tomography, tomo_bin_ids, "EE", out_dir, seed ) np.save(outpath_cov, covariance_matrix) @@ -708,7 +708,7 @@ def get_covariance_from_simulated_spectra( ) covariance_matrix = get_covariance_from_simulated_spectra( - n_sims, version, tomography, tomo_bin_ids, "EB", out_dir + n_sims, version, tomography, tomo_bin_ids, "EB", out_dir, seed ) np.save(outpath_cov, covariance_matrix) @@ -721,7 +721,7 @@ def get_covariance_from_simulated_spectra( ) covariance_matrix = get_covariance_from_simulated_spectra( - n_sims, version, tomography, tomo_bin_ids, "BB", out_dir + n_sims, version, tomography, tomo_bin_ids, "BB", out_dir, seed ) np.save(outpath_cov, covariance_matrix) diff --git a/src/sp_validation/tests/regression/test_tomo_gaussian_sims_seed_filename_mismatch.py b/src/sp_validation/tests/regression/test_tomo_gaussian_sims_seed_filename_mismatch.py new file mode 100644 index 00000000..4bfd96f7 --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_gaussian_sims_seed_filename_mismatch.py @@ -0,0 +1,100 @@ +"""Gaussian-sim covariance must aggregate the spectra its seeded workers wrote. + +Develop lacks this tomography-branch script, so these tests skip there. +""" + +import importlib.util +import inspect +from pathlib import Path + +import numpy as np +import pytest + +import sp_validation + +SCRIPT_REL = "scripts/cosmo_val/run_cl_gaussian_sims.py" +VERSION = "vtest" +TOMO = False +SEED = 7 +N_SIMS = 3 +N_ELL = 4 + + +@pytest.fixture +def gaussian_sims(): + # Resolve from the imported package, so PYTHONPATH can select another branch + # while this regression file stays in the original checkout. + root = Path(sp_validation.__file__).resolve().parents[2] + script = root / SCRIPT_REL + if not script.is_file(): + pytest.skip(f"{SCRIPT_REL} is only available on the tomography branch") + pytest.importorskip("mpi4py", reason="Gaussian-sim script requires mpi4py") + spec = importlib.util.spec_from_file_location("run_cl_gaussian_sims", script) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + if not hasattr(module, "get_covariance_from_simulated_spectra"): + pytest.skip("Covariance aggregator is only available on the tomography branch") + return module + + +def _cl_dict(rng): + # NaMaster-decoupled spin-2 x spin-2: (EE, EB, BE, BB) x n_ell. + return {"W1xW1": rng.normal(size=(4, N_ELL))} + + +def _writer_name(out_dir, sim_id): + # Naming used by run_one_simulation when it saves a realisation. + return out_dir / (f"cl_sample_{sim_id}_{VERSION}_tomography_{TOMO}_seed_{SEED}.npz") + + +def _stale_name(out_dir, sim_id): + # Pre-seed naming: what an older run left behind in the same directory. + return out_dir / f"cl_sample_{sim_id}_{VERSION}_tomography_{TOMO}.npz" + + +def _call(module, out_dir): + fn = module.get_covariance_from_simulated_spectra + kwargs = {} + if "seed" in inspect.signature(fn).parameters: + kwargs["seed"] = SEED + return fn(N_SIMS, VERSION, TOMO, [1], "EE", str(out_dir), **kwargs) + + +def test_covariance_reads_seed_suffixed_spectra_in_fresh_dir(tmp_path, gaussian_sims): + """A seed-S run must aggregate exactly its cl_sample_*_seed_S.npz files. + + Nothing else exists in this fresh directory, so aggregation must succeed + and equal np.cov of the seed-S EE realisations, computed independently here. + No HEALPix simulation, catalogue or MPI work is needed. + """ + rng = np.random.default_rng(0) + seeded = [] + for i in range(N_SIMS): + cl = _cl_dict(rng) + np.savez(_writer_name(tmp_path, i), cl_decoupled=cl) + seeded.append(cl["W1xW1"][0]) + cov = _call(gaussian_sims, tmp_path) + np.testing.assert_allclose(cov, np.cov(np.array(seeded), rowvar=False)) + + +def test_covariance_ignores_stale_unsuffixed_spectra(tmp_path, gaussian_sims): + """A seed-S covariance must not silently use stale unsuffixed spectra. + + Both sets exist, but their distinct amplitudes make their sample covariances + different. The right answer is np.cov of this run's seeded EE realisations, + not the pre-seed files left by an older run. + """ + rng = np.random.default_rng(1) + seeded, stale = [], [] + for i in range(N_SIMS): + cl = _cl_dict(rng) + np.savez(_writer_name(tmp_path, i), cl_decoupled=cl) + seeded.append(cl["W1xW1"][0]) + old = {"W1xW1": 100.0 * rng.normal(size=(4, N_ELL))} + np.savez(_stale_name(tmp_path, i), cl_decoupled=old) + stale.append(old["W1xW1"][0]) + want = np.cov(np.array(seeded), rowvar=False) + old_cov = np.cov(np.array(stale), rowvar=False) + assert not np.allclose(want, old_cov), "Fixture must distinguish the two runs" + cov = _call(gaussian_sims, tmp_path) + np.testing.assert_allclose(cov, want) From c0b2ac4e988c906ec0f8c7d1f75c5c3a8d8d5e62 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:21 +0200 Subject: [PATCH 100/107] Track per-bin tau centering as an upstream leakage defect Co-Authored-By: GPT-6.1 Sol --- ...test_tomo_tau_centering_before_bin_mask.py | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_tomo_tau_centering_before_bin_mask.py diff --git a/src/sp_validation/tests/regression/test_tomo_tau_centering_before_bin_mask.py b/src/sp_validation/tests/regression/test_tomo_tau_centering_before_bin_mask.py new file mode 100644 index 00000000..b789438c --- /dev/null +++ b/src/sp_validation/tests/regression/test_tomo_tau_centering_before_bin_mask.py @@ -0,0 +1,140 @@ +"""Tomographic tau statistics must centre galaxy ellipticities within each bin. + +The tomography branch passes a bin mask to TauStat. The mean subtracted must +be the selected bin's weighted mean, as in CovTauTh's per-bin covariance. +The shared dependency exposes this mask API on develop as well. +""" + +import numpy as np +import pytest + +rts = pytest.importorskip("shear_psf_leakage.rho_tau_stat") + +# e1 is constant in each bin; correct centring gives g1 == 0. The PSF has +# e2 == 0, so galaxy e2 noise cannot contribute to tau_0+ = . +E1_BIN = {1: 0.10, 2: 0.30} +E1_PSF = 0.10 + + +def _params(): + return { + "e1_col": "e1", + "e2_col": "e2", + "w_col": "w", + "ra_col": "RA", + "dec_col": "Dec", + "ra_PSF_col": "RA", + "dec_PSF_col": "Dec", + "e1_PSF_col": "E1_PSF", + "e2_PSF_col": "E2_PSF", + "e1_star_col": "E1_STAR", + "e2_star_col": "E2_STAR", + "PSF_size": "T_PSF", + "star_size": "T_STAR", + "patch_number": 2, + "patch_seed": 1, + "ra_units": "deg", + "dec_units": "deg", + } + + +def _galaxies(n=4000, seed=0): + rng = np.random.default_rng(seed) + gal = np.zeros( + n, + dtype=[ + ("RA", "f8"), + ("Dec", "f8"), + ("e1", "f8"), + ("e2", "f8"), + ("w", "f8"), + ("bin", "i4"), + ], + ) + gal["RA"] = rng.uniform(10, 11, n) + gal["Dec"] = rng.uniform(0, 1, n) + gal["bin"] = np.where(np.arange(n) % 2 == 0, 1, 2) + gal["e1"] = np.where(gal["bin"] == 1, E1_BIN[1], E1_BIN[2]) + gal["e2"] = rng.normal(0, 0.01, n) + gal["w"] = 1.0 + return gal + + +def _stars(n=2000, seed=1): + rng = np.random.default_rng(seed) + st = np.zeros( + n, + dtype=[ + ("RA", "f8"), + ("Dec", "f8"), + ("E1_PSF", "f8"), + ("E2_PSF", "f8"), + ("E1_STAR", "f8"), + ("E2_STAR", "f8"), + ("T_PSF", "f8"), + ("T_STAR", "f8"), + ], + ) + st["RA"] = rng.uniform(10, 11, n) + st["Dec"] = rng.uniform(0, 1, n) + st["E1_PSF"] = st["E1_STAR"] = E1_PSF + rng.normal(0, 0.01, n) + st["E2_STAR"] = rng.normal(0, 0.01, n) # Non-degenerate tau_2/tau_5 fields. + st["T_PSF"] = 1.0 + st["T_STAR"] = 1.0 + rng.normal(0, 0.01, n) + return st + + +@pytest.mark.xfail( + strict=True, + raises=AssertionError, + reason="CosmoStat/shear_psf_leakage#52: centring precedes bin masking", +) +def test_masked_galaxy_catalog_is_centred_on_bin_mean(): + """A bin's TreeCorr galaxy catalogue must have zero weighted mean e1. + + Bin 2 has constant e1 = 0.3 and the full catalogue mean is 0.2. + Subtracting the bin mean gives zero by construction; centring before + masking instead leaves g1 = +0.1 on every bin-2 galaxy. + """ + gal = _galaxies() + cats = rts.Catalogs(params=_params()) + cats.build_catalog(gal, "gal", "gal_b2", mask=gal["bin"] == 2, npatch=2) + tc = cats.get_cat("gal_b2") + mean_g1 = np.average(tc.g1, weights=tc.w) + assert mean_g1 == pytest.approx(0.0, abs=1e-12), ( + f"bin-2 mean g1 = {mean_g1:.6f}; expected 0 after removing bin mean " + f"{E1_BIN[2]}, not the full-catalogue mean" + ) + + +@pytest.mark.xfail( + strict=True, + raises=AssertionError, + reason="CosmoStat/shear_psf_leakage#52: full-catalogue mean biases bin tau", +) +def test_tomographic_tau0_plus_vanishes_for_bin_constant_ellipticity(tmp_path): + """TauStat must give tau_0+ = 0 for a bin with constant galaxy e1. + + Per-bin centring makes g1 identically zero and the PSF has p2 = 0, + so vanishes at every scale up to TreeCorr round-off. + Full-catalogue centring leaves (0.3 - 0.2) * 0.1 = 0.01 instead. + """ + gal, st = _galaxies(), _stars() + cfg = { + "ra_units": "deg", + "dec_units": "deg", + "sep_units": "arcmin", + "min_sep": 1.0, + "max_sep": 30.0, + "nbins": 5, + } + tau = rts.TauStat(params=_params(), output=str(tmp_path), treecorr_config=cfg) + tau.build_cat_to_compute_tau(st, cat_type="psf", catalog_id="v") + tau.build_cat_to_compute_tau( + gal, cat_type="gal", catalog_id="v", mask=gal["bin"] == 2 + ) + tau.compute_tau_stats("v", "tau_v.fits", var_method=None) + tau0p = np.asarray(tau.tau_stats["tau_0_p"]) + assert np.allclose(tau0p, 0.0, atol=1e-6), ( + f"bin-2 tau_0+ = {np.round(tau0p, 6).tolist()}; expected zero at all scales" + ) From 114486c7d0a4bdf96ed484a621310f6f195b6294 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 04:25:21 +0200 Subject: [PATCH 101/107] Exercise the active covariance theory and workflow plotting APIs Co-Authored-By: GPT-6.1 Sol --- ...t_pseudo_cl_covariance_producer_crashes.py | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/sp_validation/tests/regression/test_pseudo_cl_covariance_producer_crashes.py diff --git a/src/sp_validation/tests/regression/test_pseudo_cl_covariance_producer_crashes.py b/src/sp_validation/tests/regression/test_pseudo_cl_covariance_producer_crashes.py new file mode 100644 index 00000000..f9502cf3 --- /dev/null +++ b/src/sp_validation/tests/regression/test_pseudo_cl_covariance_producer_crashes.py @@ -0,0 +1,132 @@ +"""Pseudo-Cl covariance producers must run against their branch's public API.""" + +import ast +import inspect +from pathlib import Path + +import numpy as np +import pyccl as ccl +import pytest + +import sp_validation +from sp_validation.cosmo_val import CosmologyValidation + + +def _scripts(): + root = Path(__file__).resolve().parents[4] + code_root = Path(sp_validation.__file__).resolve().parents[2] + if code_root != root: + root = code_root + return root / "workflow/scripts" + + +def _cv_method_calls(script): + """Return name, positional count and keyword names of each cv method call.""" + calls = [] + for node in ast.walk(ast.parse(script.read_text())): + if ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Attribute) + and isinstance(node.func.value, ast.Name) + and node.func.value.id == "cv" + ): + calls.append( + (node.func.attr, len(node.args), [k.arg for k in node.keywords]) + ) + return calls + + +class _ReachedCatalogue(Exception): + """The real fiducial-spectrum step succeeded and reached catalogue loading.""" + + +def test_pseudo_cl_cov_does_not_multiply_fiducial_dictionary(tmp_path, monkeypatch): + """The covariance must get past its real fiducial-C_ell/pixel-window step. + + Fiducial theory returns a dictionary keyed by tomographic pair, so the + producer must apply the HEALPix window to each spectrum, not the dictionary. + The catalogue loader raises a sentinel: reaching it is correct by + construction, because theory must finish before data loading starts. + Synthetic n(z) and nside=16 keep this small; no theory result is patched. + """ + mod = pytest.importorskip( + "sp_validation.cosmo_val.pseudo_cl", + reason="tomography branch reorganized the pseudo-Cl covariance module", + ) + z = np.linspace(0.01, 2.0, 100) + nz = np.exp(-0.5 * ((z - 0.7) / 0.2) ** 2) + zpath = tmp_path / "nz.txt" + np.savetxt(zpath, np.column_stack([z, nz])) + + def open_entry(*args, **kwargs): + raise _ReachedCatalogue + + def noop(*args, **kwargs): + pass + + monkeypatch.setattr(mod, "get_params_rho_tau", lambda *args, **kwargs: {}) + monkeypatch.setattr(mod, "open_entry", open_entry) + + class Tiny(mod.PseudoClMixin): + pass + + stub = Tiny() + stub.__dict__.update( + nside=16, + cell_method="map", + force_run=True, + versions=["v"], + cc={"v": {"shear": {"redshift_path": str(zpath)}}}, + cosmo=ccl.CosmologyVanillaLCDM(), + noise_bias_method="analytic", + _output_path=lambda name: str(tmp_path / name), + _output_path_pseudo_cl_cov=lambda *args, **kwargs: str(tmp_path / "cov.fits"), + print_start=noop, + print_magenta=noop, + print_cyan=noop, + print_done=noop, + get_namaster_bin=noop, + ) + with pytest.raises(_ReachedCatalogue): + stub.calculate_pseudo_cl_inka_cov(compute_tomography=False) + + +def test_generate_pseudo_cl_cov_does_not_call_removed_methods(): + """Every cv method called by the covariance producer must exist. + + Existence is required before Python can produce any covariance. + """ + script = _scripts() / "generate_pseudo_cl_cov.py" + missing = [ + name + for name, _, _ in _cv_method_calls(script) + if not hasattr(CosmologyValidation, name) + ] + assert not missing, ( + f"{script.name} calls missing CosmologyValidation methods: {missing}" + ) + + +def test_cv_pseudo_cl_does_not_omit_required_plot_arguments(): + """The workflow's plot call must bind to its actual plotting API. + + The plot-only producer ingests saved spectra and covariances, then calls + plot_pseudo_cl_spectrum once per polarization rather than a cv method. + """ + from sp_validation.cosmo_val.pseudo_cl import plot_pseudo_cl_spectrum + + script = _scripts() / "cv_plot_pseudo_cl.py" + sig = inspect.signature(plot_pseudo_cl_spectrum) + calls = [ + node + for node in ast.walk(ast.parse(script.read_text())) + if isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "plot_pseudo_cl_spectrum" + ] + assert calls, "cv_plot_pseudo_cl.py must call plot_pseudo_cl_spectrum" + for call in calls: + sig.bind( + *([None] * len(call.args)), + **{key.arg: None for key in call.keywords}, + ) From a6e076523156a8d1c0ed5fcad92e0e7b01422434 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 05:55:24 +0200 Subject: [PATCH 102/107] Average rho/tau jackknife covariances over seeded independent layouts 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 --- src/sp_validation/rho_tau.py | 48 ++++---- src/sp_validation/statistics.py | 7 +- .../tests/test_rho_tau_jackknife.py | 116 ++++++++++++++++++ 3 files changed, 146 insertions(+), 25 deletions(-) create mode 100644 src/sp_validation/tests/test_rho_tau_jackknife.py diff --git a/src/sp_validation/rho_tau.py b/src/sp_validation/rho_tau.py index 526e02e6..e744fdf8 100644 --- a/src/sp_validation/rho_tau.py +++ b/src/sp_validation/rho_tau.py @@ -493,30 +493,32 @@ def chunks(i): rho_stat_handler.catalogs.catalogs_dict ) - # Build the catalog of galaxies. PSF was computed above - tau_stat_handler.build_cat_to_compute_tau( - load("shear"), - cat_type="gal", - catalog_id=catalog_id(i), - mask=mask_gal, - ) - - else: - print(f"Computing the patch centers for patch {i + 1}/{ncov}") - - npatch = rho_stat_handler.catalogs._params["patch_number"] - centers = jackknife_patch_centers( - rho_stat_handler.catalogs.catalogs_dict[f"psf_{catalog_id(i)}"], - npatch, + catalogs = rho_stat_handler.catalogs + centers = jackknife_patch_centers( + catalogs.catalogs_dict[f"psf_{catalog_id(i)}"], + catalogs._params["patch_number"], + seed=i, + init="kmeans++", + ) + # Fresh catalogues keep TreeCorr's cached patches consistent with + # this draw's shared star/galaxy layout. + stars = catalogs.read_shear_cat( + path_gal=None, path_psf=load("psf"), hdu=1 + ) + for cat_type in ("psf", "psf_error", "psf_size_error"): + catalogs.build_catalog( + cat=stars, + cat_type=cat_type, + key=f"{cat_type}_{catalog_id(i)}", + patch_centers=centers, + mask=mask_star, ) - - # Update the patch centers of the catalogs - for key, cat in rho_stat_handler.catalogs.catalogs_dict.items(): - cat._centers = centers - field = cat.getNField( - max_top=int.bit_length(npatch) - 1, coords="spherical" - ) - cat._patch = field.kmeans_assign_patches(centers) + tau_stat_handler.build_cat_to_compute_tau( + load("shear"), + cat_type="gal", + catalog_id=catalog_id(i), + mask=mask_gal, + ) # Compute and save rho stats rho_stat_handler.compute_rho_stats( diff --git a/src/sp_validation/statistics.py b/src/sp_validation/statistics.py index bd95932b..17e05219 100644 --- a/src/sp_validation/statistics.py +++ b/src/sp_validation/statistics.py @@ -17,7 +17,7 @@ PATCH_MIN_TOP = 6 -def jackknife_patch_centers(cat, npatch, seed=0): +def jackknife_patch_centers(cat, npatch, seed=0, init="tree"): """Seeded k-means jackknife patch centres for a TreeCorr catalogue. Seeding the k-means does not by itself fix the patches. TreeCorr starts the @@ -35,6 +35,9 @@ def jackknife_patch_centers(cat, npatch, seed=0): Number of patches. seed : int, optional Seed for the k-means initialisation. + init : str, optional + TreeCorr initialisation method. Use ``kmeans++`` for draws that sample + different seeded starting centres rather than the same tree cells. Returns ------- @@ -46,7 +49,7 @@ def jackknife_patch_centers(cat, npatch, seed=0): max_top=int.bit_length(npatch) - 1, coords="spherical", ) - _, centers = field.run_kmeans(npatch, rng=np.random.default_rng(seed)) + _, centers = field.run_kmeans(npatch, init=init, rng=np.random.default_rng(seed)) return centers diff --git a/src/sp_validation/tests/test_rho_tau_jackknife.py b/src/sp_validation/tests/test_rho_tau_jackknife.py new file mode 100644 index 00000000..bc64537a --- /dev/null +++ b/src/sp_validation/tests/test_rho_tau_jackknife.py @@ -0,0 +1,116 @@ +"""Jackknife layouts and covariance averaging on small synthetic catalogues.""" + +from pathlib import Path + +import numpy as np +from astropy.table import Table + +from sp_validation import rho_tau + + +def test_jackknife_draws_use_distinct_shared_layouts(tmp_path, monkeypatch): + rng = np.random.default_rng(37) + n = 400 + star = Table( + dict( + RA=rng.uniform(10, 14, n), + Dec=rng.uniform(10, 14, n), + p1=rng.normal(0, 0.03, n), + p2=rng.normal(0, 0.03, n), + s1=rng.normal(0, 0.04, n), + s2=rng.normal(0, 0.04, n), + tp=np.ones(n), + ts=np.full(n, 1.1), + ) + ) + gal = Table( + dict( + RA=rng.uniform(10, 14, n), + Dec=rng.uniform(10, 14, n), + e1=rng.normal(0, 0.2, n), + e2=rng.normal(0, 0.2, n), + w=rng.uniform(0.2, 1, n), + ) + ) + star.write(tmp_path / "stars.fits") + gal.write(tmp_path / "galaxies.fits") + config = { + "v": dict( + patch_number=7, + psf=dict( + path=str(tmp_path / "stars.fits"), + ra_col="RA", + dec_col="Dec", + e1_PSF_col="p1", + e2_PSF_col="p2", + e1_star_col="s1", + e2_star_col="s2", + PSF_size="tp", + star_size="ts", + ), + shear=dict( + path=str(tmp_path / "galaxies.fits"), + ra_col="RA", + dec_col="Dec", + e1_col="e1", + e2_col="e2", + w_col="w", + ), + ) + } + layouts, catalogs, chunks = [], [], {"rho": [], "tau": []} + compute = rho_tau.RhoStat.compute_rho_stats + save = np.save + + def record_compute(self, catalog_id, *args, **kwargs): + cats = self.catalogs.catalogs_dict + psf = cats[f"psf_{catalog_id}"] + layouts.append(psf.patch_centers.copy()) + catalogs.append(psf) + for prefix in ("psf_error", "psf_size_error", "gal"): + np.testing.assert_array_equal( + cats[f"{prefix}_{catalog_id}"].patch_centers, psf.patch_centers + ) + # Materialise TreeCorr's patch cache before the next draw. + assert len(psf.patches) == 7 + return compute(self, catalog_id, *args, **kwargs) + + def record_save(path, arr, *args, **kwargs): + for kind in chunks: + if Path(path).stem in {f"cov_{kind}_tau{i}" for i in range(3)}: + chunks[kind].append(np.array(arr).copy()) + return save(path, arr, *args, **kwargs) + + monkeypatch.setattr(rho_tau.RhoStat, "compute_rho_stats", record_compute) + monkeypatch.setattr(np, "save", record_save) + rho_tau.get_jackknife_cov( + config, + "v", + dict( + min_sep=1, + max_sep=150, + nbins=2, + sep_units="arcmin", + num_threads=1, + cross_patch_weight="match", + ), + str(tmp_path), + "rho", + "tau", + npatch=7, + ncov=3, + ) + assert len(layouts) == 3 + assert all( + not np.array_equal(layouts[i], layouts[j]) for i in range(3) for j in range(i) + ) + assert len({id(cat) for cat in catalogs}) == 3 + for kind in chunks: + assert len(chunks[kind]) == 3 + assert any(not np.array_equal(chunks[kind][0], cov) for cov in chunks[kind][1:]) + np.testing.assert_allclose( + np.load(tmp_path / f"cov_{kind}_{kind}_jk.npy"), + np.mean(chunks[kind], axis=0), + rtol=1e-13, + atol=0, + ) From 5c25e3ec376a0f4f35abaa2c679d99ca9762acee Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 05:55:24 +0200 Subject: [PATCH 103/107] Restore BE readback for single-field pseudo-Cl auto-spectra 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 --- src/sp_validation/cosmo_val/pseudo_cl.py | 4 ++-- src/sp_validation/tests/test_pseudo_cl.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index b9cc7f8b..d1c4a368 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -1224,12 +1224,12 @@ def _load_pseudo_cl(self, out_path, tomo_bin_pair): @staticmethod def _load_pseudo_cl_sacc(out_path): - """Read a pseudo-Cl SACC part into the ELL/EE/EB/BB dict.""" + """Read the single-field auto-spectrum, whose BE equals EB.""" # Readback of a part this producer just wrote — a legitimate pre-blind # consumer, so the fail-closed load is opted out of. s = sacc_io.load(out_path, allow_unblinded=True) ell, ee, bb, eb, _window = sacc_io.get_pseudo_cl(s, SACC_BIN) - return {"ELL": ell, "EE": ee, "EB": eb, "BB": bb} + return {"ELL": ell, "EE": ee, "EB": eb, "BE": eb.copy(), "BB": bb} def pseudo_cl_to_sacc_part( self, version, out_path, ell_eff, cl_all, wsp, nside=None diff --git a/src/sp_validation/tests/test_pseudo_cl.py b/src/sp_validation/tests/test_pseudo_cl.py index cd01ebe6..e3dae689 100644 --- a/src/sp_validation/tests/test_pseudo_cl.py +++ b/src/sp_validation/tests/test_pseudo_cl.py @@ -697,6 +697,9 @@ def capture_workspace( s = sacc_io.load(out_path, allow_unblinded=True) ell, ee, bb, eb, window = sacc_io.get_pseudo_cl(s, SACC_BIN) assert window is not None # the shared BandpowerWindow rides the part + readback = cv._load_pseudo_cl(out_path, ("all", "all")) + npt.assert_array_equal(readback["BE"], eb) + assert not np.shares_memory(readback["BE"], readback["EB"]) assert saved["nside"] is None window_ells, unwindowed_weights = bandpower_window_from_workspace( saved["workspace"] From 5d3b5d25d52eb268daf80a05b9f5134694b0fa2b Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 05:57:56 +0200 Subject: [PATCH 104/107] Reject a single COSEBIs covariance for tomographic pairs 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 --- src/sp_validation/cosmo_val/cosebis.py | 9 +++++- .../tests/test_cosebis_tomography.py | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/sp_validation/tests/test_cosebis_tomography.py diff --git a/src/sp_validation/cosmo_val/cosebis.py b/src/sp_validation/cosmo_val/cosebis.py index b1d478e1..079720bf 100644 --- a/src/sp_validation/cosmo_val/cosebis.py +++ b/src/sp_validation/cosmo_val/cosebis.py @@ -61,7 +61,8 @@ def calculate_cosebis( Number of COSEBIs modes to compute. Defaults to 10. cov_path : str, optional Path to theoretical covariance matrix. When provided, enables analytic - covariance calculation. + covariance calculation for a single pair. Cannot be combined with + ``compute_tomography=True``. scale_cuts : list of tuples, optional Explicit list of (min_theta, max_theta) scale cuts to evaluate. Overrides evaluate_all_scale_cuts when provided. @@ -91,6 +92,12 @@ def calculate_cosebis( """ self.print_start(f"Computing {version} COSEBIs") + if cov_path is not None and compute_tomography: + raise ValueError( + "cov_path holds a single ξ± covariance; it cannot serve every " + "tomographic bin pair." + ) + # Set up parameters with defaults npatch = npatch or self.npatch diff --git a/src/sp_validation/tests/test_cosebis_tomography.py b/src/sp_validation/tests/test_cosebis_tomography.py new file mode 100644 index 00000000..d58c6696 --- /dev/null +++ b/src/sp_validation/tests/test_cosebis_tomography.py @@ -0,0 +1,30 @@ +"""Covariance selection at the tomographic COSEBIs boundary.""" + +from types import SimpleNamespace + +import pytest + +from sp_validation.cosmo_val.cosebis import CosebisMixin + + +@pytest.mark.parametrize( + "options", + [ + {}, + {"scale_cuts": [(1, 10)]}, + {"evaluate_all_scale_cuts": True}, + ], +) +def test_tomographic_cosebis_rejects_single_covariance(options): + calls = [] + cv = SimpleNamespace( + npatch=7, + print_start=lambda *args: None, + _binning=lambda *args: {}, + calculate_2pcf_version=lambda *args, **kwargs: calls.append(kwargs) or {}, + ) + with pytest.raises(ValueError, match="cov_path.*single.*tomographic bin pair"): + CosebisMixin.calculate_cosebis( + cv, "v", cov_path="xi_cov.txt", compute_tomography=True, **options + ) + assert calls == [] From 3367b2bb19cfebec7e3e9883963c2f833c1c8b06 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 14:47:00 +0200 Subject: [PATCH 105/107] Continue the iNKA noise bias below l_min at the lowest band's value 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 Claude-Session: https://claude.ai/code/session_0133mw5vATUB7QfqbhYac7Fz --- src/sp_validation/cosmo_val/pseudo_cl.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sp_validation/cosmo_val/pseudo_cl.py b/src/sp_validation/cosmo_val/pseudo_cl.py index d1c4a368..686cda01 100644 --- a/src/sp_validation/cosmo_val/pseudo_cl.py +++ b/src/sp_validation/cosmo_val/pseudo_cl.py @@ -519,13 +519,11 @@ def calculate_pseudo_cl_inka_cov( wsp = wsp_dict[f"W{bin_key1}xW{bin_key2}"] noise_bias_cl = wsp.decouple_cell(noise_bias_cl) - # And then unbin it + # Unbin, then continue the white noise below lmin at the + # lowest band's value noise_bias_cl = b.unbin_cell(noise_bias_cl) - - # Force the bins not part of the mask to be zero lowest_ell = b.get_ell_list(0)[0] - for i in range(4): - noise_bias_cl[i, :lowest_ell] = 0 + noise_bias_cl[:, :lowest_ell] = noise_bias_cl[:, [lowest_ell]] else: noise_bias_cl = np.zeros((4, 2 * nside)) From 7b344114b9f18becb5eea92e08511838ff8e2ae0 Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 14:47:02 +0200 Subject: [PATCH 106/107] Give the tomographic COSEBIs regression per-pair jackknife covariances 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 Claude-Session: https://claude.ai/code/session_0133mw5vATUB7QfqbhYac7Fz --- .../test_tomo_plot_cosebis_ignores_tomography.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py b/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py index 7e779f00..63de87b7 100644 --- a/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py +++ b/src/sp_validation/tests/regression/test_tomo_plot_cosebis_ignores_tomography.py @@ -67,7 +67,7 @@ def _make_cv(tmp_path): catalog_config=str(cfg_path), output_dir=str(out), compute_tomography=True, - npatch=1, + npatch=4, ) cv.treecorr_config["num_threads"] = 2 # Hand the table straight in, bypassing the leakage-correction reader. @@ -76,16 +76,15 @@ def _make_cv(tmp_path): } cv._c1 = {VER: 0.0} cv._c2 = {VER: 0.0} - cov = tmp_path / "cov.txt" - np.savetxt(cov, np.eye(2 * NBINS) * 1e-6) + # Each bin pair takes its own jackknife covariance; a single cov_path cannot + # serve every pair. params = dict( version=VER, min_sep_int=1.0, max_sep_int=30.0, nbins_int=NBINS, - npatch=1, + npatch=4, nmodes=1, - cov_path=str(cov), ) return cv, params From fee38cc6489c8cf845baef0eb2a4f405d2a0b37d Mon Sep 17 00:00:00 2001 From: Cail Daley Date: Mon, 5 Oct 2026 16:32:41 +0200 Subject: [PATCH 107/107] GLASS suite DAG test: name rho/tau placeholders through cv_basename The test touched rho/tau inputs under develop's old basename; the merged rules name them with cv_basename, which carries the ('all','all') bin pair. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_0133mw5vATUB7QfqbhYac7Fz --- workflow/tests/test_glass_suite.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/workflow/tests/test_glass_suite.py b/workflow/tests/test_glass_suite.py index 187bde09..50e4e84b 100644 --- a/workflow/tests/test_glass_suite.py +++ b/workflow/tests/test_glass_suite.py @@ -39,17 +39,17 @@ def test_glass_rules_share_suite(toy, tmp_path, suite): (data_dir / f"cl_glass_mock_{seed:05d}_4096.npy").touch() version = config["fiducial"]["mock_version"] - suffix = toy.common.fiducial_binning_suffix(config["fiducial"]) tag = toy.common.pseudo_cl_tag(config) + base = toy.common.cv_basename(version, config["fiducial"]) dependencies = [ toy.root / "data" / "nz_SP_v0.1_A.txt", toy.cosmo_val / f"pseudo_cl_cov_{version}_{tag}.fits", *[ toy.cosmo_val / "rho_tau_stats" / name for name in ( - f"rho_stats_{version}{suffix}.fits", - f"tau_stats_{version}{suffix}.fits", - f"cov_tau_{version}{suffix}_th.npy", + f"rho_stats_{base}.fits", + f"tau_stats_{base}.fits", + f"cov_tau_{base}_th.npy", ) ], ]