Source code for PyCosmo.LinearPerturbationBase

# 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 abc
import warnings
import numpy as np
from scipy.interpolate import interp1d


[docs]class LinearPerturbationBase(object): r""" This is the parent class of LinearPerturbationApprox and LinearPerturbationBoltzmann. It provides some abstract methods for growth factors, transfer functions and the linear power spectrum. It also calculates :math:`\sigma_8` and :math:`\sigma_r`. """ __metaclass__ = abc.ABCMeta def __new__(clz, *a, **kw): # check_protypes(clz) return super(LinearPerturbationBase, clz).__new__(clz) # todo: sigma_r needs to be speeded up and made robust # todo: merge this with sigma8
[docs] def sigma_r(self, r=8.0, a=1.0): """ Calculates the rms of the density field on a given scale :math:`r`. It is a generalization of the function :meth:`sigma8`. :param r: scale radius (default set to :math:`8 h^{-1} Mpc`) :return: :math:`\\sigma_r` [1] """ r = np.atleast_1d(r) # TODO: numpy based vecoriztion ! res = np.zeros(shape=r.shape) for i in range(0, len(r)): ri = r[i] k = self._sigma_k_grid() # grid of wavenumber k [Mpc^-1] lnk = np.log(k) w = ( 3.0 / (k * ri) ** 2 * (np.sin(k * ri) / (k * ri) - np.cos(k * ri)) ) # top hat window function pk = self.powerspec_a_k(a=1.0, k=k) res[i] = np.trapz(k**3 * pk[0] * w**2, lnk) return np.sqrt(1.0 / (2.0 * np.pi**2) * res)
def _sigma_k_grid(self): return np.logspace(-5.0, 2.0, num=5000) # grid of wavenumber k [Mpc^-1]
[docs] def sigma8(self, a=1.0): """ Computes sigma8, the rms density contrast fluctuation smoothed with a top hat of radius 8 :math:`h^{-1} Mpc`. This routine is also used for the normalization of the power spectrum, when pk_type=``sigma8`` is chosen. :param a: scale factor [1] :return: sigma8 [1] """ is_number = False if not isinstance(a, np.ndarray): is_number = True a = np.atleast_1d(a) a = a.astype("float") r = 8.0 / self._params.h # smoothing radius [Mpc] k_fine = np.logspace(-5.0, 2.0, num=50000) # grid of wavenumber k [Mpc^-1] k = self._sigma_k_grid() lnk = np.log(k) res = np.zeros((len(a),)) for i, ai in enumerate(a): pk = self.powerspec_a_k(a=ai, k=k)[:, 0] pk_interpolated = interp1d(lnk, pk, "quadratic") def integrand(k): w = ( 3.0 / (k * r) ** 2 * (np.sin(k * r) / (k * r) - np.cos(k * r)) ) # top hat window function # fix round off issues of sin(k * r) / (k * r) for small k * r using # taylor: kr_limit = 1e-2 kr = r * k[k * r <= kr_limit] w[k * r <= kr_limit] = 1 - kr**2 / 10 + kr**4 / 280 return k**3 * w**2 * pk_interpolated(np.log(k)) with warnings.catch_warnings(): integral = np.trapz(integrand(k_fine), np.log(k_fine)) res[i] = integral result = np.sqrt(1.0 / (2.0 * np.pi**2) * res) if is_number: return result[0] return result
@abc.abstractmethod def growth_a(self, a=1.0, k=None, norm=0, diag_only=True): ... @abc.abstractmethod def transfer_k(self, k): ... @abc.abstractmethod def powerspec_a_k(self, a=1.0, k=0.1, diag_only=False): ...
[docs] @abc.abstractmethod def max_redshift(self, k): pass