def logSFR_M_t(logmass, t_input, t_init=None, t_q=None, M_q=None, dutycycle_prop=None, tau_prop=None, sfms_prop=None, indices=None): ''' log(SFR) as a function of M* and t_cosmic. Notes ----- * kwarg indices is for dutycycle_prop ''' # SFR evolution based on solving an ODE of SFR logsfr_time = time.time() z_init = z_of_t(t_init) # initial redshift # update quenched M* ( this is the stellar mass roughly at the time of quenching) just_quenched = np.where((t_input > t_q) & (M_q == -999.)) if len(just_quenched[0]) > 0: M_q[just_quenched] = logmass[just_quenched] # average SFR of SFMS at M* and z_init quenched = np.where(t_input > t_q) tmp_M = logmass.copy() tmp_M[quenched] = M_q[quenched] avglogsfr = sfr_evol.AverageLogSFR_sfms(tmp_M, z_init, sfms_prop=sfms_prop) # log(SFR)_SFMS evolutionfrom t0 logsfr_sfms = sfr_evol.DeltaLogSFR_sfms(z_init, z_of_t(t_input), sfms_prop=sfms_prop) # log(SFR)_duty cycle evolution from t0 to tQ logsfr_sfduty = sfr_evol.DeltaLogSFR_dutycycle( t_init, t_input, t_q=t_q, dutycycle_prop=dutycycle_prop, indices=indices) logsfr_quench = sfr_evol.DeltaLogSFR_quenching(t_q, t_input, M_q=M_q, tau_prop=tau_prop) logsfr_tot = avglogsfr + logsfr_sfms + logsfr_sfduty + logsfr_quench return [logsfr_tot, M_q]
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