def plot(self, mass=None, sfr=None, z=None, FQdist=None, sfms_prop=None, **mkwargs):
        ''' Plot the quiescent fraction as a function of stellar mass. 
        The quiescent fraction is calculated based on an evolving sSFR(M*,z) cut. 

        Parameters
        ----------
        FQdist : list
            List that specifies the mass bins and the quiescent fraction 
            (eq. [massbin, quiescentfraction])
        '''
        if FQdist is None: 
            if sfr is None or z is None: 
                raise ValueError
            fq_obj = Fq()
            masses, fq = fq_obj.Calculate(mass=mass, sfr=sfr, z=z, sfms_prop=sfms_prop) 
        else:
            masses, fq = FQdist

        self.masses = masses
        self.z = z  

        if self.kwargs == {}: 
            kwargs = mkwargs.copy() 
        else: 
            kwargs = (self.kwargs).copy()
            kwargs.update(mkwargs)
    
        if 'label' in kwargs: 
            fq_label = kwargs['label']
        else: 
            fq_label = None
        
        if 'line_color' in kwargs: 
            line_color = kwargs['line_color']
        else: 
            line_color = 'black'

        if 'line_style' in kwargs:
            line_style = kwargs['line_style'] 
        else: 
            line_style = '-'

        if 'lw' in kwargs: 
            line_width = kwargs['lw'] 
        else:
            line_width = 4

        self.subs.plot(masses, fq, 
                color = line_color, 
                lw = line_width, 
                ls = line_style, 
                label = fq_label) 

        return None   
Exemple #2
0
def _getPq(ancestor,
           descendant,
           succession=None,
           will=None,
           pq_prop=None,
           sfr_prop=None):
    '''' Calculate the quenching probability for Star-Forming ancestor galaxies
    in the ancestor CGPop object. The quenching probability is calculated using 

    P_Q = ( f_Q(Mf,zf) - f_Q(M0,z0) ) / (1 - f_Q(M0,z0)) + Offset_P_Q
    
    where Offset_P_Q is a fudge factor dictated by nuisance parameters described 
    by pq_prop. 
    
    Parameters
    ----------
    pq_prop : dict
        Quenching properties dictionary. Keywords are 'slope' and 'yint' which
        describes the offset to the quenchign probability. The quenchign probability 
        is fudged around to match the observed quenching fraction at z_final: 
        fQ(M*, z_final). 

    Returns
    -------
    P_q : array
        Array that specifies the quenching probabilities for the SF ancestor 
        galaxies. 
    '''
    z_final = descendant.zsnap  # z_final of evolution
    # SF ancestors
    sf_ancestors = np.where(ancestor.sfr_class[will] == 'star-forming')[0]

    P_q_offset = pq_prop['slope'] * (
        descendant.mass[succession[sf_ancestors]] - 9.5) + pq_prop['yint']

    # fQ(M*, z_final)
    fq_obj = Fq()
    fqf = fq_obj.model(descendant.mass[succession[sf_ancestors]],
                       z_final,
                       lit=sfr_prop['fq']['name'])
    # fQ(M*, z_initial), which is calculated using mass and redshifts when
    # the host subhalo passes the M* threshold.
    fq0 = fq_obj.model(descendant.mass_genesis[succession[sf_ancestors]],
                       descendant.zsnap_genesis[succession[sf_ancestors]],
                       lit=sfr_prop['fq']['name'])

    notallquench = np.where(fq0 < 1.0)
    P_q = np.repeat(1.0, len(sf_ancestors))
    P_q[notallquench] = (fqf[notallquench] -
                         fq0[notallquench]) / (1.0 - fq0[notallquench])
    P_q += P_q_offset

    return P_q
    def model(self, fq_prop={'name': 'wetzelsmooth'}, z=None, **mkwargs):
        ''' Plot the model Parameterized queiscent fraction as a function 
        of stellar mass
        '''
        if z is None: 
            if self.z is None: 
                raise ValeuError
            else: 
                redshift = self.z
        else: 
            redshift = z
            self.z = z 

        if self.kwargs == {}: 
            kwargs = mkwargs.copy() 
        else: 
            kwargs = (self.kwargs).copy()
            kwargs.update(mkwargs)
    
        if 'label' in kwargs and kwargs['label'] is not None: 
            fq_label = kwargs['label']
        else: 
            fq_label = fq_prop['name']+'; z = '+str(redshift) 
        
        if 'line_color' in kwargs: 
            line_color = kwargs['line_color']
        else: 
            line_color = 'black'

        if 'line_style' in kwargs:
            line_style = kwargs['line_style'] 
        else: 
            line_style = '-'

        if 'lw' in kwargs: 
            line_width = kwargs['lw'] 
        else:
            line_width = 4

        # parameterized fq 
        fq_obj = Fq()
        fq = fq_obj.model(self.masses, self.z, lit=fq_prop['name'])
        self.subs.plot(
                self.masses, fq, 
                color = line_color,  
                lw = 4, 
                ls = '--', 
                label = fq_label 
                ) 

        return None
Exemple #4
0
    def plotSFMS(self, **pltkwargs):
        ''' Plot the Star Forming Main Sequence of the CGPop object 
        
        Notes
        -----
        Uses bovy_plot.scatter_plot so things are a big clunky 
        '''
        plt.close()

        sfms_plot = plots.PlotSFMS()
        if 'scatter' in pltkwargs:
            if not pltkwargs['scatter']:
                pass
            else:
                sfms_plot.plot(mass=self.mass,
                               sfr=self.sfr,
                               sfr_class=self.sfr_class,
                               color=self.nsnap,
                               **pltkwargs)
        else:
            sfms_plot.plot(mass=self.mass,
                           sfr=self.sfr,
                           sfr_class=self.sfr_class,
                           color=self.nsnap,
                           **pltkwargs)
        if 'sfqcut' in pltkwargs.keys():
            if pltkwargs['sfqcut']:
                qfrac = Fq()
                m_arr = np.arange(9.0, 12.5, 0.5)
                sfms_plot.sub.plot(m_arr,
                                   qfrac.SFRcut(
                                       m_arr,
                                       self.zsnap,
                                       sfms_prop=self.sfr_prop['sfms']),
                                   c='k',
                                   ls='--',
                                   lw=4)

        if 'model' in pltkwargs.keys():
            if pltkwargs['model']:
                sfms_plot.model(z=self.zsnap)
        if 'savefig' in pltkwargs.keys():
            if isinstance(pltkwargs['savefig'], str):
                sfms_plot.save_fig(pltkwargs['savefig'])
            else:
                ValueError('savefig = figure_file_name')

            return None

        else:
            return sfms_plot
def Plot_fQcen_SDSS():
    ''' Compare the quiescent fraction of SDSS from the *corrected* SDSS fQ^cen 
    from Tinker et al. (2013) versus the Wetzel et al. (2013) parameterization. 
    '''
    # mass binnning we impose
    m_low = np.array([9.5, 10., 10.5, 11., 11.5])
    m_high = np.array([10., 10.5, 11., 11.5, 12.0])
    m_mid = 0.5 * (m_low + m_high)

    # SDSS
    fq_file = ''.join(
        ['dat/observations/cosmos_fq/', 'fcen_red_sdss_scatter.dat'])
    m_sdss, fqcen_sdss, N_sdss = np.loadtxt(fq_file,
                                            unpack=True,
                                            usecols=[0, 1, 2])

    fqcen_sdss_rebin = []
    for im, m_mid_i in enumerate(m_mid):
        sdss_mbin = np.where((m_sdss >= m_low[im]) & (m_sdss < m_high[im]))

        fqcen_sdss_rebin.append(
            np.sum(fqcen_sdss[sdss_mbin] * N_sdss[sdss_mbin].astype('float')) /
            np.sum(N_sdss[sdss_mbin].astype('float')))
    fqcen_sdss_rebin = np.array(fqcen_sdss_rebin)

    prettyplot()
    pretty_colors = prettycolors()
    fig = plt.figure()
    sub = fig.add_subplot(111)
    qf = Fq()
    fqcen_model = qf.model(m_mid, 0.05, lit='cosmos_tinker')
    sub.plot(m_mid,
             fqcen_model,
             c=pretty_colors[3],
             lw=3,
             label=r'Wetzel et al. (2013) fit')
    sub.scatter(m_mid,
                fqcen_sdss_rebin,
                color=pretty_colors[0],
                lw=0,
                s=40,
                label=r'Tinker et al. (2013)')

    sub.set_xlim([9.0, 12.0])
    sub.set_xlabel(r'$\mathtt{log\;M_*}$', fontsize=25)
    sub.set_ylim([0.0, 1.0])
    sub.set_ylabel(r'$\mathtt{f_Q^{cen}}$', fontsize=25)
    sub.legend(loc='upper left', scatterpoints=1, markerscale=3)
    fig_file = ''.join(['figure/test/', 'Fq_central_SDSS.png'])
    fig.savefig(fig_file, bbox_inches='tight')
    plt.close()
def _getPq(ancestor, descendant, succession=None, will=None, pq_prop=None, sfr_prop=None):
    '''' Calculate the quenching probability for Star-Forming ancestor galaxies
    in the ancestor CGPop object. The quenching probability is calculated using 

    P_Q = ( f_Q(Mf,zf) - f_Q(M0,z0) ) / (1 - f_Q(M0,z0)) + Offset_P_Q
    
    where Offset_P_Q is a fudge factor dictated by nuisance parameters described 
    by pq_prop. 
    
    Parameters
    ----------
    pq_prop : dict
        Quenching properties dictionary. Keywords are 'slope' and 'yint' which
        describes the offset to the quenchign probability. The quenchign probability 
        is fudged around to match the observed quenching fraction at z_final: 
        fQ(M*, z_final). 

    Returns
    -------
    P_q : array
        Array that specifies the quenching probabilities for the SF ancestor 
        galaxies. 
    '''
    z_final = descendant.zsnap  # z_final of evolution
    # SF ancestors
    sf_ancestors = np.where(ancestor.sfr_class[will] == 'star-forming')[0]      

    P_q_offset = pq_prop['slope'] * (descendant.mass[succession[sf_ancestors]] - 9.5) + pq_prop['yint']
    
    # fQ(M*, z_final)
    fq_obj = Fq()
    fqf = fq_obj.model(descendant.mass[succession[sf_ancestors]], 
            z_final, 
            lit = sfr_prop['fq']['name'])
    # fQ(M*, z_initial), which is calculated using mass and redshifts when 
    # the host subhalo passes the M* threshold. 
    fq0 = fq_obj.model(descendant.mass_genesis[succession[sf_ancestors]], 
            descendant.zsnap_genesis[succession[sf_ancestors]], 
            lit=sfr_prop['fq']['name'])

    notallquench = np.where(fq0 < 1.0)
    P_q = np.repeat(1.0, len(sf_ancestors))
    P_q[notallquench] = (fqf[notallquench] - fq0[notallquench] ) / (1.0 - fq0[notallquench]) 
    P_q += P_q_offset

    return P_q
