示例#1
0
def calc_LogU(nuin0, specin0, age, zmet, T, mstar=1.0, file_output=True):
    '''
    Claculates the number of lyman ionizing photons for given a spectrum
    Input spectrum must be in ergs/s/Hz!!
    Q = int(Lnu/hnu dnu, nu_0, inf) , number of hydrogen ionizing photons
    mstar is in units of solar mass
    Rin is in units of cm-3
    nh is in units of cm-3
    '''

    c = constants.c.cgs.value  # cm/s
    h = constants.h.cgs.value  # erg/s
    alpha = 2.5e-13*((T/(10**4))**(-0.85)) # cm3/s
    lam_0 = 911.6 * 1e-8  # Halpha wavelength in cm
    nh = cfg.par.HII_nh

    nuin = np.asarray(nuin0)
    specin = np.asarray(specin0)
    nu_0 = c / lam_0
    inds, = np.where(nuin >= nu_0)
    hlam, hflu = nuin[inds], specin[inds]
    nu = hlam[::-1]
    f_nu = hflu[::-1]
    integrand = f_nu / (h * nu)
    Q = simps(integrand, x=nu)*mstar
    U = (np.log10((Q*nh*(alpha**2))/(4*np.pi*(c**3))))*(1./3.)
    if file_output:
        logu_diagnostic(U, Q, mstar, age, zmet)   #Saves important parameters in an output file.
    return U
示例#2
0
def newstars_gen(stars_list):
    global sp
    if sp is None:
        sp = fsps.StellarPopulation()

    #the newstars (particle type 4; so, for cosmological runs, this is all
    #stars) are calculated in a separate function with just one argument so that it is can be fed
    #into pool.map for multithreading.
    #sp = fsps.StellarPopulation()
    sp.params["tage"] = stars_list[0].age
    sp.params["imf_type"] = cfg.par.imf_type
    sp.params["pagb"] = cfg.par.pagb
    sp.params["sfh"] = 0
    sp.params["zmet"] = stars_list[0].fsps_zmet
    sp.params["add_neb_emission"] = False
    sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model
    sp.params['gas_logu'] = cfg.par.gas_logu

    if cfg.par.FORCE_gas_logz == False:
        sp.params['gas_logz'] = np.log10(stars_list[0].metals / cfg.par.solar)
    else:
        sp.params['gas_logz'] = cfg.par.gas_logz

    #first figure out how many wavelengths there are

    spec = sp.get_spectrum(tage=stars_list[0].age,
                           zmet=stars_list[0].fsps_zmet)
    nu = 1.e8 * constants.c.cgs.value / spec[0]

    nlam = len(nu)

    stellar_nu = np.zeros([nlam])
    stellar_fnu = np.zeros([len(stars_list), nlam])

    minage = 13  #Gyr
    for i in range(len(stars_list)):
        if stars_list[i].age < minage:
            minage = stars_list[i].age

    tesc_age = np.log10((minage + cfg.par.birth_cloud_clearing_age) * 1.e9)

    # Get the number of ionizing photons from SED

    #calculate the SEDs for new stars
    for i in range(len(stars_list)):

        sp.params["tage"] = stars_list[i].age
        sp.params["imf_type"] = cfg.par.imf_type
        sp.params["pagb"] = cfg.par.pagb
        sp.params["sfh"] = 0
        sp.params["zmet"] = stars_list[i].fsps_zmet
        sp.params["add_neb_emission"] = False
        sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model

        if cfg.par.FORCE_gas_logz == False:
            LogZ = np.log10(stars_list[i].metals / cfg.par.solar)
        else:
            LogZ = cfg.par.gas_logz

        if cfg.par.CF_on == True:
            sp.params["dust_type"] = 0
            sp.params["dust1"] = 1
            sp.params["dust2"] = 0
            sp.params["dust_tesc"] = tesc_age

        #sp = fsps.StellarPopulation(tage=stars_list[i].age,imf_type=2,sfh=0,zmet=stars_list[i].fsps_zmet)
        spec = sp.get_spectrum(tage=stars_list[i].age,
                               zmet=stars_list[i].fsps_zmet)
        f = spec[1]

        #Only including particles below the maximum age limit for calulating nebular emission
        if cfg.par.add_neb_emission and stars_list[
                i].age <= cfg.par.HII_max_age:

            num_HII_clusters = int(
                np.floor((stars_list[i].mass / constants.M_sun.cgs.value) /
                         (cfg.par.stellar_cluster_mass)))
            f = np.zeros(nlam)
            neb_file_output = cfg.par.neb_file_output

            sp.params["add_neb_emission"] = False
            spec = sp.get_spectrum(tage=stars_list[i].age,
                                   zmet=stars_list[i].fsps_zmet)

            if cfg.par.FORCE_gas_logu:
                alpha = 2.5e-13 * ((cfg.par.HII_T / (10**4))**(-0.85))
                LogU = cfg.par.gas_logu
                LogQ = np.log10((10**(3 * LogU)) *
                                (36 * np.pi * (constants.c.cgs.value**3)) /
                                ((alpha**2) * cfg.par.HII_nh))
                Rin = ((3 * (10**LogQ)) /
                       (4 * np.pi * (cfg.par.HII_nh**2) * alpha))**(1. / 3.)
            else:
                LogQ, Rin, LogU = calc_LogU(
                    1.e8 * constants.c.cgs.value / spec[0],
                    spec[1] * constants.L_sun.cgs.value,
                    cfg.par.HII_nh,
                    cfg.par.HII_T,
                    mstar=cfg.par.stellar_cluster_mass)

            if cfg.par.FORCE_logq:
                LogQ = cfg.par.source_logq

            if cfg.par.FORCE_inner_radius:
                Rin = cfg.par.inner_radius

            if neb_file_output:
                logu_diagnostic(LogQ,
                                Rin,
                                LogU,
                                cfg.par.stellar_cluster_mass,
                                stars_list[i].age,
                                stars_list[i].fsps_zmet,
                                append=True)
                neb_file_output = False

            sp.params['gas_logu'] = LogU
            sp.params['gas_logz'] = LogZ
            sp.params["add_neb_emission"] = True
            if cfg.par.use_cloudy_tables:
                lam_neb, spec_neb = sp.get_spectrum(
                    tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet)
            else:
                try:
                    # Calculating ionizing photons again but for 1 Msun in order to scale the output for FSPS
                    LogQ_1, Rin_1, LogU_1 = calc_LogU(
                        1.e8 * constants.c.cgs.value / spec[0],
                        spec[1] * constants.L_sun.cgs.value, cfg.par.HII_nh,
                        cfg.par.HII_T)
                    spec_neb = get_nebular(spec[0],
                                           spec[1],
                                           cfg.par.HII_nh,
                                           LogQ,
                                           Rin,
                                           LogU,
                                           LogZ,
                                           LogQ_1,
                                           abund=cfg.par.neb_abund,
                                           useq=cfg.par.use_Q,
                                           clean_up=cfg.par.cloudy_cleanup)
                except ValueError as err:
                    lam_neb, spec_neb = sp.get_spectrum(
                        tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet)

            f = spec_neb * num_HII_clusters

        stellar_nu[:] = 1.e8 * constants.c.cgs.value / spec[0]
        stellar_fnu[i, :] = f

    return stellar_fnu
