Example #1
0
def c2_Zhao09(Mv, t):
    """
    Halo concentration from the mass assembly history, using the Zhao+09
    relation.
    
    Syntax:
    
        c2_Zhao09(Mv,t)
        
    where
    
        Mv: main-branch virial mass history [M_sun] (array)
        t: the time series of the main-branch mass history (array of the
            same size as Mv)
    
    Note that we need Mv and t in reverse chronological order, i.e., in 
    decreasing order, such that Mv[0] and t[0] is the instantaneous halo
    mass and time.
    
    Note that Mv is the Bryan and Norman 98 M_vir.
    
    Return:
        
        halo concentration c R_vir / r_-2 (float)
    """
    idx = aux.FindNearestIndex(Mv, 0.04 * Mv[0])
    return 4. * (1. + (t[0] / (3.75 * t[idx]))**8.4)**0.125
Example #2
0
    #---update tprevious
    tprevious = t

t2 = time.time()
print('    time = %.4f'%(t2-t1))

#---a few other quantities for plot
Vv = Vcirc(potential,hDekel.rh) # host halo virial velocity
#if radius.min() < r_min:
#    idx = aux.FindNearestIndex(radius,r_min)
#    tfinal = timesteps[idx]
#else:
#    tfinal = tfinaltdyn0*tdyn0
#    idx = aux.FindNearestIndex(timesteps,tfinal)
tfinal = tfinaltdyn0*tdyn0 # <<< test  
idx = aux.FindNearestIndex(timesteps,tfinal) # <<< test
mfinal = mass[idx]
print('    m(t_final)/m(0)=%.4f'%(mfinal/mv0))

########################### diagnostic plots ############################

print('>>> plot ...')
plt.close('all') # close all previous figure windows

#------------------------------------------------------------------------

# set up the figure window
fig1 = plt.figure(figsize=(16,9), dpi=80, facecolor='w', edgecolor='k') 
fig1.subplots_adjust(left=0.06, right=0.95,bottom=0.06, top=0.99,
    hspace=0.25, wspace=0.47)
gs = gridspec.GridSpec(3, 5) 
Example #3
0
def loop(itree): 
    """
    Replaces the loop "for itree in range(Ntree):", for parallelization.
    """
    time_start_tmp = time.time()
    
    np.random.seed() # [important!] reseed the random number generator
    
    lgM0 = lgM0_lo + np.random.random()*(lgM0_hi-lgM0_lo)
    cfg.M0 = 10.**lgM0
    cfg.z0 = z0
    cfg.Mres = 10.**lgMres 
    cfg.Mmin = 0.04*cfg.Mres
    
    k = 0               # the level, k, of the branch being considered
    ik = 0              # how many level-k branches have been finished
    Nk = 1              # total number of level-k branches
    Nbranch = 1         # total number of branches in the current tree
    
    Mak = [cfg.M0]      # accretion masses of level-k branches
    zak = [cfg.z0]
    idk = [0]           # branch ids of level-k branches
    ipk = [-1]          # parent ids of level-k branches (-1: no parent) 
    
    Mak_tmp = []
    zak_tmp = []
    idk_tmp = []
    ipk_tmp = []
    
    mass = np.zeros((cfg.Nmax,cfg.Nz)) - 99.
    order = np.zeros((cfg.Nmax,cfg.Nz),np.int8) - 99
    ParentID = np.zeros((cfg.Nmax,cfg.Nz),np.int16) - 99
    
    VirialRadius = np.zeros((cfg.Nmax,cfg.Nz),np.float32) - 99.
    concentration = np.zeros((cfg.Nmax,cfg.Nz),np.float32) - 99.
    DekelConcentration = np.zeros((cfg.Nmax,cfg.Nz),np.float32) - 99.
    DekelSlope = np.zeros((cfg.Nmax,cfg.Nz),np.float32) - 99.
    
    StellarMass = np.zeros((cfg.Nmax,cfg.Nz)) - 99. 
    StellarSize = np.zeros((cfg.Nmax,cfg.Nz),np.float32) - 99. 
    
    coordinates = np.zeros((cfg.Nmax,cfg.Nz,6),np.float32)
    
    while True: # loop over branches, until the full tree is completed.
    # Starting from the main branch, draw progenitor(s) using the 
    # Parkinson+08 algorithm. When there are two progenitors, the less 
    # massive one is the root of a new branch. We draw branches level by
    # level, i.e., When a new branch occurs, we record its root, but keep 
    # finishing the current branch and all the branches of the same level
    # as the current branch, before moving on to the next-level branches.
    
        M = [Mak[ik]]   # mass history of current branch in fine timestep
        z = [zak[ik]]   # the redshifts of the mass history
        cfg.M0 = Mak[ik]# descendent mass
        cfg.z0 = zak[ik]# descendent redshift
        id = idk[ik]    # branch id
        ip = ipk[ik]    # parent id
        
        while cfg.M0>cfg.Mmin:
        
            if cfg.M0>cfg.Mres: zleaf = cfg.z0 # update leaf redshift
        
            co.UpdateGlobalVariables(**cfg.cosmo)
            M1,M2,Np = co.DrawProgenitors(**cfg.cosmo)
            
            # update descendent halo mass and descendent redshift 
            cfg.M0 = M1
            cfg.z0 = cfg.zW_interp(cfg.W0+cfg.dW)
            if cfg.z0>cfg.zmax: break
            
            if Np>1 and cfg.M0>cfg.Mres: # register next-level branches
            
                Nbranch += 1
                Mak_tmp.append(M2)
                zak_tmp.append(cfg.z0)
                idk_tmp.append(Nbranch)
                ipk_tmp.append(id)
            
            # record the mass history at the original time resolution
            M.append(cfg.M0)
            z.append(cfg.z0)
        
        # Now that a branch is fully grown, do some book-keeping
        
        # convert mass-history list to array 
        M = np.array(M)
        z = np.array(z)
        
        # downsample the fine-step mass history, M(z), onto the
        # coarser output timesteps, cfg.zsample     
        Msample,zsample = aux.downsample(M,z,cfg.zsample)
        iz = aux.FindClosestIndices(cfg.zsample,zsample)
        izleaf = aux.FindNearestIndex(cfg.zsample,zleaf)
        
        # compute halo structure throughout time on the coarse grid, up
        # to the leaf point
        t = co.t(z,cfg.h,cfg.Om,cfg.OL)
        c,a,c2,Rv = [],[],[],[]
        for i in iz:
            if i > (izleaf+1): break # only compute structure below leaf
            msk = z>=cfg.zsample[i]
            if True not in msk: break # safety
            ci,ai,Msi,c2i,c2DMOi = init.Dekel_fromMAH(M[msk],t[msk],
                cfg.zsample[i],HaloResponse=HaloResponse)
            Rvi = init.Rvir(M[msk][0],Delta=200.,z=cfg.zsample[i])
            c.append(ci)
            a.append(ai)
            c2.append(c2i)
            Rv.append(Rvi)
            if i==iz[0]: Ms = Msi
            #print('    i=%6i,ci=%8.2f,ai=%8.2f,log(Msi)=%8.2f,c2i=%8.2f'%\
            #    (i,ci,ai,np.log10(Msi),c2i)) # <<< for test
        if len(c)==0: # <<< safety, dealing with rare cases where the 
            # branch's root z[0] is close to the maximum redshift -- when
            # this happens, the mass history has only one element, and 
            # z[0] can be slightly above cfg.zsample[i] for the very 
            # first iteration, leaving the lists c,a,c2,Rv never updated
            ci,ai,Msi,c2i=init.Dekel_fromMAH(M,t,z[0],
                HaloResponse=HaloResponse)
            c.append(ci)
            a.append(ai)
            c2.append(c2i)
            Rv.append(Rvi)
            Ms = Msi
            # <<< test
            #print('    branch root near max redshift: z=%7.2f,log(M)=%7.2f,c=%7.2f,a=%7.2f,c2=%7.2f,log(Ms)=%7.2f'%\
            #    (z[0],np.log10(M[0]),c[0],a[0],c2[0],np.log10(Ms))) 
        c = np.array(c)
        a = np.array(a) 
        c2 = np.array(c2) 
        Rv = np.array(Rv)
        Nc = len(c2) # length of a branch over which c2 is computed 
        
        # compute stellar size at the root of the branch, i.e., at the 
        # accretion epoch (z[0])
        Re = init.Reff(Rv[0],c2[0])
        
        # use the redshift id and parent-branch id to access the parent
        # branch's information at our current branch's accretion epoch,
        # in order to initialize the orbit
        if ip==-1: # i.e., if the branch is the main branch
            xv = np.zeros(6)
        else:
            Mp = mass[ip,iz[0]]
            cp = DekelConcentration[ip,iz[0]]
            ap = DekelSlope[ip,iz[0]]
            hp = Dekel(Mp,cp,ap,Delta=200.,z=zsample[0])
            eps = 1./np.pi*np.arccos(1.-2.*np.random.random())
            xv = init.orbit(hp,xc=1.,eps=eps)
        
        # <<< test
        #print('    id=%6i,k=%2i,z=%7.2f,log(M)=%7.2f,c=%7.2f,a=%7.2f,c2=%7.2f,log(Ms)=%7.2f,Re=%7.2f,xv=%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f'%\
        #    (id,k,z[0],np.log10(M[0]),c[0],a[0],c2[0],np.log10(Ms),Re, xv[0],xv[1],xv[2],xv[3],xv[4],xv[5]))
        
        # update the arrays for output
        mass[id,iz] = Msample
        order[id,iz] = k
        ParentID[id,iz] = ip
        
        VirialRadius[id,iz[0]:iz[0]+Nc] = Rv
        concentration[id,iz[0]:iz[0]+Nc] = c2
        DekelConcentration[id,iz[0]:iz[0]+Nc] = c
        DekelSlope[id,iz[0]:iz[0]+Nc] = a

        StellarMass[id,iz[0]] = Ms
        StellarSize[id,iz[0]] = Re
        
        coordinates[id,iz[0],:] = xv
                
        # Check if all the level-k branches have been dealt with: if so, 
        # i.e., if ik==Nk, proceed to the next level.
        ik += 1
        if ik==Nk: # all level-k branches are done!
            Mak = Mak_tmp
            zak = zak_tmp
            idk = idk_tmp
            ipk = ipk_tmp
            Nk = len(Mak)
            ik = 0
            Mak_tmp = []
            zak_tmp = []
            idk_tmp = []
            ipk_tmp = []
            if Nk==0: 
                break # jump out of "while True" if no next-level branch 
            k += 1 # update level
    
    # trim and output 
    mass = mass[:id+1,:]
    order = order[:id+1,:]
    ParentID = ParentID[:id+1,:]
    VirialRadius = VirialRadius[:id+1,:]
    concentration = concentration[:id+1,:]
    DekelConcentration = DekelConcentration[:id+1,:]
    DekelSlope = DekelSlope[:id+1,:]
    StellarMass = StellarMass[:id+1,:]
    StellarSize = StellarSize[:id+1,:]
    coordinates = coordinates[:id+1,:,:]
    np.savez(outfile1%(itree,lgM0), 
        redshift = cfg.zsample,
        CosmicTime = cfg.tsample,
        mass = mass,
        order = order,
        ParentID = ParentID,
        VirialRadius = VirialRadius,
        concentration = concentration,
        DekelConcentration = DekelConcentration,
        DekelSlope = DekelSlope,
        #VirialOverdensity = VirialOverdensity, # <<< no need in TreeGen
        StellarMass = StellarMass,
        StellarSize = StellarSize,
        coordinates = coordinates,
        )
            
    time_end_tmp = time.time()
    print('    Tree %5i: log(M_0)=%6.2f, %6i branches, %2i order, %8.1f sec'\
        %(itree,lgM0,Nbranch,k,time_end_tmp-time_start_tmp))
