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 DIG_source_add(m,reg,df_nu): print("--------------------------------\n") print("Adding DIG to Source List in source_creation\n") print("--------------------------------\n") print ("Getting specific energy dumped in each grid cell") try: rtout = cfg.model.outputfile + '.sed' try: grid_properties = np.load(cfg.model.PD_output_dir+"/grid_physical_properties."+cfg.model.snapnum_str+'_galaxy'+cfg.model.galaxy_num_str+".npz") except: grid_properties = np.load(cfg.model.PD_output_dir+"/grid_physical_properties."+cfg.model.snapnum_str+".npz") cell_info = np.load(cfg.model.PD_output_dir+"/cell_info."+cfg.model.snapnum_str+"_"+cfg.model.galaxy_num_str+".npz") except: print ("ERROR: Can't proceed with DIG nebular emission calculation. Code is unable to find the required files.") print ("Make sure you have the rtout.sed, grid_physical_properties.npz and cell_info.npz for the corresponding galaxy.") return m_out = ModelOutput(rtout) oct = m_out.get_quantities() grid = oct order = find_order(grid.refined) refined = grid.refined[order] quantities = {} for field in grid.quantities: quantities[('gas', field)] = grid.quantities[field][0][order][~refined] cell_width = cell_info["fw1"][:,0] mass = (quantities['gas','density']*units.g/units.cm**3).value * (cell_width**3) met = grid_properties["grid_gas_metallicity"] specific_energy = (quantities['gas','specific_energy']*units.erg/units.s/units.g).value specific_energy = (specific_energy * mass) # in ergs/s # Black 1987 curve has a integrated ergs/s/cm2 of 0.0278 so the factor we need to multiply it by is given by this value factor = specific_energy/(cell_width**2)/(0.0278) mask1 = np.where(mass != 0 )[0] mask = np.where((mass != 0 ) & (factor >= cfg.par.DIG_min_factor))[0] # Masking out all grid cells that have no gas mass and where the specific emergy is too low print (len(factor), len(mask1), len(mask)) factor = factor[mask] cell_width = cell_width[mask] cell_x = (cell_info["xmax"] - cell_info["xmin"])[mask] cell_y = (cell_info["ymax"] - cell_info["ymin"])[mask] cell_z = (cell_info["zmax"] - cell_info["zmin"])[mask] pos = np.vstack([cell_x, cell_y, cell_z]).transpose() met = grid_properties["grid_gas_metallicity"][:, mask] met = np.transpose(met) fnu_arr = sg.get_dig_seds(factor, cell_width, met) dat = np.load(cfg.par.pd_source_dir + "/powderday/nebular_emission/data/black_1987.npz") spec_lam = dat["lam"] nu = 1.e8 * constants.c.cgs.value / spec_lam for i in range(len(factor)): fnu = fnu_arr[i,:] nu, fnu = wavelength_compress(nu,fnu,df_nu) nu = nu[::-1] fnu = fnu[::-1] lum = np.absolute(np.trapz(fnu,x=nu))*constants.L_sun.cgs.value source = m.add_point_source() source.luminosity = lum # [ergs/s] source.spectrum = (nu,fnu) source.position = pos[i] # [cm]
def BH_source_add(m,reg,df_nu,boost): print("--------------------------------\n") print("Adding Black Holes to Source List in source_creation\n") print("--------------------------------\n") try: nholes = reg["bh","sed"].shape[0] except: print('BH source creation failed. No BH found') nholes = 0 if nholes == 0: print('BH source creation failed. No BH found') else: #temporary wavelength compress just to get the length of the #compressed nu for a master array dumnu,dumfnu = wavelength_compress(reg["bh","nu"].value,reg["bh","sed"][0,:].value,df_nu) master_bh_fnu = np.zeros([nholes,len(dumnu)]) width = reg.right_edge-reg.left_edge agn_ids = [] for i in range(nholes): #since the BH was added in a front end, we don't know #if the hole is in the actual cut out region of the yt #dataset. so we need to filter out any holes that #might not be in the simulation domain or have no luminosity. if ((reg["bh","coordinates"][i,0].in_units('kpc') < (reg.center[0].in_units('kpc')+(0.5*width[0].in_units('kpc')))) and (reg["bh","coordinates"][i,0].in_units('kpc') > (reg.center[0].in_units('kpc')-(0.5*width[0].in_units('kpc')))) and (reg["bh","coordinates"][i,1].in_units('kpc') < (reg.center[1].in_units('kpc')+(0.5*width[1].in_units('kpc')))) and (reg["bh","coordinates"][i,1].in_units('kpc') > (reg.center[1].in_units('kpc')-(0.5*width[1].in_units('kpc')))) and (reg["bh","coordinates"][i,2].in_units('kpc') < (reg.center[2].in_units('kpc')+(0.5*width[2].in_units('kpc')))) and (reg["bh","coordinates"][i,2].in_units('kpc') > (reg.center[2].in_units('kpc')-(0.5*width[2].in_units('kpc')))) and (reg["bh","luminosity"][i].value > 0)): agn_ids.append(i) print ('Number AGNs in the cutout with non zero luminositites: ', len(agn_ids)) fnu_arr = sg.get_agn_seds(agn_ids, reg) nu = reg["bh","nu"].value for j in range(len(agn_ids)): i = agn_ids[j] fnu = fnu_arr[j,:] nu, fnu = wavelength_compress(nu,fnu,df_nu) master_bh_fnu[i,:] = fnu print('Boosting BH Coordinates and adding BH #%d to the source list now'%i) #the tolist gets rid of the array brackets bh = m.add_point_source(luminosity = reg["bh","luminosity"][i].value.tolist(), spectrum = (nu,fnu), position = (reg["bh","coordinates"][i,:].in_units('cm').value-boost).tolist()) dump_AGN_SEDs(nu,master_bh_fnu,reg["bh","luminosity"].value)
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
sp = fsps.StellarPopulation() # Get dust wavelengths. This needs to preceed the generation of sources # for hyperion since the wavelengths of the SEDs need to fit in the # dust opacities. df = h5py.File(cfg.par.dustdir + cfg.par.dustfile, 'r') o = df['optical_properties'] 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) 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,
print('isochrone = bpass') cfg.par.solar = 0.020 print(f'solar metallicity = {cfg.par.solar}') print('----------------------------------------------') # Get dust wavelengths. This needs to preceed the generation of sources # for hyperion since the wavelengths of the SEDs need to fit in the # dust opacities. df = h5py.File(cfg.par.dustdir + cfg.par.dustfile, 'r') o = df['optical_properties'] 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)
sp = fsps.StellarPopulation() # Get dust wavelengths. This needs to preceed the generation of sources # for hyperion since the wavelengths of the SEDs need to fit in the # dust opacities. df = h5py.File(cfg.par.dustdir + cfg.par.dustfile, 'r') o = df['optical_properties'] df_nu = o['nu'] df_chi = o['chi'] df.close() # add sources to hyperion ad = pf.all_data() stars_list, diskstars_list, bulgestars_list = sg.star_list_gen( boost, xcent, ycent, zcent, dx, dy, dz, pf, ad) nstars = len(stars_list) if cfg.par.BH_SED == True: BH_source_add(m, pf, 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, pf.cosmological_simulation, sp) m = add_newstars(df_nu, stellar_nu, stellar_fnu, disk_fnu, bulge_fnu, stars_list, diskstars_list, bulgestars_list,
def DIG_source_add(m, reg, df_nu, boost): print("--------------------------------") print("Adding DIG to Source List in source_creation") print("--------------------------------") print("Getting the specific energy dumped in each grid cell") try: rtout = cfg.model.outputfile + '_DIG_energy_dumped.sed' try: grid_properties = np.load(cfg.model.PD_output_dir + "/grid_physical_properties." + cfg.model.snapnum_str + '_galaxy' + cfg.model.galaxy_num_str + ".npz") except: grid_properties = np.load(cfg.model.PD_output_dir + "/grid_physical_properties." + cfg.model.snapnum_str + ".npz") cell_info = np.load(cfg.model.PD_output_dir + "/cell_info." + cfg.model.snapnum_str + "_" + cfg.model.galaxy_num_str + ".npz") except: print( "ERROR: Can't proceed with DIG nebular emission calculation. Code is unable to find the required files." ) print( "Make sure you have the rtout.sed, grid_physical_properties.npz and cell_info.npz for the corresponding galaxy." ) return m_out = ModelOutput(rtout) oct = m_out.get_quantities() grid = oct order = find_order(grid.refined) refined = grid.refined[order] quantities = {} for field in grid.quantities: quantities[('gas', field)] = grid.quantities[field][0][order][~refined] cell_width = cell_info["fw1"][:, 0] mass = (quantities['gas', 'density'] * units.g / units.cm**3).value * (cell_width**3) specific_energy = (quantities['gas', 'specific_energy'] * units.erg / units.s / units.g).value specific_energy = (specific_energy * mass) # in ergs/s mask = np.where( mass != 0)[0] # Masking out all grid cells that have no gas mass pos = cell_info["fc1"][mask] - boost cell_width = cell_width[mask] met = grid_properties["grid_gas_metallicity"][:, mask] met = np.transpose(met) specific_energy = specific_energy[mask] mask = [] logU_arr = [] lam_arr = [] fnu_arr = [] for i in range(len(cell_width)): # Getting shape of the incident spectrum by taking a distance weighted average of the CLOUDY output spectrum of nearby young stars. sed_file_name = cfg.model.PD_output_dir + "neb_seds_galaxy_" + cfg.model.galaxy_num_str + ".npz" lam, fnu = get_DIG_sed_shape( pos[i], cell_width[i], sed_file=sed_file_name ) # Returns input SED shape, lam in Angstrom, fnu in Lsun/Hz # If the gas cell has no young stars within a specified distance (stars_max_dist) then skip it. if len(np.atleast_1d(fnu)) == 1: continue # Calulating the ionization parameter by extrapolating the specfic energy beyind the lyman limit # using the SED shape calculated above. logU = get_DIG_logU(lam, fnu, specific_energy[i], cell_width[i]) lam_arr.append(lam) fnu_arr.append(fnu) # Only cells with ionization parameter greater than the parameter DIG_min_logU are considered # for nebular emission calculation. This is done so as to speed up the calculation by ignoring # the cells that do not have enough energy to prduce any substantial emission if logU > cfg.par.DIG_min_logU: mask.append(i) lam_arr.append(lam) fnu_arr.append(fnu) logU_arr.append(logU) cell_width = cell_width[mask] met = met[mask] lam_arr = np.array(lam_arr) fnu_arr = np.array(fnu_arr) logU = np.array(logU_arr) pos = pos[mask] if (len(mask)) == 0: print( "No gas particles fit the criteria for calculating DIG. Skipping DIG calculation" ) return print( "----------------------------------------------------------------------------------" ) print("Calculating nebular emission from Diffused Ionized Gas for " + str(len(mask)) + " gas cells") print( "----------------------------------------------------------------------------------" ) fnu_arr_neb = sg.get_dig_seds(lam_arr, fnu_arr, logU, cell_width, met) nu = 1.e8 * constants.c.cgs.value / lam for i in range(len(logU)): fnu = fnu_arr_neb[i, :] nu, fnu = wavelength_compress(nu, fnu, df_nu) nu = nu[::-1] fnu = fnu[::-1] lum = np.absolute(np.trapz(fnu, x=nu)) * constants.L_sun.cgs.value source = m.add_point_source() source.luminosity = lum # [ergs/s] source.spectrum = (nu[::-1], fnu[::-1]) source.position = pos[i] # [cm]
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