示例#3
0
def allstars_sed_gen(stars_list, cosmoflag, sp):

    #NOTE this part is just for the gadget simulations - this will
    #eventually become obviated as it gets passed into a function to
    #populate the stars_list with objects as we start to feed in new
    #types of simulation results.

    nstars = len(stars_list)

    #get just the wavelength array
    sp.params["tage"] = stars_list[0].age
    sp.params["imf_type"] = cfg.par.imf_type
    sp.params["pagb"] = cfg.par.pagb
    sp.params["sfh"] = 0
    sp.params["zmet"] = stars_list[0].fsps_zmet
    sp.params["add_neb_emission"] = cfg.par.add_neb_emission
    sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model
    sp.params['gas_logu'] = cfg.par.gas_logu
    if cfg.par.FORCE_gas_logz == False:
        sp.params['gas_logz'] = np.log10(stars_list[0].metals / cfg.par.solar)
    else:
        sp.params['gas_logz'] = cfg.par.gas_logz
        '''
    sp = fsps.StellarPopulation(tage=stars_list[0].age,imf_type=cfg.par.imf_type,pagb = cfg.par.pagb,sfh=0,zmet=stars_list[0].fsps_zmet,
                                add_neb_emission = cfg.par.add_neb_emission, add_agb_dust_model=cfg.par.add_agb_dust_model)
                                '''
    spec = sp.get_spectrum(tage=stars_list[0].age,
                           zmet=stars_list[0].fsps_zmet)
    nu = 1.e8 * constants.c.cgs.value / spec[0]
    nlam = len(nu)

    nprocesses = np.min(
        [cfg.par.n_processes, len(stars_list)]
    )  #the pool.map will barf if there are less star bins than process threads

    #initializing the logU file newly
    logu_diagnostic(None, None, None, None, None, None, append=False)
    #save the emission lines from the newstars#
    if cfg.par.add_neb_emission: calc_emline(stars_list)

    #initialize the process pool and build the chunks
    p = Pool(processes=nprocesses)
    nchunks = nprocesses

    chunk_start_indices = []
    chunk_start_indices.append(0)  #the start index is obviously 0

    #this should just be int(nstars/nchunks) but in case nstars < nchunks, we need to ensure that this is at least  1
    delta_chunk_indices = np.max([int(nstars / nchunks), 1])
    print('delta_chunk_indices = ', delta_chunk_indices)

    for n in range(1, nchunks):
        chunk_start_indices.append(chunk_start_indices[n - 1] +
                                   delta_chunk_indices)
    '''
    chunk_start_indices = list(np.fix(np.arange(0,nstars,np.fix(nstars/nchunks))))
    #because this can result in too many chunks sometimes given the number of processors:
    chunk_start_indices = chunk_start_indices[0:nchunks]
    '''
    print('Entering Pool.map multiprocessing for Stellar SED generation')
    list_of_chunks = []
    for n in range(nchunks):
        stars_list_chunk = stars_list[
            chunk_start_indices[n]:chunk_start_indices[n] +
            delta_chunk_indices]
        #if we're on the last chunk, we might not have the full list included, so need to make sure that we have that here
        if n == nchunks - 1:
            stars_list_chunk = stars_list[chunk_start_indices[n]::]

        list_of_chunks.append(stars_list_chunk)

    t1 = datetime.now()
    chunk_sol = p.map(newstars_gen, [arg for arg in list_of_chunks])

    t2 = datetime.now()
    print('Execution time for SED generation in Pool.map multiprocessing = ' +
          str(t2 - t1))

    stellar_fnu = np.zeros([nstars, nlam])
    star_counter = 0
    for i in range(nchunks):
        fnu_list = chunk_sol[
            i]  #this is a list of the stellar_fnu's returned by that chunk
        for j in range(len(fnu_list)):
            stellar_fnu[star_counter, :] = fnu_list[j, :]
            star_counter += 1

    p.close()
    p.terminate()
    p.join()

    stellar_nu = nu

    if cosmoflag == False:

        #calculate the SED for disk stars; note, this gets calculated
        #whether or not disk stars actually exist.  if they don't exist,
        #bogus values for the disk age and metallicity are assigned based
        #on whatever par.disk_stars_age and metallicity are.  it's no big
        #deal since these SEDs don't end up getting added to the model in
        #source_creation.

        #note, even if there are no disk/bulge stars, these are still
        #created since they're completely based on input parameters in
        #parameters_master.  they just won't get used at a later point
        #as there will be no disk/bulge star positions to add them to.

        #dust_tesc is an absolute value (not relative to min star age) as the ages of these stars are input by the user

        # Load in the metallicity legend
        fsps_metals = np.loadtxt(cfg.par.metallicity_legend)

        sp.params["tage"] = cfg.par.disk_stars_age
        sp.params["imf_type"] = cfg.par.imf_type
        sp.params["pagb"] = cfg.par.pagb
        sp.params["sfh"] = 0
        sp.params["zmet"] = cfg.par.disk_stars_metals
        sp.params["add_neb_emission"] = cfg.par.add_neb_emission
        sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model
        sp.params['gas_logu'] = cfg.par.gas_logu
        if cfg.par.FORCE_gas_logz == False:
            sp.params['gas_logz'] = np.log10(
                fsps_metals[cfg.par.disk_stars_metals] / cfg.par.solar)
        else:
            sp.params['gas_logz'] = cfg.par.gas_logz

        spec = sp.get_spectrum(tage=cfg.par.disk_stars_age,
                               zmet=cfg.par.disk_stars_metals)
        disk_fnu = spec[1]

        #calculate the SED for bulge stars
        sp.params["tage"] = cfg.par.bulge_stars_age
        sp.params["imf_type"] = cfg.par.imf_type
        sp.params["pagb"] = cfg.par.pagb
        sp.params["sfh"] = 0
        sp.params["zmet"] = cfg.par.bulge_stars_metals
        sp.params["add_neb_emission"] = cfg.par.add_neb_emission
        sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model
        sp.params['gas_logu'] = cfg.par.gas_logu
        if cfg.par.FORCE_gas_logz == False:
            sp.params['gas_logz'] = np.log10(
                fsps_metals[cfg.par.bulge_stars_metals] / cfg.par.solar)
        else:
            sp.params['gas_logz'] = cfg.par.gas_logz

        spec = sp.get_spectrum(tage=cfg.par.bulge_stars_age,
                               zmet=cfg.par.bulge_stars_metals)
        bulge_fnu = spec[1]

    else:  #we have a cosmological simulation

        disk_fnu = []
        bulge_fnu = []

    total_lum_in_sed_gen = 0.
    for i in range(stellar_fnu.shape[0]):
        total_lum_in_sed_gen += np.absolute(np.trapz(stellar_fnu[i, :], x=nu))

    print('[SED_gen: ] total_lum_in_sed_gen = ', total_lum_in_sed_gen)

    #return positions,disk_positions,bulge_positions,mass,stellar_nu,stellar_fnu,disk_masses,disk_fnu,bulge_masses,bulge_fnu
    return stellar_nu, stellar_fnu, disk_fnu, bulge_fnu
