def calc_emline(stars_list): print('[SED_gen/calc_emline]: Calculating Emission Line Fluxes') #this function is awful and redundant. itloops over just the new #stars in the box and calculates the SEDs for the ones smaller #than cfg.par.HII_max_age. its a bit wasteful since we already #calculate these SEDs, but its difficult to get the file to save #in a nice format without incl global sp if sp is None: sp = fsps.StellarPopulation() #set up arrays #first how many young stars are there? newstars_idx = [] for counter, star in enumerate(stars_list): if star.age <= cfg.par.HII_max_age: newstars_idx.append(counter) num_newstars = len(newstars_idx) #set up a dummy sps model just to get number of wavelengths sp.params["tage"] = stars_list[0].age sp.params["zmet"] = stars_list[0].fsps_zmet sp.params["add_neb_emission"] = True wav, spec = sp.get_spectrum() n_emlines = len(sp.emline_wavelengths) #now set up the actual arrays master_emline_wavelength = np.zeros([n_emlines]) master_emline_lum = np.zeros([num_newstars, n_emlines]) #loop through the newstars now and save the emlines for counter, i in enumerate(newstars_idx): num_HII_clusters = int( np.floor((stars_list[i].mass / constants.M_sun.cgs.value) / (cfg.par.stellar_cluster_mass))) #first we calculate the spectrum without lines on to get logU 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 sp.params['gas_logu'] = cfg.par.gas_logu if cfg.par.FORCE_gas_logz == False: sp.params['gas_logz'] = np.log10(stars_list[i].metals / cfg.par.solar) else: sp.params['gas_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) num_HII_clusters = int( np.floor((stars_list[i].mass / constants.M_sun.cgs.value) / (cfg.par.stellar_cluster_mass))) neb_file_output = False sp.params["add_neb_emission"] = False spec = sp.get_spectrum(tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet) #now we know logU, so recalculate the lines if cfg.par.FORCE_gas_logu: LogU = cfg.par.gas_logu 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) sp.params['gas_logu'] = LogU sp.params["add_neb_emission"] = True spec = sp.get_spectrum(tage=stars_list[i].age, zmet=stars_list[i].fsps_zmet) f = spec[1] * num_HII_clusters emline_luminosity = sp.emline_luminosity * num_HII_clusters emline_wavelength = sp.emline_wavelengths #the stellar population returns the calculation in units of Lsun/1 Msun: https://github.com/dfm/python-fsps/issues/117#issuecomment-546513619 master_emline_lum[counter, :] = emline_luminosity * ( (stars_list[i].mass * u.g).to(u.Msun).value) if counter == 0: master_emline_wavelength = emline_wavelength #set up the emline output file as new, and insert the wavelengths dump_emline(master_emline_wavelength, None, append=False) #write the remainder of the emline file dump_emline(master_emline_wavelength, master_emline_lum, append=True)
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