| """ |
| .. module:: classy |
| :synopsis: Python wrapper around CLASS |
| .. moduleauthor:: Karim Benabed <benabed@iap.fr> |
| .. moduleauthor:: Benjamin Audren <benjamin.audren@epfl.ch> |
| .. moduleauthor:: Julien Lesgourgues <lesgourg@cern.ch> |
| |
| This module defines a class called Class. It is used with Monte Python to |
| extract cosmological parameters. |
| |
| # JL 14.06.2017: TODO: check whether we should free somewhere the allocated fc.filename and titles, data (4 times) |
| |
| """ |
| from math import exp,log |
| import numpy as np |
| cimport numpy as np |
| from libc.stdlib cimport * |
| from libc.stdio cimport * |
| from libc.string cimport * |
| import cython |
| cimport cython |
| from scipy.interpolate import CubicSpline |
| from scipy.interpolate import UnivariateSpline |
| from scipy.interpolate import interp1d |
|
|
| |
| import sys |
| def viewdictitems(d): |
| if sys.version_info >= (3,0): |
| return d.items() |
| else: |
| return d.viewitems() |
|
|
| ctypedef np.float64_t DTYPE_t |
| ctypedef np.int32_t DTYPE_i |
|
|
|
|
|
|
| |
| from cclassy cimport * |
|
|
| __version__ = _VERSION_.decode("utf-8") |
|
|
| |
| |
| |
| |
| |
| |
| class CosmoError(Exception): |
| def __init__(self, message=""): |
| self.message = message.decode() if isinstance(message,bytes) else message |
|
|
| def __str__(self): |
| return '\n\nError in Class: ' + self.message |
|
|
|
|
| class CosmoSevereError(CosmoError): |
| """ |
| Raised when Class failed to understand one or more input parameters. |
| |
| This case would not raise any problem in Class default behaviour. However, |
| for parameter extraction, one has to be sure that all input parameters were |
| understood, otherwise the wrong cosmological model would be selected. |
| """ |
| pass |
|
|
|
|
| class CosmoComputationError(CosmoError): |
| """ |
| Raised when Class could not compute the cosmology at this point. |
| |
| This will be caught by the parameter extraction code to give an extremely |
| unlikely value to this point |
| """ |
| pass |
|
|
|
|
| cdef class Class: |
| """ |
| Class wrapping, creates the glue between C and python |
| |
| The actual Class wrapping, the only class we will call from MontePython |
| (indeed the only one we will import, with the command: |
| from classy import Class |
| |
| """ |
| |
| |
| cdef precision pr |
| cdef background ba |
| cdef thermodynamics th |
| cdef perturbations pt |
| cdef primordial pm |
| cdef fourier fo |
| cdef transfer tr |
| cdef harmonic hr |
| cdef output op |
| cdef lensing le |
| cdef distortions sd |
| cdef file_content fc |
|
|
| cdef int computed |
| cdef int allocated |
| cdef object _pars |
| cdef object ncp |
|
|
| _levellist = ["input","background","thermodynamics","perturbations", "primordial", "fourier", "transfer", "harmonic", "lensing", "distortions"] |
|
|
| |
| |
| |
| |
| property pars: |
| def __get__(self): |
| return self._pars |
| property state: |
| def __get__(self): |
| return True |
| property Omega_nu: |
| def __get__(self): |
| return self.ba.Omega0_ncdm_tot |
| property nonlinear_method: |
| def __get__(self): |
| return self.fo.method |
|
|
| def set_default(self): |
| _pars = { |
| "output":"tCl mPk",} |
| self.set(**_pars) |
|
|
| def __cinit__(self, default=False): |
| cdef char* dumc |
| self.allocated = False |
| self.computed = False |
| self._pars = {} |
| self.fc.size=0 |
| self.fc.filename = <char*>malloc(sizeof(char)*30) |
| assert(self.fc.filename!=NULL) |
| dumc = "NOFILE" |
| sprintf(self.fc.filename,"%s",dumc) |
| self.ncp = set() |
| if default: self.set_default() |
|
|
| def __dealloc__(self): |
| if self.allocated: |
| self.struct_cleanup() |
| self.empty() |
| |
| if self.fc.size !=0: |
| self.fc.size=0 |
| free(self.fc.name) |
| free(self.fc.value) |
| free(self.fc.read) |
| free(self.fc.filename) |
|
|
| |
| def set(self,*pars,**kars): |
| oldpars = self._pars.copy() |
| if len(pars)==1: |
| self._pars.update(dict(pars[0])) |
| elif len(pars)!=0: |
| raise CosmoSevereError("bad call") |
| self._pars.update(kars) |
| if viewdictitems(self._pars) <= viewdictitems(oldpars): |
| return |
| self.computed=False |
| return True |
|
|
| def empty(self): |
| self._pars = {} |
| self.computed = False |
|
|
| |
| |
| def _fillparfile(self): |
| cdef char* dumc |
|
|
| if self.fc.size!=0: |
| free(self.fc.name) |
| free(self.fc.value) |
| free(self.fc.read) |
| self.fc.size = len(self._pars) |
| self.fc.name = <FileArg*> malloc(sizeof(FileArg)*len(self._pars)) |
| assert(self.fc.name!=NULL) |
|
|
| self.fc.value = <FileArg*> malloc(sizeof(FileArg)*len(self._pars)) |
| assert(self.fc.value!=NULL) |
|
|
| self.fc.read = <short*> malloc(sizeof(short)*len(self._pars)) |
| assert(self.fc.read!=NULL) |
|
|
| |
| i = 0 |
| for kk in self._pars: |
|
|
| dumcp = kk.strip().encode() |
| dumc = dumcp |
| sprintf(self.fc.name[i],"%s",dumc) |
| dumcp = str(self._pars[kk]).strip().encode() |
| dumc = dumcp |
| sprintf(self.fc.value[i],"%s",dumc) |
| self.fc.read[i] = _FALSE_ |
| i+=1 |
|
|
| |
| def struct_cleanup(self): |
| if(self.allocated != True): |
| return |
| if self.sd.is_allocated: |
| distortions_free(&self.sd) |
| if self.le.is_allocated: |
| lensing_free(&self.le) |
| if self.hr.is_allocated: |
| harmonic_free(&self.hr) |
| if self.tr.is_allocated: |
| transfer_free(&self.tr) |
| if self.fo.is_allocated: |
| fourier_free(&self.fo) |
| if self.pm.is_allocated: |
| primordial_free(&self.pm) |
| if self.pt.is_allocated: |
| perturbations_free(&self.pt) |
| if self.th.is_allocated: |
| thermodynamics_free(&self.th) |
| if self.ba.is_allocated: |
| background_free(&self.ba) |
| self.ncp = set() |
|
|
| self.allocated = False |
| self.computed = False |
|
|
| def _check_task_dependency(self, level): |
| """ |
| Fill the level list with all the needed modules |
| |
| .. warning:: |
| |
| the ordering of modules is obviously dependent on CLASS module order |
| in the main.c file. This has to be updated in case of a change to |
| this file. |
| |
| Parameters |
| ---------- |
| |
| level : list |
| list of strings, containing initially only the last module required. |
| For instance, to recover all the modules, the input should be |
| ['lensing'] |
| |
| """ |
| |
| if isinstance(level, str): |
| level=[level] |
| |
| levelset = set() |
| for item in level: |
| |
| if item not in self._levellist: |
| raise CosmoSevereError("Unknown computation level: '{}'".format(item)) |
| |
| levelset.update(self._levellist[:self._levellist.index(item)+1]) |
| return levelset |
|
|
| def _pars_check(self, key, value, contains=False, add=""): |
| val = "" |
| if key in self._pars: |
| val = self._pars[key] |
| if contains: |
| if value in val: |
| return True |
| else: |
| if value==val: |
| return True |
| if add: |
| sep = " " |
| if isinstance(add,str): |
| sep = add |
|
|
| if contains and val: |
| self.set({key:val+sep+value}) |
| else: |
| self.set({key:value}) |
| return True |
| return False |
|
|
| def compute(self, level=["distortions"]): |
| """ |
| compute(level=["distortions"]) |
| |
| Main function, execute all the _init methods for all desired modules. |
| This is called in MontePython, and this ensures that the Class instance |
| of this class contains all the relevant quantities. Then, one can deduce |
| Pk, Cl, etc... |
| |
| Parameters |
| ---------- |
| level : list |
| list of the last module desired. The internal function |
| _check_task_dependency will then add to this list all the |
| necessary modules to compute in order to initialize this last |
| one. The default last module is "lensing". |
| |
| .. warning:: |
| |
| level default value should be left as an array (it was creating |
| problem when casting as a set later on, in _check_task_dependency) |
| |
| """ |
| cdef ErrorMsg errmsg |
|
|
| |
| level = self._check_task_dependency(level) |
|
|
| |
| |
| |
| |
| if self.computed and self.ncp.issuperset(level): |
| return |
|
|
| |
| if self.allocated: |
| self.struct_cleanup() |
|
|
| |
| self.computed = False |
|
|
| |
| self._fillparfile() |
|
|
| |
| |
| self.ncp=set() |
| |
| |
| self.allocated = True |
|
|
| |
| |
| |
| |
| |
| |
| |
| if "input" in level: |
| if input_read_from_file(&self.fc, &self.pr, &self.ba, &self.th, |
| &self.pt, &self.tr, &self.pm, &self.hr, |
| &self.fo, &self.le, &self.sd, &self.op, errmsg) == _FAILURE_: |
| raise CosmoSevereError(errmsg) |
| self.ncp.add("input") |
| |
| problem_flag = False |
| problematic_parameters = [] |
| for i in range(self.fc.size): |
| if self.fc.read[i] == _FALSE_: |
| problem_flag = True |
| problematic_parameters.append(self.fc.name[i].decode()) |
| if problem_flag: |
| raise CosmoSevereError( |
| "Class did not read input parameter(s): %s\n" % ', '.join( |
| problematic_parameters)) |
|
|
| |
| |
| |
| if "background" in level: |
| if background_init(&(self.pr), &(self.ba)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.ba.error_message) |
| self.ncp.add("background") |
|
|
| if "thermodynamics" in level: |
| if thermodynamics_init(&(self.pr), &(self.ba), |
| &(self.th)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.th.error_message) |
| self.ncp.add("thermodynamics") |
|
|
| if "perturbations" in level: |
| if perturbations_init(&(self.pr), &(self.ba), |
| &(self.th), &(self.pt)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.pt.error_message) |
| self.ncp.add("perturbations") |
|
|
| if "primordial" in level: |
| if primordial_init(&(self.pr), &(self.pt), |
| &(self.pm)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.pm.error_message) |
| self.ncp.add("primordial") |
|
|
| if "fourier" in level: |
| if fourier_init(&self.pr, &self.ba, &self.th, |
| &self.pt, &self.pm, &self.fo) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.fo.error_message) |
| self.ncp.add("fourier") |
|
|
| if "transfer" in level: |
| if transfer_init(&(self.pr), &(self.ba), &(self.th), |
| &(self.pt), &(self.fo), &(self.tr)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.tr.error_message) |
| self.ncp.add("transfer") |
|
|
| if "harmonic" in level: |
| if harmonic_init(&(self.pr), &(self.ba), &(self.pt), |
| &(self.pm), &(self.fo), &(self.tr), |
| &(self.hr)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.hr.error_message) |
| self.ncp.add("harmonic") |
|
|
| if "lensing" in level: |
| if lensing_init(&(self.pr), &(self.pt), &(self.hr), |
| &(self.fo), &(self.le)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.le.error_message) |
| self.ncp.add("lensing") |
|
|
| if "distortions" in level: |
| if distortions_init(&(self.pr), &(self.ba), &(self.th), |
| &(self.pt), &(self.pm), &(self.sd)) == _FAILURE_: |
| self.struct_cleanup() |
| raise CosmoComputationError(self.sd.error_message) |
| self.ncp.add("distortions") |
|
|
| self.computed = True |
|
|
| |
| |
| return |
|
|
| def set_baseline(self, baseline_name): |
| |
| if ('planck' in baseline_name and '18' in baseline_name and 'lens' in baseline_name and 'bao' in baseline_name) or 'p18lb' in baseline_name.lower(): |
| self.set({'omega_b':2.255065e-02, |
| 'omega_cdm':1.193524e-01, |
| 'H0':6.776953e+01, |
| 'A_s':2.123257e-09, |
| 'n_s':9.686025e-01, |
| 'z_reio':8.227371e+00, |
|
|
| 'N_ur':2.0328, |
| 'N_ncdm':1, |
| 'm_ncdm':0.06, |
| 'T_ncdm':0.71611, |
|
|
| 'output':'mPk, tCl, pCl, lCl', |
| 'lensing':'yes', |
| 'P_k_max_h/Mpc':1.0, |
| 'non_linear':'halofit' |
| }) |
|
|
| elif ('planck' in baseline_name and '18' in baseline_name and 'lens' in baseline_name) or 'p18l' in baseline_name.lower(): |
| self.set({'omega_b':2.236219e-02, |
| 'omega_cdm':1.201668e-01, |
| 'H0':6.726996e+01, |
| 'A_s':2.102880e-09, |
| 'n_s':9.661489e-01, |
| 'z_reio':7.743057e+00, |
|
|
| 'N_ur':2.0328, |
| 'N_ncdm':1, |
| 'm_ncdm':0.06, |
| 'T_ncdm':0.71611, |
|
|
| 'output':'mPk, tCl, pCl, lCl', |
| 'lensing':'yes', |
| 'P_k_max_h/Mpc':1.0, |
| 'non_linear':'halofit' |
| }) |
|
|
| elif ('planck' in baseline_name and '18' in baseline_name) or 'p18' in baseline_name.lower(): |
| self.set({'omega_b':2.237064e-02, |
| 'omega_cdm':1.214344e-01, |
| 'H0':6.685836e+01, |
| 'A_s':2.112203e-09, |
| 'n_s':9.622800e-01, |
| 'z_reio':7.795700e+00, |
|
|
| 'N_ur':2.0328, |
| 'N_ncdm':1, |
| 'm_ncdm':0.06, |
| 'T_ncdm':0.71611, |
|
|
| 'output':'mPk, tCl, pCl, lCl', |
| 'lensing':'yes', |
| 'P_k_max_h/Mpc':1.0}) |
| else: |
| raise CosmoSevereError("Unrecognized baseline case '{}'".format(baseline_name)) |
|
|
| @property |
| def density_factor(self): |
| """ |
| The density factor required to convert from the class-units of density to kg/m^3 (SI units) |
| """ |
| return 3*_c_*_c_/(8*np.pi*_G_)/(_Mpc_over_m_*_Mpc_over_m_) |
|
|
| @property |
| def Mpc_to_m(self): |
| return _Mpc_over_m_ |
|
|
| @property |
| def kg_to_eV(self): |
| return _c_*_c_/_eV_ |
|
|
| @property |
| def kgm3_to_eVMpc3(self): |
| """ |
| Convert from kg/m^3 to eV/Mpc^3 |
| """ |
| return self.kg_to_eV*self.Mpc_to_m**3 |
|
|
| @property |
| def kg_to_Msol(self): |
| return 1/(2.0e30) |
|
|
| @property |
| def kgm3_to_MsolMpc3(self): |
| """ |
| Convert from kg/m^3 to Msol/Mpc^3 |
| """ |
| return self.kg_to_Msol*self.Mpc_to_m**3 |
|
|
| def raw_cl(self, lmax=-1, nofail=False): |
| """ |
| raw_cl(lmax=-1, nofail=False) |
| |
| Return a dictionary of the primary C_l |
| |
| Parameters |
| ---------- |
| lmax : int, optional |
| Define the maximum l for which the C_l will be returned |
| (inclusively). This number will be checked against the maximum l |
| at which they were actually computed by CLASS, and an error will |
| be raised if the desired lmax is bigger than what CLASS can |
| give. |
| nofail: bool, optional |
| Check and enforce the computation of the harmonic module |
| beforehand, with the desired lmax. |
| |
| Returns |
| ------- |
| cl : dict |
| Dictionary that contains the power spectrum for 'tt', 'te', etc... The |
| index associated with each is defined wrt. Class convention, and are non |
| important from the python point of view. It also returns now the |
| ell array. |
| """ |
| self.compute(["harmonic"]) |
| cdef int lmaxR |
|
|
| |
| |
| |
| has_flags = [ |
| (self.hr.has_tt, self.hr.index_ct_tt, 'tt'), |
| (self.hr.has_ee, self.hr.index_ct_ee, 'ee'), |
| (self.hr.has_te, self.hr.index_ct_te, 'te'), |
| (self.hr.has_bb, self.hr.index_ct_bb, 'bb'), |
| (self.hr.has_pp, self.hr.index_ct_pp, 'pp'), |
| (self.hr.has_tp, self.hr.index_ct_tp, 'tp'),] |
| spectra = [] |
|
|
| for flag, index, name in has_flags: |
| if flag: |
| spectra.append(name) |
|
|
| |
| if not spectra: |
| raise CosmoSevereError("No Cl computed") |
|
|
| |
| lmaxR = self.hr.l_max_tot |
| if lmax == -1: |
| lmax = lmaxR |
| if lmax > lmaxR: |
| if nofail: |
| self._pars_check("l_max_scalars",lmax) |
| self.compute(["lensing"]) |
| else: |
| raise CosmoSevereError("Can only compute up to lmax=%d"%lmaxR) |
|
|
| |
|
|
| |
| cdef double *rcl = <double*> calloc(self.hr.ct_size,sizeof(double)) |
|
|
| |
| cdef double **cl_md = <double**> calloc(self.hr.md_size, sizeof(double*)) |
| for index_md in range(self.hr.md_size): |
| cl_md[index_md] = <double*> calloc(self.hr.ct_size, sizeof(double)) |
|
|
| |
| cdef double **cl_md_ic = <double**> calloc(self.hr.md_size, sizeof(double*)) |
| for index_md in range(self.hr.md_size): |
| cl_md_ic[index_md] = <double*> calloc(self.hr.ct_size*self.hr.ic_ic_size[index_md], sizeof(double)) |
|
|
| |
| cl = {} |
| for elem in spectra: |
| cl[elem] = np.zeros(lmax+1, dtype=np.double) |
|
|
| success = True |
| |
| for ell from 2<=ell<lmax+1: |
| if harmonic_cl_at_l(&self.hr, ell, rcl, cl_md, cl_md_ic) == _FAILURE_: |
| success = False |
| break |
| for flag, index, name in has_flags: |
| if name in spectra: |
| cl[name][ell] = rcl[index] |
| cl['ell'] = np.arange(lmax+1) |
|
|
| free(rcl) |
| for index_md in range(self.hr.md_size): |
| free(cl_md[index_md]) |
| free(cl_md_ic[index_md]) |
| free(cl_md) |
| free(cl_md_ic) |
|
|
| |
| if not success: |
| raise CosmoSevereError(self.hr.error_message) |
|
|
| return cl |
|
|
| def lensed_cl(self, lmax=-1,nofail=False): |
| """ |
| lensed_cl(lmax=-1, nofail=False) |
| |
| Return a dictionary of the lensed C_l, computed by CLASS, without the |
| density C_ls. They must be asked separately with the function aptly |
| named density_cl |
| |
| Parameters |
| ---------- |
| lmax : int, optional |
| Define the maximum l for which the C_l will be returned (inclusively) |
| nofail: bool, optional |
| Check and enforce the computation of the lensing module beforehand |
| |
| Returns |
| ------- |
| cl : dict |
| Dictionary that contains the power spectrum for 'tt', 'te', etc... The |
| index associated with each is defined wrt. Class convention, and are non |
| important from the python point of view. |
| """ |
| self.compute(["lensing"]) |
| cdef int lmaxR |
|
|
| |
| |
| |
| has_flags = [ |
| (self.le.has_tt, self.le.index_lt_tt, 'tt'), |
| (self.le.has_ee, self.le.index_lt_ee, 'ee'), |
| (self.le.has_te, self.le.index_lt_te, 'te'), |
| (self.le.has_bb, self.le.index_lt_bb, 'bb'), |
| (self.le.has_pp, self.le.index_lt_pp, 'pp'), |
| (self.le.has_tp, self.le.index_lt_tp, 'tp'),] |
| spectra = [] |
|
|
| for flag, index, name in has_flags: |
| if flag: |
| spectra.append(name) |
|
|
| |
| if not spectra: |
| raise CosmoSevereError("No lensed Cl computed") |
|
|
| |
| lmaxR = self.le.l_lensed_max |
| if lmax == -1: |
| lmax = lmaxR |
| if lmax > lmaxR: |
| if nofail: |
| self._pars_check("l_max_scalars",lmax) |
| self.compute(["lensing"]) |
| else: |
| raise CosmoSevereError("Can only compute up to lmax=%d"%lmaxR) |
|
|
| |
| cdef double *lcl = <double*> calloc(self.le.lt_size,sizeof(double)) |
|
|
| cl = {} |
| success = True |
| |
| for elem in spectra: |
| cl[elem] = np.zeros(lmax+1, dtype=np.double) |
| for ell from 2<=ell<lmax+1: |
| if lensing_cl_at_l(&self.le,ell,lcl) == _FAILURE_: |
| success = False |
| break |
| for flag, index, name in has_flags: |
| if name in spectra: |
| cl[name][ell] = lcl[index] |
| cl['ell'] = np.arange(lmax+1) |
|
|
| free(lcl) |
|
|
| |
| if not success: |
| raise CosmoSevereError(self.le.error_message) |
|
|
| return cl |
|
|
| def density_cl(self, lmax=-1, nofail=False): |
| """ |
| density_cl(lmax=-1, nofail=False) |
| |
| Return a dictionary of the primary C_l for the matter |
| |
| Parameters |
| ---------- |
| lmax : int, optional |
| Define the maximum l for which the C_l will be returned (inclusively) |
| nofail: bool, optional |
| Check and enforce the computation of the lensing module beforehand |
| |
| Returns |
| ------- |
| cl : numpy array of numpy.ndarrays |
| Array that contains the list (in this order) of self correlation of |
| 1st bin, then successive correlations (set by non_diagonal) to the |
| following bins, then self correlation of 2nd bin, etc. The array |
| starts at index_ct_dd. |
| """ |
| self.compute(["harmonic"]) |
| cdef int lmaxR |
|
|
| lmaxR = self.pt.l_lss_max |
| has_flags = [ |
| (self.hr.has_dd, self.hr.index_ct_dd, 'dd'), |
| (self.hr.has_td, self.hr.index_ct_td, 'td'), |
| (self.hr.has_ll, self.hr.index_ct_ll, 'll'), |
| (self.hr.has_dl, self.hr.index_ct_dl, 'dl'), |
| (self.hr.has_tl, self.hr.index_ct_tl, 'tl')] |
| spectra = [] |
|
|
| for flag, index, name in has_flags: |
| if flag: |
| spectra.append(name) |
| l_max_flag = self.hr.l_max_ct[self.hr.index_md_scalars][index] |
| if l_max_flag < lmax and lmax > 0: |
| raise CosmoSevereError( |
| "the %s spectrum was computed until l=%i " % ( |
| name.upper(), l_max_flag) + |
| "but you asked a l=%i" % lmax) |
|
|
| |
| if not spectra: |
| raise CosmoSevereError("No density Cl computed") |
|
|
| |
| if lmax == -1: |
| lmax = lmaxR |
| if lmax > lmaxR: |
| if nofail: |
| self._pars_check("l_max_lss",lmax) |
| self._pars_check("output",'nCl') |
| self.compute() |
| else: |
| raise CosmoSevereError("Can only compute up to lmax=%d"%lmaxR) |
|
|
| |
| cdef double *dcl = <double*> calloc(self.hr.ct_size,sizeof(double)) |
|
|
| |
| cdef double **cl_md = <double**> calloc(self.hr.md_size, sizeof(double*)) |
| for index_md in range(self.hr.md_size): |
| cl_md[index_md] = <double*> calloc(self.hr.ct_size, sizeof(double)) |
|
|
| |
| cdef double **cl_md_ic = <double**> calloc(self.hr.md_size, sizeof(double*)) |
| for index_md in range(self.hr.md_size): |
| cl_md_ic[index_md] = <double*> calloc(self.hr.ct_size*self.hr.ic_ic_size[index_md], sizeof(double)) |
|
|
| cl = {} |
|
|
| |
| names = {'dd':[],'ll':[],'dl':[]} |
| for index_d1 in range(self.hr.d_size): |
| for index_d2 in range(index_d1, min(index_d1+self.hr.non_diag+1, self.hr.d_size)): |
| names['dd'].append("dens[%d]-dens[%d]"%(index_d1+1, index_d2+1)) |
| names['ll'].append("lens[%d]-lens[%d]"%(index_d1+1, index_d2+1)) |
| for index_d2 in range(max(index_d1-self.hr.non_diag,0), min(index_d1+self.hr.non_diag+1, self.hr.d_size)): |
| names['dl'].append("dens[%d]-lens[%d]"%(index_d1+1, index_d2+1)) |
|
|
| for elem in names: |
| if elem in spectra: |
| cl[elem] = {} |
| for name in names[elem]: |
| cl[elem][name] = np.zeros(lmax+1, dtype=np.double) |
|
|
| for elem in ['td', 'tl']: |
| if elem in spectra: |
| cl[elem] = np.zeros(lmax+1, dtype=np.double) |
|
|
| success = True |
| for ell from 2<=ell<lmax+1: |
| if harmonic_cl_at_l(&self.hr, ell, dcl, cl_md, cl_md_ic) == _FAILURE_: |
| success = False |
| break |
| if 'dd' in spectra: |
| for index, name in enumerate(names['dd']): |
| cl['dd'][name][ell] = dcl[self.hr.index_ct_dd+index] |
| if 'll' in spectra: |
| for index, name in enumerate(names['ll']): |
| cl['ll'][name][ell] = dcl[self.hr.index_ct_ll+index] |
| if 'dl' in spectra: |
| for index, name in enumerate(names['dl']): |
| cl['dl'][name][ell] = dcl[self.hr.index_ct_dl+index] |
| if 'td' in spectra: |
| cl['td'][ell] = dcl[self.hr.index_ct_td] |
| if 'tl' in spectra: |
| cl['tl'][ell] = dcl[self.hr.index_ct_tl] |
| cl['ell'] = np.arange(lmax+1) |
|
|
| free(dcl) |
| for index_md in range(self.hr.md_size): |
| free(cl_md[index_md]) |
| free(cl_md_ic[index_md]) |
| free(cl_md) |
| free(cl_md_ic) |
|
|
| |
| if not success: |
| raise CosmoSevereError(self.hr.error_message) |
| return cl |
|
|
| def z_of_r (self, z): |
| self.compute(["background"]) |
| cdef int last_index=0 |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| r = np.zeros(len(zarr),'float64') |
| dzdr = np.zeros(len(zarr),'float64') |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| i = 0 |
| for redshift in zarr: |
|
|
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| |
| r[i] = pvecback[self.ba.index_bg_conf_distance] |
| |
| dzdr[i] = pvecback[self.ba.index_bg_H] |
|
|
| i += 1 |
|
|
| free(pvecback) |
|
|
| return (r[0], dzdr[0]) if np.isscalar(z) else (r,dzdr) |
|
|
| def luminosity_distance(self, z): |
| """ |
| luminosity_distance(z) |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index = 0 |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| lum_distance = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba, redshift, long_info, |
| inter_normal, &last_index, pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| lum_distance[iz] = pvecback[self.ba.index_bg_lum_distance] |
| free(pvecback) |
|
|
| return (lum_distance[0] if np.isscalar(z) else lum_distance) |
|
|
| |
| def pk(self,double k,double z): |
| """ |
| Gives the total matter pk (in Mpc**3) for a given k (in 1/Mpc) and z (will be non linear if requested to Class, linear otherwise) |
| |
| .. note:: |
| |
| there is an additional check that output contains `mPk`, |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. You must add mPk to the list of outputs.") |
|
|
| if (self.fo.method == nl_none): |
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_linear,k,z,self.fo.index_pk_m,&pk,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
| else: |
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_nonlinear,k,z,self.fo.index_pk_m,&pk,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk |
|
|
| |
| def pk_cb(self,double k,double z): |
| """ |
| Gives the cdm+b pk (in Mpc**3) for a given k (in 1/Mpc) and z (will be non linear if requested to Class, linear otherwise) |
| |
| .. note:: |
| |
| there is an additional check that output contains `mPk`, |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk_cb |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. You must add mPk to the list of outputs.") |
| if (self.fo.has_pk_cb == _FALSE_): |
| raise CosmoSevereError("P_cb not computed (probably because there are no massive neutrinos) so you cannot ask for it") |
|
|
| if (self.fo.method == nl_none): |
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_linear,k,z,self.fo.index_pk_cb,&pk_cb,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
| else: |
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_nonlinear,k,z,self.fo.index_pk_cb,&pk_cb,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk_cb |
|
|
| |
| def pk_lin(self,double k,double z): |
| """ |
| Gives the linear total matter pk (in Mpc**3) for a given k (in 1/Mpc) and z |
| |
| .. note:: |
| |
| there is an additional check that output contains `mPk`, |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk_lin |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. You must add mPk to the list of outputs.") |
|
|
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_linear,k,z,self.fo.index_pk_m,&pk_lin,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk_lin |
|
|
| |
| def pk_cb_lin(self,double k,double z): |
| """ |
| Gives the linear cdm+b pk (in Mpc**3) for a given k (in 1/Mpc) and z |
| |
| .. note:: |
| |
| there is an additional check that output contains `mPk`, |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk_cb_lin |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. You must add mPk to the list of outputs.") |
|
|
| if (self.fo.has_pk_cb == _FALSE_): |
| raise CosmoSevereError("P_cb not computed by CLASS (probably because there are no massive neutrinos)") |
|
|
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_linear,k,z,self.fo.index_pk_cb,&pk_cb_lin,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk_cb_lin |
|
|
| |
| def pk_numerical_nw(self,double k,double z): |
| """ |
| Gives the nowiggle (smoothed) linear total matter pk (in Mpc**3) for a given k (in 1/Mpc) and z |
| |
| .. note:: |
| |
| there is an additional check that `numerical_nowiggle` was set to `yes`, |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk_numerical_nw |
|
|
| if (self.fo.has_pk_numerical_nowiggle == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. You must set `numerical_nowiggle` to `yes` in input") |
|
|
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_numerical_nowiggle,k,z,0,&pk_numerical_nw,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk_numerical_nw |
|
|
| |
| def pk_analytic_nw(self,double k): |
| """ |
| Gives the linear total matter pk (in Mpc**3) for a given k (in 1/Mpc) and z |
| |
| .. note:: |
| |
| there is an additional check that `analytic_nowiggle` was set to `yes`, |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk_analytic_nw |
|
|
| if (self.fo.has_pk_analytic_nowiggle == _FALSE_): |
| raise CosmoSevereError("No analytic nowiggle spectrum computed. You must set `analytic_nowiggle` to `yes` in input") |
|
|
| if fourier_pk_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_analytic_nowiggle,k,0.,self.fo.index_pk_m,&pk_analytic_nw,NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk_analytic_nw |
|
|
| def get_pk(self, np.ndarray[DTYPE_t,ndim=3] k, np.ndarray[DTYPE_t,ndim=1] z, int k_size, int z_size, int mu_size): |
| """ Fast function to get the power spectrum on a k and z array """ |
| self.compute(["fourier"]) |
|
|
| cdef np.ndarray[DTYPE_t, ndim=3] pk = np.zeros((k_size,z_size,mu_size),'float64') |
| cdef int index_k, index_z, index_mu |
|
|
| for index_k in range(k_size): |
| for index_z in range(z_size): |
| for index_mu in range(mu_size): |
| pk[index_k,index_z,index_mu] = self.pk(k[index_k,index_z,index_mu],z[index_z]) |
| return pk |
|
|
| def get_pk_cb(self, np.ndarray[DTYPE_t,ndim=3] k, np.ndarray[DTYPE_t,ndim=1] z, int k_size, int z_size, int mu_size): |
| """ Fast function to get the power spectrum on a k and z array """ |
| self.compute(["fourier"]) |
|
|
| cdef np.ndarray[DTYPE_t, ndim=3] pk_cb = np.zeros((k_size,z_size,mu_size),'float64') |
| cdef int index_k, index_z, index_mu |
|
|
| for index_k in range(k_size): |
| for index_z in range(z_size): |
| for index_mu in range(mu_size): |
| pk_cb[index_k,index_z,index_mu] = self.pk_cb(k[index_k,index_z,index_mu],z[index_z]) |
| return pk_cb |
|
|
| def get_pk_lin(self, np.ndarray[DTYPE_t,ndim=3] k, np.ndarray[DTYPE_t,ndim=1] z, int k_size, int z_size, int mu_size): |
| """ Fast function to get the linear power spectrum on a k and z array """ |
| self.compute(["fourier"]) |
|
|
| cdef np.ndarray[DTYPE_t, ndim=3] pk = np.zeros((k_size,z_size,mu_size),'float64') |
| cdef int index_k, index_z, index_mu |
|
|
| for index_k in range(k_size): |
| for index_z in range(z_size): |
| for index_mu in range(mu_size): |
| pk[index_k,index_z,index_mu] = self.pk_lin(k[index_k,index_z,index_mu],z[index_z]) |
| return pk |
|
|
| def get_pk_cb_lin(self, np.ndarray[DTYPE_t,ndim=3] k, np.ndarray[DTYPE_t,ndim=1] z, int k_size, int z_size, int mu_size): |
| """ Fast function to get the linear power spectrum on a k and z array """ |
| self.compute(["fourier"]) |
|
|
| cdef np.ndarray[DTYPE_t, ndim=3] pk_cb = np.zeros((k_size,z_size,mu_size),'float64') |
| cdef int index_k, index_z, index_mu |
|
|
| for index_k in range(k_size): |
| for index_z in range(z_size): |
| for index_mu in range(mu_size): |
| pk_cb[index_k,index_z,index_mu] = self.pk_cb_lin(k[index_k,index_z,index_mu],z[index_z]) |
| return pk_cb |
|
|
| def get_pk_all(self, k, z, nonlinear = True, cdmbar = False, z_axis_in_k_arr = 0, interpolation_kind='cubic'): |
| """ General function to get the P(k,z) for ARBITRARY shapes of k,z |
| Additionally, it includes the functionality of selecting wether to use the non-linear parts or not, |
| and wether to use the cdm baryon power spectrum only |
| For Multi-Dimensional k-arrays, it assumes that one of the dimensions is the z-axis |
| This is handled by the z_axis_in_k_arr integer, as described in the source code """ |
| self.compute(["fourier"]) |
|
|
| |
| |
| |
| |
|
|
| |
| |
| ispkcb = cdmbar and not (self.ba.Omega0_ncdm_tot == 0.) |
|
|
| |
| cdef np.float64_t[::1] pk_out = np.empty(self.fo.k_size, dtype='float64') |
| k_out = np.asarray(<np.float64_t[:self.fo.k_size]> self.fo.k) |
|
|
| |
| def _write_pk(z,islinear,ispkcb): |
| if fourier_pk_at_z(&self.ba,&self.fo,linear,(pk_linear if islinear else pk_nonlinear),z,(self.fo.index_pk_cb if ispkcb else self.fo.index_pk_m),&pk_out[0],NULL)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| |
| if nonlinear: |
| if self.fo.index_tau_min_nl == 0: |
| z_max_nonlinear = np.inf |
| else: |
| z_max_nonlinear = self.z_of_tau(self.fo.tau[self.fo.index_tau_min_nl]) |
| else: |
| z_max_nonlinear = -1. |
|
|
| |
| def _islinear(z): |
| if z > z_max_nonlinear or (self.fo.method == nl_none): |
| return True |
| else: |
| return False |
|
|
| |
| def _interpolate_pk_at_z(karr,z): |
| _write_pk(z,_islinear(z),ispkcb) |
| interp_func = interp1d(k_out,np.log(pk_out),kind=interpolation_kind,copy=True) |
| return np.exp(interp_func(karr)) |
|
|
| |
| if not isinstance(z,(list,np.ndarray)): |
| |
| if not isinstance(k,(list,np.ndarray)): |
| |
| |
| return ((self.pk_cb if ispkcb else self.pk) if not _islinear(z) else (self.pk_cb_lin if ispkcb else self.pk_lin))(k,z) |
| else: |
| k_arr = np.array(k) |
| result = _interpolate_pk_at_z(k_arr,z) |
| return result |
|
|
| |
| k_arr = np.array(k) |
| z_arr = np.array(z) |
| if( z_arr.ndim != 1 ): |
| raise CosmoSevereError("Can only parse one-dimensional z-arrays, not multi-dimensional") |
|
|
| if( k_arr.ndim > 1 ): |
| |
| out_pk = np.empty(np.shape(k_arr)) |
| |
| k_arr = np.moveaxis(k_arr, z_axis_in_k_arr, 0) |
| out_pk = np.moveaxis(out_pk, z_axis_in_k_arr, 0) |
| if( len(k_arr) != len(z_arr) ): |
| raise CosmoSevereError("Mismatching array lengths of the z-array") |
| for index_z in range(len(z_arr)): |
| out_pk[index_z] = _interpolate_pk_at_z(k_arr[index_z],z[index_z]) |
| |
| k_arr = np.moveaxis(k_arr, 0, z_axis_in_k_arr) |
| out_pk = np.moveaxis(out_pk, 0, z_axis_in_k_arr) |
| return out_pk |
| else: |
| |
| if isinstance(k_arr[0],(list,np.ndarray)): |
| |
| out_pk = [] |
| for index_z in range(len(z_arr)): |
| k_arr_at_z = np.array(k_arr[index_z]) |
| out_pk_at_z = _interpolate_pk_at_z(k_arr_at_z,z[index_z]) |
| out_pk.append(out_pk_at_z) |
| return out_pk |
|
|
| |
| |
| |
| out_pk = np.empty((len(z_arr),len(k_arr))) |
| for index_z in range(len(z_arr)): |
| out_pk[index_z] = _interpolate_pk_at_z(k_arr,z_arr[index_z]) |
| return out_pk |
|
|
| |
| |
| def get_pk_and_k_and_z(self, nonlinear=True, only_clustering_species = False, h_units=False): |
| """ |
| Returns a grid of matter power spectrum values and the z and k |
| at which it has been fully computed. Useful for creating interpolators. |
| |
| Parameters |
| ---------- |
| nonlinear : bool |
| Whether the returned power spectrum values are linear or non-linear (default) |
| only_clustering_species : bool |
| Whether the returned power spectrum is for galaxy clustering and excludes massive neutrinos, or always includes everything (default) |
| h_units : bool |
| Whether the units of k in output are h/Mpc or 1/Mpc (default) |
| |
| Returns |
| ------- |
| pk : grid of power spectrum values, pk[index_k,index_z] |
| k : vector of k values, k[index_k] (in units of 1/Mpc by default, or h/Mpc when setting h_units to True) |
| z : vector of z values, z[index_z] |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef np.ndarray[DTYPE_t,ndim=2] pk = np.zeros((self.fo.k_size_pk, self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=1] k = np.zeros((self.fo.k_size_pk),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=1] z = np.zeros((self.fo.ln_tau_size),'float64') |
| cdef int index_k, index_tau, index_pk |
| cdef double z_max_nonlinear, z_max_requested |
| |
|
|
| if self.fo.has_pk_matter == False: |
| raise CosmoSevereError("You ask classy to return an array of P(k,z) values, but the input parameters sent to CLASS did not require any P(k,z) calculations; add 'mPk' in 'output'") |
|
|
| if nonlinear == True and self.fo.method == nl_none: |
| raise CosmoSevereError("You ask classy to return an array of nonlinear P(k,z) values, but the input parameters sent to CLASS did not require any non-linear P(k,z) calculations; add e.g. 'halofit' or 'HMcode' in 'nonlinear'") |
|
|
| |
| if (only_clustering_species == True): |
| index_pk = self.fo.index_pk_cluster |
| else: |
| index_pk = self.fo.index_pk_total |
|
|
| |
| |
| |
|
|
| if self.fo.ln_tau_size == 1: |
| raise CosmoSevereError("You ask classy to return an array of P(k,z) values, but the input parameters sent to CLASS did not require any P(k,z) calculations for z>0; pass either a list of z in 'z_pk' or one non-zero value in 'z_max_pk'") |
| else: |
| for index_tau in range(self.fo.ln_tau_size): |
| if index_tau == self.fo.ln_tau_size-1: |
| z[index_tau] = 0. |
| else: |
| z[index_tau] = self.z_of_tau(np.exp(self.fo.ln_tau[index_tau])) |
|
|
| |
|
|
| if nonlinear == True: |
| |
| |
| z_max_nonlinear = self.z_of_tau(self.fo.tau[self.fo.index_tau_min_nl]) |
|
|
| |
| z_max_requested = z[0] |
|
|
| |
| |
| |
| if (z_max_requested > z_max_nonlinear and self.fo.index_tau_min_nl>0): |
| raise CosmoSevereError("get_pk_and_k_and_z() is trying to return P(k,z) up to z_max=%e (the redshift range of computed pk); but the input parameters sent to CLASS (in particular ppr->nonlinear_min_k_max=%e) were such that the non-linear P(k,z) could only be consistently computed up to z=%e; increase the precision parameter 'nonlinear_min_k_max', or only obtain the linear pk"%(z_max_requested,self.pr.nonlinear_min_k_max,z_max_nonlinear)) |
|
|
| |
|
|
| if h_units: |
| units=1./self.ba.h |
| else: |
| units=1 |
|
|
| for index_k in range(self.fo.k_size_pk): |
| k[index_k] = self.fo.k[index_k]*units |
|
|
| |
|
|
| for index_tau in range(self.fo.ln_tau_size): |
| for index_k in range(self.fo.k_size_pk): |
| if nonlinear == True: |
| pk[index_k, index_tau] = np.exp(self.fo.ln_pk_nl[index_pk][index_tau * self.fo.k_size + index_k]) |
| else: |
| pk[index_k, index_tau] = np.exp(self.fo.ln_pk_l[index_pk][index_tau * self.fo.k_size + index_k]) |
|
|
| return pk, k, z |
|
|
| |
| |
| def get_transfer_and_k_and_z(self, output_format='class', h_units=False): |
| """ |
| Returns a dictionary of grids of density and/or velocity transfer function values and the z and k at which it has been fully computed. |
| Useful for creating interpolators. |
| When setting CLASS input parameters, include at least one of 'dTk' (for density transfer functions) or 'vTk' (for velocity transfer functions). |
| Following the default output_format='class', all transfer functions will be normalised to 'curvature R=1' at initial time |
| (and not 'curvature R = -1/k^2' like in CAMB). |
| You may switch to output_format='camb' for the CAMB definition and normalisation of transfer functions. |
| (Then, 'dTk' must be in the input: the CAMB format only outputs density transfer functions). |
| When sticking to output_format='class', you also get the newtonian metric fluctuations phi and psi. |
| If you set the CLASS input parameter 'extra_metric_transfer_functions' to 'yes', |
| you get additional metric fluctuations in the synchronous and N-body gauges. |
| |
| Parameters |
| ---------- |
| output_format : ('class' or 'camb') |
| Format transfer functions according to CLASS (default) or CAMB |
| h_units : bool |
| Whether the units of k in output are h/Mpc or 1/Mpc (default) |
| |
| Returns |
| ------- |
| tk : dictionary containing all transfer functions. |
| For instance, the grid of values of 'd_c' (= delta_cdm) is available in tk['d_c'] |
| All these grids have indices [index_k,index,z], for instance tk['d_c'][index_k,index,z] |
| k : vector of k values (in units of 1/Mpc by default, or h/Mpc when setting h_units to True) |
| z : vector of z values |
| """ |
| self.compute(["transfer"]) |
|
|
| cdef np.ndarray[DTYPE_t,ndim=1] k = np.zeros((self.pt.k_size_pk),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=1] z = np.zeros((self.pt.ln_tau_size),'float64') |
| cdef int index_k, index_tau |
| cdef char * titles |
| cdef double * data |
| cdef file_format outf |
|
|
| |
| if (self.pt.has_density_transfers == False) and (self.pt.has_velocity_transfers == False): |
| raise CosmoSevereError("You ask classy to return transfer functions, but the input parameters sent to CLASS did not require any T(k,z) calculations; add 'dTk' and/or 'vTk' in 'output'") |
|
|
| index_md = self.pt.index_md_scalars; |
|
|
| if (self.pt.ic_size[index_md] > 1): |
| raise CosmoSevereError("For simplicity, get_transfer_and_k_and_z() has been written assuming only adiabatic initial conditions. You need to write the generalisation to cases with multiple initial conditions.") |
|
|
| |
| if output_format == 'camb': |
| outf = camb_format |
| else: |
| outf = class_format |
|
|
| |
|
|
| titles = <char*>calloc(_MAXTITLESTRINGLENGTH_,sizeof(char)) |
|
|
| if perturbations_output_titles(&self.ba,&self.pt, outf, titles)==_FAILURE_: |
| free(titles) |
| raise CosmoSevereError(self.pt.error_message) |
|
|
| tmp = <bytes> titles |
| tmp = str(tmp.decode()) |
| names = tmp.split("\t")[:-1] |
|
|
| free(titles) |
|
|
| number_of_titles = len(names) |
|
|
| |
| |
|
|
| if self.pt.ln_tau_size == 1: |
| raise CosmoSevereError("You ask classy to return an array of T_x(k,z) values, but the input parameters sent to CLASS did not require any transfer function calculations for z>0; pass either a list of z in 'z_pk' or one non-zero value in 'z_max_pk'") |
| else: |
| for index_tau in range(self.pt.ln_tau_size): |
| if index_tau == self.pt.ln_tau_size-1: |
| z[index_tau] = 0. |
| else: |
| z[index_tau] = self.z_of_tau(np.exp(self.pt.ln_tau[index_tau])) |
|
|
| |
|
|
| if h_units: |
| units=1./self.ba.h |
| else: |
| units=1 |
|
|
| k_size = self.pt.k_size_pk |
| for index_k in range(k_size): |
| k[index_k] = self.pt.k[index_md][index_k]*units |
|
|
| |
|
|
| tk = {} |
| for index_type,name in enumerate(names): |
| if index_type > 0: |
| tk[name] = np.zeros((k_size, len(z)),'float64') |
|
|
| |
| data = <double*>malloc(sizeof(double)*number_of_titles*self.pt.k_size[index_md]) |
|
|
| |
|
|
| for index_tau in range(len(z)): |
| if perturbations_output_data_at_index_tau(&self.ba, &self.pt, outf, index_tau, number_of_titles, data)==_FAILURE_: |
| free(data) |
| raise CosmoSevereError(self.pt.error_message) |
|
|
| for index_type,name in enumerate(names): |
| if index_type > 0: |
| for index_k in range(k_size): |
| tk[name][index_k, index_tau] = data[index_k*number_of_titles+index_type] |
|
|
| free(data) |
| return tk, k, z |
|
|
| |
| |
| def get_Weyl_pk_and_k_and_z(self, nonlinear=False, h_units=False): |
| """ |
| Returns a grid of Weyl potential (phi+psi) power spectrum values and the z and k |
| at which it has been fully computed. Useful for creating interpolators. |
| Note that this function just calls get_pk_and_k_and_z and corrects the output |
| by the ratio of transfer functions [(phi+psi)/d_m]^2. |
| |
| Parameters |
| ---------- |
| nonlinear : bool |
| Whether the returned power spectrum values are linear or non-linear (default) |
| h_units : bool |
| Whether the units of k in output are h/Mpc or 1/Mpc (default) |
| |
| Returns |
| ------- |
| Weyl_pk : grid of Weyl potential (phi+psi) spectrum values, Weyl_pk[index_k,index_z] |
| k : vector of k values, k[index_k] (in units of 1/Mpc by default, or h/Mpc when setting h_units to True) |
| z : vector of z values, z[index_z] |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef np.ndarray[DTYPE_t,ndim=2] pk = np.zeros((self.fo.k_size_pk,self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=1] z = np.zeros((self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=2] k4 = np.zeros((self.fo.k_size_pk, self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=2] phi = np.zeros((self.fo.k_size_pk, self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=2] psi = np.zeros((self.fo.k_size_pk, self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=2] d_m = np.zeros((self.fo.k_size_pk, self.fo.ln_tau_size),'float64') |
| cdef np.ndarray[DTYPE_t,ndim=2] Weyl_pk = np.zeros((self.fo.k_size_pk, self.fo.ln_tau_size),'float64') |
|
|
| cdef bint input_nonlinear = nonlinear |
| cdef bint input_h_units = h_units |
|
|
| cdef int index_z |
|
|
| |
| pk, k, z = self.get_pk_and_k_and_z(nonlinear=input_nonlinear, only_clustering_species = False, h_units=input_h_units) |
|
|
| |
| tk_and_k_and_z = {} |
| tk_and_k_and_z, k, z = self.get_transfer_and_k_and_z(output_format='class',h_units=input_h_units) |
| phi = tk_and_k_and_z['phi'] |
| psi = tk_and_k_and_z['psi'] |
| d_m = tk_and_k_and_z['d_m'] |
|
|
| |
| for index_z in range(self.fo.ln_tau_size): |
| k4[:,index_z] = k**4 |
|
|
| |
| |
| |
| Weyl_pk = pk * ((phi+psi)/2./d_m)**2 * k4 |
|
|
| return Weyl_pk, k, z |
|
|
| |
| |
| def sigma(self,R,z, h_units = False): |
| """ |
| Gives sigma (total matter) for a given R and z |
| (R is the radius in units of Mpc, so if R=8/h this will be the usual sigma8(z). |
| This is unless h_units is set to true, in which case R is the radius in units of Mpc/h, |
| and R=8 corresponds to sigma8(z)) |
| |
| .. note:: |
| |
| there is an additional check to verify whether output contains `mPk`, |
| and whether k_max > ... |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double sigma |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
| Rarr = np.atleast_1d(R).astype(np.float64) |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get sigma(R,z) you must add mPk to the list of outputs.") |
|
|
| if (self.pt.k_max_for_pk < self.ba.h): |
| raise CosmoSevereError("In order to get sigma(R,z) you must set 'P_k_max_h/Mpc' to 1 or bigger, in order to have k_max > 1 h/Mpc.") |
|
|
| R_in_Mpc = (Rarr if not h_units else Rarr/self.ba.h) |
|
|
| pairs = np.array(np.meshgrid(zarr,R_in_Mpc)).T.reshape(-1,2) |
|
|
| sigmas = np.empty(pairs.shape[0]) |
| for ip, pair in enumerate(pairs): |
| if fourier_sigmas_at_z(&self.pr,&self.ba,&self.fo,pair[1],pair[0],self.fo.index_pk_m,out_sigma,&sigma)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
| sigmas[ip] = sigma |
|
|
| return (sigmas[0] if (np.isscalar(z) and np.isscalar(R)) else np.squeeze(sigmas.reshape(len(zarr),len(Rarr)))) |
|
|
| |
| def sigma_cb(self,double R,double z, h_units = False): |
| """ |
| Gives sigma (cdm+b) for a given R and z |
| (R is the radius in units of Mpc, so if R=8/h this will be the usual sigma8(z) |
| This is unless h_units is set to true, in which case R is the radius in units of Mpc/h, |
| and R=8 corresponds to sigma8(z)) |
| |
| .. note:: |
| |
| there is an additional check to verify whether output contains `mPk`, |
| and whether k_max > ... |
| because otherwise a segfault will occur |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double sigma_cb |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
| Rarr = np.atleast_1d(R).astype(np.float64) |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get sigma(R,z) you must add mPk to the list of outputs.") |
|
|
| if (self.fo.has_pk_cb == _FALSE_): |
| raise CosmoSevereError("sigma_cb not computed by CLASS (probably because there are no massive neutrinos)") |
|
|
| if (self.pt.k_max_for_pk < self.ba.h): |
| raise CosmoSevereError("In order to get sigma(R,z) you must set 'P_k_max_h/Mpc' to 1 or bigger, in order to have k_max > 1 h/Mpc.") |
|
|
| R_in_Mpc = (Rarr if not h_units else Rarr/self.ba.h) |
|
|
| pairs = np.array(np.meshgrid(zarr,R_in_Mpc)).T.reshape(-1,2) |
|
|
| sigmas_cb = np.empty(pairs.shape[0]) |
| for ip, pair in enumerate(pairs): |
| if fourier_sigmas_at_z(&self.pr,&self.ba,&self.fo,R,z,self.fo.index_pk_cb,out_sigma,&sigma_cb)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
| sigmas_cb[ip] = sigma_cb |
|
|
| return (sigmas_cb[0] if (np.isscalar(z) and np.isscalar(R)) else np.squeeze(sigmas_cb.reshape(len(zarr),len(Rarr)))) |
|
|
| |
| def pk_tilt(self,double k,double z): |
| """ |
| Gives effective logarithmic slope of P_L(k,z) (total matter) for a given k and z |
| (k is the wavenumber in units of 1/Mpc, z is the redshift, the output is dimensionless) |
| |
| .. note:: |
| |
| there is an additional check to verify whether output contains `mPk` and whether k is in the right range |
| |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef double pk_tilt |
|
|
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get pk_tilt(k,z) you must add mPk to the list of outputs.") |
|
|
| if (k < self.fo.k[1] or k > self.fo.k[self.fo.k_size-2]): |
| raise CosmoSevereError("In order to get pk_tilt at k=%e 1/Mpc, you should compute P(k,z) in a wider range of k's"%k) |
|
|
| if fourier_pk_tilt_at_k_and_z(&self.ba,&self.pm,&self.fo,pk_linear,k,z,self.fo.index_pk_total,&pk_tilt)==_FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return pk_tilt |
|
|
| def age(self): |
| self.compute(["background"]) |
| return self.ba.age |
|
|
| def h(self): |
| return self.ba.h |
|
|
| def n_s(self): |
| return self.pm.n_s |
|
|
| def tau_reio(self): |
| self.compute(["thermodynamics"]) |
| return self.th.tau_reio |
|
|
| def Omega_m(self): |
| return self.ba.Omega0_m |
|
|
| def Omega_r(self): |
| return self.ba.Omega0_r |
|
|
| def theta_s_100(self): |
| self.compute(["thermodynamics"]) |
| return 100.*self.th.rs_rec/self.th.da_rec/(1.+self.th.z_rec) |
|
|
| def theta_star_100(self): |
| self.compute(["thermodynamics"]) |
| return 100.*self.th.rs_star/self.th.da_star/(1.+self.th.z_star) |
|
|
| def Omega_Lambda(self): |
| return self.ba.Omega0_lambda |
|
|
| def Omega_g(self): |
| return self.ba.Omega0_g |
|
|
| def Omega_b(self): |
| return self.ba.Omega0_b |
|
|
| def omega_b(self): |
| return self.ba.Omega0_b * self.ba.h * self.ba.h |
|
|
| def Neff(self): |
| self.compute(["background"]) |
| return self.ba.Neff |
|
|
| def k_eq(self): |
| self.compute(["background"]) |
| return self.ba.a_eq*self.ba.H_eq |
|
|
| def z_eq(self): |
| self.compute(["background"]) |
| return 1./self.ba.a_eq-1. |
|
|
| def sigma8(self): |
| self.compute(["fourier"]) |
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get sigma8, you must add mPk to the list of outputs.") |
| return self.fo.sigma8[self.fo.index_pk_m] |
|
|
| def S8(self): |
| return self.sigma8()*np.sqrt(self.Omega_m()/0.3) |
|
|
| |
| |
| |
|
|
| def sigma8_cb(self): |
| self.compute(["fourier"]) |
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get sigma8_cb, you must add mPk to the list of outputs.") |
| return self.fo.sigma8[self.fo.index_pk_cb] |
|
|
| def rs_drag(self): |
| self.compute(["thermodynamics"]) |
| return self.th.rs_d |
|
|
| def z_reio(self): |
| self.compute(["thermodynamics"]) |
| return self.th.z_reio |
|
|
| def angular_distance(self, z): |
| """ |
| angular_distance(z) |
| |
| Return the angular diameter distance (exactly, the quantity defined by Class |
| as index_bg_ang_distance in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| D_A = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| D_A[iz] = pvecback[self.ba.index_bg_ang_distance] |
|
|
| free(pvecback) |
|
|
| return (D_A[0] if np.isscalar(z) else D_A) |
|
|
| |
| |
| def angular_distance_from_to(self, z1, z2): |
| """ |
| angular_distance_from_to(z) |
| |
| Return the angular diameter distance of object at z2 as seen by observer at z1, |
| that is, sin_K((chi2-chi1)*np.sqrt(|k|))/np.sqrt(|k|)/(1+z2). |
| If z1>z2 returns zero. |
| |
| Parameters |
| ---------- |
| z1 : float |
| Observer redshift |
| z2 : float |
| Source redshift |
| |
| Returns |
| ------- |
| d_A(z1,z2) in Mpc |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| if z1>=z2: |
| return 0. |
|
|
| else: |
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| if background_at_z(&self.ba,z1,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| |
| chi1 = pvecback[self.ba.index_bg_conf_distance] |
|
|
| if background_at_z(&self.ba,z2,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| |
| chi2 = pvecback[self.ba.index_bg_conf_distance] |
|
|
| free(pvecback) |
|
|
| if self.ba.K == 0: |
| return (chi2-chi1)/(1+z2) |
| elif self.ba.K > 0: |
| return np.sin(np.sqrt(self.ba.K)*(chi2-chi1))/np.sqrt(self.ba.K)/(1+z2) |
| elif self.ba.K < 0: |
| return np.sinh(np.sqrt(-self.ba.K)*(chi2-chi1))/np.sqrt(-self.ba.K)/(1+z2) |
|
|
| def comoving_distance(self, z): |
| """ |
| comoving_distance(z) |
| |
| Return the comoving distance |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| r = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| r[iz] = pvecback[self.ba.index_bg_conf_distance] |
|
|
| free(pvecback) |
|
|
| return (r[0] if np.isscalar(z) else r) |
|
|
| def scale_independent_growth_factor(self, z): |
| """ |
| scale_independent_growth_factor(z) |
| |
| Return the scale invariant growth factor D(a) for CDM perturbations |
| (exactly, the quantity defined by Class as index_bg_D in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| D = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| D[iz] = pvecback[self.ba.index_bg_D] |
|
|
| free(pvecback) |
|
|
| return (D[0] if np.isscalar(z) else D) |
|
|
| def scale_independent_growth_factor_f(self, z): |
| """ |
| scale_independent_growth_factor_f(z) |
| |
| Return the scale independent growth factor f(z)=d ln D / d ln a for CDM perturbations |
| (exactly, the quantity defined by Class as index_bg_f in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| f = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| f[iz] = pvecback[self.ba.index_bg_f] |
|
|
| free(pvecback) |
|
|
| return (f[0] if np.isscalar(z) else f) |
|
|
| |
| def scale_dependent_growth_factor_f(self, k, z, h_units=False, nonlinear=False, Nz=20): |
| """ |
| scale_dependent_growth_factor_f(k,z) |
| |
| Return the scale dependent growth factor |
| f(z)= 1/2 * [d ln P(k,a) / d ln a] |
| = - 0.5 * (1+z) * [d ln P(k,z) / d z] |
| where P(k,z) is the total matter power spectrum |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| k : float |
| Desired wavenumber in 1/Mpc (if h_units=False) or h/Mpc (if h_units=True) |
| """ |
| self.compute(["fourier"]) |
|
|
| |
| |
| if self.fo.ln_tau_size > 1: |
| |
| z_max = self.z_of_tau(np.exp(self.fo.ln_tau[0])) |
| if (z<0) or (z>z_max): |
| raise CosmoSevereError("You asked for f(k,z) at a redshift %e outside of the computed range [0,%e]"%(z,z_max)) |
| |
| z_array = np.empty(self.fo.ln_tau_size) |
| |
| z_array[0]=0. |
| |
| if (self.fo.ln_tau_size>1): |
| for i in range(1,self.fo.ln_tau_size): |
| z_array[i] = self.z_of_tau(np.exp(self.fo.ln_tau[self.fo.ln_tau_size-1-i])) |
| else: |
| raise CosmoSevereError("You asked for the scale-dependent growth factor: this requires numerical derivation of P(k,z) w.r.t z, and thus passing a non-zero input parameter z_max_pk") |
|
|
| |
| if h_units: |
| k = k*self.ba.h |
|
|
| |
| Pk_array = np.empty_like(z_array) |
|
|
| |
| |
| |
| if nonlinear == False: |
| if self.fo.method == nl_none: |
| use_pk_lin = False |
| else: |
| use_pk_lin = True |
| else: |
| if self.fo.method == nl_none: |
| raise CosmoSevereError("You asked for the scale-dependent growth factor of non-linear matter fluctuations, but you did not ask for non-linear calculations at all") |
| else: |
| use_pk_lin = False |
|
|
| |
| if use_pk_lin == False: |
| Pk = self.pk(k,z) |
| for iz, zval in enumerate(z_array): |
| Pk_array[iz] = self.pk(k,zval) |
| else: |
| Pk = self.pk_lin(k,z) |
| for iz, zval in enumerate(z_array): |
| Pk_array[iz] = self.pk_lin(k,zval) |
|
|
| |
| dPkdz = UnivariateSpline(z_array,Pk_array,s=0).derivative()(z) |
|
|
| |
| f = -0.5*(1+z)*dPkdz/Pk |
|
|
| return f |
|
|
| |
| def scale_dependent_growth_factor_f_cb(self, k, z, h_units=False, nonlinear=False, Nz=20): |
| """ |
| scale_dependent_growth_factor_f_cb(k,z) |
| |
| Return the scale dependent growth factor calculated from CDM+baryon power spectrum P_cb(k,z) |
| f(z)= 1/2 * [d ln P_cb(k,a) / d ln a] |
| = - 0.5 * (1+z) * [d ln P_cb(k,z) / d z] |
| |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| k : float |
| Desired wavenumber in 1/Mpc (if h_units=False) or h/Mpc (if h_units=True) |
| """ |
|
|
| |
| |
| if self.fo.ln_tau_size > 1: |
| |
| z_max = self.z_of_tau(np.exp(self.fo.ln_tau[0])) |
| if (z<0) or (z>z_max): |
| raise CosmoSevereError("You asked for f_cb(k,z) at a redshift %e outside of the computed range [0,%e]"%(z,z_max)) |
| |
| z_array = np.empty(self.fo.ln_tau_size) |
| |
| z_array[0]=0. |
| |
| if (self.fo.ln_tau_size>1): |
| for i in range(1,self.fo.ln_tau_size): |
| z_array[i] = self.z_of_tau(np.exp(self.fo.ln_tau[self.fo.ln_tau_size-1-i])) |
| else: |
| raise CosmoSevereError("You asked for the scale-dependent growth factor: this requires numerical derivation of P(k,z) w.r.t z, and thus passing a non-zero input parameter z_max_pk") |
|
|
| |
| if h_units: |
| k = k*self.ba.h |
|
|
| |
| Pk_array = np.empty_like(z_array) |
|
|
| |
| |
| |
| if nonlinear == False: |
| if self.fo.method == nl_none: |
| use_pk_lin = False |
| else: |
| use_pk_lin = True |
| else: |
| if self.fo.method == nl_none: |
| raise CosmoSevereError("You asked for the scale-dependent growth factor of non-linear matter fluctuations, but you did not ask for non-linear calculations at all") |
| else: |
| use_pk_lin = False |
|
|
| |
| if use_pk_lin == False: |
| Pk = self.pk(k,z) |
| for iz, zval in enumerate(z_array): |
| Pk_array[iz] = self.pk_cb(k,zval) |
| else: |
| Pk = self.pk_lin(k,z) |
| for iz, zval in enumerate(z_array): |
| Pk_array[iz] = self.pk_cb_lin(k,zval) |
|
|
| |
| dPkdz = UnivariateSpline(z_array,Pk_array,s=0).derivative()(z) |
|
|
| |
| f = -0.5*(1+z)*dPkdz/Pk |
|
|
| return f |
|
|
| |
| |
| def scale_independent_f_sigma8(self, z): |
| """ |
| scale_independent_f_sigma8(z) |
| |
| Return the scale independent growth factor f(z) multiplied by sigma8(z) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| |
| Returns |
| ------- |
| f(z)*sigma8(z) (dimensionless) |
| """ |
| return self.scale_independent_growth_factor_f(z)*self.sigma(8,z,h_units=True) |
|
|
| |
| |
| def effective_f_sigma8(self, z, z_step=0.1): |
| """ |
| effective_f_sigma8(z) |
| |
| Returns the time derivative of sigma8(z) computed as (d sigma8/d ln a) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| z_step : float |
| Default step used for the numerical two-sided derivative. For z < z_step the step is reduced progressively down to z_step/10 while sticking to a double-sided derivative. For z< z_step/10 a single-sided derivative is used instead. |
| |
| Returns |
| ------- |
| (d ln sigma8/d ln a)(z) (dimensionless) |
| """ |
|
|
| |
| if hasattr(z, "__len__"): |
| out_array = np.empty_like(z,dtype=np.float64) |
| for iz, redshift in enumerate(z): |
| out_array[iz] = self.effective_f_sigma8(redshift, z_step=z_step) |
| return out_array |
|
|
| |
| if z >= z_step: |
| return (self.sigma(8,z-z_step,h_units=True)-self.sigma(8,z+z_step,h_units=True))/(2.*z_step)*(1+z) |
| else: |
| |
| if (z > z_step/10.): |
| z_step = z |
| return (self.sigma(8,z-z_step,h_units=True)-self.sigma(8,z+z_step,h_units=True))/(2.*z_step)*(1+z) |
| |
| else: |
| z_step /=10 |
| return (self.sigma(8,z,h_units=True)-self.sigma(8,z+z_step,h_units=True))/z_step*(1+z) |
|
|
| |
| |
| def effective_f_sigma8_spline(self, z, Nz=20): |
| """ |
| effective_f_sigma8_spline(z) |
| |
| Returns the time derivative of sigma8(z) computed as (d sigma8/d ln a) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| Nz : integer |
| Number of values used to spline sigma8(z) in the range [z-0.1,z+0.1] |
| |
| Returns |
| ------- |
| (d ln sigma8/d ln a)(z) (dimensionless) |
| """ |
| self.compute(["fourier"]) |
|
|
| if hasattr(z, "__len__"): |
| out_array = np.empty_like(z,dtype=np.float64) |
| for iz, redshift in enumerate(z): |
| out_array[iz] = self.effective_f_sigma8_spline(redshift, Nz=Nz) |
| return out_array |
|
|
| |
| if self.fo.ln_tau_size>0: |
| z_max = self.z_of_tau(np.exp(self.fo.ln_tau[0])) |
| else: |
| z_max = 0 |
|
|
| if (z<0) or (z>z_max): |
| raise CosmoSevereError("You asked for effective_f_sigma8 at a redshift %e outside of the computed range [0,%e]"%(z,z_max)) |
|
|
| if (z<0.1): |
| z_array = np.linspace(0, 0.2, num = Nz) |
| elif (z<z_max-0.1): |
| z_array = np.linspace(z-0.1, z+0.1, num = Nz) |
| else: |
| z_array = np.linspace(z_max-0.2, z_max, num = Nz) |
|
|
| sig8_array = self.sigma(8,z_array,h_units=True) |
| return -CubicSpline(z_array,sig8_array).derivative()(z)*(1+z) |
|
|
| |
| def z_of_tau(self, tau): |
| """ |
| Redshift corresponding to a given conformal time. |
| |
| Parameters |
| ---------- |
| tau : float |
| Conformal time |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| tauarr = np.atleast_1d(tau).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| z = np.empty_like(tauarr) |
| for itau, tauval in enumerate(tauarr): |
| if background_at_tau(&self.ba,tauval,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| z[itau] = 1./pvecback[self.ba.index_bg_a]-1. |
|
|
| free(pvecback) |
|
|
| return (z[0] if np.isscalar(tau) else z) |
|
|
| def Hubble(self, z): |
| """ |
| Hubble(z) |
| |
| Return the Hubble rate (exactly, the quantity defined by Class as index_bg_H |
| in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| H = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| H[iz] = pvecback[self.ba.index_bg_H] |
|
|
| free(pvecback) |
|
|
| return (H[0] if np.isscalar(z) else H) |
|
|
| def Om_m(self, z): |
| """ |
| Omega_m(z) |
| |
| Return the matter density fraction (exactly, the quantity defined by Class as index_bg_Omega_m |
| in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| Om_m = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| Om_m[iz] = pvecback[self.ba.index_bg_Omega_m] |
|
|
| free(pvecback) |
|
|
| return (Om_m[0] if np.isscalar(z) else Om_m) |
|
|
| def Om_b(self, z): |
| """ |
| Omega_b(z) |
| |
| Return the baryon density fraction (exactly, the ratio of quantities defined by Class as |
| index_bg_rho_b and index_bg_rho_crit in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| Om_b = np.empty_like(zarr) |
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| Om_b[iz] = pvecback[self.ba.index_bg_rho_b]/pvecback[self.ba.index_bg_rho_crit] |
|
|
| free(pvecback) |
|
|
| return (Om_b[0] if np.isscalar(z) else Om_b) |
|
|
| def Om_cdm(self, z): |
| """ |
| Omega_cdm(z) |
| |
| Return the cdm density fraction (exactly, the ratio of quantities defined by Class as |
| index_bg_rho_cdm and index_bg_rho_crit in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| Om_cdm = np.zeros_like(zarr) |
|
|
| if self.ba.has_cdm == True: |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
| for iz, redshift in enumerate(zarr): |
|
|
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| Om_cdm[iz] = pvecback[self.ba.index_bg_rho_cdm]/pvecback[self.ba.index_bg_rho_crit] |
|
|
| free(pvecback) |
|
|
| return (Om_cdm[0] if np.isscalar(z) else Om_cdm) |
|
|
| def Om_ncdm(self, z): |
| """ |
| Omega_ncdm(z) |
| |
| Return the ncdm density fraction (exactly, the ratio of quantities defined by Class as |
| Sum_m [ index_bg_rho_ncdm1 + n ], with n=0...N_ncdm-1, and index_bg_rho_crit in the background module) |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["background"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
|
|
| Om_ncdm = np.zeros_like(zarr) |
|
|
| if self.ba.has_ncdm == True: |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
|
|
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| rho_ncdm = 0. |
| for n in range(self.ba.N_ncdm): |
| rho_ncdm += pvecback[self.ba.index_bg_rho_ncdm1+n] |
| Om_ncdm[iz] = rho_ncdm/pvecback[self.ba.index_bg_rho_crit] |
|
|
| free(pvecback) |
|
|
| return (Om_ncdm[0] if np.isscalar(z) else Om_ncdm) |
|
|
| def ionization_fraction(self, z): |
| """ |
| ionization_fraction(z) |
| |
| Return the ionization fraction for a given redshift z |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["thermodynamics"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
| cdef double * pvecthermo |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
| xe = np.empty_like(zarr) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
| pvecthermo = <double*> calloc(self.th.th_size,sizeof(double)) |
|
|
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| free(pvecthermo) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| if thermodynamics_at_z(&self.ba,&self.th,redshift,inter_normal,&last_index,pvecback,pvecthermo) == _FAILURE_: |
| free(pvecback) |
| free(pvecthermo) |
| raise CosmoSevereError(self.th.error_message) |
|
|
| xe[iz] = pvecthermo[self.th.index_th_xe] |
|
|
| free(pvecback) |
| free(pvecthermo) |
|
|
| return (xe[0] if np.isscalar(z) else xe) |
|
|
| def baryon_temperature(self, z): |
| """ |
| baryon_temperature(z) |
| |
| Give the baryon temperature for a given redshift z |
| |
| Parameters |
| ---------- |
| z : float |
| Desired redshift |
| """ |
| self.compute(["thermodynamics"]) |
|
|
| cdef int last_index |
| cdef double * pvecback |
| cdef double * pvecthermo |
|
|
| zarr = np.atleast_1d(z).astype(np.float64) |
| Tb = np.empty_like(zarr) |
|
|
| pvecback = <double*> calloc(self.ba.bg_size,sizeof(double)) |
| pvecthermo = <double*> calloc(self.th.th_size,sizeof(double)) |
|
|
| for iz, redshift in enumerate(zarr): |
| if background_at_z(&self.ba,redshift,long_info,inter_normal,&last_index,pvecback)==_FAILURE_: |
| free(pvecback) |
| free(pvecthermo) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| if thermodynamics_at_z(&self.ba,&self.th,redshift,inter_normal,&last_index,pvecback,pvecthermo) == _FAILURE_: |
| free(pvecback) |
| free(pvecthermo) |
| raise CosmoSevereError(self.th.error_message) |
|
|
| Tb[iz] = pvecthermo[self.th.index_th_Tb] |
|
|
| free(pvecback) |
| free(pvecthermo) |
|
|
| return (Tb[0] if np.isscalar(z) else Tb) |
|
|
| def T_cmb(self): |
| """ |
| Return the CMB temperature |
| """ |
| return self.ba.T_cmb |
|
|
| |
| |
| def Omega0_m(self): |
| """ |
| Return the sum of Omega0 for all non-relativistic components |
| """ |
| return self.ba.Omega0_m |
|
|
| def get_background(self): |
| """ |
| Return an array of the background quantities at all times. |
| |
| Parameters |
| ---------- |
| |
| Returns |
| ------- |
| background : dictionary containing background. |
| """ |
| self.compute(["background"]) |
|
|
| cdef char *titles |
| cdef double* data |
| titles = <char*>calloc(_MAXTITLESTRINGLENGTH_,sizeof(char)) |
|
|
| if background_output_titles(&self.ba, titles)==_FAILURE_: |
| free(titles) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| tmp = <bytes> titles |
| tmp = str(tmp.decode()) |
| names = tmp.split("\t")[:-1] |
| number_of_titles = len(names) |
| timesteps = self.ba.bt_size |
|
|
| data = <double*>malloc(sizeof(double)*timesteps*number_of_titles) |
|
|
| if background_output_data(&self.ba, number_of_titles, data)==_FAILURE_: |
| free(titles) |
| free(data) |
| raise CosmoSevereError(self.ba.error_message) |
|
|
| background = {} |
|
|
| for i in range(number_of_titles): |
| background[names[i]] = np.zeros(timesteps, dtype=np.double) |
| for index in range(timesteps): |
| background[names[i]][index] = data[index*number_of_titles+i] |
|
|
| free(titles) |
| free(data) |
| return background |
|
|
| def get_thermodynamics(self): |
| """ |
| Return the thermodynamics quantities. |
| |
| Returns |
| ------- |
| thermodynamics : dictionary containing thermodynamics. |
| """ |
| self.compute(["thermodynamics"]) |
|
|
| cdef char *titles |
| cdef double* data |
|
|
| titles = <char*>calloc(_MAXTITLESTRINGLENGTH_,sizeof(char)) |
|
|
| if thermodynamics_output_titles(&self.ba, &self.th, titles)==_FAILURE_: |
| free(titles) |
| raise CosmoSevereError(self.th.error_message) |
|
|
| tmp = <bytes> titles |
| tmp = str(tmp.decode()) |
| names = tmp.split("\t")[:-1] |
| number_of_titles = len(names) |
| timesteps = self.th.tt_size |
|
|
| data = <double*>malloc(sizeof(double)*timesteps*number_of_titles) |
|
|
| if thermodynamics_output_data(&self.ba, &self.th, number_of_titles, data)==_FAILURE_: |
| free(titles) |
| free(data) |
| raise CosmoSevereError(self.th.error_message) |
|
|
| thermodynamics = {} |
|
|
| for i in range(number_of_titles): |
| thermodynamics[names[i]] = np.zeros(timesteps, dtype=np.double) |
| for index in range(timesteps): |
| thermodynamics[names[i]][index] = data[index*number_of_titles+i] |
|
|
| free(titles) |
| free(data) |
| return thermodynamics |
|
|
| def get_primordial(self): |
| """ |
| Return the primordial scalar and/or tensor spectrum depending on 'modes'. |
| 'output' must be set to something, e.g. 'tCl'. |
| |
| Returns |
| ------- |
| primordial : dictionary containing k-vector and primordial scalar and tensor P(k). |
| """ |
| self.compute(["primordial"]) |
|
|
| cdef char *titles |
| cdef double* data |
|
|
| titles = <char*>calloc(_MAXTITLESTRINGLENGTH_,sizeof(char)) |
|
|
| if primordial_output_titles(&self.pt, &self.pm, titles)==_FAILURE_: |
| free(titles) |
| raise CosmoSevereError(self.pm.error_message) |
|
|
| tmp = <bytes> titles |
| tmp = str(tmp.decode()) |
| names = tmp.split("\t")[:-1] |
| number_of_titles = len(names) |
| timesteps = self.pm.lnk_size |
|
|
| data = <double*>malloc(sizeof(double)*timesteps*number_of_titles) |
|
|
| if primordial_output_data(&self.pt, &self.pm, number_of_titles, data)==_FAILURE_: |
| free(titles) |
| free(data) |
| raise CosmoSevereError(self.pm.error_message) |
|
|
| primordial = {} |
|
|
| for i in range(number_of_titles): |
| primordial[names[i]] = np.zeros(timesteps, dtype=np.double) |
| for index in range(timesteps): |
| primordial[names[i]][index] = data[index*number_of_titles+i] |
|
|
| free(titles) |
| free(data) |
| return primordial |
|
|
| def get_perturbations(self, return_copy=True): |
| """ |
| Return scalar, vector and/or tensor perturbations as arrays for requested |
| k-values. |
| |
| .. note:: |
| |
| you need to specify both 'k_output_values', and have some |
| perturbations computed, for instance by setting 'output' to 'tCl'. |
| |
| Do not enable 'return_copy=False' unless you know exactly what you are doing. |
| This will mean that you get access to the direct C pointers inside CLASS. |
| That also means that if class is deallocated, |
| your perturbations array will become invalid. Beware! |
| |
| Returns |
| ------- |
| perturbations : dict of array of dicts |
| perturbations['scalar'] is an array of length 'k_output_values' of |
| dictionary containing scalar perturbations. |
| Similar for perturbations['vector'] and perturbations['tensor']. |
| """ |
| self.compute(["perturbations"]) |
|
|
| perturbations = {} |
|
|
| if self.pt.k_output_values_num<1: |
| return perturbations |
|
|
| cdef: |
| Py_ssize_t j |
| Py_ssize_t i |
| Py_ssize_t number_of_titles |
| Py_ssize_t timesteps |
| list names |
| list tmparray |
| dict tmpdict |
| double[:,::1] data_mv |
| double ** thedata |
| int * thesizes |
|
|
| |
| |
| for mode in ['scalar','vector','tensor']: |
| if mode=='scalar' and self.pt.has_scalars: |
| thetitles = <bytes> self.pt.scalar_titles |
| thedata = self.pt.scalar_perturbations_data |
| thesizes = self.pt.size_scalar_perturbation_data |
| elif mode=='vector' and self.pt.has_vectors: |
| thetitles = <bytes> self.pt.vector_titles |
| thedata = self.pt.vector_perturbations_data |
| thesizes = self.pt.size_vector_perturbation_data |
| elif mode=='tensor' and self.pt.has_tensors: |
| thetitles = <bytes> self.pt.tensor_titles |
| thedata = self.pt.tensor_perturbations_data |
| thesizes = self.pt.size_tensor_perturbation_data |
| else: |
| continue |
| thetitles = str(thetitles.decode()) |
| names = thetitles.split("\t")[:-1] |
| number_of_titles = len(names) |
| tmparray = [] |
| if number_of_titles != 0: |
| for j in range(self.pt.k_output_values_num): |
| timesteps = thesizes[j]//number_of_titles |
| tmpdict={} |
| data_mv = <double[:timesteps,:number_of_titles]> thedata[j] |
| for i in range(number_of_titles): |
| tmpdict[names[i]] = (np.asarray(data_mv[:,i]).copy() if return_copy else np.asarray(data_mv[:,i])) |
| tmparray.append(tmpdict) |
| perturbations[mode] = tmparray |
|
|
| return perturbations |
|
|
| def get_transfer(self, z=0., output_format='class'): |
| """ |
| Return the density and/or velocity transfer functions for all initial |
| conditions today. You must include 'mTk' and/or 'vCTk' in the list of |
| 'output'. The transfer functions can also be computed at higher redshift z |
| provided that 'z_pk' has been set and that 0<z<z_pk. |
| |
| Parameters |
| ---------- |
| z : redshift (default = 0) |
| output_format : ('class' or 'camb') Format transfer functions according to |
| CLASS convention (default) or CAMB convention. |
| |
| Returns |
| ------- |
| tk : dictionary containing transfer functions. |
| """ |
| self.compute(["transfer"]) |
|
|
| cdef char *titles |
| cdef double* data |
| cdef char ic_info[1024] |
| cdef FileName ic_suffix |
| cdef file_format outf |
|
|
| if (not self.pt.has_density_transfers) and (not self.pt.has_velocity_transfers): |
| return {} |
|
|
| if output_format == 'camb': |
| outf = camb_format |
| else: |
| outf = class_format |
|
|
| index_md = self.pt.index_md_scalars; |
| titles = <char*>calloc(_MAXTITLESTRINGLENGTH_,sizeof(char)) |
|
|
| if perturbations_output_titles(&self.ba,&self.pt, outf, titles)==_FAILURE_: |
| free(titles) |
| raise CosmoSevereError(self.pt.error_message) |
|
|
| tmp = <bytes> titles |
| tmp = str(tmp.decode()) |
| names = tmp.split("\t")[:-1] |
| number_of_titles = len(names) |
| timesteps = self.pt.k_size[index_md] |
|
|
| size_ic_data = timesteps*number_of_titles; |
| ic_num = self.pt.ic_size[index_md]; |
|
|
| data = <double*>malloc(sizeof(double)*size_ic_data*ic_num) |
|
|
| if perturbations_output_data_at_z(&self.ba, &self.pt, outf, <double> z, number_of_titles, data)==_FAILURE_: |
| raise CosmoSevereError(self.pt.error_message) |
|
|
| transfers = {} |
|
|
| for index_ic in range(ic_num): |
| if perturbations_output_firstline_and_ic_suffix(&self.pt, index_ic, ic_info, ic_suffix)==_FAILURE_: |
| free(titles) |
| free(data) |
| raise CosmoSevereError(self.pt.error_message) |
| ic_key = <bytes> ic_suffix |
|
|
| tmpdict = {} |
| for i in range(number_of_titles): |
| tmpdict[names[i]] = np.zeros(timesteps, dtype=np.double) |
| for index in range(timesteps): |
| tmpdict[names[i]][index] = data[index_ic*size_ic_data+index*number_of_titles+i] |
|
|
| if ic_num==1: |
| transfers = tmpdict |
| else: |
| transfers[ic_key] = tmpdict |
|
|
| free(titles) |
| free(data) |
|
|
| return transfers |
|
|
| def get_current_derived_parameters(self, names): |
| """ |
| get_current_derived_parameters(names) |
| |
| Return a dictionary containing an entry for all the names defined in the |
| input list. |
| |
| Parameters |
| ---------- |
| names : list |
| Derived parameters that can be asked from Monte Python, or |
| elsewhere. |
| |
| Returns |
| ------- |
| derived : dict |
| |
| .. warning:: |
| |
| This method used to take as an argument directly the data class from |
| Monte Python. To maintain compatibility with this old feature, a |
| check is performed to verify that names is indeed a list. If not, it |
| returns a TypeError. The old version of this function, when asked |
| with the new argument, will raise an AttributeError. |
| |
| """ |
| if type(names) != type([]): |
| raise TypeError("Deprecated") |
|
|
| self.compute(["thermodynamics"]) |
|
|
| derived = {} |
| for name in names: |
| if name == 'h': |
| value = self.ba.h |
| elif name == 'H0': |
| value = self.ba.h*100 |
| elif name == 'Omega0_lambda' or name == 'Omega_Lambda': |
| value = self.ba.Omega0_lambda |
| elif name == 'Omega0_fld': |
| value = self.ba.Omega0_fld |
| elif name == 'age': |
| value = self.ba.age |
| elif name == 'conformal_age': |
| value = self.ba.conformal_age |
| elif name == 'm_ncdm_in_eV': |
| value = self.ba.m_ncdm_in_eV[0] |
| elif name == 'm_ncdm_tot': |
| value = self.ba.Omega0_ncdm_tot*self.ba.h*self.ba.h*93.14 |
| elif name == 'Neff': |
| value = self.ba.Neff |
| elif name == 'Omega_m': |
| value = self.ba.Omega0_m |
| elif name == 'omega_m': |
| value = self.ba.Omega0_m*self.ba.h**2 |
| elif name == 'xi_idr': |
| value = self.ba.T_idr/self.ba.T_cmb |
| elif name == 'N_dg': |
| value = self.ba.Omega0_idr/self.ba.Omega0_g*8./7.*pow(11./4.,4./3.) |
| elif name == 'Gamma_0_nadm': |
| value = self.th.a_idm_dr*(4./3.)*(self.ba.h*self.ba.h*self.ba.Omega0_idr) |
| elif name == 'a_dark': |
| value = self.th.a_idm_dr |
| elif name == 'tau_reio': |
| value = self.th.tau_reio |
| elif name == 'z_reio': |
| value = self.th.z_reio |
| elif name == 'z_rec': |
| value = self.th.z_rec |
| elif name == 'tau_rec': |
| value = self.th.tau_rec |
| elif name == 'rs_rec': |
| value = self.th.rs_rec |
| elif name == 'rs_rec_h': |
| value = self.th.rs_rec*self.ba.h |
| elif name == 'ds_rec': |
| value = self.th.ds_rec |
| elif name == 'ds_rec_h': |
| value = self.th.ds_rec*self.ba.h |
| elif name == 'ra_rec': |
| value = self.th.da_rec*(1.+self.th.z_rec) |
| elif name == 'ra_rec_h': |
| value = self.th.da_rec*(1.+self.th.z_rec)*self.ba.h |
| elif name == 'da_rec': |
| value = self.th.da_rec |
| elif name == 'da_rec_h': |
| value = self.th.da_rec*self.ba.h |
| elif name == 'z_star': |
| value = self.th.z_star |
| elif name == 'tau_star': |
| value = self.th.tau_star |
| elif name == 'rs_star': |
| value = self.th.rs_star |
| elif name == 'ds_star': |
| value = self.th.ds_star |
| elif name == 'ra_star': |
| value = self.th.ra_star |
| elif name == 'da_star': |
| value = self.th.da_star |
| elif name == 'rd_star': |
| value = self.th.rd_star |
| elif name == 'z_d': |
| value = self.th.z_d |
| elif name == 'tau_d': |
| value = self.th.tau_d |
| elif name == 'ds_d': |
| value = self.th.ds_d |
| elif name == 'ds_d_h': |
| value = self.th.ds_d*self.ba.h |
| elif name == 'rs_d': |
| value = self.th.rs_d |
| elif name == 'rs_d_h': |
| value = self.th.rs_d*self.ba.h |
| elif name == 'conf_time_reio': |
| value = self.th.conf_time_reio |
| elif name == '100*theta_s': |
| value = 100.*self.th.rs_rec/self.th.da_rec/(1.+self.th.z_rec) |
| elif name == '100*theta_star': |
| value = 100.*self.th.rs_star/self.th.da_star/(1.+self.th.z_star) |
| elif name == 'theta_s_100': |
| value = 100.*self.th.rs_rec/self.th.da_rec/(1.+self.th.z_rec) |
| elif name == 'theta_star_100': |
| value = 100.*self.th.rs_star/self.th.da_star/(1.+self.th.z_star) |
| elif name == 'YHe': |
| value = self.th.YHe |
| elif name == 'n_e': |
| value = self.th.n_e |
| elif name == 'A_s': |
| value = self.pm.A_s |
| elif name == 'ln10^{10}A_s': |
| value = log(1.e10*self.pm.A_s) |
| elif name == 'ln_A_s_1e10': |
| value = log(1.e10*self.pm.A_s) |
| elif name == 'n_s': |
| value = self.pm.n_s |
| elif name == 'alpha_s': |
| value = self.pm.alpha_s |
| elif name == 'beta_s': |
| value = self.pm.beta_s |
| elif name == 'r': |
| |
| value = self.pm.r |
| elif name == 'r_0002': |
| |
| value = self.pm.r*(0.002/self.pm.k_pivot)**( |
| self.pm.n_t-self.pm.n_s-1+0.5*self.pm.alpha_s*log( |
| 0.002/self.pm.k_pivot)) |
| elif name == 'n_t': |
| value = self.pm.n_t |
| elif name == 'alpha_t': |
| value = self.pm.alpha_t |
| elif name == 'V_0': |
| value = self.pm.V0 |
| elif name == 'V_1': |
| value = self.pm.V1 |
| elif name == 'V_2': |
| value = self.pm.V2 |
| elif name == 'V_3': |
| value = self.pm.V3 |
| elif name == 'V_4': |
| value = self.pm.V4 |
| elif name == 'epsilon_V': |
| eps1 = self.pm.r*(1./16.-0.7296/16.*(self.pm.r/8.+self.pm.n_s-1.)) |
| eps2 = -self.pm.n_s+1.-0.7296*self.pm.alpha_s-self.pm.r*(1./8.+1./8.*(self.pm.n_s-1.)*(-0.7296-1.5))-(self.pm.r/8.)**2*(-0.7296-1.) |
| value = eps1*((1.-eps1/3.+eps2/6.)/(1.-eps1/3.))**2 |
| elif name == 'eta_V': |
| eps1 = self.pm.r*(1./16.-0.7296/16.*(self.pm.r/8.+self.pm.n_s-1.)) |
| eps2 = -self.pm.n_s+1.-0.7296*self.pm.alpha_s-self.pm.r*(1./8.+1./8.*(self.pm.n_s-1.)*(-0.7296-1.5))-(self.pm.r/8.)**2*(-0.7296-1.) |
| eps23 = 1./8.*(self.pm.r**2/8.+(self.pm.n_s-1.)*self.pm.r-8.*self.pm.alpha_s) |
| value = (2.*eps1-eps2/2.-2./3.*eps1**2+5./6.*eps1*eps2-eps2**2/12.-eps23/6.)/(1.-eps1/3.) |
| elif name == 'ksi_V^2': |
| eps1 = self.pm.r*(1./16.-0.7296/16.*(self.pm.r/8.+self.pm.n_s-1.)) |
| eps2 = -self.pm.n_s+1.-0.7296*self.pm.alpha_s-self.pm.r*(1./8.+1./8.*(self.pm.n_s-1.)*(-0.7296-1.5))-(self.pm.r/8.)**2*(-0.7296-1.) |
| eps23 = 1./8.*(self.pm.r**2/8.+(self.pm.n_s-1.)*self.pm.r-8.*self.pm.alpha_s) |
| value = 2.*(1.-eps1/3.+eps2/6.)*(2.*eps1**2-3./2.*eps1*eps2+eps23/4.)/(1.-eps1/3.)**2 |
| elif name == 'exp_m_2_tau_As': |
| value = exp(-2.*self.th.tau_reio)*self.pm.A_s |
| elif name == 'phi_min': |
| value = self.pm.phi_min |
| elif name == 'phi_max': |
| value = self.pm.phi_max |
| elif name == 'sigma8': |
| self.compute(["fourier"]) |
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get sigma8, you must add mPk to the list of outputs.") |
| value = self.fo.sigma8[self.fo.index_pk_m] |
| elif name == 'sigma8_cb': |
| self.compute(["fourier"]) |
| if (self.pt.has_pk_matter == _FALSE_): |
| raise CosmoSevereError("No power spectrum computed. In order to get sigma8_cb, you must add mPk to the list of outputs.") |
| value = self.fo.sigma8[self.fo.index_pk_cb] |
| elif name == 'k_eq': |
| value = self.ba.a_eq*self.ba.H_eq |
| elif name == 'a_eq': |
| value = self.ba.a_eq |
| elif name == 'z_eq': |
| value = 1./self.ba.a_eq-1. |
| elif name == 'H_eq': |
| value = self.ba.H_eq |
| elif name == 'tau_eq': |
| value = self.ba.tau_eq |
| elif name == 'g_sd': |
| self.compute(["distortions"]) |
| if (self.sd.has_distortions == _FALSE_): |
| raise CosmoSevereError("No spectral distortions computed. In order to get g_sd, you must add sd to the list of outputs.") |
| value = self.sd.sd_parameter_table[0] |
| elif name == 'y_sd': |
| self.compute(["distortions"]) |
| if (self.sd.has_distortions == _FALSE_): |
| raise CosmoSevereError("No spectral distortions computed. In order to get y_sd, you must add sd to the list of outputs.") |
| value = self.sd.sd_parameter_table[1] |
| elif name == 'mu_sd': |
| self.compute(["distortions"]) |
| if (self.sd.has_distortions == _FALSE_): |
| raise CosmoSevereError("No spectral distortions computed. In order to get mu_sd, you must add sd to the list of outputs.") |
| value = self.sd.sd_parameter_table[2] |
| else: |
| raise CosmoSevereError("%s was not recognized as a derived parameter" % name) |
| derived[name] = value |
| return derived |
|
|
| def nonlinear_scale(self, np.ndarray[DTYPE_t,ndim=1] z, int z_size): |
| """ |
| nonlinear_scale(z, z_size) |
| |
| Return the nonlinear scale for all the redshift specified in z, of size |
| z_size |
| |
| Parameters |
| ---------- |
| z : numpy array |
| Array of requested redshifts |
| z_size : int |
| Size of the redshift array |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef int index_z |
| cdef np.ndarray[DTYPE_t, ndim=1] k_nl = np.zeros(z_size,'float64') |
| cdef np.ndarray[DTYPE_t, ndim=1] k_nl_cb = np.zeros(z_size,'float64') |
| |
| |
| for index_z in range(z_size): |
| if fourier_k_nl_at_z(&self.ba,&self.fo,z[index_z],&k_nl[index_z],&k_nl_cb[index_z]) == _FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return k_nl |
|
|
| def nonlinear_scale_cb(self, np.ndarray[DTYPE_t,ndim=1] z, int z_size): |
| """ |
| |
| make nonlinear_scale_cb(z, z_size) |
| |
| Return the nonlinear scale for all the redshift specified in z, of size |
| |
| z_size |
| |
| Parameters |
| ---------- |
| z : numpy array |
| Array of requested redshifts |
| z_size : int |
| Size of the redshift array |
| """ |
| self.compute(["fourier"]) |
|
|
| cdef int index_z |
| cdef np.ndarray[DTYPE_t, ndim=1] k_nl = np.zeros(z_size,'float64') |
| cdef np.ndarray[DTYPE_t, ndim=1] k_nl_cb = np.zeros(z_size,'float64') |
| |
| |
| if (self.ba.Omega0_ncdm_tot == 0.): |
| raise CosmoSevereError( |
| "No massive neutrinos. You must use pk, rather than pk_cb." |
| ) |
| for index_z in range(z_size): |
| if fourier_k_nl_at_z(&self.ba,&self.fo,z[index_z],&k_nl[index_z],&k_nl_cb[index_z]) == _FAILURE_: |
| raise CosmoSevereError(self.fo.error_message) |
|
|
| return k_nl_cb |
|
|
| def __call__(self, ctx): |
| """ |
| Function to interface with CosmoHammer |
| |
| Parameters |
| ---------- |
| ctx : context |
| Contains several dictionaries storing data and cosmological |
| information |
| |
| """ |
| data = ctx.get('data') |
|
|
| |
| if self.state: |
| self.struct_cleanup() |
|
|
| |
| self.set(data.cosmo_arguments) |
| self.compute(["lensing"]) |
|
|
| |
| params = ctx.getData() |
| self.get_current_derived_parameters( |
| data.get_mcmc_parameters(['derived'])) |
| for elem in data.get_mcmc_parameters(['derived']): |
| data.mcmc_parameters[elem]['current'] /= \ |
| data.mcmc_parameters[elem]['scale'] |
| params[elem] = data.mcmc_parameters[elem]['current'] |
|
|
| ctx.add('boundary', True) |
| |
| ctx.add('cosmo', self) |
|
|
| def get_pk_array(self, np.ndarray[DTYPE_t,ndim=1] k, np.ndarray[DTYPE_t,ndim=1] z, int k_size, int z_size, nonlinear): |
| """ Fast function to get the power spectrum on a k and z array """ |
| self.compute(["fourier"]) |
| cdef np.ndarray[DTYPE_t, ndim=1] pk = np.zeros(k_size*z_size,'float64') |
| cdef np.ndarray[DTYPE_t, ndim=1] pk_cb = np.zeros(k_size*z_size,'float64') |
|
|
| if nonlinear == 0: |
| fourier_pks_at_kvec_and_zvec(&self.ba, &self.fo, pk_linear, <double*> k.data, k_size, <double*> z.data, z_size, <double*> pk.data, <double*> pk_cb.data) |
|
|
| else: |
| fourier_pks_at_kvec_and_zvec(&self.ba, &self.fo, pk_nonlinear, <double*> k.data, k_size, <double*> z.data, z_size, <double*> pk.data, <double*> pk_cb.data) |
|
|
| return pk |
|
|
| def get_pk_cb_array(self, np.ndarray[DTYPE_t,ndim=1] k, np.ndarray[DTYPE_t,ndim=1] z, int k_size, int z_size, nonlinear): |
| """ Fast function to get the power spectrum on a k and z array """ |
| self.compute(["fourier"]) |
| cdef np.ndarray[DTYPE_t, ndim=1] pk = np.zeros(k_size*z_size,'float64') |
| cdef np.ndarray[DTYPE_t, ndim=1] pk_cb = np.zeros(k_size*z_size,'float64') |
|
|
| if nonlinear == 0: |
| fourier_pks_at_kvec_and_zvec(&self.ba, &self.fo, pk_linear, <double*> k.data, k_size, <double*> z.data, z_size, <double*> pk.data, <double*> pk_cb.data) |
|
|
| else: |
| fourier_pks_at_kvec_and_zvec(&self.ba, &self.fo, pk_nonlinear, <double*> k.data, k_size, <double*> z.data, z_size, <double*> pk.data, <double*> pk_cb.data) |
|
|
| return pk_cb |
|
|
| def Omega0_k(self): |
| """ Curvature contribution """ |
| return self.ba.Omega0_k |
|
|
| def Omega0_cdm(self): |
| return self.ba.Omega0_cdm |
|
|
| def spectral_distortion_amplitudes(self): |
| self.compute(["distortions"]) |
| if self.sd.type_size == 0: |
| raise CosmoSevereError("No spectral distortions have been calculated. Check that the output contains 'Sd' and the compute level is at least 'distortions'.") |
| cdef np.ndarray[DTYPE_t, ndim=1] sd_type_amps = np.zeros(self.sd.type_size,'float64') |
| for i in range(self.sd.type_size): |
| sd_type_amps[i] = self.sd.sd_parameter_table[i] |
| return sd_type_amps |
|
|
| def spectral_distortion(self): |
| self.compute(["distortions"]) |
| if self.sd.x_size == 0: |
| raise CosmoSevereError("No spectral distortions have been calculated. Check that the output contains 'Sd' and the compute level is at least 'distortions'.") |
| cdef np.ndarray[DTYPE_t, ndim=1] sd_amp = np.zeros(self.sd.x_size,'float64') |
| cdef np.ndarray[DTYPE_t, ndim=1] sd_nu = np.zeros(self.sd.x_size,'float64') |
| for i in range(self.sd.x_size): |
| sd_amp[i] = self.sd.DI[i]*self.sd.DI_units*1.e26 |
| sd_nu[i] = self.sd.x[i]*self.sd.x_to_nu |
| return sd_nu,sd_amp |
|
|
|
|
| def get_sources(self): |
| """ |
| Return the source functions for all k, tau in the grid. |
| |
| Returns |
| ------- |
| sources : dictionary containing source functions. |
| k_array : numpy array containing k values. |
| tau_array: numpy array containing tau values. |
| """ |
| self.compute(["fourier"]) |
| sources = {} |
|
|
| cdef: |
| int index_k, index_tau, i_index_type; |
| int index_type; |
| int index_md = self.pt.index_md_scalars; |
| double * k = self.pt.k[index_md]; |
| double * tau = self.pt.tau_sampling; |
| int index_ic = self.pt.index_ic_ad; |
| int k_size = self.pt.k_size[index_md]; |
| int tau_size = self.pt.tau_size; |
| int tp_size = self.pt.tp_size[index_md]; |
| double *** sources_ptr = self.pt.sources; |
| double [:,:] tmparray = np.zeros((k_size, tau_size)) ; |
| double [:] k_array = np.zeros(k_size); |
| double [:] tau_array = np.zeros(tau_size); |
|
|
| names = [] |
|
|
| for index_k in range(k_size): |
| k_array[index_k] = k[index_k] |
| for index_tau in range(tau_size): |
| tau_array[index_tau] = tau[index_tau] |
|
|
| indices = [] |
|
|
| if self.pt.has_source_t: |
| indices.extend([ |
| self.pt.index_tp_t0, |
| self.pt.index_tp_t1, |
| self.pt.index_tp_t2 |
| ]) |
| names.extend([ |
| "t0", |
| "t1", |
| "t2" |
| ]) |
| if self.pt.has_source_p: |
| indices.append(self.pt.index_tp_p) |
| names.append("p") |
| if self.pt.has_source_phi: |
| indices.append(self.pt.index_tp_phi) |
| names.append("phi") |
| if self.pt.has_source_phi_plus_psi: |
| indices.append(self.pt.index_tp_phi_plus_psi) |
| names.append("phi_plus_psi") |
| if self.pt.has_source_phi_prime: |
| indices.append(self.pt.index_tp_phi_prime) |
| names.append("phi_prime") |
| if self.pt.has_source_psi: |
| indices.append(self.pt.index_tp_psi) |
| names.append("psi") |
| if self.pt.has_source_H_T_Nb_prime: |
| indices.append(self.pt.index_tp_H_T_Nb_prime) |
| names.append("H_T_Nb_prime") |
| if self.pt.index_tp_k2gamma_Nb: |
| indices.append(self.pt.index_tp_k2gamma_Nb) |
| names.append("k2gamma_Nb") |
| if self.pt.has_source_h: |
| indices.append(self.pt.index_tp_h) |
| names.append("h") |
| if self.pt.has_source_h_prime: |
| indices.append(self.pt.index_tp_h_prime) |
| names.append("h_prime") |
| if self.pt.has_source_eta: |
| indices.append(self.pt.index_tp_eta) |
| names.append("eta") |
| if self.pt.has_source_eta_prime: |
| indices.append(self.pt.index_tp_eta_prime) |
| names.append("eta_prime") |
| if self.pt.has_source_delta_tot: |
| indices.append(self.pt.index_tp_delta_tot) |
| names.append("delta_tot") |
| if self.pt.has_source_delta_m: |
| indices.append(self.pt.index_tp_delta_m) |
| names.append("delta_m") |
| if self.pt.has_source_delta_cb: |
| indices.append(self.pt.index_tp_delta_cb) |
| names.append("delta_cb") |
| if self.pt.has_source_delta_g: |
| indices.append(self.pt.index_tp_delta_g) |
| names.append("delta_g") |
| if self.pt.has_source_delta_b: |
| indices.append(self.pt.index_tp_delta_b) |
| names.append("delta_b") |
| if self.pt.has_source_delta_cdm: |
| indices.append(self.pt.index_tp_delta_cdm) |
| names.append("delta_cdm") |
| if self.pt.has_source_delta_idm: |
| indices.append(self.pt.index_tp_delta_idm) |
| names.append("delta_idm") |
| if self.pt.has_source_delta_dcdm: |
| indices.append(self.pt.index_tp_delta_dcdm) |
| names.append("delta_dcdm") |
| if self.pt.has_source_delta_fld: |
| indices.append(self.pt.index_tp_delta_fld) |
| names.append("delta_fld") |
| if self.pt.has_source_delta_scf: |
| indices.append(self.pt.index_tp_delta_scf) |
| names.append("delta_scf") |
| if self.pt.has_source_delta_dr: |
| indices.append(self.pt.index_tp_delta_dr) |
| names.append("delta_dr") |
| if self.pt.has_source_delta_ur: |
| indices.append(self.pt.index_tp_delta_ur) |
| names.append("delta_ur") |
| if self.pt.has_source_delta_idr: |
| indices.append(self.pt.index_tp_delta_idr) |
| names.append("delta_idr") |
| if self.pt.has_source_delta_ncdm: |
| for incdm in range(self.ba.N_ncdm): |
| indices.append(self.pt.index_tp_delta_ncdm1+incdm) |
| names.append("delta_ncdm[{}]".format(incdm)) |
| if self.pt.has_source_theta_tot: |
| indices.append(self.pt.index_tp_theta_tot) |
| names.append("theta_tot") |
| if self.pt.has_source_theta_m: |
| indices.append(self.pt.index_tp_theta_m) |
| names.append("theta_m") |
| if self.pt.has_source_theta_cb: |
| indices.append(self.pt.index_tp_theta_cb) |
| names.append("theta_cb") |
| if self.pt.has_source_theta_g: |
| indices.append(self.pt.index_tp_theta_g) |
| names.append("theta_g") |
| if self.pt.has_source_theta_b: |
| indices.append(self.pt.index_tp_theta_b) |
| names.append("theta_b") |
| if self.pt.has_source_theta_cdm: |
| indices.append(self.pt.index_tp_theta_cdm) |
| names.append("theta_cdm") |
| if self.pt.has_source_theta_idm: |
| indices.append(self.pt.index_tp_theta_idm) |
| names.append("theta_idm") |
| if self.pt.has_source_theta_dcdm: |
| indices.append(self.pt.index_tp_theta_dcdm) |
| names.append("theta_dcdm") |
| if self.pt.has_source_theta_fld: |
| indices.append(self.pt.index_tp_theta_fld) |
| names.append("theta_fld") |
| if self.pt.has_source_theta_scf: |
| indices.append(self.pt.index_tp_theta_scf) |
| names.append("theta_scf") |
| if self.pt.has_source_theta_dr: |
| indices.append(self.pt.index_tp_theta_dr) |
| names.append("theta_dr") |
| if self.pt.has_source_theta_ur: |
| indices.append(self.pt.index_tp_theta_ur) |
| names.append("theta_ur") |
| if self.pt.has_source_theta_idr: |
| indices.append(self.pt.index_tp_theta_idr) |
| names.append("theta_idr") |
| if self.pt.has_source_theta_ncdm: |
| for incdm in range(self.ba.N_ncdm): |
| indices.append(self.pt.index_tp_theta_ncdm1+incdm) |
| names.append("theta_ncdm[{}]".format(incdm)) |
|
|
| for index_type, name in zip(indices, names): |
| tmparray = np.empty((k_size,tau_size)) |
| for index_k in range(k_size): |
| for index_tau in range(tau_size): |
| tmparray[index_k][index_tau] = sources_ptr[index_md][index_ic*tp_size+index_type][index_tau*k_size + index_k]; |
|
|
| sources[name] = np.asarray(tmparray) |
|
|
| return (sources, np.asarray(k_array), np.asarray(tau_array)) |
|
|