Ejemplo n.º 1
0
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]
Ejemplo n.º 2
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