示例#4
0
df_nu = o['nu']
df_chi = o['chi']
df.close()

# add sources to hyperion
stars_list, diskstars_list, bulgestars_list, reg = sg.star_list_gen(
    boost, dx, dy, dz, reg, ds, sp)
nstars = len(stars_list)

# figure out N_METAL_BINS:
fsps_metals = np.array(sp.zlegend)
N_METAL_BINS = len(fsps_metals)

#initializing the nebular diagnostic file newly
if cfg.par.add_neb_emission and cfg.par.NEB_DEBUG:
    logu_diagnostic(None, None, None, None, None, None, None, append=False)
if cfg.par.add_neb_emission and cfg.par.dump_emlines:
    dump_emlines(None, None, append=False)

if cfg.par.BH_SED == True:
    BH_source_add(m, reg, df_nu, boost)

if cfg.par.FORCE_BINNED == False:
    m = direct_add_stars(df_nu, stars_list, diskstars_list, bulgestars_list,
                         ds.cosmological_simulation, m, sp)

# note - the generation of the SEDs is called within
# add_binned_seds itself, unlike add_newstars, which requires
# that sg.allstars_sed_gen() be called first.
m = add_binned_seds(df_nu, stars_list, diskstars_list, bulgestars_list,
                    ds.cosmological_simulation, m, sp)
示例#5
0
# add sources to hyperion
stars_list, diskstars_list, bulgestars_list, reg = sg.star_list_gen(boost, dx, dy, dz, reg, ds)
nstars = len(stars_list)

if cfg.par.BH_SED == True:
    BH_source_add(m, reg, df_nu, boost)


# figure out N_METAL_BINS:
fsps_metals = np.loadtxt(cfg.par.metallicity_legend)
N_METAL_BINS = len(fsps_metals)


#initializing the logU file newly
if cfg.par.add_neb_emission: logu_diagnostic(None,None,None,None,None,None,None,None,append=False)

