Ejemplo n.º 1
0
def dump_data(pf,model):
    ad = pf.all_data()
    particle_fh2 = ad["gasfh2"]
    particle_fh1 = np.ones(len(particle_fh2))-particle_fh2
    particle_gas_mass = ad["gasmasses"]
    particle_star_mass = ad["starmasses"]
    particle_star_metallicity = ad["starmetals"]
    particle_stellar_formation_time = ad["starformationtime"]
    particle_sfr = ad['gassfr'].in_units('Msun/yr')

    #these are in try/excepts in case we're not dealing with gadget and yt 3.x
    try: grid_gas_mass = ad["gassmoothedmasses"]
    except: grid_gas_mass = -1
    try: grid_gas_metallicity = ad["gassmoothedmetals"]
    except: grid_gas_metallicity = -1
    try: grid_star_mass = ad["starsmoothedmasses"]
    except: grid_star_mass = -1

    #get tdust
    m = ModelOutput(model.outputfile+'.sed')
    oct = m.get_quantities()
    tdust_pf = oct.to_yt()
    tdust_ad = tdust_pf.all_data()
    tdust = tdust_ad[ ('gas', 'temperature')]


    try: outfile = cfg.model.PD_output_dir+"grid_physical_properties."+cfg.model.snapnum_str+'_galaxy'+cfg.model.galaxy_num_str+".npz"
    except:
        outfile = cfg.model.PD_output_dir+"grid_physical_properties."+cfg.model.snapnum_str+".npz"

    np.savez(outfile,particle_fh2=particle_fh2,particle_fh1 = particle_fh1,particle_gas_mass = particle_gas_mass,particle_star_mass = particle_star_mass,particle_star_metallicity = particle_star_metallicity,particle_stellar_formation_time = particle_stellar_formation_time,grid_gas_metallicity = grid_gas_metallicity,grid_gas_mass = grid_gas_mass,grid_star_mass = grid_star_mass,particle_sfr = particle_sfr,tdust = tdust)
Ejemplo n.º 2
0
def getRadialDensity(rtout, angle, plotdir):
    """
    """
    import numpy as np
    from hyperion.model import ModelOutput


    m = ModelOutput(rtout)
    q = m.get_quantities()
    r_wall = q.r_wall; theta_wall = q.t_wall; phi_wall = q.p_wall
    # get the cell coordinates
    rc = r_wall[0:len(r_wall)-1] + 0.5*(r_wall[1:len(r_wall)]-r_wall[0:len(r_wall)-1])
    thetac = theta_wall[0:len(theta_wall)-1] + \
             0.5*(theta_wall[1:len(theta_wall)]-theta_wall[0:len(theta_wall)-1])
    phic = phi_wall[0:len(phi_wall)-1] + \
           0.5*(phi_wall[1:len(phi_wall)]-phi_wall[0:len(phi_wall)-1])
    #
    rho = q['density'].array[0]

    # find the closest angle in the thetac grid
    ind = np.argsort(abs(thetac-angle*np.pi/180.))[0]

    return rc, rho[0,ind,:]
import numpy as np
from hyperion.model import ModelOutput
from hyperion.util.constants import au, lsun

RES = 256

mo = ModelOutput('bm2_eff_vor_temperature.rtout')

g = mo.get_quantities()

from scipy.spatial import cKDTree

sites = np.array([g.x, g.y, g.z]).transpose()

tree = cKDTree(sites)

ymin, ymax = 0 * au, 60 * au
zmin, zmax = 0 * au, 60 * au

y = np.linspace(ymin, ymax, RES)
z = np.linspace(zmin, zmax, RES)

Y, Z = np.meshgrid(y, z)
YR = Y.ravel()
ZR = Z.ravel()

for x_cut in [10 * au, 26.666667 * au]:

    XR = np.ones(YR.shape) * x_cut

    map_sites = np.array([XR, YR, ZR]).transpose()
Ejemplo n.º 4
0
        ax_top = grid[i].twiny()
        ax_top.set_xticks(r_ticks)
        ax_top.set_xticklabels([])
    grid[i].tick_params('both',labelsize=14)

for i in range(4,8):
    # get the H-band simulated image
    m = ModelOutput(filename[i-4])
    image = m.get_image(group=0, inclination=0, distance=178 * pc, units='MJy/sr')

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - 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

    if size != 'full':
        pix_e2c = (w-size/2.)/w * len(val[:,0])/2
        val = val[pix_e2c:-pix_e2c, pix_e2c:-pix_e2c]
        w = size/2.

    if convolve:
Ejemplo n.º 5
0
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()
Ejemplo n.º 6
0
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()
Ejemplo n.º 7
0
class Hyperion2LIME:
    """
    Class for importing Hyperion result to LIME
    IMPORTANT: LIME uses SI units, while Hyperion uses CGS units.
    """
    def __init__(self,
                 rtout,
                 velfile,
                 cs,
                 age,
                 omega,
                 rmin=0,
                 mmw=2.37,
                 g2d=100,
                 truncate=None,
                 debug=False,
                 load_full=True,
                 fix_tsc=True,
                 hybrid_tsc=False,
                 interpolate=False,
                 TSC_dir='',
                 tsc_outdir=''):
        self.rtout = rtout
        self.velfile = velfile
        if load_full:
            self.hyperion = ModelOutput(rtout)
            self.hy_grid = self.hyperion.get_quantities()
        self.rmin = rmin * 1e2  # rmin defined in LIME, which use SI unit
        self.mmw = mmw
        self.g2d = g2d
        self.cs = cs  # in km/s
        self.age = age  # in year
        # YLY update - add omega
        self.omega = omega
        self.r_inf = self.cs * 1e5 * self.age * yr  # in cm

        # option to truncate the sphere to be a cylinder
        # the value is given in au to specify the radius of the truncated cylinder viewed from the observer
        self.truncate = truncate

        # debug option: print out every call to getDensity, getVelocity and getAbundance
        self.debug = debug

        # option to use simple Trapezoid rule average for getting density, temperature, and velocity
        self.interpolate = interpolate

        self.tsc2d = getTSC(age,
                            cs,
                            omega,
                            velfile=velfile,
                            TSC_dir=TSC_dir,
                            outdir=tsc_outdir,
                            outname='tsc_regrid')

        # # velocity grid construction
        # if load_full:
        #     # ascii.read() fails for large file.  Use pandas instead
        #     self.tsc = pd.read_csv(velfile, skiprows=1, delim_whitespace=True, header=None)
        #     self.tsc.columns = ['lp', 'xr', 'theta', 'ro', 'ur', 'utheta', 'uphi']
        #
        #     self.xr = np.unique(self.tsc['xr'])  # reduce radius: x = r/(a*t) = r/r_inf
        #     self.xr_wall = np.hstack(([2*self.xr[0]-self.xr[1]],
        #                              (self.xr[:-1]+self.xr[1:])/2,
        #                              [2*self.xr[-1]-self.xr[-2]]))
        #     self.theta = np.unique(self.tsc['theta'])
        #     self.theta_wall = np.hstack(([2*self.theta[0]-self.theta[1]],
        #                             (self.theta[:-1]+self.theta[1:])/2,
        #                             [2*self.theta[-1]-self.theta[-2]]))
        #     self.nxr = len(self.xr)
        #     self.ntheta = len(self.theta)
        #
        #     # the output of TSC fortran binary is in mass density
        #     self.tsc_rho2d = 1/(4*np.pi*G*(self.age*yr)**2)/mh/mmw * np.array(self.tsc['ro']).reshape([self.nxr, self.ntheta])
        #
        #     # self.vr2d = np.array(self.tsc['ur']).reshape([self.nxr, self.ntheta]) * self.cs*1e5
        #     # self.vtheta2d = np.array(self.tsc['utheta']).reshape([self.nxr, self.ntheta]) * self.cs*1e5
        #     # self.vphi2d = np.array(self.tsc['uphi']).reshape([self.nxr, self.ntheta]) * self.cs*1e5
        #
        #     # in unit of km/s
        #     self.vr2d = np.reshape(self.tsc['ur'].to_numpy(), (self.nxr, self.ntheta)) * np.float64(self.cs)
        #     self.vtheta2d = np.reshape(self.tsc['utheta'].to_numpy(), (self.nxr, self.ntheta)) * np.float64(self.cs)
        #     self.vphi2d = np.reshape(self.tsc['uphi'].to_numpy(), (self.nxr, self.ntheta)) * np.float64(self.cs)
        #
        #     if fix_tsc:
        #         # fix the discontinuity in v_r
        #         # vr = vr + offset * log(xr)/log(xr_break)  for xr >= xr_break
        #         for i in range(self.ntheta):
        #             dvr = abs((self.vr2d[1:,i] - self.vr2d[:-1,i])/self.vr2d[1:,i])
        #             break_pt = self.xr[1:][(dvr > 0.05) & (self.xr[1:] > 1e-3) & (self.xr[1:] < 1-2e-3)]
        #             if len(break_pt) > 0:
        #                 offset = self.vr2d[(self.xr < break_pt),i].max() - self.vr2d[(self.xr > break_pt),i].min()
        #                 self.vr2d[(self.xr >= break_pt),i] = self.vr2d[(self.xr >= break_pt),i] + offset*np.log10(self.xr[self.xr >= break_pt])/np.log10(break_pt)
        #         # YLY update - 091118
        #         # fix the discontinuity in v_phi
        #         for i in range(self.ntheta):
        #             dvr = abs((self.vphi2d[1:,i] - self.vphi2d[:-1,i])/self.vphi2d[1:,i])
        #             break_pt = self.xr[1:][(dvr > 0.1) & (self.xr[1:] > 1e-3) & (self.xr[1:] < 1-2e-3)]
        #             if len(break_pt) > 0:
        #                 offset = self.vphi2d[(self.xr < break_pt),i].min() - self.vphi2d[(self.xr > break_pt),i].max()
        #                 self.vphi2d[(self.xr >= break_pt),i] = self.vphi2d[(self.xr >= break_pt),i] + offset*np.log10(self.xr[self.xr >= break_pt])/np.log10(break_pt)
        #
        #     # hybrid TSC kinematics that switches to angular momentum conservation within the centrifugal radius
        #     if hybrid_tsc:
        #         from scipy.interpolate import interp1d
        #         for i in range(self.ntheta):
        #             rCR = self.omega**2 * G**3 * (0.975*(self.cs*1e5)**3/G*(self.age*3600*24*365))**3 * np.sin(self.theta[i])**4 / (16*(self.cs*1e5)**8)
        #             if rCR/self.r_inf >= self.xr.min():
        #                 f_vr = interp1d(self.xr, self.vr2d[:,i])
        #                 vr_rCR = f_vr(rCR/self.r_inf)
        #                 f_vphi = interp1d(self.xr, self.vphi2d[:,i])
        #                 vphi_rCR = f_vphi(rCR/self.r_inf)
        #
        #                 # radius in cylinderical coordinates
        #                 wCR = np.sin(self.theta[i]) * rCR
        #                 J = vphi_rCR * wCR
        #                 M = (vr_rCR**2 + vphi_rCR**2) * wCR / (2*G)
        #
        #                 w = self.xr*np.sin(self.theta[i])*self.r_inf
        #                 self.vr2d[(self.xr <= rCR/self.r_inf), i] = -( 2*G*M/w[self.xr <= rCR/self.r_inf] - J**2/(w[self.xr <= rCR/self.r_inf])**2 )**0.5
        #                 self.vphi2d[(self.xr <= rCR/self.r_inf), i] = J/(w[self.xr <= rCR/self.r_inf])
        #
        #     self.tsc2d = {'vr2d': self.vr2d, 'vtheta2d': self.vtheta2d, 'vphi2d': self.vphi2d}

    def Cart2Spherical(self, x, y, z, unit_convert=True):
        """
        if unit_convert, the inputs (x, y, z) are meter.
        The outputs are in cm.
        """
        if unit_convert:
            x, y, z = x * 1e2, y * 1e2, z * 1e2

        r_in = (x**2 + y**2 + z**2)**0.5
        if r_in != 0:
            t_in = np.arccos(z / r_in)
        else:
            t_in = 0

        # if x != 0:
        # p_in = np.sign(y)*np.arctan(y/x)  # the input phi is irrelevant in axisymmetric model
        # else:
        # p_in = np.sign(y)*np.pi/2
        p_in = np.arctan2(y, x)

        if r_in < self.rmin:
            r_in = self.rmin

        return (r_in, t_in, p_in)

    def Spherical2Cart(self, r, t, p):
        """
        This is only valid for axisymmetric model
        """
        x = r * np.sin(t) * np.cos(p)
        y = r * np.sin(t) * np.sin(p)
        z = r * np.cos(t)

        return (x, y, z)

    def Spherical2Cart_vector(self, coord_sph, v_sph):
        r, theta, phi = coord_sph
        vr, vt, vp = v_sph

        transform = np.matrix([[
            np.sin(theta) * np.cos(phi),
            np.cos(theta) * np.cos(phi), -np.sin(phi)
        ],
                               [
                                   np.sin(theta) * np.sin(phi),
                                   np.cos(theta) * np.sin(phi),
                                   np.cos(phi)
                               ], [np.cos(theta), -np.sin(theta), 0]])
        v_cart = transform.dot(np.array([vr, vt, vp]))

        return list(map(float, np.asarray(v_cart).flatten()))

    def locateCell(self, coord, wall_grid):
        """
        return the indice of cell at given coordinates
        """
        r, t, p = coord
        r_wall, t_wall, p_wall = wall_grid

        r_ind = min(np.argsort(abs(r_wall - r))[:2])
        t_ind = min(np.argsort(abs(t_wall - t))[:2])
        p_ind = min(np.argsort(abs(p_wall - p))[:2])

        return (r_ind, t_ind, p_ind)

    # def interpolateCell(self, coord, cube, wall_grid):
    #     """
    #     return the interpolated value at given data cube at given coordinates
    #     """
    #     r, t, p = coord
    #     r_wall, t_wall, p_wall = wall_grid
    #
    #     # the cell center
    #     r_ind = min(np.argsort(abs(r_wall-r))[:2])
    #     t_ind = min(np.argsort(abs(t_wall-t))[:2])
    #     p_ind = min(np.argsort(abs(p_wall-p))[:2])
    #
    #     # simple Trapezoid rule
    #     val_dum = 0
    #     for ri in r_ind:
    #         for ti in t_ind:
    #             for pi in p_ind:
    #                 val_dum += cube[ri, ti, pi]
    #     val = val_dum/8.0
    #
    #     return val

    def locateCell2d(self, coord, wall_grid):
        """
        return the indice of cell at given coordinates
        """
        r, t = coord
        r_wall, t_wall = wall_grid

        r_ind = min(np.argsort(abs(r_wall - r))[:2])
        t_ind = min(np.argsort(abs(t_wall - t))[:2])

        return (r_ind, t_ind)

    # def interpolateCell2d(self, coord, cube, wall_grid):
    #     """
    #     return the interpolated value at given data cube at given coordinates
    #     """
    #     r, t= coord
    #     r_wall, t_wall = wall_grid
    #
    #     r_ind = np.argsort(abs(r_wall-r))[:2]
    #     t_ind = np.argsort(abs(t_wall-t))[:2]
    #
    #     # simple Trapezoid rule
    #     val = (cube[r_ind[0], t_ind[0]] + cube[r_ind[0], t_ind[1]] +
    #            cube[r_ind[1], t_ind[0]] + cube[r_ind[1], t_ind[1]])/4.0
    #
    #     return val

    def getDensity(self, x, y, z, version='gridding', theta_cav=None):

        (r_in, t_in, p_in) = self.Cart2Spherical(x, y, z)
        if self.truncate != None:
            if (y**2 + z**2)**0.5 > self.truncate * au_si:
                return 0.0

        if version == 'hyperion':
            r_wall = self.hy_grid.r_wall
            t_wall = self.hy_grid.t_wall
            p_wall = self.hy_grid.p_wall
            self.rho = self.hy_grid.quantities['density'][0].T

            if not self.interpolate:
                indice = self.locateCell((r_in, t_in, p_in),
                                         (r_wall, t_wall, p_wall))
                rho = self.rho[indice]
            else:
                rho = self.interpolateCell((r_in, t_in, p_in), self.rho,
                                           (r_wall, t_wall, p_wall))

            # LIME needs molecule number density per cubic meter
            # if self.debug:
            #     foo = open('density.log', 'a')
            #     foo.write('%e \t %e \t %e \t %e\n' % (x,y,z,float(self.rho[indice])*self.g2d/mh/self.mmw*1e6))
            #     foo.close()

            return float(rho) * self.g2d / mh / self.mmw * 1e6

        elif version == 'gridding':
            # check for cavity
            # determine whether the cell is in the cavity
            # if (theta_cav != None) and (theta_cav != 0):
            #     # using R = 10000 AU as the reference point
            #     c0 = (10000.*au_cgs)**(-0.5)*\
            #          np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
            #
            #     # related coordinates
            #     w = abs(r_in*np.cos(np.pi/2 - t_in))
            #     _z = r_in*np.sin(np.pi/2 - t_in)
            #
            #     # condition for open cavity
            #     z_cav = c0*abs(w)**1.5
            #     cav_con = abs(_z) > abs(z_cav)
            #
            #     if cav_con:
            #         # this is still wrong, because in the "correct" model setup.  The cavity does not have zero density.
            #         rho = 0.0
            #         return float(rho)

            # isothermal solution
            if r_in > self.r_inf:
                rho = (self.cs * 1e5)**2 / (2 * np.pi * G *
                                            (r_in)**2) / mh / self.mmw * 1e6
            # TSC solution
            else:
                if not self.interpolate:
                    ind = self.locateCell2d(
                        (r_in, t_in), (self.tsc2d['xr_wall'] * self.r_inf,
                                       self.tsc2d['theta_wall']))
                    rho = self.tsc2d['rho2d'][
                        ind] * 1e6  # has been divided by "mh" and "mmw"
                else:
                    rho = self.interpolateCell2d(
                        (r_in, t_in), self.tsc2d['rho2d'],
                        (self.tsc2d['xr_wall'] * self.r_inf,
                         self.tsc2d['theta_wall'])) * 1e6

            return float(rho)

    def getTemperature(self, x, y, z, external_heating=False, r_break=None):
        r_wall = self.hy_grid.r_wall
        t_wall = self.hy_grid.t_wall
        p_wall = self.hy_grid.p_wall
        self.temp = self.hy_grid.quantities['temperature'][0].T

        if self.truncate != None:
            if (y**2 + z**2)**0.5 > self.truncate * au_si:
                return 0.0

        (r_in, t_in, p_in) = self.Cart2Spherical(x, y, z)

        if not self.interpolate:
            indice = self.locateCell((r_in, t_in, p_in),
                                     (r_wall, t_wall, p_wall))
            temp = self.temp[indice]
        else:
            temp = self.interpolateCell((r_in, t_in, p_in), cube,
                                        (r_wall, t_wall, p_wall))

        # if external_heating:
        #     # get the temperature at the outermost radius
        #     indice_lowT = self.locateCell(((r_wall[-1]+r_wall[-2])/2, t_in, p_in), (r_wall, t_wall, p_wall))
        #     lowT = self.temp[indice_lowT]
        #     # the inner radius where the temperature correction starts to apply
        #     # User-defined value
        #     # r_break = 13000*au_cgs
        #     # r_break = 2600*au_cgs
        #     r_break = r_break*au_cgs
        #
        #     if (lowT < 15) and (r_in >= r_break):
        #         dT = (r_in - r_break)*(15-lowT)/((r_wall[-1]+r_wall[-2])/2 - r_break)
        #         if float(temp) + float(dT) >= 0.0:
        #             return float(temp) + float(dT)
        #         else:
        #             return 0.0

        # test for a different approach of external heating
        if external_heating:
            from scipy.interpolate import interp1d
            # get the temperature at the outermost radius
            rc = (r_wall[1:] + r_wall[:-1]) / 2
            indice_Tmin = self.locateCell((rc.max(), t_in, p_in),
                                          (r_wall, t_wall, p_wall))
            Tmin = self.temp[indice_Tmin]

            # set an inner radius that the external heating will apply for skipping the disk, where temperature may be lower than 10 K
            # take two times the centrifugal radius
            rCen = self.omega**2 * G**3 * (0.975 * (self.cs * 1e5)**3 / G *
                                           (self.age * yr))**3 / (
                                               16 * (self.cs * 1e5)**8)
            r_ext_min = 2 * rCen

            if (temp < 10.0) and (r_in >= r_ext_min) and (Tmin < 15.0):
                rc = (r_wall[1:] + r_wall[:-1]) / 2
                f_temp = interp1d(
                    self.temp[(rc > r_ext_min), indice_Tmin[1],
                              indice_Tmin[2]], rc[rc > r_ext_min])
                r10K = f_temp(10.0)
                dT = (r_in - r10K) / (rc.max() - r10K) * (15.0 - Tmin)
                temp = float(temp) + float(dT)

        if float(temp) >= 0.0:
            return float(temp)
        else:
            return 0.0

    def getVelocity(self,
                    x,
                    y,
                    z,
                    sph=False,
                    unit_convert=True,
                    vr_factor=1.0,
                    vr_offset=0.0):
        """
        cs: effecitve sound speed in km/s;
        age: the time since the collapse began in year.
        vr_offset: in km/s
        """

        (r_in, t_in, p_in) = self.Cart2Spherical(x,
                                                 y,
                                                 z,
                                                 unit_convert=unit_convert)

        if self.truncate != None:
            if (y**2 + z**2)**0.5 > self.truncate * au_si:
                v_out = [0.0, 0.0, 0.0]
                return v_out

        # outside of infall radius, the envelope is static
        # if r_in > self.r_inf:
        #     v_sph = [0.0+vr_offset*1e3, 0.0, 0.0]
        #     v_out = self.Spherical2Cart_vector((r_in, t_in, p_in), v_sph)
        #     return v_out

        # if the input radius is smaller than the minimum in xr array,
        # use the minimum in xr array instead.
        # UPDATE (081518): return zero velocity instead
        if r_in < self.tsc2d['xr_wall'].min() * self.r_inf:
            r_in = self.tsc2d['xrc'].min() * self.r_inf

            v_out = [0.0, 0.0, 0.0]
            return v_out

        if not self.interpolate:
            ind = self.locateCell2d(
                (r_in, t_in),
                (self.tsc2d['xr_wall'] * self.r_inf, self.tsc2d['theta_wall']))
            v_sph = list(
                map(float, [
                    self.tsc2d['vr2d'][ind] * 1e5 / 1e2,
                    self.tsc2d['vtheta2d'][ind] * 1e5 / 1e2,
                    self.tsc2d['vphi2d'][ind] * 1e5 / 1e2
                ]))
        else:
            vr = self.interpolateCell2d((r_in, t_in), self.tsc2d['vr2d'],
                                        (self.tsc2d['xr_wall'] * self.r_inf,
                                         self.tsc2d['theta_wall'])) * 1e5
            vtheta = self.interpolateCell2d(
                (r_in, t_in), self.tsc2d['vtheta2d'],
                (self.tsc2d['xr_wall'] * self.r_inf,
                 self.tsc2d['theta_wall'])) * 1e5
            vphi = self.interpolateCell2d((r_in, t_in), self.tsc2d['vphi2d'],
                                          (self.tsc2d['xr_wall'] * self.r_inf,
                                           self.tsc2d['theta_wall'])) * 1e5
            v_sph = list(map(float, [vr / 1e2, vtheta / 1e2, vphi / 1e2]))

        # test for artifically reducing the radial velocity
        v_sph[0] = v_sph[0] * vr_factor  # + vr_offset*1e3
        # flatten out at the vr_offset
        # if v_sph[0] > vr_offset*1e3:
        # v_sph[0] = vr_offset*1e3 # Note infall velocity should be negative

        # A hybrid outer envelope model: -0.5 km/s uniformly within 1e4 AU and static beyond.
        # static envelope beyond 3000 AU

        # the vr_offset has a parabolic curve as a function of radius (e.g. Keto+2015)
        # parameter is taken from Keto+2015.  y = a(r - r_max)^2
        # vr is negative
        if v_sph[0] > vr_offset * 1e3:
            v_sph[0] = 50.0 * (r_in - (r_wall[-1] + r_wall[-2]) / 2)**2 * 1e3

        if sph:
            return v_sph

        v_out = self.Spherical2Cart_vector((r_in, t_in, p_in), v_sph)

        if self.debug:
            foo = open('velocity.log', 'a')
            foo.write('%e \t %e \t %e \t %f \t %f \t %f\n' %
                      (x, y, z, v_out[0], v_out[1], v_out[2]))
            foo.close()

        return v_out

    def getFFVelocity(self,
                      x,
                      y,
                      z,
                      J,
                      M,
                      sph=False,
                      unit_convert=True,
                      vr_factor=1.0):
        """
        cs: effecitve sound speed in km/s;
        age: the time since the collapse began in year.
        """

        (r_in, t_in, p_in) = self.Cart2Spherical(x,
                                                 y,
                                                 z,
                                                 unit_convert=unit_convert)

        if self.truncate != None:
            if (y**2 + z**2)**0.5 > self.truncate * au_si:
                v_out = [0.0, 0.0, 0.0]
                return v_out

        # if the input radius is smaller than the minimum in xr array,
        # use the minimum in xr array instead.
        # UPDATE: return zero velocity instead
        if r_in < self.xr_wall.min() * self.r_inf:
            r_in = self.xr.min() * self.r_inf

            v_out = [0.0, 0.0, 0.0]
            return v_out

        # use the Sakai model
        M = M * MS
        # centrifugal barrier
        cb = J**2 / (2 * G * M)

        if 2 * G * M / r_in - J**2 / r_in**2 >= 0:
            vr = (2 * G * M / r_in - J**2 / r_in**2)**0.5 * vr_factor
        else:
            vr = 0.0
        # let vk = vp at CB
        M_k = J**2 / (G * cb)
        vp = J / r_in
        vk = (G * M_k / r_in)**0.5

        if r_in >= cb:
            v_sph = [-vr / 1e2, 0.0, vp / 1e2]
        else:
            v_sph = [-vr / 1e2, 0.0, vk / 1e2]

        if sph:
            return v_sph

        v_out = self.Spherical2Cart_vector((r_in, t_in, p_in), v_sph)

        if self.debug:
            foo = open('velocity.log', 'a')
            foo.write('%e \t %e \t %e \t %f \t %f \t %f\n' %
                      (x, y, z, v_out[0], v_out[1], v_out[2]))
            foo.close()

        return v_out

    def getVelocity2(self, x, y, z, sph=False, unit_convert=True):
        """
        new method to interpolate the velocity
        cs: effecitve sound speed in km/s;
        age: the time since the collapse began in year.
        """

        (r_in, t_in, p_in) = self.Cart2Spherical(x,
                                                 y,
                                                 z,
                                                 unit_convert=unit_convert)

        if self.truncate != None:
            if (y**2 + z**2)**0.5 > self.truncate * au_si:
                v_out = [0.0, 0.0, 0.0]
                return v_out

        # outside of infall radius, the envelope is static
        if r_in > self.r_inf:
            v_out = [0.0, 0.0, 0.0]
            return v_out

        # if the input radius is smaller than the minimum in xr array,
        # use the minimum in xr array instead.
        if r_in < self.tsc2d['xr_wall'].min() * self.r_inf:
            r_in = self.tsc2d['xrc'].min() * self.r_inf
            # TODO: raise warning

        # r, t = 10*au, np.radians(30.)
        # print(r, t)
        r_corners = np.argsort(abs(r_in - self.tsc2d['xrc'] * self.r_inf))[:2]
        theta_corners = np.argsort(abs(t_in - self.tsc2d['thetac']))[:2]

        # print(r_corners, theta_corners)

        # initialize the velocity vector in spherical coordinates
        # TODO: use scipy interp2d
        v_sph = []
        for k in ['vr2d', 'vtheta2d', 'vphi2d']:
            f = interp2d(self.tsc2d['xrc'][r_corners] * self.r_inf,
                         self.tsc2d['thetac'][theta_corners],
                         self.tsc2d[k][np.ix_(r_corners, theta_corners)])
            v_sph.append(float(f(r_in, t_in) * 1e5 / 1e2))

        # v_r, v_theta, v_phi = 0.0, 0.0, 0.0
        # for rc in r_corners:
        #     for tc in theta_corners:
        #         v_r += self.vr2d[rc, tc]
        #         v_theta += self.vtheta2d[rc, tc]
        #         v_phi += self.vphi2d[rc, tc]
        # v_r = v_r/4
        # v_theta = v_theta/4
        # v_phi = v_phi/4
        #
        # v_sph = list(map(float, [v_r/1e2, v_theta/1e2, v_phi/1e2]))  # convert to SI unit (meter)
        if sph:
            return v_sph

        v_out = self.Spherical2Cart_vector((r_in, t_in, p_in), v_sph)

        if self.debug:
            foo = open('velocity.log', 'a')
            foo.write('%e \t %e \t %e \t %f \t %f \t %f\n' %
                      (x, y, z, v_out[0], v_out[1], v_out[2]))
            foo.close()

        return v_out

    def getAbundance(self, x, y, z, config, tol=10, theta_cav=None):
        # tol: the size (in AU) of the linear region between two steps
        # (try to avoid "cannot find cell" problem in LIME)

        # a_params = [abundance at outer region,
        #             fraction of outer abundance to the inner abundance,
        #             the ratio of the outer radius of the inner region to the infall radius]

        # abundances = [3.5e-8, 3.5e-9]  # inner, outer

        if self.truncate != None:
            if (y**2 + z**2)**0.5 > self.truncate * au_si:
                return 0.0

        tol = tol * au_cgs

        (r_in, t_in, p_in) = self.Cart2Spherical(x, y, z)

        # determine whether the cell is in the cavity
        if (theta_cav != None) and (theta_cav != 0):
            # using R = 10000 AU as the reference point
            c0 = (10000.*au_cgs)**(-0.5)*\
                 np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))

            # related coordinates
            w = abs(r_in * np.cos(np.pi / 2 - t_in))
            _z = r_in * np.sin(np.pi / 2 - t_in)

            # condition for open cavity
            z_cav = c0 * abs(w)**1.5
            cav_con = abs(_z) > abs(z_cav)

            if cav_con:
                abundance = 0.0
                return float(abundance)

        # single negative drop case
        # TODO: adopt a more generic model name, but keep backward compatability.
        if (config['a_model'] == 'neg_step1') or (config['a_model']
                                                  == 'step1'):
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])

            if (r_in - a2 * self.r_inf) > tol / 2:
                abundance = a0
            elif abs(r_in - a2 * self.r_inf) <= tol / 2:
                abundance = a0 * a1 + (r_in - (a2 * self.r_inf - tol / 2)) * (
                    a0 - a0 * a1) / tol
            else:
                abundance = a0 * a1

        elif (config['a_model'] == 'neg_step2') or (config['a_model']
                                                    == 'step2'):
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])
            a3 = float(config['a_params3'])
            a4 = float(config['a_params4'])

            if (r_in - a2 * self.r_inf) > tol / 2:
                abundance = a0
            # linear interpolation from the outer region to the first step
            elif abs(r_in - a2 * self.r_inf) <= tol / 2:
                abundance = a0 * a1 + (r_in - (a2 * self.r_inf - tol / 2)) * (
                    a0 - a0 * a1) / tol
            # first step
            elif (r_in - a4 * au_cgs) > tol / 5 / 2 and (a2 * self.r_inf -
                                                         r_in) > tol / 2:
                abundance = a0 * a1
            # linear interpolation from the first step to the second step
            elif abs(r_in - a4 * au_cgs) <= tol / 5 / 2:
                abundance = a0 * a3 + (r_in - (a4 * au_cgs - tol / 5 / 2)) * (
                    a0 * a1 - a0 * a3) / (tol / 5)
            else:
                abundance = a0 * a3

        elif (config['a_model'] == 'drop'):
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])
            a3 = float(config['a_params3'])
            a4 = float(config['a_params4'])

            if (r_in - a2 * au_cgs) > tol / 2:
                abundance = a0
            # linear interpolation from the outer region to the first step
            elif abs(r_in - a2 * au_cgs) <= tol / 2:
                abundance = a1 + (r_in -
                                  (a2 * au_cgs - tol / 2)) * (a0 - a1) / tol
            # first step
            elif (r_in - a4 * au_cgs) > tol / 5 / 2 and (a2 * au_cgs -
                                                         r_in) > tol / 2:
                abundance = a1
            # linear interpolation from the first step to the second step
            elif abs(r_in - a4 * au_cgs) <= tol / 5 / 2:
                abundance = a3 + (r_in - (a4 * au_cgs - tol / 5 / 2)) * (
                    a1 - a3) / (tol / 5)
            else:
                abundance = a3

        elif (config['a_model'] == 'drop2'):
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])
            a3 = float(config['a_params3'])
            a4 = float(config['a_params4'])

            if (r_in - a2 * au_cgs) > tol / 2:
                abundance = a0
            # linear interpolation from the outer region to the first step
            elif abs(r_in - a2 * au_cgs) <= tol / 2:
                abundance = a1 + (r_in -
                                  (a2 * au_cgs - tol / 2)) * (a0 - a1) / tol
            # first step
            elif (r_in - a4 * au_cgs) > tol / 5 / 2 and (a2 * au_cgs -
                                                         r_in) > tol / 2:
                abundance = a1
            # linear interpolation from the first step to the second step
            elif abs(r_in - a4 * au_cgs) <= tol / 5 / 2:
                abundance = a3 + (r_in - (a4 * au_cgs - tol / 5 / 2)) * (
                    a1 - a3) / (tol / 5)
            elif r_in >= 13 * au_cgs:
                abundance = a3
            else:
                abundance = 1e-20

        elif (config['a_model'] == 'drop3'):
            a0 = float(config['a_params0'])  # undelepted abundance
            a1 = float(config['a_params1'])  # depleted abundance
            a2 = float(config['a_params2'])  # evaporation temperature (K)
            a3 = float(config['a_params3'])  # depletion density (cm-3)
            a4 = float(config['a_params4']
                       )  # the temperature H2O starts to destory HCO+
            if a4 == -1:
                a4 = np.inf

            temp = self.getTemperature(x, y, z)
            density = self.getDensity(x, y, z) / 1e6

            if (temp <= a2) and (density >= a3):
                abundance = a1
            elif (temp <= a4):
                abundance = a0
            else:
                abundance = 1e-20

        elif config['a_model'] == 'uniform':
            abundance = float(config['a_params0'])

        elif config['a_model'] == 'lognorm':
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])
            a3 = float(config['a_params3'])  # r_in for power law decrease

            if r_in >= a2 * self.r_inf:
                abundance = a0
            elif (r_in < a2 * self.r_inf) & (r_in > a3 * au_cgs):
                abundance = a0 * a1 + a0 * (1 - a1) / (
                    np.log10(self.r_inf * a2) - np.log10(a3 * au_cgs)) * (
                        np.log10(r_in) - np.log10(a3 * au_cgs))
            else:
                abundance = a0 * a1

        elif config['a_model'] == 'powerlaw':
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])
            a3 = float(config['a_params3'])
            a4 = float(config['a_params4'])

            # re-define rMin
            # rmin = 100*au_cgs
            rmin = self.rmin

            if r_in >= a2 * self.r_inf:
                abundance = a0
            elif (r_in >= rmin) and (r_in < a2 * self.r_inf):
                # y = Ax^a3+B
                A = a0 * (1 - a1) / ((a2 * self.r_inf)**a3 - rmin**a3)
                B = a0 - a0 * (1 - a1) * (a2 * self.r_inf)**a3 / (
                    (a2 * self.r_inf)**a3 - rmin**a3)
                abundance = A * r_in**a3 + B
            else:
                abundance = a0 * a1

            # option to cap the maximum value of abundance
            if a4 > 0:
                if abundance > abs(a4):
                    abundance = abs(a4)

        elif config['a_model'] == 'powerlaw2':
            a0 = float(config['a_params0'])
            a1 = float(config['a_params1'])
            a2 = float(config['a_params2'])
            a3 = float(config['a_params3'])
            a4 = float(config['a_params4'])

            # re-define rMin
            # rmin = 100*au_cgs
            rmin = self.rmin

            if r_in >= a2 * self.r_inf:
                abundance = a0
            elif (r_in >= rmin) and (r_in < a2 * self.r_inf):
                # y = Ax^a3+B
                A = a0 * (1 - a1) / ((a2 * self.r_inf)**a3 - rmin**a3)
                B = a0 - a0 * (1 - a1) * (a2 * self.r_inf)**a3 / (
                    (a2 * self.r_inf)**a3 - rmin**a3)
                abundance = A * r_in**a3 + B
            else:
                abundance = a0 * a1

            # add the evaporation zone
            # TODO: parametrize the setup
            if (r_in <= 100 * au_cgs) and (r_in >= 13 * au_cgs):
                abundance = 1e-10

            # option to cap the maximum value of abundance
            if a4 > 0:
                if abundance > abs(a4):
                    abundance = abs(a4)

        elif config['a_model'] == 'chem':
            a0 = float(config['a_params0'])  # peak abundance
            a1 = float(config['a_params1'])  # inner abundance
            a2 = float(config['a_params2'])  # peak radius
            a3 = float(config['a_params3'])  # inner decrease power
            a4 = float(config['a_params4'])  # outer decrease power

            # radius of the evaporation front, determined by the extent of COM emission
            rCOM = 100 * au_cgs

            if r_in >= a2 * self.r_inf:
                # y = Ax^a, a < 0
                A_out = a0 / (a2 * self.r_inf)**a4
                abundance = A_out * r_in**a4
            elif (r_in >= rCOM) and (r_in < a2 * self.r_inf):
                # y = Ax^a, a > 0
                A_in = a0 / (a2 * self.r_inf)**a3
                abundance = A_in * r_in**a3
            else:
                abundance = a1

        elif config['a_model'] == 'chem2':
            a0 = float(config['a_params0'])  # peak abundance
            a1 = float(config['a_params1'])  # inner abundance
            a2 = float(config['a_params2'])  # inner peak radius [AU]
            a3 = float(config['a_params3'])  # outer peak radius [AU]
            a4 = config[
                'a_params4']  # inner/outer radius of the evaporation region

            # radius of the evaporation front, determined by the extent of COM emission
            if (a4 == '-1') or (a4
                                == '2.0/-2.0'):  # for backward compatability
                rCOM = 100 * au_cgs
                rCen = 13 * au_cgs
            else:
                rCen = float(a4.split(',')[0]) * au_cgs
                rCOM = float(a4.split(',')[1]) * au_cgs

            # innerExpo, outerExpo = [float(i) for i in config['a_params4'].split('/')]
            # fix the decreasing/increasing powers
            innerExpo = 2.0
            outerExpo = -2.0

            if r_in >= a3 * au_cgs:
                # y = Ax^a, a < 0
                A_out = a0 / (a3 * au_cgs)**outerExpo
                abundance = A_out * r_in**outerExpo
            elif (r_in < a3 * au_cgs) and (r_in >= a2 * au_cgs):
                abundance = a0
            elif (r_in >= rCOM) and (r_in < a2 * au_cgs):
                # y = Ax^a, a > 0
                A_in = a0 / (a2 * au_cgs)**innerExpo
                abundance = A_in * r_in**innerExpo
            elif (r_in >= rCen) and (r_in < rCOM):  # centrifugal radius
                abundance = a1
            else:
                abundance = 1e-20

        elif config['a_model'] == 'chem3':
            a0 = float(config['a_params0'])  # peak abundance
            a1 = float(config['a_params1'])  # inner abundance
            a2 = list(map(float, config['a_params2'].split(
                ',')))  # inner/outer radius for the maximum abundance [AU]
            a3 = list(map(float, config['a_params3'].split(
                ',')))  # inner/outer radius for the evaporation zone [AU]
            a4 = list(map(float, config['a_params4'].split(
                ',')))  # inner/outer decreasing power
            # radius of the evaporation front, determined by the extent of COM emission
            rEvap_inner = a3[0] * au_cgs
            rEvap_outer = a3[1] * au_cgs

            # test the case of a broken power law for the freeze-out zone
            # In this case, there will be three values for both a2 and a4

            # The input powers are stored as -
            #   innerExpo for all powers except for the last one
            #   outerExpo for the last power
            # fix the decreasing/increasing powers
            innerExpo = a4[:-1]
            outerExpo = a4[-1]
            # calculate the constants for each freeze-out zone
            A = []
            for i, (r_out,
                    pow) in enumerate(zip(a2[:-1][::-1], innerExpo[::-1])):
                if i == 0:
                    previous_pow = 0.0
                    _A = (r_out * au_cgs)**(-pow)
                else:
                    _A = _A * (r_out * au_cgs)**(previous_pow - pow)
                previous_pow = pow
                A.append(a0 * _A)
            A = A[::-1]

            if r_in >= a2[-1] * au_cgs:
                # y = Ax^a, a < 0
                A_out = a0 / (a2[-1] * au_cgs)**outerExpo
                abundance = A_out * r_in**outerExpo
            elif (r_in < a2[-1] * au_cgs) and (r_in >= a2[-2] * au_cgs):
                abundance = a0
            # freeze-out zone
            elif (r_in >= rEvap_outer) and (r_in < a2[-2] * au_cgs):
                # y = Ax^a, a > 0
                # determine which freeze-out zone
                ind_zone = a2[:-1].index(
                    min([
                        rr for i, rr in enumerate(a2[:-1])
                        if rr * au_cgs - r_in > 0
                    ]))
                A_in = A[ind_zone]
                Expo = innerExpo[ind_zone]
                # A_in = a0 / (a2[0]*au_cgs)**innerExpo
                # abundance = A_in * r_in**innerExpo
                abundance = A_in * r_in**Expo
            elif (r_in >= rEvap_inner) and (r_in <
                                            rEvap_outer):  # centrifugal radius
                abundance = a1
            else:
                abundance = 0.0

        elif config[
                'a_model'] == 'chem4':  # mostly for CS, which has two evaporation fronts, one for CO, and one for CS.
            a0 = float(config['a_params0'])  # peak abundance
            a1 = list(map(
                float, config['a_params1'].split(',')))  # TWO inner abundance
            a2 = list(map(float, config['a_params2'].split(
                ',')))  # inner/outer radius for the maximum abundance [AU]
            a3 = list(
                map(float, config['a_params3'].split(','))
            )  # inner/middle/outer radius for the evaporation zone [AU]
            a4 = list(map(float, config['a_params4'].split(
                ',')))  # inner/outer decreasing power
            # radius of the evaporation front, determined by the extent of COM emission
            rEvap_inner = a3[0] * au_cgs
            rEvap_middle = a3[1] * au_cgs
            rEvap_outer = a3[2] * au_cgs

            # test the case of a broken power law for the freeze-out zone
            # In this case, there will be three values for both a2 and a4

            # The input powers are stored as -
            #   innerExpo for all powers except for the last one
            #   outerExpo for the last power
            # fix the decreasing/increasing powers
            innerExpo = a4[:-1]
            outerExpo = a4[-1]
            # calculate the constants for each freeze-out zone
            A = []
            for i, (r_out,
                    pow) in enumerate(zip(a2[:-1][::-1], innerExpo[::-1])):
                if i == 0:
                    previous_pow = 0.0
                    _A = (r_out * au_cgs)**(-pow)
                else:
                    _A = _A * (r_out * au_cgs)**(previous_pow - pow)
                previous_pow = pow
                A.append(a0 * _A)
            A = A[::-1]

            if r_in >= a2[-1] * au_cgs:
                # y = Ax^a, a < 0
                A_out = a0 / (a2[-1] * au_cgs)**outerExpo
                abundance = A_out * r_in**outerExpo
            elif (r_in < a2[-1] * au_cgs) and (r_in >= a2[-2] * au_cgs):
                abundance = a0
            # freeze-out zone
            elif (r_in >= rEvap_outer) and (r_in < a2[-2] * au_cgs):
                # y = Ax^a, a > 0
                # determine which freeze-out zone
                ind_zone = a2[:-1].index(
                    min([
                        rr for i, rr in enumerate(a2[:-1])
                        if rr * au_cgs - r_in > 0
                    ]))
                A_in = A[ind_zone]
                Expo = innerExpo[ind_zone]
                # A_in = a0 / (a2[0]*au_cgs)**innerExpo
                # abundance = A_in * r_in**innerExpo
                abundance = A_in * r_in**Expo
            elif (r_in >= rEvap_middle) and (
                    r_in < rEvap_outer):  # 1st evaporation zone
                abundance = a1[1]
            elif (r_in >= rEvap_inner) and (
                    r_in < rEvap_middle):  # 1st evaporation zone
                abundance = a1[0]
            else:
                abundance = 0.0

        elif config['a_model'] == 'interp':
            filename = config['a_params0']
            adata = io.ascii.read(filename, names=['radius', 'abundance'])
            f_a = interp1d(adata['radius'], adata['abundance'])
            if (r_in < adata['radius'].min() * au_cgs) or (
                    r_in > adata['radius'].max() * au_cgs):
                abundance = 1e-40
            else:
                abundance = f_a(r_in / au_cgs)
        else:
            print('Cannot recognize the input a_model', config['a_model'])
            return False

        if self.debug:
            foo = open('abundance.log', 'a')
            foo.write('%e \t %e \t %e \t %f\n' % (x, y, z, abundance))
            foo.close()

        # uniform abundance
        # abundance = 3.5e-9

        return float(abundance)

    def radialSmoothing(self,
                        x,
                        y,
                        z,
                        variable,
                        kernel='boxcar',
                        smooth_factor=2,
                        config=None):
        # convert the coordinates from Cartian to spherical
        (r_in, t_in, p_in) = self.Cart2Spherical(x, y, z)

        # r-array for smoothing
        smoothL = r_in / smooth_factor * au_cgs
        r_arr = np.arange(r_in - smoothL / 2, r_in + smoothL / 2,
                          smoothL / 50)  # 50 bins

        # setup the smoothing kernel
        # it is not really a smoothing kernel, more like a local mean
        def averageKernel(kernel, r, var):
            if kernel == 'boxcar':
                out = np.mean(var)
            return out

        # run the corresponding look-up function for the desired variable

        var_arr = np.empty_like(r_arr)
        for i, r in enumerate(r_arr):
            (xd, yd, zd) = self.Spherical2Cart(r, t_in, p_in)
            if variable == 'abundance':
                var_arr[i] = self.getAbundance(xd / 1e2, yd / 1e2, zd / 1e2,
                                               config)
        var = averageKernel(kernel, r, var_arr)

        return float(var)
Ejemplo n.º 8
0
def DIG_source_add(m, reg, df_nu, boost):

    print("--------------------------------")
    print("Adding DIG to Source List in source_creation")
    print("--------------------------------")

    print("Getting the specific energy dumped in each grid cell")

    try:
        rtout = cfg.model.outputfile + '_DIG_energy_dumped.sed'
        try:
            grid_properties = np.load(cfg.model.PD_output_dir +
                                      "/grid_physical_properties." +
                                      cfg.model.snapnum_str + '_galaxy' +
                                      cfg.model.galaxy_num_str + ".npz")
        except:
            grid_properties = np.load(cfg.model.PD_output_dir +
                                      "/grid_physical_properties." +
                                      cfg.model.snapnum_str + ".npz")

        cell_info = np.load(cfg.model.PD_output_dir + "/cell_info." +
                            cfg.model.snapnum_str + "_" +
                            cfg.model.galaxy_num_str + ".npz")
    except:
        print(
            "ERROR: Can't proceed with DIG nebular emission calculation. Code is unable to find the required files."
        )
        print(
            "Make sure you have the rtout.sed, grid_physical_properties.npz and cell_info.npz for the corresponding galaxy."
        )

        return

    m_out = ModelOutput(rtout)
    oct = m_out.get_quantities()
    grid = oct
    order = find_order(grid.refined)
    refined = grid.refined[order]
    quantities = {}
    for field in grid.quantities:
        quantities[('gas', field)] = grid.quantities[field][0][order][~refined]

    cell_width = cell_info["fw1"][:, 0]
    mass = (quantities['gas', 'density'] * units.g /
            units.cm**3).value * (cell_width**3)
    specific_energy = (quantities['gas', 'specific_energy'] * units.erg /
                       units.s / units.g).value
    specific_energy = (specific_energy * mass)  # in ergs/s

    mask = np.where(
        mass != 0)[0]  # Masking out all grid cells that have no gas mass
    pos = cell_info["fc1"][mask] - boost
    cell_width = cell_width[mask]
    met = grid_properties["grid_gas_metallicity"][:, mask]
    met = np.transpose(met)
    specific_energy = specific_energy[mask]

    mask = []
    logU_arr = []
    lam_arr = []
    fnu_arr = []

    for i in range(len(cell_width)):

        # Getting shape of the incident spectrum by taking a distance weighted average of the CLOUDY output spectrum of nearby young stars.
        sed_file_name = cfg.model.PD_output_dir + "neb_seds_galaxy_" + cfg.model.galaxy_num_str + ".npz"
        lam, fnu = get_DIG_sed_shape(
            pos[i], cell_width[i], sed_file=sed_file_name
        )  # Returns input SED shape, lam in Angstrom, fnu in Lsun/Hz

        # If the gas cell has no young stars within a specified distance (stars_max_dist) then skip it.
        if len(np.atleast_1d(fnu)) == 1:
            continue

        # Calulating the ionization parameter by extrapolating the specfic energy beyind the lyman limit
        # using the SED shape calculated above.
        logU = get_DIG_logU(lam, fnu, specific_energy[i], cell_width[i])

        lam_arr.append(lam)
        fnu_arr.append(fnu)

        # Only cells with ionization parameter greater than the parameter DIG_min_logU are considered
        # for nebular emission calculation. This is done so as to speed up the calculation by ignoring
        # the cells that do not have enough energy to prduce any substantial emission
        if logU > cfg.par.DIG_min_logU:
            mask.append(i)
            lam_arr.append(lam)
            fnu_arr.append(fnu)
            logU_arr.append(logU)

    cell_width = cell_width[mask]
    met = met[mask]
    lam_arr = np.array(lam_arr)
    fnu_arr = np.array(fnu_arr)
    logU = np.array(logU_arr)
    pos = pos[mask]

    if (len(mask)) == 0:
        print(
            "No gas particles fit the criteria for calculating DIG. Skipping DIG calculation"
        )
        return

    print(
        "----------------------------------------------------------------------------------"
    )
    print("Calculating nebular emission from Diffused Ionized Gas for " +
          str(len(mask)) + " gas cells")
    print(
        "----------------------------------------------------------------------------------"
    )

    fnu_arr_neb = sg.get_dig_seds(lam_arr, fnu_arr, logU, cell_width, met)

    nu = 1.e8 * constants.c.cgs.value / lam

    for i in range(len(logU)):
        fnu = fnu_arr_neb[i, :]
        nu, fnu = wavelength_compress(nu, fnu, df_nu)

        nu = nu[::-1]
        fnu = fnu[::-1]

        lum = np.absolute(np.trapz(fnu, x=nu)) * constants.L_sun.cgs.value

        source = m.add_point_source()
        source.luminosity = lum  # [ergs/s]
        source.spectrum = (nu[::-1], fnu[::-1])
        source.position = pos[i]  # [cm]
Ejemplo n.º 9
0
def alma_cavity(freq,
                outdir,
                vlim,
                units='MJy/sr',
                pix=300,
                filename=None,
                label=None):
    import numpy as np
    import matplotlib.pyplot as plt
    import astropy.constants as const
    from hyperion.model import ModelOutput
    from matplotlib.ticker import MaxNLocator

    # constants setup
    c = const.c.cgs.value
    pc = const.pc.cgs.value
    au = const.au.cgs.value
    # Image in the unit of MJy/sr
    # Change it into erg/s/cm2/Hz/sr
    if units == 'erg/s/cm2/Hz/sr':
        factor = 1e-23 * 1e6
        cb_label = r'$\rm{I_{\nu}\,(erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1})}$'
    elif units == 'MJy/sr':
        factor = 1
        cb_label = r'$\rm{I_{\nu}\,(MJy\,sr^{-1})}$'

    if filename == None:
        # input files setup
        filename_reg = '/Users/yaolun/test/model12.rtout'
        filename_r2 = '/Users/yaolun/test/model13.rtout'
        filename_r15 = '/Users/yaolun/test/model17.rtout'
        filename_uni = '/Users/yaolun/test/model62.rtout'
    else:
        filename_reg = filename['reg']
        filename_r2 = filename['r2']
        filename_r15 = filename['r15']
        filename_uni = filename['uni']

    if label == None:
        label_reg = r'$\rm{const.+r^{-2}}$'
        label_r2 = r'$\rm{r^{-2}}$'
        label_r15 = r'$\rm{r^{-1.5}}$'
        label_uni = r'$\rm{uniform}$'
    else:
        label_reg = label['reg']
        label_r2 = label['r2']
        label_r15 = label['r15']
        label_uni = label['uni']

    wl_aper = [
        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
    ]
    wav = c / freq / 1e9 * 1e4
    # wav = 40

    # read in
    # regular cavity setting
    m_reg = ModelOutput(filename_reg)
    image_reg = m_reg.get_image(group=len(wl_aper) + 1,
                                inclination=0,
                                distance=178.0 * pc,
                                units='MJy/sr')
    # Calculate the image width in arcseconds given the distance used above
    rmax = max(m_reg.get_quantities().r_wall)
    w = np.degrees(rmax / image_reg.distance) * 3600.
    # w = np.degrees((1.5 * pc) / image_reg.distance) * 60.
    pix_num = len(image_reg.val[:, 0, 0])
    pix2arcsec = 2 * w / pix_num
    pix2au = np.radians(2 * w / pix_num / 3600.) * image_reg.distance / au

    iwav = np.argmin(np.abs(wav - image_reg.wav))
    # avoid zero in log
    val_reg = image_reg.val[:, :, iwav] * factor + 1e-30

    # r^-2 cavity setting
    m_r2 = ModelOutput(filename_r2)
    image_r2 = m_r2.get_image(group=len(wl_aper) + 1,
                              inclination=0,
                              distance=178.0 * pc,
                              units='MJy/sr')
    # Calculate the image width in arcseconds given the distance used above
    rmax = max(m_r2.get_quantities().r_wall)
    w = np.degrees(rmax / image_r2.distance) * 3600.
    pix_num = len(image_reg.val[:, 0, 0])
    pix2arcsec = 2 * w / pix_num
    pix2au = np.radians(2 * w / pix_num / 3600.) * image_reg.distance / au
    iwav = np.argmin(np.abs(wav - image_r2.wav))
    # avoid zero in log
    val_r2 = image_r2.val[:, :, iwav] * factor + 1e-30

    # r^-1.5 cavity setting
    m_r15 = ModelOutput(filename_r15)
    image_r15 = m_r15.get_image(group=len(wl_aper) + 1,
                                inclination=0,
                                distance=178.0 * pc,
                                units='MJy/sr')
    # Calculate the image width in arcseconds given the distance used above
    rmax = max(m_r15.get_quantities().r_wall)
    w = np.degrees(rmax / image_r15.distance) * 3600.
    pix_num = len(image_reg.val[:, 0, 0])
    pix2arcsec = 2 * w / pix_num
    pix2au = np.radians(2 * w / pix_num / 3600.) * image_reg.distance / au
    iwav = np.argmin(np.abs(wav - image_r15.wav))
    # avoid zero in log
    val_r15 = image_r15.val[:, :, iwav] * factor + 1e-30

    # uniform cavity setting
    m_uni = ModelOutput(filename_uni)
    image_uni = m_uni.get_image(group=len(wl_aper) + 1,
                                inclination=0,
                                distance=178.0 * pc,
                                units='MJy/sr')
    # Calculate the image width in arcseconds given the distance used above
    rmax = max(m_uni.get_quantities().r_wall)
    w = np.degrees(rmax / image_uni.distance) * 3600.
    print w
    pix_num = len(image_reg.val[:, 0, 0])
    pix2arcsec = 2 * w / pix_num
    pix2au = np.radians(2 * w / pix_num / 3600.) * image_reg.distance / au
    iwav = np.argmin(np.abs(wav - image_uni.wav))
    # avoid zero in log
    val_uni = image_uni.val[:, :, iwav] * factor + 1e-30

    # 1-D radial intensity profile
    # get y=0 plane, and plot it
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(111)

    reg, = ax.plot(np.linspace(-pix / 2, pix / 2, num=pix) * pix2arcsec,
                   val_reg[:, pix / 2 - 1],
                   color='b',
                   linewidth=2)
    r2, = ax.plot(np.linspace(-pix / 2, pix / 2, num=pix) * pix2arcsec,
                  val_r2[:, pix / 2 - 1],
                  color='r',
                  linewidth=1.5)
    r15, = ax.plot(np.linspace(-pix / 2, pix / 2, num=pix) * pix2arcsec,
                   val_r15[:, pix / 2 - 1],
                   '--',
                   color='r',
                   linewidth=1.5)
    uni, = ax.plot(np.linspace(-pix / 2, pix / 2, num=pix) * pix2arcsec,
                   val_uni[:, pix / 2 - 1],
                   color='k',
                   linewidth=1.5)
    ax.legend([reg, r2, r15, uni], [label_reg, label_r2, label_r15, label_uni],\
              numpoints=1, loc='lower center', fontsize=18)

    ax.set_xlim([-1, 1])
    ax.set_xlabel(r'$\rm{offset\,(arcsec)}$', fontsize=24)
    # ax.set_ylabel(r'$\rm{I_{\nu}~(erg~s^{-1}~cm^{-2}~Hz^{-1}~sr^{-1})}$', fontsize=16)
    ax.set_ylabel(cb_label, fontsize=24)

    [
        ax.spines[axis].set_linewidth(2)
        for axis in ['top', 'bottom', 'left', 'right']
    ]
    ax.minorticks_on()
    ax.tick_params('both',
                   labelsize=16,
                   width=2,
                   which='major',
                   pad=10,
                   length=5)
    ax.tick_params('both',
                   labelsize=16,
                   width=2,
                   which='minor',
                   pad=10,
                   length=2.5)

    fig.savefig(outdir + 'cavity_intensity_' + str(freq) + '.pdf',
                format='pdf',
                dpi=300,
                bbox_inches='tight')

    # 2-D intensity map
    from mpl_toolkits.axes_grid1 import AxesGrid
    image_grid = [val_reg, val_uni, val_r2, val_r15]
    label_grid = [label_reg, label_r2, label_r15, label_uni]
    fig = plt.figure(figsize=(30, 30))
    grid = AxesGrid(
        fig,
        142,  # similar to subplot(142)
        nrows_ncols=(2, 2),
        axes_pad=0,
        share_all=True,
        label_mode="L",
        cbar_location="right",
        cbar_mode="single",
    )
    for i in range(4):
        offset = np.linspace(-pix / 2, pix / 2, num=pix) * pix2arcsec
        trim = np.where(abs(offset) <= 2)
        im = grid[i].pcolor(np.linspace(-pix/2,pix/2,num=pix)*pix2arcsec, np.linspace(-pix/2,pix/2,num=pix)*pix2arcsec,\
            image_grid[i], cmap=plt.cm.jet, vmin=vlim[0], vmax=vlim[1])#vmin=(image_grid[i][trim,trim]).min(), vmax=(image_grid[i][trim,trim]).max())
        grid[i].set_xlim([-20, 20])
        grid[i].set_ylim([-20, 20])
        grid[i].set_xlabel(r'$\rm{RA\,offset\,(arcsec)}$', fontsize=14)
        grid[i].set_ylabel(r'$\rm{Dec\,offset\,(arcsec)}$', fontsize=14)
        # lg = grid[i].legend([label_grid[i]], loc='upper center', numpoints=1, fontsize=16)
        # for text in lg.get_texts():
        #     text.set_color('w')
        grid[i].text(0.5,
                     0.8,
                     label_grid[i],
                     color='w',
                     weight='heavy',
                     fontsize=18,
                     transform=grid[i].transAxes,
                     ha='center')
        grid[i].locator_params(axis='x', nbins=5)
        grid[i].locator_params(axis='y', nbins=5)
        [
            grid[i].spines[axis].set_linewidth(1.2)
            for axis in ['top', 'bottom', 'left', 'right']
        ]
        grid[i].tick_params('both',
                            labelsize=12,
                            width=1.2,
                            which='major',
                            pad=10,
                            color='white',
                            length=5)
        grid[i].tick_params('both',
                            labelsize=12,
                            width=1.2,
                            which='minor',
                            pad=10,
                            color='white',
                            length=2.5)

        # fix the overlap tick labels
        if i != 0:
            x_nbins = len(grid[i].get_xticklabels())
            y_nbins = len(grid[i].get_yticklabels())
            grid[i].yaxis.set_major_locator(MaxNLocator(nbins=5,
                                                        prune='upper'))
            if i != 2:
                grid[i].xaxis.set_major_locator(
                    MaxNLocator(nbins=5, prune='lower'))

    [grid[0].spines[axis].set_color('white') for axis in ['bottom', 'right']]
    [grid[1].spines[axis].set_color('white') for axis in ['bottom', 'left']]
    [grid[2].spines[axis].set_color('white') for axis in ['top', 'right']]
    [grid[3].spines[axis].set_color('white') for axis in ['top', 'left']]

    #     ax.set_aspect('equal')
    cb = grid.cbar_axes[0].colorbar(im)
    cb.solids.set_edgecolor("face")
    cb.ax.minorticks_on()
    cb.ax.set_ylabel(cb_label, fontsize=12)
    cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
    plt.setp(cb_obj, fontsize=12)

    # fig.text(0.5, -0.05 , r'$\rm{RA~offset~(arcsec)}$', fontsize=12, ha='center')
    # fig.text(0, 0.5, r'$\rm{Dec~offset~(arcsec)}$', fontsize=12, va='center', rotation='vertical')

    fig.savefig(outdir + 'cavity_2d_intensity_' + str(freq) + '.png',
                format='png',
                dpi=300,
                bbox_inches='tight')
Ejemplo n.º 10
0
def get_physical_props_single(yso, grids, cell_sizes, save_dir, oversample=[3],
        dust_out=None, logger=get_logger(__name__)):
    """Calculate and write the physical properties a model with one source.

    Parameters:
        yso: the model parameters.
        grids: grids where the model will be evaluated
        template: filename of the *define_model.c* file.
        logger: logging system.
    """
    # Models with more than one source should be treated in another function
    # because the oversampling should be different.

    # FITS list
    fitslist = []

    # Validate oversample
    if len(oversample)==1 and len(oversample)!=len(cell_sizes):
        oversample = oversample * len(cell_sizes)
    elif len(oversample)==len(cell_sizes):
        pass
    else:
        raise ValueError('The length of oversample != number of grids')

    # Temperature function
    if yso.params.get('DEFAULT', 'quantities_from'):
        hmodel = yso.params.get('DEFAULT', 'quantities_from')
        logger.info('Loading Hyperion model: %s', os.path.basename(hmodel))
        hmodel = ModelOutput(os.path.expanduser(hmodel))
        q = hmodel.get_quantities()
        temperature = np.sum(q['temperature'].array[1:], axis=0)
        r, th = q.r*u.cm, q.t*u.rad
        temp_func = get_temp_func(yso.params, temperature, r, th)
    elif dust_out is not None:
        logger.info('Loading Hyperion model: %s', os.path.basename(dust_out))
        hmodel = ModelOutput(os.path.expanduser(dust_out))
        q = hmodel.get_quantities()
        temperature = np.sum(q['temperature'].array[1:], axis=0)
        r, th = q.r*u.cm, q.t*u.rad
        temp_func = get_temp_func(yso.params, temperature, r, th)
    else:
        raise NotImplementedError

    # Start from smaller to larger grid
    inv_i = len(grids) - 1
    for i,(grid,cellsz) in enumerate(zip(grids, cell_sizes)):
        # Initialize grid axes
        print '='*80
        logger.info('Working on grid: %i', i)
        logger.info('Oversampling factor: %i', oversample[i])
        logger.info('Grid cell size: %i', cellsz)
        # Multiply by units
        x = grid[0]['x'] * grid[1]['x']
        y = grid[0]['y'] * grid[1]['y']
        z = grid[0]['z'] * grid[1]['z']
        xi = np.unique(x)
        yi = np.unique(y)
        zi = np.unique(z)

        # Density and velocity
        dens, (vx, vy, vz), temp = phys_oversampled_cart(
                xi, yi, zi, yso, temp_func, oversample=oversample[i], 
                logger=logger)

        # Replace out of range values
        dens[dens.cgs<=0./u.cm**3] = 10./u.cm**3
        temp[np.logical_or(np.isnan(temp.value), temp.value<2.7)] = 2.7 * u.K

        # Replace the inner region by rebbining the previous grid
        if i>0:
            # Walls of central cells
            j = cell_sizes.index(cellsz)
            xlen = cell_sizes[j-1] * len(xprev) * u.au
            nxmid = int(xlen.value) / cellsz
            xw = np.linspace(-0.5*xlen.value, 0.5*xlen.value, nxmid+1) * u.au
            ylen = cell_sizes[j-1] * len(yprev) * u.au
            nymid = int(ylen.value) / cellsz
            yw = np.linspace(-0.5*ylen.value, 0.5*ylen.value, nymid+1) * u.au
            zlen = cell_sizes[j-1] * len(zprev) * u.au
            nzmid = int(zlen.value) / cellsz
            zw = np.linspace(-0.5*zlen.value, 0.5*zlen.value, nzmid+1) * u.au
            if nxmid==nymid==nzmid==0:
                logger.warning('The inner grid is smaller than current grid size')
            else:
                logger.info('The inner %ix%ix%i cells will be replaced', nxmid,
                        nymid, nzmid)

                # Rebin previous grid
                # Density
                vol_prev = (cell_sizes[j-1]*u.au)**3
                vol = (cellsz * u.au)**3
                N_cen = vol_prev.cgs * rebin_regular_nd(dens_prev.cgs.value, 
                        zprev.cgs.value, yprev.cgs.value, xprev.cgs.value, 
                        bins=(zw.cgs.value,yw.cgs.value,xw.cgs.value), 
                        statistic='sum') * dens_prev.cgs.unit
                dens_cen = N_cen / vol
                dens_cen = dens_cen.to(dens.unit)
                # Temperature
                T_cen = rebin_regular_nd(temp_prev.value * dens_prev.cgs.value,
                        zprev.cgs.value, yprev.cgs.value, xprev.cgs.value, 
                        bins=(zw.cgs.value,yw.cgs.value, xw.cgs.value), 
                        statistic='sum') * temp_prev.unit * dens_prev.cgs.unit
                T_cen = vol_prev.cgs * T_cen / N_cen.cgs
                T_cen = T_cen.to(temp.unit)

                # Replace
                dens[len(zi)/2-nzmid/2:len(zi)/2+nzmid/2,
                        len(yi)/2-nymid/2:len(yi)/2+nymid/2,
                        len(xi)/2-nxmid/2:len(xi)/2+nxmid/2] = dens_cen
                temp[len(zi)/2-nzmid/2:len(zi)/2+nzmid/2,
                        len(yi)/2-nymid/2:len(yi)/2+nymid/2,
                        len(xi)/2-nxmid/2:len(xi)/2+nxmid/2] = T_cen
        dens_prev = dens
        temp_prev = temp
        xprev = xi
        yprev = yi
        zprev = zi

        # Linewidth and abundance
        linewidth = yso.linewidth(x, y, z, temp).to(u.cm/u.s)

        # Abundance per molecule
        abns = {}
        abn_fmt = 'abn_%s_%i'
        j = 1
        for section in yso.params.sections():
            if not section.lower().startswith('abundance'):
                continue
            mol = yso[section, 'molecule']
            abns[abn_fmt % (mol, inv_i)] = yso.abundance(x, y, z, temp, 
                    index=j, ignore_min=False)
            j = j+1

        # Write FITS
        kw = {'temp%i'%inv_i: temp.value, 'dens%i'%inv_i: dens.cgs.value, 
                'vx%i'%inv_i: vx.cgs.value, 'vy%i'%inv_i: vy.cgs.value, 
                'vz%i'%inv_i: vz.cgs.value, 
                'lwidth%i'%inv_i: linewidth.cgs.value}
        kw.update(abns)
        fitsnames = write_fits(os.path.expanduser(save_dir), 
                **kw)
        fitslist += fitsnames
        inv_i = inv_i - 1

    return fitslist
Ejemplo n.º 11
0
def hyperion_image(rtout, wave, plotdir, printname, dstar=200., group=0, marker=0,
                    size='full', convolve=False, unit=None, scalebar=None):
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    import astropy.constants as const
    from hyperion.model import ModelOutput
    # Package for matching the colorbar
    from mpl_toolkits.axes_grid1 import make_axes_locatable

    pc = const.pc.cgs.value

    if unit == None:
        unit = 'erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}'

    m = ModelOutput(rtout)

    # Extract the image.
    image = m.get_image(group=group, inclination=0, distance=dstar * pc, units='MJy/sr')

    # print np.shape(image.val)
    # Open figure and create axes
    fig = plt.figure(figsize=(8,8))
    ax = fig.add_subplot(111)

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - 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
    factor = 1
    # avoid zero in log
    # flip the image, because the setup of inclination is upside down
    val = image.val[::-1, :, iwav] * factor + 1e-30

    if convolve:
        from astropy.convolution import convolve, Gaussian2DKernel
        img_res = 2*w/len(val[:,0])
        kernel = Gaussian2DKernel(0.27/2.354/img_res)
        val = convolve(val, kernel)

    if size != 'full':
        pix_e2c = (w-size/2.)/w * len(val[:,0])/2
        val = val[pix_e2c:-pix_e2c, pix_e2c:-pix_e2c]
        w = size/2.

    # 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(val,
            # norm=mpl.colors.LogNorm(vmin=1.515e-01, vmax=4.118e+01),
            norm=mpl.colors.LogNorm(vmin=1e-04, vmax=1e+01),
            cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1)

    # draw the flux extraction regions
    # x = 100
    # y = 100
    # area = x*y / 4.25e10
    # offset = 50
    #
    # pos_n = (len(val[0,:])/2.-1,len(val[0,:])/2.-1 + offset*len(val[0,:])/2/w)
    # pos_s = (len(val[0,:])/2.-1,len(val[0,:])/2.-1 - offset*len(val[0,:])/2/w)
    #
    # import matplotlib.patches as patches
    # ax.add_patch(patches.Rectangle((-x/2, -y), x, y, fill=False, edgecolor='lime'))
    # ax.add_patch(patches.Rectangle((-x/2, 0), x, y, fill=False, edgecolor='lime'))

    # plot the marker for center position by default or user input offset
    ax.plot([0],[-marker], '+', color='lime', markersize=10, mew=2)
    ax.set_xlim([-w,w])
    ax.set_ylim([-w,w])
    # ax.plot([0],[-10], '+', color='m', markersize=10, mew=2)
    print(w)

    # plot scalebar
    if scalebar != None:
        ax.plot([0.85*w-scalebar, 0.85*w], [-0.8*w, -0.8*w], color='w', linewidth=3)
        # add text
        ax.text(0.85*w-scalebar/2, -0.9*w, r'$\rm{'+str(scalebar)+"\,arcsec}$",
                color='w', fontsize=18, fontweight='bold', ha='center')

    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=16)
    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.
    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{Intensity\,['+unit+']}$',fontsize=16)
    cb.ax.tick_params('both', width=1.5, which='major', length=3)
    cb.ax.tick_params('both', width=1.5, which='minor', length=2)
    cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
    plt.setp(cb_obj,fontsize=18)
    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=18)
    for label in cb.ax.get_yticklabels():
        label.set_fontproperties(ticks_font)

    ax.set_xlabel(r'$\rm{RA\,Offset\,[arcsec]}$', fontsize=16)
    ax.set_ylabel(r'$\rm{Dec\,Offset\,[arcsec]}$', fontsize=16)

    # set the frame color
    ax.spines['bottom'].set_color('white')
    ax.spines['top'].set_color('white')
    ax.spines['left'].set_color('white')
    ax.spines['right'].set_color('white')

    ax.tick_params(axis='both', which='major', width=1.5, labelsize=18, color='white', length=5)
    ax.text(0.7,0.88,str(wave) + r'$\rm{\,\mu m}$',fontsize=20,color='white', transform=ax.transAxes)

    fig.savefig(plotdir+printname+'_image_'+str(wave)+'.pdf', format='pdf', dpi=300, bbox_inches='tight')
    fig.clf()
Ejemplo n.º 12
0
def hyperion_image(rtout, wave, plotdir, printname, dstar=178., group=0, marker=0,
                    size='full', convolve=False, unit=None):
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    import astropy.constants as const
    from hyperion.model import ModelOutput
    # Package for matching the colorbar
    from mpl_toolkits.axes_grid1 import make_axes_locatable

    pc = const.pc.cgs.value

    if unit == None:
        unit = r'$\rm{log(I_{\nu})\,[erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$'

    m = ModelOutput(rtout)

    # Extract the image.
    image = m.get_image(group=group, inclination=0, distance=dstar * pc, units='mJy')

    print np.shape(image.val)
    # Open figure and create axes
    fig = plt.figure(figsize=(8,8))
    ax = fig.add_subplot(111)

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - 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
    factor = 1
    # avoid zero in log
    # flip the image, because the setup of inclination is upside down
    val = image.val[::-1, :, iwav] * factor + 1e-30

    if convolve:
        from astropy.convolution import convolve, Gaussian2DKernel
        img_res = 2*w/len(val[:,0])
        kernel = Gaussian2DKernel(0.27/2.354/img_res)
        val = convolve(val, kernel)

    if size != 'full':
        pix_e2c = (w-size/2.)/w * len(val[:,0])/2
        val = val[pix_e2c:-pix_e2c, pix_e2c:-pix_e2c]
        w = size/2.

    # 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= -20, vmax= -15,
    #           cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1)
    im = ax.imshow(val,
              cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1)
    print val.max()

    # plot the marker for center position by default or user input offset
    ax.plot([0],[-marker], '+', color='ForestGreen', markersize=10, mew=2)
    ax.set_xlim([-w,w])
    ax.set_ylim([-w,w])
    # ax.plot([0],[-10], '+', color='m', markersize=10, mew=2)


    # 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.
    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(unit,fontsize=18)
    cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
    plt.setp(cb_obj,fontsize=14)
    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=14)
    for label in cb.ax.get_yticklabels():
        label.set_fontproperties(ticks_font)

    ax.set_xlabel(r'$\rm{RA\,Offset\,(arcsec)}$', fontsize=18)
    ax.set_ylabel(r'$\rm{Dec\,Offset\,(arcsec)}$', fontsize=18)

    ax.tick_params(axis='both', which='major', labelsize=18)
    ax.text(0.7,0.88,str(wave) + r'$\rm{\,\mu m}$',fontsize=20,color='white', transform=ax.transAxes)

    fig.savefig(plotdir+printname+'_image_'+str(wave)+'.pdf', format='pdf', dpi=300, bbox_inches='tight')
    fig.clf()
Ejemplo n.º 13
0
def azimuthal_simulation(rtout, beam_size, wave, dist=200., group=22):
	"""
	rtout: the filepath to the output file of Hyperion
	beam_size: the beam size used for the width of annulus
	dist: the physical distance to the source
	group: the group which contains image
	"""
	import numpy as np
	import matplotlib.pyplot as plt
	import astropy.constants as const
	from hyperion.model import ModelOutput

	# constant setup
	pc = const.pc.cgs.value
	au = const.au.cgs.value

	output = {'wave': wave, 'annuli': [], 'flux_annuli': []}

	# Read in the Hyperion output file
	m = ModelOutput(rtout)
	# get image
	image = m.get_image(group=5, inclination=0, distance=dist*pc, units='Jy')

	# Calculate the image width in arcsec given the distance to the source
	rmax = max(m.get_quantities().r_wall)
	w = np.degrees(rmax / image.distance) * 3600
	# grid of radii of annulus
	annuli = np.linspace(beam_size/2., np.floor((w-beam_size/2.)/beam_size)*beam_size, np.floor((w-beam_size/2.)/beam_size))	# plot

	fig = plt.figure(figsize=(8,6))
	ax = fig.add_subplot(111)

	# iternate through wavelength
	if type(wave) == int or type(wave) == float:
		wave = [wave]
	color_list = plt.cm.viridis(np.linspace(0, 1, len(wave)+1))
	for i in range(len(wave)):
		wav = wave[i]
		# Find the closest wavelength
		iwav = np.argmin(np.abs(wav - image.wav))
		# avoid zero when log, and flip the image
		val = image.val[::-1, :, iwav]
		# determine the center of the image
		npix = len(val[:,0])
		center = np.array([npix/2. + 0.5, npix/2. + 0.5])
		scale = 2*rmax/npix
		# create index array of the image
		x = np.empty_like(val)
		for j in range(len(val[0,:])):
			x[:,j] = j

		flux_annuli = np.empty_like(annuli)
		for k in range(len(annuli)):
			flux_annuli[k] = np.sum(val[(((x-center[0])**2+(x.T-center[1])**2)**0.5*2*w/npix >= annuli[k]-beam_size/2.) & \
										(((x-center[0])**2+(x.T-center[1])**2)**0.5*2*w/npix < annuli[k]+beam_size/2.)])
		output['annuli'].append(np.array(annuli))
		output['flux_annuli'].append(flux_annuli)
		flux_annuli = flux_annuli/np.nanmax(flux_annuli)

		ax.plot(np.log10(annuli*dist), np.log10(flux_annuli), 'o-', color=color_list[i], \
				markersize=3, mec='None', label=r'$\rm{'+str(wav)+'\,\mu m}$')
	ax.axvline(np.log10((w-beam_size/2.)*dist), linestyle='--', color='k')
	ax.axvline(np.log10(w*dist), linestyle='-', color='k')

	ax.legend(loc='best', fontsize=12, numpoints=1, ncol=2)
	ax.set_xlabel(r'$\rm{log(Radius)\,[AU]}$', fontsize=18)
	ax.set_ylabel(r'${\rm log(}F/F_{\rm max})$', fontsize=18)
	fig.gca().set_ylim(top=0.1)
	[ax.spines[axis].set_linewidth(1.5) for axis in ['top','bottom','left','right']]
	ax.minorticks_on()
	ax.tick_params('both',labelsize=18,width=1.5,which='major',pad=15,length=5)
	ax.tick_params('both',labelsize=18,width=1.5,which='minor',pad=15,length=2.5)

	fig.savefig('/Users/yaolun/test/annuli_profile.pdf', format='pdf', dpi=300, bbox_inches='tight')
	fig.clf()

	return output
Ejemplo n.º 14
0
def azimuthal_avg_radial_intensity(wave, rtout, plotname, dstar,
                                   annulus_width=10, rrange=[10,200], group=8, obs=None,
                                   other_obs=None):

    """
    The 'obs' option only works for Herschel PACS/SPIRE image.
    The 'obs' option now accept
    """

    import numpy as np
    import matplotlib as mpl
    # to avoid X server error
    mpl.use('Agg')
    from astropy.io import ascii, fits
    import matplotlib.pyplot as plt
    from photutils import aperture_photometry as ap
    from photutils import CircularAperture, CircularAnnulus
    from astropy import units as u
    from astropy.coordinates import SkyCoord
    from astropy import wcs
    from hyperion.model import ModelOutput
    import astropy.constants as const
    import os

    pc = const.pc.cgs.value
    AU = const.au.cgs.value

    # radial grid in arcsec
    # make the annulus center on
    r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.5
    # r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.55

    # source_center = '12 01 36.3 -65 08 53.0'

    def ExtractIntensityObs(rrange, annulus_width, obs):
        import numpy as np
        from astropy.io import fits
        from astropy.coordinates import SkyCoord
        from astropy import wcs
        from photutils import aperture_photometry as ap
        from photutils import CircularAperture, CircularAnnulus

        r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.5
        # r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.55

        imgpath = obs['imgpath']
        source_center = obs['source_center']
        # Read in data and set up coversions
        im_hdu = fits.open(imgpath)
        im = im_hdu[1].data
        wave = im_hdu[0].header['WAVELNTH']
        # error
        if (wave < 200.0) & (wave > 70.0):
            im_err = im_hdu[5].data
        elif (wave > 200.0) & (wave < 670.0):
            im_err = im_hdu[2].data
        else:
            im_err_exten = raw_input('The extension that includes the image error: ')
            im_err = im_hdu[int(im_err_exten)].data

        w = wcs.WCS(im_hdu[1].header)

        coord = SkyCoord(source_center, unit=(u.hourangle, u.deg))
        pixcoord = w.wcs_world2pix(coord.ra.degree, coord.dec.degree, 1)
        pix2arcsec = abs(im_hdu[1].header['CDELT1'])*3600.

        # determine whether need to convert the unit
        factor = 1
        print 'Image unit is ', im_hdu[1].header['BUNIT']
        if im_hdu[1].header['BUNIT'] != 'Jy/pixel':
            print 'Image unit is ', im_hdu[1].header['BUNIT']

            if im_hdu[1].header['BUNIT'] == 'MJy/sr':
                # convert intensity unit from MJy/sr to Jy/pixel
                factor = 1e6/4.25e10*abs(im_hdu[1].header['CDELT1']*im_hdu[1].header['CDELT2'])*3600**2
            else:
                factor = raw_input('What is the conversion factor to Jy/pixel?')

        I = np.empty_like(r[:-1])
        I_low = np.empty_like(r[:-1])
        I_hi = np.empty_like(r[:-1])
        I_err = np.empty_like(r[:-1])

        # for calculating the uncertainty from the variation within each annulus
        # construct the x- and y-matrix
        grid_x, grid_y = np.meshgrid(np.linspace(0,len(im[0,:])-1,len(im[0,:])),
                                     np.linspace(0,len(im[:,0])-1,len(im[:,0])))

        grid_dist = ((grid_x-pixcoord[0])**2+(grid_y-pixcoord[1])**2)**0.5

        # iteration
        for ir in range(len(r)-1):
            aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec, r_out=r[ir+1]/pix2arcsec)
            phot = ap(im, aperture, error=im_err)
            I[ir] = phot['aperture_sum'].data * factor / aperture.area()

            # uncertainty
            im_dum = np.where((grid_dist < r[ir+1]/pix2arcsec) & (grid_dist >= r[ir]/pix2arcsec), im, np.nan)

            # estimate the uncertainty by offsetting the annulus by +/- 1 pixel
            offset = -1
            if r[ir]/pix2arcsec + offset < 0:
                offset = -r[ir]/pix2arcsec
            aperture = CircularAnnulus((pixcoord[0],pixcoord[1]),
                                r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset)
            phot = ap(im, aperture, error=im_err)
            I_low[ir] = phot['aperture_sum'].data * factor / aperture.area()

            offset = 1
            aperture = CircularAnnulus((pixcoord[0],pixcoord[1]),
                                r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset)
            phot = ap(im, aperture, error=im_err)
            I_hi[ir] = phot['aperture_sum'].data * factor / aperture.area()

        I_err = (abs(I_low - I) + abs(I_hi - I))/2.

        return r, I, I_err

    if obs != None:
        I_obs = []
        for o in obs:
            if 'label' not in o.keys():
                label_dum = r'$\rm{observation}$'
                color_dum = 'g'
                linestyle_dum = '-'
                rrange_dum = rrange
                annulus_width_dum = annulus_width
            else:
                label_dum = o['label']
                color_dum = o['plot_color']
                linestyle_dum = o['plot_linestyle']
                rrange_dum = o['rrange']
                annulus_width_dum = o['annulus_width']

            r_dum, I_dum, I_err_dum = ExtractIntensityObs(rrange_dum, annulus_width_dum, o)
            # determine the label
            I_obs.append({'imgpath':o['imgpath'], 'r':r_dum, 'I':I_dum, 'I_err':I_err_dum, 'label': label_dum,
                          'plot_color':color_dum, 'plot_linestyle':linestyle_dum})

        # The first image should be the one to be compared primarily, and written out
        I = I_obs[0]['I']
        I_err = I_obs[0]['I_err']
        imgpath = I_obs[0]['imgpath']
        #

    # read in from RTout
    rtout = ModelOutput(rtout)

    im = rtout.get_image(group=group, inclination=0, distance=dstar*pc, units='Jy', uncertainties=True)
    factor = 1

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - im.wav))
    # avoid zero when log, and flip the image
    val = im.val[::-1, :, iwav]
    unc = im.unc[::-1, :, iwav]

    w = np.degrees(max(rtout.get_quantities().r_wall) / im.distance) * 3600
    npix = len(val[:,0])
    pix2arcsec = 2*w/npix

    I_sim = np.empty_like(r[:-1])
    I_sim_hi = np.empty_like(r[:-1])
    I_sim_low = np.empty_like(r[:-1])
    I_sim_err = np.empty_like(r[:-1])

    # for calculating the uncertainty from the variation within each annulus
    # construct the x- and y-matrix
    grid_x, grid_y = np.meshgrid(np.linspace(0,npix-1,npix),
                                 np.linspace(0,npix-1,npix))

    dist_x = abs(grid_x - ((npix-1)/2.))
    dist_y = abs(grid_y - ((npix-1)/2.))

    grid_dist = (dist_x**2+dist_y**2)**0.5

    # iteration
    for ir in range(len(r)-1):
        aperture = CircularAnnulus((npix/2.+0.5, npix/2.+0.5),
                            r_in=r[ir]/pix2arcsec, r_out=r[ir+1]/pix2arcsec)
        phot = ap(val, aperture, error=unc)
        I_sim[ir] = phot['aperture_sum'].data / aperture.area()

        # uncertainty
        im_dum = np.where((grid_dist < r[ir+1]/pix2arcsec) & (grid_dist >= r[ir]/pix2arcsec), val, np.nan)
        # I_sim_err[ir] = phot['aperture_sum_err'].data / aperture.area()
        # I_sim_err[ir] = (np.nanstd(im_dum)**2+phot['aperture_sum_err'].data**2)**0.5 * factor / aperture.area()

        offset = -1
        aperture = CircularAnnulus((npix/2.+0.5, npix/2.+0.5),
                            r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset)
        phot = ap(val, aperture, error=unc)
        I_sim_low[ir] = phot['aperture_sum'].data * factor / aperture.area()

        offset = 1
        aperture = CircularAnnulus((npix/2.+0.5, npix/2.+0.5),
                            r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset)
        phot = ap(val, aperture, error=unc)
        I_sim_hi[ir] = phot['aperture_sum'].data * factor / aperture.area()

    I_sim_err = (abs(I_sim_low - I_sim)+ abs(I_sim_hi - I_sim))/2.


    if obs != None:
        # write the numbers into file
        foo = open(plotname+'_radial_profile_'+str(wave)+'um.txt', 'w')
        # print some header info
        foo.write('# wavelength '+str(wave)+' um \n')
        foo.write('# image file '+os.path.basename(imgpath)+' \n')
        foo.write('# annulus width '+str(annulus_width)+' arcsec \n')
        # write profiles
        foo.write('r_in \t I \t I_err \t I_sim \t I_sim_err \n')
        foo.write('# [arcsec] \t [Jy/pixel] \t [Jy/pixel] \t [Jy/pixel] \t [Jy/pixel] \n')
        for i in range(len(I)):
            foo.write('%f \t %e \t %e \t %e \t %e \n' % (r[i]+annulus_width/2., I[i], I_err[i], I_sim[i], I_sim_err[i]))
        foo.close()
    else:
        # write the numbers into file
        foo = open(plotname+'_radial_profile_'+str(wave)+'um.txt', 'w')
        # print some header info
        foo.write('# wavelength '+str(wave)+' um \n')
        foo.write('# annulus width '+str(annulus_width)+' arcsec \n')
        # write profiles
        foo.write('r_in \t I_sim \t I_sim_err \n')
        foo.write('# [arcsec] \t [Jy/pixel] \t [Jy/pixel] \n')
        for i in range(len(I_sim)):
            foo.write('%f \t %e \t %e \n' % (r[i]+annulus_width/2., I_sim[i], I_sim_err[i]))
        foo.close()

    # plot
    fig = plt.figure(figsize=(8,6))
    ax = fig.add_subplot(111)

    I_sim_hi = np.log10((I_sim+I_sim_err)/I_sim.max())-np.log10(I_sim/I_sim.max())
    I_sim_low = np.log10(I_sim/I_sim.max())-np.log10((I_sim-I_sim_err)/I_sim.max())
    i_sim = ax.errorbar(np.log10(r[:-1]*dstar), np.log10(I_sim/I_sim.max()), color='b',
                    yerr=(I_sim_low, I_sim_hi), marker='o', linestyle='-', mec='None', markersize=5,
                    ecolor='b', elinewidth=1.5, capthick=1.5, barsabove=True)

    if obs != None:
        plot_profile = []
        plot_label = []
        for o in I_obs:
            I_hi = np.log10((o['I']+o['I_err'])/o['I'].max())-np.log10(o['I']/o['I'].max())
            I_low = np.log10(o['I']/o['I'].max())-np.log10((o['I']-o['I_err'])/o['I'].max())
            i = ax.errorbar(np.log10(o['r'][:-1]*dstar), np.log10(o['I']/o['I'].max()), color=o['plot_color'],
                            yerr=(I_low, I_hi), marker='o', linestyle=o['plot_linestyle'], mec='None', markersize=5,
                            ecolor=o['plot_color'], elinewidth=1.5, capthick=1.5, barsabove=True)
            plot_profile.append(i)
            plot_label.append(o['label'])

        plot_profile.append(i_sim)
        plot_label.append(r'$\rm{simulation}$')
        ax.legend(plot_profile, plot_label,
                  fontsize=16, numpoints=1, loc='best')
    else:
        ax.legend([i_sim], [r'$\rm{simulation}$'], fontsize=16, numpoints=1, loc='best')

    # limit radius
    ax.axvline([np.log10(100*dstar)], color='k', linestyle='--', linewidth=1)
    #
    [ax.spines[axis].set_linewidth(1.5) for axis in ['top','bottom','left','right']]
    ax.minorticks_on()
    ax.tick_params('both',labelsize=18,width=1.5,which='major',pad=10,length=5)
    ax.tick_params('both',labelsize=18,width=1.5,which='minor',pad=10,length=2.5)
    ax.set_xlabel(r'$\rm{log(\it{b})\,[\rm{AU}]}$', fontsize=18)
    ax.set_ylabel(r'$\rm{log(I\,/\,I_{max})}$', fontsize=18)

    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=18)
    for label in ax.get_xticklabels():
        label.set_fontproperties(ticks_font)
    for label in ax.get_yticklabels():
        label.set_fontproperties(ticks_font)

    fig.savefig(plotname+'_radial_profile_'+str(wave)+'um.pdf', format='pdf', dpi=300, bbox_inches='tight')
    fig.clf()
Ejemplo n.º 15
0
class YSOModelSim(object):
	
	def __init__(self,name,folder,T=9000,M_sun=5.6,L_sun=250,disk_mass=0.01,disk_rmax=100, 
		env=True,env_type='power',rc=400,mdot=1e-8,env_mass=0.1,env_rmin=30,env_rmax=5000,cav=True,cav_r0=500,cav_rho_0=8e-24,cav_theta=25,env_power=-1.5,
		Npix=149,angles=[20.,45.,60.,80],angles2=[60.,60.,60.,60.], amb_dens=8e-24, disk="Flared",disk_rmin=1., amb_rmin=1., amb_rmax=1000., innerdustfile='OH5.hdf5',
		outerdustfile='d03_5.5_3.0_A.hdf5',beta=1.1):
		self.name=name
		self.folder=folder
		self.T=T
		self.M_sun=M_sun*msun
		self.L_sun=L_sun*lsun
		self.disk_mass=disk_mass*msun
		self.disk_rmax=disk_rmax*au
		self.disk_rmin=disk_rmin*au
		self.disk_h_0 = OptThinRadius(1600)
		self.env=env
		self.disk=disk
		self.env_type=env_type
		self.env_mass=env_mass*msun
		self.env_rmin=env_rmin*au
		self.env_rmax=env_rmax*au
		self.mdot=mdot #*msun/yr*self.M_sun # disk accretion rate
		self.rc=rc*au
		self.cav=cav
		self.cav_rho_0=cav_rho_0
		self.cav_r0=cav_r0*au
		self.cav_theta=cav_theta
		self.Npix=Npix
		self.angles=angles
		self.angles2=angles2
		self.amb_dens=amb_dens
		self.amb_rmin=amb_rmin
		self.amb_rmax=amb_rmax*au
		self.env_power=env_power
		self.dustfile=innerdustfile
		self.dustfile_out=outerdustfile
		self.limval = max(self.env_rmax,1000*au)
		self.beta = beta

	def modelDump(self):
		sp.call('rm %s.mod ' % (self.folder+self.name),shell=True)
		pickle.dump(self,open(self.folder+self.name+'.mod','wb'))
		time.sleep(2)

	def modelPrint(self):
		#string= self.folder+ self.name+'\n'
		string="T="+str(self.T)+"K"+'\n'
		string+= "M="+str(self.M_sun/msun)+'Msun'+'\n'
		string+= "L="+str(self.L_sun/lsun)+'Lsun'+'\n'
		string+= "Disk="+str(self.disk)+'\n'
		string+= "Disk_mass="+str(self.disk_mass/msun)+'Msun'+'\n'
		string+= "Disk_rmax="+str(self.disk_rmax/au)+'AU'+'\n'
		string+= "Disk_rmin="+str(self.disk_rmin/au)+'AU'+'\n'
		string+= "env="+str(self.env)+'\n'
		string+= "env_type="+self.env_type+'\n'
		string+= "env_mass="+str(self.env_mass/msun)+'Msun'+'\n'
		string+= "env_rmax="+str(self.env_rmax/au)+'AU'+'\n'
		string+= "env_rmin="+str(self.env_rmin/au)+'AU'+'\n'
		if self.env_type == 'ulrich' and self.env==True:
			string+= "mass_ulrich="+str((8.*np.pi*self.env_rho_0*self.rc**3*pow(self.env_rmax/self.rc,1.5)/(3.*np.sqrt(2)))/msun)+'Msun'+'\n'
		string+= "mdot="+str(self.mdot)+'Msun/yr'+'\n' # (only if env_type="Ulrich")
		string+= "rc="+str(self.rc/au)+'AU'+'\n' # (only if env_type="Ulrich")
		string+= "cav="+str(self.cav)+'\n'
		string+= "cav_theta="+str(self.cav_theta)+'\n'
		string+= "cav_r0="+str(self.cav_r0/au)+'\n'
		string+= "env_power="+str(self.env_power)+'\n'
		string+= "disk_h_0="+str(self.disk_h_0)+'\n'
		string+= "dustfile="+self.dustfile+'\n'
		string+= "dustfile_out="+self.dustfile_out+'\n'
		string+= "amb_dens="+str(self.amb_dens)+'\n'
		string+= "amb_rmin="+str(self.amb_rmin)+'\n'
		string+= "amb_rmax="+str(self.amb_rmax/au)+'\n'
		string+= "angles="+str(self.angles)+'\n'
		print string
		return string

	def dust_gen(self,dustfile,dustfile_out='d03_5.5_3.0_A.hdf5'):
		### first, we need to load Tracy's dust files and manipulate them to feed to Hyperion
		### wavelength (microns),Cext,Csca,Kappa,g,pmax,theta (ignored)
		### albedo = Csca/Cext
		### opacity kappa is in cm^2/gm, dust_gas extinction opactity (absorption+scattering) - assumes gas-to=dust raio of 100
		### see Whitney et al. 2003a
		
#		tracy_dust = np.loadtxt('Tracy_models/OH5.par')

#		### format for dust: d = HenyeyGreensteinDust(nu, albedo, chi, g, p_lin_max)
#		nu = const.c.value/ (tracy_dust[:,0]*1e-6)
#		albedo = tracy_dust[:,2]/tracy_dust[:,1]
#		chi = tracy_dust[:,3]
#		g = tracy_dust[:,4]
#		p_lin_max = tracy_dust[:,5]

#		### flip the table to have an increasing frequency
#		nu = nu[::-1]
#		albedo = albedo[::-1]
#		chi=chi[::-1]
#		g=g[::-1]
#		p_lin_max=p_lin_max[::-1]

#		### create the dust model
#		d = HenyeyGreensteinDust(nu, albedo, chi, g, p_lin_max)
#		d.optical_properties.extrapolate_wav(0.001,1.e7)
#		d.plot('OH5.png')
#		d.write('OH5.hdf5')
		
		self.d = SphericalDust()
		self.d.read(dustfile)
		self.d.plot(str(dustfile.split(',')[:-1])+'.png')
		self.d_out = SphericalDust()
		self.d_out.read(dustfile_out)
		#self.d_out.read(dustfile)
		self.d_out.plot(str(dustfile_out.split(',')[:-1])+'.png')

	def initModel(self):
		### Use Tracy parameter file to set up the model 
		self.dust_gen(self.dustfile,self.dustfile_out)
		mi = AnalyticalYSOModel()

		mi.star.temperature = self.T
		mi.star.mass = self.M_sun
		mi.star.luminosity = self.L_sun
		mi.star.radius=np.sqrt(mi.star.luminosity/(4.0*np.pi*sigma*mi.star.temperature**4))
		#m.star.luminosity = 4.0*np.pi*m.star.radius**2*sigma*m.star.temperature**4
		print mi.star.luminosity/lsun
		self.luminosity=mi.star.luminosity/lsun

		if self.disk=="Flared":
			print "Adding flared disk"
			disk = mi.add_flared_disk()
			disk.dust=self.d
			if self.dustfile == 'd03_5.5_3.0_A.hdf5':
				disk.mass=self.disk_mass/100.
			else: disk.mass=self.disk_mass
			disk.rmin=OptThinRadius(1600) #self.disk_rmin
			print "disk.rmin = ",disk.rmin,disk.rmin/au
			disk.rmax=self.disk_rmax
			disk.r_0 = self.disk_rmin
			disk.h_0 = disk.r_0/10. #self.disk_h_0*au
			disk.beta=self.beta
			disk.p = -1.
		elif self.disk=="Alpha":
			print "Adding alpha disk"
			disk = mi.add_alpha_disk()
			disk.dust=self.d
			if self.dustfile == 'd03_5.5_3.0_A.hdf5':
				disk.mass=self.disk_mass/100.
			else: disk.mass=self.disk_mass
			disk.rmin=OptThinRadius(1600)
			disk.rmax=self.disk_rmax
			disk.r_0 = self.disk_rmin
			disk.h_0 = disk.r_0/10. #self.disk_h_0*au
			disk.beta=1.1
			disk.p = -1
			disk.mdot=self.mdot
			disk.star = mi.star
			
		#print 'Disk density:',disk.rho_0

		
		if self.env==True and self.env_type=='power':
			envelope=mi.add_power_law_envelope()
			envelope.dust=self.d_out
			envelope.r_0=self.env_rmin
			#envelope.r_0 = OptThinRadius(1600)
			if self.dustfile_out == 'd03_5.5_3.0_A.hdf5':
				envelope.mass=self.env_mass/100.
			else: envelope.mass=self.env_mass
			envelope.rmin=self.env_rmin
			envelope.rmax=self.env_rmax
			envelope.power=self.env_power
			#print 'Envelope rho:',envelope.rho_0
		elif self.env==True and self.env_type=='ulrich':
			envelope=mi.add_ulrich_envelope()
			envelope.dust=self.d_out
			envelope.mdot=1e-6*msun/yr # has little impact on the fluxes, so fixed
			envelope.rc=self.rc
			envelope.rmin=self.env_rmin
			envelope.rmax=self.env_rmax
		if self.env==True:
			self.env_rho_0 = envelope.rho_0
			print 'Envelope rho:',envelope.rho_0

		#print "Rho_0 = ",envelope.rho_0
		if self.cav==True:
			cavity=envelope.add_bipolar_cavity()
			cavity.dust=self.d_out
			cavity.power=1.5
			cavity.cap_to_envelope_density=True ### prevents the cavity density to go above the envelope's density
			cavity.r_0=self.cav_r0
			cavity.theta_0=self.cav_theta
			cavity.rho_0=self.cav_rho_0 #in g/cm^3
			cavity.rho_exp=0.0
			
		
#		if self.env==True:
#			ambient=mi.add_ambient_medium(subtract=[envelope,disk])
#		if self.dustfile_out == 'd03_5.5_3.0_A.hdf5':
#			ambient.rho=self.amb_dens/100.
#		else: ambient.rho=self.amb_dens
#		ambient.rmin=OptThinRadius(1600.)
#		ambient.rmax=self.env_rmax
#		ambient.dust=self.d_out
		

		'''*** Grid parameters ***'''
		mi.set_spherical_polar_grid_auto(199,49,1)

		# Specify that the specific energy and density are needed
		mi.conf.output.output_specific_energy = 'last'
		mi.conf.output.output_density = 'last'


		'''**** Output Data ****'''
		image = mi.add_peeled_images(sed=True,image=False)
		image.set_wavelength_range(150,1,3000)
		#image.set_image_size(self.Npix,self.Npix)
		#image.set_image_limits(-self.limval,self.limval,-self.limval,self.limval)
		image.set_aperture_range(1,100000.*au,100000.*au)
		image.set_viewing_angles(self.angles,self.angles2)
		#image.set_track_origin('detailed')
		image.set_uncertainties(True)

		''' Use the modified random walk
		*** Advanced ***'
		YES = DIFFUSION  = Whether to use the diffusion
		'''
		if self.env==True:
			#mi.set_pda(True)
			mi.set_mrw(True)
		else:
			mi.set_pda(False)
			mi.set_mrw(False)

		# Use raytracing to improve s/n of thermal/source emission
		mi.set_raytracing(True)


		'''**** Preliminaries ****'''
		mi.set_n_initial_iterations(5)
		mi.set_n_photons(initial=1e6,imaging=1e6,raytracing_sources=1e5,raytracing_dust=1e6)
		mi.set_convergence(True, percentile=99.0, absolute=2.0, relative=1.1)
		self.m = mi

	def runModel(self):
		self.initModel()
		self.m.write(self.folder+self.name+'.rtin')
		self.m.run(self.folder+self.name+'.rtout', mpi=True,n_processes=6)

	def plotData(self,ax,sourcename):
		if sourcename != 'None':
			folder_export="/n/a2/mrizzo/Dropbox/SOFIA/Processed_Data/"
			sourcetable = pickle.load(open(folder_export+"totsourcetable_fits.data","r"))

			markers = ['v','p','D','^','h','o','*','x','d','<']
			TwoMASS = ['j','h','ks']
			uTwoMASS = ["e_"+col for col in TwoMASS]
			wlTwoMASS = [1.3,1.6,2.2]
			colTwoMASS = colors[0]
			markerTwoMASS = markers[0]
			labelTwoMASS = '2MASS'
			Spitzer = ['i1','i2','i3','i4','m1','m2']
			uSpitzer = ["e_"+col for col in Spitzer]
			wlSpitzer = [3.6,4.5,5.8,8.,24,70]
			colSpitzer = colors[1]
			markerSpitzer = markers[1]
			labelSpitzer = 'Spitzer'
			WISE = ['w1','w2','w3','w4']
			uWISE = ["e_"+col for col in WISE]
			wlWISE = [3.4,4.6,12,22]
			colWISE = colors[2]
			labelWISE = 'WISE'
			markerWISE = markers[2]
			SOFIA = ['F11','F19','F31','F37']
			uSOFIA = ["e_"+col for col in SOFIA]
			wlSOFIA = [11.1,19.7,31.5,37.1]
			colSOFIA = colors[3]
			markerSOFIA = markers[3]
			labelSOFIA = 'SOFIA'
			IRAS = ['Fnu_12','Fnu_25','Fnu_60','Fnu_100']
			uIRAS = ["e_"+col for col in IRAS]
			wlIRAS = [12,25,60,100]
			colIRAS = colors[4]
			markerIRAS = markers[4]
			labelIRAS = 'IRAS'
			AKARI = ['S65','S90','S140','S160']
			uAKARI = ["e_"+col for col in AKARI]
			wlAKARI = [65,90,140,160]
			colAKARI = colors[5]
			markerAKARI = markers[5]
			labelAKARI = 'AKARI'
			ENOCH = ['Fp']
			uENOCH = ["e_"+col for col in ENOCH]
			wlENOCH = [1300]
			colENOCH = colors[6]
			markerENOCH = markers[6]
			labelENOCH = 'ENOCH'
			HERSCHEL = ['H70','H160','H250','H350','H500']
			uHERSCHEL = ["e_"+col for col in HERSCHEL]
			wlHERSCHEL = [70,160,250,350,500]
			colHERSCHEL = colors[7]
			markerHERSCHEL = markers[7]
			labelHERSCHEL = 'HERSCHEL'
			SCUBA = ['S450','S850','S1300']
			uSCUBA = ["e_"+col for col in SCUBA]
			wlSCUBA = [450,850,1300]
			colSCUBA = colors[8]
			markerSCUBA = markers[8]
			labelSCUBA = 'SCUBA'
			alpha=1
			sources = sourcetable.group_by('SOFIA_name')
			for key,sourcetable in zip(sources.groups.keys,sources.groups):
				if sourcename == sourcetable['SOFIA_name'][0]:	
					#print sourcetable['SOFIA_name'][0]
					p.plotData(ax,sourcetable,markerTwoMASS,TwoMASS,uTwoMASS,wlTwoMASS,colTwoMASS,labelTwoMASS,alpha)
					p.plotData(ax,sourcetable,markerSpitzer,Spitzer,uSpitzer,wlSpitzer,colSpitzer,labelSpitzer,alpha)
					p.plotData(ax,sourcetable,markerWISE,WISE,uWISE,wlWISE,colWISE,labelWISE,alpha)
					p.plotData(ax,sourcetable,markerSOFIA,SOFIA,uSOFIA,wlSOFIA,colSOFIA,labelSOFIA,alpha)
					p.plotData(ax,sourcetable,markerIRAS,IRAS,uIRAS,wlIRAS,colIRAS,labelIRAS,alpha)
					p.plotData(ax,sourcetable,markerAKARI,AKARI,uAKARI,wlAKARI,colAKARI,labelAKARI,alpha)
					p.plotData(ax,sourcetable,markerENOCH,ENOCH,uENOCH,wlENOCH,colENOCH,labelENOCH,alpha)
					p.plotData(ax,sourcetable,markerHERSCHEL,HERSCHEL,uHERSCHEL,wlHERSCHEL,colHERSCHEL,labelHERSCHEL,alpha)
					p.plotData(ax,sourcetable,markerSCUBA,SCUBA,uSCUBA,wlSCUBA,colSCUBA,labelSCUBA,alpha)

	def calcChi2(self,dist_pc=140,extinction=0, sourcename='Oph.1'):
		self.dist=dist_pc*pc
		self.extinction=extinction
		chi = np.loadtxt('kmh94_3.1_full.chi')
		wav = np.loadtxt('kmh94_3.1_full.wav')
		Chi = interp1d(wav,chi,kind='linear')
		modelname = self.folder+self.name
		self.mo = ModelOutput(modelname+'.rtout')
		
		# get the sed of all inclination
		sed = self.mo.get_sed(aperture=-1, inclination='all', distance=self.dist,units='Jy')
				
		# calculate the optical depth at all wavelengths
		tau = self.extinction*Chi(sed.wav)/Chi(0.550)/1.086
		
		# calculate extinction values
		ext = np.array([np.exp(-tau) for i in range(sed.val.shape[0])])
		
		# apply extinction to model
		extinct_values = np.log10(sed.val.transpose()*ext.T)
		
		# data points and errors
		folder_export="/n/a2/mrizzo/Dropbox/SOFIA/Processed_Data/"
		sourcetable = pickle.load(open(folder_export+"totsourcetable_fits.data","r"))
		TwoMASS = ['j','h','ks']
		uTwoMASS = ["e_"+col for col in TwoMASS]
		wlTwoMASS = [1.3,1.6,2.2]
		labelTwoMASS = '2MASS'
		Spitzer = ['i1','i2','i3','i4']
		uSpitzer = ["e_"+col for col in Spitzer]
		wlSpitzer = [3.6,4.5,5.8,8.]
		labelSpitzer = 'Spitzer'
		SOFIA = ['F11','F19','F31','F37']
		uSOFIA = ["e_"+col for col in SOFIA]
		wlSOFIA = [11.1,19.7,31.5,37.1]
		labelSOFIA = 'SOFIA'
		sources = sourcetable.group_by('SOFIA_name')
		for key,source in zip(sources.groups.keys,sources.groups):
			if sourcename == source['SOFIA_name'][0]:	
				datapoints = source[TwoMASS+Spitzer+SOFIA]
				dataerrors = source[uTwoMASS+uSpitzer+uSOFIA]
				print p.nptable(datapoints),p.nptable(dataerrors)
				
				# calculate log10 of quantities required for chi squared
				logFnu = np.log10(p.nptable(datapoints))-0.5*(1./np.log(10.))*p.nptable(dataerrors)**2/p.nptable(datapoints)**2
				varlogFnu = (1./np.log(10)/p.nptable(datapoints))**2*p.nptable(dataerrors)**2
				print extinct_values,extinct_values.shape
				
				# for each inclination, calculate chi squared; need to interpolate to get model at required wavelengths
				Ninc = extinct_values.shape[1]
				chi2 = np.zeros(Ninc)
				wl=wlTwoMASS+wlSpitzer+wlSOFIA
				N = len(wl)
				for j in range(Ninc):
					interp_func = interp1d(sed.wav,extinct_values[:,j],kind='linear')
					interp_vals = interp_func(wl)
					chi2[j] = 1./N * np.sum((logFnu - interp_vals)**2/varlogFnu)
					
				print chi2

	def plotModel(self,dist_pc=140,inc=3,extinction=0,show=False,sourcename='Oph.1'):
		self.dist=dist_pc*pc
		self.inc=inc
		self.extinction=extinction
		modelname = self.folder+self.name
		self.mo = ModelOutput(modelname+'.rtout')

		#tracy_dust = np.loadtxt('Tracy_models/OH5.par')
		chi = np.loadtxt('kmh94_3.1_full.chi')
		wav = np.loadtxt('kmh94_3.1_full.wav')
		Chi = interp1d(wav,chi,kind='linear')



		fig = plt.figure(figsize=(20,14))
		ax=fig.add_subplot(2,3,1)
		sed = self.mo.get_sed(aperture=-1, inclination='all', distance=self.dist)
		#print tracy_dust[11,1],Cext(sed.wav[-1]),Cext(sed.wav[-1])/tracy_dust[11,1]
		tau = self.extinction*Chi(sed.wav)/Chi(0.550)/1.086
		#print Cext(sed.wav)/tracy_dust[11,1]
		ext = np.array([np.exp(-tau) for i in range(sed.val.shape[0])])
		#print tau,np.exp(-tau)
		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='black')
		ax.set_title(modelname+'_seds, Av='+str(self.extinction))
		ax.set_xlim(sed.wav.min(), 1300)
		ax.set_ylim(1e-13, 1e-7)
		ax.set_xlabel(r'$\lambda$ [$\mu$m]')
		ax.set_ylabel(r'$\lambda F_\lambda$ [ergs/cm$^2/s$]')
		self.plotData(ax,sourcename)
		ax.set_xscale('log')
		ax.set_yscale('log')

		#ax.set_ylabel(r'$F_{Jy}$ [Jy]')
		#plt.legend(loc=4)

		ax=fig.add_subplot(2,3,2)
		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist)
		ext=np.exp(-tau)
		ax.loglog(sed.wav, sed.val.transpose()*ext.T, lw=3,color='black',label='source_total')
		ax.set_xlim(sed.wav.min(), 1300)
		ax.set_ylim(1e-13, 1e-7)  ### for lamFlam
		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='source_emit')
		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='blue',label='source_emit')
		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='source_scat')
		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='teal',label='source_scat')
		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='dust_emit')
		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='red',label='dust_emit')
		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='dust_scat')
		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='orange',label='dust_scat')
		self.plotData(ax,sourcename)
		ax.set_xscale('log')
		ax.set_yscale('log')
		ax.set_title('seds_inc=inc')
		ax.set_xlabel(r'$\lambda$ [$\mu$m]')
		ax.set_ylabel(r'$\lambda F_\lambda$ [ergs/cm$^2/s$]')
		#ax.set_ylabel(r'$F_{Jy}$ [Jy]')
		leg = ax.legend(loc=4,fontsize='small')
		#leg = plt.gca().get_legend()
		#plt.setp(leg.get_text(),fontsize='small')
		# Extract the quantities
		g = self.mo.get_quantities()
		
		# Get the wall positions for r and theta
		rw, tw = g.r_wall / au, g.t_wall

		# Make a 2-d grid of the wall positions (used by pcolormesh)
		R, T = np.meshgrid(rw, tw)

		# Calculate the position of the cell walls in cartesian coordinates
		X, Z = R * np.sin(T), R * np.cos(T)

		# Make a plot in (x, z) space for different zooms
		from matplotlib.colors import LogNorm,PowerNorm
		# Make a plot in (r, theta) space
		ax = fig.add_subplot(2, 3, 3)
		if g.shape[-1]==2:
			c = ax.pcolormesh(X, Z, g['temperature'][0].array[0, :, :]+g['temperature'][1].array[0, :, :],norm=PowerNorm(gamma=0.5,vmin=1,vmax=500))
		else :
			c = ax.pcolormesh(X, Z, g['temperature'][0].array[0, :, :],norm=PowerNorm(gamma=0.5,vmin=1,vmax=500))
		#ax.set_xscale('log')
		#ax.set_yscale('log')
		ax.set_xlim(X.min(), X.max()/5.)
		ax.set_ylim(Z.min()/10., Z.max()/10.)
		ax.set_xlabel('x (au)')
		ax.set_ylabel('z (au)')
		#ax.set_yticks([np.pi, np.pi * 0.75, np.pi * 0.5, np.pi * 0.25, 0.])
		#ax.set_yticklabels([r'$\pi$', r'$3\pi/4$', r'$\pi/2$', r'$\pi/4$', r'$0$'])
		cb = fig.colorbar(c)
		ax.set_title('Temperature structure')
		cb.set_label('Temperature (K)')
		#fig.savefig(modelname+'_temperature_spherical_rt.png', bbox_inches='tight')


		ax = fig.add_subplot(2, 3, 4)
		if g.shape[-1]==2:
			c = ax.pcolormesh(X, Z, g['density'][0].array[0, :, :]+g['density'][1].array[0, :, :],norm=LogNorm(vmin=1e-22,vmax=g['density'][0].array[0, :, :].max()))
		else :
			c = ax.pcolormesh(X, Z, g['density'][0].array[0, :, :],norm=LogNorm(vmin=1e-22,vmax=g['density'][0].array[0, :, :].max()))
		#ax.set_xscale('log')
		#ax.set_yscale('log')
		ax.set_xlim(X.min(), X.max()/5.)
		ax.set_ylim(Z.min()/10., Z.max()/10.)
		ax.set_xlabel('x (au)')
		ax.set_ylabel('z (au)')
		ax.set_title('Density structure')
		cb = fig.colorbar(c)
		cb.set_label('Density (g/cm2)')

		### plot the convolved image with the 37 micron filter (manually set to slice 18 of the cube - this would change with wavelength coverage)
		ax = fig.add_subplot(2, 3, 5)
		self.image = self.mo.get_image(inclination=inc,distance=self.dist,units='Jy')
		fits.writeto(modelname+'_inc_'+str(inc)+'.fits',self.image.val.swapaxes(0,2).swapaxes(1,2),clobber=True)

		### need to convolve the image with a Gaussian PSF
		pix = 2.*self.limval/au/self.Npix # in AU/pix
		pix_asec = pix/(self.dist/pc) # in asec/pix
		airy_asec = 3.5 #asec
		airy_pix = airy_asec/pix_asec # in pix
		gauss_pix = airy_pix/2.35 # in Gaussian std 
		print "Gaussian std: ",gauss_pix

		from scipy.ndimage.filters import gaussian_filter as gauss
		#print [(i,sed.wav[i]) for i in range(len(sed.wav))]

		img37 = self.image.val[:,:,18]
		convol = gauss(img37,gauss_pix,mode='constant',cval=0.0)
		Nc = self.Npix/2
		hw = min(int(20./pix_asec),Nc) #(max is Nc)
		#ax.imshow(img37,norm=LogNorm(vmin=1e-20,vmax=img37.max()))
		#ax.imshow(img37,interpolation='nearest')
		#ax.imshow(convol,norm=LogNorm(vmin=1e-20,vmax=img37.max()))
		#ax.imshow(convol,interpolation='nearest',norm=LogNorm(vmin=1e-20,vmax=img37.max()))
		ax.imshow(convol[Nc-hw:Nc+hw,Nc-hw:Nc+hw],interpolation='nearest',origin='lower',cmap=plt.get_cmap('gray'))
		airy_disk = plt.Circle((airy_pix*1.3,airy_pix*1.3),airy_pix,color=colors[3])		
		ax.add_artist(airy_disk)
		ax.text(airy_pix*3,airy_pix*1.3/2.0,'SOFIA 37um Airy disk',color=colors[3])
		ax.set_title('Convolved image')
		fits.writeto(modelname+'_inc_'+str(inc)+'_convol37.fits',convol,clobber=True)

		### draw a cross-section of the image to show the spatial extension in linear scale, to compare with what we observe in the model.
		ax = fig.add_subplot(2, 3, 6)
		ax.plot(range(Nc-hw,Nc+hw),convol[Nc-hw:Nc+hw,Nc-1],label='cross-section 1')
		ax.plot(range(Nc-hw,Nc+hw),convol[Nc-1,Nc-hw:Nc+hw],label='cross-section 2')
		maxconvol = convol[Nc-hw:Nc+hw,Nc-1].max()
		gauss = np.exp( -(np.array(range(-hw,hw))**2 / (2. * gauss_pix**2)))
		gauss/= gauss.max()
		gauss*=maxconvol
		ax.plot(range(Nc-hw,Nc+hw),gauss,label='SOFIA beam')
		leg = ax.legend(loc=2,fontsize='small')
		#leg = plt.gca().get_legend()
		#plt.setp(leg.get_text(),fontsize='small')
		ax.set_title('Cross section at the center')

		string=self.modelPrint()
		fig.text(0.0,0.14,string+'Av='+str(self.extinction)+'\n'+'dist='+str(self.dist/pc)+'\n',color='r')
		fig.savefig(modelname+'.png', bbox_inches='tight',dpi=300)

		if show:
			plt.show()
			
	def plotSim(self,dist_pc=140,inc=3,extinction=0,show=False):
		self.dist=dist_pc*pc
		self.inc=inc
		self.extinction=extinction
		modelname = self.folder+self.name
		self.mo = ModelOutput(modelname+'.rtout')

		#tracy_dust = np.loadtxt('Tracy_models/OH5.par')
		#chi = np.loadtxt('kmh94_3.1_full.chi')
		#wav = np.loadtxt('kmh94_3.1_full.wav')
		#Chi = interp1d(wav,chi,kind='linear')



		fig = plt.figure(figsize=(20,14))
		ax=fig.add_subplot(1,3,1)
		sed = self.mo.get_sed(aperture=-1, inclination='all', distance=self.dist)
		#print tracy_dust[11,1],Cext(sed.wav[-1]),Cext(sed.wav[-1])/tracy_dust[11,1]
		#tau = self.extinction*Chi(sed.wav)/Chi(0.550)/1.086
		#print Cext(sed.wav)/tracy_dust[11,1]
		#ext = np.array([np.exp(-tau) for i in range(sed.val.shape[0])])
		#print tau,np.exp(-tau)
		ax.loglog(sed.wav, sed.val.transpose(), color='black')
		ax.set_title(modelname+'_seds, Av='+str(self.extinction))
		ax.set_xlim(sed.wav.min(), 1300)
		ax.set_ylim(1e-13, 1e-7)
		ax.set_xlabel(r'$\lambda$ [$\mu$m]')
		ax.set_ylabel(r'$\lambda F_\lambda$ [ergs/cm$^2/s$]')
		#self.plotData(ax,sourcename)
		ax.set_xscale('log')
		ax.set_yscale('log')

		#ax.set_ylabel(r'$F_{Jy}$ [Jy]')
		#plt.legend(loc=4)