def Plot_fQcen_SDSS(): 
    ''' Compare the quiescent fraction of SDSS from the *corrected* SDSS fQ^cen 
    from Tinker et al. (2013) versus the Wetzel et al. (2013) parameterization. 
    '''
    # mass binnning we impose 
    m_low = np.array([9.5, 10., 10.5, 11., 11.5]) 
    m_high = np.array([10., 10.5, 11., 11.5, 12.0])
    m_mid = 0.5 * (m_low + m_high) 

    # SDSS 
    fq_file = ''.join(['dat/observations/cosmos_fq/', 'fcen_red_sdss_scatter.dat']) 
    m_sdss, fqcen_sdss, N_sdss = np.loadtxt(fq_file, unpack=True, usecols=[0,1,2])
    
    fqcen_sdss_rebin = [] 
    for im, m_mid_i in enumerate(m_mid): 
        sdss_mbin = np.where(
                (m_sdss >= m_low[im]) & 
                (m_sdss < m_high[im])) 
    
        fqcen_sdss_rebin.append(
                np.sum(fqcen_sdss[sdss_mbin] * N_sdss[sdss_mbin].astype('float'))/np.sum(N_sdss[sdss_mbin].astype('float'))
                )
    fqcen_sdss_rebin = np.array(fqcen_sdss_rebin)

    prettyplot() 
    pretty_colors = prettycolors()  
    fig = plt.figure() 
    sub = fig.add_subplot(111)
    qf = Fq()
    fqcen_model = qf.model(m_mid, 0.05, lit='cosmos_tinker') 
    sub.plot(m_mid, fqcen_model, 
            c=pretty_colors[3], lw=3, label=r'Wetzel et al. (2013) fit') 
    sub.scatter(m_mid, fqcen_sdss_rebin, 
            color=pretty_colors[0], lw=0, s=40, label=r'Tinker et al. (2013)') 

    sub.set_xlim([9.0, 12.0]) 
    sub.set_xlabel(r'$\mathtt{log\;M_*}$', fontsize=25) 
    sub.set_ylim([0.0, 1.0]) 
    sub.set_ylabel(r'$\mathtt{f_Q^{cen}}$', fontsize=25) 
    sub.legend(loc='upper left', scatterpoints=1, markerscale=3) 
    fig_file = ''.join(['figure/test/', 
        'Fq_central_SDSS.png']) 
    fig.savefig(fig_file, bbox_inches='tight') 
    plt.close() 
Exemple #8
0
    def Fq(self, **fq_kwargs):
        '''
        Calculate quiescent fraction of CenQue class object
        '''
        if self.zsnap is None:
            raise ValueError
        if self.mass is None:
            raise ValueError
        if self.sfr is None:
            raise ValueError
        if self.sfr_prop is None:
            raise ValueError

        qfrac = Fq()
        # Star-forming or Quiescent
        return qfrac.Calculate(mass=self.mass,
                               sfr=self.sfr,
                               z=self.zsnap,
                               sfms_prop=self.sfr_prop['sfms'])
Exemple #9
0
def DataSummary(Mrcut=18, observables=['ssfr']): 
    ''' Summary statistics of the data. In our case that is the 
    SSFR distribution of the SDSS group catalog.
    '''
    obvs = []
    if 'ssfr' in observables:
        # Group Catalog object
        groupcat = GroupCat(Mrcut=Mrcut, position='central')
        # SSFR distribution of group catalog
        bins, dist = groupcat.Ssfr()   
        obvs.append([np.array(bins), np.array(dist)])
    if 'fqz03' in observables: 
        qfrac = Fq()
        M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
        M_mid = 0.5 * (M_bin[:-1] + M_bin[1:])
        fq_model = qfrac.model(M_mid, 0.3412, lit='wetzel')
        obvs.append([M_mid, fq_model])
    
    if len(observables) == 1: 
        obvs = obvs[0]
    return obvs
Exemple #10
0
def SimSummary(observables=['ssfr'], **sim_kwargs):
    ''' Summary statistics of the simulation. In our case, the simulation is InheritSF 
    and the summary statistic is the SSFR distribution. 
    '''
    obvs = []
    if 'ssfr' in observables:
        bloodline = InheritSF(
                1,
                nsnap_ancestor=sim_kwargs['nsnap_ancestor'],
                subhalo_prop=sim_kwargs['subhalo_prop'], 
                sfr_prop=sim_kwargs['sfr_prop'], 
                evol_prop=sim_kwargs['evol_prop'])
        descendant = getattr(bloodline, 'descendant_snapshot1') 
        
        bins, dist = descendant.Ssfr()
        obvs.append([np.array(bins), np.array(dist)])
    if 'fqz03' in observables: 
        bloodline = InheritSF(
                6,
                nsnap_ancestor=sim_kwargs['nsnap_ancestor'],
                subhalo_prop=sim_kwargs['subhalo_prop'], 
                sfr_prop=sim_kwargs['sfr_prop'], 
                evol_prop=sim_kwargs['evol_prop'])
        descendant = getattr(bloodline, 'descendant_snapshot6') 

        qfrac = Fq()
        M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
        M_mid = 0.5 * (M_bin[:-1] + M_bin[1:]) 

        sfq = qfrac.Classify(descendant.mass, descendant.sfr, descendant.zsnap, 
                sfms_prop=sim_kwargs['sfr_prop']['sfms'])

        ngal, dum = np.histogram(descendant.mass, bins=M_bin)
        ngal_q, dum = np.histogram(descendant.mass[sfq == 'quiescent'], bins=M_bin)

        obvs.append([M_mid, ngal_q.astype('float')/ngal.astype('float')])
    
    if len(observables) == 1: 
        obvs = obvs[0]
    return obvs
Exemple #11
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()
Exemple #12
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 SimSummary(observables=['ssfr'], **sim_kwargs):
    ''' Summary statistics of the simulation. In our case, the simulation is Inherit 
    and the summary statistic is the SSFR distribution. 
    '''
    obvs = []

    if 'fqz03' in observables and 'fqz_multi' in observables: 
        raise ValueError

    nsnap_ds = [1]
    if 'fqz03' in observables: 
        nsnap_ds += [6]
    if 'fqz_multi' in observables: 
        nsnap_ds += [3, 6] 

    inh = Inherit(nsnap_ds, 
            nsnap_ancestor=sim_kwargs['nsnap_ancestor'],
            subhalo_prop=sim_kwargs['subhalo_prop'], 
            sfr_prop=sim_kwargs['sfr_prop'], 
            evol_prop=sim_kwargs['evol_prop'])
    des_dict = inh() 

    # SSFR 
    des1 = des_dict['1']
    if 'ssfr' in observables: 
        bins, dist = des1.Ssfr()
        obvs.append([np.array(bins), np.array(dist)])

    if 'fqz03' in observables:  # fQ(nsnap = 6) 
        des6 = des_dict['6']

        qfrac = Fq()
        M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
        M_mid = 0.5 * (M_bin[:-1] + M_bin[1:]) 

        sfq = qfrac.Classify(des6.mass, des6.sfr, des6.zsnap, 
                sfms_prop=sim_kwargs['sfr_prop']['sfms'])

        ngal, dum = np.histogram(des6.mass, bins=M_bin)
        ngal_q, dum = np.histogram(des6.mass[sfq == 'quiescent'], bins=M_bin)

        obvs.append([M_mid, ngal_q.astype('float')/ngal.astype('float')])

    if 'fqz_multi' in observables:  # fQ at multiple snapshots 
        qfrac = Fq()
        M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
        M_mid = 0.5 * (M_bin[:-1] + M_bin[1:]) 

        fq_list = [M_mid]

        for nd in nsnap_ds: 
            des_tmp = des_dict[str(nd)]

            sfq = qfrac.Classify(des_tmp.mass, des_tmp.sfr, des_tmp.zsnap, 
                    sfms_prop=sim_kwargs['sfr_prop']['sfms'])

            ngal, dum = np.histogram(des_tmp.mass, bins=M_bin)
            ngal_q, dum = np.histogram(des_tmp.mass[sfq == 'quiescent'], bins=M_bin)
            fq_tmp = ngal_q.astype('float')/ngal.astype('float')

            fq_list += [fq_tmp]
        # ancesotr 
        sfq = qfrac.Classify(inh.ancestor.mass, inh.ancestor.sfr, inh.ancestor.zsnap, 
                sfms_prop=sim_kwargs['sfr_prop']['sfms'])
        ngal, dum = np.histogram(inh.ancestor.mass, bins=M_bin)
        ngal_q, dum = np.histogram(inh.ancestor.mass[sfq == 'quiescent'], bins=M_bin)
        fq_tmp = ngal_q.astype('float')/ngal.astype('float')
        fq_list += [fq_tmp]

        obvs.append(fq_list)
    
    if len(observables) == 1: 
        obvs = obvs[0]
    return obvs
