06 — Power Spectra Comparison

Purpose: Compare the two-point statistics of Agora test maps, DDPM-generated samples, and Gaussian realisations.

This notebook computes and plots the angular auto- and cross-power spectra for the CIB and tSZ channels:

  1. Auto-spectra — Cℓ^{CIB} and Cℓ^{tSZ} for all three sample types, binned over 300 < ℓ < 4000 in bins of Δℓ = 60.

  2. Cross-spectrum — Cℓ^{CIB×tSZ} probing spatial correlation between dusty star-forming galaxies and hot gas traced by tSZ.

  3. Residuals — (C_ℓ^{Agora} − C_ℓ^{DDPM}) / σ(C_ℓ^{Agora}) to quantify agreement within sample variance.

  4. Multi-frequency correlations — cross-correlation coefficients between the 95, 150, and 857 GHz CIB channels (Appendix B).

Inputs:

  • Test maps and DDPM samples: data/low_pass/2mJy/*.npy

  • Gaussian baseline: data/low_pass/2mJy/gaussian_cib_tsz_2mJy_lp.npy

  • ILC noise power spectra: data/ilc/ilc_weights_residuals_agora_fg_model.npy

Outputs: power spectrum comparison plots (Figure 4), multi-frequency correlation plots (Figure 9).

Key module functions:

  • foregrounds_diffusion.flatmaps.map2cl

  • foregrounds_diffusion.flatmaps.cl2map

  • foregrounds_diffusion.preprocessing.renormalize_dm_maps

Paper reference: §4.3 (Figure 4), Appendix B (Figure 9).

[10]:
!pip install -e ~/cmb_foregrounds_diffusion/
Obtaining file:///home/apb86/cmb_foregrounds_diffusion
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done
Requirement already satisfied: numpy>=1.26 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (1.26.4)
Requirement already satisfied: scipy>=1.11 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (1.17.0)
Requirement already satisfied: torch>=2.10 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (2.10.0)
Requirement already satisfied: torchvision>=0.25 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (0.25.0)
Requirement already satisfied: accelerate>=1.12 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (1.12.0)
Requirement already satisfied: denoising-diffusion-pytorch>=2.2.5 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (2.2.6)
Requirement already satisfied: healpy>=1.19 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (1.19.0)
Requirement already satisfied: astropy>=7.2 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (7.2.0)
Requirement already satisfied: einops>=0.8 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (0.8.2)
Requirement already satisfied: ema-pytorch>=0.7 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (0.7.9)
Requirement already satisfied: pillow>=12.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (12.1.1)
Requirement already satisfied: pytorch-fid>=0.3 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (0.3.0)
Requirement already satisfied: tqdm>=4.67 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from foregrounds_diffusion==0.1.0) (4.67.3)
Requirement already satisfied: packaging>=20.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from accelerate>=1.12->foregrounds_diffusion==0.1.0) (26.0)
Requirement already satisfied: psutil in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from accelerate>=1.12->foregrounds_diffusion==0.1.0) (7.2.2)
Requirement already satisfied: pyyaml in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from accelerate>=1.12->foregrounds_diffusion==0.1.0) (6.0.3)
Requirement already satisfied: huggingface_hub>=0.21.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from accelerate>=1.12->foregrounds_diffusion==0.1.0) (1.4.1)
Requirement already satisfied: safetensors>=0.4.3 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.7.0)
Requirement already satisfied: pyerfa>=2.0.1.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from astropy>=7.2->foregrounds_diffusion==0.1.0) (2.0.1.5)
Requirement already satisfied: astropy-iers-data>=0.2025.10.27.0.39.10 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from astropy>=7.2->foregrounds_diffusion==0.1.0) (0.2026.2.9.0.50.33)
Requirement already satisfied: filelock in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (3.24.0)
Requirement already satisfied: fsspec>=2023.5.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (2026.2.0)
Requirement already satisfied: hf-xet<2.0.0,>=1.2.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (1.2.0)
Requirement already satisfied: httpx<1,>=0.23.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.28.1)
Requirement already satisfied: shellingham in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (1.5.4)
Requirement already satisfied: typer-slim in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.23.1)
Requirement already satisfied: typing-extensions>=4.1.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (4.15.0)
Requirement already satisfied: anyio in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (4.12.1)
Requirement already satisfied: certifi in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (2026.1.4)
Requirement already satisfied: httpcore==1.* in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (1.0.9)
Requirement already satisfied: idna in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from httpx<1,>=0.23.0->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (3.11)
Requirement already satisfied: h11>=0.16 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.16.0)
Requirement already satisfied: sympy>=1.13.3 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (1.14.0)
Requirement already satisfied: networkx>=2.5.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (3.6.1)
Requirement already satisfied: jinja2 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (3.1.6)
Requirement already satisfied: cuda-bindings==12.9.4 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.9.4)
Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.8.93 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.8.93)
Requirement already satisfied: nvidia-cuda-runtime-cu12==12.8.90 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.8.90)
Requirement already satisfied: nvidia-cuda-cupti-cu12==12.8.90 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.8.90)
Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (9.10.2.21)
Requirement already satisfied: nvidia-cublas-cu12==12.8.4.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.8.4.1)
Requirement already satisfied: nvidia-cufft-cu12==11.3.3.83 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (11.3.3.83)
Requirement already satisfied: nvidia-curand-cu12==10.3.9.90 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (10.3.9.90)
Requirement already satisfied: nvidia-cusolver-cu12==11.7.3.90 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (11.7.3.90)
Requirement already satisfied: nvidia-cusparse-cu12==12.5.8.93 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.5.8.93)
Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (0.7.1)
Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (2.27.5)
Requirement already satisfied: nvidia-nvshmem-cu12==3.4.5 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (3.4.5)
Requirement already satisfied: nvidia-nvtx-cu12==12.8.90 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.8.90)
Requirement already satisfied: nvidia-nvjitlink-cu12==12.8.93 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (12.8.93)
Requirement already satisfied: nvidia-cufile-cu12==1.13.1.3 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (1.13.1.3)
Requirement already satisfied: triton==3.6.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from torch>=2.10->foregrounds_diffusion==0.1.0) (3.6.0)
Requirement already satisfied: cuda-pathfinder~=1.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from cuda-bindings==12.9.4->torch>=2.10->foregrounds_diffusion==0.1.0) (1.3.4)
Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from sympy>=1.13.3->torch>=2.10->foregrounds_diffusion==0.1.0) (1.3.0)
Requirement already satisfied: MarkupSafe>=2.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from jinja2->torch>=2.10->foregrounds_diffusion==0.1.0) (3.0.3)
Requirement already satisfied: typer>=0.23.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.23.1)
Requirement already satisfied: click>=8.0.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from typer>=0.23.1->typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (8.3.1)
Requirement already satisfied: rich>=10.11.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from typer>=0.23.1->typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (14.3.2)
Requirement already satisfied: annotated-doc>=0.0.2 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from typer>=0.23.1->typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.0.4)
Requirement already satisfied: markdown-it-py>=2.2.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from rich>=10.11.0->typer>=0.23.1->typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (4.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from rich>=10.11.0->typer>=0.23.1->typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (2.19.2)
Requirement already satisfied: mdurl~=0.1 in /home/apb86/diffusion_project_env/lib64/python3.11/site-packages (from markdown-it-py>=2.2.0->rich>=10.11.0->typer>=0.23.1->typer-slim->huggingface_hub>=0.21.0->accelerate>=1.12->foregrounds_diffusion==0.1.0) (0.1.2)
Building wheels for collected packages: foregrounds_diffusion
  Building editable for foregrounds_diffusion (pyproject.toml) ... done
  Created wheel for foregrounds_diffusion: filename=foregrounds_diffusion-0.1.0-0.editable-py3-none-any.whl size=4647 sha256=9b4a5bfb6cba73cb3523287555f265a1816dd6813f956e15c85c3e7a905bfe49
  Stored in directory: /tmp/pip-ephem-wheel-cache-5kbod56x/wheels/ad/05/47/d622ea03cc2c607997f65b7643dab9c4f27fa5f37d82097115
Successfully built foregrounds_diffusion
Installing collected packages: foregrounds_diffusion
  Attempting uninstall: foregrounds_diffusion
    Found existing installation: foregrounds_diffusion 0.1.0
    Uninstalling foregrounds_diffusion-0.1.0:
      Successfully uninstalled foregrounds_diffusion-0.1.0
Successfully installed foregrounds_diffusion-0.1.0

[notice] A new release of pip is available: 26.0.1 -> 26.1.2
[notice] To update, run: pip install --upgrade pip

1 Configuration

Map geometry and ℓ-binning. The 6° × 6° patches at 1.41′/px give a minimum multipole of ℓ_min ≈ 1/6° ≈ 10 and a Nyquist multipole of ℓ_Nyq ≈ 6° × 256 / (6° × 1.41′ / 60°) ≈ 7700. We analyse 300 < ℓ < 4000 in bins of Δℓ = 60 to match the ℓ-range used in the paper’s power spectrum comparison.

[ ]:
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path

from foregrounds_diffusion.flatmaps import map2cl
from foregrounds_diffusion.preprocessing import renormalize_dm_maps, denormalize_dm_maps
from foregrounds_diffusion.moments import mean_cls, mean_cross_cls

PTSRC = 2
flatskymapparams = [256, 256, 1.40625, 1.40625]   # [nx, ny, dx_arcmin, dy_arcmin]
LMIN, LMAX, LBIN = 300, 4000, 60
N_MAPS = 500            # maximum patches to analyse

PROJECT_ROOT = Path("/home/apb86/cmb_foregrounds_diffusion")
PATCHES_DIR = Path(f"data/low_pass/{PTSRC}mJy")

2 Load and denormalise maps

Load the normalised .npy arrays and the norm_params file written by 03_patch_extraction.ipynb. Denormalise each channel back to physical units (µK) using denormalize_dm_maps before computing power spectra, so that Cℓ values are in µK².

[ ]:
# Load normalised training patches (channels-last: shape (N, H, W, 1)) and
# the DDPM sample array (channels-first: shape (N, 2, H, W)).
# norm_params = [cib_mean, cib_std, tsz_mean, tsz_std] — saved by notebook 03
# (both channels z-scored; denormalise with denormalize_dm_maps).
cib_maps    = np.load(PATCHES_DIR / f"CIB_map_150GHz_256_st6_minmax_{PTSRC}mJy_zero_lp.npy")
tsz_maps    = np.load(PATCHES_DIR / f"tSZ3_map_150GHz_256_st6_minmax_{PTSRC}mJy_norm_lp.npy")
ddpm_raw    = np.load(PROJECT_ROOT / "data" / "low_pass" / f"{PTSRC}mJy" / f"new_samples_cib_tsz_{PTSRC}mJy_zero_norm_6x6_w_au_lp.npy")
gauss_maps  = np.load(PATCHES_DIR / f"gaussian_cib_tsz_{PTSRC}mJy_lp.npy")

train_maps = np.concatenate([cib_maps, tsz_maps], axis=-1)  # (N, H, W, 2)
print(f"train_maps shape: {train_maps.shape}")
print(f"ddpm_raw shape:   {ddpm_raw.shape}")

# Load saved normalisation parameters from patch extraction
norm_params = np.load(PATCHES_DIR / f"norm_params_{PTSRC}mJy.npy")
cib_mean, cib_std, tsz_mean, tsz_std = norm_params
print(f"CIB — mean: {cib_mean:.4f}  std: {cib_std:.4f}")
print(f"tSZ — mean: {tsz_mean:.4f}  std: {tsz_std:.4f}")

ddpm_renorm = denormalize_dm_maps(ddpm_raw, cib_mean, cib_std, tsz_mean, tsz_std)
print(f"Denormalised DDPM — CIB range: [{ddpm_renorm[:,0].min():.4f}, {ddpm_renorm[:,0].max():.4f}]")
print(f"Denormalised DDPM — tSZ range: [{ddpm_renorm[:,1].min():.4f}, {ddpm_renorm[:,1].max():.4f}]")

# DDPM samples are (N, 2, H, W) channels-first; Agora/Gaussian are (N, H, W, 1)
ddpm_cib = ddpm_renorm[:, 0]   # (N, H, W)
ddpm_tsz = ddpm_renorm[:, 1]

agora_cib  = cib_maps[:, :, :, 0]
agora_tsz  = tsz_maps[:, :, :, 0]

# gauss_maps shape depends on generation; handle both (N, 2, H, W) and (N, H, W, 2)
if gauss_maps.ndim == 4 and gauss_maps.shape[1] == 2:
    gauss_cib = gauss_maps[:, 0]
    gauss_tsz = gauss_maps[:, 1]
else:
    gauss_cib = gauss_maps[:, :, :, 0]
    gauss_tsz = gauss_maps[:, :, :, 1]

n_maps = min(N_MAPS, len(agora_cib), len(ddpm_cib), len(gauss_cib))
print(f"Using {n_maps} maps from each source for power spectrum estimation")

3 Inspect sample maps

Quick sanity check: print pixel statistics and display a randomly selected Agora and DDPM patch side-by-side. Useful for catching normalisation errors or loading mistakes before the expensive power-spectrum computation.

[13]:
cib_ddpm_1 = ddpm_tsz[3]
print(np.min(cib_ddpm_1))
print(np.max(cib_ddpm_1))
print(np.mean(cib_ddpm_1))
print(np.std(cib_ddpm_1))
plt.imshow(cib_ddpm_1)
plt.colorbar()
-5.1153903
-1.3764648
-4.0962086
0.754244
[13]:
<matplotlib.colorbar.Colorbar at 0x7fafd83c5fd0>
../_images/tutorials_06_power_spectra_7_2.png
[14]:
cib_agora_1 = agora_tsz[4,:,:]
print(np.min(cib_agora_1))
print(np.max(cib_agora_1))
print(np.mean(cib_agora_1))
print(np.std(cib_agora_1))
plt.imshow(cib_agora_1)
plt.colorbar()
-17.703312607014137
9.040638558318697
0.061315292845084246
2.2010691284847526
[14]:
<matplotlib.colorbar.Colorbar at 0x7fafd2dd3490>
../_images/tutorials_06_power_spectra_8_2.png
[ ]:
# Compute binned power spectra over the test set.
# mean_cls returns (el, mean_cl, std_cl) — the standard deviation across N maps
# gives the sample variance, not the noise on the mean.  Divide by sqrt(N) for
# the error on the mean if comparing to theory predictions.
el, cl_cib_agora,  cl_cib_agora_err  = mean_cls(agora_cib[:n_maps],  flatskymapparams, LMIN, LMAX, LBIN)
_,  cl_cib_ddpm,   cl_cib_ddpm_err   = mean_cls(ddpm_cib[:n_maps],   flatskymapparams, LMIN, LMAX, LBIN)
_,  cl_cib_gauss,  cl_cib_gauss_err  = mean_cls(gauss_cib[:n_maps],  flatskymapparams, LMIN, LMAX, LBIN)

_,  cl_tsz_agora,  cl_tsz_agora_err  = mean_cls(agora_tsz[:n_maps],  flatskymapparams, LMIN, LMAX, LBIN)
_,  cl_tsz_ddpm,   cl_tsz_ddpm_err   = mean_cls(ddpm_tsz[:n_maps],   flatskymapparams, LMIN, LMAX, LBIN)
_,  cl_tsz_gauss,  cl_tsz_gauss_err  = mean_cls(gauss_tsz[:n_maps],  flatskymapparams, LMIN, LMAX, LBIN)

_,  cl_cross_agora,  _  = mean_cross_cls(agora_cib[:n_maps], agora_tsz[:n_maps], flatskymapparams, LMIN, LMAX, LBIN)
_,  cl_cross_ddpm,   _  = mean_cross_cls(ddpm_cib[:n_maps],  ddpm_tsz[:n_maps],  flatskymapparams, LMIN, LMAX, LBIN)
_,  cl_cross_gauss,  _  = mean_cross_cls(gauss_cib[:n_maps], gauss_tsz[:n_maps], flatskymapparams, LMIN, LMAX, LBIN)

4 Compute power spectra

mean_cls loops over N patches, calls map2cl for each, and returns the mean and standard deviation of the power spectrum across the sample. mean_cross_cls similarly computes the cross-spectrum between the CIB and tSZ channels. A positive cross-spectrum indicates that CIB-bright and tSZ-bright regions are spatially correlated — expected because both tracers peak in massive halos.

[16]:
fig, axes = plt.subplots(1, 3, figsize=(15, 5))


for ax, cl_a, err_a, cl_d, err_d, cl_g, title in zip(
    axes,
    [cl_cib_agora,   cl_tsz_agora,   cl_cross_agora],
    [cl_cib_agora_err, cl_tsz_agora_err, np.zeros_like(cl_cross_agora)],
    [cl_cib_ddpm,    cl_tsz_ddpm,    cl_cross_ddpm],
    [cl_cib_ddpm_err,  cl_tsz_ddpm_err,  np.zeros_like(cl_cross_ddpm)],
    [cl_cib_gauss,   cl_tsz_gauss,   cl_cross_gauss],
    [r"$C_\ell^{\rm CIB}$", r"$C_\ell^{\rm tSZ}$", r"$C_\ell^{\rm CIB \times tSZ}$"],
):

    ax.fill_between(el, cl_a - err_a, cl_a + err_a, alpha=0.3, color="C0")
    ax.fill_between(el, cl_d - err_d, cl_d + err_d, alpha=0.3, color="C1")
    ax.plot(el, cl_a, label="Agora", color="C0")
    ax.plot(el, cl_d, label="DDPM", color="C1")
    ax.plot(el, cl_g, label="Gaussian", color="C2", linestyle="--")
    ax.set_xlabel(r"$\ell$");  ax.set_ylabel(title);  ax.legend()
    ax.set_yscale("log")

plt.tight_layout()
plt.show()
../_images/tutorials_06_power_spectra_11_0.png

5 Results and residuals

Plot the mean power spectra with 1σ error bands (sample variance across N patches). The residual panel shows (Agora − DDPM) / σ_Agora in units of the Agora sample variance: values within ±2 indicate agreement within the Agora statistical scatter. Persistent bias at high ℓ would indicate the DDPM has not learnt the correct small-scale power.

[17]:
# Fractional residuals normalised by Agora sample variance

resid_cib = (cl_cib_agora - cl_cib_ddpm) / (cl_cib_agora_err + 1e-30)
resid_tsz = (cl_tsz_agora - cl_tsz_ddpm) / (cl_tsz_agora_err + 1e-30)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.axhline(0, color="k", lw=0.8, ls="--")
ax1.plot(el, resid_cib)
ax1.set_xlabel(r"$\ell$");  ax1.set_ylabel(r"$(C_\ell^{\rm Agora} - C_\ell^{\rm DDPM}) / \sigma$")
ax1.set_title("CIB residuals")

ax2.axhline(0, color="k", lw=0.8, ls="--")
ax2.plot(el, resid_tsz)
ax2.set_xlabel(r"$\ell$");  ax2.set_title("tSZ residuals")
plt.tight_layout();  plt.show()

../_images/tutorials_06_power_spectra_13_0.png
[ ]: