Exemple #1
0
def direct_add_stars(df_nu, stars_list, diskstars_list, bulgestars_list,
                     cosmoflag, m, sp):

    print("--------------------------------\n")
    print("Adding unbinned stars to the grid\n")
    print("--------------------------------\n")

    totallum_newstars = 0.
    unbinned_stars_list = []

    for star in stars_list:
        if star.age <= cfg.par.max_age_direct:
            unbinned_stars_list.append(star)

    nstars = len(unbinned_stars_list)
    if (nstars == 0):
        print("No unbinned stars to add")
        return m

    else:
        print("Number of unbinned stars to added: ", nstars)

    stellar_nu, stellar_fnu, disk_fnu, bulge_fnu = sg.allstars_sed_gen(
        unbinned_stars_list, cosmoflag, sp)

    for i in range(nstars):
        nu = stellar_nu[:]
        fnu = stellar_fnu[i, :]

        nu, fnu = wavelength_compress(nu, fnu, df_nu)
        # reverse the arrays for hyperion
        nu = nu[::-1]
        fnu = fnu[::-1]

        lum = np.absolute(np.trapz(
            fnu,
            x=nu)) * unbinned_stars_list[i].mass / constants.M_sun.cgs.value
        lum *= constants.L_sun.cgs.value

        pos = unbinned_stars_list[i].positions

        # add new stars
        totallum_newstars += lum
        #m.add_spherical_source(luminosity = lum,radius = 10.*const.rsun,spectrum = (nu,fnu),

        m.add_point_source(luminosity=lum, spectrum=(nu, fnu), position=pos)

    print('[source_creation/add_unbinned_newstars:] totallum_newstars = ',
          totallum_newstars)

    if cosmoflag == False:
        add_bulge_disk_stars(df_nu, stellar_nu, stellar_fnu, disk_fnu,
                             bulge_fnu, unbinned_stars_list, diskstars_list,
                             bulgestars_list, m)

    m.set_sample_sources_evenly(True)

    return m
def add_binned_seds(df_nu,stars_list,diskstars_list,bulgestars_list,cosmoflag,m,sp):
    
    # calculate max and min ages
    minimum_age = 15 #Gyr - obviously too high of a number
    maximum_age = 0 #Gyr

    # calculate the minimum and maximum luminosity
    minimum_mass = 1e15*constants.M_sun.cgs.value #msun - some absurdly large value for a single stellar cluster
    maximum_mass = 0 #msun

    # calculate the minimum and maximum stellar metallicity
    minimum_metallicity = 1.e5 #some absurdly large metallicity
    maximum_metallicity = 0

    nstars = len(stars_list)
    for i in range(nstars):
        #if stars_list[i].metals[0] < minimum_metallicity: minimum_metallicity = stars_list[i].metals[0]
        #if stars_list[i].metals[0] > maximum_metallicity: maximum_metallicity = stars_list[i].metals[0]
        
        if stars_list[i].mass < minimum_mass: minimum_mass = stars_list[i].mass
        if stars_list[i].mass > maximum_mass: maximum_mass = stars_list[i].mass

        if stars_list[i].age < minimum_age: minimum_age = stars_list[i].age
        if stars_list[i].age > maximum_age: maximum_age = stars_list[i].age

    # If Flag is set we do not bin stars younger than the age set by max_age_unbinned_stars
    if not cfg.par.FORCE_BINNED:
        if cfg.par.max_age_direct > minimum_age:
            minimum_age = cfg.par.max_age_direct + 0.001

    delta_age = (maximum_age-minimum_age)/cfg.par.N_STELLAR_AGE_BINS

    if delta_age <= 0: # If max age for direct adding stars is greater than the max age of stars in the galaxy then 
        return m       # exit the function since there are no stars left for binning

    # define the metallicity bins: we do this by saying that they are the number of metallicity bins in FSPS

    fsps_metals = np.array(sp.zlegend)
    N_METAL_BINS = len(fsps_metals)

    # note the bins are NOT metallicity, but rather the zmet keys in
    # fsps (i.e. the zmet column in Table 1 of the fsps manual)
    metal_bins = np.arange(N_METAL_BINS)+1

    # define the age bins in log space so that we maximise resolution around young stars
    age_bins = 10.**(np.linspace(np.log10(minimum_age),np.log10(maximum_age),cfg.par.N_STELLAR_AGE_BINS))

    #tack on the maximum age bin
    age_bins = np.append(age_bins,age_bins[-1]+delta_age)

   
    #define the mass bins (log)
    #note - for some codes, all star particles have the same mass.  in this case, we have to have a trap:
    if minimum_mass == maximum_mass or cfg.par.N_MASS_BINS == 0: 
        mass_bins = np.zeros(cfg.par.N_MASS_BINS+1)+minimum_mass
    else:
        delta_mass = (np.log10(maximum_mass)-np.log10(minimum_mass))/cfg.par.N_MASS_BINS
        mass_bins = np.arange(np.log10(minimum_mass),np.log10(maximum_mass),delta_mass)
        mass_bins = np.append(mass_bins,mass_bins[-1]+delta_mass)
        mass_bins = 10.**mass_bins
        
    print ('mass_bins = ',mass_bins)
    print ('metal_bins = ',metal_bins)
    print ('age_bins = ',age_bins)

    #has_stellar_mass is a 3D boolean array that's [wz,wa,wm] big and
    #says whether or not that bin is being used downstream for
    #creating a point source collection (i.e. that it actually has at
    #least one star cluster that falls into it)
    has_stellar_mass = np.zeros([N_METAL_BINS,cfg.par.N_STELLAR_AGE_BINS+1,cfg.par.N_MASS_BINS+1],dtype=bool)


    stars_in_bin = {} #this will be a dictionary that holds the list
    #of star particles that go in every [wz,wa,wm]
    #group.  The keys will be tuples that hold a
    #(wz,wa,wm) set that we will then use later to
    #speed up adding sources.
    
    for i in range(nstars):
        wz = find_nearest(metal_bins,stars_list[i].fsps_zmet)
        wa = find_nearest(age_bins,stars_list[i].age)
        wm = find_nearest(mass_bins,stars_list[i].mass)
        
        stars_list[i].sed_bin = [wz,wa,wm]
        has_stellar_mass[wz,wa,wm] = True

        if (wz,wa,wm) in stars_in_bin:
            stars_in_bin[(wz,wa,wm)].append(i)
        else:
            stars_in_bin[(wz,wa,wm)] = [i]


    print ('assigning stars to SED bins')
    sed_bins_list=[]
    sed_bins_list_has_stellar_mass = []
       
    #we loop through age bins +1 because the max values were tacked
    #onto those bin lists. but for metal bins, this isn't the case, so
    #we don't loop the extra +1
    for wz in range(N_METAL_BINS):
        for wa in range(cfg.par.N_STELLAR_AGE_BINS+1):
            for wm in range(cfg.par.N_MASS_BINS+1):
                sed_bins_list.append(Sed_Bins(mass_bins[wm],fsps_metals[wz],age_bins[wa],metal_bins[wz]))
                if has_stellar_mass[wz,wa,wm] == True:
                    stars_metals = []
                    for star in stars_in_bin[(wz,wa,wm)]:
                        stars_metals.append(stars_list[star].all_metals)
                    stars_metals = np.array(stars_metals)
                    stars_metals = np.mean(stars_metals,axis=0)
                    #print(stars_metals, metal_bins[wz], fsps_metals[wz])
                    sed_bins_list_has_stellar_mass.append(Sed_Bins(mass_bins[wm],fsps_metals[wz],age_bins[wa],metal_bins[wz],stars_metals))
   
    #sed_bins_list is a list of Sed_Bins objects that have the
    #information about what mass bin, metal bin and age bin they
    #correspond to.  It is unnecessary, and heavy computational work
    #to re-create the SED for each of these bins - rather, we can just
    #calculate the SED for the bins that have any actual stellar mass.
            
    print ('Running SPS for Binned SEDs')
    print ('calculating the SEDs for ',len(sed_bins_list_has_stellar_mass),' bins')
    
    binned_stellar_nu,binned_stellar_fnu_has_stellar_mass,disk_fnu,bulge_fnu,mfrac = sg.allstars_sed_gen(sed_bins_list_has_stellar_mass,cosmoflag,sp)

    #since the binned_stellar_fnu_has_stellar_mass is now
    #[len(sed_bins_list_has_stellar_mass),nlam)] big, we need to
    #transform it back to the a larger array.  this is an ugly loop
    #that could probably be prettier...but whatever.  this saves >an
    #order of magnitude in time in SED gen.  
    nlam = binned_stellar_nu.shape[0]
    binned_stellar_fnu = np.zeros([len(sed_bins_list),nlam])
    binned_mfrac = np.zeros([len(sed_bins_list)])

    counter = 0
    counter_has_stellar_mass = 0
    for wz in range(N_METAL_BINS):
        for wa in range(cfg.par.N_STELLAR_AGE_BINS+1):
            for wm in range(cfg.par.N_MASS_BINS+1):
                if has_stellar_mass[wz,wa,wm] == True:
                    binned_mfrac[counter] = mfrac[counter_has_stellar_mass]
                    binned_stellar_fnu[counter,:] = binned_stellar_fnu_has_stellar_mass[counter_has_stellar_mass,:]
                    counter_has_stellar_mass += 1 
                counter+=1

    print(f'after selecting for ones with stellar mass: {np.shape(binned_stellar_fnu)}')


    
    '''
    #DEBUG trap for nans and infs
    if np.isinf(np.sum(binned_stellar_nu)):  pdb.set_trace()
    if np.isinf(np.sum(binned_stellar_fnu)): pdb.set_trace()
    if np.isnan(np.sum(binned_stellar_nu)): pdb.set_trace()
    if np.isnan(np.sum(binned_stellar_fnu)): pdb.set_trace()
    '''

    #now binned_stellar_nu and binned_stellar_fnu are the SEDs for the bins in order of wz, wa, wm 
    
    #create the point source collections: we loop through the bins and
    #see what star particles correspond to these.  if any do, then we
    #add them to a list, and create a point source collection out of
    #these


    print ('adding point source collections')
    t1=datetime.now()


    totallum = 0 
    totalmass = 0 
    counter=0
    for wz in range(N_METAL_BINS):
        for wa in range(cfg.par.N_STELLAR_AGE_BINS+1):
            for wm in range(cfg.par.N_MASS_BINS+1):
                
                if has_stellar_mass[wz,wa,wm] == True:
                
                    source = m.add_point_source_collection()
                    
                    
                    nu = binned_stellar_nu
                    fnu = binned_stellar_fnu[counter,:]
                    nu,fnu = wavelength_compress(nu,fnu,df_nu)
                    
                    #reverse for hyperion
                    nu = nu[::-1]
                    fnu = fnu[::-1]

                    
                    #source luminosities
                    #here, each (wz, wa, wm) bin will have an associated mfrac that corresponds to the fnu generated for this bin
                    #while each star particle in the bin has a distinct mass, they all share mfrac as this value depends only on the age and Z of the star
                    #thus, there are 'counter' number of binned_mfrac values (to match the number of fnu arrays)
                    lum = np.array([stars_list[i].mass/constants.M_sun.cgs.value*constants.L_sun.cgs.value/binned_mfrac[counter] for i in stars_in_bin[(wz,wa,wm)]])
                    lum *= np.absolute(np.trapz(fnu,x=nu))
                    source.luminosity = lum
                    


                    for i in stars_in_bin[(wz,wa,wm)]:  totalmass += stars_list[i].mass
                    
                    #source positions
                    pos = np.zeros([len(stars_in_bin[(wz,wa,wm)]),3])
                    #for i in range(len(stars_in_bin[(wz,wa,wm)])): pos[i,:] = stars_list[i].positions
                    for i in range(len(stars_in_bin[(wz,wa,wm)])):
                        pos[i,:] = stars_list[stars_in_bin[(wz,wa,wm)][i]].positions

                    source.position=pos

                    #source spectrum
                    source.spectrum = (nu,fnu)
                                    
                    totallum += np.sum(source.luminosity)

                    
                    '''
                    if np.isnan(lum): 
                        print 'lum is a nan in point source collection addition. exiting now.'
                        sys.exit()
                    if np.isinf(lum): 
                        print 'lum is an inf in point source collection addition. exiting now.'
                        sys.exit()
                    '''
                counter+=1

                
    if cosmoflag == False: add_bulge_disk_stars(df_nu,binned_stellar_nu,binned_stellar_fnu,disk_fnu,bulge_fnu,stars_list,diskstars_list,bulgestars_list,m)

    m.set_sample_sources_evenly(True)

    t2=datetime.now()
    print ('[source_creation/add_binned_seds:] Execution time for point source collection adding = '+str(t2-t1))
    print ('[source_creation/add_binned_seds:] Total Luminosity of point source collection is: ',totallum)


    return m