if cfg.par.FORCE_BINNED == False:
    m = direct_add_stars(df_nu, stars_list, diskstars_list, bulgestars_list, ds.cosmological_simulation, m, sp)

# note - the generation of the SEDs is called within
# add_binned_seds itself, unlike add_newstars, which requires
# that sg.allstars_sed_gen() be called first.
m = add_binned_seds(df_nu, stars_list, diskstars_list,bulgestars_list, ds.cosmological_simulation, m, sp)



#set the random seets
if cfg.par.FORCE_RANDOM_SEED == False:
    m.set_seed(random.randrange(0,10000)*-1)
else:
示例#6
0
df_nu = o['nu']
df_chi = o['chi']
df.close()


# add sources to hyperion
stars_list, diskstars_list, bulgestars_list, reg = sg.star_list_gen(boost, dx, dy, dz, reg, ds, sp, m)
nstars = len(stars_list)

# figure out N_METAL_BINS:
fsps_metals = np.array(sp.zlegend)
N_METAL_BINS = len(fsps_metals)


#initializing the nebular diagnostic file newly
if cfg.par.add_neb_emission and cfg.par.NEB_DEBUG: logu_diagnostic(None,None,None,None,None,None,None,append=False)
if cfg.par.add_neb_emission and cfg.par.dump_emlines: dump_emlines(None,append=False)
if cfg.par.add_neb_emission and (cfg.par.SAVE_NEB_SEDS or cfg.par.add_DIG_neb): dump_NEB_SEDs(None, None, None, append=False)

if cfg.par.BH_SED == True:
    BH_source_add(m, reg, df_nu, boost)


if cfg.par.FORCE_BINNED == False:
    m = direct_add_stars(df_nu, stars_list, diskstars_list, bulgestars_list, ds.cosmological_simulation, m, sp)

