Esempio 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]
def AssignCenSFR(tf, abcrun=None, prior_name=None):
    ''' Given SGPop object, assign SFRs at infalling time based on the 
    median ABC Run of the Central Galaxy evolution 
    '''
    if abcrun is None:
        raise ValueError
    if prior_name is None:
        raise ValueError
    abcinh = ABCInherit(tf, abcrun=abcrun, prior_name=prior_name)

    inh = abcinh.PickleLoad('all')

    sg_obj = SGPop()
    sg_obj.ImportSubhalo(subhalo_prop=abcinh.sim_kwargs['subhalo_prop'])
    sg_obj.sfms_prop = abcinh.sim_kwargs['sfr_prop']['sfms']
    sg_obj.abcrun = abcrun
    sg_obj.prior_name = prior_name

    sg_obj.first_infall_sfr = np.repeat(-999., len(sg_obj.first_infall_nsnap))
    for i_snap in range(2, abcinh.sim_kwargs['nsnap_ancestor']):
        infall_now = np.where((sg_obj.first_infall_nsnap == i_snap)
                              & (sg_obj.first_infall_mass > 0.))[0]

        # satellite galaxies that end up massive but have 0. infalling mass
        #massive_infall_zero =np.where(
        #        (sg_obj.first_infall_mass[infall_now] == 0.) &
        #        (sg_obj.mass[infall_now] > 0.)
        #        )

        des_snap = inh[str(i_snap)]
        m_bins = np.arange(des_snap.mass.min() - 0.1,
                           des_snap.mass.max() + 0.2, 0.2)

        for i_m in range(len(m_bins) - 1):
            in_massbin = np.where((des_snap.mass >= m_bins[i_m])
                                  & (des_snap.mass < m_bins[i_m + 1]))

            infall_massbin = np.where(
                (sg_obj.first_infall_nsnap == i_snap)
                & (sg_obj.first_infall_mass >= m_bins[i_m])
                & (sg_obj.first_infall_mass < m_bins[i_m + 1]))

            if len(infall_massbin[0]) == 0:
                continue

            des_ssfrs = des_snap.sfr[in_massbin] - des_snap.mass[in_massbin]
            pdf_ssfr_i, ssfr_binedges = np.histogram(des_ssfrs,
                                                     bins=np.arange(
                                                         -13.0, -8.0, 0.2),
                                                     normed=True)

            cdf_ssfr_i = np.zeros(len(ssfr_binedges))
            cdf_ssfr_i[1:] = np.cumsum(pdf_ssfr_i * np.diff(ssfr_binedges))
            cdf_interp = interp1d(cdf_ssfr_i, ssfr_binedges, kind='linear')

            rand_i = np.random.uniform(size=len(infall_massbin[0]))
            sg_obj.first_infall_sfr[infall_massbin] = cdf_interp(rand_i) + \
                    sg_obj.first_infall_mass[infall_massbin]

    # assign SFR to galaxies that fell in before nsnap = 15
    old_infall = np.where(
        (sg_obj.first_infall_nsnap >= abcinh.sim_kwargs['nsnap_ancestor'])
        & (sg_obj.first_infall_mass > 0.))[0]

    Pq = np.random.uniform(size=len(old_infall))
    qfrac = Fq()
    fq_oldinfall = qfrac.model(sg_obj.first_infall_mass[old_infall],
                               sg_obj.first_infall_z[old_infall],
                               lit='wetzel')

    # star-forming
    sfing = np.where(Pq > fq_oldinfall)
    sig_sfms = sfr_evol.ScatterLogSFR_sfms(
        sg_obj.first_infall_mass[old_infall[sfing]],
        sg_obj.first_infall_z[old_infall[sfing]],
        sfms_prop=sg_obj.sfms_prop)
    mu_sfms = sfr_evol.AverageLogSFR_sfms(
        sg_obj.first_infall_mass[old_infall[sfing]],
        sg_obj.first_infall_z[old_infall[sfing]],
        sfms_prop=sg_obj.sfms_prop)
    sg_obj.first_infall_sfr[old_infall[sfing]] = mu_sfms + np.random.randn(
        len(sfing[0])) * sig_sfms

    # quiescent
    qed = np.where(Pq <= fq_oldinfall)
    mu_ssfr_q = sfr_evol.AverageLogSSFR_q_peak(
        sg_obj.first_infall_mass[old_infall[qed]])
    sig_ssfr_q = sfr_evol.ScatterLogSSFR_q_peak(
        sg_obj.first_infall_mass[old_infall[qed]])

    ssfr_q = mu_ssfr_q + np.random.randn(len(qed[0])) * sig_ssfr_q

    sg_obj.first_infall_sfr[
        old_infall[qed]] = ssfr_q + sg_obj.first_infall_mass[old_infall[qed]]

    return sg_obj
Esempio n. 3
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()
Esempio n. 4
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