# Copyright (c) 2014-2015, CosmicPy Developers
# Licensed under CeCILL 2.1 - see LICENSE.rst
"""
:mod:`cosmicpy.spectra` -- Clustering and lensing power spectra
==============================================================
.. module:: cosmicpy.spectra
:synopsis: Computes galaxy spectra
.. moduleauthor:: Francois Lanusse <francois.lanusse@cea.fr>
.. moduleauthor:: Anais Rassat <anais.rassat@epfl.ch>
.. Created on Jun 14, 2013 by Francois Lanusse
.. autosummary::
spectra
"""
from scipy.integrate import *
from scipy.interpolate import interp1d
from numpy import *
from .tools import BesselWindow
from .utils import *
from . import constants as const
from scipy.special import jn_zeros
[docs]class spectra(object):
""" Computes and stores all the window functions """
def __init__(self, cosmo, surv, lmax=1000, **kwargs):
self.cosmo = cosmo
self.surv = surv
self._n_a = 256 + 1
self._lmax = lmax
# Number of points in the dsbt
self._chimax = self.surv.chimax(cosmo)
self._kmax_bessel = 0.5
bess_zeros = jn_zeros(2, 10000)
self._nmax = where(bess_zeros > self._kmax_bessel * self._chimax)[0][0]
self.besselWindow = BesselWindow(self._nmax,
self._lmax,
self._kmax_bessel,
"qlnTable.dat")
self.update(**kwargs)
def update(self, **kwargs):
if len(kwargs) > 0:
self.cosmo.update(**kwargs)
self.surv.update(**kwargs)
# initializes the tabulation arrays
(self._a, self._da) = linspace(z2a(self.surv.zmax),
z2a(self.surv.zmin),
self._n_a,
retstep=True,
endpoint=False)
self._z = a2z(self._a)
self._chi = self.cosmo.a2chi(self._a)
[docs] def g(self, i, l, z):
""" galaxy clustering window function """
# Checks that the bin index is correct
if (i < 0 or i >= self.surv.nzbins):
print ("Error, bin number " + i + " is out of range.")
return 0.0
z = atleast_1d(z)
l = atleast_1d(l)
b = zeros((len(l), len(z)))
r = self.cosmo.a2chi(z2a(z))
for j in range(len(z)):
k = (l + 0.5) / r[j]
b[:, j] = self.surv.zbins[i].nz(z[j])*self.surv.bias(z[j], k)
return b
[docs] def cl_gg(self, i, j, l, shotNoise=False, linear=True, **kwargs):
""" galaxy-galaxy angular power spectrum in the Limber approximation"""
l = atleast_1d(l)
(a, da) = linspace(z2a(max([self.surv.zbins[i].zmax,
self.surv.zbins[j].zmax])),
z2a(min([self.surv.zbins[i].zmin,
self.surv.zbins[j].zmin])),
self._n_a, retstep=True, endpoint=False)
clintegrand = 1.0 / (self.cosmo.dchioverda(a) *
(self.cosmo.f_k(a)**2) * a**4)
if linear:
pl = self.cosmo.pl_lin(l, a, **kwargs)
else:
pl = self.cosmo.pl(l, a, **kwargs)
res = romb(clintegrand * (self.g(i, l, a2z(a)) * self.g(j, l, a2z(a)) *
pl), da)
if shotNoise and i == j:
res += 1.0 / self.surv.zbins[i].ngal
return res
[docs] def W(self, l, k1=None, k2=None, evol=True, fid_cosmo=None, kmax=0.25):
r"""
Computes the Spherical Fourier-Bessel window function for the survey
Parameters
----------
l : int
Order of the Bessel functions.
k1 : array_like, optional
Value of the scale at which to evaluate the window function
in h/Mpc. If None is provided, the window function is evaluated
at discrete points defined by the zeroes of the Bessel
functions up to the kmax parameter (def : None).
k2 : array_like, optional
Value of the scale at which to evaluate the window function
in h/Mpc. If None is provided, a symmetric Bessel window is
computed :math:`W_l(k1, k1)` (def : None).
evol : boolean, optional
Flag to include the time dependent linear bias and growth in
the computation of the window function (def : True).
fid_cosmo : Cosmology, optional
Fiducial cosmology to use for the computation of redshift to
comoving distance in the case of a real survey. If None is
provided, the true cosmology is used (def : None).
kmax : float, optional
Maximum scale at which to compute the window if k1 is not
provided. This is usefull to avoid computing the
window at non linear scales which would be cut afterwards
(def : 0.25).
Notes
-----
:math:: W_l (k1,k2) = \int k1 \phi(r)
j_l(k1 r) j_l(k2 r) r^2 dr
"""
# Computing the selection function for the requested modes l
chi_l = self.besselWindow.rgrid(l)
phi = self.surv.phi(self.cosmo, chi_l)
# Include growth and bias if requested
if evol:
# Include growth
a = self.cosmo.chi2a(chi_l)
g = self.cosmo.G(a)
phi *= g
# Include scale dependent bias, either using the provided k or
# using the default k_ln grid
if k2 is None:
k = self.besselWindow.kgrid(l, self._chimax)
b = self.surv.bias(a2z(a), k)
else:
b = self.surv.bias(a2z(a), k2)
phi = einsum('i,ij->ij', phi, b)
else:
if k2 is None:
k = self.besselWindow.kgrid(l, self._chimax)
else:
k = k2
phi = einsum('i,ij->ij', phi, ones((len(phi), len(k))))
# Computing the comoving distance for a redshift space survey
# according to a fiducial cosmology
if fid_cosmo is None:
s = chi_l
else:
s = fid_cosmo.a2chi(self.cosmo.chi2a(chi_l))
# First case, if k1 is not specified, the window is computed for W_l(k_{l n}, k2)
# with an adapted step in k2 to correctly sample the main lobe of the
# window
if k1 is None:
return self.besselWindow.tabulated(l, self._chimax,
kmax, s, phi)
k1 = atleast_1d(k1)
# Second case, k1 is provided, not k2. In this case,
# an optimal window is computed
if k2 is None:
return self.besselWindow.optimal(l, self._chimax, k1, s, phi)
k2 = atleast_1d(k2)
# 3rd case, k1 and k2 are provided
return self.besselWindow.custom(l, k1, k2, s, phi)
def cl_sfb(self, l, k=None, shotNoise=False, onlyNoise=False,
evol=True, fid_cosmo=None, kmax=0.25, **kwargs):
l = atleast_1d(l)
kmax = atleast_1d(kmax)
if kmax.size != l.size:
kmax = ones_like(l) * kmax[0]
if k is None:
# Computing linear scale cut
res = []
for i in range(len(l)):
k1, k2, w = self.W(l[i], evol=evol, kmax=kmax[i],
fid_cosmo=fid_cosmo)
pk = self.cosmo.pk_lin(k2, **kwargs)
mat = zeros((len(k1), len(k1)))
integrand = w*k2**2*pk
for j in range(len(k1)):
integ = (2.0 / pi)**2 * simps(integrand[j, :] * w[0:j+1, :], x=k2)
mat[j, 0:j + 1] = integ
mat[0:j + 1, j] = integ
if shotNoise:
mat += sqrt(2) / pi * k1*self.W(l[i], k1, k1, evol=False) / self.surv.N
res.append([k1, mat])
if len(res) == 1:
return res[0]
else:
return res
else:
k = atleast_1d(k)
res = zeros((l.size, k.size))
noise = zeros((l.size, k.size))
if not onlyNoise:
for i in range(len(l)):
kw, w = self.W(l[i], k, evol=evol)
pk = self.cosmo.pk_lin(kw[:, :], **kwargs)
integrand = square(kw * w) * pk
res[i] = (2.0 / pi)**2 * trapz(abs(integrand), x=kw)
if shotNoise or onlyNoise:
for i in range(l.size):
w = k * diag(self.W(l[i], k, k, evol=False)) / self.surv.N
noise[i, :] = sqrt(2) / pi * w
if onlyNoise:
return noise.squeeze()
elif shotNoise:
return (res + noise).squeeze()
else:
return res.squeeze()