# note - the generation of the SEDs is called within
# add_binned_seds itself, unlike add_newstars, which requires
# that sg.allstars_sed_gen() be called first.
m = add_binned_seds(df_nu, stars_list, diskstars_list,bulgestars_list, ds.cosmological_simulation, m, sp)
示例#7
0
def newstars_gen(stars_list):
    global sp
    if sp is None:
        sp = fsps.StellarPopulation()

    #the newstars (particle type 4; so, for cosmological runs, this is all
    #stars) are calculated in a separate function with just one argument so that it is can be fed
    #into pool.map for multithreading.
    #sp = fsps.StellarPopulation()
    sp.params["tage"] = stars_list[0].age
    sp.params["imf_type"] = cfg.par.imf_type
    sp.params["pagb"] = cfg.par.pagb
    sp.params["sfh"] = 0
    sp.params["zmet"] = stars_list[0].fsps_zmet
    sp.params["add_neb_emission"] = False
    sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model

    #first figure out how many wavelengths there are

    spec = sp.get_spectrum(tage=stars_list[0].age,
                           zmet=stars_list[0].fsps_zmet)
    nu = 1.e8 * constants.c.cgs.value / spec[0]

    nlam = len(nu)

    stellar_nu = np.zeros([nlam])
    stellar_fnu = np.zeros([len(stars_list), nlam])
    mfrac = np.zeros([len(stars_list)])

    minage = 13  #Gyr
    for i in range(len(stars_list)):
        if stars_list[i].age < minage:
            minage = stars_list[i].age

    tesc_age = np.log10((minage + cfg.par.birth_cloud_clearing_age) * 1.e9)

    # Get the number of ionizing photons from SED

    #calculate the SEDs for new stars
    for i in range(len(stars_list)):

        sp.params["tage"] = stars_list[i].age
        sp.params["imf_type"] = cfg.par.imf_type
        sp.params["imf1"] = cfg.par.imf1
        sp.params["imf2"] = cfg.par.imf2
        sp.params["imf3"] = cfg.par.imf3
        sp.params["pagb"] = cfg.par.pagb
        sp.params["sfh"] = 0
        sp.params["zmet"] = stars_list[i].fsps_zmet
        sp.params["add_neb_emission"] = False
        sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model

        if cfg.par.CF_on == True:
            sp.params["dust_type"] = 0
            sp.params["dust1"] = 1
            sp.params["dust2"] = 0
            sp.params["dust_tesc"] = tesc_age

        spec_noneb = sp.get_spectrum(tage=stars_list[i].age,
                                     zmet=stars_list[i].fsps_zmet)
        f = spec_noneb[1]
        #NOTE: FSPS SSP/CSP spectra are scaled by *formed* mass, not current mass. i.e., the SFHs of the SSP/CSP are normalized such that 1 solar mass
        #is formed over the history. This means that the stellar spectra are normalized by the integral of the SFH =/= current
        #(surviving, observed, etc.) stellar mass. In simulations, we only know the current star particle mass. To get the formed mass for an SSP,
        #we generate the surviving mass fraction (sp.stellar_mass) to extrapolate the initial mass from the current mass, metallicity, and age
        #this 'mfrac' is used to scale the FSPS SSP luminosities in source_creation
        mfrac[i] = sp.stellar_mass

        pagb = cfg.par.add_pagb_stars and cfg.par.PAGB_min_age <= stars_list[
            i].age <= cfg.par.PAGB_max_age
        young_star = cfg.par.add_young_stars and cfg.par.HII_min_age <= stars_list[
            i].age <= cfg.par.HII_max_age

        if (cfg.par.add_neb_emission or cfg.par.use_cmdf) and (young_star
                                                               or pagb):

            # For each star particle we break it into a collection or cluster of star particles which have the same property as the parent star particle
            # but their masses and ages follow a power-law distribution if use_cmdf and use_age_distribution are set to True respectively and it falls
            # under the constraints set in parameters_master. To do so we create 1D arrays that store the masses of each cluster (cluster_mass), num of particles
            # in that cluster (num_cluster), and age of that cluster (age_cluster). So for example (assuming default values), a 1e6 solar mass particle
            # will first be broken down into 6 particles with different masses ranging from 10^3.5 Msun to 10^5 Msun. Each of these particles will be further
            # broken down into 5 particles with their ages are distributed as per the age distribution. Thus in total, this one particle will be broken
            # down into 30 particles and these arrays will store the properties of all the 30 particles. This allows us to consider these as 30 individual
            # particles rest of the calculation and their fluxes are combined in end to get the final result for this one particle.

            cluster_mass = [
                np.log10(stars_list[i].mass / constants.M_sun.cgs.value)
            ]
            num_clusters = [1]
            age_clusters = [stars_list[i].age]

            if stars_list[
                    i].mass / constants.M_sun.cgs.value > 10**cfg.par.cmdf_max_mass and cfg.par.use_cmdf:
                cluster_mass, num_clusters = cmdf(
                    stars_list[i].mass / constants.M_sun.cgs.value,
                    int(cfg.par.cmdf_bins), cfg.par.cmdf_min_mass,
                    cfg.par.cmdf_max_mass, cfg.par.cmdf_beta)
                age_clusters = []
                for k in range(len(cluster_mass)):
                    age_clusters.append(stars_list[i].age)

                if cfg.par.use_age_distribution:
                    num_clusters_cmdf = num_clusters
                    cluster_mass_cmdf = cluster_mass
                    num_clusters = []
                    cluster_mass = []
                    age_clusters = []
                    for k in range(len(cluster_mass_cmdf)):
                        num, t = age_dist(num_clusters_cmdf[k],
                                          stars_list[i].age)
                        rescale = np.sum(num_clusters_cmdf[k]) / np.sum(num)

                        for l in range(len(num)):
                            if num[l] == 0:
                                continue
                            num_clusters.append(num[l])
                            cluster_mass.append(
                                np.log10((10**cluster_mass_cmdf[k]) * rescale))
                            age_clusters.append(t[l])

                cluster_mass = np.array(cluster_mass)
                num_clusters = np.array(num_clusters)
                age_clusters = np.array(age_clusters)

            f = np.zeros(nlam)
            cloudy_nlam = len(
                np.genfromtxt(cfg.par.pd_source_dir +
                              "/powderday/nebular_emission/data/refLines.dat",
                              delimiter=','))
            line_em = np.zeros([cloudy_nlam])

            for j in range(len(cluster_mass)):
                num_HII_clusters = num_clusters[j]
                age = age_clusters[j]
                neb_file_output = cfg.par.NEB_DEBUG

                sp.params["add_neb_emission"] = False
                if cfg.par.add_neb_emission:
                    # id_val = 0, 1, 2 for young stars, Post-AGB star and AGNs respectively.
                    if young_star:
                        id_val = 0
                        Rinner_per_Rs = cfg.par.HII_Rinner_per_Rs
                        nh = cfg.par.HII_nh
                        escape_fraction = cfg.par.HII_escape_fraction

                    elif pagb:
                        id_val = 1
                        Rinner_per_Rs = cfg.par.PAGB_Rinner_per_Rs
                        nh = cfg.par.PAGB_nh
                        escape_fraction = cfg.par.PAGB_escape_fraction

                    if cfg.par.HII_alpha_enhance:  #Setting Zstar based on Fe/H
                        Fe = stars_list[i].all_metals[-1]

                        # Gizmo metallicity structure, photospheric abundances from Asplund et al. 2009:
                        # Photospheric mass fraction of H = 0.7381
                        # Photospheric mass fraction of Fe = 1.31e-3

                        # Converting from mass fraction to atomic fraction of Fe
                        # Taking atmoic mass of H = 1.008u
                        # Taking atmoic mass of Fe = 55.845u
                        FeH = (Fe / 0.7381) * (1.008 / 55.845)

                        # Solar atomic fraction of Fe. Calculated by substituting Fe = 1.31e-3 in the previous equation
                        FeH_sol = 3.22580645e-5

                        Logzsol = np.log10(FeH / FeH_sol)

                        sp1 = fsps.StellarPopulation(zcontinuous=1)
                        sp1.params["tage"] = age
                        sp1.params["imf_type"] = cfg.par.imf_type
                        sp1.params["imf1"] = cfg.par.imf1
                        sp1.params["imf2"] = cfg.par.imf2
                        sp1.params["imf3"] = cfg.par.imf3
                        sp1.params["pagb"] = cfg.par.pagb
                        sp1.params["sfh"] = 0
                        sp1.params["zmet"] = stars_list[i].fsps_zmet
                        sp1.params["add_neb_emission"] = False
                        sp1.params[
                            "add_agb_dust_model"] = cfg.par.add_agb_dust_model
                        sp1.params["logzsol"] = Logzsol

                        if cfg.par.CF_on == True:
                            sp1.params["dust_type"] = 0
                            sp1.params["dust1"] = 1
                            sp1.params["dust2"] = 0
                            sp1.params["dust_tesc"] = tesc_age

                        spec = sp1.get_spectrum(tage=age)
                        mfrac_neb = sp1.stellar_mass

                    else:
                        spec = sp.get_spectrum(tage=age,
                                               zmet=stars_list[i].fsps_zmet)
                        mfrac_neb = sp.stellar_mass

                    alpha = 2.5e-13  # Recombination Rate (assuming T = 10^4 K)

                    if cfg.par.FORCE_gas_logu[id_val]:
                        LogU = cfg.par.gas_logu[id_val]
                        LogQ = np.log10(
                            (10**(3 * LogU)) * (36 * np.pi *
                                                (constants.c.cgs.value**3)) /
                            ((alpha**2) * nh))
                        Rs = ((3 * (10**LogQ)) / (4 * np.pi *
                                                  (nh**2) * alpha))**(1. / 3.)

                    elif cfg.par.FORCE_logq[id_val]:
                        LogQ = cfg.par.source_logq[id_val]
                        Rs = ((3 * (10**LogQ)) / (4 * np.pi *
                                                  (nh**2) * alpha))**(1. / 3.)
                        LogU = np.log10(
                            (10**LogQ) /
                            (4 * np.pi * Rs * Rs * nh * constants.c.cgs.value))

                    else:
                        LogQ = calc_LogQ(1.e8 * constants.c.cgs.value /
                                         spec[0],
                                         spec[1] * constants.L_sun.cgs.value,
                                         efrac=escape_fraction,
                                         mstar=10**cluster_mass[j],
                                         mfrac=mfrac_neb)
                        Rs = ((3 * (10**LogQ)) / (4 * np.pi *
                                                  (nh**2) * alpha))**(1. / 3.)
                        LogU = np.log10(
                            (10**LogQ) /
                            (4 * np.pi * Rs * Rs * nh * constants.c.cgs.value)
                        ) + cfg.par.gas_logu_init[id_val]
                        LogQ = np.log10(
                            (10**(3 * LogU)) * (36 * np.pi *
                                                (constants.c.cgs.value**3)) /
                            ((alpha**2) * nh))
                        Rs = ((3 * (10**LogQ)) / (4 * np.pi *
                                                  (nh**2) * alpha))**(1. / 3.)

                    if cfg.par.FORCE_inner_radius[id_val]:
                        Rin = cfg.par.inner_radius[id_val]

                    else:
                        Rin = Rinner_per_Rs * Rs

                    if cfg.par.FORCE_gas_logz[id_val]:
                        LogZ = cfg.par.gas_logz[id_val]

                    else:
                        LogZ = np.log10(stars_list[i].metals / cfg.par.solar)

                    if neb_file_output:
                        if cfg.par.use_cloudy_tables:
                            Rin = 1.e19  # Rinner is fixed at 1.e19 cm for lookup tables

                        if cfg.par.FORCE_inner_radius[id_val]:
                            Rin = cfg.par.inner_radius[id_val]

                        LogU = np.log10(
                            (10**LogQ) / (4 * np.pi * Rin * Rin * nh *
                                          constants.c.cgs.value))

                        logu_diagnostic(LogQ,
                                        LogU,
                                        LogZ,
                                        Rs,
                                        10**cluster_mass[j],
                                        num_HII_clusters,
                                        age,
                                        append=True)
                        neb_file_output = False

                    sp.params['gas_logu'] = LogU
                    sp.params['gas_logz'] = LogZ
                    sp.params["add_neb_emission"] = True
                    if cfg.par.use_cloudy_tables:
                        lam_neb, spec_neb = sp.get_spectrum(
                            tage=age, zmet=stars_list[i].fsps_zmet)
                        line_lum = sp.emline_luminosity
                        wave_line = sp.emline_wavelengths
                    else:
                        try:
                            # Calculating ionizing photons again but for 1 Msun in order to scale the output for FSPS
                            LogQ_1 = calc_LogQ(
                                1.e8 * constants.c.cgs.value / spec[0],
                                spec[1] * constants.L_sun.cgs.value,
                                efrac=escape_fraction)
                            #LogQ_1 = LogQ_1 + cfg.par.gas_logu_init[id_val]

                            spec_neb, wave_line, line_lum = get_nebular(
                                spec[0],
                                spec[1],
                                nh,
                                stars_list[i].all_metals,
                                logq=LogQ,
                                radius=Rin,
                                logu=LogU,
                                logz=LogZ,
                                logq_1=LogQ_1,
                                Dust=False,
                                abund=cfg.par.neb_abund[id_val],
                                clean_up=cfg.par.cloudy_cleanup,
                                index=id_val)
                        except ValueError as err:
                            # If the CLOUDY run crashes we switch to using lookup tables for young stars but throw an error for post-AGB stars.
                            if young_star:
                                print(
                                    "WARNING: Switching to using lookup tables pre-packed with FSPS to calculate nebular emission for this particle."
                                )
                                print(
                                    "WARNING: The emission line fluxes repoted may not be accurate if the particle lies outside the range of the lookup table paramters."
                                )
                                lam_neb, spec_neb = sp.get_spectrum(
                                    tage=age, zmet=stars_list[i].fsps_zmet)
                                line_lum = sp.emline_luminosity
                                wave_line = sp.emline_wavelengths
                            else:
                                print(
                                    "ERROR: Can't switch to using lookup tables."
                                )
                                print(
                                    "ERROR: Please check the CLOUDY output file to figure out why the run was unsuccessful"
                                )
                                raise ValueError('CLOUDY run was unsucessful')

                else:
                    lam_neb, spec_neb = sp.get_spectrum(
                        tage=age, zmet=stars_list[i].fsps_zmet)

                weight = num_HII_clusters * (10**cluster_mass[j]) / (
                    stars_list[i].mass / constants.M_sun.cgs.value)
                f = f + spec_neb * weight
                if cfg.par.add_neb_emission and cfg.par.dump_emlines:
                    line_em = line_em + line_lum * weight

            if cfg.par.add_neb_emission and cfg.par.dump_emlines:
                #the stellar population returns the calculation in units of Lsun/1 Msun: https://github.com/dfm/python-fsps/issues/117#issuecomment-546513619
                line_em = line_em * (stars_list[i].mass * u.g).to(
                    u.Msun).value * 3.839e33  # Units: ergs/s
                line_em = np.append(line_em, age)
                dump_emlines(wave_line, line_em)

        stellar_nu[:] = 1.e8 * constants.c.cgs.value / spec[0]
        stellar_fnu[i, :] = f
    return stellar_fnu, mfrac