def ABC(T,
        eps_input,
        Npart=1000,
        cen_tf=None,
        cen_prior_name=None,
        cen_abcrun=None):
    ''' ABC-PMC implementation. 

    Parameters
    ----------
    T : (int) 
        Number of iterations

    eps_input : (float)
        Starting epsilon threshold value 

    N_part : (int)
        Number of particles

    prior_name : (string)
        String that specifies what prior to use.

    abcrun : (string)
        String that specifies abc run information 
    '''
    abcinh = ABCInherit(cen_tf, abcrun=cen_abcrun, prior_name=cen_prior_name)

    # Data (Group Catalog Satellite fQ)
    grpcat = GroupCat(Mrcut=18, position='satellite')
    grpcat.Read()
    qfrac = Fq()
    m_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
    M_mid = 0.5 * (m_bin[:-1] + m_bin[1:])

    sfq = qfrac.Classify(grpcat.mass,
                         grpcat.sfr,
                         np.median(grpcat.z),
                         sfms_prop=abcinh.sim_kwargs['sfr_prop']['sfms'])
    ngal, dum = np.histogram(grpcat.mass, bins=m_bin)
    ngal_q, dum = np.histogram(grpcat.mass[sfq == 'quiescent'], bins=m_bin)
    data_sum = [M_mid, ngal_q.astype('float') / ngal.astype('float')]

    # Simulator
    cen_assigned_sat_file = ''.join([
        '/data1/hahn/pmc_abc/pickle/', 'satellite', '.cenassign', '.',
        cen_abcrun, '_ABC', '.', cen_prior_name, '_prior', '.p'
    ])

    if not os.path.isfile(cen_assigned_sat_file):
        sat_cen = AssignCenSFR(cen_tf,
                               abcrun=cen_abcrun,
                               prior_name=cen_prior_name)
        pickle.dump(sat_cen, open(cen_assigned_sat_file, 'wb'))
    else:
        sat_cen = pickle.load(open(cen_assigned_sat_file, 'rb'))

    def Simz(tt):  # Simulator (forward model)
        tqdel_dict = {'name': 'explin', 'm': tt[0], 'b': tt[1]}

        sat_evol = EvolveSatSFR(sat_cen, tqdelay_dict=tqdel_dict)

        sfq_sim = qfrac.Classify(sat_evol.mass,
                                 sat_evol.sfr,
                                 sat_evol.zsnap,
                                 sfms_prop=sat_evol.sfms_prop)
        ngal_sim, dum = np.histogram(sat_evol.mass, bins=m_bin)
        ngal_q_sim, dum = np.histogram(sat_evol.mass[sfq_sim == 'quiescent'],
                                       bins=m_bin)
        sim_sum = [
            M_mid,
            ngal_q_sim.astype('float') / ngal_sim.astype('float')
        ]
        return sim_sum

    # Priors
    prior_min = [-11.75, 2.]
    prior_max = [-10.25, 4.]
    prior = abcpmc.TophatPrior(prior_min, prior_max)  # ABCPMC prior object

    def rho(simum, datum):
        datum_dist = datum[1]
        simum_dist = simum[1]
        drho = np.sum((datum_dist - simum_dist)**2)
        return drho

    abcrun_flag = cen_abcrun + '_central'

    theta_file = lambda pewl: ''.join([
        code_dir(), 'dat/pmc_abc/', 'Satellite.tQdelay.theta_t',
        str(pewl), '_', abcrun_flag, '.dat'
    ])
    w_file = lambda pewl: ''.join([
        code_dir(), 'dat/pmc_abc/', 'Satellite.tQdelay.w_t',
        str(pewl), '_', abcrun_flag, '.dat'
    ])
    dist_file = lambda pewl: ''.join([
        code_dir(), 'dat/pmc_abc/', 'Satellite.tQdelay.dist_t',
        str(pewl), '_', abcrun_flag, '.dat'
    ])
    eps_file = ''.join([
        code_dir(), 'dat/pmc_abc/Satellite.tQdelay.epsilon_', abcrun_flag,
        '.dat'
    ])

    eps = abcpmc.ConstEps(T, eps_input)
    try:
        mpi_pool = mpi_util.MpiPool()
        abcpmc_sampler = abcpmc.Sampler(
            N=Npart,  # N_particles
            Y=data_sum,  # data
            postfn=Simz,  # simulator 
            dist=rho,  # distance function  
            pool=mpi_pool)
    except AttributeError:
        abcpmc_sampler = abcpmc.Sampler(
            N=Npart,  # N_particles
            Y=data_sum,  # data
            postfn=Simz,  # simulator 
            dist=rho)  # distance function
    abcpmc_sampler.particle_proposal_cls = abcpmc.ParticleProposal

    pools = []
    f = open(eps_file, "w")
    f.close()
    eps_str = ''
    for pool in abcpmc_sampler.sample(prior, eps, pool=None):
        print '----------------------------------------'
        print 'eps ', pool.eps
        new_eps_str = '\t' + str(pool.eps) + '\n'
        if eps_str != new_eps_str:  # if eps is different, open fiel and append
            f = open(eps_file, "a")
            eps_str = new_eps_str
            f.write(eps_str)
            f.close()

        print("T:{0},ratio: {1:>.4f}".format(pool.t, pool.ratio))
        print eps(pool.t)

        # write theta, weights, and distances to file
        np.savetxt(theta_file(pool.t),
                   pool.thetas,
                   header='tQdelay_slope, tQdelay_offset')
        np.savetxt(w_file(pool.t), pool.ws)
        np.savetxt(dist_file(pool.t), pool.dists)

        # update epsilon based on median thresholding
        eps.eps = np.median(pool.dists)
        pools.append(pool)

    return pools
