def sSFR_peng(self, logMs, z, *ssfr_params): #PENG sSFR z_temp = np.minimum(z, 2) #z_temp = z t = cosmo.lookback_time(9999).value - cosmo.lookback_time( z_temp).value ## Ms = np.power(10, logMs) beta = -0 ssfr = 2.5e-9 * (Ms / (10e10))**(beta) * (t / 3.5)**(-2.2) return ssfr ## per year
def sSFR_speagle(logMs, z): z_temp = np.minimum(z,6) logMs_temp = np.maximum(logMs, 8.5) t = cosmo.lookback_time(9999).value - cosmo.lookback_time(z_temp).value ## WARNING:: TRY cosmo.age(z).value instead!! x_1 = 0.840 x_2 = 0.026 x_3 = 6.510 x_4 = 0.110 logSFR = (x_1 - x_2*t) * logMs_temp - (x_3 - x_4*t) ssfr = np.power(10, logSFR) / np.power(10,logMs_temp) return ssfr ## peryear
def gen_field_analytic(self, p): self.logMStar_setup = np.arange(self.logM_min, 12, 0.01) phi_init_sf = self.schechter_SMF_func(self.logMStar_setup) phi_init_q = np.zeros(len(phi_init_sf)) t_init = cosmo.lookback_time(self.z_init).value #gyr t_final = cosmo.lookback_time(self.z_final).value #gyr t = t_init z = self.z_init phi_sf = [phi_init_sf] phi_q = [phi_init_q] z_range = [z] integ_an = integration_utils(self.DE_2, hmax=0.1, hmin=1e-5, htol=1e-9) #_an == analytic condition = True # Always run at least once force = False while t >= t_final and condition: inits = np.array([phi_init_sf, phi_init_q], copy=True) output = integ_an.RK45(p, inits, t, force, mpp=False, analytic=True) phi_init_sf, phi_init_q = output[0, :], output[1, :] phi_sf.append(np.copy(phi_init_sf)) phi_q.append(np.copy(phi_init_q)) if (t - integ_an.step) > t_final: pass else: integ_an.step = t - t_final force = True condition = False print(t, integ_an.step) t -= integ_an.step z = z_at_value(cosmo.age, (cosmo.age(0).value - t) * u.Gyr, zmin=-1e-6) z_range.append(z) self.phi_sf_interp_an = interp2d(self.logMStar_setup, z_range, phi_sf) self.phi_q_interp_an = interp2d(self.logMStar_setup, z_range, phi_q)
def setup_evolve(self): t_init = cosmo.lookback_time(self.z_init).value #gyr self.t_final = cosmo.lookback_time(self.z_final).value #gyr self.t = t_init self.z = self.z_init # self.integ = integration_utils(self.DE, hmax=0.1, hmin=1e-2, htol=1e0) self.condition = True # Always run at least once # self.force = False self.hmass = np.ma.power(10, np.array(self.hmass_init, copy=True)) #halo mass self.gmass = np.ma.zeros(self.hmass.shape) #gas mass self.smass = np.ma.zeros(self.hmass.shape) #stellar mass
def plot_redshift_ax(ax, z=[0.05, 0.5, 1, 2, 3], cosmology=None, method='time'): """Add twin axis for redshift to plots versus time Args: ax (object): Matplotlib axes object z (list, optional): List of redshifts. Defaults to [0.05, 0.5, 1, 2, 3]. cosmology (object, optional): Astropy cosmology object. Defaults to None. method (str): Units of x-axis. Defaults to cosmic time [Gyr]. Returns: object: Matplotlib axes object """ if cosmology is None: from astropy.cosmology import Planck15 as cosmology if method == 'lookback': ages = cosmology.lookback_time(z).value elif method == 'time': ages = cosmology.age(z).value elif method == 'log': ages = np.log10(1 + np.asarray(z)) else: raise NotImplementedError ax2 = ax.twiny() l, r = ax.get_xlim() ax2.set_xlim(l, r) ax2.set_xticks(ages) ax2.set_xticklabels(z) return ax2
def setup_evolve(self): t_init = cosmo.lookback_time(self.z_init).value #gyr self.t_final = cosmo.lookback_time(self.z_final).value #gyr self.t = t_init self.z = self.z_init self.integ = integration_utils(self.DE, hmax=1, hmin=1e-5, htol=1e1) self.condition = True # Always run at least once self.force = False self.mass_array = np.ma.power(10, np.array(self.sf_masses, copy=True)) self.ssfr_params = self.gen_ssfr_params(len(self.sf_masses)) self.phi_sf_interp = self.schechter_SMF_func
def update_death_date(self, mass_array, ssfr_params): tt = time() N = mass_array.shape[0] n = self.death_date.shape[0] dn = int(N - n) if dn == 0: death_date = self.death_date else: death_date = np.ma.zeros(N) death_date[0:n] = self.death_date[:] infall_times = cosmo.lookback_time(self.infall_z[n:N]).value delay_times = self.t_delay([ mass_array[n:N], ssfr_params[:, n:N], self.infall_Mh[n:N], self.infall_z[n:N] ]) #delay_times = pool.map( self.t_delay, ([ms,mh,z] for (ms,mh,z) in zip(mass_array[n:N], self.infall_Mh[n:N], self.infall_z[n:N]))) death_date[n:N] = infall_times - delay_times not_masked = np.logical_not(mass_array.mask) if len(mass_array[not_masked]) == 0: pass else: #print(mass_array[not_masked], len(mass_array[not_masked])) infall_times = cosmo.lookback_time(self.infall_z[not_masked]).value delay_times = self.t_delay([ mass_array[not_masked], ssfr_params[:, not_masked], self.infall_Mh[not_masked], self.infall_z[not_masked] ]) #delay_times = pool.map( self.t_delay, ([ms,mh,z] for (ms,mh,z) in zip(mass_array[not_masked], self.infall_Mh[not_masked], self.infall_z[not_masked]))) death_date[not_masked] = infall_times - delay_times self.death_date = death_date print('update death date: {}'.format(time() - tt))
def local_rate(model, zmin, zmax, cosmic=False, cbc_type=None, zmerge_min=0, zmerge_max=0.1, N_zbins=100): """ Calculates the local merger rate, i.e. mergers that occur between zmerge_min and zmerge_max """ if zmin == 0: # account for log-spaced bins zmin = 1e-3 zbins = np.logspace(np.log10(zmin), np.log10(zmax), N_zbins + 1) zbin_contribution = [] # work down from highest zbin for zbin_low, zbin_high in tqdm(zip(zbins[::-1][1:], zbins[::-1][:-1]), total=len(zbins) - 1): # get local mergers per unit mass floc = fmerge_at_z(model, zbin_low, zbin_high, cosmic, cbc_type, zmerge_min=zmerge_min, zmerge_max=zmerge_max) # get redshift at middle of the log-spaced zbin midz = 10**(np.log10(zbin_low) + (np.log10(zbin_high) - np.log10(zbin_low)) / 2.0) # get SFR at this redshift sfr = sfr_z(midz) * u.M_sun * u.Mpc**(-3) * u.yr**(-1) # cosmological factor E_z = (cosmo._Onu0 * (1 + midz)**4 + cosmo._Om0 * (1 + midz)**3 + cosmo._Ok0 * (1 + midz)**2 + cosmo._Ode0)**(1. / 2) # add the contribution from this zbin to the sum zbin_contribution.append( ((sfr * floc / ((1 + midz) * E_z)) * (zbin_high - zbin_low)).value) # reintroduce units to the list zbin_contribution = np.asarray(zbin_contribution) * u.Mpc**-3 * u.yr**-1 # get the total contribution for all zbins and local rate zbin_summation = np.sum(zbin_contribution).to(u.Gpc**-3 * u.yr**-1) R_local = ((1.0 / (cosmo._H0 * cosmo.lookback_time(zmerge_max))) * zbin_summation).to(u.Gpc**-3 * u.yr**-1) # get the midpoints of the bins to return midz = 10**(np.log10(zbins[:-1]) + (np.log10(zbins[1:]) - np.log10(zbins[:-1])) / 2.0) return R_local, zbin_contribution, midz
def sSFR_speagle(self, logMs, z, ssfr_params): z_temp = np.minimum(z, 6) logMs_temp = np.maximum(logMs, 8.5) t = cosmo.lookback_time(9999).value - cosmo.lookback_time( z_temp).value ## WARNING:: TRY cosmo.age(z).value instead!! try: x_1 = ssfr_params[0, :] x_2 = ssfr_params[1, :] x_3 = ssfr_params[2, :] x_4 = ssfr_params[3, :] except: x_1 = ssfr_params[0] x_2 = ssfr_params[1] x_3 = ssfr_params[2] x_4 = ssfr_params[3] logSFR = (x_1 - x_2 * t) * logMs_temp - (x_3 - x_4 * t) ssfr = np.power(10, logSFR) / np.power(10, logMs_temp) return ssfr ## peryear
def func_delta_N_sf_final(self, item): z, delays, j = item logMs = np.log10(self.M_star(np.power(10, self.logMHalo_eff), z)) phi_sf = self.phi_sf_z_10_interp(logMs, z) phi_q = self.phi_q_z_10_interp(logMs, z) t_end = cosmo.lookback_time(self.z_0).value for i in range(0, len(logMs)): m = logMs[i] td = delays[i] dt = 0.5 #Gyr t = cosmo.lookback_time(z).value t_init = np.copy(t) end_condition = True while t > np.maximum(t_end, t_init - td) and end_condition: #print(t) dn_b = self.dN_blue(m, z, phi_sf[i]) * phi_sf[i] * dt dn_r = self.dN_red(m, z, phi_sf[i], phi_q[i]) * phi_sf[i] * dt #print(dn_b,phi_sf[i]) phi_sf[i] += dn_b phi_q[i] += dn_r if t - dt <= np.maximum(t_end, t_init - td): dt = t - np.maximum(t_end, t_init - td) end_condition = False t -= dt if t_end <= t_init - td: phi_q[i] += phi_sf[i] phi_sf[i] = 0 return [phi_sf, phi_q]
def update_ssfr_params(self): try: delta_t = self.t_old - self.t except: self.t_old = cosmo.lookback_time(self.z_init).value delta_t = self.t_old - self.t print(delta_t) if delta_t >= self.ssfr_update_time_interval: #Gyr n = len(self.sf_masses) self.ssfr_params = self.gen_ssfr_params(n) m = len(self.cluster_masses) self.cluster_ssfr_p = self.gen_ssfr_params(m) self.t_old = self.t print('*new ssfr params*') else: pass
def grow_cluster(self): new_progenitor_mass = np.power(10, self.M_main(self.z)) mass_increase = new_progenitor_mass - self.progenitor_mass A_norm = mass_increase / self.A_denom(self.z) * self.n_cluster self.progenitor_mass = new_progenitor_mass temp_mass_array = np.ma.copy( self.mass_array) # identify integration range temp_mass_array.mask = np.ma.nomask Ms_min = np.ma.min(temp_mass_array) Ms_max = np.ma.max(temp_mass_array) temp_mass_array = None phi_sf_at_z = lambda x: self.phi_sf_interp(np.log10(x)) N = A_norm * quad(phi_sf_at_z, Ms_min, Ms_max, epsrel=1e-2)[0] N_floor = int(np.floor(N)) N_rest = N - N_floor if N_floor != 0: ii = np.random.randint(low=0, high=len(self.mass_array), size=N_floor) if np.random.uniform(low=0.0, high=1.0, size=1) <= N_rest: iii = np.random.randint(low=0, high=len(self.mass_array), size=1) N = int(N_floor + 1) else: N = int(N_floor) print(self.t, self.integ.step, A_norm, N) if N == 0: pass else: if self.cluster_masses is None: if N_floor > 0: m = len(self.ssfr_params[:, 0]) self.cluster_masses = np.ma.zeros(N) self.cluster_ssfr_p = np.ma.zeros([m, N]) self.cluster_masses[0:N_floor] = np.ma.copy( self.mass_array[ii]) self.cluster_ssfr_p[:, 0:N_floor] = np.ma.copy( self.ssfr_params[:, ii]) if N == (N_floor + 1) and N_floor > 0: self.cluster_masses[-1] = np.ma.copy(self.mass_array[iii]) self.cluster_ssfr_p[:, -1] = np.ma.copy( self.ssfr_params[:, iii]).flatten() elif N == (N_floor + 1): self.cluster_masses = np.ma.copy(self.mass_array[iii]) self.cluster_ssfr_p = np.ma.copy( self.ssfr_params[:, iii]).flatten() self.infall_z = np.ma.zeros(N) + self.z self.infall_Ms = np.ma.copy(self.cluster_masses) self.infall_Mh = self.find_M_halo(self.infall_Ms, self.z) ##### self.z #self.infall_Mh = self.p.map(self.find_M_halo, ([ms,self.z] for ms in self.infall_Ms)) if self.oc_flag and N != 0: td = np.ma.array( self.t_delay([ self.cluster_masses, self.cluster_ssfr_p, self.infall_Mh, self.z ])) self.death_date = cosmo.lookback_time(self.z).value - td self.mass_history.append(self.cluster_masses) else: n = len(self.cluster_masses) m = len(self.ssfr_params[:, 0]) new_arr_mass = np.ma.zeros(n + N) new_arr_ssfr_p = np.ma.zeros([m, n + N]) new_arr_z = np.ma.zeros(n + N) new_arr_Ms = np.ma.zeros(n + N) new_arr_Mh = np.ma.zeros(n + N) new_arr_flag_MQ = np.ma.zeros(n + N) new_arr_mass[0:n] = self.cluster_masses[:] new_arr_ssfr_p[:, 0:n] = self.cluster_ssfr_p[:, :] new_arr_z[0:n] = self.infall_z[:] new_arr_Ms[0:n] = self.infall_Ms[:] new_arr_Mh[0:n] = self.infall_Mh[:] new_arr_flag_MQ[0:n] = self.MQ_flags[:] if N_floor > 0: new_arr_mass[n:n + N_floor] = np.ma.copy( self.mass_array[ii]) new_arr_ssfr_p[:, n:n + N_floor] = np.ma.reshape( self.ssfr_params[:, ii], (m, -1)) #, copy=True) if N == (N_floor + 1): new_arr_mass[-1] = np.ma.copy(self.mass_array[iii]) new_arr_ssfr_p[:, -1] = np.ma.copy( self.ssfr_params[:, iii]).flatten() if self.oc_flag: new_arr_flag_OC = np.ma.zeros(n + N) new_arr_flag_OC[0:n] = self.OC_flags[:] self.OC_flags = new_arr_flag_OC new_arr_z[n:n + N] = np.ma.zeros(N) + self.z new_arr_Ms[n:n + N] = np.ma.copy(new_arr_mass[n:n + N]) new_arr_Mh[n:n + N] = self.find_M_halo(new_arr_mass[n:n + N], self.z) ###self.z #new_arr_Mh[n:n+N] = p.map(self.find_M_halo, ([ms,self.z] for ms in new_arr_mass[n:n+N])) self.cluster_masses = new_arr_mass self.cluster_ssfr_p = new_arr_ssfr_p self.infall_z = new_arr_z self.infall_Ms = new_arr_Ms self.infall_Mh = new_arr_Mh self.MQ_flags = new_arr_flag_MQ self.mass_history.append(self.cluster_masses)
def gen_cluster(self): ''' Generates the cluster. Samples N galaxies from the star forming galaxy array ''' self.logM0 = self.cluster_M self.z_0 = self.z_final self.MQ_flags = [] self.OC_flags = [] self.progenitor_mass = np.power(10, self.M_main(self.z_init)) A_norm = self.progenitor_mass / self.A_denom( self.z_init) * self.n_cluster temp_mass_array = np.ma.copy(self.mass_array) temp_mass_array.mask = np.ma.nomask Ms_min = np.ma.min(temp_mass_array) Ms_max = np.ma.max(temp_mass_array) phi_sf_at_z = lambda x: self.phi_sf_interp(np.log10(x)) N = A_norm * quad(phi_sf_at_z, Ms_min, Ms_max, epsrel=1e-2)[0] N_floor = int(np.floor(N)) N_rest = N - N_floor if N_floor != 0: ii = np.random.randint(low=0, high=len(self.mass_array), size=N_floor) if np.random.uniform(low=0.0, high=1.0, size=1) <= N_rest: iii = np.random.randint(low=0, high=len(self.mass_array), size=1) N = int(N_floor + 1) else: N = int(N_floor) if N == 0: self.cluster_masses = None self.cluster_ssfr_p = None self.infall_Ms = None self.infall_Mh = None self.infall_z = None else: m = len(self.ssfr_params[:, 0]) if N_floor > 0: self.cluster_masses = np.ma.zeros(N) self.cluster_ssfr_p = np.ma.zeros([m, N]) self.cluster_masses[0:N_floor] = np.ma.copy( self.mass_array[ii]) self.cluster_ssfr_p[:, 0:N_floor] = np.ma.copy( self.ssfr_params[:, ii]) if N == (N_floor + 1) and N_floor > 0: self.cluster_masses[-1] = np.ma.copy(self.mass_array[iii]) self.cluster_ssfr_p[:, -1] = np.ma.copy( self.ssfr_params[:, iii]).flatten() elif N == (N_floor + 1) and N_floor == 0: self.cluster_masses = np.ma.copy(self.mass_array[iii]) self.cluster_ssfr_p = np.ma.copy( self.ssfr_params[:, iii]).flatten() self.infall_Ms = np.ma.copy(self.cluster_masses) self.infall_Mh = self.find_M_halo(self.infall_Ms, self.z_init) ###### z_init self.infall_z = np.ma.zeros(N) + self.z_init self.mass_history.append(self.cluster_masses) if self.oc_flag and N != 0: td = np.ma.array( self.t_delay([ self.cluster_masses, self.cluster_ssfr_p, self.infall_Mh, self.z_init ])) self.death_date = cosmo.lookback_time(self.z_init).value - td
def history_tracks(self, model): n = len(model.mass_history) m = len(model.mass_history[-1]) number_of_lines = 10000 prob_threshold = number_of_lines / m tt = cosmo.lookback_time(np.unique(model.infall_z)) mass_history = np.ma.zeros([n, m]) for i, mass_arr in enumerate(model.mass_history): mass_history[i, 0:int(mass_arr.shape[0])] = mass_arr[:] mass_history = np.ma.log10(mass_history[::-1, :]) fig, ax = plt.subplots() count_mq = 0 count_oc = 0 count_sf = 0 print('Expected total: {}'.format(number_of_lines)) for i in range(0, m): p = np.random.uniform() if p < prob_threshold: ax.plot(tt, mass_history[:, i], color='k', alpha=0.01) # if model.oc_flag: # if OC_flags[i]: # ax.plot(tt, mass_history[:,i], color='C1', alpha=0.01) # if MQ_flags[i] == 0: # #ax.plot(tt, mass_history[:,i], color='b', alpha=0.01) # pass # else: # ax.plot(tt, mass_history[:,i], color='r', alpha=0.01) # pass # final_masses = mass_history[mass_history[:,i].mask == False, i] # try: # if final_masses[0] > 8.9 and final_masses[0] < 9.1: # temp = True # if model.oc_flag: # if OC_flags[i]: # ax.plot(tt, mass_history[:,i], color='purple', alpha=0.5) # count_oc += 1 # temp = False # if MQ_flags[i] == 0 and temp: # ax.plot(tt, mass_history[:,i], color='C0', alpha=0.5) # count_sf += 1 # elif temp: # ax.plot(tt, mass_history[:,i], color='r', alpha=0.5) # count_mq += 1 # pass # except: # pass # final_masses = mass_history[mass_history[:,i].mask == False, i] # try: #some galaxies infall quenched. those will fail this step # if final_masses[0] > 8.9 and final_masses[0] < 9.1: # ax.plot(tt, mass_history[:,i], color='C0', alpha=0.1) # except: # pass print('Mass Quenched: {}'.format(count_mq)) print('Over Consumed: {}'.format(count_oc)) print('Star Forming: {}'.format(count_sf)) ax.set_xlabel('Lookback Time [Gyr]') ax.set_ylabel('Stellar Mass [log($M_*/M_\odot$)]') fig.savefig('./images/history_path.png', dpi=220)
def fmerge_at_z(model, zbin_low, zbin_high, cosmic=False, cbc_type=None, zmerge_min=0, zmerge_max=0.1): """ Calculates the number of mergers of a particular CBC type per unit mass N_cor,i = f_bin f_IMF N_merger,i / Mtot,sim `model` should be a dict containing multiple models at different metallicities Each model will receive a weight based on its metallicity, and the metallicity distribution at that particular redshift In COSMIC, Mtot,sim already accounts for f_bin and f_IMF """ f_merge = [] met_weights = [] for met in sorted(model.keys()): mass_stars = model[met]['mass_stars'] if cosmic: bpp = model[met][cbc_type] # get delay times for all merging binaries (merger happens at evol_type=6) merger = bpp.loc[bpp['evol_type'] == 6] else: merger = model[met]['mergers'] # get the fraction of systems born between [zlow,zhigh] that merger between [zmerge_min,zmerge_max] if zbin_low == 0: # special treatment for the first bin in the local universe tdelay_min = 0 tdelay_max = cosmo.lookback_time(zbin_high).to(u.Myr).value else: tdelay_min = (cosmo.lookback_time(zbin_low) - cosmo.lookback_time(zmerge_max)).to(u.Myr).value tdelay_max = (cosmo.lookback_time(zbin_high) - cosmo.lookback_time(zmerge_min)).to(u.Myr).value if cosmic: Nmerge_zbin = len(merger.loc[(merger['tphys'] <= tdelay_max) & (merger['tphys'] > tdelay_min)]) else: Nmerge_zbin = len(merger.loc[(merger['t_delay'] <= tdelay_max) & (merger['t_delay'] > tdelay_min)]) # get the number of mergers per unit mass f_merge.append(float(Nmerge_zbin) / mass_stars) # get redshift in the middle of this log-spaced interval midz = 10**(np.log10(zbin_low) + (np.log10(zbin_high) - np.log10(zbin_low)) / 2.0) # append the relative weight of this metallicity at this particular redshift met_weights.append(metal_disp_z(midz, met)) # normalize metallicity weights so that they sum to unity met_weights = np.asarray(met_weights) / np.sum(met_weights) # return weighted sum of f_merge, units of Msun**-1 return np.sum(np.asarray(f_merge) * met_weights) * u.Msun**(-1)
def gen_cluster(self, z_0, z_max, logM0, eta, show_plots): print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') print('Generating cluster with :') print('z_0 = {}; logM0 = {}; eta = {}'.format(z_0, logM0, eta)) delta_z = 0.05 self.eta = eta self.z_0 = z_0 self.logM0 = logM0 self.z_range = np.arange(self.z_0, z_max, delta_z) pool = mp.Pool(processes=3) self.delta_m = 0.05 self.logMStar_setup = np.arange(3, 12, 0.1) self.logMHalo_bins = np.arange(self.M_min, self.M_max, self.delta_m) self.logMHalo_eff = np.array([ (self.logMHalo_bins[i] + self.logMHalo_bins[i + 1]) / 2 for i in range(0, len(self.logMHalo_bins) - 1) ]) self.logMStar_bins = np.array([ np.log10(self.M_star(np.power(10, self.logMHalo_bins), z)) for z in self.z_range ]) self.logMStar_eff = np.array([[ (self.logMStar_bins[i, j] + self.logMStar_bins[i, j + 1]) / 2 for j in range(0, len(self.logMHalo_bins) - 1) ] for i, z in enumerate(self.z_range)]) self.logMStar_low = np.array([m for m in self.logMStar_bins[:, :-1]]) self.logMStar_upp = np.array([m for m in self.logMStar_bins[:, 1:]]) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ phi_init_sf = self.phi_sf_3(self.logMStar_setup) phi_init_q = np.zeros(len(phi_init_sf)) phi_sf_z_10 = [] phi_q_z_10 = [] z_eff = [] for i in range(1, len(self.z_range)): z = self.z_range[-i] z_eff.append((z + self.z_range[-i - 1]) / 2) dt = cosmo.lookback_time(z).value - cosmo.lookback_time( self.z_range[-i - 1]).value dn_blue = self.dN_blue(self.logMStar_setup, z, phi_init_sf) * dt dn_red = self.dN_red(self.logMStar_setup, z, phi_init_sf, phi_init_q) * dt phi_init_sf += dn_blue phi_init_q += dn_red phi_sf_z_10.append(np.copy(phi_init_sf)) phi_q_z_10.append(np.copy(phi_init_q)) z_midp = np.array(z_eff) self.phi_sf_z_10_interp = interp2d(self.logMStar_setup, z_midp[z_midp <= z_max], phi_sf_z_10) self.phi_q_z_10_interp = interp2d(self.logMStar_setup, z_midp[z_midp <= z_max], phi_q_z_10) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ phi_init_sf = self.phi_sf_3(self.logMStar_setup) phi_init_q = np.zeros(len(phi_init_sf)) phi_sf_z_4 = [] phi_q_z_4 = [] for i in range(1, len(self.z_range[self.z_range <= 10 + delta_z / 2])): z = self.z_range[-i] dt = cosmo.lookback_time(z).value - cosmo.lookback_time( self.z_range[-i - 1]).value dn_blue = self.dN_blue(self.logMStar_setup, z, phi_init_sf) * dt dn_red = self.dN_red(self.logMStar_setup, z, phi_init_sf, phi_init_q) * dt phi_init_sf += dn_blue phi_init_q += dn_red phi_sf_z_4.append(np.copy(phi_init_sf)) phi_q_z_4.append(np.copy(phi_init_q)) self.phi_sf_z_4_interp = interp2d(self.logMStar_setup, z_midp[z_midp <= 10], phi_sf_z_4) self.phi_q_z_4_interp = interp2d(self.logMStar_setup, z_midp[z_midp <= 10], phi_q_z_4) Halo_masses = np.array(pool.map(self.M_main, (z for z in self.z_range))) delta_halos = [ np.log10( np.power(10, Halo_masses[i]) - np.power(10, Halo_masses[i + 1])) for i in range(0, len(self.z_range) - 1) ] delta_halos.append(Halo_masses[-1]) delta_halos = np.array(delta_halos) self.A = np.power(10, delta_halos) / np.array( pool.map(self.func_A_normal, (z for z in self.z_range))) self.delta_N_sf_infall = np.array( pool.map(self.func_delta_N_sf, ([z, i] for i, z in enumerate(self.z_range)))) self.delta_N_q_infall = np.array( pool.map(self.func_delta_N_q, ([z, i] for i, z in enumerate(self.z_range)))) self.delta_N_a_infall = self.delta_N_q_infall + self.delta_N_sf_infall self.delta_td = np.array( pool.map(self.func_delta_td, (m for m in self.logMHalo_bins))).T out = np.array( pool.map(self.func_delta_N_sf_final, ([z, self.delta_td[i, :], i] for i, z in enumerate(self.z_range)))) self.delta_N_sf_final, self.delta_N_q_final = out[:, 0, :], out[:, 1, :] self.delta_N_a_final = self.delta_N_sf_final + self.delta_N_q_final logMs_final = np.arange(8, 12, 0.1) N_sf_per_mass = self.group_by_mass(self.delta_N_sf_final, logMs_final) N_q_per_mass = self.group_by_mass(self.delta_N_q_final, logMs_final) N_a_per_mass = N_sf_per_mass + N_q_per_mass N_field_q_per_mass = self.phi_q_z_4_interp(logMs_final, self.z_0) N_field_a_per_mass = self.phi_q_z_4_interp( logMs_final, self.z_0) + self.phi_sf_z_4_interp( logMs_final, self.z_0) pool.close() if show_plots: ### final stellar masses of galaxies #fig,ax = plt.subplots() #contourf_ = plt.contourf(self.z_range, self.logMHalo_bins, self.logMStar_bins.T) #ax.set_xlabel('redshift') #ax.set_ylabel('halo mass') #ax.set_title('Stellar Mass') #cbar = fig.colorbar(contourf_) ### verification that first derivative is okay! #fig,ax = plt.subplots() #ax.semilogy(self.logMHalo_bins, self.M_star(np.power(10,self.logMHalo_bins), z=0.35), 'k') #ax2 = ax.twinx() #ax2.plot(self.logMHalo_bins, self.dMs_dMh(np.power(10,self.logMHalo_bins),z=0.35), linestyle='--') #ax.set_ylabel('Stellar Mass [log($M_*/M_\odot$)]') #ax.set_xlabel('log Halo Mass') #ax2.set_ylabel('$dM_{*}/dMh$') ## simple plot of phi fig, ax = plt.subplots() muzz_q = self.phi_q_2(logMs_final, z=1.2) muzz_sf = self.phi_sf_2(logMs_final, z=1.2) peng_f = self.phi_q_z_4_interp(logMs_final, 1.2) peng_f_sf = self.phi_sf_z_4_interp(logMs_final, 1.2) #peng_c = self.phi_q_z_10_interp(logMs_final, 1.2) #ax.semilogy(logMs_final, self.phi_sf_2(logMs_final, z=1.2), label='Muzz, SF', color='b') ax.semilogy(logMs_final, muzz_sf, label='Muzz, SF', color='C0') ax.semilogy(logMs_final, muzz_q, label='Muzz, Q', color='r') #ax.semilogy(logMs_final, self.phi_sf_z_4_interp(logMs_final, 1.2)*1.5e2, label='Peng, SF', color='b', linestyle='--') ax.semilogy(logMs_final, peng_f * np.max(muzz_q) / np.max(peng_f), label='Peng, Q', color='r', linestyle='--') ax.semilogy(logMs_final, peng_f_sf * np.max(muzz_q) / np.max(peng_f), label='Peng, SF', color='C0', linestyle='--') #ax.semilogy(logMs_final, peng_c * np.max(muzz_q)/ np.max(peng_c), label='Peng cls, Q', color='r', alpha=0.3) ax.set_xlabel('Stellar Mass [log($M_*/M_\odot$)]') ax.set_ylabel('$\Phi_{field}$ [z=1.2]') ax.set_xlim([8.4, 12]) ax.set_ylim([1e-6, 1e-2]) ax.legend() ### Contour plot of delay times vs halo mass and redshift #fig,ax = plt.subplots() #contourf_ = ax.contourf(self.z_range, self.logMHalo_bins, self.delta_td.T, np.arange(0,14,2), extend='both', cmap=cm.seismic_r) #ax.set_xlabel('Redshift [z]') #ax.set_ylabel('Halo Mass [log($M_h$/$M_\odot$)]') #ax.set_title('$t_{delay}$ [Gyr]') #cbar = fig.colorbar(contourf_) ### Contour plot of all galaxies vs halo mass and redshift at INFALL #fig,ax = plt.subplots() #contourf_ = ax.contourf(self.z_range, self.logMHalo_eff, np.log10(self.delta_N_a_infall.T))#, np.arange(-15,16,3), extend='both', cmap=cm.seismic_r) #ax.set_xlabel('Redshift [z]') #ax.set_ylabel('Halo Mass [log($M_h$/$M_\odot$)]') #ax.set_title('Delta N [Infall]') #cbar = fig.colorbar(contourf_) ### Contour plot of star forming galaxies vs halo mass and redshift #fig,ax = plt.subplots() #contourf_ = ax.contourf(self.z_range, self.logMHalo_eff, np.log10(self.delta_N_sf_final.T))#, np.arange(-15,16,3), extend='both', cmap=cm.seismic_r) #ax.set_xlabel('Redshift [z]') #ax.set_ylabel('Halo Mass [log($M_h$/$M_\odot$)]') #ax.set_title('Delta N [SF]') #cbar = fig.colorbar(contourf_) ### Contour plot of quiescent vs stellar mass and redshift #fig,ax = plt.subplots() #contourf_ = ax.contourf(self.z_range, self.logMHalo_eff, np.log10(self.delta_N_q_final.T))#, np.arange(-15,16,3), extend='both', cmap=cm.seismic_r) #ax.set_xlabel('Redshift [z]') #ax.set_ylabel('Halo Mass [log($M_h$/$M_\odot$)]') #ax.set_title('Delta N [Q]') #cbar = fig.colorbar(contourf_) ### Plot of peng sSFR #fig,ax = plt.subplots() #ax.plot(self.z_range, self.SSFR(np.array(10),self.z_range)) #ax.set_xlabel('Redshift [z]') #ax.set_ylabel('sSFR yr$^{-1}$') #ax.set_title('[Peng+2010]') #plt.show() ### Plot of Quenched Fraction per mass #fig,ax = plt.subplots()#figsize=(5,4), tight_layout=True, sharey=True) #ax.plot(logMs_final, N_q_per_mass/N_a_per_mass, label='Cluster') #ax.plot(logMs_final, N_field_q_per_mass/N_field_a_per_mass, label='Field') #ax.set_xlabel('Stellar Mass [log($M_*$/$M_\odot$)]') #ax.set_ylabel('Quenched Fraction') #ax.set_xlim([9,12]) #ax.set_ylim([0,1]) #ax.legend(bbox_to_anchor=(1,0.9)) ### Time slice of delay times #fig,ax = plt.subplots() #ii = 20 #temp = cosmo.lookback_time(self.z_range[ii]).value - cosmo.lookback_time(self.z_0).value #ax.plot(self.logMStar_bins[ii,:], self.delta_td[ii,:]) #ax.plot([8,12], [temp,temp]) #ax.set_xlabel('Stellar Mass [$M_*$]') #ax.set_ylabel('Delay Times [Gyr]') #ax.set_title('z = {:.1f}'.format(self.z_range[ii])) #ax.set_xlim([8,12]) #ax.set_ylim([0,12]) #plt.show() plt.show() plt.close('all') #FIX TDELAY..... return (logMs_final, N_q_per_mass, N_a_per_mass, N_field_q_per_mass, N_field_a_per_mass)
## CASE 1: system merges ## assign lookback time w/ randomly sampled redshift from weighted distribution ## alive/disrupted systems have merge_index = -1 if (merger_type != -1): ## check if BBH in COSMIC if (merger_type == 1414): this_BBH = True remnant_mass_k1 = bpp['mass0_1'].iloc[merge_index] remnant_mass_k2 = bpp['mass0_2'].iloc[merge_index] delay_time = bpp['tphys'].iloc[merge_index] z_merge = z_at_value(cosmo.lookback_time, delay_time * u.Myr) lookback_time = cosmo.lookback_time(z_f).to(u.Myr).value if (delay_time <= lookback_time): merge_by_z0 = True p_det = calc_detection_prob( remnant_mass_k1, remnant_mass_k2, z_merge) ## detection probability for this merger if (this_BBH): this_BBHm = True else: merge_by_z0 = False remnant_mass_k1 = bpp['mass0_1'].iloc[merge_index] remnant_mass_k2 = bpp['mass0_2'].iloc[merge_index] ## CASE 2: system does not merge
m15_init_idx = min(enumerate(np.abs(beh['col2'] - 15)), key=itemgetter(1))[0] # find halo mass 10^15 Msun for SFH3 m13_init_idx = min(enumerate(np.abs(beh['col2'] - 13)), key=itemgetter(1))[0] # find halo mass 10^13 Msun for SFH4 m11_init_idx = min( enumerate(np.abs(beh['col2'] - 11.4)), key=itemgetter(1))[0] # find halo mass 10^11.4 Msun for SFH5 m15_idx = [beh['col2'] == beh['col2'][m15_init_idx]] m13_idx = [beh['col2'] == beh['col2'][m13_init_idx]] m11_idx = [beh['col2'] == beh['col2'][m11_init_idx]] nz = np.sum(m15_idx) zs = beh['col1'][m15_idx] - 1 dts = np.zeros(nz - 1) lbt = Cosmo.lookback_time(beh['col1'][m15_idx] - 1) for i in range(0, nz - 1): dts[i] = lbt[i + 1].value - lbt[i].value Zevol_record = np.zeros((len(Zevol_coeff), nz, len(Zs))) ################ M weighted Z calculation ####################### for k in range(0, 3): # for each SFH if k == 0: sfr = 10**beh['col3'][m15_idx] elif k == 1: sfr = 10**beh['col3'][m13_idx] else: sfr = 10**beh['col3'][m11_idx] for i in range(0, nz - 1): # for each z nomi = np.zeros(len(Zs)) # Z * dz * SFR denomi = 0 # dz * SFR
def delay_times(self, model): z_init = model.z_init z_final = model.z_final z_range = np.arange(z_final, z_init, 0.1) logMh_range = np.arange(7, 15, 0.1) Mh_range = np.power(10, logMh_range) xx = [] xx_2 = [] yy = [] #zz = [] for z in z_range: xx.append(np.zeros(len(Mh_range)) + z) xx_2.append(np.zeros(len(Mh_range)) + cosmo.lookback_time(z).value) yy.append(np.log10(model.M_star(Mh_range, z))) #zz.append(cosmo.lookback_time(z).value - model.t_delay_2(Mh_range, z)) xx = np.array(xx).flatten() xx_2 = np.array(xx_2).flatten() yy = np.array(yy).flatten() #zz = np.array(zz).flatten() fig, ax = plt.subplots(tight_layout=True) #contourf_ = ax.tricontourf(xx,yy,zz, np.arange(0,13,0.1), extend='both') ax.scatter(model.infall_z_Q, model.final_mass_cluster_Q, s=0.1, c='r') ax.scatter(model.infall_z_SF, model.final_mass_cluster_SF, s=0.1, c='b') ax.set_xlabel('Redshift [z]') ax.set_ylabel('Stellar Mass [log($M_*$/$M_\odot$)]') #ax.set_ylim([8,12]) #cbar = fig.colorbar(contourf_, label='Quenched due to OC') fig.savefig('./images/cluster_final_m_z.png', dpi=220) fig, ax = plt.subplots(tight_layout=True) #contourf_ = ax.tricontourf(xx_2,yy,zz, np.arange(0,13,0.1), extend='both') try: ax.scatter(cosmo.lookback_time(model.infall_z_Q).value, model.final_mass_cluster_Q, s=0.1, c='r') except: pass try: ax.scatter(cosmo.lookback_time(model.infall_z_SF).value, model.final_mass_cluster_SF, s=0.1, c='b') except: pass ax.set_xlabel('Lookback Time [Gyr]') ax.set_ylabel('Stellar Mass [log($M_*$/$M_\odot$)]') #ax.set_ylim([8,12]) #cbar = fig.colorbar(contourf_, label='Quenched due to OC') fig.savefig('./images/cluster_final_m.png', dpi=220) fig, ax = plt.subplots(tight_layout=True) #contourf_ = ax.tricontourf(xx_2,yy,zz, np.arange(0,13,0.1), extend='both') temp_z = np.ma.copy(model.infall_z) temp_z.mask = np.ma.nomask temp_m = np.ma.copy(model.infall_Ms) temp_m.mask = np.ma.nomask temp_m = np.ma.log10(temp_m) ax.scatter(cosmo.lookback_time(temp_z).value, temp_m, s=0.1, c='r') ax.set_xlabel('Lookback Time [Gyr]') ax.set_ylabel('Stellar Mass [log($M_*$/$M_\odot$)]') #ax.set_ylim([8,12]) #cbar = fig.colorbar(contourf_, label='Quenched due to OC') fig.savefig('./images/cluster_infall_m.png', dpi=220)