示例#8
0
def newstars_gen(stars_list):
    global sp
    if sp is None:
        sp = fsps.StellarPopulation()

    #the newstars (particle type 4; so, for cosmological runs, this is all
    #stars) are calculated in a separate function with just one argument so that it is can be fed 
    #into pool.map for multithreading.
    #sp = fsps.StellarPopulation()
    sp.params["tage"] = stars_list[0].age
    sp.params["imf_type"] = cfg.par.imf_type
    sp.params["pagb"] = cfg.par.pagb
    sp.params["sfh"] = 0
    sp.params["zmet"] = stars_list[0].fsps_zmet
    sp.params["add_neb_emission"] = False
    sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model

    #first figure out how many wavelengths there are
    
    spec = sp.get_spectrum(tage=stars_list[0].age,zmet=stars_list[0].fsps_zmet)
    nu = 1.e8*constants.c.cgs.value/spec[0]

    nlam = len(nu)

    stellar_nu = np.zeros([nlam])
    stellar_fnu = np.zeros([len(stars_list),nlam])
    
  
    minage = 13 #Gyr
    for i in range(len(stars_list)): 
        if stars_list[i].age < minage:
            minage = stars_list[i].age

    tesc_age = np.log10((minage+cfg.par.birth_cloud_clearing_age)*1.e9)


    # Get the number of ionizing photons from SED

    #calculate the SEDs for new stars
    for i in range(len(stars_list)):
        
        sp.params["tage"] = stars_list[i].age
        sp.params["imf_type"] = cfg.par.imf_type
        sp.params["imf1"] = cfg.par.imf1
        sp.params["imf2"] = cfg.par.imf2
        sp.params["imf3"] = cfg.par.imf3
        sp.params["pagb"] = cfg.par.pagb
        sp.params["sfh"] = 0
        sp.params["zmet"] = stars_list[i].fsps_zmet
        sp.params["add_neb_emission"] = False
        sp.params["add_agb_dust_model"] = cfg.par.add_agb_dust_model

        if cfg.par.CF_on == True:
            sp.params["dust_type"] = 0
            sp.params["dust1"] = 1
            sp.params["dust2"] = 0
            sp.params["dust_tesc"] = tesc_age

        spec_noneb = sp.get_spectrum(tage=stars_list[i].age,zmet=stars_list[i].fsps_zmet)
        f = spec_noneb[1]

        pagb = cfg.par.add_pagb_stars and cfg.par.PAGB_min_age <= stars_list[i].age <= cfg.par.PAGB_max_age
        young_star = cfg.par.add_young_stars and stars_list[i].age <= cfg.par.HII_max_age

        if (cfg.par.add_neb_emission or cfg.par.use_cmdf) and (young_star or pagb):

            # Cluster Mass Distribution Funtion is used only when the star particle's mass is gretaer than the maximum cluster mass and use_cmdf is True. 

            if stars_list[i].mass/constants.M_sun.cgs.value > 10**cfg.par.cmdf_max_mass and cfg.par.use_cmdf:
                cluster_mass, num_clusters = cmdf(stars_list[i].mass/constants.M_sun.cgs.value,int(cfg.par.cmdf_bins),cfg.par.cmdf_min_mass,
                        cfg.par.cmdf_max_mass, cfg.par.cmdf_beta)
            
            else:
                cluster_mass = [np.log10(stars_list[i].mass/constants.M_sun.cgs.value)]
                num_clusters = [1]

            f = np.zeros(nlam)
            cloudy_nlam = len(np.genfromtxt(cfg.par.pd_source_dir + "/powderday/nebular_emission/data/refLines.dat", delimiter=','))
            line_em = np.zeros([cloudy_nlam])
            
            for j in range(len(cluster_mass)):
                num_HII_clusters = num_clusters[j]
                neb_file_output = cfg.par.NEB_DEBUG

                sp.params["add_neb_emission"] = False
                if cfg.par.add_neb_emission:
                    # id_val = 0, 1, 2 for young stars, Post-AGB star and AGNs respectively.
                    if young_star:
                        id_val = 0
                        Rinner_per_Rs = cfg.par.HII_Rinner_per_Rs
                        nh = cfg.par.HII_nh       
                        escape_fraction  = cfg.par.HII_escape_fraction
                    
                    elif pagb:
                        id_val = 1
                        Rinner_per_Rs = cfg.par.PAGB_Rinner_per_Rs
                        nh = cfg.par.PAGB_nh    
                        escape_fraction  = cfg.par.PAGB_escape_fraction

                    spec = sp.get_spectrum(tage=stars_list[i].age,zmet=stars_list[i].fsps_zmet)

                    alpha = 2.5e-13 # Recombination Rate (assuming T = 10^4 K)

                    if cfg.par.FORCE_gas_logu[id_val]:
                        LogU = cfg.par.gas_logu[id_val]
                        LogQ = np.log10((10 ** (3*LogU))*(36*np.pi*(constants.c.cgs.value**3))/((alpha**2)*nh))
                        Rs = ((3*(10 ** LogQ))/(4*np.pi*(nh**2)*alpha))**(1./3.)
                    
                    elif cfg.par.FORCE_logq[id_val]:
                        LogQ = cfg.par.source_logq[id_val]
                        Rs = ((3*(10 ** LogQ))/(4*np.pi*(nh**2)*alpha))**(1./3.)
                        LogU = np.log10((10**LogQ)/(4*np.pi*Rs*Rs*nh*constants.c.cgs.value))

                    else:
                        LogQ = calc_LogQ(1.e8*constants.c.cgs.value/spec[0], spec[1]*constants.L_sun.cgs.value
                                , efrac=escape_fraction, mstar=10**cluster_mass[j])        
                        Rs = ((3*(10 ** LogQ))/(4*np.pi*(nh**2)*alpha))**(1./3.)
                        LogU = np.log10((10**LogQ)/(4*np.pi*Rs*Rs*nh*constants.c.cgs.value))+cfg.par.gas_logu_init[id_val]
                        LogQ = np.log10((10 ** (3*LogU))*(36*np.pi*(constants.c.cgs.value**3))/((alpha**2)*nh))
                        Rs = ((3*(10 ** LogQ))/(4*np.pi*(nh**2)*alpha))**(1./3.)

                    if cfg.par.FORCE_inner_radius[id_val]:
                        Rin = cfg.par.inner_radius[id_val]

                    else:
                        Rin = Rinner_per_Rs*Rs
               
                    
                    if cfg.par.FORCE_gas_logz[id_val]:
                        LogZ = cfg.par.gas_logz[id_val]
                    
                    else:
                        LogZ = np.log10(stars_list[i].metals/cfg.par.solar)
                    
                    
                    if neb_file_output:
                        if cfg.par.use_cloudy_tables:
                            Rin = 1.e19   # Rinner is fixed at 1.e19 cm for lookup tables
                        logu_diagnostic(LogQ, LogU, LogZ, Rin, 10**cluster_mass[j], num_HII_clusters, stars_list[i].age, append=True)
                        neb_file_output = False

                    sp.params['gas_logu'] = LogU
                    sp.params['gas_logz'] = LogZ
                    sp.params["add_neb_emission"] = True  
                    if cfg.par.use_cloudy_tables:
                        lam_neb, spec_neb = sp.get_spectrum(tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet)
                        line_lum = sp.emline_luminosity
                        wave_line = sp.emline_wavelengths
                    else:
                        try:
                            # Calculating ionizing photons again but for 1 Msun in order to scale the output for FSPS
                            LogQ_1 = calc_LogQ(1.e8 * constants.c.cgs.value / spec[0], spec[1] * constants.L_sun.cgs.value,
                                    efrac=escape_fraction)
                               
                            spec_neb, wave_line, line_lum = get_nebular(spec[0], spec[1], nh, LogQ, Rin, LogU, LogZ, LogQ_1, stars_list[i].all_metals, 
                                                    Dust=False, abund=cfg.par.neb_abund[id_val], clean_up = cfg.par.cloudy_cleanup, index=id_val)
                        except ValueError as err:
                            # If the CLOUDY run crashes we switch to using lookup tables for young stars but throw an error for post-AGB stars.
                            if  young_star:
                                print ("WARNING: Switching to using lookup tables pre-packed with FSPS to calculate nebular emission for this particle.") 
                                print ("WARNING: The emission line fluxes repoted may not be accurate if the particle lies outside the range of the lookup table paramters.")
                                lam_neb, spec_neb = sp.get_spectrum(tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet)
                                line_lum = sp.emline_luminosity
                                wave_line = sp.emline_wavelengths
                            else:
                                print ("ERROR: Can't switch to using lookup tables. ")
                                print ("ERROR: Please check the CLOUDY output file to figure out why the run was unsuccessful" )
                                raise ValueError('CLOUDY run was unsucessful')
                
                else:
                    lam_neb, spec_neb = sp.get_spectrum(tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet)

                weight = num_HII_clusters*(10**cluster_mass[j])/(stars_list[i].mass/constants.M_sun.cgs.value)    
                f = f + spec_neb*weight
                if cfg.par.add_neb_emission and cfg.par.dump_emlines:
                    line_em = line_em + line_lum*weight
        
            if cfg.par.add_neb_emission and cfg.par.dump_emlines:
                #the stellar population returns the calculation in units of Lsun/1 Msun: https://github.com/dfm/python-fsps/issues/117#issuecomment-546513619
                line_em = line_em * ((stars_list[i].mass*u.g).to(u.Msun).value) * (3.839e33)  # Units: ergs/s
                line_em = np.append(line_em, stars_list[i].age)
                dump_emlines(wave_line, line_em)

        stellar_nu[:] = 1.e8*constants.c.cgs.value/spec[0]
        stellar_fnu[i,:] = f

    return stellar_fnu