def EvolveSatSFR(sg_in, tqdelay_dict=None):
    ''' Evolve the infalling SFR assigned based on central galaxy SFRs
    '''
    if tqdelay_dict == None:
        raise ValueError

    # initalize
    sg_obj = SGPop()
    sg_obj.ilk = sg_in.ilk.copy()
    sg_obj.pos = sg_in.pos.copy()
    sg_obj.snap_index = sg_in.snap_index.copy()
    sg_obj.zsnap = sg_in.zsnap.copy()
    sg_obj.t_cosmic = sg_in.t_cosmic.copy()
    sg_obj.sfms_prop = sg_in.sfms_prop.copy()
    sg_obj.abcrun = sg_in.abcrun
    sg_obj.prior_name = sg_in.prior_name
    sg_obj.mass = sg_in.mass.copy()
    sg_obj.sfr = np.repeat(-999., len(sg_obj.mass))
    sg_obj.ssfr = np.repeat(-999., len(sg_obj.mass))
    sg_obj.halo_mass = sg_in.halo_mass.copy()
    sg_obj.first_infall_nsnap = sg_in.first_infall_nsnap.copy()
    sg_obj.first_infall_z = sg_in.first_infall_z.copy()
    sg_obj.first_infall_t = sg_in.first_infall_t.copy()
    sg_obj.first_infall_sfr = sg_in.first_infall_sfr.copy()
    sg_obj.first_infall_mass = sg_in.first_infall_mass.copy()
    sg_obj.t_qstart = np.repeat(-999., len(sg_obj.mass))
    sg_obj.z_qstart = np.repeat(-999., len(sg_obj.mass))
    sg_obj.q_ssfr = np.repeat(-999., len(sg_obj.mass))

    # First classify the galaxies into SF, Q, and Qing
    # this is so that t_Q,start = t_inf for Qing galaxies,
    # Q galaxies are left alone
    # SF galaxies are affected by the delay time
    infall = np.where((sg_obj.first_infall_mass > 0.)
                      & (sg_obj.first_infall_sfr > -999.))

    fq_obj = Fq()
    sfq_class = fq_obj.Classify(sg_obj.first_infall_mass[infall],
                                sg_obj.first_infall_sfr[infall],
                                sg_obj.first_infall_z[infall],
                                sg_obj.sfms_prop)

    sf_infall = (infall[0])[np.where(
        sfq_class == 'star-forming')]  # starforming @ infall
    q_infall = (infall[0])[np.where(
        sfq_class == 'quiescent')]  # quiescent @ infall

    ssfr_final = sfr_evol.AverageLogSSFR_q_peak(sg_obj.mass[infall]) + \
            sfr_evol.ScatterLogSSFR_q_peak(sg_obj.mass[infall]) * np.random.randn(len(infall[0]))

    # sub-divide the quiescent @ infall population to those that are
    # quenching @ infall + quiescent @ infall based on simple SSFR cut
    q_mass_infall = sg_obj.first_infall_mass[q_infall]
    q_sfr_infall = sg_obj.first_infall_sfr[q_infall]
    q_ssfr_infall = q_sfr_infall - q_mass_infall

    q_cut_SSFR = sfr_evol.AverageLogSSFR_q_peak(q_mass_infall) + \
            1.5 * sfr_evol.ScatterLogSSFR_q_peak(q_mass_infall)

    qing_infall = q_infall[np.where(q_ssfr_infall > q_cut_SSFR)]  # quenching
    qq_infall = q_infall[np.where(q_ssfr_infall <= q_cut_SSFR)]  # quenched

    # Quiescent @ infall-----
    # The SSFR is preserved from infall, so effectively their SFR decreases
    sg_obj.ssfr[qq_infall] = sg_obj.first_infall_sfr[qq_infall] - \
            sg_obj.first_infall_mass[qq_infall]
    sg_obj.sfr[qq_infall] = sg_obj.mass[qq_infall] + sg_obj.ssfr[qq_infall]

    # Quenching @ infall-----
    # Quenching satellite galaxies skip the delay phase and immediately
    # start quenching when the simulation begins.
    sg_obj.t_qstart[qing_infall] = sg_obj.first_infall_t[qing_infall]
    sg_obj.z_qstart[qing_infall] = sg_obj.first_infall_z[qing_infall]
    sg_obj.q_ssfr[qing_infall] = sfr_evol.AverageLogSSFR_q_peak(sg_obj.mass[qing_infall]) + \
            sfr_evol.ScatterLogSSFR_q_peak(sg_obj.mass[qing_infall]) * np.random.randn(len(qing_infall))

    dlogSFR_MS_M = 0.53 * (sg_obj.mass[qing_infall] -
                           sg_obj.first_infall_mass[qing_infall])

    dlogSFR_MS_z = sg_obj.sfms_prop['zslope'] * (
        sg_obj.zsnap - sg_obj.first_infall_z[qing_infall])

    dlogSFR_Q = sfr_evol.DeltaLogSFR_quenching(sg_obj.t_qstart[qing_infall],
                                               sg_obj.t_cosmic,
                                               M_q=sg_obj.mass[qing_infall],
                                               tau_prop={'name': 'satellite'})

    sg_obj.sfr[qing_infall] = sg_obj.first_infall_sfr[qing_infall] + \
            dlogSFR_MS_M + dlogSFR_MS_z + dlogSFR_Q
    sg_obj.ssfr[
        qing_infall] = sg_obj.sfr[qing_infall] - sg_obj.mass[qing_infall]

    # deal with over quenching
    #overquenched = np.where(sg_obj.ssfr[qing_infall] < sg_obj.q_ssfr[qing_infall])
    #sg_obj.ssfr[qing_infall[overquenched]] = sg_obj.q_ssfr[qing_infall[overquenched]]

    # Star-forming @ infall
    if tqdelay_dict['name'] == 'hacked':
        sg_obj.t_qstart[sf_infall] = sg_obj.first_infall_t[sf_infall] + \
                tDelay(sg_obj.mass[sf_infall], **tqdelay_dict)
    else:
        sg_obj.t_qstart[sf_infall] = sg_obj.first_infall_t[sf_infall] + \
                tDelay(sg_obj.first_infall_mass[sf_infall], **tqdelay_dict)
    # if t_qstart > t_cosmic, then it does not quenching during the simualtion
    sf_q = np.where(sg_obj.t_qstart[sf_infall] < sg_obj.t_cosmic)
    sf_noq = np.where(sg_obj.t_qstart[sf_infall] >= sg_obj.t_cosmic)
    sf_infall_q = sf_infall[sf_q]
    sf_infall_noq = sf_infall[sf_noq]

    sg_obj.z_qstart[sf_infall_q] = Util.get_zsnap(sg_obj.t_qstart[sf_infall_q])
    sg_obj.z_qstart[sf_infall_noq] = 999.

    # SF galaxies that quench
    sg_obj.q_ssfr[sf_infall_q] = \
            sfr_evol.AverageLogSSFR_q_peak(sg_obj.mass[sf_infall_q]) + \
            sfr_evol.ScatterLogSSFR_q_peak(sg_obj.mass[sf_infall_q]) * \
            np.random.randn(len(sf_infall_q))

    dlogSFR_MS_M = 0.53 * (sg_obj.mass[sf_infall_q] -
                           sg_obj.first_infall_mass[sf_infall_q])

    dlogSFR_MS_z = sg_obj.sfms_prop['zslope'] * (
        sg_obj.zsnap - sg_obj.first_infall_z[sf_infall_q])
    dlogSFR_Q = sfr_evol.DeltaLogSFR_quenching(sg_obj.t_qstart[sf_infall_q],
                                               sg_obj.t_cosmic,
                                               M_q=sg_obj.mass[sf_infall_q],
                                               tau_prop={'name': 'satellite'})

    sg_obj.sfr[sf_infall_q] = sg_obj.first_infall_sfr[sf_infall_q] + \
            dlogSFR_MS_M + dlogSFR_MS_z + dlogSFR_Q
    sg_obj.ssfr[
        sf_infall_q] = sg_obj.sfr[sf_infall_q] - sg_obj.mass[sf_infall_q]
    # deal with over quenching
    #overquenched = np.where(sg_obj.ssfr[sf_infall_q] < sg_obj.q_ssfr[sf_infall_q])
    #sg_obj.ssfr[sf_infall_q[overquenched]] = sg_obj.q_ssfr[sf_infall_q[overquenched]]

    # SF galaxies that do NOT quench
    dlogSFR_MS_M = 0.53 * (sg_obj.mass[sf_infall_noq] -
                           sg_obj.first_infall_mass[sf_infall_noq])

    dlogSFR_MS_z = sg_obj.sfms_prop['zslope'] * (
        sg_obj.zsnap - sg_obj.first_infall_z[sf_infall_noq])

    sg_obj.sfr[sf_infall_noq] = sg_obj.first_infall_sfr[sf_infall_noq] + \
            dlogSFR_MS_M + dlogSFR_MS_z
    sg_obj.ssfr[
        sf_infall_noq] = sg_obj.sfr[sf_infall_noq] - sg_obj.mass[sf_infall_noq]

    # deal with overquenching all at once
    overquench = np.where(sg_obj.ssfr[infall] < ssfr_final)
    sg_obj.ssfr[infall[0][overquench]] = ssfr_final[overquench]
    sg_obj.sfr[infall[0][overquench]] = ssfr_final[overquench] + sg_obj.mass[
        infall[0][overquench]]

    return sg_obj
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
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
def DescendantQAplot(nsnap_descendants, **sfinh_kwargs):
    ''' The ultimate QAplot t rule them all. 4 panels showing all the properties.
    '''
    sfinherit_file = InheritSF_file(nsnap_descendants, **sfinh_kwargs)
    bloodline = Lineage(nsnap_ancestor=sfinh_kwargs['nsnap_ancestor'],
                        subhalo_prop=sfinh_kwargs['subhalo_prop'])
    if not isinstance(nsnap_descendants, list):
        nsnap_descendants = [nsnap_descendants]
    bloodline.Read(nsnap_descendants, filename=sfinherit_file)

    for nsnap_descendant in nsnap_descendants:
        descendant = getattr(bloodline,
                             'descendant_snapshot' + str(nsnap_descendant))
        descendant.sfr_prop = sfinh_kwargs['sfr_prop']
        started_here = np.where(
            descendant.nsnap_genesis == sfinh_kwargs['nsnap_ancestor'])
        start_mass = descendant.mass_genesis[started_here]

        # Mass bins
        mass_bins = np.arange(7., 12.5, 0.5)
        mass_bin_low = mass_bins[:-1]
        mass_bin_high = mass_bins[1:]

        plt.close()
        prettyplot()
        fig = plt.figure(1, figsize=[25, 6])
        for i_sub in range(1, 5):
            sub_i = fig.add_subplot(1, 4, i_sub)

            if i_sub == 1:  # SMF
                mf = SMF()
                mass, phi = mf.Obj(descendant)

                sub_i.plot(mass,
                           phi,
                           lw=4,
                           c=pretty_colors[descendant.nsnap],
                           label=r'Simulated')

                censub = CentralSubhalos()
                censub.Read(descendant.nsnap,
                            scatter=sfinh_kwargs['subhalo_prop']['scatter'],
                            source=sfinh_kwargs['subhalo_prop']['source'],
                            nsnap_ancestor=sfinh_kwargs['nsnap_ancestor'])

                mass, phi = mf._smf(censub.mass)
                sub_i.plot(mass,
                           phi,
                           c='k',
                           lw=4,
                           ls='--',
                           label='Central Subhalos')

                sub_i.set_ylim([10**-5, 10**-1])
                sub_i.set_xlim([7.5, 12.0])
                plt.xticks([8., 9., 10., 11., 12.])
                sub_i.set_yscale('log')

                # x,y labels
                sub_i.set_xlabel(r'Mass $\mathtt{M_*}$', fontsize=25)
                sub_i.set_ylabel(r'Stellar Mass Function $\mathtt{\Phi}$',
                                 fontsize=25)
                sub_i.legend(loc='upper right', frameon=False)

            elif i_sub == 2:  # SFMS
                # SMF composition based on their initial mass at nsnap_ancestor
                for i_m in range(len(mass_bin_low)):
                    mbin = np.where((start_mass > mass_bin_low[i_m])
                                    & (start_mass <= mass_bin_high[i_m]))

                    sub_i.scatter(descendant.mass[started_here[0][mbin]],
                                  descendant.sfr[started_here[0][mbin]],
                                  color=pretty_colors[i_m],
                                  label=r"$\mathtt{M_{*,i}=}$" +
                                  str(round(mass_bin_low[i_m], 2)) + "-" +
                                  str(round(mass_bin_high[i_m], 2)))

                qfrac = Fq()
                m_arr = np.arange(9.0, 12.5, 0.5)
                sub_i.plot(m_arr,
                           qfrac.SFRcut(
                               m_arr,
                               descendant.zsnap,
                               sfms_prop=(sfinh_kwargs['sfr_prop'])['sfms']),
                           c='k',
                           ls='--',
                           lw=4)

                sub_i.set_xlim([9.0, 12.0])
                sub_i.set_ylim([-5.0, 2.0])
                sub_i.set_xlabel(r'$\mathtt{log\;M_*}$', fontsize=25)
                sub_i.set_ylabel(r'$\mathtt{log\;SFR}$', fontsize=25)

            elif i_sub == 3:  #SMHM
                for i_m in range(len(mass_bin_low)):
                    mbin = np.where((start_mass > mass_bin_low[i_m])
                                    & (start_mass <= mass_bin_high[i_m]))

                    sub_i.scatter(descendant.halo_mass[started_here[0][mbin]],
                                  descendant.mass[started_here[0][mbin]],
                                  color=pretty_colors[i_m],
                                  label=r"$\mathtt{M_{*,i}=}$" +
                                  str(round(mass_bin_low[i_m], 2)) + "-" +
                                  str(round(mass_bin_high[i_m], 2)))

                stellarmass = descendant.mass[started_here]
                halomass = descendant.halo_mass[started_here]
                mbin = np.arange(halomass.min(), halomass.max(), 0.25)
                mlow = mbin[:-1]
                mhigh = mbin[1:]

                muMstar = np.zeros(len(mlow))
                sigMstar = np.zeros(len(mlow))

                for im in range(len(mlow)):
                    mbin = np.where((halomass > mlow[im])
                                    & (halomass <= mhigh[im]))
                    muMstar[im] = np.mean(stellarmass[mbin])
                    sigMstar[im] = np.std(stellarmass[mbin])
                sub_i.errorbar(0.5 * (mlow + mhigh),
                               muMstar,
                               yerr=sigMstar,
                               color='k',
                               lw=3,
                               fmt='o',
                               capthick=2)

                sub_i.set_ylim([9.0, 12.0])
                sub_i.set_xlim([10.0, 15.0])
                sub_i.set_ylabel(r'Stellar Mass $\mathtt{M_*}$', fontsize=25)
                sub_i.set_xlabel(r'Halo Mass $\mathtt{M_{Halo}}$', fontsize=25)

                #sub_i.legend(loc='upper left', frameon=False, scatterpoints=1)
            elif i_sub == 4:  # Fq
                #mass, fq = descendant.Fq()
                sfq = qfrac.Classify(descendant.mass,
                                     descendant.sfr,
                                     descendant.zsnap,
                                     sfms_prop=descendant.sfr_prop['sfms'])
                gc = GroupCat(Mrcut=18, position='central')
                gc.Read()
                gc_sfq = qfrac.Classify(gc.mass,
                                        gc.sfr,
                                        np.mean(gc.z),
                                        sfms_prop=descendant.sfr_prop['sfms'])
                #sub_i.plot(mass, fq, color=pretty_colors[descendant.nsnap], lw=3, ls='--',
                #        label=r'$\mathtt{z =} '+str(descendant.zsnap)+'$')
                M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
                M_low = M_bin[:-1]
                M_high = M_bin[1:]
                M_mid = 0.5 * (M_low + M_high)

                fq = np.zeros(len(M_low))
                gc_fq = np.zeros(len(M_low))
                for i_m in xrange(len(M_low)):
                    mlim = np.where((descendant.mass > M_low[i_m])
                                    & (descendant.mass <= M_high[i_m]))
                    gc_mlim = np.where((gc.mass > M_low[i_m])
                                       & (gc.mass <= M_high[i_m]))
                    ngal = np.float(len(mlim[0]))
                    gc_ngal = np.float(len(gc_mlim[0]))

                    if ngal != 0:  # no galaxy in mass bin
                        ngal_q = np.float(
                            len(np.where(sfq[mlim] == 'quiescent')[0]))
                        fq[i_m] = ngal_q / ngal
                    if gc_ngal != 0:
                        gc_ngal_q = np.float(
                            len(np.where(gc_sfq[gc_mlim] == 'quiescent')[0]))
                        gc_fq[i_m] = gc_ngal_q / gc_ngal

                sub_i.plot(M_mid,
                           fq,
                           color=pretty_colors[descendant.nsnap],
                           lw=3,
                           label=r'$\mathtt{z =} ' + str(descendant.zsnap) +
                           '$')

                fq_model = qfrac.model(
                    M_bin,
                    descendant.zsnap,
                    lit=sfinh_kwargs['sfr_prop']['fq']['name'])
                sub_i.plot(M_bin,
                           fq_model,
                           color='k',
                           lw=4,
                           ls='--',
                           label=sfinh_kwargs['sfr_prop']['fq']['name'])
                sub_i.scatter(M_mid,
                              gc_fq,
                              color='k',
                              s=100,
                              lw=0,
                              label='Group Catalog')

                sub_i.set_xlim([9.0, 12.0])
                sub_i.set_ylim([0.0, 1.0])

                sub_i.set_xlabel(r'Mass $\mathtt{M_*}$')
                sub_i.set_ylabel(r'Quiescent Fraction $\mathtt{f_Q}$',
                                 fontsize=20)

                sub_i.legend(loc='upper left',
                             frameon=False,
                             scatterpoints=1,
                             markerscale=0.75)

        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0)
        fig_name = ''.join([
            'figure/test/', 'QAplot.', '.nsnap',
            str(nsnap_descendant), '.'.join(
                (sfinherit_file.rsplit('/')[-1]).rsplit('.')[:-1]), '.png'
        ])
        fig.savefig(fig_name, bbox_inches='tight')
        plt.close()
    return None
