Source code for cosmofit.likelihoods.planck
"""
Planck CMB distance-prior likelihood.
Implements the standard "compressed CMB" likelihood used
throughout the dark-energy literature: instead of the full
Planck power-spectrum likelihood, the CMB constraint is reduced
to a 3-vector
p = (R, l_A, omega_b_h2)
R = shift parameter = sqrt(Omega_m) * H0 * D_M(z*) / c
l_A = acoustic scale = pi * D_M(z*) / r_s(z*)
omega_b_h2 = physical baryon density = Omega_b * h^2
evaluated against a Gaussian likelihood with the Planck-derived
mean vector and covariance matrix (see :mod:`data.loader`).
z*, r_s(z*) and the radiation-aware E(z) behind D_M(z*) all come from
:class:`cosmology.calculators.recombination.RecombinationCalculator`,
which follows Chen, Huang & Wang (2019)'s *own* definitions
(their Eqs. 1-6) rather than a more detailed independent
calculation.
That is deliberate and it matters. These priors are not a measurement
of the sky -- they are a compression of Planck's own fit, computed
under a specific set of conventions, and the theory prediction has to
share those conventions or the comparison is inconsistent. An earlier
version of this module did not, and returned chi2 ~ 100 for 3 data
points at *Planck's own best-fit LCDM* (l_A off by -8.9 sigma), which
biased every joint fit that included this dataset. It now returns
chi2 ~ 0.4 there. See the recombination module's docstring for the
full diagnosis.
Validation
----------
The prediction has been cross-checked two ways: against CAMB 2.0.1
(z_star agrees to 0.002%), and against an independent ``scipy.quad``
implementation of the CHW19 recipe across flat/open/closed LCDM and
CPL (R and l_A agree to <0.01 sigma).
References
----------
Chen, Huang & Wang (2019), JCAP 02 (2019) 028, arXiv:1808.05724.
"""
from __future__ import annotations
import numpy as np
from cosmofit.cosmology.core.constants import c as SPEED_OF_LIGHT
from cosmofit.data.loader import load_planck
from .base import BaseLikelihood
#: LaTeX label for each entry of the distance-prior vector, keyed
#: by the identifiers in ``PlanckDataset.labels`` -- for figure
#: axes and tick labels
#: (:meth:`~plots.FitPlotter.planck_residuals`). The dataset keeps
#: the plain identifiers the data file uses; only the display side
#: spells them as symbols.
OBSERVABLE_LABELS = {
"R": r"$R$",
"lA": r"$\ell_A$",
"omega_b_h2": r"$\omega_b h^2$",
}
[docs]
class PlanckLikelihood(BaseLikelihood):
[docs]
def __init__(
self,
cosmology,
version="planck2018",
):
dataset = load_planck(
version,
)
super().__init__(
name="Planck",
dataset=dataset,
cosmology=cosmology,
)
# --------------------------------------------------------
@property
def observable(
self,
):
return "(R, l_A, omega_b_h2)"
# --------------------------------------------------------
def model(
self,
) -> np.ndarray:
"""
Compute the theoretical Planck distance-prior vector
(R, l_A, omega_b_h2) for the current cosmology.
D_M(z*) is obtained from :meth:`RecombinationCalculator.chi_star`,
which integrates chi(z) = int dz/E(z) directly out to
z* ~ 1090, rather than through the cosmology's cached low-z
distance-integrator grid (which only spans z=0..5 by
default, sized for the low-redshift CC/BAO/SN likelihoods).
This keeps the low-z grid's resolution -- and the
performance of every other likelihood -- unaffected by the
CMB's much larger redshift range.
"""
recomb = self.cosmology.recombination
z_star = recomb.z_star()
r_s = recomb.sound_horizon(z_star)
chi_star = recomb.chi_star(z_star)
dm_star = self.cosmology.distance.DM_from_chi(chi_star)
R = (
np.sqrt(self.cosmology.Omega_m)
* self.cosmology.H0
* dm_star
/ SPEED_OF_LIGHT
)
lA = np.pi * dm_star / r_s
omega_b_h2 = recomb.omega_b_h2
return np.array(
[R, lA, omega_b_h2],
dtype=float,
)
# --------------------------------------------------------
def residuals(
self,
) -> np.ndarray:
"""
Compute residuals.
"""
return (
self.data.values
- self.model()
)
# --------------------------------------------------------
def chi2(
self,
) -> float:
"""
Compute chi-square.
"""
return self.covariance.chi2(
self.residuals(),
)