# This file is part of PyCosmo, a multipurpose cosmology calculation tool in Python.
#
# Copyright (C) 2013-2021 ETH Zurich, Institute for Particle and Astrophysics and SIS
# ID.
#
# This program is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this
# program. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from scipy import integrate, special
from .LinearPerturbationBase import LinearPerturbationBase
[docs]class LinearPerturbationApprox(LinearPerturbationBase):
r"""
Class created to manage fitting functions for the computation of the linear matter
power spectrum :math:`P_{lin}(k)`. The fitting functions provide transfer
functions that are then used to compute the power spectrum.
The different fitting functions can be selected using the *set function*:
.. code-block:: python
cosmo.set(pk_type = option)
where *option* can be one the following keywords:
- ``EH`` for `Eisenstein & Hu, 1998, ApJ, 511, 5 (default) <https://arxiv.org/abs/astro-ph/9710252>`_
- ``BBKS`` for BBKS as summarized by `Peacock, 1997, MNRAS, 284, 885 <https://arxiv.org/abs/astro-ph/9608151>`_
**For developers:** in order to compare the codes :math:`\textsf{PyCosmo}` and
:math:`\texttt{CCL}` in terms of linear matter power spectrum computed with
the *BBKS* fitting function, the user should choose a routine which is
optimized for this purpose. This further option can be selected as:
.. code-block:: python
cosmo.set(pk_type = 'BBKS_CCL')
where ``BBKS_CCL`` follows the implementation in the
`CCL code <https://arxiv.org/abs/1812.05995>`_ .
"""
def __init__(self, cosmo):
self._cosmo = cosmo
self._params = cosmo.params
self._background = cosmo.background
self._enrich_params()
[docs] def max_redshift(self, k):
"""computes max redshift for which this model is applicable.
:param k: wavenumber k, introduced in the abstract base class since this
might be needed for some models.
[:math:`h Mpc^{-1}`]
:returns: redshift
"""
k = np.atleast_1d(k)
avec = np.linspace(1e-4, 1, 10000)
ai = avec[np.argmax(self._background._omega_m_a(avec))]
return (1 / ai - 1).repeat(len(k))
def _D1(self, a=1.0, norm=0):
"""
Computes the linear growth factor :math:`D(a)` by integrating the growth
differential equation.
:param a: scale factor [1]
:param norm: normalisation scheme: 0: D(a=1)=1 (default), 1:
D(a)=a in matter era,
.. code-block:: python
cosmo.lin_pert.growth_a(a)
"""
# assert k is None, 'this model {} does not consider k'.format(self)
a = np.atleast_1d(a)
ai = 1.0 / (self.max_redshift(None) + 1)[0]
a_start = min(
np.concatenate((a, np.array([ai])))
) # initial condition for integration
if a_start < ai:
raise ValueError(f"initial a_start={a_start} too high (at max {ai})")
a = np.atleast_1d(a)
a_out = np.concatenate((np.array([a_start]), a, np.array([1.0])))
perm = np.argsort(a_out) # keeping track of index that would sort the array a
inverse_perm = np.argsort(perm) # useful indec for going back to original
a_out = a_out[perm]
y_start = np.array([1.0, 0.0]) # initial conditions: y1=G=1, y2=dG/dlna=0.
x_out = np.log(a_out)
u = integrate.odeint(self._growth_derivs, y_start, x_out)
u0 = u[:, 0][inverse_perm]
if norm == 0:
D = u0[1:-1] * a / u0[-1] # normalise to D=1 at a=1
elif norm == 1:
D = u0[1:-1] * a # normalise to D=a in matter dominated era
else:
raise ValueError("invalid value for norm, must be 0 or 1")
return D
[docs] def growth_a(self, a, k=None, norm=0, diag_only=True):
"""
Returns the linear growth factor by calling a function that
integrates the growth differential equation.
It returns a vector of len(a) if k is None or a number, or if diag_only is set
to True. Otherwise it returns a matrix with dimension (len(k), len(a)).
:param a: scale factor [1]
:param k: wavenumber k, necessary for massive neutrino models
[:math:`h Mpc^{-1}`]
:param norm: normalisation scheme: norm = 0: D(a=1)=1 (default),
norm = 1: D(a)=a in matter era
:param diag_only: if set to False, the growth factor is repeated and
reshaped to match the shape of k
:return: growth factor [1]
"""
if (
self._params.massive_nu_total_mass == 0.0
or self._params.N_massive_nu == 0.0
):
if k is None or np.isscalar(k) or diag_only:
return self._D1(a, norm)
k = np.atleast_1d(k)
return np.repeat(self._D1(a, norm).reshape(1, -1), len(k), axis=0)
if k is None:
raise ValueError(
"You must specify k since growth factor in EH with heavy neutrinos is"
" k dependent!"
)
a = np.atleast_1d(a)
k = np.atleast_1d(k)
if diag_only:
assert a.shape == k.shape, "in diag_only mode a and k must have same length"
growth_nu = self._D1(a, norm=1) * (1.0 + self._params._z_equality)
term_1 = self._D1(a) * growth_nu ** (-self._params._p_cb)
if diag_only:
return term_1 * self._D_cb_diag(growth_nu, k)
return term_1 * self._D_cb(growth_nu, k)
def _growth_derivs(self, y, x):
"""Compute derivatives for the ODE for calcuting the growth factor.
!!!NEED TO INCLUDE REFERENCE!!!
"""
a_temp = np.exp(
np.array(x)
) # ; x=ln(a) #; compute derivatives of y1=G and y2=dG/dlna
f1 = y[1] # f1=dy1/dx=y2=dG/dlna
_dlnh_dlna = self._background._dlnh_dlna(a=a_temp)[0]
f2 = -(4.0 + _dlnh_dlna) * y[1] - (
3.0 + _dlnh_dlna - 1.5 * self._background._omega_m_a(a=a_temp)
) * y[0]
return [f1, f2]
def _growth_derivs_nu(self, y, x):
"""Compute derivatives for the ODE for calcuting the growth factor including
massive neutrinos.
!!!NEED TO INCLUDE REFERENCE!!!
"""
a_temp = np.exp(
np.array(x)
) # ; x=ln(a) #; compute derivatives of y1=G and y2=dG/dlna
f1 = y[1] # f1=dy1/dx=y2=dG/dlna
_dlnh_dlna = self._background._dlnh_dlna_massive_nu(a=a_temp)
f2 = -(4.0 + _dlnh_dlna) * y[1] - (
3.0 + _dlnh_dlna - 1.5 * self._background._omega_m_a(a=a_temp)
) * y[0]
return [f1, f2]
def _growth_hyper_a(self, a=1.0): # , Om=0.25,z=0):
"""
LCDM growth function D(z) using hypergeometric function. Only
valid for LCDM.
This comes from Aseem Paranjape and is used for testing.
; param: a: scale factor
; return: D(a): growth factor normalised to 1 at a=1.
"""
a = np.atleast_1d(a)
a_temp = np.append(a, [1.0])
acube = a_temp**3
hbyh0 = np.sqrt(self._background._H2_H02_a(a=a_temp))
g = (
hbyh0
/ np.sqrt(self._params.omega_m)
* np.power(a_temp, 2.5)
* special.hyp2f1(
5.0 / 6, 1.5, 11.0 / 6, -acube * (1.0 / self._params.omega_m - 1)
)
)
g /= g[-1] # normalised to 1 at a=1.0
return g[:-1]
[docs] def transfer_k(self, k):
r"""
Computes the linear matter transfer function using a choice of the currently
available fitting functions.
:param k: wavenumber :math:`[Mpc^{-1}]`
:return: Matter transfer function :math:`T(k)` in :math:`Mpc^{3/2}`.
"""
if self._params.pk_type == "EH":
if (
self._params.massive_nu_total_mass == 0.0
or self._params.N_massive_nu == 0.0
):
tk = self._transfer_EH(k)
else:
tk = self._T_master(k)
elif self._params.pk_type in ("BBKS", "BBKS_CCL"):
tk = self._transfer_BBKS(k)
else:
print(
"transfer_k: error - only EH and BBKS fitting functions are supported"
)
return tk
[docs] def powerspec_a_k(self, a=1.0, k=0.1, diag_only=False):
"""
Computes the linear total matter power spectrum, :math:`P_{lin}(k)`, using a
choice of fitting functions.
:param a: scale factor [1]
:param k: wavenumber :math:`[Mpc]^{-1}`
:param diag_only: if set to True: compute powerspectrum for pairs
:math:`a_i, k_i`, else consider all combinations
:math:`a_i, k_j`
:return: Linear matter power spectrum, :math:`P_{lin}(k)`, in :math:`[Mpc]^3`.
Example:
.. code-block:: python
cosmo.set(pk_type = option)
cosmo.lin_pert.powerspec_a_k(a,k)
where ``option`` can be set to one of the fitting functions (``EH`` or
``BBKS``).
"""
a = np.atleast_1d(a)
k = np.atleast_1d(k)
if diag_only:
assert len(a) == len(k)
T_k = self.transfer_k(k=k)
growth = self.growth_a(a, k, diag_only=diag_only)
# using equation in section 2.4 of notes
norm = (
2.0
* np.pi**2
* self._params.deltah_norm**2
* (self._params.c / self._params.H0) ** (3.0 + self._params.n)
)
if diag_only:
pk = norm * growth**2 * k**self._params.n * T_k**2
else:
pk = norm * growth**2 * (k**self._params.n * T_k**2).reshape(-1, 1)
return pk
def _transfer_BBKS(self, k):
"""
BBKS transfer function as summarized by Peacock (1997, MNRAS, 284, 885)
:param k: wavenumber [Mpc^-1]
:return: T(k): BBKS matter transfer function [1]
"""
k = np.atleast_1d(k)
q_pd = k / self._params.h / self._params.gamma
if self._params.pk_type == "BBKS_CCL":
tfac = self._params.Tcmb / 2.7
q_pd = q_pd * tfac**2
tk = (
np.log(1.0 + 2.34 * q_pd)
/ (2.34 * q_pd)
* (
1.0
+ 3.89 * q_pd
+ (16.1 * q_pd) ** 2
+ (5.46 * q_pd) ** 3
+ (6.71 * q_pd) ** 4
)
** (-0.25)
)
return tk
def _transfer_EH(self, k):
"""Return the CDM + baryon transfer function as defined in
Eisenstein & Hu, 1998, ApJ, 511, 5, Equation (16) Input: wave
vector k in Mpc^-1"""
T = self._params.omega_b / self._params.omega_m * self._T_b(
k
) + self._params.omega_dm / self._params.omega_m * self._T_c(k)
return T
def _jn_spher(self, n, x):
"""Returns the spherical Bessel function of order n.
This is used to compute the oscillatory feature of the baryonic
transfer function as in Eisenstein & Hu, 1998, ApJ, 511, 5"""
jn_spher = np.sqrt(np.pi / (2.0 * x)) * special.jn(n + 0.5, x)
return jn_spher
def _G(self, y):
"""Returns the function G as defined in Eisenstein & Hu, 1998,
ApJ, 511, 5, Equation (15)
G(y) = y(-6. sqrt(1 + y) + (2 + 3 y) ln((sqrt(1 + y) + 1)/(sqrt(1 + y) - 1)))
and needed to calculate alpha_b"""
G = y * (
-6.0 * np.sqrt(1.0 + y)
+ (2.0 + 3.0 * y)
* np.log((np.sqrt(1.0 + y) + 1.0) / (np.sqrt(1.0 + y) - 1.0))
)
return G
def _photon2baryon_dens(self, z):
"""Returns the ratio of the baryon to photon momentum density R
as defined in Eisenstein & Hu, 1998, ApJ, 511, 5, Equation (5)
for redshift z
R = 31.5 omega_b h^2 sigma_27^-4 (z/10^3)^-1"""
R = (
31.5
* self._params.omega_b
* self._params.h**2
* self._params._sigma_27 ** (-4)
* (z / 10**3) ** (-1.0)
)
return R
def _T_0(self, k, alpha_c, beta_c):
"""Returns the transfer function T_0 as defined in Eisenstein &
Hu, 1998, ApJ, 511, 5, Equation (19)
T_0 = ln(e+1.8 beta_c q)/(ln(e+1.8 beta_c q) + C q^2)
where
q = (k [Mpc^-1])/(13.41 k_eq)
C = 14.2/alpha_c + 386/(1+69.9 q^1.08)
alpha_c = a_1^(-omega_b/omega_0) a_2^(-(omega_b/omega_0)^3)
a_1 = (46.9 omega_0 h^2)^0.670 (1+(32.1 omega_0 h^2)^-0.532)
a_2 = (12.0 omega_0 h^2)^0.424 (1+(45.0 omega_0 h^2)^-0.582)
beta_c^-1 = 1 + b_1 ((omega_c/omega_0)^b_2 - 1)
b_1 = 0.944 (1 + (458 omega_0 h^2)^-0.708)^-1
b_2 = (0.395 omega_0 h^2)^-0.0266"""
# Define the needed variables as in Eqs. (10), (20)
q = k / (13.41 * self._params._k_eq)
C = 14.2 / alpha_c + 386.0 / (1.0 + 69.9 * q**1.08)
T_0 = np.log(np.e + 1.8 * beta_c * q) / (
np.log(np.e + 1.8 * beta_c * q) + C * q**2
)
return T_0
def _T_b(self, k):
"""Returns the baryonic part of the transfer function as defined
in Eisenstein & Hu, 1998, ApJ, 511, 5, Equation (21)
T_b = (T_0(k,1,1)/(1 + (k s/5.2)^2)
+ alpha_b/(1 + (beta_c/(k s))^3)*e^-(k/k_Silk)^1.4
) *j_0(k s_tilde)
"""
# Define the needed variable as in Eq. (22)
s_tilde = self._params._sound_horiz / (
1.0 + (self._params._beta_node / (k * self._params._sound_horiz)) ** 3
) ** (1.0 / 3.0)
T_b = (
self._T_0(k, 1.0, 1.0) / (1.0 + (k * self._params._sound_horiz / 5.2) ** 2)
+ self._params._alpha_b
/ (1.0 + (self._params._beta_b / (k * self._params._sound_horiz)) ** 3.0)
* np.exp(-((k / self._params._k_Silk) ** 1.4))
) * self._jn_spher(0, k * s_tilde)
return T_b
def _T_c(self, k):
"""Returns the CDM part of the transfer function as defined in
Eisenstein & Hu, 1998, ApJ, 511, 5, Equation (17)
T_c = f T_0(k,1,beta_c) + (1 - f) T_0(k,alpha_c,beta_c)
"""
f = 1.0 / (1.0 + (k * self._params._sound_horiz / 5.4) ** 4) # Eq. (18)
T_c = f * self._T_0(k, 1.0, self._params._beta_c) + (1.0 - f) * self._T_0(
k, self._params._alpha_c, self._params._beta_c
)
return T_c
def _gamma_eff(self, k):
"""
:param k: wave vector k in Mpc^-1
:return: scale-dependent rescaling of the zero-baryon shape parameter gamma
defined in Eisenstein & Hu, 1999, ApJ, 511, 1, Equation (16)
Gamma_eff =\
Omega_0 * h^2 * (sqrt(alpha_nu) + (1 - sqrt(alpha_nu)) / (1 + (0.43 * k * s)^4))
"""
gamma_eff = (
(self._params.omega_m + self._params.omega_nu_m)
* self._params.h**2
* (
np.sqrt(self._params._alpha_nu)
+ (1.0 - np.sqrt(self._params._alpha_nu))
/ (1.0 + (0.43 * k * self._params._sound_horiz_nu) ** 4.0)
)
)
return gamma_eff
def _q_eff(self, k):
"""
:param k: wave vector k in Mpc^-1
:return: scale-dependent equation (17) defined in Eisenstein & Hu, 1999, ApJ,
511, 1
q_eff = k * Sigma_2.7^2 / (Gamma_eff * Mpc^-1)
"""
q_eff = k * self._params._sigma_27**2.0 / self._gamma_eff(k)
return q_eff
def _T_master(self, k):
"""
:param k: wave vector k in Mpc^-1
:return: scale-dependent but time-independent master function defined in
Eisenstein & Hu, 1999, ApJ, 511, 1, Equation (24)
T_master(k) = T_sup(k) * B(k)
"""
return self._T_sup(k) * self._B(k)
def _T_sup(self, k):
"""
T_sup(k) = L / (L + C * q_eff^2)
:param k: wave vector k in Mpc^-1
:return: small-scale suppressed CDM + baryon + massive neutrinos transfer
function as defined in
Eisenstein & Hu, 1999, ApJ, 511, 1, Equation (18)
"""
L = np.log(
np.e
+ 1.84
* self._params._beta_c_nu
* np.sqrt(self._params._alpha_nu)
* self._q_eff(k)
) # Eq. (19)
C = 14.4 + 325.0 / (1.0 + 60.5 * self._q_eff(k) ** 1.08) # Eq. (20)
return L / (L + C * self._q_eff(k) ** 2)
def _B(self, k):
"""
:param k: wave vector k in Mpc^-1
:return: returns the function B(k) defined by Eq. (22) in Eisenstein & Hu, 1999,
ApJ, 511, 1
"""
assert self._params.N_massive_nu > 0, "need N_massive_nu > 0"
assert self._params.massive_nu_total_mass > 0, "need massive_nu_total_mass > 0"
q_nu = k / (
3.42
* np.sqrt(self._params._f_nu / self._params.N_massive_nu)
* self._params._k_eq_nu
)
b_val = 1.0 + (
1.24
* (self._params._f_nu**0.64)
* (self._params.N_massive_nu ** (0.3 + 0.6 * self._params._f_nu))
) / (q_nu**-1.6 + q_nu**0.8)
return b_val
def _y_fs(self, k):
"""
:param k: wave vector k in Mpc^-1
:return: free-streaming epoch as a function of scale defined by Eq. (14) in
Eisenstein & Hu, 1999, ApJ, 511, 1
"""
if self._params.massive_nu_total_mass == 0.0:
return 0.0
q = (
k
* self._params._sigma_27**2.0
* ((self._params.omega_m + self._params.omega_nu_m) * self._params.h**2)
** -1.0
)
y_fs = (
17.2
* self._params._f_nu
* (1.0 + 0.488 * self._params._f_nu ** -(7.0 / 6.0))
* (self._params.N_massive_nu * q / self._params._f_nu) ** 2.0
)
return y_fs
def _D_cb(self, growth_nu, k):
"""
:param a: scale factor [1]
:param k: wave vector k in Mpc^-1
:return: DM + baryons growth rate in the presence of free-streaming defined by
Eq. (12) in Eisenstein & Hu, 1999, ApJ, 511, 1
"""
return (1.0 + (np.outer(growth_nu, 1.0 / (1.0 + self._y_fs(k))).T) ** 0.7) ** (
self._params._p_cb / 0.7
)
def _D_cb_diag(self, growth_nu, k):
"""
:param a: scale factor [1]
:param k: wave vector k in Mpc^-1
:return: DM + baryons growth rate in the presence of free-streaming defined by
Eq. (12) in Eisenstein & Hu, 1999, ApJ, 511, 1
"""
return (1.0 + (growth_nu / (1.0 + self._y_fs(k))) ** 0.7) ** (
self._params._p_cb / 0.7
)
def _enrich_params(self):
"""Sets the constant parameters needed for the
LinearPerturbationApprox class. If pk_type = EH it sets the
parameters needed for computing the transfer function as defined
in Eisenstein & Hu, 1998, ApJ, 511, 5.
If pk_type = BBKS it sets the parameters needed for computing
the BBKS transfer function as summarized by Peacock (1997,
MNRAS, 284, 885)
"""
if self._params.pk_type == "EH":
self._set_eh_params()
if self._params.pk_type in ("BBKS", "BBKS_CCL"):
"""This calculates Gamma for the linear powerspectrum using the
prescription Sugiyama (1995, APJS, 100, 281)"""
gamma = (
self._params.omega_m
* self._params.h
* np.exp(
-self._params.omega_b
* (1.0 + np.sqrt(2.0 * self._params.h) / self._params.omega_m)
)
)
self._params.gamma = gamma
# normalise linear power spectrum #TODO: perhaps avoid writing in
# params; perhaps needs more error checking
if self._params.pk_norm_type == "deltah":
self._params.deltah_norm = self._params.pk_norm
self._params.sigma8 = self.sigma8()
if self._params.pk_norm_type == "sigma8":
self._params.sigma8 = self._params.pk_norm
self._params.deltah_norm = 4.0e-5 # arbitrary temporary value
sigma8_temp = self.sigma8()
self._params.deltah_norm = (
self._params.deltah_norm * self._params.sigma8 / sigma8_temp
)
if self._params.pk_norm_type == "A_s":
raise NotImplementedError(
"A_s normalization only available for the Boltzmann solver, \
choose deltah or sigma8"
)
return None
def _set_eh_params(self):
if (
self._params.N_massive_nu == 0.0
or self._params.massive_nu_total_mass == 0.0
):
omh2 = (self._params.omega_m + self._params.omega_nu_m) * self._params.h**2
self._params._k_Silk = (
1.6
* (self._params.omega_b * self._params.h**2) ** 0.52
* (omh2) ** 0.73
* (1.0 + (10.4 * omh2) ** -0.95)
) # Eq. (7), units: Mpc^-1
# CDM transfer function, Equations (11), (12)
a1 = (46.9 * omh2) ** 0.670 * (1.0 + (32.1 * omh2) ** -0.532)
a2 = (12.0 * omh2) ** 0.424 * (1.0 + (45.0 * omh2) ** -0.582)
self._params._alpha_c = a1 ** (
-self._params.omega_b / self._params.omega_m
) * a2 ** (-((self._params.omega_b / self._params.omega_m) ** 3.0))
b1 = 0.944 * (1.0 + (458.0 * omh2) ** -0.708) ** -1
b2 = (0.395 * omh2) ** -0.0266
self._params._beta_c = 1.0 / (
1.0 + b1 * ((self._params.omega_dm / self._params.omega_m) ** b2 - 1.0)
)
# Baryon transfer function, Equations (14), (24), (23)
# alpha_b = 2.07 k_eq s (1 + R_d)^-3/4 G((1 + z_eq)/(1 + z_d))
# beta_b = 0.5 + omega_b/omega_m_0 + (3 - 2 omega_b/omega_m_0) sqrt((17.2
# omega_m_0 h^2)^2 + 1)
# b_node = 8.41 (omega_m_0 h^2)^0.435
self._params._alpha_b = (
2.07
* self._params._k_eq
* self._params._sound_horiz
* (1.0 + self._params._R_drag) ** -0.75
* self._G(
(1.0 + self._params._z_equality) / (1.0 + self._params._z_drag)
)
)
self._params._beta_b = (
0.5
+ self._params.omega_b / self._params.omega_m
+ (3 - 2 * self._params.omega_b / self._params.omega_m)
* np.sqrt((17.2 * omh2) ** 2 + 1)
)
self._params._beta_node = 8.41 * omh2**0.435
else:
omh2_nu = (
self._params.omega_m + self._params.omega_nu_m
) * self._params.h**2
omb2_nu = self._params.omega_b * self._params.h**2
# equations for massive neutrinos from Eisenstein & Hu, 1999, ApJ, 511, 1
# Redshift at matter-radiation equality, Eq. (2) in Eisenstein & Hu, 1998,
# ApJ, 496, 2 (modified to include Omega_nu_m in Omega_m)
z_equality_nu = 2.5e4 * omh2_nu * self._params._sigma_27**-4
b_1_nu = 0.313 * omh2_nu**-0.419 * (1.0 + 0.607 * omh2_nu**0.674)
b_2_nu = 0.238 * omh2_nu**0.223
z_drag_nu = (
1291.0
* (omh2_nu**0.251)
/ (1.0 + 0.659 * omh2_nu**0.828)
* (1.0 + b_1_nu * (self._params.omega_b * self._params.h**2) ** b_2_nu)
) # Redshift at drag epoch, Eq. (4)
# Wave vector values, modified to include Omega_nu_m in Omega_m
self._params._k_eq_nu = (
7.46e-2 * omh2_nu * self._params._sigma_27**-2
) # Eq. (3), units: Mpc^-1
# sound horizon given by Eq. (4) in Eisenstein & Hu, 1999, ApJ, 511, 1
# (modified to include Omega_nu_m in Omega_m)
self._params._sound_horiz_nu = (
44.5
* np.log(9.83 / omh2_nu)
/ np.sqrt(1.0 + 10.0 * omb2_nu ** (3.0 / 4.0))
)
# ratios of the density of the species DM, baryons and massive neutrinos to
# the total matter density
# 0..1
f_c = self._params.omega_dm / (
self._params.omega_dm + self._params.omega_b + self._params.omega_nu_m
)
# 0 without massuive nu:
f_nu = self._params._f_nu = self._params.omega_nu_m / (
self._params.omega_dm + self._params.omega_b + self._params.omega_nu_m
)
# 1 without massive nu:
f_cb = (self._params.omega_dm + self._params.omega_b) / (
self._params.omega_dm + self._params.omega_b + self._params.omega_nu_m
)
f_nub = (self._params.omega_nu_m + self._params.omega_b) / (
self._params.omega_dm + self._params.omega_b + self._params.omega_nu_m
)
# Eq. (11) for DM only (p_c) and for DM + baryons (p_cb)
p_c = (1.0 / 4.0) * (5.0 - np.sqrt(1.0 + 24.0 * f_c))
# 0 without massive nu:
p_cb = self._params._p_cb = (1.0 / 4.0) * (5.0 - np.sqrt(1.0 + 24.0 * f_cb))
# Eq. (21)
self._params._beta_c_nu = 1.0 / (1.0 - 0.949 * f_nub)
# Eq. (3)
y_d = (1.0 + z_equality_nu) / (1.0 + z_drag_nu)
# Eq. (15)
self._params._alpha_nu = (
(f_c / f_cb)
* (5.0 - 2 * (p_c + p_cb))
/ (5.0 - 4.0 * p_cb)
* (
(1.0 - 0.553 * f_nub + 0.126 * f_nub**3.0)
/ (1.0 - 0.193 * np.sqrt(f_nu) + 0.169 * f_nu)
)
* (1.0 + y_d) ** (p_cb - p_c)
* (
1.0
+ (p_c - p_cb)
/ 2.0
* (1.0 + 1.0 / ((3.0 - 4.0 * p_c) * (7.0 - 4.0 * p_cb)))
* (1.0 + y_d) ** -1
)
)
[docs] def print_params(self):
"""
Print the parameters related to the chosen linear fitting function (*EH* or
*BBKS*).
Example:
.. code-block:: python
cosmo.lin_pert.print_params()
"""
if self._params.pk_type == "EH":
print("")
print(
"----",
(
"Derived cosmology parameters for Eisenstein and Hu"
" transfer function "
).ljust(70, "-"),
)
print()
print(
" {:45s}: {}".format(
"k_Silk (Silk damping scale [Mpc-1])", self._params._k_Silk
)
)
if self._params.pk_type in ("BBKS", "BBKS_CCL"):
print(
"----",
"Derived cosmology parameters for BBKS transfer function ".ljust(
70, "-"
),
)
print()
print(
" {:45s}: {}".format(
"gamma (Gamma Sugiyama [h Mpc-1])", self._params.gamma
)
)