#		ax=fig.add_subplot(2,3,2)
#		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist)
#		ext=np.exp(-tau)
#		ax.loglog(sed.wav, sed.val.transpose()*ext.T, lw=3,color='black',label='source_total')
#		ax.set_xlim(sed.wav.min(), 1300)
#		ax.set_ylim(1e-13, 1e-7)  ### for lamFlam
#		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='source_emit')
#		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='blue',label='source_emit')
#		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='source_scat')
#		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='teal',label='source_scat')
#		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='dust_emit')
#		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='red',label='dust_emit')
#		sed = self.mo.get_sed(aperture=-1, inclination=self.inc, distance=self.dist,component='dust_scat')
#		ax.loglog(sed.wav, sed.val.transpose()*ext.T, color='orange',label='dust_scat')
#		#self.plotData(ax,sourcename)
#		ax.set_xscale('log')
#		ax.set_yscale('log')
#		ax.set_title('seds_inc=inc')
#		ax.set_xlabel(r'$\lambda$ [$\mu$m]')
#		ax.set_ylabel(r'$\lambda F_\lambda$ [ergs/cm$^2/s$]')
#		#ax.set_ylabel(r'$F_{Jy}$ [Jy]')
#		leg = ax.legend(loc=4,fontsize='small')
		#leg = plt.gca().get_legend()
		#plt.setp(leg.get_text(),fontsize='small')
		# Extract the quantities
		g = self.mo.get_quantities()
		
		# Get the wall positions for r and theta
		rw, tw = g.r_wall / au, g.t_wall

		# Make a 2-d grid of the wall positions (used by pcolormesh)
		R, T = np.meshgrid(rw, tw)

		# Calculate the position of the cell walls in cartesian coordinates
		X, Z = R * np.sin(T), R * np.cos(T)

		# Make a plot in (x, z) space for different zooms
		from matplotlib.colors import LogNorm,PowerNorm
		# Make a plot in (r, theta) space
		ax = fig.add_subplot(1, 3, 2)
		if g.shape[-1]==2:
			c = ax.pcolormesh(X, Z, g['temperature'][0].array[0, :, :]+g['temperature'][1].array[0, :, :],norm=PowerNorm(gamma=0.5,vmin=1,vmax=500))
		else :
			c = ax.pcolormesh(X, Z, g['temperature'][0].array[0, :, :],norm=PowerNorm(gamma=0.5,vmin=1,vmax=500))
		#ax.set_xscale('log')
		#ax.set_yscale('log')
		ax.set_xlim(X.min(), X.max())
		ax.set_ylim(Z.min(), Z.max())
		ax.set_xlabel('x (au)')
		ax.set_ylabel('z (au)')
		#ax.set_yticks([np.pi, np.pi * 0.75, np.pi * 0.5, np.pi * 0.25, 0.])
		#ax.set_yticklabels([r'$\pi$', r'$3\pi/4$', r'$\pi/2$', r'$\pi/4$', r'$0$'])
		cb = fig.colorbar(c)
		ax.set_title('Temperature structure')
		cb.set_label('Temperature (K)')
		#fig.savefig(modelname+'_temperature_spherical_rt.png', bbox_inches='tight')


		ax = fig.add_subplot(1, 3, 3)
		if g.shape[-1]==2:
			c = ax.pcolormesh(X, Z, g['density'][0].array[0, :, :]+g['density'][1].array[0, :, :],norm=LogNorm(vmin=1e-22,vmax=g['density'][0].array[0, :, :].max()))
		else :
			c = ax.pcolormesh(X, Z, g['density'][0].array[0, :, :],norm=LogNorm(vmin=1e-22,vmax=g['density'][0].array[0, :, :].max()))
		#ax.set_xscale('log')
		#ax.set_yscale('log')
		ax.set_xlim(X.min(), X.max())
		ax.set_ylim(Z.min(), Z.max())
		ax.set_xlabel('x (au)')
		ax.set_ylabel('z (au)')
		ax.set_title('Density structure')
		cb = fig.colorbar(c)
		cb.set_label('Density (g/cm2)')

#		### plot the convolved image with the 37 micron filter (manually set to slice 18 of the cube - this would change with wavelength coverage)
#		ax = fig.add_subplot(2, 3, 5)
#		self.image = self.mo.get_image(inclination=inc,distance=self.dist,units='Jy')
#		fits.writeto(modelname+'_inc_'+str(inc)+'.fits',self.image.val.swapaxes(0,2).swapaxes(1,2),clobber=True)

#		### need to convolve the image with a Gaussian PSF
#		pix = 2.*self.limval/au/self.Npix # in AU/pix
#		pix_asec = pix/(self.dist/pc) # in asec/pix
#		airy_asec = 3.5 #asec
#		airy_pix = airy_asec/pix_asec # in pix
#		gauss_pix = airy_pix/2.35 # in Gaussian std 
#		print "Gaussian std: ",gauss_pix

#		from scipy.ndimage.filters import gaussian_filter as gauss
#		#print [(i,sed.wav[i]) for i in range(len(sed.wav))]

#		img37 = self.image.val[:,:,18]
#		convol = gauss(img37,gauss_pix,mode='constant',cval=0.0)
#		Nc = self.Npix/2
#		hw = min(int(20./pix_asec),Nc) #(max is Nc)
#		#ax.imshow(img37,norm=LogNorm(vmin=1e-20,vmax=img37.max()))
#		#ax.imshow(img37,interpolation='nearest')
#		#ax.imshow(convol,norm=LogNorm(vmin=1e-20,vmax=img37.max()))
#		#ax.imshow(convol,interpolation='nearest',norm=LogNorm(vmin=1e-20,vmax=img37.max()))
#		ax.imshow(convol[Nc-hw:Nc+hw,Nc-hw:Nc+hw],interpolation='nearest',origin='lower',cmap=plt.get_cmap('gray'))
#		airy_disk = plt.Circle((airy_pix*1.3,airy_pix*1.3),airy_pix,color=colors[3])		
#		ax.add_artist(airy_disk)
#		ax.text(airy_pix*3,airy_pix*1.3/2.0,'SOFIA 37um Airy disk',color=colors[3])
#		ax.set_title('Convolved image')
#		fits.writeto(modelname+'_inc_'+str(inc)+'_convol37.fits',convol,clobber=True)

#		### draw a cross-section of the image to show the spatial extension in linear scale, to compare with what we observe in the model.
#		ax = fig.add_subplot(2, 3, 6)
#		ax.plot(range(Nc-hw,Nc+hw),convol[Nc-hw:Nc+hw,Nc-1],label='cross-section 1')
#		ax.plot(range(Nc-hw,Nc+hw),convol[Nc-1,Nc-hw:Nc+hw],label='cross-section 2')
#		maxconvol = convol[Nc-hw:Nc+hw,Nc-1].max()
#		gauss = np.exp( -(np.array(range(-hw,hw))**2 / (2. * gauss_pix**2)))
#		gauss/= gauss.max()
#		gauss*=maxconvol
#		ax.plot(range(Nc-hw,Nc+hw),gauss,label='SOFIA beam')
#		leg = ax.legend(loc=2,fontsize='small')
#		#leg = plt.gca().get_legend()
#		#plt.setp(leg.get_text(),fontsize='small')
#		ax.set_title('Cross section at the center')

		string=self.modelPrint()
		fig.text(0.0,0.14,string+'Av='+str(self.extinction)+'\n'+'dist='+str(self.dist/pc)+'\n',color='r')
		fig.savefig(modelname+'.png', bbox_inches='tight',dpi=300)

		if show:
			plt.show()
Ejemplo n.º 16
0
def azimuthal_simulation(rtout, beam_size, wave, dist=200., group=22):
    """
	rtout: the filepath to the output file of Hyperion
	beam_size: the beam size used for the width of annulus
	dist: the physical distance to the source
	group: the group which contains image
	"""
    import numpy as np
    import matplotlib.pyplot as plt
    import astropy.constants as const
    from hyperion.model import ModelOutput

    # constant setup
    pc = const.pc.cgs.value
    au = const.au.cgs.value

    output = {'wave': wave, 'annuli': [], 'flux_annuli': []}

    # Read in the Hyperion output file
    m = ModelOutput(rtout)
    # get image
    image = m.get_image(group=5, inclination=0, distance=dist * pc, units='Jy')

    # Calculate the image width in arcsec given the distance to the source
    rmax = max(m.get_quantities().r_wall)
    w = np.degrees(rmax / image.distance) * 3600
    # grid of radii of annulus
    annuli = np.linspace(beam_size / 2.,
                         np.floor(
                             (w - beam_size / 2.) / beam_size) * beam_size,
                         np.floor((w - beam_size / 2.) / beam_size))  # plot

    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(111)

    # iternate through wavelength
    if type(wave) == int or type(wave) == float:
        wave = [wave]
    color_list = plt.cm.viridis(np.linspace(0, 1, len(wave) + 1))
    for i in range(len(wave)):
        wav = wave[i]
        # Find the closest wavelength
        iwav = np.argmin(np.abs(wav - image.wav))
        # avoid zero when log, and flip the image
        val = image.val[::-1, :, iwav]
        # determine the center of the image
        npix = len(val[:, 0])
        center = np.array([npix / 2. + 0.5, npix / 2. + 0.5])
        scale = 2 * rmax / npix
        # create index array of the image
        x = np.empty_like(val)
        for j in range(len(val[0, :])):
            x[:, j] = j

        flux_annuli = np.empty_like(annuli)
        for k in range(len(annuli)):
            flux_annuli[k] = np.sum(val[(((x-center[0])**2+(x.T-center[1])**2)**0.5*2*w/npix >= annuli[k]-beam_size/2.) & \
                   (((x-center[0])**2+(x.T-center[1])**2)**0.5*2*w/npix < annuli[k]+beam_size/2.)])
        output['annuli'].append(np.array(annuli))
        output['flux_annuli'].append(flux_annuli)
        flux_annuli = flux_annuli / np.nanmax(flux_annuli)

        ax.plot(np.log10(annuli*dist), np.log10(flux_annuli), 'o-', color=color_list[i], \
          markersize=3, mec='None', label=r'$\rm{'+str(wav)+'\,\mu m}$')
    ax.axvline(np.log10((w - beam_size / 2.) * dist),
               linestyle='--',
               color='k')
    ax.axvline(np.log10(w * dist), linestyle='-', color='k')

    ax.legend(loc='best', fontsize=12, numpoints=1, ncol=2)
    ax.set_xlabel(r'$\rm{log(Radius)\,[AU]}$', fontsize=18)
    ax.set_ylabel(r'${\rm log(}F/F_{\rm max})$', fontsize=18)
    fig.gca().set_ylim(top=0.1)
    [
        ax.spines[axis].set_linewidth(1.5)
        for axis in ['top', 'bottom', 'left', 'right']
    ]
    ax.minorticks_on()
    ax.tick_params('both',
                   labelsize=18,
                   width=1.5,
                   which='major',
                   pad=15,
                   length=5)
    ax.tick_params('both',
                   labelsize=18,
                   width=1.5,
                   which='minor',
                   pad=15,
                   length=2.5)

    fig.savefig('/Users/yaolun/test/annuli_profile.pdf',
                format='pdf',
                dpi=300,
                bbox_inches='tight')
    fig.clf()

    return output
Ejemplo n.º 17
0
m.set_spherical_polar_grid(r, t, p)

dens = zeros((nr - 1, nt - 1, np - 1)) + 1.0e-17

m.add_density_grid(dens, d)

source = m.add_spherical_source()
source.luminosity = lsun
source.radius = rsun
source.temperature = 4000.

m.set_n_photons(initial=1000000, imaging=0)
m.set_convergence(True, percentile=99., absolute=2., relative=1.02)

m.write("test_spherical.rtin")

m.run("test_spherical.rtout", mpi=False)

n = ModelOutput('test_spherical.rtout')

grid = n.get_quantities()

temp = grid.quantities['temperature'][0]

for i in range(9):
    plt.imshow(temp[i,:,:],origin="lower",interpolation="nearest", \
            vmin=temp.min(),vmax=temp.max())
    plt.colorbar()
    plt.show()
Ejemplo n.º 18
0
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()
Ejemplo n.º 19
0
def set_physical_props(yso, grids, cell_sizes, save_dir, oversample=3,
        dust_out=None, logger=get_logger(__name__)):
    """Calculate and write the physical properties of the model.

    Parameters:
        yso: the model parameters.
        grids: grids where the model will be evaluated
        template: filename of the *define_model.c* file.
        logger: logging system.
    """
    # Load temperature function
    if yso.params.get('DEFAULT', 'quantities_from'):
        hmodel = yso.params.get('DEFAULT', 'quantities_from')
        logger.info('Loading Hyperion model: %s', os.path.basename(hmodel))
        hmodel = ModelOutput(os.path.expanduser(hmodel))
        q = hmodel.get_quantities()
        temperature = np.sum(q['temperature'].array[1:], axis=0)
        r, th = q.r*u.cm, q.t*u.rad
        temp_func = get_temp_func(yso.params, temperature, r, th)
    elif dust_out is not None:
        logger.info('Loading Hyperion model: %s', os.path.basename(dust_out))
        hmodel = ModelOutput(os.path.expanduser(dust_out))
        q = hmodel.get_quantities()
        temperature = np.sum(q['temperature'].array[1:], axis=0)
        r, th = q.r*u.cm, q.t*u.rad
        temp_func = get_temp_func(yso.params, temperature, r, th)
    else:
        raise NotImplementedError

    # Open template
    fitslist = []
    # Start from smaller to larger grid
    for i,grid,cellsz in zip(range(len(grids))[::-1], grids, cell_sizes):
        print '='*80
        logger.info('Working on grid: %i', i)
        logger.info('Grid cell size: %i', cellsz)
        x = grid[0]['x'] * grid[1]['x']
        y = grid[0]['y'] * grid[1]['y']
        z = grid[0]['z'] * grid[1]['z']
        xi = np.unique(x)
        yi = np.unique(y)
        zi = np.unique(z)

        # Density and velocity
        dens, (vx, vy, vz), temp = phys_oversampled_cart(xi, yi, zi, yso,
                temp_func, oversample=oversample if i!=2 else 5, logger=logger)
        dens[dens.cgs<=0./u.cm**3] = 10./u.cm**3
        temp[np.isnan(temp.value)] = 2.7 * u.K

        # Replace the inner region by rebbining the previous grid
        if i<len(grids)-1:
            # Walls of central cells
            j = cell_sizes.index(cellsz)
            xlen = cell_sizes[j-1] * len(xprev) * u.au
            nxmid = int(xlen.value) / cellsz
            xw = np.linspace(-0.5*xlen.value, 0.5*xlen.value, nxmid+1) * u.au
            ylen = cell_sizes[j-1] * len(yprev) * u.au
            nymid = int(ylen.value) / cellsz
            yw = np.linspace(-0.5*ylen.value, 0.5*ylen.value, nymid+1) * u.au
            zlen = cell_sizes[j-1] * len(zprev) * u.au
            nzmid = int(zlen.value) / cellsz
            zw = np.linspace(-0.5*zlen.value, 0.5*zlen.value, nzmid+1) * u.au
            if nxmid==nymid==nzmid==0:
                logger.warning('The inner grid is smaller than current grid size')
            else:
                logger.info('The inner %ix%ix%i cells will be replaced', nxmid,
                        nymid, nzmid)

                # Rebin previous grid
                # Density
                vol_prev = (cell_sizes[j-1]*u.au)**3
                vol = (cellsz * u.au)**3
                N_cen = vol_prev.cgs * rebin_regular_nd(dens_prev.cgs.value, 
                        zprev.cgs.value, yprev.cgs.value, xprev.cgs.value, 
                        bins=(zw.cgs.value,yw.cgs.value,xw.cgs.value), 
                        statistic='sum') * dens_prev.cgs.unit
                dens_cen = N_cen / vol
                dens_cen = dens_cen.to(dens.unit)
                # Temperature
                T_cen = rebin_regular_nd(temp_prev.value * dens_prev.cgs.value,
                        zprev.cgs.value, yprev.cgs.value, xprev.cgs.value, 
                        bins=(zw.cgs.value,yw.cgs.value, xw.cgs.value), 
                        statistic='sum') * temp_prev.unit * dens_prev.cgs.unit
                T_cen = vol_prev.cgs * T_cen / N_cen.cgs
                T_cen = T_cen.to(temp.unit)

                # Replace
                dens[len(zi)/2-nzmid/2:len(zi)/2+nzmid/2,
                        len(yi)/2-nymid/2:len(yi)/2+nymid/2,
                        len(xi)/2-nxmid/2:len(xi)/2+nxmid/2] = dens_cen
                temp[len(zi)/2-nzmid/2:len(zi)/2+nzmid/2,
                        len(yi)/2-nymid/2:len(yi)/2+nymid/2,
                        len(xi)/2-nxmid/2:len(xi)/2+nxmid/2] = T_cen
        dens_prev = dens
        temp_prev = temp
        xprev = xi
        yprev = yi
        zprev = zi

        # Abundance
        abundance = yso.abundance(temp)
        
        # Linewidth
        amu = 1.660531e-24 * u.g
        atoms = yso.params.getfloat('Velocity', 'atoms')
        c_s2 = ct.k_B * temp / (atoms * amu)
        linewidth = np.sqrt(yso.params.getquantity('Velocity', 'linewidth')**2
                + c_s2)

        # Write FITS
        fitsnames = write_fits(os.path.expanduser(save_dir), 
                **{'temp%i'%i: temp.value, 'dens%i'%i: dens.cgs.value, 
                'vx%i'%i: vx.cgs.value, 'vy%i'%i: vy.cgs.value, 'vz%i'%i:
                vz.cgs.value, 'abn%i'%i: abundance,
                'lwidth%i'%i: linewidth.cgs.value})
        fitslist += fitsnames

    return fitslist
