From 9f65abe4fae1a4e52715d43032a726cd4a9da86e Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 6 May 2026 15:09:41 -0400 Subject: [PATCH 01/15] Move v2 corrections to this branch --- zeus21/correlations_v2.py | 1024 +++++++++++++++++++++++++++++++++++++ zeus21/cosmology_v2.py | 536 +++++++++++++++++++ zeus21/inputs_v2.py | 996 ++++++++++++++++++++++++++++++++++++ 3 files changed, 2556 insertions(+) create mode 100644 zeus21/correlations_v2.py create mode 100644 zeus21/cosmology_v2.py create mode 100644 zeus21/inputs_v2.py diff --git a/zeus21/correlations_v2.py b/zeus21/correlations_v2.py new file mode 100644 index 0000000..3b9ce03 --- /dev/null +++ b/zeus21/correlations_v2.py @@ -0,0 +1,1024 @@ +""" + +Code to compute correlation functions from power spectra and functions of them. Holds two classes: Correlations (with matter correlation functions smoothed over different R), and Power_Spectra (which will compute and hold the 21-cm power spectrum and power for derived quantities like xa, Tk, etc.) + +Author: Julian B. Muñoz +UT Austin and Harvard CfA - January 2023 + +Edited by Hector Afonso G. Cruz +JHU - July 2024 + +Edited by Sarah Libanore +BGU - July 2025 + +""" + +import numpy as np +from scipy.interpolate import UnivariateSpline +from scipy.interpolate import interp1d +import mcfit +from scipy.special import gammaincc #actually very fast, no need to approximate +import numexpr as ne + +from . import constants +from . import cosmology +from . import z21_utilities + + + +class Power_Spectra: + "Get power spetrum from correlation functions and coefficients" + + def __init__(self, User_Parameters, Cosmo_Parameters, Astro_Parameters, T21_coefficients, RSD_MODE=1): + +# print("STEP 0: Variable Setup") + #set up some variables + self._rs_input_mcfit = Cosmo_Parameters.rlist_CF #just to make notation simpler + self.klist_PS = Cosmo_Parameters._klistCF + self.RSD_MODE = RSD_MODE #redshift-space distortion mode. 0 = None (mu=0), 1 = Spherical avg (like 21-cmFAST), 2 = LoS only (mu=1). 2 is more observationally relevant, whereas 1 the standard assumption in sims. 0 is just for comparison with real-space #TODO: mode to save at different mu + + #first get the linear window functions -- note it already has growth factor in it, so it multiplies Pmatter(z=0) + #fix some arrays: TYTYTY HERE + + self._zGreaterMatrix100, self._iRnonlinear, self._corrdNL = self._prepare_corr_arrays(Cosmo_Parameters, T21_coefficients) + + self.kwindow, self.windowalpha_II = self.get_xa_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 2) + self._kwindowX, self.windowxray_II = self.get_Tx_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 2) + + + if Astro_Parameters.USE_POPIII == True: + # SarahLibanore: add AstroParams to use flag on quadratic order + self.kwindow, self.windowalpha_III = self.get_xa_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 3) + # SarahLibanore: add AstroParams to use flag on quadratic order + self._kwindowX, self.windowxray_III = self.get_Tx_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 3) + else: + self.windowalpha_III = np.zeros_like(self.windowalpha_II) + self.windowxray_III = np.zeros_like(self.windowxray_II) + + #calculate some growth etc, and the bubble biases for the xHI linear window function: + self._lingrowthd = cosmology.growth(Cosmo_Parameters, T21_coefficients.zintegral) + + + + ############################## + +# print("STEP 1: Computing Nonlinear Power Spectra") + #finally, get all the nonlinear correlation functions: +# print("Computing Pop II-dependent power spectra") + # SarahLibanore: add AstroParams to use flag on quadratic order + self.get_all_corrs_II(Astro_Parameters, User_Parameters, Cosmo_Parameters, T21_coefficients) + + if Astro_Parameters.USE_POPIII == True: +# print("Computing Pop IIxIII-dependent cross power spectra") + self.get_all_corrs_IIxIII(Cosmo_Parameters, T21_coefficients) + +# print("Computing Pop III-dependent power spectra") + self.get_all_corrs_III(User_Parameters, Cosmo_Parameters, T21_coefficients) + else: + #bypases Pop III correlation routine and sets all Pop III-dependent correlations to zero + self._IIxIII_deltaxi_xa = np.zeros_like(self._II_deltaxi_xa) + self._IIxIII_deltaxi_Tx = np.zeros_like(self._II_deltaxi_xa) + self._IIxIII_deltaxi_xaTx = np.zeros_like(self._II_deltaxi_xa) + + self._III_deltaxi_xa = np.zeros_like(self._II_deltaxi_xa) + self._III_deltaxi_dxa = np.zeros_like(self._II_deltaxi_xa) + + self._III_deltaxi_Tx = np.zeros_like(self._II_deltaxi_xa) + self._III_deltaxi_xaTx = np.zeros_like(self._II_deltaxi_xa) + self._III_deltaxi_dTx = np.zeros_like(self._II_deltaxi_xa) + + + self._k3over2pi2 = (self.klist_PS**3)/(2.0 * np.pi**2) + + #and now define power spectra: + #for xalpha, first linear + self._Pk_xa_lin_II = self.windowalpha_II**2 * Cosmo_Parameters._PklinCF + self._Pk_xa_lin_III = self.windowalpha_III**2 * Cosmo_Parameters._PklinCF ###TO DO (linearized VCB flucts):+ self.windowalphaVel_III**2 * Cosmo_Parameters._PkEtaCF + self._Pk_xa_lin_IIxIII = 2* self.windowalpha_II * self.windowalpha_III * Cosmo_Parameters._PklinCF #Pop IIxIII cross term doesn't have a velocity component + + self.Deltasq_xa_lin_II = self._Pk_xa_lin_II * self._k3over2pi2 #note that it still has units of xa_avg + self.Deltasq_xa_lin_III = self._Pk_xa_lin_III * self._k3over2pi2 #note that it still has units of xa_avg + self.Deltasq_xa_lin_IIxIII = self._Pk_xa_lin_IIxIII * self._k3over2pi2 #note that it still has units of xa_avg + + #nonlinear corrections too: + self._d_Pk_xa_nl_II = self.get_list_PS(self._II_deltaxi_xa, T21_coefficients.zintegral) + self._d_Pk_xa_nl_III = self.get_list_PS(self._III_deltaxi_xa, T21_coefficients.zintegral) #velocity correlations already embedded in nonlinear computation + self._d_Pk_xa_nl_IIxIII = self.get_list_PS(self._IIxIII_deltaxi_xa, T21_coefficients.zintegral) + + self.Deltasq_xa_II = self.Deltasq_xa_lin_II + self._d_Pk_xa_nl_II * self._k3over2pi2 #note that it still has units of xa_avg + self.Deltasq_xa_III = self.Deltasq_xa_lin_III + self._d_Pk_xa_nl_III * self._k3over2pi2 #note that it still has units of xa_avg + self.Deltasq_xa_IIxIII = self.Deltasq_xa_lin_IIxIII + self._d_Pk_xa_nl_IIxIII * self._k3over2pi2 #note that it still has units of xa_avg + + + ############################## + + + #and same for xray + self._Pk_Tx_lin_II = self.windowxray_II**2 * Cosmo_Parameters._PklinCF + self._Pk_Tx_lin_III = self.windowxray_III**2 * Cosmo_Parameters._PklinCF ###TO DO (linearized VCB flucts):+ self.windowxrayVel_III**2 * Cosmo_Parameters._PkEtaCF + self._Pk_Tx_lin_IIxIII = 2* self.windowxray_II * self.windowxray_III * Cosmo_Parameters._PklinCF #Pop IIxIII cross term doesn't have a velocity component + + self.Deltasq_Tx_lin_II = self._Pk_Tx_lin_II * self._k3over2pi2 + self.Deltasq_Tx_lin_III = self._Pk_Tx_lin_III * self._k3over2pi2 + self.Deltasq_Tx_lin_IIxIII = self._Pk_Tx_lin_IIxIII * self._k3over2pi2 + + self._d_Pk_Tx_nl_II = self.get_list_PS(self._II_deltaxi_Tx, T21_coefficients.zintegral) + self._d_Pk_Tx_nl_III = self.get_list_PS(self._III_deltaxi_Tx, T21_coefficients.zintegral) + self._d_Pk_Tx_nl_IIxIII = self.get_list_PS(self._IIxIII_deltaxi_Tx, T21_coefficients.zintegral) + + self.Deltasq_Tx_II = self.Deltasq_Tx_lin_II + self._d_Pk_Tx_nl_II * self._k3over2pi2 + self.Deltasq_Tx_III = self.Deltasq_Tx_lin_III + self._d_Pk_Tx_nl_III * self._k3over2pi2 + self.Deltasq_Tx_IIxIII = self.Deltasq_Tx_lin_IIxIII + self._d_Pk_Tx_nl_IIxIII * self._k3over2pi2 + + + ############################## + + + #and their cross correlation + self._Pk_xaTx_lin_II = self.windowalpha_II * self.windowxray_II * Cosmo_Parameters._PklinCF + self._Pk_xaTx_lin_III = self.windowalpha_III * self.windowxray_III * Cosmo_Parameters._PklinCF ###TO DO (linearized VCB flucts):+ self.windowalphaVel_III * self.windowxrayVel_III * Cosmo_Parameters._PkEtaCF + self._Pk_xaTx_lin_IIxIII = (self.windowalpha_II * self.windowxray_III + self.windowalpha_III * self.windowxray_II) * Cosmo_Parameters._PklinCF + + self.Deltasq_xaTx_lin_II = self._Pk_xaTx_lin_II * self._k3over2pi2 + self.Deltasq_xaTx_lin_III = self._Pk_xaTx_lin_III * self._k3over2pi2 + self.Deltasq_xaTx_lin_IIxIII = self._Pk_xaTx_lin_IIxIII * self._k3over2pi2 + + self._d_Pk_xaTx_nl_II = self.get_list_PS(self._II_deltaxi_xaTx, T21_coefficients.zintegral) + self._d_Pk_xaTx_nl_III = self.get_list_PS(self._III_deltaxi_xaTx, T21_coefficients.zintegral) + self._d_Pk_xaTx_nl_IIxIII = self.get_list_PS(self._IIxIII_deltaxi_xaTx, T21_coefficients.zintegral) + + self.Deltasq_xaTx_II = self.Deltasq_xaTx_lin_II + self._d_Pk_xaTx_nl_II * self._k3over2pi2 #note that it still has units of xa_avg + self.Deltasq_xaTx_III = self.Deltasq_xaTx_lin_III + self._d_Pk_xaTx_nl_III * self._k3over2pi2 #note that it still has units of xa_avg + self.Deltasq_xaTx_IIxIII = self.Deltasq_xaTx_lin_IIxIII + self._d_Pk_xaTx_nl_IIxIII * self._k3over2pi2 #note that it still has units of xa_avg + + + ############################## + + + #and the same for deltaNL and its cross terms: + self._Pk_d_lin = np.outer(self._lingrowthd**2, Cosmo_Parameters._PklinCF) #No Pop II or III contribution + self.Deltasq_d_lin = self._Pk_d_lin * self._k3over2pi2 #note that it still has units of xa_avg + + self._Pk_dxa_lin_II = (self.windowalpha_II.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF + self._Pk_dxa_lin_III = (self.windowalpha_III.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF #No velocity component + + self._Pk_dTx_lin_II = (self.windowxray_II.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF + self._Pk_dTx_lin_III = (self.windowxray_III.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF #No velocity component + + self.Deltasq_dxa_lin_II = self._Pk_dxa_lin_II * self._k3over2pi2 + self.Deltasq_dxa_lin_III = self._Pk_dxa_lin_III * self._k3over2pi2 #No velocity component + + self.Deltasq_dTx_lin_II = self._Pk_dTx_lin_II * self._k3over2pi2 + self.Deltasq_dTx_lin_III = self._Pk_dTx_lin_III * self._k3over2pi2 #No velocity component + + self._Pk_d = self._Pk_d_lin + + self._Pk_dxa_II = self._Pk_dxa_lin_II + self._Pk_dxa_III = self._Pk_dxa_lin_III + + self._Pk_dTx_II = self._Pk_dTx_lin_II + self._Pk_dTx_III = self._Pk_dTx_lin_III + + if(User_Parameters.FLAG_DO_DENS_NL): #note that the nonlinear terms (cross and auto) below here have the growth already accounted for + + self._d_Pk_d_nl = self.get_list_PS(self._II_deltaxi_d, T21_coefficients.zintegral) + self._Pk_d += self._d_Pk_d_nl + + self._d_Pk_dxa_nl_II = self.get_list_PS(self._II_deltaxi_dxa, T21_coefficients.zintegral) + self._d_Pk_dxa_nl_III = self.get_list_PS(self._III_deltaxi_dxa, T21_coefficients.zintegral) + self._Pk_dxa_II += self._d_Pk_dxa_nl_II + self._Pk_dxa_III += self._d_Pk_dxa_nl_III + + self._d_Pk_dTx_nl_II = self.get_list_PS(self._II_deltaxi_dTx, T21_coefficients.zintegral) + self._d_Pk_dTx_nl_III = self.get_list_PS(self._III_deltaxi_dTx, T21_coefficients.zintegral) + + self._Pk_dTx_II += self._d_Pk_dTx_nl_II + self._Pk_dTx_III += self._d_Pk_dTx_nl_III + + self.Deltasq_d = self._Pk_d * self._k3over2pi2 + + self.Deltasq_dxa_II = self._Pk_dxa_II * self._k3over2pi2 + self.Deltasq_dxa_III = self._Pk_dxa_III * self._k3over2pi2 + + self.Deltasq_dTx_II = self._Pk_dTx_II * self._k3over2pi2 + self.Deltasq_dTx_III = self._Pk_dTx_III * self._k3over2pi2 + + + ############################## + + + #and xHI too. Linear part does not have bubbles, only delta part + if(constants.FLAG_DO_BUBBLES): + #auto + self._Pk_xion_lin = self.windowxion**2 * Cosmo_Parameters._PklinCF + self.Deltasq_xion_lin = self._Pk_xion_lin * self._k3over2pi2 + + self._d_Pk_xion_nl = self.get_list_PS(self._deltaxi_xi, T21_coefficients.zintegral) + self.Deltasq_xion = self.Deltasq_xion_lin + self._d_Pk_xion_nl * self._k3over2pi2 + + #cross with density + self._Pk_dxion_lin = (self.windowxion.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF + self.Deltasq_dxion_lin = self._Pk_dxion_lin * self._k3over2pi2 + + self._d_Pk_dxion_nl = self.get_list_PS(self._deltaxi_dxi, T21_coefficients.zintegral) + self.Deltasq_dxion = self.Deltasq_dxion_lin + self._d_Pk_dxion_nl * self._k3over2pi2 + + #cross with xa + self._Pk_xaxion_lin = self.windowxion * self.windowalpha * Cosmo_Parameters._PklinCF + self.Deltasq_xaxion_lin = self._Pk_xaxion_lin * self._k3over2pi2 + + self._d_Pk_xaxion_nl = self.get_list_PS(self._deltaxi_xaxi, T21_coefficients.zintegral) + self.Deltasq_xaxion = self.Deltasq_xaxion_lin + self._d_Pk_xaxion_nl * self._k3over2pi2 + + #and cross with Tx + self._Pk_Txxion_lin = self.windowxion * self.windowxray * Cosmo_Parameters._PklinCF + self.Deltasq_Txxion_lin = self._Pk_Txxion_lin * self._k3over2pi2 + + self._d_Pk_Txxion_nl = self.get_list_PS(self._deltaxi_Txxi, T21_coefficients.zintegral) + self.Deltasq_Txxion = self.Deltasq_Txxion_lin + self._d_Pk_Txxion_nl * self._k3over2pi2 + else: + self.Deltasq_xion = np.zeros_like(self.Deltasq_d) + self.Deltasq_xion_lin = np.zeros_like(self.Deltasq_d) + self.Deltasq_dxion = np.zeros_like(self.Deltasq_d) + self.Deltasq_dxion_lin = np.zeros_like(self.Deltasq_d) + self.Deltasq_xaxion = np.zeros_like(self.Deltasq_d) + self.Deltasq_xaxion_lin = np.zeros_like(self.Deltasq_d) + self.Deltasq_Txxion = np.zeros_like(self.Deltasq_d) + self.Deltasq_Txxion_lin = np.zeros_like(self.Deltasq_d) + #These have to be defined even if no EoR bubbles + + + ############################## + +# print('STEP 2: Computing 21-cm Power Spectrum') + #and get the PS of T21 too. + self._betaT = T21_coefficients.T_CMB/T21_coefficients.Tk_avg /(T21_coefficients.invTcol_avg**-1 - T21_coefficients.T_CMB) #multiplies \delta T_x and \delta T_ad [both dimensionful, not \deltaT/T] + self._betaxa = 1./(1. + T21_coefficients.xa_avg)/T21_coefficients.xa_avg #multiplies \delta x_a [again not \delta xa/xa] + + #calculate beta_adiabatic + self._dlingrowthd_dz = cosmology.dgrowth_dz(Cosmo_Parameters, T21_coefficients.zintegral) + + _factor_adi_ = (1+T21_coefficients.zintegral)**2 + _integrand_adi = T21_coefficients.Tk_avg*self._dlingrowthd_dz/_factor_adi_ * T21_coefficients.dlogzint*T21_coefficients.zintegral + + if(Cosmo_Parameters.Flag_emulate_21cmfast==True): + _hizintegral = 0.0 #they do not account for the adiabatic history prior to starting their evolution. It misses ~half of the adiabatic flucts. + else: + #the z>zmax part of the integral we do aside. Assume Tk=Tadiabatic from CLASS. + _zlisthighz_ = np.linspace(T21_coefficients.zintegral[-1], 99., 100) #beyond z=100 need to explictly tell CLASS to save growth + _dgrowthhighz_ = cosmology.dgrowth_dz(Cosmo_Parameters, _zlisthighz_) + _hizintegral = np.trapezoid(cosmology.Tadiabatic(Cosmo_Parameters,_zlisthighz_) + /(1+_zlisthighz_)**2 * _dgrowthhighz_, _zlisthighz_) + + self._betaTad_ = -2./3. * _factor_adi_/self._lingrowthd * (np.cumsum(_integrand_adi[::-1])[::-1] + _hizintegral) #units of Tk_avg. Internal sum goes from high to low z (backwards), minus sign accounts for it properly so it's positive. + self._betaTad_ *= self._betaT #now it's dimensionless, since it multiplies \delta_m(k,z) + + + + self._betad = (1.0 + self._betaTad_)# this includes both the usual (1+d) and the adiabatic Tk contribution. Now we add RSD + if(self.RSD_MODE==0): #no RSD (real space) + pass #nothing to change + elif(self.RSD_MODE==1): #spherically avg'd RSD + self._betad += constants.MU_AVG ** 2 + elif(self.RSD_MODE==2): #LoS RSD (mu=1) + self._betad += constants.MU_LoS ** 2 + else: + print('Error, have to choose an RSD mode! RSD_MODE') + + if(constants.FLAG_DO_BUBBLES): + self._betaxion = - 1.0/T21_coefficients.xHI_avg * np.heaviside(constants.ZMAX_Bubbles - T21_coefficients.zintegral, 0.5) # xion = 1 - xHI, only for zikj', self._allbetas, self._allbetas) + + #Sum Pop II and Pop III contributions + self.Deltasq_d = self.Deltasq_d + self.Deltasq_dxa = self.Deltasq_dxa_II + self.Deltasq_dxa_III + self.Deltasq_dTx = self.Deltasq_dTx_II + self.Deltasq_dTx_III + + self.Deltasq_xa = self.Deltasq_xa_II + self.Deltasq_xa_III + self.Deltasq_xa_IIxIII + self.Deltasq_xaTx = self.Deltasq_xaTx_II + self.Deltasq_xaTx_III + self.Deltasq_xaTx_IIxIII + self.Deltasq_Tx = self.Deltasq_Tx_II + self.Deltasq_Tx_III + self.Deltasq_Tx_IIxIII + + + self._allcorrs = np.array( [[self.Deltasq_d, self.Deltasq_dxa, self.Deltasq_dTx, self.Deltasq_dxion], \ + [self.Deltasq_dxa, self.Deltasq_xa, self.Deltasq_xaTx, self.Deltasq_xaxion], \ + [self.Deltasq_dTx, self.Deltasq_xaTx, self.Deltasq_Tx, self.Deltasq_Txxion], \ + [self.Deltasq_dxion, self.Deltasq_xaxion, self.Deltasq_Txxion, self.Deltasq_xion]]\ + ) + + self.Deltasq_T21 = np.einsum('ijk...,ijkl...->kl...', self._allbetamatrix, self._allcorrs) + self.Deltasq_T21 = (self.Deltasq_T21.T*T21_coefficients.T21avg**2).T + + self.Deltasq_dT21 = (np.einsum('ik...,ikl...->kl...',self._allbetas,self._allcorrs[0]).T*T21_coefficients.T21avg).T + + + #Sum Linear Pop II and Pop III contributions + self.Deltasq_d_lin = self.Deltasq_d_lin + self.Deltasq_dxa_lin = self.Deltasq_dxa_lin_II + self.Deltasq_dxa_lin_III + self.Deltasq_dTx_lin = self.Deltasq_dTx_lin_II + self.Deltasq_dTx_lin_III + + self.Deltasq_xa_lin = self.Deltasq_xa_lin_II + self.Deltasq_xa_lin_III + self.Deltasq_xa_lin_IIxIII + self.Deltasq_xaTx_lin = self.Deltasq_xaTx_lin_II + self.Deltasq_xaTx_lin_III + self.Deltasq_xaTx_lin_IIxIII + self.Deltasq_Tx_lin = self.Deltasq_Tx_lin_II + self.Deltasq_Tx_lin_III + self.Deltasq_Tx_lin_IIxIII + + + self._allcorrs_lin = np.array( [[self.Deltasq_d_lin, self.Deltasq_dxa_lin, self.Deltasq_dTx_lin, self.Deltasq_dxion_lin], \ + [self.Deltasq_dxa_lin, self.Deltasq_xa_lin, self.Deltasq_xaTx_lin, self.Deltasq_xaxion_lin], \ + [self.Deltasq_dTx_lin, self.Deltasq_xaTx_lin, self.Deltasq_Tx_lin, self.Deltasq_Txxion_lin], \ + [self.Deltasq_dxion_lin, self.Deltasq_xaxion_lin, self.Deltasq_Txxion_lin, self.Deltasq_xion_lin]]\ + ) + + self.Deltasq_T21_lin = np.einsum('ijk...,ijkl...->kl...', self._allbetamatrix, self._allcorrs_lin) + self.Deltasq_T21_lin = (self.Deltasq_T21_lin.T*T21_coefficients.T21avg**2).T + + self.Deltasq_dT21_lin = (np.einsum('ik...,ikl...->kl...',self._allbetas,self._allcorrs_lin[0]).T*T21_coefficients.T21avg).T +# print("Power Spectral Routine Done!") + + + + def _prepare_corr_arrays(self, Cosmo_Parameters, T21_coefficients): + zGM = np.copy(T21_coefficients.zGreaterMatrix) + zGM[np.isnan(zGM)] = 100 + iR = np.arange(Cosmo_Parameters.indexmaxNL) + corr = Cosmo_Parameters.xi_RR_CF[np.ix_(iR, iR)] + corr[:Cosmo_Parameters.indexminNL, :Cosmo_Parameters.indexminNL] = \ + corr[Cosmo_Parameters.indexminNL, Cosmo_Parameters.indexminNL] + return zGM, iR, corr.reshape((1, *corr.shape)) + + # SarahLibanore: add AstroParams to use flag on quadratic order + def get_xa_window(self, Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 0): #set pop to 2 or 3, default zero just so python doesn't complain + "Returns the xa window function for all z in zintegral" + + coeffzp = T21_coefficients.coeff1LyAzp + coeffJaxa = T21_coefficients.coeff_Ja_xa + + growthRmatrix = cosmology.growth(Cosmo_Parameters, self._zGreaterMatrix100) + + if pop == 2: + coeffRmatrix = T21_coefficients.coeff2LyAzpRR_II + gammaRmatrix = T21_coefficients.gamma_II_index2D * growthRmatrix + elif pop == 3: + coeffRmatrix = T21_coefficients.coeff2LyAzpRR_III + gammaRmatrix = T21_coefficients.gamma_III_index2D * growthRmatrix + else: + print("Must set pop to either 2 or 3!") + + _wincoeffsMatrix = coeffRmatrix * gammaRmatrix + # SarahLibanore: quadratic order in the lognormal + if Astro_Parameters.quadratic_SFRD_lognormal: + _wincoeffsMatrix *= 1./(1-2.*T21_coefficients.gamma2_II_index2D*T21_coefficients.sigmaofRtab**2) + + if(Cosmo_Parameters.Flag_emulate_21cmfast==False): #do the standard 1D TopHat + _wincoeffsMatrix /=(4*np.pi * Cosmo_Parameters._Rtabsmoo**2) * (Cosmo_Parameters._Rtabsmoo * Cosmo_Parameters._dlogRR) # so we can just use mcfit for logFFT, 1/(4pir^2 * Delta r) + _kwinalpha, _win_alpha = self.get_Pk_from_xi(Cosmo_Parameters._Rtabsmoo, _wincoeffsMatrix) + + else: + _kwinalpha = self.klist_PS + + coeffRgammaRmatrix = coeffRmatrix * gammaRmatrix + coeffRgammaRmatrix = coeffRgammaRmatrix.reshape(*coeffRgammaRmatrix.shape, 1) + + dummyMesh, RtabsmooMesh, kWinAlphaMesh = np.meshgrid(T21_coefficients.zintegral, Cosmo_Parameters._Rtabsmoo, _kwinalpha, indexing = 'ij', sparse = True) + + _win_alpha = coeffRgammaRmatrix * z21_utilities._WinTH(RtabsmooMesh, kWinAlphaMesh) + _win_alpha = np.sum(_win_alpha, axis = 1) + + _win_alpha *= np.array([coeffzp*coeffJaxa]).T + + return _kwinalpha, _win_alpha + + + # SarahLibanore: add AstroParams to use flag on quadratic order + def get_Tx_window(self, Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 0): #set pop to 2 or 3, default zero just so python doesn't complain + "Returns the Tx window function for all z in zintegral" + + coeffzp = np.array([T21_coefficients.coeff1Xzp]).T + growthRmatrix = cosmology.growth(Cosmo_Parameters, self._zGreaterMatrix100) + + if pop == 2: + coeffRmatrix = T21_coefficients.coeff2XzpRR_II + gammaRmatrix = T21_coefficients.gamma_II_index2D * growthRmatrix + _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_II#z-dependent, includes 10^40 erg/s/SFR normalizaiton and erg/K conversion factor, and the 1/(1+z)^2 factor to compensate the adiabatic cooling of the Tx olny part + elif pop == 3: + coeffRmatrix = T21_coefficients.coeff2XzpRR_III + gammaRmatrix = T21_coefficients.gamma_III_index2D * growthRmatrix + _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_III + else: + print("Must set pop to either 2 or 3!") + + # SarahLibanore: quadratic order in the lognormal + if Astro_Parameters.quadratic_SFRD_lognormal: + gammaRmatrix *= (1/(1-2.*T21_coefficients.gamma2_II_index2D*T21_coefficients.sigmaofRtab**2)) + + if(Cosmo_Parameters.Flag_emulate_21cmfast==False): #do the standard 1D TopHat + _wincoeffs = coeffRmatrix * gammaRmatrix #array in logR space + _wincoeffs /=(4*np.pi * Cosmo_Parameters._Rtabsmoo**2) * (Cosmo_Parameters._Rtabsmoo * Cosmo_Parameters._dlogRR) # so we can just use mcfit for logFFT, 1/(4pir^2) * Delta r + _kwinTx, _win_Tx_curr = self.get_Pk_from_xi(Cosmo_Parameters._Rtabsmoo, _wincoeffs) + + else: + _kwinTx = self.klist_PS + + coeffRgammaRmatrix = coeffRmatrix * gammaRmatrix + coeffRgammaRmatrix = coeffRgammaRmatrix.reshape(*coeffRgammaRmatrix.shape, 1) + + dummyMesh, RtabsmooMesh, kWinTxMesh = np.meshgrid(T21_coefficients.zintegral, Cosmo_Parameters._Rtabsmoo, _kwinTx, indexing = 'ij', sparse = True) + + _win_Tx_curr = coeffRgammaRmatrix * z21_utilities._WinTH(RtabsmooMesh, kWinTxMesh) + _win_Tx_curr = np.sum(_win_Tx_curr , axis = 1) + + _win_Tx = _win_Tx_curr * coeffzp + _win_Tx = np.cumsum(_win_Tx[::-1], axis = 0)[::-1] + + _win_Tx =_win_Tx * np.array([_coeffTx_units]).T + + return _kwinTx, _win_Tx + + + # SarahLibanore: function modified to include quadratic order + def get_all_corrs_II(self, Astro_Parameters, User_Parameters, Cosmo_Parameters, T21_coefficients): + + "Returns the Pop II components of the correlation functions of all observables at each z in zintegral" + #HAC: I deleted the bubbles and EoR part, to be done later..... + #self._iRnonlinear = np.arange(Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexmaxNL) + + + _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_II #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor + + growthRmatrix = cosmology.growth(Cosmo_Parameters,self._zGreaterMatrix100[:, self._iRnonlinear]) + + coeffzp1xa = T21_coefficients.coeff1LyAzp * T21_coefficients.coeff_Ja_xa + coeffzp1Tx = T21_coefficients.coeff1Xzp + + coeffR1xa = T21_coefficients.coeff2LyAzpRR_II[:,self._iRnonlinear] + coeffR1Tx = T21_coefficients.coeff2XzpRR_II[:,self._iRnonlinear] + + coeffmatrixxa = coeffR1xa.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * coeffR1xa.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + + # gammaR1 = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] * growthRmatrix + # gammamatrixR1R1 = gammaR1.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * gammaR1.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + + # gammaTimesCorrdNL = ne.evaluate('gammamatrixR1R1 * corrdNL')#np.einsum('ijkl,ijkl->ijkl', gammamatrixR1R1, corrdNL, optimize = True) #same thing as gammamatrixR1R1 * corrdNL but faster + + + # SarahLibanore : change to introduce quantities required in the second order correction + # --- # + growthRmatrix1 = growthRmatrix.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) + growthRmatrix2 = growthRmatrix.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + growth_corr = growthRmatrix1 * growthRmatrix2 + + gammaR1 = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] + sigmaR1 = T21_coefficients.sigmaofRtab[:, self._iRnonlinear] + sR1 = (sigmaR1).reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) + sR2 = (sigmaR1).reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + + g1 = (gammaR1 * sigmaR1).reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) + g2 = (gammaR1 * sigmaR1).reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + gammamatrixR1R1 = g1 * g2 + + corrdNL = self._corrdNL + corrdNL_gs = ne.evaluate('corrdNL * growth_corr/ (sR1 * sR2)') + gammaTimesCorrdNL = ne.evaluate('gammamatrixR1R1 * corrdNL_gs') + + if Astro_Parameters.quadratic_SFRD_lognormal: + + gammaR1NL = T21_coefficients.gamma2_II_index2D[:, self._iRnonlinear] + g1NL = (gammaR1NL * sigmaR1**2).reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) + g2NL = (gammaR1NL * sigmaR1**2).reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + + numerator_NL = ne.evaluate('gammaTimesCorrdNL+ g1 * g1 * (0.5 - g2NL * (1 - corrdNL_gs * corrdNL_gs)) + g2 * g2 * (0.5 - g1NL * (1 - corrdNL_gs * corrdNL_gs))') + + denominator_NL = ne.evaluate('1. - 2 * g1NL - 2 * g2NL + 4 * g1NL * g2NL * (1 - corrdNL_gs * corrdNL_gs)') + + norm1 = ne.evaluate('exp(g1 * g1 / (2 - 4 * g1NL)) / sqrt(1 - 2 * g1NL)') + norm2 = ne.evaluate('exp(g2 * g2 / (2 - 4 * g2NL)) / sqrt(1 - 2 * g2NL)') + + log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm1 * norm2)') + nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') + + # use second order in SFRD lognormal approx + expGammaCorrMinusLinear = ne.evaluate('nonlinearcorrelation - 1-gammaTimesCorrdNL/((-1+2.*g1NL)*(-1+2.*g2NL))') + else: + expGammaCorrMinusLinear = ne.evaluate('exp(gammaTimesCorrdNL) - 1 - gammaTimesCorrdNL') + + self._II_deltaxi_xa = np.einsum('ijkl->il', coeffmatrixxa * expGammaCorrMinusLinear, optimize = True) + self._II_deltaxi_xa *= np.array([coeffzp1xa]).T**2 #brings it to xa units + + if (User_Parameters.FLAG_DO_DENS_NL): + D_coeffR1xa = coeffR1xa.reshape(*coeffR1xa.shape, 1) + DDgammaR1 = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] + D_gammaR1 = DDgammaR1.reshape(*DDgammaR1.shape , 1) + D_growthRmatrix = growthRmatrix[:,:1].reshape(*growthRmatrix[:,:1].shape, 1) + D_corrdNL = corrdNL[:1,0,:,:] + + # SarahLibanore + if Astro_Parameters.quadratic_SFRD_lognormal: + + DDsigmaR1 = T21_coefficients.sigmaofRtab[:, self._iRnonlinear] + D_sigmaR1 = DDsigmaR1.reshape(*DDsigmaR1.shape , 1) + DDgammaR1N = T21_coefficients.gamma2_II_index2D[:, self._iRnonlinear] + D_gammaR1N = DDgammaR1N.reshape(*DDgammaR1N.shape , 1) + + gammaTimesCorrdNL = ne.evaluate('D_gammaR1 * D_growthRmatrix* D_growthRmatrix * D_corrdNL') + numerator_NL = ne.evaluate('gammaTimesCorrdNL+ D_gammaR1 * D_gammaR1 * D_sigmaR1* D_sigmaR1 /2 + D_gammaR1N * D_growthRmatrix * D_growthRmatrix* D_growthRmatrix * D_growthRmatrix * (D_corrdNL * D_corrdNL)') + + denominator_NL = ne.evaluate('1. - 2 * D_gammaR1N*D_sigmaR1*D_sigmaR1') + + norm1 = ne.evaluate('exp(D_gammaR1 * D_gammaR1 * D_sigmaR1* D_sigmaR1 * D_gammaR1 * D_gammaR1 * D_sigmaR1* D_sigmaR1 / (2 - 4 * D_gammaR1N*D_sigmaR1*D_sigmaR1)) / sqrt(1 - 2 * D_gammaR1N*D_sigmaR1*D_sigmaR1)') + + log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm1)') + nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') + + self._II_deltaxi_dxa = np.sum(D_coeffR1xa * ( + nonlinearcorrelation - 1 - D_gammaR1 * D_growthRmatrix**2 * D_corrdNL/(1-2.*D_gammaR1N*D_sigmaR1**2) + ), axis = 1) + + else: + self._II_deltaxi_dxa = np.sum(D_coeffR1xa * ((np.exp(D_gammaR1 * D_growthRmatrix**2 * D_corrdNL )-1.0 ) - D_gammaR1 * D_growthRmatrix**2 * D_corrdNL), axis = 1) + + self._II_deltaxi_d = (np.exp(growthRmatrix[:,:1]**2 * corrdNL[0,0,0,:]) - 1.0) - growthRmatrix[:,:1]**2 * corrdNL[0,0,0,:] + + self._II_deltaxi_dxa *= np.array([coeffzp1xa]).T + + + ### To compute Tx quantities, I'm broadcasting arrays such that the axes are zp1, R1, zp2, R2, and looping over r + # gammaR2 = np.copy(gammaR1) #already has growth factor in this + # gammamatrixR1R2 = gammaR1.reshape(*gammaR1.shape, 1, 1) * gammaR2.reshape(1, 1, *gammaR2.shape) + + coeffzp1Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(*T21_coefficients.coeff1Xzp.shape, 1, 1, 1) + coeffzp2Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(1, 1, *T21_coefficients.coeff1Xzp.shape, 1) + + coeffR2Tx = np.copy(coeffR1Tx) + coeffmatrixTxTx = coeffR1Tx.reshape(*coeffR1Tx.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) + coeffmatrixxaTx = coeffR1xa.reshape(*coeffR1xa.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) + coeffsTxALL = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx + coeffsXaTxALL = coeffzp2Tx * coeffmatrixxaTx + + gammaR2 = np.copy(gammaR1) #already has growth factor in this + sigmaR2 = np.copy(sigmaR1) #already has growth factor in this + + growthRmatrix1 = growthRmatrix.reshape(*gammaR1.shape, 1, 1) + growthRmatrix2 = growthRmatrix.reshape(1, 1, *gammaR2.shape) + growth_corr = growthRmatrix1 * growthRmatrix2 + + g1 = (gammaR1 * sigmaR1).reshape(*gammaR1.shape, 1, 1) + sR1 = (sigmaR1).reshape(*gammaR1.shape, 1, 1) + g2 = (gammaR2 * sigmaR2).reshape(1, 1, *gammaR2.shape) + sR2 = (sigmaR2).reshape(1, 1, *gammaR2.shape) + if Astro_Parameters.quadratic_SFRD_lognormal: + gammaR2NL = np.copy(gammaR1NL) + g1NL = (gammaR1NL * sigmaR1**2).reshape(*gammaR1NL.shape, 1, 1) + g2NL = (gammaR2NL * sigmaR2**2).reshape(1, 1, *gammaR2NL.shape) + + gammamatrixR1R2 = g1 * g2 + + self._II_deltaxi_Tx = np.zeros_like(self._II_deltaxi_xa) + self._II_deltaxi_xaTx = np.zeros_like(self._II_deltaxi_xa) + corrdNLBIG = corrdNL[:,:, np.newaxis, :,:] #dimensions zp1, R1, zp2, R2, and r which will be looped over below + for ir in range(len(Cosmo_Parameters._Rtabsmoo)): + corrdNL = corrdNLBIG[:,:,:,:,ir] + + corrdNL_gs = ne.evaluate('corrdNL * growth_corr / (sR1 * sR2)') + + #HAC: Computations using ne.evaluate(...) use numexpr, which speeds up computations of massive numpy arrays + gammaTimesCorrdNL = ne.evaluate('gammamatrixR1R2 * corrdNL_gs') + if Astro_Parameters.quadratic_SFRD_lognormal: + + numerator_NL = ne.evaluate('gammaTimesCorrdNL + g1 * g1 * (0.5 - g2NL * (1 - corrdNL_gs * corrdNL_gs)) + g2 * g2 * (0.5 - g1NL * (1 - corrdNL_gs * corrdNL_gs))') + denominator_NL = ne.evaluate('1. - 2 * g1NL - 2 * g2NL + 4 * g1NL * g2NL * (1 - corrdNL_gs * corrdNL_gs)') + norm1 = ne.evaluate('exp(g1 * g1 / (2 - 4 * g1NL)) / sqrt(1 - 2 * g1NL)') + norm2 = ne.evaluate('exp(g2 * g2 / (2 - 4 * g2NL)) / sqrt(1 - 2 * g2NL)') + + log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm1 * norm2)') + nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') + + # use second order in SFRD lognormal approx + expGammaCorrMinusLinear = ne.evaluate('nonlinearcorrelation - 1-gammaTimesCorrdNL/((-1+2.*g1NL)*(-1+2.*g2NL))') + else: + expGammaCorrMinusLinear = ne.evaluate('exp(gammaTimesCorrdNL) - 1 - gammaTimesCorrdNL') + + deltaXiTxAddend = ne.evaluate('coeffsTxALL * expGammaCorrMinusLinear') + deltaXiTxAddend = np.einsum('ijkl->ik', deltaXiTxAddend, optimize = True) #equivalent to np.sum(deltaXiTxAddend, axis = (1, 3)) + deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] + deltaXiTxAddend = np.moveaxis(deltaXiTxAddend, 1, 0) + deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] + self._II_deltaxi_Tx[:,ir] = np.einsum('ii->i', deltaXiTxAddend, optimize = True) + + deltaXiXaTxAddend = ne.evaluate('coeffsXaTxALL * expGammaCorrMinusLinear') + deltaXiXaTxAddend = np.einsum('ijkl->ik', deltaXiXaTxAddend, optimize = True) #equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) + deltaXiXaTxAddend = np.moveaxis(deltaXiXaTxAddend, 1, 0) + deltaXiXaTxAddend = np.cumsum(deltaXiXaTxAddend[::-1], axis = 0)[::-1] + self._II_deltaxi_xaTx[:,ir] = np.einsum('ii->i', deltaXiXaTxAddend, optimize = True) + + + self._II_deltaxi_Tx *= np.array([_coeffTx_units]).T**2 + self._II_deltaxi_xaTx *= np.array([coeffzp1xa * _coeffTx_units]).T + + + if (User_Parameters.FLAG_DO_DENS_NL): + D_coeffR2Tx = coeffR2Tx.reshape(1, *coeffR2Tx.shape, 1) + D_coeffzp2Tx = coeffzp2Tx.flatten().reshape(1, *coeffzp2Tx.flatten().shape, 1) + DDgammaR2 = np.copy(DDgammaR1) + D_gammaR2 = DDgammaR2.reshape(1, *DDgammaR2.shape , 1) + D_growthRmatrix = growthRmatrix[:,0].reshape(*growthRmatrix[:,0].shape, 1, 1, 1) + D_corrdNL = corrdNLBIG.squeeze()[0].reshape(1, 1, *corrdNLBIG.squeeze()[0].shape) + + if Astro_Parameters.quadratic_SFRD_lognormal: + + DDsigmaR2 = np.copy(DDsigmaR1) + D_sigmaR2 = DDsigmaR2.reshape(1, *DDsigmaR2.shape , 1) + DDgammaR2N = np.copy(DDgammaR1N) + D_gammaR2N = DDgammaR2N.reshape(1, *DDgammaR2N.shape , 1) + + gammaTimesCorrdNL = ne.evaluate('D_gammaR2 * D_growthRmatrix* D_growthRmatrix * D_corrdNL') + numerator_NL = ne.evaluate('gammaTimesCorrdNL+ D_gammaR2 * D_gammaR2 * D_sigmaR2* D_sigmaR2 /2 + D_gammaR2N * D_growthRmatrix* D_growthRmatrix* D_growthRmatrix* D_growthRmatrix * (D_corrdNL * D_corrdNL)') + + denominator_NL = ne.evaluate('1. - 2 * D_gammaR2N*D_sigmaR2*D_sigmaR2') + + norm2 = ne.evaluate('exp(D_gammaR2 * D_gammaR2 * D_sigmaR2* D_sigmaR2 * D_gammaR2 * D_gammaR2 * D_sigmaR2* D_sigmaR2 / (2 - 4 * D_gammaR2N*D_sigmaR2*D_sigmaR2)) / sqrt(1 - 2 * D_gammaR2N*D_sigmaR2*D_sigmaR2)') + + log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm2)') + nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') + + self._II_deltaxi_dTx = D_coeffzp2Tx * np.sum(D_coeffR2Tx * (nonlinearcorrelation -1 -D_gammaR2*D_growthRmatrix**2 *D_corrdNL/(1-2.*D_gammaR2N*D_sigmaR2**2)), axis = 2) + + else: + self._II_deltaxi_dTx = D_coeffzp2Tx * np.sum(D_coeffR2Tx * ((np.exp(D_gammaR2 * D_growthRmatrix**2 * D_corrdNL)-1.0) - D_gammaR2 * D_growthRmatrix**2 * D_corrdNL), axis = 2) + + + self._II_deltaxi_dTx = np.moveaxis(self._II_deltaxi_dTx, 1, 0) + self._II_deltaxi_dTx = np.cumsum(self._II_deltaxi_dTx[::-1], axis = 0)[::-1] + self._II_deltaxi_dTx = np.moveaxis(self._II_deltaxi_dTx, 1, 0) + self._II_deltaxi_dTx = np.einsum('iik->ik', self._II_deltaxi_dTx, optimize = True) + self._II_deltaxi_dTx *= np.array([_coeffTx_units]).T + + return 1 + + def get_all_corrs_IIxIII(self, Cosmo_Parameters, T21_coefficients): + """ + Returns the Pop IIxIII cross-correlation function of all observables at each z in zintegral + """ + #HAC: I deleted the bubbles and EoR part, to be done later..... + + + corrdNL = self._corrdNL + + + _coeffTx_units_II = T21_coefficients.coeff_Gammah_Tx_II #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor + _coeffTx_units_III = T21_coefficients.coeff_Gammah_Tx_III #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor + + growthRmatrix = cosmology.growth(Cosmo_Parameters,self._zGreaterMatrix100[:, self._iRnonlinear]) + gammaR1_II = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] * growthRmatrix + gammaR1_III = T21_coefficients.gamma_III_index2D[:, self._iRnonlinear] * growthRmatrix + + coeffzp1xa = T21_coefficients.coeff1LyAzp * T21_coefficients.coeff_Ja_xa + coeffzp1Tx = T21_coefficients.coeff1Xzp + + coeffR1xa_II = T21_coefficients.coeff2LyAzpRR_II[:,self._iRnonlinear] + coeffR1xa_III = T21_coefficients.coeff2LyAzpRR_III[:,self._iRnonlinear] + + coeffR1Tx_II = T21_coefficients.coeff2XzpRR_II[:,self._iRnonlinear] + coeffR1Tx_III = T21_coefficients.coeff2XzpRR_III[:,self._iRnonlinear] + + gammamatrix_R1II_R1III = gammaR1_II.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * gammaR1_III.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + coeffmatrixxa_R1II_R1III = coeffR1xa_II.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * coeffR1xa_III.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + + gammaTimesCorrdNL = ne.evaluate('gammamatrix_R1II_R1III * corrdNL') #np.einsum('ijkl,ijkl->ijkl', gammamatrix_R1II_R1III, corrdNL, optimize = True) #same thing as gammamatrixR1R1 * corrdNL but faster + expGammaCorrMinusLinear = ne.evaluate('exp(gammaTimesCorrdNL) - 1 - gammaTimesCorrdNL') + + self._IIxIII_deltaxi_xa = 2 * np.einsum('ijkl->il', coeffmatrixxa_R1II_R1III * expGammaCorrMinusLinear, optimize = True) #factor of 2 to account for cross-term + self._IIxIII_deltaxi_xa *= np.array([coeffzp1xa]).T**2 #brings it to xa units + + ###No density cross-term because density by itself doesn't have a Pop II + III contribution; the xa and Tx contribution is already accounted for in the Pop II- and Pop III-only get_all_corrs + + ### To compute Tx quantities, I'm broadcasting arrays such that the axes are zp1, R1, zp2, R2, r + + gammaR2_II = np.copy(gammaR1_II) #already has growth factor in this + gammaR2_III = np.copy(gammaR1_III) #already has growth factor in this + + gammamatrix_R1II_R2III = gammaR1_II.reshape(*gammaR1_II.shape, 1, 1) * gammaR2_III.reshape(1, 1, *gammaR2_III.shape) + gammamatrix_R1III_R2II = gammaR1_III.reshape(*gammaR1_III.shape, 1, 1) * gammaR2_II.reshape(1, 1, *gammaR2_II.shape) + + coeffzp1Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(*T21_coefficients.coeff1Xzp.shape, 1, 1, 1) + coeffzp2Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(1, 1, *T21_coefficients.coeff1Xzp.shape, 1) + + coeffR2Tx_II = np.copy(coeffR1Tx_II) + coeffR2Tx_III = np.copy(coeffR1Tx_III) + + coeffmatrixTxTx_R1II_R2III = coeffR1Tx_II.reshape(*coeffR1Tx_II.shape, 1, 1) * coeffR2Tx_III.reshape(1, 1, *coeffR2Tx_III.shape) + coeffmatrixTxTx_R1III_R2II = coeffR1Tx_III.reshape(*coeffR1Tx_III.shape, 1, 1) * coeffR2Tx_II.reshape(1, 1, *coeffR2Tx_II.shape) + + coeffmatrixxaTx_R1II_R2III = coeffR1xa_II.reshape(*coeffR1xa_II.shape, 1, 1) * coeffR2Tx_III.reshape(1, 1, *coeffR2Tx_III.shape) + coeffmatrixxaTx_R1III_R2II = coeffR1xa_III.reshape(*coeffR1xa_III.shape, 1, 1) * coeffR2Tx_II.reshape(1, 1, *coeffR2Tx_II.shape) + + coeffsTxALL_R1II_R2III = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx_R1II_R2III + coeffsTxALL_R1III_R2II = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx_R1III_R2II + coeffsXaTxALL_R1II_R2III = coeffzp2Tx * coeffmatrixxaTx_R1II_R2III + coeffsXaTxALL_R1III_R2II = coeffzp2Tx * coeffmatrixxaTx_R1III_R2II + + self._IIxIII_deltaxi_Tx = np.zeros_like(self._IIxIII_deltaxi_xa) + _IIxIII_deltaxi_xaTx1 = np.zeros_like(self._IIxIII_deltaxi_xa) + _IIxIII_deltaxi_xaTx2 = np.zeros_like(self._IIxIII_deltaxi_xa) + corrdNLBIG = corrdNL[:,:, np.newaxis, :,:] #dimensions zp1, R1, zp2, R2, and r, the last of which will be looped over below + + for ir in range(len(Cosmo_Parameters._Rtabsmoo)): + corrdNL = corrdNLBIG[:,:,:,:,ir] + + #HAC: Computations using ne.evaluate(...) use numexpr, which speeds up computations of massive numpy arrays + + gamma_R1II_R2III_CorrdNL = ne.evaluate('gammamatrix_R1II_R2III * corrdNL') + expGamma_R1II_R2III_CorrdNL = ne.evaluate('exp(gamma_R1II_R2III_CorrdNL) - 1 - gamma_R1II_R2III_CorrdNL') + + gamma_R1III_R2II_CorrdNL = ne.evaluate('gammamatrix_R1III_R2II * corrdNL') + expGamma_R1III_R2II_CorrdNL = ne.evaluate('exp(gammamatrix_R1III_R2II * corrdNL) - 1 - gammamatrix_R1III_R2II * corrdNL') + + deltaXiTxAddend = ne.evaluate('coeffsTxALL_R1II_R2III * expGamma_R1II_R2III_CorrdNL + coeffsTxALL_R1III_R2II * expGamma_R1III_R2II_CorrdNL') + deltaXiTxAddend = np.einsum('ijkl->ik', deltaXiTxAddend, optimize = True)# equivalent to np.sum(deltaXiTxAddend, axis = (1, 3)) + deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] + deltaXiTxAddend = np.moveaxis(deltaXiTxAddend, 1, 0) + deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] + self._IIxIII_deltaxi_Tx[:,ir] = np.einsum('ii->i', deltaXiTxAddend, optimize = True) + + #Tx in R2 uses Pop III quantities + deltaXiXaTxAddend1 = ne.evaluate('coeffsXaTxALL_R1II_R2III * expGamma_R1II_R2III_CorrdNL') + deltaXiXaTxAddend1 = np.einsum('ijkl->ik', deltaXiXaTxAddend1, optimize = True) # equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) + deltaXiXaTxAddend1 = np.moveaxis(deltaXiXaTxAddend1, 1, 0) + deltaXiXaTxAddend1 = np.cumsum(deltaXiXaTxAddend1[::-1], axis = 0)[::-1] + _IIxIII_deltaxi_xaTx1[:, ir] = np.einsum('ii->i', deltaXiXaTxAddend1, optimize = True) + + #Tx in R2 uses Pop II quantities + deltaXiXaTxAddend2 = ne.evaluate('coeffsXaTxALL_R1III_R2II * expGamma_R1III_R2II_CorrdNL') + deltaXiXaTxAddend2 = np.einsum('ijkl->ik', deltaXiXaTxAddend2, optimize = True) # equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) + deltaXiXaTxAddend2 = np.moveaxis(deltaXiXaTxAddend2, 1, 0) + deltaXiXaTxAddend2 = np.cumsum(deltaXiXaTxAddend2[::-1], axis = 0)[::-1] + _IIxIII_deltaxi_xaTx2[:, ir] = np.einsum('ii->i', deltaXiXaTxAddend2, optimize = True) + + self._IIxIII_deltaxi_Tx *= np.array([_coeffTx_units_II * _coeffTx_units_III]).T + + _IIxIII_deltaxi_xaTx1 *= np.array([coeffzp1xa * _coeffTx_units_III]).T + _IIxIII_deltaxi_xaTx2 *= np.array([coeffzp1xa * _coeffTx_units_II]).T + self._IIxIII_deltaxi_xaTx = _IIxIII_deltaxi_xaTx1 + _IIxIII_deltaxi_xaTx2 + + return 1 + + + # === BEGIN EDIT (par/perp eta split): generalized signature. + # Original signature was get_xi_Sum_2ExpEta(self, xiEta, etaCoeff1, etaCoeff2) + # using the isotropic factor (1 - K*xiEta)**(3/2). The new signature accepts + # both modes; pass xiEtaperp=None for the legacy isotropic calculation. === + def get_xi_Sum_2ExpEta(self, xiEtapar, xiEtaperp, etaCoeff1, etaCoeff2): + """ + Computes the correlation function of the VCB portion of the SFRD, expressed using sums of two exponentials + if rho(z1, x1) / rhobar = Ae^-b tilde(eta) + Ce^-d tilde(eta) and rho(z2, x2) / rhobar = Fe^-g tilde(eta) + He^-k tilde(eta) + Then this computes - + Refer to eq. A12 in 2407.18294 for more details + + Two computational modes are supported via the second argument: + + * ``xiEtaperp is None`` (legacy / isotropic): + The first argument ``xiEtapar`` is treated as the single + isotropic eta correlation function ``xiEta``, and the + original four-term factor ``(1 - K * xiEta)**(3/2)`` is + used for each term. + + * ``xiEtaperp is not None`` (parallel/perpendicular split): + The first argument is the parallel eta correlation function + and the second is the perpendicular one. Each + ``(1 - K * xiEta)**(3/2)`` factor is replaced by + ``((1 - K * xiEtapar) * (1 - K * xiEtaperp)**2)**(1/2)`` + with K = 6*K1*K2/((1+2*K1)*(1+2*K2)). + + Callers should select exactly one mode and pass the matching + correlation function(s); the alternative array(s) need not (and + should not) be constructed. + """ + + aa, bb, cc, dd = etaCoeff1 + ff, gg, hh, kk = etaCoeff2 + + normBB = ne.evaluate('(1+2*bb)**(3/2)') + normGG = ne.evaluate('(1+2*gg)**(3/2)') + normDD = ne.evaluate('(1+2*dd)**(3/2)') + normKK = ne.evaluate('(1+2*kk)**(3/2)') + + afBG = ne.evaluate('aa * ff / normBB / normGG') + ahBK = ne.evaluate('aa * hh / normBB / normKK') + cfDG = ne.evaluate('cc * ff / normDD / normGG') + chDK = ne.evaluate('cc * hh / normDD / normKK') + + # --- EDIT (par/perp eta split): mode dispatch on xiEtaperp --- + if xiEtaperp is None: + # Legacy isotropic eta correlation: single xiEta argument. + xiEta = xiEtapar + xiNumerator = ne.evaluate('afBG * (1 / (1 - 6*bb * gg * xiEta / ((1+2*bb)*(1+2*gg)))**(3/2) - 1) + ahBK * (1 / (1 - 6*bb * kk * xiEta / ((1+2*bb)*(1+2*kk)))**(3/2) - 1) + cfDG * (1 / (1 - 6*dd * gg * xiEta / ((1+2*dd)*(1+2*gg)))**(3/2) - 1) + chDK * (1 / (1 - 6*dd * kk * xiEta / ((1+2*dd)*(1+2*kk)))**(3/2) - 1)') + else: + # Parallel/perpendicular split: + # (1 - K * xi)**(3/2) -> ((1 - K * xipar) * (1 - K * xiperp)**2)**(1/2) + xiNumerator = ne.evaluate( + 'afBG * (1 / ((1 - 6*bb*gg*xiEtapar/((1+2*bb)*(1+2*gg))) * (1 - 6*bb*gg*xiEtaperp/((1+2*bb)*(1+2*gg)))**2)**(1/2) - 1)' + ' + ahBK * (1 / ((1 - 6*bb*kk*xiEtapar/((1+2*bb)*(1+2*kk))) * (1 - 6*bb*kk*xiEtaperp/((1+2*bb)*(1+2*kk)))**2)**(1/2) - 1)' + ' + cfDG * (1 / ((1 - 6*dd*gg*xiEtapar/((1+2*dd)*(1+2*gg))) * (1 - 6*dd*gg*xiEtaperp/((1+2*dd)*(1+2*gg)))**2)**(1/2) - 1)' + ' + chDK * (1 / ((1 - 6*dd*kk*xiEtapar/((1+2*dd)*(1+2*kk))) * (1 - 6*dd*kk*xiEtaperp/((1+2*dd)*(1+2*kk)))**2)**(1/2) - 1)' + ) + # --- end EDIT (par/perp eta split) --- + + xiDenominator = ne.evaluate('afBG + ahBK + cfDG + chDK') + + xiTotal = ne.evaluate('xiNumerator / xiDenominator') + + return xiTotal + # === END EDIT (par/perp eta split): get_xi_Sum_2ExpEta === + + + def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients): + "Returns the Pop III components of the correlation functions of all observables at each z in zintegral" + #HAC: I deleted the bubbles and EoR part, to be done later..... + + + corrdNL = self._corrdNL + + # === BEGIN EDIT (par/perp eta split): runtime dispatch. + # Switch between legacy isotropic eta correlation and the + # parallel/perpendicular split. Default False (legacy) so existing + # behavior is preserved if the flag is absent on Cosmo_Parameters. + # The code computes EITHER xiEta OR (xiEtapar, xiEtaperp), never both. === + USE_ETA_PARPERP_SPLIT = getattr(Cosmo_Parameters, 'USE_ETA_PARPERP_SPLIT', False) + + # Build EITHER the isotropic eta CF matrix OR the par/perp pair, + # but not both, so the unused branch never allocates memory. + if USE_ETA_PARPERP_SPLIT: + corrEtaNLpar = Cosmo_Parameters.xiEtaPar_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] + corrEtaNLpar[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNLpar[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] + corrEtaNLpar = corrEtaNLpar.reshape(1, *corrEtaNLpar.shape) + + corrEtaNLperp = Cosmo_Parameters.xiEtaPerp_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] + corrEtaNLperp[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNLperp[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] + corrEtaNLperp = corrEtaNLperp.reshape(1, *corrEtaNLperp.shape) + else: + corrEtaNL = Cosmo_Parameters.xiEta_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] + corrEtaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] + corrEtaNL = corrEtaNL.reshape(1, *corrEtaNL.shape) + # === END EDIT (par/perp eta split): eta-CF matrix construction === + + + _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_III #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor + + growthRmatrix = cosmology.growth(Cosmo_Parameters,self._zGreaterMatrix100[:, self._iRnonlinear]) + gammaR1 = T21_coefficients.gamma_III_index2D[:, self._iRnonlinear] * growthRmatrix + + vcbCoeffs1 = T21_coefficients.vcb_expFitParams[:, self._iRnonlinear] + vcbCoeffsR1 = np.transpose(vcbCoeffs1, (2, 0, 1)) + vcbCoeffsR1 = vcbCoeffsR1[:,:,:,np.newaxis,np.newaxis] + vcbCoeffsR2 = np.moveaxis(vcbCoeffsR1, 3, 2) + + coeffzp1xa = T21_coefficients.coeff1LyAzp * T21_coefficients.coeff_Ja_xa + coeffzp1Tx = T21_coefficients.coeff1Xzp + + coeffR1xa = T21_coefficients.coeff2LyAzpRR_III[:,self._iRnonlinear] + coeffR1Tx = T21_coefficients.coeff2XzpRR_III[:,self._iRnonlinear] + + gammamatrixR1R1 = gammaR1.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * gammaR1.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + coeffmatrixxa = coeffR1xa.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * coeffR1xa.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) + + gammaCorrdNL = ne.evaluate('gammamatrixR1R1 * corrdNL') #np.einsum('ijkl,ijkl->ijkl', gammamatrixR1R1, corrdNL, optimize = True) #same thing as gammamatrixR1R1 * corrdNL but faster + expGammaCorr = ne.evaluate('exp(gammaCorrdNL) - 1') # equivalent to np.exp(gammaTimesCorrdNL)-1.0 + + if Cosmo_Parameters.USE_RELATIVE_VELOCITIES == True: + # --- EDIT (par/perp eta split): mode dispatch for xa term --- + if USE_ETA_PARPERP_SPLIT: + etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaNLpar, corrEtaNLperp, vcbCoeffsR1, vcbCoeffsR2) + else: + etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaNL, None, vcbCoeffsR1, vcbCoeffsR2) + # --- end EDIT --- + totalCorr = ne.evaluate('expGammaCorr * etaCorr_xa + expGammaCorr + etaCorr_xa - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order + else: + totalCorr = ne.evaluate('expGammaCorr - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order + + self._III_deltaxi_xa = np.einsum('ijkl->il', coeffmatrixxa * totalCorr , optimize = True) # equivalent to self._III_deltaxi_xa = np.sum(coeffmatrixxa * ((np.exp(gammaTimesCorrdNL)-1.0) - gammaTimesCorrdNL), axis = (1,2)) + self._III_deltaxi_xa *= np.array([coeffzp1xa]).T**2 #brings it to xa units + + if (User_Parameters.FLAG_DO_DENS_NL): #no velocity contribution to density + D_coeffR1xa = coeffR1xa.reshape(*coeffR1xa.shape, 1) + D_gammaR1 = gammaR1.reshape(*gammaR1.shape , 1) + D_growthRmatrix = growthRmatrix[:,:1].reshape(*growthRmatrix[:,:1].shape, 1) + D_corrdNL = corrdNL[:1,0,:,:] + + self._III_deltaxi_dxa = np.sum(D_coeffR1xa * ((np.exp(D_gammaR1 * D_growthRmatrix * D_corrdNL )-1.0 ) - D_gammaR1 * D_growthRmatrix * D_corrdNL), axis = 1) + self._III_deltaxi_dxa *= np.array([coeffzp1xa]).T + + ### To compute Tx quantities, I'm broadcasting arrays such that the axes are zp1, R1, zp2, R2, r + + gammaR2 = np.copy(gammaR1) #already has growth factor in this + gammamatrixR1R2 = gammaR1.reshape(*gammaR1.shape, 1, 1) * gammaR2.reshape(1, 1, *gammaR2.shape) + + coeffzp1Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(*T21_coefficients.coeff1Xzp.shape, 1, 1, 1) + coeffzp2Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(1, 1, *T21_coefficients.coeff1Xzp.shape, 1) + coeffR2Tx = np.copy(coeffR1Tx) + coeffmatrixTxTx = coeffR1Tx.reshape(*coeffR1Tx.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) + coeffmatrixxaTx = coeffR1xa.reshape(*coeffR1xa.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) + coeffsTxALL = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx + coeffsXaTxALL = coeffzp2Tx * coeffmatrixxaTx + + corrdNLBIG = corrdNL[:,:, np.newaxis, :, :] + # --- EDIT (par/perp eta split): expand only the eta arrays we built --- + if USE_ETA_PARPERP_SPLIT: + corrEtaNLparBIG = corrEtaNLpar[:,:, np.newaxis, :, :] + corrEtaNLperpBIG = corrEtaNLperp[:,:, np.newaxis, :, :] + else: + corrEtaNLBIG = corrEtaNL[:,:, np.newaxis, :, :] + # --- end EDIT --- + + vcbCoeffsR1 = vcbCoeffsR1[:,:,:,:,:] + vcbCoeffsR2 = np.transpose(vcbCoeffsR1, (0,3,4,1,2)) + + self._III_deltaxi_Tx = np.zeros_like(self._III_deltaxi_xa) + self._III_deltaxi_xaTx = np.zeros_like(self._III_deltaxi_xa) + self._III_deltaxi_dTx = np.zeros_like(self._III_deltaxi_xa) + + for ir in range(len(Cosmo_Parameters._Rtabsmoo)): + corrdNL = corrdNLBIG[:,:,:,:,ir] + # --- EDIT (par/perp eta split): per-ir slice of the active eta CF --- + if USE_ETA_PARPERP_SPLIT: + corrEtaNLpar = corrEtaNLparBIG[:,:,:,:,ir] + corrEtaNLperp = corrEtaNLperpBIG[:,:,:,:,ir] + else: + corrEtaNL = corrEtaNLBIG[:,:,:,:,ir] + # --- end EDIT --- + + gammaCorrdNL = ne.evaluate('gammamatrixR1R2 * corrdNL') + expGammaCorrdNL = ne.evaluate('exp(gammaCorrdNL) - 1') + + if Cosmo_Parameters.USE_RELATIVE_VELOCITIES == True: + # --- EDIT (par/perp eta split): mode dispatch for Tx term --- + if USE_ETA_PARPERP_SPLIT: + etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaNLpar, corrEtaNLperp, vcbCoeffsR1, vcbCoeffsR2) + else: + etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaNL, None, vcbCoeffsR1, vcbCoeffsR2) + # --- end EDIT --- + totalCorr = ne.evaluate('expGammaCorrdNL * etaCorr_Tx + expGammaCorrdNL + etaCorr_Tx - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order + else: + totalCorr = ne.evaluate('expGammaCorrdNL - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order + + deltaXiTxAddend = ne.evaluate('coeffsTxALL * totalCorr') # equivalent to np.multiply(coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx, totalCorr, out = outDummy) + deltaXiTxAddend = np.einsum('ijkl->ik', deltaXiTxAddend, optimize=True) # equivalent to np.sum(deltaXiTxAddend, axis = (1, 3)) + deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] + deltaXiTxAddend = np.moveaxis(deltaXiTxAddend, 1, 0) + deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] + self._III_deltaxi_Tx[:, ir] = np.einsum('ii->i', deltaXiTxAddend, optimize = True) + + deltaXiXaTxAddend = ne.evaluate('coeffsXaTxALL * totalCorr') # equivalent to np.multiply(coeffzp2Tx * coeffmatrixxaTx, totalCorr, out = outDummy) + deltaXiXaTxAddend = np.einsum('ijkl->ik', deltaXiXaTxAddend, optimize=True) # equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) + deltaXiXaTxAddend = np.moveaxis(deltaXiXaTxAddend, 1, 0) + deltaXiXaTxAddend = np.cumsum(deltaXiXaTxAddend[::-1], axis = 0)[::-1] + self._III_deltaxi_xaTx[:, ir] = np.einsum('ii->i', deltaXiXaTxAddend, optimize = True) + + if (User_Parameters.FLAG_DO_DENS_NL): #no velocity contribution to density + D_coeffR2Tx = coeffR2Tx.reshape(1, *coeffR2Tx.shape, 1) + D_coeffzp2Tx = coeffzp2Tx.flatten().reshape(1, *coeffzp2Tx.flatten().shape, 1) + D_gammaR2 = gammaR2.reshape(1, *gammaR2.shape , 1) + D_growthRmatrix = growthRmatrix[:,0].reshape(*growthRmatrix[:,0].shape, 1, 1, 1) + D_corrdNL = corrdNLBIG.squeeze()[0].reshape(1, 1, *corrdNLBIG.squeeze()[0].shape) + + self._III_deltaxi_dTx = D_coeffzp2Tx * np.sum(D_coeffR2Tx * ((np.exp(D_gammaR2 * D_growthRmatrix * D_corrdNL)-1.0) - D_gammaR2 * D_growthRmatrix * D_corrdNL), axis = 2) + + self._III_deltaxi_dTx = np.moveaxis(self._III_deltaxi_dTx, 1, 0) + self._III_deltaxi_dTx = np.cumsum(self._III_deltaxi_dTx[::-1], axis = 0)[::-1] + self._III_deltaxi_dTx = np.moveaxis(self._III_deltaxi_dTx, 1, 0) + self._III_deltaxi_dTx = np.einsum('iik->ik', self._III_deltaxi_dTx, optimize = True) + self._III_deltaxi_dTx *= np.array([_coeffTx_units]).T + + self._III_deltaxi_Tx *= np.array([_coeffTx_units]).T**2 + self._III_deltaxi_xaTx *= np.array([coeffzp1xa * _coeffTx_units]).T + + return 1 + + + def get_list_PS(self, xi_list, zlisttoconvert): + "Returns the power spectrum given a list of CFs (xi_list) evaluated at z=zlisttoconvert as input" + + _Pk_list = [] + + for izp,zp in enumerate(zlisttoconvert): + + _kzp, _Pkzp = self.get_Pk_from_xi(self._rs_input_mcfit,xi_list[izp]) + _Pk_list.append(_Pkzp) + #can ignore _kzp, it's the same as klist_PS above by construction + + + return np.array(_Pk_list) + + + def get_Pk_from_xi(self, rsinput, xiinput): + "Generic Fourier Transform, returns Pk from an input Corr Func xi. kPf should be the same as _klistCF" + + kPf, Pf = mcfit.xi2P(rsinput, l=0, lowring=True)(xiinput, extrap=False) + + return kPf, Pf \ No newline at end of file diff --git a/zeus21/cosmology_v2.py b/zeus21/cosmology_v2.py new file mode 100644 index 0000000..55bb71d --- /dev/null +++ b/zeus21/cosmology_v2.py @@ -0,0 +1,536 @@ +""" + +Cosmology helper functions and other tools + +Author: Julian B. Muñoz +UT Austin and Harvard CfA - January 2023 + +Edited by Hector Afonso G. Cruz +JHU - July 2024 + +Edited by Emilie Thelie +UT Austin - April 2026 +""" + +import numpy as np +# === BEGIN EDIT (par/perp eta split): added imports needed by runclass below === +from classy import Class +from scipy.interpolate import RegularGridInterpolator +from scipy.interpolate import interp1d + +import mcfit +# === END EDIT (par/perp eta split) === + +from . import constants +# === BEGIN EDIT (cosmology_v2: par/perp eta split) === +# Use the patched Cosmo_Parameters from inputs_v2, which honors +# USE_ETA_PARPERP_SPLIT in both runclass() and run_correlations(). +from .inputs_v2 import Cosmo_Parameters +# === END EDIT (cosmology_v2: par/perp eta split) === + +# === BEGIN EDIT (cosmology_v2: par/perp eta split) === +# Rewritten cosmo_wrapper. The previous version imported a non-existent +# `Correlations` class from zeus21.correlations[_v2] and instantiated +# Cosmo_Parameters with the wrong arg count, so it could never run. +# +# This version delegates all CLASS + correlation-function setup to +# inputs_v2.Cosmo_Parameters.__post_init__, which now: +# * dispatches in runclass() between legacy `k_eta`/`P_eta` and the +# split `k_etapar`/`k_etaperp` keys based on USE_ETA_PARPERP_SPLIT; +# * dispatches in run_correlations() between building xiEta_RR_CF and +# building (xiEtaPar_RR_CF, xiEtaPerp_RR_CF). +# +# `Cosmo_Parameters_Input` is treated as a duck-typed object: any of its +# attributes that match a Cosmo_Parameters field are forwarded as kwargs. +# Callers can therefore pass a SimpleNamespace, a dataclass, or a custom +# class. +# +# Returns (CosmoParams, ClassCosmo, HMFintclass). +_COSMO_PARAM_FIELDS = ( + 'omegab', 'omegac', 'h_fid', 'As', 'ns', 'tau_fid', + 'kmax_CLASS', 'zmax_CLASS', 'zmin_CLASS', + 'Rs_min', 'Rs_max', + 'Flag_emulate_21cmfast', 'USE_RELATIVE_VELOCITIES', + 'USE_ETA_PARPERP_SPLIT', 'HMF_CHOICE', +) + +def cosmo_wrapper(User_Parameters, Cosmo_Parameters_Input): + """ + Wrapper that instantiates the full cosmology pipeline for the v2 + code path. Returns (Cosmo_Parameters, ClassCosmo, HMF_interpolator). + + Parameters + ---------- + User_Parameters : zeus21.User_Parameters + Cosmo_Parameters_Input : object + Any object whose attributes are a subset of the Cosmo_Parameters + fields (omegab, omegac, ..., USE_RELATIVE_VELOCITIES, + USE_ETA_PARPERP_SPLIT, HMF_CHOICE). A types.SimpleNamespace or + a small dataclass works fine. + + The boolean ``Cosmo_Parameters_Input.USE_ETA_PARPERP_SPLIT`` selects: + * False (default): legacy isotropic eta correlation function. + * True: parallel/perpendicular split (xiEtaPar_RR_CF and + xiEtaPerp_RR_CF are populated; xiEta_RR_CF is NOT). + """ + kwargs = { + name: getattr(Cosmo_Parameters_Input, name) + for name in _COSMO_PARAM_FIELDS + if hasattr(Cosmo_Parameters_Input, name) + } + + CosmoParams = Cosmo_Parameters(UserParams=User_Parameters, **kwargs) + ClassCosmo = CosmoParams.ClassCosmo + + HMFintclass = HMF_interpolator(User_Parameters, CosmoParams) + + return CosmoParams, ClassCosmo, HMFintclass +# === END EDIT (cosmology_v2: par/perp eta split): cosmo_wrapper === + + +# === BEGIN EDIT (par/perp eta split): new runclass() supporting two +# mutually exclusive modes for the v_cb^2 ("eta") correlation function, +# selected by CosmologyIn.USE_ETA_PARPERP_SPLIT (default False = legacy). +# Only the keys for the selected mode are written to ClassCosmo.pars, +# so a stale consumer of the other mode fails loudly. === +def runclass(CosmologyIn): + """ + Set up CLASS cosmology. Takes CosmologyIn class input and returns CLASS Cosmology object. + + Two distinct treatments of the v_cb^2 ("eta") correlation function are + supported, selected by the boolean attribute + ``CosmologyIn.USE_ETA_PARPERP_SPLIT`` (default False if absent): + + * False (legacy): + Stores a single isotropic eta power spectrum on + ``ClassCosmo.pars`` under keys ``k_eta`` and ``P_eta``, + built from the integrand ``6*psi0**2 + 3*psi2**2``. + + * True (parallel/perpendicular split): + Stores two eta power spectra on ``ClassCosmo.pars`` under + keys ``k_etapar``/``P_etapar`` and ``k_etaperp``/``P_etaperp``, + built from the integrands ``6*(psi0 + psi2)**2`` and + ``6*(psi0 - psi2/2)**2`` respectively. These feed the + modified four-term factor in + ``Power_Spectra.get_xi_Sum_2ExpEta``: + ((1 - K*xiEtapar) * (1 - K*xiEtaperp)**2)**(1/2) + in place of the legacy (1 - K*xiEta)**(3/2). + + The two modes are mutually exclusive: only the keys for the selected + mode are set, so a stale consumer of the other mode fails loudly. + """ + USE_ETA_PARPERP_SPLIT = getattr(CosmologyIn, 'USE_ETA_PARPERP_SPLIT', False) + + ClassCosmo = Class() + ClassCosmo.set({'omega_b': CosmologyIn.omegab,'omega_cdm': CosmologyIn.omegac, + 'h': CosmologyIn.h_fid,'A_s': CosmologyIn.As,'n_s': CosmologyIn.ns,'tau_reio': CosmologyIn.tau_fid}) + ClassCosmo.set({'output':'mPk','lensing':'no','P_k_max_1/Mpc':CosmologyIn.kmax_CLASS, 'z_max_pk': CosmologyIn.zmax_CLASS}) + ClassCosmo.set({'gauge':'synchronous'}) + + ClassCosmo.compute() + + ClassCosmo.pars['Flag_emulate_21cmfast'] = CosmologyIn.Flag_emulate_21cmfast + ClassCosmo.pars['USE_ETA_PARPERP_SPLIT'] = USE_ETA_PARPERP_SPLIT + + if CosmologyIn.USE_RELATIVE_VELOCITIES == True: + + kMAX_VCB = 50.0 + z_rec = ClassCosmo.get_current_derived_parameters(['z_rec'])['z_rec'] + z_drag = ClassCosmo.get_current_derived_parameters(['z_d'])['z_d'] + + ClassCosmoVCB = Class() + ClassCosmoVCB.set({'omega_b': CosmologyIn.omegab,'omega_cdm': CosmologyIn.omegac, + 'h': CosmologyIn.h_fid,'A_s': CosmologyIn.As,'n_s': CosmologyIn.ns,'tau_reio': CosmologyIn.tau_fid}) + ClassCosmoVCB.set({'output':'vTk'}) + ClassCosmoVCB.set({'P_k_max_1/Mpc':kMAX_VCB, 'z_max_pk':12000}) + ClassCosmoVCB.set({'gauge':'newtonian'}) + ClassCosmoVCB.compute() + velTransFunc = ClassCosmoVCB.get_transfer(z_drag) + + kVel = velTransFunc['k (h/Mpc)'] * CosmologyIn.h_fid + theta_b = velTransFunc['t_b'] + theta_c = velTransFunc['t_cdm'] + + sigma_vcb = np.sqrt(np.trapz(CosmologyIn.As * (kVel/0.05)**(CosmologyIn.ns-1) /kVel * (theta_b - theta_c)**2/kVel**2, kVel)) * constants.c_kms + ClassCosmo.pars['sigma_vcb'] = sigma_vcb + + velArr = np.geomspace(0.01, constants.c_kms, 1000) + vavgIntegrand = (3 / (2 * np.pi * sigma_vcb**2))**(3/2) * 4 * np.pi * velArr**2 * np.exp(-3 * velArr**2 / (2 * sigma_vcb**2)) + ClassCosmo.pars['v_avg'] = np.trapz(vavgIntegrand * velArr, velArr) + + ClassCosmo.pars['k_vcb'] = kVel + ClassCosmo.pars['theta_b'] = theta_b + ClassCosmo.pars['theta_c'] = theta_c + P_vcb = CosmologyIn.As * (kVel/0.05)**(CosmologyIn.ns-1) * (theta_b - theta_c)**2/kVel**2 * 2 * np.pi**2 / kVel**3 + + p_vcb_intp = interp1d(np.log(kVel), P_vcb) + ClassCosmo.pars['P_vcb'] = P_vcb + + kVelIntp = np.geomspace(1e-4, kMAX_VCB, 512) + rVelIntp = 2 * np.pi / kVelIntp + + j0bessel = lambda x: np.sin(x)/x + j2bessel = lambda x: (3 / x**2 - 1) * np.sin(x)/x - 3*np.cos(x)/x**2 + + psi0 = 1 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapz(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j0bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) + psi2 = -2 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapz(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j2bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) + + # === BEGIN EDIT (par/perp eta split): exactly one of the two + # branches below executes. Each writes ONLY the keys that match + # its mode so consumers cannot silently mix conventions. === + if USE_ETA_PARPERP_SPLIT: + # Parallel/perpendicular split: store ONLY the par and perp keys. + k_etapar, P_etapar = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 + psi2)**2, extrap = False) + k_etaperp, P_etaperp = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 - psi2/2.0)**2, extrap = False) + + ClassCosmo.pars['k_etapar'] = k_etapar[P_etapar > 0] + ClassCosmo.pars['P_etapar'] = P_etapar[P_etapar > 0] + ClassCosmo.pars['k_etaperp'] = k_etaperp[P_etaperp > 0] + ClassCosmo.pars['P_etaperp'] = P_etaperp[P_etaperp > 0] + # NOTE: 'k_eta'/'P_eta' intentionally NOT set so a stale + # isotropic consumer fails loudly rather than using wrong CF. + else: + # Legacy isotropic eta: store ONLY the single-key version. + k_eta, P_eta = mcfit.xi2P(rVelIntp, l=0, lowring = True)((6 * psi0**2 + 3 * psi2**2), extrap = False) + ClassCosmo.pars['k_eta'] = k_eta[P_eta > 0] + ClassCosmo.pars['P_eta'] = P_eta[P_eta > 0] + # === END EDIT (par/perp eta split) === + + else: + ClassCosmo.pars['v_avg'] = 0.0 + ClassCosmo.pars['sigma_vcb'] = 1.0 + + return ClassCosmo +# === END EDIT (par/perp eta split): end of new runclass() === + + + + +def time_at_redshift(ClassyCosmo,z): + """ + Returns the age of the Universe (in Gyrs) corresponding to a given redshift. + + Parameters + ---------- + ClassyCosmo: zeus21.runclass class + Sets up Class cosmology. + z: float + Redshift. + """ + background = ClassyCosmo.get_background() + classy_t, classy_z = background['proper time [Gyr]'], background['z'] + classy_tinterp = interp1d(classy_z, classy_t) + return classy_tinterp(z) + +def redshift_at_time(ClassyCosmo,t): + """ + Returns the redshift corresponding to a given age of the Universe (in Gyrs). + + Parameters + ---------- + ClassyCosmo: zeus21.runclass class + Sets up Class cosmology. + t: float + Age in Gyrs. + """ + background = ClassyCosmo.get_background() + classy_t, classy_z = background['proper time [Gyr]'], background['z'] + classy_tinterp = interp1d(classy_t, classy_z) + return classy_tinterp(t) + +def Hub(Cosmo_Parameters, z): +#Hubble(z) in km/s/Mpc + return Cosmo_Parameters.h_fid * 100 * np.sqrt(Cosmo_Parameters.OmegaM * pow(1+z,3.)+Cosmo_Parameters.OmegaR * pow(1+z,4.)+Cosmo_Parameters.OmegaL) + +def HubinvMpc(Cosmo_Parameters, z): +#H(z) in 1/Mpc + return Hub(Cosmo_Parameters,z)/constants.c_kms + +def Hubinvyr(Cosmo_Parameters,z): +#H(z) in 1/yr + return Hub(Cosmo_Parameters,z)*constants.KmToMpc*constants.yrTos + +def rho_baryon(Cosmo_Parameters,z): +#\rho_baryon in Msun/Mpc^3 as a function of z + return Cosmo_Parameters.OmegaB * Cosmo_Parameters.rhocrit * pow(1+z,3.0) + +def n_H(Cosmo_Parameters, z): +#density of hydrogen nuclei (neutral or ionized) in 1/cm^3 + return rho_baryon(Cosmo_Parameters, z) *( 1- Cosmo_Parameters.Y_He)/(constants.mH_GeV/constants.MsuntoGeV) / (constants.Mpctocm**3.0) + +#def n_baryon(Cosmo_Parameters, z): +##density of baryons in 1/cm^3 +# return rho_baryon(Cosmo_Parameters, z) / Cosmo_Parameters.mu_baryon_Msun / (constants.Mpctocm**3.0) + + + +def Tcmb(ClassCosmo, z): + T0CMB = ClassCosmo.T_cmb() + return T0CMB*(1+z) + +def Tadiabatic(CosmoParams, z): + "Returns T_adiabatic as a function of z from thermodynamics in CLASS" + return CosmoParams.Tadiabaticint(z) +def xefid(CosmoParams, z): + "Returns fiducial x_e(z) w/o any sources. Uses thermodynamics in CLASS for z>15, and fixed below to avoid the tanh approx." + _zcutCLASSxe = 15. + _xecutCLASSxe = CosmoParams.xetanhint(_zcutCLASSxe) + return CosmoParams.xetanhint(z) * np.heaviside(z - _zcutCLASSxe, 0.5) + _xecutCLASSxe * np.heaviside(_zcutCLASSxe - z, 0.5) + +def adiabatic_index(z): + "Returns adiabatic index (delta_Tad/delta) as a function of z. Fit from 1506.04152. to ~3% on z = 6 − 50)." + return 0.58 - 0.005*(z-10.) + + +def MhofRad(Cosmo_Parameters,R): + #convert input Radius in Mpc comoving to Mass in Msun + return Cosmo_Parameters.constRM *pow(R, 3.0) + +def RadofMh(Cosmo_Parameters,M): + #convert input M halo in Msun radius in cMpc + return pow(M/Cosmo_Parameters.constRM, 1/3.0) + + + +def ST_HMF(Cosmo_Parameters, Mass, sigmaM, dsigmadM): + A_ST = Cosmo_Parameters.Amp_ST + a_ST = Cosmo_Parameters.a_ST + p_ST = Cosmo_Parameters.p_ST + delta_crit_ST = Cosmo_Parameters.delta_crit_ST + + nutilde = np.sqrt(a_ST) * delta_crit_ST/sigmaM + + return -A_ST * np.sqrt(2./np.pi) * nutilde * (1. + nutilde**(-2.0*p_ST)) * np.exp(-nutilde**2/2.0) * (Cosmo_Parameters.rho_M0 / (Mass * sigmaM)) * dsigmadM + + +def Tink_HMF(Cosmo_Parameters, Mass, sigmaM, dsigmadM,z): + #Tinker08 form of the HMF. All in physical (no h) units. Form from App.A of Yung+23 (2309.14408) + f = f_GUREFT_physical(sigmaM,z) + return f*(Cosmo_Parameters.rho_M0 / (Mass)) * np.abs(dsigmadM/sigmaM) + +def f_GUREFT_physical(sigma,z): + #Fit in eq A2 in Yung+23 (2309.14408), fit to z<20 (Implementation thanks to Aaron Yung). Physical because no h. + #sigma(M,z) is the input, no growth here bc we use class with full sigma evolution + k = np.array([ 1.37657725e-01, -1.00382125e-02, 1.02963559e-03, 1.06641384e+00, + 2.47557563e-02, -2.83342017e-03, 4.86693806e+00, 9.21235623e-02, + -1.42628278e-02, 1.19837952e+00, 1.42966892e-03, -3.30740460e-04]) + A = lambda x: k[0] + k[1]*x + k[2]*(x**2) + a = lambda x: k[3] + k[4]*x + k[5]*(x**2) + b = lambda x: k[6] + k[7]*x + k[8]*(x**2) + c = lambda x: k[9] + k[10]*x + k[11]*(x**2) + sig = sigma + #cap coefficients at z=20 to avoid extrapolation + zuse = np.fmin(z,20.0) + return A(zuse) * (((sig/b(zuse))**(-a(zuse))) + 1.0 ) * np.exp(-c(zuse)/(sig**2)) + +def PS_HMF_unnorm(Cosmo_Parameters, Mass, nu, dlogSdM): + 'Returns the Press-Schechter HMF (unnormalized since we will take ratios), given a halo Mass [Msun], nu = delta_tilde/S_tilde, with delta_tilde = delta_crit - delta_R, and variance S = sigma(M)^2 - sigma(R)^2. Used for 21cmFAST mode.' + + return nu * np.exp(-Cosmo_Parameters.a_corr_EPS*nu**2/2.0) * dlogSdM* (1.0 / Mass) + #written so that dsigmasq/dM appears directly, since that is not modified by EPS, whereas sigma_tot^2 = sigma^2(M) - sigma^2(R). The sigma in denominator will be sigma_tot + + + +class HMF_interpolator: + "Class that builds an interpolator of the HMF. Returns an interpolator" + + def __init__(self, User_Parameters, Cosmo_Parameters): + + self._Mhmin = 1e5 #originally 1e5 + self._Mhmax = 1e14 + self._NMhs = np.floor(35*User_Parameters.precisionboost).astype(int) + self.Mhtab = np.logspace(np.log10(self._Mhmin),np.log10(self._Mhmax),self._NMhs) # Halo mases in Msun + self.RMhtab = RadofMh(Cosmo_Parameters, self.Mhtab) + + self.logtabMh = np.log(self.Mhtab) + + + self._zmin=Cosmo_Parameters.zmin_CLASS + self._zmax = Cosmo_Parameters.zmax_CLASS + self._Nzs=np.floor(100*User_Parameters.precisionboost).astype(int) + self.zHMFtab = np.linspace(self._zmin,self._zmax,self._Nzs) + + #check resolution + if (Cosmo_Parameters.kmax_CLASS < 1.0/self.RMhtab[0]): + print('Warning! kmax_CLASS may be too small! Run CLASS with higher kmax') + + self.sigmaMhtab = np.array([[Cosmo_Parameters.ClassCosmo.sigma(RR,zz) for zz in self.zHMFtab] for RR in self.RMhtab]) + + self._depsM=0.01 #for derivatives, relative to M + self.dsigmadMMhtab = np.array([[(Cosmo_Parameters.ClassCosmo.sigma(RadofMh(Cosmo_Parameters, MM*(1+self._depsM)),zz)-Cosmo_Parameters.ClassCosmo.sigma(RadofMh(Cosmo_Parameters, MM*(1-self._depsM)),zz))/(MM*2.0*self._depsM) for zz in self.zHMFtab] for MM in self.Mhtab]) + + + if(Cosmo_Parameters.Flag_emulate_21cmfast==True): + #ADJUST BY HAND adjust sigmas to match theirs, since the CLASS TF they use is at a fixed cosmology from 21cmvFAST but the input cosmology is different + self.sigmaMhtab*=np.sqrt(0.975)#/0.9845 + self.dsigmadMMhtab*=np.sqrt(0.975)#/0.9845 + + #this correction is because 21cmFAST uses the dicke() function to compute growth, which is ~0.5% offset at high z. This offset makes our growth the same as dicke() for a Planck2018 cosmology. Has to be added separately to the growth(z) correction above since they come in different places + _offsetgrowthdicke21cmFAST = 1-0.000248*(self.zHMFtab-5.) + self.sigmaMhtab*=_offsetgrowthdicke21cmFAST + self.dsigmadMMhtab*=_offsetgrowthdicke21cmFAST + #Note that these two changes may be different if away from Planck2018 + + + + self.HMFtab = np.zeros_like(self.sigmaMhtab) + + + + + + for iM, MM in enumerate(self.Mhtab): + for iz, zz in enumerate(self.zHMFtab): + sigmaM = self.sigmaMhtab[iM,iz] + dsigmadM = self.dsigmadMMhtab[iM,iz] + + if(Cosmo_Parameters.HMF_CHOICE == 'ST'): + self.HMFtab[iM,iz] = ST_HMF(Cosmo_Parameters, MM, sigmaM, dsigmadM) + elif(Cosmo_Parameters.HMF_CHOICE == 'Yung'): + self.HMFtab[iM,iz] = Tink_HMF(Cosmo_Parameters, MM, sigmaM, dsigmadM,zz) + else: + print('ERROR, use a correct Cosmo_Parameters.HMF_CHOICE') + self.HMFtab[iM,iz] = 0.0 + + + + + + _HMFMIN = np.exp(-300.) #min HMF to avoid overflowing + logHMF_ST_trim = self.HMFtab + logHMF_ST_trim[np.array(logHMF_ST_trim <= 0.)] = _HMFMIN + logHMF_ST_trim = np.log(logHMF_ST_trim) + + + self.fitMztab = [np.log(self.Mhtab), self.zHMFtab] + self.logHMFint = RegularGridInterpolator(self.fitMztab, logHMF_ST_trim, bounds_error = False, fill_value = -np.inf) ###HAC: Changed to -np.inf so HMFint = exp(-np.inf)= zero to fix nans in sfrd.py + + self.sigmaintlog = RegularGridInterpolator(self.fitMztab, self.sigmaMhtab, bounds_error = False, fill_value = np.nan)# no need to log since it doesnt vary dramatically + + self.dsigmadMintlog = RegularGridInterpolator(self.fitMztab, self.dsigmadMMhtab, bounds_error = False, fill_value = np.nan) + + + #also build an interpolator for sigma(R) of the R we integrate over (for CD and EoR). These R >> Rhalo typically, so need new table. + self.sigmaofRtab = np.array([[Cosmo_Parameters.ClassCosmo.sigma(RR,zz) for zz in self.zHMFtab] for RR in Cosmo_Parameters._Rtabsmoo]) + self.fitRztab = [np.log(Cosmo_Parameters._Rtabsmoo), self.zHMFtab] + self.sigmaRintlog = RegularGridInterpolator(self.fitRztab, self.sigmaofRtab, bounds_error = False, fill_value = np.nan) #no need to log either + + + + + def HMF_int(self, Mh, z): + "Interpolator to find HMF(M,z), designed to take a single z but an array of Mh in Msun" + _logMh = np.log(Mh) + + logMhvec = np.asarray([_logMh]) if np.isscalar(_logMh) else np.asarray(_logMh) + inarray = np.array([[LM,z] for LM in logMhvec]) + + return np.exp(self.logHMFint(inarray) ) + + + + def sigma_int(self,Mh,z): + "Interpolator to find sigma(M,z), designed to take a single z but an array of Mh in Msun" + _logMh = np.log(Mh) + logMhvec = np.asarray([_logMh]) if np.isscalar(_logMh) else np.asarray(_logMh) + inarray = np.array([[LM,z] for LM in logMhvec]) + return self.sigmaintlog(inarray) + + def sigmaR_int(self,RR,z): + "Interpolator to find sigma(RR,z), designed to take a single z but an array of RR in cMpc" + _logRR = np.log(RR) + logRRvec = np.asarray([_logRR]) if np.isscalar(_logRR) else np.asarray(_logRR) + inarray = np.array([[LR,z] for LR in logRRvec]) + return self.sigmaRintlog(inarray) + + + def dsigmadM_int(self,Mh,z): + "Interpolator to find dsigma/dM(M,z), designed to take a single z but an array of Mh in Msun. Used in 21cmFAST mode" + _logMh = np.log(Mh) + logMhvec = np.asarray([_logMh]) if np.isscalar(_logMh) else np.asarray(_logMh) + inarray = np.array([[LM,z] for LM in logMhvec]) + return self.dsigmadMintlog(inarray) + + +def growth(Cosmo_Parameters, z): + "Scale-independent growth factor, interpolated from CLASS" + zlist = np.asarray([z]) if np.isscalar(z) else np.asarray(z) + if (Cosmo_Parameters.Flag_emulate_21cmfast==True): + _offsetgrowthdicke21cmFAST = 1-0.000248*(zlist-5.) #as in HMF, to fix growth. have to do it independently since it depends on z. + return Cosmo_Parameters.growthint(zlist) * _offsetgrowthdicke21cmFAST + else: + return Cosmo_Parameters.growthint(zlist) + + +def dgrowth_dz(CosmoParams, z): + "Derivative of growth factor growth() w.r.t. z" + zlist = np.asarray([z]) if np.isscalar(z) else np.asarray(z) + dzlist = zlist*0.001 + return (growth(CosmoParams, z+dzlist)-growth(CosmoParams, z-dzlist))/(2.0*dzlist) + + +def redshift_of_chi(CosmoParams, z): + "Returns z(chi) for any input comoving distance from today chi in Mpc" + return CosmoParams.zfofRint(z) + + + +def T021(Cosmo_Parameters, z): + "Prefactor in mK to T21 that only depends on cosmological parameters and z. Eg Eq.(21) in 2110.13919" + return 34 * pow((1+z)/16.,0.5) * (Cosmo_Parameters.omegab/0.022) * pow(Cosmo_Parameters.omegam/0.14,-0.5) + + +#UNUSED bias, just for reference +def bias_ST(Cosmo_Parameters, sigmaM): + # from https://arxiv.org/pdf/1007.4201.pdf Table 1 + a_ST = Cosmo_Parameters.a_ST + p_ST = Cosmo_Parameters.p_ST + delta_crit_ST = Cosmo_Parameters.delta_crit_ST + nu = delta_crit_ST/sigmaM + nutilde = np.sqrt(a_ST) * nu + + return 1.0 + (nutilde**2 - 1.0 + 2. * p_ST/(1.0 + nutilde**(2. * p_ST) ) )/delta_crit_ST + +def bias_Tinker(Cosmo_Parameters, sigmaM): + #from https://arxiv.org/pdf/1001.3162.pdf, Delta=200 + delta_crit_ST = Cosmo_Parameters.delta_crit_ST + nu = delta_crit_ST/sigmaM + + #Tinker fit + _Deltahalo = 200; + _yhalo = np.log10(_Deltahalo) + _Abias = 1.0 + 0.24 * _yhalo * np.exp(-(4.0/_yhalo)**4.) + _abias = 0.44*_yhalo-0.88 + _Bbias = 0.183 + _bbias = 1.5 + _Cbias = 0.019 + 0.107 * _yhalo + 0.19 * np.exp(-(4.0/_yhalo)**4.) + _cbias = 2.4 + + return 1.0 - _Abias*(nu**_abias/(nu**_abias + delta_crit_ST**_abias)) + _Bbias * nu**_bbias + _Cbias * nu**_cbias + +#UNUSED: +# def interp2Dlinear_only_y(arrayxy, arrayz, x, y): +# "2D interpolator where the x axis is assumed to be an array identical to the trained x. That is, an array of 1D linear interpolators. arrayxy is [x,y]. arrayz is result. x is the x input (=arrayxy[0]), and y the y input. Returns z result (array)" +# if((x != arrayxy[0]).all()): +# print('ERROR on interp2Dlinear_only_y, x need be the same in interp and input') +# return -1 +# Ny = len(arrayxy[1]) +# ymin, ymax = arrayxy[1][[0,-1]] +# if((y > ymax or y>> zeus21.User_Parameters(precisionboost=0.5) + + Parameters can also be changed afterwards: + >>> UserParams = zeus21.User_Parameters() + >>> UserParams.precisionboost = 0.5 + + + Parameters + ---------- + precisionboost: float + Make integrals take more points for boost in precision, the baseline being 1.0. + dlogzint_target: + Target number of redshift bins for the redsfhit arrays in log space. + FLAG_FORCE_LINEAR_CF: int (False or True) + False to do standard calculation, True to force linearization of correlation function. + MIN_R_NONLINEAR: float + Minimum radius R/cMpc in which we start doing the nonlinear calculation. + Below ~1 it will blow up because sigma > 1 eventually, and our exp(delta) approximation breaks. + Check if you play with it and if you change Window(). + MAX_R_NONLINEAR: float + Maximum radius R/cMpc in which we start doing the nonlinear calculation (above this it is very linear) + FLAG_DO_DENS_NL: bool + Whether to do the nonlinear (ie lognormal) calculation for the density field itself and its cross correlations. + Small (<3%) correction in dd, but non trivial (~10%) in d-xa and d-Tx + FLAG_WF_ITERATIVE: bool + Whether to iteratively do the WF correction as in Hirata2006. + zmin_T21: float + Minimum redshift to which we compute the T21 signals. + DO_ONLY_GLOBAL: bool + Whether zeus21 only runs the global T21 signal (and not fluctuations). + + Attributes + ---------- + C2_RENORMALIZATION_FLAG: int (False or True) + Whether to renormalize the C2 oefficients (appendix in 2302.08506). + """ + + precisionboost: float = 1.0 + dlogzint_target: float = 0.02 + FLAG_FORCE_LINEAR_CF: bool = False + MIN_R_NONLINEAR: float = 2.0 + MAX_R_NONLINEAR: float = 100.0 + FLAG_DO_DENS_NL: bool = False + FLAG_WF_ITERATIVE: bool = True + zmin_T21: float = 5. + DO_ONLY_GLOBAL: bool = False + + C2_RENORMALIZATION_FLAG: int = _field(init=False) + + def __post_init__(self): + schema = { + "FLAG_FORCE_LINEAR_CF": (bool, None), + "FLAG_DO_DENS_NL": (bool, None), + "FLAG_WF_ITERATIVE": (bool, None), + "DO_ONLY_GLOBAL": (bool, None), + } + validate_fields(self, schema) + + self.C2_RENORMALIZATION_FLAG = not self.FLAG_FORCE_LINEAR_CF + + +@dataclass(kw_only=True) +class Cosmo_Parameters: + """ + Cosmological parameters (including the 6 LCDM + other parameters) for zeus21 and running of CLASS. + + Parameters + ---------- + UserParams: User_Parameters + zeus21 class for the user parameters. + omegab: float + Baryon density * h^2. + omegac: float + CDM density * h^2. + h_fid: float + Hubble constant / 100. + As: float + Amplitude of initial fluctuations. + ns: float + Spectral index. + tau_fid: float + Optical depth to reionization. + kmax_CLASS: float + Maximum wavenumber to be passed to CLASS. + zmax_CLASS: float + Maximum redshift to be passed to CLASS. + zmin_CLASS: float + Minimum redshift to be passed to CLASS. + Rs_min: float + Minimum radius to be passed to CLASS. + Rs_max: float + Maximum radius to be passed to CLASS. + Flag_emulate_21cmfast: bool + Whether zeus21 emulates 21cmFAST cosmology (used in HMF, LyA, and X-ray opacity calculations). Default is False. + When False, sets the Star Formation Rate model to GALLUMI-like, and when True to 21cmfast-like (ignores Mc and beta and has a t* later in SFR()). + USE_RELATIVE_VELOCITIES: bool + Whether to use v_cb. + HMF_CHOICE: str + Which HMF to use. + "ST" for the classic Sheth-Tormen (f(nu)), "Yung" for the Tinker08 (f(sigma)) calibrated to Yung+23. + + Attributes + ---------- + ClassCosmo: Class + CLASS instance to compute cosmology. + omegam: float + Matter density * h^2. + OmegaM: float + Matter density. + rhocrit: float + Critical density. + OmegaR: float + Radiation density. + OmegaL: float + Dark energy density. + OmegaB: float + Baryon density. + rho_M0: float + Actual matter density. + z_rec: float + Recombination reshift. + sigma_vcb: float + Square root of the variance of the relative velocity field. + vcb_avg: float + Average of the relative velocity field. + Y_He: float + Helium mass fraction. + x_He: + Helium-to-hydrogen number density ratio. + f_H: float + Hydrogen number density ratio relative to baryons. + f_He: float + Helium number density ratio relative to baryons. + mu_baryon: float + Mean baryonic weight. + mu_baryon_Msun: float + Mean baryonic weight relative to the solar mass. + constRM: float + Radius-to-mass conversions for HMF. Used for CLASS input so assumes tophat. + zfofRint: interp1d + Interpolation for the redshift as a function of the comoving distance. + chiofzint: interp1d + Interpolation for the comoving distance as a function of the redshift. + Hofzint: interp1d + Interpolation for the Hubble rate as a function of the redshift. + Tadiabaticint: + Interpolation for the adiabatic temperature as a function of redshift. + xetanhint: interp1d + Interpolation for the electron fraction as a function of redshift. + growthint: interp1d + Interpolation for the growth faction as a function of redshift. + NRs: np.ndarray + Number of radii. + indexminNL: np.ndarray + Index of the minimum radius R/cMpc in which we start doing the nonlinear calculation. + indexmaxNL: np.ndarray + Index of the maximum radius R/cMpc in which we start doing the nonlinear calculation. + a_ST: float + Rescaling of the HMF barrier. + p_ST: float + Correction factor for the abundance of small mass objects. + Amp_ST: float + Normalization factor for the halo mass function. + delta_crit_ST: float + Barrier for halo to collapse in Sheth-Tormen formalism. + a_corr_EPS: float + Correction to the EPS relation between nu and nu' when doing extended PS. Follows hi-z simulation results from Schneider+21. + """ + ### Non-default parameters + UserParams: InitVar[User_Parameters] + + + ### Default parameters + # 6 LCDM parameters + omegab: float = 0.0223828 + omegac: float = 0.1201075 + h_fid: float = 0.67810 + As: float = 2.100549e-09 + ns: float = 0.9660499 + tau_fid: float = 0.05430842 + + # Other params for CLASS + kmax_CLASS: float = 500. + zmax_CLASS: float = 50. + zmin_CLASS: float = 5. + + # Shells that we integrate over at each z. + Rs_min: float = 0.05 ### ASK JULIAN for changing the name + Rs_max: float = 2000. ### ASK JULIAN for changing the name + + # Flags + Flag_emulate_21cmfast: bool = False + USE_RELATIVE_VELOCITIES: bool = False + # === BEGIN EDIT (inputs_v2: par/perp eta split) === + # When True (and USE_RELATIVE_VELOCITIES is also True), build the + # parallel/perpendicular eta power spectra and corresponding windowed + # correlation functions (xiEtaPar_RR_CF, xiEtaPerp_RR_CF) instead of + # the legacy isotropic xiEta_RR_CF. Consumed by + # zeus21.correlations_v2.Power_Spectra via + # getattr(Cosmo_Parameters, 'USE_ETA_PARPERP_SPLIT', False). + USE_ETA_PARPERP_SPLIT: bool = False + # === END EDIT (inputs_v2: par/perp eta split) === + HMF_CHOICE: str = "ST" + + + ### Additional parameters and attributes set in the following + # LCDM parameters + ClassCosmo: Class = _field(init=False) + omegam: float = _field(init=False) + OmegaM: float = _field(init=False) + rhocrit: float = _field(init=False) + OmegaR: float = _field(init=False) + OmegaL: float = _field(init=False) + OmegaB: float = _field(init=False) + rho_M0: float = _field(init=False) + z_rec: float = _field(init=False) + + # v_cb parameters + sigma_vcb: float = _field(init=False) + vcb_avg: float = _field(init=False) + + # Number densities and mass fractions + Y_He: float = _field(init=False) + x_He: float = _field(init=False) + f_H: float = _field(init=False) + f_He: float = _field(init=False) + mu_baryon: float = _field(init=False) + mu_baryon_Msun: float = _field(init=False) + + # R->M conversions for HMF + constRM: float = _field(init=False) + + # Redshifts and comoving distances + _ztabinchi: np.ndarray = _field(init=False) + _chitab: Any = _field(init=False) + _Hztab: Any = _field(init=False) + zfofRint: interp1d = _field(init=False) + chiofzint: interp1d = _field(init=False) + Hofzint: interp1d = _field(init=False) + + # Thermodynamics + Tadiabaticint: interp1d = _field(init=False) + xetanhint: interp1d = _field(init=False) + + # Growth + growthint: interp1d = _field(init=False) + + # Radii + NRs: np.ndarray = _field(init=False) + _Rtabsmoo: np.ndarray = _field(init=False) + _dlogRR: np.ndarray = _field(init=False) + indexminNL: np.ndarray = _field(init=False) + indexmaxNL: np.ndarray = _field(init=False) + + # HMF-related constants + a_ST: float = _field(init=False) + p_ST: float = _field(init=False) + Amp_ST: float = _field(init=False) + delta_crit_ST: float = _field(init=False) + a_corr_EPS: float = _field(init=False) + + tageofzMyr: interp1d = _field(init=False) + zfoftageMyr: interp1d = _field(init=False) + + def __post_init__(self, UserParams): + + schema = { + "Flag_emulate_21cmfast": (bool, None), + "USE_RELATIVE_VELOCITIES": (bool, None), + # === BEGIN EDIT (inputs_v2: par/perp eta split) === + "USE_ETA_PARPERP_SPLIT": (bool, None), + # === END EDIT (inputs_v2: par/perp eta split) === + "HMF_CHOICE": (str, {'ST','Yung'}), + } + validate_fields(self, schema) + + # run CLASS + self.ClassCosmo = self.runclass() + + # derived params + self.omegam = self.omegab + self.omegac + self.OmegaM = self.ClassCosmo.Omega_m() + self.rhocrit = 3 * 100**2 / (8 * np.pi* constants.MsunToKm * constants.c_kms**2 * constants.KmToMpc) * self.h_fid**2 # Msun/Mpc^3 + self.OmegaR = self.ClassCosmo.Omega_r() + self.OmegaL = self.ClassCosmo.Omega_Lambda() + self.OmegaB = self.ClassCosmo.Omega_b() + self.rho_M0 = self.OmegaM * self.rhocrit + + _zlistforage = np.logspace(5,-3,10000) + _zlistforage[-1]=0.0 + _Hztab = self.ClassCosmo.z_of_r(_zlistforage)[1] #chi and dchi/dz + + ### TODO: check if this is the same as cosmic time in cosmology + tagetabyr = -cumulative_trapezoid(constants.Mpctoyr/_Hztab/(1+_zlistforage),_zlistforage) + tagetabyr = np.insert(tagetabyr,0,0) + + self.tageofzMyr = interp1d(_zlistforage,tagetabyr/1e6) #interpolators for age in Myr as a function of z + self.zfoftageMyr = interp1d(tagetabyr/1e6,_zlistforage) #and it's inverse, z for age t in Myr + + + self.z_rec = self.ClassCosmo.get_current_derived_parameters(['z_rec'])['z_rec'] + + ### v_cb flag + self.sigma_vcb = self.ClassCosmo.pars['sigma_vcb'] + self.vcb_avg = self.ClassCosmo.pars['v_avg'] + + ### number densities and mass fractions + self.Y_He = self.ClassCosmo.get_current_derived_parameters(['YHe'])['YHe'] + self.x_He = self.Y_He/4.0/(1.0 - self.Y_He) #=nHe/nH + self.f_H = (1.0 - self.Y_He)/(1.0 - 3.0/4.0 * self.Y_He) #=nH/nb + self.f_He = self.Y_He/4.0/(1.0 - 3.0/4.0 * self.Y_He) #=nHe/nb + + self.mu_baryon = (1 + self.x_He * 4.)/(1 + self.x_He) * constants.mH_GeV #mproton ~ 0.94 GeV + self.mu_baryon_Msun = self.mu_baryon / constants.MsuntoGeV + + # for R->M conversions for HMF. Used for CLASS input so assumes tophat. + self.constRM = self.OmegaM*self.rhocrit * 4.0 * np.pi/3.0 + + # redshifts and comoving distances + self._ztabinchi = np.linspace(0.0, 1100. , 10000) #cheap so do a lot + self._chitab, self._Hztab = self.ClassCosmo.z_of_r(self._ztabinchi) #chi and dchi/dz + self.zfofRint = interp1d(self._chitab, self._ztabinchi) + self.chiofzint = interp1d(self._ztabinchi,self._chitab) + self.Hofzint = interp1d(self._ztabinchi,self._Hztab) + + # thermodynamics + _thermo = self.ClassCosmo.get_thermodynamics() + self.Tadiabaticint = interp1d(_thermo['z'], _thermo['Tb [K]']) + self.xetanhint = interp1d(_thermo['z'], _thermo['x_e']) + + # growth + _ztabingrowth = np.linspace(0., 100. , 2000) + _growthtabint = np.array([self.ClassCosmo.scale_independent_growth_factor(zz) for zz in _ztabingrowth]) + self.growthint = interp1d(_ztabingrowth,_growthtabint) + + # shells that we integrate over at each z. + if self.Flag_emulate_21cmfast: + self.Rs_min = 0.62*1.5 #same as minmum R in 21cmFAST for their standard 1.5 Mpc cell resolution. 0.62 is their 'L_FACTOR' + self.Rs_max = 500. #same as R_XLy_MAX in 21cmFAST. Too low? + + # radii + self.NRs = np.floor(45*UserParams.precisionboost).astype(int) + self._Rtabsmoo = np.logspace(np.log10(self.Rs_min), np.log10(self.Rs_max), self.NRs) # Smoothing Radii in Mpc com + self._dlogRR = np.log(self.Rs_max/self.Rs_min)/(self.NRs-1.0) + + self.indexminNL = (np.log(UserParams.MIN_R_NONLINEAR/self.Rs_min)/self._dlogRR).astype(int) + self.indexmaxNL = (np.log(UserParams.MAX_R_NONLINEAR/self.Rs_min)/self._dlogRR).astype(int) + 1 #to ensure it captures MAX_R + + # HMF-related constants + if not self.Flag_emulate_21cmfast: # standard, best fit ST from Schneider+21 + self.a_ST = 0.707 # OG ST fit, or 0.85 to fit 1805.00021 + self.p_ST = 0.3 + self.Amp_ST = 0.3222 + self.delta_crit_ST = 1.686 + self.a_corr_EPS = self.a_ST + else: # emulate 21cmFAST, including HMF from Jenkins 2001 + self.HMF_CHOICE = 'ST' # forced to match their functional form + print('Since Flag_emulate_21cmfast==True, the code set HMF_CHOICE==ST') + self.a_ST = 0.73 + self.p_ST = 0.175 + self.Amp_ST = 0.353 + self.delta_crit_ST = 1.68 + self.a_corr_EPS = 1.0 + + # Run matter and relative velocities correlations + self.run_correlations() + + def runclass(self): + "Set up CLASS cosmology. Takes CosmologyIn class input and returns CLASS Cosmology object" + ClassCosmo = Class() + ClassCosmo.set({'omega_b': self.omegab,'omega_cdm': self.omegac, + 'h': self.h_fid,'A_s': self.As,'n_s': self.ns,'tau_reio': self.tau_fid}) + ClassCosmo.set({'output':'mPk','lensing':'no','P_k_max_1/Mpc':self.kmax_CLASS, 'z_max_pk': self.zmax_CLASS}) ###HAC: add vTK to outputs + ClassCosmo.set({'gauge':'synchronous'}) + #hfid = ClassCosmo.h() # get reduced Hubble for conversions to 1/Mpc + + # and run it (see warmup for their doc) + ClassCosmo.compute() + + ClassCosmo.pars['Flag_emulate_21cmfast'] = self.Flag_emulate_21cmfast + + ###HAC: Adding VCB feedback via a second run of CLASS: + if self.USE_RELATIVE_VELOCITIES: + + kMAX_VCB = 50.0 + ###HAC: getting z_rec from first CLASS run + z_rec = ClassCosmo.get_current_derived_parameters(['z_rec'])['z_rec'] + z_drag = ClassCosmo.get_current_derived_parameters(['z_d'])['z_d'] + + ###HAC: Running CLASS a second time just to get velocity transfer functions at recombination + ClassCosmoVCB = Class() + ClassCosmoVCB.set({'omega_b': self.omegab,'omega_cdm': self.omegac, + 'h': self.h_fid,'A_s': self.As,'n_s': self.ns,'tau_reio': self.tau_fid}) + ClassCosmoVCB.set({'output':'vTk'}) + ClassCosmoVCB.set({'P_k_max_1/Mpc':kMAX_VCB, 'z_max_pk':12000}) + ClassCosmoVCB.set({'gauge':'newtonian'}) + ClassCosmoVCB.compute() + velTransFunc = ClassCosmoVCB.get_transfer(z_drag) + + kVel = velTransFunc['k (h/Mpc)'] * self.h_fid + theta_b = velTransFunc['t_b'] + theta_c = velTransFunc['t_cdm'] + + sigma_vcb = np.sqrt(np.trapezoid(self.As * (kVel/0.05)**(self.ns-1) /kVel * (theta_b - theta_c)**2/kVel**2, kVel)) * constants.c_kms + ClassCosmo.pars['sigma_vcb'] = sigma_vcb + + ###HAC: now computing average velocity assuming a Maxwell-Boltzmann distribution of velocities + velArr = np.geomspace(0.01, constants.c_kms, 1000) #in km/s + vavgIntegrand = (3 / (2 * np.pi * sigma_vcb**2))**(3/2) * 4 * np.pi * velArr**2 * np.exp(-3 * velArr**2 / (2 * sigma_vcb**2)) + ClassCosmo.pars['v_avg'] = np.trapezoid(vavgIntegrand * velArr, velArr) + + ###HAC: Computing Vcb Power Spectrum + ClassCosmo.pars['k_vcb'] = kVel + ClassCosmo.pars['theta_b'] = theta_b + ClassCosmo.pars['theta_c'] = theta_c + P_vcb = self.As * (kVel/0.05)**(self.ns-1) * (theta_b - theta_c)**2/kVel**2 * 2 * np.pi**2 / kVel**3 + + p_vcb_intp = interp1d(np.log(kVel), P_vcb) + ClassCosmo.pars['P_vcb'] = P_vcb + + ###HAC: Computing Vcb^2 (eta) Power Spectra + kVelIntp = np.geomspace(1e-4, kMAX_VCB, 512) + rVelIntp = 2 * np.pi / kVelIntp + + j0bessel = lambda x: np.sin(x)/x + j2bessel = lambda x: (3 / x**2 - 1) * np.sin(x)/x - 3*np.cos(x)/x**2 + + psi0 = 1 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapezoid(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j0bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) + psi2 = -2 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapezoid(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j2bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) + + # === BEGIN EDIT (inputs_v2: par/perp eta split) === + # Mutually exclusive: only the keys for the active mode are + # written to ClassCosmo.pars so a stale consumer of the other + # mode fails loudly. + if self.USE_ETA_PARPERP_SPLIT: + k_etapar, P_etapar = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 + psi2)**2, extrap = False) + k_etaperp, P_etaperp = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 - psi2/2.0)**2, extrap = False) + + ClassCosmo.pars['k_etapar'] = k_etapar[P_etapar > 0] + ClassCosmo.pars['P_etapar'] = P_etapar[P_etapar > 0] + ClassCosmo.pars['k_etaperp'] = k_etaperp[P_etaperp > 0] + ClassCosmo.pars['P_etaperp'] = P_etaperp[P_etaperp > 0] + else: + k_eta, P_eta = mcfit.xi2P(rVelIntp, l=0, lowring = True)((6 * psi0**2 + 3 * psi2**2), extrap = False) + + ClassCosmo.pars['k_eta'] = k_eta[P_eta > 0] + ClassCosmo.pars['P_eta'] = P_eta[P_eta > 0] + # === END EDIT (inputs_v2: par/perp eta split) === + + # print("HAC: Finished running CLASS a second time to get velocity transfer functions") + + else: + ClassCosmo.pars['v_avg'] = 0.0 + ClassCosmo.pars['sigma_vcb'] = 1.0 #Avoids excess computation, but doesn't matter what value we set it to because the flag in inputs.py sets all feedback parameters to zero + + return ClassCosmo + + def run_correlations(self): + #we choose the k to match exactly the log FFT of input Rtabsmoo. + + self._klistCF, _dummy_ = mcfit.xi2P(self._Rtabsmoo, l=0, lowring=True)(0*self._Rtabsmoo, extrap=False) + self.NkCF = len(self._klistCF) + + self._PklinCF = np.zeros(self.NkCF) # P(k) in 1/Mpc^3 + for ik, kk in enumerate(self._klistCF): + self._PklinCF[ik] = self.ClassCosmo.pk(kk, 0.0) # function .pk(k,z) + + + + self._xif = mcfit.P2xi(self._klistCF, l=0, lowring=True) + + + self.xi_RR_CF = self.get_xi_R1R2(field = 'delta') + self.ClassCosmo.pars['xi_RR_CF'] = np.copy(self.xi_RR_CF) #store correlation function for gamma_III correction in SFRD + + ###HAC: Interpolated object for eta power spectrum + # === BEGIN EDIT (inputs_v2: par/perp eta split) === + # Branch on USE_ETA_PARPERP_SPLIT so the two modes are mutually + # exclusive: only the CF arrays for the active mode are populated. + if self.USE_RELATIVE_VELOCITIES == True: + if self.USE_ETA_PARPERP_SPLIT: + P_etapar_interp = interp1d(self.ClassCosmo.pars['k_etapar'], self.ClassCosmo.pars['P_etapar'], bounds_error = False, fill_value = 0) + P_etaperp_interp = interp1d(self.ClassCosmo.pars['k_etaperp'], self.ClassCosmo.pars['P_etaperp'], bounds_error = False, fill_value = 0) + self._PkEtaParCF = P_etapar_interp(self._klistCF) + self._PkEtaPerpCF = P_etaperp_interp(self._klistCF) + self.xiEtaPar_RR_CF = self.get_xi_R1R2(field = 'vcb_par') + self.xiEtaPerp_RR_CF = self.get_xi_R1R2(field = 'vcb_perp') + else: + P_eta_interp = interp1d(self.ClassCosmo.pars['k_eta'], self.ClassCosmo.pars['P_eta'], bounds_error = False, fill_value = 0) + self._PkEtaCF = P_eta_interp(self._klistCF) + self.xiEta_RR_CF = self.get_xi_R1R2(field = 'vcb') + else: + self._PkEtaCF = np.zeros_like(self._PklinCF) + self.xiEta_RR_CF = np.zeros_like(self.xi_RR_CF) + # === END EDIT (inputs_v2: par/perp eta split) === + + + def get_xi_R1R2 (self, field = None): + "same as get_xi_z0_lin but smoothed over two different radii with Window(k,R) \ + same separations rs as get_xi_z0_lin so it does not output them." + + lengthRarray = self.NRs + windowR1 = z21_utilities.Window(self._klistCF.reshape(lengthRarray, 1, 1), self._Rtabsmoo.reshape(1, 1, lengthRarray)) + windowR2 = z21_utilities.Window(self._klistCF.reshape(1, lengthRarray,1), self._Rtabsmoo.reshape(1, 1, lengthRarray)) + + if field == 'delta': + _PkRR = np.array([[self._PklinCF]]) * windowR1 * windowR2 + elif field == 'vcb': + _PkRR = np.array([[self._PkEtaCF]]) * windowR1 * windowR2 + # === BEGIN EDIT (inputs_v2: par/perp eta split) === + elif field == 'vcb_par': + _PkRR = np.array([[self._PkEtaParCF]]) * windowR1 * windowR2 + elif field == 'vcb_perp': + _PkRR = np.array([[self._PkEtaPerpCF]]) * windowR1 * windowR2 + # === END EDIT (inputs_v2: par/perp eta split) === + else: + raise ValueError('field has to be either delta or vcb in get_xi_R1R2') + + self.rlist_CF, xi_RR_CF = self._xif(_PkRR, extrap = False) + + return xi_RR_CF + + + +@dataclass(kw_only=True) +class Astro_Parameters: + """ + Astrophysical parameters for zeus21. + + Parameters + ---------- + Cosmo_Parameters: Cosmo_Parameters + zeus21 class for the cosmological parameters. Needs to be inputed. + accretion_model: str + Accretion model. "exp" for exponential, "EPS" for EPS. Default is "EPS". + USE_POPIII: bool + Whether to use Pop III. Default is False. + USE_LW_FEEDBACK: bool + Whether to use the Lyman-Werner feedback. Default is True. + quadratic_SFRD_lognormal: bool + Whether to use the second order correction to the SFRD approximation. Default is True. + epsstar: float + Amplitude of the star formation efficiency (at M_pivot). Default is 0.1. + dlog10epsstardz: float + Derivative of epsstar with respect to z. Default is 0. + alphastar: float + Power law index of the star formation efficiency at low masses. Default 0.5. + betastar: float + Power law index of the star formation efficiency at high masses. Only used when astromodel=0. Default -0.5. + Mc: float + Mass at which the star formation efficiency cuts. Only used when astromodel=0. Default 3e11. + sigmaUV: float + Stochasticity (gaussian rms) in the halo-galaxy connection P(MUV | Mh). Default is 0.5. + alphastar_III: float + Power law index of the Pop III star formation efficiency at low masses. Default 0. + betastar_III: float + Power law index of the Pop III star formation efficiency at high masses. Default 0. + fstar_III: float + Peak amplitude of the Pop III star formation efficiency. Default 10**(-2.5). + Mc_III: float + Mass at which the Pop III star formation efficiency cuts. Default 1e7. + dlog10epsstardz_III: float + Derivative of epsstar with respect to z for Pop III. Default is 0. + N_alpha_perbaryon_II: float + Number of photons between LyA and Ly Continuum per baryon (from LB05). Default is 9690. + N_alpha_perbaryon_III: float + Number of photons between LyA and Ly Continuum per baryon (from LB05) for Pop III. Default is 17900. + L40_xray: float + Soft-band (E<2 keV) lum/SFR in Xrays in units of 10^40 erg/s/(Msun/yr). Default is 3.0. + E0_xray: float + Minimum energy in eV. Default is 500. + alpha_xray: float + Xray SED power-law index. Default is -1. + L40_xray_III: float + Soft-band (E<2 keV) lum/SFR in Xrays in units of 10^40 erg/s/(Msun/yr) for Pop III. Default is 3.0. + alpha_xray_III: float + Xray SED power-law index. Default is -1. + Emax_xray_norm: float + Max energy in eV to normalize SED. Default at 2000 eV. + fesc10: float + Amplitude of the escape fraction. Default is 0.1. + Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. + alphaesc: float + Index for the escape fraction. Default is 0. + Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. + fesc7_III: float + Amplitude of the Pop III escape fraction. Default is 10**(-1.35). + Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. + alphaesc_III: float + Index for the Pop III escape fraction. Default is -0.3. + Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. + clumping: float = 3. + Clumping factor, which is z-independent and fixed for now. Default is 3, changed to 2 when Flag_emulate_21cmfast=True. + R_linear_sigma_fit_input: float + Initial guess radius at which the linear fit of the barrier is computed. Default is 3. + FLAG_BMF_converge: bool + Whether zeus21 allow the BMF to try and make the average ionized fraction converge. Default is True. + max_iter: int + Maximum iteration allowed for the convergence of the BMF. Default is 10. + ZMAX_REION: float + Maximum redshift to which the reionization quantities are computed. Default is 30. + Rbub_min: float + Minimum bubble radius. Default is 0.05. + A_LW: float + Parameters controlling the LW feedback factor (see Munoz+22, eq 13). Default is 2.0. + beta_LW: float + Parameters controlling the LW feedback factor (see Munoz+22, eq 13). Default is 0.6. + A_vcb: float + Normalization for the relative velocity feedback parameter. Default is 1.0. + beta_vcb: float + Spectral index for the relative velocity feedback parameter. Default 1.8 + Mturn_fixed: float | None + Turn-over halo mass at which the star formation rate cuts. Default is None. + FLAG_MTURN_SHARP: bool + Whether to do sharp cut at Mturn_fixed or regular exponential cutoff. Only active if FLAG_MTURN_FIXED and turned on by hand. Default is False. + C0dust: float + Calibration parameter for the dust correction for UVLF. Default is 4.43 (following Meurer+99). Input 4.54 for Overzier+01. + C1dust: float + Calibration parameter for the dust correction for UVLF. Default 1.99 for Meurer99. Input 2.07 for Overzier+01. + + Attributes + ---------- + _zpivot: float + Redshift at which the eps and dlogeps/dz are evaluated. Set by zeus21 to 8. + fstarmax: float + Peak amplitude for the star formation efficiency. Set by zeus21 to 1. + _zpivot_III: float + Redshift at which the eps and dlogeps/dz are evaluated for Pop III. Set by zeus21 to 8. + Emax_xray_integral: float + Max energy in eV that zeus21 integrate up to. Higher than Emax_xray_norm since photons can redshift from higher z. Set by zeus21 to 10000. + Nen_xray: int + Number of energies to do the xray integrals. Set by zeus21 to 30. + _log10EMIN_INTEGRATE: float + Minimum energy zeus21 integrates to, to account for photons coming from higher z that redshift. + _log10EMAX_INTEGRATE: float + Maximum energy zeus21 integrates to, to account for photons coming from higher z that redshift. + Energylist: np.ndarray + Energies, in eV. + dlogEnergy: float + Used to get dlog instead of dlog10. + N_ion_perbaryon_II: int + Number of ionizing photons per baryon. Fixed for PopII-type (Salpeter) by zeus21 to 5000. + N_ion_perbaryon_III: int + Number of ionizing photons per baryon for Pop III. Fixed for PopIII-type to 44000 (or 52480 when Flag_emulate_21cmfast=True), from Klessen & Glover 2023 Table A2 (2303.12500). + N_LW_II: float + Number of LW photons per baryon. + Assuming BL05 stellar spectrum, equal to N_alpha_perbaryon_II * fraction of photons that fall in the LW band. + N_LW_III: float + Number of LW photons per baryon. + Assuming Intermediate IMF from 2202.02099, equal to 4.86e-22 / (11.9 * u.eV).to(u.erg).value * 5.8e14. + FLAG_MTURN_FIXED: bool + Whether to fix Mturn or use Matom(z) at each z. Set by zeus21 depending on Mturn_fixed. + + """ + ### Non-default parameters + CosmoParams: InitVar[Cosmo_Parameters] + + + ### Default and init=False parameters + # Flags + accretion_model: str = "exp" + USE_POPIII: bool = False + USE_LW_FEEDBACK: bool = True + quadratic_SFRD_lognormal: bool = True + + # SFR(Mh) parameters - popII + epsstar: float = 0.1 + dlog10epsstardz: float = 0.0 + alphastar: float = 0.5 + betastar: float = -0.5 + Mc: float = 3e11 + _zpivot: float = _field(init=False) + fstarmax: float = _field(init=False) + + # SFR(Mh) parameters - popIII + epsstar_III: float = 10**(-2.5) + dlog10epsstardz_III: float = 0.0 + alphastar_III: float = 0. + betastar_III: float = 0. + Mc_III: float = 1e7 + _zpivot_III: float = _field(init=False) + + # SFR(Mh) parameters - popIII Atomic Cooling Component + USE_POPIII_ACH: bool = False + DETACH_III_ACH: bool = False + epsstar_III_ACH: float = 0. + dlog10epsstardz_III_ACH: float = 0.0 + alphastar_III_ACH: float = 0. + betastar_III_ACH: float = 0. + Mc_III_ACH: float = 1e7 + _zpivot_III_ACH: float = _field(init=False) + + # Lyman-alpha parameters + N_alpha_perbaryon_II: float = 9690 + N_alpha_perbaryon_III: float = 17900 + + # Xray parameters, assumed power-law for now + L40_xray: float = 3.0 + E0_xray: float = 500. + alpha_xray: float = -1.0 + L40_xray_III: float = 3.0 + alpha_xray_III: float = -1.0 + Emax_xray_norm: float = 2000 + Emax_xray_integral: float = _field(init=False) # Max energy in eV that we integrate up to. Higher than Emax_xray_norm since photons can redshift from higher z + + # table with how many energies we integrate over + Nen_xray: int = _field(init=False) + _log10EMIN_INTEGRATE: float = _field(init=False) # to account for photons coming from higher z that redshift + _log10EMAX_INTEGRATE: float = _field(init=False) + Energylist: np.ndarray = _field(init=False) # in eV + dlogEnergy: float = _field(init=False) # to get dlog instead of dlog10 + + # Reionization parameters + fesc10: float = 0.1 + alphaesc: float = 0.0 + fesc7_III: float = 10**(-1.35) + alphaesc_III: float = -0.3 + clumping: float = 3. + N_ion_perbaryon_II: int = _field(init=False) # fixed for PopII-type (Salpeter) + N_ion_perbaryon_III: int = _field(init=False) # fixed for PopIII-type, from Klessen & Glover 2023 Table A2 (2303.12500) + R_linear_sigma_fit_input: float = 10. + FLAG_BMF_converge: bool = True + max_iter: int = 10 + ZMAX_REION: float = 30 + Rbub_min: float = 0.05 + + # Lyman-Werner feedback paramters + A_LW: float = 2.0 + beta_LW: float = 0.6 + N_LW_II: float = _field(init=False) # number of LW photons per baryon #assuming BL05 stellar spectrum, equal to N_alpha_perbaryon_II * fraction of photons that fall in the LW band + N_LW_III: float = _field(init=False) # number of LW photons per baryon #assuming Intermediate IMF from 2202.02099, equal to 4.86e-22 / (11.9 * u.eV).to(u.erg).value * 5.8e14 + + # relative velocity + A_vcb: float = 1.0 + beta_vcb: float = 1.8 + + # 21cmFAST emulation: SFE parameters + Mturn_fixed: float | None = None + FLAG_MTURN_SHARP: bool = False + FLAG_MTURN_FIXED: bool = _field(init=False) # whether to fix Mturn or use Matom(z) at each z + + # BURSTINESS + FLAG_USE_PSD: bool = False + FLAG_COMPARE_BAGPIPES: bool = False + SEDMODEL: str = "BPASS" + sigmaPSD: float = 0.5, + dsigmaPSDdlog10Mh: float = 0.0, + tauPSD: float = 10.0, + dlog10tauPSDdlog10Mh: float = 0.0, + _tcut_LUV_short: float = 30.0 #where we separate LUV short and long, in Myr, 30 Myr or 2*tau, whichever longer + FLAG_RENORMALIZE_AVG_SFH: bool = True + _minsigmaPSD: float = _field(init=False) + _maxsigmaPSD: float = _field(init=False) + _mintauPSD: float = _field(init=False) + _maxtauPSD: float = _field(init=False) + _tagesMyr: float = _field(init=False) + _dt_FFT: float = _field(init=False) + _N_FFT: float = _field(init=False) + _omegamin: float = _field(init=False) + _omegamax: float = _field(init=False) + + def __post_init__(self, CosmoParams): + + schema = { + "accretion_model": (str, {"EPS", "exp"}), + "USE_POPIII": (bool, None), + "USE_POPIII_ACH": (bool, None), + "DETACH_III_ACH": (bool, None), + "USE_LW_FEEDBACK": (bool, None), + "quadratic_SFRD_lognormal": (bool, None), + "FLAG_MTURN_SHARP": (bool, None), + "FLAG_USE_PSD": (bool, None), + "FLAG_COMPARE_BAGPIPES": (bool, None), + "FLAG_RENORMALIZE_AVG_SFH": (bool, None), + "SEDMODEL": (str, {"bagpipes", "BPASS_binaries", "BPASS"}), + } + validate_fields(self, schema) + + ### which SFR model we use. 0=Gallumi-like, 1=21cmfast-like + if not CosmoParams.Flag_emulate_21cmfast: # GALLUMI-like + self.accretion_model = self.accretion_model # choose the accretion model: 0 = exponential, 1= EPS. Default = EPS. + else: # 21cmfast-like, ignores Mc and beta and has a t* later in SFR() + self.tstar = 0.5 + self.fstar10 = self.epsstar + + # SFR(Mh) parameters + self._zpivot = 8.0 # fixed, at which z we evaluate eps and dlogeps/dz + self._zpivot_III = 8.0 # fixed, at which z we evaluate eps and dlogeps/dz + self._zpivot_III_ACH = 8.0 # fixed, at which z we evaluate eps and dlogeps/dz + self.fstarmax = 1.0 # where we cap it + + # Xray parameters + self.Emax_xray_integral = 10000. # Max energy in eV that we integrate up to. Higher than Emax_xray_norm since photons can redshift from higher z + if(self.E0_xray < constants.EN_ION_HI): + print("What the heck? How can E0_XRAY < EN_ION_HI?") + + # table with how many energies we integrate over + self.Nen_xray = 30 + self._log10EMIN_INTEGRATE = np.log10(self.E0_xray/2.0) # to account for photons coming from higher z that redshift + self._log10EMAX_INTEGRATE = np.log10(self.Emax_xray_integral) + self.Energylist = np.logspace(self._log10EMIN_INTEGRATE,self._log10EMAX_INTEGRATE,self.Nen_xray) # in eV + self.dlogEnergy = (self._log10EMAX_INTEGRATE - self._log10EMIN_INTEGRATE)/(self.Nen_xray-1.0)*np.log(10.) # to get dlog instead of dlog10 + + # Reionization parameters + if CosmoParams.Flag_emulate_21cmfast: + self.clumping = 2.0 # this is the 21cmFAST value + # number of ionizing photons per baryon + self.N_ion_perbaryon_II = 5000 # fixed for PopII-type (Salpeter) + if CosmoParams.Flag_emulate_21cmfast: + self.N_ion_perbaryon_III = 44000 # fixed for PopIII-type, from Klessen & Glover 2023 Table A2 (2303.12500) + else: + self.N_ion_perbaryon_III = 52480 + + ### HAC: LW feedback parameters + if not self.USE_LW_FEEDBACK: + self.A_LW = 0.0 + self.beta_LW = 0.0 + # number of LW photons per baryon + if not CosmoParams.Flag_emulate_21cmfast: + self.N_LW_II = 6200.0 #assuming BL05 stellar spectrum, equal to N_alpha_perbaryon_II * fraction of photons that fall in the LW band + self.N_LW_III = 12900.0 #assuming Intermediate IMF from 2202.02099, equal to 4.86e-22 / (11.9 * u.eV).to(u.erg).value * 5.8e14 + else: + popIIcorrection = 0.6415670418531249/2.5 #scaling used by 21cmfast to get correct number of Pop II LW photons per baryon + self.N_LW_II = popIIcorrection * self.N_alpha_perbaryon_II + popIIIcorrection = 0.7184627927009317/6.5 #scaling used by 21cmfast to get correct number of Pop III LW photons per baryon + self.N_LW_III = popIIIcorrection * self.N_alpha_perbaryon_III + + ### HAC: Relative Velocities parameters + if not CosmoParams.USE_RELATIVE_VELOCITIES: + self.A_vcb = 0.0 + self.beta_vcb = 0.0 + + ### 21cmFAST emulation: SFE parameters + if(self.Mturn_fixed == None): #The FIXED/SHARP routine below only applies to Pop II, not to Pop III + self.FLAG_MTURN_FIXED = False # whether to fix Mturn or use Matom(z) at each z + else: + self.FLAG_MTURN_FIXED = True # whether to fix Mturn or use Matom(z) at each z + + + self._minsigmaPSD = 0.1 #minimum sigma for the PSD, to avoid numerical issues in the FFT + self._maxsigmaPSD = 4.0 #maximum sigma for the PSD, there'll never be enough samples if sigma>~6-10 + self._mintauPSD = 1.0 # Myrminimum tau for the PSD, to avoid numerical issues in the FFT + self._maxtauPSD = 300.0 + + self._tagesMyr = np.logspace(-2, 3, 79) #times (ages) we integrate over at each z, Mh, in Myr (TODO: add precisionboost) + + + self._dt_FFT = 0.3 # FFT timescale resolution, Myr, high to resolve the PS_SFR and window functions well (TODO: add UserParams precisionboost here) + self._N_FFT = int(512/(self._dt_FFT/0.3)) # Recommend to use power of 2 for efficient FFT, resolve up to ~0.5Gyr at least + + + self._omegamin = 2*np.pi/1e3 + self._omegamax = np.pi/1.0 + + + +@dataclass(kw_only=True) +class LF_Parameters: + ''' + sigmaUV: float + Stochasticity (gaussian rms) in the halo-galaxy connection P(MUV | Mh). Default is 0.5. + _kappaUV: float + SFR/LUV. Set by zeus21 to the value from Madau+Dickinson14. + Fully degenerate with epsilon. + _kappaUV_III: float + SFR/LUV for PopIII. Set by zeus21 to the value from Madau+Dickinson14. + Assume X more efficient than PopII. + ''' + + zcenter: float = 6. + zwidth: float = 0.5 + + MUVcenters: np.ndarray | float = _field(default_factory=lambda: np.linspace(-23,-14,100)) + MUVwidths: np.ndarray | float = 0.5 + + FLAG_RENORMALIZE_LUV = False #whether to renormalize the lognormal LUV with sigmaUV to recover or otherwise . Recommend False. + + sigmaUV: float = 0.5 + + log10LHacenters: np.ndarray | float = _field(default_factory=lambda: np.linspace(38,45,10)) + log10LHawidths: np.ndarray | float = 0.5 + + FLAG_COMPUTE_UVLF: bool = True + FLAG_COMPUTE_HaLF: bool = False + + ### Dust parameters for UVLFs + DUST_FLAG: bool = True + DUST_model: str = 'Bouwens13' + HIGH_Z_DUST: bool = True + _zmaxdata: float = 8.0 + C0dust: float = 4.43 + C1dust: float = 1.99 #4.43, 1.99 is Meurer99; 4.54, 2.07 is Overzier01 + _kappaUV: float = _field(init=False) #SFR/LUV, value from Madau+Dickinson14, fully degenerate with epsilon + _kappaUV_III: float = _field(init=False) #SFR/LUV for PopIII. Assume X more efficient than PopII + + sigma_times_AUV_dust: float = 0. + + def __post_init__(self): + schema = { + "DUST_FLAG": (bool, None), + "FLAG_RENORMALIZE_LUV": (bool, None), + "FLAG_COMPUTE_UVLF": (bool, None), + "FLAG_COMPUTE_HaLF": (bool, None), + "HIGH_Z_DUST": (bool, None), + "DUST_model": (str, {"Bouwens13", "Zhao24"}), + } + validate_fields(self, schema) + + + # --- normalize MUV --- + if np.isscalar(self.zcenter): + self.MUVcenters = np.array(self.MUVcenters, dtype=float) + else: + self.MUVcenters = np.atleast_1d(self.MUVcenters).astype(float) + + # --- normalize MUVwidth --- + if np.isscalar(self.MUVwidths): + # broadcast scalar to same length as zcenter + self.MUVwidths = np.full_like(self.MUVcenters, self.MUVwidths, dtype=float) + else: + self.MUVwidths = np.atleast_1d(self.MUVwidths).astype(float) + + # --- consistency check --- + if self.MUVwidths.shape != self.MUVcenters.shape: + raise ValueError( + f"MUVwidth shape {self.MUVwidths.shape} does not match MUVcenter shape {self.MUVcenters.shape}" + ) + + # --- normalize logLHa --- + if np.isscalar(self.log10LHacenters): + self.log10LHacenters = np.array([self.log10LHacenters], dtype=float) + else: + self.log10LHacenters = np.atleast_1d(self.log10LHacenters).astype(float) + + # --- normalize logHazwidth --- + if np.isscalar(self.log10LHawidths): + # broadcast scalar to same length as zcenter + self.log10LHawidths = np.full_like(self.log10LHacenters, self.log10LHawidths, dtype=float) + else: + self.log10LHawidths = np.atleast_1d(self.log10LHawidths).astype(float) + + # --- consistency check --- + if self.log10LHawidths.shape != self.log10LHacenters.shape: + raise ValueError( + f"log10Hawidth shape {self.log10LHawidths.shape} does not match log10Hacenter shape {self.log10LHacenters.shape}" + ) + + ### Dust parameters for UVLFs + self._kappaUV = 1.15e-28 #SFR/LUV, value from Madau+Dickinson14, fully degenerate with epsilon + self._kappaUV_III = self._kappaUV #SFR/LUV for PopIII. Assume X more efficient than PopII + + +def validate_fields(obj, schema: dict): + for field, (expected_type, allowed_values) in schema.items(): + value = getattr(obj, field) + + if not isinstance(value, expected_type): + raise TypeError( + f"{field} must be of type {expected_type.__name__}, got {type(value).__name__}" + ) + + if allowed_values is not None and value not in allowed_values: + raise ValueError( + f"{field} must be one of {allowed_values}, got '{value}'" + ) From 3b72fdaeb01bb05d6defd0696f9e45b9775d28a5 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 6 May 2026 15:34:39 -0400 Subject: [PATCH 02/15] Incorporate v2 changes to new inputs.py --- zeus21/inputs.py | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/zeus21/inputs.py b/zeus21/inputs.py index 30925de..5924427 100644 --- a/zeus21/inputs.py +++ b/zeus21/inputs.py @@ -222,6 +222,7 @@ class Cosmo_Parameters: # Flags Flag_emulate_21cmfast: bool = False USE_RELATIVE_VELOCITIES: bool = False + USE_ANISO_XI_ETA: bool = False HMF_CHOICE: str = "ST" @@ -289,6 +290,7 @@ def __post_init__(self, UserParams): schema = { "Flag_emulate_21cmfast": (bool, None), "USE_RELATIVE_VELOCITIES": (bool, None), + "USE_ANISO_XI_ETA": (bool, None), "HMF_CHOICE": (str, {'ST','Yung'}), } validate_fields(self, schema) @@ -447,10 +449,19 @@ def runclass(self): psi0 = 1 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapezoid(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j0bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) psi2 = -2 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapezoid(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j2bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) - k_eta, P_eta = mcfit.xi2P(rVelIntp, l=0, lowring = True)((6 * psi0**2 + 3 * psi2**2), extrap = False) + if self.USE_ANISO_XI_ETA: + k_eta_para, P_eta_para = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 + psi2)**2, extrap = False) + k_eta_perp, P_eta_perp = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 - psi2/2.0)**2, extrap = False) + + ClassCosmo.pars['k_eta_para'] = k_eta_para[P_eta_para > 0] + ClassCosmo.pars['P_eta_para'] = P_eta_para[P_eta_para > 0] + ClassCosmo.pars['k_eta_perp'] = k_eta_perp[P_eta_perp > 0] + ClassCosmo.pars['P_eta_perp'] = P_eta_perp[P_eta_perp > 0] + else: + k_eta, P_eta = mcfit.xi2P(rVelIntp, l=0, lowring = True)((6 * psi0**2 + 3 * psi2**2), extrap = False) - ClassCosmo.pars['k_eta'] = k_eta[P_eta > 0] - ClassCosmo.pars['P_eta'] = P_eta[P_eta > 0] + ClassCosmo.pars['k_eta'] = k_eta[P_eta > 0] + ClassCosmo.pars['P_eta'] = P_eta[P_eta > 0] # print("HAC: Finished running CLASS a second time to get velocity transfer functions") @@ -480,9 +491,17 @@ def run_correlations(self): ###HAC: Interpolated object for eta power spectrum if self.USE_RELATIVE_VELOCITIES == True: - P_eta_interp = interp1d(self.ClassCosmo.pars['k_eta'], self.ClassCosmo.pars['P_eta'], bounds_error = False, fill_value = 0) - self._PkEtaCF = P_eta_interp(self._klistCF) - self.xiEta_RR_CF = self.get_xi_R1R2(field = 'vcb') + if self.USE_ANISO_XI_ETA: + P_eta_para_interp = interp1d(self.ClassCosmo.pars['k_eta_para'], self.ClassCosmo.pars['P_eta_para'], bounds_error = False, fill_value = 0) + P_eta_perp_interp = interp1d(self.ClassCosmo.pars['k_eta_perp'], self.ClassCosmo.pars['P_eta_perp'], bounds_error = False, fill_value = 0) + self._PkEtaParaCF = P_eta_para_interp(self._klistCF) + self._PkEtaPerpCF = P_eta_perp_interp(self._klistCF) + self.xiEtaPara_RR_CF = self.get_xi_R1R2(field = 'vcb_para') + self.xiEtaPerp_RR_CF = self.get_xi_R1R2(field = 'vcb_perp') + else: + P_eta_interp = interp1d(self.ClassCosmo.pars['k_eta'], self.ClassCosmo.pars['P_eta'], bounds_error = False, fill_value = 0) + self._PkEtaCF = P_eta_interp(self._klistCF) + self.xiEta_RR_CF = self.get_xi_R1R2(field = 'vcb') else: self._PkEtaCF = np.zeros_like(self._PklinCF) self.xiEta_RR_CF = np.zeros_like(self.xi_RR_CF) @@ -500,7 +519,13 @@ def get_xi_R1R2 (self, field = None): _PkRR = np.array([[self._PklinCF]]) * windowR1 * windowR2 elif field == 'vcb': _PkRR = np.array([[self._PkEtaCF]]) * windowR1 * windowR2 + elif field == 'vcb_para': + _PkRR = np.array([[self._PkEtaParaCF]]) * windowR1 * windowR2 + elif field == 'vcb_perp': + _PkRR = np.array([[self._PkEtaPerpCF]]) * windowR1 * windowR2 else: + if self.USE_ANISO_XI_ETA: + raise ValueError('field has to be either delta, vcb_para or vcb_perp in get_xi_R1R2') raise ValueError('field has to be either delta or vcb in get_xi_R1R2') self.rlist_CF, xi_RR_CF = self._xif(_PkRR, extrap = False) From f7a51ec8222d4634a1ac0b22b621564c09883d90 Mon Sep 17 00:00:00 2001 From: Ryan Zhang Date: Wed, 6 May 2026 17:32:23 -0400 Subject: [PATCH 03/15] Separate corrEtaNL computation by aniso flag --- zeus21/correlations.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/zeus21/correlations.py b/zeus21/correlations.py index 5e4e096..a759313 100644 --- a/zeus21/correlations.py +++ b/zeus21/correlations.py @@ -807,10 +807,18 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) corrdNL = self._corrdNL - - corrEtaNL = Cosmo_Parameters.xiEta_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] - corrEtaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] - corrEtaNL = corrEtaNL.reshape(1, *corrEtaNL.shape) + if User_Parameters.USE_ANISO_XI_ETA: + corrEtaParaNL = Cosmo_Parameters.xiEtaPara_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] + corrEtaParaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaParaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] + corrEtaParaNL = corrEtaParaNL.reshape(1, *corrEtaParaNL.shape) + + corrEtaPerpNL = Cosmo_Parameters.xiEtaPerp_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] + corrEtaPerpNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaPerpNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] + corrEtaPerpNL = corrEtaPerpNL.reshape(1, *corrEtaPerpNL.shape) + else: + corrEtaNL = Cosmo_Parameters.xiEta_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] + corrEtaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] + corrEtaNL = corrEtaNL.reshape(1, *corrEtaNL.shape) _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_III #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor @@ -943,4 +951,4 @@ def get_Pk_from_xi(self, rsinput, xiinput): kPf, Pf = mcfit.xi2P(rsinput, l=0, lowring=True)(xiinput, extrap=False) - return kPf, Pf \ No newline at end of file + return kPf, Pf From 17f3a88638bfd5fd436aa2123c0da880fb12d02e Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 6 May 2026 23:20:58 -0400 Subject: [PATCH 04/15] Minor fix --- zeus21/correlations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeus21/correlations.py b/zeus21/correlations.py index a759313..26a3b64 100644 --- a/zeus21/correlations.py +++ b/zeus21/correlations.py @@ -807,7 +807,7 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) corrdNL = self._corrdNL - if User_Parameters.USE_ANISO_XI_ETA: + if Cosmo_Parameters.USE_ANISO_XI_ETA: corrEtaParaNL = Cosmo_Parameters.xiEtaPara_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] corrEtaParaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaParaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] corrEtaParaNL = corrEtaParaNL.reshape(1, *corrEtaParaNL.shape) From 2967001c9a80277aba3ff0d694366dc19067d00a Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 6 May 2026 23:35:42 -0400 Subject: [PATCH 05/15] Incorporate v2 changes into correlations.py --- zeus21/correlations.py | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/zeus21/correlations.py b/zeus21/correlations.py index 26a3b64..b45e54c 100644 --- a/zeus21/correlations.py +++ b/zeus21/correlations.py @@ -770,7 +770,7 @@ def get_all_corrs_IIxIII(self, Cosmo_Parameters, T21_coefficients): return 1 - def get_xi_Sum_2ExpEta(self, xiEta, etaCoeff1, etaCoeff2): + def get_xi_Sum_2ExpEta(self, xiEtaPara, xiEtaPerp, etaCoeff1, etaCoeff2): """ Computes the correlation function of the VCB portion of the SFRD, expressed using sums of two exponentials if rho(z1, x1) / rhobar = Ae^-b tilde(eta) + Ce^-d tilde(eta) and rho(z2, x2) / rhobar = Fe^-g tilde(eta) + He^-k tilde(eta) @@ -791,8 +791,17 @@ def get_xi_Sum_2ExpEta(self, xiEta, etaCoeff1, etaCoeff2): cfDG = ne.evaluate('cc * ff / normDD / normGG') chDK = ne.evaluate('cc * hh / normDD / normKK') - #The below involves horribly long writing, but breaking this into pieces makes for slightly longer computation time - xiNumerator = ne.evaluate('afBG * (1 / (1 - 6*bb * gg * xiEta / ((1+2*bb)*(1+2*gg)))**(3/2) - 1) + ahBK * (1 / (1 - 6*bb * kk * xiEta / ((1+2*bb)*(1+2*kk)))**(3/2) - 1) + cfDG * (1 / (1 - 6*dd * gg * xiEta / ((1+2*dd)*(1+2*gg)))**(3/2) - 1) + chDK * (1 / (1 - 6*dd * kk * xiEta / ((1+2*dd)*(1+2*kk)))**(3/2) - 1)') + if xiEtaPerp is None: + #The below involves horribly long writing, but breaking this into pieces makes for slightly longer computation time + xiNumerator = ne.evaluate('afBG * (1 / (1 - 6*bb * gg * xiEta / ((1+2*bb)*(1+2*gg)))**(3/2) - 1) + ahBK * (1 / (1 - 6*bb * kk * xiEta / ((1+2*bb)*(1+2*kk)))**(3/2) - 1) + cfDG * (1 / (1 - 6*dd * gg * xiEta / ((1+2*dd)*(1+2*gg)))**(3/2) - 1) + chDK * (1 / (1 - 6*dd * kk * xiEta / ((1+2*dd)*(1+2*kk)))**(3/2) - 1)') + else: + xiNumerator = ne.evaluate( + 'afBG * (1 / ((1 - 6*bb*gg*xiEtaPara/((1+2*bb)*(1+2*gg))) * (1 - 6*bb*gg*xiEtaPerp/((1+2*bb)*(1+2*gg)))**2)**(1/2) - 1)' + ' + ahBK * (1 / ((1 - 6*bb*kk*xiEtaPara/((1+2*bb)*(1+2*kk))) * (1 - 6*bb*kk*xiEtaPerp/((1+2*bb)*(1+2*kk)))**2)**(1/2) - 1)' + ' + cfDG * (1 / ((1 - 6*dd*gg*xiEtaPara/((1+2*dd)*(1+2*gg))) * (1 - 6*dd*gg*xiEtaPerp/((1+2*dd)*(1+2*gg)))**2)**(1/2) - 1)' + ' + chDK * (1 / ((1 - 6*dd*kk*xiEtaPara/((1+2*dd)*(1+2*kk))) * (1 - 6*dd*kk*xiEtaPerp/((1+2*dd)*(1+2*kk)))**2)**(1/2) - 1)' + ) + xiDenominator = ne.evaluate('afBG + ahBK + cfDG + chDK') xiTotal = ne.evaluate('xiNumerator / xiDenominator') @@ -844,7 +853,10 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) expGammaCorr = ne.evaluate('exp(gammaCorrdNL) - 1') # equivalent to np.exp(gammaTimesCorrdNL)-1.0 if Cosmo_Parameters.USE_RELATIVE_VELOCITIES == True: - etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaNL, vcbCoeffsR1, vcbCoeffsR2) + if Cosmo_Parameters.USE_ANISO_XI_ETA: + etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaParaNL, corrEtaPerpNL, vcbCoeffsR1, vcbCoeffsR2) + else: + etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaNL, None, vcbCoeffsR1, vcbCoeffsR2) totalCorr = ne.evaluate('expGammaCorr * etaCorr_xa + expGammaCorr + etaCorr_xa - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order else: totalCorr = ne.evaluate('expGammaCorr - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order @@ -875,7 +887,11 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) coeffsXaTxALL = coeffzp2Tx * coeffmatrixxaTx corrdNLBIG = corrdNL[:,:, np.newaxis, :, :] - corrEtaNLBIG = corrEtaNL[:,:, np.newaxis, :, :] + if Cosmo_Parameters.USE_ANISO_XI_ETA: + corrEtaParaNLBIG = corrEtaParaNL[:,:, np.newaxis, :, :] + corrEtaPerpNLBIG = corrEtaPerpNL[:,:, np.newaxis, :, :] + else: + corrEtaNLBIG = corrEtaNL[:,:, np.newaxis, :, :] vcbCoeffsR1 = vcbCoeffsR1[:,:,:,:,:] vcbCoeffsR2 = np.transpose(vcbCoeffsR1, (0,3,4,1,2)) @@ -886,13 +902,20 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) for ir in range(len(Cosmo_Parameters._Rtabsmoo)): corrdNL = corrdNLBIG[:,:,:,:,ir] - corrEtaNL = corrEtaNLBIG[:,:,:,:,ir] + if Cosmo_Parameters.USE_ANISO_XI_ETA: + corrEtaParaNL = corrEtaParaNLBIG[:,:,:,:,ir] + corrEtaPerpNL = corrEtaPerpNLBIG[:,:,:,:,ir] + else: + corrEtaNL = corrEtaNLBIG[:,:,:,:,ir] gammaCorrdNL = ne.evaluate('gammamatrixR1R2 * corrdNL') expGammaCorrdNL = ne.evaluate('exp(gammaCorrdNL) - 1') if Cosmo_Parameters.USE_RELATIVE_VELOCITIES == True: - etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaNL, vcbCoeffsR1, vcbCoeffsR2) + if Cosmo_Parameters.USE_ANISO_XI_ETA: + etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaParaNL, corrEtaPerpNL, vcbCoeffsR1, vcbCoeffsR2) + else: + etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaNL, None, vcbCoeffsR1, vcbCoeffsR2) totalCorr = ne.evaluate('expGammaCorrdNL * etaCorr_Tx + expGammaCorrdNL + etaCorr_Tx - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order else: totalCorr = ne.evaluate('expGammaCorrdNL - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order From e22adc7358a6ec985a108c941bdb0544ee918360 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 6 May 2026 23:54:03 -0400 Subject: [PATCH 06/15] Minor fix in xi_Sum function --- zeus21/correlations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zeus21/correlations.py b/zeus21/correlations.py index b45e54c..f13167c 100644 --- a/zeus21/correlations.py +++ b/zeus21/correlations.py @@ -792,6 +792,7 @@ def get_xi_Sum_2ExpEta(self, xiEtaPara, xiEtaPerp, etaCoeff1, etaCoeff2): chDK = ne.evaluate('cc * hh / normDD / normKK') if xiEtaPerp is None: + xiEta = xiEtaPara #The below involves horribly long writing, but breaking this into pieces makes for slightly longer computation time xiNumerator = ne.evaluate('afBG * (1 / (1 - 6*bb * gg * xiEta / ((1+2*bb)*(1+2*gg)))**(3/2) - 1) + ahBK * (1 / (1 - 6*bb * kk * xiEta / ((1+2*bb)*(1+2*kk)))**(3/2) - 1) + cfDG * (1 / (1 - 6*dd * gg * xiEta / ((1+2*dd)*(1+2*gg)))**(3/2) - 1) + chDK * (1 / (1 - 6*dd * kk * xiEta / ((1+2*dd)*(1+2*kk)))**(3/2) - 1)') else: From 8ad7ec76cd879ca70fc9fcee04357911f8c3f025 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 00:31:58 -0400 Subject: [PATCH 07/15] Patch against use_vcb=False but use_aniso=True input --- zeus21/correlations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zeus21/correlations.py b/zeus21/correlations.py index f13167c..b2ac7f0 100644 --- a/zeus21/correlations.py +++ b/zeus21/correlations.py @@ -817,7 +817,7 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) corrdNL = self._corrdNL - if Cosmo_Parameters.USE_ANISO_XI_ETA: + if Cosmo_Parameters.USE_ANISO_XI_ETA and Cosmo_Parameters.USE_RELATIVE_VELOCITIES: corrEtaParaNL = Cosmo_Parameters.xiEtaPara_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] corrEtaParaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaParaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] corrEtaParaNL = corrEtaParaNL.reshape(1, *corrEtaParaNL.shape) @@ -888,7 +888,7 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) coeffsXaTxALL = coeffzp2Tx * coeffmatrixxaTx corrdNLBIG = corrdNL[:,:, np.newaxis, :, :] - if Cosmo_Parameters.USE_ANISO_XI_ETA: + if Cosmo_Parameters.USE_ANISO_XI_ETA and Cosmo_Parameters.USE_RELATIVE_VELOCITIES: corrEtaParaNLBIG = corrEtaParaNL[:,:, np.newaxis, :, :] corrEtaPerpNLBIG = corrEtaPerpNL[:,:, np.newaxis, :, :] else: @@ -903,7 +903,7 @@ def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients) for ir in range(len(Cosmo_Parameters._Rtabsmoo)): corrdNL = corrdNLBIG[:,:,:,:,ir] - if Cosmo_Parameters.USE_ANISO_XI_ETA: + if Cosmo_Parameters.USE_ANISO_XI_ETA and Cosmo_Parameters.USE_RELATIVE_VELOCITIES: corrEtaParaNL = corrEtaParaNLBIG[:,:,:,:,ir] corrEtaPerpNL = corrEtaPerpNLBIG[:,:,:,:,ir] else: From 2f32a3a5987220b5edbea2f84e5ee16efe04cab3 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 00:43:00 -0400 Subject: [PATCH 08/15] Add import interp1d --- zeus21/cosmology.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zeus21/cosmology.py b/zeus21/cosmology.py index 3ae4bc7..3103666 100644 --- a/zeus21/cosmology.py +++ b/zeus21/cosmology.py @@ -14,6 +14,7 @@ import numpy as np from scipy.interpolate import RegularGridInterpolator +from scipy.interpolate import interp1d from . import constants from .inputs import Cosmo_Parameters From bd2c2251b8fbcff8ff99a5d1be157fa151ea2062 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 01:27:35 -0400 Subject: [PATCH 09/15] Update cosmo_wrapper to match docstring cosmo_wrapper now takes user-specified cosmo parameters so that Cosmo_Parameters class is called with all kwargs; now returns ClassCosmo --- zeus21/cosmology.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/zeus21/cosmology.py b/zeus21/cosmology.py index 3103666..0279eec 100644 --- a/zeus21/cosmology.py +++ b/zeus21/cosmology.py @@ -19,16 +19,31 @@ from . import constants from .inputs import Cosmo_Parameters -def cosmo_wrapper(User_Parameters): +Cosmo_Param_Fields = ( + 'omegab', 'omegac', 'h_fid', 'As', 'ns', 'tau_fid', + 'kmax_CLASS', 'zmax_CLASS', 'zmin_CLASS', + 'Rs_min', 'Rs_max', + 'Flag_emulate_21cmfast', 'USE_RELATIVE_VELOCITIES', + 'USE_ANISO_XI_ETA', 'HMF_CHOICE', +) + +def cosmo_wrapper(User_Parameters, Cosmo_Parameters_Input): """ - Wrapper function for all the cosmology. It takes Cosmo_Parameters_Input and returns: - Cosmo_Parameters, Class_Cosmo, Correlations, HMF_interpolator + Wrapper function for all the cosmology. + It takes User_Parameters, Cosmo_Parameters_Input and returns: + Cosmo_Parameters, Class_Cosmo, HMF_interpolator """ - - CosmoParams = Cosmo_Parameters(User_Parameters) + kwargs = { + name: getattr(Cosmo_Parameters_Input, name) + for name in Cosmo_Param_Fields + if hasattr(Cosmo_Parameters_Input, name) + } + + CosmoParams = Cosmo_Parameters(UserParams=User_Parameters, **kwargs) + ClassCosmo = CosmoParams.ClassCosmo HMFintclass = HMF_interpolator(User_Parameters,CosmoParams) - return CosmoParams, HMFintclass + return CosmoParams, ClassCosmo, HMFintclass From 0ae3ed2a842b23458803e523a1e26cd1ac396e1a Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 02:30:42 -0400 Subject: [PATCH 10/15] Add matplotlib to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 9b133ad..bb61983 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ pytest numpy>=2.0 scipy +matplotlib mcfit numexpr astropy From 39e2095a9e1a153de31198ce3e92ea2d7f6b0f90 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 15:00:12 -0400 Subject: [PATCH 11/15] Add notebook summary of aniso xiEta modification --- aniso_xiEta_modifications.ipynb | 341 ++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 aniso_xiEta_modifications.ipynb diff --git a/aniso_xiEta_modifications.ipynb b/aniso_xiEta_modifications.ipynb new file mode 100644 index 0000000..abea646 --- /dev/null +++ b/aniso_xiEta_modifications.ipynb @@ -0,0 +1,341 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "688c31dd", + "metadata": {}, + "source": [ + "# Anisotropic xiEta comparison\n", + "Compare results with `USE_ANISO_XI_ETA=False` vs `True` (with `USE_RELATIVE_VELOCITIES=True`).\n", + "This notebook focuses on xiEta, the global T21 signal, and the 21-cm power spectrum outputs." + ] + }, + { + "cell_type": "markdown", + "id": "1cc8654d", + "metadata": {}, + "source": [ + "## Notes\n", + "- Runtime depends on CLASS and the SFRD integrals; toggle `FAST_MODE` to speed up.\n", + "- The anisotropic flag only matters when `USE_RELATIVE_VELOCITIES=True`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "aca446c6", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from scipy.interpolate import interp1d\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib as mpl\n", + "from matplotlib.lines import Line2D\n", + "\n", + "from cycler import cycler\n", + "from types import SimpleNamespace\n", + "import zeus21 as zeus21_hack\n", + "\n", + "plt.rc('text', usetex=True)\n", + "plt.rc('font', family='serif', size=12)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "aff3a5c4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/yuranzhang/Zeus21_prod/zeus21/sfrd.py:568: RuntimeWarning: invalid value encountered in log\n", + " darr1_darr2 = np.log(arr1[:,:,midpoint+1]/arr1[:,:,midpoint-1]) / (arr2[:,:,0,midpoint+1] - arr2[:,:,0,midpoint-1])\n", + "/Users/yuranzhang/Zeus21_prod/zeus21/sfrd.py:572: RuntimeWarning: invalid value encountered in log\n", + " der1_II = np.log(arr1[:,:,midpoint]/arr1[:,:,midpoint-1])/(arr2[:,:,0,midpoint] - arr2[:,:,0,midpoint-1]) #ln(y2/y1)/(x2-x1)\n" + ] + } + ], + "source": [ + "UserParams = zeus21_hack.User_Parameters(precisionboost=1.2)\n", + "\n", + "CosmoParams_base = zeus21_hack.Cosmo_Parameters(UserParams=UserParams,\n", + " USE_RELATIVE_VELOCITIES=True)\n", + "AstroParams_base = zeus21_hack.Astro_Parameters(CosmoParams=CosmoParams_base,\n", + " USE_POPIII=True)\n", + "HMFinterp_base = zeus21_hack.HMF_interpolator(User_Parameters=UserParams, \n", + " Cosmo_Parameters=CosmoParams_base)\n", + "Coeffs_base = zeus21_hack.get_T21_coefficients(UserParams, CosmoParams_base, \n", + " AstroParams_base, HMFinterp_base)\n", + "PS_base = zeus21_hack.Power_Spectra(UserParams, CosmoParams_base, AstroParams_base, Coeffs_base)\n", + "\n", + "CosmoParams_aniso = zeus21_hack.Cosmo_Parameters(UserParams=UserParams,\n", + " USE_RELATIVE_VELOCITIES=True,\n", + " USE_ANISO_XI_ETA=True)\n", + "AstroParams_aniso = zeus21_hack.Astro_Parameters(CosmoParams=CosmoParams_aniso,\n", + " USE_POPIII=True)\n", + "HMFinterp_aniso = zeus21_hack.HMF_interpolator(User_Parameters=UserParams, \n", + " Cosmo_Parameters=CosmoParams_aniso)\n", + "Coeffs_aniso = zeus21_hack.get_T21_coefficients(UserParams, CosmoParams_aniso, \n", + " AstroParams_aniso, HMFinterp_aniso)\n", + "PS_aniso = zeus21_hack.Power_Spectra(UserParams, CosmoParams_aniso, AstroParams_aniso, Coeffs_aniso)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b191096d", + "metadata": {}, + "outputs": [], + "source": [ + "rlist = CosmoParams_base._Rtabsmoo\n", + "zlist = Coeffs_base.zintegral\n", + "klist = PS_base.klist_PS\n", + "R1choose = 0\n", + "R2choose = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "48d3f0e5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "xiEta_base = CosmoParams_base.xiEta_RR_CF[R1choose, R2choose, :]\n", + "xiEta_para = CosmoParams_aniso.xiEtaPara_RR_CF[R1choose, R2choose, :]\n", + "xiEta_perp = CosmoParams_aniso.xiEtaPerp_RR_CF[R1choose, R2choose, :]\n", + "\n", + "rdense = np.linspace(rlist[0], rlist[-1], 300)\n", + "xiEta_base_interp = interp1d(rlist, xiEta_base, axis=0, kind='cubic')\n", + "xiEta_para_interp = interp1d(rlist, xiEta_para, axis=0, kind='cubic')\n", + "xiEta_perp_interp = interp1d(rlist, xiEta_perp, axis=0, kind='cubic')\n", + "xiEta_base_dense = xiEta_base_interp(rdense)\n", + "xiEta_para_dense = xiEta_para_interp(rdense)\n", + "xiEta_perp_dense = xiEta_perp_interp(rdense)\n", + "\n", + "colors = mpl.colormaps[\"viridis\"](np.linspace(0.0, 0.8, 3))\n", + "mpl.rcParams[\"axes.prop_cycle\"] = cycler(color=colors)\n", + "\n", + "fig, ax = plt.subplots(figsize=(6, 4))\n", + "ax.plot(rdense, xiEta_base_dense, lw=1, label=r'$\\xi_{\\eta}^{\\mathrm{base}}$')\n", + "ax.plot(rdense, xiEta_para_dense, ls='--', lw=1, label=r'$\\xi_{\\eta}^{\\parallel}$')\n", + "ax.plot(rdense, xiEta_perp_dense, ls='--', lw=1, label=r'$\\xi_{\\eta}^{\\perp}$')\n", + "ax.set_xlim(1e-1, 1e3)\n", + "ax.set_ylim(-1e-2, 1)\n", + "ax.set_xscale('log')\n", + "ax.set_yscale('symlog', linthresh=1e-4)\n", + "ax.set_xlabel(r'$r$ [Mpc]')\n", + "ax.set_ylabel(r'$\\xi_{\\eta}(r)$')\n", + "ax.grid(True, alpha=0.3)\n", + "ax.legend(fontsize=12, frameon=False, loc='lower left')" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "d181b017", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "colors = mpl.colormaps[\"Paired\"].colors\n", + "mpl.rcParams[\"axes.prop_cycle\"] = cycler(color=colors)\n", + "\n", + "Ts_base = 1.0/Coeffs_base._invTs_avg\n", + "Ts_aniso = 1.0/Coeffs_aniso._invTs_avg\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 5), sharex=True, gridspec_kw={'height_ratios':[2,1]})\n", + "\n", + "ax1.plot(zlist, Coeffs_base.Tk_avg, lw=1, label=r'$T_k^{\\rm base}$')\n", + "ax1.plot(zlist, Coeffs_aniso.Tk_avg, ls='--', lw=1, label=r'$T_k^{\\rm aniso}$')\n", + "\n", + "ax1.plot(zlist, Ts_base, lw=1, label=r'$T_S^{\\rm base}$')\n", + "ax1.plot(zlist, Ts_aniso, ls='--', lw=1, label=r'$T_S^{\\rm aniso}$')\n", + "\n", + "ax1.plot(zlist, Coeffs_base.T_CMB, label=r'$T_{\\rm CMB}$')\n", + "\n", + "ax1.set_ylabel(r'Temperatures [K]')\n", + "ax1.set_yscale('log')\n", + "ax1.legend(fontsize=12, frameon=False)\n", + "\n", + "ax2.plot(zlist, (Ts_base - Ts_aniso) / Ts_aniso, 'g', label=r'$T_S$')\n", + "ax2.plot(zlist, (Coeffs_aniso.Tk_avg - Coeffs_base.Tk_avg) / Coeffs_base.Tk_avg, 'c', label=r'$T_k$')\n", + "ax2.axhline(0, color='gray', lw=0.5)\n", + "ax2.set_yscale('symlog', linthresh=1e-7)\n", + "ax2.set_ylabel('frac diff')\n", + "ax2.set_xlabel(r'$z$')\n", + "ax2.legend(fontsize=12, frameon=False)\n", + "plt.tight_layout()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "223627d8", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "z_targets = np.array([10.0, 12.0, 15.0, 17.0, 18.7, 21.0])\n", + "z_idx = [int(np.argmin(np.abs(zlist - z))) for z in z_targets]\n", + "z_samples = zlist[z_idx]\n", + "\n", + "interp_base = interp1d(zlist, PS_base.Deltasq_T21, axis=0, bounds_error=False, fill_value=\"extrapolate\")\n", + "interp_aniso = interp1d(zlist, PS_aniso.Deltasq_T21, axis=0, bounds_error=False, fill_value=\"extrapolate\")\n", + "\n", + "P21_base = interp_base(z_samples)\n", + "P21_aniso = interp_aniso(z_samples)\n", + "frac_diff = (P21_base - P21_aniso) / P21_base\n", + "\n", + "def smooth_1d(values, window=7):\n", + " window = max(3, int(window))\n", + " if window % 2 == 0:\n", + " window += 1\n", + " kernel = np.ones(window) / window\n", + " return np.convolve(values, kernel, mode=\"same\")\n", + "\n", + "# colors = mpl.colormaps[\"plasma\"].resampled(len(z_samples))\n", + "colors = mpl.colormaps[\"plasma\"](np.linspace(0.0, 0.9, len(z_samples)))\n", + "\n", + "fig, (ax_top, ax_bot) = plt.subplots(2, 1, sharex=True, \n", + " figsize=(7, 7), \n", + " gridspec_kw={\"height_ratios\": [1, 1]})\n", + "\n", + "k_mask = (klist > 5e-3) & (klist < 2.0)\n", + "k_trim = klist[k_mask]\n", + "k_dense = np.geomspace(k_trim.min(), k_trim.max(), 300)\n", + "\n", + "for i, z in enumerate(z_samples):\n", + " color = colors[i]\n", + " p1 = P21_base[i][k_mask]\n", + " p2 = P21_aniso[i][k_mask]\n", + " fd = frac_diff[i][k_mask]\n", + "\n", + " interp_logp1 = interp1d(np.log10(k_trim), np.log10(p1), bounds_error=False, fill_value=\"extrapolate\")\n", + " interp_logp2 = interp1d(np.log10(k_trim), np.log10(p2), bounds_error=False, fill_value=\"extrapolate\")\n", + " interp_fd = interp1d(np.log10(k_trim), fd, bounds_error=False, fill_value=\"extrapolate\")\n", + "\n", + " logp1_dense = interp_logp1(np.log10(k_dense))\n", + " logp2_dense = interp_logp2(np.log10(k_dense))\n", + " fd_dense = interp_fd(np.log10(k_dense))\n", + "\n", + " p1_smooth = 10**smooth_1d(logp1_dense, window=6)\n", + " p2_smooth = 10**smooth_1d(logp2_dense, window=6)\n", + " fd_smooth = smooth_1d(fd_dense, window=8)\n", + "\n", + " ax_top.loglog(k_dense, p1_smooth, lw=1, color=color)\n", + " ax_top.loglog(k_dense, p2_smooth, ls=\"--\", lw=1, color=color)\n", + " ax_bot.semilogx(k_dense, fd_smooth, lw=1, color=color)\n", + "\n", + "ax_top.set_ylim(5e-2, 2e2)\n", + "ax_top.set_xlim(1e-2, 1)\n", + "ax_top.set_ylabel(r\"$\\Delta^2_{21}(k)$ [mK$^2$]\")\n", + "ax_bot.set_xlabel(r\"$k$ [1/Mpc]\")\n", + "ax_bot.set_ylabel(r\"(approx$-$aniso)/aniso\")\n", + "ax_bot.axhline(0.0, color=\"0.3\", lw=0.8)\n", + "\n", + "ax_top.grid(True, which=\"both\", axis=\"x\", alpha=0.2)\n", + "ax_top.grid(True, which=\"major\", axis=\"y\", alpha=0.2)\n", + "ax_bot.grid(True, which=\"both\", alpha=0.2)\n", + "\n", + "z_handles = [Line2D([0], [0], color=colors[i], lw=1, ls=\"-\") for i in range(len(z_samples))]\n", + "z_labels = [rf\"$z$={z:.2f}\" for z in z_samples]\n", + "style_handles = [\n", + " Line2D([0], [0], color=\"0.2\", lw=1, ls=\"-\"),\n", + " Line2D([0], [0], color=\"0.2\", lw=1, ls=\"--\"),\n", + "]\n", + "style_labels = [\"base\", \"aniso\"]\n", + "\n", + "legend_z = ax_top.legend(\n", + " z_handles, z_labels, fontsize=12, frameon=False, loc=\"lower right\",\n", + " bbox_to_anchor=(1.0, 0.0), borderaxespad=0.2, handlelength=2.0\n", + " )\n", + "ax_top.add_artist(legend_z)\n", + "ax_top.legend(\n", + " style_handles, style_labels, fontsize=12, frameon=False, loc=\"lower right\",\n", + " bbox_to_anchor=(0.8, 0.0), borderaxespad=0.2, handlelength=2.0\n", + " )\n", + "\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "id": "21ff2769", + "metadata": {}, + "source": [ + "## cosmo_wrapper changes\n", + "- Accepts a duck-typed `Cosmo_Parameters_Input` and forwards supported fields via keyword args.\n", + "- Uses `Cosmo_Parameters(UserParams=...)` so it is compatible with `kw_only=True`.\n", + "- Returns `ClassCosmo` directly from `CosmoParams.ClassCosmo` (no duplicate `runclass` logic).\n", + "- Respects `USE_ANISO_XI_ETA` when provided in the input object." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv (3.10.0)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From aa6f1dcfb596c7179c9c701317d4d67ba748fe4a Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 18:58:35 -0400 Subject: [PATCH 12/15] Update cosmo_wrapper cosmo_wrapper now takes User_Parameters and kwargs --- zeus21/cosmology.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/zeus21/cosmology.py b/zeus21/cosmology.py index 0279eec..0b994e7 100644 --- a/zeus21/cosmology.py +++ b/zeus21/cosmology.py @@ -19,27 +19,29 @@ from . import constants from .inputs import Cosmo_Parameters -Cosmo_Param_Fields = ( - 'omegab', 'omegac', 'h_fid', 'As', 'ns', 'tau_fid', - 'kmax_CLASS', 'zmax_CLASS', 'zmin_CLASS', - 'Rs_min', 'Rs_max', - 'Flag_emulate_21cmfast', 'USE_RELATIVE_VELOCITIES', - 'USE_ANISO_XI_ETA', 'HMF_CHOICE', +from dataclasses import fields + +Cosmo_Param_Fields = tuple( + f.name for f in fields(Cosmo_Parameters) + if f.init and f.name != "UserParams" ) -def cosmo_wrapper(User_Parameters, Cosmo_Parameters_Input): +def cosmo_wrapper(User_Parameters, **kwargs): """ Wrapper function for all the cosmology. - It takes User_Parameters, Cosmo_Parameters_Input and returns: + It takes User_Parameters plus keyword arguments and returns: Cosmo_Parameters, Class_Cosmo, HMF_interpolator """ - kwargs = { - name: getattr(Cosmo_Parameters_Input, name) - for name in Cosmo_Param_Fields - if hasattr(Cosmo_Parameters_Input, name) + cosmo_kwargs = { + name: value + for name, value in kwargs.items() + if name in Cosmo_Param_Fields } + for name in kwargs: + if name not in Cosmo_Param_Fields: + print(f"cosmo_wrapper: ignoring unsupported kwarg '{name}'") - CosmoParams = Cosmo_Parameters(UserParams=User_Parameters, **kwargs) + CosmoParams = Cosmo_Parameters(UserParams=User_Parameters, **cosmo_kwargs) ClassCosmo = CosmoParams.ClassCosmo HMFintclass = HMF_interpolator(User_Parameters,CosmoParams) From bb7e0fcf82a01068c5182663a52d8afd2b0d89c0 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 19:29:57 -0400 Subject: [PATCH 13/15] Update notebook comments --- aniso_xiEta_modifications.ipynb | 119 ++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 22 deletions(-) diff --git a/aniso_xiEta_modifications.ipynb b/aniso_xiEta_modifications.ipynb index abea646..6ac1145 100644 --- a/aniso_xiEta_modifications.ipynb +++ b/aniso_xiEta_modifications.ipynb @@ -5,19 +5,17 @@ "id": "688c31dd", "metadata": {}, "source": [ - "# Anisotropic xiEta comparison\n", - "Compare results with `USE_ANISO_XI_ETA=False` vs `True` (with `USE_RELATIVE_VELOCITIES=True`).\n", - "This notebook focuses on xiEta, the global T21 signal, and the 21-cm power spectrum outputs." + "This notebook shows the effect of $\\xi_\\eta$ anisotropy on the 21-cm power spectrum, controlled by the `USE_ANISO_XI_ETA` flag in this modification.\n", + "\n", + "All other parameter values (except `precisionboost`) are left at default." ] }, { "cell_type": "markdown", - "id": "1cc8654d", + "id": "0580c594", "metadata": {}, "source": [ - "## Notes\n", - "- Runtime depends on CLASS and the SFRD integrals; toggle `FAST_MODE` to speed up.\n", - "- The anisotropic flag only matters when `USE_RELATIVE_VELOCITIES=True`." + "## Module imports" ] }, { @@ -42,6 +40,22 @@ "plt.rc('font', family='serif', size=12)" ] }, + { + "cell_type": "markdown", + "id": "2a1c1872", + "metadata": {}, + "source": [ + "## Zeus setup" + ] + }, + { + "cell_type": "markdown", + "id": "a6a384ad", + "metadata": {}, + "source": [ + "Takes a few minutes to run." + ] + }, { "cell_type": "code", "execution_count": 2, @@ -62,6 +76,7 @@ "source": [ "UserParams = zeus21_hack.User_Parameters(precisionboost=1.2)\n", "\n", + "# Base code\n", "CosmoParams_base = zeus21_hack.Cosmo_Parameters(UserParams=UserParams,\n", " USE_RELATIVE_VELOCITIES=True)\n", "AstroParams_base = zeus21_hack.Astro_Parameters(CosmoParams=CosmoParams_base,\n", @@ -72,6 +87,7 @@ " AstroParams_base, HMFinterp_base)\n", "PS_base = zeus21_hack.Power_Spectra(UserParams, CosmoParams_base, AstroParams_base, Coeffs_base)\n", "\n", + "# With anisotropic xi_eta\n", "CosmoParams_aniso = zeus21_hack.Cosmo_Parameters(UserParams=UserParams,\n", " USE_RELATIVE_VELOCITIES=True,\n", " USE_ANISO_XI_ETA=True)\n", @@ -84,6 +100,14 @@ "PS_aniso = zeus21_hack.Power_Spectra(UserParams, CosmoParams_aniso, AstroParams_aniso, Coeffs_aniso)" ] }, + { + "cell_type": "markdown", + "id": "04a20c89", + "metadata": {}, + "source": [ + "## Plot \\& compare" + ] + }, { "cell_type": "code", "execution_count": 3, @@ -98,19 +122,27 @@ "R2choose = 0" ] }, + { + "cell_type": "markdown", + "id": "79678937", + "metadata": {}, + "source": [ + "### $\\xi_\\eta(r)$" + ] + }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 4, "id": "48d3f0e5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 29, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, @@ -155,15 +187,23 @@ "ax.legend(fontsize=12, frameon=False, loc='lower left')" ] }, + { + "cell_type": "markdown", + "id": "ebfb90a0", + "metadata": {}, + "source": [ + "### Temperatures (no difference)" + ] + }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 5, "id": "d181b017", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -187,25 +227,29 @@ "ax1.plot(zlist, Ts_base, lw=1, label=r'$T_S^{\\rm base}$')\n", "ax1.plot(zlist, Ts_aniso, ls='--', lw=1, label=r'$T_S^{\\rm aniso}$')\n", "\n", - "ax1.plot(zlist, Coeffs_base.T_CMB, label=r'$T_{\\rm CMB}$')\n", + "ax1.plot(zlist, Coeffs_base.T_CMB, label=r'$T_{\\rm CMB}^{\\rm base}$')\n", + "ax1.plot(zlist, Coeffs_aniso.T_CMB, ls='--', label=r'$T_{\\rm CMB}^{\\rm aniso}$')\n", "\n", "ax1.set_ylabel(r'Temperatures [K]')\n", "ax1.set_yscale('log')\n", + "ax1.tick_params(direction='in')\n", "ax1.legend(fontsize=12, frameon=False)\n", "\n", - "ax2.plot(zlist, (Ts_base - Ts_aniso) / Ts_aniso, 'g', label=r'$T_S$')\n", - "ax2.plot(zlist, (Coeffs_aniso.Tk_avg - Coeffs_base.Tk_avg) / Coeffs_base.Tk_avg, 'c', label=r'$T_k$')\n", + "ax2.plot(zlist, (Ts_base - Ts_aniso) / Ts_aniso, 'g', ls='--', label=r'$T_S$')\n", + "ax2.plot(zlist, (Coeffs_aniso.Tk_avg - Coeffs_base.Tk_avg) / Coeffs_base.Tk_avg, 'c', ls='-.', label=r'$T_k$')\n", + "ax2.plot(zlist, (Coeffs_base.T_CMB - Coeffs_aniso.T_CMB) / Coeffs_aniso.T_CMB, 'm', ls=':', label=r'$T_{\\rm CMB}$')\n", "ax2.axhline(0, color='gray', lw=0.5)\n", "ax2.set_yscale('symlog', linthresh=1e-7)\n", "ax2.set_ylabel('frac diff')\n", "ax2.set_xlabel(r'$z$')\n", - "ax2.legend(fontsize=12, frameon=False)\n", + "ax2.tick_params(direction='in')\n", + "ax2.legend(fontsize=12, frameon=False, loc='lower right')\n", "plt.tight_layout()\n" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 6, "id": "223627d8", "metadata": {}, "outputs": [ @@ -309,11 +353,42 @@ "id": "21ff2769", "metadata": {}, "source": [ - "## cosmo_wrapper changes\n", - "- Accepts a duck-typed `Cosmo_Parameters_Input` and forwards supported fields via keyword args.\n", - "- Uses `Cosmo_Parameters(UserParams=...)` so it is compatible with `kw_only=True`.\n", - "- Returns `ClassCosmo` directly from `CosmoParams.ClassCosmo` (no duplicate `runclass` logic).\n", - "- Respects `USE_ANISO_XI_ETA` when provided in the input object." + "## `cosmo_wrapper` changes" + ] + }, + { + "cell_type": "markdown", + "id": "e91c14a2", + "metadata": {}, + "source": [ + "`cosmo_wrapper` has been edited based on the existing docstring. It now takes keyword arguments as well as the original `User_Parameters`; any kwarg that appears in the `Cosmo_Parameters` fields will be passed to `Cosmo_Parameters`. This ensures that all user-defined cosmo parameters and flags are respected by the wrapper. It now returns `ClassCosmo` alongside the original `CosmoParams` and `HMFintclass`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "35df9656", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "0.02242\n", + "\n" + ] + } + ], + "source": [ + "UserParams_w = zeus21_hack.User_Parameters(precisionboost=1.2)\n", + "\n", + "CosmoParams_w, ClassCosmo_w, HMFintclass_w = zeus21_hack.cosmo_wrapper(UserParams_w,\n", + " omegab=0.02242,\n", + " USE_RELATIVE_VELOCITIES=True)\n", + "print(CosmoParams_w.USE_RELATIVE_VELOCITIES)\n", + "print(ClassCosmo_w.omega_b)\n", + "print(HMFintclass_w.sigmaintlog)" ] } ], From 7e29e62b40c049dd150e4aec5bf15727a3caa301 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 22:33:30 -0400 Subject: [PATCH 14/15] Remove dev files --- zeus21/correlations_v2.py | 1024 ------------------------------------- zeus21/cosmology_v2.py | 536 ------------------- zeus21/inputs_v2.py | 996 ------------------------------------ 3 files changed, 2556 deletions(-) delete mode 100644 zeus21/correlations_v2.py delete mode 100644 zeus21/cosmology_v2.py delete mode 100644 zeus21/inputs_v2.py diff --git a/zeus21/correlations_v2.py b/zeus21/correlations_v2.py deleted file mode 100644 index 3b9ce03..0000000 --- a/zeus21/correlations_v2.py +++ /dev/null @@ -1,1024 +0,0 @@ -""" - -Code to compute correlation functions from power spectra and functions of them. Holds two classes: Correlations (with matter correlation functions smoothed over different R), and Power_Spectra (which will compute and hold the 21-cm power spectrum and power for derived quantities like xa, Tk, etc.) - -Author: Julian B. Muñoz -UT Austin and Harvard CfA - January 2023 - -Edited by Hector Afonso G. Cruz -JHU - July 2024 - -Edited by Sarah Libanore -BGU - July 2025 - -""" - -import numpy as np -from scipy.interpolate import UnivariateSpline -from scipy.interpolate import interp1d -import mcfit -from scipy.special import gammaincc #actually very fast, no need to approximate -import numexpr as ne - -from . import constants -from . import cosmology -from . import z21_utilities - - - -class Power_Spectra: - "Get power spetrum from correlation functions and coefficients" - - def __init__(self, User_Parameters, Cosmo_Parameters, Astro_Parameters, T21_coefficients, RSD_MODE=1): - -# print("STEP 0: Variable Setup") - #set up some variables - self._rs_input_mcfit = Cosmo_Parameters.rlist_CF #just to make notation simpler - self.klist_PS = Cosmo_Parameters._klistCF - self.RSD_MODE = RSD_MODE #redshift-space distortion mode. 0 = None (mu=0), 1 = Spherical avg (like 21-cmFAST), 2 = LoS only (mu=1). 2 is more observationally relevant, whereas 1 the standard assumption in sims. 0 is just for comparison with real-space #TODO: mode to save at different mu - - #first get the linear window functions -- note it already has growth factor in it, so it multiplies Pmatter(z=0) - #fix some arrays: TYTYTY HERE - - self._zGreaterMatrix100, self._iRnonlinear, self._corrdNL = self._prepare_corr_arrays(Cosmo_Parameters, T21_coefficients) - - self.kwindow, self.windowalpha_II = self.get_xa_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 2) - self._kwindowX, self.windowxray_II = self.get_Tx_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 2) - - - if Astro_Parameters.USE_POPIII == True: - # SarahLibanore: add AstroParams to use flag on quadratic order - self.kwindow, self.windowalpha_III = self.get_xa_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 3) - # SarahLibanore: add AstroParams to use flag on quadratic order - self._kwindowX, self.windowxray_III = self.get_Tx_window(Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 3) - else: - self.windowalpha_III = np.zeros_like(self.windowalpha_II) - self.windowxray_III = np.zeros_like(self.windowxray_II) - - #calculate some growth etc, and the bubble biases for the xHI linear window function: - self._lingrowthd = cosmology.growth(Cosmo_Parameters, T21_coefficients.zintegral) - - - - ############################## - -# print("STEP 1: Computing Nonlinear Power Spectra") - #finally, get all the nonlinear correlation functions: -# print("Computing Pop II-dependent power spectra") - # SarahLibanore: add AstroParams to use flag on quadratic order - self.get_all_corrs_II(Astro_Parameters, User_Parameters, Cosmo_Parameters, T21_coefficients) - - if Astro_Parameters.USE_POPIII == True: -# print("Computing Pop IIxIII-dependent cross power spectra") - self.get_all_corrs_IIxIII(Cosmo_Parameters, T21_coefficients) - -# print("Computing Pop III-dependent power spectra") - self.get_all_corrs_III(User_Parameters, Cosmo_Parameters, T21_coefficients) - else: - #bypases Pop III correlation routine and sets all Pop III-dependent correlations to zero - self._IIxIII_deltaxi_xa = np.zeros_like(self._II_deltaxi_xa) - self._IIxIII_deltaxi_Tx = np.zeros_like(self._II_deltaxi_xa) - self._IIxIII_deltaxi_xaTx = np.zeros_like(self._II_deltaxi_xa) - - self._III_deltaxi_xa = np.zeros_like(self._II_deltaxi_xa) - self._III_deltaxi_dxa = np.zeros_like(self._II_deltaxi_xa) - - self._III_deltaxi_Tx = np.zeros_like(self._II_deltaxi_xa) - self._III_deltaxi_xaTx = np.zeros_like(self._II_deltaxi_xa) - self._III_deltaxi_dTx = np.zeros_like(self._II_deltaxi_xa) - - - self._k3over2pi2 = (self.klist_PS**3)/(2.0 * np.pi**2) - - #and now define power spectra: - #for xalpha, first linear - self._Pk_xa_lin_II = self.windowalpha_II**2 * Cosmo_Parameters._PklinCF - self._Pk_xa_lin_III = self.windowalpha_III**2 * Cosmo_Parameters._PklinCF ###TO DO (linearized VCB flucts):+ self.windowalphaVel_III**2 * Cosmo_Parameters._PkEtaCF - self._Pk_xa_lin_IIxIII = 2* self.windowalpha_II * self.windowalpha_III * Cosmo_Parameters._PklinCF #Pop IIxIII cross term doesn't have a velocity component - - self.Deltasq_xa_lin_II = self._Pk_xa_lin_II * self._k3over2pi2 #note that it still has units of xa_avg - self.Deltasq_xa_lin_III = self._Pk_xa_lin_III * self._k3over2pi2 #note that it still has units of xa_avg - self.Deltasq_xa_lin_IIxIII = self._Pk_xa_lin_IIxIII * self._k3over2pi2 #note that it still has units of xa_avg - - #nonlinear corrections too: - self._d_Pk_xa_nl_II = self.get_list_PS(self._II_deltaxi_xa, T21_coefficients.zintegral) - self._d_Pk_xa_nl_III = self.get_list_PS(self._III_deltaxi_xa, T21_coefficients.zintegral) #velocity correlations already embedded in nonlinear computation - self._d_Pk_xa_nl_IIxIII = self.get_list_PS(self._IIxIII_deltaxi_xa, T21_coefficients.zintegral) - - self.Deltasq_xa_II = self.Deltasq_xa_lin_II + self._d_Pk_xa_nl_II * self._k3over2pi2 #note that it still has units of xa_avg - self.Deltasq_xa_III = self.Deltasq_xa_lin_III + self._d_Pk_xa_nl_III * self._k3over2pi2 #note that it still has units of xa_avg - self.Deltasq_xa_IIxIII = self.Deltasq_xa_lin_IIxIII + self._d_Pk_xa_nl_IIxIII * self._k3over2pi2 #note that it still has units of xa_avg - - - ############################## - - - #and same for xray - self._Pk_Tx_lin_II = self.windowxray_II**2 * Cosmo_Parameters._PklinCF - self._Pk_Tx_lin_III = self.windowxray_III**2 * Cosmo_Parameters._PklinCF ###TO DO (linearized VCB flucts):+ self.windowxrayVel_III**2 * Cosmo_Parameters._PkEtaCF - self._Pk_Tx_lin_IIxIII = 2* self.windowxray_II * self.windowxray_III * Cosmo_Parameters._PklinCF #Pop IIxIII cross term doesn't have a velocity component - - self.Deltasq_Tx_lin_II = self._Pk_Tx_lin_II * self._k3over2pi2 - self.Deltasq_Tx_lin_III = self._Pk_Tx_lin_III * self._k3over2pi2 - self.Deltasq_Tx_lin_IIxIII = self._Pk_Tx_lin_IIxIII * self._k3over2pi2 - - self._d_Pk_Tx_nl_II = self.get_list_PS(self._II_deltaxi_Tx, T21_coefficients.zintegral) - self._d_Pk_Tx_nl_III = self.get_list_PS(self._III_deltaxi_Tx, T21_coefficients.zintegral) - self._d_Pk_Tx_nl_IIxIII = self.get_list_PS(self._IIxIII_deltaxi_Tx, T21_coefficients.zintegral) - - self.Deltasq_Tx_II = self.Deltasq_Tx_lin_II + self._d_Pk_Tx_nl_II * self._k3over2pi2 - self.Deltasq_Tx_III = self.Deltasq_Tx_lin_III + self._d_Pk_Tx_nl_III * self._k3over2pi2 - self.Deltasq_Tx_IIxIII = self.Deltasq_Tx_lin_IIxIII + self._d_Pk_Tx_nl_IIxIII * self._k3over2pi2 - - - ############################## - - - #and their cross correlation - self._Pk_xaTx_lin_II = self.windowalpha_II * self.windowxray_II * Cosmo_Parameters._PklinCF - self._Pk_xaTx_lin_III = self.windowalpha_III * self.windowxray_III * Cosmo_Parameters._PklinCF ###TO DO (linearized VCB flucts):+ self.windowalphaVel_III * self.windowxrayVel_III * Cosmo_Parameters._PkEtaCF - self._Pk_xaTx_lin_IIxIII = (self.windowalpha_II * self.windowxray_III + self.windowalpha_III * self.windowxray_II) * Cosmo_Parameters._PklinCF - - self.Deltasq_xaTx_lin_II = self._Pk_xaTx_lin_II * self._k3over2pi2 - self.Deltasq_xaTx_lin_III = self._Pk_xaTx_lin_III * self._k3over2pi2 - self.Deltasq_xaTx_lin_IIxIII = self._Pk_xaTx_lin_IIxIII * self._k3over2pi2 - - self._d_Pk_xaTx_nl_II = self.get_list_PS(self._II_deltaxi_xaTx, T21_coefficients.zintegral) - self._d_Pk_xaTx_nl_III = self.get_list_PS(self._III_deltaxi_xaTx, T21_coefficients.zintegral) - self._d_Pk_xaTx_nl_IIxIII = self.get_list_PS(self._IIxIII_deltaxi_xaTx, T21_coefficients.zintegral) - - self.Deltasq_xaTx_II = self.Deltasq_xaTx_lin_II + self._d_Pk_xaTx_nl_II * self._k3over2pi2 #note that it still has units of xa_avg - self.Deltasq_xaTx_III = self.Deltasq_xaTx_lin_III + self._d_Pk_xaTx_nl_III * self._k3over2pi2 #note that it still has units of xa_avg - self.Deltasq_xaTx_IIxIII = self.Deltasq_xaTx_lin_IIxIII + self._d_Pk_xaTx_nl_IIxIII * self._k3over2pi2 #note that it still has units of xa_avg - - - ############################## - - - #and the same for deltaNL and its cross terms: - self._Pk_d_lin = np.outer(self._lingrowthd**2, Cosmo_Parameters._PklinCF) #No Pop II or III contribution - self.Deltasq_d_lin = self._Pk_d_lin * self._k3over2pi2 #note that it still has units of xa_avg - - self._Pk_dxa_lin_II = (self.windowalpha_II.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF - self._Pk_dxa_lin_III = (self.windowalpha_III.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF #No velocity component - - self._Pk_dTx_lin_II = (self.windowxray_II.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF - self._Pk_dTx_lin_III = (self.windowxray_III.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF #No velocity component - - self.Deltasq_dxa_lin_II = self._Pk_dxa_lin_II * self._k3over2pi2 - self.Deltasq_dxa_lin_III = self._Pk_dxa_lin_III * self._k3over2pi2 #No velocity component - - self.Deltasq_dTx_lin_II = self._Pk_dTx_lin_II * self._k3over2pi2 - self.Deltasq_dTx_lin_III = self._Pk_dTx_lin_III * self._k3over2pi2 #No velocity component - - self._Pk_d = self._Pk_d_lin - - self._Pk_dxa_II = self._Pk_dxa_lin_II - self._Pk_dxa_III = self._Pk_dxa_lin_III - - self._Pk_dTx_II = self._Pk_dTx_lin_II - self._Pk_dTx_III = self._Pk_dTx_lin_III - - if(User_Parameters.FLAG_DO_DENS_NL): #note that the nonlinear terms (cross and auto) below here have the growth already accounted for - - self._d_Pk_d_nl = self.get_list_PS(self._II_deltaxi_d, T21_coefficients.zintegral) - self._Pk_d += self._d_Pk_d_nl - - self._d_Pk_dxa_nl_II = self.get_list_PS(self._II_deltaxi_dxa, T21_coefficients.zintegral) - self._d_Pk_dxa_nl_III = self.get_list_PS(self._III_deltaxi_dxa, T21_coefficients.zintegral) - self._Pk_dxa_II += self._d_Pk_dxa_nl_II - self._Pk_dxa_III += self._d_Pk_dxa_nl_III - - self._d_Pk_dTx_nl_II = self.get_list_PS(self._II_deltaxi_dTx, T21_coefficients.zintegral) - self._d_Pk_dTx_nl_III = self.get_list_PS(self._III_deltaxi_dTx, T21_coefficients.zintegral) - - self._Pk_dTx_II += self._d_Pk_dTx_nl_II - self._Pk_dTx_III += self._d_Pk_dTx_nl_III - - self.Deltasq_d = self._Pk_d * self._k3over2pi2 - - self.Deltasq_dxa_II = self._Pk_dxa_II * self._k3over2pi2 - self.Deltasq_dxa_III = self._Pk_dxa_III * self._k3over2pi2 - - self.Deltasq_dTx_II = self._Pk_dTx_II * self._k3over2pi2 - self.Deltasq_dTx_III = self._Pk_dTx_III * self._k3over2pi2 - - - ############################## - - - #and xHI too. Linear part does not have bubbles, only delta part - if(constants.FLAG_DO_BUBBLES): - #auto - self._Pk_xion_lin = self.windowxion**2 * Cosmo_Parameters._PklinCF - self.Deltasq_xion_lin = self._Pk_xion_lin * self._k3over2pi2 - - self._d_Pk_xion_nl = self.get_list_PS(self._deltaxi_xi, T21_coefficients.zintegral) - self.Deltasq_xion = self.Deltasq_xion_lin + self._d_Pk_xion_nl * self._k3over2pi2 - - #cross with density - self._Pk_dxion_lin = (self.windowxion.T * self._lingrowthd).T * Cosmo_Parameters._PklinCF - self.Deltasq_dxion_lin = self._Pk_dxion_lin * self._k3over2pi2 - - self._d_Pk_dxion_nl = self.get_list_PS(self._deltaxi_dxi, T21_coefficients.zintegral) - self.Deltasq_dxion = self.Deltasq_dxion_lin + self._d_Pk_dxion_nl * self._k3over2pi2 - - #cross with xa - self._Pk_xaxion_lin = self.windowxion * self.windowalpha * Cosmo_Parameters._PklinCF - self.Deltasq_xaxion_lin = self._Pk_xaxion_lin * self._k3over2pi2 - - self._d_Pk_xaxion_nl = self.get_list_PS(self._deltaxi_xaxi, T21_coefficients.zintegral) - self.Deltasq_xaxion = self.Deltasq_xaxion_lin + self._d_Pk_xaxion_nl * self._k3over2pi2 - - #and cross with Tx - self._Pk_Txxion_lin = self.windowxion * self.windowxray * Cosmo_Parameters._PklinCF - self.Deltasq_Txxion_lin = self._Pk_Txxion_lin * self._k3over2pi2 - - self._d_Pk_Txxion_nl = self.get_list_PS(self._deltaxi_Txxi, T21_coefficients.zintegral) - self.Deltasq_Txxion = self.Deltasq_Txxion_lin + self._d_Pk_Txxion_nl * self._k3over2pi2 - else: - self.Deltasq_xion = np.zeros_like(self.Deltasq_d) - self.Deltasq_xion_lin = np.zeros_like(self.Deltasq_d) - self.Deltasq_dxion = np.zeros_like(self.Deltasq_d) - self.Deltasq_dxion_lin = np.zeros_like(self.Deltasq_d) - self.Deltasq_xaxion = np.zeros_like(self.Deltasq_d) - self.Deltasq_xaxion_lin = np.zeros_like(self.Deltasq_d) - self.Deltasq_Txxion = np.zeros_like(self.Deltasq_d) - self.Deltasq_Txxion_lin = np.zeros_like(self.Deltasq_d) - #These have to be defined even if no EoR bubbles - - - ############################## - -# print('STEP 2: Computing 21-cm Power Spectrum') - #and get the PS of T21 too. - self._betaT = T21_coefficients.T_CMB/T21_coefficients.Tk_avg /(T21_coefficients.invTcol_avg**-1 - T21_coefficients.T_CMB) #multiplies \delta T_x and \delta T_ad [both dimensionful, not \deltaT/T] - self._betaxa = 1./(1. + T21_coefficients.xa_avg)/T21_coefficients.xa_avg #multiplies \delta x_a [again not \delta xa/xa] - - #calculate beta_adiabatic - self._dlingrowthd_dz = cosmology.dgrowth_dz(Cosmo_Parameters, T21_coefficients.zintegral) - - _factor_adi_ = (1+T21_coefficients.zintegral)**2 - _integrand_adi = T21_coefficients.Tk_avg*self._dlingrowthd_dz/_factor_adi_ * T21_coefficients.dlogzint*T21_coefficients.zintegral - - if(Cosmo_Parameters.Flag_emulate_21cmfast==True): - _hizintegral = 0.0 #they do not account for the adiabatic history prior to starting their evolution. It misses ~half of the adiabatic flucts. - else: - #the z>zmax part of the integral we do aside. Assume Tk=Tadiabatic from CLASS. - _zlisthighz_ = np.linspace(T21_coefficients.zintegral[-1], 99., 100) #beyond z=100 need to explictly tell CLASS to save growth - _dgrowthhighz_ = cosmology.dgrowth_dz(Cosmo_Parameters, _zlisthighz_) - _hizintegral = np.trapezoid(cosmology.Tadiabatic(Cosmo_Parameters,_zlisthighz_) - /(1+_zlisthighz_)**2 * _dgrowthhighz_, _zlisthighz_) - - self._betaTad_ = -2./3. * _factor_adi_/self._lingrowthd * (np.cumsum(_integrand_adi[::-1])[::-1] + _hizintegral) #units of Tk_avg. Internal sum goes from high to low z (backwards), minus sign accounts for it properly so it's positive. - self._betaTad_ *= self._betaT #now it's dimensionless, since it multiplies \delta_m(k,z) - - - - self._betad = (1.0 + self._betaTad_)# this includes both the usual (1+d) and the adiabatic Tk contribution. Now we add RSD - if(self.RSD_MODE==0): #no RSD (real space) - pass #nothing to change - elif(self.RSD_MODE==1): #spherically avg'd RSD - self._betad += constants.MU_AVG ** 2 - elif(self.RSD_MODE==2): #LoS RSD (mu=1) - self._betad += constants.MU_LoS ** 2 - else: - print('Error, have to choose an RSD mode! RSD_MODE') - - if(constants.FLAG_DO_BUBBLES): - self._betaxion = - 1.0/T21_coefficients.xHI_avg * np.heaviside(constants.ZMAX_Bubbles - T21_coefficients.zintegral, 0.5) # xion = 1 - xHI, only for zikj', self._allbetas, self._allbetas) - - #Sum Pop II and Pop III contributions - self.Deltasq_d = self.Deltasq_d - self.Deltasq_dxa = self.Deltasq_dxa_II + self.Deltasq_dxa_III - self.Deltasq_dTx = self.Deltasq_dTx_II + self.Deltasq_dTx_III - - self.Deltasq_xa = self.Deltasq_xa_II + self.Deltasq_xa_III + self.Deltasq_xa_IIxIII - self.Deltasq_xaTx = self.Deltasq_xaTx_II + self.Deltasq_xaTx_III + self.Deltasq_xaTx_IIxIII - self.Deltasq_Tx = self.Deltasq_Tx_II + self.Deltasq_Tx_III + self.Deltasq_Tx_IIxIII - - - self._allcorrs = np.array( [[self.Deltasq_d, self.Deltasq_dxa, self.Deltasq_dTx, self.Deltasq_dxion], \ - [self.Deltasq_dxa, self.Deltasq_xa, self.Deltasq_xaTx, self.Deltasq_xaxion], \ - [self.Deltasq_dTx, self.Deltasq_xaTx, self.Deltasq_Tx, self.Deltasq_Txxion], \ - [self.Deltasq_dxion, self.Deltasq_xaxion, self.Deltasq_Txxion, self.Deltasq_xion]]\ - ) - - self.Deltasq_T21 = np.einsum('ijk...,ijkl...->kl...', self._allbetamatrix, self._allcorrs) - self.Deltasq_T21 = (self.Deltasq_T21.T*T21_coefficients.T21avg**2).T - - self.Deltasq_dT21 = (np.einsum('ik...,ikl...->kl...',self._allbetas,self._allcorrs[0]).T*T21_coefficients.T21avg).T - - - #Sum Linear Pop II and Pop III contributions - self.Deltasq_d_lin = self.Deltasq_d_lin - self.Deltasq_dxa_lin = self.Deltasq_dxa_lin_II + self.Deltasq_dxa_lin_III - self.Deltasq_dTx_lin = self.Deltasq_dTx_lin_II + self.Deltasq_dTx_lin_III - - self.Deltasq_xa_lin = self.Deltasq_xa_lin_II + self.Deltasq_xa_lin_III + self.Deltasq_xa_lin_IIxIII - self.Deltasq_xaTx_lin = self.Deltasq_xaTx_lin_II + self.Deltasq_xaTx_lin_III + self.Deltasq_xaTx_lin_IIxIII - self.Deltasq_Tx_lin = self.Deltasq_Tx_lin_II + self.Deltasq_Tx_lin_III + self.Deltasq_Tx_lin_IIxIII - - - self._allcorrs_lin = np.array( [[self.Deltasq_d_lin, self.Deltasq_dxa_lin, self.Deltasq_dTx_lin, self.Deltasq_dxion_lin], \ - [self.Deltasq_dxa_lin, self.Deltasq_xa_lin, self.Deltasq_xaTx_lin, self.Deltasq_xaxion_lin], \ - [self.Deltasq_dTx_lin, self.Deltasq_xaTx_lin, self.Deltasq_Tx_lin, self.Deltasq_Txxion_lin], \ - [self.Deltasq_dxion_lin, self.Deltasq_xaxion_lin, self.Deltasq_Txxion_lin, self.Deltasq_xion_lin]]\ - ) - - self.Deltasq_T21_lin = np.einsum('ijk...,ijkl...->kl...', self._allbetamatrix, self._allcorrs_lin) - self.Deltasq_T21_lin = (self.Deltasq_T21_lin.T*T21_coefficients.T21avg**2).T - - self.Deltasq_dT21_lin = (np.einsum('ik...,ikl...->kl...',self._allbetas,self._allcorrs_lin[0]).T*T21_coefficients.T21avg).T -# print("Power Spectral Routine Done!") - - - - def _prepare_corr_arrays(self, Cosmo_Parameters, T21_coefficients): - zGM = np.copy(T21_coefficients.zGreaterMatrix) - zGM[np.isnan(zGM)] = 100 - iR = np.arange(Cosmo_Parameters.indexmaxNL) - corr = Cosmo_Parameters.xi_RR_CF[np.ix_(iR, iR)] - corr[:Cosmo_Parameters.indexminNL, :Cosmo_Parameters.indexminNL] = \ - corr[Cosmo_Parameters.indexminNL, Cosmo_Parameters.indexminNL] - return zGM, iR, corr.reshape((1, *corr.shape)) - - # SarahLibanore: add AstroParams to use flag on quadratic order - def get_xa_window(self, Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 0): #set pop to 2 or 3, default zero just so python doesn't complain - "Returns the xa window function for all z in zintegral" - - coeffzp = T21_coefficients.coeff1LyAzp - coeffJaxa = T21_coefficients.coeff_Ja_xa - - growthRmatrix = cosmology.growth(Cosmo_Parameters, self._zGreaterMatrix100) - - if pop == 2: - coeffRmatrix = T21_coefficients.coeff2LyAzpRR_II - gammaRmatrix = T21_coefficients.gamma_II_index2D * growthRmatrix - elif pop == 3: - coeffRmatrix = T21_coefficients.coeff2LyAzpRR_III - gammaRmatrix = T21_coefficients.gamma_III_index2D * growthRmatrix - else: - print("Must set pop to either 2 or 3!") - - _wincoeffsMatrix = coeffRmatrix * gammaRmatrix - # SarahLibanore: quadratic order in the lognormal - if Astro_Parameters.quadratic_SFRD_lognormal: - _wincoeffsMatrix *= 1./(1-2.*T21_coefficients.gamma2_II_index2D*T21_coefficients.sigmaofRtab**2) - - if(Cosmo_Parameters.Flag_emulate_21cmfast==False): #do the standard 1D TopHat - _wincoeffsMatrix /=(4*np.pi * Cosmo_Parameters._Rtabsmoo**2) * (Cosmo_Parameters._Rtabsmoo * Cosmo_Parameters._dlogRR) # so we can just use mcfit for logFFT, 1/(4pir^2 * Delta r) - _kwinalpha, _win_alpha = self.get_Pk_from_xi(Cosmo_Parameters._Rtabsmoo, _wincoeffsMatrix) - - else: - _kwinalpha = self.klist_PS - - coeffRgammaRmatrix = coeffRmatrix * gammaRmatrix - coeffRgammaRmatrix = coeffRgammaRmatrix.reshape(*coeffRgammaRmatrix.shape, 1) - - dummyMesh, RtabsmooMesh, kWinAlphaMesh = np.meshgrid(T21_coefficients.zintegral, Cosmo_Parameters._Rtabsmoo, _kwinalpha, indexing = 'ij', sparse = True) - - _win_alpha = coeffRgammaRmatrix * z21_utilities._WinTH(RtabsmooMesh, kWinAlphaMesh) - _win_alpha = np.sum(_win_alpha, axis = 1) - - _win_alpha *= np.array([coeffzp*coeffJaxa]).T - - return _kwinalpha, _win_alpha - - - # SarahLibanore: add AstroParams to use flag on quadratic order - def get_Tx_window(self, Astro_Parameters, Cosmo_Parameters, T21_coefficients, pop = 0): #set pop to 2 or 3, default zero just so python doesn't complain - "Returns the Tx window function for all z in zintegral" - - coeffzp = np.array([T21_coefficients.coeff1Xzp]).T - growthRmatrix = cosmology.growth(Cosmo_Parameters, self._zGreaterMatrix100) - - if pop == 2: - coeffRmatrix = T21_coefficients.coeff2XzpRR_II - gammaRmatrix = T21_coefficients.gamma_II_index2D * growthRmatrix - _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_II#z-dependent, includes 10^40 erg/s/SFR normalizaiton and erg/K conversion factor, and the 1/(1+z)^2 factor to compensate the adiabatic cooling of the Tx olny part - elif pop == 3: - coeffRmatrix = T21_coefficients.coeff2XzpRR_III - gammaRmatrix = T21_coefficients.gamma_III_index2D * growthRmatrix - _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_III - else: - print("Must set pop to either 2 or 3!") - - # SarahLibanore: quadratic order in the lognormal - if Astro_Parameters.quadratic_SFRD_lognormal: - gammaRmatrix *= (1/(1-2.*T21_coefficients.gamma2_II_index2D*T21_coefficients.sigmaofRtab**2)) - - if(Cosmo_Parameters.Flag_emulate_21cmfast==False): #do the standard 1D TopHat - _wincoeffs = coeffRmatrix * gammaRmatrix #array in logR space - _wincoeffs /=(4*np.pi * Cosmo_Parameters._Rtabsmoo**2) * (Cosmo_Parameters._Rtabsmoo * Cosmo_Parameters._dlogRR) # so we can just use mcfit for logFFT, 1/(4pir^2) * Delta r - _kwinTx, _win_Tx_curr = self.get_Pk_from_xi(Cosmo_Parameters._Rtabsmoo, _wincoeffs) - - else: - _kwinTx = self.klist_PS - - coeffRgammaRmatrix = coeffRmatrix * gammaRmatrix - coeffRgammaRmatrix = coeffRgammaRmatrix.reshape(*coeffRgammaRmatrix.shape, 1) - - dummyMesh, RtabsmooMesh, kWinTxMesh = np.meshgrid(T21_coefficients.zintegral, Cosmo_Parameters._Rtabsmoo, _kwinTx, indexing = 'ij', sparse = True) - - _win_Tx_curr = coeffRgammaRmatrix * z21_utilities._WinTH(RtabsmooMesh, kWinTxMesh) - _win_Tx_curr = np.sum(_win_Tx_curr , axis = 1) - - _win_Tx = _win_Tx_curr * coeffzp - _win_Tx = np.cumsum(_win_Tx[::-1], axis = 0)[::-1] - - _win_Tx =_win_Tx * np.array([_coeffTx_units]).T - - return _kwinTx, _win_Tx - - - # SarahLibanore: function modified to include quadratic order - def get_all_corrs_II(self, Astro_Parameters, User_Parameters, Cosmo_Parameters, T21_coefficients): - - "Returns the Pop II components of the correlation functions of all observables at each z in zintegral" - #HAC: I deleted the bubbles and EoR part, to be done later..... - #self._iRnonlinear = np.arange(Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexmaxNL) - - - _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_II #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor - - growthRmatrix = cosmology.growth(Cosmo_Parameters,self._zGreaterMatrix100[:, self._iRnonlinear]) - - coeffzp1xa = T21_coefficients.coeff1LyAzp * T21_coefficients.coeff_Ja_xa - coeffzp1Tx = T21_coefficients.coeff1Xzp - - coeffR1xa = T21_coefficients.coeff2LyAzpRR_II[:,self._iRnonlinear] - coeffR1Tx = T21_coefficients.coeff2XzpRR_II[:,self._iRnonlinear] - - coeffmatrixxa = coeffR1xa.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * coeffR1xa.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - - # gammaR1 = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] * growthRmatrix - # gammamatrixR1R1 = gammaR1.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * gammaR1.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - - # gammaTimesCorrdNL = ne.evaluate('gammamatrixR1R1 * corrdNL')#np.einsum('ijkl,ijkl->ijkl', gammamatrixR1R1, corrdNL, optimize = True) #same thing as gammamatrixR1R1 * corrdNL but faster - - - # SarahLibanore : change to introduce quantities required in the second order correction - # --- # - growthRmatrix1 = growthRmatrix.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) - growthRmatrix2 = growthRmatrix.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - growth_corr = growthRmatrix1 * growthRmatrix2 - - gammaR1 = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] - sigmaR1 = T21_coefficients.sigmaofRtab[:, self._iRnonlinear] - sR1 = (sigmaR1).reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) - sR2 = (sigmaR1).reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - - g1 = (gammaR1 * sigmaR1).reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) - g2 = (gammaR1 * sigmaR1).reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - gammamatrixR1R1 = g1 * g2 - - corrdNL = self._corrdNL - corrdNL_gs = ne.evaluate('corrdNL * growth_corr/ (sR1 * sR2)') - gammaTimesCorrdNL = ne.evaluate('gammamatrixR1R1 * corrdNL_gs') - - if Astro_Parameters.quadratic_SFRD_lognormal: - - gammaR1NL = T21_coefficients.gamma2_II_index2D[:, self._iRnonlinear] - g1NL = (gammaR1NL * sigmaR1**2).reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) - g2NL = (gammaR1NL * sigmaR1**2).reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - - numerator_NL = ne.evaluate('gammaTimesCorrdNL+ g1 * g1 * (0.5 - g2NL * (1 - corrdNL_gs * corrdNL_gs)) + g2 * g2 * (0.5 - g1NL * (1 - corrdNL_gs * corrdNL_gs))') - - denominator_NL = ne.evaluate('1. - 2 * g1NL - 2 * g2NL + 4 * g1NL * g2NL * (1 - corrdNL_gs * corrdNL_gs)') - - norm1 = ne.evaluate('exp(g1 * g1 / (2 - 4 * g1NL)) / sqrt(1 - 2 * g1NL)') - norm2 = ne.evaluate('exp(g2 * g2 / (2 - 4 * g2NL)) / sqrt(1 - 2 * g2NL)') - - log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm1 * norm2)') - nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') - - # use second order in SFRD lognormal approx - expGammaCorrMinusLinear = ne.evaluate('nonlinearcorrelation - 1-gammaTimesCorrdNL/((-1+2.*g1NL)*(-1+2.*g2NL))') - else: - expGammaCorrMinusLinear = ne.evaluate('exp(gammaTimesCorrdNL) - 1 - gammaTimesCorrdNL') - - self._II_deltaxi_xa = np.einsum('ijkl->il', coeffmatrixxa * expGammaCorrMinusLinear, optimize = True) - self._II_deltaxi_xa *= np.array([coeffzp1xa]).T**2 #brings it to xa units - - if (User_Parameters.FLAG_DO_DENS_NL): - D_coeffR1xa = coeffR1xa.reshape(*coeffR1xa.shape, 1) - DDgammaR1 = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] - D_gammaR1 = DDgammaR1.reshape(*DDgammaR1.shape , 1) - D_growthRmatrix = growthRmatrix[:,:1].reshape(*growthRmatrix[:,:1].shape, 1) - D_corrdNL = corrdNL[:1,0,:,:] - - # SarahLibanore - if Astro_Parameters.quadratic_SFRD_lognormal: - - DDsigmaR1 = T21_coefficients.sigmaofRtab[:, self._iRnonlinear] - D_sigmaR1 = DDsigmaR1.reshape(*DDsigmaR1.shape , 1) - DDgammaR1N = T21_coefficients.gamma2_II_index2D[:, self._iRnonlinear] - D_gammaR1N = DDgammaR1N.reshape(*DDgammaR1N.shape , 1) - - gammaTimesCorrdNL = ne.evaluate('D_gammaR1 * D_growthRmatrix* D_growthRmatrix * D_corrdNL') - numerator_NL = ne.evaluate('gammaTimesCorrdNL+ D_gammaR1 * D_gammaR1 * D_sigmaR1* D_sigmaR1 /2 + D_gammaR1N * D_growthRmatrix * D_growthRmatrix* D_growthRmatrix * D_growthRmatrix * (D_corrdNL * D_corrdNL)') - - denominator_NL = ne.evaluate('1. - 2 * D_gammaR1N*D_sigmaR1*D_sigmaR1') - - norm1 = ne.evaluate('exp(D_gammaR1 * D_gammaR1 * D_sigmaR1* D_sigmaR1 * D_gammaR1 * D_gammaR1 * D_sigmaR1* D_sigmaR1 / (2 - 4 * D_gammaR1N*D_sigmaR1*D_sigmaR1)) / sqrt(1 - 2 * D_gammaR1N*D_sigmaR1*D_sigmaR1)') - - log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm1)') - nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') - - self._II_deltaxi_dxa = np.sum(D_coeffR1xa * ( - nonlinearcorrelation - 1 - D_gammaR1 * D_growthRmatrix**2 * D_corrdNL/(1-2.*D_gammaR1N*D_sigmaR1**2) - ), axis = 1) - - else: - self._II_deltaxi_dxa = np.sum(D_coeffR1xa * ((np.exp(D_gammaR1 * D_growthRmatrix**2 * D_corrdNL )-1.0 ) - D_gammaR1 * D_growthRmatrix**2 * D_corrdNL), axis = 1) - - self._II_deltaxi_d = (np.exp(growthRmatrix[:,:1]**2 * corrdNL[0,0,0,:]) - 1.0) - growthRmatrix[:,:1]**2 * corrdNL[0,0,0,:] - - self._II_deltaxi_dxa *= np.array([coeffzp1xa]).T - - - ### To compute Tx quantities, I'm broadcasting arrays such that the axes are zp1, R1, zp2, R2, and looping over r - # gammaR2 = np.copy(gammaR1) #already has growth factor in this - # gammamatrixR1R2 = gammaR1.reshape(*gammaR1.shape, 1, 1) * gammaR2.reshape(1, 1, *gammaR2.shape) - - coeffzp1Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(*T21_coefficients.coeff1Xzp.shape, 1, 1, 1) - coeffzp2Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(1, 1, *T21_coefficients.coeff1Xzp.shape, 1) - - coeffR2Tx = np.copy(coeffR1Tx) - coeffmatrixTxTx = coeffR1Tx.reshape(*coeffR1Tx.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) - coeffmatrixxaTx = coeffR1xa.reshape(*coeffR1xa.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) - coeffsTxALL = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx - coeffsXaTxALL = coeffzp2Tx * coeffmatrixxaTx - - gammaR2 = np.copy(gammaR1) #already has growth factor in this - sigmaR2 = np.copy(sigmaR1) #already has growth factor in this - - growthRmatrix1 = growthRmatrix.reshape(*gammaR1.shape, 1, 1) - growthRmatrix2 = growthRmatrix.reshape(1, 1, *gammaR2.shape) - growth_corr = growthRmatrix1 * growthRmatrix2 - - g1 = (gammaR1 * sigmaR1).reshape(*gammaR1.shape, 1, 1) - sR1 = (sigmaR1).reshape(*gammaR1.shape, 1, 1) - g2 = (gammaR2 * sigmaR2).reshape(1, 1, *gammaR2.shape) - sR2 = (sigmaR2).reshape(1, 1, *gammaR2.shape) - if Astro_Parameters.quadratic_SFRD_lognormal: - gammaR2NL = np.copy(gammaR1NL) - g1NL = (gammaR1NL * sigmaR1**2).reshape(*gammaR1NL.shape, 1, 1) - g2NL = (gammaR2NL * sigmaR2**2).reshape(1, 1, *gammaR2NL.shape) - - gammamatrixR1R2 = g1 * g2 - - self._II_deltaxi_Tx = np.zeros_like(self._II_deltaxi_xa) - self._II_deltaxi_xaTx = np.zeros_like(self._II_deltaxi_xa) - corrdNLBIG = corrdNL[:,:, np.newaxis, :,:] #dimensions zp1, R1, zp2, R2, and r which will be looped over below - for ir in range(len(Cosmo_Parameters._Rtabsmoo)): - corrdNL = corrdNLBIG[:,:,:,:,ir] - - corrdNL_gs = ne.evaluate('corrdNL * growth_corr / (sR1 * sR2)') - - #HAC: Computations using ne.evaluate(...) use numexpr, which speeds up computations of massive numpy arrays - gammaTimesCorrdNL = ne.evaluate('gammamatrixR1R2 * corrdNL_gs') - if Astro_Parameters.quadratic_SFRD_lognormal: - - numerator_NL = ne.evaluate('gammaTimesCorrdNL + g1 * g1 * (0.5 - g2NL * (1 - corrdNL_gs * corrdNL_gs)) + g2 * g2 * (0.5 - g1NL * (1 - corrdNL_gs * corrdNL_gs))') - denominator_NL = ne.evaluate('1. - 2 * g1NL - 2 * g2NL + 4 * g1NL * g2NL * (1 - corrdNL_gs * corrdNL_gs)') - norm1 = ne.evaluate('exp(g1 * g1 / (2 - 4 * g1NL)) / sqrt(1 - 2 * g1NL)') - norm2 = ne.evaluate('exp(g2 * g2 / (2 - 4 * g2NL)) / sqrt(1 - 2 * g2NL)') - - log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm1 * norm2)') - nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') - - # use second order in SFRD lognormal approx - expGammaCorrMinusLinear = ne.evaluate('nonlinearcorrelation - 1-gammaTimesCorrdNL/((-1+2.*g1NL)*(-1+2.*g2NL))') - else: - expGammaCorrMinusLinear = ne.evaluate('exp(gammaTimesCorrdNL) - 1 - gammaTimesCorrdNL') - - deltaXiTxAddend = ne.evaluate('coeffsTxALL * expGammaCorrMinusLinear') - deltaXiTxAddend = np.einsum('ijkl->ik', deltaXiTxAddend, optimize = True) #equivalent to np.sum(deltaXiTxAddend, axis = (1, 3)) - deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] - deltaXiTxAddend = np.moveaxis(deltaXiTxAddend, 1, 0) - deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] - self._II_deltaxi_Tx[:,ir] = np.einsum('ii->i', deltaXiTxAddend, optimize = True) - - deltaXiXaTxAddend = ne.evaluate('coeffsXaTxALL * expGammaCorrMinusLinear') - deltaXiXaTxAddend = np.einsum('ijkl->ik', deltaXiXaTxAddend, optimize = True) #equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) - deltaXiXaTxAddend = np.moveaxis(deltaXiXaTxAddend, 1, 0) - deltaXiXaTxAddend = np.cumsum(deltaXiXaTxAddend[::-1], axis = 0)[::-1] - self._II_deltaxi_xaTx[:,ir] = np.einsum('ii->i', deltaXiXaTxAddend, optimize = True) - - - self._II_deltaxi_Tx *= np.array([_coeffTx_units]).T**2 - self._II_deltaxi_xaTx *= np.array([coeffzp1xa * _coeffTx_units]).T - - - if (User_Parameters.FLAG_DO_DENS_NL): - D_coeffR2Tx = coeffR2Tx.reshape(1, *coeffR2Tx.shape, 1) - D_coeffzp2Tx = coeffzp2Tx.flatten().reshape(1, *coeffzp2Tx.flatten().shape, 1) - DDgammaR2 = np.copy(DDgammaR1) - D_gammaR2 = DDgammaR2.reshape(1, *DDgammaR2.shape , 1) - D_growthRmatrix = growthRmatrix[:,0].reshape(*growthRmatrix[:,0].shape, 1, 1, 1) - D_corrdNL = corrdNLBIG.squeeze()[0].reshape(1, 1, *corrdNLBIG.squeeze()[0].shape) - - if Astro_Parameters.quadratic_SFRD_lognormal: - - DDsigmaR2 = np.copy(DDsigmaR1) - D_sigmaR2 = DDsigmaR2.reshape(1, *DDsigmaR2.shape , 1) - DDgammaR2N = np.copy(DDgammaR1N) - D_gammaR2N = DDgammaR2N.reshape(1, *DDgammaR2N.shape , 1) - - gammaTimesCorrdNL = ne.evaluate('D_gammaR2 * D_growthRmatrix* D_growthRmatrix * D_corrdNL') - numerator_NL = ne.evaluate('gammaTimesCorrdNL+ D_gammaR2 * D_gammaR2 * D_sigmaR2* D_sigmaR2 /2 + D_gammaR2N * D_growthRmatrix* D_growthRmatrix* D_growthRmatrix* D_growthRmatrix * (D_corrdNL * D_corrdNL)') - - denominator_NL = ne.evaluate('1. - 2 * D_gammaR2N*D_sigmaR2*D_sigmaR2') - - norm2 = ne.evaluate('exp(D_gammaR2 * D_gammaR2 * D_sigmaR2* D_sigmaR2 * D_gammaR2 * D_gammaR2 * D_sigmaR2* D_sigmaR2 / (2 - 4 * D_gammaR2N*D_sigmaR2*D_sigmaR2)) / sqrt(1 - 2 * D_gammaR2N*D_sigmaR2*D_sigmaR2)') - - log_norm = ne.evaluate('log(sqrt(denominator_NL) * norm2)') - nonlinearcorrelation = ne.evaluate('exp(numerator_NL/denominator_NL - log_norm)') - - self._II_deltaxi_dTx = D_coeffzp2Tx * np.sum(D_coeffR2Tx * (nonlinearcorrelation -1 -D_gammaR2*D_growthRmatrix**2 *D_corrdNL/(1-2.*D_gammaR2N*D_sigmaR2**2)), axis = 2) - - else: - self._II_deltaxi_dTx = D_coeffzp2Tx * np.sum(D_coeffR2Tx * ((np.exp(D_gammaR2 * D_growthRmatrix**2 * D_corrdNL)-1.0) - D_gammaR2 * D_growthRmatrix**2 * D_corrdNL), axis = 2) - - - self._II_deltaxi_dTx = np.moveaxis(self._II_deltaxi_dTx, 1, 0) - self._II_deltaxi_dTx = np.cumsum(self._II_deltaxi_dTx[::-1], axis = 0)[::-1] - self._II_deltaxi_dTx = np.moveaxis(self._II_deltaxi_dTx, 1, 0) - self._II_deltaxi_dTx = np.einsum('iik->ik', self._II_deltaxi_dTx, optimize = True) - self._II_deltaxi_dTx *= np.array([_coeffTx_units]).T - - return 1 - - def get_all_corrs_IIxIII(self, Cosmo_Parameters, T21_coefficients): - """ - Returns the Pop IIxIII cross-correlation function of all observables at each z in zintegral - """ - #HAC: I deleted the bubbles and EoR part, to be done later..... - - - corrdNL = self._corrdNL - - - _coeffTx_units_II = T21_coefficients.coeff_Gammah_Tx_II #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor - _coeffTx_units_III = T21_coefficients.coeff_Gammah_Tx_III #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor - - growthRmatrix = cosmology.growth(Cosmo_Parameters,self._zGreaterMatrix100[:, self._iRnonlinear]) - gammaR1_II = T21_coefficients.gamma_II_index2D[:, self._iRnonlinear] * growthRmatrix - gammaR1_III = T21_coefficients.gamma_III_index2D[:, self._iRnonlinear] * growthRmatrix - - coeffzp1xa = T21_coefficients.coeff1LyAzp * T21_coefficients.coeff_Ja_xa - coeffzp1Tx = T21_coefficients.coeff1Xzp - - coeffR1xa_II = T21_coefficients.coeff2LyAzpRR_II[:,self._iRnonlinear] - coeffR1xa_III = T21_coefficients.coeff2LyAzpRR_III[:,self._iRnonlinear] - - coeffR1Tx_II = T21_coefficients.coeff2XzpRR_II[:,self._iRnonlinear] - coeffR1Tx_III = T21_coefficients.coeff2XzpRR_III[:,self._iRnonlinear] - - gammamatrix_R1II_R1III = gammaR1_II.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * gammaR1_III.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - coeffmatrixxa_R1II_R1III = coeffR1xa_II.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * coeffR1xa_III.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - - gammaTimesCorrdNL = ne.evaluate('gammamatrix_R1II_R1III * corrdNL') #np.einsum('ijkl,ijkl->ijkl', gammamatrix_R1II_R1III, corrdNL, optimize = True) #same thing as gammamatrixR1R1 * corrdNL but faster - expGammaCorrMinusLinear = ne.evaluate('exp(gammaTimesCorrdNL) - 1 - gammaTimesCorrdNL') - - self._IIxIII_deltaxi_xa = 2 * np.einsum('ijkl->il', coeffmatrixxa_R1II_R1III * expGammaCorrMinusLinear, optimize = True) #factor of 2 to account for cross-term - self._IIxIII_deltaxi_xa *= np.array([coeffzp1xa]).T**2 #brings it to xa units - - ###No density cross-term because density by itself doesn't have a Pop II + III contribution; the xa and Tx contribution is already accounted for in the Pop II- and Pop III-only get_all_corrs - - ### To compute Tx quantities, I'm broadcasting arrays such that the axes are zp1, R1, zp2, R2, r - - gammaR2_II = np.copy(gammaR1_II) #already has growth factor in this - gammaR2_III = np.copy(gammaR1_III) #already has growth factor in this - - gammamatrix_R1II_R2III = gammaR1_II.reshape(*gammaR1_II.shape, 1, 1) * gammaR2_III.reshape(1, 1, *gammaR2_III.shape) - gammamatrix_R1III_R2II = gammaR1_III.reshape(*gammaR1_III.shape, 1, 1) * gammaR2_II.reshape(1, 1, *gammaR2_II.shape) - - coeffzp1Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(*T21_coefficients.coeff1Xzp.shape, 1, 1, 1) - coeffzp2Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(1, 1, *T21_coefficients.coeff1Xzp.shape, 1) - - coeffR2Tx_II = np.copy(coeffR1Tx_II) - coeffR2Tx_III = np.copy(coeffR1Tx_III) - - coeffmatrixTxTx_R1II_R2III = coeffR1Tx_II.reshape(*coeffR1Tx_II.shape, 1, 1) * coeffR2Tx_III.reshape(1, 1, *coeffR2Tx_III.shape) - coeffmatrixTxTx_R1III_R2II = coeffR1Tx_III.reshape(*coeffR1Tx_III.shape, 1, 1) * coeffR2Tx_II.reshape(1, 1, *coeffR2Tx_II.shape) - - coeffmatrixxaTx_R1II_R2III = coeffR1xa_II.reshape(*coeffR1xa_II.shape, 1, 1) * coeffR2Tx_III.reshape(1, 1, *coeffR2Tx_III.shape) - coeffmatrixxaTx_R1III_R2II = coeffR1xa_III.reshape(*coeffR1xa_III.shape, 1, 1) * coeffR2Tx_II.reshape(1, 1, *coeffR2Tx_II.shape) - - coeffsTxALL_R1II_R2III = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx_R1II_R2III - coeffsTxALL_R1III_R2II = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx_R1III_R2II - coeffsXaTxALL_R1II_R2III = coeffzp2Tx * coeffmatrixxaTx_R1II_R2III - coeffsXaTxALL_R1III_R2II = coeffzp2Tx * coeffmatrixxaTx_R1III_R2II - - self._IIxIII_deltaxi_Tx = np.zeros_like(self._IIxIII_deltaxi_xa) - _IIxIII_deltaxi_xaTx1 = np.zeros_like(self._IIxIII_deltaxi_xa) - _IIxIII_deltaxi_xaTx2 = np.zeros_like(self._IIxIII_deltaxi_xa) - corrdNLBIG = corrdNL[:,:, np.newaxis, :,:] #dimensions zp1, R1, zp2, R2, and r, the last of which will be looped over below - - for ir in range(len(Cosmo_Parameters._Rtabsmoo)): - corrdNL = corrdNLBIG[:,:,:,:,ir] - - #HAC: Computations using ne.evaluate(...) use numexpr, which speeds up computations of massive numpy arrays - - gamma_R1II_R2III_CorrdNL = ne.evaluate('gammamatrix_R1II_R2III * corrdNL') - expGamma_R1II_R2III_CorrdNL = ne.evaluate('exp(gamma_R1II_R2III_CorrdNL) - 1 - gamma_R1II_R2III_CorrdNL') - - gamma_R1III_R2II_CorrdNL = ne.evaluate('gammamatrix_R1III_R2II * corrdNL') - expGamma_R1III_R2II_CorrdNL = ne.evaluate('exp(gammamatrix_R1III_R2II * corrdNL) - 1 - gammamatrix_R1III_R2II * corrdNL') - - deltaXiTxAddend = ne.evaluate('coeffsTxALL_R1II_R2III * expGamma_R1II_R2III_CorrdNL + coeffsTxALL_R1III_R2II * expGamma_R1III_R2II_CorrdNL') - deltaXiTxAddend = np.einsum('ijkl->ik', deltaXiTxAddend, optimize = True)# equivalent to np.sum(deltaXiTxAddend, axis = (1, 3)) - deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] - deltaXiTxAddend = np.moveaxis(deltaXiTxAddend, 1, 0) - deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] - self._IIxIII_deltaxi_Tx[:,ir] = np.einsum('ii->i', deltaXiTxAddend, optimize = True) - - #Tx in R2 uses Pop III quantities - deltaXiXaTxAddend1 = ne.evaluate('coeffsXaTxALL_R1II_R2III * expGamma_R1II_R2III_CorrdNL') - deltaXiXaTxAddend1 = np.einsum('ijkl->ik', deltaXiXaTxAddend1, optimize = True) # equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) - deltaXiXaTxAddend1 = np.moveaxis(deltaXiXaTxAddend1, 1, 0) - deltaXiXaTxAddend1 = np.cumsum(deltaXiXaTxAddend1[::-1], axis = 0)[::-1] - _IIxIII_deltaxi_xaTx1[:, ir] = np.einsum('ii->i', deltaXiXaTxAddend1, optimize = True) - - #Tx in R2 uses Pop II quantities - deltaXiXaTxAddend2 = ne.evaluate('coeffsXaTxALL_R1III_R2II * expGamma_R1III_R2II_CorrdNL') - deltaXiXaTxAddend2 = np.einsum('ijkl->ik', deltaXiXaTxAddend2, optimize = True) # equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) - deltaXiXaTxAddend2 = np.moveaxis(deltaXiXaTxAddend2, 1, 0) - deltaXiXaTxAddend2 = np.cumsum(deltaXiXaTxAddend2[::-1], axis = 0)[::-1] - _IIxIII_deltaxi_xaTx2[:, ir] = np.einsum('ii->i', deltaXiXaTxAddend2, optimize = True) - - self._IIxIII_deltaxi_Tx *= np.array([_coeffTx_units_II * _coeffTx_units_III]).T - - _IIxIII_deltaxi_xaTx1 *= np.array([coeffzp1xa * _coeffTx_units_III]).T - _IIxIII_deltaxi_xaTx2 *= np.array([coeffzp1xa * _coeffTx_units_II]).T - self._IIxIII_deltaxi_xaTx = _IIxIII_deltaxi_xaTx1 + _IIxIII_deltaxi_xaTx2 - - return 1 - - - # === BEGIN EDIT (par/perp eta split): generalized signature. - # Original signature was get_xi_Sum_2ExpEta(self, xiEta, etaCoeff1, etaCoeff2) - # using the isotropic factor (1 - K*xiEta)**(3/2). The new signature accepts - # both modes; pass xiEtaperp=None for the legacy isotropic calculation. === - def get_xi_Sum_2ExpEta(self, xiEtapar, xiEtaperp, etaCoeff1, etaCoeff2): - """ - Computes the correlation function of the VCB portion of the SFRD, expressed using sums of two exponentials - if rho(z1, x1) / rhobar = Ae^-b tilde(eta) + Ce^-d tilde(eta) and rho(z2, x2) / rhobar = Fe^-g tilde(eta) + He^-k tilde(eta) - Then this computes - - Refer to eq. A12 in 2407.18294 for more details - - Two computational modes are supported via the second argument: - - * ``xiEtaperp is None`` (legacy / isotropic): - The first argument ``xiEtapar`` is treated as the single - isotropic eta correlation function ``xiEta``, and the - original four-term factor ``(1 - K * xiEta)**(3/2)`` is - used for each term. - - * ``xiEtaperp is not None`` (parallel/perpendicular split): - The first argument is the parallel eta correlation function - and the second is the perpendicular one. Each - ``(1 - K * xiEta)**(3/2)`` factor is replaced by - ``((1 - K * xiEtapar) * (1 - K * xiEtaperp)**2)**(1/2)`` - with K = 6*K1*K2/((1+2*K1)*(1+2*K2)). - - Callers should select exactly one mode and pass the matching - correlation function(s); the alternative array(s) need not (and - should not) be constructed. - """ - - aa, bb, cc, dd = etaCoeff1 - ff, gg, hh, kk = etaCoeff2 - - normBB = ne.evaluate('(1+2*bb)**(3/2)') - normGG = ne.evaluate('(1+2*gg)**(3/2)') - normDD = ne.evaluate('(1+2*dd)**(3/2)') - normKK = ne.evaluate('(1+2*kk)**(3/2)') - - afBG = ne.evaluate('aa * ff / normBB / normGG') - ahBK = ne.evaluate('aa * hh / normBB / normKK') - cfDG = ne.evaluate('cc * ff / normDD / normGG') - chDK = ne.evaluate('cc * hh / normDD / normKK') - - # --- EDIT (par/perp eta split): mode dispatch on xiEtaperp --- - if xiEtaperp is None: - # Legacy isotropic eta correlation: single xiEta argument. - xiEta = xiEtapar - xiNumerator = ne.evaluate('afBG * (1 / (1 - 6*bb * gg * xiEta / ((1+2*bb)*(1+2*gg)))**(3/2) - 1) + ahBK * (1 / (1 - 6*bb * kk * xiEta / ((1+2*bb)*(1+2*kk)))**(3/2) - 1) + cfDG * (1 / (1 - 6*dd * gg * xiEta / ((1+2*dd)*(1+2*gg)))**(3/2) - 1) + chDK * (1 / (1 - 6*dd * kk * xiEta / ((1+2*dd)*(1+2*kk)))**(3/2) - 1)') - else: - # Parallel/perpendicular split: - # (1 - K * xi)**(3/2) -> ((1 - K * xipar) * (1 - K * xiperp)**2)**(1/2) - xiNumerator = ne.evaluate( - 'afBG * (1 / ((1 - 6*bb*gg*xiEtapar/((1+2*bb)*(1+2*gg))) * (1 - 6*bb*gg*xiEtaperp/((1+2*bb)*(1+2*gg)))**2)**(1/2) - 1)' - ' + ahBK * (1 / ((1 - 6*bb*kk*xiEtapar/((1+2*bb)*(1+2*kk))) * (1 - 6*bb*kk*xiEtaperp/((1+2*bb)*(1+2*kk)))**2)**(1/2) - 1)' - ' + cfDG * (1 / ((1 - 6*dd*gg*xiEtapar/((1+2*dd)*(1+2*gg))) * (1 - 6*dd*gg*xiEtaperp/((1+2*dd)*(1+2*gg)))**2)**(1/2) - 1)' - ' + chDK * (1 / ((1 - 6*dd*kk*xiEtapar/((1+2*dd)*(1+2*kk))) * (1 - 6*dd*kk*xiEtaperp/((1+2*dd)*(1+2*kk)))**2)**(1/2) - 1)' - ) - # --- end EDIT (par/perp eta split) --- - - xiDenominator = ne.evaluate('afBG + ahBK + cfDG + chDK') - - xiTotal = ne.evaluate('xiNumerator / xiDenominator') - - return xiTotal - # === END EDIT (par/perp eta split): get_xi_Sum_2ExpEta === - - - def get_all_corrs_III(self, User_Parameters, Cosmo_Parameters, T21_coefficients): - "Returns the Pop III components of the correlation functions of all observables at each z in zintegral" - #HAC: I deleted the bubbles and EoR part, to be done later..... - - - corrdNL = self._corrdNL - - # === BEGIN EDIT (par/perp eta split): runtime dispatch. - # Switch between legacy isotropic eta correlation and the - # parallel/perpendicular split. Default False (legacy) so existing - # behavior is preserved if the flag is absent on Cosmo_Parameters. - # The code computes EITHER xiEta OR (xiEtapar, xiEtaperp), never both. === - USE_ETA_PARPERP_SPLIT = getattr(Cosmo_Parameters, 'USE_ETA_PARPERP_SPLIT', False) - - # Build EITHER the isotropic eta CF matrix OR the par/perp pair, - # but not both, so the unused branch never allocates memory. - if USE_ETA_PARPERP_SPLIT: - corrEtaNLpar = Cosmo_Parameters.xiEtaPar_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] - corrEtaNLpar[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNLpar[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] - corrEtaNLpar = corrEtaNLpar.reshape(1, *corrEtaNLpar.shape) - - corrEtaNLperp = Cosmo_Parameters.xiEtaPerp_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] - corrEtaNLperp[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNLperp[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] - corrEtaNLperp = corrEtaNLperp.reshape(1, *corrEtaNLperp.shape) - else: - corrEtaNL = Cosmo_Parameters.xiEta_RR_CF[np.ix_(self._iRnonlinear,self._iRnonlinear)] - corrEtaNL[0:Cosmo_Parameters.indexminNL,0:Cosmo_Parameters.indexminNL] = corrEtaNL[Cosmo_Parameters.indexminNL,Cosmo_Parameters.indexminNL] - corrEtaNL = corrEtaNL.reshape(1, *corrEtaNL.shape) - # === END EDIT (par/perp eta split): eta-CF matrix construction === - - - _coeffTx_units = T21_coefficients.coeff_Gammah_Tx_III #includes -10^40 erg/s/SFR normalizaiton and erg/K conversion factor - - growthRmatrix = cosmology.growth(Cosmo_Parameters,self._zGreaterMatrix100[:, self._iRnonlinear]) - gammaR1 = T21_coefficients.gamma_III_index2D[:, self._iRnonlinear] * growthRmatrix - - vcbCoeffs1 = T21_coefficients.vcb_expFitParams[:, self._iRnonlinear] - vcbCoeffsR1 = np.transpose(vcbCoeffs1, (2, 0, 1)) - vcbCoeffsR1 = vcbCoeffsR1[:,:,:,np.newaxis,np.newaxis] - vcbCoeffsR2 = np.moveaxis(vcbCoeffsR1, 3, 2) - - coeffzp1xa = T21_coefficients.coeff1LyAzp * T21_coefficients.coeff_Ja_xa - coeffzp1Tx = T21_coefficients.coeff1Xzp - - coeffR1xa = T21_coefficients.coeff2LyAzpRR_III[:,self._iRnonlinear] - coeffR1Tx = T21_coefficients.coeff2XzpRR_III[:,self._iRnonlinear] - - gammamatrixR1R1 = gammaR1.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * gammaR1.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - coeffmatrixxa = coeffR1xa.reshape(len(T21_coefficients.zintegral), 1, len(self._iRnonlinear),1) * coeffR1xa.reshape(len(T21_coefficients.zintegral), len(self._iRnonlinear), 1,1) - - gammaCorrdNL = ne.evaluate('gammamatrixR1R1 * corrdNL') #np.einsum('ijkl,ijkl->ijkl', gammamatrixR1R1, corrdNL, optimize = True) #same thing as gammamatrixR1R1 * corrdNL but faster - expGammaCorr = ne.evaluate('exp(gammaCorrdNL) - 1') # equivalent to np.exp(gammaTimesCorrdNL)-1.0 - - if Cosmo_Parameters.USE_RELATIVE_VELOCITIES == True: - # --- EDIT (par/perp eta split): mode dispatch for xa term --- - if USE_ETA_PARPERP_SPLIT: - etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaNLpar, corrEtaNLperp, vcbCoeffsR1, vcbCoeffsR2) - else: - etaCorr_xa = self.get_xi_Sum_2ExpEta(corrEtaNL, None, vcbCoeffsR1, vcbCoeffsR2) - # --- end EDIT --- - totalCorr = ne.evaluate('expGammaCorr * etaCorr_xa + expGammaCorr + etaCorr_xa - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order - else: - totalCorr = ne.evaluate('expGammaCorr - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order - - self._III_deltaxi_xa = np.einsum('ijkl->il', coeffmatrixxa * totalCorr , optimize = True) # equivalent to self._III_deltaxi_xa = np.sum(coeffmatrixxa * ((np.exp(gammaTimesCorrdNL)-1.0) - gammaTimesCorrdNL), axis = (1,2)) - self._III_deltaxi_xa *= np.array([coeffzp1xa]).T**2 #brings it to xa units - - if (User_Parameters.FLAG_DO_DENS_NL): #no velocity contribution to density - D_coeffR1xa = coeffR1xa.reshape(*coeffR1xa.shape, 1) - D_gammaR1 = gammaR1.reshape(*gammaR1.shape , 1) - D_growthRmatrix = growthRmatrix[:,:1].reshape(*growthRmatrix[:,:1].shape, 1) - D_corrdNL = corrdNL[:1,0,:,:] - - self._III_deltaxi_dxa = np.sum(D_coeffR1xa * ((np.exp(D_gammaR1 * D_growthRmatrix * D_corrdNL )-1.0 ) - D_gammaR1 * D_growthRmatrix * D_corrdNL), axis = 1) - self._III_deltaxi_dxa *= np.array([coeffzp1xa]).T - - ### To compute Tx quantities, I'm broadcasting arrays such that the axes are zp1, R1, zp2, R2, r - - gammaR2 = np.copy(gammaR1) #already has growth factor in this - gammamatrixR1R2 = gammaR1.reshape(*gammaR1.shape, 1, 1) * gammaR2.reshape(1, 1, *gammaR2.shape) - - coeffzp1Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(*T21_coefficients.coeff1Xzp.shape, 1, 1, 1) - coeffzp2Tx = np.copy(T21_coefficients.coeff1Xzp).reshape(1, 1, *T21_coefficients.coeff1Xzp.shape, 1) - coeffR2Tx = np.copy(coeffR1Tx) - coeffmatrixTxTx = coeffR1Tx.reshape(*coeffR1Tx.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) - coeffmatrixxaTx = coeffR1xa.reshape(*coeffR1xa.shape, 1, 1) * coeffR2Tx.reshape(1, 1, *coeffR2Tx.shape) - coeffsTxALL = coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx - coeffsXaTxALL = coeffzp2Tx * coeffmatrixxaTx - - corrdNLBIG = corrdNL[:,:, np.newaxis, :, :] - # --- EDIT (par/perp eta split): expand only the eta arrays we built --- - if USE_ETA_PARPERP_SPLIT: - corrEtaNLparBIG = corrEtaNLpar[:,:, np.newaxis, :, :] - corrEtaNLperpBIG = corrEtaNLperp[:,:, np.newaxis, :, :] - else: - corrEtaNLBIG = corrEtaNL[:,:, np.newaxis, :, :] - # --- end EDIT --- - - vcbCoeffsR1 = vcbCoeffsR1[:,:,:,:,:] - vcbCoeffsR2 = np.transpose(vcbCoeffsR1, (0,3,4,1,2)) - - self._III_deltaxi_Tx = np.zeros_like(self._III_deltaxi_xa) - self._III_deltaxi_xaTx = np.zeros_like(self._III_deltaxi_xa) - self._III_deltaxi_dTx = np.zeros_like(self._III_deltaxi_xa) - - for ir in range(len(Cosmo_Parameters._Rtabsmoo)): - corrdNL = corrdNLBIG[:,:,:,:,ir] - # --- EDIT (par/perp eta split): per-ir slice of the active eta CF --- - if USE_ETA_PARPERP_SPLIT: - corrEtaNLpar = corrEtaNLparBIG[:,:,:,:,ir] - corrEtaNLperp = corrEtaNLperpBIG[:,:,:,:,ir] - else: - corrEtaNL = corrEtaNLBIG[:,:,:,:,ir] - # --- end EDIT --- - - gammaCorrdNL = ne.evaluate('gammamatrixR1R2 * corrdNL') - expGammaCorrdNL = ne.evaluate('exp(gammaCorrdNL) - 1') - - if Cosmo_Parameters.USE_RELATIVE_VELOCITIES == True: - # --- EDIT (par/perp eta split): mode dispatch for Tx term --- - if USE_ETA_PARPERP_SPLIT: - etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaNLpar, corrEtaNLperp, vcbCoeffsR1, vcbCoeffsR2) - else: - etaCorr_Tx = self.get_xi_Sum_2ExpEta(corrEtaNL, None, vcbCoeffsR1, vcbCoeffsR2) - # --- end EDIT --- - totalCorr = ne.evaluate('expGammaCorrdNL * etaCorr_Tx + expGammaCorrdNL + etaCorr_Tx - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order - else: - totalCorr = ne.evaluate('expGammaCorrdNL - gammaCorrdNL') ###TO DO (linearized VCB flucts): - etaCorr_xa_lin #note that the Taylor expansion of the cross-term is 0 to linear order - - deltaXiTxAddend = ne.evaluate('coeffsTxALL * totalCorr') # equivalent to np.multiply(coeffzp1Tx * coeffzp2Tx * coeffmatrixTxTx, totalCorr, out = outDummy) - deltaXiTxAddend = np.einsum('ijkl->ik', deltaXiTxAddend, optimize=True) # equivalent to np.sum(deltaXiTxAddend, axis = (1, 3)) - deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] - deltaXiTxAddend = np.moveaxis(deltaXiTxAddend, 1, 0) - deltaXiTxAddend = np.cumsum(deltaXiTxAddend[::-1], axis = 0)[::-1] - self._III_deltaxi_Tx[:, ir] = np.einsum('ii->i', deltaXiTxAddend, optimize = True) - - deltaXiXaTxAddend = ne.evaluate('coeffsXaTxALL * totalCorr') # equivalent to np.multiply(coeffzp2Tx * coeffmatrixxaTx, totalCorr, out = outDummy) - deltaXiXaTxAddend = np.einsum('ijkl->ik', deltaXiXaTxAddend, optimize=True) # equivalent to np.sum(deltaXiXaTxAddend, axis = (1, 3)) - deltaXiXaTxAddend = np.moveaxis(deltaXiXaTxAddend, 1, 0) - deltaXiXaTxAddend = np.cumsum(deltaXiXaTxAddend[::-1], axis = 0)[::-1] - self._III_deltaxi_xaTx[:, ir] = np.einsum('ii->i', deltaXiXaTxAddend, optimize = True) - - if (User_Parameters.FLAG_DO_DENS_NL): #no velocity contribution to density - D_coeffR2Tx = coeffR2Tx.reshape(1, *coeffR2Tx.shape, 1) - D_coeffzp2Tx = coeffzp2Tx.flatten().reshape(1, *coeffzp2Tx.flatten().shape, 1) - D_gammaR2 = gammaR2.reshape(1, *gammaR2.shape , 1) - D_growthRmatrix = growthRmatrix[:,0].reshape(*growthRmatrix[:,0].shape, 1, 1, 1) - D_corrdNL = corrdNLBIG.squeeze()[0].reshape(1, 1, *corrdNLBIG.squeeze()[0].shape) - - self._III_deltaxi_dTx = D_coeffzp2Tx * np.sum(D_coeffR2Tx * ((np.exp(D_gammaR2 * D_growthRmatrix * D_corrdNL)-1.0) - D_gammaR2 * D_growthRmatrix * D_corrdNL), axis = 2) - - self._III_deltaxi_dTx = np.moveaxis(self._III_deltaxi_dTx, 1, 0) - self._III_deltaxi_dTx = np.cumsum(self._III_deltaxi_dTx[::-1], axis = 0)[::-1] - self._III_deltaxi_dTx = np.moveaxis(self._III_deltaxi_dTx, 1, 0) - self._III_deltaxi_dTx = np.einsum('iik->ik', self._III_deltaxi_dTx, optimize = True) - self._III_deltaxi_dTx *= np.array([_coeffTx_units]).T - - self._III_deltaxi_Tx *= np.array([_coeffTx_units]).T**2 - self._III_deltaxi_xaTx *= np.array([coeffzp1xa * _coeffTx_units]).T - - return 1 - - - def get_list_PS(self, xi_list, zlisttoconvert): - "Returns the power spectrum given a list of CFs (xi_list) evaluated at z=zlisttoconvert as input" - - _Pk_list = [] - - for izp,zp in enumerate(zlisttoconvert): - - _kzp, _Pkzp = self.get_Pk_from_xi(self._rs_input_mcfit,xi_list[izp]) - _Pk_list.append(_Pkzp) - #can ignore _kzp, it's the same as klist_PS above by construction - - - return np.array(_Pk_list) - - - def get_Pk_from_xi(self, rsinput, xiinput): - "Generic Fourier Transform, returns Pk from an input Corr Func xi. kPf should be the same as _klistCF" - - kPf, Pf = mcfit.xi2P(rsinput, l=0, lowring=True)(xiinput, extrap=False) - - return kPf, Pf \ No newline at end of file diff --git a/zeus21/cosmology_v2.py b/zeus21/cosmology_v2.py deleted file mode 100644 index 55bb71d..0000000 --- a/zeus21/cosmology_v2.py +++ /dev/null @@ -1,536 +0,0 @@ -""" - -Cosmology helper functions and other tools - -Author: Julian B. Muñoz -UT Austin and Harvard CfA - January 2023 - -Edited by Hector Afonso G. Cruz -JHU - July 2024 - -Edited by Emilie Thelie -UT Austin - April 2026 -""" - -import numpy as np -# === BEGIN EDIT (par/perp eta split): added imports needed by runclass below === -from classy import Class -from scipy.interpolate import RegularGridInterpolator -from scipy.interpolate import interp1d - -import mcfit -# === END EDIT (par/perp eta split) === - -from . import constants -# === BEGIN EDIT (cosmology_v2: par/perp eta split) === -# Use the patched Cosmo_Parameters from inputs_v2, which honors -# USE_ETA_PARPERP_SPLIT in both runclass() and run_correlations(). -from .inputs_v2 import Cosmo_Parameters -# === END EDIT (cosmology_v2: par/perp eta split) === - -# === BEGIN EDIT (cosmology_v2: par/perp eta split) === -# Rewritten cosmo_wrapper. The previous version imported a non-existent -# `Correlations` class from zeus21.correlations[_v2] and instantiated -# Cosmo_Parameters with the wrong arg count, so it could never run. -# -# This version delegates all CLASS + correlation-function setup to -# inputs_v2.Cosmo_Parameters.__post_init__, which now: -# * dispatches in runclass() between legacy `k_eta`/`P_eta` and the -# split `k_etapar`/`k_etaperp` keys based on USE_ETA_PARPERP_SPLIT; -# * dispatches in run_correlations() between building xiEta_RR_CF and -# building (xiEtaPar_RR_CF, xiEtaPerp_RR_CF). -# -# `Cosmo_Parameters_Input` is treated as a duck-typed object: any of its -# attributes that match a Cosmo_Parameters field are forwarded as kwargs. -# Callers can therefore pass a SimpleNamespace, a dataclass, or a custom -# class. -# -# Returns (CosmoParams, ClassCosmo, HMFintclass). -_COSMO_PARAM_FIELDS = ( - 'omegab', 'omegac', 'h_fid', 'As', 'ns', 'tau_fid', - 'kmax_CLASS', 'zmax_CLASS', 'zmin_CLASS', - 'Rs_min', 'Rs_max', - 'Flag_emulate_21cmfast', 'USE_RELATIVE_VELOCITIES', - 'USE_ETA_PARPERP_SPLIT', 'HMF_CHOICE', -) - -def cosmo_wrapper(User_Parameters, Cosmo_Parameters_Input): - """ - Wrapper that instantiates the full cosmology pipeline for the v2 - code path. Returns (Cosmo_Parameters, ClassCosmo, HMF_interpolator). - - Parameters - ---------- - User_Parameters : zeus21.User_Parameters - Cosmo_Parameters_Input : object - Any object whose attributes are a subset of the Cosmo_Parameters - fields (omegab, omegac, ..., USE_RELATIVE_VELOCITIES, - USE_ETA_PARPERP_SPLIT, HMF_CHOICE). A types.SimpleNamespace or - a small dataclass works fine. - - The boolean ``Cosmo_Parameters_Input.USE_ETA_PARPERP_SPLIT`` selects: - * False (default): legacy isotropic eta correlation function. - * True: parallel/perpendicular split (xiEtaPar_RR_CF and - xiEtaPerp_RR_CF are populated; xiEta_RR_CF is NOT). - """ - kwargs = { - name: getattr(Cosmo_Parameters_Input, name) - for name in _COSMO_PARAM_FIELDS - if hasattr(Cosmo_Parameters_Input, name) - } - - CosmoParams = Cosmo_Parameters(UserParams=User_Parameters, **kwargs) - ClassCosmo = CosmoParams.ClassCosmo - - HMFintclass = HMF_interpolator(User_Parameters, CosmoParams) - - return CosmoParams, ClassCosmo, HMFintclass -# === END EDIT (cosmology_v2: par/perp eta split): cosmo_wrapper === - - -# === BEGIN EDIT (par/perp eta split): new runclass() supporting two -# mutually exclusive modes for the v_cb^2 ("eta") correlation function, -# selected by CosmologyIn.USE_ETA_PARPERP_SPLIT (default False = legacy). -# Only the keys for the selected mode are written to ClassCosmo.pars, -# so a stale consumer of the other mode fails loudly. === -def runclass(CosmologyIn): - """ - Set up CLASS cosmology. Takes CosmologyIn class input and returns CLASS Cosmology object. - - Two distinct treatments of the v_cb^2 ("eta") correlation function are - supported, selected by the boolean attribute - ``CosmologyIn.USE_ETA_PARPERP_SPLIT`` (default False if absent): - - * False (legacy): - Stores a single isotropic eta power spectrum on - ``ClassCosmo.pars`` under keys ``k_eta`` and ``P_eta``, - built from the integrand ``6*psi0**2 + 3*psi2**2``. - - * True (parallel/perpendicular split): - Stores two eta power spectra on ``ClassCosmo.pars`` under - keys ``k_etapar``/``P_etapar`` and ``k_etaperp``/``P_etaperp``, - built from the integrands ``6*(psi0 + psi2)**2`` and - ``6*(psi0 - psi2/2)**2`` respectively. These feed the - modified four-term factor in - ``Power_Spectra.get_xi_Sum_2ExpEta``: - ((1 - K*xiEtapar) * (1 - K*xiEtaperp)**2)**(1/2) - in place of the legacy (1 - K*xiEta)**(3/2). - - The two modes are mutually exclusive: only the keys for the selected - mode are set, so a stale consumer of the other mode fails loudly. - """ - USE_ETA_PARPERP_SPLIT = getattr(CosmologyIn, 'USE_ETA_PARPERP_SPLIT', False) - - ClassCosmo = Class() - ClassCosmo.set({'omega_b': CosmologyIn.omegab,'omega_cdm': CosmologyIn.omegac, - 'h': CosmologyIn.h_fid,'A_s': CosmologyIn.As,'n_s': CosmologyIn.ns,'tau_reio': CosmologyIn.tau_fid}) - ClassCosmo.set({'output':'mPk','lensing':'no','P_k_max_1/Mpc':CosmologyIn.kmax_CLASS, 'z_max_pk': CosmologyIn.zmax_CLASS}) - ClassCosmo.set({'gauge':'synchronous'}) - - ClassCosmo.compute() - - ClassCosmo.pars['Flag_emulate_21cmfast'] = CosmologyIn.Flag_emulate_21cmfast - ClassCosmo.pars['USE_ETA_PARPERP_SPLIT'] = USE_ETA_PARPERP_SPLIT - - if CosmologyIn.USE_RELATIVE_VELOCITIES == True: - - kMAX_VCB = 50.0 - z_rec = ClassCosmo.get_current_derived_parameters(['z_rec'])['z_rec'] - z_drag = ClassCosmo.get_current_derived_parameters(['z_d'])['z_d'] - - ClassCosmoVCB = Class() - ClassCosmoVCB.set({'omega_b': CosmologyIn.omegab,'omega_cdm': CosmologyIn.omegac, - 'h': CosmologyIn.h_fid,'A_s': CosmologyIn.As,'n_s': CosmologyIn.ns,'tau_reio': CosmologyIn.tau_fid}) - ClassCosmoVCB.set({'output':'vTk'}) - ClassCosmoVCB.set({'P_k_max_1/Mpc':kMAX_VCB, 'z_max_pk':12000}) - ClassCosmoVCB.set({'gauge':'newtonian'}) - ClassCosmoVCB.compute() - velTransFunc = ClassCosmoVCB.get_transfer(z_drag) - - kVel = velTransFunc['k (h/Mpc)'] * CosmologyIn.h_fid - theta_b = velTransFunc['t_b'] - theta_c = velTransFunc['t_cdm'] - - sigma_vcb = np.sqrt(np.trapz(CosmologyIn.As * (kVel/0.05)**(CosmologyIn.ns-1) /kVel * (theta_b - theta_c)**2/kVel**2, kVel)) * constants.c_kms - ClassCosmo.pars['sigma_vcb'] = sigma_vcb - - velArr = np.geomspace(0.01, constants.c_kms, 1000) - vavgIntegrand = (3 / (2 * np.pi * sigma_vcb**2))**(3/2) * 4 * np.pi * velArr**2 * np.exp(-3 * velArr**2 / (2 * sigma_vcb**2)) - ClassCosmo.pars['v_avg'] = np.trapz(vavgIntegrand * velArr, velArr) - - ClassCosmo.pars['k_vcb'] = kVel - ClassCosmo.pars['theta_b'] = theta_b - ClassCosmo.pars['theta_c'] = theta_c - P_vcb = CosmologyIn.As * (kVel/0.05)**(CosmologyIn.ns-1) * (theta_b - theta_c)**2/kVel**2 * 2 * np.pi**2 / kVel**3 - - p_vcb_intp = interp1d(np.log(kVel), P_vcb) - ClassCosmo.pars['P_vcb'] = P_vcb - - kVelIntp = np.geomspace(1e-4, kMAX_VCB, 512) - rVelIntp = 2 * np.pi / kVelIntp - - j0bessel = lambda x: np.sin(x)/x - j2bessel = lambda x: (3 / x**2 - 1) * np.sin(x)/x - 3*np.cos(x)/x**2 - - psi0 = 1 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapz(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j0bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) - psi2 = -2 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapz(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j2bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) - - # === BEGIN EDIT (par/perp eta split): exactly one of the two - # branches below executes. Each writes ONLY the keys that match - # its mode so consumers cannot silently mix conventions. === - if USE_ETA_PARPERP_SPLIT: - # Parallel/perpendicular split: store ONLY the par and perp keys. - k_etapar, P_etapar = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 + psi2)**2, extrap = False) - k_etaperp, P_etaperp = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 - psi2/2.0)**2, extrap = False) - - ClassCosmo.pars['k_etapar'] = k_etapar[P_etapar > 0] - ClassCosmo.pars['P_etapar'] = P_etapar[P_etapar > 0] - ClassCosmo.pars['k_etaperp'] = k_etaperp[P_etaperp > 0] - ClassCosmo.pars['P_etaperp'] = P_etaperp[P_etaperp > 0] - # NOTE: 'k_eta'/'P_eta' intentionally NOT set so a stale - # isotropic consumer fails loudly rather than using wrong CF. - else: - # Legacy isotropic eta: store ONLY the single-key version. - k_eta, P_eta = mcfit.xi2P(rVelIntp, l=0, lowring = True)((6 * psi0**2 + 3 * psi2**2), extrap = False) - ClassCosmo.pars['k_eta'] = k_eta[P_eta > 0] - ClassCosmo.pars['P_eta'] = P_eta[P_eta > 0] - # === END EDIT (par/perp eta split) === - - else: - ClassCosmo.pars['v_avg'] = 0.0 - ClassCosmo.pars['sigma_vcb'] = 1.0 - - return ClassCosmo -# === END EDIT (par/perp eta split): end of new runclass() === - - - - -def time_at_redshift(ClassyCosmo,z): - """ - Returns the age of the Universe (in Gyrs) corresponding to a given redshift. - - Parameters - ---------- - ClassyCosmo: zeus21.runclass class - Sets up Class cosmology. - z: float - Redshift. - """ - background = ClassyCosmo.get_background() - classy_t, classy_z = background['proper time [Gyr]'], background['z'] - classy_tinterp = interp1d(classy_z, classy_t) - return classy_tinterp(z) - -def redshift_at_time(ClassyCosmo,t): - """ - Returns the redshift corresponding to a given age of the Universe (in Gyrs). - - Parameters - ---------- - ClassyCosmo: zeus21.runclass class - Sets up Class cosmology. - t: float - Age in Gyrs. - """ - background = ClassyCosmo.get_background() - classy_t, classy_z = background['proper time [Gyr]'], background['z'] - classy_tinterp = interp1d(classy_t, classy_z) - return classy_tinterp(t) - -def Hub(Cosmo_Parameters, z): -#Hubble(z) in km/s/Mpc - return Cosmo_Parameters.h_fid * 100 * np.sqrt(Cosmo_Parameters.OmegaM * pow(1+z,3.)+Cosmo_Parameters.OmegaR * pow(1+z,4.)+Cosmo_Parameters.OmegaL) - -def HubinvMpc(Cosmo_Parameters, z): -#H(z) in 1/Mpc - return Hub(Cosmo_Parameters,z)/constants.c_kms - -def Hubinvyr(Cosmo_Parameters,z): -#H(z) in 1/yr - return Hub(Cosmo_Parameters,z)*constants.KmToMpc*constants.yrTos - -def rho_baryon(Cosmo_Parameters,z): -#\rho_baryon in Msun/Mpc^3 as a function of z - return Cosmo_Parameters.OmegaB * Cosmo_Parameters.rhocrit * pow(1+z,3.0) - -def n_H(Cosmo_Parameters, z): -#density of hydrogen nuclei (neutral or ionized) in 1/cm^3 - return rho_baryon(Cosmo_Parameters, z) *( 1- Cosmo_Parameters.Y_He)/(constants.mH_GeV/constants.MsuntoGeV) / (constants.Mpctocm**3.0) - -#def n_baryon(Cosmo_Parameters, z): -##density of baryons in 1/cm^3 -# return rho_baryon(Cosmo_Parameters, z) / Cosmo_Parameters.mu_baryon_Msun / (constants.Mpctocm**3.0) - - - -def Tcmb(ClassCosmo, z): - T0CMB = ClassCosmo.T_cmb() - return T0CMB*(1+z) - -def Tadiabatic(CosmoParams, z): - "Returns T_adiabatic as a function of z from thermodynamics in CLASS" - return CosmoParams.Tadiabaticint(z) -def xefid(CosmoParams, z): - "Returns fiducial x_e(z) w/o any sources. Uses thermodynamics in CLASS for z>15, and fixed below to avoid the tanh approx." - _zcutCLASSxe = 15. - _xecutCLASSxe = CosmoParams.xetanhint(_zcutCLASSxe) - return CosmoParams.xetanhint(z) * np.heaviside(z - _zcutCLASSxe, 0.5) + _xecutCLASSxe * np.heaviside(_zcutCLASSxe - z, 0.5) - -def adiabatic_index(z): - "Returns adiabatic index (delta_Tad/delta) as a function of z. Fit from 1506.04152. to ~3% on z = 6 − 50)." - return 0.58 - 0.005*(z-10.) - - -def MhofRad(Cosmo_Parameters,R): - #convert input Radius in Mpc comoving to Mass in Msun - return Cosmo_Parameters.constRM *pow(R, 3.0) - -def RadofMh(Cosmo_Parameters,M): - #convert input M halo in Msun radius in cMpc - return pow(M/Cosmo_Parameters.constRM, 1/3.0) - - - -def ST_HMF(Cosmo_Parameters, Mass, sigmaM, dsigmadM): - A_ST = Cosmo_Parameters.Amp_ST - a_ST = Cosmo_Parameters.a_ST - p_ST = Cosmo_Parameters.p_ST - delta_crit_ST = Cosmo_Parameters.delta_crit_ST - - nutilde = np.sqrt(a_ST) * delta_crit_ST/sigmaM - - return -A_ST * np.sqrt(2./np.pi) * nutilde * (1. + nutilde**(-2.0*p_ST)) * np.exp(-nutilde**2/2.0) * (Cosmo_Parameters.rho_M0 / (Mass * sigmaM)) * dsigmadM - - -def Tink_HMF(Cosmo_Parameters, Mass, sigmaM, dsigmadM,z): - #Tinker08 form of the HMF. All in physical (no h) units. Form from App.A of Yung+23 (2309.14408) - f = f_GUREFT_physical(sigmaM,z) - return f*(Cosmo_Parameters.rho_M0 / (Mass)) * np.abs(dsigmadM/sigmaM) - -def f_GUREFT_physical(sigma,z): - #Fit in eq A2 in Yung+23 (2309.14408), fit to z<20 (Implementation thanks to Aaron Yung). Physical because no h. - #sigma(M,z) is the input, no growth here bc we use class with full sigma evolution - k = np.array([ 1.37657725e-01, -1.00382125e-02, 1.02963559e-03, 1.06641384e+00, - 2.47557563e-02, -2.83342017e-03, 4.86693806e+00, 9.21235623e-02, - -1.42628278e-02, 1.19837952e+00, 1.42966892e-03, -3.30740460e-04]) - A = lambda x: k[0] + k[1]*x + k[2]*(x**2) - a = lambda x: k[3] + k[4]*x + k[5]*(x**2) - b = lambda x: k[6] + k[7]*x + k[8]*(x**2) - c = lambda x: k[9] + k[10]*x + k[11]*(x**2) - sig = sigma - #cap coefficients at z=20 to avoid extrapolation - zuse = np.fmin(z,20.0) - return A(zuse) * (((sig/b(zuse))**(-a(zuse))) + 1.0 ) * np.exp(-c(zuse)/(sig**2)) - -def PS_HMF_unnorm(Cosmo_Parameters, Mass, nu, dlogSdM): - 'Returns the Press-Schechter HMF (unnormalized since we will take ratios), given a halo Mass [Msun], nu = delta_tilde/S_tilde, with delta_tilde = delta_crit - delta_R, and variance S = sigma(M)^2 - sigma(R)^2. Used for 21cmFAST mode.' - - return nu * np.exp(-Cosmo_Parameters.a_corr_EPS*nu**2/2.0) * dlogSdM* (1.0 / Mass) - #written so that dsigmasq/dM appears directly, since that is not modified by EPS, whereas sigma_tot^2 = sigma^2(M) - sigma^2(R). The sigma in denominator will be sigma_tot - - - -class HMF_interpolator: - "Class that builds an interpolator of the HMF. Returns an interpolator" - - def __init__(self, User_Parameters, Cosmo_Parameters): - - self._Mhmin = 1e5 #originally 1e5 - self._Mhmax = 1e14 - self._NMhs = np.floor(35*User_Parameters.precisionboost).astype(int) - self.Mhtab = np.logspace(np.log10(self._Mhmin),np.log10(self._Mhmax),self._NMhs) # Halo mases in Msun - self.RMhtab = RadofMh(Cosmo_Parameters, self.Mhtab) - - self.logtabMh = np.log(self.Mhtab) - - - self._zmin=Cosmo_Parameters.zmin_CLASS - self._zmax = Cosmo_Parameters.zmax_CLASS - self._Nzs=np.floor(100*User_Parameters.precisionboost).astype(int) - self.zHMFtab = np.linspace(self._zmin,self._zmax,self._Nzs) - - #check resolution - if (Cosmo_Parameters.kmax_CLASS < 1.0/self.RMhtab[0]): - print('Warning! kmax_CLASS may be too small! Run CLASS with higher kmax') - - self.sigmaMhtab = np.array([[Cosmo_Parameters.ClassCosmo.sigma(RR,zz) for zz in self.zHMFtab] for RR in self.RMhtab]) - - self._depsM=0.01 #for derivatives, relative to M - self.dsigmadMMhtab = np.array([[(Cosmo_Parameters.ClassCosmo.sigma(RadofMh(Cosmo_Parameters, MM*(1+self._depsM)),zz)-Cosmo_Parameters.ClassCosmo.sigma(RadofMh(Cosmo_Parameters, MM*(1-self._depsM)),zz))/(MM*2.0*self._depsM) for zz in self.zHMFtab] for MM in self.Mhtab]) - - - if(Cosmo_Parameters.Flag_emulate_21cmfast==True): - #ADJUST BY HAND adjust sigmas to match theirs, since the CLASS TF they use is at a fixed cosmology from 21cmvFAST but the input cosmology is different - self.sigmaMhtab*=np.sqrt(0.975)#/0.9845 - self.dsigmadMMhtab*=np.sqrt(0.975)#/0.9845 - - #this correction is because 21cmFAST uses the dicke() function to compute growth, which is ~0.5% offset at high z. This offset makes our growth the same as dicke() for a Planck2018 cosmology. Has to be added separately to the growth(z) correction above since they come in different places - _offsetgrowthdicke21cmFAST = 1-0.000248*(self.zHMFtab-5.) - self.sigmaMhtab*=_offsetgrowthdicke21cmFAST - self.dsigmadMMhtab*=_offsetgrowthdicke21cmFAST - #Note that these two changes may be different if away from Planck2018 - - - - self.HMFtab = np.zeros_like(self.sigmaMhtab) - - - - - - for iM, MM in enumerate(self.Mhtab): - for iz, zz in enumerate(self.zHMFtab): - sigmaM = self.sigmaMhtab[iM,iz] - dsigmadM = self.dsigmadMMhtab[iM,iz] - - if(Cosmo_Parameters.HMF_CHOICE == 'ST'): - self.HMFtab[iM,iz] = ST_HMF(Cosmo_Parameters, MM, sigmaM, dsigmadM) - elif(Cosmo_Parameters.HMF_CHOICE == 'Yung'): - self.HMFtab[iM,iz] = Tink_HMF(Cosmo_Parameters, MM, sigmaM, dsigmadM,zz) - else: - print('ERROR, use a correct Cosmo_Parameters.HMF_CHOICE') - self.HMFtab[iM,iz] = 0.0 - - - - - - _HMFMIN = np.exp(-300.) #min HMF to avoid overflowing - logHMF_ST_trim = self.HMFtab - logHMF_ST_trim[np.array(logHMF_ST_trim <= 0.)] = _HMFMIN - logHMF_ST_trim = np.log(logHMF_ST_trim) - - - self.fitMztab = [np.log(self.Mhtab), self.zHMFtab] - self.logHMFint = RegularGridInterpolator(self.fitMztab, logHMF_ST_trim, bounds_error = False, fill_value = -np.inf) ###HAC: Changed to -np.inf so HMFint = exp(-np.inf)= zero to fix nans in sfrd.py - - self.sigmaintlog = RegularGridInterpolator(self.fitMztab, self.sigmaMhtab, bounds_error = False, fill_value = np.nan)# no need to log since it doesnt vary dramatically - - self.dsigmadMintlog = RegularGridInterpolator(self.fitMztab, self.dsigmadMMhtab, bounds_error = False, fill_value = np.nan) - - - #also build an interpolator for sigma(R) of the R we integrate over (for CD and EoR). These R >> Rhalo typically, so need new table. - self.sigmaofRtab = np.array([[Cosmo_Parameters.ClassCosmo.sigma(RR,zz) for zz in self.zHMFtab] for RR in Cosmo_Parameters._Rtabsmoo]) - self.fitRztab = [np.log(Cosmo_Parameters._Rtabsmoo), self.zHMFtab] - self.sigmaRintlog = RegularGridInterpolator(self.fitRztab, self.sigmaofRtab, bounds_error = False, fill_value = np.nan) #no need to log either - - - - - def HMF_int(self, Mh, z): - "Interpolator to find HMF(M,z), designed to take a single z but an array of Mh in Msun" - _logMh = np.log(Mh) - - logMhvec = np.asarray([_logMh]) if np.isscalar(_logMh) else np.asarray(_logMh) - inarray = np.array([[LM,z] for LM in logMhvec]) - - return np.exp(self.logHMFint(inarray) ) - - - - def sigma_int(self,Mh,z): - "Interpolator to find sigma(M,z), designed to take a single z but an array of Mh in Msun" - _logMh = np.log(Mh) - logMhvec = np.asarray([_logMh]) if np.isscalar(_logMh) else np.asarray(_logMh) - inarray = np.array([[LM,z] for LM in logMhvec]) - return self.sigmaintlog(inarray) - - def sigmaR_int(self,RR,z): - "Interpolator to find sigma(RR,z), designed to take a single z but an array of RR in cMpc" - _logRR = np.log(RR) - logRRvec = np.asarray([_logRR]) if np.isscalar(_logRR) else np.asarray(_logRR) - inarray = np.array([[LR,z] for LR in logRRvec]) - return self.sigmaRintlog(inarray) - - - def dsigmadM_int(self,Mh,z): - "Interpolator to find dsigma/dM(M,z), designed to take a single z but an array of Mh in Msun. Used in 21cmFAST mode" - _logMh = np.log(Mh) - logMhvec = np.asarray([_logMh]) if np.isscalar(_logMh) else np.asarray(_logMh) - inarray = np.array([[LM,z] for LM in logMhvec]) - return self.dsigmadMintlog(inarray) - - -def growth(Cosmo_Parameters, z): - "Scale-independent growth factor, interpolated from CLASS" - zlist = np.asarray([z]) if np.isscalar(z) else np.asarray(z) - if (Cosmo_Parameters.Flag_emulate_21cmfast==True): - _offsetgrowthdicke21cmFAST = 1-0.000248*(zlist-5.) #as in HMF, to fix growth. have to do it independently since it depends on z. - return Cosmo_Parameters.growthint(zlist) * _offsetgrowthdicke21cmFAST - else: - return Cosmo_Parameters.growthint(zlist) - - -def dgrowth_dz(CosmoParams, z): - "Derivative of growth factor growth() w.r.t. z" - zlist = np.asarray([z]) if np.isscalar(z) else np.asarray(z) - dzlist = zlist*0.001 - return (growth(CosmoParams, z+dzlist)-growth(CosmoParams, z-dzlist))/(2.0*dzlist) - - -def redshift_of_chi(CosmoParams, z): - "Returns z(chi) for any input comoving distance from today chi in Mpc" - return CosmoParams.zfofRint(z) - - - -def T021(Cosmo_Parameters, z): - "Prefactor in mK to T21 that only depends on cosmological parameters and z. Eg Eq.(21) in 2110.13919" - return 34 * pow((1+z)/16.,0.5) * (Cosmo_Parameters.omegab/0.022) * pow(Cosmo_Parameters.omegam/0.14,-0.5) - - -#UNUSED bias, just for reference -def bias_ST(Cosmo_Parameters, sigmaM): - # from https://arxiv.org/pdf/1007.4201.pdf Table 1 - a_ST = Cosmo_Parameters.a_ST - p_ST = Cosmo_Parameters.p_ST - delta_crit_ST = Cosmo_Parameters.delta_crit_ST - nu = delta_crit_ST/sigmaM - nutilde = np.sqrt(a_ST) * nu - - return 1.0 + (nutilde**2 - 1.0 + 2. * p_ST/(1.0 + nutilde**(2. * p_ST) ) )/delta_crit_ST - -def bias_Tinker(Cosmo_Parameters, sigmaM): - #from https://arxiv.org/pdf/1001.3162.pdf, Delta=200 - delta_crit_ST = Cosmo_Parameters.delta_crit_ST - nu = delta_crit_ST/sigmaM - - #Tinker fit - _Deltahalo = 200; - _yhalo = np.log10(_Deltahalo) - _Abias = 1.0 + 0.24 * _yhalo * np.exp(-(4.0/_yhalo)**4.) - _abias = 0.44*_yhalo-0.88 - _Bbias = 0.183 - _bbias = 1.5 - _Cbias = 0.019 + 0.107 * _yhalo + 0.19 * np.exp(-(4.0/_yhalo)**4.) - _cbias = 2.4 - - return 1.0 - _Abias*(nu**_abias/(nu**_abias + delta_crit_ST**_abias)) + _Bbias * nu**_bbias + _Cbias * nu**_cbias - -#UNUSED: -# def interp2Dlinear_only_y(arrayxy, arrayz, x, y): -# "2D interpolator where the x axis is assumed to be an array identical to the trained x. That is, an array of 1D linear interpolators. arrayxy is [x,y]. arrayz is result. x is the x input (=arrayxy[0]), and y the y input. Returns z result (array)" -# if((x != arrayxy[0]).all()): -# print('ERROR on interp2Dlinear_only_y, x need be the same in interp and input') -# return -1 -# Ny = len(arrayxy[1]) -# ymin, ymax = arrayxy[1][[0,-1]] -# if((y > ymax or y>> zeus21.User_Parameters(precisionboost=0.5) - - Parameters can also be changed afterwards: - >>> UserParams = zeus21.User_Parameters() - >>> UserParams.precisionboost = 0.5 - - - Parameters - ---------- - precisionboost: float - Make integrals take more points for boost in precision, the baseline being 1.0. - dlogzint_target: - Target number of redshift bins for the redsfhit arrays in log space. - FLAG_FORCE_LINEAR_CF: int (False or True) - False to do standard calculation, True to force linearization of correlation function. - MIN_R_NONLINEAR: float - Minimum radius R/cMpc in which we start doing the nonlinear calculation. - Below ~1 it will blow up because sigma > 1 eventually, and our exp(delta) approximation breaks. - Check if you play with it and if you change Window(). - MAX_R_NONLINEAR: float - Maximum radius R/cMpc in which we start doing the nonlinear calculation (above this it is very linear) - FLAG_DO_DENS_NL: bool - Whether to do the nonlinear (ie lognormal) calculation for the density field itself and its cross correlations. - Small (<3%) correction in dd, but non trivial (~10%) in d-xa and d-Tx - FLAG_WF_ITERATIVE: bool - Whether to iteratively do the WF correction as in Hirata2006. - zmin_T21: float - Minimum redshift to which we compute the T21 signals. - DO_ONLY_GLOBAL: bool - Whether zeus21 only runs the global T21 signal (and not fluctuations). - - Attributes - ---------- - C2_RENORMALIZATION_FLAG: int (False or True) - Whether to renormalize the C2 oefficients (appendix in 2302.08506). - """ - - precisionboost: float = 1.0 - dlogzint_target: float = 0.02 - FLAG_FORCE_LINEAR_CF: bool = False - MIN_R_NONLINEAR: float = 2.0 - MAX_R_NONLINEAR: float = 100.0 - FLAG_DO_DENS_NL: bool = False - FLAG_WF_ITERATIVE: bool = True - zmin_T21: float = 5. - DO_ONLY_GLOBAL: bool = False - - C2_RENORMALIZATION_FLAG: int = _field(init=False) - - def __post_init__(self): - schema = { - "FLAG_FORCE_LINEAR_CF": (bool, None), - "FLAG_DO_DENS_NL": (bool, None), - "FLAG_WF_ITERATIVE": (bool, None), - "DO_ONLY_GLOBAL": (bool, None), - } - validate_fields(self, schema) - - self.C2_RENORMALIZATION_FLAG = not self.FLAG_FORCE_LINEAR_CF - - -@dataclass(kw_only=True) -class Cosmo_Parameters: - """ - Cosmological parameters (including the 6 LCDM + other parameters) for zeus21 and running of CLASS. - - Parameters - ---------- - UserParams: User_Parameters - zeus21 class for the user parameters. - omegab: float - Baryon density * h^2. - omegac: float - CDM density * h^2. - h_fid: float - Hubble constant / 100. - As: float - Amplitude of initial fluctuations. - ns: float - Spectral index. - tau_fid: float - Optical depth to reionization. - kmax_CLASS: float - Maximum wavenumber to be passed to CLASS. - zmax_CLASS: float - Maximum redshift to be passed to CLASS. - zmin_CLASS: float - Minimum redshift to be passed to CLASS. - Rs_min: float - Minimum radius to be passed to CLASS. - Rs_max: float - Maximum radius to be passed to CLASS. - Flag_emulate_21cmfast: bool - Whether zeus21 emulates 21cmFAST cosmology (used in HMF, LyA, and X-ray opacity calculations). Default is False. - When False, sets the Star Formation Rate model to GALLUMI-like, and when True to 21cmfast-like (ignores Mc and beta and has a t* later in SFR()). - USE_RELATIVE_VELOCITIES: bool - Whether to use v_cb. - HMF_CHOICE: str - Which HMF to use. - "ST" for the classic Sheth-Tormen (f(nu)), "Yung" for the Tinker08 (f(sigma)) calibrated to Yung+23. - - Attributes - ---------- - ClassCosmo: Class - CLASS instance to compute cosmology. - omegam: float - Matter density * h^2. - OmegaM: float - Matter density. - rhocrit: float - Critical density. - OmegaR: float - Radiation density. - OmegaL: float - Dark energy density. - OmegaB: float - Baryon density. - rho_M0: float - Actual matter density. - z_rec: float - Recombination reshift. - sigma_vcb: float - Square root of the variance of the relative velocity field. - vcb_avg: float - Average of the relative velocity field. - Y_He: float - Helium mass fraction. - x_He: - Helium-to-hydrogen number density ratio. - f_H: float - Hydrogen number density ratio relative to baryons. - f_He: float - Helium number density ratio relative to baryons. - mu_baryon: float - Mean baryonic weight. - mu_baryon_Msun: float - Mean baryonic weight relative to the solar mass. - constRM: float - Radius-to-mass conversions for HMF. Used for CLASS input so assumes tophat. - zfofRint: interp1d - Interpolation for the redshift as a function of the comoving distance. - chiofzint: interp1d - Interpolation for the comoving distance as a function of the redshift. - Hofzint: interp1d - Interpolation for the Hubble rate as a function of the redshift. - Tadiabaticint: - Interpolation for the adiabatic temperature as a function of redshift. - xetanhint: interp1d - Interpolation for the electron fraction as a function of redshift. - growthint: interp1d - Interpolation for the growth faction as a function of redshift. - NRs: np.ndarray - Number of radii. - indexminNL: np.ndarray - Index of the minimum radius R/cMpc in which we start doing the nonlinear calculation. - indexmaxNL: np.ndarray - Index of the maximum radius R/cMpc in which we start doing the nonlinear calculation. - a_ST: float - Rescaling of the HMF barrier. - p_ST: float - Correction factor for the abundance of small mass objects. - Amp_ST: float - Normalization factor for the halo mass function. - delta_crit_ST: float - Barrier for halo to collapse in Sheth-Tormen formalism. - a_corr_EPS: float - Correction to the EPS relation between nu and nu' when doing extended PS. Follows hi-z simulation results from Schneider+21. - """ - ### Non-default parameters - UserParams: InitVar[User_Parameters] - - - ### Default parameters - # 6 LCDM parameters - omegab: float = 0.0223828 - omegac: float = 0.1201075 - h_fid: float = 0.67810 - As: float = 2.100549e-09 - ns: float = 0.9660499 - tau_fid: float = 0.05430842 - - # Other params for CLASS - kmax_CLASS: float = 500. - zmax_CLASS: float = 50. - zmin_CLASS: float = 5. - - # Shells that we integrate over at each z. - Rs_min: float = 0.05 ### ASK JULIAN for changing the name - Rs_max: float = 2000. ### ASK JULIAN for changing the name - - # Flags - Flag_emulate_21cmfast: bool = False - USE_RELATIVE_VELOCITIES: bool = False - # === BEGIN EDIT (inputs_v2: par/perp eta split) === - # When True (and USE_RELATIVE_VELOCITIES is also True), build the - # parallel/perpendicular eta power spectra and corresponding windowed - # correlation functions (xiEtaPar_RR_CF, xiEtaPerp_RR_CF) instead of - # the legacy isotropic xiEta_RR_CF. Consumed by - # zeus21.correlations_v2.Power_Spectra via - # getattr(Cosmo_Parameters, 'USE_ETA_PARPERP_SPLIT', False). - USE_ETA_PARPERP_SPLIT: bool = False - # === END EDIT (inputs_v2: par/perp eta split) === - HMF_CHOICE: str = "ST" - - - ### Additional parameters and attributes set in the following - # LCDM parameters - ClassCosmo: Class = _field(init=False) - omegam: float = _field(init=False) - OmegaM: float = _field(init=False) - rhocrit: float = _field(init=False) - OmegaR: float = _field(init=False) - OmegaL: float = _field(init=False) - OmegaB: float = _field(init=False) - rho_M0: float = _field(init=False) - z_rec: float = _field(init=False) - - # v_cb parameters - sigma_vcb: float = _field(init=False) - vcb_avg: float = _field(init=False) - - # Number densities and mass fractions - Y_He: float = _field(init=False) - x_He: float = _field(init=False) - f_H: float = _field(init=False) - f_He: float = _field(init=False) - mu_baryon: float = _field(init=False) - mu_baryon_Msun: float = _field(init=False) - - # R->M conversions for HMF - constRM: float = _field(init=False) - - # Redshifts and comoving distances - _ztabinchi: np.ndarray = _field(init=False) - _chitab: Any = _field(init=False) - _Hztab: Any = _field(init=False) - zfofRint: interp1d = _field(init=False) - chiofzint: interp1d = _field(init=False) - Hofzint: interp1d = _field(init=False) - - # Thermodynamics - Tadiabaticint: interp1d = _field(init=False) - xetanhint: interp1d = _field(init=False) - - # Growth - growthint: interp1d = _field(init=False) - - # Radii - NRs: np.ndarray = _field(init=False) - _Rtabsmoo: np.ndarray = _field(init=False) - _dlogRR: np.ndarray = _field(init=False) - indexminNL: np.ndarray = _field(init=False) - indexmaxNL: np.ndarray = _field(init=False) - - # HMF-related constants - a_ST: float = _field(init=False) - p_ST: float = _field(init=False) - Amp_ST: float = _field(init=False) - delta_crit_ST: float = _field(init=False) - a_corr_EPS: float = _field(init=False) - - tageofzMyr: interp1d = _field(init=False) - zfoftageMyr: interp1d = _field(init=False) - - def __post_init__(self, UserParams): - - schema = { - "Flag_emulate_21cmfast": (bool, None), - "USE_RELATIVE_VELOCITIES": (bool, None), - # === BEGIN EDIT (inputs_v2: par/perp eta split) === - "USE_ETA_PARPERP_SPLIT": (bool, None), - # === END EDIT (inputs_v2: par/perp eta split) === - "HMF_CHOICE": (str, {'ST','Yung'}), - } - validate_fields(self, schema) - - # run CLASS - self.ClassCosmo = self.runclass() - - # derived params - self.omegam = self.omegab + self.omegac - self.OmegaM = self.ClassCosmo.Omega_m() - self.rhocrit = 3 * 100**2 / (8 * np.pi* constants.MsunToKm * constants.c_kms**2 * constants.KmToMpc) * self.h_fid**2 # Msun/Mpc^3 - self.OmegaR = self.ClassCosmo.Omega_r() - self.OmegaL = self.ClassCosmo.Omega_Lambda() - self.OmegaB = self.ClassCosmo.Omega_b() - self.rho_M0 = self.OmegaM * self.rhocrit - - _zlistforage = np.logspace(5,-3,10000) - _zlistforage[-1]=0.0 - _Hztab = self.ClassCosmo.z_of_r(_zlistforage)[1] #chi and dchi/dz - - ### TODO: check if this is the same as cosmic time in cosmology - tagetabyr = -cumulative_trapezoid(constants.Mpctoyr/_Hztab/(1+_zlistforage),_zlistforage) - tagetabyr = np.insert(tagetabyr,0,0) - - self.tageofzMyr = interp1d(_zlistforage,tagetabyr/1e6) #interpolators for age in Myr as a function of z - self.zfoftageMyr = interp1d(tagetabyr/1e6,_zlistforage) #and it's inverse, z for age t in Myr - - - self.z_rec = self.ClassCosmo.get_current_derived_parameters(['z_rec'])['z_rec'] - - ### v_cb flag - self.sigma_vcb = self.ClassCosmo.pars['sigma_vcb'] - self.vcb_avg = self.ClassCosmo.pars['v_avg'] - - ### number densities and mass fractions - self.Y_He = self.ClassCosmo.get_current_derived_parameters(['YHe'])['YHe'] - self.x_He = self.Y_He/4.0/(1.0 - self.Y_He) #=nHe/nH - self.f_H = (1.0 - self.Y_He)/(1.0 - 3.0/4.0 * self.Y_He) #=nH/nb - self.f_He = self.Y_He/4.0/(1.0 - 3.0/4.0 * self.Y_He) #=nHe/nb - - self.mu_baryon = (1 + self.x_He * 4.)/(1 + self.x_He) * constants.mH_GeV #mproton ~ 0.94 GeV - self.mu_baryon_Msun = self.mu_baryon / constants.MsuntoGeV - - # for R->M conversions for HMF. Used for CLASS input so assumes tophat. - self.constRM = self.OmegaM*self.rhocrit * 4.0 * np.pi/3.0 - - # redshifts and comoving distances - self._ztabinchi = np.linspace(0.0, 1100. , 10000) #cheap so do a lot - self._chitab, self._Hztab = self.ClassCosmo.z_of_r(self._ztabinchi) #chi and dchi/dz - self.zfofRint = interp1d(self._chitab, self._ztabinchi) - self.chiofzint = interp1d(self._ztabinchi,self._chitab) - self.Hofzint = interp1d(self._ztabinchi,self._Hztab) - - # thermodynamics - _thermo = self.ClassCosmo.get_thermodynamics() - self.Tadiabaticint = interp1d(_thermo['z'], _thermo['Tb [K]']) - self.xetanhint = interp1d(_thermo['z'], _thermo['x_e']) - - # growth - _ztabingrowth = np.linspace(0., 100. , 2000) - _growthtabint = np.array([self.ClassCosmo.scale_independent_growth_factor(zz) for zz in _ztabingrowth]) - self.growthint = interp1d(_ztabingrowth,_growthtabint) - - # shells that we integrate over at each z. - if self.Flag_emulate_21cmfast: - self.Rs_min = 0.62*1.5 #same as minmum R in 21cmFAST for their standard 1.5 Mpc cell resolution. 0.62 is their 'L_FACTOR' - self.Rs_max = 500. #same as R_XLy_MAX in 21cmFAST. Too low? - - # radii - self.NRs = np.floor(45*UserParams.precisionboost).astype(int) - self._Rtabsmoo = np.logspace(np.log10(self.Rs_min), np.log10(self.Rs_max), self.NRs) # Smoothing Radii in Mpc com - self._dlogRR = np.log(self.Rs_max/self.Rs_min)/(self.NRs-1.0) - - self.indexminNL = (np.log(UserParams.MIN_R_NONLINEAR/self.Rs_min)/self._dlogRR).astype(int) - self.indexmaxNL = (np.log(UserParams.MAX_R_NONLINEAR/self.Rs_min)/self._dlogRR).astype(int) + 1 #to ensure it captures MAX_R - - # HMF-related constants - if not self.Flag_emulate_21cmfast: # standard, best fit ST from Schneider+21 - self.a_ST = 0.707 # OG ST fit, or 0.85 to fit 1805.00021 - self.p_ST = 0.3 - self.Amp_ST = 0.3222 - self.delta_crit_ST = 1.686 - self.a_corr_EPS = self.a_ST - else: # emulate 21cmFAST, including HMF from Jenkins 2001 - self.HMF_CHOICE = 'ST' # forced to match their functional form - print('Since Flag_emulate_21cmfast==True, the code set HMF_CHOICE==ST') - self.a_ST = 0.73 - self.p_ST = 0.175 - self.Amp_ST = 0.353 - self.delta_crit_ST = 1.68 - self.a_corr_EPS = 1.0 - - # Run matter and relative velocities correlations - self.run_correlations() - - def runclass(self): - "Set up CLASS cosmology. Takes CosmologyIn class input and returns CLASS Cosmology object" - ClassCosmo = Class() - ClassCosmo.set({'omega_b': self.omegab,'omega_cdm': self.omegac, - 'h': self.h_fid,'A_s': self.As,'n_s': self.ns,'tau_reio': self.tau_fid}) - ClassCosmo.set({'output':'mPk','lensing':'no','P_k_max_1/Mpc':self.kmax_CLASS, 'z_max_pk': self.zmax_CLASS}) ###HAC: add vTK to outputs - ClassCosmo.set({'gauge':'synchronous'}) - #hfid = ClassCosmo.h() # get reduced Hubble for conversions to 1/Mpc - - # and run it (see warmup for their doc) - ClassCosmo.compute() - - ClassCosmo.pars['Flag_emulate_21cmfast'] = self.Flag_emulate_21cmfast - - ###HAC: Adding VCB feedback via a second run of CLASS: - if self.USE_RELATIVE_VELOCITIES: - - kMAX_VCB = 50.0 - ###HAC: getting z_rec from first CLASS run - z_rec = ClassCosmo.get_current_derived_parameters(['z_rec'])['z_rec'] - z_drag = ClassCosmo.get_current_derived_parameters(['z_d'])['z_d'] - - ###HAC: Running CLASS a second time just to get velocity transfer functions at recombination - ClassCosmoVCB = Class() - ClassCosmoVCB.set({'omega_b': self.omegab,'omega_cdm': self.omegac, - 'h': self.h_fid,'A_s': self.As,'n_s': self.ns,'tau_reio': self.tau_fid}) - ClassCosmoVCB.set({'output':'vTk'}) - ClassCosmoVCB.set({'P_k_max_1/Mpc':kMAX_VCB, 'z_max_pk':12000}) - ClassCosmoVCB.set({'gauge':'newtonian'}) - ClassCosmoVCB.compute() - velTransFunc = ClassCosmoVCB.get_transfer(z_drag) - - kVel = velTransFunc['k (h/Mpc)'] * self.h_fid - theta_b = velTransFunc['t_b'] - theta_c = velTransFunc['t_cdm'] - - sigma_vcb = np.sqrt(np.trapezoid(self.As * (kVel/0.05)**(self.ns-1) /kVel * (theta_b - theta_c)**2/kVel**2, kVel)) * constants.c_kms - ClassCosmo.pars['sigma_vcb'] = sigma_vcb - - ###HAC: now computing average velocity assuming a Maxwell-Boltzmann distribution of velocities - velArr = np.geomspace(0.01, constants.c_kms, 1000) #in km/s - vavgIntegrand = (3 / (2 * np.pi * sigma_vcb**2))**(3/2) * 4 * np.pi * velArr**2 * np.exp(-3 * velArr**2 / (2 * sigma_vcb**2)) - ClassCosmo.pars['v_avg'] = np.trapezoid(vavgIntegrand * velArr, velArr) - - ###HAC: Computing Vcb Power Spectrum - ClassCosmo.pars['k_vcb'] = kVel - ClassCosmo.pars['theta_b'] = theta_b - ClassCosmo.pars['theta_c'] = theta_c - P_vcb = self.As * (kVel/0.05)**(self.ns-1) * (theta_b - theta_c)**2/kVel**2 * 2 * np.pi**2 / kVel**3 - - p_vcb_intp = interp1d(np.log(kVel), P_vcb) - ClassCosmo.pars['P_vcb'] = P_vcb - - ###HAC: Computing Vcb^2 (eta) Power Spectra - kVelIntp = np.geomspace(1e-4, kMAX_VCB, 512) - rVelIntp = 2 * np.pi / kVelIntp - - j0bessel = lambda x: np.sin(x)/x - j2bessel = lambda x: (3 / x**2 - 1) * np.sin(x)/x - 3*np.cos(x)/x**2 - - psi0 = 1 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapezoid(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j0bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) - psi2 = -2 / 3 / (sigma_vcb/constants.c_kms)**2 * np.trapezoid(kVelIntp**2 / 2 / np.pi**2 * p_vcb_intp(np.log(kVelIntp)) * j2bessel(kVelIntp * np.transpose([rVelIntp])), kVelIntp, axis = 1) - - # === BEGIN EDIT (inputs_v2: par/perp eta split) === - # Mutually exclusive: only the keys for the active mode are - # written to ClassCosmo.pars so a stale consumer of the other - # mode fails loudly. - if self.USE_ETA_PARPERP_SPLIT: - k_etapar, P_etapar = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 + psi2)**2, extrap = False) - k_etaperp, P_etaperp = mcfit.xi2P(rVelIntp, l=0, lowring = True)(6 * (psi0 - psi2/2.0)**2, extrap = False) - - ClassCosmo.pars['k_etapar'] = k_etapar[P_etapar > 0] - ClassCosmo.pars['P_etapar'] = P_etapar[P_etapar > 0] - ClassCosmo.pars['k_etaperp'] = k_etaperp[P_etaperp > 0] - ClassCosmo.pars['P_etaperp'] = P_etaperp[P_etaperp > 0] - else: - k_eta, P_eta = mcfit.xi2P(rVelIntp, l=0, lowring = True)((6 * psi0**2 + 3 * psi2**2), extrap = False) - - ClassCosmo.pars['k_eta'] = k_eta[P_eta > 0] - ClassCosmo.pars['P_eta'] = P_eta[P_eta > 0] - # === END EDIT (inputs_v2: par/perp eta split) === - - # print("HAC: Finished running CLASS a second time to get velocity transfer functions") - - else: - ClassCosmo.pars['v_avg'] = 0.0 - ClassCosmo.pars['sigma_vcb'] = 1.0 #Avoids excess computation, but doesn't matter what value we set it to because the flag in inputs.py sets all feedback parameters to zero - - return ClassCosmo - - def run_correlations(self): - #we choose the k to match exactly the log FFT of input Rtabsmoo. - - self._klistCF, _dummy_ = mcfit.xi2P(self._Rtabsmoo, l=0, lowring=True)(0*self._Rtabsmoo, extrap=False) - self.NkCF = len(self._klistCF) - - self._PklinCF = np.zeros(self.NkCF) # P(k) in 1/Mpc^3 - for ik, kk in enumerate(self._klistCF): - self._PklinCF[ik] = self.ClassCosmo.pk(kk, 0.0) # function .pk(k,z) - - - - self._xif = mcfit.P2xi(self._klistCF, l=0, lowring=True) - - - self.xi_RR_CF = self.get_xi_R1R2(field = 'delta') - self.ClassCosmo.pars['xi_RR_CF'] = np.copy(self.xi_RR_CF) #store correlation function for gamma_III correction in SFRD - - ###HAC: Interpolated object for eta power spectrum - # === BEGIN EDIT (inputs_v2: par/perp eta split) === - # Branch on USE_ETA_PARPERP_SPLIT so the two modes are mutually - # exclusive: only the CF arrays for the active mode are populated. - if self.USE_RELATIVE_VELOCITIES == True: - if self.USE_ETA_PARPERP_SPLIT: - P_etapar_interp = interp1d(self.ClassCosmo.pars['k_etapar'], self.ClassCosmo.pars['P_etapar'], bounds_error = False, fill_value = 0) - P_etaperp_interp = interp1d(self.ClassCosmo.pars['k_etaperp'], self.ClassCosmo.pars['P_etaperp'], bounds_error = False, fill_value = 0) - self._PkEtaParCF = P_etapar_interp(self._klistCF) - self._PkEtaPerpCF = P_etaperp_interp(self._klistCF) - self.xiEtaPar_RR_CF = self.get_xi_R1R2(field = 'vcb_par') - self.xiEtaPerp_RR_CF = self.get_xi_R1R2(field = 'vcb_perp') - else: - P_eta_interp = interp1d(self.ClassCosmo.pars['k_eta'], self.ClassCosmo.pars['P_eta'], bounds_error = False, fill_value = 0) - self._PkEtaCF = P_eta_interp(self._klistCF) - self.xiEta_RR_CF = self.get_xi_R1R2(field = 'vcb') - else: - self._PkEtaCF = np.zeros_like(self._PklinCF) - self.xiEta_RR_CF = np.zeros_like(self.xi_RR_CF) - # === END EDIT (inputs_v2: par/perp eta split) === - - - def get_xi_R1R2 (self, field = None): - "same as get_xi_z0_lin but smoothed over two different radii with Window(k,R) \ - same separations rs as get_xi_z0_lin so it does not output them." - - lengthRarray = self.NRs - windowR1 = z21_utilities.Window(self._klistCF.reshape(lengthRarray, 1, 1), self._Rtabsmoo.reshape(1, 1, lengthRarray)) - windowR2 = z21_utilities.Window(self._klistCF.reshape(1, lengthRarray,1), self._Rtabsmoo.reshape(1, 1, lengthRarray)) - - if field == 'delta': - _PkRR = np.array([[self._PklinCF]]) * windowR1 * windowR2 - elif field == 'vcb': - _PkRR = np.array([[self._PkEtaCF]]) * windowR1 * windowR2 - # === BEGIN EDIT (inputs_v2: par/perp eta split) === - elif field == 'vcb_par': - _PkRR = np.array([[self._PkEtaParCF]]) * windowR1 * windowR2 - elif field == 'vcb_perp': - _PkRR = np.array([[self._PkEtaPerpCF]]) * windowR1 * windowR2 - # === END EDIT (inputs_v2: par/perp eta split) === - else: - raise ValueError('field has to be either delta or vcb in get_xi_R1R2') - - self.rlist_CF, xi_RR_CF = self._xif(_PkRR, extrap = False) - - return xi_RR_CF - - - -@dataclass(kw_only=True) -class Astro_Parameters: - """ - Astrophysical parameters for zeus21. - - Parameters - ---------- - Cosmo_Parameters: Cosmo_Parameters - zeus21 class for the cosmological parameters. Needs to be inputed. - accretion_model: str - Accretion model. "exp" for exponential, "EPS" for EPS. Default is "EPS". - USE_POPIII: bool - Whether to use Pop III. Default is False. - USE_LW_FEEDBACK: bool - Whether to use the Lyman-Werner feedback. Default is True. - quadratic_SFRD_lognormal: bool - Whether to use the second order correction to the SFRD approximation. Default is True. - epsstar: float - Amplitude of the star formation efficiency (at M_pivot). Default is 0.1. - dlog10epsstardz: float - Derivative of epsstar with respect to z. Default is 0. - alphastar: float - Power law index of the star formation efficiency at low masses. Default 0.5. - betastar: float - Power law index of the star formation efficiency at high masses. Only used when astromodel=0. Default -0.5. - Mc: float - Mass at which the star formation efficiency cuts. Only used when astromodel=0. Default 3e11. - sigmaUV: float - Stochasticity (gaussian rms) in the halo-galaxy connection P(MUV | Mh). Default is 0.5. - alphastar_III: float - Power law index of the Pop III star formation efficiency at low masses. Default 0. - betastar_III: float - Power law index of the Pop III star formation efficiency at high masses. Default 0. - fstar_III: float - Peak amplitude of the Pop III star formation efficiency. Default 10**(-2.5). - Mc_III: float - Mass at which the Pop III star formation efficiency cuts. Default 1e7. - dlog10epsstardz_III: float - Derivative of epsstar with respect to z for Pop III. Default is 0. - N_alpha_perbaryon_II: float - Number of photons between LyA and Ly Continuum per baryon (from LB05). Default is 9690. - N_alpha_perbaryon_III: float - Number of photons between LyA and Ly Continuum per baryon (from LB05) for Pop III. Default is 17900. - L40_xray: float - Soft-band (E<2 keV) lum/SFR in Xrays in units of 10^40 erg/s/(Msun/yr). Default is 3.0. - E0_xray: float - Minimum energy in eV. Default is 500. - alpha_xray: float - Xray SED power-law index. Default is -1. - L40_xray_III: float - Soft-band (E<2 keV) lum/SFR in Xrays in units of 10^40 erg/s/(Msun/yr) for Pop III. Default is 3.0. - alpha_xray_III: float - Xray SED power-law index. Default is -1. - Emax_xray_norm: float - Max energy in eV to normalize SED. Default at 2000 eV. - fesc10: float - Amplitude of the escape fraction. Default is 0.1. - Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. - alphaesc: float - Index for the escape fraction. Default is 0. - Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. - fesc7_III: float - Amplitude of the Pop III escape fraction. Default is 10**(-1.35). - Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. - alphaesc_III: float - Index for the Pop III escape fraction. Default is -0.3. - Escape fraction assumed to be a power law normalized (fesc10) at M=1e10 Msun with index alphaesc. - clumping: float = 3. - Clumping factor, which is z-independent and fixed for now. Default is 3, changed to 2 when Flag_emulate_21cmfast=True. - R_linear_sigma_fit_input: float - Initial guess radius at which the linear fit of the barrier is computed. Default is 3. - FLAG_BMF_converge: bool - Whether zeus21 allow the BMF to try and make the average ionized fraction converge. Default is True. - max_iter: int - Maximum iteration allowed for the convergence of the BMF. Default is 10. - ZMAX_REION: float - Maximum redshift to which the reionization quantities are computed. Default is 30. - Rbub_min: float - Minimum bubble radius. Default is 0.05. - A_LW: float - Parameters controlling the LW feedback factor (see Munoz+22, eq 13). Default is 2.0. - beta_LW: float - Parameters controlling the LW feedback factor (see Munoz+22, eq 13). Default is 0.6. - A_vcb: float - Normalization for the relative velocity feedback parameter. Default is 1.0. - beta_vcb: float - Spectral index for the relative velocity feedback parameter. Default 1.8 - Mturn_fixed: float | None - Turn-over halo mass at which the star formation rate cuts. Default is None. - FLAG_MTURN_SHARP: bool - Whether to do sharp cut at Mturn_fixed or regular exponential cutoff. Only active if FLAG_MTURN_FIXED and turned on by hand. Default is False. - C0dust: float - Calibration parameter for the dust correction for UVLF. Default is 4.43 (following Meurer+99). Input 4.54 for Overzier+01. - C1dust: float - Calibration parameter for the dust correction for UVLF. Default 1.99 for Meurer99. Input 2.07 for Overzier+01. - - Attributes - ---------- - _zpivot: float - Redshift at which the eps and dlogeps/dz are evaluated. Set by zeus21 to 8. - fstarmax: float - Peak amplitude for the star formation efficiency. Set by zeus21 to 1. - _zpivot_III: float - Redshift at which the eps and dlogeps/dz are evaluated for Pop III. Set by zeus21 to 8. - Emax_xray_integral: float - Max energy in eV that zeus21 integrate up to. Higher than Emax_xray_norm since photons can redshift from higher z. Set by zeus21 to 10000. - Nen_xray: int - Number of energies to do the xray integrals. Set by zeus21 to 30. - _log10EMIN_INTEGRATE: float - Minimum energy zeus21 integrates to, to account for photons coming from higher z that redshift. - _log10EMAX_INTEGRATE: float - Maximum energy zeus21 integrates to, to account for photons coming from higher z that redshift. - Energylist: np.ndarray - Energies, in eV. - dlogEnergy: float - Used to get dlog instead of dlog10. - N_ion_perbaryon_II: int - Number of ionizing photons per baryon. Fixed for PopII-type (Salpeter) by zeus21 to 5000. - N_ion_perbaryon_III: int - Number of ionizing photons per baryon for Pop III. Fixed for PopIII-type to 44000 (or 52480 when Flag_emulate_21cmfast=True), from Klessen & Glover 2023 Table A2 (2303.12500). - N_LW_II: float - Number of LW photons per baryon. - Assuming BL05 stellar spectrum, equal to N_alpha_perbaryon_II * fraction of photons that fall in the LW band. - N_LW_III: float - Number of LW photons per baryon. - Assuming Intermediate IMF from 2202.02099, equal to 4.86e-22 / (11.9 * u.eV).to(u.erg).value * 5.8e14. - FLAG_MTURN_FIXED: bool - Whether to fix Mturn or use Matom(z) at each z. Set by zeus21 depending on Mturn_fixed. - - """ - ### Non-default parameters - CosmoParams: InitVar[Cosmo_Parameters] - - - ### Default and init=False parameters - # Flags - accretion_model: str = "exp" - USE_POPIII: bool = False - USE_LW_FEEDBACK: bool = True - quadratic_SFRD_lognormal: bool = True - - # SFR(Mh) parameters - popII - epsstar: float = 0.1 - dlog10epsstardz: float = 0.0 - alphastar: float = 0.5 - betastar: float = -0.5 - Mc: float = 3e11 - _zpivot: float = _field(init=False) - fstarmax: float = _field(init=False) - - # SFR(Mh) parameters - popIII - epsstar_III: float = 10**(-2.5) - dlog10epsstardz_III: float = 0.0 - alphastar_III: float = 0. - betastar_III: float = 0. - Mc_III: float = 1e7 - _zpivot_III: float = _field(init=False) - - # SFR(Mh) parameters - popIII Atomic Cooling Component - USE_POPIII_ACH: bool = False - DETACH_III_ACH: bool = False - epsstar_III_ACH: float = 0. - dlog10epsstardz_III_ACH: float = 0.0 - alphastar_III_ACH: float = 0. - betastar_III_ACH: float = 0. - Mc_III_ACH: float = 1e7 - _zpivot_III_ACH: float = _field(init=False) - - # Lyman-alpha parameters - N_alpha_perbaryon_II: float = 9690 - N_alpha_perbaryon_III: float = 17900 - - # Xray parameters, assumed power-law for now - L40_xray: float = 3.0 - E0_xray: float = 500. - alpha_xray: float = -1.0 - L40_xray_III: float = 3.0 - alpha_xray_III: float = -1.0 - Emax_xray_norm: float = 2000 - Emax_xray_integral: float = _field(init=False) # Max energy in eV that we integrate up to. Higher than Emax_xray_norm since photons can redshift from higher z - - # table with how many energies we integrate over - Nen_xray: int = _field(init=False) - _log10EMIN_INTEGRATE: float = _field(init=False) # to account for photons coming from higher z that redshift - _log10EMAX_INTEGRATE: float = _field(init=False) - Energylist: np.ndarray = _field(init=False) # in eV - dlogEnergy: float = _field(init=False) # to get dlog instead of dlog10 - - # Reionization parameters - fesc10: float = 0.1 - alphaesc: float = 0.0 - fesc7_III: float = 10**(-1.35) - alphaesc_III: float = -0.3 - clumping: float = 3. - N_ion_perbaryon_II: int = _field(init=False) # fixed for PopII-type (Salpeter) - N_ion_perbaryon_III: int = _field(init=False) # fixed for PopIII-type, from Klessen & Glover 2023 Table A2 (2303.12500) - R_linear_sigma_fit_input: float = 10. - FLAG_BMF_converge: bool = True - max_iter: int = 10 - ZMAX_REION: float = 30 - Rbub_min: float = 0.05 - - # Lyman-Werner feedback paramters - A_LW: float = 2.0 - beta_LW: float = 0.6 - N_LW_II: float = _field(init=False) # number of LW photons per baryon #assuming BL05 stellar spectrum, equal to N_alpha_perbaryon_II * fraction of photons that fall in the LW band - N_LW_III: float = _field(init=False) # number of LW photons per baryon #assuming Intermediate IMF from 2202.02099, equal to 4.86e-22 / (11.9 * u.eV).to(u.erg).value * 5.8e14 - - # relative velocity - A_vcb: float = 1.0 - beta_vcb: float = 1.8 - - # 21cmFAST emulation: SFE parameters - Mturn_fixed: float | None = None - FLAG_MTURN_SHARP: bool = False - FLAG_MTURN_FIXED: bool = _field(init=False) # whether to fix Mturn or use Matom(z) at each z - - # BURSTINESS - FLAG_USE_PSD: bool = False - FLAG_COMPARE_BAGPIPES: bool = False - SEDMODEL: str = "BPASS" - sigmaPSD: float = 0.5, - dsigmaPSDdlog10Mh: float = 0.0, - tauPSD: float = 10.0, - dlog10tauPSDdlog10Mh: float = 0.0, - _tcut_LUV_short: float = 30.0 #where we separate LUV short and long, in Myr, 30 Myr or 2*tau, whichever longer - FLAG_RENORMALIZE_AVG_SFH: bool = True - _minsigmaPSD: float = _field(init=False) - _maxsigmaPSD: float = _field(init=False) - _mintauPSD: float = _field(init=False) - _maxtauPSD: float = _field(init=False) - _tagesMyr: float = _field(init=False) - _dt_FFT: float = _field(init=False) - _N_FFT: float = _field(init=False) - _omegamin: float = _field(init=False) - _omegamax: float = _field(init=False) - - def __post_init__(self, CosmoParams): - - schema = { - "accretion_model": (str, {"EPS", "exp"}), - "USE_POPIII": (bool, None), - "USE_POPIII_ACH": (bool, None), - "DETACH_III_ACH": (bool, None), - "USE_LW_FEEDBACK": (bool, None), - "quadratic_SFRD_lognormal": (bool, None), - "FLAG_MTURN_SHARP": (bool, None), - "FLAG_USE_PSD": (bool, None), - "FLAG_COMPARE_BAGPIPES": (bool, None), - "FLAG_RENORMALIZE_AVG_SFH": (bool, None), - "SEDMODEL": (str, {"bagpipes", "BPASS_binaries", "BPASS"}), - } - validate_fields(self, schema) - - ### which SFR model we use. 0=Gallumi-like, 1=21cmfast-like - if not CosmoParams.Flag_emulate_21cmfast: # GALLUMI-like - self.accretion_model = self.accretion_model # choose the accretion model: 0 = exponential, 1= EPS. Default = EPS. - else: # 21cmfast-like, ignores Mc and beta and has a t* later in SFR() - self.tstar = 0.5 - self.fstar10 = self.epsstar - - # SFR(Mh) parameters - self._zpivot = 8.0 # fixed, at which z we evaluate eps and dlogeps/dz - self._zpivot_III = 8.0 # fixed, at which z we evaluate eps and dlogeps/dz - self._zpivot_III_ACH = 8.0 # fixed, at which z we evaluate eps and dlogeps/dz - self.fstarmax = 1.0 # where we cap it - - # Xray parameters - self.Emax_xray_integral = 10000. # Max energy in eV that we integrate up to. Higher than Emax_xray_norm since photons can redshift from higher z - if(self.E0_xray < constants.EN_ION_HI): - print("What the heck? How can E0_XRAY < EN_ION_HI?") - - # table with how many energies we integrate over - self.Nen_xray = 30 - self._log10EMIN_INTEGRATE = np.log10(self.E0_xray/2.0) # to account for photons coming from higher z that redshift - self._log10EMAX_INTEGRATE = np.log10(self.Emax_xray_integral) - self.Energylist = np.logspace(self._log10EMIN_INTEGRATE,self._log10EMAX_INTEGRATE,self.Nen_xray) # in eV - self.dlogEnergy = (self._log10EMAX_INTEGRATE - self._log10EMIN_INTEGRATE)/(self.Nen_xray-1.0)*np.log(10.) # to get dlog instead of dlog10 - - # Reionization parameters - if CosmoParams.Flag_emulate_21cmfast: - self.clumping = 2.0 # this is the 21cmFAST value - # number of ionizing photons per baryon - self.N_ion_perbaryon_II = 5000 # fixed for PopII-type (Salpeter) - if CosmoParams.Flag_emulate_21cmfast: - self.N_ion_perbaryon_III = 44000 # fixed for PopIII-type, from Klessen & Glover 2023 Table A2 (2303.12500) - else: - self.N_ion_perbaryon_III = 52480 - - ### HAC: LW feedback parameters - if not self.USE_LW_FEEDBACK: - self.A_LW = 0.0 - self.beta_LW = 0.0 - # number of LW photons per baryon - if not CosmoParams.Flag_emulate_21cmfast: - self.N_LW_II = 6200.0 #assuming BL05 stellar spectrum, equal to N_alpha_perbaryon_II * fraction of photons that fall in the LW band - self.N_LW_III = 12900.0 #assuming Intermediate IMF from 2202.02099, equal to 4.86e-22 / (11.9 * u.eV).to(u.erg).value * 5.8e14 - else: - popIIcorrection = 0.6415670418531249/2.5 #scaling used by 21cmfast to get correct number of Pop II LW photons per baryon - self.N_LW_II = popIIcorrection * self.N_alpha_perbaryon_II - popIIIcorrection = 0.7184627927009317/6.5 #scaling used by 21cmfast to get correct number of Pop III LW photons per baryon - self.N_LW_III = popIIIcorrection * self.N_alpha_perbaryon_III - - ### HAC: Relative Velocities parameters - if not CosmoParams.USE_RELATIVE_VELOCITIES: - self.A_vcb = 0.0 - self.beta_vcb = 0.0 - - ### 21cmFAST emulation: SFE parameters - if(self.Mturn_fixed == None): #The FIXED/SHARP routine below only applies to Pop II, not to Pop III - self.FLAG_MTURN_FIXED = False # whether to fix Mturn or use Matom(z) at each z - else: - self.FLAG_MTURN_FIXED = True # whether to fix Mturn or use Matom(z) at each z - - - self._minsigmaPSD = 0.1 #minimum sigma for the PSD, to avoid numerical issues in the FFT - self._maxsigmaPSD = 4.0 #maximum sigma for the PSD, there'll never be enough samples if sigma>~6-10 - self._mintauPSD = 1.0 # Myrminimum tau for the PSD, to avoid numerical issues in the FFT - self._maxtauPSD = 300.0 - - self._tagesMyr = np.logspace(-2, 3, 79) #times (ages) we integrate over at each z, Mh, in Myr (TODO: add precisionboost) - - - self._dt_FFT = 0.3 # FFT timescale resolution, Myr, high to resolve the PS_SFR and window functions well (TODO: add UserParams precisionboost here) - self._N_FFT = int(512/(self._dt_FFT/0.3)) # Recommend to use power of 2 for efficient FFT, resolve up to ~0.5Gyr at least - - - self._omegamin = 2*np.pi/1e3 - self._omegamax = np.pi/1.0 - - - -@dataclass(kw_only=True) -class LF_Parameters: - ''' - sigmaUV: float - Stochasticity (gaussian rms) in the halo-galaxy connection P(MUV | Mh). Default is 0.5. - _kappaUV: float - SFR/LUV. Set by zeus21 to the value from Madau+Dickinson14. - Fully degenerate with epsilon. - _kappaUV_III: float - SFR/LUV for PopIII. Set by zeus21 to the value from Madau+Dickinson14. - Assume X more efficient than PopII. - ''' - - zcenter: float = 6. - zwidth: float = 0.5 - - MUVcenters: np.ndarray | float = _field(default_factory=lambda: np.linspace(-23,-14,100)) - MUVwidths: np.ndarray | float = 0.5 - - FLAG_RENORMALIZE_LUV = False #whether to renormalize the lognormal LUV with sigmaUV to recover or otherwise . Recommend False. - - sigmaUV: float = 0.5 - - log10LHacenters: np.ndarray | float = _field(default_factory=lambda: np.linspace(38,45,10)) - log10LHawidths: np.ndarray | float = 0.5 - - FLAG_COMPUTE_UVLF: bool = True - FLAG_COMPUTE_HaLF: bool = False - - ### Dust parameters for UVLFs - DUST_FLAG: bool = True - DUST_model: str = 'Bouwens13' - HIGH_Z_DUST: bool = True - _zmaxdata: float = 8.0 - C0dust: float = 4.43 - C1dust: float = 1.99 #4.43, 1.99 is Meurer99; 4.54, 2.07 is Overzier01 - _kappaUV: float = _field(init=False) #SFR/LUV, value from Madau+Dickinson14, fully degenerate with epsilon - _kappaUV_III: float = _field(init=False) #SFR/LUV for PopIII. Assume X more efficient than PopII - - sigma_times_AUV_dust: float = 0. - - def __post_init__(self): - schema = { - "DUST_FLAG": (bool, None), - "FLAG_RENORMALIZE_LUV": (bool, None), - "FLAG_COMPUTE_UVLF": (bool, None), - "FLAG_COMPUTE_HaLF": (bool, None), - "HIGH_Z_DUST": (bool, None), - "DUST_model": (str, {"Bouwens13", "Zhao24"}), - } - validate_fields(self, schema) - - - # --- normalize MUV --- - if np.isscalar(self.zcenter): - self.MUVcenters = np.array(self.MUVcenters, dtype=float) - else: - self.MUVcenters = np.atleast_1d(self.MUVcenters).astype(float) - - # --- normalize MUVwidth --- - if np.isscalar(self.MUVwidths): - # broadcast scalar to same length as zcenter - self.MUVwidths = np.full_like(self.MUVcenters, self.MUVwidths, dtype=float) - else: - self.MUVwidths = np.atleast_1d(self.MUVwidths).astype(float) - - # --- consistency check --- - if self.MUVwidths.shape != self.MUVcenters.shape: - raise ValueError( - f"MUVwidth shape {self.MUVwidths.shape} does not match MUVcenter shape {self.MUVcenters.shape}" - ) - - # --- normalize logLHa --- - if np.isscalar(self.log10LHacenters): - self.log10LHacenters = np.array([self.log10LHacenters], dtype=float) - else: - self.log10LHacenters = np.atleast_1d(self.log10LHacenters).astype(float) - - # --- normalize logHazwidth --- - if np.isscalar(self.log10LHawidths): - # broadcast scalar to same length as zcenter - self.log10LHawidths = np.full_like(self.log10LHacenters, self.log10LHawidths, dtype=float) - else: - self.log10LHawidths = np.atleast_1d(self.log10LHawidths).astype(float) - - # --- consistency check --- - if self.log10LHawidths.shape != self.log10LHacenters.shape: - raise ValueError( - f"log10Hawidth shape {self.log10LHawidths.shape} does not match log10Hacenter shape {self.log10LHacenters.shape}" - ) - - ### Dust parameters for UVLFs - self._kappaUV = 1.15e-28 #SFR/LUV, value from Madau+Dickinson14, fully degenerate with epsilon - self._kappaUV_III = self._kappaUV #SFR/LUV for PopIII. Assume X more efficient than PopII - - -def validate_fields(obj, schema: dict): - for field, (expected_type, allowed_values) in schema.items(): - value = getattr(obj, field) - - if not isinstance(value, expected_type): - raise TypeError( - f"{field} must be of type {expected_type.__name__}, got {type(value).__name__}" - ) - - if allowed_values is not None and value not in allowed_values: - raise ValueError( - f"{field} must be one of {allowed_values}, got '{value}'" - ) From ba86876e1832b4405852ca1c51eee774451779db Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2026 22:49:36 -0400 Subject: [PATCH 15/15] Update docstring for get_xi_Sum_2ExpEta --- zeus21/correlations.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zeus21/correlations.py b/zeus21/correlations.py index b2ac7f0..2d4e2d2 100644 --- a/zeus21/correlations.py +++ b/zeus21/correlations.py @@ -776,6 +776,9 @@ def get_xi_Sum_2ExpEta(self, xiEtaPara, xiEtaPerp, etaCoeff1, etaCoeff2): if rho(z1, x1) / rhobar = Ae^-b tilde(eta) + Ce^-d tilde(eta) and rho(z2, x2) / rhobar = Fe^-g tilde(eta) + He^-k tilde(eta) Then this computes - Refer to eq. A12 in 2407.18294 for more details + + Computes velocity correlation with xiEta anisotropy, i.e., with xiEtePara and xiEtaPerp separated + At call site, if isotropic version is desired, isotropic xiEta is passed in for xiEtaPara and xiEtaPerp is set to None """ aa, bb, cc, dd = etaCoeff1