def AssignSFR(mass, redshift, sfr_prop=None, ancestor=None, descendant=None, quiet=True):
    ''' Assign star-forming properties based on the input mass and redshift. 
    Return sfr_class (star-forming or quiescent), SFR, and sSFR given a set 
    of masses and redshifts.

    Parameters
    ----------
    mass : array
        Array of galaxy stellar mass
    redshift : array
        Array of corresponding galaxy redshift
    sfr_prop : dictionary
        Dictionary that specifies the star-forming properties.

    Notes
    -----
    * Takes ~1.5 seconds for assign_sfr_ancestor in lineage object
    '''
    if sfr_prop is None: 
        raise ValueError('Specify SFR Properties dictionary')
    
    # Dictionaries that specify fQ, SFMS, f_GV
    fq_dict = sfr_prop['fq']        
    sfms_dict = sfr_prop['sfms']    
    gv_dict = sfr_prop['gv']        
    if 'subhalogrowth' in sfr_prop.keys(): 
        if descendant is None: 
            raise ValueError
        if ancestor is None: 
            raise ValueError
        for key in descendant.__dict__.keys(): 
            if 'ancestor' in key: 
                ancestor_index = getattr(descendant, key) 

    ngal = len(mass) # Ngal 
    sfr_class= np.repeat('', ngal).astype('|S16') 
    sfr      = np.repeat(-999., ngal) 
    ssfr     = np.repeat(-999., ngal)
    delta_sfr   = np.repeat(-999., ngal)
    avg_sfr     = np.repeat(-999., ngal)
    tQ  = np.repeat(999., ngal) 
    MQ  = np.repeat(-999., ngal)

    qfrac = Fq()    # Fq object
    M_bins = np.arange(6.0, 13., 0.5)   # mass bin 
    M_mid = 0.5 * (M_bins[:-1] + M_bins[1:])

    # For z = z_ancestor 
    massive = np.where((mass > 0.0) & (redshift == redshift.max()))[0] 
    ngal = len(massive) 
    ngal_M, dum = np.histogram(mass[massive], bins=M_bins)
    
    np.random.seed()
    rand = np.random.uniform(0., 1., ngal)
    # f_GV(M*)  Green valley fraction 
    gvfrac = gv_dict['slope'] * (mass[massive] - gv_dict['fidmass']) + gv_dict['offset']
    greenvalley = np.where(rand < gvfrac)[0]
    ngal_green = len(greenvalley) 
    ngal_green_M, dum = np.histogram(mass[massive[greenvalley]], bins=M_bins)
    if not quiet: 
        print ngal_green, ' green valley galaxies out of ', ngal, ' galaxies'
    
    sfr_class[massive[greenvalley]] = 'star-forming'    # classified as star-forming
    ssfr_q_peak = AverageLogSSFR_q_peak(mass[massive[greenvalley]]) # Q peak SSFR value
    ssfr_sf_peak = AverageLogSFR_sfms(mass[massive[greenvalley]],   # SF peak SSFR value 
            redshift[massive[greenvalley]], 
            sfms_prop=sfms_dict) - mass[massive[greenvalley]]
    ssfr[massive[greenvalley]] = np.random.uniform(ssfr_q_peak, ssfr_sf_peak, ngal_green)
    sfr[massive[greenvalley]] = ssfr[massive[greenvalley]] + mass[massive[greenvalley]]
    tQ[massive[greenvalley]] = Util.get_tsnap(redshift.max())  # quenching cosmic time 
    MQ[massive[greenvalley]] = mass[massive[greenvalley]]
    
    # some green valley galaxies will be classified in observations as 
    # quiescent or star-forming.
    green_class = qfrac.Classify(
            mass[massive[greenvalley]], 
            sfr[massive[greenvalley]], 
            redshift[massive[greenvalley]], 
            sfms_prop=sfms_dict)
    GQ = np.where(green_class == 'quiescent')
    ngal_GQ = len(GQ[0]) 
    ngal_GQ_M, dum = np.histogram(mass[massive[greenvalley[GQ]]], bins=M_bins)

    # f_Q(M*, z) for each massive galaxy
    fq_M = qfrac.model(M_mid, redshift.max(), lit=fq_dict['name']) 
    fq_prime_arr = (ngal_M * fq_M - ngal_GQ_M)/(ngal_M - ngal_green_M)
    fq_prime_arr[np.where(ngal_M == 0)] = 0.
    fq_prime_M = interpolate.interp1d(M_mid, fq_prime_arr, kind='linear') 
    
    # assign SFR to the not so green valley
    notgreenvalley = np.where(rand >= gvfrac)[0]
    ngal_notgreenvalley = len(notgreenvalley) 
    rand_notgreen = np.random.uniform(0., 1., ngal_notgreenvalley)
    
    fq_prime = fq_prime_M(mass[massive[notgreenvalley]])    # fQ'

    lowz = np.where((mass > 0.0) & (redshift != redshift.max()))[0] 
    ngal_lowz = len(lowz)
    rand_lowz = np.random.uniform(0., 1., ngal_lowz) 
    fq_lowz = qfrac.model(mass[lowz], redshift[lowz], lit=fq_dict['name']) 
    quiescent = np.concatenate([
        massive[notgreenvalley[np.where(rand_notgreen < fq_prime)]], 
        lowz[np.where(rand_lowz < fq_lowz)]])
    starforming = np.concatenate([
        massive[notgreenvalley[np.where(rand_notgreen >= fq_prime)]], 
        lowz[np.where(rand_lowz >= fq_lowz)]])
    ngal_q = len(quiescent)
    ngal_sf = len(starforming)

    # Assign SFR to quiescent galaxies
    sfr_class[quiescent] = 'quiescent'
    mu_q_ssfr = AverageLogSSFR_q_peak(mass[quiescent])
    sig_q_ssfr = ScatterLogSSFR_q_peak(mass[quiescent])
    ssfr[quiescent] = sig_q_ssfr * np.random.randn(ngal_q) + mu_q_ssfr 
    sfr[quiescent]  = ssfr[quiescent] + mass[quiescent]
    
    # Assign SFR to star-forming galaxies 
    sfr_class[starforming] = 'star-forming'
    mu_sf_sfr = AverageLogSFR_sfms(
            mass[starforming], 
            redshift[starforming], 
            sfms_prop=sfms_dict)
    sigma_sf_sfr = ScatterLogSFR_sfms(
            mass[starforming], 
            redshift[starforming],
            sfms_prop=sfms_dict)
    avg_sfr[starforming] = mu_sf_sfr
    delta_sfr[starforming] = sigma_sf_sfr * np.random.randn(ngal_sf)
    sfr[starforming] = mu_sf_sfr + delta_sfr[starforming]
    ssfr[starforming] = sfr[starforming] - mass[starforming]

    if 'subhalogrowth' in sfr_prop.keys(): 
        # if SFR assignment is dependent on subhalo growth, 
        # loop through mass bins and assign SFR by abundance matching the 
        # SFR to the Delta M*_sham. 
        mass_range = np.arange(mass[starforming].min(), mass[starforming].max(), 0.1)
        m_low = mass_range[:-1]
        m_high = mass_range[1:]
        for im in range(len(m_low)):
            sf_mass_bin = np.where(
                    (mass[starforming] > m_low[im]) & 
                    (mass[starforming] <= m_high[im]))[0]
            succession, will = Util.intersection_index(
                    ancestor_index, 
                    ancestor.snap_index[starforming])
            deltaM = descendant.mass[succession] - mass[starforming[will]]
            dM_sort_index = np.argsort(deltaM)

            deltaSFR = sigma_sf_sfr * np.random.randn(len(deltaM))
            dSFR_sort_index = np.argsort(deltaSFR)
            # abundance matched.             
            delta_sfr[starforming[will[dM_sort_index]]] = deltaSFR[dSFR_sort_index]

        sfr[starforming] = mu_sf_sfr + delta_sfr[starforming]
        ssfr[starforming] = sfr[starforming] - mass[starforming]
    
    return [sfr_class, sfr, ssfr, delta_sfr, avg_sfr, tQ, MQ] 