Exemple #3
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)

if par.FORCE_BINNING == False:
    stellar_nu, stellar_fnu, disk_fnu, bulge_fnu = sg.allstars_sed_gen(
        stars_list, diskstars_list, bulgestars_list,
        ds.cosmological_simulation, sp)
    m = add_newstars(df_nu, stellar_nu, stellar_fnu, disk_fnu, bulge_fnu,
                     stars_list, diskstars_list, bulgestars_list,
                     ds.cosmological_simulation, m)

else:
    # 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)

# save SEDs
# stars and black holes can't both be in the sim and write stellar SEDs to a file becuase they have different wavelength sizes
def direct_add_stars(df_nu, stars_list, diskstars_list, bulgestars_list,
                     cosmoflag, m, sp):

    print("--------------------------------\n")
    print("Adding unbinned stars to the grid\n")
    print("--------------------------------\n")

    totallum_newstars = 0.
    unbinned_stars_list = []

    for star in stars_list:
        if star.age <= cfg.par.max_age_direct:
            unbinned_stars_list.append(star)

    nstars = len(unbinned_stars_list)
    if (nstars == 0):
        print("No unbinned stars to add")
        return m

    else:
        print("Number of unbinned stars to added: ", nstars)

    stellar_nu, stellar_fnu, disk_fnu, bulge_fnu, mfrac = sg.allstars_sed_gen(
        unbinned_stars_list, cosmoflag, sp)

    #SED_gen now returns an additional parameter, mfrac, to properly scale the FSPS SSP spectra, which are in units of formed mass, not current stellar mass
    pos_arr = []
    fnu_arr = []
    for i in range(nstars):
        nu = stellar_nu[:]
        fnu = stellar_fnu[i, :]

        nu, fnu = wavelength_compress(nu, fnu, df_nu)
        # reverse the arrays for hyperion
        nu = nu[::-1]
        fnu = fnu[::-1]

        lum = np.absolute(
            np.trapz(fnu, x=nu)
        ) * unbinned_stars_list[i].mass / constants.M_sun.cgs.value / mfrac[i]
        lum *= constants.L_sun.cgs.value

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

        if young_star or pagb:
            pos = unbinned_stars_list[i].positions
            pos_arr.append(pos)
            fnu_arr.append(stellar_fnu[i, :])

        # add new stars
        totallum_newstars += lum
        #m.add_spherical_source(luminosity = lum,radius = 10.*const.rsun,spectrum = (nu,fnu),

        m.add_point_source(luminosity=lum, spectrum=(nu, fnu), position=pos)

    print('[source_creation/add_unbinned_newstars:] totallum_newstars = ',
          totallum_newstars)

    if cfg.par.add_neb_emission and (cfg.par.SAVE_NEB_SEDS
                                     or cfg.par.add_DIG_neb) and (len(pos_arr)
                                                                  != 0):
        dump_NEB_SEDs(stellar_nu, fnu_arr, pos_arr)

    if cosmoflag == False:
        add_bulge_disk_stars(df_nu, stellar_nu, stellar_fnu, disk_fnu,
                             bulge_fnu, unbinned_stars_list, diskstars_list,
                             bulgestars_list, m)

    m.set_sample_sources_evenly(True)

    return m