Ejemplo n.º 20
0
def inspect_output(rtout,plotdir,quantities=None):
	import numpy as np
	import hyperion as hp
	import os
	import matplotlib.pyplot as plt
	from hyperion.model import ModelOutput
	import astropy.constants as const
	import matplotlib as mat
	from matplotlib.colors import LogNorm


	# Constants setup
	c         = const.c.cgs.value
	AU        = const.au.cgs.value                         # Astronomical Unit       [cm]
	pc        = const.pc.cgs.value                         # Parsec                  [cm]
	MS        = const.M_sun.cgs.value                      # Solar mass              [g]
	LS        = const.L_sun.cgs.value                      # Solar luminosity        [erg/s]
	RS        = const.R_sun.cgs.value                      # Solar radius            [cm]
	G         = const.G.cgs.value                          # Gravitational constant  [cm^3/g/s^2]
	yr        = 60*60*24*365.                              # Years in seconds        [s]
	PI        = np.pi                                      # PI constant
	sigma     = const.sigma_sb.cgs.value                   # Stefan-Boltzmann constant 
	mh        = const.m_p.cgs.value + const.m_e.cgs.value  # Mass of Hydrogen atom   [g]

	# Get the dir path of rtout file
	indir = os.path.dirname(rtout)

	m = ModelOutput(rtout)
	grid = m.get_quantities()
	rc     = 0.5*(grid.r_wall[0:-1]+grid.r_wall[1:])
	thetac = 0.5*(grid.t_wall[0:-1]+grid.t_wall[1:])
	phic   = 0.5*(grid.p_wall[0:-1]+grid.p_wall[1:])

	print 'Only works for density now'
	if quantities == None:
		quantities = input('What quantity you want to take a look at? ')
	elif quantities == 'density':
		rho = grid[quantities][0].array.T
		rho2d = np.sum(rho**2,axis=2)/np.sum(rho,axis=2)

		# Read in TSC-only envelope
		rho_tsc = np.genfromtxt(indir+'/rhoenv.dat').T
		# extrapolate
		def poly(x, y, x0, deg=1):
		    import numpy as np
		    p = np.polyfit(x, y, deg)
		    y0 = 0
		    for i in range(0, len(p)):
		        y0 = y0 + p[i]*x0**(len(p)-i-1)
		    return y0

		print 'Warning: hard coded infall radius (3500 AU) is used for extrapolating TSC envelope'
		r_inf = 3500 * AU
		rhoenv = rho_tsc.copy()
		for ithetac in range(0, len(thetac)):
		    rho_dum = np.log10(rhoenv[(rc > 1.1*r_inf) & (np.isnan(rhoenv[:,ithetac]) == False),ithetac])
		    rc_dum = np.log10(rc[(rc > 1.1*r_inf) & (np.isnan(rhoenv[:,ithetac]) == False)])
		#     rho_dum_nan = np.log10(rhoenv[(rc > 1.1*r_inf) & (np.isnan(rhoenv[:,ithetac]) == True),ithetac])
		    rc_dum_nan = np.log10(rc[(rc > 1.1*r_inf) & (np.isnan(rhoenv[:,ithetac]) == True)])
		    for i in range(0, len(rc_dum_nan)):
		        rho_extrapol = poly(rc_dum, rho_dum, rc_dum_nan[i])
		        rhoenv[(np.log10(rc) == rc_dum_nan[i]),ithetac] = 10**rho_extrapol
		rho_tsc = rhoenv
		rho_tsc3d = np.empty_like(rho)
		for i in range(0, len(rho[0,0,:])):
		    rho_tsc3d[:,:,i] = rho_tsc
		rho_tsc2d = np.sum(rho_tsc3d**2,axis=2)/np.sum(rho_tsc3d,axis=2)

		rho2d_exp = np.hstack((rho2d,rho2d,rho2d[:,0:1]))
		thetac_exp = np.hstack((thetac-PI/2, thetac+PI/2, thetac[0]-PI/2))

		# Make the plot
		fig = plt.figure(figsize=(8,6))
		ax_env  = fig.add_subplot(111,projection='polar')

		zmin = 1e-22/mh
		cmap = 'jet'
		img_env = ax_env.pcolormesh(thetac_exp,rc/AU,rho2d_exp/mh,cmap=cmap,norm=LogNorm(vmin=zmin,vmax=np.nanmax(rho2d_exp/mh)))
		ax_env.pcolormesh(thetac_exp-PI,rc/AU,rho2d_exp/mh,cmap=cmap,norm=LogNorm(vmin=zmin,vmax=np.nanmax(rho2d_exp/mh)))

		ax_env.set_xlabel(r'$\mathrm{Polar~angle~(Degree)}$',fontsize=20)
		ax_env.set_ylabel(r'$\mathrm{Radius~(AU)}$',fontsize=20)
		ax_env.tick_params(labelsize=20)
		# ax_env.set_yticks(np.arange(0,R_env_max/AU,R_env_max/AU/5))
		ax_env.set_xticklabels([r'$\mathrm{90^{\circ}}$',r'$\mathrm{45^{\circ}}$',r'$\mathrm{0^{\circ}}$',r'$\mathrm{-45^{\circ}}$',\
								r'$\mathrm{-90^{\circ}}$',r'$\mathrm{-135^{\circ}}$',r'$\mathrm{180^{\circ}}$',r'$\mathrm{135^{\circ}}$'])
		ax_env.set_ylim([0,100])
		ax_env.grid(True)
		cb = fig.colorbar(img_env, pad=0.1)
		cb.ax.set_ylabel(r'$\mathrm{Averaged~Density~(cm^{-3})}$',fontsize=20)
		cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
		plt.setp(cb_obj,fontsize=20)
		fig.savefig(plotdir+'dust_density.png',format='png',dpi=300,bbox_inches='tight')
		fig.clf()

		# Radial density plot
		fig = plt.figure(figsize=(12,9))
		ax = fig.add_subplot(111)

		plot_grid = [0,19,39,59,79,99,119,139,159,179,199]
        plot_grid = [0,39,79,119,159,199]
		# c_range = range(len(plot_grid))
		# cNorm  = mat.colors.Normalize(vmin=0, vmax=c_range[-1])
		# color map 1
		# cm1 = plt.get_cmap('Blues') 
		# scalarMap1 = mat.cm.ScalarMappable(norm=cNorm, cmap=cm1)
		# color map 2
		# cm2 = plt.get_cmap('Reds') 
		# scalarMap2 = mat.cm.ScalarMappable(norm=cNorm, cmap=cm2)
		alpha = np.linspace(0.3,1,len(plot_grid))

		for i in plot_grid:
			# colorVal1 = scalarMap1.to_rgba(c_range[plot_grid.index(i)])
			# colorVal2 = scalarMap2.to_rgba(c_range[plot_grid.index(i)])			
			rho_plot, = ax.plot(np.log10(rc/AU), np.log10(rho2d[:,i]),'o',color='b',linewidth=1.5, markersize=3, alpha=alpha[plot_grid.index(i)])
			tsc_only, = ax.plot(np.log10(rc/AU), np.log10(rho_tsc2d[:,i]),'-',color='r',linewidth=1.5, markersize=3, alpha=alpha[plot_grid.index(i)])
		# lg = plt.legend([wrong, wrong2, wrong_mid, wrong2_mid],\
		#                 [r'$\mathrm{Before~fixing~\theta~(pole)}$',r'$\mathrm{After~fixing~\theta~(pole)}$',r'$\mathrm{Before~fixing~\theta~(midplane)}$',r'$\mathrm{After~fixing~\theta~(midplane)}$'],\
		#                 fontsize=20, numpoints=1)
		ax.set_xlabel(r'$\mathrm{log(Radius)~(AU)}$',fontsize=20)
		ax.set_ylabel(r'$\mathrm{log(Density)~(g~cm^{-3})}$',fontsize=20)
		[ax.spines[axis].set_linewidth(1.5) for axis in ['top','bottom','left','right']]
		ax.minorticks_on()
		ax.tick_params('both',labelsize=18,width=1.5,which='major',pad=15,length=5)
		ax.tick_params('both',labelsize=18,width=1.5,which='minor',pad=15,length=2.5)
		ax.set_ylim([-23,-11])
		ax.set_xlim([np.log10(0.8),np.log10(10000)])

		fig.savefig(plotdir+'radial_density.pdf',format='pdf',dpi=300,bbox_inches='tight')
		fig.clf()
Ejemplo n.º 21
0
def hyperion_image(rtout,
                   wave,
                   plotdir,
                   printname,
                   dstar=200.,
                   group=0,
                   marker=0,
                   size='full',
                   convolve=False,
                   unit=None,
                   scalebar=None):
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    import astropy.constants as const
    from hyperion.model import ModelOutput
    # Package for matching the colorbar
    from mpl_toolkits.axes_grid1 import make_axes_locatable

    pc = const.pc.cgs.value

    if unit == None:
        unit = 'erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}'

    m = ModelOutput(rtout)

    # Extract the image.
    image = m.get_image(group=group,
                        inclination=0,
                        distance=dstar * pc,
                        units='MJy/sr')

    # print np.shape(image.val)
    # Open figure and create axes
    fig = plt.figure(figsize=(8, 8))
    ax = fig.add_subplot(111)

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - 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
    factor = 1
    # avoid zero in log
    # flip the image, because the setup of inclination is upside down
    val = image.val[::-1, :, iwav] * factor + 1e-30

    if convolve:
        from astropy.convolution import convolve, Gaussian2DKernel
        img_res = 2 * w / len(val[:, 0])
        kernel = Gaussian2DKernel(0.27 / 2.354 / img_res)
        val = convolve(val, kernel)

    if size != 'full':
        pix_e2c = (w - size / 2.) / w * len(val[:, 0]) / 2
        val = val[pix_e2c:-pix_e2c, pix_e2c:-pix_e2c]
        w = size / 2.

    # 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(
        val,
        # norm=mpl.colors.LogNorm(vmin=1.515e-01, vmax=4.118e+01),
        norm=mpl.colors.LogNorm(vmin=1e-04, vmax=1e+01),
        cmap=cmap,
        origin='lower',
        extent=[-w, w, -w, w],
        aspect=1)

    # draw the flux extraction regions
    # x = 100
    # y = 100
    # area = x*y / 4.25e10
    # offset = 50
    #
    # pos_n = (len(val[0,:])/2.-1,len(val[0,:])/2.-1 + offset*len(val[0,:])/2/w)
    # pos_s = (len(val[0,:])/2.-1,len(val[0,:])/2.-1 - offset*len(val[0,:])/2/w)
    #
    # import matplotlib.patches as patches
    # ax.add_patch(patches.Rectangle((-x/2, -y), x, y, fill=False, edgecolor='lime'))
    # ax.add_patch(patches.Rectangle((-x/2, 0), x, y, fill=False, edgecolor='lime'))

    # plot the marker for center position by default or user input offset
    ax.plot([0], [-marker], '+', color='lime', markersize=10, mew=2)
    ax.set_xlim([-w, w])
    ax.set_ylim([-w, w])
    # ax.plot([0],[-10], '+', color='m', markersize=10, mew=2)
    print(w)

    # plot scalebar
    if scalebar != None:
        ax.plot([0.85 * w - scalebar, 0.85 * w], [-0.8 * w, -0.8 * w],
                color='w',
                linewidth=3)
        # add text
        ax.text(0.85 * w - scalebar / 2,
                -0.9 * w,
                r'$\rm{' + str(scalebar) + "\,arcsec}$",
                color='w',
                fontsize=18,
                fontweight='bold',
                ha='center')

    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=16)
    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.
    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{Intensity\,[' + unit + ']}$', fontsize=16)
    cb.ax.tick_params('both', width=1.5, which='major', length=3)
    cb.ax.tick_params('both', width=1.5, which='minor', length=2)
    cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
    plt.setp(cb_obj, fontsize=18)
    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=18)
    for label in cb.ax.get_yticklabels():
        label.set_fontproperties(ticks_font)

    ax.set_xlabel(r'$\rm{RA\,Offset\,[arcsec]}$', fontsize=16)
    ax.set_ylabel(r'$\rm{Dec\,Offset\,[arcsec]}$', fontsize=16)

    # set the frame color
    ax.spines['bottom'].set_color('white')
    ax.spines['top'].set_color('white')
    ax.spines['left'].set_color('white')
    ax.spines['right'].set_color('white')

    ax.tick_params(axis='both',
                   which='major',
                   width=1.5,
                   labelsize=18,
                   color='white',
                   length=5)
    ax.text(0.7,
            0.88,
            str(wave) + r'$\rm{\,\mu m}$',
            fontsize=20,
            color='white',
            transform=ax.transAxes)

    fig.savefig(plotdir + printname + '_image_' + str(wave) + '.pdf',
                format='pdf',
                dpi=300,
                bbox_inches='tight')
    fig.clf()
import numpy as np
import glob

import matplotlib.pyplot as plt

from hyperion.model import ModelOutput

import sphviewer as sph
from sphviewer.tools import QuickView

run = '/blue/narayanan/c.lovell/simba/m100n1024/run_sed/snap_078_hires_orthogonal'

groupID = 3
fname = glob.glob('%s/gal_%i/snap078.galaxy*.rtout.sed' % (run, groupID))[0]
m = ModelOutput(fname)
_pf = m.get_quantities().to_yt()
ad = _pf.all_data()

_kpc = 3.08568025e+21
_temp = np.array(ad.to_dataframe('temperature'))
_mass = np.array(ad.to_dataframe('cell_mass'))
_density = np.array(ad.to_dataframe('density'))
_radius = np.array(ad.to_dataframe('radius')) / _kpc
_x = np.array(ad.to_dataframe('x')) / _kpc
_y = np.array(ad.to_dataframe('y')) / _kpc
_z = np.array(ad.to_dataframe('z')) / _kpc
_coods = np.squeeze(np.array([_x, _z, _y])).T

radius = 15  # kpc
mask = _radius < radius
print("radius:", radius)
Ejemplo n.º 23
0
import numpy as np
from hyperion.model import ModelOutput
import matplotlib.pyplot as plt
from yt.mods import write_bitmap, ColorTransferFunction

plt.rcParams['font.family'] = 'Arial'

# Read in model from Hyperion

m = ModelOutput('pla704850_lev7_129.rtout')
grid = m.get_quantities()

# Convert quantities to yt
pf = grid.to_yt()

# Instantiate the ColorTransferfunction.
tmin, tmax = 1.3, 2.3
tf_temp = ColorTransferFunction((tmin, tmax))
dmin, dmax = -20, -16
tf_dens = ColorTransferFunction((dmin, dmax))

# Set up the camera parameters: center, looking direction, width, resolution
c = (pf.domain_right_edge + pf.domain_left_edge) / 2.0

L = np.array([1.0, 1.0, 1.0])
W = 0.7 / pf["unitary"]
N = 512

# Create camera objects

cam_temp = pf.h.camera(c, L, W, N, tf_temp,
Ejemplo n.º 24
0
def plot_results(cli):
    
    file = filename(cli, "plot")
    file += ".rtout"
    
    #
    # Read in the model:
    #
    model = ModelOutput(file)
    
    los = [0 for k in range(3)]
    los[0] = '30degree'
    los[1] = '80degree'
    los[2] = '88degree'
    
    
    if(cli.mode == "images"):
        
        #
        # Extract the quantities
        #
        g = model.get_quantities()
        
        #
        # Get the wall positions:
        #
        ww = g.w_wall / pc
        zw = g.z_wall / pc
        pw = g.p_wall
        
        grid_Nw = len(ww) - 1
        grid_Nz = len(zw) - 1
        grid_Np = len(pw) - 1
        
        #
        # Graphics:
        #
        fig = plt.figure()
        
        Imaxp = [0 for i in range(5)]
        Imaxp[0] = 1e-15 # in W/cm^2
        Imaxp[1] = 1e-14 # in W/cm^2
        Imaxp[2] = 1e-15 # in W/cm^2
        Imaxp[3] = 1e-15 # in W/cm^2
        Imaxp[4] = 1e-18 # in W/cm^2
        
        for k in range(0, 3):
            if(cli.verbose):
                print("Group: ", k)
            
            image = model.get_image(distance=1e+7*pc, units='ergs/cm^2/s', inclination=0, component='total', group=k)
            #source_emit = model.get_image(distance=1e+7*pc, units='MJy/sr', inclination=0, component='source_emit', group=k)
            #dust_emit   = model.get_image(distance=1e+7*pc, units='MJy/sr', inclination=0, component='dust_emit'  , group=k)
            #source_scat = model.get_image(distance=1e+7*pc, units='MJy/sr', inclination=0, component='source_scat', group=k)
            #dust_scat   = model.get_image(distance=1e+7*pc, units='MJy/sr', inclination=0, component='dust_scat'  , group=k)
            
            if(cli.verbose):
                print(" Data cube: ", image.val.shape)
                print(" Wavelengths =", image.wav)
                print(" Uncertainties =", image.unc)
            
            image_Nx=image.val.shape[0]
            image_Ny=image.val.shape[1]
            Nwavelength=image.val.shape[2]
            
            if(cli.verbose):
                print(" Image Nx =", image_Nx)
                print(" Image Ny =", image_Ny)
                print(" Nwavelength =", Nwavelength)
            
            for i in range(0, Nwavelength):
                
                if(cli.verbose):
                    print(" Image #", i,":")
                    print("  Wavelength =", image.wav[i])
                
                image.val[:, :, i] *= 1e-4 # in W/m^2
                
                #Imin = np.min(image.val[:, :, i])
                #Imax = np.max(image.val[:, :, i])
                #Imax = Imaxp[i]
                #Imin = Imax/1e+20
                Imax = np.max(image.val[:, :, i])/5
                Imin = 0.0
                
                if(cli.verbose):
                    print("  Intensity min data values =", np.min(image.val[:, :, i]))
                    print("  Intensity max data values =", np.max(image.val[:, :, i]))
                    print("  Intensity min color-table =", Imin)
                    print("  Intensity max color-table =", Imax)
                
                #ax = fig.add_subplot(2, 1, 2)
                ax = fig.add_subplot(1, 1, 1)
                # 'hot', see http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps
                ax.imshow(image.val[:, :, i], vmin=Imin, vmax=Imax, cmap=plt.cm.hot, origin='lower')
                ax.set_xticks([0,100,200,300,400,500], minor=False)
                ax.set_yticks([0,100,200,300,400,500], minor=False)
                ax.set_xlabel('x (pixel)')
                ax.set_ylabel('y (pixel)')
                ax.set_title(str(image.wav[i]) + ' microns' + '\n' + los[k], y=0.88, x=0.5, color='white')
                
                #ax = fig.add_subplot(2, 1, 1)
                #ax.imshow([np.logspace(np.log10(Imin+1e-10),np.log10(Imax/10),100),np.logspace(np.log10(Imin+1e-10),np.log10(Imax/10),100)], vmin=Imin, vmax=Imax/10, cmap=plt.cm.gist_heat)
                #ax.set_xticks(np.logspace(np.log10(Imin+1e-10),np.log10(Imax/10),1), minor=False)
                ##ax.set_xticks(np.linspace(np.log10(Imin+1e-10),np.log10(Imax/10),10), minor=False)
                #ax.set_yticks([], minor=False)
                #ax.set_xlabel('flux (MJy/sr)')
                
                #x = plt.colorbar()
                #print(x)
                
                file = filename(cli, "plot")
                file += "_wavelength=" + str(image.wav[i]) + "micron_los=" + los[k] + ".png"
                
                fig.savefig(file, bbox_inches='tight')
                if(cli.verbose):
                    print("  The image graphics was written to", file)
                plt.clf()
    
    elif(cli.mode == "seds"):
        
        #
        # Graphics:
        #
        fig = plt.figure()
        
        for k in range(0, 3):
            if(cli.verbose):
                print("Group: ", k)
            
            sed = model.get_sed(distance=1e+7*pc, inclination=0, aperture=-1, group=k)
            #units='ergs/cm^2/s' # = default, if distance is specified
            ax = fig.add_subplot(1, 1, 1)
            ax.loglog(sed.wav, sed.val)
            ax.set_xlabel(r'$\lambda$ [$\mu$m]')
            ax.set_ylabel(r'$\lambda F_\lambda$ [ergs/s/cm$^2$]')
            ax.set_xlim(0.09, 1000.0)
            ax.set_ylim(1.e-13, 1.e-7)
            
            file = filename(cli, "plot")
            file += "_los=" + los[k] + ".png"
            fig.savefig(file)
            if(cli.verbose):
                print(" The sed graphics was written to", file)
            plt.clf()
        
        #
        # Data files:
        #
        for k in range(0, 3):
            sed = model.get_sed(distance=1e+7*pc, inclination=0, aperture=-1, group=k)
            file = filename(cli, "plot")
            file += "_los=" + los[k] + ".dat"
            sedtable = open(file, 'w')
            sedtable.write("# wavelength [micron] - flux [erg cm^-2 s^-1]\n")
            for lp in range(0, len(sed.wav)):
                l = len(sed.wav)-lp-1
                line = str("%.4e" % sed.wav[l]) + " " + str("%.4e" % sed.val[l]) + "\n"
                sedtable.write(line)
            sedtable.close()

    else:
        print("ERROR: The specified mode", mode, "is not available. Use 'images' or 'seds' only.")
Ejemplo n.º 25
0
from mpl_toolkits.axes_grid1 import make_axes_locatable

# constant setup
pc = const.pc.cgs.value

m = ModelOutput(filename)
image = m.get_image(group=22,
                    inclination=0,
                    distance=dist * pc,
                    units='MJy/sr')
# Find the closest wavelength
iwav = np.argmin(np.abs(wave - 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

# plot the image
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111)

cmap = plt.cm.CMRmap
im = ax.imshow(np.log10(val),
Ejemplo n.º 26
0
    def run_thermal_hyperion(self, nphot=1e6, mrw=False, pda=False, \
            niterations=20, percentile=99., absolute=2.0, relative=1.02, \
            max_interactions=1e8, mpi=False, nprocesses=None):
        d = []
        for i in range(len(self.grid.dust)):
            d.append(IsotropicDust( \
                    self.grid.dust[i].nu[::-1].astype(numpy.float64), \
                    self.grid.dust[i].albedo[::-1].astype(numpy.float64), \
                    self.grid.dust[i].kext[::-1].astype(numpy.float64)))

        m = HypModel()
        if (self.grid.coordsystem == "cartesian"):
            m.set_cartesian_grid(self.grid.w1*AU, self.grid.w2*AU, \
                    self.grid.w3*AU)
        elif (self.grid.coordsystem == "cylindrical"):
            m.set_cylindrical_polar_grid(self.grid.w1*AU, self.grid.w3*AU, \
                    self.grid.w2)
        elif (self.grid.coordsystem == "spherical"):
            m.set_spherical_polar_grid(self.grid.w1*AU, self.grid.w2, \
                    self.grid.w3)

        for i in range(len(self.grid.density)):
            if (self.grid.coordsystem == "cartesian"):
                m.add_density_grid(numpy.transpose(self.grid.density[i], \
                        axes=(2,1,0)), d[i])
            if (self.grid.coordsystem == "cylindrical"):
                m.add_density_grid(numpy.transpose(self.grid.density[i], \
                        axes=(1,2,0)), d[i])
            if (self.grid.coordsystem == "spherical"):
                m.add_density_grid(numpy.transpose(self.grid.density[i], \
                        axes=(2,1,0)), d[i])

        sources = []
        for i in range(len(self.grid.stars)):
            sources.append(m.add_spherical_source())
            sources[i].luminosity = self.grid.stars[i].luminosity * L_sun
            sources[i].radius = self.grid.stars[i].radius * R_sun
            sources[i].temperature = self.grid.stars[i].temperature

        m.set_mrw(mrw)
        m.set_pda(pda)
        m.set_max_interactions(max_interactions)
        m.set_n_initial_iterations(niterations)
        m.set_n_photons(initial=nphot, imaging=0)
        m.set_convergence(True, percentile=percentile, absolute=absolute, \
                relative=relative)

        m.write("temp.rtin")

        m.run("temp.rtout", mpi=mpi, n_processes=nprocesses)

        n = ModelOutput("temp.rtout")

        grid = n.get_quantities()

        self.grid.temperature = []
        temperature = grid.quantities['temperature']
        for i in range(len(temperature)):
            if (self.grid.coordsystem == "cartesian"):
                self.grid.temperature.append(numpy.transpose(temperature[i], \
                        axes=(2,1,0)))
            if (self.grid.coordsystem == "cylindrical"):
                self.grid.temperature.append(numpy.transpose(temperature[i], \
                        axes=(2,0,1)))
            if (self.grid.coordsystem == "spherical"):
                self.grid.temperature.append(numpy.transpose(temperature[i], \
                        axes=(2,1,0)))

        os.system("rm temp.rtin temp.rtout")
Ejemplo n.º 27
0
def azimuthal_avg_radial_intensity(wave,
                                   imgpath,
                                   source_center,
                                   rtout,
                                   plotname,
                                   annulus_width=10,
                                   group=8,
                                   dstar=200.):

    import numpy as np
    import matplotlib as mpl
    # to avoid X server error
    mpl.use('Agg')
    from astropy.io import ascii, fits
    import matplotlib.pyplot as plt
    from photutils import aperture_photometry as ap
    from photutils import CircularAperture, CircularAnnulus
    from astropy import units as u
    from astropy.coordinates import SkyCoord
    from astropy import wcs
    from hyperion.model import ModelOutput
    import astropy.constants as const
    import os

    pc = const.pc.cgs.value
    AU = const.au.cgs.value

    # source_center = '12 01 36.3 -65 08 53.0'

    # Read in data and set up coversions
    im_hdu = fits.open(imgpath)
    im = im_hdu[1].data
    # error
    if (wave < 200.0) & (wave > 70.0):
        im_err = im_hdu[5].data
    elif (wave > 200.0) & (wave < 670.0):
        im_err = im_hdu[5].data
    else:
        im_err_exten = raw_input(
            'The extension that includes the image error: ')
        im_err = im_hdu[int(im_err_exten)].data

    w = wcs.WCS(im_hdu[1].header)

    coord = SkyCoord(source_center, unit=(u.hourangle, u.deg))
    pixcoord = w.wcs_world2pix(coord.ra.degree, coord.dec.degree, 1)
    pix2arcsec = abs(im_hdu[1].header['CDELT1']) * 3600.
    # convert intensity unit from MJy/sr to Jy/pixel
    factor = 1e6 / 4.25e10 * abs(
        im_hdu[1].header['CDELT1'] * im_hdu[1].header['CDELT2']) * 3600**2

    # radial grid in arcsec
    # annulus_width = 10
    r = np.arange(10, 200, annulus_width, dtype=float)
    I = np.empty_like(r[:-1])
    I_err = np.empty_like(r[:-1])

    # iteration
    for ir in range(len(r) - 1):
        aperture = CircularAnnulus((pixcoord[0], pixcoord[1]),
                                   r_in=r[ir] / pix2arcsec,
                                   r_out=r[ir + 1] / pix2arcsec)
        #     print aperture.r_in
        phot = ap(im, aperture, error=im_err)
        I[ir] = phot['aperture_sum'].data * factor / aperture.area()
        I_err[ir] = phot['aperture_sum_err'].data * factor / aperture.area()
        # print r[ir], I[ir]

    # read in from RTout
    rtout = ModelOutput(rtout)
    # setting up parameters
    # dstar = 200.
    # group = 8
    # wave = 500.0

    im = rtout.get_image(group=group,
                         inclination=0,
                         distance=dstar * pc,
                         units='Jy',
                         uncertainties=True)

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - im.wav))
    # avoid zero when log, and flip the image
    val = im.val[::-1, :, iwav]
    unc = im.unc[::-1, :, iwav]

    w = np.degrees(max(rtout.get_quantities().r_wall) / im.distance) * 3600
    npix = len(val[:, 0])
    pix2arcsec = 2 * w / npix

    # radial grid in arcsec
    # annulus_width = 10
    r = np.arange(10, 200, annulus_width, dtype=float)
    I_sim = np.empty_like(r[:-1])
    I_sim_err = np.empty_like(r[:-1])

    # iteration
    for ir in range(len(r) - 1):
        aperture = CircularAnnulus((npix / 2. + 0.5, npix / 2. + 0.5),
                                   r_in=r[ir] / pix2arcsec,
                                   r_out=r[ir + 1] / pix2arcsec)
        #     print aperture.r_in
        phot = ap(val, aperture, error=unc)
        I_sim[ir] = phot['aperture_sum'].data / aperture.area()
        I_sim_err[ir] = phot['aperture_sum_err'].data / aperture.area()
        # print r[ir], I_sim[ir]

    # write the numbers into file
    foo = open(plotname + '_radial_profile_' + str(wave) + 'um.txt', 'w')
    # print some header info
    foo.write('# wavelength ' + str(wave) + ' um \n')
    foo.write('# image file ' + os.path.basename(imgpath) + ' \n')
    foo.write('# annulus width ' + str(annulus_width) + ' arcsec \n')
    # write profiles
    foo.write('r_in[arcsec] \t I \t I_err \t I_sim \t I_sim_err \n')
    for i in range(len(I)):
        foo.write('%f \t %e \t %e \t %e \t %e \n' %
                  (r[i], I[i], I_err[i], I_sim[i], I_sim_err[i]))
    foo.close()

    # plot
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(111)

    I_sim_hi = np.log10(
        (I_sim + I_sim_err) / I_sim.max()) - np.log10(I_sim / I_sim.max())
    I_sim_low = np.log10(I_sim / I_sim.max()) - np.log10(
        (I_sim - I_sim_err) / I_sim.max())

    I_hi = np.log10((I + I_err) / I.max()) - np.log10(I / I.max())
    I_low = np.log10(I / I.max()) - np.log10((I - I_err) / I.max())

    i_sim = ax.errorbar(np.log10(r[:-1] * dstar),
                        np.log10(I_sim / I_sim.max()),
                        yerr=(I_sim_low, I_sim_hi),
                        marker='o',
                        linestyle='-',
                        mec='None',
                        markersize=10)
    i = ax.errorbar(np.log10(r[:-1] * dstar),
                    np.log10(I / I.max()),
                    yerr=(I_low, I_hi),
                    marker='o',
                    linestyle='-',
                    mec='None',
                    markersize=10)

    ax.legend([i, i_sim], [r'$\rm{observation}$', r'$\rm{simulation}$'],
              fontsize=16,
              numpoints=1,
              loc='upper right')
    [
        ax.spines[axis].set_linewidth(1.5)
        for axis in ['top', 'bottom', 'left', 'right']
    ]
    ax.minorticks_on()
    ax.tick_params('both',
                   labelsize=18,
                   width=1.5,
                   which='major',
                   pad=10,
                   length=5)
    ax.tick_params('both',
                   labelsize=18,
                   width=1.5,
                   which='minor',
                   pad=10,
                   length=2.5)
    ax.set_xlabel(r'$\rm{log(Radius)\,[AU]}$', fontsize=18)
    ax.set_ylabel(r'$\rm{log(I\,/\,I_{max})}$', fontsize=18)

    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=18)
    for label in ax.get_xticklabels():
        label.set_fontproperties(ticks_font)
    for label in ax.get_yticklabels():
        label.set_fontproperties(ticks_font)

    fig.savefig(plotname + '_radial_profile_' + str(wave) + 'um.pdf',
                format='pdf',
                dpi=300,
                bbox_inches='tight')
    fig.clf()