Exemple #20
0
def AssignSFR(mass,
              redshift,
              sfr_prop=None,
              ancestor=None,
              descendant=None,
              quiet=True):
    ''' Assign star-forming properties based on the input mass and redshift. 
    Return sfr_class (star-forming or quiescent), SFR, and sSFR given a set 
    of masses and redshifts.

    Parameters
    ----------
    mass : array
        Array of galaxy stellar mass
    redshift : array
        Array of corresponding galaxy redshift
    sfr_prop : dictionary
        Dictionary that specifies the star-forming properties.

    Notes
    -----
    * Takes ~1.5 seconds for assign_sfr_ancestor in lineage object
    '''
    if sfr_prop is None:
        raise ValueError('Specify SFR Properties dictionary')

    # Dictionaries that specify fQ, SFMS, f_GV
    fq_dict = sfr_prop['fq']
    sfms_dict = sfr_prop['sfms']
    gv_dict = sfr_prop['gv']
    if 'subhalogrowth' in sfr_prop.keys():
        if descendant is None:
            raise ValueError
        if ancestor is None:
            raise ValueError
        for key in descendant.__dict__.keys():
            if 'ancestor' in key:
                ancestor_index = getattr(descendant, key)

    ngal = len(mass)  # Ngal
    sfr_class = np.repeat('', ngal).astype('|S16')
    sfr = np.repeat(-999., ngal)
    ssfr = np.repeat(-999., ngal)
    delta_sfr = np.repeat(-999., ngal)
    avg_sfr = np.repeat(-999., ngal)
    tQ = np.repeat(999., ngal)
    MQ = np.repeat(-999., ngal)

    qfrac = Fq()  # Fq object
    M_bins = np.arange(6.0, 13., 0.5)  # mass bin
    M_mid = 0.5 * (M_bins[:-1] + M_bins[1:])

    # For z = z_ancestor
    massive = np.where((mass > 0.0) & (redshift == redshift.max()))[0]
    ngal = len(massive)
    ngal_M, dum = np.histogram(mass[massive], bins=M_bins)

    np.random.seed()
    rand = np.random.uniform(0., 1., ngal)
    # f_GV(M*)  Green valley fraction
    gvfrac = gv_dict['slope'] * (mass[massive] -
                                 gv_dict['fidmass']) + gv_dict['offset']
    greenvalley = np.where(rand < gvfrac)[0]
    ngal_green = len(greenvalley)
    ngal_green_M, dum = np.histogram(mass[massive[greenvalley]], bins=M_bins)
    if not quiet:
        print ngal_green, ' green valley galaxies out of ', ngal, ' galaxies'

    sfr_class[
        massive[greenvalley]] = 'star-forming'  # classified as star-forming
    ssfr_q_peak = AverageLogSSFR_q_peak(
        mass[massive[greenvalley]])  # Q peak SSFR value
    ssfr_sf_peak = AverageLogSFR_sfms(
        mass[massive[greenvalley]],  # SF peak SSFR value 
        redshift[massive[greenvalley]],
        sfms_prop=sfms_dict) - mass[massive[greenvalley]]
    ssfr[massive[greenvalley]] = np.random.uniform(ssfr_q_peak, ssfr_sf_peak,
                                                   ngal_green)
    sfr[massive[greenvalley]] = ssfr[massive[greenvalley]] + mass[
        massive[greenvalley]]
    tQ[massive[greenvalley]] = Util.get_tsnap(
        redshift.max())  # quenching cosmic time
    MQ[massive[greenvalley]] = mass[massive[greenvalley]]

    # some green valley galaxies will be classified in observations as
    # quiescent or star-forming.
    green_class = qfrac.Classify(mass[massive[greenvalley]],
                                 sfr[massive[greenvalley]],
                                 redshift[massive[greenvalley]],
                                 sfms_prop=sfms_dict)
    GQ = np.where(green_class == 'quiescent')
    ngal_GQ = len(GQ[0])
    ngal_GQ_M, dum = np.histogram(mass[massive[greenvalley[GQ]]], bins=M_bins)

    # f_Q(M*, z) for each massive galaxy
    fq_M = qfrac.model(M_mid, redshift.max(), lit=fq_dict['name'])
    fq_prime_arr = (ngal_M * fq_M - ngal_GQ_M) / (ngal_M - ngal_green_M)
    fq_prime_arr[np.where(ngal_M == 0)] = 0.
    fq_prime_M = interpolate.interp1d(M_mid, fq_prime_arr, kind='linear')

    # assign SFR to the not so green valley
    notgreenvalley = np.where(rand >= gvfrac)[0]
    ngal_notgreenvalley = len(notgreenvalley)
    rand_notgreen = np.random.uniform(0., 1., ngal_notgreenvalley)

    fq_prime = fq_prime_M(mass[massive[notgreenvalley]])  # fQ'

    lowz = np.where((mass > 0.0) & (redshift != redshift.max()))[0]
    ngal_lowz = len(lowz)
    rand_lowz = np.random.uniform(0., 1., ngal_lowz)
    fq_lowz = qfrac.model(mass[lowz], redshift[lowz], lit=fq_dict['name'])
    quiescent = np.concatenate([
        massive[notgreenvalley[np.where(rand_notgreen < fq_prime)]],
        lowz[np.where(rand_lowz < fq_lowz)]
    ])
    starforming = np.concatenate([
        massive[notgreenvalley[np.where(rand_notgreen >= fq_prime)]],
        lowz[np.where(rand_lowz >= fq_lowz)]
    ])
    ngal_q = len(quiescent)
    ngal_sf = len(starforming)

    # Assign SFR to quiescent galaxies
    sfr_class[quiescent] = 'quiescent'
    mu_q_ssfr = AverageLogSSFR_q_peak(mass[quiescent])
    sig_q_ssfr = ScatterLogSSFR_q_peak(mass[quiescent])
    ssfr[quiescent] = sig_q_ssfr * np.random.randn(ngal_q) + mu_q_ssfr
    sfr[quiescent] = ssfr[quiescent] + mass[quiescent]

    # Assign SFR to star-forming galaxies
    sfr_class[starforming] = 'star-forming'
    mu_sf_sfr = AverageLogSFR_sfms(mass[starforming],
                                   redshift[starforming],
                                   sfms_prop=sfms_dict)
    sigma_sf_sfr = ScatterLogSFR_sfms(mass[starforming],
                                      redshift[starforming],
                                      sfms_prop=sfms_dict)
    avg_sfr[starforming] = mu_sf_sfr
    delta_sfr[starforming] = sigma_sf_sfr * np.random.randn(ngal_sf)
    sfr[starforming] = mu_sf_sfr + delta_sfr[starforming]
    ssfr[starforming] = sfr[starforming] - mass[starforming]

    if 'subhalogrowth' in sfr_prop.keys():
        # if SFR assignment is dependent on subhalo growth,
        # loop through mass bins and assign SFR by abundance matching the
        # SFR to the Delta M*_sham.
        mass_range = np.arange(mass[starforming].min(),
                               mass[starforming].max(), 0.1)
        m_low = mass_range[:-1]
        m_high = mass_range[1:]
        for im in range(len(m_low)):
            sf_mass_bin = np.where((mass[starforming] > m_low[im])
                                   & (mass[starforming] <= m_high[im]))[0]
            succession, will = Util.intersection_index(
                ancestor_index, ancestor.snap_index[starforming])
            deltaM = descendant.mass[succession] - mass[starforming[will]]
            dM_sort_index = np.argsort(deltaM)

            deltaSFR = sigma_sf_sfr * np.random.randn(len(deltaM))
            dSFR_sort_index = np.argsort(deltaSFR)
            # abundance matched.
            delta_sfr[starforming[
                will[dM_sort_index]]] = deltaSFR[dSFR_sort_index]

        sfr[starforming] = mu_sf_sfr + delta_sfr[starforming]
        ssfr[starforming] = sfr[starforming] - mass[starforming]

    return [sfr_class, sfr, ssfr, delta_sfr, avg_sfr, tQ, MQ]
def DescendantQAplot(nsnap_descendants, **sfinh_kwargs): 
    ''' The ultimate QAplot t rule them all. 4 panels showing all the properties.
    '''
    sfinherit_file = InheritSF_file(nsnap_descendants, **sfinh_kwargs)
    bloodline = Lineage(
            nsnap_ancestor=sfinh_kwargs['nsnap_ancestor'], 
            subhalo_prop=sfinh_kwargs['subhalo_prop']
            )
    if not isinstance(nsnap_descendants, list): 
        nsnap_descendants = [nsnap_descendants]
    bloodline.Read(nsnap_descendants, filename=sfinherit_file)
    
    for nsnap_descendant in nsnap_descendants: 
        descendant = getattr(bloodline, 'descendant_snapshot'+str(nsnap_descendant)) 
        descendant.sfr_prop = sfinh_kwargs['sfr_prop']
        started_here = np.where(descendant.nsnap_genesis == sfinh_kwargs['nsnap_ancestor'])
        start_mass = descendant.mass_genesis[started_here]

        # Mass bins
        mass_bins = np.arange(7., 12.5, 0.5)
        mass_bin_low = mass_bins[:-1]
        mass_bin_high = mass_bins[1:]

        plt.close()
        prettyplot()
        fig = plt.figure(1, figsize=[25,6])
        for i_sub in range(1,5): 
            sub_i = fig.add_subplot(1,4,i_sub) 
            
            if i_sub == 1:  # SMF
                mf = SMF()
                mass, phi = mf.Obj(descendant)

                sub_i.plot(mass, phi, lw=4, c=pretty_colors[descendant.nsnap], label=r'Simulated')

                censub = CentralSubhalos()
                censub.Read(descendant.nsnap, 
                        scatter=sfinh_kwargs['subhalo_prop']['scatter'], 
                        source=sfinh_kwargs['subhalo_prop']['source'], 
                        nsnap_ancestor=sfinh_kwargs['nsnap_ancestor'])

                mass, phi = mf._smf(censub.mass)
                sub_i.plot(mass, phi, c='k', lw=4, ls='--', label='Central Subhalos') 

                sub_i.set_ylim([10**-5, 10**-1])
                sub_i.set_xlim([7.5, 12.0])
                plt.xticks([8., 9., 10., 11., 12.])
                sub_i.set_yscale('log')

                # x,y labels
                sub_i.set_xlabel(r'Mass $\mathtt{M_*}$', fontsize=25) 
                sub_i.set_ylabel(r'Stellar Mass Function $\mathtt{\Phi}$', fontsize=25) 
                sub_i.legend(loc='upper right', frameon=False)

            elif i_sub == 2: # SFMS
                # SMF composition based on their initial mass at nsnap_ancestor 
                for i_m in range(len(mass_bin_low)): 
                    mbin = np.where((start_mass > mass_bin_low[i_m]) & (start_mass <= mass_bin_high[i_m]))

                    sub_i.scatter(
                            descendant.mass[started_here[0][mbin]], 
                            descendant.sfr[started_here[0][mbin]], 
                            color=pretty_colors[i_m], 
                            label=r"$\mathtt{M_{*,i}=}$"+str(round(mass_bin_low[i_m],2))+"-"+str(round(mass_bin_high[i_m],2))
                            )
                
                qfrac = Fq()
                m_arr = np.arange(9.0, 12.5, 0.5)
                sub_i.plot(
                        m_arr, 
                        qfrac.SFRcut(m_arr, descendant.zsnap, sfms_prop=(sfinh_kwargs['sfr_prop'])['sfms']), 
                        c='k', ls='--', lw=4)

                sub_i.set_xlim([9.0, 12.0])
                sub_i.set_ylim([-5.0, 2.0])
                sub_i.set_xlabel(r'$\mathtt{log\;M_*}$', fontsize=25)
                sub_i.set_ylabel(r'$\mathtt{log\;SFR}$', fontsize=25)

            elif i_sub == 3: #SMHM
                for i_m in range(len(mass_bin_low)): 
                    mbin = np.where((start_mass > mass_bin_low[i_m]) & (start_mass <= mass_bin_high[i_m]))

                    sub_i.scatter(
                            descendant.halo_mass[started_here[0][mbin]], 
                            descendant.mass[started_here[0][mbin]], 
                            color=pretty_colors[i_m], 
                            label=r"$\mathtt{M_{*,i}=}$"+str(round(mass_bin_low[i_m],2))+"-"+str(round(mass_bin_high[i_m],2))
                            )

                stellarmass = descendant.mass[started_here]
                halomass = descendant.halo_mass[started_here]
                mbin = np.arange(halomass.min(), halomass.max(), 0.25) 
                mlow = mbin[:-1]
                mhigh = mbin[1:]
                
                muMstar = np.zeros(len(mlow))
                sigMstar = np.zeros(len(mlow))

                for im in range(len(mlow)): 
                    mbin = np.where((halomass > mlow[im]) & (halomass <= mhigh[im]))
                    muMstar[im] = np.mean(stellarmass[mbin])
                    sigMstar[im] = np.std(stellarmass[mbin])
                sub_i.errorbar(0.5*(mlow+mhigh), muMstar, yerr=sigMstar, color='k', lw=3, fmt='o', capthick=2)

                sub_i.set_ylim([9.0, 12.0])
                sub_i.set_xlim([10.0, 15.0])
                sub_i.set_ylabel(r'Stellar Mass $\mathtt{M_*}$', fontsize=25) 
                sub_i.set_xlabel(r'Halo Mass $\mathtt{M_{Halo}}$', fontsize=25) 

                #sub_i.legend(loc='upper left', frameon=False, scatterpoints=1)
            elif i_sub == 4: # Fq
                #mass, fq = descendant.Fq()
                sfq = qfrac.Classify(descendant.mass, descendant.sfr, descendant.zsnap, 
                        sfms_prop=descendant.sfr_prop['sfms'])
                gc = GroupCat(Mrcut=18, position='central')
                gc.Read()
                gc_sfq = qfrac.Classify(gc.mass, gc.sfr, np.mean(gc.z), 
                        sfms_prop=descendant.sfr_prop['sfms'])
                #sub_i.plot(mass, fq, color=pretty_colors[descendant.nsnap], lw=3, ls='--', 
                #        label=r'$\mathtt{z =} '+str(descendant.zsnap)+'$')
                M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
                M_low = M_bin[:-1]
                M_high = M_bin[1:]
                M_mid = 0.5 * (M_low + M_high)

                fq = np.zeros(len(M_low))
                gc_fq = np.zeros(len(M_low))
                for i_m in xrange(len(M_low)):
                    mlim = np.where((descendant.mass > M_low[i_m]) & (descendant.mass <= M_high[i_m]))
                    gc_mlim = np.where((gc.mass > M_low[i_m]) & (gc.mass <= M_high[i_m]))
                    ngal = np.float(len(mlim[0]))
                    gc_ngal = np.float(len(gc_mlim[0]))

                    if ngal != 0:  # no galaxy in mass bin 
                        ngal_q = np.float(len(np.where(sfq[mlim] == 'quiescent')[0]))
                        fq[i_m] = ngal_q/ngal
                    if gc_ngal != 0:
                        gc_ngal_q = np.float(len(np.where(gc_sfq[gc_mlim] == 'quiescent')[0]))
                        gc_fq[i_m] = gc_ngal_q/gc_ngal

                sub_i.plot(M_mid, fq, color=pretty_colors[descendant.nsnap], lw=3, label=r'$\mathtt{z =} '+str(descendant.zsnap)+'$')
                
                fq_model = qfrac.model(M_bin, descendant.zsnap, lit=sfinh_kwargs['sfr_prop']['fq']['name'])
                sub_i.plot(M_bin, fq_model, color='k', lw=4, ls='--', label=sfinh_kwargs['sfr_prop']['fq']['name'])
                sub_i.scatter(M_mid, gc_fq, color='k', s=100, lw=0, label='Group Catalog')

                sub_i.set_xlim([9.0, 12.0])
                sub_i.set_ylim([0.0, 1.0])

                sub_i.set_xlabel(r'Mass $\mathtt{M_*}$') 
                sub_i.set_ylabel(r'Quiescent Fraction $\mathtt{f_Q}$', fontsize=20) 

                sub_i.legend(loc='upper left', frameon=False, scatterpoints=1, markerscale=0.75)

        plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) 
        fig_name = ''.join([
            'figure/test/', 
            'QAplot.',
            '.nsnap', str(nsnap_descendant),  
            '.'.join((sfinherit_file.rsplit('/')[-1]).rsplit('.')[:-1]), 
            '.png'])
        fig.savefig(fig_name, bbox_inches='tight') 
        plt.close()
    return None 
    def plot(self, mass=None, ssfr=None, SSFRdist=None, sfms_prop=None, z=None, **pltkwargs): 
        ''' Plot the sSFR distriubiton 
        '''
        if self.kwargs == {}: 
            kwargs = pltkwargs.copy() 
        else: 
            kwargs = (self.kwargs).copy()
            kwargs.update(pltkwargs)

        if SSFRdist is not None: 
            ssfr_bin_mid, ssfr_dist = SSFRdist
        else: 
            if mass is None or ssfr is None: 
                raise ValueError
            else: 
                Ssfr_obj = Ssfr()
                ssfr_bin_mid, ssfr_dist = Ssfr_obj.Calculate(mass, ssfr)
        
        # loop through mass bin 
        for i_mass, panel_mass in enumerate(self.panel_mass_bins):       
            # plot configuration
            if 'label' in kwargs:   # label 
                hist_label = kwargs['label']
            else: 
                hist_label = None 
            # line color 
            if 'line_color' in kwargs: 
                if isinstance(kwargs['line_color'], str): 
                    line_color = kwargs['line_color']
                elif isinstance(kwargs['line_color'], int): 
                    line_color = self.pretty_colors[kwargs['line_color']]
            else: 
                line_color = 'black'
            # line style
            if 'line_style' in kwargs:
                line_style = kwargs['line_style'] 
            else: 
                line_style = '-'
            # line width
            if 'lw' in kwargs: 
                line_width = kwargs['lw'] 
            else:
                line_width = 4

            if 'alpha' in kwargs: 
                alpha = kwargs['alpha']
            else:
                alpha = 1

            self.subs[i_mass].plot(
                    ssfr_bin_mid[i_mass], 
                    ssfr_dist[i_mass], 
                    color=line_color, 
                    lw=line_width, 
                    ls=line_style, 
                    label=hist_label, 
                    alpha=alpha 
                    ) 
            
            if sfms_prop is not None: 
                if z is None: 
                    raise ValueError
                qf = Fq()
                self.subs[i_mass].vlines(
                        qf.SFRcut(np.array([np.mean(panel_mass)]), z, 
                            sfms_prop=sfms_prop 
                            )-np.mean(panel_mass), 
                        0., 100., 
                        lw=3, linestyle='--', color='k')

            if i_mass == 2: 
                #plt.sca(self.subs[i_mass])
                self.subs[i_mass].set_xticks([-13, -12, -11, -10, -9, -8])
                self.subs[i_mass].set_yticks([0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4])

        return None   
