def xir(self, rvals, hankelN=6000, hankelh=0.0005, ft_hankel=None): """ Function that performs a Hankel transform to obtain the 2-point correlation function corresponding to the power spectrum. """ if ft_hankel is None: ft_hankel = hankel.SymmetricFourierTransform(ndim=3, N=hankelN, h=hankelh) xivals = ft_hankel.transform(f=self.pkinterp, k=rvals, ret_err=False, ret_cumsum=False, inverse=True) return xivals
def create_profile_grid_fourier(log_kvals_rvir_dict, log_conc_dict, gamma_dict, output_file="default.npz", hankelN=12000, hankelh=1e-6, verbose=True): """ Function to create and save to memory a grid of Fourier-space density profiles, so that we can later use them to speed-up calculations using interpolations. The resulting grid will depend on three coordinates: - log10(k * rvir) - log10(concentration) - gamma The input dictionaries define the min/max/number of steps for each of the coordinates. The profile will be computed for mass=1, and with the appropriate value of rho_s to be properly normalized (i.e., u(k) --> 1 as k --> 0). We also save an array containing the computed rho_s (for mass=1, rvir=1) as function of log10(concentration) and gamma for later use. As this should be called only once before the runs, I use the slower (but more precise) function 'alt_rhos_modNFW' to compute rho_s. """ k_rvir = np.logspace(log_kvals_rvir_dict['min'], log_kvals_rvir_dict['max'], log_kvals_rvir_dict['N']) log_k_rvir = np.log10(k_rvir) concentration = np.logspace(log_conc_dict['min'], log_conc_dict['max'], log_conc_dict['N']) log_conc = np.log10(concentration) gamma_exp = np.linspace(gamma_dict['min'], gamma_dict['max'], gamma_dict['N']) ft_hankel = hankel.SymmetricFourierTransform(ndim=3, N=hankelN, h=hankelh) profile_grid = np.empty( (log_kvals_rvir_dict['N'], log_conc_dict['N'], gamma_dict['N'])) rho_s_grid = np.empty((log_conc_dict['N'], gamma_dict['N'])) for i, conc in enumerate(concentration): if verbose: print(f"Concentration = {conc} ({i} of {log_conc_dict['N']})") for j, g_exp in enumerate(gamma_exp): rho_s = alt_rhos_modNFW(mass=1, rvir=1, conc=conc, gamma=g_exp) norm_prof_func = \ lambda x: profile_ModNFW_config_scalar(rvals=x, rho_s=1, conc=conc, rvir=1, gamma_exp=g_exp) profile_grid[:, i, j] = \ rho_s*ft_hankel.transform(f=norm_prof_func, k=k_rvir, ret_err=False, ret_cumsum=False) rho_s_grid[i, j] = rho_s np.savez(output_file, log10_k_rvir=log_k_rvir, log10_concentration=log_conc, gamma=gamma_exp, profile_grid=profile_grid, rho_s_unit=rho_s_grid) log_file = output_file + ".log" with open(log_file, 'a') as flog: flog.write( "Arrays in " + output_file + " created by function " "densprofile.create_profile_grid_fourier(), with options:\n") flog.write("Limits for log(k * rvir) array: " + str(log_kvals_rvir_dict) + "\n") flog.write("Limits for log(concentration) array: " + str(log_conc_dict) + "\n") flog.write("Limits for gamma array: " + str(gamma_dict) + "\n") flog.write("Hankel parameters: N = %d, h = %f\n" % (hankelN, hankelh)) return 0
def profile_ModNFW_fourier_hankel(kvals, mass, rho_s, rvir, conc, gamma=1.0, hankelN=6000, hankelh=1e-5, ft_hankel=None): """ Try to do the same as the function profile_ModNFW_fourier_parameters, but using the Hankel transforms in the 'hankel' library. We use the fact that the transform we need to do is the same as to convert from xi(r) to P(k), assuming that the rho(r) function provides already a truncated profile. For now, using N=6000, h=1e-5, and our cut at k=1/(10 rvir), I get a difference typically below 1.5% comparing to the u(k) calculated analytically for gamma=1. Could in principle use the multidimensional version of hankel to speed up the transformation (avoiding explicit loop over Nm). However, for useful values of Nm this rockets up the memory usage, so this approach is not viable in practice. """ kvals = np.atleast_1d(kvals) assert kvals.ndim == 1 Nk = len(kvals) mass = np.atleast_1d(mass) rho_s = np.atleast_1d(rho_s) rvir = np.atleast_1d(rvir) conc = np.atleast_1d(conc) assert mass.ndim == 1 assert rho_s.ndim == 1 assert rvir.ndim == 1 assert conc.ndim == 1 Nm = len(mass) assert Nm == len(rho_s) == len(rvir) == len(conc) if ft_hankel is None: ft_hankel = hankel.SymmetricFourierTransform(ndim=3, N=hankelN, h=hankelh) uprof_out = np.ones((Nk, Nm), float) for i in range(Nm): idx_calc = (kvals > 1. / (10 * rvir[i])) norm_prof_func = lambda x: \ profile_ModNFW_config_scalar(rvals=x, rho_s=rho_s[i], conc=conc[i], rvir=rvir[i], gamma_exp=gamma)/mass[i] uprof_out[idx_calc, i] = ft_hankel.transform(f=norm_prof_func, k=kvals[idx_calc], ret_err=False, ret_cumsum=False) # I add a condition, so that I make u(k)=1 for # all k < 1/(10 rvir). # This is a bit arbitrary, what works to avoid the oscillations # that appear at small k # rvir_grid, kvals_grid = np.meshgrid(rvir, kvals) # uprof_out[kvals_grid < 1./(10*rvir_grid)] = 1.0 return uprof_out
def hod_from_parameters(redshift=0, OmegaM0=0.27, OmegaL0=0.73, OmegaB0=0.046, H0=70.0, use_camb=True, init_power_amplitude=2.2e-9, init_power_spect_index=0.96, camb_halofit_version=None, camb_kmax=200.0, camb_k_per_logint=30, powesp_matter_file=None, powesp_linz0_file=None, hod_type=1, hod_mass_min=1e11, hod_mass_1=1e12, hod_alpha=1.0, hod_siglogM=0.5, hod_mass_0=1e11, f_gal=1.0, gamma=1.0, logM_min=8.0, logM_max=16.0, logM_step=0.005, scale_dep_bias=True, use_mvir_limit=True, halo_exclusion_model=2, use_tinker_bias_params=True, hankelN=6000, hankelh=0.0005, rmin=0.01, rmax=100.0, nr=100, rlog=True, fprof_grid_log_krvir=None, fprof_grid_log_conc=None, fprof_grid_gamma=None, fprof_grid_profile=None, fprof_grid_rhos=None, fprof_hankelN=12000, fprof_hankelh=1e-6, fprof_Nk_interp=100, fprof_Nm_interp=100): """ Construct an HODClustering object defining all the needed parameters. """ assert redshift >= 0 if redshift > 10: raise UserWarning("You entered a quite high value of redshift (>10). \ I am not sure these models are valid there, proceed at your own risk.") # First, build the Cosmology object # As we work always using distances in Mpc/h, we should be # fine setting H0=100 assert OmegaM0 >= 0 if (OmegaM0 + OmegaL0) != 1: raise UserWarning("You are using a non-flat cosmology. \ Are you sure that is what you really want?") cosmo_object = ac.LambdaCDM(H0=100, Om0=OmegaM0, Ode0=OmegaL0) # Calculate or read in the needed PowerSpectrum objects if use_camb: pk_matter_object = get_camb_pk(redshift=redshift, OmegaM0=OmegaM0, OmegaL0=OmegaL0, OmegaB0=OmegaB0, H0=H0, Pinit_As=init_power_amplitude, Pinit_n=init_power_spect_index, nonlinear=True, halofit_model=camb_halofit_version, kmax=camb_kmax, k_per_logint=camb_k_per_logint) pk_linz0_object = get_camb_pk(redshift=0, OmegaM0=OmegaM0, OmegaL0=OmegaL0, OmegaB0=OmegaB0, H0=H0, Pinit_As=init_power_amplitude, Pinit_n=init_power_spect_index, nonlinear=False, kmax=camb_kmax, k_per_logint=camb_k_per_logint) else: pk_matter_object = PowerSpectrum.fromfile(powesp_matter_file) pk_linz0_object = PowerSpectrum.fromfile(powesp_linz0_file) # Build the HOD object hod_object = hodmodel.HODModel(hod_type=hod_type, mass_min=hod_mass_min, mass_1=hod_mass_1, alpha=hod_alpha, siglogM=hod_siglogM, mass_0=hod_mass_0) # Build the halo model object. # We have two options for the parameters defining the bias: # - Use the original bias parameters from Sheth (2001), MoWhite2002 # - Use the modified parameters following Tinker (2005) if use_tinker_bias_params: bpar = 0.35 cpar = 0.8 else: bpar = 0.5 cpar = 0.6 halo_object = halomodel.HaloModelMW02(cosmo=cosmo_object, powesp_lin_0=pk_linz0_object, redshift=redshift, par_b=bpar, par_c=cpar) # Now, create the Hankel FourierTransform object needed for the conversions # P(k) --> xi(r) ft_hankel = hankel.SymmetricFourierTransform(ndim=3, N=hankelN, h=hankelh) # Define the array of r-values for the HODClustering object assert rmax > rmin assert rmin >= 0 assert nr > 0 if rlog: rvals_array = np.logspace(np.log10(rmin), np.log10(rmax), nr) else: rvals_array = np.linspace(rmin, rmax, nr) # Create the Hankel FourierTransform object corresponding to the conversion # of the halo radial profiles from config. to Fourier space fprof_ft_hankel = hankel.SymmetricFourierTransform(ndim=3, N=fprof_hankelN, h=fprof_hankelh) # And finally, define the clustering object model_clustering_object = \ HODClustering(redshift=redshift, cosmo=cosmo_object, powesp_matter=pk_matter_object, hod_instance=hod_object, halo_instance=halo_object, powesp_lin_0=pk_linz0_object, f_gal=f_gal, gamma=gamma, logM_min=logM_min, logM_max=logM_max, logM_step=logM_step, scale_dep_bias=scale_dep_bias, use_mvir_limit=use_mvir_limit, halo_exclusion_model=halo_exclusion_model, ft_hankel=ft_hankel, rvalues=rvals_array, fprof_grid_log_krvir=fprof_grid_log_krvir, fprof_grid_log_conc=fprof_grid_log_conc, fprof_grid_gamma=fprof_grid_gamma, fprof_grid_profile=fprof_grid_profile, fprof_grid_rhos=fprof_grid_rhos, fprof_ft_hankel=fprof_ft_hankel, fprof_Nk_interp=fprof_Nk_interp, fprof_Nm_interp=fprof_Nm_interp) print("New HODClustering object created, " f"galaxy density = {model_clustering_object.gal_dens:.4} (h/Mpc)^3") return model_clustering_object