Example #4
0
def loop(file):
    """
    Replaces the loop "for file in files:", for parallelization.
    """

    time_start_tmp = time.time()

    #---load trees
    f = np.load(file)
    redshift = f['redshift']
    CosmicTime = f['CosmicTime']
    mass = f['mass']
    order = f['order']
    ParentID = f['ParentID']
    VirialRadius = f['VirialRadius']
    concentration = f['concentration']
    DekelConcentration = f['DekelConcentration']
    DekelSlope = f['DekelSlope']
    StellarMass = f['StellarMass']
    StellarSize = f['StellarSize']
    coordinates = f['coordinates']
    VirialOverdensity = np.zeros(mass.shape, np.float32) + 200.
    MaxCircularVelocity = np.zeros(mass.shape, np.float32) - 99.

    #---identify the roots of the branches
    izroot = mass.argmax(axis=1)  # root-redshift ids of all the branches
    idx = np.arange(mass.shape[0])  # branch ids of all the branches
    levels = np.unique(order[order > 0])  # all >0 levels in the tree

    for level in levels:  # loop over levels from low to high

        for id in idx:  # loop over branches

            iza = izroot[id]
            if order[id, iza] != level: continue  # level by level

            #---initialize

            # read satellite properties at accretion
            za = redshift[iza]
            ta = CosmicTime[iza]
            ma = mass[id, iza]
            ka = order[id, iza]
            ipa = ParentID[id, iza]
            ca = DekelConcentration[id, iza]
            aa = DekelSlope[id, iza]
            c2a = concentration[id, iza]
            Da = VirialOverdensity[id, iza]
            msa = StellarMass[id, iza]
            lea = StellarSize[id, iza]
            xva = coordinates[id, iza, :]

            # initialize satellite and orbit
            s = Dekel(ma, ca, aa, Delta=Da, z=za)
            o = orbit(xva)

            # initialize quantities repeatedly used for tidal tracks
            s001a = s.sh
            lma = s.rmax
            vma = s.Vcirc(lma)
            lealma = lea / lma
            mma = s.M(lma)

            MaxCircularVelocity[id, iza] = vma

            # initialize instantaneous quantities to be updated
            m = ma
            r = np.sqrt(xva[0]**2 + xva[2]**2)
            k = ka
            ip = ipa
            trelease = ta  # cosmic time at lastest release
            tprevious = ta  # cosmic time at previous timestep

            #---evolve
            for iz in np.arange(iza)[::-1]:  # loop over time to evolve

                z = redshift[iz]
                tcurrent = CosmicTime[iz]

                #---time since in current host
                t = tcurrent - trelease

                #---timestep
                dt = tcurrent - tprevious

                #---update host potential
                Mp = mass[ip, iz]
                cp = DekelConcentration[ip, iz]
                ap = DekelSlope[ip, iz]
                Dp = VirialOverdensity[ip, iz]
                if (fd > 0.) and (k == 1):

                    c2p = concentration[ip, iz]
                    Rvp = VirialRadius[ip, iz]
                    Rep = gh.Reff(Rvp, c2p)
                    adp = 0.766421 / (1. + 1. / flattening) * Rep
                    bdp = adp / flattening
                    Mdp = fd * Mp
                    p = [
                        Dekel(Mp, cp, ap, Delta=Dp, z=z),
                        MN(Mdp, adp, bdp),
                    ]

                else:

                    p = [
                        Dekel(Mp, cp, ap, Delta=Dp, z=z),
                    ]

                #---evolve orbit
                if r > cfg.Rres:

                    o.integrate(t, p, m)
                    xv = o.xv  # note that the coordinates are updated
                    # internally in the orbit instance "o" when calling
                    # the ".integrate" method, here we assign them to
                    # a new variable "xv" only for bookkeeping

                else:  # i.e., the satellite has merged to its host, so
                    # no need for orbit integration; to avoid potential
                    # numerical issues, we assign a dummy coordinate that
                    # is almost zero but not exactly zero

                    xv = np.array([cfg.Rres, 0., 0., 0., 0., 0.])

                r = np.sqrt(xv[0]**2 + xv[2]**2)

                #---evolve satellite
                if m > cfg.Mres:

                    # evolve subhalo properties
                    m, lt = ev.msub(s,
                                    p,
                                    xv,
                                    dt,
                                    choice='King62',
                                    alpha=StrippingEfficiency)
                    a = s.alphah  # assume constant innermost slope
                    c, D = ev.Dekel2(m, ma, lma, vma, aa, s001a, z=z)
                    s = Dekel(m, c, a, Delta=D, z=z)
                    lh = s.rh
                    c2 = s.rh / s.r2
                    s001 = s.sh
                    vm = s.Vcirc(s.rmax)

                    # evolve baryonic properties
                    mm = s.M(s.rmax)  # update m_max
                    g_le, g_ms = ev.g_EPW18(mm / mma, s001a, lealma)
                    le = lea * g_le
                    ms = msa * g_ms
                    ms = min(ms, m)  # <<< safety, perhaps rarely triggered

                else:  # we do nothing about disrupted satellite, s.t.,
                    # its properties right before disruption would be
                    # stored in the output arrays

                    pass

                #---if order>1, determine if releasing this high-order
                #   subhalo to its grandparent-host, and if releasing,
                #   update the orbit instance
                if k > 1:

                    if (r > VirialRadius[ip, iz]) & (iz <= izroot[ip]):
                        # <<< play with the release condition: for now,
                        # release if the instant orbital radius is larger
                        # than the virial radius of the immediate host,
                        # and if the immediate host is already a
                        # satellite of the grandparent-host

                        xv = coordinates[ip, iz, :]  # <<< may change later:
                        # for now, the released satellite picks up
                        # the coordinates of its immediate parent
                        o = orbit(xv)
                        ip = ParentID[ip, iz]  # update parent id
                        k = k - 1  # update instant order
                        trelease = tcurrent  # update release time

                #---update the arrays for output
                mass[id, iz] = m
                order[id, iz] = k
                ParentID[id, iz] = ip
                VirialRadius[id, iz] = lh
                concentration[id, iz] = c2
                DekelConcentration[id, iz] = c
                DekelSlope[id, iz] = a
                VirialOverdensity[id, iz] = D
                MaxCircularVelocity[id, iz] = vm
                StellarMass[id, iz] = ms
                StellarSize[id, iz] = le
                coordinates[id, iz, :] = xv

                #---update tprevious
                tprevious = tcurrent

                # <<< test
                #print('    id=%5i,k=%2i,ip=%5i,z=%6.2f,r=%9.4f,log(m)=%6.2f,D=%7.1f,c2=%6.2f,s001=%5.2f,log(ms)=%6.2f,le=%7.2f,xv=%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f'%\
                #    (id,k,ip,z,r,np.log10(m),D,c2,s001,np.log10(ms),le, xv[0],xv[1],xv[2],xv[3],xv[4],xv[5]))

    #---output
    outfile = outdir + file[len(datadir):]
    np.savez(
        outfile,
        redshift=redshift,
        CosmicTime=CosmicTime,
        mass=mass,
        order=order,
        ParentID=ParentID,
        VirialRadius=VirialRadius,
        concentration=concentration,
        DekelConcentration=DekelConcentration,
        DekelSlope=DekelSlope,
        VirialOverdensity=VirialOverdensity,
        MaxCircularVelocity=MaxCircularVelocity,
        StellarMass=StellarMass,
        StellarSize=StellarSize,
        coordinates=coordinates,
    )

    #---on-screen prints
    M0 = mass[0, 0]
    m0 = mass[:, 0][1:]
    Ms0 = StellarMass[0, 0]
    ms0 = StellarMass[:, 0][1:]

    msk = (m0 > 1e-4 * M0) & (m0 < M0)  # <<< latter condition to be removed
    fsub = m0[msk].sum() / M0
    fstar = ms0[msk].sum() / Ms0

    MAH = mass[0, :]
    iz50 = aux.FindNearestIndex(MAH, 0.5 * M0)
    z50 = redshift[iz50]

    time_end_tmp = time.time()
    print('    %s: %5.2f min, z50=%5.2f,fsub(>1e-4)=%8.5f,fstar=%8.5f'%\
        (outfile,(time_end_tmp-time_start_tmp)/60.,z50,fsub,fstar))