def hmp(self, z=0): """Generating halo mass profiles Parameters ---------- z : float Redshift of the snapshot """ rhocrit = Planck15.critical_density(z).to(units.Msun / units.kpc**3) \ / (Planck15.H(z) / 100) for i in range(len(self.params)): _input = self.params[i] halos = _input['rockstar'].binnedhalos[_input['bin']] _input['hmp'] = MyHMP( halos, _input['gadget'].data, float(_input['rockstar'].header['Particle_mass'][0])) rmin = _input['rockstar'].header['Softening_length'] / 2.0 rmax = 2 * np.max(halos['rvir']) _input['rockstar'].binnedhalos['mean_rvir'] = sum(halos['rvir']) \ / len(halos['rvir']) rbins = np.logspace(np.log10(rmin), np.log10(rmax), num=50, base=10) _input['hmp'].hmp(rbins, rhocrit.value)
def hmp(self, z=0): """Generating halo mass profiles Parameters ---------- z : float Redshift of the snapshot """ rhocrit = Planck15.critical_density(z).to(units.Msun / units.kpc**3) \ / (Planck15.H(z) / 100) for i in range(len(self.params)): _input = self.params[i] halos = _input['rockstar'].binnedhalos[_input['bin']] _input['hmp'] = MyHMP( halos, _input['gadget'].data, float(_input['rockstar'].header['Particle_mass'][0])) rmin = _input['rockstar'].header['Softening_length'] / 2.0 rmax = 2 * np.max(halos['rvir']) _input['rockstar'].binnedhalos['mean_rvir'] = sum(halos['rvir']) \ / len(halos['rvir']) rbins = np.logspace(np.log10(rmin), np.log10(rmax), num=50, base=10) _input['hmp'].hmp(rbins, rhocrit.value)
def rvir(profile, overdensity=180, z=0.5): rho_crit = Planck15.critical_density(z).value mtot = profile['total_mass'].value #g/cm^3 r = profile['radius'].value #cm V = 4 * np.pi * (r**3) / 3. cum_rho = mtot / V rvir = r[np.argmin(abs(cum_rho - (overdensity * rho_crit)))] return rvir
def frac_in_halos(zvals, Mlow, Mhigh, rmax=1.): """ Calculate the fraction of dark matter in collapsed halos over a mass range and at a given redshift Note that the fraction of DM associated with these halos will be scaled down by an additional factor of f_diffuse Requires Aemulus HMF to be installed Args: zvals: ndarray Mlow: float In h^-1 units already so this will be applied for the halo mass function Mhigh: float In h^-1 units already rmax: float Extent of the halo in units of rvir Returns: ratios: ndarray rho_halo / rho_m """ hmfe = init_hmf() M = np.logspace(np.log10(Mlow * cosmo.h), np.log10(Mhigh * cosmo.h), num=1000) lM = np.log(M) ratios = [] for z in zvals: a = 1. / (1.0 + z) # scale factor # Setup #dndlM = np.array([hmfe.dndlnM(Mi, a)[0] for Mi in M]) dndlM = hmfe.dndlnM(M, z) M_spl = IUS(lM, M * dndlM) # Integrate rho_tot = M_spl.integral(np.log(Mlow * cosmo.h), np.log( Mhigh * cosmo.h)) * units.M_sun / units.Mpc**3 # Cosmology rho_M = cosmo.critical_density(z) * cosmo.Om(z) / ( 1 + z)**3 # Tinker calculations are all mass ratio = (rho_tot * cosmo.h**2 / rho_M).decompose() # ratios.append(ratio) ratios = np.array(ratios) # Boost halos if extend beyond rvir (homologous in mass, but constant concentration is an approx) if rmax != 1.: #from pyigm.cgm.models import ModifiedNFW c = 7.7 nfw = ModifiedNFW(c=c) M_ratio = nfw.fy_dm(rmax * nfw.c) / nfw.fy_dm(nfw.c) ratios *= M_ratio # Return return np.array(ratios)
class CosmoParams: hubble_constant: float = Planck15.H0.value omega_bary_hsqr: float = Planck15.Ob0 * Planck15.h**2 omega_cdm_hsqr: float = Planck15.Odm0 * Planck15.h**2 spectral_index: float = 0.9667 scalar_amp: float = 2.2e-9 sigma_8: float = 0.830 tau_reion: float = 0.06 omega_bary: float = Planck15.Ob0 omega_cdm: float = Planck15.Odm0 omega_matter: float = Planck15.Om0 omega_lambda: float = Planck15.Ode0 rho_crit: float = Planck15.critical_density(0).to(u.Msun / u.Mpc**3).value h: float = Planck15.h cmb_temp: float = Planck15.Tcmb0.value w0: float = -1.0 wa: float = 0.0 neutrino_mass_sum: float = 0.06 use_ppf: bool = True flat: bool = True def _check_flatness(self): if not np.allclose(self.omega_matter + self.omega_lambda, 1, rtol=1e-2): raise NonFlatUniverseError( 'omega_matter and omega_lambda must sum to 1') def _check_matter_consistency(self): if not np.allclose(self.omega_matter, self.omega_cdm + self.omega_bary): raise MatterInconsistencyError( 'omega_cdm and omega_bary must sum to omega_matter') def _check_hsqr_params(self): if not np.allclose(self.omega_bary, self.omega_bary_hsqr / self.h**2): raise MatterInconsistencyError( 'omega_bary_hsqr must equal omega_bary * h**2') if not np.allclose(self.omega_cdm, self.omega_cdm_hsqr / self.h**2): raise MatterInconsistencyError( 'omega_cdm_hsqr must equal omega_cdm * h**2') def _check_hubble_consistency(self): if not np.allclose(self.hubble_constant / 100, self.h): raise HubbleConstantError('hubble_constant must equal h*100') def __post_init__(self): if self.flat: self._check_flatness() self._check_matter_consistency() self._check_hsqr_params() self._check_hubble_consistency()
def plot(file, ax, label=None, linestyle='solid', rnorm=None, znorm=None): f = h5py.File(file)['fields'] t = f['temperature'].value * kB r = f['radius'].value * du d = f['density'].value k = entropy(f) if rnorm: r500 = rvir(f, overdensity=500) r /= r500 if znorm: rho_crit = Planck15.critical_density(znorm).value d /= rho_crit ax1, ax2, ax3 = ax.flatten() ax1.plot(r, t, color=m.to_rgba(i), label=label, linestyle=linestyle) ax2.loglog(r, d, color=m.to_rgba(i), linestyle=linestyle) ax3.loglog(r, k, color=m.to_rgba(i), linestyle=linestyle)
def setup_param(self, cosmo=None): """ Setup key parameters of the model """ # Cosmology if cosmo is None: self.rhoc = 9.2e-30 * units.g / units.cm**3 self.fb = 0.16 # Baryon fraction self.H0 = 70. * units.km / units.s / units.Mpc else: self.rhoc = cosmo.critical_density(self.z) self.fb = cosmo.Ob0 / cosmo.Om0 self.H0 = cosmo.H0 # Dark Matter self.r200 = (((3 * self.M_halo) / (4 * np.pi * 200 * self.rhoc))**(1 / 3)).to('kpc') self.rho0 = 200 * self.rhoc / 3 * self.c**3 / self.fy_dm( self.c) # Central density # Baryons self.M_b = self.M_halo * self.fb self.rho0_b = (self.M_b / (4 * np.pi) * (self.c / self.r200)**3 / self.fy_b(self.c)).cgs # Misc self.mu = 1.33 # Reduced mass correction for Helium
def build_grid(z_FRB=1., ntrial=10, seed=12345, Mlow=1e10, r_max=2., outfile=None, dz_box=0.1, dz_grid=0.01, f_hot=0.75, verbose=True): """ Generate a universe of dark matter halos with DM measurements Mainly an internal function for generating useful output grids. Requires the Aemulus Halo Mass function Args: z_FRB: float, optional ntrial: int, optional seed: int, optional Mlow: float, optional h^-1 mass r_max: float, optional Extent of the halo in units of rvir outfile: str, optional Write dz_box: float, optional Size of the slice of the universe for each sub-calculation dz_grid: float, optional redshift spacing in the DM grid f_hot: float Fraction of the cosmic fraction of matter in diffuse gas (for DM) Returns: DM_grid: ndarray (ntrial, nz) halo_tbl: Table Table of all the halos intersected """ Mhigh = 1e16 # Msun # mNFW y0 = 2. alpha = 2. warnings.warn("Ought to do concentration properly someday!") cgm = ModifiedNFW(alpha=alpha, y0=y0, f_hot=f_hot) icm = ICM() # Random numbers rstate = np.random.RandomState(seed) # Init HMF hmfe = init_hmf() # Boxes nbox = int(z_FRB / dz_box) nz = int(z_FRB / dz_grid) dX = int(np.sqrt(ntrial))+1 # npad = 6 # Mpc base_l = 2*dX + npad print('L_base = {} cMpc'.format(base_l)) warnings.warn("Worry about being big enough given cMpc vs pMpc") DM_grid = np.zeros((ntrial,nz)) # Spline distance to z D_max = cosmo.comoving_distance(z_FRB) D_val = np.linspace(1e-3,D_max.value,200) # IS THIS FINE ENOUGH? z_val = np.array([z_at_value(cosmo.comoving_distance, iz) for iz in D_val*units.Mpc]) D_to_z = IUS(D_val, z_val) # Save halo info #halos = [[] for i in range(ntrial)] halo_i, M_i, R_i, DM_i, z_i = [], [], [], [], [] # Loop me prev_zbox = 0. #for ss in range(nbox): #for ss in [0]: for ss in [5]: zbox = ss*dz_box + dz_box/2. print('zbox = {}'.format(zbox)) a = 1./(1.0 + zbox) # Scale factor # Mass function M = np.logspace(np.log10(Mlow*cosmo.h), np.log10(Mhigh*cosmo.h), num=1000) lM = np.log(M) dndlM = np.array([hmf.dndlM(Mi, a) for Mi in M]) n_spl = IUS(lM, dndlM) cum_n = np.array([n_spl.integral(np.log(Mlow*cosmo.h), ilM) for ilM in lM]) ncum_n = cum_n/cum_n[-1] # As z increases, we have numerical issues at the high mass end (they are too rare) try: mhalo_spl = IUS(ncum_n, lM) except ValueError: # Kludge me print("REDUCING Mhigh by 2x") Mhigh /= 2. M = np.logspace(np.log10(Mlow*cosmo.h), np.log10(Mhigh*cosmo.h), num=1000) lM = np.log(M) dndlM = np.array([hmf.dndlM(Mi, a) for Mi in M]) n_spl = IUS(lM, dndlM) cum_n = np.array([n_spl.integral(np.log(Mlow*cosmo.h), ilM) for ilM in lM]) ncum_n = cum_n/cum_n[-1] # mhalo_spl = IUS(ncum_n, lM) # Volume -- Box with base l = 2Mpc D_zn = cosmo.comoving_distance(zbox + dz_box/2.) # Full box D_zp = cosmo.comoving_distance(ss*dz_box) # Previous D_z = D_zn - D_zp V = D_z * (base_l*units.Mpc)**2 # Average N_halo avg_n = hmf.n_bin(Mlow*cosmo.h, Mhigh*cosmo.h, a) * cosmo.h**3 * units.Mpc**-3 avg_N = (V * avg_n).value # Assume Gaussian stats for number of halos N_halo = int(np.round(avg_N + np.sqrt(avg_N)*rstate.randn(1))) # Random masses randM = rstate.random_sample(N_halo) rM = np.exp(mhalo_spl(randM)) / cosmo.h # r200 r200 = (((3*rM*units.M_sun.cgs) / (4*np.pi*200*cosmo.critical_density(zbox)))**(1/3)).to('kpc') # Random locations (X,Y,Z) X_c = rstate.random_sample(N_halo)*base_l # Mpc Y_c = rstate.random_sample(N_halo)*base_l # Mpc Z_c = (rstate.random_sample(N_halo)*D_z.to('Mpc') + D_zp).value # Check mass fraction if verbose: Mtot = np.log10(np.sum(rM)) M_m = (cosmo.critical_density(zbox)*cosmo.Om(zbox) * V/(1+zbox)**3).to('M_sun') #print("N_halo: {} avg_N: {}".format(N_halo, avg_N)) print("z: {} Mhalo/M_m = {}".format(zbox, 10**Mtot/M_m.value)) print(frac_in_halos([zbox], Mlow, Mhigh)) # Redshifts z_ran = D_to_z(Z_c) # Loop on trials all_DMs = [] all_nhalo = [] all_r200 = [] for itrial in range(ntrial): # X,Y trial X_trial = npad//2 + (2*itrial%dX) # Step by 2Mpc Y_trial = npad//2 + 2*itrial // dX # Impact parameters try: R_com = np.sqrt((X_c-X_trial)**2 + (Y_c-Y_trial)**2) # Mpc except: pdb.set_trace() R_phys = R_com * 1000. / (1+z_ran) * units.kpc # Cut intersect = R_phys < r_max*r200 print("We hit {} halos".format(np.sum(intersect))) all_nhalo.append(np.sum(intersect)) if not np.any(intersect): all_DMs.append(0.) continue # Loop -- FIND A WAY TO SPEED THIS UP! DMs = [] for iobj in np.where(intersect)[0]: # Init if rM[iobj] > 1e14: # Use ICM model model = icm else: model = cgm model.log_Mhalo=np.log10(rM[iobj]) model.M_halo = 10.**model.log_Mhalo * constants.M_sun.cgs model.z = zbox # To be consistent with above; should be close enough model.setup_param(cosmo=cosmo) # DM DM = model.Ne_Rperp(R_phys[iobj], rmax=r_max, add_units=False)/(1+model.z) DMs.append(DM) # Save halo info halo_i.append(itrial) M_i.append(model.M_halo.value) R_i.append(R_phys[iobj].value) DM_i.append(DM) z_i.append(z_ran[iobj]) all_r200.append(cgm.r200.value) # Save em iz = (z_ran[intersect]/dz_grid).astype(int) DM_grid[itrial,iz] += DMs all_DMs.append(np.sum(DMs)) #print(DMs, np.log10(rM[intersect]), R_phys[intersect]) if (itrial % 100) == 0: pdb.set_trace() # Table the halos halo_tbl = Table() halo_tbl['trial'] = halo_i halo_tbl['M'] = M_i halo_tbl['R'] = R_i halo_tbl['DM'] = DM_i halo_tbl['z'] = z_i # Write if outfile is not None: print("Writing to {}".format(outfile)) np.save(outfile, DM_grid, allow_pickle=False) halo_tbl.write(outfile+'.fits', overwrite=True) return DM_grid, halo_tbl
def halo_incidence(Mlow, zFRB, radius=None, hmfe=None, Mhigh=1e16, nsample=20, cumul=False): """ Calculate the (approximate) average number of intersections to halos of a given minimum mass to a given zFRB. Requires Aemulus HMF to be installed Args: Mlow: float Mass of minimum halo in Solar masses The code deals with h^-1 factors so that you do not The minimum value is 2e10 zFRB: float Redshift of the FRB radius: Quantity, optional The calculation will specify this radius as rvir derived from Mlow unless this is specified. And this rvir *will* vary with redshift hmfe (hmf.hmf_emulator, optional): Halo mass function emulator from Aeumulus Mhigh: float, optional Mass of maximum halo in Solar masses nsammple: int, optional Number of samplings in redshift 20 should be enough cumul: bool, optional Return the cumulative quantities instead Returns: If cumul is False Navg: float Number of average intersections elif cumul is True zeval: ndarray Ncumul: ndarray """ # Mlow limit if Mlow < 2e10: warnings.warn("Calculations are limited to Mlow > 2e10") return # HMF if hmfe is None: hmfe = init_hmf() # zs = np.linspace(0., zFRB, nsample) # Mean density ns = [] for iz in zs: ns.append(hmfe.n_in_bins((Mlow * cosmo.h, Mhigh * cosmo.h), iz) * cosmo.h**3) # * units.Mpc**-3 # Interpolate ns = units.Quantity(ns*units.Mpc**-3) # Radii if radius is None: rhoc = cosmo.critical_density(zs) #https://arxiv.org/pdf/1312.4629.pdf eq5 q = cosmo.Ode0/(cosmo.Ode0+cosmo.Om0*(1+zs)**3) rhovir = (18*np.pi**2-82*q-39*q**2)*rhoc r200 = (((3*Mlow*constants.M_sun.cgs) / (4*np.pi*rhovir))**(1/3)).to('kpc') else: r200 = np.ones_like(zs) * radius # Ap Ap = np.pi * r200**2 # l(X) loX = ((constants.c/cosmo.H0) * ns * Ap).decompose().value # dX X = cosmo.absorption_distance(zs) dX = X - np.roll(X,1) dX[0] = 0. # Finish if cumul: Navg = np.cumsum(loX * dX) return zs, Navg else: import pdb; pdb.set_trace() Navg = np.sum(loX * dX) return Navg