The spectrum¶
emu_pk returns the linear matter power spectrum for a cosmology and a
redshift. The parameter vector is in emu_pk.box.PARAMS order:
import numpy as np
from emu_pk import PkEmulator, box
print(box.PARAMS)
# ('omega_b', 'omega_cdm', 'h', 'n_s', 'ln10A_s', 'sum_mnu', 'w0', 'wa')
emu = PkEmulator()
k = np.logspace(-4, 2, 400) # h/Mpc
theta = np.array([0.02237, 0.1200, 0.6736, 0.9649, 3.044, 0.06, -1.0, 0.0])
pk = emu.pk(k, z=0.0, params=theta)

Left: \(P_m(k,z)\) at the Planck 2018 cosmology across the trained redshift range. Right: the same five curves, as a fractional residual against a CLASS solve of the same cosmology. The shaded band is the range the accuracy claims are scored over, \(k \in [10^{-3}, 10]\ h\,\mathrm{Mpc}^{-1}\).
Two things to read off it. The residual is well under a tenth of a percent across six decades of \(k\) and the whole redshift range, and its structure is concentrated at the acoustic scale, \(k \approx 0.1\)–\(0.5\ h\,\mathrm{Mpc}^{-1}\), where the spectrum has the most features per decade and where any emulator works hardest.
Note
This is the fiducial cosmology, which sits near the middle of the training box where the emulator is at its best. It is not a held-out average, and the residual here is smaller than the accuracy you should assume. For that, see Accuracy, which scores a held-out design and reports a median of 0.111 %.
Two spectra¶
pk_m = emu.pk(k, 0.0, theta) # cdm + baryons + massive neutrinos
pk_cb = emu.pk_cb(k, 0.0, theta) # cdm + baryons only
cb is cold dark matter and baryons. Neutrinos do not fall into haloes
below their free-streaming scale, so the field that collapses is the cold one
and \(\sigma(M)\) is better built from \(P_{cb}\); lensing and anything responding
to all the mass wants \(P_m\). See Quickstart for the relation between
them.
They come from one network with two output heads, which is what stops them drifting apart in a way that would show up downstream as a spurious cold-versus-total effect. With \(\Sigma m_\nu = 0\) they are identical, because with no massive species the cold field and the total field are the same field.
Many redshifts¶
Redshift is a network input, not a separate model, so asking for several costs one vectorised evaluation:
z = np.linspace(0, 5, 21)
pk = emu.pk(k, z, theta) # (21, 400)
Above the grid¶
The training grid reaches \(k = 200\ h\,\mathrm{Mpc}^{-1}\), which is what a
halo-model \(\sigma(M)\) integral needs. Above it the emulator continues as a
power law rather than clamping — jnp.interp would hold the last value, and a
clamped linear spectrum is flat where it should be falling as
\(k^{-3}\ln^2 k\). That is a safety net here rather than a load-bearing
extrapolation, but it is a net and not a cliff.