def model_sn(x, z, day, sn_av): # pull out spectrum for the chosen day day_idx_ = np.argmin(abs(sn_day_arr - day)) day_idx = np.where(salt2_spec['day'] == sn_day_arr[day_idx_])[0] sn_spec_llam = salt2_spec['flam'][day_idx] sn_spec_lam = salt2_spec['lam'][day_idx] # ------ Apply dust extinction sn_dusty_llam = du.get_dust_atten_model(sn_spec_lam, sn_spec_llam, sn_av) # ------ Apply redshift sn_lam_z, sn_flam_z = apply_redshift(sn_spec_lam, sn_dusty_llam, z) # ------ Apply some LSF. # This is a NUISANCE FACTOR ONLY FOR NOW # When we use actual SNe they will be point sources. #lsf_sigma = 0.5 #sn_flam_z = scipy.ndimage.gaussian_filter1d(input=sn_flam_z, sigma=lsf_sigma) sn_mod = griddata(points=sn_lam_z, values=sn_flam_z, xi=x) # ------ combine host light # some fraction to account for host contamination # This fraction is a free parameter #sn_flam_hostcomb = sn_mod + host_frac * host_flam return sn_mod
def get_sn_spec_path(redshift, day_chosen=None, chosen_av=None): """ This function will assign a random spectrum from the basic SALT2 spectrum form Lou. Equal probability is given to any day relative to maximum. This will change for the final version. The spectrum file contains a type 1A spectrum from -20 to +50 days relative to max. Since the -20 spectrum is essentially empty, I won't choose that spectrum for now. Also, for now, I will restrict this function to -5 to +20 days relative to maximum. """ # Create array for days relative to max days_arr = np.arange(-5, 30, 1) # Define scaling factor # Check sn_scaling.py in same folder as this code sn_scalefac = 2.0842526537870818e+48 # choose a random day relative to max if not day_chosen: day_chosen = np.random.choice(days_arr) # pull out spectrum for the chosen day day_idx = np.where(salt2_spec['day'] == day_chosen)[0] sn_spec_lam = salt2_spec['lam'][day_idx] sn_spec_llam = salt2_spec['llam'][day_idx] * sn_scalefac # Apply dust extinction # Apply Calzetti dust extinction depending on av value chosen if not chosen_av: av_arr = np.arange(0.0, 5.0, 0.001) # in mags chosen_av = np.random.choice(av_arr) sn_dusty_llam = du.get_dust_atten_model(sn_spec_lam, sn_spec_llam, chosen_av) # Apply redshift sn_wav_z, sn_flux = apply_redshift(sn_spec_lam, sn_dusty_llam, redshift) # Save individual spectrum file if it doesn't already exist sn_spec_path = roman_sims_seds + "salt2_spec_day" + str(day_chosen) + \ "_z" + "{:.3f}".format(redshift).replace('.', 'p') + \ "_av" + "{:.3f}".format(chosen_av).replace('.', 'p') + \ ".txt" if not os.path.isfile(sn_spec_path): fh_sn = open(sn_spec_path, 'w') fh_sn.write("# lam flux") fh_sn.write("\n") for j in range(len(sn_wav_z)): fh_sn.write("{:.2f}".format(sn_wav_z[j]) + " " + str(sn_flux[j])) fh_sn.write("\n") fh_sn.close() #print("Saved:", sn_spec_path) return sn_spec_path
def model_galaxy(x, z, ms, age, logtau, av): tau = 10**logtau # logtau is log of tau in gyr if tau < 20.0: tau_int_idx = int((tau - int(np.floor(tau))) * 1e3) age_idx = np.argmin(abs(model_ages - age * 1e9)) model_idx = tau_int_idx * len(model_ages) + age_idx models_taurange_idx = np.argmin( abs(np.arange(tau_low, tau_high, 1) - int(np.floor(tau)))) models_arr = all_m62_models[models_taurange_idx] elif tau >= 20.0: logtau_arr = np.arange(1.30, 2.01, 0.01) logtau_idx = np.argmin(abs(logtau_arr - logtau)) age_idx = np.argmin(abs(model_ages - age * 1e9)) model_idx = logtau_idx * len(model_ages) + age_idx models_arr = all_m62_models[-1] model_llam = np.asarray(models_arr[model_idx], dtype=np.float64) # ------ Apply dust extinction ml = np.asarray(model_lam, dtype=np.float64) model_dusty_llam = du.get_dust_atten_model(ml, model_llam, av) # ------ Multiply luminosity by stellar mass model_dusty_llam = model_dusty_llam * 10**ms # ------ Apply redshift model_lam_z, model_flam_z = apply_redshift(ml, model_dusty_llam, z) model_flam_z = Lsol * model_flam_z # ------ Apply LSF model_lsfconv = gaussian_filter1d(input=model_flam_z, sigma=1.0) # ------ Downgrade to grism resolution model_mod = griddata(points=model_lam_z, values=model_lsfconv, xi=x) return model_mod
def model_sn(x, z, day, sn_av): # pull out spectrum for the chosen day day_idx_ = np.argmin(abs(sn_day_arr - day)) day_idx = np.where(salt2_spec['day'] == sn_day_arr[day_idx_])[0] sn_spec_llam = salt2_spec['flam'][day_idx] * sn_scalefac sn_spec_lam = salt2_spec['lam'][day_idx] # ------ Apply dust extinction sn_dusty_llam = du.get_dust_atten_model(sn_spec_lam, sn_spec_llam, sn_av) # ------ Apply redshift sn_lam_z, sn_flam_z = apply_redshift(sn_spec_lam, sn_dusty_llam, z) # ------ Regrid to Roman wavelength sampling sn_mod = griddata(points=sn_lam_z, values=sn_flam_z, xi=x) return sn_mod
def get_gal_spec_path(redshift): """ This function will generate a template SED assuming a composite stellar population using BC03. -- SFH is assumed to be exponential. -- where tau is in between 0.01 to 15.0 (in Gyr) -- Age is dependent on z and only allows for models that are at least 100 Myr younger than the Universe. -- Dust is applied assuming a Calzetti form for the dust extinction law. -- Metallicity is one of the six options in BC03. -- Log of stellar mass/M_sol is between 10.0 to 11.5 """ plot_tocheck = False # ---------- Choosing stellar population parameters ----------- # # Choose stellar pop parameters at random # --------- Stellar mass log_stellar_mass_arr = np.linspace(10.0, 11.5, 100) log_stellar_mass_chosen = np.random.choice(log_stellar_mass_arr) log_stellar_mass_str = "{:.2f}".format(log_stellar_mass_chosen).replace( '.', 'p') # --------- Age age_arr = np.arange(0.1, 13.0, 0.005) # in Gyr # Now choose age consistent with given redshift # i.e., make sure model is not older than the Universe # Allowing at least 100 Myr for the first galaxies to form after Big Bang age_at_z = astropy_cosmo.age(redshift).value # in Gyr age_lim = age_at_z - 0.1 # in Gyr chosen_age = np.random.choice(age_arr) while chosen_age > age_lim: chosen_age = np.random.choice(age_arr) # --------- SFH # Choose SFH form from a few different models # and then choose params for the chosen SFH form sfh_forms = [ 'instantaneous', 'constant', 'exponential', 'linearly_declining' ] # choose_sfh(sfh_forms[2]) tau_arr = np.arange(0.01, 15.0, 0.005) # in Gyr chosen_tau = np.random.choice(tau_arr) # --------- Metallicity metals_arr = np.array([0.0001, 0.0004, 0.004, 0.008, 0.02, 0.05]) # While the newer 2016 version has an additional metallicity # referred to as "m82", the documentation never specifies the # actual metallicity associated with it. So I'm ignoring that one. metals = 0.02 # np.random.choice(metals_arr) # Get hte metallicity string if metals == 0.0001: metallicity = 'm22' elif metals == 0.0004: metallicity = 'm32' elif metals == 0.004: metallicity = 'm42' elif metals == 0.008: metallicity = 'm52' elif metals == 0.02: metallicity = 'm62' elif metals == 0.05: metallicity = 'm72' # ----------- CALL BC03 ----------- # The BC03 generated spectra will always be at redshift=0 and dust free. # This code will apply dust extinction and redshift effects manually #outdir = home + '/Documents/bc03_output_dir/' #gen_bc03_spectrum(chosen_tau, metals, outdir) logtau = np.log10(chosen_tau) bc03_spec_wav = np.array(model_lam, dtype=np.float64) bc03_spec_llam = get_bc03_spec(chosen_age, logtau) # Apply Calzetti dust extinction depending on av value chosen av_arr = np.arange(0.0, 5.0, 0.001) # in mags chosen_av = np.random.choice(av_arr) bc03_dusty_llam = du.get_dust_atten_model(bc03_spec_wav, bc03_spec_llam, chosen_av) # Multiply flux by stellar mass bc03_dusty_llam = bc03_dusty_llam * 10**log_stellar_mass_chosen # Convert to physical units bc03_dusty_llam *= Lsol # --------------------- CHECK ---------------------- # ---------------------- TBD ----------------------- # 1. # Given the distribution you have for SFHs here, # can you recover the correct cosmic star formation # history? i.e., if you took the distribution of models # you have and computed the cosmic SFH do you get the # Madau diagram back? # 2. # Do your model galaxies follow other scaling relations? # Apply IGM depending on boolean flag #if apply_igm: # pass bc03_wav_z, bc03_flux = apply_redshift(bc03_spec_wav, bc03_dusty_llam, redshift) if plot_tocheck: fig = plt.figure() ax = fig.add_subplot(111) ax.set_xlabel(r'$\lambda\ \mathrm{[\AA]}$', fontsize=14) ax.set_ylabel(r'$f_\lambda\ \mathrm{[erg\, s^{-1}\, cm^{-2}\, \AA]}$', fontsize=14) #ax.plot(bc03_spec_wav, bc03_spec_llam, label='Orig model') #ax.plot(bc03_spec_wav, bc03_dusty_llam, label='Dusty model') ax.plot(bc03_wav_z, bc03_flux, label='Redshfited dusty model with chosen Ms') ax.legend(loc=0) ax.set_xlim(500, 25000) plt.show() # Save file gal_spec_path = roman_sims_seds + 'bc03_template' + \ "_z" + "{:.3f}".format(redshift).replace('.', 'p') + \ "_ms" + log_stellar_mass_str + \ "_age" + "{:.3f}".format(chosen_age).replace('.', 'p') + \ "_tau" + "{:.3f}".format(chosen_tau).replace('.', 'p') + \ "_met" + "{:.4f}".format(metals).replace('.', 'p') + \ "_av" + "{:.3f}".format(chosen_av).replace('.', 'p') + \ ".txt" # Print info to screen # print("\n") # print("--------------------------------------------------") # print("Randomly chosen redshift:", redshift) # print("Age limit at redshift [Gyr]:", age_lim) # print("\nRandomly chosen stellar population parameters:") # print("Age [Gyr]:", chosen_age) # print("log(stellar mass) [log(Ms/M_sol)]:", log_stellar_mass_chosen) # print("Tau [exp. decl. timescale, Gyr]:", chosen_tau) # print("Metallicity (abs. frac.):", metals) # print("Dust extinction in V-band (mag):", chosen_av) # print("\nWill save to file:", gal_spec_path) # Save individual spectrum file if it doesn't already exist if not os.path.isfile(gal_spec_path): fh_gal = open(gal_spec_path, 'w') fh_gal.write("# lam flux") fh_gal.write("\n") for j in range(len(bc03_flux)): fh_gal.write("{:.2f}".format(bc03_wav_z[j]) + " " + str(bc03_flux[j])) fh_gal.write("\n") fh_gal.close() return gal_spec_path
def model_galaxy(x, z, ms, age, logtau, av): """ Expects to get the following arguments x: observed wavelength grid z: redshift to apply to template ms: log of the stellar mass age: age of SED in Gyr tau: exponential SFH timescale in Gyr metallicity: absolute fraction of metals av: visual dust extinction """ """ metals = 0.02 # Get the metallicity in the format that BC03 needs if metals == 0.0001: metallicity = 'm22' elif metals == 0.0004: metallicity = 'm32' elif metals == 0.004: metallicity = 'm42' elif metals == 0.008: metallicity = 'm52' elif metals == 0.02: metallicity = 'm62' elif metals == 0.05: metallicity = 'm72' """ tau = 10**logtau # logtau is log of tau in gyr #print("log(tau [Gyr]):", logtau) #print("Tau [Gyr]:", tau) #print("Age [Gyr]:", age) if tau < 20.0: tau_int_idx = int((tau - int(np.floor(tau))) * 1e3) age_idx = np.argmin(abs(model_ages - age*1e9)) model_idx = tau_int_idx * len(model_ages) + age_idx models_taurange_idx = np.argmin(abs(np.arange(tau_low, tau_high, 1) - \ int(np.floor(tau)))) models_arr = all_m62_models[models_taurange_idx] elif tau >= 20.0: logtau_arr = np.arange(1.30, 2.01, 0.01) logtau_idx = np.argmin(abs(logtau_arr - logtau)) age_idx = np.argmin(abs(model_ages - age*1e9)) model_idx = logtau_idx * len(model_ages) + age_idx models_arr = all_m62_models[-1] model_llam = np.asarray(models_arr[model_idx], dtype=np.float64) """ This np.asarray stuff (here and for model_lam below) is very important for numba to be able to do its magic. It does not like args passed into a numba @jit(nopython=True) decorated function to come from np.load(..., mmap_mode='r'). So I made the arrays passed into the function explicitly be numpy arrays of dtype=np.float64. For now only the two functions in dust_utils are numba decorated because applying the dust extinction was the most significant bottleneck in this code. I suspect if more functions were numba decorated then the code will go even faster. E.g., after using numba an SN run of 2000 steps finishes in <~2 min whereas it used to take ~25 min (on my laptop). On PLFFSN2 the same run used to take ~9 min, it now finishes in seconds! For a galaxy a run of 2000 steps """ # ------ Apply dust extinction ml = np.asarray(model_lam, dtype=np.float64) model_dusty_llam = du.get_dust_atten_model(ml, model_llam, av) # ------ Multiply luminosity by stellar mass model_dusty_llam = model_dusty_llam * 10**ms # ------ Apply redshift model_lam_z, model_flam_z = apply_redshift(ml, model_dusty_llam, z) model_flam_z = Lsol * model_flam_z # ------ Apply LSF model_lsfconv = gaussian_filter1d(input=model_flam_z, sigma=1.0) # ------ Downgrade to grism resolution model_mod = griddata(points=model_lam_z, values=model_lsfconv, xi=x) return model_mod
def model_host(x, z, age, logtau, av): """ Expects to get the following arguments x: observed wavelength grid z: redshift to apply to template ms: log of the stellar mass age: age of SED in Gyr tau: exponential SFH timescale in Gyr metallicity: absolute fraction of metals av: visual dust extinction """ metals = 0.02 tau = 10**logtau # logtau is log of tau in gyr #print("log(tau [Gyr]):", logtau) #print("Tau [Gyr]:", tau) #print("Age [Gyr]:", age) if tau < 20.0: tau_int_idx = int((tau - int(np.floor(tau))) * 1e3) age_idx = np.argmin(abs(model_ages - age*1e9)) model_idx = tau_int_idx * len(model_ages) + age_idx models_taurange_idx = np.argmin(abs(np.arange(tau_low, tau_high, 1) - int(np.floor(tau)))) models_arr = all_m62_models[models_taurange_idx] #print("Tau int and age index:", tau_int_idx, age_idx) #print("Tau and age from index:", models_taurange_idx+tau_int_idx/1e3, model_ages[age_idx]/1e9) #print("Model tau range index:", models_taurange_idx) elif tau >= 20.0: logtau_arr = np.arange(1.30, 2.01, 0.01) logtau_idx = np.argmin(abs(logtau_arr - logtau)) age_idx = np.argmin(abs(model_ages - age*1e9)) model_idx = logtau_idx * len(model_ages) + age_idx models_arr = all_m62_models[-1] #print("logtau and age index:", logtau_idx, age_idx) #print("Tau and age from index:", 10**(logtau_arr[logtau_idx]), model_ages[age_idx]/1e9) #print("Model index:", model_idx) model_llam = models_arr[model_idx] # ------ Apply dust extinction model_dusty_llam = get_dust_atten_model(model_lam, model_llam, av) # ------ Multiply luminosity by stellar mass #model_dusty_llam = model_dusty_llam * 10**ms # ------ Apply redshift model_lam_z, model_flam_z = cosmo.apply_redshift(model_lam, model_dusty_llam, z) Lsol = 3.826e33 model_flam_z = Lsol * model_flam_z # ------ Apply LSF model_lsfconv = gaussian_filter1d(input=model_flam_z, sigma=1.0) # ------ Downgrade to grism resolution model_mod = griddata(points=model_lam_z, values=model_lsfconv, xi=x) model_err = np.zeros(len(x)) model_cont_norm, model_err_cont_norm = divcont(x, model_mod, model_err, showplot=False) return model_cont_norm
def model_galaxy(x, z, ms, age, logtau, av, stellar_vdisp=False): """ Expects to get the following arguments x: observed wavelength grid z: redshift to apply to template ms: log of the stellar mass age: age of SED in Gyr tau: exponential SFH timescale in Gyr metallicity: absolute fraction of metals av: visual dust extinction """ # If using hte larger model set with no emission lines """ tau = 10**logtau # logtau is log of tau in gyr #print("log(tau [Gyr]):", logtau) #print("Tau [Gyr]:", tau) #print("Age [Gyr]:", age) if tau < 20.0: tau_int_idx = int((tau - int(np.floor(tau))) * 1e3) age_idx = np.argmin(abs(model_ages - age*1e9)) model_idx = tau_int_idx * len(model_ages) + age_idx models_taurange_idx = np.argmin(abs(np.arange(tau_low, tau_high, 1) - int(np.floor(tau)))) models_arr = all_m62_models[models_taurange_idx] elif tau >= 20.0: logtau_arr = np.arange(1.30, 2.01, 0.01) logtau_idx = np.argmin(abs(logtau_arr - logtau)) age_idx = np.argmin(abs(model_ages - age*1e9)) model_idx = logtau_idx * len(model_ages) + age_idx models_arr = all_m62_models[-1] # Force to numpy array for numba model_llam = np.asarray(models_arr[model_idx], dtype=np.float64) """ # Smaller model set with emission lines tau = 10**logtau # logtau is log of tau in gyr tauv = av / 1.086 model_llam = get_template(np.log10(age * 1e9), tau, tauv, 0.02, \ log_age_arr, metal_arr, tau_gyr_arr, tauv_arr, model_grid) model_llam = np.asarray(model_llam, dtype=np.float64) # ------ Apply stellar velocity dispersion # ------ and dust attenuation # assumed for now as a constant 220 km/s # TODO: optimize # -- This does not have to be done each time the model function # is called because we're assuming a constant vel disp if stellar_vdisp: sigmav = 220 model_vdisp = add_stellar_vdisp(ml, model_llam, sigmav) # ------ Apply dust extinction model_dusty_llam = du.get_dust_atten_model(ml, model_vdisp, av) else: # ------ Apply dust extinction model_dusty_llam = du.get_dust_atten_model(ml, model_llam, av) model_dusty_llam = np.asarray(model_dusty_llam, dtype=np.float64) # ------ Multiply luminosity by stellar mass model_dusty_llam = model_dusty_llam * 10**ms # ------ Apply redshift model_lam_z, model_flam_z = apply_redshift(ml, model_dusty_llam, z) #model_flam_z = Lsol * model_flam_z # ------ Apply LSF model_lsfconv = gaussian_filter1d(input=model_flam_z, sigma=10.0) # ------ Downgrade and regrid to grism resolution model_mod = griddata(points=model_lam_z, values=model_lsfconv, xi=x) return model_mod