def get_input_seds(file): totallum = 0 m = Model() m.use_sources(file) nsources = len(m.sources) for i in range(nsources): tempnu = m.sources[i].spectrum["nu"] tempfnu = m.sources[i].spectrum["fnu"] if i == 0: fnu = np.zeros(len(tempnu)) #now we need to scale this because the spectrum is just in #terms of an SSP, and we need to scale by the total luminosity #that wen t into the model (i.e. by the actual stellar mass #used in powderday). ssp_lum = np.absolute(np.trapz(tempnu, tempfnu)) * constants.L_sun.cgs lum_scale = np.sum( m.sources[i].luminosity ) / ssp_lum #we have to do np.sum in case the sources were in a collection tempfnu *= lum_scale.value for i in range(len(fnu)): fnu[i] += tempfnu[i] #ipdb.set_trace() return tempnu, fnu
def temp_hyperion(rtout,outdir, bb_dust=False): import numpy as np import matplotlib as mpl mpl.use('Agg') import matplotlib.pyplot as plt import os from hyperion.model import ModelOutput import astropy.constants as const from matplotlib.colors import LogNorm # seaborn colormap import seaborn.apionly as sns # constants setup AU = const.au.cgs.value # misc variable setup print_name = os.path.splitext(os.path.basename(rtout))[0] m = ModelOutput(rtout) q = m.get_quantities() # get the grid info ri, thetai = q.r_wall, q.t_wall rc = 0.5*( ri[0:len(ri)-1] + ri[1:len(ri)] ) thetac = 0.5*( thetai[0:len(thetai)-1] + thetai[1:len(thetai)] ) # get the temperature profile # and average across azimuthal angle # temperature array in [phi, theta, r] temp = q['temperature'][0].array.T temp2d = np.sum(temp**2, axis=2)/np.sum(temp, axis=2) temp2d_exp = np.hstack((temp2d,temp2d,temp2d[:,0:1])) thetac_exp = np.hstack((thetac-np.pi/2, thetac+np.pi/2, thetac[0]-np.pi/2)) mag = 1 fig = plt.figure(figsize=(mag*8,mag*6)) ax = fig.add_subplot(111, projection='polar') # cmap = sns.cubehelix_palette(light=1, as_cmap=True) cmap = plt.cm.CMRmap im = ax.pcolormesh(thetac_exp, rc/AU, temp2d_exp, cmap=cmap, norm=LogNorm(vmin=5, vmax=100)) # # cmap = plt.cm.RdBu_r # im = ax.pcolormesh(thetac_exp, np.log10(rc/AU), temp2d_exp/10, cmap=cmap, norm=LogNorm(vmin=0.1, vmax=10)) # print temp2d_exp.min(), temp2d_exp.max() im.set_edgecolor('face') ax.set_xlabel(r'$\rm{Polar\,angle\,(Degree)}$',fontsize=20) # ax.set_ylabel(r'$\rm{Radius\,(AU)}$',fontsize=20, labelpad=-140, color='grey') # ax.set_ylabel('',fontsize=20, labelpad=-140, color='grey') ax.tick_params(labelsize=16) ax.tick_params(axis='y', colors='grey') ax.set_yticks(np.hstack((np.arange(0,(int(max(rc)/AU/10000.)+1)*10000, 10000),max(rc)/AU))) # # ax.set_yticks(np.log10(np.array([1, 10, 100, 1000, 10000, max(rc)/AU]))) # ax.set_yticklabels([]) ax.grid(True, color='LightGray', linewidth=1.5) # ax.grid(True, color='k', linewidth=1) ax.set_xticklabels([r'$\rm{90^{\circ}}$',r'$\rm{45^{\circ}}$',r'$\rm{0^{\circ}}$',r'$\rm{-45^{\circ}}$',\ r'$\rm{-90^{\circ}}$',r'$\rm{-135^{\circ}}$',r'$\rm{180^{\circ}}$',r'$\rm{135^{\circ}}$']) cb = fig.colorbar(im, pad=0.1) cb.ax.set_ylabel(r'$\rm{Averaged\,Temperature\,(K)}$',fontsize=20) cb.set_ticks([5,10,20,30,40,50,60,70,80,90,100]) cb.set_ticklabels([r'$\rm{5}$',r'$\rm{10}$',r'$\rm{20}$',r'$\rm{30}$',r'$\rm{40}$',r'$\rm{50}$',r'$\rm{60}$',r'$\rm{70}$',r'$\rm{80}$',r'$\rm{90}$',r'$\rm{>100}$']) # # cb.ax.set_ylabel(r'$\rm{log(T/10)}$',fontsize=20) # cb.set_ticks([0.1, 10**-0.5, 1, 10**0.5, 10]) # cb.set_ticklabels([r'$\rm{-1}$',r'$\rm{-0.5}$',r'$\rm{0}$',r'$\rm{0.5}$',r'$\rm{\geq 1}$']) # cb_obj = plt.getp(cb.ax.axes, 'yticklabels') plt.setp(cb_obj,fontsize=20) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=20) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) fig.savefig(outdir+print_name+'_temperature.png', format='png', dpi=300, bbox_inches='tight') fig.clf() # Plot the radial temperature profile fig = plt.figure(figsize=(12,9)) ax = fig.add_subplot(111) plot_grid = [0,99,199] label_grid = [r'$\rm{outflow}$', r'$\rm{45^{\circ}}$', r'$\rm{midplane}$'] alpha = np.linspace(0.3,1.0,len(plot_grid)) color_list = [[0.8507598215729224, 0.6322174528970308, 0.6702243543099417],\ [0.5687505862870377, 0.3322661256969763, 0.516976691731939],\ [0.1750865648952205, 0.11840023306916837, 0.24215989137836502]] for i in plot_grid: temp_rad, = ax.plot(np.log10(rc/AU), np.log10(temp2d[:,i]),'-',color=color_list[plot_grid.index(i)],\ linewidth=2, markersize=3,label=label_grid[plot_grid.index(i)]) # plot the theoretical prediction for black body dust without considering the extinction if bb_dust == True: from hyperion.model import Model sigma = const.sigma_sb.cgs.value lsun = const.L_sun.cgs.value dum = Model() dum.use_sources(rtout) L_cen = dum.sources[0].luminosity/lsun t_bbdust = (L_cen*lsun/(16*np.pi*sigma*rc**2))**(0.25) temp_bbdust, = ax.plot(np.log10(rc/AU), np.log10(t_bbdust), '--', color='r', linewidth=2.5,label=r'$\rm{blackbody\,dust}$') ax.legend(loc='upper right', numpoints=1, fontsize=24) ax.set_xlabel(r'$\rm{log\,R\,(AU)}$',fontsize=24) ax.set_ylabel(r'$\rm{log\,T\,(K)}$',fontsize=24) [ax.spines[axis].set_linewidth(2) for axis in ['top','bottom','left','right']] ax.minorticks_on() ax.tick_params('both',labelsize=24,width=2,which='major',pad=15,length=5) ax.tick_params('both',labelsize=24,width=2,which='minor',pad=15,length=2.5) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=24) for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) ax.set_ylim([0,4]) fig.gca().set_xlim(left=np.log10(0.05)) # ax.set_xlim([np.log10(0.8),np.log10(10000)]) fig.savefig(outdir+print_name+'_temp_radial.pdf',format='pdf',dpi=300,bbox_inches='tight') fig.clf()
def extract_hyperion(filename,indir=None,outdir=None,dstar=200.0,aperture=None, save=True,filter_func=False,plot_all=False,clean=False, exclude_wl=[],log=True,image=True,obj='BHR71', print_data_w_aper=False,mag=1.5): """ filename: The path to Hyperion output file indir: The path to the directory which contains observations data outdir: The path to the directory for storing extracted plots and ASCII files """ def l_bol(wl,fv,dstar): import numpy as np import astropy.constants as const # wavelength unit: um # Flux density unit: Jy # constants setup # c = const.c.cgs.value pc = const.pc.cgs.value PI = np.pi SL = const.L_sun.cgs.value # Convert the unit from Jy to erg s-1 cm-2 Hz-1 fv = np.array(fv)*1e-23 freq = c/(1e-4*np.array(wl)) diff_dum = freq[1:]-freq[0:-1] freq_interpol = np.hstack((freq[0:-1]+diff_dum/2.0,freq[0:-1]+diff_dum/2.0,freq[0],freq[-1])) freq_interpol = freq_interpol[np.argsort(freq_interpol)[::-1]] fv_interpol = np.empty(len(freq_interpol)) # calculate the histogram style of spectrum # for i in range(0,len(fv)): if i == 0: fv_interpol[i] = fv[i] else: fv_interpol[2*i-1] = fv[i-1] fv_interpol[2*i] = fv[i] fv_interpol[-1] = fv[-1] dv = freq_interpol[0:-1]-freq_interpol[1:] dv = np.delete(dv,np.where(dv==0)) fv = fv[np.argsort(freq)] freq = freq[np.argsort(freq)] return (np.trapz(fv,freq)*4.*PI*(dstar*pc)**2)/SL # function for properly calculating uncertainty of spectrophotometry value def unc_spectrophoto(wl, unc, trans): # adopting smiliar procedure as Trapezoidal rule # (b-a) * [ f(a) + f(b) ] / 2 # return ( np.sum( trans[:-1]**2 * unc[:-1]**2 * (wl[1:]-wl[:-1])**2 ) / np.trapz(trans, x=wl)**2 )**0.5 # to avoid X server error import matplotlib as mpl mpl.use('Agg') # import matplotlib.pyplot as plt import numpy as np import os from hyperion.model import ModelOutput, Model from scipy.interpolate import interp1d from hyperion.util.constants import pc, c, lsun, au from astropy.io import ascii import sys from phot_filter import phot_filter from get_obs import get_obs # Open the model m = ModelOutput(filename) # Read in the observation data and calculate the noise & variance if indir == None: indir = raw_input('Path to the observation data: ') if outdir == None: outdir = raw_input('Path for the output: ') # assign the file name from the input file print_name = os.path.splitext(os.path.basename(filename))[0] # use a canned function to extract observational data obs_data = get_obs(indir, obj=obj) # unit in um, Jy wl_tot, flux_tot, unc_tot = obs_data['spec'] flux_tot = flux_tot*1e-23 # convert unit from Jy to erg s-1 cm-2 Hz-1 unc_tot = unc_tot*1e-23 l_bol_obs = l_bol(wl_tot, flux_tot*1e23, dstar) wl_phot, flux_phot, flux_sig_phot = obs_data['phot'] flux_phot = flux_phot*1e-23 # convert unit from Jy to erg s-1 cm-2 Hz-1 flux_sig_phot = flux_sig_phot*1e-23 if aperture == None: aperture = {'wave': [3.6, 4.5, 5.8, 8.0, 8.5, 9, 9.7, 10, 10.5, 11, 16, 20, 24, 35, 70, 100, 160, 250, 350, 500, 850],\ 'aperture': [7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 20.4, 20.4, 20.4, 20.4, 24.5, 24.5, 24.5, 24.5, 24.5, 24.5, 24.5]} # assign wl_aper and aper from dictionary of aperture wl_aper = aperture['wave'] aper = aperture['aperture'] # create the non-repetitive aperture list and index array aper_reduced = list(set(aper)) index_reduced = np.arange(1, len(aper_reduced)+1) # '+1': the zeroth slice corresponds to infinite aperture # Create the plot fig = plt.figure(figsize=(8*mag,6*mag)) ax_sed = fig.add_subplot(1, 1, 1) # Plot the observed SED if not clean: color_seq = ['Green','Red','Black'] else: color_seq = ['DimGray','DimGray','DimGray'] # plot the observations # plot in log scale if log: pacs, = ax_sed.plot(np.log10(wl_tot[(wl_tot>40) & (wl_tot<190.31)]), np.log10(c/(wl_tot[(wl_tot>40) & (wl_tot<190.31)]*1e-4)*flux_tot[(wl_tot>40) & (wl_tot<190.31)]), '-',color=color_seq[0],linewidth=1.5*mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]),np.log10(c/(wl_tot[wl_tot > 194]*1e-4)*flux_tot[wl_tot > 194]), '-',color=color_seq[1],linewidth=1.5*mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]),np.log10(c/(wl_tot[wl_tot < 40]*1e-4)*flux_tot[wl_tot < 40]), '-',color=color_seq[2],linewidth=1.5*mag, alpha=0.7) photometry, = ax_sed.plot(np.log10(wl_phot),np.log10(c/(wl_phot*1e-4)*flux_phot),'s',mfc='DimGray',mec='k',markersize=8) # plot the observed photometry data ax_sed.errorbar(np.log10(wl_phot),np.log10(c/(wl_phot*1e-4)*flux_phot), yerr=[np.log10(c/(wl_phot*1e-4)*flux_phot)-np.log10(c/(wl_phot*1e-4)*(flux_phot-flux_sig_phot)), np.log10(c/(wl_phot*1e-4)*(flux_phot+flux_sig_phot))-np.log10(c/(wl_phot*1e-4)*flux_phot)], fmt='s',mfc='DimGray',mec='k',markersize=8) # plot in normal scale else: pacs, = ax_sed.plot(np.log10(wl_tot[(wl_tot>40) & (wl_tot<190.31)]), c/(wl_tot[(wl_tot>40) & (wl_tot<190.31)]*1e-4)*flux_tot[(wl_tot>40) & (wl_tot<190.31)], '-',color=color_seq[0],linewidth=1.5*mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]),c/(wl_tot[wl_tot > 194]*1e-4)*flux_tot[wl_tot > 194], '-',color=color_seq[1],linewidth=1.5*mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]),c/(wl_tot[wl_tot < 40]*1e-4)*flux_tot[wl_tot < 40], '-',color=color_seq[2],linewidth=1.5*mag, alpha=0.7) photometry, = ax_sed.plot(wl_phot,c/(wl_phot*1e-4)*flux_phot,'s',mfc='DimGray',mec='k',markersize=8) # plot the observed photometry data ax_sed.errorbar(np.log10(wl_phot),c/(wl_phot*1e-4)*flux_phot, yerr=[c/(wl_phot*1e-4)*flux_phot-c/(wl_phot*1e-4)*(flux_phot-flux_sig_phot), c/(wl_phot*1e-4)*(flux_phot+flux_sig_phot)-c/(wl_phot*1e-4)*flux_phot], fmt='s',mfc='DimGray',mec='k',markersize=8) # if keyword 'clean' is not set, print L_bol derived from observations at upper right corner. if not clean: ax_sed.text(0.75,0.9,r'$\rm{L_{bol}= %5.2f L_{\odot}}$' % l_bol_obs, fontsize=mag*16,transform=ax_sed.transAxes) # getting SED with infinite aperture sed_inf = m.get_sed(group=0, inclination=0, aperture=-1, distance=dstar*pc, uncertainties=True) # plot the simulated SED with infinite aperture if clean == False: sim, = ax_sed.plot(np.log10(sed_inf.wav), np.log10(sed_inf.val), '-', color='GoldenRod', linewidth=0.5*mag) ax_sed.fill_between(np.log10(sed_inf.wav), np.log10(sed_inf.val-sed_inf.unc), np.log10(sed_inf.val+sed_inf.unc),color='GoldenRod', alpha=0.5) ####################################### # get fluxes with different apertures # ####################################### # this is non-reduced wavelength array because this is for printing out fluxes at all channels specified by users flux_aper = np.zeros_like(wl_aper, dtype=float) unc_aper = np.zeros_like(wl_aper, dtype=float) a = np.zeros_like(wl_aper) + 1 color_list = plt.cm.jet(np.linspace(0, 1, len(wl_aper)+1)) for i in range(0, len(wl_aper)): # occasionally users might want not to report some wavelength channels if wl_aper[i] in exclude_wl: continue # getting simulated SED from Hyperion output. (have to match with the reduced index) sed_dum = m.get_sed(group=index_reduced[np.where(aper_reduced == aper[i])], inclination=0,aperture=-1,distance=dstar*pc, uncertainties=True) # plot the whole SED from this aperture (optional) if plot_all == True: ax_sed.plot(np.log10(sed_dum.wav), np.log10(sed_dum.val),'-', color=color_list[i]) ax_sed.fill_between(np.log10(sed_dum.wav), np.log10(sed_dum.val-sed_dum.unc), np.log10(sed_dum.val+sed_dum.unc),\ color=color_list[i], alpha=0.5) # Extracting spectrophotometry values from simulated SED # Not using the photometry filer function to extract spectrophotometry values # sort by wavelength first. sort_wl = np.argsort(sed_dum.wav) val_sort = sed_dum.val[sort_wl] unc_sort = sed_dum.unc[sort_wl] wav_sort = sed_dum.wav[sort_wl] # Before doing that, convert vSv to F_lambda flux_dum = val_sort / wav_sort unc_dum = unc_sort / wav_sort # If no using filter function to extract the spectrophotometry, # then use the spectral resolution. if filter_func == False: # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((wav_sort < wl_aper[i]*(1+1./res)) & (wav_sort > wl_aper[i]*(1-1./res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(flux_dum[ind]) unc_aper[i] = np.mean(unc_dum[ind]) else: f = interp1d(wav_sort, flux_dum) f_unc = interp1d(wav_sort, unc_dum) flux_aper[i] = f(wl_aper[i]) unc_aper[i] = f_unc(wl_aper[i]) # Using photometry filter function to extract spectrophotometry values else: # apply the filter function # decide the filter name if wl_aper[i] == 70: fil_name = 'Herschel PACS 70um' elif wl_aper[i] == 100: fil_name = 'Herschel PACS 100um' elif wl_aper[i] == 160: fil_name = 'Herschel PACS 160um' elif wl_aper[i] == 250: fil_name = 'Herschel SPIRE 250um' elif wl_aper[i] == 350: fil_name = 'Herschel SPIRE 350um' elif wl_aper[i] == 500: fil_name = 'Herschel SPIRE 500um' elif wl_aper[i] == 3.6: fil_name = 'IRAC Channel 1' elif wl_aper[i] == 4.5: fil_name = 'IRAC Channel 2' elif wl_aper[i] == 5.8: fil_name = 'IRAC Channel 3' elif wl_aper[i] == 8.0: fil_name = 'IRAC Channel 4' elif wl_aper[i] == 24: fil_name = 'MIPS 24um' elif wl_aper[i] == 850: fil_name = 'SCUBA 850WB' else: fil_name = None if fil_name != None: filter_func = phot_filter(fil_name) # Simulated SED should have enough wavelength coverage for applying photometry filters. f = interp1d(wav_sort, flux_dum) f_unc = interp1d(wav_sort, unc_dum) flux_aper[i] = np.trapz(f(filter_func['wave']/1e4)*\ filter_func['transmission'],x=filter_func['wave']/1e4 )/\ np.trapz(filter_func['transmission'], x=filter_func['wave']/1e4) # fix a bug unc_aper[i] = unc_spectrophoto(filter_func['wave']/1e4, f_unc(filter_func['wave']/1e4), filter_func['transmission']) else: # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((wav_sort < wl_aper[i]*(1+1./res)) & (wav_sort > wl_aper[i]*(1-1./res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(flux_dum[ind]) unc_aper[i] = np.mean(unc_dum[ind]) else: f = interp1d(wav_sort, flux_dum) f_unc = interp1d(wav_sort, unc_dum) flux_aper[i] = f(wl_aper[i]) unc_aper[i] = f_unc(wl_aper[i]) # temperory step: solve issue of uncertainty greater than the value for i in range(len(wl_aper)): if unc_aper[i] >= flux_aper[i]: unc_aper[i] = flux_aper[i] - 1e-20 ########################### # Observations Extraction # ########################### # perform the same procedure of flux extraction of aperture flux with observed spectra # wl_aper = np.array(wl_aper, dtype=float) obs_aper_wl = wl_aper[(wl_aper >= min(wl_tot)) & (wl_aper <= max(wl_tot))] obs_aper_flux = np.zeros_like(obs_aper_wl) obs_aper_unc = np.zeros_like(obs_aper_wl) # have change the simulation part to work in F_lambda for fliter convolution # flux_tot and unc_tot have units of erg/s/cm2/Hz. Need to convert it to F_lambda (erg/s/cm2/um) fnu2fl = c/(wl_tot*1e-4)/wl_tot # # wl_tot and flux_tot are already hstacked and sorted by wavelength for i in range(0, len(obs_aper_wl)): # sometime users want not report some wavelength channels if obs_aper_wl[i] in exclude_wl: continue if filter_func == False: # use a rectangle function the average the simulated SED # apply the spectral resolution if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i]*(1+1./res)) & (wl_tot > obs_aper_wl[i]*(1-1./res))) if len(ind[0]) != 0: obs_aper_flux[i] = np.mean(fnu2fl[ind]*flux_tot[ind]) obs_aper_unc[i] = np.mean(fnu2fl[ind]*unc_tot[ind]) else: f = interp1d(wl_tot, fnu2fl*flux_tot) f_unc = interp1d(wl_tot, fnu2fl*unc_tot) obs_aper_flux[i] = f(obs_aper_wl[i]) obs_aper_unc[i] = f_unc(obs_aper_wl[i]) else: # apply the filter function # decide the filter name if obs_aper_wl[i] == 70: fil_name = 'Herschel PACS 70um' elif obs_aper_wl[i] == 100: fil_name = 'Herschel PACS 100um' elif obs_aper_wl[i] == 160: fil_name = 'Herschel PACS 160um' elif obs_aper_wl[i] == 250: fil_name = 'Herschel SPIRE 250um' elif obs_aper_wl[i] == 350: fil_name = 'Herschel SPIRE 350um' elif obs_aper_wl[i] == 500: fil_name = 'Herschel SPIRE 500um' elif obs_aper_wl[i] == 3.6: fil_name = 'IRAC Channel 1' elif obs_aper_wl[i] == 4.5: fil_name = 'IRAC Channel 2' elif obs_aper_wl[i] == 5.8: fil_name = 'IRAC Channel 3' elif obs_aper_wl[i] == 8.0: fil_name = 'IRAC Channel 4' elif obs_aper_wl[i] == 24: fil_name = 'MIPS 24um' elif obs_aper_wl[i] == 850: fil_name = 'SCUBA 850WB' # do not have SCUBA spectra else: fil_name = None if fil_name != None: filter_func = phot_filter(fil_name) # Observed SED needs to be trimmed before applying photometry filters filter_func = filter_func[(filter_func['wave']/1e4 >= min(wl_tot))*\ ((filter_func['wave']/1e4 >= 54.8)+(filter_func['wave']/1e4 <= 36.0853))*\ ((filter_func['wave']/1e4 <= 95.05)+(filter_func['wave']/1e4 >=103))*\ ((filter_func['wave']/1e4 <= 190.31)+(filter_func['wave']/1e4 >= 195))*\ (filter_func['wave']/1e4 <= max(wl_tot))] f = interp1d(wl_tot, fnu2fl*flux_tot) f_unc = interp1d(wl_tot, fnu2fl*unc_tot) obs_aper_flux[i] = np.trapz(f(filter_func['wave']/1e4)*filter_func['transmission'], x=filter_func['wave']/1e4)/\ np.trapz(filter_func['transmission'], x=filter_func['wave']/1e4) obs_aper_unc[i] = unc_spectrophoto(filter_func['wave']/1e4, f_unc(filter_func['wave']/1e4), filter_func['transmission']) else: # use a rectangle function the average the simulated SED # apply the spectral resolution if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i]*(1+1./res)) & (wl_tot > obs_aper_wl[i]*(1-1./res))) if len(ind[0]) != 0: obs_aper_flux[i] = np.mean(fnu2fl[ind]*flux_tot[ind]) obs_aper_unc[i] = np.mean(fnu2fl[ind]*unc_tot[ind]) else: f = interp1d(wl_tot, fnu2fl*flux_tot) f_unc = interp1d(wl_tot, fnu2fl*unc_tot) obs_aper_flux[i] = f(obs_aper_wl[i]) obs_aper_unc[i] = f_unc(obs_aper_wl[i]) # plot the aperture-extracted spectrophotometry fluxes from observed spectra and simulations # in log-scale if log: aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), np.log10(obs_aper_flux * obs_aper_wl ),\ yerr=[np.log10(obs_aper_flux*obs_aper_wl)-np.log10(obs_aper_flux*obs_aper_wl-obs_aper_unc*obs_aper_wl), np.log10(obs_aper_flux*obs_aper_wl+obs_aper_unc*obs_aper_wl)-np.log10(obs_aper_flux*obs_aper_wl)],\ fmt='s', mec='None', mfc='r', markersize=10, linewidth=1.5, ecolor='Red', elinewidth=3, capthick=3, barsabove=True) aper = ax_sed.errorbar(np.log10(wl_aper),np.log10(flux_aper*wl_aper),\ yerr=[np.log10(flux_aper*wl_aper)-np.log10(flux_aper*wl_aper-unc_aper*wl_aper), np.log10(flux_aper*wl_aper+unc_aper*wl_aper)-np.log10(flux_aper*wl_aper)],\ fmt='o', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=2.5, linewidth=1.7, ecolor='Blue', elinewidth=3, barsabove=True) ax_sed.set_ylim([-14,-7]) ax_sed.set_xlim([0,3.2]) # in normal scale (normal in y-axis) else: aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), obs_aper_flux*obs_aper_wl, yerr=obs_aper_unc*obs_aper_wl,\ fmt='s', mec='None', mfc='r', markersize=10, linewidth=1.5, ecolor='Red', elinewidth=3, capthick=3, barsabove=True) aper = ax_sed.errorbar(np.log10(wl_aper),flux_aper*wl_aper, yerr=unc_aper*wl_aper,\ fmt='o', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=2.5, linewidth=1.7, ecolor='Blue', elinewidth=3, barsabove=True) ax_sed.set_xlim([0,3.2]) # calculate the bolometric luminosity of the aperture # print flux_aper l_bol_sim = l_bol(wl_aper, flux_aper*wl_aper/(c/np.array(wl_aper)*1e4)*1e23, dstar) print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # print out the sed into ascii file for reading in later if save == True: # unapertured SED foo = open(outdir+print_name+'_sed_inf.txt','w') foo.write('%12s \t %12s \t %12s \n' % ('wave','vSv','sigma_vSv')) for i in range(0, len(sed_inf.wav)): foo.write('%12g \t %12g \t %12g \n' % (sed_inf.wav[i], sed_inf.val[i], sed_inf.unc[i])) foo.close() # SED with convolution of aperture sizes foo = open(outdir+print_name+'_sed_w_aperture.txt','w') foo.write('%12s \t %12s \t %12s \n' % ('wave','vSv','sigma_vSv')) for i in range(0, len(wl_aper)): foo.write('%12g \t %12g \t %12g \n' % (wl_aper[i], flux_aper[i]*wl_aper[i], unc_aper[i]*wl_aper[i])) foo.close() # print out the aperture-convolved fluxex from observations if print_data_w_aper: foo = open(outdir+print_name+'_obs_w_aperture.txt','w') foo.write('%12s \t %12s \t %12s \n' % ('wave','Jy','sigma_Jy')) for i in range(0, len(obs_aper_wl)): foo.write('%12g \t %12g \t %12g \n' % (obs_aper_wl[i], obs_aper_flux[i]*obs_aper_wl[i]/(c/obs_aper_wl[i]*1e4)*1e23, obs_aper_unc[i]*obs_aper_wl[i]/(c/obs_aper_wl[i]*1e4)*1e23)) foo.close() # read the input central luminosity by reading in the source information from output file dum = Model() dum.use_sources(filename) L_cen = dum.sources[0].luminosity/lsun # legend lg_data = ax_sed.legend([irs, photometry, aper, aper_obs], [r'$\rm{observation}$', r'$\rm{photometry}$',r'$\rm{F_{aper,sim}}$',r'$\rm{F_{aper,obs}}$'], loc='upper left',fontsize=14*mag,numpoints=1,framealpha=0.3) if clean == False: lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f\,L_{\odot},\,L_{center}=%5.2f\,L_{\odot}}$' % (l_bol_sim, L_cen)], \ loc='lower right',fontsize=mag*16) plt.gca().add_artist(lg_data) # plot setting ax_sed.set_xlabel(r'$\rm{log\,\lambda\,[{\mu}m]}$',fontsize=mag*20) ax_sed.set_ylabel(r'$\rm{log\,\nu S_{\nu}\,[erg\,s^{-1}\,cm^{-2}]}$',fontsize=mag*20) [ax_sed.spines[axis].set_linewidth(1.5*mag) for axis in ['top','bottom','left','right']] ax_sed.minorticks_on() ax_sed.tick_params('both',labelsize=mag*18,width=1.5*mag,which='major',pad=15,length=5*mag) ax_sed.tick_params('both',labelsize=mag*18,width=1.5*mag,which='minor',pad=15,length=2.5*mag) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=mag*18) for label in ax_sed.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax_sed.get_yticklabels(): label.set_fontproperties(ticks_font) # Write out the plot fig.savefig(outdir+print_name+'_sed.pdf',format='pdf',dpi=300,bbox_inches='tight') fig.clf() # option for suppress image plotting (for speed) if image: # Package for matching the colorbar from mpl_toolkits.axes_grid1 import make_axes_locatable, ImageGrid # Users may change the unit: mJy, Jy, MJy/sr, ergs/cm^2/s, ergs/cm^2/s/Hz # !!! image = m.get_image(group=len(aper_reduced)+1, inclination=0, distance=dstar*pc, units='MJy/sr') # Open figure and create axes fig = plt.figure(figsize=(12,12)) grid = ImageGrid(fig, 111,nrows_ncols=(3,3),direction='row', add_all=True,label_mode='1',share_all=True, cbar_location='right',cbar_mode='single', cbar_size='3%',cbar_pad=0) for i, wav in enumerate([3.6, 8.0, 9.7, 24, 40, 100, 250, 500, 1000]): ax = grid[i] # Find the closest wavelength iwav = np.argmin(np.abs(wav - image.wav)) # Calculate the image width in arcseconds given the distance used above # get the max radius rmax = max(m.get_quantities().r_wall) w = np.degrees(rmax / image.distance) * 3600. # Image in the unit of MJy/sr # Change it into erg/s/cm2/Hz/sr factor = 1e-23*1e6 # avoid zero in log # flip the image, because the setup of inclination is upside down val = image.val[::-1, :, iwav] * factor + 1e-30 # This is the command to show the image. The parameters vmin and vmax are # the min and max levels for the colorscale (remove for default values). cmap = plt.cm.CMRmap im = ax.imshow(np.log10(val), vmin= -22, vmax= -12, cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1) ax.set_xlabel(r'$\rm{RA\,Offset\,[arcsec]}$', fontsize=14) ax.set_ylabel(r'$\rm{Dec\,Offset\,[arcsec]}$', fontsize=14) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=14) for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) # Colorbar setting cb = ax.cax.colorbar(im) cb.solids.set_edgecolor('face') cb.ax.minorticks_on() cb.ax.set_ylabel(r'$\rm{log(I_{\nu})\,[erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$',fontsize=18) cb_obj = plt.getp(cb.ax.axes, 'yticklabels') plt.setp(cb_obj,fontsize=18) ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=18) for label in cb.ax.get_yticklabels(): label.set_fontproperties(ticks_font) ax.tick_params(axis='both', which='major', labelsize=16) ax.text(0.7,0.88,str(wav) + r'$\rm{\,\mu m}$',fontsize=16,color='white', transform=ax.transAxes) fig.savefig(outdir+print_name+'_image_gridplot.pdf', format='pdf', dpi=300, bbox_inches='tight') fig.clf()
def extract_hyperion(filename,indir=None,outdir=None,dstar=178.0,wl_aper=None,save=True): def l_bol(wl,fv,dist=178.0): import numpy as np import astropy.constants as const # wavelength unit: um # Flux density unit: Jy # # constants setup # c = const.c.cgs.value pc = const.pc.cgs.value PI = np.pi SL = const.L_sun.cgs.value # Convert the unit from Jy to erg s-1 cm-2 Hz-1 fv = np.array(fv)*1e-23 freq = c/(1e-4*np.array(wl)) diff_dum = freq[1:]-freq[0:-1] freq_interpol = np.hstack((freq[0:-1]+diff_dum/2.0,freq[0:-1]+diff_dum/2.0,freq[0],freq[-1])) freq_interpol = freq_interpol[np.argsort(freq_interpol)[::-1]] fv_interpol = np.empty(len(freq_interpol)) # calculate the histogram style of spectrum # for i in range(0,len(fv)): if i == 0: fv_interpol[i] = fv[i] else: fv_interpol[2*i-1] = fv[i-1] fv_interpol[2*i] = fv[i] fv_interpol[-1] = fv[-1] dv = freq_interpol[0:-1]-freq_interpol[1:] dv = np.delete(dv,np.where(dv==0)) fv = fv[np.argsort(freq)] freq = freq[np.argsort(freq)] return (np.trapz(fv,freq)*4.*PI*(dist*pc)**2)/SL import matplotlib.pyplot as plt import numpy as np import os from hyperion.model import ModelOutput from hyperion.model import Model from scipy.interpolate import interp1d from hyperion.util.constants import pc, c, lsun # Read in the observation data and calculate the noise & variance if indir == None: indir = '/Users/yaolun/bhr71/' if outdir == None: outdir = '/Users/yaolun/bhr71/hyperion/' # assign the file name from the input file print_name = os.path.splitext(os.path.basename(filename))[0] # [wl_pacs,flux_pacs,unc_pacs] = np.genfromtxt(indir+'BHR71_centralSpaxel_PointSourceCorrected_CorrectedYES_trim_continuum.txt',\ dtype='float',skip_header=1).T # Convert the unit from Jy to erg cm-2 Hz-1 flux_pacs = flux_pacs*1e-23 [wl_spire,flux_spire] = np.genfromtxt(indir+'BHR71_spire_corrected_continuum.txt',dtype='float',skip_header=1).T flux_spire = flux_spire*1e-23 wl_obs = np.hstack((wl_pacs,wl_spire)) flux_obs = np.hstack((flux_pacs,flux_spire)) [wl_pacs_data,flux_pacs_data,unc_pacs_data] = np.genfromtxt(indir+'BHR71_centralSpaxel_PointSourceCorrected_CorrectedYES_trim.txt',\ dtype='float').T [wl_spire_data,flux_spire_data] = np.genfromtxt(indir+'BHR71_spire_corrected.txt',\ dtype='float').T [wl_pacs_flat,flux_pacs_flat,unc_pacs_flat] = np.genfromtxt(indir+'BHR71_centralSpaxel_PointSourceCorrected_CorrectedYES_trim_flat_spectrum.txt',\ dtype='float',skip_header=1).T [wl_spire_flat,flux_spire_flat] = np.genfromtxt(indir+'BHR71_spire_corrected_flat_spectrum.txt',dtype='float',skip_header=1).T # Convert the unit from Jy to erg cm-2 Hz-1 flux_pacs_flat = flux_pacs_flat*1e-23 flux_spire_flat = flux_spire_flat*1e-23 flux_pacs_data = flux_pacs_data*1e-23 flux_spire_data = flux_spire_data*1e-23 wl_pacs_noise = wl_pacs_data flux_pacs_noise = flux_pacs_data-flux_pacs-flux_pacs_flat wl_spire_noise = wl_spire_data flux_spire_noise = flux_spire_data-flux_spire-flux_spire_flat # Read in the Spitzer IRS spectrum [wl_irs, flux_irs]= (np.genfromtxt(indir+'bhr71_spitzer_irs.txt',skip_header=2,dtype='float').T)[0:2] # Convert the unit from Jy to erg cm-2 Hz-1 flux_irs = flux_irs*1e-23 # Remove points with zero or negative flux ind = flux_irs > 0 wl_irs = wl_irs[ind] flux_irs = flux_irs[ind] # Calculate the local variance (for spire), use the instrument uncertainty for pacs # wl_noise_5 = wl_spire_noise[(wl_spire_noise > 194)*(wl_spire_noise <= 304)] flux_noise_5 = flux_spire_noise[(wl_spire_noise > 194)*(wl_spire_noise <= 304)] wl_noise_6 = wl_spire_noise[wl_spire_noise > 304] flux_noise_6 = flux_spire_noise[wl_spire_noise > 304] wl_noise = [wl_pacs_data[wl_pacs_data<=190.31],wl_noise_5,wl_noise_6] flux_noise = [unc_pacs[wl_pacs_data<=190.31],flux_noise_5,flux_noise_6] sig_num = 20 sigma_noise = [] for i in range(0,len(wl_noise)): sigma_dum = np.zeros([len(wl_noise[i])]) for iwl in range(0,len(wl_noise[i])): if iwl < sig_num/2: sigma_dum[iwl] = np.std(np.hstack((flux_noise[i][0:sig_num/2],flux_noise[i][0:sig_num/2-iwl]))) elif len(wl_noise[i])-iwl < sig_num/2: sigma_dum[iwl] = np.std(np.hstack((flux_noise[i][iwl:],flux_noise[i][len(wl_noise[i])-sig_num/2:]))) else: sigma_dum[iwl] = np.std(flux_noise[i][iwl-sig_num/2:iwl+sig_num/2]) sigma_noise = np.hstack((sigma_noise,sigma_dum)) sigma_noise = np.array(sigma_noise) # Read in the photometry data phot = np.genfromtxt(indir+'bhr71.txt',dtype=None,skip_header=1,comments='%') wl_phot = [] flux_phot = [] flux_sig_phot = [] note = [] for i in range(0,len(phot)): wl_phot.append(phot[i][0]) flux_phot.append(phot[i][1]) flux_sig_phot.append(phot[i][2]) note.append(phot[i][4]) wl_phot = np.array(wl_phot) # Convert the unit from Jy to erg cm-2 Hz-1 flux_phot = np.array(flux_phot)*1e-23 flux_sig_phot = np.array(flux_sig_phot)*1e-23 # Print the observed L_bol wl_tot = np.hstack((wl_irs,wl_obs,wl_phot)) flux_tot = np.hstack((flux_irs,flux_obs,flux_phot)) flux_tot = flux_tot[np.argsort(wl_tot)] wl_tot = wl_tot[np.argsort(wl_tot)] l_bol_obs = l_bol(wl_tot,flux_tot*1e23) # Open the model m = ModelOutput(filename) if wl_aper == None: wl_aper = [3.6, 4.5, 5.8, 8.0, 10, 16, 20, 24, 35, 70, 100, 160, 250, 350, 500, 850] # Create the plot mag = 1.5 fig = plt.figure(figsize=(8*mag,6*mag)) ax_sed = fig.add_subplot(1, 1, 1) # Plot the observed SED # plot the observed spectra pacs, = ax_sed.plot(np.log10(wl_pacs),np.log10(c/(wl_pacs*1e-4)*flux_pacs),'-',color='DimGray',linewidth=1.5*mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_spire),np.log10(c/(wl_spire*1e-4)*flux_spire),'-',color='DimGray',linewidth=1.5*mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_irs),np.log10(c/(wl_irs*1e-4)*flux_irs),'-',color='DimGray',linewidth=1.5*mag, alpha=0.7) # ax_sed.text(0.75,0.9,r'$\rm{L_{bol}= %5.2f L_{\odot}}$' % l_bol_obs,fontsize=mag*16,transform=ax_sed.transAxes) # plot the observed photometry data photometry, = ax_sed.plot(np.log10(wl_phot),np.log10(c/(wl_phot*1e-4)*flux_phot),'s',mfc='DimGray',mec='k',markersize=8) ax_sed.errorbar(np.log10(wl_phot),np.log10(c/(wl_phot*1e-4)*flux_phot),\ yerr=[np.log10(c/(wl_phot*1e-4)*flux_phot)-np.log10(c/(wl_phot*1e-4)*(flux_phot-flux_sig_phot)),\ np.log10(c/(wl_phot*1e-4)*(flux_phot+flux_sig_phot))-np.log10(c/(wl_phot*1e-4)*flux_phot)],\ fmt='s',mfc='DimGray',mec='k',markersize=8) # Extract the SED for the smallest inclination and largest aperture, and # scale to 300pc. In Python, negative indices can be used for lists and # arrays, and indicate the position from the end. So to get the SED in the # largest aperture, we set aperture=-1. # aperture group is aranged from smallest to infinite sed_inf = m.get_sed(group=0, inclination=0, aperture=-1, distance=dstar * pc) # l_bol_sim = l_bol(sed_inf.wav, sed_inf.val/(c/sed_inf.wav*1e4)*1e23) # print sed.wav, sed.val # print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # plot the simulated SED # sim, = ax_sed.plot(np.log10(sed_inf.wav), np.log10(sed_inf.val), '-', color='k', linewidth=1.5*mag, alpha=0.7) # get flux at different apertures flux_aper = np.empty_like(wl_aper) unc_aper = np.empty_like(wl_aper) for i in range(0, len(wl_aper)): sed_dum = m.get_sed(group=i+1, inclination=0, aperture=-1, distance=dstar * pc) # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((sed_dum.wav < wl_aper[i]*(1+1./res)) & (sed_dum.wav > wl_aper[i]*(1-1./res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(sed_dum.val[ind]) else: f = interp1d(sed_dum.wav, sed_dum.val) flux_aper[i] = f(wl_aper[i]) # perform the same procedure of flux extraction of aperture flux with observed spectra wl_aper = np.array(wl_aper) obs_aper_wl = wl_aper[(wl_aper >= min(wl_irs)) & (wl_aper <= max(wl_spire))] obs_aper_sed = np.empty_like(obs_aper_wl) sed_tot = c/(wl_tot*1e-4)*flux_tot # wl_tot and flux_tot are already hstacked and sorted by wavelength for i in range(0, len(obs_aper_wl)): if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i]*(1+1./res)) & (wl_tot > obs_aper_wl[i]*(1-1./res))) if len(ind[0]) != 0: obs_aper_sed[i] = np.mean(sed_tot[ind]) else: f = interp1d(wl_tot, sed_tot) obs_aper_sed[i] = f(wl_aper[i]) aper_obs, = ax_sed.plot(np.log10(obs_aper_wl),np.log10(obs_aper_sed), 's-', mec='None', mfc='r', color='r',markersize=10, linewidth=1.5) # # interpolate the uncertainty (maybe not the best way to do this) # print sed_dum.unc # f = interp1d(sed_dum.wav, sed_dum.unc) # unc_aper[i] = f(wl_aper[i]) # if wl_aper[i] == 9.7: # ax_sed.plot(np.log10(sed_dum.wav), np.log10(sed_dum.val), '-', linewidth=1.5*mag) # print l_bol(sed_dum.wav, sed_dum.val/(c/sed_dum.wav*1e4)*1e23) aper, = ax_sed.plot(np.log10(wl_aper),np.log10(flux_aper),'o-', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=3, linewidth=1.7) # calculate the bolometric luminosity of the aperture l_bol_sim = l_bol(wl_aper, flux_aper/(c/np.array(wl_aper)*1e4)*1e23) print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # print out the sed into ascii file for reading in later if save == True: # unapertured SED foo = open(outdir+print_name+'_sed_inf.txt','w') foo.write('%12s \t %12s \n' % ('wave','vSv')) for i in range(0, len(sed_inf.wav)): foo.write('%12g \t %12g \n' % (sed_inf.wav[i], sed_inf.val[i])) foo.close() # SED with convolution of aperture sizes foo = open(outdir+print_name+'_sed_w_aperture.txt','w') foo.write('%12s \t %12s \n' % ('wave','vSv')) for i in range(0, len(wl_aper)): foo.write('%12g \t %12g \n' % (wl_aper[i], flux_aper[i])) foo.close() # Read in and plot the simulated SED produced by RADMC-3D using the same parameters # [wl,fit] = np.genfromtxt(indir+'hyperion/radmc_comparison/spectrum.out',dtype='float',skip_header=3).T # l_bol_radmc = l_bol(wl,fit*1e23/dstar**2) # radmc, = ax_sed.plot(np.log10(wl),np.log10(c/(wl*1e-4)*fit/dstar**2),'-',color='DimGray', linewidth=1.5*mag, alpha=0.5) # print the L bol of the simulated SED (both Hyperion and RADMC-3D) # lg_sim = ax_sed.legend([sim,radmc],[r'$\rm{L_{bol,sim}=%5.2f~L_{\odot},~L_{center}=9.18~L_{\odot}}$' % l_bol_sim, \ # r'$\rm{L_{bol,radmc3d}=%5.2f~L_{\odot},~L_{center}=9.18~L_{\odot}}$' % l_bol_radmc],\ # loc='lower right',fontsize=mag*16) # read the input central luminosity by reading in the source information from output file dum = Model() dum.use_sources(filename) L_cen = dum.sources[0].luminosity/lsun # lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f~L_{\odot},~L_{center}=%5.2f~L_{\odot}}$' % (l_bol_sim, L_cen)], \ # loc='lower right',fontsize=mag*16) # lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f~L_{\odot},~L_{bol,obs}=%5.2f~L_{\odot}}$' % (l_bol_sim, l_bol_obs)], \ # loc='lower right',fontsize=mag*16) # text = ax_sed.text(0.2 ,0.05 ,r'$\rm{L_{bol,simulation}=%5.2f~L_{\odot},~L_{bol,observation}=%5.2f~L_{\odot}}$' % (l_bol_sim, l_bol_obs),fontsize=mag*16,transform=ax_sed.transAxes) # text.set_bbox(dict( edgecolor='k',facecolor='None',alpha=0.3,pad=10.0)) # plot setting ax_sed.set_xlabel(r'$\rm{log\,\lambda\,({\mu}m)}$',fontsize=mag*20) ax_sed.set_ylabel(r'$\rm{log\,\nu S_{\nu}\,(erg\,cm^{-2}\,s^{-1})}$',fontsize=mag*20) [ax_sed.spines[axis].set_linewidth(1.5*mag) for axis in ['top','bottom','left','right']] ax_sed.minorticks_on() ax_sed.tick_params('both',labelsize=mag*18,width=1.5*mag,which='major',pad=15,length=5*mag) ax_sed.tick_params('both',labelsize=mag*18,width=1.5*mag,which='minor',pad=15,length=2.5*mag) ax_sed.set_ylim([-13,-7.5]) ax_sed.set_xlim([0,3]) # lg_data = ax_sed.legend([sim, aper], [r'$\rm{w/o~aperture}$', r'$\rm{w/~aperture}$'], \ # loc='upper left', fontsize=14*mag, framealpha=0.3, numpoints=1) lg_data = ax_sed.legend([irs, photometry, aper, aper_obs],\ [r'$\rm{observation}$',\ r'$\rm{photometry}$',r'$\rm{F_{aper,sim}}$',r'$\rm{F_{aper,obs}}$'],\ loc='upper left',fontsize=14*mag,numpoints=1,framealpha=0.3) # plt.gca().add_artist(lg_sim) # Write out the plot fig.savefig(outdir+print_name+'_sed.pdf',format='pdf',dpi=300,bbox_inches='tight') fig.clf() # Package for matching the colorbar from mpl_toolkits.axes_grid1 import make_axes_locatable # Extract the image for the first inclination, and scale to 300pc. We # have to specify group=1 as there is no image in group 0. image = m.get_image(group=len(wl_aper)+1, inclination=0, distance=dstar * pc, units='MJy/sr') # image = m.get_image(group=14, inclination=0, distance=dstar * pc, units='MJy/sr') # Open figure and create axes # fig = plt.figure(figsize=(8, 8)) fig, axarr = plt.subplots(3, 3, sharex='col', sharey='row',figsize=(13.5,12)) # Pre-set maximum for colorscales VMAX = {} # VMAX[3.6] = 10. # VMAX[24] = 100. # VMAX[160] = 2000. # VMAX[500] = 2000. VMAX[100] = 10. VMAX[250] = 100. VMAX[500] = 2000. VMAX[1000] = 2000. # We will now show four sub-plots, each one for a different wavelength # for i, wav in enumerate([3.6, 24, 160, 500]): # for i, wav in enumerate([100, 250, 500, 1000]): # for i, wav in enumerate([4.5, 9.7, 24, 40, 70, 100, 250, 500, 1000]): for i, wav in enumerate([3.6, 8.0, 9.7, 24, 40, 100, 250, 500, 1000]): # ax = fig.add_subplot(3, 3, i + 1) ax = axarr[i/3, i%3] # Find the closest wavelength iwav = np.argmin(np.abs(wav - image.wav)) # Calculate the image width in arcseconds given the distance used above rmax = max(m.get_quantities().r_wall) w = np.degrees(rmax / image.distance) * 3600. # w = np.degrees((1.5 * pc) / image.distance) * 60. # Image in the unit of MJy/sr # Change it into erg/s/cm2/Hz/sr factor = 1e-23*1e6 # avoid zero in log val = image.val[:, :, iwav] * factor + 1e-30 # This is the command to show the image. The parameters vmin and vmax are # the min and max levels for the colorscale (remove for default values). im = ax.imshow(np.log10(val), vmin= -22, vmax= -12, cmap=plt.cm.jet, origin='lower', extent=[-w, w, -w, w], aspect=1) # Colorbar setting # create an axes on the right side of ax. The width of cax will be 5% # of ax and the padding between cax and ax will be fixed at 0.05 inch. if (i+1) % 3 == 0: divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cb = fig.colorbar(im, cax=cax) cb.solids.set_edgecolor("face") cb.ax.minorticks_on() cb.ax.set_ylabel(r'$\rm{log(I_{\nu})\,[erg\,s^{-2}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$',fontsize=12) cb_obj = plt.getp(cb.ax.axes, 'yticklabels') plt.setp(cb_obj,fontsize=12) if (i+1) == 7: # Finalize the plot ax.set_xlabel('RA Offset (arcsec)', fontsize=14) ax.set_ylabel('Dec Offset (arcsec)', fontsize=14) ax.tick_params(axis='both', which='major', labelsize=16) ax.set_adjustable('box-forced') ax.text(0.7,0.88,str(wav) + r'$\rm{\,\mu m}$',fontsize=18,color='white',weight='bold',transform=ax.transAxes) fig.subplots_adjust(hspace=0,wspace=-0.2) # Adjust the spaces between the subplots # plt.tight_layout() fig.savefig(outdir+print_name+'_cube_plot.png', format='png', dpi=300, bbox_inches='tight') fig.clf()
def setup_model(cli): lsun_TRUST = 3.839e33 # # Hyperion setup: # model = Model() if(cli.mode == "temperature"): # # Dust properties: # dust_properties = SphericalDust('dust_integrated_full_scattering.hdf5') # # Write dust properties: # dust_properties.write('dust_properties.hdf5') dust_properties.plot('dust_properties.png') # # Specify galaxy setup: # hR = 4000.0*pc # [cm] Rmax = 5.0*hR # [cm] hz_oldstars = 350.0*pc # [cm] hz_youngstars = 200.0*pc # [cm] hz_dust = 200.0*pc # [cm] zmax_oldstars = 5.0*hz_oldstars # [cm] zmax_youngstars = 5.0*hz_youngstars # [cm] zmax_dust = 5.0*hz_dust # [cm] zmax = zmax_oldstars # [cm] reff = 1600.0*pc # [cm] n = 3.0 q = 0.6 bn = 2.0*n - 1.0/3.0 + 4.0/405.0/n + 46.0/25515.0/n/n + 131.0/1148175.0/n/n/n temperature_oldstars = 3500.0 # [K] temperature_youngstars = 10000.0 # [K] temperature_bulge = 3500.0 # [K] luminosity_oldstars = 4.0e+10*lsun_TRUST # [ergs/s] luminosity_youngstars = 1.0e+10*lsun_TRUST # [ergs/s] luminosity_bulge = 3.0e+10*lsun_TRUST # [ergs/s] w_oldstars = 0.25 w_youngstars = 0.75 w_dust = 0.75 phi0_oldstars = 0.0 phi0_youngstars = 20.0 * pi/180.0 phi0_dust = 20.0 * pi/180.0 modes = 2 pitchangle = 20.0 * pi/180.0 # # Grid setup: # grid_wmin = 0.0 grid_wmax = Rmax grid_zmin = -zmax grid_zmax = +zmax grid_pmin = 0.0 grid_pmax = 2.0*pi grid_dx = cli.resolution*pc grid_dw = grid_dx # uniform resolution grid_dz = grid_dx # uniform resolution grid_dp = grid_dx # resolution at characteristic radial disk spatial scale hR = 4000.0 pc grid_Nw = int((grid_wmax - grid_wmin) / grid_dw) + 1 grid_Nz = int((grid_zmax - grid_zmin) / grid_dz) + 1 if(cli.case == 1): grid_Np = 1 if(cli.case == 2): grid_Np = int((grid_pmax - grid_pmin) * hR / grid_dp) if(cli.verbose): print("Grid setup:") print(" Grid resolution =",cli.resolution, "pc.") print(" grid_Nw =",grid_Nw) print(" grid_Nz =",grid_Nz) print(" grid_Np =",grid_Np) #grid_w = np.logspace(np.log10(grid_wmin), np.log10(grid_wmax), grid_Nw) #grid_w = np.hstack([0., grid_w]) # add innermost cell interface at w=0 grid_w = np.linspace(grid_wmin, grid_wmax, grid_Nw+1) grid_z = np.linspace(grid_zmin, grid_zmax, grid_Nz+1) grid_p = np.linspace(grid_pmin, grid_pmax, grid_Np+1) model.set_cylindrical_polar_grid(grid_w, grid_z, grid_p) # # Dust density and sources setup: # rho_oldstars = np.zeros(model.grid.shape) rho_youngstars = np.zeros(model.grid.shape) rho_bulge = np.zeros(model.grid.shape) rho_dust = np.zeros(model.grid.shape) for k in range(0, grid_Np): for j in range(0, grid_Nz): for i in range(0, grid_Nw): R = model.grid.gw[k,j,i] z = model.grid.gz[k,j,i] m = math.sqrt(R*R + z*z/q/q) rho_dust[k,j,i] = math.exp(- R/hR -abs(z)/hz_dust ) rho_oldstars[k,j,i] = math.exp(- R/hR -abs(z)/hz_oldstars ) rho_youngstars[k,j,i] = math.exp(- R/hR -abs(z)/hz_youngstars) rho_bulge[k,j,i] = math.pow(m/reff, 0.5/n - 1.0) * math.exp(- bn * math.pow(m/reff, 1.0/n)) if(cli.case == 2): phi = model.grid.gp[k,j,i] perturb = math.sin(modes * (math.log(R/hR) / math.tan(pitchangle) - (phi - phi0_dust))) rho_dust[k,j,i] *= (1.0 + w_dust * perturb) perturb = math.sin(modes * (math.log(R/hR) / math.tan(pitchangle) - (phi - phi0_oldstars))) rho_oldstars[k,j,i] *= (1.0 + w_oldstars * perturb) perturb = math.sin(modes * (math.log(R/hR) / math.tan(pitchangle) - (phi - phi0_youngstars))) rho_youngstars[k,j,i] *= (1.0 + w_youngstars * perturb) rho_dust[model.grid.gw > grid_wmax] = 0 rho_dust[model.grid.gz < grid_zmin] = 0 rho_dust[model.grid.gz > grid_zmax] = 0 kappa_ref = dust_properties.optical_properties.interp_chi_wav(0.55693) rho0 = cli.opticaldepth / (2.0 * hz_dust * kappa_ref) rho_dust[:] *= rho0 model.add_density_grid(rho_dust, 'dust_properties.hdf5') source_oldstars = model.add_map_source() source_oldstars.luminosity = luminosity_oldstars source_oldstars.temperature = temperature_oldstars source_oldstars.map = rho_oldstars source_youngstars = model.add_map_source() source_youngstars.luminosity = luminosity_youngstars source_youngstars.temperature = temperature_youngstars source_youngstars.map = rho_youngstars source_bulge = model.add_map_source() source_bulge.luminosity = luminosity_bulge source_bulge.temperature = temperature_bulge source_bulge.map = rho_bulge # # Check face-on optical depth at 1.0 micron (per gram dust) through the dust disk: # tau = 0 k = 0 i = 0 for j in range(0, grid_Nz): #print(model.grid.gz[k,j,i]/pc, rho_dust[k,j,i]) dz = model.grid.widths[1,k,j,i] dtau = dz * rho_dust[k,j,i] * kappa_ref tau += dtau deviation = 100.0 * abs(cli.opticaldepth - tau) / cli.opticaldepth if(cli.verbose): print("Check optical depth of dust density setup:") print(" kappa(0.55693 micron) = ", kappa_ref, "cm^2 g^-1") print(" Numerical integration of the face-on optical depth at 0.55693 micron through the central dust disk yields tau = ", tau) print(" This corresponds to a deviation to the chosen setup value of", deviation, "percent") # # Check central dust density: # rho_max = np.max(rho_dust) if(cli.opticaldepth < 1.0): rho_setup = 1.04366e-4 * msun/pc/pc/pc if(cli.opticaldepth < 3.0): rho_setup = 5.21829e-4 * msun/pc/pc/pc else: rho_setup = 2.60915e-3 * msun/pc/pc/pc deviation = 100.0 * abs(rho_setup - rho_max) / rho_setup if(cli.verbose): print("Check value of central dust density:") print(" rho_max = ", rho_max, "g cm^-3") print(" This corresponds to a deviation to the chosen setup value of", deviation, "percent") # # To compute total photon numbers: # grid_N = grid_Nw * grid_Nz * grid_Np if(cli.verbose): print("Radiation setup:") print(" photons_temperature / cell =", cli.photons_temperature) print(" photons_temperature total =", grid_N * cli.photons_temperature) file = filename(cli, "temperature") file += ".rtin" else: file = filename(cli, "temperature") file += ".rtout" try: with open(file): if(cli.verbose): print("Using the specific energy distribution from file", file) model.use_geometry(file) model.use_quantities(file, only_initial=False, copy=False) model.use_sources(file) except IOError: print("ERROR: File '", file, "' cannot be found. \nERROR: This file, containing the specific energy density, has to be computed first via calling hyperion.") exit(2) # # To compute total photon numbers: # grid_Nw = len(model.grid.gw[0,0,:]) grid_Nz = len(model.grid.gw[0,:,0]) grid_Np = len(model.grid.gw[:,0,0]) grid_N = grid_Nw * grid_Nz * grid_Np if(cli.verbose): print("Grid setup:") print(" grid_Nw =",grid_Nw) print(" grid_Nz =",grid_Nz) print(" grid_Np =",grid_Np) print("Radiation setup:") print(" photons_temperature / cell =", cli.photons_temperature) print(" photons_temperature total =", grid_N * cli.photons_temperature) print(" photons_raytracing / cell =", cli.photons_raytracing) print(" photons_raytracing total =", grid_N * cli.photons_raytracing) print(" photons_imaging / cell =", cli.photons_imaging) print(" photons_imaging total =", grid_N * cli.photons_imaging) file = filename(cli, "") file += ".rtin" ## ## Temperature, Images, and SEDs: ## if(cli.mode == "temperature"): model.set_raytracing(True) model.set_n_photons( initial = grid_N * cli.photons_temperature, raytracing_sources = grid_N * cli.photons_raytracing, raytracing_dust = grid_N * cli.photons_raytracing, imaging = grid_N * cli.photons_imaging ) elif(cli.mode == "images"): model.set_n_initial_iterations(0) model.set_raytracing(True) # old setup: model.set_monochromatic(True, wavelengths=[0.4, 1.0, 10.0, 100.0, 500.0]) model.set_monochromatic(True, wavelengths=[0.45483, 1.2520, 26.114, 242.29]) model.set_n_photons( raytracing_sources = grid_N * cli.photons_raytracing, raytracing_dust = grid_N * cli.photons_raytracing, imaging_sources = grid_N * cli.photons_imaging, imaging_dust = grid_N * cli.photons_imaging ) # group = 0 image1 = model.add_peeled_images(sed=False, image=True) image1.set_image_size(501, 501) image1.set_image_limits(-12500.0*pc, +12500.0*pc, -12500.0*pc, +12500.0*pc) image1.set_viewing_angles([30],[0]) image1.set_uncertainties(True) image1.set_output_bytes(8) image1.set_track_origin('basic') # group = 1 image2 = model.add_peeled_images(sed=False, image=True) image2.set_image_size(501, 501) image2.set_image_limits(-12500.0*pc, +12500.0*pc, -12500.0*pc, +12500.0*pc) image2.set_viewing_angles([80],[90]) image2.set_uncertainties(True) image2.set_output_bytes(8) image2.set_track_origin('basic') # group = 2 image3 = model.add_peeled_images(sed=False, image=True) image3.set_image_size(501, 501) image3.set_image_limits(-12500.0*pc, +12500.0*pc, -12500.0*pc, +12500.0*pc) image3.set_viewing_angles([88],[0]) # mostly edge-on image3.set_uncertainties(True) image3.set_output_bytes(8) image3.set_track_origin('basic') elif(cli.mode == "seds"): model.set_n_initial_iterations(0) model.set_raytracing(True) model.set_n_photons( raytracing_sources = grid_N * cli.photons_raytracing, raytracing_dust = grid_N * cli.photons_raytracing, imaging = grid_N * cli.photons_imaging ) # group = 0 sed1 = model.add_peeled_images(sed=True, image=False) sed1.set_wavelength_range(47, 0.081333, 1106.56) sed1.set_viewing_angles([30],[0]) sed1.set_peeloff_origin((0, 0, 0)) sed1.set_aperture_range(1, 25000.0*pc, 25000.0*pc) sed1.set_uncertainties(True) sed1.set_output_bytes(8) sed1.set_track_origin('basic') # group = 1 sed2 = model.add_peeled_images(sed=True, image=False) sed2.set_wavelength_range(47, 0.081333, 1106.56) sed2.set_viewing_angles([80],[0]) sed2.set_peeloff_origin((0, 0, 0)) sed2.set_aperture_range(1, 25000.0*pc, 25000.0*pc) sed2.set_uncertainties(True) sed2.set_output_bytes(8) sed2.set_track_origin('basic') # group = 2 sed3 = model.add_peeled_images(sed=True, image=False) sed3.set_wavelength_range(47, 0.081333, 1106.56) sed3.set_viewing_angles([88],[0]) sed3.set_peeloff_origin((0, 0, 0)) sed3.set_aperture_range(1, 25000.0*pc, 25000.0*pc) sed3.set_uncertainties(True) sed3.set_output_bytes(8) sed3.set_track_origin('basic') ## ## Write model for hyperion runs: ## model.conf.output.output_density = 'last' model.conf.output.output_specific_energy = 'last' model.conf.output.output_n_photons = 'last' model.write(file) if(cli.verbose): print("The input file for hyperion was written to", file)
def setup_model(cli): # # Hyperion setup: # model = Model() if(cli.mode == "temperature"): # # Dust properties: # dust_properties = SphericalDust('dust_integrated_full_scattering.hdf5') # # Write dust properties: # dust_properties.write('dust_properties.hdf5') dust_properties.plot('dust_properties.png') # # Grid setup: # grid_wmin = 0 grid_wmax = 5.0*pc # 4.0*pc grid_zmin = 0.0*pc grid_zmax = 10.0*pc grid_pmin = 0 grid_pmax = 2*pi grid_dx = cli.resolution*pc grid_dw = grid_dx # uniform resolution grid_dz = grid_dx # uniform resolution grid_dp = grid_dx # resolution at filament location at r = 1 pc grid_Nw = int((grid_wmax - grid_wmin) / grid_dw) grid_Nz = int((grid_zmax - grid_zmin) / grid_dz) grid_Np = int(2*pi * 1.0*pc / grid_dp) if(cli.verbose): print("Grid setup:") print(" Grid resolution =",cli.resolution, "pc.") print(" grid_Nw =",grid_Nw) print(" grid_Nz =",grid_Nz) print(" grid_Np =",grid_Np) #grid_w = np.logspace(np.log10(grid_wmin), np.log10(grid_wmax), grid_Nw) #grid_w = np.hstack([0., grid_w]) # add innermost cell interface at w=0 grid_w = np.linspace(grid_wmin, grid_wmax, grid_Nw+1) grid_z = np.linspace(grid_zmin, grid_zmax, grid_Nz+1) grid_p = np.linspace(grid_pmin, grid_pmax, grid_Np+1) model.set_cylindrical_polar_grid(grid_w, grid_z, grid_p) # # Dust density setup: # RC = 0.1*pc nC = 6.6580e+03 # in cm^-3 nC *= cli.opticaldepth # the optical depth at 1 micron nC *= m_h # in g cm^-3 nC /= 100.0 # converts from gas to dust density rho = np.zeros(model.grid.shape) # # n(r) = nC / [ 1.0 + (r/RC)**2.0 ] # x = -sin(2.0×pi×t) pc, y = +cos(2.0×pi×t) pc, z = 10.0×t pc, t = [0.0, 1.0] # => t = m.grid.gz / (10*pc) # => phi(t) = mod(360*t+270, 360) # for k in range(0, grid_Np): for j in range(0, grid_Nz): for i in range(0, grid_Nw): t = model.grid.gz[k,j,i] / (10*pc) if(cli.filament == "linear"): filament_center_x = 0 filament_center_y = 0 elif(cli.filament == "spiraling"): filament_center_x = - math.sin(2*pi*t)*pc filament_center_y = + math.cos(2*pi*t)*pc spherical_grid_r = model.grid.gw[k,j,i] spherical_grid_phi = model.grid.gp[k,j,i] cartesian_grid_x = spherical_grid_r * math.cos(spherical_grid_phi) cartesian_grid_y = spherical_grid_r * math.sin(spherical_grid_phi) rsquared = ( (cartesian_grid_x - filament_center_x)**2 + (cartesian_grid_y - filament_center_y)**2 ) rho[k,j,i] = nC / (1.0 + (rsquared / (RC*RC))) if rsquared**0.5 > 3*pc: rho[k,j,i] = 0 rho[model.grid.gw > grid_wmax] = 0 rho[model.grid.gz < grid_zmin] = 0 rho[model.grid.gz > grid_zmax] = 0 model.add_density_grid(rho, 'dust_properties.hdf5') # # Check optical depth through the filament: # # (y,z = 0, 2.5 pc goes through the filament center in all setups) # # Determine index of closest grid cell to z = 2.5 pc: # dz_last = 2*abs(grid_zmax-grid_zmin) for j in range(0, grid_Nz): dz = abs(model.grid.gz[0,j,0] - 2.5*pc) if(dz > dz_last): j=j-1 break else: dz_last = dz # # Opacity at 1.0 micron (per gram dust): # chi = dust_properties.optical_properties.interp_chi_wav(1.0) tau_max = 0 for k in range(0, grid_Np): tau = 0 for i in range(0, grid_Nw): dr = model.grid.widths[0,k,j,i] dtau = dr * rho[k,j,i] * chi tau += dtau tau_max = max(tau_max, tau) if(cli.filament == "linear"): tau_max *= 2 dev = 100 * abs(cli.opticaldepth - tau_max) / cli.opticaldepth if(cli.verbose): print("Check:") print(" Numerical integration of the optical depth through the filament center yields tau = ", tau_max) print(" This corresponds to a deviation to the chosen setup value of", dev, "percent") # # Source: # if(cli.sources == "external"): nu, jnu = np.loadtxt('bg_intensity_modified.txt', unpack=True) source_R = 5*pc source = model.add_external_spherical_source() source.peeloff = False source.position = (0, 0, 5.0*pc) # in a Cartesian frame source.radius = source_R source.spectrum = (nu, jnu) #source_MeanIntensity_J = <integrate bg_intensity.txt> #source_Area = 4.0 * pi * source_R*source_R source.luminosity = 8237.0*lsun #source_Area * pi * source_MeanIntensity_J elif(cli.sources == "stellar"): source = model.add_point_source() source.luminosity = 3.839e35 # in ergs s^-1 source.temperature = 10000.0 # in K if(cli.filament == "linear"): source.position = (3.0*pc, 0, 5.0*pc) elif(cli.filament == "spiraling"): source.position = (0 , 0, 3.0*pc) # # To compute total photon numbers: # grid_N = grid_Nw * grid_Nz * grid_Np if(cli.verbose): print("Radiation setup:") print(" photons_temperature / cell =", cli.photons_temperature) print(" photons_temperature total =", grid_N * cli.photons_temperature) file = filename(cli, "temperature") file += ".rtin" else: file = filename(cli, "temperature") file += ".rtout" try: with open(file): if(cli.verbose): print("Using the specific energy distribution from file", file) model.use_geometry(file) model.use_quantities(file, only_initial=False, copy=False) model.use_sources(file) except IOError: print("ERROR: File '", file, "' cannot be found. \nERROR: This file, containing the specific energy density, has to be computed first via calling hyperion.") exit(2) # # To compute total photon numbers: # grid_Nw = len(model.grid.gw[0,0,:]) grid_Nz = len(model.grid.gw[0,:,0]) grid_Np = len(model.grid.gw[:,0,0]) grid_N = grid_Nw * grid_Nz * grid_Np if(cli.verbose): print("Grid setup:") print(" grid_Nw =",grid_Nw) print(" grid_Nz =",grid_Nz) print(" grid_Np =",grid_Np) print("Radiation setup:") print(" photons_temperature / cell =", cli.photons_temperature) print(" photons_temperature total =", grid_N * cli.photons_temperature) print(" photons_raytracing / cell =", cli.photons_raytracing) print(" photons_raytracing total =", grid_N * cli.photons_raytracing) print(" photons_imaging / cell =", cli.photons_imaging) print(" photons_imaging total =", grid_N * cli.photons_imaging) file = filename(cli, "") file += ".rtin" ## ## Temperature, Images, and SEDs: ## if(cli.mode == "temperature"): model.set_raytracing(True) model.set_n_photons( initial = grid_N * cli.photons_temperature, raytracing_sources = grid_N * cli.photons_raytracing, raytracing_dust = grid_N * cli.photons_raytracing, imaging = grid_N * cli.photons_imaging ) elif(cli.mode == "images"): model.set_n_initial_iterations(0) model.set_raytracing(True) model.set_monochromatic(True, wavelengths=[100.0, 500.0, 0.55, 2.2]) model.set_n_photons( raytracing_sources = grid_N * cli.photons_raytracing, raytracing_dust = grid_N * cli.photons_raytracing, imaging_sources = grid_N * cli.photons_imaging, imaging_dust = grid_N * cli.photons_imaging ) # group = 0 image1x = model.add_peeled_images(sed=False, image=True) image1x.set_image_size(300, 300) image1x.set_image_limits(-5*pc, +5*pc, 0, 10*pc) image1x.set_viewing_angles([90],[0]) # along the x-direction image1x.set_uncertainties(True) image1x.set_output_bytes(8) image1x.set_track_origin('basic') # group = 1 image1y = model.add_peeled_images(sed=False, image=True) image1y.set_image_size(300, 300) image1y.set_image_limits(-5*pc, +5*pc, 0, 10*pc) image1y.set_viewing_angles([90],[90]) # along the y-direction image1y.set_uncertainties(True) image1y.set_output_bytes(8) image1y.set_track_origin('basic') # group = 2 image1z = model.add_peeled_images(sed=False, image=True) image1z.set_image_size(300, 300) image1z.set_image_limits(-5*pc, +5*pc, -5*pc, +5*pc) image1z.set_viewing_angles([0],[0]) # along the z-direction image1z.set_uncertainties(True) image1z.set_output_bytes(8) image1z.set_track_origin('basic') elif(cli.mode == "sed"): model.set_n_initial_iterations(0) model.set_raytracing(True) model.set_n_photons( raytracing_sources = grid_N * cli.photons_raytracing, raytracing_dust = grid_N * cli.photons_raytracing, imaging = grid_N * cli.photons_imaging ) # group = 0 sed1 = model.add_peeled_images(sed=True, image=False) sed1.set_wavelength_range(250, 0.01, 2000.0) sed1.set_viewing_angles([90],[0]) # along the x-direction sed1.set_peeloff_origin((0, 0, 2.5*pc)) sed1.set_aperture_range(1, 0.3*pc, 0.3*pc) sed1.set_uncertainties(True) sed1.set_output_bytes(8) sed1.set_track_origin('basic') # group = 1 sed2 = model.add_peeled_images(sed=True, image=False) sed2.set_wavelength_range(250, 0.01, 2000.0) sed2.set_viewing_angles([90],[0]) # along the x-direction sed2.set_peeloff_origin((0, 0, 5.0*pc)) sed2.set_aperture_range(1, 0.3*pc, 0.3*pc) sed2.set_uncertainties(True) sed2.set_output_bytes(8) sed2.set_track_origin('basic') # group = 2 sed3 = model.add_peeled_images(sed=True, image=False) sed3.set_wavelength_range(250, 0.01, 2000.0) sed3.set_viewing_angles([90],[0]) # along the x-direction sed3.set_peeloff_origin((0, 0, 7.5*pc)) sed3.set_aperture_range(1, 0.3*pc, 0.3*pc) sed3.set_uncertainties(True) sed3.set_output_bytes(8) sed3.set_track_origin('basic') ## ## Write model for hyperion runs: ## model.conf.output.output_density = 'last' model.conf.output.output_specific_energy = 'last' model.conf.output.output_n_photons = 'last' model.write(file) if(cli.verbose): print("The input file for hyperion was written to", file)
def extract_hyperion(filename,indir=None,outdir=None,dstar=178.0,wl_aper=None,save=True,filter_func=False,\ plot_all=False,clean=False,exclude_wl=[],log=True): def l_bol(wl, fv, dist=178.0): import numpy as np import astropy.constants as const # wavelength unit: um # Flux density unit: Jy # # constants setup # c = const.c.cgs.value pc = const.pc.cgs.value PI = np.pi SL = const.L_sun.cgs.value # Convert the unit from Jy to erg s-1 cm-2 Hz-1 fv = np.array(fv) * 1e-23 freq = c / (1e-4 * np.array(wl)) diff_dum = freq[1:] - freq[0:-1] freq_interpol = np.hstack( (freq[0:-1] + diff_dum / 2.0, freq[0:-1] + diff_dum / 2.0, freq[0], freq[-1])) freq_interpol = freq_interpol[np.argsort(freq_interpol)[::-1]] fv_interpol = np.empty(len(freq_interpol)) # calculate the histogram style of spectrum # for i in range(0, len(fv)): if i == 0: fv_interpol[i] = fv[i] else: fv_interpol[2 * i - 1] = fv[i - 1] fv_interpol[2 * i] = fv[i] fv_interpol[-1] = fv[-1] dv = freq_interpol[0:-1] - freq_interpol[1:] dv = np.delete(dv, np.where(dv == 0)) fv = fv[np.argsort(freq)] freq = freq[np.argsort(freq)] return (np.trapz(fv, freq) * 4. * PI * (dist * pc)**2) / SL # to avoid X server error import matplotlib as mpl mpl.use('Agg') # import matplotlib.pyplot as plt import numpy as np import os from hyperion.model import ModelOutput, Model from scipy.interpolate import interp1d from hyperion.util.constants import pc, c, lsun, au from astropy.io import ascii import sys sys.path.append(os.path.expanduser('~') + '/programs/spectra_analysis/') from phot_filter import phot_filter from get_bhr71_obs import get_bhr71_obs # seaborn colormap, because jet is bad obviously import seaborn.apionly as sns # Read in the observation data and calculate the noise & variance if indir == None: indir = '/Users/yaolun/bhr71/' if outdir == None: outdir = '/Users/yaolun/bhr71/hyperion/' # assign the file name from the input file print_name = os.path.splitext(os.path.basename(filename))[0] # use a canned function to extract BHR71 observational data bhr71 = get_bhr71_obs(indir) # unit in um, Jy wl_tot, flux_tot, unc_tot = bhr71['spec'] flux_tot = flux_tot * 1e-23 # convert unit from Jy to erg s-1 cm-2 Hz-1 unc_tot = unc_tot * 1e-23 l_bol_obs = l_bol(wl_tot, flux_tot * 1e23) wl_phot, flux_phot, flux_sig_phot = bhr71['phot'] flux_phot = flux_phot * 1e-23 # convert unit from Jy to erg s-1 cm-2 Hz-1 flux_sig_phot = flux_sig_phot * 1e-23 # Print the observed L_bol # wl_tot = np.hstack((wl_irs,wl_obs,wl_phot)) # flux_tot = np.hstack((flux_irs,flux_obs,flux_phot)) # flux_tot = flux_tot[np.argsort(wl_tot)] # wl_tot = wl_tot[np.argsort(wl_tot)] # l_bol_obs = l_bol(wl_tot,flux_tot*1e23) # Open the model m = ModelOutput(filename) if wl_aper == None: wl_aper = [ 3.6, 4.5, 5.8, 8.0, 10, 16, 20, 24, 35, 70, 100, 160, 250, 350, 500, 850 ] # Create the plot mag = 1.5 fig = plt.figure(figsize=(8 * mag, 6 * mag)) ax_sed = fig.add_subplot(1, 1, 1) # Plot the observed SED # plot the observed spectra if not clean: color_seq = ['Green', 'Red', 'Blue'] else: color_seq = ['DimGray', 'DimGray', 'DimGray'] # plot the observations if log: pacs, = ax_sed.plot(np.log10(wl_tot[(wl_tot>40) & (wl_tot<190.31)]),\ np.log10(c/(wl_tot[(wl_tot>40) & (wl_tot<190.31)]*1e-4)*flux_tot[(wl_tot>40) & (wl_tot<190.31)]),\ '-',color=color_seq[0],linewidth=1.5*mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]),np.log10(c/(wl_tot[wl_tot > 194]*1e-4)*flux_tot[wl_tot > 194]),\ '-',color=color_seq[1],linewidth=1.5*mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]),np.log10(c/(wl_tot[wl_tot < 40]*1e-4)*flux_tot[wl_tot < 40]),\ '-',color=color_seq[2],linewidth=1.5*mag, alpha=0.7) photometry, = ax_sed.plot(np.log10(wl_phot), np.log10(c / (wl_phot * 1e-4) * flux_phot), 's', mfc='DimGray', mec='k', markersize=8) # plot the observed photometry data ax_sed.errorbar(np.log10(wl_phot),np.log10(c/(wl_phot*1e-4)*flux_phot),\ yerr=[np.log10(c/(wl_phot*1e-4)*flux_phot)-np.log10(c/(wl_phot*1e-4)*(flux_phot-flux_sig_phot)),\ np.log10(c/(wl_phot*1e-4)*(flux_phot+flux_sig_phot))-np.log10(c/(wl_phot*1e-4)*flux_phot)],\ fmt='s',mfc='DimGray',mec='k',markersize=8) else: pacs, = ax_sed.plot(np.log10(wl_tot[(wl_tot>40) & (wl_tot<190.31)]),\ c/(wl_tot[(wl_tot>40) & (wl_tot<190.31)]*1e-4)*flux_tot[(wl_tot>40) & (wl_tot<190.31)],\ '-',color=color_seq[0],linewidth=1.5*mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]),c/(wl_tot[wl_tot > 194]*1e-4)*flux_tot[wl_tot > 194],\ '-',color=color_seq[1],linewidth=1.5*mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]),c/(wl_tot[wl_tot < 40]*1e-4)*flux_tot[wl_tot < 40],\ '-',color=color_seq[2],linewidth=1.5*mag, alpha=0.7) photometry, = ax_sed.plot(wl_phot, c / (wl_phot * 1e-4) * flux_phot, 's', mfc='DimGray', mec='k', markersize=8) # plot the observed photometry data ax_sed.errorbar(np.log10(wl_phot),c/(wl_phot*1e-4)*flux_phot,\ yerr=[c/(wl_phot*1e-4)*flux_phot-c/(wl_phot*1e-4)*(flux_phot-flux_sig_phot),\ c/(wl_phot*1e-4)*(flux_phot+flux_sig_phot)-c/(wl_phot*1e-4)*flux_phot],\ fmt='s',mfc='DimGray',mec='k',markersize=8) if not clean: ax_sed.text(0.75, 0.9, r'$\rm{L_{bol}= %5.2f L_{\odot}}$' % l_bol_obs, fontsize=mag * 16, transform=ax_sed.transAxes) # else: # pacs, = ax_sed.plot(np.log10(wl_tot[(wl_tot>40) & (wl_tot<190.31)]),\ # np.log10(c/(wl_tot[(wl_tot>40) & (wl_tot<190.31)]*1e-4)*flux_tot[(wl_tot>40) & (wl_tot<190.31)]),\ # '-',color='DimGray',linewidth=1.5*mag, alpha=0.7) # spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]),np.log10(c/(wl_tot[wl_tot > 194]*1e-4)*flux_tot[wl_tot > 194]),\ # '-',color='DimGray',linewidth=1.5*mag, alpha=0.7) # irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]),np.log10(c/(wl_tot[wl_tot < 40]*1e-4)*flux_tot[wl_tot < 40]),\ # '-',color='DimGray',linewidth=1.5*mag, alpha=0.7) # Extract the SED for the smallest inclination and largest aperture, and # scale to 300pc. In Python, negative indices can be used for lists and # arrays, and indicate the position from the end. So to get the SED in the # largest aperture, we set aperture=-1. # aperture group is aranged from smallest to infinite sed_inf = m.get_sed(group=0, inclination=0, aperture=-1, distance=dstar * pc, uncertainties=True) # plot the simulated SED if clean == False: sim, = ax_sed.plot(np.log10(sed_inf.wav), np.log10(sed_inf.val), '-', color='GoldenRod', linewidth=0.5 * mag) ax_sed.fill_between(np.log10(sed_inf.wav), np.log10(sed_inf.val-sed_inf.unc), np.log10(sed_inf.val+sed_inf.unc),\ color='GoldenRod', alpha=0.5) # get flux at different apertures flux_aper = np.zeros_like(wl_aper, dtype=float) unc_aper = np.zeros_like(wl_aper, dtype=float) a = np.zeros_like(wl_aper) + 1 color_list = plt.cm.jet(np.linspace(0, 1, len(wl_aper) + 1)) for i in range(0, len(wl_aper)): if wl_aper[i] in exclude_wl: continue # if (wl_aper[i] == 5.8) or (wl_aper[i] == 8.0) or (wl_aper[i] == 10.5) or (wl_aper[i] == 11): # continue sed_dum = m.get_sed(group=i + 1, inclination=0, aperture=-1, distance=dstar * pc, uncertainties=True) if plot_all == True: ax_sed.plot(np.log10(sed_dum.wav), np.log10(sed_dum.val), '-', color=color_list[i]) ax_sed.fill_between(np.log10(sed_dum.wav), np.log10(sed_dum.val-sed_dum.unc), np.log10(sed_dum.val+sed_dum.unc),\ color=color_list[i], alpha=0.5) if filter_func == False: # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((sed_dum.wav < wl_aper[i] * (1 + 1. / res)) & (sed_dum.wav > wl_aper[i] * (1 - 1. / res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(sed_dum.val[ind]) unc_aper[i] = np.mean(sed_dum.unc[ind]) else: f = interp1d(sed_dum.wav, sed_dum.val) f_unc = interp1d(sed_dum.wav, sed_dum.unc) flux_aper[i] = f(wl_aper[i]) unc_aper[i] = f_unc(wl_aper[i]) else: # apply the filter function # decide the filter name if wl_aper[i] == 70: fil_name = 'Herschel PACS 70um' elif wl_aper[i] == 100: fil_name = 'Herschel PACS 100um' elif wl_aper[i] == 160: fil_name = 'Herschel PACS 160um' elif wl_aper[i] == 250: fil_name = 'Herschel SPIRE 250um' elif wl_aper[i] == 350: fil_name = 'Herschel SPIRE 350um' elif wl_aper[i] == 500: fil_name = 'Herschel SPIRE 500um' elif wl_aper[i] == 3.6: fil_name = 'IRAC Channel 1' elif wl_aper[i] == 4.5: fil_name = 'IRAC Channel 2' elif wl_aper[i] == 5.8: fil_name = 'IRAC Channel 3' elif wl_aper[i] == 8.0: fil_name = 'IRAC Channel 4' elif wl_aper[i] == 24: fil_name = 'MIPS 24um' elif wl_aper[i] == 850: fil_name = 'SCUBA 850WB' else: fil_name = None if fil_name != None: filter_func = phot_filter(fil_name) # Simulated SED should have enough wavelength coverage for applying photometry filters. f = interp1d(sed_dum.wav, sed_dum.val) f_unc = interp1d(sed_dum.wav, sed_dum.unc) flux_aper[i] = np.trapz( filter_func['wave'] / 1e4, f(filter_func['wave'] / 1e4) * filter_func['transmission']) / np.trapz( filter_func['wave'] / 1e4, filter_func['transmission']) unc_aper[i] = abs( np.trapz((filter_func['wave'] / 1e4)**2, (f_unc(filter_func['wave'] / 1e4) * filter_func['transmission'])**2))**0.5 / abs( np.trapz(filter_func['wave'] / 1e4, filter_func['transmission'])) else: # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((sed_dum.wav < wl_aper[i] * (1 + 1. / res)) & (sed_dum.wav > wl_aper[i] * (1 - 1. / res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(sed_dum.val[ind]) unc_aper[i] = np.mean(sed_dum.unc[ind]) else: f = interp1d(sed_dum.wav, sed_dum.val) f_unc = interp1d(sed_dum.wav, sed_dum.unc) flux_aper[i] = f(wl_aper[i]) unc_aper[i] = f_unc(wl_aper[i]) # temperory step: solve issue of uncertainty greater than the value for i in range(len(wl_aper)): if unc_aper[i] >= flux_aper[i]: unc_aper[i] = flux_aper[i] - 1e-20 # perform the same procedure of flux extraction of aperture flux with observed spectra wl_aper = np.array(wl_aper, dtype=float) obs_aper_wl = wl_aper[(wl_aper >= min(wl_tot)) & (wl_aper <= max(wl_tot))] obs_aper_sed = np.zeros_like(obs_aper_wl) obs_aper_sed_unc = np.zeros_like(obs_aper_wl) sed_tot = c / (wl_tot * 1e-4) * flux_tot sed_unc_tot = c / (wl_tot * 1e-4) * unc_tot # wl_tot and flux_tot are already hstacked and sorted by wavelength for i in range(0, len(obs_aper_wl)): if obs_aper_wl[i] in exclude_wl: continue if filter_func == False: # use a rectangle function the average the simulated SED # apply the spectral resolution if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i] * (1 + 1. / res)) & (wl_tot > obs_aper_wl[i] * (1 - 1. / res))) if len(ind[0]) != 0: obs_aper_sed[i] = np.mean(sed_tot[ind]) obs_aper_sed_unc[i] = np.mean(sed_unc_tot[ind]) else: f = interp1d(wl_tot, sed_tot) f_unc = interp1d(wl_tot, sed_unc_tot) obs_aper_sed[i] = f(obs_aper_wl[i]) obs_aper_sed_unc[i] = f_unc(obs_aper_wl[i]) else: # apply the filter function # decide the filter name if obs_aper_wl[i] == 70: fil_name = 'Herschel PACS 70um' elif obs_aper_wl[i] == 100: fil_name = 'Herschel PACS 100um' elif obs_aper_wl[i] == 160: fil_name = 'Herschel PACS 160um' elif obs_aper_wl[i] == 250: fil_name = 'Herschel SPIRE 250um' elif obs_aper_wl[i] == 350: fil_name = 'Herschel SPIRE 350um' elif obs_aper_wl[i] == 500: fil_name = 'Herschel SPIRE 500um' elif obs_aper_wl[i] == 3.6: fil_name = 'IRAC Channel 1' elif obs_aper_wl[i] == 4.5: fil_name = 'IRAC Channel 2' elif obs_aper_wl[i] == 5.8: fil_name = 'IRAC Channel 3' elif obs_aper_wl[i] == 8.0: fil_name = 'IRAC Channel 4' elif obs_aper_wl[i] == 24: fil_name = 'MIPS 24um' # elif obs_aper_wl[i] == 850: # fil_name = 'SCUBA 850WB' # do not have SCUBA spectra else: fil_name = None # print obs_aper_wl[i], fil_name if fil_name != None: filter_func = phot_filter(fil_name) # Observed SED needs to be trimmed before applying photometry filters filter_func = filter_func[(filter_func['wave']/1e4 >= min(wl_tot))*\ ((filter_func['wave']/1e4 >= 54.8)+(filter_func['wave']/1e4 <= 36.0853))*\ ((filter_func['wave']/1e4 <= 95.05)+(filter_func['wave']/1e4 >=103))*\ ((filter_func['wave']/1e4 <= 190.31)+(filter_func['wave']/1e4 >= 195))*\ (filter_func['wave']/1e4 <= max(wl_tot))] f = interp1d(wl_tot, sed_tot) f_unc = interp1d(wl_tot, sed_unc_tot) obs_aper_sed[i] = np.trapz( filter_func['wave'] / 1e4, f(filter_func['wave'] / 1e4) * filter_func['transmission']) / np.trapz( filter_func['wave'] / 1e4, filter_func['transmission']) obs_aper_sed_unc[i] = abs( np.trapz((filter_func['wave'] / 1e4)**2, (f_unc(filter_func['wave'] / 1e4) * filter_func['transmission'])**2))**0.5 / abs( np.trapz(filter_func['wave'] / 1e4, filter_func['transmission'])) else: # use a rectangle function the average the simulated SED # apply the spectral resolution if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i] * (1 + 1. / res)) & (wl_tot > obs_aper_wl[i] * (1 - 1. / res))) if len(ind[0]) != 0: obs_aper_sed[i] = np.mean(sed_tot[ind]) obs_aper_sed_unc[i] = np.mean(sed_unc_tot[ind]) else: f = interp1d(wl_tot, sed_tot) f_unc = interp1d(wl_tot, sed_unc_tot) obs_aper_sed[i] = f(obs_aper_wl[i]) obs_aper_sed_unc[i] = f_unc(obs_aper_wl[i]) # if clean == False: # if log: # aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), np.log10(obs_aper_sed), \ # yerr=[np.log10(obs_aper_sed)-np.log10(obs_aper_sed-obs_aper_sed_unc), np.log10(obs_aper_sed+obs_aper_sed_unc)-np.log10(obs_aper_sed)],\ # fmt='s', mec='Magenta', mfc='Magenta', markersize=10, elinewidth=3, ecolor='Magenta',capthick=3,barsabove=True) # aper = ax_sed.errorbar(np.log10(wl_aper), np.log10(flux_aper),\ # yerr=[np.log10(flux_aper)-np.log10(flux_aper-unc_aper), np.log10(flux_aper+unc_aper)-np.log10(flux_aper)],\ # fmt='o', mfc='None', mec='k', ecolor='Black', markersize=12, markeredgewidth=3, elinewidth=3, barsabove=True) # else: # aper_obs = ax_sed.errorbar(obs_aper_wl, obs_aper_sed, yerr=obs_aper_sed_unc,\ # fmt='s', mec='Magenta', mfc='Magenta', markersize=10, elinewidth=3, ecolor='Magenta',capthick=3,barsabove=True) # aper = ax_sed.errorbar(wl_aper, flux_aper, yerr=unc_aper,\ # fmt='o', mfc='None', mec='k', ecolor='Black', markersize=12, markeredgewidth=3, elinewidth=3, barsabove=True) # else: if log: aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), np.log10(obs_aper_sed),\ yerr=[np.log10(obs_aper_sed)-np.log10(obs_aper_sed-obs_aper_sed_unc), np.log10(obs_aper_sed+obs_aper_sed_unc)-np.log10(obs_aper_sed)],\ fmt='s', mec='None', mfc='r', markersize=10, linewidth=1.5, ecolor='Red', elinewidth=3, capthick=3, barsabove=True) aper = ax_sed.errorbar(np.log10(wl_aper),np.log10(flux_aper),\ yerr=[np.log10(flux_aper)-np.log10(flux_aper-unc_aper), np.log10(flux_aper+unc_aper)-np.log10(flux_aper)],\ fmt='o', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=2.5, linewidth=1.7, ecolor='Blue', elinewidth=3, barsabove=True) ax_sed.set_ylim([-14, -7]) ax_sed.set_xlim([0, 3]) else: aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), obs_aper_sed, yerr=obs_aper_sed_unc,\ fmt='s', mec='None', mfc='r', markersize=10, linewidth=1.5, ecolor='Red', elinewidth=3, capthick=3, barsabove=True) aper = ax_sed.errorbar(np.log10(wl_aper),flux_aper, yerr=unc_aper,\ fmt='o', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=2.5, linewidth=1.7, ecolor='Blue', elinewidth=3, barsabove=True) # ax_sed.set_xlim([1, 1000]) ax_sed.set_xlim([0, 3]) # ax_sed.set_ylim([1e-14, 1e-8]) # calculate the bolometric luminosity of the aperture # print flux_aper l_bol_sim = l_bol(wl_aper, flux_aper / (c / np.array(wl_aper) * 1e4) * 1e23) print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # print out the sed into ascii file for reading in later if save == True: # unapertured SED foo = open(outdir + print_name + '_sed_inf.txt', 'w') foo.write('%12s \t %12s \t %12s \n' % ('wave', 'vSv', 'sigma_vSv')) for i in range(0, len(sed_inf.wav)): foo.write('%12g \t %12g \t %12g \n' % (sed_inf.wav[i], sed_inf.val[i], sed_inf.unc[i])) foo.close() # SED with convolution of aperture sizes foo = open(outdir + print_name + '_sed_w_aperture.txt', 'w') foo.write('%12s \t %12s \t %12s \n' % ('wave', 'vSv', 'sigma_vSv')) for i in range(0, len(wl_aper)): foo.write('%12g \t %12g \t %12g \n' % (wl_aper[i], flux_aper[i], unc_aper[i])) foo.close() # Read in and plot the simulated SED produced by RADMC-3D using the same parameters # [wl,fit] = np.genfromtxt(indir+'hyperion/radmc_comparison/spectrum.out',dtype='float',skip_header=3).T # l_bol_radmc = l_bol(wl,fit*1e23/dstar**2) # radmc, = ax_sed.plot(np.log10(wl),np.log10(c/(wl*1e-4)*fit/dstar**2),'-',color='DimGray', linewidth=1.5*mag, alpha=0.5) # print the L bol of the simulated SED (both Hyperion and RADMC-3D) # lg_sim = ax_sed.legend([sim,radmc],[r'$\rm{L_{bol,sim}=%5.2f\,L_{\odot},\,L_{center}=9.18\,L_{\odot}}$' % l_bol_sim, \ # r'$\rm{L_{bol,radmc3d}=%5.2f\,L_{\odot},\,L_{center}=9.18\,L_{\odot}}$' % l_bol_radmc],\ # loc='lower right',fontsize=mag*16) # read the input central luminosity by reading in the source information from output file dum = Model() dum.use_sources(filename) L_cen = dum.sources[0].luminosity / lsun # legend lg_data = ax_sed.legend([irs, photometry, aper, aper_obs],\ [r'$\rm{observation}$',\ r'$\rm{photometry}$',r'$\rm{F_{aper,sim}}$',r'$\rm{F_{aper,obs}}$'],\ loc='upper left',fontsize=14*mag,numpoints=1,framealpha=0.3) if clean == False: lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f\,L_{\odot},\,L_{center}=%5.2f\,L_{\odot}}$' % (l_bol_sim, L_cen)], \ loc='lower right',fontsize=mag*16) plt.gca().add_artist(lg_data) # plot setting ax_sed.set_xlabel(r'$\rm{log\,\lambda\,({\mu}m)}$', fontsize=mag * 20) ax_sed.set_ylabel(r'$\rm{log\,\nu S_{\nu}\,(erg/cm^{2}/s)}$', fontsize=mag * 20) [ ax_sed.spines[axis].set_linewidth(1.5 * mag) for axis in ['top', 'bottom', 'left', 'right'] ] ax_sed.minorticks_on() ax_sed.tick_params('both', labelsize=mag * 18, width=1.5 * mag, which='major', pad=15, length=5 * mag) ax_sed.tick_params('both', labelsize=mag * 18, width=1.5 * mag, which='minor', pad=15, length=2.5 * mag) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=mag * 18) for label in ax_sed.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax_sed.get_yticklabels(): label.set_fontproperties(ticks_font) # if clean == False: # lg_data = ax_sed.legend([irs, pacs, spire,photometry],[r'$\rm{{\it Spitzer}-IRS}$',r'$\rm{{\it Herschel}-PACS}$',r'$\rm{{\it Herschel}-SPIRE}$',r'$\rm{Photometry}$'],\ # loc='upper left',fontsize=14*mag,numpoints=1,framealpha=0.3) # plt.gca().add_artist(lg_sim) # else: # lg_data = ax_sed.legend([irs, photometry, aper, aper_obs],\ # [r'$\rm{observation}$',\ # r'$\rm{photometry}$',r'$\rm{F_{aper,sim}}$',r'$\rm{F_{aper,obs}}$'],\ # loc='upper left',fontsize=14*mag,numpoints=1,framealpha=0.3) # Write out the plot fig.savefig(outdir + print_name + '_sed.pdf', format='pdf', dpi=300, bbox_inches='tight') fig.clf() # Package for matching the colorbar from mpl_toolkits.axes_grid1 import make_axes_locatable # Extract the image for the first inclination, and scale to 300pc. We # have to specify group=1 as there is no image in group 0. image = m.get_image(group=len(wl_aper) + 1, inclination=0, distance=dstar * pc, units='MJy/sr') # image = m.get_image(group=14, inclination=0, distance=dstar * pc, units='MJy/sr') # Open figure and create axes # fig = plt.figure(figsize=(8, 8)) fig, axarr = plt.subplots(3, 3, sharex='col', sharey='row', figsize=(13.5, 12)) # Pre-set maximum for colorscales VMAX = {} # VMAX[3.6] = 10. # VMAX[24] = 100. # VMAX[160] = 2000. # VMAX[500] = 2000. VMAX[100] = 10. VMAX[250] = 100. VMAX[500] = 2000. VMAX[1000] = 2000. # We will now show four sub-plots, each one for a different wavelength # for i, wav in enumerate([3.6, 24, 160, 500]): # for i, wav in enumerate([100, 250, 500, 1000]): # for i, wav in enumerate([4.5, 9.7, 24, 40, 70, 100, 250, 500, 1000]): for i, wav in enumerate([3.6, 8.0, 9.7, 24, 40, 100, 250, 500, 1000]): # ax = fig.add_subplot(3, 3, i + 1) ax = axarr[i / 3, i % 3] # Find the closest wavelength iwav = np.argmin(np.abs(wav - image.wav)) # Calculate the image width in arcseconds given the distance used above # get the max radius rmax = max(m.get_quantities().r_wall) w = np.degrees(rmax / image.distance) * 3600. # Image in the unit of MJy/sr # Change it into erg/s/cm2/Hz/sr factor = 1e-23 * 1e6 # avoid zero in log # flip the image, because the setup of inclination is upside down val = image.val[::-1, :, iwav] * factor + 1e-30 # val = image.val[:, :, iwav] * factor + 1e-30 # This is the command to show the image. The parameters vmin and vmax are # the min and max levels for the colorscale (remove for default values). # cmap = sns.cubehelix_palette(start=0.1, rot=-0.7, gamma=0.2, as_cmap=True) cmap = plt.cm.CMRmap im = ax.imshow(np.log10(val), vmin=-22, vmax=-12, cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=14) for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) # Colorbar setting # create an axes on the right side of ax. The width of cax will be 5% # of ax and the padding between cax and ax will be fixed at 0.05 inch. if (i + 1) % 3 == 0: divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cb = fig.colorbar(im, cax=cax) cb.solids.set_edgecolor("face") cb.ax.minorticks_on() cb.ax.set_ylabel( r'$\rm{log(I_{\nu})\,[erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$', fontsize=12) cb_obj = plt.getp(cb.ax.axes, 'yticklabels') plt.setp(cb_obj, fontsize=12) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=12) for label in cb.ax.get_yticklabels(): label.set_fontproperties(ticks_font) if (i + 1) == 7: # Finalize the plot ax.set_xlabel(r'$\rm{RA\,Offset\,(arcsec)}$', fontsize=14) ax.set_ylabel(r'$\rm{Dec\,Offset\,(arcsec)}$', fontsize=14) ax.tick_params(axis='both', which='major', labelsize=16) ax.set_adjustable('box-forced') ax.text(0.7, 0.88, str(wav) + r'$\rm{\,\mu m}$', fontsize=16, color='white', transform=ax.transAxes) fig.subplots_adjust(hspace=0, wspace=-0.2) # Adjust the spaces between the subplots # plt.tight_layout() fig.savefig(outdir + print_name + '_cube_plot.png', format='png', dpi=300, bbox_inches='tight') fig.clf()
def extract_hyperion(filename, indir=None, outdir=None, dstar=178.0, wl_aper=None, save=True): def l_bol(wl, fv, dist=178.0): import numpy as np import astropy.constants as const # wavelength unit: um # Flux density unit: Jy # # constants setup # c = const.c.cgs.value pc = const.pc.cgs.value PI = np.pi SL = const.L_sun.cgs.value # Convert the unit from Jy to erg s-1 cm-2 Hz-1 fv = np.array(fv) * 1e-23 freq = c / (1e-4 * np.array(wl)) diff_dum = freq[1:] - freq[0:-1] freq_interpol = np.hstack( (freq[0:-1] + diff_dum / 2.0, freq[0:-1] + diff_dum / 2.0, freq[0], freq[-1])) freq_interpol = freq_interpol[np.argsort(freq_interpol)[::-1]] fv_interpol = np.empty(len(freq_interpol)) # calculate the histogram style of spectrum # for i in range(0, len(fv)): if i == 0: fv_interpol[i] = fv[i] else: fv_interpol[2 * i - 1] = fv[i - 1] fv_interpol[2 * i] = fv[i] fv_interpol[-1] = fv[-1] dv = freq_interpol[0:-1] - freq_interpol[1:] dv = np.delete(dv, np.where(dv == 0)) fv = fv[np.argsort(freq)] freq = freq[np.argsort(freq)] return (np.trapz(fv, freq) * 4. * PI * (dist * pc)**2) / SL import matplotlib.pyplot as plt import numpy as np import os from hyperion.model import ModelOutput from hyperion.model import Model from scipy.interpolate import interp1d from hyperion.util.constants import pc, c, lsun # Read in the observation data and calculate the noise & variance if indir == None: indir = '/Users/yaolun/bhr71/' if outdir == None: outdir = '/Users/yaolun/bhr71/hyperion/' # assign the file name from the input file print_name = os.path.splitext(os.path.basename(filename))[0] # [wl_pacs,flux_pacs,unc_pacs] = np.genfromtxt(indir+'BHR71_centralSpaxel_PointSourceCorrected_CorrectedYES_trim_continuum.txt',\ dtype='float',skip_header=1).T # Convert the unit from Jy to erg cm-2 Hz-1 flux_pacs = flux_pacs * 1e-23 [wl_spire, flux_spire] = np.genfromtxt(indir + 'BHR71_spire_corrected_continuum.txt', dtype='float', skip_header=1).T flux_spire = flux_spire * 1e-23 wl_obs = np.hstack((wl_pacs, wl_spire)) flux_obs = np.hstack((flux_pacs, flux_spire)) [wl_pacs_data,flux_pacs_data,unc_pacs_data] = np.genfromtxt(indir+'BHR71_centralSpaxel_PointSourceCorrected_CorrectedYES_trim.txt',\ dtype='float').T [wl_spire_data,flux_spire_data] = np.genfromtxt(indir+'BHR71_spire_corrected.txt',\ dtype='float').T [wl_pacs_flat,flux_pacs_flat,unc_pacs_flat] = np.genfromtxt(indir+'BHR71_centralSpaxel_PointSourceCorrected_CorrectedYES_trim_flat_spectrum.txt',\ dtype='float',skip_header=1).T [wl_spire_flat, flux_spire_flat ] = np.genfromtxt(indir + 'BHR71_spire_corrected_flat_spectrum.txt', dtype='float', skip_header=1).T # Convert the unit from Jy to erg cm-2 Hz-1 flux_pacs_flat = flux_pacs_flat * 1e-23 flux_spire_flat = flux_spire_flat * 1e-23 flux_pacs_data = flux_pacs_data * 1e-23 flux_spire_data = flux_spire_data * 1e-23 wl_pacs_noise = wl_pacs_data flux_pacs_noise = flux_pacs_data - flux_pacs - flux_pacs_flat wl_spire_noise = wl_spire_data flux_spire_noise = flux_spire_data - flux_spire - flux_spire_flat # Read in the Spitzer IRS spectrum [wl_irs, flux_irs] = (np.genfromtxt(indir + 'bhr71_spitzer_irs.txt', skip_header=2, dtype='float').T)[0:2] # Convert the unit from Jy to erg cm-2 Hz-1 flux_irs = flux_irs * 1e-23 # Remove points with zero or negative flux ind = flux_irs > 0 wl_irs = wl_irs[ind] flux_irs = flux_irs[ind] # Calculate the local variance (for spire), use the instrument uncertainty for pacs # wl_noise_5 = wl_spire_noise[(wl_spire_noise > 194) * (wl_spire_noise <= 304)] flux_noise_5 = flux_spire_noise[(wl_spire_noise > 194) * (wl_spire_noise <= 304)] wl_noise_6 = wl_spire_noise[wl_spire_noise > 304] flux_noise_6 = flux_spire_noise[wl_spire_noise > 304] wl_noise = [wl_pacs_data[wl_pacs_data <= 190.31], wl_noise_5, wl_noise_6] flux_noise = [unc_pacs[wl_pacs_data <= 190.31], flux_noise_5, flux_noise_6] sig_num = 20 sigma_noise = [] for i in range(0, len(wl_noise)): sigma_dum = np.zeros([len(wl_noise[i])]) for iwl in range(0, len(wl_noise[i])): if iwl < sig_num / 2: sigma_dum[iwl] = np.std( np.hstack((flux_noise[i][0:sig_num / 2], flux_noise[i][0:sig_num / 2 - iwl]))) elif len(wl_noise[i]) - iwl < sig_num / 2: sigma_dum[iwl] = np.std( np.hstack( (flux_noise[i][iwl:], flux_noise[i][len(wl_noise[i]) - sig_num / 2:]))) else: sigma_dum[iwl] = np.std(flux_noise[i][iwl - sig_num / 2:iwl + sig_num / 2]) sigma_noise = np.hstack((sigma_noise, sigma_dum)) sigma_noise = np.array(sigma_noise) # Read in the photometry data phot = np.genfromtxt(indir + 'bhr71.txt', dtype=None, skip_header=1, comments='%') wl_phot = [] flux_phot = [] flux_sig_phot = [] note = [] for i in range(0, len(phot)): wl_phot.append(phot[i][0]) flux_phot.append(phot[i][1]) flux_sig_phot.append(phot[i][2]) note.append(phot[i][4]) wl_phot = np.array(wl_phot) # Convert the unit from Jy to erg cm-2 Hz-1 flux_phot = np.array(flux_phot) * 1e-23 flux_sig_phot = np.array(flux_sig_phot) * 1e-23 # Print the observed L_bol wl_tot = np.hstack((wl_irs, wl_obs, wl_phot)) flux_tot = np.hstack((flux_irs, flux_obs, flux_phot)) flux_tot = flux_tot[np.argsort(wl_tot)] wl_tot = wl_tot[np.argsort(wl_tot)] l_bol_obs = l_bol(wl_tot, flux_tot * 1e23) # Open the model m = ModelOutput(filename) if wl_aper == None: wl_aper = [ 3.6, 4.5, 5.8, 8.0, 10, 16, 20, 24, 35, 70, 100, 160, 250, 350, 500, 850 ] # Create the plot mag = 1.5 fig = plt.figure(figsize=(8 * mag, 6 * mag)) ax_sed = fig.add_subplot(1, 1, 1) # Plot the observed SED # plot the observed spectra pacs, = ax_sed.plot(np.log10(wl_pacs), np.log10(c / (wl_pacs * 1e-4) * flux_pacs), '-', color='DimGray', linewidth=1.5 * mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_spire), np.log10(c / (wl_spire * 1e-4) * flux_spire), '-', color='DimGray', linewidth=1.5 * mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_irs), np.log10(c / (wl_irs * 1e-4) * flux_irs), '-', color='DimGray', linewidth=1.5 * mag, alpha=0.7) # ax_sed.text(0.75,0.9,r'$\rm{L_{bol}= %5.2f L_{\odot}}$' % l_bol_obs,fontsize=mag*16,transform=ax_sed.transAxes) # plot the observed photometry data photometry, = ax_sed.plot(np.log10(wl_phot), np.log10(c / (wl_phot * 1e-4) * flux_phot), 's', mfc='DimGray', mec='k', markersize=8) ax_sed.errorbar(np.log10(wl_phot),np.log10(c/(wl_phot*1e-4)*flux_phot),\ yerr=[np.log10(c/(wl_phot*1e-4)*flux_phot)-np.log10(c/(wl_phot*1e-4)*(flux_phot-flux_sig_phot)),\ np.log10(c/(wl_phot*1e-4)*(flux_phot+flux_sig_phot))-np.log10(c/(wl_phot*1e-4)*flux_phot)],\ fmt='s',mfc='DimGray',mec='k',markersize=8) # Extract the SED for the smallest inclination and largest aperture, and # scale to 300pc. In Python, negative indices can be used for lists and # arrays, and indicate the position from the end. So to get the SED in the # largest aperture, we set aperture=-1. # aperture group is aranged from smallest to infinite sed_inf = m.get_sed(group=0, inclination=0, aperture=-1, distance=dstar * pc) # l_bol_sim = l_bol(sed_inf.wav, sed_inf.val/(c/sed_inf.wav*1e4)*1e23) # print sed.wav, sed.val # print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # plot the simulated SED # sim, = ax_sed.plot(np.log10(sed_inf.wav), np.log10(sed_inf.val), '-', color='k', linewidth=1.5*mag, alpha=0.7) # get flux at different apertures flux_aper = np.empty_like(wl_aper) unc_aper = np.empty_like(wl_aper) for i in range(0, len(wl_aper)): sed_dum = m.get_sed(group=i + 1, inclination=0, aperture=-1, distance=dstar * pc) # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((sed_dum.wav < wl_aper[i] * (1 + 1. / res)) & (sed_dum.wav > wl_aper[i] * (1 - 1. / res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(sed_dum.val[ind]) else: f = interp1d(sed_dum.wav, sed_dum.val) flux_aper[i] = f(wl_aper[i]) # perform the same procedure of flux extraction of aperture flux with observed spectra wl_aper = np.array(wl_aper) obs_aper_wl = wl_aper[(wl_aper >= min(wl_irs)) & (wl_aper <= max(wl_spire))] obs_aper_sed = np.empty_like(obs_aper_wl) sed_tot = c / (wl_tot * 1e-4) * flux_tot # wl_tot and flux_tot are already hstacked and sorted by wavelength for i in range(0, len(obs_aper_wl)): if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i] * (1 + 1. / res)) & (wl_tot > obs_aper_wl[i] * (1 - 1. / res))) if len(ind[0]) != 0: obs_aper_sed[i] = np.mean(sed_tot[ind]) else: f = interp1d(wl_tot, sed_tot) obs_aper_sed[i] = f(wl_aper[i]) aper_obs, = ax_sed.plot(np.log10(obs_aper_wl), np.log10(obs_aper_sed), 's-', mec='None', mfc='r', color='r', markersize=10, linewidth=1.5) # # interpolate the uncertainty (maybe not the best way to do this) # print sed_dum.unc # f = interp1d(sed_dum.wav, sed_dum.unc) # unc_aper[i] = f(wl_aper[i]) # if wl_aper[i] == 9.7: # ax_sed.plot(np.log10(sed_dum.wav), np.log10(sed_dum.val), '-', linewidth=1.5*mag) # print l_bol(sed_dum.wav, sed_dum.val/(c/sed_dum.wav*1e4)*1e23) aper, = ax_sed.plot(np.log10(wl_aper), np.log10(flux_aper), 'o-', mec='Blue', mfc='None', color='b', markersize=12, markeredgewidth=3, linewidth=1.7) # calculate the bolometric luminosity of the aperture l_bol_sim = l_bol(wl_aper, flux_aper / (c / np.array(wl_aper) * 1e4) * 1e23) print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # print out the sed into ascii file for reading in later if save == True: # unapertured SED foo = open(outdir + print_name + '_sed_inf.txt', 'w') foo.write('%12s \t %12s \n' % ('wave', 'vSv')) for i in range(0, len(sed_inf.wav)): foo.write('%12g \t %12g \n' % (sed_inf.wav[i], sed_inf.val[i])) foo.close() # SED with convolution of aperture sizes foo = open(outdir + print_name + '_sed_w_aperture.txt', 'w') foo.write('%12s \t %12s \n' % ('wave', 'vSv')) for i in range(0, len(wl_aper)): foo.write('%12g \t %12g \n' % (wl_aper[i], flux_aper[i])) foo.close() # Read in and plot the simulated SED produced by RADMC-3D using the same parameters # [wl,fit] = np.genfromtxt(indir+'hyperion/radmc_comparison/spectrum.out',dtype='float',skip_header=3).T # l_bol_radmc = l_bol(wl,fit*1e23/dstar**2) # radmc, = ax_sed.plot(np.log10(wl),np.log10(c/(wl*1e-4)*fit/dstar**2),'-',color='DimGray', linewidth=1.5*mag, alpha=0.5) # print the L bol of the simulated SED (both Hyperion and RADMC-3D) # lg_sim = ax_sed.legend([sim,radmc],[r'$\rm{L_{bol,sim}=%5.2f~L_{\odot},~L_{center}=9.18~L_{\odot}}$' % l_bol_sim, \ # r'$\rm{L_{bol,radmc3d}=%5.2f~L_{\odot},~L_{center}=9.18~L_{\odot}}$' % l_bol_radmc],\ # loc='lower right',fontsize=mag*16) # read the input central luminosity by reading in the source information from output file dum = Model() dum.use_sources(filename) L_cen = dum.sources[0].luminosity / lsun # lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f~L_{\odot},~L_{center}=%5.2f~L_{\odot}}$' % (l_bol_sim, L_cen)], \ # loc='lower right',fontsize=mag*16) # lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f~L_{\odot},~L_{bol,obs}=%5.2f~L_{\odot}}$' % (l_bol_sim, l_bol_obs)], \ # loc='lower right',fontsize=mag*16) # text = ax_sed.text(0.2 ,0.05 ,r'$\rm{L_{bol,simulation}=%5.2f~L_{\odot},~L_{bol,observation}=%5.2f~L_{\odot}}$' % (l_bol_sim, l_bol_obs),fontsize=mag*16,transform=ax_sed.transAxes) # text.set_bbox(dict( edgecolor='k',facecolor='None',alpha=0.3,pad=10.0)) # plot setting ax_sed.set_xlabel(r'$\rm{log\,\lambda\,({\mu}m)}$', fontsize=mag * 20) ax_sed.set_ylabel(r'$\rm{log\,\nu S_{\nu}\,(erg\,cm^{-2}\,s^{-1})}$', fontsize=mag * 20) [ ax_sed.spines[axis].set_linewidth(1.5 * mag) for axis in ['top', 'bottom', 'left', 'right'] ] ax_sed.minorticks_on() ax_sed.tick_params('both', labelsize=mag * 18, width=1.5 * mag, which='major', pad=15, length=5 * mag) ax_sed.tick_params('both', labelsize=mag * 18, width=1.5 * mag, which='minor', pad=15, length=2.5 * mag) ax_sed.set_ylim([-13, -7.5]) ax_sed.set_xlim([0, 3]) # lg_data = ax_sed.legend([sim, aper], [r'$\rm{w/o~aperture}$', r'$\rm{w/~aperture}$'], \ # loc='upper left', fontsize=14*mag, framealpha=0.3, numpoints=1) lg_data = ax_sed.legend([irs, photometry, aper, aper_obs],\ [r'$\rm{observation}$',\ r'$\rm{photometry}$',r'$\rm{F_{aper,sim}}$',r'$\rm{F_{aper,obs}}$'],\ loc='upper left',fontsize=14*mag,numpoints=1,framealpha=0.3) # plt.gca().add_artist(lg_sim) # Write out the plot fig.savefig(outdir + print_name + '_sed.pdf', format='pdf', dpi=300, bbox_inches='tight') fig.clf() # Package for matching the colorbar from mpl_toolkits.axes_grid1 import make_axes_locatable # Extract the image for the first inclination, and scale to 300pc. We # have to specify group=1 as there is no image in group 0. image = m.get_image(group=len(wl_aper) + 1, inclination=0, distance=dstar * pc, units='MJy/sr') # image = m.get_image(group=14, inclination=0, distance=dstar * pc, units='MJy/sr') # Open figure and create axes # fig = plt.figure(figsize=(8, 8)) fig, axarr = plt.subplots(3, 3, sharex='col', sharey='row', figsize=(13.5, 12)) # Pre-set maximum for colorscales VMAX = {} # VMAX[3.6] = 10. # VMAX[24] = 100. # VMAX[160] = 2000. # VMAX[500] = 2000. VMAX[100] = 10. VMAX[250] = 100. VMAX[500] = 2000. VMAX[1000] = 2000. # We will now show four sub-plots, each one for a different wavelength # for i, wav in enumerate([3.6, 24, 160, 500]): # for i, wav in enumerate([100, 250, 500, 1000]): # for i, wav in enumerate([4.5, 9.7, 24, 40, 70, 100, 250, 500, 1000]): for i, wav in enumerate([3.6, 8.0, 9.7, 24, 40, 100, 250, 500, 1000]): # ax = fig.add_subplot(3, 3, i + 1) ax = axarr[i / 3, i % 3] # Find the closest wavelength iwav = np.argmin(np.abs(wav - image.wav)) # Calculate the image width in arcseconds given the distance used above rmax = max(m.get_quantities().r_wall) w = np.degrees(rmax / image.distance) * 3600. # w = np.degrees((1.5 * pc) / image.distance) * 60. # Image in the unit of MJy/sr # Change it into erg/s/cm2/Hz/sr factor = 1e-23 * 1e6 # avoid zero in log val = image.val[:, :, iwav] * factor + 1e-30 # This is the command to show the image. The parameters vmin and vmax are # the min and max levels for the colorscale (remove for default values). im = ax.imshow(np.log10(val), vmin=-22, vmax=-12, cmap=plt.cm.jet, origin='lower', extent=[-w, w, -w, w], aspect=1) # Colorbar setting # create an axes on the right side of ax. The width of cax will be 5% # of ax and the padding between cax and ax will be fixed at 0.05 inch. if (i + 1) % 3 == 0: divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cb = fig.colorbar(im, cax=cax) cb.solids.set_edgecolor("face") cb.ax.minorticks_on() cb.ax.set_ylabel( r'$\rm{log(I_{\nu})\,[erg\,s^{-2}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$', fontsize=12) cb_obj = plt.getp(cb.ax.axes, 'yticklabels') plt.setp(cb_obj, fontsize=12) if (i + 1) == 7: # Finalize the plot ax.set_xlabel('RA Offset (arcsec)', fontsize=14) ax.set_ylabel('Dec Offset (arcsec)', fontsize=14) ax.tick_params(axis='both', which='major', labelsize=16) ax.set_adjustable('box-forced') ax.text(0.7, 0.88, str(wav) + r'$\rm{\,\mu m}$', fontsize=18, color='white', weight='bold', transform=ax.transAxes) fig.subplots_adjust(hspace=0, wspace=-0.2) # Adjust the spaces between the subplots # plt.tight_layout() fig.savefig(outdir + print_name + '_cube_plot.png', format='png', dpi=300, bbox_inches='tight') fig.clf()
def extract_hyperion(filename, indir=None, outdir=None, dstar=200.0, aperture=None, save=True, filter_func=False, plot_all=False, clean=False, exclude_wl=[], log=True, image=True, obj='BHR71', print_data_w_aper=False, mag=1.5): """ filename: The path to Hyperion output file indir: The path to the directory which contains observations data outdir: The path to the directory for storing extracted plots and ASCII files """ def l_bol(wl, fv, dstar): import numpy as np import astropy.constants as const # wavelength unit: um # Flux density unit: Jy # constants setup # c = const.c.cgs.value pc = const.pc.cgs.value PI = np.pi SL = const.L_sun.cgs.value # Convert the unit from Jy to erg s-1 cm-2 Hz-1 fv = np.array(fv) * 1e-23 freq = c / (1e-4 * np.array(wl)) diff_dum = freq[1:] - freq[0:-1] freq_interpol = np.hstack( (freq[0:-1] + diff_dum / 2.0, freq[0:-1] + diff_dum / 2.0, freq[0], freq[-1])) freq_interpol = freq_interpol[np.argsort(freq_interpol)[::-1]] fv_interpol = np.empty(len(freq_interpol)) # calculate the histogram style of spectrum # for i in range(0, len(fv)): if i == 0: fv_interpol[i] = fv[i] else: fv_interpol[2 * i - 1] = fv[i - 1] fv_interpol[2 * i] = fv[i] fv_interpol[-1] = fv[-1] dv = freq_interpol[0:-1] - freq_interpol[1:] dv = np.delete(dv, np.where(dv == 0)) fv = fv[np.argsort(freq)] freq = freq[np.argsort(freq)] return (np.trapz(fv, freq) * 4. * PI * (dstar * pc)**2) / SL # function for properly calculating uncertainty of spectrophotometry value def unc_spectrophoto(wl, unc, trans): # adopting smiliar procedure as Trapezoidal rule # (b-a) * [ f(a) + f(b) ] / 2 # return (np.sum(trans[:-1]**2 * unc[:-1]**2 * (wl[1:] - wl[:-1])**2) / np.trapz(trans, x=wl)**2)**0.5 # to avoid X server error import matplotlib as mpl mpl.use('Agg') # import matplotlib.pyplot as plt import numpy as np import os from hyperion.model import ModelOutput, Model from scipy.interpolate import interp1d from hyperion.util.constants import pc, c, lsun, au from astropy.io import ascii import sys from phot_filter import phot_filter from get_obs import get_obs # Open the model m = ModelOutput(filename) # Read in the observation data and calculate the noise & variance if indir == None: indir = raw_input('Path to the observation data: ') if outdir == None: outdir = raw_input('Path for the output: ') # assign the file name from the input file print_name = os.path.splitext(os.path.basename(filename))[0] # use a canned function to extract observational data obs_data = get_obs(indir, obj=obj) # unit in um, Jy wl_tot, flux_tot, unc_tot = obs_data['spec'] flux_tot = flux_tot * 1e-23 # convert unit from Jy to erg s-1 cm-2 Hz-1 unc_tot = unc_tot * 1e-23 l_bol_obs = l_bol(wl_tot, flux_tot * 1e23, dstar) wl_phot, flux_phot, flux_sig_phot = obs_data['phot'] flux_phot = flux_phot * 1e-23 # convert unit from Jy to erg s-1 cm-2 Hz-1 flux_sig_phot = flux_sig_phot * 1e-23 if aperture == None: aperture = {'wave': [3.6, 4.5, 5.8, 8.0, 8.5, 9, 9.7, 10, 10.5, 11, 16, 20, 24, 30, 70, 100, 160, 250, 350, 500, 850],\ 'aperture': [7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 7.2, 20.4, 20.4, 20.4, 20.4, 24.5, 24.5, 24.5, 24.5, 24.5, 24.5, 24.5]} # assign wl_aper and aper from dictionary of aperture wl_aper = aperture['wave'] aper = aperture['aperture'] # create the non-repetitive aperture list and index array aper_reduced = sorted(list(set(aper))) index_reduced = np.arange( 1, len(aper_reduced) + 1) # '+1': the zeroth slice corresponds to infinite aperture # Create the plot fig = plt.figure(figsize=(8 * mag, 6 * mag)) ax_sed = fig.add_subplot(1, 1, 1) # Plot the observed SED if not clean: color_seq = ['Green', 'Red', 'Black'] else: color_seq = ['DimGray', 'DimGray', 'DimGray'] # plot the observations # plot in log scale if log: pacs, = ax_sed.plot( np.log10(wl_tot[(wl_tot > 40) & (wl_tot < 190.31)]), np.log10(c / (wl_tot[(wl_tot > 40) & (wl_tot < 190.31)] * 1e-4) * flux_tot[(wl_tot > 40) & (wl_tot < 190.31)]), '-', color=color_seq[0], linewidth=1.5 * mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]), np.log10(c / (wl_tot[wl_tot > 194] * 1e-4) * flux_tot[wl_tot > 194]), '-', color=color_seq[1], linewidth=1.5 * mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]), np.log10(c / (wl_tot[wl_tot < 40] * 1e-4) * flux_tot[wl_tot < 40]), '-', color=color_seq[2], linewidth=1.5 * mag, alpha=0.7) photometry, = ax_sed.plot(np.log10(wl_phot), np.log10(c / (wl_phot * 1e-4) * flux_phot), 's', mfc='DimGray', mec='k', markersize=8) # plot the observed photometry data ax_sed.errorbar( np.log10(wl_phot), np.log10(c / (wl_phot * 1e-4) * flux_phot), yerr=[ np.log10(c / (wl_phot * 1e-4) * flux_phot) - np.log10(c / (wl_phot * 1e-4) * (flux_phot - flux_sig_phot)), np.log10(c / (wl_phot * 1e-4) * (flux_phot + flux_sig_phot)) - np.log10(c / (wl_phot * 1e-4) * flux_phot) ], fmt='s', mfc='DimGray', mec='k', markersize=8) # plot in normal scale else: pacs, = ax_sed.plot( np.log10(wl_tot[(wl_tot > 40) & (wl_tot < 190.31)]), c / (wl_tot[(wl_tot > 40) & (wl_tot < 190.31)] * 1e-4) * flux_tot[(wl_tot > 40) & (wl_tot < 190.31)], '-', color=color_seq[0], linewidth=1.5 * mag, alpha=0.7) spire, = ax_sed.plot(np.log10(wl_tot[wl_tot > 194]), c / (wl_tot[wl_tot > 194] * 1e-4) * flux_tot[wl_tot > 194], '-', color=color_seq[1], linewidth=1.5 * mag, alpha=0.7) irs, = ax_sed.plot(np.log10(wl_tot[wl_tot < 40]), c / (wl_tot[wl_tot < 40] * 1e-4) * flux_tot[wl_tot < 40], '-', color=color_seq[2], linewidth=1.5 * mag, alpha=0.7) photometry, = ax_sed.plot(wl_phot, c / (wl_phot * 1e-4) * flux_phot, 's', mfc='DimGray', mec='k', markersize=8) # plot the observed photometry data ax_sed.errorbar( np.log10(wl_phot), c / (wl_phot * 1e-4) * flux_phot, yerr=[ c / (wl_phot * 1e-4) * flux_phot - c / (wl_phot * 1e-4) * (flux_phot - flux_sig_phot), c / (wl_phot * 1e-4) * (flux_phot + flux_sig_phot) - c / (wl_phot * 1e-4) * flux_phot ], fmt='s', mfc='DimGray', mec='k', markersize=8) # if keyword 'clean' is not set, print L_bol derived from observations at upper right corner. if not clean: ax_sed.text(0.75, 0.9, r'$\rm{L_{bol}= %5.2f L_{\odot}}$' % l_bol_obs, fontsize=mag * 16, transform=ax_sed.transAxes) # getting SED with infinite aperture sed_inf = m.get_sed(group=0, inclination=0, aperture=-1, distance=dstar * pc, uncertainties=True) # plot the simulated SED with infinite aperture if clean == False: sim, = ax_sed.plot(np.log10(sed_inf.wav), np.log10(sed_inf.val), '-', color='GoldenRod', linewidth=0.5 * mag) ax_sed.fill_between(np.log10(sed_inf.wav), np.log10(sed_inf.val - sed_inf.unc), np.log10(sed_inf.val + sed_inf.unc), color='GoldenRod', alpha=0.5) ####################################### # get fluxes with different apertures # ####################################### # this is non-reduced wavelength array because this is for printing out fluxes at all channels specified by users flux_aper = np.zeros_like(wl_aper, dtype=float) unc_aper = np.zeros_like(wl_aper, dtype=float) a = np.zeros_like(wl_aper) + 1 color_list = plt.cm.jet(np.linspace(0, 1, len(wl_aper) + 1)) for i in range(0, len(wl_aper)): # occasionally users might want not to report some wavelength channels if wl_aper[i] in exclude_wl: continue # getting simulated SED from Hyperion output. (have to match with the reduced index) sed_dum = m.get_sed( group=index_reduced[np.where(aper_reduced == aper[i])], inclination=0, aperture=-1, distance=dstar * pc, uncertainties=True) # plot the whole SED from this aperture (optional) if plot_all == True: ax_sed.plot(np.log10(sed_dum.wav), np.log10(sed_dum.val), '-', color=color_list[i]) ax_sed.fill_between(np.log10(sed_dum.wav), np.log10(sed_dum.val-sed_dum.unc), np.log10(sed_dum.val+sed_dum.unc),\ color=color_list[i], alpha=0.5) # Extracting spectrophotometry values from simulated SED # Not using the photometry filer function to extract spectrophotometry values # sort by wavelength first. sort_wl = np.argsort(sed_dum.wav) val_sort = sed_dum.val[sort_wl] unc_sort = sed_dum.unc[sort_wl] wav_sort = sed_dum.wav[sort_wl] # Before doing that, convert vSv to F_lambda flux_dum = val_sort / wav_sort unc_dum = unc_sort / wav_sort # If no using filter function to extract the spectrophotometry, # then use the spectral resolution. if filter_func == False: # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((wav_sort < wl_aper[i] * (1 + 1. / res)) & (wav_sort > wl_aper[i] * (1 - 1. / res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(flux_dum[ind]) unc_aper[i] = np.mean(unc_dum[ind]) else: f = interp1d(wav_sort, flux_dum) f_unc = interp1d(wav_sort, unc_dum) flux_aper[i] = f(wl_aper[i]) unc_aper[i] = f_unc(wl_aper[i]) # Using photometry filter function to extract spectrophotometry values else: # apply the filter function # decide the filter name if wl_aper[i] == 70: fil_name = 'Herschel PACS 70um' elif wl_aper[i] == 100: fil_name = 'Herschel PACS 100um' elif wl_aper[i] == 160: fil_name = 'Herschel PACS 160um' elif wl_aper[i] == 250: fil_name = 'Herschel SPIRE 250um' elif wl_aper[i] == 350: fil_name = 'Herschel SPIRE 350um' elif wl_aper[i] == 500: fil_name = 'Herschel SPIRE 500um' elif wl_aper[i] == 3.6: fil_name = 'IRAC Channel 1' elif wl_aper[i] == 4.5: fil_name = 'IRAC Channel 2' elif wl_aper[i] == 5.8: fil_name = 'IRAC Channel 3' elif wl_aper[i] == 8.0: fil_name = 'IRAC Channel 4' elif wl_aper[i] == 24: fil_name = 'MIPS 24um' elif wl_aper[i] == 850: fil_name = 'SCUBA 850WB' else: fil_name = None if fil_name != None: filter_func = phot_filter(fil_name, indir) # Simulated SED should have enough wavelength coverage for applying photometry filters. f = interp1d(wav_sort, flux_dum) f_unc = interp1d(wav_sort, unc_dum) flux_aper[i] = np.trapz(f(filter_func['wave']/1e4)*\ filter_func['transmission'],x=filter_func['wave']/1e4 )/\ np.trapz(filter_func['transmission'], x=filter_func['wave']/1e4) # fix a bug unc_aper[i] = unc_spectrophoto( filter_func['wave'] / 1e4, f_unc(filter_func['wave'] / 1e4), filter_func['transmission']) else: # use a rectangle function the average the simulated SED # apply the spectral resolution if (wl_aper[i] < 50.) & (wl_aper[i] >= 5): res = 60. elif wl_aper[i] < 5: res = 10. else: res = 1000. ind = np.where((wav_sort < wl_aper[i] * (1 + 1. / res)) & (wav_sort > wl_aper[i] * (1 - 1. / res))) if len(ind[0]) != 0: flux_aper[i] = np.mean(flux_dum[ind]) unc_aper[i] = np.mean(unc_dum[ind]) else: f = interp1d(wav_sort, flux_dum) f_unc = interp1d(wav_sort, unc_dum) flux_aper[i] = f(wl_aper[i]) unc_aper[i] = f_unc(wl_aper[i]) # temperory step: solve issue of uncertainty greater than the value for i in range(len(wl_aper)): if unc_aper[i] >= flux_aper[i]: unc_aper[i] = flux_aper[i] - 1e-20 ########################### # Observations Extraction # ########################### # perform the same procedure of flux extraction of aperture flux with observed spectra # wl_aper = np.array(wl_aper, dtype=float) obs_aper_wl = wl_aper[(wl_aper >= min(wl_tot)) & (wl_aper <= max(wl_tot))] obs_aper_flux = np.zeros_like(obs_aper_wl) obs_aper_unc = np.zeros_like(obs_aper_wl) # have change the simulation part to work in F_lambda for fliter convolution # flux_tot and unc_tot have units of erg/s/cm2/Hz. Need to convert it to F_lambda (erg/s/cm2/um) fnu2fl = c / (wl_tot * 1e-4) / wl_tot # # wl_tot and flux_tot are already hstacked and sorted by wavelength for i in range(0, len(obs_aper_wl)): # sometime users want not report some wavelength channels if obs_aper_wl[i] in exclude_wl: continue if filter_func == False: # use a rectangle function the average the simulated SED # apply the spectral resolution if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i] * (1 + 1. / res)) & (wl_tot > obs_aper_wl[i] * (1 - 1. / res))) if len(ind[0]) != 0: obs_aper_flux[i] = np.mean(fnu2fl[ind] * flux_tot[ind]) obs_aper_unc[i] = np.mean(fnu2fl[ind] * unc_tot[ind]) else: f = interp1d(wl_tot, fnu2fl * flux_tot) f_unc = interp1d(wl_tot, fnu2fl * unc_tot) obs_aper_flux[i] = f(obs_aper_wl[i]) obs_aper_unc[i] = f_unc(obs_aper_wl[i]) else: # apply the filter function # decide the filter name if obs_aper_wl[i] == 70: fil_name = 'Herschel PACS 70um' elif obs_aper_wl[i] == 100: fil_name = 'Herschel PACS 100um' elif obs_aper_wl[i] == 160: fil_name = 'Herschel PACS 160um' elif obs_aper_wl[i] == 250: fil_name = 'Herschel SPIRE 250um' elif obs_aper_wl[i] == 350: fil_name = 'Herschel SPIRE 350um' elif obs_aper_wl[i] == 500: fil_name = 'Herschel SPIRE 500um' elif obs_aper_wl[i] == 3.6: fil_name = 'IRAC Channel 1' elif obs_aper_wl[i] == 4.5: fil_name = 'IRAC Channel 2' elif obs_aper_wl[i] == 5.8: fil_name = 'IRAC Channel 3' elif obs_aper_wl[i] == 8.0: fil_name = 'IRAC Channel 4' elif obs_aper_wl[i] == 24: fil_name = 'MIPS 24um' elif obs_aper_wl[i] == 850: fil_name = 'SCUBA 850WB' # do not have SCUBA spectra else: fil_name = None if fil_name != None: filter_func = phot_filter(fil_name, indir) # Observed SED needs to be trimmed before applying photometry filters filter_func = filter_func[(filter_func['wave']/1e4 >= min(wl_tot))*\ ((filter_func['wave']/1e4 >= 54.8)+(filter_func['wave']/1e4 <= 36.0853))*\ ((filter_func['wave']/1e4 <= 95.05)+(filter_func['wave']/1e4 >=103))*\ ((filter_func['wave']/1e4 <= 190.31)+(filter_func['wave']/1e4 >= 195))*\ (filter_func['wave']/1e4 <= max(wl_tot))] f = interp1d(wl_tot, fnu2fl * flux_tot) f_unc = interp1d(wl_tot, fnu2fl * unc_tot) obs_aper_flux[i] = np.trapz(f(filter_func['wave']/1e4)*filter_func['transmission'], x=filter_func['wave']/1e4)/\ np.trapz(filter_func['transmission'], x=filter_func['wave']/1e4) obs_aper_unc[i] = unc_spectrophoto( filter_func['wave'] / 1e4, f_unc(filter_func['wave'] / 1e4), filter_func['transmission']) else: # use a rectangle function the average the simulated SED # apply the spectral resolution if (obs_aper_wl[i] < 50.) & (obs_aper_wl[i] >= 5): res = 60. elif obs_aper_wl[i] < 5: res = 10. else: res = 1000. ind = np.where((wl_tot < obs_aper_wl[i] * (1 + 1. / res)) & (wl_tot > obs_aper_wl[i] * (1 - 1. / res))) if len(ind[0]) != 0: obs_aper_flux[i] = np.mean(fnu2fl[ind] * flux_tot[ind]) obs_aper_unc[i] = np.mean(fnu2fl[ind] * unc_tot[ind]) else: f = interp1d(wl_tot, fnu2fl * flux_tot) f_unc = interp1d(wl_tot, fnu2fl * unc_tot) obs_aper_flux[i] = f(obs_aper_wl[i]) obs_aper_unc[i] = f_unc(obs_aper_wl[i]) # plot the aperture-extracted spectrophotometry fluxes from observed spectra and simulations # in log-scale if log: aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), np.log10(obs_aper_flux * obs_aper_wl ),\ yerr=[np.log10(obs_aper_flux*obs_aper_wl)-np.log10(obs_aper_flux*obs_aper_wl-obs_aper_unc*obs_aper_wl), np.log10(obs_aper_flux*obs_aper_wl+obs_aper_unc*obs_aper_wl)-np.log10(obs_aper_flux*obs_aper_wl)],\ fmt='s', mec='None', mfc='r', markersize=10, linewidth=1.5, ecolor='Red', elinewidth=3, capthick=3, barsabove=True) aper = ax_sed.errorbar(np.log10(wl_aper),np.log10(flux_aper*wl_aper),\ yerr=[np.log10(flux_aper*wl_aper)-np.log10(flux_aper*wl_aper-unc_aper*wl_aper), np.log10(flux_aper*wl_aper+unc_aper*wl_aper)-np.log10(flux_aper*wl_aper)],\ fmt='o', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=2.5, linewidth=1.7, ecolor='Blue', elinewidth=3, barsabove=True) ax_sed.set_ylim([-14, -7]) ax_sed.set_xlim([0, 3.2]) # in normal scale (normal in y-axis) else: aper_obs = ax_sed.errorbar(np.log10(obs_aper_wl), obs_aper_flux*obs_aper_wl, yerr=obs_aper_unc*obs_aper_wl,\ fmt='s', mec='None', mfc='r', markersize=10, linewidth=1.5, ecolor='Red', elinewidth=3, capthick=3, barsabove=True) aper = ax_sed.errorbar(np.log10(wl_aper),flux_aper*wl_aper, yerr=unc_aper*wl_aper,\ fmt='o', mec='Blue', mfc='None', color='b',markersize=12, markeredgewidth=2.5, linewidth=1.7, ecolor='Blue', elinewidth=3, barsabove=True) ax_sed.set_xlim([0, 3.2]) # calculate the bolometric luminosity of the aperture # print flux_aper l_bol_sim = l_bol( wl_aper, flux_aper * wl_aper / (c / np.array(wl_aper) * 1e4) * 1e23, dstar) print 'Bolometric luminosity of simulated spectrum: %5.2f lsun' % l_bol_sim # print out the sed into ascii file for reading in later if save == True: # unapertured SED foo = open(outdir + print_name + '_sed_inf.txt', 'w') foo.write('%12s \t %12s \t %12s \n' % ('wave', 'vSv', 'sigma_vSv')) for i in range(0, len(sed_inf.wav)): foo.write('%12g \t %12g \t %12g \n' % (sed_inf.wav[i], sed_inf.val[i], sed_inf.unc[i])) foo.close() # SED with convolution of aperture sizes foo = open(outdir + print_name + '_sed_w_aperture.txt', 'w') foo.write('%12s \t %12s \t %12s \n' % ('wave', 'vSv', 'sigma_vSv')) for i in range(0, len(wl_aper)): foo.write('%12g \t %12g \t %12g \n' % (wl_aper[i], flux_aper[i] * wl_aper[i], unc_aper[i] * wl_aper[i])) foo.close() # print out the aperture-convolved fluxex from observations if print_data_w_aper: foo = open(outdir + print_name + '_obs_w_aperture.txt', 'w') foo.write('%12s \t %12s \t %12s \n' % ('wave', 'Jy', 'sigma_Jy')) for i in range(0, len(obs_aper_wl)): foo.write('%12g \t %12g \t %12g \n' % (obs_aper_wl[i], obs_aper_flux[i] * obs_aper_wl[i] / (c / obs_aper_wl[i] * 1e4) * 1e23, obs_aper_unc[i] * obs_aper_wl[i] / (c / obs_aper_wl[i] * 1e4) * 1e23)) foo.close() # read the input central luminosity by reading in the source information from output file dum = Model() dum.use_sources(filename) L_cen = dum.sources[0].luminosity / lsun # legend lg_data = ax_sed.legend([irs, photometry, aper, aper_obs], [ r'$\rm{observation}$', r'$\rm{photometry}$', r'$\rm{F_{aper,sim}}$', r'$\rm{F_{aper,obs}}$' ], loc='upper left', fontsize=14 * mag, numpoints=1, framealpha=0.3) if clean == False: lg_sim = ax_sed.legend([sim],[r'$\rm{L_{bol,sim}=%5.2f\,L_{\odot},\,L_{center}=%5.2f\,L_{\odot}}$' % (l_bol_sim, L_cen)], \ loc='lower right',fontsize=mag*16) plt.gca().add_artist(lg_data) # plot setting ax_sed.set_xlabel(r'$\rm{log\,\lambda\,[{\mu}m]}$', fontsize=mag * 20) ax_sed.set_ylabel(r'$\rm{log\,\nu S_{\nu}\,[erg\,s^{-1}\,cm^{-2}]}$', fontsize=mag * 20) [ ax_sed.spines[axis].set_linewidth(1.5 * mag) for axis in ['top', 'bottom', 'left', 'right'] ] ax_sed.minorticks_on() ax_sed.tick_params('both', labelsize=mag * 18, width=1.5 * mag, which='major', pad=15, length=5 * mag) ax_sed.tick_params('both', labelsize=mag * 18, width=1.5 * mag, which='minor', pad=15, length=2.5 * mag) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=mag * 18) for label in ax_sed.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax_sed.get_yticklabels(): label.set_fontproperties(ticks_font) # Write out the plot fig.savefig(outdir + print_name + '_sed.pdf', format='pdf', dpi=300, bbox_inches='tight') fig.clf() # option for suppress image plotting (for speed) if image: # Package for matching the colorbar from mpl_toolkits.axes_grid1 import make_axes_locatable, ImageGrid # Users may change the unit: mJy, Jy, MJy/sr, ergs/cm^2/s, ergs/cm^2/s/Hz # !!! image = m.get_image(group=len(aper_reduced) + 1, inclination=0, distance=dstar * pc, units='MJy/sr') # Open figure and create axes fig = plt.figure(figsize=(12, 12)) grid = ImageGrid(fig, 111, nrows_ncols=(3, 3), direction='row', add_all=True, label_mode='1', share_all=True, cbar_location='right', cbar_mode='single', cbar_size='3%', cbar_pad=0) for i, wav in enumerate([3.6, 8.0, 9.7, 24, 40, 100, 250, 500, 1000]): ax = grid[i] # Find the closest wavelength iwav = np.argmin(np.abs(wav - image.wav)) # Calculate the image width in arcseconds given the distance used above # get the max radius rmax = max(m.get_quantities().r_wall) w = np.degrees(rmax / image.distance) * 3600. # Image in the unit of MJy/sr # Change it into erg/s/cm2/Hz/sr factor = 1e-23 * 1e6 # avoid zero in log # flip the image, because the setup of inclination is upside down val = image.val[::-1, :, iwav] * factor + 1e-30 # This is the command to show the image. The parameters vmin and vmax are # the min and max levels for the colorscale (remove for default values). cmap = plt.cm.CMRmap im = ax.imshow(np.log10(val), vmin=-22, vmax=-12, cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1) ax.set_xlabel(r'$\rm{RA\,Offset\,[arcsec]}$', fontsize=14) ax.set_ylabel(r'$\rm{Dec\,Offset\,[arcsec]}$', fontsize=14) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=14) for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) # Colorbar setting cb = ax.cax.colorbar(im) cb.solids.set_edgecolor('face') cb.ax.minorticks_on() cb.ax.set_ylabel( r'$\rm{log(I_{\nu})\,[erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$', fontsize=18) cb_obj = plt.getp(cb.ax.axes, 'yticklabels') plt.setp(cb_obj, fontsize=18) ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=18) for label in cb.ax.get_yticklabels(): label.set_fontproperties(ticks_font) ax.tick_params(axis='both', which='major', labelsize=16) ax.text(0.7, 0.88, str(wav) + r'$\rm{\,\mu m}$', fontsize=16, color='white', transform=ax.transAxes) fig.savefig(outdir + print_name + '_image_gridplot.pdf', format='pdf', dpi=300, bbox_inches='tight') fig.clf()