def PlotObservedSSFR(observable, isedfit=False, Peak=False):
    '''
    '''
    prettyplot()
    if Peak and 'groupcat' not in observable:
        raise ValeuError

    mass_bins = [[9.7, 10.1], [10.1, 10.5], [10.5, 10.9], [10.9, 11.3]]
    if 'groupcat' in observable:
        zbins = [0.03]
    elif observable == 'sdssprimus':
        zbins = [0.1, 0.3, 0.5, 0.7, 0.9]

    qf = Fq()

    for i_z, z in enumerate(zbins):
        fig = plt.figure(figsize=(16, 16))
        fig.subplots_adjust(hspace=0., wspace=0.)
        subs = [fig.add_subplot(2, 2, i_mass + 1) for i_mass in xrange(4)]

        # preset kwargs for group and SDSS+PRIMUS catalogs
        if 'groupcat' in observable:
            if 'sat' in observable:
                obs_str = 'Group Catalog Satellites'
                censat = 'satellite'
            elif 'cen' in observable:
                obs_str = 'Group Catalog Centrals'
                censat = 'central'
            file_flag = observable

            if not isedfit:
                kwargs = {'Mrcut': 18, 'position': censat}
                file_flag += ''
            else:
                kwargs = {'Mrcut': 18, 'position': censat, 'isedfit': True}
                obs_str += ' iSEDfit'
                file_flag += 'isedfit'
        else:
            obs_str = 'iSEDfit z=' + str(round(z, 2))
            kwargs = {'redshift': z, 'environment': 'no'}
        # SSFR SF peak fit
        if Peak:
            sfpeakfit = FitObservedSSFR_Peaks(observable=observable,
                                              sfq='star-forming',
                                              **kwargs)
            qpeakfit = FitObservedSSFR_Peaks(observable=observable,
                                             sfq='quiescent',
                                             **kwargs)

            print 'SF Slope ', sfpeakfit[0], ', Offset ', sfpeakfit[1]
            print 'Q Slope ', qpeakfit[0], ', Offset ', qpeakfit[1]

        ssfr_bin_mid, ssfr_dist = ObservedSSFR(observable, **kwargs)

        for i_mass, mbin in enumerate(mass_bins):
            subs[i_mass].plot(ssfr_bin_mid[i_mass],
                              ssfr_dist[i_mass],
                              color='k',
                              lw=4,
                              ls='-',
                              label=None)
            if Peak:
                ssfr_sfpeak = sfpeakfit[0] * (
                    np.mean(mbin) - 10.5) + sfpeakfit[1] - np.mean(mbin)
                subs[i_mass].vlines(ssfr_sfpeak,
                                    0.0,
                                    100.,
                                    lw=3,
                                    linestyle='--',
                                    color='blue')

                ssfr_qpeak = qpeakfit[0] * (np.mean(mbin) -
                                            10.5) + qpeakfit[1] - np.mean(mbin)
                subs[i_mass].vlines(ssfr_qpeak,
                                    0.0,
                                    100.,
                                    lw=3,
                                    linestyle='--',
                                    color='red')

            subs[i_mass].vlines(qf.SFRcut(np.array([np.mean(mbin)]),
                                          z,
                                          sfms_prop={
                                              'name': 'kinked',
                                              'mslope_lowmass': 0.7,
                                              'zslope': 1.5
                                          }) - np.mean(mbin),
                                0.,
                                100.,
                                lw=3,
                                linestyle='--',
                                color='k')

            subs[i_mass].set_xlim([-13.0, -7.0])
            subs[i_mass].set_ylim([0.0, 1.6])

            massbin_str = ''.join([
                r'$\mathtt{log \; M_{*} = [',
                str(mass_bins[i_mass][0]), ',\;',
                str(mass_bins[i_mass][1]), ']}$'
            ])
            subs[i_mass].text(-10.5, 1.4, massbin_str, fontsize=24)

            if i_mass == 0:
                subs[i_mass].set_ylabel(r'$\mathtt{P(log \; SSFR)}$',
                                        fontsize=20)
                subs[i_mass].set_xticklabels([])
            elif i_mass == 1:
                subs[i_mass].set_xticklabels([])
                subs[i_mass].set_yticklabels([])
            elif i_mass == 2:
                #sub.set_yticklabels([])
                subs[i_mass].set_ylabel(r'$\mathtt{P(log \; SSFR)}$',
                                        fontsize=20)
                subs[i_mass].set_xlabel(r'$\mathtt{log \; SSFR \;[yr^{-1}]}$',
                                        fontsize=20)
            else:
                subs[i_mass].set_yticklabels([])
                subs[i_mass].set_xlabel(r'$\mathtt{log \; SSFR \;[yr^{-1}]}$',
                                        fontsize=20)

                #subs[i_mass].legend(loc='lower right', frameon=False)

        fig_file = ''.join([
            'figure/test/'
            'observedSSFR', observable, '.z',
            str(round(z, 2)), '.png'
        ])

        fig.savefig(fig_file, bbox_inches='tight', dpi=150)
    return None
