Beispiel #1
0
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
Beispiel #2
0
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()
Beispiel #3
0
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
Beispiel #5
0
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
Beispiel #10
0
    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