Source code for hmfast.halos.concentration

import jax
import jax.numpy as jnp
from functools import partial
from abc import ABC, abstractmethod

from hmfast.halos.massdef import MassDefinition



[docs] class Concentration(ABC): """ Parent concentration class from which concentration-mass relations inherit. Child classes must implement :meth:`c_delta`. """
[docs] @abstractmethod @partial(jax.jit, static_argnums=(0, 4)) def c_delta(self, cosmology, m, z, mass_def=None): """Required concentration evaluator.""" pass
[docs] class ConstantConcentration(Concentration): """ Constant concentration-mass relation. Agnostic to the choice of mass definition. The concentration parameter :math:`c_\\Delta` is fixed to a user-specified value for all halos. """ def __init__(self, c): self.c = c pass
[docs] @partial(jax.jit, static_argnums=(0, 4)) def c_delta(self, cosmology, m, z, mass_def=MassDefinition(delta=200, reference="critical")): """ Returns a constant value for the concentration parameter, broadcast to the shape of the input masses and redshifts. Parameters ---------- cosmology : Cosmology Cosmology used to evaluate the concentration relation. m : array-like Halo masses in physical :math:`M_\\odot`. z : array-like Redshifts. mass_def : MassDefinition, optional Target halo mass definition. Included for API consistency. Returns ------- float or array-like Concentration values with shape :math:`(N_m, N_z)`, where singleton dimensions get squeezed before return. """ return jnp.squeeze(jnp.broadcast_to(self.c, (len(jnp.atleast_1d(m)), len(jnp.atleast_1d(z)))))
[docs] class D08Concentration(Concentration): """ Concentration-mass relation from `Duffy et al. (2008) <https://ui.adsabs.harvard.edu/abs/2008MNRAS.390L..64D/abstract>`_. The fitted relation is .. math:: c_\\Delta(M, z) = A \\left(\\frac{M}{M_\\mathrm{pivot}}\\right)^B (1+z)^C where :math:`A`, :math:`B`, :math:`C`, and :math:`M_\\mathrm{pivot}` are fit parameters. Calibrated for 200c, 200m, and virial mass definitions. """ def __init__(self): pass
[docs] @partial(jax.jit, static_argnums=(0, 4)) def c_delta(self, cosmology, m, z, mass_def=MassDefinition(delta=200, reference="critical")): """ Compute the concentration parameter. Parameters ---------- cosmology : Cosmology Cosmology used to evaluate the concentration relation. m : array-like Halo masses in physical :math:`M_\\odot`. z : array-like Redshifts. mass_def : MassDefinition, optional Target halo mass definition. Defaults to ``MassDefinition(delta="vir", reference="critical")``. Returns ------- float or array-like Concentration values with shape :math:`(N_m, N_z)`, where singleton dimensions get squeezed before return. """ m, z = jnp.atleast_1d(m), jnp.atleast_1d(z) h = cosmology.H0 / 100.0 m_internal = m * h mdef = mass_def # Parameter Lookup Table coeffs = { (200, "critical"): (5.71, -0.084, -0.47, 2e12), (200, "mean"): (10.14, -0.081, -1.01, 2e12), ("vir", "critical"): (7.85, -0.081, -0.71, 2e12), } key = (mdef.delta, mdef.reference) if key not in coeffs: raise ValueError(f"Mass definition {key} incompatible with the selected concentration-mass relation.") A, B, C, M_pivot = coeffs[key] return jnp.squeeze(A * (m_internal[:, None] / M_pivot)**B * (1 + z[None, :])**C)
[docs] class B13Concentration(Concentration): """ Concentration-mass relation from `Bhattacharya et al. (2013) <https://ui.adsabs.harvard.edu/abs/2013ApJ...766...32B/abstract>`_. The fitted relation is .. math:: c_\\Delta(M, z) = A D(z)^B \\nu^C where :math:`D(z)` is the linear growth factor and :math:`\\nu(M, z) = \\frac{\\delta_c}{\\sigma(M, z)}`, with :math:`\\delta_c \\approx 1.686` and :math:`\\sigma(M, z)` the linear-theory variance of the density field smoothed on the mass scale :math:`M`. Calibrated for 200c, 200m, and virial mass definitions. """ def __init__(self): pass
[docs] @partial(jax.jit, static_argnums=(0, 4)) def c_delta(self, cosmology, m, z, mass_def=MassDefinition(delta=200, reference="critical")): """ Compute the concentration parameter. Parameters ---------- cosmology : Cosmology Cosmology used to evaluate the concentration relation. m : array-like Halo masses in physical :math:`M_\\odot`. z : array-like Redshifts. mass_def : MassDefinition, optional Target halo mass definition. Defaults to ``MassDefinition(delta="vir", reference="critical")``. Returns ------- float or array-like Concentration values with shape :math:`(N_m, N_z)`, where singleton dimensions get squeezed before return. """ m, z = jnp.atleast_1d(m), jnp.atleast_1d(z) mdef = mass_def # Parameter Lookup Table coeffs = { (200, "critical"): (5.9, 0.54, -0.35), (200, "mean"): (9.0, 1.15, -0.29), ("vir", "critical"): (7.7, 0.9, -0.29), } key = (mdef.delta, mdef.reference) D = jnp.atleast_1d(cosmology.growth_factor(z)) def compute_c(masses, redshifts, A, B, C): masses = jnp.asarray(masses) redshifts = jnp.atleast_1d(redshifts) sigma_m = jnp.reshape(cosmology.sigma_m(masses, redshifts), (len(masses), len(redshifts))) delta_c = jnp.atleast_1d(cosmology.delta_c(redshifts, prescription="EdS"))[None, :] nu = delta_c / sigma_m return A * D[None, :]**B * nu**C if key not in coeffs: raise ValueError(f"Mass definition {key} incompatible with the selected concentration-mass relation.") A, B, C = coeffs[key] return jnp.squeeze(compute_c(m, z, A, B, C))
[docs] class K11Concentration(Concentration): """ Concentration-mass relation from `Klypin et al. (2011) <https://ui.adsabs.harvard.edu/abs/2011ApJ...740..102K/abstract>`_. The fitted relation is .. math:: c_\\mathrm{vir}(M, z) = c_0(z) \\left(\\frac{M}{10^{12} \\; M_\\odot / h}\\right)^{-0.075} \\left[1 + \\left(\\frac{M}{M_0(z)}\\right)^{0.26}\\right] where :math:`c_0(z)` and :math:`M_0(z)` are interpolated from the values in Table 3 of the reference. Calibrated for the virial mass definition only. """ def __init__(self): pass
[docs] @partial(jax.jit, static_argnums=(0, 4)) def c_delta(self, cosmology, m, z, mass_def=MassDefinition(delta=200, reference="critical")): """ Compute the concentration parameter. Parameters ---------- cosmology : Cosmology Cosmology used to evaluate the concentration relation. m : array-like Halo masses in physical :math:`M_\\odot`. z : array-like Redshifts. mass_def : MassDefinition, optional Target halo mass definition. Defaults to ``MassDefinition(delta="vir", reference="critical")``. Returns ------- float or array-like Concentration values with shape :math:`(N_m, N_z)`, where singleton dimensions get squeezed before return. """ m, z = jnp.atleast_1d(m), jnp.atleast_1d(z) h = cosmology.H0 / 100.0 m_internal = m * h mdef = mass_def key = (mdef.delta, mdef.reference) if key != ("vir", "critical"): raise ValueError(f"Mass definition {key} incompatible with the selected concentration-mass relation.") z_tab = jnp.array([0.0, 0.31578947, 0.63157895, 0.94736842, 1.26315789, 1.57894737, 1.89473684, 2.21052632, 2.52631579, 2.84210526, 3.15789474, 3.47368421, 3.78947368, 4.10526316, 4.42105263, 4.73684211, 5.05263158, 5.36842105, 5.68421053, 6.0]) c0_tab = jnp.array([9.6, 7.89848895, 6.57388797, 5.59198421, 4.82413741, 4.2543651, 3.80201899, 3.4341066, 3.15047911, 2.92643281, 2.74396076, 2.60306296, 2.50373941, 2.44412709, 2.40000661, 2.36392585, 2.33588481, 2.31588348, 2.30392188, 2.3]) lnM0_tab = jnp.array([45.46291469, 41.51644832, 38.29554435, 35.80033605, 34.03132449, 32.96668373, 32.12518764, 31.309971, 30.52126833, 29.79801323, 29.16858499, 28.6329836, 28.19120908, 27.84158345, 27.56229323, 27.34662656, 27.19458346, 27.10616391, 27.08136792, 27.12019549]) c0 = jnp.interp(z, z_tab, c0_tab) m0 = jnp.exp(jnp.interp(z, z_tab, lnM0_tab)) return jnp.squeeze( c0[None, :] * (m_internal[:, None] / 1e12) ** (-0.075) * (1.0 + (m_internal[:, None] / m0[None, :]) ** 0.26) )