Ejemplo n.º 28
0
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()
Ejemplo n.º 29
0
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()
Ejemplo n.º 30
0
    def run_thermal_hyperion(self, nphot=1e6, mrw=False, pda=False, \
            niterations=20, percentile=99., absolute=2.0, relative=1.02, \
            max_interactions=1e8, mpi=False, nprocesses=None):
        d = []
        for i in range(len(self.grid.dust)):
            d.append(IsotropicDust( \
                    self.grid.dust[i].nu[::-1].astype(numpy.float64), \
                    self.grid.dust[i].albedo[::-1].astype(numpy.float64), \
                    self.grid.dust[i].kext[::-1].astype(numpy.float64)))

        m = HypModel()
        if (self.grid.coordsystem == "cartesian"):
            m.set_cartesian_grid(self.grid.w1*AU, self.grid.w2*AU, \
                    self.grid.w3*AU)
        elif (self.grid.coordsystem == "cylindrical"):
            m.set_cylindrical_polar_grid(self.grid.w1*AU, self.grid.w3*AU, \
                    self.grid.w2)
        elif (self.grid.coordsystem == "spherical"):
            m.set_spherical_polar_grid(self.grid.w1*AU, self.grid.w2, \
                    self.grid.w3)

        for i in range(len(self.grid.density)):
            if (self.grid.coordsystem == "cartesian"):
                m.add_density_grid(numpy.transpose(self.grid.density[i], \
                        axes=(2,1,0)), d[i])
            if (self.grid.coordsystem == "cylindrical"):
                m.add_density_grid(numpy.transpose(self.grid.density[i], \
                        axes=(1,2,0)), d[i])
            if (self.grid.coordsystem == "spherical"):
                m.add_density_grid(numpy.transpose(self.grid.density[i], \
                        axes=(2,1,0)), d[i])

        sources = []
        for i in range(len(self.grid.stars)):
            sources.append(m.add_spherical_source())
            sources[i].luminosity = self.grid.stars[i].luminosity * L_sun
            sources[i].radius = self.grid.stars[i].radius * R_sun
            sources[i].temperature = self.grid.stars[i].temperature

        m.set_mrw(mrw)
        m.set_pda(pda)
        m.set_max_interactions(max_interactions)
        m.set_n_initial_iterations(niterations)
        m.set_n_photons(initial=nphot, imaging=0)
        m.set_convergence(True, percentile=percentile, absolute=absolute, \
                relative=relative)

        m.write("temp.rtin")

        m.run("temp.rtout", mpi=mpi, n_processes=nprocesses)

        n = ModelOutput("temp.rtout")

        grid = n.get_quantities()

        self.grid.temperature = []
        temperature = grid.quantities['temperature']
        for i in range(len(temperature)):
            if (self.grid.coordsystem == "cartesian"):
                self.grid.temperature.append(numpy.transpose(temperature[i], \
                        axes=(2,1,0)))
            if (self.grid.coordsystem == "cylindrical"):
                self.grid.temperature.append(numpy.transpose(temperature[i], \
                        axes=(2,0,1)))
            if (self.grid.coordsystem == "spherical"):
                self.grid.temperature.append(numpy.transpose(temperature[i], \
                        axes=(2,1,0)))

        os.system("rm temp.rtin temp.rtout")
Ejemplo n.º 31
0
def DIG_source_add(m,reg,df_nu):

    print("--------------------------------\n")
    print("Adding DIG to Source List in source_creation\n")
    print("--------------------------------\n")

    print ("Getting specific energy dumped in each grid cell")

    try:
        rtout = cfg.model.outputfile + '.sed'
        try: 
            grid_properties = np.load(cfg.model.PD_output_dir+"/grid_physical_properties."+cfg.model.snapnum_str+'_galaxy'+cfg.model.galaxy_num_str+".npz")
        except:
            grid_properties = np.load(cfg.model.PD_output_dir+"/grid_physical_properties."+cfg.model.snapnum_str+".npz")

        cell_info = np.load(cfg.model.PD_output_dir+"/cell_info."+cfg.model.snapnum_str+"_"+cfg.model.galaxy_num_str+".npz")
    except:
        print ("ERROR: Can't proceed with DIG nebular emission calculation. Code is unable to find the required files.") 
        print ("Make sure you have the rtout.sed, grid_physical_properties.npz and cell_info.npz for the corresponding galaxy.")

        return 

    m_out = ModelOutput(rtout)
    oct = m_out.get_quantities()
    grid = oct
    order = find_order(grid.refined)
    refined = grid.refined[order]
    quantities = {}
    for field in grid.quantities:
        quantities[('gas', field)] = grid.quantities[field][0][order][~refined]

    cell_width = cell_info["fw1"][:,0]
    mass = (quantities['gas','density']*units.g/units.cm**3).value * (cell_width**3)
    met = grid_properties["grid_gas_metallicity"]
    specific_energy = (quantities['gas','specific_energy']*units.erg/units.s/units.g).value
    specific_energy = (specific_energy * mass) # in ergs/s

    # Black 1987 curve has a integrated ergs/s/cm2 of 0.0278 so the factor we need to multiply it by is given by this value
    factor = specific_energy/(cell_width**2)/(0.0278) 

    mask1 = np.where(mass != 0 )[0]
    mask = np.where((mass != 0 ) & (factor >= cfg.par.DIG_min_factor))[0] # Masking out all grid cells that have no gas mass and where the specific emergy is too low
    print (len(factor), len(mask1), len(mask))
    
    factor = factor[mask]
    cell_width = cell_width[mask]
    cell_x = (cell_info["xmax"] - cell_info["xmin"])[mask]
    cell_y = (cell_info["ymax"] - cell_info["ymin"])[mask]
    cell_z = (cell_info["zmax"] - cell_info["zmin"])[mask]
    pos = np.vstack([cell_x, cell_y, cell_z]).transpose()

    met = grid_properties["grid_gas_metallicity"][:, mask]
    met = np.transpose(met)
    
    fnu_arr = sg.get_dig_seds(factor, cell_width, met)
    
    dat = np.load(cfg.par.pd_source_dir + "/powderday/nebular_emission/data/black_1987.npz")
    spec_lam = dat["lam"]
    nu = 1.e8 * constants.c.cgs.value / spec_lam 

    for i in range(len(factor)):
        fnu = fnu_arr[i,:]
        nu, fnu = wavelength_compress(nu,fnu,df_nu)
        
        nu = nu[::-1]
        fnu = fnu[::-1]
        
        lum = np.absolute(np.trapz(fnu,x=nu))*constants.L_sun.cgs.value
        
        source = m.add_point_source()
        source.luminosity = lum # [ergs/s]
        source.spectrum = (nu,fnu)
        source.position = pos[i] # [cm]
Ejemplo n.º 32
0
m.set_spherical_polar_grid(r, t, p)

dens = zeros((nr-1,nt-1,np-1)) + 1.0e-17

m.add_density_grid(dens, d)

source = m.add_spherical_source()
source.luminosity = lsun
source.radius = rsun
source.temperature = 4000.

m.set_n_photons(initial=1000000, imaging=0)
m.set_convergence(True, percentile=99., absolute=2., relative=1.02)

m.write("test_spherical.rtin")

m.run("test_spherical.rtout", mpi=False)

n = ModelOutput('test_spherical.rtout')

grid = n.get_quantities()

temp = grid.quantities['temperature'][0]

for i in range(9):
    plt.imshow(temp[i,:,:],origin="lower",interpolation="nearest", \
            vmin=temp.min(),vmax=temp.max())
    plt.colorbar()
    plt.show()
Ejemplo n.º 33
0
def plot_results(cli):
	
	file = filename(cli, "plot")
	file += ".rtout"
	
	#
	# Read in the model:
	#
	model = ModelOutput(file)
	
	if(cli.mode == "images"):
	
		#
		# Extract the quantities
		#
		g = model.get_quantities()
	
		#
		# Get the wall positions:
		#
		ww = g.w_wall / pc
		zw = g.z_wall / pc
		pw = g.p_wall
	
		grid_Nw = len(ww) - 1
		grid_Nz = len(zw) - 1
		grid_Np = len(pw) - 1
		
		#
		# Graphics:
		#
		fig = plt.figure()
	
		los = [0 for i in range(3)]
		los[0] = 'x'
		los[1] = 'y'
		los[2] = 'z'
	
		#Imaxp = [0 for i in range(4)]
		##Imaxp[0] = 1e-4
		#Imaxp[1] = 1e-5
		#Imaxp[2] = 1e-7
		#Imaxp[3] = 1e-8
	
		for k in range(0, 3):
			if(cli.verbose):
				print("Group: ", k)
		
			image = model.get_image(distance=1*pc, units='MJy/sr', inclination=0, component='total', group=k)
			source_emit = model.get_image(distance=1*pc, units='MJy/sr', inclination=0, component='source_emit', group=k)
			dust_emit   = model.get_image(distance=1*pc, units='MJy/sr', inclination=0, component='dust_emit'  , group=k)
			source_scat = model.get_image(distance=1*pc, units='MJy/sr', inclination=0, component='source_scat', group=k)
			dust_scat   = model.get_image(distance=1*pc, units='MJy/sr', inclination=0, component='dust_scat'  , group=k)
			
			if(cli.verbose):
				print(" Data cube: ", image.val.shape)
				print(" Wavelengths =", image.wav)
				print(" Uncertainties =", image.unc)
		
			image_Nx=image.val.shape[0]
			image_Ny=image.val.shape[1]
			Nwavelength=image.val.shape[2]

			if(cli.verbose):
				print(" Image Nx =", image_Nx)
				print(" Image Ny =", image_Ny)
				print(" Nwavelength =", Nwavelength)
			
			for i in range(0, Nwavelength):
				
				if(cli.verbose):
					print(" Image #", i,":")
					print("  Wavelength =", image.wav[i])
	
				Imin = np.min(image.val[:, :, i])
				Imax = np.max(image.val[:, :, i])
				# TODO: compute the mean value as well and use this for specifying the maximum value/color?!
		
				if(cli.verbose):
					print("  Intensity min =", Imin)
					print("  Intensity max =", Imax)
			
				#Imax=Imaxp[i]
	
				#ax = fig.add_subplot(2, 1, 2)
				ax = fig.add_subplot(1, 1, 1)
				if(image.wav[i] < 10.0):
					ax.imshow(source_scat.val[:, :, i] + dust_scat.val[:, :, i], vmin=Imin, vmax=Imax/10, cmap=plt.cm.gist_heat, origin='lower')
				else:
					ax.imshow(image.val[:, :, i], vmin=Imin, vmax=Imax/10, cmap=plt.cm.gist_heat, origin='lower')
				ax.set_xticks([0,100,200,300], minor=False)
				ax.set_yticks([0,100,200,300], minor=False)
				ax.set_xlabel('x (pixel)')
				ax.set_ylabel('y (pixel)')
				ax.set_title(str(image.wav[i]) + ' microns' + '\n' + los[k] + '-direction', y=0.88, x=0.5, color='white')
				
				#ax = fig.add_subplot(2, 1, 1)
				#ax.imshow([np.logspace(np.log10(Imin+1e-10),np.log10(Imax/10),100),np.logspace(np.log10(Imin+1e-10),np.log10(Imax/10),100)], vmin=Imin, vmax=Imax/10, cmap=plt.cm.gist_heat)
				#ax.set_xticks(np.logspace(np.log10(Imin+1e-10),np.log10(Imax/10),1), minor=False)
				##ax.set_xticks(np.linspace(np.log10(Imin+1e-10),np.log10(Imax/10),10), minor=False)
				#ax.set_yticks([], minor=False)
				#ax.set_xlabel('flux (MJy/sr)')
	
				file = filename(cli, "plot")
				file += "_wavelength=" + str(image.wav[i]) + "micron_los=" + los[k] + ".png"
	
				fig.savefig(file, bbox_inches='tight')
				if(cli.verbose):
					print("  The image graphics was written to", file)
				plt.clf()
	
	elif(cli.mode == "sed"):
	
		#
		# Graphics:
		#
		fig = plt.figure()

		z_center = [0 for i in range(3)]
		z_center[0] = '2.5'
		z_center[1] = '5.0'
		z_center[2] = '7.5'
		
		for k in range(0, 3):
			if(cli.verbose):
				print("Group: ", k)
				
			sed = model.get_sed(distance=1*pc, inclination=0, aperture=-1, group=k)
			
			ax = fig.add_subplot(1, 1, 1)
			ax.loglog(sed.wav, sed.val)
			ax.set_xlabel(r'$\lambda$ [$\mu$m]')
			ax.set_ylabel(r'$\lambda F_\lambda$ [ergs/s/cm$^2$]')
			ax.set_xlim(0.01, 2000.0)
			#ax.set_ylim(2.e-16, 2.e-9)
			
			file = filename(cli, "plot")
			file += "_z=" + z_center[k] + ".png"
			fig.savefig(file)
			if(cli.verbose):
				print(" The sed graphics was written to", file)
			plt.clf()
	
	else:
		print("ERROR: The specified mode", mode, "is not available. Use 'images' or 'sed' only.")
Ejemplo n.º 34
0
def hyperion_image(rtout,
                   wave,
                   plotdir,
                   printname,
                   dstar=178.,
                   group=0,
                   marker=0,
                   size='full',
                   convolve=False,
                   unit=None):
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    import astropy.constants as const
    from hyperion.model import ModelOutput
    # Package for matching the colorbar
    from mpl_toolkits.axes_grid1 import make_axes_locatable

    pc = const.pc.cgs.value

    if unit == None:
        unit = r'$\rm{log(I_{\nu})\,[erg\,s^{-1}\,cm^{-2}\,Hz^{-1}\,sr^{-1}]}$'

    m = ModelOutput(rtout)

    # Extract the image.
    image = m.get_image(group=group,
                        inclination=0,
                        distance=dstar * pc,
                        units='mJy')

    print np.shape(image.val)
    # Open figure and create axes
    fig = plt.figure(figsize=(8, 8))
    ax = fig.add_subplot(111)

    # Find the closest wavelength
    iwav = np.argmin(np.abs(wave - 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
    factor = 1
    # avoid zero in log
    # flip the image, because the setup of inclination is upside down
    val = image.val[::-1, :, iwav] * factor + 1e-30

    if convolve:
        from astropy.convolution import convolve, Gaussian2DKernel
        img_res = 2 * w / len(val[:, 0])
        kernel = Gaussian2DKernel(0.27 / 2.354 / img_res)
        val = convolve(val, kernel)

    if size != 'full':
        pix_e2c = (w - size / 2.) / w * len(val[:, 0]) / 2
        val = val[pix_e2c:-pix_e2c, pix_e2c:-pix_e2c]
        w = size / 2.

    # 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= -20, vmax= -15,
    #           cmap=cmap, origin='lower', extent=[-w, w, -w, w], aspect=1)
    im = ax.imshow(val,
                   cmap=cmap,
                   origin='lower',
                   extent=[-w, w, -w, w],
                   aspect=1)
    print val.max()

    # plot the marker for center position by default or user input offset
    ax.plot([0], [-marker], '+', color='ForestGreen', markersize=10, mew=2)
    ax.set_xlim([-w, w])
    ax.set_ylim([-w, w])
    # ax.plot([0],[-10], '+', color='m', markersize=10, mew=2)

    # 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.
    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(unit, fontsize=18)
    cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
    plt.setp(cb_obj, fontsize=14)
    # fix the tick label font
    ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral', size=14)
    for label in cb.ax.get_yticklabels():
        label.set_fontproperties(ticks_font)

    ax.set_xlabel(r'$\rm{RA\,Offset\,(arcsec)}$', fontsize=18)
    ax.set_ylabel(r'$\rm{Dec\,Offset\,(arcsec)}$', fontsize=18)

    ax.tick_params(axis='both', which='major', labelsize=18)
    ax.text(0.7,
            0.88,
            str(wave) + r'$\rm{\,\mu m}$',
            fontsize=20,
            color='white',
            transform=ax.transAxes)

    fig.savefig(plotdir + printname + '_image_' + str(wave) + '.pdf',
                format='pdf',
                dpi=300,
                bbox_inches='tight')
    fig.clf()
Ejemplo n.º 35
0
g2d = 100
mmw = 2.37
mh = const.m_p.cgs.value + const.m_e.cgs.value
AU = const.au.cgs.value

model = np.arange(99,133).astype('str')
# color map
cmap = plt.cm.viridis
color_array = [cmap(np.linspace(0, 0.9, len(model))[i]) for i in range(len(model))]

fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111)

for i in range(len(model)):
    m = ModelOutput('/home/bettyjo/yaolun/hyperion/bhr71/controlled/model'+model[i]+'/model'+model[i]+'.rtout')
    q = m.get_quantities()
    r = q.r_wall
    rc = 0.5*(r[0:len(r)-1]+r[1:len(r)])
    rho = q['density'][0].array
    rho2d = np.sum(rho**2,axis=0)/np.sum(rho,axis=0)
    plt.plot(np.log10(rc[rc > 0.14*AU]/AU), np.log10(rho2d[199,rc > 0.14*AU]/g2d/mmw/mh)-0.1*i, '-',
             color=color_array[i], linewidth=1)
ax.set_ylim([-2,9])
ax.set_xlabel(r'$\rm{log(Radius)\,(AU)}$',fontsize=20)
ax.set_ylabel(r'$\rm{log(Dust\,Density)\,(cm^{-3})}$',fontsize=20)
[ax.spines[axis].set_linewidth(1.5) for axis in ['top','bottom','left','right']]
ax.minorticks_on()
ax.tick_params('both',labelsize=18,width=1.5,which='major',pad=15,length=5)
ax.tick_params('both',labelsize=18,width=1.5,which='minor',pad=15,length=2.5)

# fix the tick label font
Ejemplo n.º 36
0
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()
Ejemplo n.º 37
0
#Script showing how to extract some rtout files that were run on an
#octree format

from __future__ import print_function
from hyperion.model import ModelOutput
from hyperion.grid.yt3_wrappers import find_order
import astropy.units as u
import numpy as np

run = '/home/desika.narayanan/pd_git/tests/SKIRT/gizmo_mw_zoom/pd_skirt_comparison.134.rtout.sed'

m = ModelOutput(run)


oct = m.get_quantities()
#ds = oct.to_yt()

#ripped from hyperion/grid/yt3_wrappers.py -- we do this because
#something about load_octree in yt4.x is only returning the first cell
grid = oct
order = find_order(grid.refined)
refined = grid.refined[order]

quantities = {}
for field in grid.quantities:
    quantities[('gas', field)] = np.atleast_2d(grid.quantities[field][0][order][~refined]).transpose()

specific_energy = quantities['gas','specific_energy']*u.erg/u.s/u.g
dust_temp = quantities['gas','temperature']*u.K
dust_density = quantities['gas','density']*u.g/u.cm**3
Ejemplo n.º 38
0
import numpy as np
from hyperion.model import ModelOutput
import matplotlib.pyplot as plt
from yt.mods import write_bitmap, ColorTransferFunction

plt.rcParams['font.family'] = 'Arial'

# Read in model from Hyperion

m = ModelOutput('pla704850_lev7_129.rtout')
grid = m.get_quantities()

# Convert quantities to yt
pf = grid.to_yt()

# Instantiate the ColorTransferfunction.
tmin, tmax = 1.3, 2.3
tf_temp = ColorTransferFunction((tmin, tmax))
dmin, dmax = -20, -16
tf_dens = ColorTransferFunction((dmin, dmax))

# Set up the camera parameters: center, looking direction, width, resolution
c = (pf.domain_right_edge + pf.domain_left_edge) / 2.0

L = np.array([1.0, 1.0, 1.0])
W = 0.7 / pf["unitary"]
N = 512

# Create camera objects

cam_temp = pf.h.camera(c,