Halo Density Profiles¶
clenspy.halo supplies two 3D density profiles, \(\rho(r)\), and their
Fourier transforms — the raw building blocks projected profiles
(Projected Density Profiles) and the two-halo term
(The Two-Halo Term) are both built from.
\(\rho(r)\) and \(\tilde\rho(k)/M_{200}\) for an NFW halo and an Einasto profile sharing the same \(r_s=r_{200}/c_{200}\) and the same enclosed mass at \(r_{200}\) — both curves divided by the same \(M_{200}\). Truncated NFW and Einasto track closely inside \(r_{200}\) and above \(k\sim1\,{\rm Mpc}^{-1}\); below that, Einasto’s \(\tilde\rho(k)/M_{200}\) plateaus at a finite value above 1 rather than at it — it keeps \(\sim\)87% more mass beyond \(r_{200}\) than the NFW truncation discards, visible as the shallower fall in \(\rho(r)\) past the dotted line. The dotted curve is the untruncated NFW, included to show why NFW cannot be compared to Einasto the same way: rather than plateauing at some finite value like Einasto’s, it keeps climbing as \(k\to0\) with no limit — because, unlike Einasto’s, its total mass is not just larger, it is infinite (see the note below).¶
NFW: two parameters, closed form everywhere¶
The NFW profile is fixed by mass and concentration alone,
with \(\rho_s\) set by requiring the profile to enclose \(M_{200}\) within
\(r_{200}\). NfwProfile carries no cosmology — the mass definition (200
times which density) is entirely the caller’s choice, made once by
passing rho_ref (default: the comoving mean matter density, i.e.
\(M_{200m}\)).
The Fourier transform has a closed form (Cooray & Sheth 2002, Eq. 81), truncated at \(r_{200} = c_{200}r_s\) or left infinite in extent:
or, for the untruncated (infinite-extent) profile,
with \(c\equiv c_{200}\) in both.
Note
NfwProfile.fourier returns \(\tilde\rho(k)\) — carrying units of mass, and
going to \(M\) (not \(1\)) as \(k\to0\) — not the dimensionless mass-normalized
\(u(k\mid M) \equiv \tilde\rho(k)/M\) that most halo-model formulas mean by
“the profile’s Fourier transform.” Divide by the halo’s own m200 at the
call site to get \(u(k\mid M)\); this matches pyccl’s
HaloProfileNFW.fourier convention exactly.
Note
NFW’s \(\rho\propto r^{-3}\) falloff makes its untruncated enclosed mass
\(M(r) = 4\pi\rho_s r_s^3\left[\ln(1+x) - x/(1+x)\right]\) diverge — only
logarithmically, but it never converges as \(r\to\infty\). There is no
finite “NFW total mass” the way there is for Einasto; truncating at
\(r_{200}\) isn’t a numerical convenience, it’s what makes the mass finite
at all. That is why NfwProfile.fourier(k, truncated=False) in the figure
above keeps rising as \(k\to0\) rather than settling on a plateau like
Einasto’s — passing truncated=False with no radius at which to stop
integrating leaves nothing for \(\tilde\rho(k{=}0)\) to converge to.
Einasto: a curved profile with no fixed inner slope¶
The Einasto profile trades the NFW’s fixed asymptotic slopes for a single shape parameter \(\alpha = 1/n\),
with \(h = r_s/(2n)^n\). Its logarithmic slope,
goes to \(0\) as \(r\to0\) — Einasto has no central cusp: \(\rho(r)\)
approaches the finite \(\rho_0\) smoothly, unlike NFW’s \(\rho\propto r^{-1}\)
divergence at small \(r\). EinastoProfile reports the enclosed mass and
total mass in closed form for any \(n\) via the incomplete gamma function,
power_spectrum rescales the same Fourier transform,
evaluated, for \(n>1\) (large-\(k\) series, converges for all \(k\), \(\tilde k\equiv kh\)),
and by an FFTLog transform of density for \(0<n<1\), away from the \(n=1\)
and \(n=1/2\) anchors’ own closed forms. fourier(k) is \((4\pi)^2P(k)\), so
the two methods agree exactly. See Einasto Profile: Mathematics and Current Implementation for the full
per-regime dispatch, the closed forms at \(n=1,1/2\), and the low-\(n\)
numerical stability analysis.
Note
This power_spectrum/\(P(k)\) is internal Einasto machinery, not the
cosmological matter power spectrum of Power Spectrum — same symbol,
unrelated quantity. Like NfwProfile.fourier above, EinastoProfile.fourier
is mass-dimensioned and goes to \(M_{\rm tot}\) (not \(1\)) as \(k\to0\); it is
not the dimensionless, mass-normalized \(u(k\mid M)\) that halo-model
formulas expect. Divide by total_mass at the call site to get \(u(k\mid
M)\), exactly as for NfwProfile.
Note
alpha here is the Einasto shape index, unrelated to the HOD satellite
power-law index or a source-redshift-distribution slope elsewhere in
clenspy — same Greek letter, three unrelated physical quantities.
Example¶
from clenspy.halo import EinastoProfile, NfwProfile
# h-free absolute units: mass in Msun, lengths in Mpc, densities in
# Msun/Mpc^3, wavenumbers in 1/Mpc -- no cosmology object needed, only the
# reference density rho_ref that mass_def is measured against (default:
# the comoving mean matter density, giving M_200m).
m200 = 1e14 # Msun
c200 = 5.0
nfw = NfwProfile(m200=m200, c200=c200)
print(f"r200 = {nfw.r200:.4f} Mpc, rs = {nfw.rs:.4f} Mpc, "
f"rho_s = {nfw.rho_s:.3e} Msun/Mpc^3")
r = np.array([0.1, 0.5, 1.0, 2.0]) # Mpc
print("rho_NFW(r) [Msun/Mpc^3] =", nfw.density(r))
# same r_s as the NFW halo (r_s = r200/c200), rho_0 solved so the enclosed
# mass at r200 matches m200 -- a fair shape-only comparison at fixed mass
# and scale radius. alpha=0.25 is a typical cluster shape (Retana-Montenegro
# et al. 2012 report alpha ~ 0.16-0.25 for clusters).
alpha = 0.25
rho0_unit = EinastoProfile(alpha=alpha, rho_0=1.0, r_s=nfw.rs, tol=1e-4)
rho0 = m200 / rho0_unit.enclosed_mass(nfw.r200)
einasto = EinastoProfile(alpha=alpha, rho_0=rho0, r_s=nfw.rs, tol=1e-4)
print("rho_Einasto(r) [Msun/Mpc^3] =", einasto.density(r))
# fourier() returns rho_tilde(k), the *unnormalized* FT -- units of mass,
# going to M as k -> 0, not the dimensionless mass-normalized u(k|M).
k = np.array([0.1, 1.0, 10.0]) # 1/Mpc
print("rho_tilde_NFW(k) [Msun] =", nfw.fourier(k))
print("rho_tilde_Einasto(k) [Msun] =", einasto.fourier(k))
r200 = 1.4303 Mpc, rs = 0.2861 Mpc, rho_s = 3.547e+14 Msun/Mpc^3
rho_NFW(r) [Msun/Mpc^3] = [5.57109391e+14 2.68756655e+13 5.02012592e+12 7.94381981e+11]
rho_Einasto(r) [Msun/Mpc^3] = [5.83600877e+14 2.77183496e+13 4.86300049e+12 6.13799586e+11]
rho_tilde_NFW(k) [Msun] = [9.98998564e+13 9.05346969e+13 8.91993930e+12]
rho_tilde_Einasto(k) [Msun] = [1.83204136e+14 1.06342641e+14 9.27420738e+12]
See also: API Reference for the full clenspy.halo reference,
Notation for the symbol table, Einasto Profile: Mathematics and Current Implementation for the Einasto
profile’s derivations.