emu_pk.ratio¶
The CLASS-distilled correction to a massless-LambdaCDM linear spectrum.
A network trained on massless LambdaCDM cannot be asked for a spectrum with neutrinos in it, and gets no response at all to dark energy. This module supplies both corrections, as ratios measured from CLASS on a grid and interpolated:
The neutrino part is indexed by \(f_\nu = \Omega_\nu/\Omega_m\) rather than by \(\Sigma m_\nu\), so one table serves every \(h\) and \(\Omega_m\) instead of being tied to the cosmology it was built at.
Both ratios are exactly 1 at the LambdaCDM massless corner – the stored
log-ratio is exactly zero at \(f_\nu = 0\), \(w_0 = -1\), \(w_a =
0\) – so the correction is applied unconditionally, with no if sum_mnu > 0
branch. That is not tidiness: a Python branch on \(\Sigma m_\nu\) is a
branch on a value that is a tracer under jax.grad, and would break the
gradient the whole differentiable path exists to provide.
Factorisation¶
The correction ships as two factors, neutrinos and dark energy,
because the full five-axis cube needs 16 derivative arrays for a tensor-product
Hermite and the factors need 4 and 8 over much smaller cubes – megabytes
against hundreds. Whether that is allowed is not assumed:
build_ratio() runs
the full grid regardless, measures the largest residual of the factorisation
against it, and stores the number in the table as resid_max. If it is not
comfortably below the emulator’s own shape error the factorisation is the wrong
call, and the number is there to say so rather than to be trusted.
Validity¶
Outside the grid the interpolation would clamp, silently understating the
correction, so suppression_m() raises instead. A plausible wrong number
is worse than an exception. The check calls float(), which raises on a
tracer, so it is skipped under tracing rather than attempted – a jitted
forward model is checked once when it is built, outside the trace.
The table is regenerated in two steps, with classy installed – solve the
grid, then assemble it:
python -m emu_pk.generate --mode ratio --shard 0 --n-per-shard 300 --out shards_ratio
python -m emu_pk.assemble --mode ratio --shards shards_ratio
The shipped .npz means ordinary use never needs either.
- emu_pk.ratio.load(path=None)[source]¶
Resolve the path first, then hit the cache.
lru_cachekeys on the call signature, soload()andload(None)are two different keys and would build – and hold – two independent copies of a table that is over a hundred megabytes once its derivative arrays exist. Resolving to a single canonical string before the cache is what makes them the same call.- Return type:
- emu_pk.ratio.suppression_m(k, f_nu, z=0.0, w0=-1.0, wa=0.0, table=None)[source]¶
\(P_m(k,z;\Sigma m_\nu,w_0,w_a)/P_m(k,z;0,-1,0)\).
Exactly 1 at the LambdaCDM massless corner.
- emu_pk.ratio.suppression_cb(k, f_nu, z=0.0, w0=-1.0, wa=0.0, table=None)[source]¶
\(P_{cb}(k,z;\Sigma m_\nu,w_0,w_a)/P_m(k,z;0,-1,0)\).
Note the denominator is the massless total, not the massive total: this converts a massless-trained spectrum straight to the cold one, in a single multiplication, without ever forming the massive total in between.
- emu_pk.ratio.MNU_MAX = 0.6¶
Largest summed neutrino mass the table covers, in eV.
- emu_pk.ratio.Z_MAX = 5.0¶
Largest redshift the table covers.
- emu_pk.ratio.W0_RANGE = (-1.3, -0.7)¶
Range of
w0the table covers, as(low, high). Narrower thanemu_pk.box’s own bounds: the table was built for a different purpose and its grid was sized for that.
- emu_pk.ratio.WA_RANGE = (-0.7, 0.5)¶
Range of
wathe table covers, as(low, high).