CMB spectra, lensing, and tensors
Unlensed spectra
cmb_spectra assembles $C_\ell^{TT}$, $C_\ell^{EE}$, $C_\ell^{TE}$ by the line-of-sight method: the Boltzmann solution for each $k$ is folded into source functions (Sachs–Wolfe, integrated Sachs–Wolfe, Doppler, polarization — individually switchable for diagnostics) and projected with spherical Bessel functions. In curved geometries the projection uses the hyperspherical radial functions with exact recurrences, for open and closed models alike. Against CLASS, temperature and polarization agree at the 0.3% level.
Resolution matters more than it seems: the acoustic peaks converge only when the linear $k$-grid spacing falls below $\Delta k \approx 1.2\times10^{-5} \,\mathrm{Mpc}^{-1}$, and an under-resolved run produces a plausible-looking spectrum with the first peak shifted — it does not fail loudly. cmb_spectra now measures its own grid against this criterion and warns with the nk it would need. The error also does not cancel between two cosmologies (their $\eta_0$ differ, so their grids alias differently), so ratio comparisons need both sides converged.
Lensing
lensed_cmb_spectra computes the lensing potential $C_\ell^{\phi\phi}$ from the Weyl-potential transfer along the line of sight and applies the exact correlation-function (Challinor–Lewis) remapping — the same non-perturbative method CLASS and CAMB use — producing lensed TT/TE/EE and the pure lensing $BB$. Wigner rotation functions are generated by stable recurrences. Validation against CLASS: lensed TT to 0.3%, EE 0.4%, $C_\ell^{\phi\phi}$ to better than 1%. Two practical notes: the lensing potential needs its own $k_{\max}$ (about $0.7\,\mathrm{Mpc}^{-1}$ for $\ell \lesssim 2000$ — its kernel reaches small distances and hence high $k$), and the lensed $BB$ under-converges in the top few multipoles below the internal buffer, which dl_buffer controls.
Tensors
solve_tensor_perturbations evolves the tensor amplitude with its radiation anisotropic-stress damping, and tensor_cmb_spectra projects TT/EE/BB with the tensor radial functions — again exactly in all three geometries, where the flat and curved projectors are independent code paths tested against each other in the $K \to 0$ limit. The primordial tensor spectrum can come from the consistency relation or from the inflaton module's computed $\Delta_t^2(k)$.
Reference
Cosmic.CMBSpectra — Type
CMBSpectraAngular power spectra. TT, EE, TE are Vectors indexed by ℓ from ℓ_min, in μK² (raw Cℓ, not ℓ(ℓ+1)Cℓ/2π – use D_ℓ for that).
Cosmic.PrimordialMatrix — Type
PrimordialMatrix(modes; amp, tilt, k_pivot = 0.05)Primordial cross-power specification for a set of scalar modes (any of :adiabatic, :cdi, :bi, :nid, :niv). amp and tilt are symmetric n×n matrices over modes; the dimensionless cross power of modes i,j is
𝒫_ij(k) = amp[i,j] · (k/k_pivot)^(tilt[i,j] − 1),so the diagonal holds each mode's auto-amplitude and tilt (the adiabatic entry is just A_s, n_s) and the off-diagonal holds the cross-correlations. The total harmonic spectrum sums every ordered pair,
C_ℓ^XY = 4π Σ_ij ∫ dk/k 𝒫_ij(k) Θ_ℓ^{X,i}(k) Θ_ℓ^{Y,j}(k).amp should be positive semi-definite for a physical spectrum; this is not enforced (an unphysical matrix simply yields C_ℓ that can go negative).
Cosmic.SourceFunction — Type
SourceFunctionThe line-of-sight sources for one mode k, tabulated on a conformal-time grid. S_T is the temperature source, S_E the polarization source (which is just the visibility-weighted quadrupole, g·Π).
Cosmic.D_ℓ — Function
D_ℓ(spec, which = :TT)ℓ(ℓ+1)C_ℓ/2π in μK² – the form the CMB is conventionally plotted in, chosen because a scale-invariant Sachs-Wolfe plateau is flat in it.
Cosmic.cmb_spectra — Method
cmb_spectra(c, rec, prim::PrimordialMatrix; kwargs...)Correlated scalar CMB spectra for a mixture of adiabatic and isocurvature modes, combined through the primordial matrix prim (see PrimordialMatrix). Each mode is evolved with its own initial condition, projected to transfer functions Θℓ^{T,i}, Θℓ^{E,i}, and summed over all mode pairs. Flat geometry only. Returns a CMBSpectra (φφ/Tφ empty).
Passing PrimordialMatrix([:adiabatic]; amp=[A_s;;], tilt=[n_s;;]) reproduces the ordinary adiabatic cmb_spectra.
Cosmic.cmb_spectra — Method
cmb_spectra(c, rec; lmax = 1500, nk = 3000, kmax = nothing, nk_solve = nothing)Compute Cℓ^TT, Cℓ^EE and C_ℓ^TE.
C_ℓ = 4π ∫ dln k Δ²_ℛ(k) Θ_ℓ(k)²Two grids matter and both are easy to under-resolve:
- k. Θ_ℓ(k) oscillates with period ≈ 2π/η₀ ≈ 4.4e-4 /Mpc, so the k-grid must resolve that, not the smooth envelope. Under-sampling does not blur the spectrum, it aliases – producing spurious wiggles that look like physics. The grid is therefore linear at high k, not logarithmic.
- k_max. A multipole ℓ is sourced by k ≈ ℓ/η₀, so kmax must reach beyond ℓmax/η₀ or the highest multipoles are simply missing power.
Cost is one Boltzmann solve per k, threaded.
nk_solve decouples the two grids. The Bessel-scale oscillation above lives in the projection Θℓ(k) = ∫ dη S(k,η) jℓ(k(η₀−η)); the line-of-sight source S(k,η) itself varies in k only on the sound-horizon scale Δk ≈ 2π/rs ≈ 0.04 /Mpc. Passing `nksolve = ntherefore solves the Boltzmann hierarchy on onlynk-points, interpolates the source tables linearly onto the fullnk-point grid, and projects densely — the exact integral, evaluated on interpolated sources.nksolve = 600carries ~80 solve points per source oscillation and reproduces the all-k result to ≲0.1% while cutting the hierarchy cost ~8×. The defaultnothingsolves at every k; tighten by raisingnksolve` and confirming the spectra stop changing, same as any grid knob.
Cosmic.source_function — Method
source_function(c, bg, p; nη = 4000)Build ST and SE for the solved mode p.
The derivatives d(g vb)/dη and d²(g Π)/dη² are taken numerically on the grid. That is safe here because g vb and g Π are smooth and compactly supported – the visibility function kills them away from last scattering – so there is no cancellation to amplify and no tail to lose.
Cosmic.LensedCMBSpectra — Type
LensedCMBSpectraLensed spectra at every integer ℓ from 2 to lmax, in μK². BB is the lensing B-mode generated from E; the unlensed scalar BB is zero.
Cosmic.lensed_cmb_spectra — Method
lensed_cmb_spectra(c, rec; lmax = 2000, dl_buffer = 500, kernel = :lastscatter, ...)Unlensed spectra plus Cℓ^φφ in one mode sweep (`cmbspectra(...; lensing = true)), then the correlation-function lensing. Returns(lensed, unlensed)-- theLensedCMBSpectraand theCMBSpectra` carrying φφ and Tφ.
dl_buffer sets how far past lmax the unlensed spectra are computed, because lensing couples each multipole to its neighbours. TT/TE/EE converge with the default 500 across the whole range; lensed BB is a pure off-diagonal EE→BB transfer that draws power from a broad band of ℓ', so its top ~dl_buffer/2 multipoles are under-converged – with buffer 500, BB is trustworthy to about ℓ = lmax − 300 and falls ~20% low at ℓ = lmax itself. Ask for BB out to some ℓ by giving lmax a few hundred above it, or raise dl_buffer. Reaching φφ (and thus lensed BB) to high ℓ also needs a large kmax in the sweep: the lensing kernel maps ℓ to k ≈ ℓ/χ over a range of χ down to small values, so C_ℓ^φφ at ℓ = 2000 wants kmax ≈ 0.7, not the 2.2·lmax/η₀ that suffices for TT.
Cosmic.lensed_from_cls — Method
lensed_from_cls(TT, EE, TE, φφ; lmax_out, nmu = 0)Lens dense unlensed spectra (vectors indexed 1 ⟺ ℓ = 0, through ℓunlensedmax) with Cℓ^φφ, returning (TT, EE, TE, BB) at ℓ = 2..lmaxout. This is the Challinor-Lewis full-sky correlation-function algorithm; the unlensed input should extend a few hundred multipoles beyond lmax_out.
Cosmic.solve_tensor_perturbations — Method
solve_tensor_perturbations(c, bg, k; ...)Evolve one primordial gravitational-wave mode and the particle hierarchies that source and are sourced by it. For nonzero curvature this uses the exact curved streaming coefficients, curved hierarchy closure, (k²+2K)h propagation term, and curvature-normalised regular initial mode. No flat radial projection is performed by this function.
Cosmic.tensor_cmb_spectra — Method
tensor_cmb_spectra(c, rec; r=0.01, n_t=-r/8, ...)Primordial tensor CMB spectra. r is the tensor-to-scalar ratio at k_pivot; the default tilt is the single-field consistency relation but can be supplied independently. The returned spectra are raw C_l in μK² and include primordial tensor TT, EE, BB, and TE (not lensing-generated scalar BB).