1111
1212# Parameters
1313xmin_Tk = 1e-5 # Scale at which to switch to Taylor expansion approximation in tophat Fourier functions
14- eps_sigmaV = 1e-3 # Accuracy of the sigmaV integration NOTE: Seems to fail with higher accuracy
14+ eps_sigmaR = 1e-4 # Accuracy of the sigmaR integration
15+ eps_sigmaV = 1e-4 # Accuracy of the sigmaV integration NOTE: Seems to fail with higher accuracy
16+ eps_neff = 1e-4 # Accuracy of the neff integration (d ln sigma^2/d ln k)
1517
1618### Backgroud ###
1719
18- def redshift_from_scalefactor (a ) :
20+ def redshift_from_scalefactor (a : float ) -> float :
1921 return - 1. + 1. / a
2022
2123
22- def scalefactor_from_redshift (z ) :
24+ def scalefactor_from_redshift (z : float ) -> float :
2325 return 1. / (1. + z )
2426
2527
@@ -54,6 +56,27 @@ def Tk_EH_nowiggle(k:np.ndarray, h:float, wm:float, wb:float, T_CMB=2.725) -> np
5456 return Tk_nw
5557
5658
59+ def Tk_cold_ratio (k :np .ndarray , g :float , wm :float , h :float , f_nu :float , N_nu :int , T_CMB = 2.725 ) -> np .ndarray :
60+ '''
61+ Ratio of cold to matter transfer function from Eistenstein & Hu (1999)
62+ This can be used to get the cold-matter spectrum approximately from the matter spectrum
63+ Captures the scale-dependent growth with neutrino free-streaming scale
64+ '''
65+ if f_nu == 0. : # Fix to unity if there are no neutrinos
66+ Tk_cold_ratio = 1.
67+ else :
68+ pcb = (5. - np .sqrt (1. + 24. * (1. - f_nu )))/ 4. # Growth exponent for unclustered neutrinos completely
69+ BigT = T_CMB / 2.7 # Big Theta for temperature
70+ zeq = 2.5e4 * wm * BigT ** (- 4 ) # Matter-radiation equality redshift
71+ D = (1. + zeq )* g # Growth normalised such that D=(1.+z_eq)/(1+z) at early times
72+ q = k * h * BigT ** 2 / wm # Wave number relative to the horizon scale at equality (equation 5)
73+ yfs = 17.2 * f_nu * (1. + 0.488 * f_nu ** (- 7. / 6. ))* (N_nu * q / f_nu )** 2 # Free streaming scale (equation 14)
74+ Dcb = (1. + (D / (1. + yfs ))** 0.7 )** (pcb / 0.7 ) # Cold growth functon
75+ Dcbnu = ((1. - f_nu )** (0.7 / pcb )+ (D / (1. + yfs ))** 0.7 )** (pcb / 0.7 ) # Cold and neutrino growth function
76+ Tk_cold_ratio = Dcb / Dcbnu # Finally, the ratio
77+ return Tk_cold_ratio
78+
79+
5780def _Tophat_k (x :np .ndarray ) -> np .ndarray :
5881 '''
5982 Fourier transform of a tophat function.
@@ -74,56 +97,71 @@ def _dTophat_k(x:np.ndarray) -> np.ndarray:
7497 return np .where (np .abs (x )< xmin , - x / 5. + x ** 3 / 70. , (3. / x ** 4 )* ((x ** 2 - 3. )* np .sin (x )+ 3. * x * np .cos (x )))
7598
7699
77- def _sigmaR_integrand (k :np .array , R :float , Pk :callable ) -> np .ndarray :
100+ def _transformed_integrand (t :np .ndarray , R :float , Pk :callable , integrand :callable , alpha = 3. ) -> np .ndarray :
101+ '''
102+ Transform an integral over k from 0 to infinity to one over t from 0 to 1
103+ NOTE: This does not seem to help much when combined with scipy.integrate.quad
104+ '''
105+ kR = (- 1. + 1. / t )** alpha
106+ k = kR if R == 0. else kR / R
107+ return integrand (k , R , Pk )* k * alpha / (t * (1. - t ))
108+
109+
110+ def _sigmaR_integrand (k :np .ndarray , R :float , Pk :callable ) -> np .ndarray :
78111 '''
79112 Integrand for calculating sigma(R)
80- Note that k can be a float or an arraay here
81- args:
82- k: Fourier wavenumber (or array of these) [h/Mpc]
83- R: Comoving Lagrangian radius [Mpc/h]
84- Pk: Function of k to evaluate the linear power spectrum
113+ Note that k can be a float or an array here
85114 '''
86115 return Pk (k )* (k ** 2 )* _Tophat_k (k * R )** 2
87116
88117
89- def _sigmaR_quad (R :float , Pk :callable ) -> float :
118+ def sigmaR (R :np . ndarray , Pk :callable , eps = eps_sigmaR , transform_integrand = False ) -> np . ndarray :
90119 '''
91- Quad integration
120+ Quad integration TODO: This is slow, there must be something better to do
92121 args:
93122 R: Comoving Lagrangian radius [Mpc/h]
94123 Pk: Function of k to evaluate the linear power spectrum
95124 '''
96125 def sigmaR_vec (R :float , Pk :callable ):
97- kmin , kmax = 0. , np .inf
98- sigma_squared , _ = integrate .quad (lambda k : _sigmaR_integrand (k , R , Pk ), kmin , kmax )
99- sigma = np .sqrt (sigma_squared / (2. * np .pi ** 2 ))
100- return sigma
101- sigma_func = np .vectorize (sigmaR_vec , excluded = ['Pk' ])
102- return sigma_func (R , Pk )
103-
104-
105- def _sigmaV_integrand (k :float , Pk :callable ):
106- return Pk (k )
107- # def _sigmaV_integrand(t, Pk, alpha=3.):
108- # k = (-1.+1./t)**alpha
109- # return Pk(k)*k*alpha/(t*(1.-t))
110-
111-
112- def sigmaV (Pk :callable , eps = eps_sigmaV ) -> float :
126+ if transform_integrand :
127+ sigmaR_squared , _ = integrate .quad (lambda t : _transformed_integrand (t , R , Pk , _sigmaR_integrand ),
128+ 0. , 1. , epsabs = eps , epsrel = eps )
129+ else :
130+ kmin , kmax = 0. , np .inf
131+ sigmaR_squared , _ = integrate .quad (lambda k : _sigmaR_integrand (k , R , Pk ),
132+ kmin , kmax , epsabs = eps , epsrel = eps )
133+ sigmaR = np .sqrt (sigmaR_squared / (2. * np .pi ** 2 ))
134+ return sigmaR
135+ sigmaR_func = np .vectorize (sigmaR_vec , excluded = ['Pk' ])
136+ return sigmaR_func (R , Pk )
137+
138+
139+ def _sigmaV_integrand (k :np .ndarray , R :float , Pk :callable ) -> np .ndarray :
140+ if R == 0. :
141+ integrand = Pk (k )
142+ else :
143+ integrand = Pk (k )* _Tophat_k (k * R )** 2
144+ return integrand
145+
146+
147+ def sigmaV (R :float , Pk :callable , eps = eps_sigmaV , transform_integrand = False ) -> float :
113148 '''
114- Quad integration; R=0
149+ Integration to get the 1D RMS in the linear displacement field
115150 TODO: This generates a warning sometimes, there must be a cleverer way to integrate here.
116151 Unless eps_sigmaV > 1e-3 the integration fails for z=0 sometimes, but not after being called for
117- z > 0. I really don't understaand this, but it's annoying and should be fixed.
152+ z > 0. I really don't understand this, but it's annoying and should be fixed.
118153 I should look at how CAMB deals with these type of integrals (e.g., sigmaR).
119154 args:
120155 Pk: Function of k to evaluate the linear power spectrum
121156 eps: Integration accuracy
122157 '''
123- kmin , kmax = 0. , np .inf
124- #sigmaV_squared, _ = integrate.quad(Pk, kmin, kmax, epsrel=eps, epsabs=eps)
125- sigmaV_squared , _ = integrate .quad (lambda k : _sigmaV_integrand (k , Pk ), kmin , kmax , epsrel = eps , epsabs = eps )
126- #sigmaV_squared, _ = integrate.quad(_sigmaV_integrand, 0., 1., args=(Pk,), epsabs=eps, epsrel=eps)
158+ if transform_integrand :
159+ sigmaV_squared , _ = integrate .quad (lambda t : _transformed_integrand (t , R , Pk , _sigmaV_integrand ),
160+ 0. , 1. , epsabs = eps , epsrel = eps )
161+ else :
162+ kmin , kmax = 0. , np .inf
163+ sigmaV_squared , _ = integrate .quad (lambda k : _sigmaV_integrand (k , R , Pk ),
164+ kmin , kmax , epsabs = eps , epsrel = eps )
127165 sigmaV = np .sqrt (sigmaV_squared / (2. * np .pi ** 2 ))
128166 sigmaV /= np .sqrt (3. ) # Convert from 3D displacement to 1D displacement
129167 return sigmaV
@@ -133,29 +171,28 @@ def _dsigmaR_integrand(k:float, R:float, Pk:callable) -> float:
133171 return Pk (k )* (k ** 3 )* _Tophat_k (k * R )* _dTophat_k (k * R )
134172
135173
136- def dlnsigma2_dlnR (R :float , Pk :callable ) -> float :
174+ def _dlnsigma2_dlnR (R :float , Pk :callable , eps = eps_neff , transform_integrand = False ) -> float :
137175 '''
138176 Calculates d(ln sigma^2)/d(ln R) by integration
139177 3+neff = -d(ln sigma^2) / dR
178+ TODO: Vectorize
140179 '''
141- # def dsigmaR_vec(R, Pk):
142- # kmin, kmax = 0., np.inf # Evaluate the integral and convert to a nicer form
143- # dsigma, _ = integrate.quad(lambda k: _dsigmaR_integrand(k, R, Pk), kmin, kmax)
144- # dsigma = R*dsigma/(np.pi*_sigmaR_quad(R, Pk))**2
145- # return dsigma
146- # dsigma_func = np.vectorize(dsigmaR_vec, excluded=['Pk'])
147- # return dsigma_func(R, Pk)
148- kmin , kmax = 0. , np .inf # Evaluate the integral and convert to a nicer form
149- dsigma , _ = integrate .quad (lambda k : _dsigmaR_integrand (k , R , Pk ), kmin , kmax )
150- dsigma = R * dsigma / (np .pi * _sigmaR_quad (R , Pk ))** 2
180+ if transform_integrand :
181+ dsigma , _ = integrate .quad (lambda t : _transformed_integrand (t , R , Pk , _dsigmaR_integrand ),
182+ 0. , 1. , epsabs = eps , epsrel = eps )
183+ else :
184+ kmin , kmax = 0. , np .inf # Evaluate the integral and convert to a nicer form
185+ dsigma , _ = integrate .quad (lambda k : _dsigmaR_integrand (k , R , Pk ),
186+ kmin , kmax , epsabs = eps , epsrel = eps )
187+ dsigma = R * dsigma / (np .pi * sigmaR (R , Pk ))** 2
151188 return dsigma
152189
153190
154191def neff (R :float , Pk :callable ) -> float :
155192 '''
156193 Effective index of the power spectrum at scale 'R'
157194 '''
158- return - 3. - dlnsigma2_dlnR (R , Pk )
195+ return - 3. - _dlnsigma2_dlnR (R , Pk )
159196
160197### ###
161198
0 commit comments