def Plot_dFqdt(): ''' Plot the evolution of the derivative of the quiescent fraction as a function of M* and t for different parameterized models of f_Q d f_Q / d t ''' prettyplot() pretty_colors = prettycolors() dt = 0.01 M_arr = np.arange(9.0, 12.5, 0.5) t_arr = np.arange(6.0, 13.0, 0.2) fig = plt.figure() sub = fig.add_subplot(111) for iM, Mstar in enumerate(M_arr): qf = gp.Fq() lit = 'wetzelsmooth' sub.plot(t_arr, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_arr], ls='-', lw=3, c=pretty_colors[iM], label=r"$\mathtt{M}_* = " + str(round(Mstar, 2)) + "$") lit = 'wetzel' sub.plot(t_arr, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_arr], ls=':', lw=3, c=pretty_colors[iM]) lit = 'cosmosinterp' t_blah = np.arange(8.0, 9.6, 0.1) sub.plot(t_blah, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_blah], ls='--', lw=3, c=pretty_colors[iM]) lit = 'cosmosfit' t_blah = np.arange(8.0, 9.6, 0.1) sub.plot(t_blah, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_blah], ls='-.', lw=3, c=pretty_colors[iM]) sub.set_xlim([t_arr.min(), t_arr.max()]) sub.legend(loc='upper left') sub.set_ylim([0., 0.2]) #plt.show() fig_file = ''.join(['figure/test/', 'dfqdt.png']) fig.savefig(fig_file, bbox_inches='tight') return None
def Evol_shamMstarSFR(t0, tfs, t_step=0.5, tQ0=None, MQ0=None, SFR0=None, q_Mshams=None, sf_Mshams=None, quiet=True, **kwargs): ''' Evolve SFR of the SF galaxies while quenching some of them at the same time. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by first guessing, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' dutycycle_prop = kwargs['dutycycle_prop'] tau_prop = kwargs['tau_prop'] fq_prop = kwargs['fq_prop'] sfms_prop = kwargs['sfms_prop'] massevol_prop = kwargs['massevol_prop'] fudge_prop = kwargs['fudge_prop'] tQ = tQ0.copy() Mq = MQ0.copy() t00 = t0.min() # earliest starting time (cosmic time of ancestor snapshot) t_evol = t_snap[np.abs(t_snap - np.max(tfs)).argmin() - 1:np.abs(t_snap - t00).argmin() + 1][::-1] qf = Fq() Fq_anal = qf.model # Mass bins M_bins = np.arange(6.0, 13., 0.5) M_mid = 0.5 * (M_bins[:-1] + M_bins[1:]) for i_t, tt in enumerate(t_evol[:-1]): if not quiet: print 't_cosmic = ', tt t_one_tstep = time.time() # M_sham of the quiescent galaxies at the closest snapshot. closest_t0_snap = np.abs(t_snap - tt).argmin() - 1 Msham_q0 = q_Mshams[:, closest_t0_snap].copy() Msham_sf0 = sf_Mshams[:, closest_t0_snap].copy() within = np.where((Msham_sf0 > 0.) & (t0 <= tt)) # tsnap_genesis <= t sf_within = np.where((Msham_sf0 > 0.) & (t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) M_t0_sample = np.concatenate( [Msham_sf0[within], Msham_q0[np.where(Msham_q0 > 0.)]]) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of quenching probability given by dFq/dt # P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep Ng0, dum = np.histogram(M_t0_sample, bins=M_bins) Nsf0, dum = np.histogram(Msham_sf0[sf_within], bins=M_bins) P_q_arr = Ng0 / Nsf0 * dFqdt( M_mid, tt + 0.5 * t_step, lit=fq_prop['name']) * t_step P_q_arr[np.where(Nsf0 == 0)] = 0. if not quiet: print 'M* : ', M_mid[-6:] print 'P_q : ', P_q_arr[-6:] Pq_M = interpolate.interp1d(M_mid, P_q_arr, kind='linear') P_q = Pq_M(Msham_sf0[sf_within]) q_ing0 = np.where(P_q > P_sf) Nqing0 = len(q_ing0[0]) # M_sham of the quiescent galaxies at the closest snapshot. closest_t_snap = np.abs(t_snap - t_evol[i_t + 1]).argmin() - 1 Msham_q1 = q_Mshams[:, closest_t_snap] Msham_sf1 = sf_Mshams[:, closest_t_snap] M_t1_sample = np.concatenate([Msham_sf1[within], Msham_q1]) # dPQ correction to the quenching to account for change in Ng(M*,t) Ngp, dum = np.histogram(M_t1_sample, bins=M_bins) dPq = (Ngp - Ng0) / Nsf0.astype('float') * Fq_anal( M_mid, z_of_t(tt + t_step), lit=fq_prop['name']) dPq[np.where(dPq < 0.)] = 0. dPq[np.where(Nsf0 == 0)] = 0. if not quiet: print 'dPq : ', dPq[-6:] dPq_M = interpolate.interp1d(M_mid, dPq, kind='linear') P_q += dPq_M(Msham_sf0[sf_within]) # fudge factor for P_Q fudge_factor = fudge_prop['slope'] * ( Msham_sf0[sf_within] - fudge_prop['fidmass']) + fudge_prop['offset'] fudge_factor[np.where(fudge_factor < 1.)] = 1. P_q *= fudge_factor q_ing = np.where(P_sf < P_q) if not quiet: print 'Initial guess ', Nqing0, ' final: ', len( q_ing[0]), ' SF gal out of ', Nsf_0, ' start quenching' print time.time() - t_one_tstep print '----------------' # assign them quenching times then actually evolve their stellar masses tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt + t_step, size=len(q_ing[0])) quenching = np.where((Mq == -999.) & (tQ < 999.) & (tQ > 0))[0] Nquenching = len(quenching) closest_tQ_index = np.abs( np.tile(t_snap, (Nquenching, 1)) - np.tile(tQ[quenching].reshape(Nquenching, 1), (1, len(t_snap)))).argmin(axis=1) - 1 Mq[quenching] = sf_Mshams[quenching, closest_tQ_index].copy() SFR_list = [] for tf in tfs: closest_tf_snap = np.abs(t_snap - tf).argmin() - 1 Msham_f = sf_Mshams[:, closest_tf_snap].copy() if closest_tf_snap == 0: before = Msham_f[:10] kwargs_sfr = { 't_init': t0.copy(), 't_q': tQ.copy(), 'M_q': Mq.copy(), 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop } SFR, M_qq = logSFR_M_t(Msham_f.copy(), tf, **kwargs_sfr) if closest_tf_snap == 0: print 'is same? ', np.array_equal(before, Msham_f[:10]) print tf, SFR.min(), SFR.max() # correct for the galaxies that were originally in the green valley. # ultimately doesn't make a difference in the outcome, but to be meticulous gv0 = np.where(tQ0 == 0.) # galaxies that were initiall quenching SFR[gv0] -= sfr_evol.AverageLogSFR_sfms(Mq[gv0], z_of_t(t0[gv0]), sfms_prop=sfms_prop) SFR[gv0] -= sfr_evol.DeltaLogSFR_quenching(tQ[gv0], t0[gv0], M_q=Mq[gv0], tau_prop=tau_prop) SFR[gv0] += SFR0[gv0].copy() SFR_list.append(SFR.copy()) del SFR return SFR_list, tQ.copy()
def MstarSFR_simul_evol_OBSOLETE(M0, t0, tf, t_step=0.2, ancestor_Mq=None, quiet=True, **kwargs): ''' Evolve stellar mass, SFR, and quench galaxies simultaneously. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' dutycycle_prop = kwargs['dutycycle_prop'] tau_prop = kwargs['tau_prop'] fq_prop = kwargs['fq_prop'] sfms_prop = kwargs['sfms_prop'] massevol_prop = kwargs['massevol_prop'] Mstar = M0 SFR = np.repeat(-999., len(M0)) tQ = np.repeat(999., len(M0)) Mq = np.repeat(-999., len(M0)) t00 = t0.min() # earliest starting time (cosmic time of ancestor snapshot) t_evol = np.arange(t00, tf + t_step, t_step) t_evol[-1] = tf qf = Fq() Fq_anal = qf.model for tt in t_evol: if not quiet: print 't_cosmic = ', tt within = np.where(t0 <= tt) # tsnap_genesis <= t sf_within = np.where((t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep t_offset = np.zeros(len(sf_within)) #t_offset = -2.7 * (Mstar[sf_within] - 11.5) #t_offset[np.where(t_offset < 0.)] = 0. fudge = 1. # fudge factor f_Ng = ( 1. / (1. - Fq_anal(Mstar[sf_within], z_of_t(tt), lit=fq_prop['name']))) whereinf = np.where(f_Ng == np.inf) P_q = fudge * f_Ng * dFqdt(Mstar[sf_within], tt + t_offset + 0.5 * t_step, lit=fq_prop['name']) * t_step P_q[whereinf] = 1.0 q_ing = np.where(P_q > P_sf) if not quiet: print 'P_q', P_q.min(), P_q.max(), P_q.mean() print 'Initial guess ', len( q_ing[0] ), ' SF galaxies out of ', Nsf_0, ' galaxies start quenching' # assign them quenching times tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt + t_step, size=len(q_ing[0])) kwargs_sfr = { 't_init': t0[within], 't_q': tQ[within], 'M_q': Mq[within], 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop, 'indices': within } M_evol, sfr_evol, Mq_evol = M_integrate(Mstar[within], tt, tt + t_step, massevol_prop=massevol_prop, kwargs_sfr=kwargs_sfr) Mstar[within] = M_evol SFR[within] = sfr_evol Mq[within] = Mq_evol if SFR.min() == -999.: raise ValueError return Mstar, SFR, tQ
def _Evol_MshamSFR(self, nsnap_d, allwill, sf_ancestor, q_ancestor): ''' Evolve SFR of the SF galaxies while quenching some of them at the same time. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by first guessing, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' tQ0 = self.ancestor.tQ[allwill[sf_ancestor]].copy() tQ = self.ancestor.tQ[allwill[sf_ancestor]].copy() Mq = self.ancestor.MQ[allwill[sf_ancestor]].copy() # earliest starting time (cosmic time of ancestor snapshot) t0 = self.ancestor.tsnap_genesis[allwill[sf_ancestor]].copy() t00 = t0.min() i_low = np.abs(t_snap-np.max(self.tsnap_descendants)).argmin()-1 i_high = np.abs(t_snap-t00).argmin()+1 t_evol = t_snap[i_low:i_high][::-1] qf = Fq() Fq_anal = qf.model M_bins = np.arange(5.5, 13., 0.5) M_mid = 0.5 * (M_bins[:-1] + M_bins[1:]) q_Mshams = self.MshamEvol[allwill[q_ancestor],:].copy() sf_Mshams = self.MshamEvol[allwill[sf_ancestor],:].copy() for i_t, tt in enumerate(t_evol[:-1]): t_step = t_evol[i_t+1] - tt if not self.quiet: print 't_cosmic = ', tt t_one_tstep = time.time() # M_sham of the quiescent galaxies at the closest snapshot. closest_t0_snap = np.abs(t_snap-tt).argmin() - 1 Msham_q0 = q_Mshams[:, closest_t0_snap] Msham_sf0 = sf_Mshams[:, closest_t0_snap] within = np.where((Msham_sf0 > 0.) & (t0 <= tt)) # tsnap_genesis <= t sf_within = np.where((Msham_sf0 > 0.) & (t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) M_t0_sample = np.concatenate( [Msham_sf0[within], Msham_q0[np.where(Msham_q0 > 0.)]] ) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of quenching probability given by dFq/dt # P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep Ng0, dum = np.histogram(M_t0_sample, bins=M_bins) Nsf0, dum = np.histogram(Msham_sf0[sf_within], bins=M_bins) P_q_arr = Ng0/Nsf0 * \ dFqdt(M_mid, tt + 0.5 * t_step, lit=self.fq_prop['name']) * t_step P_q_arr[np.where(Nsf0 == 0)] = 0. if not self.quiet: print 'M* : ', M_mid[-6:] print 'P_q : ', P_q_arr[-6:] Pq_M = interpolate.interp1d(M_mid, P_q_arr, kind='linear') P_q = Pq_M(Msham_sf0[sf_within]) q_ing0 = np.where(P_q > P_sf) Nqing0 = len(q_ing0[0]) # M_sham of the quiescent galaxies at the closest snapshot. closest_t1_snap = np.abs(t_snap - t_evol[i_t+1]).argmin() - 1 Msham_q1 = q_Mshams[:, closest_t1_snap] Msham_sf1 = sf_Mshams[:, closest_t1_snap] M_t1_sample = np.concatenate([Msham_sf1[within], Msham_q1]) # dPQ correction to the quenching to account for change in Ng(M*,t) Ngp, dum = np.histogram(M_t1_sample, bins=M_bins) dPq = (Ngp - Ng0)/Nsf0.astype('float') * \ Fq_anal(M_mid, z_of_t(tt + t_step), lit=self.fq_prop['name']) dPq[np.where(dPq < 0.)] = 0. dPq[np.where(Nsf0 == 0)] = 0. if not self.quiet: print 'dPq : ', dPq[-6:] dPq_M = interpolate.interp1d(M_mid, dPq, kind='linear') P_q += dPq_M(Msham_sf0[sf_within]) # fudge factor for P_Q fudge_factor = self.fudge_prop['slope'] * (Msham_sf0[sf_within] - self.fudge_prop['fidmass']) + self.fudge_prop['offset'] fudge_factor[np.where(fudge_factor < 1.)] = 1. P_q *= fudge_factor if self.printPQ: # store quenching probabilities Pq_out = {} Pq_out['mass'] = M_mid fPQ = self.fudge_prop['slope'] * (M_mid - self.fudge_prop['fidmass']) + \ self.fudge_prop['offset'] fPQ[np.where(fPQ < 1.)] = 1. Pq_out['Pq'] = fPQ * (P_q_arr + dPq) Pq_out['Pq_fid'] = P_q_arr + dPq if self.PQs is None: self.PQs = {} self.PQs[str(z_of_t(tt))] = Pq_out q_ing = np.where(P_sf < P_q) if not self.quiet: print 'Initial guess ', Nqing0, ' final: ', len(q_ing[0]), ' SF gal out of ', Nsf_0, ' start quenching' print time.time() - t_one_tstep print '----------------' # assign them quenching times then actually evolve their stellar masses tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt+t_step, size=len(q_ing[0])) quenching = np.where((Mq == -999.) & (tQ < 999.) & (tQ > 0.))[0] Nquenching = len(quenching) closest_tQ_index = np.abs( np.tile(t_snap, (Nquenching, 1)) - np.tile(tQ[quenching].reshape(Nquenching, 1), (1, len(t_snap))) ).argmin(axis=1) - 1 Mq[quenching] = sf_Mshams[quenching, closest_tQ_index] z0 = z_of_t(t0) # initial redshift gv0 = np.where(tQ0 == 0.)[0] # galaxies that were initial quenching t_f = self.descendant_dict[str(nsnap_d)].t_cosmic z_f = self.descendant_dict[str(nsnap_d)].zsnap closest_tf_snap = np.abs(t_snap - t_f).argmin() - 1 Msham_f = sf_Mshams[:, closest_tf_snap].copy() q_ed = np.where(t_f > tQ) tmp_M = Msham_f.copy() tmp_M[q_ed] = Mq[q_ed].copy() # log(SFR)_SFMS evolution from t0 logsfr_sfms = sfr_evol.AverageLogSFR_sfms(tmp_M, z0, sfms_prop=self.sfms_prop) + \ sfr_evol.DeltaLogSFR_sfms(z0, z_f, sfms_prop=self.sfms_prop) # log(SFR)_duty cycle evolution from t0 to tQ logsfr_sfduty = sfr_evol.DeltaLogSFR_dutycycle( t0, t_f, t_q=tQ, dutycycle_prop=self.dutycycle_prop ) logsfr_quench = sfr_evol.DeltaLogSFR_quenching( tQ, t_f, M_q=Mq, tau_prop=self.tau_prop) logSFR = logsfr_sfms + logsfr_quench + logsfr_sfduty # correct for the galaxies that were originally in the green valley. # ultimately doesn't make a difference in the outcome, but to be meticulous logSFR[gv0] = (self.ancestor.sfr[allwill[sf_ancestor]])[gv0] + \ sfr_evol.DeltaLogSFR_sfms(z0[gv0], z_f, sfms_prop=self.sfms_prop) + \ sfr_evol.DeltaLogSFR_quenching( np.repeat(self.ancestor.t_cosmic, len(gv0)), t_f, M_q=Mq[gv0], tau_prop=self.tau_prop) return logSFR, tQ
def Evol_IntegMstarSFR(M0, t0, tf, t_step=0.5, tQ0=None, MQ0=None, ancestor_Mq=None, **kwargs): ''' Evolve stellar mass and SFR of the SF galaxies while quenching some of them at the same time. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by first guessing, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' dutycycle_prop = kwargs['dutycycle_prop'] tau_prop = kwargs['tau_prop'] fq_prop = kwargs['fq_prop'] sfms_prop = kwargs['sfms_prop'] massevol_prop = kwargs['massevol_prop'] fudge_prop = kwargs['fudge_prop'] Mstar = M0 SFR = np.repeat(-999., len(M0)) tQ = tQ0 Mq = MQ0 t00 = t0.min() # earliest starting time (cosmic time of ancestor snapshot) t_evol = np.arange(t00, tf + t_step, t_step) t_evol[-1] = tf qf = Fq() Fq_anal = qf.model # Mass bins M_bins = np.arange(6.0, 13., 0.5) M_mid = 0.5 * (M_bins[:-1] + M_bins[1:]) for tt in t_evol: print 't_cosmic = ', tt t_one_tstep = time.time() within = np.where(t0 <= tt) # tsnap_genesis <= t sf_within = np.where((t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) # M_sham of the quiescent galaxies at the closest snapshot. closest_t0_snap = np.abs(t_snap - tt).argmin() - 1 Msham_q0 = ancestor_Mq[:, closest_t0_snap] M_t0_sample = np.concatenate( [Mstar[within], Msham_q0[np.where(Msham_q0 > 0.)]]) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of quenching probability given by dFq/dt # P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep Ng0, dum = np.histogram(M_t0_sample, bins=M_bins) Nsf0, dum = np.histogram(Mstar[sf_within], bins=M_bins) P_q_arr = Ng0 / Nsf0 * dFqdt( M_mid, tt + 0.5 * t_step, lit=fq_prop['name']) * t_step P_q_arr[np.where(Nsf0 == 0)] = 0. print 'M* : ', M_mid[-6:] print 'P_q : ', P_q_arr[-6:] Pq_M = interpolate.interp1d(M_mid, P_q_arr, kind='linear') P_q = Pq_M(Mstar[sf_within]) q_ing = np.where(P_q > P_sf) Nqing0 = len(q_ing[0]) # assign them quenching times tQ_tmp = tQ tQ_tmp[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt + t_step, size=Nqing0) kwargs_sfr = { 't_init': t0[within], 't_q': tQ_tmp[within], 'M_q': Mq[within], 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop, 'indices': within } M_evol, sfr_evol, Mq_evol = M_integrate(Mstar[within], tt, tt + t_step, massevol_prop=massevol_prop, kwargs_sfr=kwargs_sfr) # M_sham of the quiescent galaxies at the closest snapshot. closest_t_snap = np.abs(t_snap - tt - t_step).argmin() - 1 Msham_qf = ancestor_Mq[:, closest_t_snap] M_tf_sample = np.concatenate([M_evol, Msham_qf]) # dPQ correction to the quenching to account for change in Ng(M*,t) Ngp, dum = np.histogram(M_tf_sample, bins=M_bins) dPq = (Ngp - Ng0) / Nsf0.astype('float') * Fq_anal( M_mid, z_of_t(tt + t_step), lit=fq_prop['name']) dPq[np.where(dPq < 0.)] = 0. dPq[np.where(Nsf0 == 0)] = 0. print 'dPq : ', dPq[-6:] dPq_M = interpolate.interp1d(M_mid, dPq, kind='linear') P_q += dPq_M(Mstar[sf_within]) fudge_factor = fudge_prop['slope'] * ( Mstar[sf_within] - fudge_prop['fidmass']) + fudge_prop['offset'] fudge_factor[np.where(fudge_factor < 1.)] = 1. P_q *= fudge_factor q_ing = np.where(P_sf < P_q) print 'Initial guess ', Nqing0, ' after correction: ', len( q_ing[0] ), ' SF galaxies out of ', Nsf_0, ' galaxies start quenching' print time.time() - t_one_tstep print '----------------' # assign them quenching times then actually evolve their stellar masses tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt + t_step, size=len(q_ing[0])) kwargs_sfr = { 't_init': t0[within], 't_q': tQ[within], 'M_q': Mq[within], 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop, 'indices': within } M_evol, sfr_evol, Mq_evol = M_integrate(Mstar[within], tt, tt + t_step, massevol_prop=massevol_prop, kwargs_sfr=kwargs_sfr) Mstar[within] = M_evol SFR[within] = sfr_evol Mq[within] = Mq_evol if SFR.min() == -999.: raise ValueError return Mstar, SFR, tQ
def MstarSFR_simul_evol_OBSOLETE(M0, t0, tf, t_step=0.2, ancestor_Mq=None, quiet=True, **kwargs): ''' Evolve stellar mass, SFR, and quench galaxies simultaneously. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' dutycycle_prop = kwargs['dutycycle_prop'] tau_prop = kwargs['tau_prop'] fq_prop = kwargs['fq_prop'] sfms_prop = kwargs['sfms_prop'] massevol_prop = kwargs['massevol_prop'] Mstar = M0 SFR = np.repeat(-999., len(M0)) tQ = np.repeat(999., len(M0)) Mq = np.repeat(-999., len(M0)) t00 = t0.min() # earliest starting time (cosmic time of ancestor snapshot) t_evol = np.arange(t00, tf+t_step, t_step) t_evol[-1] = tf qf = Fq() Fq_anal = qf.model for tt in t_evol: if not quiet: print 't_cosmic = ', tt within = np.where(t0 <= tt) # tsnap_genesis <= t sf_within = np.where((t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep t_offset = np.zeros(len(sf_within)) #t_offset = -2.7 * (Mstar[sf_within] - 11.5) #t_offset[np.where(t_offset < 0.)] = 0. fudge = 1. # fudge factor f_Ng = (1./(1.- Fq_anal(Mstar[sf_within], z_of_t(tt), lit=fq_prop['name']))) whereinf = np.where(f_Ng == np.inf) P_q = fudge * f_Ng * dFqdt(Mstar[sf_within], tt + t_offset + 0.5 * t_step, lit=fq_prop['name']) * t_step P_q[whereinf] = 1.0 q_ing = np.where(P_q > P_sf) if not quiet: print 'P_q', P_q.min(), P_q.max(), P_q.mean() print 'Initial guess ', len(q_ing[0]), ' SF galaxies out of ', Nsf_0, ' galaxies start quenching' # assign them quenching times tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt+t_step, size=len(q_ing[0])) kwargs_sfr = { 't_init': t0[within], 't_q': tQ[within], 'M_q': Mq[within], 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop, 'indices': within } M_evol, sfr_evol, Mq_evol = M_integrate(Mstar[within], tt, tt+t_step, massevol_prop=massevol_prop, kwargs_sfr=kwargs_sfr) Mstar[within] = M_evol SFR[within] = sfr_evol Mq[within] = Mq_evol if SFR.min() == -999.: raise ValueError return Mstar, SFR, tQ
def Evol_shamMstarSFR(t0, tfs, t_step=0.5, tQ0=None, MQ0=None, SFR0=None, q_Mshams=None, sf_Mshams=None, quiet=True, **kwargs): ''' Evolve SFR of the SF galaxies while quenching some of them at the same time. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by first guessing, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' dutycycle_prop = kwargs['dutycycle_prop'] tau_prop = kwargs['tau_prop'] fq_prop = kwargs['fq_prop'] sfms_prop = kwargs['sfms_prop'] massevol_prop = kwargs['massevol_prop'] fudge_prop = kwargs['fudge_prop'] tQ = tQ0.copy() Mq = MQ0.copy() t00 = t0.min() # earliest starting time (cosmic time of ancestor snapshot) t_evol = t_snap[np.abs(t_snap-np.max(tfs)).argmin()-1: np.abs(t_snap-t00).argmin()+1][::-1] qf = Fq() Fq_anal = qf.model # Mass bins M_bins = np.arange(6.0, 13., 0.5) M_mid = 0.5 * (M_bins[:-1] + M_bins[1:]) for i_t, tt in enumerate(t_evol[:-1]): if not quiet: print 't_cosmic = ', tt t_one_tstep = time.time() # M_sham of the quiescent galaxies at the closest snapshot. closest_t0_snap = np.abs(t_snap-tt).argmin() - 1 Msham_q0 = q_Mshams[:, closest_t0_snap].copy() Msham_sf0 = sf_Mshams[:, closest_t0_snap].copy() within = np.where((Msham_sf0 > 0.) & (t0 <= tt)) # tsnap_genesis <= t sf_within = np.where((Msham_sf0 > 0.) & (t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) M_t0_sample = np.concatenate([Msham_sf0[within], Msham_q0[np.where(Msham_q0 > 0.)]]) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of quenching probability given by dFq/dt # P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep Ng0, dum = np.histogram(M_t0_sample, bins=M_bins) Nsf0, dum = np.histogram(Msham_sf0[sf_within], bins=M_bins) P_q_arr = Ng0/Nsf0 * dFqdt(M_mid, tt + 0.5 * t_step, lit=fq_prop['name']) * t_step P_q_arr[np.where(Nsf0 == 0)] = 0. if not quiet: print 'M* : ', M_mid[-6:] print 'P_q : ', P_q_arr[-6:] Pq_M = interpolate.interp1d(M_mid, P_q_arr, kind='linear') P_q = Pq_M(Msham_sf0[sf_within]) q_ing0 = np.where(P_q > P_sf) Nqing0 = len(q_ing0[0]) # M_sham of the quiescent galaxies at the closest snapshot. closest_t_snap = np.abs(t_snap - t_evol[i_t+1]).argmin() - 1 Msham_q1 = q_Mshams[:, closest_t_snap] Msham_sf1 = sf_Mshams[:, closest_t_snap] M_t1_sample = np.concatenate([Msham_sf1[within], Msham_q1]) # dPQ correction to the quenching to account for change in Ng(M*,t) Ngp, dum = np.histogram(M_t1_sample, bins=M_bins) dPq = (Ngp - Ng0)/Nsf0.astype('float') * Fq_anal(M_mid, z_of_t(tt + t_step), lit=fq_prop['name']) dPq[np.where(dPq < 0.)] = 0. dPq[np.where(Nsf0 == 0)] = 0. if not quiet: print 'dPq : ', dPq[-6:] dPq_M = interpolate.interp1d(M_mid, dPq, kind='linear') P_q += dPq_M(Msham_sf0[sf_within]) # fudge factor for P_Q fudge_factor = fudge_prop['slope'] * (Msham_sf0[sf_within] - fudge_prop['fidmass']) + fudge_prop['offset'] fudge_factor[np.where(fudge_factor < 1.)] = 1. P_q *= fudge_factor q_ing = np.where(P_sf < P_q) if not quiet: print 'Initial guess ', Nqing0, ' final: ', len(q_ing[0]), ' SF gal out of ', Nsf_0, ' start quenching' print time.time() - t_one_tstep print '----------------' # assign them quenching times then actually evolve their stellar masses tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt+t_step, size=len(q_ing[0])) quenching = np.where((Mq == -999.) & (tQ < 999.) & (tQ > 0))[0] Nquenching = len(quenching) closest_tQ_index = np.abs( np.tile(t_snap, (Nquenching,1)) - np.tile(tQ[quenching].reshape(Nquenching,1), (1, len(t_snap))) ).argmin(axis=1) - 1 Mq[quenching] = sf_Mshams[quenching, closest_tQ_index].copy() SFR_list = [] for tf in tfs: closest_tf_snap = np.abs(t_snap-tf).argmin() - 1 Msham_f = sf_Mshams[:, closest_tf_snap].copy() if closest_tf_snap == 0: before = Msham_f[:10] kwargs_sfr = { 't_init': t0.copy(), 't_q': tQ.copy(), 'M_q': Mq.copy(), 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop } SFR, M_qq = logSFR_M_t(Msham_f.copy(), tf, **kwargs_sfr) if closest_tf_snap == 0: print 'is same? ', np.array_equal(before, Msham_f[:10]) print tf, SFR.min(), SFR.max() # correct for the galaxies that were originally in the green valley. # ultimately doesn't make a difference in the outcome, but to be meticulous gv0 = np.where(tQ0 == 0.) # galaxies that were initiall quenching SFR[gv0] -= sfr_evol.AverageLogSFR_sfms(Mq[gv0], z_of_t(t0[gv0]), sfms_prop=sfms_prop) SFR[gv0] -= sfr_evol.DeltaLogSFR_quenching(tQ[gv0], t0[gv0], M_q=Mq[gv0], tau_prop=tau_prop) SFR[gv0] += SFR0[gv0].copy() SFR_list.append(SFR.copy()) del SFR return SFR_list, tQ.copy()
def Evol_IntegMstarSFR(M0, t0, tf, t_step=0.5, tQ0=None, MQ0=None, ancestor_Mq=None, **kwargs): ''' Evolve stellar mass and SFR of the SF galaxies while quenching some of them at the same time. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by first guessing, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' dutycycle_prop = kwargs['dutycycle_prop'] tau_prop = kwargs['tau_prop'] fq_prop = kwargs['fq_prop'] sfms_prop = kwargs['sfms_prop'] massevol_prop = kwargs['massevol_prop'] fudge_prop = kwargs['fudge_prop'] Mstar = M0 SFR = np.repeat(-999., len(M0)) tQ = tQ0 Mq = MQ0 t00 = t0.min() # earliest starting time (cosmic time of ancestor snapshot) t_evol = np.arange(t00, tf+t_step, t_step) t_evol[-1] = tf qf = Fq() Fq_anal = qf.model # Mass bins M_bins = np.arange(6.0, 13., 0.5) M_mid = 0.5 * (M_bins[:-1] + M_bins[1:]) for tt in t_evol: print 't_cosmic = ', tt t_one_tstep = time.time() within = np.where(t0 <= tt) # tsnap_genesis <= t sf_within = np.where((t0 <= tt) & (tQ == 999.))[0] # Not quenching SF galaxies Nsf_0 = len(sf_within) # M_sham of the quiescent galaxies at the closest snapshot. closest_t0_snap = np.abs(t_snap-tt).argmin() - 1 Msham_q0 = ancestor_Mq[:, closest_t0_snap] M_t0_sample = np.concatenate([Mstar[within], Msham_q0[np.where(Msham_q0 > 0.)]]) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of quenching probability given by dFq/dt # P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep Ng0, dum = np.histogram(M_t0_sample, bins=M_bins) Nsf0, dum = np.histogram(Mstar[sf_within], bins=M_bins) P_q_arr = Ng0/Nsf0 * dFqdt(M_mid, tt + 0.5 * t_step, lit=fq_prop['name']) * t_step P_q_arr[np.where(Nsf0 == 0)] = 0. print 'M* : ', M_mid[-6:] print 'P_q : ', P_q_arr[-6:] Pq_M = interpolate.interp1d(M_mid, P_q_arr, kind='linear') P_q = Pq_M(Mstar[sf_within]) q_ing = np.where(P_q > P_sf) Nqing0 = len(q_ing[0]) # assign them quenching times tQ_tmp = tQ tQ_tmp[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt+t_step, size=Nqing0) kwargs_sfr = { 't_init': t0[within], 't_q': tQ_tmp[within], 'M_q': Mq[within], 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop, 'indices': within } M_evol, sfr_evol, Mq_evol = M_integrate(Mstar[within], tt, tt+t_step, massevol_prop=massevol_prop, kwargs_sfr=kwargs_sfr) # M_sham of the quiescent galaxies at the closest snapshot. closest_t_snap = np.abs(t_snap - tt - t_step).argmin() - 1 Msham_qf = ancestor_Mq[:, closest_t_snap] M_tf_sample = np.concatenate([M_evol, Msham_qf]) # dPQ correction to the quenching to account for change in Ng(M*,t) Ngp, dum = np.histogram(M_tf_sample, bins=M_bins) dPq = (Ngp - Ng0)/Nsf0.astype('float') * Fq_anal(M_mid, z_of_t(tt + t_step), lit=fq_prop['name']) dPq[np.where(dPq < 0.)] = 0. dPq[np.where(Nsf0 == 0)] = 0. print 'dPq : ', dPq[-6:] dPq_M = interpolate.interp1d(M_mid, dPq, kind='linear') P_q += dPq_M(Mstar[sf_within]) fudge_factor = fudge_prop['slope'] * (Mstar[sf_within] - fudge_prop['fidmass']) + fudge_prop['offset'] fudge_factor[np.where(fudge_factor < 1.)] = 1. P_q *= fudge_factor q_ing = np.where(P_sf < P_q) print 'Initial guess ', Nqing0, ' after correction: ', len(q_ing[0]), ' SF galaxies out of ', Nsf_0, ' galaxies start quenching' print time.time() - t_one_tstep print '----------------' # assign them quenching times then actually evolve their stellar masses tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt+t_step, size=len(q_ing[0])) kwargs_sfr = { 't_init': t0[within], 't_q': tQ[within], 'M_q': Mq[within], 'dutycycle_prop': dutycycle_prop, 'tau_prop': tau_prop, 'sfms_prop': sfms_prop, 'indices': within } M_evol, sfr_evol, Mq_evol = M_integrate(Mstar[within], tt, tt+t_step, massevol_prop=massevol_prop, kwargs_sfr=kwargs_sfr) Mstar[within] = M_evol SFR[within] = sfr_evol Mq[within] = Mq_evol if SFR.min() == -999.: raise ValueError return Mstar, SFR, tQ
def Plot_dFqdt(): ''' Plot the evolution of the derivative of the quiescent fraction as a function of M* and t for different parameterized models of f_Q d f_Q / d t ''' prettyplot() pretty_colors = prettycolors() dt = 0.01 M_arr = np.arange(9.0, 12.5, 0.5) t_arr = np.arange(6.0, 13.0, 0.2) fig = plt.figure() sub = fig.add_subplot(111) for iM, Mstar in enumerate(M_arr): qf = gp.Fq() lit = 'wetzelsmooth' sub.plot( t_arr, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_arr], ls='-', lw=3, c=pretty_colors[iM], label=r"$\mathtt{M}_* = "+str(round(Mstar,2))+"$" ) lit = 'wetzel' sub.plot( t_arr, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_arr], ls=':', lw=3, c=pretty_colors[iM] ) lit = 'cosmosinterp' t_blah = np.arange(8.0, 9.6, 0.1) sub.plot( t_blah, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_blah], ls='--', lw=3, c=pretty_colors[iM] ) lit = 'cosmosfit' t_blah = np.arange(8.0, 9.6, 0.1) sub.plot( t_blah, [gp.dFqdt(Mstar, tt, lit=lit, dt=dt) for tt in t_blah], ls='-.', lw=3, c=pretty_colors[iM] ) sub.set_xlim([t_arr.min(), t_arr.max()]) sub.legend(loc='upper left') sub.set_ylim([0., 0.2]) #plt.show() fig_file = ''.join(['figure/test/', 'dfqdt.png']) fig.savefig(fig_file, bbox_inches='tight') return None
def _Evol_MshamSFR(self, nsnap_d, allwill, sf_ancestor, q_ancestor): ''' Evolve SFR of the SF galaxies while quenching some of them at the same time. Notes ----- * SF galaxies are quenched based on the following prescription: The number of galaxies that start quenching between t0 and t0+tstep is determined by first guessing, N_quenching = N_sf(t0) * (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep ''' tQ0 = self.ancestor.tQ[allwill[sf_ancestor]].copy() tQ = self.ancestor.tQ[allwill[sf_ancestor]].copy() Mq = self.ancestor.MQ[allwill[sf_ancestor]].copy() # earliest starting time (cosmic time of ancestor snapshot) t0 = self.ancestor.tsnap_genesis[allwill[sf_ancestor]].copy() t00 = t0.min() i_low = np.abs(t_snap - np.max(self.tsnap_descendants)).argmin() - 1 i_high = np.abs(t_snap - t00).argmin() + 1 t_evol = t_snap[i_low:i_high][::-1] qf = Fq() Fq_anal = qf.model M_bins = np.arange(5.5, 13., 0.5) M_mid = 0.5 * (M_bins[:-1] + M_bins[1:]) q_Mshams = self.MshamEvol[allwill[q_ancestor], :].copy() sf_Mshams = self.MshamEvol[allwill[sf_ancestor], :].copy() for i_t, tt in enumerate(t_evol[:-1]): t_step = t_evol[i_t + 1] - tt if not self.quiet: print 't_cosmic = ', tt t_one_tstep = time.time() # M_sham of the quiescent galaxies at the closest snapshot. closest_t0_snap = np.abs(t_snap - tt).argmin() - 1 Msham_q0 = q_Mshams[:, closest_t0_snap] Msham_sf0 = sf_Mshams[:, closest_t0_snap] within = np.where( (Msham_sf0 > 0.) & (t0 <= tt)) # tsnap_genesis <= t sf_within = np.where((Msham_sf0 > 0.) & (t0 <= tt) & (tQ == 999.))[ 0] # Not quenching SF galaxies Nsf_0 = len(sf_within) M_t0_sample = np.concatenate( [Msham_sf0[within], Msham_q0[np.where(Msham_q0 > 0.)]]) P_sf = np.random.uniform(0., 1., Nsf_0) # Initial estimate of quenching probability given by dFq/dt # P_q = (1/( 1 - fQ(t0) )) * dFq/dt(t0 + 0.5 tstep) * tstep Ng0, dum = np.histogram(M_t0_sample, bins=M_bins) Nsf0, dum = np.histogram(Msham_sf0[sf_within], bins=M_bins) P_q_arr = Ng0/Nsf0 * \ dFqdt(M_mid, tt + 0.5 * t_step, lit=self.fq_prop['name']) * t_step P_q_arr[np.where(Nsf0 == 0)] = 0. if not self.quiet: print 'M* : ', M_mid[-6:] print 'P_q : ', P_q_arr[-6:] Pq_M = interpolate.interp1d(M_mid, P_q_arr, kind='linear') P_q = Pq_M(Msham_sf0[sf_within]) q_ing0 = np.where(P_q > P_sf) Nqing0 = len(q_ing0[0]) # M_sham of the quiescent galaxies at the closest snapshot. closest_t1_snap = np.abs(t_snap - t_evol[i_t + 1]).argmin() - 1 Msham_q1 = q_Mshams[:, closest_t1_snap] Msham_sf1 = sf_Mshams[:, closest_t1_snap] M_t1_sample = np.concatenate([Msham_sf1[within], Msham_q1]) # dPQ correction to the quenching to account for change in Ng(M*,t) Ngp, dum = np.histogram(M_t1_sample, bins=M_bins) dPq = (Ngp - Ng0)/Nsf0.astype('float') * \ Fq_anal(M_mid, z_of_t(tt + t_step), lit=self.fq_prop['name']) dPq[np.where(dPq < 0.)] = 0. dPq[np.where(Nsf0 == 0)] = 0. if not self.quiet: print 'dPq : ', dPq[-6:] dPq_M = interpolate.interp1d(M_mid, dPq, kind='linear') P_q += dPq_M(Msham_sf0[sf_within]) # fudge factor for P_Q fudge_factor = self.fudge_prop['slope'] * ( Msham_sf0[sf_within] - self.fudge_prop['fidmass']) + self.fudge_prop['offset'] fudge_factor[np.where(fudge_factor < 1.)] = 1. P_q *= fudge_factor if self.printPQ: # store quenching probabilities Pq_out = {} Pq_out['mass'] = M_mid fPQ = self.fudge_prop['slope'] * (M_mid - self.fudge_prop['fidmass']) + \ self.fudge_prop['offset'] fPQ[np.where(fPQ < 1.)] = 1. Pq_out['Pq'] = fPQ * (P_q_arr + dPq) Pq_out['Pq_fid'] = P_q_arr + dPq if self.PQs is None: self.PQs = {} self.PQs[str(z_of_t(tt))] = Pq_out q_ing = np.where(P_sf < P_q) if not self.quiet: print 'Initial guess ', Nqing0, ' final: ', len( q_ing[0]), ' SF gal out of ', Nsf_0, ' start quenching' print time.time() - t_one_tstep print '----------------' # assign them quenching times then actually evolve their stellar masses tQ[sf_within[q_ing]] = np.random.uniform(low=tt, high=tt + t_step, size=len(q_ing[0])) quenching = np.where((Mq == -999.) & (tQ < 999.) & (tQ > 0.))[0] Nquenching = len(quenching) closest_tQ_index = np.abs( np.tile(t_snap, (Nquenching, 1)) - np.tile(tQ[quenching].reshape(Nquenching, 1), (1, len(t_snap)))).argmin(axis=1) - 1 Mq[quenching] = sf_Mshams[quenching, closest_tQ_index] z0 = z_of_t(t0) # initial redshift gv0 = np.where(tQ0 == 0.)[0] # galaxies that were initial quenching t_f = self.descendant_dict[str(nsnap_d)].t_cosmic z_f = self.descendant_dict[str(nsnap_d)].zsnap closest_tf_snap = np.abs(t_snap - t_f).argmin() - 1 Msham_f = sf_Mshams[:, closest_tf_snap].copy() q_ed = np.where(t_f > tQ) tmp_M = Msham_f.copy() tmp_M[q_ed] = Mq[q_ed].copy() # log(SFR)_SFMS evolution from t0 logsfr_sfms = sfr_evol.AverageLogSFR_sfms(tmp_M, z0, sfms_prop=self.sfms_prop) + \ sfr_evol.DeltaLogSFR_sfms(z0, z_f, sfms_prop=self.sfms_prop) # log(SFR)_duty cycle evolution from t0 to tQ logsfr_sfduty = sfr_evol.DeltaLogSFR_dutycycle( t0, t_f, t_q=tQ, dutycycle_prop=self.dutycycle_prop) logsfr_quench = sfr_evol.DeltaLogSFR_quenching(tQ, t_f, M_q=Mq, tau_prop=self.tau_prop) logSFR = logsfr_sfms + logsfr_quench + logsfr_sfduty # correct for the galaxies that were originally in the green valley. # ultimately doesn't make a difference in the outcome, but to be meticulous logSFR[gv0] = (self.ancestor.sfr[allwill[sf_ancestor]])[gv0] + \ sfr_evol.DeltaLogSFR_sfms(z0[gv0], z_f, sfms_prop=self.sfms_prop) + \ sfr_evol.DeltaLogSFR_quenching( np.repeat(self.ancestor.t_cosmic, len(gv0)), t_f, M_q=Mq[gv0], tau_prop=self.tau_prop) return logSFR, tQ