Exemple #24
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
def DescendantSFMS_composition(nsnap_descendant,
                               abc_step=29,
                               bovyplot=False,
                               **sfinh_kwargs):
    ''' SFMS for the SF Inherited Descendant galaxy population  
    '''
    sfinherit_file = InheritSF_file(nsnap_descendant,
                                    abc_step=abc_step,
                                    **sfinh_kwargs)
    bloodline = Lineage(nsnap_ancestor=sfinh_kwargs['nsnap_ancestor'],
                        subhalo_prop=sfinh_kwargs['subhalo_prop'])
    bloodline.Read([nsnap_descendant], filename=sfinherit_file)

    descendant = getattr(bloodline,
                         'descendant_snapshot' + str(nsnap_descendant))
    if not bovyplot:
        sfms_plot = descendant.plotSFMS(bovyplot=False, scatter=False)

        # SMF composition based on their initial mass at nsnap_ancestor
        started_here = np.where(
            descendant.nsnap_genesis == sfinh_kwargs['nsnap_ancestor'])
        start_mass = descendant.mass_genesis[started_here]

        mass_bins = np.arange(7., 12.5, 0.5)
        mass_bin_low = mass_bins[:-1]
        mass_bin_high = mass_bins[1:]

        for i_m in range(len(mass_bin_low)):

            mbin = np.where((start_mass > mass_bin_low[i_m])
                            & (start_mass <= mass_bin_high[i_m]))

            sfms_plot.plot(
                mass=descendant.mass[started_here[0][mbin]],
                sfr=descendant.sfr[started_here[0][mbin]],
                sfr_class=descendant.sfr_class[started_here[0][mbin]],
                gal_class='quiescent',
                bovyplot=False,
                sigSFR=False,
                color=i_m,
                label=r"$\mathtt{M_{*,i}=}$" +
                str(round(mass_bin_low[i_m], 2)) + "-" +
                str(round(mass_bin_high[i_m], 2)))

        qfrac = Fq()
        m_arr = np.arange(9.0, 12.5, 0.5)
        sfms_plot.sub.plot(m_arr,
                           qfrac.SFRcut(
                               m_arr,
                               descendant.zsnap,
                               sfms_prop=(sfinh_kwargs['sfr_prop'])['sfms']),
                           c='k',
                           ls='--',
                           lw=4)
        bovyplot_str = ''
    else:
        sfms_plot = descendant.plotSFMS(bovyplot=True)
        bovyplot_str = '.bovy'

    sfms_plot.sub.legend(loc='lower right', scatterpoints=1)
    fig_file = ''.join([
        'figure/test/', 'SFMS_composition.', '.'.join(
            (sfinherit_file.rsplit('/')[-1]).rsplit('.')[:-1]), bovyplot_str,
        '.png'
    ])
    sfms_plot.save_fig(fig_file)
    return None
Exemple #26
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 QAplot(descendant, sfinh_kwargs, fig_name=None, **kwargs):
    ''' Given galpop object and the SF Inheritance parametes, 
    plot its SMF, 
    '''
    # Mass bins
    mass_bins = np.arange(7., 12.5, 0.5)
    mass_bin_low = mass_bins[:-1]
    mass_bin_high = mass_bins[1:]

    plt.close()
    prettyplot()
    fig = plt.figure(1, figsize=[20,12])
    for i_sub in range(1,6): 
        sub_i = fig.add_subplot(2,3,i_sub) 
    
        if i_sub == 1:  # SMF
            mf = SMF()
            mass, phi = mf.Obj(descendant)

            sub_i.plot(mass, phi, lw=4, c='r', label=r'Simulated')

            censub = CentralSubhalos()
            censub.Read(descendant.nsnap, 
                    scatter=sfinh_kwargs['subhalo_prop']['scatter'], 
                    source=sfinh_kwargs['subhalo_prop']['source'], 
                    nsnap_ancestor=sfinh_kwargs['nsnap_ancestor'])

            mass, phi = mf._smf(censub.mass)
            sub_i.plot(mass, phi, c='k', lw=4, ls='--', label='Central Subhalos') 

            sub_i.set_ylim([10**-5, 10**-1])
            sub_i.set_xlim([7.5, 12.0])
            plt.xticks([8., 9., 10., 11., 12.])
            sub_i.set_yscale('log')

            # x,y labels
            sub_i.set_ylabel(r'Stellar Mass ($\mathtt{log\; M_*}$)', fontsize=20) 
            sub_i.set_ylabel(r'Stellar Mass Function $\mathtt{\Phi}$', fontsize=20) 
            sub_i.legend(loc='upper right', frameon=False)

        elif i_sub == 2: # SFMS
            # SMF composition based on their initial mass at nsnap_ancestor 

            # random subsample 
            n_tot = len(descendant.mass)
            r_subsample = np.random.choice(n_tot, size=100000)
            sub_i.scatter(descendant.mass[r_subsample], descendant.sfr[r_subsample], color='b', s=0.5) 

            qfrac = Fq()
            m_arr = np.arange(9.0, 12.5, 0.5)
            sub_i.plot(
                    m_arr, 
                    qfrac.SFRcut(m_arr, descendant.zsnap, sfms_prop=(sfinh_kwargs['sfr_prop'])['sfms']), 
                    c='k', ls='--', lw=4)

            sub_i.text(10.5, -4., r'$\mathtt{z='+str(descendant.zsnap)+'}$', fontsize=25)

            sub_i.set_xlim([9.0, 12.0])
            sub_i.set_ylim([-5.0, 2.0])
            sub_i.set_ylabel(r'Stellar Mass ($\mathtt{log\; M_*}$)', fontsize=20) 
            sub_i.set_ylabel(r'$\mathtt{log\;SFR}$', fontsize=20)

        elif i_sub == 3: #SMHM
            #corner.hist2d(descendant.halo_mass, descendant.mass, ax=sub_i, c='b', fill_contours=True, smooth=1.0)
            n_tot = len(descendant.mass)
            r_subsample = np.random.choice(n_tot, size=100000)
            sub_i.scatter(descendant.halo_mass[r_subsample], descendant.mass[r_subsample], color='b', s=0.5)

            stellarmass = descendant.mass
            halomass = descendant.halo_mass
            mbin = np.arange(halomass.min(), halomass.max(), 0.25) 
            mlow = mbin[:-1]
            mhigh = mbin[1:]
            
            muMstar = np.zeros(len(mlow))
            sigMstar = np.zeros(len(mlow))

            for im in range(len(mlow)): 
                mbin = np.where((halomass > mlow[im]) & (halomass <= mhigh[im]))
                muMstar[im] = np.mean(stellarmass[mbin])
                sigMstar[im] = np.std(stellarmass[mbin])
            sub_i.errorbar(0.5*(mlow+mhigh), muMstar, yerr=sigMstar, color='k', lw=3, fmt='o', capthick=2)

            sub_i.set_ylim([9.0, 12.0])
            sub_i.set_xlim([10.0, 15.0])
            sub_i.set_ylabel(r'Stellar Mass ($\mathtt{log\; M_*}$)', fontsize=20) 
            sub_i.set_xlabel(r'Halo Mass ($\mathtt{log\;M_{Halo}}$)', fontsize=20) 

        elif i_sub == 4: # Fq
            sfq = qfrac.Classify(descendant.mass, descendant.sfr, descendant.zsnap, 
                    sfms_prop=descendant.sfr_prop['sfms'])
            gc = GroupCat(Mrcut=18, position='central')
            gc.Read()
            gc_sfq = qfrac.Classify(gc.mass, gc.sfr, np.mean(gc.z), 
                    sfms_prop=descendant.sfr_prop['sfms'])
            #sub_i.plot(mass, fq, color=pretty_colors[descendant.nsnap], lw=3, ls='--', 
            #        label=r'$\mathtt{z =} '+str(descendant.zsnap)+'$')
            M_bin = np.array([9.7, 10.1, 10.5, 10.9, 11.3])
            M_low = M_bin[:-1]
            M_high = M_bin[1:]
            M_mid = 0.5 * (M_low + M_high)

            fq = np.zeros(len(M_low))
            gc_fq = np.zeros(len(M_low))
            for i_m in xrange(len(M_low)):
                mlim = np.where((descendant.mass > M_low[i_m]) & (descendant.mass <= M_high[i_m]))
                gc_mlim = np.where((gc.mass > M_low[i_m]) & (gc.mass <= M_high[i_m]))
                ngal = np.float(len(mlim[0]))
                gc_ngal = np.float(len(gc_mlim[0]))

                if ngal != 0:  # no galaxy in mass bin 
                    ngal_q = np.float(len(np.where(sfq[mlim] == 'quiescent')[0]))
                    fq[i_m] = ngal_q/ngal
                if gc_ngal != 0:
                    gc_ngal_q = np.float(len(np.where(gc_sfq[gc_mlim] == 'quiescent')[0]))
                    gc_fq[i_m] = gc_ngal_q/gc_ngal

            sub_i.plot(M_mid, fq, color='r', lw=3, label=r'Simulated')
            
            # fQ of the Group Catalog 
            sub_i.scatter(M_mid, gc_fq, color='k', s=100, lw=0, label='Group Catalog')
            # fQ of the model (input) fQ
            fq_model = qfrac.model(M_bin, descendant.zsnap, lit=sfinh_kwargs['sfr_prop']['fq']['name'])
            sub_i.plot(M_bin, fq_model, 
                    color='k', lw=4, ls='--', 
                    label=sfinh_kwargs['sfr_prop']['fq']['name'].replace('_', ' ').title() 
                    )

            sub_i.set_xlim([9.0, 12.0])
            sub_i.set_ylim([0.0, 1.0])

            sub_i.set_ylabel(r'Stellar Mass ($\mathtt{log\; M_*}$)', fontsize=20) 
            sub_i.set_ylabel(r'Quiescent Fraction $\mathtt{f_Q}$', fontsize=20) 

            sub_i.legend(loc='upper left', frameon=False, scatterpoints=1, markerscale=0.75)

        elif i_sub == 5: # Tau_Q(M*)
            mass_bin = np.arange(9.0, 12.0, 0.1)

            tau_m = getTauQ(mass_bin, tau_prop=sfinh_kwargs['evol_prop']['tau']) 
            sub_i.plot(10**mass_bin, tau_m, color='r', lw=4, label='Simulated') 

            if 'taus' in kwargs: 
                tau_slopes, tau_offsets = kwargs['taus']

                i_tau_ms = np.zeros((len(mass_bin), len(tau_slopes)))
                for i_tau in range(len(tau_slopes)): 
                    i_tau_prop = sfinh_kwargs['evol_prop']['tau'].copy()
                    i_tau_prop['slope'] = tau_slopes[i_tau]
                    i_tau_prop['yint'] = tau_offsets[i_tau]

                    i_tau_m = getTauQ(mass_bin, tau_prop=i_tau_prop) 
                    #sub_i.plot(10**mass_bin, i_tau_m, color='r', alpha=0.1, lw=2) 
                    i_tau_ms[:,i_tau] = i_tau_m
            
                sig_tau = np.zeros(len(mass_bin))
                for im in range(len(mass_bin)):
                    sig_tau[im] = np.std(i_tau_ms[im,:])
                
                sub_i.errorbar(10**mass_bin, tau_m, yerr=sig_tau, color='r', lw=4) 

            # satellite quenching fraction for comparison 
            tau_sat = getTauQ(mass_bin, tau_prop={'name': 'satellite'}) 

            sub_i.plot(10**mass_bin, tau_sat, color='black', lw=4, ls='--', label=r'Satellite (Wetzel+ 2013)') 

            sub_i.set_xlim([10**9.5, 10**11.75])
            sub_i.set_ylim([0.0, 2.0])
            sub_i.set_xscale('log')

            sub_i.set_xlabel(r'Stellar Mass $[\mathtt{M_\odot}]$',fontsize=20) 
            sub_i.legend(loc='upper right') 

            sub_i.set_ylabel(r'Quenching Timescales $(\tau_\mathtt{Q})$ [Gyr]',fontsize=20) 
        
    plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) 
    if fig_name is None: 
        plt.show()
        plt.close()
    else: 
        fig.savefig(fig_name, bbox_inches='tight') 
        plt.close()
    return None