11# Standard imports
2- import numpy as np
32from time import time
43
54# Third-party imports
5+ import numpy as np
66import camb
77import pyhalomodel as halo
88
1515Pk_lin_extrap_kmax = 1e10 # NOTE: This interplays with the sigmaV integration in a disconcerting way
1616sigma_cold_approx = False # Should the Eisenstein & Hu (1999) approximation be used for the cold transfer function?
1717
18- def power (k :np .array , zs :np .array , CAMB_results :camb .CAMBdata ,
19- Mmin = 1e0 , Mmax = 1e18 , nM = 256 , verbose = False ,
20- baryons = False , baryon_params = None , hmcodetweaks = True ) -> np .ndarray :
18+ def power (k :np .array , zs :np .array , CAMB_results :camb .CAMBdata , T_AGN = None ,
19+ Mmin = 1e0 , Mmax = 1e18 , nM = 256 , tweaks = True , verbose = False ) -> np .ndarray :
2120 '''
2221 Calculates the HMcode matter-matter power spectrum
2322 Args:
2423 k: Array of comoving wavenumbers [h/Mpc]
2524 zs: Array of redshifts (ordered from high to low)
2625 CAMB_results: CAMBdata structure
26+ T_AGN: AGN feedback temperature [K] (None to disable)
2727 Mmin: Minimum mass for the halo-model calculation [Msun/h]
2828 Mmax: Maximum mass for the halo-model calculation [Msun/h]
2929 nM: Number of mass bins for the halo-model calculation
30- baryons: if true: include baryonic effects from HMCode2020; if false: Dark Matter Only
31- baryon_params: Dictionary containing the 6 baryonic parameters from HMCode2020
32- hmcodetweaks: if true: Use the changes to the vanilla halo model from HMCode2020; if false: Use vanilla halo model
30+ tweaks: Use the changes to the vanilla halo model from HMCode2020 if true
3331 Returns:
3432 Array of matter power spectra: Pk[z, k]
3533 '''
3634
3735 # Checks
3836 if verbose : t_start = time ()
3937 if not util .is_array_monotonic (- np .array (zs )):
40- raise ValueError ('Redshift must be monotonically decreasing' )
38+ raise ValueError ('Redshifts must be monotonically decreasing' )
4139
4240 # Halo mass range
4341 M = util .logspace (Mmin , Mmax , nM )
@@ -64,15 +62,19 @@ def power(k:np.array, zs:np.array, CAMB_results:camb.CAMBdata,
6462 print ('Linear growth at z=0: {:.3}' .format (growth (1. )))
6563 print ()
6664
67- if verbose and baryons :
68- print ("Using baryonic feedback model from HMCode2020" )
69- print ("B0: {:.4f}" .format (baryon_params ["B0" ]))
70- print ("Bz: {:.4f}" .format (baryon_params ["Bz" ]))
71- print ("Mb0: {:.1e}" .format (baryon_params ["Mb0" ]))
72- print ("Mbz: {:.4f}" .format (baryon_params ["Mbz" ]))
73- print ("f*0: {:.4f}" .format (baryon_params ["f0" ]))
74- print ("f*z: {:.4f}" .format (baryon_params ["fz" ]))
75- print ()
65+ # Baryonic feedback
66+ if T_AGN :
67+ feedback_params = _get_feedback_parameters (T_AGN )
68+ if verbose :
69+ print ('Using baryonic feedback model from HMCode2020' )
70+ print ('log_10(T_AGN/K): {:.2f}' .format (np .log10 (T_AGN )))
71+ print ('B_0: {:.4f}' .format (feedback_params ['B0' ]))
72+ print ('B_z: {:.4f}' .format (feedback_params ['Bz' ]))
73+ print ('Mb_0: {:.1e}' .format (feedback_params ['Mb0' ]))
74+ print ('Mb_z: {:.4f}' .format (feedback_params ['Mbz' ]))
75+ print ('f*_0: {:.4f}' .format (feedback_params ['f0' ]))
76+ print ('f*_z: {:.4f}' .format (feedback_params ['fz' ]))
77+ print ()
7678
7779 # Linear power interpolator
7880 interp , _ , k_interp = CAMB_results .get_matter_power_interpolator (nonlinear = False ,
@@ -144,46 +146,36 @@ def power(k:np.array, zs:np.array, CAMB_results:camb.CAMBdata,
144146 print ('RMS in matter displacement field: {:.4} Mpc/h' .format (sigmaV ))
145147 print ('Effective index at collapse scale: {:.4}' .format (neff ))
146148
147- # HMcode parameters (Table 2 of https://arxiv.org/pdf/2009.01858.pdf)
148- kd = 0.05699 * sigma8 ** - 1.089 # Two-halo damping wavenumber; equation (16)
149- f = 0.2696 * sigma8 ** 0.9403 # Two-halo fractional damping; equation (16)
150- nd = 2.853 # Two-halo damping power; equation (16)
151- ks = 0.05618 * sigma8 ** - 1.013 # One-halo damping wavenumber; equation (17)
152- eta = 0.1281 * sigma8 ** - 0.3644 # Halo bloating parameter; equation (19)
153- B = 5.196 # Minimum halo concentration; equation (20)
154- alpha = 1.875 * (1.603 )** neff # Transition smoothing; equation (23)
155-
156-
157- if hmcodetweaks == False : #Go to vanilla halo model if no hmcode tweaks should be used
158- B = 4
159- eta = 0
160- alpha = 1
161-
162- if verbose and hmcodetweaks :
163- print ('Two-halo damping wavenumber; kd: {:.4} h/Mpc' .format (kd ))
164- print ('Two-halo fractional damping; f: {:.4}' .format (f ))
165- print ('Two-halo damping power; nd: {:.4}' .format (nd ))
166- print ('One-halo damping wavenumber; k*: {:.4} h/Mpc' .format (ks ))
167- print ('Halo bloating; eta: {:.4}' .format (eta ))
168- print ('Minimum halo concentration; B: {:.4}' .format (B ))#, B/4.)
169- print ('Transition smoothing; alpha: {:.4}' .format (alpha ))
170- print ()
171- elif verbose :
172- print ("Using vanilla halo model" )
173-
174- if baryons :
175- B = baryon_params ["B0" ]* np .power (10 ,z * baryon_params ["Bz" ])
176- Mb = baryon_params ["Mb0" ]* np .power (10 , z * baryon_params ["Mbz" ])
177- fstar = baryon_params ["f0" ]* np .power (10 , z * baryon_params ["fz" ])
178-
179-
180-
181- if verbose and baryons :
182- print ("Halo concentration parameter B:{:.4f}" .format (B ))
183- print ("Gas loss halo mass parameter Mb:{:1e}" .format (Mb ))
184- print ("Effective halo stellar mass fraction fstar:{:.4f}" .format (f ))
185-
186-
149+ if tweaks : # HMcode parameters (Table 2 of https://arxiv.org/pdf/2009.01858.pdf)
150+ kd = 0.05699 * sigma8 ** - 1.089 # Two-halo damping wavenumber; equation (16)
151+ f = 0.2696 * sigma8 ** 0.9403 # Two-halo fractional damping; equation (16)
152+ nd = 2.853 # Two-halo damping power; equation (16)
153+ ks = 0.05618 * sigma8 ** - 1.013 # One-halo damping wavenumber; equation (17)
154+ eta = 0.1281 * sigma8 ** - 0.3644 # Halo bloating parameter; equation (19)
155+ B = 5.196 # Minimum halo concentration; equation (20)
156+ alpha = 1.875 * (1.603 )** neff # Transition smoothing; equation (23)
157+ else : # Use vanilla-ish halo model if no HMcode tweaks used
158+ B , eta = 4. , 0.
159+
160+ if T_AGN :
161+ B = feedback_params ['B0' ]* np .power (10 , z * feedback_params ['Bz' ])
162+ Mb = feedback_params ['Mb0' ]* np .power (10 , z * feedback_params ['Mbz' ])
163+ fstar = feedback_params ['f0' ]* np .power (10 , z * feedback_params ['fz' ])
164+
165+ if verbose :
166+ if tweaks :
167+ print ('Two-halo damping wavenumber; kd: {:.4} h/Mpc' .format (kd ))
168+ print ('Two-halo fractional damping; f: {:.4}' .format (f ))
169+ print ('Two-halo damping power; nd: {:.4}' .format (nd ))
170+ print ('One-halo damping wavenumber; k*: {:.4} h/Mpc' .format (ks ))
171+ print ('Halo bloating; eta: {:.4}' .format (eta ))
172+ print ('Minimum halo concentration; B: {:.4}' .format (B ))#, B/4.)
173+ print ('Transition smoothing; alpha: {:.4}' .format (alpha ))
174+ if T_AGN :
175+ print ('Gas-loss halo-mass parameter Mb: {:1e}' .format (Mb ))
176+ print ('Effective halo stellar-mass fraction f*: {:.4f}' .format (f ))
177+ print ()
178+
187179 # Halo concentration
188180 zf = _get_halo_collapse_redshifts (M , z , iz , dc , growth , CAMB_results , cold = True ) # Halo formation redshift
189181 c = B * (1 + zf )/ (1. + z ) # Halo concentration; equation (20)
@@ -194,37 +186,39 @@ def power(k:np.array, zs:np.array, CAMB_results:camb.CAMBdata,
194186 rv = hmod .virial_radius (M )
195187 Uk = np .ones ((len (k ), len (M )))
196188 for iM , (_rv , _c , _nu ) in enumerate (zip (rv , c , nu )): # TODO: Remove loop for speed?
197- if baryons :
198- Uk [:, iM ] = _win_NFW_baryons (k * (_nu ** eta ), _rv , _c , M [iM ], Mb , fstar , Om_b , Om_m , Om_c , hmod .rhom )[:,0 ]
199-
189+ if T_AGN :
190+ Uk [:, iM ] = _win_NFW_baryons (k * (_nu ** eta ), _rv , _c , M [iM ], Mb , fstar , Om_m , Om_c , Om_b )[:, 0 ]
200191 else :
201192 Uk [:, iM ] = _win_NFW (k * (_nu ** eta ), _rv , _c )[:, 0 ]
202- if baryons :
203- profile = halo .profile .Fourier (k , M , Uk , amplitude = M / hmod .rhom , mass_tracer = True ) # NOTE: No Factor of 1-f_nu for baryonic, because this effect is already included!
204-
205- else :
206- profile = halo .profile .Fourier (k , M , Uk , amplitude = M * (1. - f_nu )/ hmod .rhom , mass_tracer = True ) # NOTE: Factor of 1-f_nu
207-
193+ if T_AGN : # NOTE: No Factor of 1-f_nu for baryonic, because this effect is already included!
194+ profile = halo .profile .Fourier (k , M , Uk , amplitude = M / hmod .rhom , mass_tracer = True )
195+ else : # NOTE: Factor of 1-f_nu in profile amplitude
196+ profile = halo .profile .Fourier (k , M , Uk , amplitude = M * (1. - f_nu )/ hmod .rhom , mass_tracer = True )
197+
208198 # Vanilla power spectrum calculation
209- _ , Pk_1h_ , _ = hmod .power_spectrum (k , Pk_lin , M , sigmaM , {'m' : profile }, simple_twohalo = True )
199+ _ , _Pk_1h , _ = hmod .power_spectrum (k , Pk_lin , M , sigmaM , {'m' : profile }, simple_twohalo = True )
210200
211201 # HMcode tweaks
212- P_wig = _get_Pk_wiggle (k , Pk_lin , CAMB_results ) # Isolate spectral wiggle; footnote 7
213- Pk_dwl = Pk_lin - (1. - np .exp (- (k * sigmaV )** 2 ))* P_wig # Constuct linear spectrum with smoothed wiggle; equation (15)
214- Pk_2h = Pk_dwl * (1. - f * (k / kd )** nd / (1. + (k / kd )** nd )) # Two-halo term; equation (16)
215- Pk_1h = (k / ks )** 4 / (1. + (k / ks )** 4 )* Pk_1h_ ['m-m' ] # One-halo term; equation (17)
216- Pk_hm = (Pk_2h ** alpha + Pk_1h ** alpha )** (1. / alpha ) # Total prediction via smoothed sum; equation (23)
217-
218- if hmcodetweaks == False :
219- Pk_hm = Pk_lin + Pk_1h_ ['m-m' ]
220-
202+ if tweaks :
203+ Pk_wig = _get_Pk_wiggle (k , Pk_lin , CAMB_results ) # Isolate spectral wiggle; footnote 7
204+ Pk_dwl = Pk_lin - (1. - np .exp (- (k * sigmaV )** 2 ))* Pk_wig # Constuct linear spectrum with smoothed wiggle; equation (15)
205+ Pk_2h = Pk_dwl * (1. - f * (k / kd )** nd / (1. + (k / kd )** nd )) # Two-halo term; equation (16)
206+ Pk_1h = (k / ks )** 4 / (1. + (k / ks )** 4 )* _Pk_1h ['m-m' ] # One-halo term; equation (17)
207+ Pk_hm = (Pk_2h ** alpha + Pk_1h ** alpha )** (1. / alpha ) # Total prediction via smoothed sum; equation (23)
208+ else :
209+ Pk_hm = Pk_lin + _Pk_1h ['m-m' ]
221210 Pk_HMcode [iz , :] = Pk_hm
222211
212+ if tweaks and T_AGN :
213+ suppression = _get_feedback_suppression (k , zs , CAMB_results , T_AGN , Mmin = Mmin , Mmax = Mmax , nM = nM , verbose = False )
214+ Pk_HMcode *= suppression
215+
223216 # Finish
224217 if verbose :
225218 t_finish = time ()
226219 print ('HMcode predictions complete for {:} redshifts' .format (len (zs )))
227220 print ('Total HMcode run time: {:.3f}s' .format (t_finish - t_start ))
221+ print ()
228222 return Pk_HMcode
229223
230224
@@ -328,56 +322,50 @@ def _win_NFW(k:np.ndarray, rv:np.ndarray, c:np.ndarray) -> np.ndarray:
328322 return Wk
329323
330324
331- def _win_NFW_baryons (k :np .ndarray , rv :np .ndarray , c :np .ndarray , M :np .ndarray , Mb :float , f :float , Om_b :float , Om_m :float , Om_c :float , rhom :float ) -> np .ndarray :
332- """Normalised Fourier transform for NFW profile, including baryonic effects
333- Is the same as Equation (25) of Mead et al. (2021)
325+ def _win_NFW_baryons (k :np .ndarray , rv :np .ndarray , c :np .ndarray ,
326+ M :np .ndarray , Mb :float , fstar :float ,
327+ Om_m :float , Om_c :float , Om_b :float ) -> np .ndarray :
328+ '''
329+ Normalised Fourier transform for NFW profile, including baryonic effects
330+ Equation (25) from Mead et al. (2021)
334331 Calls _win_NFW
335- """
336- Wk = _win_NFW (k , rv , c )
337-
338- fg = (Om_b / Om_m - f )* (M / Mb )** 2 / (1 + (M / Mb )** 2 ) #Gas content (Eq. 24 with beta=2 specified)
339- Wk = ((Om_c )/ Om_m + fg )* Wk
340- Wk += f
341-
332+ '''
333+ Wk = _win_NFW (k , rv , c )
334+ fg = (Om_b / Om_m - fstar )* (M / Mb )** 2 / (1. + (M / Mb )** 2 ) # Gas content (Eq. 24 with beta=2)
335+ Wk = (Om_c / Om_m + fg )* Wk
336+ Wk += fstar
342337 return Wk
343338
344339
345- def get_Baryon_Parameters (T_AGN :float ) -> dict :
346- """Maps 1-Param baryon feedback model from HMCode2020 to 6 baryonic parameters
340+ def _get_feedback_parameters (T_AGN :float ) -> dict :
341+ '''
342+ Maps one-Param baryon feedback model from HMCode2020 to 6 baryonic parameters
347343 Uses parameters from Table 5 in Mead et al. (2021)
348- Warning:
349- This fit was obtained using the vanilla halo model! If the hmcode tweaks are used, different values are likely needed.
350-
351- Args:
352- T_AGN (float): AGN Temperature parameter
353-
354- Returns:
355- dict: baryonification parameters, B0, Bz, Mb0, Mbz, f0, fz
356- """
357-
358- theta = np .log10 (T_AGN / np .power (10 ,7.8 ))
359- params = {}
360- params ["B0" ]= 3.44 - 0.496 * theta
361- params ["Bz" ]= - 0.0671 - 0.0371 * theta
362- params ["Mb0" ]= np .power (10 , 13.87 + 1.81 * theta ) #Units: Msun/h
363- params ["Mbz" ]= - 0.108 + 0.195 * theta
364- params ["f0" ]= (2.01 - 0.3 * theta )* 1e-2
365- params ["fz" ]= 0.409 + 0.0224 * theta
344+ This fit was obtained using the vanilla halo model!
345+ If the hmcode tweaks are used, different values are likely needed.
346+ '''
347+ theta = np .log10 (T_AGN / np .power (10 , 7.8 ))
348+ params = {
349+ 'B0' : 3.44 - 0.496 * theta ,
350+ 'Bz' : - 0.0671 - 0.0371 * theta ,
351+ 'Mb0' : np .power (10 , 13.87 + 1.81 * theta ), # [Msun/h]
352+ 'Mbz' : - 0.108 + 0.195 * theta ,
353+ 'f0' : (2.01 - 0.3 * theta )* 1e-2 ,
354+ 'fz' : 0.409 + 0.0224 * theta ,
355+ }
366356 return params
367357
368- def get_Baryon_Suppression (k :np .array , zs :np .array , CAMB_results :camb .CAMBdata , T_AGN = None , hmcodetweaks = False ,
369- Mmin = 1e0 , Mmax = 1e18 , nM = 256 , verbose = False ):
370- """Calculates the ratio of the powerspectrum with baryonic effect to the dark-matter-only powerspectrum for a given T_AGN
371- Assumes the 1-param model from HMCode2020
372- Warning:
373- Since the fit for the baryonic effects was obtained with the vanilla halo model, it is not safe to set hmcodetweaks=True
374- """
375-
376-
377- baryon_params = get_Baryon_Parameters (T_AGN )
378-
379- Pk_dmo = power (k , zs , CAMB_results , Mmin , Mmax , nM , verbose , baryons = False , hmcodetweaks = hmcodetweaks )
380-
381- Pk_bar = power (k , zs , CAMB_results , Mmin , Mmax , nM , verbose , baryons = True , baryon_params = baryon_params , hmcodetweaks = hmcodetweaks )
382-
383- return Pk_bar / Pk_dmo
358+
359+ def _get_feedback_suppression (k :np .array , zs :np .array , CAMB_results :camb .CAMBdata , T_AGN :float ,
360+ Mmin = 1e0 , Mmax = 1e18 , nM = 256 , verbose = False ) -> np .ndarray :
361+ '''
362+ Calculates the ratio of the powerspectrum with baryonic effects to that of dark-matter-only
363+ Assumes the one-parameter T_AGN model from HMCode2020
364+ Warning: Since the fit for the baryonic effects was obtained with the vanilla halo model,
365+ it is not safe to set tweaks=True below
366+ '''
367+ Pk_gravity = power (k , zs , CAMB_results , T_AGN = None , Mmin = Mmin , Mmax = Mmax , nM = nM ,
368+ tweaks = False , verbose = verbose )
369+ Pk_feedback = power (k , zs , CAMB_results , T_AGN = T_AGN , Mmin = Mmin , Mmax = Mmax , nM = nM ,
370+ tweaks = False , verbose = verbose )
371+ return Pk_feedback / Pk_gravity
0 commit comments