Observable quantities
This page shows observable quantities that can be derived from solutions of the Einstein-Boltzmann system, such as power spectra and distances.
Primordial power spectra
SymBoltz.spectrum_primordial — Function
spectrum_primordial(k, h, As, ns=1.0; kp = 0.05*(L100/h))Compute the primordial power spectrum
\[P₀(k) = 2π² Aₛ (k/kₚ)^{nₛ-1} / k³\]
with spectral amplitude As, spectral index ns and pivot scale wavenumber kp at the wavenumber(s) k. All wavenumbers are in units of $H₀/c$, and the default pivot scale is 0.05/Mpc.
Example
using SymBoltz, Plots
M = ΛCDM()
pars = Dict(M.g.h => 0.7, M.I.As => 2e-9, M.I.ns => 0.95)
ks = 10 .^ range(-2, +4, length=100)
Ps = spectrum_primordial(ks, M, pars)
plot(log10.(ks), log10.(Ps); xlabel = "log10(k / (H₀/c))", ylabel = "log10(P / (c/H₀)³)")Matter power spectra
SymBoltz.spectrum_matter — Function
spectrum_matter([modes,] prob::CosmologyProblem, k[, τ]; kwargs...)Compute the matter power spectrum
\[P(k,τ) = P₀(k) |Δ(k,τ)|²\]
of the total gauge-invariant overdensity
\[Δ = (∑ₛρₛΔₛ) / (∑ₛρₛ)\]
for one or more modes at wavenumbers k and conformal time(s) τ from the problem prob. The problem is solved for the given $k$, and the matter power spectrum is saved at the given $τ$.
modesmust be:c(CDM),:b(baryons),:h(massive neutrinos),:m(matter; equivalent to $c+b+h$), a vector thereof, or unspecified to use:m.kmust be a vector of wavenumbers in units of $H₀/c$.τmust be a single or a vector of conformal times, or unspecified to use $τ = τ₀$ today.kwargs...are keyword arguments that are forwarded tosolve(prob, k; kwargs...).
spectrum_matter([modes,] sol::CosmologySolution, k[, τ]; kwargs...)Compute the matter power spectrum in the same way, but interpolate between wavenumbers and times already stored in the solution sol.
SymBoltz.spectrum_matter_nonlinear — Function
spectrum_matter_nonlinear(sol::CosmologySolution, k)Compute the nonlinear matter power spectrum from the cosmology solution sol at wavenumber(s) k using halofit implemented in MatterPower.jl.
Example
With explicitly chosen wavenumbers:
using SymBoltz, Plots
M = ΛCDM()
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
ks = 10 .^ range(-2, +5, length=100)
sol = solve(prob, ks)
# Linear power spectrum
modes = [:m, :cb, :h]
Ps = spectrum_matter(modes, sol, ks)
plot(
log10.(ks), transpose(log10.(Ps));
xlabel = "log10(k / (H₀/c))", ylabel = "log10(P / (c/H₀)³)",
label = permutedims("linear (SymBoltz), " .* string.(modes)), ylims = (-15, -6),
linestyle = [:solid :dash :dot :dashdot :dashdotdot], legend_position = :bottomleft
)
# Nonlinear power spectrum (from halofit)
Ps = spectrum_matter_nonlinear(sol, ks)
plot!(
log10.(ks), log10.(Ps);
label = "non-linear (halofit), matter", legend_position = :bottomleft
)As a function of conformal time and redshift:
τs = range(sol[M.τ][end], 0.5, length=10)
Ps = spectrum_matter(sol, ks, τs)
zs = sol(M.g.z, τs) # corresponding redshifts
plot(
log10.(ks), transpose(log10.(Ps));
xlabel = "log10(k / (H₀/c))", ylabel = "log10(P / (c/H₀)³)",
label = permutedims("z=".*string.(round.(zs;digits=1))),
legend_position = :bottomleft
)CMB power spectra
SymBoltz.spectrum_cmb — Function
spectrum_cmb(ΘlAs::AbstractMatrix, ΘlBs::AbstractMatrix, P0s::AbstractVector, ls::AbstractVector, ks::AbstractVector; normalization = :Cl, thread = true)Compute the angular power spectrum
\[Cₗᴬᴮ = (2/π) ∫\mathrm{d}k \, k² P₀(k) Θₗᴬ(k,τ₀) Θₗᴮ(k,τ₀)\]
for the given ls. If normalization == :Dl, compute $Dₗ = Cₗ l (l+1) / 2π$ instead.
spectrum_cmb(modes::AbstractVector{<:Symbol}, prob::CosmologyProblem, jl::SphericalBesselCache; normalization = :Cl, kinterp = nothing, Δkτ0 = 2π/4, xs = cosgrid(0.0, 1.0; length=1200), l_limber = 11, bgalg = default_bgalg(prob), bgreltol = 1e-7, bgabstol = 1e-7, bgopts = (), ptalg = default_ptalg(prob), ptreltol = 1e-5, ptabstol = 1e-5, ptopts = (), thread = true, verbose = false, kwargs...)Compute angular CMB power spectra $Cₗᴬᴮ$ at angular wavenumbers ls from the cosmological problem prob. The requested modes are specified as a vector of symbols in the form :AB, where A and B are T (temperature), E (E-mode polarization) or ψ (lensing). The spectra are of dimensionless temperature fluctuations relative to the present photon temperature $T_{γ0}$; multiply by $T_{γ0}^2$ to get dimensionful spectra. Returns a matrix of $Cₗ$ if normalization is :Cl, or $Dₗ = l(l+1)/2π$ if normalization is :Dl.
Precision parameters
xs: Grid of $(τ-τᵢ)/(τ₀-τᵢ)$ specifying the $τ$-points that will be sampled in line-of-sight integration (also if the independent variable is not $τ$), or an integer number of points interpolated from the background time steps.kinterp: Interpolator that decides which $k$-modes the perturbation ODEs will be solved explicitly for, and then interpolated in-between to a finer grid set byΔkτ0.Δkτ0: Grid spacing to use when integrating over $k$ to project to $ℓ$-space.l_limber: Use Limber approximation for lensing line-of-sight integrals with equal or greater $ℓ$.bgalg/ptalg,bgreltol/ptreltol,bgabstol/ptabstol: ODE algorithms and tolerances for the background/perturbation stages.bgopts/ptopts: extra options for the background/perturbation ODE solves.
Examples
using SymBoltz
M = ΛCDM()
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
ls = 10:10:1000
jl = SphericalBesselCache(ls)
modes = [:TT, :TE, :ψψ, :ψT]
Dls = spectrum_cmb(modes, prob, jl; normalization = :Dl)spectrum_cmb(modes::AbstractVector, prob::CosmologyProblem, jl::SphericalBesselCache, ls::AbstractVector; kwargs...)Same, but compute the spectrum properly only for jl.l and then interpolate the results to all ls.
Example
using SymBoltz, Plots
M = ΛCDM()
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
ls = 25:25:3000 # 25, 50, ..., 3000
jl = SphericalBesselCache(ls)
modes = [:TT, :EE, :TE, :ψψ, :ψT, :ψE]
Dls = spectrum_cmb(modes, prob, jl; normalization = :Dl)
plot(ls, log10.(abs.(Dls)); xlabel = "l", ylabel = "lg(Dₗ)", label = permutedims(String.(modes)))Two-point correlation function
SymBoltz.correlation_function — Function
correlation_function(sol::CosmologySolution; N = 2048, spline = true)Compute the two-point correlation function in real space by Fourier transforming the matter power spectrum of sol with N points the FFTLog algorithm implemented in TwoFAST. Returns N radii and correlation function values (e.g. r, ξ).
Example
using SymBoltz, Plots
M = ΛCDM()
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
ks = 10 .^ range(-2, +6, length=300)
sol = solve(prob, ks)
rs, ξs = correlation_function(sol)
rs = rs * L100 # convert from c/H₀ to Mpc/h
plot(rs, @. ξs * rs^2; xlims = (0, 200), xlabel = "r / (Mpc/h)", ylabel = "r² ξ / (Mpc/h)²")Matter density fluctuations
SymBoltz.variance_matter — Function
variance_matter(sol::CosmologySolution, R)Compute the variance $⟨δ²⟩$ of the linear matter density field with a top-hat filter with radius R in units of $c/H₀$. Wraps the implementation in MatterPower.jl.
SymBoltz.stddev_matter — Function
stddev_matter(sol::CosmologySolution, R)Compute the standard deviation $√(⟨δ²⟩)$ of the linear matter density field with a top-hat filter with radius R in units of $c/H₀$.
using SymBoltz, Plots
M = ΛCDM()
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
ks = 10 .^ range(-2, +6, length=300)
sol = solve(prob, ks)
Rs = 10 .^ range(0.5, 2.5, length=100) # Mpc/h
σs = stddev_matter.(sol, Rs / L100) # Mpc/h to c/H₀
plot(log10.(Rs), log10.(σs); xlabel = "lg(R / (Mpc/h))", ylabel = "lg(σ)", label = nothing)
σ8 = stddev_matter(sol, 8 / L100) # 8 Mpc/h to c/H₀
scatter!((log10(8), log10(σ8)), series_annotation = text(" σ₈ = $(round(σ8; digits=3))", :left), label = nothing)Distance measures
SymBoltz.distance_luminosity — Function
distance_luminosity(χ, a, h, Ωk0 = 0)Compute luminosity distances (in meters)
\[d_L = \frac{c}{H_0} \frac{r}{a}, \quad \mathrm{where} \quad r = \chi \, \frac{\sin\left(\sqrt{-Ω_{k0}} \, \chi\right)}{\sqrt{-Ω_{k0}} \, \chi},\]
from conformal lookback times χ, scale factors a, Hubble parameter h and curvature density Ωk0.
using SymBoltz, Plots
M = RMΛ(K = SymBoltz.curvature(SymBoltz.metric()))
pars = Dict(
M.r.Ω₀ => 5e-5,
M.m.Ω₀ => 0.3,
M.K.Ω₀ => 0.1,
M.r.T₀ => NaN,
M.g.h => 0.7
)
prob = CosmologyProblem(M, pars)
sol = solve(prob)
zs = 0.0:1.0:10.0
τs = SymBoltz.timeseries(sol, M.g.z, zs) # times at given redshifts
dLs = distance_luminosity(sol(M.χ, τs), sol(M.g.a, τs), sol[M.g.h], sol[M.K.Ω₀]) / SymBoltz.Gpc
plot(zs, dLs; marker=:dot, xlabel="z", ylabel="dL / Gpc", label=nothing)Sound horizon (BAO scale)
SymBoltz.sound_horizon — Function
sound_horizon(sol::CosmologySolution)Cumulatively integrate the sound horizon
\[ rₛ(τ) = ∫_0^τ dτ cₛ = ∫_0^τ \frac{dτ}{√(3(1+3ρ_b/4ρ_γ))}\]
to the time steps of the solution sol.
using SymBoltz, Plots
M = ΛCDM()
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
sol = solve(prob)
τs = sol[M.τ]
rs = sound_horizon(sol)
plot(τs, rs; xlabel = "τ / H₀⁻¹", ylabel = "rₛ / (c/H₀)")Source functions
SymBoltz.source_grid — Function
source_grid(
prob::CosmologyProblem, Ss, τs, ks[, bgsols];
bgalg = default_bgalg(prob), bgreltol = 1e-7, bgabstol = 1e-7, bgopts = (),
ptalg = default_ptalg(prob), ptreltol = 1e-5, ptabstol = 1e-5, ptopts = (),
thread = true, verbose = false
)Compute and evaluate source functions $S(τ,k)$ with symbolic expressions Ss on a grid with conformal times τs and wavenumbers ks from the problem prob. Returns a matrix of size (Nτ, Nk), where each element is a vector of length NS = length(Ss) holding all source values at that (τ, k) point.
The algorithms bgalg/ptalg, tolerances bgreltol/ptreltol and bgabstol/ptabstol and extra options bgopts/ptopts are passed to the background/perturbation ODE solves. If the background solutions bgsols are passed, they are used directly and the background options are not accepted.
using SymBoltz, Plots, DataInterpolations
M = ΛCDM(h = nothing, ν = nothing)
pars = parameters_Planck18(M)
prob = CosmologyProblem(M, pars)
sol = solve(prob)
τs = sol[M.τ] # conformal times in background solution
ks = exp.(range(log(1.0), log(2000.0), length = 50)) # logarithmic k-grid
Ss = source_grid(prob, M.ST, τs, ks)
iτ = argmax(sol[M.b.v]) # index of decoupling time
iτs = iτ-75:iτ+75 # indices around decoupling
p1 = surface(ks, τs[iτs], Ss[iτs, :]; camera = (45, 25), xlabel = "k", ylabel = "τ", zlabel = "S", colorbar = false)
lgas = -6.0:0.2:0.0
τs = LinearInterpolation(sol[M.τ], sol[log10(M.g.a)])(lgas) # τ at given lg(a)
ks = 5.0:5.0:100.0
Ss = source_grid(prob, M.g.Ψ, τs, ks)
p2 = wireframe(ks, lgas, Ss; camera = (75, 20), xlabel = "k", ylabel = "lg(a)", zlabel = "Φ")
plot(p1, p2)Line-of-sight integration
SymBoltz.los_integrate — Function
los_integrate(Ss::AbstractMatrix{T}, ls::AbstractVector, τs::AbstractVector, ks::AbstractVector, jl::SphericalBesselCache; l_limber = typemax(Int), thread = true, verbose = false) where {T}For the given ls and ks, compute the line-of-sight integrals
\[Iₗ(k) = ∫dτ S(k,τ) jₗ(k(τ₀-τ))\]
over the source function values Ss against the spherical Bessel functions $jₗ(x)$ cached in jl. The element Ss[i,j] holds the source function value $S(τᵢ, kⱼ)$. The Limber approximation
\[Iₗ ≈ √(π/(2l+1)) S(τ₀-(l+1/2)/k, k)\]
is used for l ≥ l_limber.