def _Overquenching(descendants, successions=None, wills=None, sf_ancestors=None): for i_d, descendant in enumerate(descendants): # Assign final quenched SSFR to star-forming galaxies. This is specified # due to the fact that there's a lower bound on the SSFR. These values are effectively # hardcoded in order to reproduce the quiescent peak of the SSFR # distribution, which is more of a lower bound. #q_ssfr_mean = get_q_ssfr_mean(descendant.mass[succession[sf_ancestors]]) sf_succession = (successions[i_d])[sf_ancestors[i_d]] avg_q_ssfr = sfr_evol.AverageLogSSFR_q_peak( descendant.mass[sf_succession]) sigma_q_ssfr = sfr_evol.ScatterLogSSFR_q_peak( descendant.mass[sf_succession]) min_q_ssfr = sigma_q_ssfr * np.random.randn( len(sf_succession)) + avg_q_ssfr descendants[i_d].min_ssfr[sf_succession] = min_q_ssfr # Deal with over quenched galaxies overquenched = np.where(descendants[i_d].min_ssfr[sf_succession] > descendants[i_d].ssfr[sf_succession]) if len(overquenched[0]) > 0: descendants[i_d].ssfr[sf_succession[overquenched]] = \ descendant.min_ssfr[sf_succession[overquenched]] descendants[i_d].sfr[sf_succession[overquenched]] = \ descendant.ssfr[sf_succession[overquenched]] \ + descendant.mass[sf_succession[overquenched]] return descendants
def _tQuench(ancestor, descendant, P_q, succession=None, will=None): ''' Given ancestor and descendent galaxy populations AND the quenching probabilities, calculate the cosmic time when the star-forming ancestor galaxies, begins to quench. The the quenching time is sampled uniformly between t_final and t_snap_gensis. ''' sf_ancestors = np.where( ancestor.sfr_class[will] == 'star-forming')[0] # SF ancestors np.random.seed() randoms = np.random.uniform(0., 1., len(sf_ancestors)) is_qing = np.where(P_q > randoms) # quenching is_notqing = np.where(P_q <= randoms) # not quenching descendant.sfr_class[succession[sf_ancestors[is_qing]]] = 'quiescent' descendant.sfr_class[succession[sf_ancestors[is_notqing]]] = 'star-forming' #print len(is_qing[0]), ' is quenching' #print len(is_notqing[0]), ' is not quenching' np.random.seed() t_final = descendant.t_cosmic # final t_cosmic t_q = (t_final - ancestor.tsnap_genesis[will[sf_ancestors]]) * np.random.uniform( 0., 1., len(sf_ancestors)) t_q += ancestor.tsnap_genesis[will[sf_ancestors]] z_q = z_of_t(t_q) t_q[is_notqing] = 999.0 # galaxies that will never quench z_q[is_notqing] = -999.0 # Assign final quenched SSFR to star-forming galaxies. This is specified # due to the fact that there's a lower bound on the SSFR. These values are effectively # hardcoded in order to reproduce the quiescent peak of the SSFR # distribution, which is more of a lower bound. #q_ssfr_mean = get_q_ssfr_mean(descendant.mass[succession[sf_ancestors]]) avg_q_ssfr = sfr_evol.AverageLogSSFR_q_peak( descendant.mass[succession[sf_ancestors]]) sigma_q_ssfr = sfr_evol.ScatterLogSSFR_q_peak( descendant.mass[succession[sf_ancestors]]) min_q_ssfr = sigma_q_ssfr * np.random.randn(len(sf_ancestors)) + avg_q_ssfr descendant.min_ssfr[sf_ancestors] = min_q_ssfr return descendant, t_q, z_q
def _Overquenching(self): ''' Account for overquenching Assign final quenched SSFR to star-forming galaxies. This is specified due to the fact that there's a lower bound on the SSFR. These values are effectively hardcoded in order to reproduce the quiescent peak of the SSFR distribution, which is more of a lower bound. ''' for n_d in self.descendant_dict.keys(): des = self.descendant_dict[str(n_d)] sf_succ = des.succession[des.sf_ancestor] avg_q_ssfr = sfr_evol.AverageLogSSFR_q_peak(des.mass[sf_succ]) sigma_q_ssfr = sfr_evol.ScatterLogSSFR_q_peak(des.mass[sf_succ]) min_q_ssfr = sigma_q_ssfr * np.random.randn( len(sf_succ)) + avg_q_ssfr self.descendant_dict[str( n_d)].min_ssfr[sf_succ] = min_q_ssfr.copy() # Deal with over quenched galaxies overquenched = np.where( self.descendant_dict[str(n_d)].min_ssfr[sf_succ] > self.descendant_dict[str(n_d)].ssfr[sf_succ]) if len(overquenched[0]) > 0: self.descendant_dict[str(n_d)].ssfr[sf_succ[overquenched]] = \ self.descendant_dict[str(n_d)].min_ssfr[sf_succ[overquenched]] self.descendant_dict[str(n_d)].sfr[sf_succ[overquenched]] = \ self.descendant_dict[str(n_d)].ssfr[sf_succ[overquenched]] + \ self.descendant_dict[str(n_d)].mass[sf_succ[overquenched]] # some indicator that the galaxy has fully quenched! # 1 = has fully quenched 0 = otherwise self.descendant_dict[str(n_d)].quenched[ sf_succ[overquenched]] = 1 del n_d del des return None
def EvolveSatSFR(sg_in, tqdelay_dict=None): ''' Evolve the infalling SFR assigned based on central galaxy SFRs ''' if tqdelay_dict == None: raise ValueError # initalize sg_obj = SGPop() sg_obj.ilk = sg_in.ilk.copy() sg_obj.pos = sg_in.pos.copy() sg_obj.snap_index = sg_in.snap_index.copy() sg_obj.zsnap = sg_in.zsnap.copy() sg_obj.t_cosmic = sg_in.t_cosmic.copy() sg_obj.sfms_prop = sg_in.sfms_prop.copy() sg_obj.abcrun = sg_in.abcrun sg_obj.prior_name = sg_in.prior_name sg_obj.mass = sg_in.mass.copy() sg_obj.sfr = np.repeat(-999., len(sg_obj.mass)) sg_obj.ssfr = np.repeat(-999., len(sg_obj.mass)) sg_obj.halo_mass = sg_in.halo_mass.copy() sg_obj.first_infall_nsnap = sg_in.first_infall_nsnap.copy() sg_obj.first_infall_z = sg_in.first_infall_z.copy() sg_obj.first_infall_t = sg_in.first_infall_t.copy() sg_obj.first_infall_sfr = sg_in.first_infall_sfr.copy() sg_obj.first_infall_mass = sg_in.first_infall_mass.copy() sg_obj.t_qstart = np.repeat(-999., len(sg_obj.mass)) sg_obj.z_qstart = np.repeat(-999., len(sg_obj.mass)) sg_obj.q_ssfr = np.repeat(-999., len(sg_obj.mass)) # First classify the galaxies into SF, Q, and Qing # this is so that t_Q,start = t_inf for Qing galaxies, # Q galaxies are left alone # SF galaxies are affected by the delay time infall = np.where((sg_obj.first_infall_mass > 0.) & (sg_obj.first_infall_sfr > -999.)) fq_obj = Fq() sfq_class = fq_obj.Classify(sg_obj.first_infall_mass[infall], sg_obj.first_infall_sfr[infall], sg_obj.first_infall_z[infall], sg_obj.sfms_prop) sf_infall = (infall[0])[np.where( sfq_class == 'star-forming')] # starforming @ infall q_infall = (infall[0])[np.where( sfq_class == 'quiescent')] # quiescent @ infall ssfr_final = sfr_evol.AverageLogSSFR_q_peak(sg_obj.mass[infall]) + \ sfr_evol.ScatterLogSSFR_q_peak(sg_obj.mass[infall]) * np.random.randn(len(infall[0])) # sub-divide the quiescent @ infall population to those that are # quenching @ infall + quiescent @ infall based on simple SSFR cut q_mass_infall = sg_obj.first_infall_mass[q_infall] q_sfr_infall = sg_obj.first_infall_sfr[q_infall] q_ssfr_infall = q_sfr_infall - q_mass_infall q_cut_SSFR = sfr_evol.AverageLogSSFR_q_peak(q_mass_infall) + \ 1.5 * sfr_evol.ScatterLogSSFR_q_peak(q_mass_infall) qing_infall = q_infall[np.where(q_ssfr_infall > q_cut_SSFR)] # quenching qq_infall = q_infall[np.where(q_ssfr_infall <= q_cut_SSFR)] # quenched # Quiescent @ infall----- # The SSFR is preserved from infall, so effectively their SFR decreases sg_obj.ssfr[qq_infall] = sg_obj.first_infall_sfr[qq_infall] - \ sg_obj.first_infall_mass[qq_infall] sg_obj.sfr[qq_infall] = sg_obj.mass[qq_infall] + sg_obj.ssfr[qq_infall] # Quenching @ infall----- # Quenching satellite galaxies skip the delay phase and immediately # start quenching when the simulation begins. sg_obj.t_qstart[qing_infall] = sg_obj.first_infall_t[qing_infall] sg_obj.z_qstart[qing_infall] = sg_obj.first_infall_z[qing_infall] sg_obj.q_ssfr[qing_infall] = sfr_evol.AverageLogSSFR_q_peak(sg_obj.mass[qing_infall]) + \ sfr_evol.ScatterLogSSFR_q_peak(sg_obj.mass[qing_infall]) * np.random.randn(len(qing_infall)) dlogSFR_MS_M = 0.53 * (sg_obj.mass[qing_infall] - sg_obj.first_infall_mass[qing_infall]) dlogSFR_MS_z = sg_obj.sfms_prop['zslope'] * ( sg_obj.zsnap - sg_obj.first_infall_z[qing_infall]) dlogSFR_Q = sfr_evol.DeltaLogSFR_quenching(sg_obj.t_qstart[qing_infall], sg_obj.t_cosmic, M_q=sg_obj.mass[qing_infall], tau_prop={'name': 'satellite'}) sg_obj.sfr[qing_infall] = sg_obj.first_infall_sfr[qing_infall] + \ dlogSFR_MS_M + dlogSFR_MS_z + dlogSFR_Q sg_obj.ssfr[ qing_infall] = sg_obj.sfr[qing_infall] - sg_obj.mass[qing_infall] # deal with over quenching #overquenched = np.where(sg_obj.ssfr[qing_infall] < sg_obj.q_ssfr[qing_infall]) #sg_obj.ssfr[qing_infall[overquenched]] = sg_obj.q_ssfr[qing_infall[overquenched]] # Star-forming @ infall if tqdelay_dict['name'] == 'hacked': sg_obj.t_qstart[sf_infall] = sg_obj.first_infall_t[sf_infall] + \ tDelay(sg_obj.mass[sf_infall], **tqdelay_dict) else: sg_obj.t_qstart[sf_infall] = sg_obj.first_infall_t[sf_infall] + \ tDelay(sg_obj.first_infall_mass[sf_infall], **tqdelay_dict) # if t_qstart > t_cosmic, then it does not quenching during the simualtion sf_q = np.where(sg_obj.t_qstart[sf_infall] < sg_obj.t_cosmic) sf_noq = np.where(sg_obj.t_qstart[sf_infall] >= sg_obj.t_cosmic) sf_infall_q = sf_infall[sf_q] sf_infall_noq = sf_infall[sf_noq] sg_obj.z_qstart[sf_infall_q] = Util.get_zsnap(sg_obj.t_qstart[sf_infall_q]) sg_obj.z_qstart[sf_infall_noq] = 999. # SF galaxies that quench sg_obj.q_ssfr[sf_infall_q] = \ sfr_evol.AverageLogSSFR_q_peak(sg_obj.mass[sf_infall_q]) + \ sfr_evol.ScatterLogSSFR_q_peak(sg_obj.mass[sf_infall_q]) * \ np.random.randn(len(sf_infall_q)) dlogSFR_MS_M = 0.53 * (sg_obj.mass[sf_infall_q] - sg_obj.first_infall_mass[sf_infall_q]) dlogSFR_MS_z = sg_obj.sfms_prop['zslope'] * ( sg_obj.zsnap - sg_obj.first_infall_z[sf_infall_q]) dlogSFR_Q = sfr_evol.DeltaLogSFR_quenching(sg_obj.t_qstart[sf_infall_q], sg_obj.t_cosmic, M_q=sg_obj.mass[sf_infall_q], tau_prop={'name': 'satellite'}) sg_obj.sfr[sf_infall_q] = sg_obj.first_infall_sfr[sf_infall_q] + \ dlogSFR_MS_M + dlogSFR_MS_z + dlogSFR_Q sg_obj.ssfr[ sf_infall_q] = sg_obj.sfr[sf_infall_q] - sg_obj.mass[sf_infall_q] # deal with over quenching #overquenched = np.where(sg_obj.ssfr[sf_infall_q] < sg_obj.q_ssfr[sf_infall_q]) #sg_obj.ssfr[sf_infall_q[overquenched]] = sg_obj.q_ssfr[sf_infall_q[overquenched]] # SF galaxies that do NOT quench dlogSFR_MS_M = 0.53 * (sg_obj.mass[sf_infall_noq] - sg_obj.first_infall_mass[sf_infall_noq]) dlogSFR_MS_z = sg_obj.sfms_prop['zslope'] * ( sg_obj.zsnap - sg_obj.first_infall_z[sf_infall_noq]) sg_obj.sfr[sf_infall_noq] = sg_obj.first_infall_sfr[sf_infall_noq] + \ dlogSFR_MS_M + dlogSFR_MS_z sg_obj.ssfr[ sf_infall_noq] = sg_obj.sfr[sf_infall_noq] - sg_obj.mass[sf_infall_noq] # deal with overquenching all at once overquench = np.where(sg_obj.ssfr[infall] < ssfr_final) sg_obj.ssfr[infall[0][overquench]] = ssfr_final[overquench] sg_obj.sfr[infall[0][overquench]] = ssfr_final[overquench] + sg_obj.mass[ infall[0][overquench]] return sg_obj
def AssignCenSFR(tf, abcrun=None, prior_name=None): ''' Given SGPop object, assign SFRs at infalling time based on the median ABC Run of the Central Galaxy evolution ''' if abcrun is None: raise ValueError if prior_name is None: raise ValueError abcinh = ABCInherit(tf, abcrun=abcrun, prior_name=prior_name) inh = abcinh.PickleLoad('all') sg_obj = SGPop() sg_obj.ImportSubhalo(subhalo_prop=abcinh.sim_kwargs['subhalo_prop']) sg_obj.sfms_prop = abcinh.sim_kwargs['sfr_prop']['sfms'] sg_obj.abcrun = abcrun sg_obj.prior_name = prior_name sg_obj.first_infall_sfr = np.repeat(-999., len(sg_obj.first_infall_nsnap)) for i_snap in range(2, abcinh.sim_kwargs['nsnap_ancestor']): infall_now = np.where((sg_obj.first_infall_nsnap == i_snap) & (sg_obj.first_infall_mass > 0.))[0] # satellite galaxies that end up massive but have 0. infalling mass #massive_infall_zero =np.where( # (sg_obj.first_infall_mass[infall_now] == 0.) & # (sg_obj.mass[infall_now] > 0.) # ) des_snap = inh[str(i_snap)] m_bins = np.arange(des_snap.mass.min() - 0.1, des_snap.mass.max() + 0.2, 0.2) for i_m in range(len(m_bins) - 1): in_massbin = np.where((des_snap.mass >= m_bins[i_m]) & (des_snap.mass < m_bins[i_m + 1])) infall_massbin = np.where( (sg_obj.first_infall_nsnap == i_snap) & (sg_obj.first_infall_mass >= m_bins[i_m]) & (sg_obj.first_infall_mass < m_bins[i_m + 1])) if len(infall_massbin[0]) == 0: continue des_ssfrs = des_snap.sfr[in_massbin] - des_snap.mass[in_massbin] pdf_ssfr_i, ssfr_binedges = np.histogram(des_ssfrs, bins=np.arange( -13.0, -8.0, 0.2), normed=True) cdf_ssfr_i = np.zeros(len(ssfr_binedges)) cdf_ssfr_i[1:] = np.cumsum(pdf_ssfr_i * np.diff(ssfr_binedges)) cdf_interp = interp1d(cdf_ssfr_i, ssfr_binedges, kind='linear') rand_i = np.random.uniform(size=len(infall_massbin[0])) sg_obj.first_infall_sfr[infall_massbin] = cdf_interp(rand_i) + \ sg_obj.first_infall_mass[infall_massbin] # assign SFR to galaxies that fell in before nsnap = 15 old_infall = np.where( (sg_obj.first_infall_nsnap >= abcinh.sim_kwargs['nsnap_ancestor']) & (sg_obj.first_infall_mass > 0.))[0] Pq = np.random.uniform(size=len(old_infall)) qfrac = Fq() fq_oldinfall = qfrac.model(sg_obj.first_infall_mass[old_infall], sg_obj.first_infall_z[old_infall], lit='wetzel') # star-forming sfing = np.where(Pq > fq_oldinfall) sig_sfms = sfr_evol.ScatterLogSFR_sfms( sg_obj.first_infall_mass[old_infall[sfing]], sg_obj.first_infall_z[old_infall[sfing]], sfms_prop=sg_obj.sfms_prop) mu_sfms = sfr_evol.AverageLogSFR_sfms( sg_obj.first_infall_mass[old_infall[sfing]], sg_obj.first_infall_z[old_infall[sfing]], sfms_prop=sg_obj.sfms_prop) sg_obj.first_infall_sfr[old_infall[sfing]] = mu_sfms + np.random.randn( len(sfing[0])) * sig_sfms # quiescent qed = np.where(Pq <= fq_oldinfall) mu_ssfr_q = sfr_evol.AverageLogSSFR_q_peak( sg_obj.first_infall_mass[old_infall[qed]]) sig_ssfr_q = sfr_evol.ScatterLogSSFR_q_peak( sg_obj.first_infall_mass[old_infall[qed]]) ssfr_q = mu_ssfr_q + np.random.randn(len(qed[0])) * sig_ssfr_q sg_obj.first_infall_sfr[ old_infall[qed]] = ssfr_q + sg_obj.first_infall_mass[old_infall[qed]] return sg_obj