Example #1
0
def get_input_seds(file):

    totallum = 0
    m = Model()
    m.use_sources(file)
    nsources = len(m.sources)

    for i in range(nsources):

        tempnu = m.sources[i].spectrum["nu"]
        tempfnu = m.sources[i].spectrum["fnu"]

        if i == 0: fnu = np.zeros(len(tempnu))

        #now we need to scale this because the spectrum is just in
        #terms of an SSP, and we need to scale by the total luminosity
        #that wen t into the model (i.e. by the actual stellar mass
        #used in powderday).

        ssp_lum = np.absolute(np.trapz(tempnu, tempfnu)) * constants.L_sun.cgs
        lum_scale = np.sum(
            m.sources[i].luminosity
        ) / ssp_lum  #we have to do np.sum in case the sources were in a collection
        tempfnu *= lum_scale.value

        for i in range(len(fnu)):
            fnu[i] += tempfnu[i]

    #ipdb.set_trace()

    return tempnu, fnu
Example #2
0
def enzo_m_gen(fname, field_add):

    reg, ds1 = enzo_grid_generate(fname, field_add)

    amr = AMRGrid.from_yt(
        ds1, quantity_mapping={'density': ('gas', 'dust_density')})
    '''
    levels = ds.index.max_level
    
    amr = AMRGrid()
    for ilevel in range(levels):
        level = amr.add_level()
        
    for igrid in ds.index.select_grids(ilevel):
        print igrid
        grid = level.add_grid()
        grid.xmin,grid.xmax = igrid.LeftEdge[0].in_units('cm'),igrid.RightEdge[0].in_units('cm')
        grid.ymin,grid.ymax = igrid.LeftEdge[1].in_units('cm'),igrid.RightEdge[1].in_units('cm')
        grid.zmin,grid.zmax = igrid.LeftEdge[2].in_units('cm'),igrid.RightEdge[2].in_units('cm')
        grid.quantities["density"] = np.transpose(np.array(igrid[("gas","metal_density")].in_units('g/cm**3')*cfg.par.dusttometals_ratio))
        grid.nx,grid.ny,grid.nz = igrid[("gas","metal_density")].shape
    '''

    m = Model()

    m.set_amr_grid(amr)

    #CMB DISABLED -- UNCOMMENT THIS TO FIX THIS.  The main issue is
    #that I'm not sure what shape to give to the np.repeat
    #array of energy_density_absorbed; I think it needs to be the ARM Grid shape but i'm not quite sure if it needs to be an AMRGrid()
    #energy_density_absorbed=energy_density_absorbed_by_CMB()
    #energy_density_absorbed =np.repeat(energy_density_absorbed.value,reg.index.num_grids)#amr['density'].shape)

    d = SphericalDust(cfg.par.dustdir + cfg.par.dustfile)
    if cfg.par.SUBLIMATION == True:
        d.set_sublimation_temperature(
            'fast', temperature=cfg.par.SUBLIMATION_TEMPERATURE)

    m.add_density_grid(amr["density"], d)
    #uncomment when we're ready to put CMB in (and comment out previous line)
    #m.add_density_grid(amr['density'],d,specific_energy=energy_density_absorbed)
    #m.set_specific_energy_type('additional')

    center = ds1.arr([cfg.model.x_cent, cfg.model.y_cent, cfg.model.z_cent],
                     'code_length')
    [xcent, ycent, zcent
     ] = center.in_units('cm')  #boost needs to be in cm since that's what the

    boost = np.array([xcent, ycent, zcent])

    dx = ds1.domain_width[0].in_units('cm')
    dy = ds1.domain_width[1].in_units('cm')
    dz = ds1.domain_width[2].in_units('cm')

    return m, xcent, ycent, zcent, dx, dy, dz, reg, ds1, boost
Example #3
0
def enzo_m_gen(fname, field_add):

    reg, ds1 = enzo_grid_generate(fname, field_add)

    amr = yt_dataset_to_amr_grid_xyz(
        ds1, quantity_mapping={'density': ('gas', 'dust_density')})

    m = Model()

    #save in the m__dict__ that we're in an amr geometry
    m.__dict__['grid_type'] = 'amr'

    m.set_amr_grid(amr)

    #CMB DISABLED -- UNCOMMENT THIS TO FIX THIS.  The main issue is
    #that I'm not sure what shape to give to the np.repeat
    #array of energy_density_absorbed; I think it needs to be the ARM Grid shape but i'm not quite sure if it needs to be an AMRGrid()
    #energy_density_absorbed=energy_density_absorbed_by_CMB()
    #energy_density_absorbed =np.repeat(energy_density_absorbed.value,reg.index.num_grids)#amr['density'].shape)

    d = SphericalDust(cfg.par.dustdir + cfg.par.dustfile)
    if cfg.par.SUBLIMATION == True:
        d.set_sublimation_temperature(
            'fast', temperature=cfg.par.SUBLIMATION_TEMPERATURE)

    m.add_density_grid(amr["density"], d)
    #uncomment when we're ready to put CMB in (and comment out previous line)
    #m.add_density_grid(amr['density'],d,specific_energy=energy_density_absorbed)
    #m.set_specific_energy_type('additional')

    center = ds1.arr([cfg.model.x_cent, cfg.model.y_cent, cfg.model.z_cent],
                     'code_length')
    [xcent, ycent, zcent
     ] = center.in_units('cm')  #boost needs to be in cm since that's what the

    boost = np.array([xcent, ycent, zcent])

    dx = ds1.domain_width[0].in_units('cm')
    dy = ds1.domain_width[1].in_units('cm')
    dz = ds1.domain_width[2].in_units('cm')

    return m, xcent, ycent, zcent, dx, dy, dz, reg, ds1, boost
Example #4
0
def setup_model(indir,outdir,outname,tsc=True,idl=False,plot=False,low_res=True,
                flat=True,scale=1,mono=False):
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    import matplotlib.pyplot as plt
    import matplotlib as mat
    import os
    from matplotlib.colors import LogNorm
    from scipy.optimize import fsolve
    from scipy.optimize import newton
    from scipy.integrate import nquad
    from envelope_func import func
    from hyperion.model import Model

    # Constants setup
    c         = const.c.cgs.value
    AU        = 1.49598e13     # Astronomical Unit       [cm]
    pc        = 3.08572e18     # Parsec                  [cm]
    MS        = 1.98892e33     # Solar mass              [g]
    LS        = 3.8525e33      # Solar luminosity        [erg/s]
    RS        = 6.96e10        # Solar radius            [cm]
    G         = 6.67259e-8     # Gravitational constant  [cm3/g/s^2]
    yr        = 60*60*24*365   # Years in seconds
    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

    m = Model()

    # Create dust properties

    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust_radmc = dict()
    [dust_radmc['wl'],dust_radmc['abs'],dust_radmc['scat'],dust_radmc['g']] =
                    np.genfromtxt('dustkappa_oh5_extended.inp',skip_header=2).T
Example #5
0
import glob

import numpy as np

from hyperion.model import Model
from hyperion.dust import SphericalDust
from hyperion.util.constants import pc

import yaml
settings = yaml.load(open('settings.yml'))
 
WAV = np.logspace(-1, 3, 45)

for model_path in glob.glob(os.path.join('models', '*_temperature.rtout')):

    m = Model.read(model_path, only_initial=False)

    m.set_n_initial_iterations(0)

    del m.n_photons['initial']
    del m.n_photons['last']

    i = m.add_peeled_images(sed=True, image=False)
    i.set_viewing_angles([0., 30., 60., 90., 120., 150., 180.],
                         [0., 0., 0., 0., 0., 0., 0.])
    i.set_track_origin('basic')

    i = m.add_peeled_images(sed=True, image=False)
    i.set_viewing_angles([0., 30., 60., 90., 120., 150., 180.],
                         [0., 0., 0., 0., 0., 0., 0.])
    i.set_track_origin('basic')
Example #6
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()
Example #7
0
#!/usr/bin/env python3

from numpy import loadtxt, zeros, ones, arange
from hyperion.model import Model
from hyperion.model import ModelOutput
from hyperion.dust import IsotropicDust
from hyperion.util.constants import *
import matplotlib.pyplot as plt

m = Model()

data = loadtxt('../dustkappa_yso.inp', skiprows=2)
nu = c / (data[:, 0].copy()[::-1] * 1.0e-4)
kabs = data[:, 1].copy()[::-1]
ksca = data[:, 2].copy()[::-1]
chi = kabs + ksca
albedo = ksca / chi

d = IsotropicDust(nu, albedo, chi)

nr = 10
nt = 10
np = 10

r = arange(nr) * au / 2
t = arange(nt) / (nt - 1.) * pi
p = arange(np) / (np - 1.) * 2 * pi

m.set_spherical_polar_grid(r, t, p)

dens = zeros((nr - 1, nt - 1, np - 1)) + 1.0e-17
Example #8
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()
# A simple model to check what happens when a source is moving towards dust and
# we observe both the source and the dust. If we observe the source such that
# the dust is directly behind, and the source is moving towards the dust, we
# should see red-shifted emission from the source and blue-shifted scattered
# light emission.

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import c

m = Model()

m.set_cartesian_grid([-1., 0, 1], [-1., 1.], [-1., 1])

density = np.zeros(m.grid.shape)
density[:, :, 0] = 1.

m.add_density_grid(density, 'kmh_lite.hdf5')

# narrow emission line spectrum at 1 micron
wav = np.array([0.9999, 1.0001])
fnu = np.array([1., 1.])
nu = c / (wav * 1.e-4)

s = m.add_spherical_source()
s.position = 0.5, 0., 0.
s.velocity = -1e8, 0., 0.
s.spectrum = nu[::-1], fnu[::-1]
s.luminosity = 1
s.radius = 0.1
Example #10
0
def setup_model(cli):
	
    lsun_TRUST = 3.839e33
        
    #
    # Hyperion setup:
    #
    model = Model()


    if(cli.mode == "temperature"):
        #
        # Dust properties:
        #
        dust_properties = SphericalDust('dust_integrated_full_scattering.hdf5')
            
            
        #
        # Write dust properties:
        #
        dust_properties.write('dust_properties.hdf5')
        dust_properties.plot('dust_properties.png')
        
        
        #
        # Specify galaxy setup:
        #
        hR                     =  4000.0*pc             # [cm]
        Rmax                   =     5.0*hR             # [cm]
        hz_oldstars            =   350.0*pc             # [cm]
        hz_youngstars          =   200.0*pc             # [cm]
        hz_dust                =   200.0*pc             # [cm]
        zmax_oldstars          =     5.0*hz_oldstars    # [cm]
        zmax_youngstars        =     5.0*hz_youngstars  # [cm]
        zmax_dust              =     5.0*hz_dust        # [cm]
        zmax                   =  zmax_oldstars         # [cm]
        reff                   =  1600.0*pc             # [cm]
        n                      =     3.0
        q                      =     0.6
        bn                     = 2.0*n - 1.0/3.0 + 4.0/405.0/n + 46.0/25515.0/n/n + 131.0/1148175.0/n/n/n
        temperature_oldstars   =  3500.0                # [K]
        temperature_youngstars = 10000.0                # [K]
        temperature_bulge      =  3500.0                # [K]
        luminosity_oldstars    =     4.0e+10*lsun_TRUST # [ergs/s]
        luminosity_youngstars  =     1.0e+10*lsun_TRUST # [ergs/s]
        luminosity_bulge       =     3.0e+10*lsun_TRUST # [ergs/s]
        
        w_oldstars             =     0.25
        w_youngstars           =     0.75
        w_dust                 =     0.75
        phi0_oldstars          =     0.0
        phi0_youngstars        =    20.0 * pi/180.0
        phi0_dust              =    20.0 * pi/180.0
        modes                  =     2
        pitchangle             =    20.0 * pi/180.0
        
        
        
        #
        # Grid setup:
        #
        grid_wmin =  0.0
        grid_wmax =  Rmax
        grid_zmin = -zmax
        grid_zmax = +zmax
        grid_pmin =  0.0
        grid_pmax =  2.0*pi
        
        grid_dx = cli.resolution*pc
        grid_dw = grid_dx # uniform resolution
        grid_dz = grid_dx # uniform resolution
        grid_dp = grid_dx # resolution at characteristic radial disk spatial scale hR = 4000.0 pc
        
        grid_Nw   = int((grid_wmax - grid_wmin) / grid_dw) + 1
        grid_Nz   = int((grid_zmax - grid_zmin) / grid_dz) + 1
        if(cli.case == 1):
            grid_Np = 1
        if(cli.case == 2):
            grid_Np = int((grid_pmax - grid_pmin) * hR / grid_dp)
        
        if(cli.verbose):
            print("Grid setup:")
            print(" Grid resolution =",cli.resolution, "pc.")
            print(" grid_Nw =",grid_Nw)
            print(" grid_Nz =",grid_Nz)
            print(" grid_Np =",grid_Np)
        
        #grid_w      = np.logspace(np.log10(grid_wmin), np.log10(grid_wmax), grid_Nw)
        #grid_w      = np.hstack([0., grid_w]) # add innermost cell interface at w=0
        grid_w    = np.linspace(grid_wmin, grid_wmax, grid_Nw+1)
        grid_z    = np.linspace(grid_zmin, grid_zmax, grid_Nz+1)
        grid_p    = np.linspace(grid_pmin, grid_pmax, grid_Np+1)
        
        model.set_cylindrical_polar_grid(grid_w, grid_z, grid_p)
        
        #
        # Dust density and sources setup:
        #
        rho_oldstars   = np.zeros(model.grid.shape)
        rho_youngstars = np.zeros(model.grid.shape)
        rho_bulge      = np.zeros(model.grid.shape)
        rho_dust       = np.zeros(model.grid.shape)
        
        for k in range(0, grid_Np):
            for j in range(0, grid_Nz):
                for i in range(0, grid_Nw):
                    
                    R = model.grid.gw[k,j,i]
                    z = model.grid.gz[k,j,i]
                    m = math.sqrt(R*R + z*z/q/q)
                    
                    rho_dust[k,j,i]       = math.exp(- R/hR -abs(z)/hz_dust      )
                    rho_oldstars[k,j,i]   = math.exp(- R/hR -abs(z)/hz_oldstars  )
                    rho_youngstars[k,j,i] = math.exp(- R/hR -abs(z)/hz_youngstars)
                    rho_bulge[k,j,i]      = math.pow(m/reff, 0.5/n - 1.0) * math.exp(- bn * math.pow(m/reff, 1.0/n))
                    
                    if(cli.case == 2):
                        phi = model.grid.gp[k,j,i]
                        perturb = math.sin(modes * (math.log(R/hR) / math.tan(pitchangle) - (phi - phi0_dust)))
                        rho_dust[k,j,i]       *= (1.0 + w_dust       * perturb)
                        perturb = math.sin(modes * (math.log(R/hR) / math.tan(pitchangle) - (phi - phi0_oldstars)))
                        rho_oldstars[k,j,i]   *= (1.0 + w_oldstars   * perturb)
                        perturb = math.sin(modes * (math.log(R/hR) / math.tan(pitchangle) - (phi - phi0_youngstars)))
                        rho_youngstars[k,j,i] *= (1.0 + w_youngstars * perturb)
        
        rho_dust[model.grid.gw > grid_wmax] = 0
        rho_dust[model.grid.gz < grid_zmin] = 0
        rho_dust[model.grid.gz > grid_zmax] = 0
        
        kappa_ref     = dust_properties.optical_properties.interp_chi_wav(0.55693)
        rho0          = cli.opticaldepth / (2.0 * hz_dust * kappa_ref)
        rho_dust[:]  *= rho0
        model.add_density_grid(rho_dust, 'dust_properties.hdf5')
        
        source_oldstars                = model.add_map_source()
        source_oldstars.luminosity     = luminosity_oldstars
        source_oldstars.temperature    = temperature_oldstars
        source_oldstars.map            = rho_oldstars
        
        source_youngstars              = model.add_map_source()
        source_youngstars.luminosity   = luminosity_youngstars
        source_youngstars.temperature  = temperature_youngstars
        source_youngstars.map          = rho_youngstars
        
        source_bulge                   = model.add_map_source()
        source_bulge.luminosity        = luminosity_bulge
        source_bulge.temperature       = temperature_bulge
        source_bulge.map               = rho_bulge
        
        
        #
        # Check face-on optical depth at 1.0 micron (per gram dust) through the dust disk:
        #
        tau   = 0
        
        k = 0
        i = 0
        for j in range(0, grid_Nz):
            #print(model.grid.gz[k,j,i]/pc, rho_dust[k,j,i])
            dz   = model.grid.widths[1,k,j,i]
            dtau = dz * rho_dust[k,j,i] * kappa_ref
            tau += dtau
        
        deviation = 100.0 * abs(cli.opticaldepth - tau) / cli.opticaldepth
        
        if(cli.verbose):
            print("Check optical depth of dust density setup:")
            print(" kappa(0.55693 micron) = ", kappa_ref, "cm^2 g^-1")
            print(" Numerical integration of the face-on optical depth at 0.55693 micron through the central dust disk yields tau = ", tau)
            print(" This corresponds to a deviation to the chosen setup value of", deviation, "percent")
    
        #
        # Check central dust density:
        #
        rho_max = np.max(rho_dust)
        if(cli.opticaldepth < 1.0):
            rho_setup = 1.04366e-4 * msun/pc/pc/pc
        if(cli.opticaldepth < 3.0):
            rho_setup = 5.21829e-4 * msun/pc/pc/pc
        else:
            rho_setup = 2.60915e-3 * msun/pc/pc/pc

        deviation = 100.0 * abs(rho_setup - rho_max) / rho_setup

        if(cli.verbose):
            print("Check value of central dust density:")
            print(" rho_max = ", rho_max, "g cm^-3")
            print(" This corresponds to a deviation to the chosen setup value of", deviation, "percent")

        #
        # To compute total photon numbers:
        #
        grid_N = grid_Nw * grid_Nz * grid_Np
        if(cli.verbose):
            print("Radiation setup:")
            print(" photons_temperature / cell =", cli.photons_temperature)
            print(" photons_temperature total  =", grid_N * cli.photons_temperature)

        file = filename(cli, "temperature")
        file += ".rtin"
    
    
    else:
        file = filename(cli, "temperature")
        file += ".rtout"
        
        try:
            with open(file):
                if(cli.verbose):
                    print("Using the specific energy distribution from file", file)
                model.use_geometry(file)
                model.use_quantities(file, only_initial=False, copy=False)
                model.use_sources(file)
            
        except IOError:
            print("ERROR: File '", file, "' cannot be found. \nERROR: This file, containing the specific energy density, has to be computed first via calling hyperion.")
            exit(2)
        
		#
		# To compute total photon numbers:
		#
        grid_Nw = len(model.grid.gw[0,0,:])
        grid_Nz = len(model.grid.gw[0,:,0])
        grid_Np = len(model.grid.gw[:,0,0])
        grid_N = grid_Nw * grid_Nz * grid_Np
        if(cli.verbose):
            print("Grid setup:")
            print(" grid_Nw =",grid_Nw)
            print(" grid_Nz =",grid_Nz)
            print(" grid_Np =",grid_Np)
            print("Radiation setup:")
            print(" photons_temperature / cell =", cli.photons_temperature)
            print(" photons_temperature total  =", grid_N * cli.photons_temperature)
            print(" photons_raytracing / cell  =", cli.photons_raytracing)
            print(" photons_raytracing total   =", grid_N * cli.photons_raytracing)
            print(" photons_imaging / cell     =", cli.photons_imaging)
            print(" photons_imaging total      =", grid_N * cli.photons_imaging)
        
        file = filename(cli, "")
        file += ".rtin"


    ##
    ## Temperature, Images, and SEDs:
    ##
    if(cli.mode == "temperature"):
    
        model.set_raytracing(True)
        model.set_n_photons(
            initial            = grid_N * cli.photons_temperature,
            raytracing_sources = grid_N * cli.photons_raytracing,
            raytracing_dust    = grid_N * cli.photons_raytracing,
            imaging            = grid_N * cli.photons_imaging
        )
        
    elif(cli.mode == "images"):
        
        model.set_n_initial_iterations(0)
        model.set_raytracing(True)
        # old setup: model.set_monochromatic(True, wavelengths=[0.4, 1.0, 10.0, 100.0, 500.0])
        model.set_monochromatic(True, wavelengths=[0.45483, 1.2520, 26.114, 242.29])
        model.set_n_photons(
            raytracing_sources = grid_N * cli.photons_raytracing,
            raytracing_dust    = grid_N * cli.photons_raytracing,
            imaging_sources    = grid_N * cli.photons_imaging,
            imaging_dust       = grid_N * cli.photons_imaging
        )
    
        # group = 0
        image1 = model.add_peeled_images(sed=False, image=True)
        image1.set_image_size(501, 501)
        image1.set_image_limits(-12500.0*pc, +12500.0*pc, -12500.0*pc, +12500.0*pc)
        image1.set_viewing_angles([30],[0])
        image1.set_uncertainties(True)
        image1.set_output_bytes(8)
        image1.set_track_origin('basic')
    
        # group = 1
        image2 = model.add_peeled_images(sed=False, image=True)
        image2.set_image_size(501, 501)
        image2.set_image_limits(-12500.0*pc, +12500.0*pc, -12500.0*pc, +12500.0*pc)
        image2.set_viewing_angles([80],[90])
        image2.set_uncertainties(True)
        image2.set_output_bytes(8)
        image2.set_track_origin('basic')
    
        # group = 2
        image3 = model.add_peeled_images(sed=False, image=True)
        image3.set_image_size(501, 501)
        image3.set_image_limits(-12500.0*pc, +12500.0*pc, -12500.0*pc, +12500.0*pc)
        image3.set_viewing_angles([88],[0]) # mostly edge-on
        image3.set_uncertainties(True)
        image3.set_output_bytes(8)
        image3.set_track_origin('basic')

    elif(cli.mode == "seds"):
        
        model.set_n_initial_iterations(0)
        model.set_raytracing(True)
        model.set_n_photons(
            raytracing_sources = grid_N * cli.photons_raytracing,
            raytracing_dust    = grid_N * cli.photons_raytracing,
            imaging            = grid_N * cli.photons_imaging
        )
    
        # group = 0
        sed1 = model.add_peeled_images(sed=True, image=False)
        sed1.set_wavelength_range(47, 0.081333, 1106.56)
        sed1.set_viewing_angles([30],[0])
        sed1.set_peeloff_origin((0, 0, 0))
        sed1.set_aperture_range(1, 25000.0*pc, 25000.0*pc)
        sed1.set_uncertainties(True)
        sed1.set_output_bytes(8)
        sed1.set_track_origin('basic')
        
        # group = 1
        sed2 = model.add_peeled_images(sed=True, image=False)
        sed2.set_wavelength_range(47, 0.081333, 1106.56)
        sed2.set_viewing_angles([80],[0])
        sed2.set_peeloff_origin((0, 0, 0))
        sed2.set_aperture_range(1, 25000.0*pc, 25000.0*pc)
        sed2.set_uncertainties(True)
        sed2.set_output_bytes(8)
        sed2.set_track_origin('basic')
    
        # group = 2
        sed3 = model.add_peeled_images(sed=True, image=False)
        sed3.set_wavelength_range(47, 0.081333, 1106.56)
        sed3.set_viewing_angles([88],[0])
        sed3.set_peeloff_origin((0, 0, 0))
        sed3.set_aperture_range(1, 25000.0*pc, 25000.0*pc)
        sed3.set_uncertainties(True)
        sed3.set_output_bytes(8)
        sed3.set_track_origin('basic')

    ##
    ## Write model for hyperion runs:
    ##
    model.conf.output.output_density         = 'last'
    model.conf.output.output_specific_energy = 'last'
    model.conf.output.output_n_photons       = 'last'
    model.write(file)
    if(cli.verbose):
        print("The input file for hyperion was written to", file)
Example #11
0
def setup_model(outdir,record_dir,outname,params,dust_file,tsc=True,idl=False,plot=False,\
                low_res=True,flat=True,scale=1,radmc=False,mono=False,record=True,dstar=178.,\
                aperture=None,dyn_cav=False,fix_params=None,alma=False,power=2,better_im=False,ellipsoid=False,\
                TSC_dir='~/programs/misc/TSC/', IDL_path='/Applications/exelis/idl83/bin/idl',auto_disk=0.25):
    """
    params = dictionary of the model parameters
    alma keyword is obsoleted 
    outdir: The directory for storing Hyperion input files
    record_dir: The directory contains "model_list.txt" for recording parameters
    TSC_dir: Path the TSC-related IDL routines
    IDL_path: The IDL executable 
    """
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    #
    import matplotlib.pyplot as plt
    import os
    from matplotlib.colors import LogNorm
    from scipy.integrate import nquad
    from hyperion.model import Model
    from record_hyperion import record_hyperion
    from outflow_inner_edge import outflow_inner_edge
    from pprint import pprint
    # import pdb
    # pdb.set_trace()

    # Constants setup
    c         = const.c.cgs.value
    AU        = 1.49598e13     # Astronomical Unit       [cm]
    pc        = 3.08572e18     # Parsec                  [cm]
    MS        = 1.98892e33     # Solar mass              [g]
    LS        = 3.8525e33      # Solar luminosity        [erg/s]
    RS        = 6.96e10        # Solar radius            [cm]
    G         = 6.67259e-8     # Gravitational constant  [cm3/g/s^2]
    yr        = 60*60*24*365   # Years in seconds
    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
    g2d       = 100.
    mmw       = 2.37   # Kauffmann 2008


    m = Model()

    # Create dust properties

    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust = dict()
    # [dust_radmc['wl'], dust_radmc['abs'], dust_radmc['scat'], dust_radmc['g']] = np.genfromtxt(dust_file,skip_header=2).T
    [dust['nu'], dust['albedo'], dust['chi'], dust['g']] = np.genfromtxt(dust_file).T
    # opacity per mass of dust?
    # dust_hy = dict()
    # dust_hy['nu'] = c/dust_radmc['wl']*1e4
    # ind = np.argsort(dust_hy['nu'])
    # dust_hy['nu'] = dust_hy['nu'][ind]
    # dust_hy['albedo'] = (dust_radmc['scat']/(dust_radmc['abs']+dust_radmc['scat']))[ind]
    # dust_hy['chi'] = (dust_radmc['abs']+dust_radmc['scat'])[ind]
    # dust_hy['g'] = dust_radmc['g'][ind]
    # dust_hy['p_lin_max'] = 0*dust_radmc['wl'][ind]     # assume no polarization

    # d = HenyeyGreensteinDust(dust_hy['nu'], dust_hy['albedo'], dust_hy['chi'], dust_hy['g'], dust_hy['p_lin_max'])
    d = HenyeyGreensteinDust(dust['nu'], dust['albedo'], dust['chi'], dust['g'], dust['g']*0)
    # dust sublimation option
    d.set_sublimation_temperature('slow', temperature=1600.0)
    d.set_lte_emissivities(n_temp=3000,
                       temp_min=0.1,
                       temp_max=2000.)
    # try to solve the freq. problem
    d.optical_properties.extrapolate_nu(3.28e15, 4e15)
    #
    d.write(outdir+os.path.basename(dust_file).split('.')[0]+'.hdf5')
    d.plot(outdir+os.path.basename(dust_file).split('.')[0]+'.png')
    plt.clf()

    # Grids and Density
    # Calculation inherited from the script used for RADMC-3D

    # Grid Parameters
    nx        = 300L
    if low_res == True:
        nx    = 100L
    ny        = 400L
    nz        = 50L
    [nx, ny, nz] = [int(scale*nx), int(scale*ny), int(scale*nz)]

    # TSC model input setting
    # params    = np.genfromtxt(indir+'/tsc_params.dat', dtype=None)
    dict_params = params # input_reader(params_file)
    # TSC model parameter
    cs        = dict_params['Cs']*1e5
    t         = dict_params['age']  # year
    omega     = dict_params['Omega0']
    # calculate related parameters
    M_env_dot = 0.975*cs**3/G
    mstar     = M_env_dot * t * yr
    R_cen     = omega**2 * G**3 * mstar**3 /(16*cs**8)
    R_inf     = cs * t * yr
    # M_env_dot = dict_params['M_env_dot']*MS/yr
    # R_cen     = dict_params['R_cen']*AU
    # R_inf     = dict_params['R_inf']*AU
    # protostar parameter
    tstar     = dict_params['tstar']
    R_env_max = dict_params['R_env_max']*AU
    theta_cav = dict_params['theta_cav']
    rho_cav_center = dict_params['rho_cav_center']
    rho_cav_edge   = dict_params['rho_cav_edge']*AU
    rstar     = dict_params['rstar']*RS
    # Mostly fixed parameter
    M_disk    = dict_params['M_disk']*MS
    beta      = dict_params['beta']
    h100      = dict_params['h100']*AU
    rho_cav   = dict_params['rho_cav']
    # make M_disk varies with mstar, which is the mass of star+disk
    if auto_disk != None:
        if M_disk != 0:
            print 'M_disk is reset to %4f of mstar (star+disk)' % auto_disk
            M_disk = mstar * auto_disk
        else:
            print 'M_disk = 0 is found.  M_disk is set to 0.'

    # ellipsoid cavity parameter
    if ellipsoid == True:
        a_out = 130 * 178. * AU
        b_out = 50  * 178. * AU
        z_out = a_out
        # a_in  = 77.5 * 178. * AU
        # b_in  = 30   * 178. * AU
        a_in  = dict_params['a_in'] * 178. * AU
        b_in  = a_in/a_out*b_out
        z_in  = a_in
        # rho_cav_out = 1e4 * mh
        # rho_cav_in  = 1e3 * mh
        rho_cav_out = dict_params['rho_cav_out'] * mh
        rho_cav_in  = dict_params['rho_cav_in']  * mh
    # Calculate the dust sublimation radius
    T_sub = 1600
    a     = 1   #in micron
    # realistic dust
    # d_sub = 2.9388e7*(a/0.1)**-0.2 * (4*np.pi*rstar**2*sigma*tstar**4/LS)**0.5 / T_sub**3 *AU
    # black body dust
    d_sub = (LS/16./np.pi/sigma/AU**2*(4*np.pi*rstar**2*sigma*tstar**4/LS)/T_sub**4)**0.5 *AU
    # use the dust sublimation radius as the inner radius of disk and envelope
    R_disk_min = d_sub
    R_env_min  = d_sub
    rin        = rstar
    rout       = R_env_max
    R_disk_max = R_cen

    # Do the variable conversion
    # cs = (G * M_env_dot / 0.975)**(1/3.)  # cm/s
    # t = R_inf / cs / yr   # in year
    # mstar = M_env_dot * t * yr
    # omega = (R_cen * 16*cs**8 / (G**3 * mstar**3))**0.5

    # print the variables for radmc3d
    print 'Dust sublimation radius %6f AU' % (d_sub/AU)
    print 'M_star %4f Solar mass' % (mstar/MS)
    print 'Infall radius %4f AU' % (R_inf / AU)


    # if there is any parameter found in fix_params, then fix them
    if fix_params != None:
        if 'R_min' in fix_params.keys():
            R_disk_min = fix_params['R_min']*AU
            R_env_min  = fix_params['R_min']*AU

    # Make the Coordinates
    #
    ri           = rin * (rout/rin)**(np.arange(nx+1).astype(dtype='float')/float(nx))
    ri           = np.hstack((0.0, ri))
    thetai       = PI*np.arange(ny+1).astype(dtype='float')/float(ny)
    phii         = PI*2.0*np.arange(nz+1).astype(dtype='float')/float(nz)
    
    # Keep the constant cell size in r-direction at large radii
    #
    if flat == True:
        ri_cellsize = ri[1:-1]-ri[0:-2]
        ind = np.where(ri_cellsize/AU > 100.0)[0][0]       # The largest cell size is 100 AU
        ri = np.hstack((ri[0:ind],ri[ind]+np.arange(np.ceil((rout-ri[ind])/100/AU))*100*AU))
        nxx = nx
        nx = len(ri)-1    
    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc           = 0.5*( ri[0:nx]     + ri[1:nx+1] )
    thetac       = 0.5*( thetai[0:ny] + thetai[1:ny+1] )
    phic         = 0.5*( phii[0:nz]   + phii[1:nz+1] )
    # phic         = 0.5*( phii[0:nz-1]   + phii[1:nz] )

    # Make the dust density model
    # Make the density profile of the envelope
    #
    total_mass = 0
    if tsc == False:
        print 'Calculating the dust density profile with infall solution...'
        if theta_cav != 0:
            # c0 = R_env_max**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
            # using R = 10000 AU as the reference point
            c0 = (10000.*AU)**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
        else:
            c0 = 0
        rho_env  = np.zeros([len(rc),len(thetac),len(phic)])
        rho_disk = np.zeros([len(rc),len(thetac),len(phic)])
        rho      = np.zeros([len(rc),len(thetac),len(phic)])

        if dyn_cav == True:
            print 'WARNING: Calculation of interdependent cavity property has not implemented in infall-only solution!'
        # Normalization for the total disk mass
        def f(w,z,beta,rstar,h100):
            f = 2*PI*w*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/(w**beta*h100/100**beta))**2)
            return f

        rho_0 = M_disk/(nquad(f,[[R_disk_min,R_disk_max],[-R_env_max,R_env_max]], args=(beta,rstar,h100)))[0]
        i = 0
        j = 0
        if 'rho_cav_center' in locals() == False:
            rho_cav_center = 5.27e-18 # 1.6e-17  # 5.27e-18
            print 'Use 5.27e-18 as the default value for cavity center'
        if 'rho_cav_edge' in locals() == False:
            rho_cav_edge = 40*AU
            print 'Use 40 AU as the default value for size of the inner region'
        discont = 1
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])

                        if ellipsoid == False:
                            z_cav = c0*abs(w)**1.5
                            if z_cav == 0:
                                z_cav = R_env_max
                            cav_con = abs(z) > abs(z_cav)
                        else:
                            # condition for the outer ellipsoid
                            cav_con = (2*(w/b_out)**2 + ((abs(z)-z_out)/a_out)**2) < 1
                        if cav_con:
                            # open cavity
                            if ellipsoid == False:
                                if rho_cav_edge == 0:
                                    rho_cav_edge = R_env_min
                                if (rc[ir] <= rho_cav_edge) & (rc[ir] >= R_env_min):
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center#*((rc[ir]/AU)**2)
                                else:
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center*discont*(rho_cav_edge/rc[ir])**power
                                i += 1
                            else:
                                # condition for the inner ellipsoid
                                if (2*(w/b_in)**2 + ((abs(z)-z_in)/a_in)**2) > 1:
                                    rho_env[ir,itheta,iphi] = rho_cav_out
                                else:
                                    rho_env[ir,itheta,iphi] = rho_cav_in
                                i +=1
                        else:
                            j += 1
                            mu = abs(np.cos(thetac[itheta]))
                            # Implement new root finding algorithm
                            roots = np.roots(np.array([1.0, 0.0, rc[ir]/R_cen-1.0, -mu*rc[ir]/R_cen]))
                            if len(roots[roots.imag == 0]) == 1:
                                if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                                    mu_o_dum = roots[roots.imag == 0]
                                else:
                                    mu_o_dum = -0.5
                                    print 'Problem with cubic solving, cos(theta) = ', mu_o_dum
                                    print 'parameters are ', np.array([1.0, 0.0, rc[ir]/R_cen-1.0, -mu*rc[ir]/R_cen])
                            else:
                                mu_o_dum = -0.5
                                for imu in range(0, len(roots)):
                                    if roots[imu]*mu >= 0.0:
                                        if (abs((abs(roots[imu]) - 1.0)) <= 1e-5):
                                            mu_o_dum = 1.0 * np.sign(mu)
                                        else:
                                            mu_o_dum = roots[imu]
                                if mu_o_dum == -0.5:
                                    print 'Problem with cubic solving, roots are: ', roots
                            mu_o = mu_o_dum.real
                            rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*R_cen**3)**0.5)*(rc[ir]/R_cen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*R_cen/rc[ir])**(-1)
                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w/(100*AU))**beta)*h100
                            rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # Combine envelope and disk
                        rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-30
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1/3.)*(ri[ir+1]**3 - ri[ir]**3) * (phii[iphi+1]-phii[iphi]) * -(np.cos(thetai[itheta+1])-np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass

        rho_env  = rho_env  + 1e-40
        rho_disk = rho_disk + 1e-40
        rho      = rho      + 1e-40
    # TSC model
    else:
        print 'Calculating the dust density profile with TSC solution...'
        if theta_cav != 0:
            # c0 = R_env_max**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
            c0 = (1e4*AU)**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
        else:
            c0 = 0
        # If needed, calculate the TSC model via IDL
        #
        if idl == True:
            print 'Using IDL to calculate the TSC model.  Make sure you are running this on mechine with IDL.'
            import pidly
            # idl = pidly.IDL('/Applications/exelis/idl82/bin/idl')
            idl = pidly.IDL(IDL_path)
            idl('.r '+TSC_dir+'tsc.pro')
            # idl.pro('tsc_run', outdir=outdir, grid=[nxx,ny,nz], time=t, c_s=cs, omega=omega, rstar=rstar, renv_min=R_env_min, renv_max=R_env_max)
            # idl.pro('tsc_run', outdir=outdir, grid=[nxx,ny,nz], time=t, c_s=cs, omega=omega, rstar=rstar, renv_min=R_env_min, renv_max=min([R_inf,max(ri)])) # min([R_inf,max(ri)])
            #
            # only run TSC calculation within infall radius
            # modify the rc array
            rc_idl = rc[(rc < min([R_inf,max(ri)]))]
            idl.pro('tsc_run', outdir=outdir, rc=rc_idl, thetac=thetac, time=t, c_s=cs, omega=omega, renv_min=R_env_min)#, rstar=rstar, renv_min=R_env_min, renv_max=min([R_inf,max(ri)])) # min([R_inf,max(ri)])
        else:
            print 'Read the pre-computed TSC model.'
            rc_idl = rc[(rc < min([R_inf,max(ri)]))]
        # read in the exist file
        rho_env_tsc_idl = np.genfromtxt(outdir+'rhoenv.dat').T
        # because only region within infall radius is calculated by IDL program, need to project it to the original grid
        rho_env_tsc = np.zeros([len(rc), len(thetac)])
        for irc in range(len(rc)):
            if rc[irc] in rc_idl:
                rho_env_tsc[irc,:] = rho_env_tsc_idl[np.where(rc_idl == rc[irc]),:]

        # extrapolate for the NaN values at the outer radius, usually at radius beyond the infall radius
        # using r^-2 profile at radius greater than infall radius
        # and map the 2d strcuture onto 3d grid
        def poly(x, y, x0, deg=2):
            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
        # rho_env_copy = np.array(rho_env_tsc)
        # if max(rc) > R_inf:
        #     ind_infall = np.where(rc <= R_inf)[0][-1]
        #     print ind_infall
        #     for ithetac in range(0, len(thetac)):
        #         # rho_dum = np.log10(rho_env_copy[(rc > R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == False),ithetac])
        #         # rc_dum = np.log10(rc[(rc > R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == False)])
        #         # rc_dum_nan = np.log10(rc[(rc > R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == True)])
        #         # # print rc_dum
        #         # for i in range(0, len(rc_dum_nan)):
        #         #     rho_extrapol = poly(rc_dum, rho_dum, rc_dum_nan[i])
        #         #     rho_env_copy[(np.log10(rc) == rc_dum_nan[i]),ithetac] = 10**rho_extrapol
        #         #
        #         for i in range(ind_infall, len(rc)):
        #             rho_env_copy[i, ithetac] =  10**(np.log10(rho_env_copy[ind_infall, ithetac]) - 2*(np.log10(rc[i]/rc[ind_infall])))
        # rho_env2d = rho_env_copy
        # rho_env = np.empty((nx,ny,nz))
        # for i in range(0, nz):
        #     rho_env[:,:,i] = rho_env2d
        # map TSC solution from IDL to actual 2-D grid
        rho_env_tsc2d = np.empty((nx,ny)) 
        if max(ri) > R_inf:
            ind_infall = np.where(rc <= R_inf)[0][-1]
            for i in range(0, len(rc)):
                if i <= ind_infall:
                    rho_env_tsc2d[i,:] = rho_env_tsc[i,:]
                else:
                    rho_env_tsc2d[i,:] =  10**(np.log10(rho_env_tsc[ind_infall,:]) - 2*(np.log10(rc[i]/rc[ind_infall])))
        else:
            rho_env_tsc2d = rho_env_tsc
        # map it to 3-D grid
        rho_env = np.empty((nx,ny,nz))
        for i in range(0, nz):
            rho_env[:,:,i] = rho_env_tsc2d

        if dyn_cav == True:
            print 'Calculate the cavity properties using the criteria that swept-up mass = outflowed mass'
            # using swept-up mass = flow mass to derive the edge of the extended flat density region
            v_outflow = 1e2 * 1e5
            rho_cav_edge = outflow_inner_edge(np.copy(rho_env), (ri,thetai,phii),M_env_dot,v_outflow,theta_cav, R_env_min)
            dict_params['rho_cav_edge'] = rho_cav_edge
            # assume gas-to-dust ratio = 100
            rho_cav_center = 0.01 * 0.1*M_env_dot*rho_cav_edge/v_outflow/2 / (2*np.pi/3*rho_cav_edge**3*(1-np.cos(np.radians(theta_cav))))
            dict_params['rho_cav_center'] = rho_cav_center
            print 'inner edge is %5f AU and density is %e g/cm3' % (rho_cav_edge/AU, rho_cav_center)

        # create the array of density of disk and the whole structure
        #
        rho_disk = np.zeros([len(rc),len(thetac),len(phic)])
        rho      = np.zeros([len(rc),len(thetac),len(phic)])
        # Calculate the disk scale height by the normalization of h100
        def f(w,z,beta,rstar,h100):
            f = 2*PI*w*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/(w**beta*h100/100**beta))**2)
            return f
        # The function for calculating the normalization of disk using the total disk mass
        #
        rho_0 = M_disk/(nquad(f,[[R_disk_min,R_disk_max],[-R_env_max,R_env_max]], args=(beta,rstar,h100)))[0]
        i = 0
        j = 0
        if 'rho_cav_center' in locals() == False:
            rho_cav_center = 5.27e-18 # 1.6e-17  # 5.27e-18
            print 'Use 5.27e-18 as the default value for cavity center'
        if 'rho_cav_edge' in locals() == False:
            rho_cav_edge = 40*AU
            print 'Use 40 AU as the default value for size of the inner region'
        discont = 1
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])

                        if ellipsoid == False:
                            z_cav = c0*abs(w)**1.5
                            if z_cav == 0:
                                z_cav = R_env_max
                            cav_con = abs(z) > abs(z_cav)
                        else:
                            # condition for the outer ellipsoid
                            cav_con = (2*(w/b_out)**2 + ((abs(z)-z_out)/a_out)**2) < 1
                        if cav_con:
                            # open cavity
                            if ellipsoid == False:
                                if rho_cav_edge == 0:
                                    rho_cav_edge = R_env_min
                                if (rc[ir] <= rho_cav_edge) & (rc[ir] >= R_env_min):
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center#*((rc[ir]/AU)**2)
                                else:
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center*discont*(rho_cav_edge/rc[ir])**power
                                i += 1
                            else:
                                # condition for the inner ellipsoid
                                if (2*(w/b_in)**2 + ((abs(z)-z_in)/a_in)**2) > 1:
                                    rho_env[ir,itheta,iphi] = rho_cav_out
                                else:
                                    rho_env[ir,itheta,iphi] = rho_cav_in
                                i +=1

                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w/(100*AU))**beta)*h100 
                            rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # Combine envelope and disk
                        rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-40
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1/3.)*(ri[ir+1]**3 - ri[ir]**3) * (phii[iphi+1]-phii[iphi]) * -(np.cos(thetai[itheta+1])-np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass
        # rho_env  = rho_env  + 1e-40
        # rho_disk = rho_disk + 1e-40
        # rho      = rho      + 1e-40
    # apply gas-to-dust ratio of 100
    rho_dust = rho/g2d
    total_mass_dust = total_mass/MS/g2d
    print 'Total dust mass = %f Solar mass' % total_mass_dust

    if record == True:
        # Record the input and calculated parameters
        params = dict_params.copy()
        params.update({'d_sub': d_sub/AU, 'M_env_dot': M_env_dot/MS*yr, 'R_inf': R_inf/AU, 'R_cen': R_cen/AU, 'mstar': mstar/MS, 'M_tot_gas': total_mass/MS})
        record_hyperion(params,record_dir)

    if plot == True:
        # rc setting
        # mat.rcParams['text.usetex'] = True
        # mat.rcParams['font.family'] = 'serif'
        # mat.rcParams['font.serif'] = 'Times'
        # mat.rcParams['font.sans-serif'] = 'Computer Modern Sans serif'

        # Plot the azimuthal averaged density
        fig = plt.figure(figsize=(8,6))
        ax_env  = fig.add_subplot(111,projection='polar')
        # take the weighted average
        # rho2d is the 2-D projection of gas density
        rho2d = np.sum(rho**2,axis=2)/np.sum(rho,axis=2)

        zmin = 1e-22/mmw/mh
        cmap = plt.cm.CMRmap
        rho2d_exp = np.hstack((rho2d,rho2d,rho2d[:,0:1]))
        thetac_exp = np.hstack((thetac-PI/2, thetac+PI/2, thetac[0]-PI/2))
        # plot the gas density
        img_env = ax_env.pcolormesh(thetac_exp,rc/AU,rho2d_exp/mmw/mh,cmap=cmap,norm=LogNorm(vmin=zmin,vmax=1e9)) # np.nanmax(rho2d_exp/mmw/mh)

        ax_env.set_xlabel(r'$\rm{Polar\,angle\,(Degree)}$',fontsize=20)
        ax_env.set_ylabel(r'$\rm{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_ylim([0,10000])
        ax_env.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}}$'])
        # fix the tick label font
        ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=20)
        for label in ax_env.get_yticklabels():
            label.set_fontproperties(ticks_font)

        ax_env.grid(True)
        cb = fig.colorbar(img_env, pad=0.1)
        cb.ax.set_ylabel(r'$\rm{Averaged\,Gas\,Density\,(cm^{-3})}$',fontsize=20)
        cb.set_ticks([1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9])
        cb.set_ticklabels([r'$\rm{10^{2}}$',r'$\rm{10^{3}}$',r'$\rm{10^{4}}$',r'$\rm{10^{5}}$',r'$\rm{10^{6}}$',\
                           r'$\rm{10^{7}}$',r'$\rm{10^{8}}$',r'$\rm{\geq 10^{9}}$'])
        cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
        plt.setp(cb_obj,fontsize=20)
        fig.savefig(outdir+outname+'_gas_density.png', format='png', dpi=300, bbox_inches='tight')
        fig.clf()

        # Plot the radial density profile
        fig = plt.figure(figsize=(12,9))
        ax = fig.add_subplot(111)

        plot_grid = [0,49,99,149,199]
        alpha = np.linspace(0.3,1.0,len(plot_grid))
        for i in plot_grid:
            rho_rad, = ax.plot(np.log10(rc/AU), np.log10(rho2d[:,i]/g2d/mmw/mh),'-',color='b',linewidth=2, markersize=3,alpha=alpha[plot_grid.index(i)])
            tsc_only, = ax.plot(np.log10(rc/AU), np.log10(rho_env_tsc2d[:,i]/mmw/mh),'o',color='r',linewidth=2, markersize=3,alpha=alpha[plot_grid.index(i)])
        rinf = ax.axvline(np.log10(R_inf/AU), linestyle='--', color='k', linewidth=1.5)
        cen_r = ax.axvline(np.log10(R_cen/AU), linestyle=':', color='k', linewidth=1.5)
        # sisslope, = ax.plot(np.log10(rc/AU), -2*np.log10(rc/AU)+A-(-2)*np.log10(plot_r_inf), linestyle='--', color='Orange', linewidth=1.5)
        # gt_R_cen_slope, = ax.plot(np.log10(rc/AU), -1.5*np.log10(rc/AU)+B-(-1.5)*np.log10(plot_r_inf), linestyle='--', color='Orange', linewidth=1.5)
        # lt_R_cen_slope, = ax.plot(np.log10(rc/AU), -0.5*np.log10(rc/AU)+A-(-0.5)*np.log10(plot_r_inf), linestyle='--', color='Orange', linewidth=1.5)

        lg = plt.legend([rho_rad, tsc_only, rinf, cen_r],\
                        [r'$\rm{\rho_{dust}}$',r'$\rm{\rho_{tsc}}$',r'$\rm{infall\,radius}$',r'$\rm{centrifugal\,radius}$'],\
                        fontsize=20, numpoints=1)
        ax.set_xlabel(r'$\rm{log(Radius)\,(AU)}$',fontsize=20)
        ax.set_ylabel(r'$\rm{log(Gas \slash 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
        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)

        ax.set_ylim([0,15])
        fig.gca().set_xlim(left=np.log10(0.05))
        # ax.set_xlim([np.log10(0.8),np.log10(10000)])

        # subplot shows the radial density profile along the midplane
        ax_mid = plt.axes([0.2,0.2,0.2,0.2], frameon=True)
        ax_mid.plot(np.log10(rc/AU), np.log10(rho2d[:,199]/g2d/mmw/mh),'o',color='b',linewidth=1, markersize=2)
        ax_mid.plot(np.log10(rc/AU), np.log10(rho_env_tsc2d[:,199]/mmw/mh),'-',color='r',linewidth=1, markersize=2)
        # ax_mid.set_ylim([0,10])
        # ax_mid.set_xlim([np.log10(0.8),np.log10(10000)])
        ax_mid.set_ylim([0,15])
        fig.savefig(outdir+outname+'_gas_radial.pdf',format='pdf',dpi=300,bbox_inches='tight')
        fig.clf()

    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)
    # temperary for comparing full TSC and infall-only TSC model
    # import sys
    # sys.path.append(os.path.expanduser('~')+'/programs/misc/')
    # from tsc_comparison import tsc_com
    # rho_tsc, rho_ulrich = tsc_com()
    m.add_density_grid(rho_dust.T, d)
    # m.add_density_grid(rho.T, outdir+'oh5.hdf5')    # numpy read the array in reverse order

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print 'L_center =  % 5.2f L_sun' % ((4*PI*rstar**2)*sigma*(tstar**4)/LS)

    # # add an infrared source at the center
    # L_IR = 0.04
    # ir_source = m.add_spherical_source()
    # ir_source.luminosity = L_IR*LS
    # ir_source.radius = rstar      # [cm]
    # ir_source.temperature = 500 # [K]  peak at 10 um
    # ir_source.position = (0., 0., 0.)
    # print 'Additional IR source, L_IR = %5.2f L_sun' % L_IR

    # Setting up the wavelength for monochromatic radiative transfer
    lambda0 = 0.1
    lambda1 = 2.0
    lambda2 = 50.0
    lambda3 = 95.0
    lambda4 = 200.0
    lambda5 = 314.0
    lambda6 = 1000.0
    n01     = 10.0
    n12     = 20.0
    n23     = 50.0

    lam01   = lambda0 * (lambda1/lambda0)**(np.arange(n01)/n01)
    lam12   = lambda1 * (lambda2/lambda1)**(np.arange(n12)/n12)
    lam23   = lambda2 * (lambda6/lambda2)**(np.arange(n23+1)/n23)

    lam      = np.concatenate([lam01,lam12,lam23])
    nlam    = len(lam)

    # Create camera wavelength points
    n12     = 70.0
    n23     = 70.0
    n34     = 70.0
    n45     = 50.0
    n56     = 50.0
    
    lam12   = lambda1 * (lambda2/lambda1)**(np.arange(n12)/n12)
    lam23   = lambda2 * (lambda3/lambda2)**(np.arange(n23)/n23)
    lam34   = lambda3 * (lambda4/lambda3)**(np.arange(n34)/n34)
    lam45   = lambda4 * (lambda5/lambda4)**(np.arange(n45)/n45)
    lam56   = lambda5 * (lambda6/lambda5)**(np.arange(n56+1)/n56)

    lam_cam = np.concatenate([lam12,lam23,lam34,lam45,lam56])
    n_lam_cam = len(lam_cam)

    # Radiative transfer setting

    # number of photons for temp and image
    lam_list = lam.tolist()
    # print lam_list
    m.set_raytracing(True)
    # option of using more photons for imaging
    if better_im == False:
        im_photon = 1e6
    else:
        im_photon = 5e7

    if mono == True:
        # Monechromatic radiative transfer setting
        m.set_monochromatic(True, wavelengths=lam_list)
        m.set_n_photons(initial=1000000, imaging_sources=im_photon, imaging_dust=im_photon,raytracing_sources=1000000, raytracing_dust=1000000)
    else:
        # regular wavelength grid setting
        m.set_n_photons(initial=1000000, imaging=im_photon,raytracing_sources=1000000, raytracing_dust=1000000)    
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(20)
    # m.set_convergence(True, percentile=95., absolute=1.5, relative=1.02)
    m.set_convergence(True, percentile=dict_params['percentile'], absolute=dict_params['absolute'], relative=dict_params['relative'])
    m.set_mrw(True)   # Gamma = 1 by default
    # m.set_forced_first_scattering(forced_first_scattering=True)

    # Setting up images and SEDs
    # SED setting

    # Infinite aperture
    syn_inf = m.add_peeled_images(image=False)
    # use the index of wavelength array used by the monochromatic radiative transfer
    if mono == False:
        syn_inf.set_wavelength_range(1400, 2.0, 1400.0)
    syn_inf.set_viewing_angles([dict_params['view_angle']], [0.0])
    syn_inf.set_uncertainties(True)
    syn_inf.set_output_bytes(8)

    # aperture
    # 7.2 in 10 um scaled by lambda / 10
    # flatten beyond 20 um
    # default aperture
    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, 1300],\
                    '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, 101]}
    # 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)

    # name = np.arange(1,len(wl_aper)+1)
    # aper = np.empty_like(wl_aper)
    # for i in range(0, len(wl_aper)):
    #     if wl_aper[i] < 5:
    #         # aper[i] = 1.2 * 7
    #         aper[i] = 1.8 * 4
    #     elif (wl_aper[i] < 14) & (wl_aper[i] >=5):
    #         # aper[i] = 7.2 * wl_aper[i]/10.
    #         aper[i] = 1.8 * 4
    #     elif (wl_aper[i] >= 14) & (wl_aper[i] <40):
    #         # aper[i] = 7.2 * 2
    #         aper[i] = 5.1 * 4
    #     else:
    #         aper[i] = 24.5

    # dict_peel_sed = {}
    # for i in range(0, len(wl_aper)):
    #     aper_dum = aper[i]/2 * (1/3600.*np.pi/180.)*dstar*pc
    #     dict_peel_sed[str(name[i])] = m.add_peeled_images(image=False)
    #     # use the index of wavelength array used by the monochromatic radiative transfer
    #     if mono == False:
    #         # dict_peel_sed[str(name[i])].set_wavelength_range(1300, 2.0, 1300.0)
    #         dict_peel_sed[str(name[i])].set_wavelength_range(1000, 2.0, 1000.0)
    #     dict_peel_sed[str(name[i])].set_viewing_angles([dict_params['view_angle']], [0.0])
    #     # aperture should be given in cm
    #     dict_peel_sed[str(name[i])].set_aperture_range(1, aper_dum, aper_dum)
    #     dict_peel_sed[str(name[i])].set_uncertainties(True)
    #     dict_peel_sed[str(name[i])].set_output_bytes(8)

    dict_peel_sed = {}
    for i in range(0, len(aper_reduced)):
        aper_dum = aper_reduced[i]/2 * (1/3600.*np.pi/180.)*dstar*pc
        dict_peel_sed[str(index_reduced[i])] = m.add_peeled_images(image=False)
        # use the index of wavelength array used by the monochromatic radiative transfer
        if mono == False:
            dict_peel_sed[str(index_reduced[i])].set_wavelength_range(1400, 2.0, 1400.0)
        dict_peel_sed[str(index_reduced[i])].set_viewing_angles([dict_params['view_angle']], [0.0])
        # aperture should be given in cm and its the radius of the aperture
        dict_peel_sed[str(index_reduced[i])].set_aperture_range(1, aper_dum, aper_dum)
        dict_peel_sed[str(index_reduced[i])].set_uncertainties(True)
        dict_peel_sed[str(index_reduced[i])].set_output_bytes(8)

    # image setting
    syn_im = m.add_peeled_images(sed=False)
    # use the index of wavelength array used by the monochromatic radiative transfer
    if mono == False:
        syn_im.set_wavelength_range(1400, 2.0, 1400.0)
    # pixel number
    syn_im.set_image_size(300, 300)
    syn_im.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    syn_im.set_viewing_angles([dict_params['view_angle']], [0.0])
    syn_im.set_uncertainties(True)
    # output as 64-bit
    syn_im.set_output_bytes(8)
    

    # Output setting
    # Density
    m.conf.output.output_density = 'last'

    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'

    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'

    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'

    m.write(outdir+outname+'.rtin')

    if radmc == True:
        # RADMC-3D still use a pre-defined aperture with lazy for-loop
        aper = np.zeros([len(lam)])
        ind = 0
        for wl in lam:
            if wl < 5:
                aper[ind] = 8.4
            elif wl >= 5 and wl < 14:
                aper[ind] = 1.8 * 4
            elif wl >= 14 and wl < 40:
                aper[ind] = 5.1 * 4
            else:
                aper[ind] = 24.5
            ind += 1

        # Write the wavelength_micron.inp file
        #
        f_wave = open(outdir+'wavelength_micron.inp','w')
        f_wave.write('%d \n' % int(nlam))
        for ilam in range(0,nlam):
            f_wave.write('%f \n' % lam[ilam])
        f_wave.close()

        # Write the camera_wavelength_micron.inp file
        #
        f_wave_cam = open(outdir+'camera_wavelength_micron.inp','w')
        f_wave_cam.write('%d \n' % int(nlam))
        for ilam in range(0,nlam):
            f_wave_cam.write('%f \n' % lam[ilam])
        f_wave_cam.close()

        # Write the aperture_info.inp
        #
        f_aper = open(outdir+'aperture_info.inp','w')
        f_aper.write('1 \n')
        f_aper.write('%d \n' % int(nlam))
        for iaper in range(0, len(aper)):
            f_aper.write('%f \t %f \n' % (lam[iaper],aper[iaper]/2))
        f_aper.close()

        # Write the stars.inp file
        #
        f_star = open(outdir+'stars.inp','w')
        f_star.write('2\n')
        f_star.write('1 \t %d \n' % int(nlam))
        f_star.write('\n')
        f_star.write('%e \t %e \t %e \t %e \t %e \n' % (rstar*0.9999,mstar,0,0,0))
        f_star.write('\n')
        for ilam in range(0,nlam):
            f_star.write('%f \n' % lam[ilam])
        f_star.write('\n')
        f_star.write('%f \n' % -tstar)
        f_star.close()

        # Write the grid file
        #
        f_grid = open(outdir+'amr_grid.inp','w')
        f_grid.write('1\n')                               # iformat
        f_grid.write('0\n')                               # AMR grid style  (0=regular grid, no AMR)
        f_grid.write('150\n')                             # Coordinate system  coordsystem<100: Cartisian; 100<=coordsystem<200: Spherical; 200<=coordsystem<300: Cylindrical
        f_grid.write('0\n')                               # gridinfo
        f_grid.write('1 \t 1 \t 1 \n')                    # Include x,y,z coordinate
        f_grid.write('%d \t %d \t %d \n' % (int(nx)-1,int(ny),int(nz)))    # Size of the grid
        [f_grid.write('%e \n' % ri[ir]) for ir in range(1,len(ri))]
        [f_grid.write('%f \n' % thetai[itheta]) for itheta in range(0,len(thetai))]
        [f_grid.write('%f \n' % phii[iphi]) for iphi in range(0,len(phii))]
        f_grid.close()

        # Write the density file
        #
        f_dust = open(outdir+'dust_density.inp','w')
        f_dust.write('1 \n')                      # format number
        f_dust.write('%d \n' % int((nx-1)*ny*nz)) # Nr of cells
        f_dust.write('1 \n')                      # Nr of dust species
        for iphi in range(0,len(phic)):
            for itheta in range(0,len(thetac)):
                for ir in range(1,len(rc)):
                    f_dust.write('%e \n' % rho_dust[ir,itheta,iphi])
        f_dust.close()


        # Write the dust opacity table
        f_dustkappa = open(outdir+'dustkappa_oh5_extended.inp','w')
        f_dustkappa.write('3 \n')                       # format index for including g-factor
        f_dustkappa.write('%d \n' % len(dust['nu']))    # number of wavlength/frequency in the table
        for i in range(len(dust['nu'])):
            f_dustkappa.write('%f \t %f \t %f \t %f \n' % (c/dust['nu'][i]*1e4, dust['chi'][i], dust['chi'][i]*dust['albedo'][i]/(1-dust['albedo'][i]), dust['g'][i]))
        f_dustkappa.close()

        # Write the Dust opacity control file
        # 
        f_opac = open(outdir+'dustopac.inp','w')
        f_opac.write('2               Format number of this file\n')
        f_opac.write('1               Nr of dust species\n')
        f_opac.write('============================================================================\n')
        f_opac.write('1               Way in which this dust species is read\n')
        f_opac.write('0               0=Thermal grain\n')
        # f_opac.write('klaus           Extension of name of dustkappa_***.inp file\n')
        f_opac.write('oh5_extended    Extension of name of dustkappa_***.inp file\n')
        f_opac.write('----------------------------------------------------------------------------\n')
        f_opac.close()
                

        # In[112]:

        # Write the radmc3d.inp control file
        #
        f_control = open(outdir+'radmc3d.inp','w')
        f_control.write('nphot = %d \n' % 100000)
        f_control.write('scattering_mode_max = 2\n')
        f_control.write('camera_min_drr = 0.1\n')
        f_control.write('camera_min_dangle = 0.1\n')
        f_control.write('camera_spher_cavity_relres = 0.1\n')
        f_control.write('istar_sphere = 1\n')
        f_control.write('modified_random_walk = 1\n')
        f_control.close()

    return m

# from input_reader import input_reader_table
# from pprint import pprint
# filename = '/Users/yaolun/programs/misc/hyperion/test_input.txt'
# params = input_reader_table(filename)
# pprint(params[0])
# indir = '/Users/yaolun/test/'
# outdir = '/Users/yaolun/test/'
# dust_file = '/Users/yaolun/programs/misc/oh5_hyperion.txt'
# # dust_file = '/Users/yaolun/Copy/dust_model/Ormel2011/hyperion/(ic-sil,gra)3opc.txt'
# # fix_params = {'R_min': 0.14}
# fix_params = {}
# setup_model(indir,outdir,'model_test',params[0],dust_file,plot=True,record=False,\
#     idl=False,radmc=False,fix_params=fix_params,ellipsoid=False)
# A simple model to check what happens when a source is moving towards dust and
# we observe both the source and the dust. If we observe the source such that
# the dust is directly behind, and the source is moving towards the dust, we
# should see red-shifted emission from the source and blue-shifted scattered
# light emission.

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import c

m = Model()

m.set_cartesian_grid([-1.,0, 1], [-1., 1.], [-1., 1])

density = np.zeros(m.grid.shape)
density[:,:,0] = 1.

vx = np.ones(m.grid.shape) * -1e8
vy = np.zeros(m.grid.shape)
vz = np.zeros(m.grid.shape)

m.add_density_grid(density, 'kmh_lite.hdf5', velocity=(vx, vy, vz))

# narrow emission line spectrum at 1 micron
wav = np.array([0.9999, 1.0001])
fnu = np.array([1., 1.])
nu = c / (wav * 1.e-4)

s = m.add_spherical_source()
s.position = 0.5, 0., 0.
s.velocity = -1e8, 0., 0.
import numpy as np

from hyperion.model import Model
from hyperion.util.constants import pc, c

# The following value is taken from Mathis, Mezger, and Panagia (1983)
FOUR_PI_JNU = 0.0217

# Initialize model
m = Model()

# Set up grid
m.set_spherical_polar_grid([0., 1.001 * pc],
                           [0., np.pi],
                           [0., 2. * np.pi])

# Read in MMP83 spectrum
wav, jlambda = np.loadtxt('mmp83.txt', unpack=True)
nu = c / (wav * 1.e-4)
jnu = jlambda * wav / nu

# Set up the source - note that the normalization of the spectrum is not
# important - the luminosity is set separately.
s = m.add_external_spherical_source()
s.radius = pc
s.spectrum = (nu, jnu)
s.luminosity = np.pi * pc * pc * FOUR_PI_JNU

# Add an inside observer with an all-sky camera
image = m.add_peeled_images(sed=False, image=True)
image.set_inside_observer((0., 0., 0.))
Example #14
0
import random
random.seed('hyperion')  # ensure that random numbers are the same every time

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import pc, lsun

# Define cell walls
x = np.linspace(-10., 10., 101) * pc
y = np.linspace(-10., 10., 101) * pc
z = np.linspace(-10., 10., 101) * pc

# Initialize model and set up density grid
m = Model()
m.set_cartesian_grid(x, y, z)
m.add_density_grid(np.ones((100, 100, 100)) * 1.e-20, 'kmh_lite.hdf5')

# Generate random sources
for i in range(100):
    s = m.add_point_source()
    xs = random.uniform(-10., 10.) * pc
    ys = random.uniform(-10., 10.) * pc
    zs = random.uniform(-10., 10.) * pc
    s.position = (xs, ys, zs)
    s.luminosity = 10. ** random.uniform(0., 3.) * lsun
    s.temperature = random.uniform(3000., 8000.)

# Specify that the specific energy and density are needed
m.conf.output.output_specific_energy = 'last'
m.conf.output.output_density = 'last'
Example #15
0
def setup_model(indir,
                outdir,
                model=False,
                denser_wall=False,
                plot=False,
                low_res=False,
                flat=True,
                scale=1.0):
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    import matplotlib.pyplot as plt
    import matplotlib as mat
    import os
    from matplotlib.colors import LogNorm
    from scipy.optimize import fsolve
    from scipy.integrate import nquad
    from envelope_func import func
    from hyperion.model import Model

    # Constants setup
    c = const.c.cgs.value
    AU = 1.49598e13  # Astronomical Unit       [cm]
    pc = 3.08572e18  # Parsec                  [cm]
    MS = 1.98892e33  # Solar mass              [g]
    LS = 3.8525e33  # Solar luminosity        [erg/s]
    RS = 6.96e10  # Solar radius            [cm]
    G = 6.67259e-8  # Gravitational constant  [cm3/g/s^2]
    yr = 60 * 60 * 24 * 365  # Years in seconds
    PI = np.pi  # PI constant
    sigma = const.sigma_sb.cgs.value  # Stefan-Boltzmann constant

    m = Model()

    # Create dust properties

    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust_radmc = dict()
    [dust_radmc['wl'], dust_radmc['abs'], dust_radmc['scat'],
     dust_radmc['g']] = np.genfromtxt('dustkappa_oh5_extended.inp',
                                      skip_header=2).T
    # opacity per mass of dust?
    dust_hy = dict()
    dust_hy['nu'] = c / dust_radmc['wl'] * 1e4
    ind = np.argsort(dust_hy['nu'])
    dust_hy['nu'] = dust_hy['nu'][ind]
    dust_hy['albedo'] = (dust_radmc['scat'] /
                         (dust_radmc['abs'] + dust_radmc['scat']))[ind]
    dust_hy['chi'] = (dust_radmc['abs'] + dust_radmc['scat'])[ind]
    dust_hy['g'] = dust_radmc['g'][ind]
    dust_hy['p_lin_max'] = 0 * dust_radmc['wl'][ind]  # assume no polarization

    d = HenyeyGreensteinDust(dust_hy['nu'], dust_hy['albedo'], dust_hy['chi'],
                             dust_hy['g'], dust_hy['p_lin_max'])
    # dust sublimation does not occur
    # d.set_sublimation_temperature(None)
    d.write(outdir + 'oh5.hdf5')
    d.plot(outdir + 'oh5.png')

    # Grids and Density
    # Calculation inherited from the script used for RADMC-3D

    # Parameters setup
    # Import the model parameters from another file
    #
    params = np.genfromtxt(indir + '/params.dat', dtype=None)
    tstar = params[0][1]
    mstar = params[1][1] * MS
    rstar = params[2][1] * RS
    M_env_dot = params[3][1] * MS / yr
    M_disk_dot = params[4][1] * MS / yr
    R_env_max = params[5][1] * AU
    R_env_min = params[6][1] * AU
    theta_cav = params[7][1]
    R_disk_max = params[8][1] * AU
    R_disk_min = params[9][1] * AU
    R_cen = R_disk_max
    M_disk = params[10][1] * MS
    beta = params[11][1]
    h100 = params[12][1] * AU
    rho_cav = params[13][1]
    if denser_wall == True:
        wall = params[14][1] * AU
        rho_wall = params[15][1]
    rho_cav_center = params[16][1]
    rho_cav_edge = params[17][1] * AU

    # Grid Parameters
    nx = 300L
    if low_res == True:
        nx = 100L
    ny = 400L
    nz = 50L
    [nx, ny, nz] = [scale * nx, scale * ny, scale * nz]
    # nx        = 20
    # ny        = 40
    # nz        = 5

    # Model Parameters
    #
    rin = rstar
    rout = R_env_max
    rcen = R_cen

    # Star Parameters
    #
    mstar = mstar
    rstar = rstar * 0.9999
    tstar = tstar
    pstar = [0., 0., 0.]

    # Make the Coordinates
    #
    ri = rin * (rout / rin)**(np.arange(nx + 1).astype(dtype='float') /
                              float(nx))
    ri = np.hstack((0.0, ri))
    thetai = PI * np.arange(ny + 1).astype(dtype='float') / float(ny)
    phii = PI * 2.0 * np.arange(nz + 1).astype(dtype='float') / float(nz)

    # Keep the constant cell size in r-direction
    #
    if flat == True:
        ri_cellsize = ri[1:-1] - ri[0:-2]
        ind = np.where(
            ri_cellsize / AU > 100.0)[0][0]  # The largest cell size is 100 AU
        ri = np.hstack(
            (ri[0:ind],
             ri[ind] + np.arange(np.ceil(
                 (rout - ri[ind]) / 100 / AU)) * 100 * AU))
        nxx = nx
        nx = len(ri) - 1

    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc = 0.5 * (ri[0:nx] + ri[1:nx + 1])
    thetac = 0.5 * (thetai[0:ny] + thetai[1:ny + 1])
    phic = 0.5 * (phii[0:nz] + phii[1:nz + 1])
    # phic         = 0.5*( phii[0:nz-1]   + phii[1:nz] )

    # Make the dust density model
    # Make the density profile of the envelope
    #
    print 'Calculating the dust density profile...'
    if theta_cav != 0:
        c0 = R_env_max**(-0.5) * np.sqrt(1 / np.sin(np.radians(theta_cav))**3 -
                                         1 / np.sin(np.radians(theta_cav)))
    else:
        c0 = 0
    rho_env = np.zeros([len(rc), len(thetac), len(phic)])
    rho_disk = np.zeros([len(rc), len(thetac), len(phic)])
    rho = np.zeros([len(rc), len(thetac), len(phic)])

    def f(w, z, beta, rstar, h100):
        f = 2 * PI * w * (1 - np.sqrt(rstar / w)) * (rstar / w)**(
            beta + 1) * np.exp(-0.5 * (z / (w**beta * h100 / 100**beta))**2)
        return f

    rho_0 = M_disk / (nquad(
        f, [[R_disk_min, R_disk_max], [-R_env_max, R_env_max]],
        args=(beta, rstar, h100)))[0]
    i = 0
    j = 0
    if 'rho_cav_center' in locals() == False:
        rho_cav_center = 5.27e-18  # 1.6e-17  # 5.27e-18
        print 'Use 5.27e-18 as the default value for cavity center'
    if 'rho_cav_edge' in locals() == False:
        rho_cav_edge = 40 * AU
        print 'Use 40 AU as the default value for size of the inner region'
    discont = 1
    if denser_wall == False:
        for ir in range(0, len(rc)):
            for itheta in range(0, len(thetac)):
                for iphi in range(0, len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir] * np.cos(np.pi / 2 - thetac[itheta]))
                        z = rc[ir] * np.sin(np.pi / 2 - thetac[itheta])
                        z_cav = c0 * abs(w)**1.5
                        if z_cav == 0:
                            z_cav = R_env_max
                        if abs(z) > abs(z_cav):
                            # rho_env[ir,itheta,iphi] = rho_cav
                            # Modification for using density gradient in the cavity
                            if rc[ir] <= rho_cav_edge:
                                rho_env[
                                    ir, itheta,
                                    iphi] = rho_cav_center  #*((rc[ir]/AU)**2)
                            else:
                                rho_env[ir, itheta,
                                        iphi] = rho_cav_center * discont * (
                                            rho_cav_edge / rc[ir])**2
                            i += 1
                        else:
                            j += 1
                            mu = abs(np.cos(thetac[itheta]))
                            mu_o = np.abs(
                                fsolve(func, [0.5, 0.5, 0.5],
                                       args=(rc[ir], rcen, mu))[0])
                            rho_env[ir, itheta, iphi] = M_env_dot / (
                                4 * PI * (G * mstar * rcen**3)**0.5
                            ) * (rc[ir] / rcen)**(-3. / 2) * (1 + mu / mu_o)**(
                                -0.5) * (mu / mu_o +
                                         2 * mu_o**2 * rcen / rc[ir])**(-1)
                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w / (100 * AU))**beta) * h100
                            rho_disk[ir, itheta, iphi] = rho_0 * (1 - np.sqrt(
                                rstar / w)) * (rstar / w)**(beta + 1) * np.exp(
                                    -0.5 * (z / h)**2)
                        # Combine envelope and disk
                        rho[ir, itheta,
                            iphi] = rho_disk[ir, itheta,
                                             iphi] + rho_env[ir, itheta, iphi]

                        # # testing the effect of new solver
                        # # Envelope profile
                        # w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        # z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])
                        # z_cav = c0*abs(w)**1.5
                        # if z_cav == 0:
                        #     z_cav = R_env_max
                        # if abs(z) > abs(z_cav):
                        #     # rho_env[ir,itheta,iphi] = rho_cav
                        #     # Modification for using density gradient in the cavity
                        #     if rc[ir] <= rho_cav_edge:
                        #         rho_env[ir,itheta,iphi] = rho_cav_center#*((rc[ir]/AU)**2)
                        #     else:
                        #         rho_env[ir,itheta,iphi] = rho_cav_center*discont*(rho_cav_edge/rc[ir])**2
                        #     i += 1
                        # else:
                        #     j += 1
                        #     mu = abs(np.cos(thetac[itheta]))
                        #     # Implement new root finding algorithm
                        #     roots = np.roots(np.array([1.0, 0.0, rc[ir]/rcen-1.0, -mu*rc[ir]/rcen]))
                        #     if len(roots[roots.imag == 0]) == 1:
                        #         if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                        #             mu_o_dum = roots[roots.imag == 0]
                        #         else:
                        #             mu_o_dum = -0.5
                        #             print 'Problem with cubic solving, cos(theta) = ', mu_o_dum
                        #             print 'parameters are ', np.array([1.0, 0.0, rc[ir]/rcen-1.0, -mu*rc[ir]/rcen])
                        #     else:
                        #         mu_o_dum = -0.5
                        #         for imu in range(0, len(roots)):
                        #             if roots[imu]*mu >= 0.0:
                        #                 if (abs((abs(roots[imu]) - 1.0)) <= 1e-5):
                        #                     mu_o_dum = 1.0 * np.sign(mu)
                        #                 else:
                        #                     mu_o_dum = roots[imu]
                        #         if mu_o_dum == -0.5:
                        #             print 'Problem with cubic solving, roots are: ', roots
                        #     mu_o = mu_o_dum.real
                        #     rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*rcen**3)**0.5)*(rc[ir]/rcen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*rcen/rc[ir])**(-1)
                        # # Disk profile
                        # if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                        #     h = ((w/(100*AU))**beta)*h100
                        #     rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # # Combine envelope and disk
                        # rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
                    else:
                        rho[ir, itheta, iphi] = 1e-30
        rho_env = rho_env + 1e-40
        rho_disk = rho_disk + 1e-40
        rho = rho + 1e-40
    else:
        for ir in range(0, len(rc)):
            for itheta in range(0, len(thetac)):
                for iphi in range(0, len(phic)):
                    # Envelope profile
                    w = abs(rc[ir] * np.cos(thetac[itheta]))
                    z = rc[ir] * np.sin(thetac[itheta])
                    z_cav = c * abs(w)**1.5
                    z_cav_wall = c * abs(w - wall)**1.5
                    if z_cav == 0:
                        z_cav = R_env_max
                    if abs(z) > abs(z_cav):
                        # rho_env[ir,itheta,iphi] = rho_cav
                        # Modification for using density gradient in the cavity
                        if rc[ir] <= 20 * AU:
                            rho_env[ir, itheta,
                                    iphi] = rho_cav_center * ((rc[ir] / AU)**2)
                        else:
                            rho_env[ir, itheta,
                                    iphi] = rho_cav_center * discont * (
                                        20 * AU / rc[ir])**2
                        i += 1
                    elif (abs(z) > abs(z_cav_wall)) and (abs(z) < abs(z_cav)):
                        rho_env[ir, itheta, iphi] = rho_wall
                    else:
                        j += 1
                        mu = abs(np.cos(thetac[itheta]))
                        mu_o = np.abs(
                            fsolve(func, [0.5, 0.5, 0.5],
                                   args=(rc[ir], rcen, mu))[0])
                        rho_env[ir, itheta, iphi] = M_env_dot / (
                            4 * PI * (G * mstar * rcen**3)**0.5) * (
                                rc[ir] / rcen)**(-3. / 2) * (1 + mu / mu_o)**(
                                    -0.5) * (mu / mu_o +
                                             2 * mu_o**2 * rcen / rc[ir])**(-1)
                    # Disk profile
                    if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                        h = ((w / (100 * AU))**beta) * h100
                        rho_disk[ir, itheta,
                                 iphi] = rho_0 * (1 - np.sqrt(rstar / w)) * (
                                     rstar / w)**(beta + 1) * np.exp(
                                         -0.5 * (z / h)**2)
                    # Combine envelope and disk
                    rho[ir, itheta,
                        iphi] = rho_disk[ir, itheta,
                                         iphi] + rho_env[ir, itheta, iphi]
        rho_env = rho_env + 1e-40
        rho_disk = rho_disk + 1e-40
        rho = rho + 1e-40

    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)
    m.add_density_grid(rho.T, outdir +
                       'oh5.hdf5')  # numpy read the array in reverse order

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4 * PI * rstar**2) * sigma * (tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print 'L_center =  % 5.2f L_sun' % ((4 * PI * rstar**2) * sigma *
                                        (tstar**4) / LS)

    # Setting up images and SEDs
    image = m.add_peeled_images()
    image.set_wavelength_range(300, 2.0, 670.0)
    # pixel number
    image.set_image_size(300, 300)
    image.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    image.set_viewing_angles([82.0], [0.0])
    image.set_uncertainties(True)
    # output as 64-bit
    image.set_output_bytes(8)

    # Radiative transfer setting

    # number of photons for temp and image
    m.set_raytracing(True)
    m.set_n_photons(initial=1000000,
                    imaging=1000000,
                    raytracing_sources=1000000,
                    raytracing_dust=1000000)
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(5)
    m.set_convergence(True, percentile=99., absolute=1.5, relative=1.02)
    m.set_mrw(True)  # Gamma = 1 by default

    # Output setting
    # Density
    m.conf.output.output_density = 'last'

    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'

    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'

    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'

    m.write(outdir + 'old_setup2.rtin')
Example #16
0
import numpy as np

from hyperion.model import Model
from hyperion.util.constants import pc, lsun

# Initialize model
m = Model()

# Set up 64x64x64 cartesian grid
w = np.linspace(-pc, pc, 64)
m.set_cartesian_grid(w, w, w)

# Add density grid with constant density and add a higher density cube inside to
# cause a shadow.
density = np.ones(m.grid.shape) * 1e-21
density[26:38, 26:38, 26:38] = 1.e-18
m.add_density_grid(density, 'kmh_lite.hdf5')

# Add a point source in the center
s = m.add_point_source()
s.position = (0.4 * pc, 0., 0.)
s.luminosity = 1000 * lsun
s.temperature = 6000.

# Add multi-wavelength image for a single viewing angle
image = m.add_peeled_images(sed=False, image=True)
image.set_wavelength_range(1, 190., 210.)
image.set_viewing_angles(np.repeat(45., 36), np.linspace(5., 355., 36))
image.set_image_size(400, 400)
image.set_image_limits(-1.5 * pc, 1.5 * pc, -1.5 * pc, 1.5 * pc)
Example #17
0
import numpy as np
from hyperion.model import Model
from hyperion.dust import SphericalDust
from hyperion.util.constants import pc

for tau_v in [0.1, 1.0, 20.0]:

    m = Model.read('bm1_slab_eff_tau{0:05.2f}_temperature.rtout'.format(tau_v), only_initial=False)

    m.set_n_initial_iterations(0)

    del m.n_photons['initial']
    del m.n_photons['last']

    i = m.add_peeled_images()
    i.set_viewing_angles([0., 30., 60., 90., 120., 150., 180.], [0., 0., 0., 0., 0., 0., 0.])
    i.set_image_limits(-7.5 * pc, 7.5 * pc, -7.5 * pc, 7.5 * pc)
    i.set_image_size(300, 300)

    # Set up monochromatic mode
    m.set_monochromatic(True, wavelengths=[0.165, 0.570, 21.3, 161.6])

    # Use raytracing
    m.set_raytracing(True)

    # Set up number of photons
    m.set_n_photons(imaging_sources=1e9, imaging_dust=1e9,
                    raytracing_sources=1, raytracing_dust=1e9)

    # Write out and run
    m.write('bm1_slab_effgrain_tau_{0:05.2f}_images.rtin'.format(tau_v), overwrite=True)
Example #18
0
def sph_m_gen(fname,field_add):

    refined,dustdens,fc1,fw1,reg,ds = yt_octree_generate(fname,field_add)
    
    if float(yt.__version__[0:3]) >= 4:
        xmin = (fc1[:,0]-fw1[:,0]/2.).to('cm') #in proper cm 
        xmax = (fc1[:,0]+fw1[:,0]/2.).to('cm')
        ymin = (fc1[:,1]-fw1[:,1]/2.).to('cm')
        ymax = (fc1[:,1]+fw1[:,1]/2.).to('cm')
        zmin = (fc1[:,2]-fw1[:,2]/2.).to('cm')
        zmax = (fc1[:,2]+fw1[:,2]/2.).to('cm')
    else:
        xmin = (fc1[:,0]-fw1[:,0]/2.).convert_to_units('cm') #in proper cm
        xmax = (fc1[:,0]+fw1[:,0]/2.).convert_to_units('cm')
        ymin = (fc1[:,1]-fw1[:,1]/2.).convert_to_units('cm')
        ymax = (fc1[:,1]+fw1[:,1]/2.).convert_to_units('cm')
        zmin = (fc1[:,2]-fw1[:,2]/2.).convert_to_units('cm')
        zmax = (fc1[:,2]+fw1[:,2]/2.).convert_to_units('cm')

    #dx,dy,dz are the edges of the parent grid
    dx = (np.max(xmax)-np.min(xmin)).value
    dy = (np.max(ymax)-np.min(ymin)).value
    dz = (np.max(zmax)-np.min(zmin)).value


    xcent = float(ds.quan(cfg.model.x_cent,"code_length").to('cm').value)
    ycent = float(ds.quan(cfg.model.y_cent,"code_length").to('cm').value)
    zcent = float(ds.quan(cfg.model.z_cent,"code_length").to('cm').value)

    boost = np.array([xcent,ycent,zcent])
    print ('[sph_tributary] boost = ',boost)
    print ('[sph_tributary] xmin (pc)= ',np.min(xmin.to('pc')))
    print ('[sph_tributary] xmax (pc)= ',np.max(xmax.to('pc')))
    print ('[sph_tributary] ymin (pc)= ',np.min(ymin.to('pc')))
    print ('[sph_tributary] ymax (pc)= ',np.max(ymax.to('pc')))
    print ('[sph_tributary] zmin (pc)= ',np.min(zmin.to('pc')))
    print ('[sph_tributary] zmax (pc)= ',np.max(zmax.to('pc')))
    #Tom Robitaille's conversion from z-first ordering (yt's default) to
    #x-first ordering (the script should work both ways)



    refined_array = np.array(refined)
    refined_array = np.squeeze(refined_array)
    
    order = find_order(refined_array)
    refined_reordered = []
    dustdens_reordered = np.zeros(len(order))
    


    
    for i in range(len(order)): 
        refined_reordered.append(refined[order[i]])
        dustdens_reordered[i] = dustdens[order[i]]


    refined = refined_reordered
    dustdens=dustdens_reordered

    #hyperion octree stats
    max_level = hos.hyperion_octree_stats(refined)


    pto.test_octree(refined,max_level)
    
    if float(yt.__version__[0:3]) >= 4:
        dump_cell_info(refined,fc1.to('cm'),fw1.to('cm'),xmin,xmax,ymin,ymax,zmin,zmax)
    else:
        dump_cell_info(refined,fc1.convert_to_units('cm'),fw1.convert_to_units('cm'),xmin,xmax,ymin,ymax,zmin,zmax)
    
    np.save('refined.npy',refined)
    np.save('density.npy',dustdens)
    

    #========================================================================
    #Initialize Hyperion Model
    #========================================================================

    m = Model()
    
    #save in the m__dict__ that we're in an oct geometry
    m.__dict__['grid_type']='oct'

    print ('Setting Octree Grid with Parameters: ')



    #m.set_octree_grid(xcent,ycent,zcent,
    #                  dx,dy,dz,refined)
    m.set_octree_grid(0,0,0,dx/2,dy/2,dz/2,refined)    

    #get CMB:
    
    energy_density_absorbed=energy_density_absorbed_by_CMB()
    specific_energy = np.repeat(energy_density_absorbed.value,dustdens.shape)


    if cfg.par.otf_extinction == False:
        
        if cfg.par.PAH == True:

            # load PAH fractions for usg, vsg, and big (grain sizes)
            frac = cfg.par.PAH_frac
            
            # Normalize to 1
            total = np.sum(list(frac.values()))
            frac = {k: v / total for k, v in frac.items()}

            for size in frac.keys():
                d = SphericalDust(cfg.par.dustdir+'%s.hdf5'%size)
                if cfg.par.SUBLIMATION == True:
                    d.set_sublimation_temperature('fast',temperature=cfg.par.SUBLIMATION_TEMPERATURE)
                #m.add_density_grid(dustdens * frac[size], cfg.par.dustdir+'%s.hdf5' % size)
                m.add_density_grid(dustdens*frac[size],d,specific_energy=specific_energy)
            m.set_enforce_energy_range(cfg.par.enforce_energy_range)
        else:
            d = SphericalDust(cfg.par.dustdir+cfg.par.dustfile)
            if cfg.par.SUBLIMATION == True:
                d.set_sublimation_temperature('fast',temperature=cfg.par.SUBLIMATION_TEMPERATURE)
            m.add_density_grid(dustdens,d,specific_energy=specific_energy)
        #m.add_density_grid(dustdens,cfg.par.dustdir+cfg.par.dustfile)  


    else: #instead of using a constant extinction law across the
          #entire galaxy, we'll compute it on a cell-by-cell basis by
          #using information about the grain size distribution from
          #the simulation itself.


        print("==============================================\n")
        print("Entering OTF Extinction Calculation\n")
        print("Note: For very high-resolution grids, this may cause memory issues due to adding ncells dust grids")
        print("==============================================\n")
        
        ad = ds.all_data()
        nsizes = ad['PartType3','Dust_Size'].shape[1]
        ncells = reg.parameters["octree_of_sizes"].shape[0]
        #ensure that the grid has particles
        for isize in range(nsizes):
            try:
                assert (np.sum(reg.parameters["octree_of_sizes"][:,isize]) > 0)
            except AssertionError:
                raise AssertionError("[sph_tributary:] The grain size distribution smoothed onto the octree has deposited no particles.  Try either increasing your box size, or decreasing n_ref in parameters_master.  Alternatively, run the simulation with otf_extinction=False")

        #define the grid of sizes that will be used in tributary_dust_add
        grid_of_sizes = reg.parameters["octree_of_sizes"]
        
        active_dust_add(ds,m,grid_of_sizes,nsizes,dustdens,specific_energy,refined)
        

    m.set_specific_energy_type('additional')

    return m,xcent,ycent,zcent,dx,dy,dz,reg,ds,boost
Example #19
0
def setup_model(outdir,record_dir,outname,params,dust_file,tsc=True,idl=False,plot=False,\
                low_res=True,flat=True,scale=1,radmc=False,mono=False,record=True,dstar=178.,\
                aperture=None,dyn_cav=False,fix_params=None,alma=False,power=2,better_im=False,ellipsoid=False,\
                TSC_dir='~/programs/misc/TSC/', IDL_path='/Applications/exelis/idl83/bin/idl',auto_disk=0.25):
    """
    params = dictionary of the model parameters
    alma keyword is obsoleted 
    outdir: The directory for storing Hyperion input files
    record_dir: The directory contains "model_list.txt" for recording parameters
    TSC_dir: Path the TSC-related IDL routines
    IDL_path: The IDL executable 
    """
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    #
    import matplotlib.pyplot as plt
    import os
    from matplotlib.colors import LogNorm
    from scipy.integrate import nquad
    from hyperion.model import Model
    from record_hyperion import record_hyperion
    from outflow_inner_edge import outflow_inner_edge
    from pprint import pprint
    # import pdb
    # pdb.set_trace()

    # Constants setup
    c = const.c.cgs.value
    AU = 1.49598e13  # Astronomical Unit       [cm]
    pc = 3.08572e18  # Parsec                  [cm]
    MS = 1.98892e33  # Solar mass              [g]
    LS = 3.8525e33  # Solar luminosity        [erg/s]
    RS = 6.96e10  # Solar radius            [cm]
    G = 6.67259e-8  # Gravitational constant  [cm3/g/s^2]
    yr = 60 * 60 * 24 * 365  # Years in seconds
    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
    g2d = 100.
    mmw = 2.37  # Kauffmann 2008

    m = Model()

    # Create dust properties

    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust = dict()
    # [dust_radmc['wl'], dust_radmc['abs'], dust_radmc['scat'], dust_radmc['g']] = np.genfromtxt(dust_file,skip_header=2).T
    [dust['nu'], dust['albedo'], dust['chi'],
     dust['g']] = np.genfromtxt(dust_file).T
    # opacity per mass of dust?
    # dust_hy = dict()
    # dust_hy['nu'] = c/dust_radmc['wl']*1e4
    # ind = np.argsort(dust_hy['nu'])
    # dust_hy['nu'] = dust_hy['nu'][ind]
    # dust_hy['albedo'] = (dust_radmc['scat']/(dust_radmc['abs']+dust_radmc['scat']))[ind]
    # dust_hy['chi'] = (dust_radmc['abs']+dust_radmc['scat'])[ind]
    # dust_hy['g'] = dust_radmc['g'][ind]
    # dust_hy['p_lin_max'] = 0*dust_radmc['wl'][ind]     # assume no polarization

    # d = HenyeyGreensteinDust(dust_hy['nu'], dust_hy['albedo'], dust_hy['chi'], dust_hy['g'], dust_hy['p_lin_max'])
    d = HenyeyGreensteinDust(dust['nu'], dust['albedo'], dust['chi'],
                             dust['g'], dust['g'] * 0)
    # dust sublimation option
    d.set_sublimation_temperature('slow', temperature=1600.0)
    d.set_lte_emissivities(n_temp=3000, temp_min=0.1, temp_max=2000.)
    # try to solve the freq. problem
    d.optical_properties.extrapolate_nu(3.28e15, 4e15)
    #
    d.write(outdir + os.path.basename(dust_file).split('.')[0] + '.hdf5')
    d.plot(outdir + os.path.basename(dust_file).split('.')[0] + '.png')
    plt.clf()

    # Grids and Density
    # Calculation inherited from the script used for RADMC-3D

    # Grid Parameters
    nx = 300L
    if low_res == True:
        nx = 100L
    ny = 400L
    nz = 50L
    [nx, ny, nz] = [int(scale * nx), int(scale * ny), int(scale * nz)]

    # TSC model input setting
    # params    = np.genfromtxt(indir+'/tsc_params.dat', dtype=None)
    dict_params = params  # input_reader(params_file)
    # TSC model parameter
    cs = dict_params['Cs'] * 1e5
    t = dict_params['age']  # year
    omega = dict_params['Omega0']
    # calculate related parameters
    M_env_dot = 0.975 * cs**3 / G
    mstar = M_env_dot * t * yr
    R_cen = omega**2 * G**3 * mstar**3 / (16 * cs**8)
    R_inf = cs * t * yr
    # M_env_dot = dict_params['M_env_dot']*MS/yr
    # R_cen     = dict_params['R_cen']*AU
    # R_inf     = dict_params['R_inf']*AU
    # protostar parameter
    tstar = dict_params['tstar']
    R_env_max = dict_params['R_env_max'] * AU
    theta_cav = dict_params['theta_cav']
    rho_cav_center = dict_params['rho_cav_center']
    rho_cav_edge = dict_params['rho_cav_edge'] * AU
    rstar = dict_params['rstar'] * RS
    # Mostly fixed parameter
    M_disk = dict_params['M_disk'] * MS
    beta = dict_params['beta']
    h100 = dict_params['h100'] * AU
    rho_cav = dict_params['rho_cav']
    # make M_disk varies with mstar, which is the mass of star+disk
    if auto_disk != None:
        if M_disk != 0:
            print 'M_disk is reset to %4f of mstar (star+disk)' % auto_disk
            M_disk = mstar * auto_disk
        else:
            print 'M_disk = 0 is found.  M_disk is set to 0.'

    # ellipsoid cavity parameter
    if ellipsoid == True:
        a_out = 130 * 178. * AU
        b_out = 50 * 178. * AU
        z_out = a_out
        # a_in  = 77.5 * 178. * AU
        # b_in  = 30   * 178. * AU
        a_in = dict_params['a_in'] * 178. * AU
        b_in = a_in / a_out * b_out
        z_in = a_in
        # rho_cav_out = 1e4 * mh
        # rho_cav_in  = 1e3 * mh
        rho_cav_out = dict_params['rho_cav_out'] * mh
        rho_cav_in = dict_params['rho_cav_in'] * mh
    # Calculate the dust sublimation radius
    T_sub = 1600
    a = 1  #in micron
    # realistic dust
    # d_sub = 2.9388e7*(a/0.1)**-0.2 * (4*np.pi*rstar**2*sigma*tstar**4/LS)**0.5 / T_sub**3 *AU
    # black body dust
    d_sub = (LS / 16. / np.pi / sigma / AU**2 *
             (4 * np.pi * rstar**2 * sigma * tstar**4 / LS) /
             T_sub**4)**0.5 * AU
    # use the dust sublimation radius as the inner radius of disk and envelope
    R_disk_min = d_sub
    R_env_min = d_sub
    rin = rstar
    rout = R_env_max
    R_disk_max = R_cen

    # Do the variable conversion
    # cs = (G * M_env_dot / 0.975)**(1/3.)  # cm/s
    # t = R_inf / cs / yr   # in year
    # mstar = M_env_dot * t * yr
    # omega = (R_cen * 16*cs**8 / (G**3 * mstar**3))**0.5

    # print the variables for radmc3d
    print 'Dust sublimation radius %6f AU' % (d_sub / AU)
    print 'M_star %4f Solar mass' % (mstar / MS)
    print 'Infall radius %4f AU' % (R_inf / AU)

    # if there is any parameter found in fix_params, then fix them
    if fix_params != None:
        if 'R_min' in fix_params.keys():
            R_disk_min = fix_params['R_min'] * AU
            R_env_min = fix_params['R_min'] * AU

    # Make the Coordinates
    #
    ri = rin * (rout / rin)**(np.arange(nx + 1).astype(dtype='float') /
                              float(nx))
    ri = np.hstack((0.0, ri))
    thetai = PI * np.arange(ny + 1).astype(dtype='float') / float(ny)
    phii = PI * 2.0 * np.arange(nz + 1).astype(dtype='float') / float(nz)

    # Keep the constant cell size in r-direction at large radii
    #
    if flat == True:
        ri_cellsize = ri[1:-1] - ri[0:-2]
        ind = np.where(
            ri_cellsize / AU > 100.0)[0][0]  # The largest cell size is 100 AU
        ri = np.hstack(
            (ri[0:ind],
             ri[ind] + np.arange(np.ceil(
                 (rout - ri[ind]) / 100 / AU)) * 100 * AU))
        nxx = nx
        nx = len(ri) - 1
    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc = 0.5 * (ri[0:nx] + ri[1:nx + 1])
    thetac = 0.5 * (thetai[0:ny] + thetai[1:ny + 1])
    phic = 0.5 * (phii[0:nz] + phii[1:nz + 1])
    # phic         = 0.5*( phii[0:nz-1]   + phii[1:nz] )

    # Make the dust density model
    # Make the density profile of the envelope
    #
    total_mass = 0
    if tsc == False:
        print 'Calculating the dust density profile with infall solution...'
        if theta_cav != 0:
            # c0 = R_env_max**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
            # using R = 10000 AU as the reference point
            c0 = (10000. *
                  AU)**(-0.5) * np.sqrt(1 / np.sin(np.radians(theta_cav))**3 -
                                        1 / np.sin(np.radians(theta_cav)))
        else:
            c0 = 0
        rho_env = np.zeros([len(rc), len(thetac), len(phic)])
        rho_disk = np.zeros([len(rc), len(thetac), len(phic)])
        rho = np.zeros([len(rc), len(thetac), len(phic)])

        if dyn_cav == True:
            print 'WARNING: Calculation of interdependent cavity property has not implemented in infall-only solution!'
        # Normalization for the total disk mass
        def f(w, z, beta, rstar, h100):
            f = 2 * PI * w * (1 - np.sqrt(rstar / w)) * (rstar / w)**(
                beta + 1) * np.exp(-0.5 * (z /
                                           (w**beta * h100 / 100**beta))**2)
            return f

        rho_0 = M_disk / (nquad(
            f, [[R_disk_min, R_disk_max], [-R_env_max, R_env_max]],
            args=(beta, rstar, h100)))[0]
        i = 0
        j = 0
        if 'rho_cav_center' in locals() == False:
            rho_cav_center = 5.27e-18  # 1.6e-17  # 5.27e-18
            print 'Use 5.27e-18 as the default value for cavity center'
        if 'rho_cav_edge' in locals() == False:
            rho_cav_edge = 40 * AU
            print 'Use 40 AU as the default value for size of the inner region'
        discont = 1
        for ir in range(0, len(rc)):
            for itheta in range(0, len(thetac)):
                for iphi in range(0, len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir] * np.cos(np.pi / 2 - thetac[itheta]))
                        z = rc[ir] * np.sin(np.pi / 2 - thetac[itheta])

                        if ellipsoid == False:
                            z_cav = c0 * abs(w)**1.5
                            if z_cav == 0:
                                z_cav = R_env_max
                            cav_con = abs(z) > abs(z_cav)
                        else:
                            # condition for the outer ellipsoid
                            cav_con = (2 * (w / b_out)**2 +
                                       ((abs(z) - z_out) / a_out)**2) < 1
                        if cav_con:
                            # open cavity
                            if ellipsoid == False:
                                if rho_cav_edge == 0:
                                    rho_cav_edge = R_env_min
                                if (rc[ir] <= rho_cav_edge) & (rc[ir] >=
                                                               R_env_min):
                                    rho_env[
                                        ir, itheta,
                                        iphi] = g2d * rho_cav_center  #*((rc[ir]/AU)**2)
                                else:
                                    rho_env[
                                        ir, itheta,
                                        iphi] = g2d * rho_cav_center * discont * (
                                            rho_cav_edge / rc[ir])**power
                                i += 1
                            else:
                                # condition for the inner ellipsoid
                                if (2 * (w / b_in)**2 +
                                    ((abs(z) - z_in) / a_in)**2) > 1:
                                    rho_env[ir, itheta, iphi] = rho_cav_out
                                else:
                                    rho_env[ir, itheta, iphi] = rho_cav_in
                                i += 1
                        else:
                            j += 1
                            mu = abs(np.cos(thetac[itheta]))
                            # Implement new root finding algorithm
                            roots = np.roots(
                                np.array([
                                    1.0, 0.0, rc[ir] / R_cen - 1.0,
                                    -mu * rc[ir] / R_cen
                                ]))
                            if len(roots[roots.imag == 0]) == 1:
                                if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                                    mu_o_dum = roots[roots.imag == 0]
                                else:
                                    mu_o_dum = -0.5
                                    print 'Problem with cubic solving, cos(theta) = ', mu_o_dum
                                    print 'parameters are ', np.array([
                                        1.0, 0.0, rc[ir] / R_cen - 1.0,
                                        -mu * rc[ir] / R_cen
                                    ])
                            else:
                                mu_o_dum = -0.5
                                for imu in range(0, len(roots)):
                                    if roots[imu] * mu >= 0.0:
                                        if (abs(
                                            (abs(roots[imu]) - 1.0)) <= 1e-5):
                                            mu_o_dum = 1.0 * np.sign(mu)
                                        else:
                                            mu_o_dum = roots[imu]
                                if mu_o_dum == -0.5:
                                    print 'Problem with cubic solving, roots are: ', roots
                            mu_o = mu_o_dum.real
                            rho_env[ir, itheta, iphi] = M_env_dot / (
                                4 * PI * (G * mstar * R_cen**3)**0.5) * (
                                    rc[ir] / R_cen)**(-3. / 2) * (
                                        1 + mu / mu_o)**(-0.5) * (
                                            mu / mu_o +
                                            2 * mu_o**2 * R_cen / rc[ir])**(-1)
                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w / (100 * AU))**beta) * h100
                            rho_disk[ir, itheta, iphi] = rho_0 * (1 - np.sqrt(
                                rstar / w)) * (rstar / w)**(beta + 1) * np.exp(
                                    -0.5 * (z / h)**2)
                        # Combine envelope and disk
                        rho[ir, itheta,
                            iphi] = rho_disk[ir, itheta,
                                             iphi] + rho_env[ir, itheta, iphi]
                    else:
                        rho[ir, itheta, iphi] = 1e-30
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1 / 3.) * (
                        ri[ir + 1]**3 -
                        ri[ir]**3) * (phii[iphi + 1] - phii[iphi]) * -(np.cos(
                            thetai[itheta + 1]) - np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass

        rho_env = rho_env + 1e-40
        rho_disk = rho_disk + 1e-40
        rho = rho + 1e-40
    # TSC model
    else:
        print 'Calculating the dust density profile with TSC solution...'
        if theta_cav != 0:
            # c0 = R_env_max**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
            c0 = (1e4 *
                  AU)**(-0.5) * np.sqrt(1 / np.sin(np.radians(theta_cav))**3 -
                                        1 / np.sin(np.radians(theta_cav)))
        else:
            c0 = 0
        # If needed, calculate the TSC model via IDL
        #
        if idl == True:
            print 'Using IDL to calculate the TSC model.  Make sure you are running this on mechine with IDL.'
            import pidly
            # idl = pidly.IDL('/Applications/exelis/idl82/bin/idl')
            idl = pidly.IDL(IDL_path)
            idl('.r ' + TSC_dir + 'tsc.pro')
            # idl.pro('tsc_run', outdir=outdir, grid=[nxx,ny,nz], time=t, c_s=cs, omega=omega, rstar=rstar, renv_min=R_env_min, renv_max=R_env_max)
            # idl.pro('tsc_run', outdir=outdir, grid=[nxx,ny,nz], time=t, c_s=cs, omega=omega, rstar=rstar, renv_min=R_env_min, renv_max=min([R_inf,max(ri)])) # min([R_inf,max(ri)])
            #
            # only run TSC calculation within infall radius
            # modify the rc array
            rc_idl = rc[(rc < min([R_inf, max(ri)]))]
            idl.pro(
                'tsc_run',
                outdir=outdir,
                rc=rc_idl,
                thetac=thetac,
                time=t,
                c_s=cs,
                omega=omega,
                renv_min=R_env_min
            )  #, rstar=rstar, renv_min=R_env_min, renv_max=min([R_inf,max(ri)])) # min([R_inf,max(ri)])
        else:
            print 'Read the pre-computed TSC model.'
            rc_idl = rc[(rc < min([R_inf, max(ri)]))]
        # read in the exist file
        rho_env_tsc_idl = np.genfromtxt(outdir + 'rhoenv.dat').T
        # because only region within infall radius is calculated by IDL program, need to project it to the original grid
        rho_env_tsc = np.zeros([len(rc), len(thetac)])
        for irc in range(len(rc)):
            if rc[irc] in rc_idl:
                rho_env_tsc[irc, :] = rho_env_tsc_idl[np.where(
                    rc_idl == rc[irc]), :]

        # extrapolate for the NaN values at the outer radius, usually at radius beyond the infall radius
        # using r^-2 profile at radius greater than infall radius
        # and map the 2d strcuture onto 3d grid
        def poly(x, y, x0, deg=2):
            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

        # rho_env_copy = np.array(rho_env_tsc)
        # if max(rc) > R_inf:
        #     ind_infall = np.where(rc <= R_inf)[0][-1]
        #     print ind_infall
        #     for ithetac in range(0, len(thetac)):
        #         # rho_dum = np.log10(rho_env_copy[(rc > R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == False),ithetac])
        #         # rc_dum = np.log10(rc[(rc > R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == False)])
        #         # rc_dum_nan = np.log10(rc[(rc > R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == True)])
        #         # # print rc_dum
        #         # for i in range(0, len(rc_dum_nan)):
        #         #     rho_extrapol = poly(rc_dum, rho_dum, rc_dum_nan[i])
        #         #     rho_env_copy[(np.log10(rc) == rc_dum_nan[i]),ithetac] = 10**rho_extrapol
        #         #
        #         for i in range(ind_infall, len(rc)):
        #             rho_env_copy[i, ithetac] =  10**(np.log10(rho_env_copy[ind_infall, ithetac]) - 2*(np.log10(rc[i]/rc[ind_infall])))
        # rho_env2d = rho_env_copy
        # rho_env = np.empty((nx,ny,nz))
        # for i in range(0, nz):
        #     rho_env[:,:,i] = rho_env2d
        # map TSC solution from IDL to actual 2-D grid
        rho_env_tsc2d = np.empty((nx, ny))
        if max(ri) > R_inf:
            ind_infall = np.where(rc <= R_inf)[0][-1]
            for i in range(0, len(rc)):
                if i <= ind_infall:
                    rho_env_tsc2d[i, :] = rho_env_tsc[i, :]
                else:
                    rho_env_tsc2d[i, :] = 10**(
                        np.log10(rho_env_tsc[ind_infall, :]) - 2 *
                        (np.log10(rc[i] / rc[ind_infall])))
        else:
            rho_env_tsc2d = rho_env_tsc
        # map it to 3-D grid
        rho_env = np.empty((nx, ny, nz))
        for i in range(0, nz):
            rho_env[:, :, i] = rho_env_tsc2d

        if dyn_cav == True:
            print 'Calculate the cavity properties using the criteria that swept-up mass = outflowed mass'
            # using swept-up mass = flow mass to derive the edge of the extended flat density region
            v_outflow = 1e2 * 1e5
            rho_cav_edge = outflow_inner_edge(np.copy(rho_env),
                                              (ri, thetai, phii), M_env_dot,
                                              v_outflow, theta_cav, R_env_min)
            dict_params['rho_cav_edge'] = rho_cav_edge
            # assume gas-to-dust ratio = 100
            rho_cav_center = 0.01 * 0.1 * M_env_dot * rho_cav_edge / v_outflow / 2 / (
                2 * np.pi / 3 * rho_cav_edge**3 *
                (1 - np.cos(np.radians(theta_cav))))
            dict_params['rho_cav_center'] = rho_cav_center
            print 'inner edge is %5f AU and density is %e g/cm3' % (
                rho_cav_edge / AU, rho_cav_center)

        # create the array of density of disk and the whole structure
        #
        rho_disk = np.zeros([len(rc), len(thetac), len(phic)])
        rho = np.zeros([len(rc), len(thetac), len(phic)])

        # Calculate the disk scale height by the normalization of h100
        def f(w, z, beta, rstar, h100):
            f = 2 * PI * w * (1 - np.sqrt(rstar / w)) * (rstar / w)**(
                beta + 1) * np.exp(-0.5 * (z /
                                           (w**beta * h100 / 100**beta))**2)
            return f

        # The function for calculating the normalization of disk using the total disk mass
        #
        rho_0 = M_disk / (nquad(
            f, [[R_disk_min, R_disk_max], [-R_env_max, R_env_max]],
            args=(beta, rstar, h100)))[0]
        i = 0
        j = 0
        if 'rho_cav_center' in locals() == False:
            rho_cav_center = 5.27e-18  # 1.6e-17  # 5.27e-18
            print 'Use 5.27e-18 as the default value for cavity center'
        if 'rho_cav_edge' in locals() == False:
            rho_cav_edge = 40 * AU
            print 'Use 40 AU as the default value for size of the inner region'
        discont = 1
        for ir in range(0, len(rc)):
            for itheta in range(0, len(thetac)):
                for iphi in range(0, len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir] * np.cos(np.pi / 2 - thetac[itheta]))
                        z = rc[ir] * np.sin(np.pi / 2 - thetac[itheta])

                        if ellipsoid == False:
                            z_cav = c0 * abs(w)**1.5
                            if z_cav == 0:
                                z_cav = R_env_max
                            cav_con = abs(z) > abs(z_cav)
                        else:
                            # condition for the outer ellipsoid
                            cav_con = (2 * (w / b_out)**2 +
                                       ((abs(z) - z_out) / a_out)**2) < 1
                        if cav_con:
                            # open cavity
                            if ellipsoid == False:
                                if rho_cav_edge == 0:
                                    rho_cav_edge = R_env_min
                                if (rc[ir] <= rho_cav_edge) & (rc[ir] >=
                                                               R_env_min):
                                    rho_env[
                                        ir, itheta,
                                        iphi] = g2d * rho_cav_center  #*((rc[ir]/AU)**2)
                                else:
                                    rho_env[
                                        ir, itheta,
                                        iphi] = g2d * rho_cav_center * discont * (
                                            rho_cav_edge / rc[ir])**power
                                i += 1
                            else:
                                # condition for the inner ellipsoid
                                if (2 * (w / b_in)**2 +
                                    ((abs(z) - z_in) / a_in)**2) > 1:
                                    rho_env[ir, itheta, iphi] = rho_cav_out
                                else:
                                    rho_env[ir, itheta, iphi] = rho_cav_in
                                i += 1

                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w / (100 * AU))**beta) * h100
                            rho_disk[ir, itheta, iphi] = rho_0 * (1 - np.sqrt(
                                rstar / w)) * (rstar / w)**(beta + 1) * np.exp(
                                    -0.5 * (z / h)**2)
                        # Combine envelope and disk
                        rho[ir, itheta,
                            iphi] = rho_disk[ir, itheta,
                                             iphi] + rho_env[ir, itheta, iphi]
                    else:
                        rho[ir, itheta, iphi] = 1e-40
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1 / 3.) * (
                        ri[ir + 1]**3 -
                        ri[ir]**3) * (phii[iphi + 1] - phii[iphi]) * -(np.cos(
                            thetai[itheta + 1]) - np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass
        # rho_env  = rho_env  + 1e-40
        # rho_disk = rho_disk + 1e-40
        # rho      = rho      + 1e-40
    # apply gas-to-dust ratio of 100
    rho_dust = rho / g2d
    total_mass_dust = total_mass / MS / g2d
    print 'Total dust mass = %f Solar mass' % total_mass_dust

    if record == True:
        # Record the input and calculated parameters
        params = dict_params.copy()
        params.update({
            'd_sub': d_sub / AU,
            'M_env_dot': M_env_dot / MS * yr,
            'R_inf': R_inf / AU,
            'R_cen': R_cen / AU,
            'mstar': mstar / MS,
            'M_tot_gas': total_mass / MS
        })
        record_hyperion(params, record_dir)

    if plot == True:
        # rc setting
        # mat.rcParams['text.usetex'] = True
        # mat.rcParams['font.family'] = 'serif'
        # mat.rcParams['font.serif'] = 'Times'
        # mat.rcParams['font.sans-serif'] = 'Computer Modern Sans serif'

        # Plot the azimuthal averaged density
        fig = plt.figure(figsize=(8, 6))
        ax_env = fig.add_subplot(111, projection='polar')
        # take the weighted average
        # rho2d is the 2-D projection of gas density
        rho2d = np.sum(rho**2, axis=2) / np.sum(rho, axis=2)

        zmin = 1e-22 / mmw / mh
        cmap = plt.cm.CMRmap
        rho2d_exp = np.hstack((rho2d, rho2d, rho2d[:, 0:1]))
        thetac_exp = np.hstack(
            (thetac - PI / 2, thetac + PI / 2, thetac[0] - PI / 2))
        # plot the gas density
        img_env = ax_env.pcolormesh(
            thetac_exp,
            rc / AU,
            rho2d_exp / mmw / mh,
            cmap=cmap,
            norm=LogNorm(vmin=zmin, vmax=1e9))  # np.nanmax(rho2d_exp/mmw/mh)

        ax_env.set_xlabel(r'$\rm{Polar\,angle\,(Degree)}$', fontsize=20)
        ax_env.set_ylabel(r'$\rm{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_ylim([0,10000])
        ax_env.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}}$'])
        # fix the tick label font
        ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',
                                                     size=20)
        for label in ax_env.get_yticklabels():
            label.set_fontproperties(ticks_font)

        ax_env.grid(True)
        cb = fig.colorbar(img_env, pad=0.1)
        cb.ax.set_ylabel(r'$\rm{Averaged\,Gas\,Density\,(cm^{-3})}$',
                         fontsize=20)
        cb.set_ticks([1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9])
        cb.set_ticklabels([r'$\rm{10^{2}}$',r'$\rm{10^{3}}$',r'$\rm{10^{4}}$',r'$\rm{10^{5}}$',r'$\rm{10^{6}}$',\
                           r'$\rm{10^{7}}$',r'$\rm{10^{8}}$',r'$\rm{\geq 10^{9}}$'])
        cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
        plt.setp(cb_obj, fontsize=20)
        fig.savefig(outdir + outname + '_gas_density.png',
                    format='png',
                    dpi=300,
                    bbox_inches='tight')
        fig.clf()

        # Plot the radial density profile
        fig = plt.figure(figsize=(12, 9))
        ax = fig.add_subplot(111)

        plot_grid = [0, 49, 99, 149, 199]
        alpha = np.linspace(0.3, 1.0, len(plot_grid))
        for i in plot_grid:
            rho_rad, = ax.plot(np.log10(rc / AU),
                               np.log10(rho2d[:, i] / g2d / mmw / mh),
                               '-',
                               color='b',
                               linewidth=2,
                               markersize=3,
                               alpha=alpha[plot_grid.index(i)])
            tsc_only, = ax.plot(np.log10(rc / AU),
                                np.log10(rho_env_tsc2d[:, i] / mmw / mh),
                                'o',
                                color='r',
                                linewidth=2,
                                markersize=3,
                                alpha=alpha[plot_grid.index(i)])
        rinf = ax.axvline(np.log10(R_inf / AU),
                          linestyle='--',
                          color='k',
                          linewidth=1.5)
        cen_r = ax.axvline(np.log10(R_cen / AU),
                           linestyle=':',
                           color='k',
                           linewidth=1.5)
        # sisslope, = ax.plot(np.log10(rc/AU), -2*np.log10(rc/AU)+A-(-2)*np.log10(plot_r_inf), linestyle='--', color='Orange', linewidth=1.5)
        # gt_R_cen_slope, = ax.plot(np.log10(rc/AU), -1.5*np.log10(rc/AU)+B-(-1.5)*np.log10(plot_r_inf), linestyle='--', color='Orange', linewidth=1.5)
        # lt_R_cen_slope, = ax.plot(np.log10(rc/AU), -0.5*np.log10(rc/AU)+A-(-0.5)*np.log10(plot_r_inf), linestyle='--', color='Orange', linewidth=1.5)

        lg = plt.legend([rho_rad, tsc_only, rinf, cen_r],\
                        [r'$\rm{\rho_{dust}}$',r'$\rm{\rho_{tsc}}$',r'$\rm{infall\,radius}$',r'$\rm{centrifugal\,radius}$'],\
                        fontsize=20, numpoints=1)
        ax.set_xlabel(r'$\rm{log(Radius)\,(AU)}$', fontsize=20)
        ax.set_ylabel(r'$\rm{log(Gas \slash 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
        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)

        ax.set_ylim([0, 15])
        fig.gca().set_xlim(left=np.log10(0.05))
        # ax.set_xlim([np.log10(0.8),np.log10(10000)])

        # subplot shows the radial density profile along the midplane
        ax_mid = plt.axes([0.2, 0.2, 0.2, 0.2], frameon=True)
        ax_mid.plot(np.log10(rc / AU),
                    np.log10(rho2d[:, 199] / g2d / mmw / mh),
                    'o',
                    color='b',
                    linewidth=1,
                    markersize=2)
        ax_mid.plot(np.log10(rc / AU),
                    np.log10(rho_env_tsc2d[:, 199] / mmw / mh),
                    '-',
                    color='r',
                    linewidth=1,
                    markersize=2)
        # ax_mid.set_ylim([0,10])
        # ax_mid.set_xlim([np.log10(0.8),np.log10(10000)])
        ax_mid.set_ylim([0, 15])
        fig.savefig(outdir + outname + '_gas_radial.pdf',
                    format='pdf',
                    dpi=300,
                    bbox_inches='tight')
        fig.clf()

    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)
    # temperary for comparing full TSC and infall-only TSC model
    # import sys
    # sys.path.append(os.path.expanduser('~')+'/programs/misc/')
    # from tsc_comparison import tsc_com
    # rho_tsc, rho_ulrich = tsc_com()
    m.add_density_grid(rho_dust.T, d)
    # m.add_density_grid(rho.T, outdir+'oh5.hdf5')    # numpy read the array in reverse order

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4 * PI * rstar**2) * sigma * (tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print 'L_center =  % 5.2f L_sun' % ((4 * PI * rstar**2) * sigma *
                                        (tstar**4) / LS)

    # # add an infrared source at the center
    # L_IR = 0.04
    # ir_source = m.add_spherical_source()
    # ir_source.luminosity = L_IR*LS
    # ir_source.radius = rstar      # [cm]
    # ir_source.temperature = 500 # [K]  peak at 10 um
    # ir_source.position = (0., 0., 0.)
    # print 'Additional IR source, L_IR = %5.2f L_sun' % L_IR

    # Setting up the wavelength for monochromatic radiative transfer
    lambda0 = 0.1
    lambda1 = 2.0
    lambda2 = 50.0
    lambda3 = 95.0
    lambda4 = 200.0
    lambda5 = 314.0
    lambda6 = 1000.0
    n01 = 10.0
    n12 = 20.0
    n23 = 50.0

    lam01 = lambda0 * (lambda1 / lambda0)**(np.arange(n01) / n01)
    lam12 = lambda1 * (lambda2 / lambda1)**(np.arange(n12) / n12)
    lam23 = lambda2 * (lambda6 / lambda2)**(np.arange(n23 + 1) / n23)

    lam = np.concatenate([lam01, lam12, lam23])
    nlam = len(lam)

    # Create camera wavelength points
    n12 = 70.0
    n23 = 70.0
    n34 = 70.0
    n45 = 50.0
    n56 = 50.0

    lam12 = lambda1 * (lambda2 / lambda1)**(np.arange(n12) / n12)
    lam23 = lambda2 * (lambda3 / lambda2)**(np.arange(n23) / n23)
    lam34 = lambda3 * (lambda4 / lambda3)**(np.arange(n34) / n34)
    lam45 = lambda4 * (lambda5 / lambda4)**(np.arange(n45) / n45)
    lam56 = lambda5 * (lambda6 / lambda5)**(np.arange(n56 + 1) / n56)

    lam_cam = np.concatenate([lam12, lam23, lam34, lam45, lam56])
    n_lam_cam = len(lam_cam)

    # Radiative transfer setting

    # number of photons for temp and image
    lam_list = lam.tolist()
    # print lam_list
    m.set_raytracing(True)
    # option of using more photons for imaging
    if better_im == False:
        im_photon = 1e6
    else:
        im_photon = 5e7

    if mono == True:
        # Monechromatic radiative transfer setting
        m.set_monochromatic(True, wavelengths=lam_list)
        m.set_n_photons(initial=1000000,
                        imaging_sources=im_photon,
                        imaging_dust=im_photon,
                        raytracing_sources=1000000,
                        raytracing_dust=1000000)
    else:
        # regular wavelength grid setting
        m.set_n_photons(initial=1000000,
                        imaging=im_photon,
                        raytracing_sources=1000000,
                        raytracing_dust=1000000)
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(20)
    # m.set_convergence(True, percentile=95., absolute=1.5, relative=1.02)
    m.set_convergence(True,
                      percentile=dict_params['percentile'],
                      absolute=dict_params['absolute'],
                      relative=dict_params['relative'])
    m.set_mrw(True)  # Gamma = 1 by default
    # m.set_forced_first_scattering(forced_first_scattering=True)

    # Setting up images and SEDs
    # SED setting

    # Infinite aperture
    syn_inf = m.add_peeled_images(image=False)
    # use the index of wavelength array used by the monochromatic radiative transfer
    if mono == False:
        syn_inf.set_wavelength_range(1400, 2.0, 1400.0)
    syn_inf.set_viewing_angles([dict_params['view_angle']], [0.0])
    syn_inf.set_uncertainties(True)
    syn_inf.set_output_bytes(8)

    # aperture
    # 7.2 in 10 um scaled by lambda / 10
    # flatten beyond 20 um
    # default aperture
    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, 1300],\
                    '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, 101]}
    # 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)

    # name = np.arange(1,len(wl_aper)+1)
    # aper = np.empty_like(wl_aper)
    # for i in range(0, len(wl_aper)):
    #     if wl_aper[i] < 5:
    #         # aper[i] = 1.2 * 7
    #         aper[i] = 1.8 * 4
    #     elif (wl_aper[i] < 14) & (wl_aper[i] >=5):
    #         # aper[i] = 7.2 * wl_aper[i]/10.
    #         aper[i] = 1.8 * 4
    #     elif (wl_aper[i] >= 14) & (wl_aper[i] <40):
    #         # aper[i] = 7.2 * 2
    #         aper[i] = 5.1 * 4
    #     else:
    #         aper[i] = 24.5

    # dict_peel_sed = {}
    # for i in range(0, len(wl_aper)):
    #     aper_dum = aper[i]/2 * (1/3600.*np.pi/180.)*dstar*pc
    #     dict_peel_sed[str(name[i])] = m.add_peeled_images(image=False)
    #     # use the index of wavelength array used by the monochromatic radiative transfer
    #     if mono == False:
    #         # dict_peel_sed[str(name[i])].set_wavelength_range(1300, 2.0, 1300.0)
    #         dict_peel_sed[str(name[i])].set_wavelength_range(1000, 2.0, 1000.0)
    #     dict_peel_sed[str(name[i])].set_viewing_angles([dict_params['view_angle']], [0.0])
    #     # aperture should be given in cm
    #     dict_peel_sed[str(name[i])].set_aperture_range(1, aper_dum, aper_dum)
    #     dict_peel_sed[str(name[i])].set_uncertainties(True)
    #     dict_peel_sed[str(name[i])].set_output_bytes(8)

    dict_peel_sed = {}
    for i in range(0, len(aper_reduced)):
        aper_dum = aper_reduced[i] / 2 * (1 / 3600. * np.pi /
                                          180.) * dstar * pc
        dict_peel_sed[str(index_reduced[i])] = m.add_peeled_images(image=False)
        # use the index of wavelength array used by the monochromatic radiative transfer
        if mono == False:
            dict_peel_sed[str(index_reduced[i])].set_wavelength_range(
                1400, 2.0, 1400.0)
        dict_peel_sed[str(index_reduced[i])].set_viewing_angles(
            [dict_params['view_angle']], [0.0])
        # aperture should be given in cm and its the radius of the aperture
        dict_peel_sed[str(index_reduced[i])].set_aperture_range(
            1, aper_dum, aper_dum)
        dict_peel_sed[str(index_reduced[i])].set_uncertainties(True)
        dict_peel_sed[str(index_reduced[i])].set_output_bytes(8)

    # image setting
    syn_im = m.add_peeled_images(sed=False)
    # use the index of wavelength array used by the monochromatic radiative transfer
    if mono == False:
        syn_im.set_wavelength_range(1400, 2.0, 1400.0)
    # pixel number
    syn_im.set_image_size(300, 300)
    syn_im.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    syn_im.set_viewing_angles([dict_params['view_angle']], [0.0])
    syn_im.set_uncertainties(True)
    # output as 64-bit
    syn_im.set_output_bytes(8)

    # Output setting
    # Density
    m.conf.output.output_density = 'last'

    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'

    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'

    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'

    m.write(outdir + outname + '.rtin')

    if radmc == True:
        # RADMC-3D still use a pre-defined aperture with lazy for-loop
        aper = np.zeros([len(lam)])
        ind = 0
        for wl in lam:
            if wl < 5:
                aper[ind] = 8.4
            elif wl >= 5 and wl < 14:
                aper[ind] = 1.8 * 4
            elif wl >= 14 and wl < 40:
                aper[ind] = 5.1 * 4
            else:
                aper[ind] = 24.5
            ind += 1

        # Write the wavelength_micron.inp file
        #
        f_wave = open(outdir + 'wavelength_micron.inp', 'w')
        f_wave.write('%d \n' % int(nlam))
        for ilam in range(0, nlam):
            f_wave.write('%f \n' % lam[ilam])
        f_wave.close()

        # Write the camera_wavelength_micron.inp file
        #
        f_wave_cam = open(outdir + 'camera_wavelength_micron.inp', 'w')
        f_wave_cam.write('%d \n' % int(nlam))
        for ilam in range(0, nlam):
            f_wave_cam.write('%f \n' % lam[ilam])
        f_wave_cam.close()

        # Write the aperture_info.inp
        #
        f_aper = open(outdir + 'aperture_info.inp', 'w')
        f_aper.write('1 \n')
        f_aper.write('%d \n' % int(nlam))
        for iaper in range(0, len(aper)):
            f_aper.write('%f \t %f \n' % (lam[iaper], aper[iaper] / 2))
        f_aper.close()

        # Write the stars.inp file
        #
        f_star = open(outdir + 'stars.inp', 'w')
        f_star.write('2\n')
        f_star.write('1 \t %d \n' % int(nlam))
        f_star.write('\n')
        f_star.write('%e \t %e \t %e \t %e \t %e \n' %
                     (rstar * 0.9999, mstar, 0, 0, 0))
        f_star.write('\n')
        for ilam in range(0, nlam):
            f_star.write('%f \n' % lam[ilam])
        f_star.write('\n')
        f_star.write('%f \n' % -tstar)
        f_star.close()

        # Write the grid file
        #
        f_grid = open(outdir + 'amr_grid.inp', 'w')
        f_grid.write('1\n')  # iformat
        f_grid.write('0\n')  # AMR grid style  (0=regular grid, no AMR)
        f_grid.write(
            '150\n'
        )  # Coordinate system  coordsystem<100: Cartisian; 100<=coordsystem<200: Spherical; 200<=coordsystem<300: Cylindrical
        f_grid.write('0\n')  # gridinfo
        f_grid.write('1 \t 1 \t 1 \n')  # Include x,y,z coordinate
        f_grid.write('%d \t %d \t %d \n' %
                     (int(nx) - 1, int(ny), int(nz)))  # Size of the grid
        [f_grid.write('%e \n' % ri[ir]) for ir in range(1, len(ri))]
        [
            f_grid.write('%f \n' % thetai[itheta])
            for itheta in range(0, len(thetai))
        ]
        [f_grid.write('%f \n' % phii[iphi]) for iphi in range(0, len(phii))]
        f_grid.close()

        # Write the density file
        #
        f_dust = open(outdir + 'dust_density.inp', 'w')
        f_dust.write('1 \n')  # format number
        f_dust.write('%d \n' % int((nx - 1) * ny * nz))  # Nr of cells
        f_dust.write('1 \n')  # Nr of dust species
        for iphi in range(0, len(phic)):
            for itheta in range(0, len(thetac)):
                for ir in range(1, len(rc)):
                    f_dust.write('%e \n' % rho_dust[ir, itheta, iphi])
        f_dust.close()

        # Write the dust opacity table
        f_dustkappa = open(outdir + 'dustkappa_oh5_extended.inp', 'w')
        f_dustkappa.write('3 \n')  # format index for including g-factor
        f_dustkappa.write(
            '%d \n' %
            len(dust['nu']))  # number of wavlength/frequency in the table
        for i in range(len(dust['nu'])):
            f_dustkappa.write('%f \t %f \t %f \t %f \n' %
                              (c / dust['nu'][i] * 1e4, dust['chi'][i],
                               dust['chi'][i] * dust['albedo'][i] /
                               (1 - dust['albedo'][i]), dust['g'][i]))
        f_dustkappa.close()

        # Write the Dust opacity control file
        #
        f_opac = open(outdir + 'dustopac.inp', 'w')
        f_opac.write('2               Format number of this file\n')
        f_opac.write('1               Nr of dust species\n')
        f_opac.write(
            '============================================================================\n'
        )
        f_opac.write(
            '1               Way in which this dust species is read\n')
        f_opac.write('0               0=Thermal grain\n')
        # f_opac.write('klaus           Extension of name of dustkappa_***.inp file\n')
        f_opac.write(
            'oh5_extended    Extension of name of dustkappa_***.inp file\n')
        f_opac.write(
            '----------------------------------------------------------------------------\n'
        )
        f_opac.close()

        # In[112]:

        # Write the radmc3d.inp control file
        #
        f_control = open(outdir + 'radmc3d.inp', 'w')
        f_control.write('nphot = %d \n' % 100000)
        f_control.write('scattering_mode_max = 2\n')
        f_control.write('camera_min_drr = 0.1\n')
        f_control.write('camera_min_dangle = 0.1\n')
        f_control.write('camera_spher_cavity_relres = 0.1\n')
        f_control.write('istar_sphere = 1\n')
        f_control.write('modified_random_walk = 1\n')
        f_control.close()

    return m


# from input_reader import input_reader_table
# from pprint import pprint
# filename = '/Users/yaolun/programs/misc/hyperion/test_input.txt'
# params = input_reader_table(filename)
# pprint(params[0])
# indir = '/Users/yaolun/test/'
# outdir = '/Users/yaolun/test/'
# dust_file = '/Users/yaolun/programs/misc/oh5_hyperion.txt'
# # dust_file = '/Users/yaolun/Copy/dust_model/Ormel2011/hyperion/(ic-sil,gra)3opc.txt'
# # fix_params = {'R_min': 0.14}
# fix_params = {}
# setup_model(indir,outdir,'model_test',params[0],dust_file,plot=True,record=False,\
#     idl=False,radmc=False,fix_params=fix_params,ellipsoid=False)
Example #20
0
import numpy as np
from hyperion.model import Model
from hyperion.dust import SphericalDust
from hyperion.util.constants import pc

for tau_v in [0.1, 1.0, 20.0]:

    m = Model()

    # Global geometry:
    #
    # * slab
    # * system size = 10x10x10 pc
    # * system coordinates (x,y,z min/max) = -5 to +5 pc
    # * slab z extent = -2 to -5 pc
    # * slab xy extend = -5 pc to 5 pc
    # * z optical depth @0.55um in slab = 0.1, 1, 20
    # * optical depth outside slab = 0

    x = np.linspace(-5 * pc, 5 * pc, 100)
    y = np.linspace(-5 * pc, 5 * pc, 100)
    z = np.hstack([np.linspace(-5 * pc, -2 * pc, 100), 5 * pc])

    m.set_cartesian_grid(x, y, z)

    # Grain Properties:

    d = SphericalDust('integrated_hg_scattering.hdf5')
    chi_v = d.optical_properties.interp_chi_wav(0.55)

    # Determine density in slab
Example #21
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()
import numpy as np
from hyperion.model import Model
from hyperion.dust import SphericalDust
from hyperion.util.constants import au

m = Model.read('bm2_eff_vor_temperature.rtout', only_initial=False)

m.set_n_initial_iterations(0)

del m.n_photons['initial']
del m.n_photons['last']

i = m.add_peeled_images()
i.set_viewing_angles([0., 90., 90., 90., 90., 180.], [0., 0., 90., 180., 270., 0.])
i.set_image_limits(-60 * au, 60 * au, -60 * au, 60 * au)
i.set_image_size(300, 300)

# Set up monochromatic mode
m.set_monochromatic(True, wavelengths=[0.10019, 0.55165, 2.00293, 10.03850, 101.15800])

# Use raytracing
m.set_raytracing(True)

# Set up number of photons
m.set_n_photons(imaging_sources=1e7, imaging_dust=1e7,
                raytracing_sources=1, raytracing_dust=1e7)

# Write out and run
m.write('bm2_eff_images.rtin', overwrite=True)
m.run('bm2_eff_images.rtout', mpi=True)
Example #23
0
def arepo_m_gen(fname, field_add):

    reg, ds, dustdens = arepo_vornoi_grid_generate(fname, field_add)

    xcent = ds.quan(cfg.model.x_cent, 'code_length').to('cm')  #proper cm
    ycent = ds.quan(cfg.model.y_cent, 'code_length').to('cm')
    zcent = ds.quan(cfg.model.z_cent, 'code_length').to('cm')

    boost = np.array([xcent, ycent, zcent])
    print('[arepo_tributary/vornoi_m_gen]:  boost = ', boost)

    #========================================================================
    #Initialize Hyperion Model
    #========================================================================

    m = Model()

    #because we boost the stars to a [0,0,0] coordinate center, we
    #want to make sure our vornoi tesslation is created in the same manner.

    particle_x = reg["gascoordinates"][:, 0].to('cm')
    particle_y = reg["gascoordinates"][:, 1].to('cm')
    particle_z = reg["gascoordinates"][:, 2].to('cm')

    #just for the sake of symmetry, pass on a dx,dy,dz since it can be
    #used optionally downstream in other functions.
    dx = 2. * ds.quan(cfg.par.zoom_box_len, 'kpc').to('cm')
    dy = 2. * ds.quan(cfg.par.zoom_box_len, 'kpc').to('cm')
    dz = 2. * ds.quan(cfg.par.zoom_box_len, 'kpc').to('cm')

    print('[arepo_tributary] boost = ', boost)
    print('[arepo_tributary] xmin (pc)= ', (xcent - dx / 2.).to('pc'))
    print('[arepo_tributary] xmax (pc)= ', (xcent + dx / 2.).to('pc'))
    print('[arepo_tributary] ymin (pc)= ', (ycent - dy / 2.).to('pc'))
    print('[arepo_tributary] ymax (pc)= ', (ycent + dy / 2.).to('pc'))
    print('[arepo_tributary] zmin (pc)= ', (zcent - dz / 2.).to('pc'))
    print('[arepo_tributary] zmax (pc)= ', (zcent + dz / 2.).to('pc'))

    x_pos_boost = (particle_x - xcent).to('cm')
    y_pos_boost = (particle_y - ycent).to('cm')
    z_pos_boost = (particle_z - zcent).to('cm')

    m.set_voronoi_grid(x_pos_boost.value, y_pos_boost.value, z_pos_boost.value)

    #get CMB:

    energy_density_absorbed = energy_density_absorbed_by_CMB()
    specific_energy = np.repeat(energy_density_absorbed.value, dustdens.shape)

    if cfg.par.PAH == True:

        # load PAH fractions for usg, vsg, and big (grain sizes)
        frac = cfg.par.PAH_frac

        # Normalize to 1
        total = np.sum(list(frac.values()))
        frac = {k: v / total for k, v in frac.items()}

        for size in frac.keys():
            d = SphericalDust(cfg.par.dustdir + '%s.hdf5' % size)
            if cfg.par.SUBLIMATION == True:
                d.set_sublimation_temperature(
                    'fast', temperature=cfg.par.SUBLIMATION_TEMPERATURE)
            #m.add_density_grid(dustdens * frac[size], cfg.par.dustdir+'%s.hdf5' % size)
            m.add_density_grid(dustdens * frac[size],
                               d,
                               specific_energy=specific_energy)
        m.set_enforce_energy_range(cfg.par.enforce_energy_range)
    else:
        d = SphericalDust(cfg.par.dustdir + cfg.par.dustfile)
        if cfg.par.SUBLIMATION == True:
            d.set_sublimation_temperature(
                'fast', temperature=cfg.par.SUBLIMATION_TEMPERATURE)
        m.add_density_grid(dustdens, d, specific_energy=specific_energy)
        #m.add_density_grid(dustdens,cfg.par.dustdir+cfg.par.dustfile)
    m.set_specific_energy_type('additional')

    return m, xcent, ycent, zcent, dx.value, dy.value, dz.value, reg, ds, boost
import os

import numpy as np

from hyperion.model import Model
from hyperion.dust import SphericalDust
from hyperion.util.constants import pc, au, sigma, pi, rsun

NPHOTONS = 1e7

if not os.path.exists('models'):
    os.mkdir('models')

# TODO: remove dust around source

m = Model()

x = np.linspace(0., 60. * au, 256)
y = np.linspace(0., 60. * au, 256)
z = np.linspace(0., 60. * au, 256)

x = np.hstack([-10 * au, x])
y = np.hstack([-10 * au, y])
z = np.hstack([-10 * au, z])

m.set_cartesian_grid(x, y, z)

# Grain Properties:

d = SphericalDust('integrated_hg_scattering.hdf5')
chi_v = d.optical_properties.interp_chi_wav(0.55)
# A simple model with no dust, just sources, to check that the Doppler Shifting
# is working correctly. This model consists of a disk of sources that is
# rotating in a solid body fashion around the origin. All sources have a
# spectrum that consists of a single spectral line that is narrow enough that we
# can easily see the rotation in a multi-wavelength image. We check that the
# image looks sensible in both binned and peeled images.

# Same as setup_indiv but using a point source collection

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import c

m = Model()

m.set_cartesian_grid([-1., 1], [-1., 1.], [-1., 1])

N = 100000
w = np.random.random(N)**0.5
p = np.random.uniform(0, 2 * np.pi, N)
z = np.random.uniform(-0.1, 0.1, N)

x = w * np.cos(p)
y = w * np.sin(p)

# solid body with 1000 km/s on the outside
v = w * 1e8  # cm / s

vx = - v * np.sin(p)
vy = + v * np.cos(p)
vz = np.repeat(0, N)
Example #26
0
import random
random.seed('hyperion')  # ensure that random numbers are the same every time

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import pc, lsun

# Define cell walls
x = np.linspace(-10., 10., 101) * pc
y = np.linspace(-10., 10., 101) * pc
z = np.linspace(-10., 10., 101) * pc

# Initialize model and set up density grid
m = Model()
m.set_cartesian_grid(x, y, z)
m.add_density_grid(np.ones((100, 100, 100)) * 1.e-20, 'kmh_lite.hdf5')

# Generate random sources
for i in range(100):
    s = m.add_point_source()
    xs = random.uniform(-10., 10.) * pc
    ys = random.uniform(-10., 10.) * pc
    zs = random.uniform(-10., 10.) * pc
    s.position = (xs, ys, zs)
    s.luminosity = 10. ** random.uniform(0., 3.) * lsun
    s.temperature = random.uniform(3000., 8000.)

# Specify that the specific energy and density are needed
m.conf.output.output_specific_energy = 'last'
m.conf.output.output_density = 'last'
import numpy as np

from hyperion.model import Model
from hyperion.util.constants import pc, lsun

# Initialize model
m = Model()

# Set up 64x64x64 cartesian grid
w = np.linspace(-pc, pc, 64)
m.set_cartesian_grid(w, w, w)

# Add density grid with constant density and add a higher density cube inside to
# cause a shadow.
density = np.ones(m.grid.shape) * 1e-21
density[26:38, 26:38, 26:38] = 1.e-18
m.add_density_grid(density, 'kmh_lite.hdf5')

# Add a point source in the center
s = m.add_point_source()
s.position = (0.4 * pc, 0., 0.)
s.luminosity = 1000 * lsun
s.temperature = 6000.

# Add multi-wavelength image for a single viewing angle
image = m.add_peeled_images(sed=False, image=True)
image.set_wavelength_range(1, 190., 210.)
image.set_viewing_angles(np.repeat(45., 36), np.linspace(5., 355., 36))
image.set_image_size(400, 400)
image.set_image_limits(-1.5 * pc, 1.5 * pc, -1.5 * pc, 1.5 * pc)
# A simple model to check what happens when a source is moving towards dust and
# we observe both the source and the dust. If we observe the source such that
# the dust is directly behind, and the source is moving towards the dust, we
# should see red-shifted emission from the source and blue-shifted scattered
# light emission.

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import c

m = Model()

m.set_cartesian_grid([-1.0, 0, 1], [-1.0, 1.0], [-1.0, 1])

density = np.zeros(m.grid.shape)
density[:, :, 0] = 1.0

m.add_density_grid(density, "kmh_lite.hdf5")

# narrow emission line spectrum at 1 micron
wav = np.array([0.9999, 1.0001])
fnu = np.array([1.0, 1.0])
nu = c / (wav * 1.0e-4)

s = m.add_spherical_source()
s.position = 0.5, 0.0, 0.0
s.velocity = -1e8, 0.0, 0.0
s.spectrum = nu[::-1], fnu[::-1]
s.luminosity = 1
s.radius = 0.1
Example #29
0
def arepo_m_gen(fname, field_add):

    reg, ds, dustdens = arepo_vornoi_grid_generate(fname, field_add)

    xcent = ds.quan(cfg.model.x_cent, 'code_length').to('cm')  #proper cm
    ycent = ds.quan(cfg.model.y_cent, 'code_length').to('cm')
    zcent = ds.quan(cfg.model.z_cent, 'code_length').to('cm')

    boost = np.array([xcent, ycent, zcent])
    print('[arepo_tributary/vornoi_m_gen]:  boost = ', boost)

    #========================================================================
    #Initialize Hyperion Model
    #========================================================================

    m = Model()

    #because we boost the stars to a [0,0,0] coordinate center, we
    #want to make sure our vornoi tesslation is created in the same manner.

    particle_x = reg["gas", "coordinates"][:, 0].to('cm')
    particle_y = reg["gas", "coordinates"][:, 1].to('cm')
    particle_z = reg["gas", "coordinates"][:, 2].to('cm')

    #just for the sake of symmetry, pass on a dx,dy,dz since it can be
    #used optionally downstream in other functions.
    dx = 2. * ds.quan(cfg.par.zoom_box_len, 'kpc').to('cm')
    dy = 2. * ds.quan(cfg.par.zoom_box_len, 'kpc').to('cm')
    dz = 2. * ds.quan(cfg.par.zoom_box_len, 'kpc').to('cm')

    print('[arepo_tributary] boost = ', boost)
    print('[arepo_tributary] xmin (pc)= ', (xcent - dx / 2.).to('pc'))
    print('[arepo_tributary] xmax (pc)= ', (xcent + dx / 2.).to('pc'))
    print('[arepo_tributary] ymin (pc)= ', (ycent - dy / 2.).to('pc'))
    print('[arepo_tributary] ymax (pc)= ', (ycent + dy / 2.).to('pc'))
    print('[arepo_tributary] zmin (pc)= ', (zcent - dz / 2.).to('pc'))
    print('[arepo_tributary] zmax (pc)= ', (zcent + dz / 2.).to('pc'))

    x_pos_boost = (particle_x - xcent).to('cm')
    y_pos_boost = (particle_y - ycent).to('cm')
    z_pos_boost = (particle_z - zcent).to('cm')

    m.set_voronoi_grid(x_pos_boost.value, y_pos_boost.value, z_pos_boost.value)

    #get CMB:

    energy_density_absorbed = energy_density_absorbed_by_CMB()
    specific_energy = np.repeat(energy_density_absorbed.value, dustdens.shape)

    if cfg.par.otf_extinction == False:

        if cfg.par.PAH == True:

            # load PAH fractions for usg, vsg, and big (grain sizes)
            frac = cfg.par.PAH_frac

            # Normalize to 1
            total = np.sum(list(frac.values()))
            frac = {k: v / total for k, v in frac.items()}

            for size in frac.keys():
                d = SphericalDust(cfg.par.dustdir + '%s.hdf5' % size)
                if cfg.par.SUBLIMATION == True:
                    d.set_sublimation_temperature(
                        'fast', temperature=cfg.par.SUBLIMATION_TEMPERATURE)
                    #m.add_density_grid(dustdens * frac[size], cfg.par.dustdir+'%s.hdf5' % size)
                m.add_density_grid(dustdens * frac[size],
                                   d,
                                   specific_energy=specific_energy)
            m.set_enforce_energy_range(cfg.par.enforce_energy_range)
        else:
            d = SphericalDust(cfg.par.dustdir + cfg.par.dustfile)
            if cfg.par.SUBLIMATION == True:
                d.set_sublimation_temperature(
                    'fast', temperature=cfg.par.SUBLIMATION_TEMPERATURE)
            m.add_density_grid(dustdens, d, specific_energy=specific_energy)
        #m.add_density_grid(dustdens,cfg.par.dustdir+cfg.par.dustfile)

    else:  #instead of using a constant extinction law across the
        #entire galaxy, we'll compute it on a cell-by-cell bassis by
        #using information about the grain size distribution from
        #the simulation itself.

        ad = ds.all_data()
        nsizes = reg['PartType0', 'NumGrains'].shape[1]
        try:
            assert (np.sum(ad['PartType0', 'NumGrains']) > 0)
        except AssertionError:
            raise AssertionError(
                "[arepo_tributary:] There are no dust grains in this simulation.  This can sometimes happen in an early snapshot of a simulation where the dust has not yet had time to form."
            )
        grid_of_sizes = reg['PartType0', 'NumGrains']
        active_dust_add(ds, m, grid_of_sizes, nsizes, dustdens,
                        specific_energy)

    m.set_specific_energy_type('additional')

    return m, xcent, ycent, zcent, dx.value, dy.value, dz.value, reg, ds, boost
Example #30
0
import numpy as np
from hyperion.model import Model
from hyperion.dust import SphericalDust
from hyperion.util.constants import pc

for tau_v in [0.1, 1.0, 20.0]:

    m = Model.read('bm1_slab_eff_tau{0:05.2f}_temperature.rtout'.format(tau_v),
                   only_initial=False)

    m.set_n_initial_iterations(0)

    del m.n_photons['initial']
    del m.n_photons['last']

    i = m.add_peeled_images(sed=True, image=False)
    i.set_viewing_angles([0., 30., 60., 90., 120., 150., 180.],
                         [0., 0., 0., 0., 0., 0., 0.])

    # Set up monochromatic mode
    wavelengths = np.loadtxt('wave_grid_bm1_res5.dat')
    m.set_monochromatic(True, wavelengths=wavelengths)

    # Use raytracing
    m.set_raytracing(True)

    # Set up number of photons
    m.set_n_photons(imaging_sources=1e6,
                    imaging_dust=1e6,
                    raytracing_sources=1,
                    raytracing_dust=1e6)
Example #31
0
def setup_model(outdir, record_dir, outname, params, dust_file, wav_range, aperture,
                tsc=True, idl=False, plot=False, low_res=True, max_rCell=100,
                scale=1, radmc=False, mono_wave=None, norecord=False,
                dstar=200., dyn_cav=False, fix_params=None,
                power=2, mc_photons=1e6, im_photons=1e6, ellipsoid=False,
                TSC_dir='~/programs/misc/TSC/',
                IDL_path='/Applications/exelis/idl83/bin/idl', auto_disk=0.25,
                fast_plot=False, image_only=False, ulrich=False):
    """
    params = dictionary of the model parameters
    'alma' keyword is obsoleted
    outdir: The directory for storing Hyperion input files
    record_dir: The directory contains "model_list.txt" for recording parameters
    TSC_dir: Path the TSC-related IDL routines
    IDL_path: The IDL executable
    fast_plot: Do not plot the polar plot of the density because the rendering
               takes quite a lot of time.
    mono: monochromatic radiative transfer mode (need to specify the wavelength
          or a list of wavelength with 'mono_wave')
    image_only: only run for images
    """
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    #
    import matplotlib.pyplot as plt
    import os
    from matplotlib.colors import LogNorm
    from scipy.integrate import nquad
    from hyperion.model import Model
    from record_hyperion import record_hyperion
    from pprint import pprint

    # 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  [cm3/g/s^2]
    yr        = 60*60*24*365           # Years in seconds
    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
    g2d       = 100.
    mmw       = 2.37                   # Kauffmann 2008

    m = Model()

    # min and max wavelength to compute (need to define them first for checking dust properties)
    wav_min, wav_max, wav_num = wav_range

    # Create dust properties
    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    dust = dict()
    [dust['nu'], dust['albedo'], dust['chi'], dust['g']] = np.genfromtxt(dust_file).T
    d = HenyeyGreensteinDust(dust['nu'], dust['albedo'], dust['chi'], dust['g'], dust['g']*0)
    # dust sublimation option
    # dust sublimation temperture specified here
    T_sub = 1600.0
    d.set_sublimation_temperature('slow', temperature=T_sub)
    d.set_lte_emissivities(n_temp=3000,
                           temp_min=0.1,
                           temp_max=2000.)
    # if the min and/or max wavelength fall out of range
    if c/wav_min/1e-4 > dust['nu'].max():
        d.optical_properties.extrapolate_nu(dust['nu'].min(), c/wav_min/1e-4)
        print('minimum wavelength is out of dust model.  The dust model is extrapolated.')
    if c/wav_max/1e-4 < dust['nu'].min():
        d.optical_properties.extrapolate_nu(c/wav_max/1e-4, dust['nu'].max())
        print('maximum wavelength is out of dust model.  The dust model is extrapolated.')

    # try to solve the freq. problem
    d.optical_properties.extrapolate_nu(3.28e15, 5e15)
    #
    d.write(outdir+os.path.basename(dust_file).split('.')[0]+'.hdf5')
    d.plot(outdir+os.path.basename(dust_file).split('.')[0]+'.png')
    plt.clf()

    # Grids and Density

    # Grid Parameters
    nx        = 300L
    if low_res == True:
        nx    = 100L
    ny        = 400L
    nz        = 50L
    [nx, ny, nz] = [int(scale*nx), int(scale*ny), int(scale*nz)]

    # TSC model input setting
    dict_params = params
    # TSC model parameter
    cs        = dict_params['Cs']*1e5
    t         = dict_params['age']  # year
    omega     = dict_params['Omega0']
    # calculate related parameters
    M_env_dot = 0.975*cs**3/G
    mstar     = M_env_dot * t * yr
    R_cen     = omega**2 * G**3 * mstar**3 /(16*cs**8)
    R_inf     = cs * t * yr
    # protostar parameter
    tstar     = dict_params['tstar']
    R_env_max = dict_params['R_env_max']*AU
    theta_cav = dict_params['theta_cav']
    rho_cav_center = dict_params['rho_cav_center']
    rho_cav_edge   = dict_params['rho_cav_edge']*AU
    rstar     = dict_params['rstar']*RS
    # Mostly fixed parameter
    M_disk    = dict_params['M_disk']*MS
    beta      = dict_params['beta']
    h100      = dict_params['h100']*AU
    rho_cav   = dict_params['rho_cav']
    # make M_disk varies with mstar, which is the mass of star+disk
    if auto_disk != None:
        if M_disk != 0:
            print('M_disk is reset to %4f of mstar (star+disk)' % auto_disk)
            M_disk = mstar * auto_disk
        else:
            print('M_disk = 0 is found.  M_disk is set to 0.')

    # ellipsoid cavity parameter
    if ellipsoid == True:
        print('Use ellipsoid cavity (experimental)')
        # the numbers are given in arcsec
        a_out = 130 * dstar * AU
        b_out = 50  * dstar * AU
        z_out = a_out
        a_in  = dict_params['a_in'] * dstar * AU
        b_in  = a_in/a_out*b_out
        z_in  = a_in
        rho_cav_out = dict_params['rho_cav_out'] * mh
        rho_cav_in  = dict_params['rho_cav_in']  * mh

    # Calculate the dust sublimation radius
    # dust sublimation temperature specified when setting up the dust properties
    # realistic dust
    # a     = 1   # in micron
    # d_sub = 2.9388e7*(a/0.1)**-0.2 * (4*np.pi*rstar**2*sigma*tstar**4/LS)**0.5 / T_sub**3 *AU
    # black body dust
    d_sub = (LS/16./np.pi/sigma/AU**2*(4*np.pi*rstar**2*sigma*tstar**4/LS)/T_sub**4)**0.5 *AU
    # use the dust sublimation radius as the inner radius of disk and envelope
    R_disk_min = d_sub
    R_env_min  = d_sub
    rin        = rstar
    rout       = R_env_max
    R_disk_max = R_cen

    # print the variables
    print('Dust sublimation radius %6f AU' % (d_sub/AU))
    print('M_star %4f Solar mass' % (mstar/MS))
    print('Infall radius %4f AU' % (R_inf / AU))

    # if there is any parameter found in fix_params, then fix them
    if fix_params != None:
        if 'R_min' in fix_params.keys():
            R_disk_min = fix_params['R_min']*AU
            R_env_min  = fix_params['R_min']*AU

    # Make the Coordinates
    #
    ri           = rin * (rout/rin)**(np.arange(nx+1).astype(dtype='float')/float(nx))
    ri           = np.hstack((0.0, ri))
    thetai       = PI*np.arange(ny+1).astype(dtype='float')/float(ny)
    phii         = PI*2.0*np.arange(nz+1).astype(dtype='float')/float(nz)

    # Keep the constant cell size in r-direction at large radii
    #
    if max_rCell != None:
        ri_cellsize = ri[1:-1]-ri[0:-2]
        ind = np.where(ri_cellsize/AU > max_rCell)[0][0]       # The largest cell size is 100 AU
        ri = np.hstack((ri[0:ind],
                        ri[ind]+np.arange(np.ceil((rout-ri[ind])/max_rCell/AU))*max_rCell*AU))
        nxx = nx
        nx = len(ri)-1
    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc           = 0.5*( ri[0:nx]     + ri[1:nx+1] )
    thetac       = 0.5*( thetai[0:ny] + thetai[1:ny+1] )
    phic         = 0.5*( phii[0:nz]   + phii[1:nz+1] )

    # for non-TSC model
    if ulrich:
        import hyperion as hp
        from hyperion.model import AnalyticalYSOModel

        non_tsc = AnalyticalYSOModel()

        # Define the luminsoity source
        nt_source = non_tsc.add_spherical_source()
        nt_source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
        nt_source.radius = rstar  # [cm]
        nt_source.temperature = tstar  # [K]
        nt_source.position = (0., 0., 0.)
        nt_source.mass = mstar

        # Envelope structure
        #
        nt_envelope = non_tsc.add_ulrich_envelope()
        nt_envelope.mdot = M_env_dot    # Infall rate
        nt_envelope.rmin = rin          # Inner radius
        nt_envelope.rc   = R_cen        # Centrifugal radius
        nt_envelope.rmax = R_env_max    # Outer radius
        nt_envelope.star = nt_source

        nt_grid = hp.grid.SphericalPolarGrid(ri, thetai, phii)

        rho_env_ulrich = nt_envelope.density(nt_grid).T
        rho_env_ulrich2d = np.sum(rho_env_ulrich**2, axis=2)/np.sum(rho_env_ulrich, axis=2)

    # Make the dust density model
    #
    # total mass counter
    total_mass = 0

    # normalization constant for cavity shape
    if theta_cav != 0:
        # using R = 10000 AU as the reference point
        c0 = (10000.*AU)**(-0.5)*\
             np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
    else:
        c0 = 0

    # empty density grid to be filled later
    rho = np.zeros([len(rc), len(thetac), len(phic)])

    # Normalization for the total disk mass
    def f(w, z, beta, rstar, h100):
        f = 2*PI*w*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/(w**beta*h100/100**beta))**2)
        return f
    rho_0 = M_disk/(nquad(f,[[R_disk_min,R_disk_max],[-R_env_max,R_env_max]], args=(beta,rstar,h100)))[0]

    # TODO: review
    if dyn_cav == True:
        if not tsc:
            print('WARNING: Calculation of interdependent cavity property has not implemented in infall-only solution!')
        else:
            from outflow_inner_edge import outflow_inner_edge
            # typical no used.  Just an approach I tried to make the size of the
            # constant desnity region self-consistent with the outflow cavity.
            print 'Calculate the cavity properties using the criteria that swept-up mass = outflowed mass'
            # using swept-up mass = flow mass to derive the edge of the extended flat density region
            v_outflow = 1e2 * 1e5
            rho_cav_edge = outflow_inner_edge(np.copy(rho_env), (ri,thetai,phii),M_env_dot,v_outflow,theta_cav, R_env_min)
            dict_params['rho_cav_edge'] = rho_cav_edge
            # assume gas-to-dust ratio = 100
            rho_cav_center = 0.01 * 0.1*M_env_dot*rho_cav_edge/v_outflow/2 / (2*np.pi/3*rho_cav_edge**3*(1-np.cos(np.radians(theta_cav))))
            dict_params['rho_cav_center'] = rho_cav_center
            print 'inner edge is %5f AU and density is %e g/cm3' % (rho_cav_edge/AU, rho_cav_center)


    # default setting for the density profile in cavity
    if 'rho_cav_center' in locals() == False:
        rho_cav_center = 5e-19
        print('Use 5e-19 as the default value for cavity center')
    if 'rho_cav_edge' in locals() == False:
        rho_cav_edge = 40*AU
        print('Use 40 AU as the default value for size of the inner region')
    # discontinuity factor inside and outside of cavity inner edge
    discont = 1
    # determine the edge of constant region in the cavity
    if rho_cav_edge == 0:
        rho_cav_edge = R_env_min


    if not tsc:
        print('Calculating the dust density profile with infall solution...')

        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > R_env_min:
                        # related coordinates
                        w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])

                        # Disk profile or envelope/cavity
                        if ((w >= R_disk_min) and (w <= R_disk_max)):
                            h = ((w/(100*AU))**beta)*h100
                            rho_dum = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        else:
                            # determine whether the current cell is in the cavity
                            if ellipsoid == False:
                                z_cav = c0*abs(w)**1.5
                                if z_cav == 0:
                                    z_cav = R_env_max
                                cav_con = abs(z) > abs(z_cav)
                                if theta_cav == 90:
                                    cav_con = True
                            else:
                                # condition for the outer ellipsoid
                                cav_con = (2*(w/b_out)**2 + ((abs(z)-z_out)/a_out)**2) < 1

                            # cavity density
                            if cav_con:
                                # open cavity
                                if ellipsoid == False:
                                    if (rc[ir] <= rho_cav_edge) & (rc[ir] >= R_env_min):
                                        rho_dum = g2d * rho_cav_center
                                    else:
                                        rho_dum = g2d * rho_cav_center*discont*(rho_cav_edge/rc[ir])**power
                                else:
                                    # condition for the inner ellipsoid
                                    if (2*(w/b_in)**2 + ((abs(z)-z_in)/a_in)**2) > 1:
                                        rho_dum = rho_cav_out
                                    else:
                                        rho_dum = rho_cav_in
                            # envelope density
                            else:
                                mu = abs(np.cos(thetac[itheta]))
                                # Implement new root finding algorithm
                                roots = np.roots(np.array([1.0, 0.0, rc[ir]/R_cen-1.0, -mu*rc[ir]/R_cen]))
                                if len(roots[roots.imag == 0]) == 1:
                                    if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                                        mu_o_dum = roots[roots.imag == 0]
                                    else:
                                        mu_o_dum = -0.5
                                        print('Problem with cubic solving, cos(theta) = ', mu_o_dum)
                                        print('parameters are ', np.array([1.0, 0.0, rc[ir]/R_cen-1.0, -mu*rc[ir]/R_cen]))
                                else:
                                    mu_o_dum = -0.5
                                    for imu in range(0, len(roots)):
                                        if roots[imu]*mu >= 0.0:
                                            if (abs((abs(roots[imu]) - 1.0)) <= 1e-5):
                                                mu_o_dum = 1.0 * np.sign(mu)
                                            else:
                                                mu_o_dum = roots[imu]
                                    if mu_o_dum == -0.5:
                                        print('Problem with cubic solving, roots are: ', roots)
                                mu_o = mu_o_dum.real
                                rho_dum = M_env_dot/(4*PI*(G*mstar*R_cen**3)**0.5)*(rc[ir]/R_cen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*R_cen/rc[ir])**(-1)
                        rho[ir,itheta,iphi] = rho_dum
                    else:
                        rho[ir,itheta,iphi] = 1e-30
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1/3.)*(ri[ir+1]**3 - ri[ir]**3) * (phii[iphi+1]-phii[iphi]) * -(np.cos(thetai[itheta+1])-np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass

    # TSC model
    else:
        print('Calculating the dust density profile with TSC solution...')

        # If needed, calculate the TSC model via IDL
        #
        if idl == True:
            print('Using IDL to calculate the TSC model.  Make sure you are running this on mechine with IDL.')
            import pidly
            idl = pidly.IDL(IDL_path)
            idl('.r '+TSC_dir+'tsc.pro')
            idl('.r '+TSC_dir+'tsc_run.pro')
            #
            # only run TSC calculation within infall radius
            # modify the rc array
            ind_infall = np.where(rc >= R_inf)[0][0]
            if max(ri) > R_inf:
                rc_idl = rc[0:ind_infall+1]
            else:
                rc_idl = rc[rc < max(ri)]
            idl.pro('tsc_run', indir=TSC_dir, outdir=outdir, rc=rc_idl, thetac=thetac, time=t,
                    c_s=cs, omega=omega, renv_min=R_env_min)
            file_idl = 'rhoenv.dat'
        else:
            print('Read the pre-computed TSC model.')
            ind_infall = np.where(rc >= R_inf)[0][0]
            if max(ri) > R_inf:
                rc_idl = rc[0:ind_infall+1]
            else:
                rc_idl = rc[rc < max(ri)]
            if idl != False:
                file_idl = idl

        # read in the exist file
        rho_env_tsc_idl = np.genfromtxt(outdir+file_idl).T
        # because only region within infall radius is calculated by IDL program,
        # need to project it to the original grid
        rho_env_tsc = np.zeros([len(rc), len(thetac)])
        for irc in range(len(rc)):
            if rc[irc] in rc_idl:
                rho_env_tsc[irc,:] = rho_env_tsc_idl[np.squeeze(np.where(rc_idl == rc[irc])),:]

        # extrapolate for the NaN values at the outer radius, usually at radius beyond the infall radius
        # using r^-2 profile at radius greater than infall radius
        # and map the 2d strcuture onto 3-D grid
        # map TSC solution from IDL to actual 2-D grid
        rho_env_tsc2d = np.empty((nx,ny))
        if max(ri) > R_inf:
            for i in range(0, len(rc)):
                if i <= ind_infall:
                    rho_env_tsc2d[i,:] = rho_env_tsc[i,:]
                else:
                    rho_env_tsc2d[i,:] = 10**(np.log10(rho_env_tsc[ind_infall,:]) - 2*(np.log10(rc[i]/rc[ind_infall])))
        else:
            rho_env_tsc2d = rho_env_tsc

        # map it to 3-D grid
        rho_env = np.repeat(rho_env_tsc2d[:,:,np.newaxis], nz, axis=2)

        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > R_env_min:
                        # related coordinates
                        w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])

                        # initialize dummer rho for disk and cavity
                        rho_dum = 0
                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w/(100*AU))**beta)*h100
                            rho_dum = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        else:
                        # determine whether the current cell is in the cavity
                            if ellipsoid == False:
                                z_cav = c0*abs(w)**1.5
                                if z_cav == 0:
                                    z_cav = R_env_max
                                cav_con = abs(z) > abs(z_cav)
                            else:
                                # condition for the outer ellipsoid
                                cav_con = (2*(w/b_out)**2 + ((abs(z)-z_out)/a_out)**2) < 1

                            if cav_con:
                                # open cavity
                                if ellipsoid == False:
                                    if (rc[ir] <= rho_cav_edge) & (rc[ir] >= R_env_min):
                                        rho_dum = g2d * rho_cav_center
                                    else:
                                        rho_dum = g2d * rho_cav_center*discont*(rho_cav_edge/rc[ir])**power
                                else:
                                    # condition for the inner ellipsoid
                                    if (2*(w/b_in)**2 + ((abs(z)-z_in)/a_in)**2) > 1:
                                        rho_dum = rho_cav_out
                                    else:
                                        rho_dum = rho_cav_in

                        rho[ir, itheta, iphi] = rho_env[ir, itheta, iphi] + rho_dum

                    else:
                        rho[ir,itheta,iphi] = 1e-40

                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1/3.)*(ri[ir+1]**3 - ri[ir]**3) * (phii[iphi+1]-phii[iphi]) * -(np.cos(thetai[itheta+1])-np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass
    # apply gas-to-dust ratio of 100
    rho_dust = rho/g2d
    total_mass_dust = total_mass/MS/g2d
    print('Total dust mass = %f Solar mass' % total_mass_dust)

    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)
    m.add_density_grid(rho_dust.T, d)

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print('L_center =  % 5.2f L_sun' % ((4*PI*rstar**2)*sigma*(tstar**4)/LS))

    # radiative transfer settigs
    m.set_raytracing(True)

    # determine the number of photons for imaging
    # the case of monochromatic
    if mono_wave != None:
        if (type(mono_wave) == int) or (type(mono_wave) == float) or (type(mono_wave) == str):
            mono_wave = float(mono_wave)
            mono_wave = [mono_wave]

        # Monochromatic radiative transfer setting
        m.set_monochromatic(True, wavelengths=mono_wave)
        m.set_n_photons(initial=mc_photons, imaging_sources=im_photon,
                        imaging_dust=im_photon, raytracing_sources=im_photon,
                        raytracing_dust=im_photon)
    # regular SED
    else:
        m.set_n_photons(initial=mc_photons, imaging=im_photon * wav_num,
                        raytracing_sources=im_photon,
                        raytracing_dust=im_photon)
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(20)
    m.set_convergence(True, percentile=dict_params['percentile'],
                            absolute=dict_params['absolute'],
                            relative=dict_params['relative'])
    m.set_mrw(True)   # Gamma = 1 by default

    # Setting up images and SEDs
    if not image_only:
        # SED setting
        # Infinite aperture
        syn_inf = m.add_peeled_images(image=False)
        # use the index of wavelength array used by the monochromatic radiative transfer
        if mono_wave == None:
            syn_inf.set_wavelength_range(wav_num, wav_min, wav_max)
        syn_inf.set_viewing_angles([dict_params['view_angle']], [0.0])
        syn_inf.set_uncertainties(True)
        syn_inf.set_output_bytes(8)

        # aperture
        # 7.2 in 10 um scaled by lambda / 10
        # flatten beyond 20 um
        # default aperture (should always specify a set of apertures)

        # 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)

        dict_peel_sed = {}
        for i in range(0, len(aper_reduced)):
            aper_dum = aper_reduced[i]/2 * (1/3600.*np.pi/180.)*dstar*pc
            dict_peel_sed[str(index_reduced[i])] = m.add_peeled_images(image=False)
            # use the index of wavelength array used by the monochromatic radiative transfer
            if mono == False:
                dict_peel_sed[str(index_reduced[i])].set_wavelength_range(wav_num, wav_min, wav_max)
            dict_peel_sed[str(index_reduced[i])].set_viewing_angles([dict_params['view_angle']], [0.0])
            # aperture should be given in cm and its the radius of the aperture
            dict_peel_sed[str(index_reduced[i])].set_aperture_range(1, aper_dum, aper_dum)
            dict_peel_sed[str(index_reduced[i])].set_uncertainties(True)
            dict_peel_sed[str(index_reduced[i])].set_output_bytes(8)

    # image setting
    syn_im = m.add_peeled_images(sed=False)
    # use the index of wavelength array used by the monochromatic radiative transfer
    if mono_wave == None:
        syn_im.set_wavelength_range(wav_num, wav_min, wav_max)
        pix_num = 300
    else:
        pix_num = 8000
    #
    syn_im.set_image_size(pix_num, pix_num)
    syn_im.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    syn_im.set_viewing_angles([dict_params['view_angle']], [0.0])
    syn_im.set_uncertainties(True)
    syn_im.set_output_bytes(8)

    # Output setting
    # Density
    m.conf.output.output_density = 'last'
    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'
    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'
    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'
    m.write(outdir+outname+'.rtin')

    if plot:
        # rho2d is the 2-D projection of gas density
        # take the weighted average
        rho2d = np.sum(rho**2, axis=2)/np.sum(rho, axis=2)

        if fast_plot == False:
            # Plot the azimuthal averaged density
            fig = plt.figure(figsize=(8,6))
            ax_env  = fig.add_subplot(111, projection='polar')

            # zmin = 1e-22/mmw/mh
            zmin = 1e-1
            cmap = plt.cm.CMRmap
            rho2d_exp = np.hstack((rho2d, rho2d, rho2d[:,0:1]))
            thetac_exp = np.hstack((thetac-PI/2, thetac+PI/2, thetac[0]-PI/2))
            # plot the gas density
            img_env = ax_env.pcolormesh(thetac_exp, rc/AU, rho2d_exp/mmw/mh,
                                        cmap=cmap,
                                        norm=LogNorm(vmin=zmin,vmax=1e6))

            ax_env.set_xlabel(r'$\rm{Polar\,angle\,(Degree)}$',fontsize=20)
            ax_env.set_ylabel('', fontsize=20, labelpad=-140)
            ax_env.tick_params(labelsize=18)
            ax_env.set_yticks(np.hstack((np.arange(0,(int(R_env_max/AU/10000.)+1)*10000, 10000),R_env_max/AU)))
            ax_env.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}}$'])
            ax_env.set_yticklabels([])
            # fix the tick label font
            ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=20)
            for label in ax_env.get_yticklabels():
                label.set_fontproperties(ticks_font)

            ax_env.grid(True, color='LightGray', linewidth=1.5)
            cb = fig.colorbar(img_env, pad=0.1)
            cb.ax.set_ylabel(r'$\rm{Averaged\,Gas\,Density\,(cm^{-3})}$',fontsize=20)
            cb.set_ticks([1e-1,1e0,1e1,1e2,1e3,1e4,1e5,1e6])
            cb.set_ticklabels([r'$\rm{10^{-1}}$',r'$\rm{10^{0}}$',r'$\rm{10^{1}}$',r'$\rm{10^{2}}$',r'$\rm{10^{3}}$',
                               r'$\rm{10^{4}}$',r'$\rm{10^{5}}$',r'$\rm{\geq 10^{6}}$'])

            cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
            plt.setp(cb_obj, fontsize=20)
            fig.savefig(outdir+outname+'_gas_density.png', format='png', dpi=300, bbox_inches='tight')
            fig.clf()

        # Plot the radial density profile
        fig = plt.figure(figsize=(12,9))
        ax = fig.add_subplot(111)

        plot_grid = [0, 49, 99, 149, 199]
        color_grid = ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00']
        label = [r'$\rm{\theta='+str(int(np.degrees(thetai[plot_grid[0]])))+'^{\circ}}$',
                 r'$\rm{\theta='+str(int(np.degrees(thetai[plot_grid[1]])))+'^{\circ}}$',
                 r'$\rm{\theta='+str(1+int(np.degrees(thetai[plot_grid[2]])))+'^{\circ}}$',
                 r'$\rm{\theta='+str(int(np.degrees(thetai[plot_grid[3]])))+'^{\circ}}$',
                 r'$\rm{\theta='+str(1+int(np.degrees(thetai[plot_grid[4]])))+'^{\circ}}$']
        alpha = np.linspace(0.3, 1.0, len(plot_grid))
        for i in plot_grid:
            ax.plot(np.log10(rc[rc > 0.14*AU]/AU), np.log10(rho2d[rc > 0.14*AU,i]/g2d/mmw/mh)+plot_grid[::-1].index(i)*-0.2,'-',color=color_grid[plot_grid.index(i)],mec='None',linewidth=2.5, \
                    markersize=3, label=label[plot_grid.index(i)])
        ax.axvline(np.log10(R_inf/AU), linestyle='--', color='k', linewidth=1.5, label=r'$\rm{infall\,radius}$')
        ax.axvline(np.log10(R_cen/AU), linestyle=':', color='k', linewidth=1.5, label=r'$\rm{centrifugal\,radius}$')

        lg = plt.legend(fontsize=20, numpoints=1, ncol=2, framealpha=0.7, loc='upper right')

        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
        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)

        ax.set_ylim([0,11])
        fig.gca().set_xlim(left=np.log10(0.05))
        fig.savefig(outdir+outname+'_gas_radial.pdf',format='pdf',dpi=300,bbox_inches='tight')
        fig.clf()

    # Record the input and calculated parameters
    if not norecord == True:
        params = dict_params.copy()
        params.update({'d_sub': d_sub/AU,
                       'M_env_dot': M_env_dot/MS*yr,
                       'R_inf': R_inf/AU,
                       'R_cen': R_cen/AU,
                       'mstar': mstar/MS,
                       'M_tot_gas': total_mass/MS})
        record_hyperion(params,record_dir)


    return m
Example #32
0
def setup_model(indir,outdir,model=False,denser_wall=False,plot=False,low_res=False,flat=True,scale=1.0):
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    import matplotlib.pyplot as plt
    import matplotlib as mat
    import os
    from matplotlib.colors import LogNorm
    from scipy.optimize import fsolve
    from scipy.integrate import nquad
    from envelope_func import func
    from hyperion.model import Model

    # Constants setup
    c         = const.c.cgs.value
    AU        = 1.49598e13     # Astronomical Unit       [cm]
    pc        = 3.08572e18     # Parsec                  [cm]
    MS        = 1.98892e33     # Solar mass              [g]
    LS        = 3.8525e33      # Solar luminosity        [erg/s]
    RS        = 6.96e10        # Solar radius            [cm]
    G         = 6.67259e-8     # Gravitational constant  [cm3/g/s^2]
    yr        = 60*60*24*365   # Years in seconds
    PI        = np.pi          # PI constant
    sigma     = const.sigma_sb.cgs.value  # Stefan-Boltzmann constant 


    m = Model()

    # Create dust properties

    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust_radmc = dict()
    [dust_radmc['wl'], dust_radmc['abs'], dust_radmc['scat'], dust_radmc['g']] = np.genfromtxt('dustkappa_oh5_extended.inp',skip_header=2).T
    # opacity per mass of dust?
    dust_hy = dict()
    dust_hy['nu'] = c/dust_radmc['wl']*1e4
    ind = np.argsort(dust_hy['nu'])
    dust_hy['nu'] = dust_hy['nu'][ind]
    dust_hy['albedo'] = (dust_radmc['scat']/(dust_radmc['abs']+dust_radmc['scat']))[ind]
    dust_hy['chi'] = (dust_radmc['abs']+dust_radmc['scat'])[ind]
    dust_hy['g'] = dust_radmc['g'][ind]
    dust_hy['p_lin_max'] = 0*dust_radmc['wl'][ind]     # assume no polarization

    d = HenyeyGreensteinDust(dust_hy['nu'], dust_hy['albedo'], dust_hy['chi'], dust_hy['g'], dust_hy['p_lin_max'])
    # dust sublimation does not occur
    # d.set_sublimation_temperature(None)
    d.write(outdir+'oh5.hdf5')
    d.plot(outdir+'oh5.png')

    # Grids and Density
    # Calculation inherited from the script used for RADMC-3D

    # Parameters setup
    # Import the model parameters from another file 
    #
    params     = np.genfromtxt(indir+'/params.dat',dtype=None)
    tstar      = params[0][1]
    mstar      = params[1][1]*MS
    rstar      = params[2][1]*RS
    M_env_dot  = params[3][1]*MS/yr
    M_disk_dot = params[4][1]*MS/yr
    R_env_max  = params[5][1]*AU
    R_env_min  = params[6][1]*AU
    theta_cav  = params[7][1]
    R_disk_max = params[8][1]*AU
    R_disk_min = params[9][1]*AU
    R_cen      = R_disk_max
    M_disk     = params[10][1]*MS
    beta       = params[11][1]
    h100       = params[12][1]*AU
    rho_cav    = params[13][1]
    if denser_wall == True:
        wall       = params[14][1]*AU
        rho_wall   = params[15][1]
    rho_cav_center = params[16][1]
    rho_cav_edge   = params[17][1]*AU
    
    # Grid Parameters
    nx        = 300L
    if low_res == True:
        nx    = 100L
    ny        = 400L
    nz        = 50L
    [nx, ny, nz] = [scale*nx, scale*ny, scale*nz]
    # nx        = 20
    # ny        = 40
    # nz        = 5

    
    # Model Parameters
    #
    rin       = rstar
    rout      = R_env_max
    rcen      = R_cen

    # Star Parameters
    #
    mstar    = mstar
    rstar    = rstar*0.9999
    tstar    = tstar
    pstar    = [0.,0.,0.]

    # Make the Coordinates
    #
    ri           = rin * (rout/rin)**(np.arange(nx+1).astype(dtype='float')/float(nx))
    ri           = np.hstack((0.0, ri))
    thetai       = PI*np.arange(ny+1).astype(dtype='float')/float(ny)
    phii         = PI*2.0*np.arange(nz+1).astype(dtype='float')/float(nz)
    
    # Keep the constant cell size in r-direction
    #
    if flat == True:
        ri_cellsize = ri[1:-1]-ri[0:-2]
        ind = np.where(ri_cellsize/AU > 100.0)[0][0]       # The largest cell size is 100 AU
        ri = np.hstack((ri[0:ind],ri[ind]+np.arange(np.ceil((rout-ri[ind])/100/AU))*100*AU))
        nxx = nx
        nx = len(ri)-1    

    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc           = 0.5*( ri[0:nx]     + ri[1:nx+1] )
    thetac       = 0.5*( thetai[0:ny] + thetai[1:ny+1] )
    phic         = 0.5*( phii[0:nz]   + phii[1:nz+1] )
    # phic         = 0.5*( phii[0:nz-1]   + phii[1:nz] )

    # Make the dust density model
    # Make the density profile of the envelope
    #
    print 'Calculating the dust density profile...'
    if theta_cav != 0:
        c0 = R_env_max**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
    else:
        c0 = 0
    rho_env  = np.zeros([len(rc),len(thetac),len(phic)])
    rho_disk = np.zeros([len(rc),len(thetac),len(phic)])
    rho      = np.zeros([len(rc),len(thetac),len(phic)])
    def f(w,z,beta,rstar,h100):
        f = 2*PI*w*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/(w**beta*h100/100**beta))**2)
        return f
    rho_0 = M_disk/(nquad(f,[[R_disk_min,R_disk_max],[-R_env_max,R_env_max]], args=(beta,rstar,h100)))[0]
    i = 0
    j = 0
    if 'rho_cav_center' in locals() == False:
        rho_cav_center = 5.27e-18 # 1.6e-17  # 5.27e-18
        print 'Use 5.27e-18 as the default value for cavity center'
    if 'rho_cav_edge' in locals() == False:
        rho_cav_edge = 40*AU
        print 'Use 40 AU as the default value for size of the inner region'
    discont = 1
    if denser_wall == False:
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir]*np.cos(np.pi/2-thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2-thetac[itheta])
                        z_cav = c0*abs(w)**1.5
                        if z_cav == 0:
                            z_cav = R_env_max
                        if abs(z) > abs(z_cav):
                            # rho_env[ir,itheta,iphi] = rho_cav
                            # Modification for using density gradient in the cavity
                            if rc[ir] <= rho_cav_edge:
                                rho_env[ir,itheta,iphi] = rho_cav_center#*((rc[ir]/AU)**2)
                            else:
                                rho_env[ir,itheta,iphi] = rho_cav_center*discont*(rho_cav_edge/rc[ir])**2
                            i += 1
                        else:
                            j += 1
                            mu = abs(np.cos(thetac[itheta]))
                            mu_o = np.abs(fsolve(func,[0.5,0.5,0.5],args=(rc[ir],rcen,mu))[0])
                            rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*rcen**3)**0.5)*(rc[ir]/rcen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*rcen/rc[ir])**(-1)
                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w/(100*AU))**beta)*h100
                            rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # Combine envelope and disk
                        rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]

                        # # testing the effect of new solver
                        # # Envelope profile
                        # w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        # z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])
                        # z_cav = c0*abs(w)**1.5
                        # if z_cav == 0:
                        #     z_cav = R_env_max
                        # if abs(z) > abs(z_cav):
                        #     # rho_env[ir,itheta,iphi] = rho_cav
                        #     # Modification for using density gradient in the cavity
                        #     if rc[ir] <= rho_cav_edge:
                        #         rho_env[ir,itheta,iphi] = rho_cav_center#*((rc[ir]/AU)**2)
                        #     else:
                        #         rho_env[ir,itheta,iphi] = rho_cav_center*discont*(rho_cav_edge/rc[ir])**2
                        #     i += 1
                        # else:
                        #     j += 1
                        #     mu = abs(np.cos(thetac[itheta]))
                        #     # Implement new root finding algorithm
                        #     roots = np.roots(np.array([1.0, 0.0, rc[ir]/rcen-1.0, -mu*rc[ir]/rcen]))
                        #     if len(roots[roots.imag == 0]) == 1:
                        #         if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                        #             mu_o_dum = roots[roots.imag == 0]
                        #         else:
                        #             mu_o_dum = -0.5
                        #             print 'Problem with cubic solving, cos(theta) = ', mu_o_dum
                        #             print 'parameters are ', np.array([1.0, 0.0, rc[ir]/rcen-1.0, -mu*rc[ir]/rcen])
                        #     else:
                        #         mu_o_dum = -0.5
                        #         for imu in range(0, len(roots)):
                        #             if roots[imu]*mu >= 0.0:
                        #                 if (abs((abs(roots[imu]) - 1.0)) <= 1e-5):
                        #                     mu_o_dum = 1.0 * np.sign(mu)
                        #                 else:
                        #                     mu_o_dum = roots[imu]
                        #         if mu_o_dum == -0.5:
                        #             print 'Problem with cubic solving, roots are: ', roots
                        #     mu_o = mu_o_dum.real
                        #     rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*rcen**3)**0.5)*(rc[ir]/rcen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*rcen/rc[ir])**(-1)
                        # # Disk profile
                        # if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                        #     h = ((w/(100*AU))**beta)*h100
                        #     rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # # Combine envelope and disk
                        # rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-30
        rho_env  = rho_env  + 1e-40
        rho_disk = rho_disk + 1e-40
        rho      = rho      + 1e-40
    else:
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    # Envelope profile
                    w = abs(rc[ir]*np.cos(thetac[itheta]))
                    z = rc[ir]*np.sin(thetac[itheta])
                    z_cav = c*abs(w)**1.5
                    z_cav_wall = c*abs(w-wall)**1.5
                    if z_cav == 0:
                        z_cav = R_env_max
                    if abs(z) > abs(z_cav):
                        # rho_env[ir,itheta,iphi] = rho_cav
                        # Modification for using density gradient in the cavity
                        if rc[ir] <= 20*AU:
                            rho_env[ir,itheta,iphi] = rho_cav_center*((rc[ir]/AU)**2)
                        else:
                            rho_env[ir,itheta,iphi] = rho_cav_center*discont*(20*AU/rc[ir])**2
                        i += 1
                    elif (abs(z) > abs(z_cav_wall)) and (abs(z) < abs(z_cav)):
                        rho_env[ir,itheta,iphi] = rho_wall
                    else:
                        j += 1
                        mu = abs(np.cos(thetac[itheta]))
                        mu_o = np.abs(fsolve(func,[0.5,0.5,0.5],args=(rc[ir],rcen,mu))[0])
                        rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*rcen**3)**0.5)*(rc[ir]/rcen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*rcen/rc[ir])**(-1)
                    # Disk profile
                    if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                        h = ((w/(100*AU))**beta)*h100
                        rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                    # Combine envelope and disk
                    rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
        rho_env  = rho_env  + 1e-40
        rho_disk = rho_disk + 1e-40
        rho      = rho      + 1e-40

    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)
    m.add_density_grid(rho.T, outdir+'oh5.hdf5')    # numpy read the array in reverse order

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print 'L_center =  % 5.2f L_sun' % ((4*PI*rstar**2)*sigma*(tstar**4)/LS)

    # Setting up images and SEDs
    image = m.add_peeled_images()
    image.set_wavelength_range(300, 2.0, 670.0)
    # pixel number
    image.set_image_size(300, 300)
    image.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    image.set_viewing_angles([82.0], [0.0])
    image.set_uncertainties(True)
    # output as 64-bit
    image.set_output_bytes(8)

    # Radiative transfer setting

    # number of photons for temp and image
    m.set_raytracing(True)
    m.set_n_photons(initial=1000000, imaging=1000000, raytracing_sources=1000000, raytracing_dust=1000000)
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(5)
    m.set_convergence(True, percentile=99., absolute=1.5, relative=1.02)
    m.set_mrw(True)   # Gamma = 1 by default

    # Output setting
    # Density
    m.conf.output.output_density = 'last'

    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'

    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'

    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'

    m.write(outdir+'old_setup2.rtin')
Example #33
0
def setup_model(cli):

	#
	# Hyperion setup:
	#
	model = Model()


	if(cli.mode == "temperature"):
		#
		# Dust properties:
		#
		dust_properties = SphericalDust('dust_integrated_full_scattering.hdf5')


		#
		# Write dust properties:
		#
		dust_properties.write('dust_properties.hdf5')
		dust_properties.plot('dust_properties.png')

	
		#
		# Grid setup:
		#
		grid_wmin =  0
		grid_wmax =  5.0*pc # 4.0*pc
		grid_zmin =  0.0*pc
		grid_zmax = 10.0*pc
		grid_pmin =  0
		grid_pmax =  2*pi

		grid_dx = cli.resolution*pc
		grid_dw = grid_dx # uniform resolution
		grid_dz = grid_dx # uniform resolution
		grid_dp = grid_dx # resolution at filament location at r = 1 pc

		grid_Nw   = int((grid_wmax - grid_wmin) / grid_dw)
		grid_Nz   = int((grid_zmax - grid_zmin) / grid_dz)
		grid_Np   = int(2*pi * 1.0*pc / grid_dp)

		if(cli.verbose):
			print("Grid setup:")
			print(" Grid resolution =",cli.resolution, "pc.")
			print(" grid_Nw =",grid_Nw)
			print(" grid_Nz =",grid_Nz)
			print(" grid_Np =",grid_Np)

		#grid_w      = np.logspace(np.log10(grid_wmin), np.log10(grid_wmax), grid_Nw)
		#grid_w      = np.hstack([0., grid_w]) # add innermost cell interface at w=0
		grid_w    = np.linspace(grid_wmin, grid_wmax, grid_Nw+1)
		grid_z    = np.linspace(grid_zmin, grid_zmax, grid_Nz+1)
		grid_p    = np.linspace(grid_pmin, grid_pmax, grid_Np+1)

		model.set_cylindrical_polar_grid(grid_w, grid_z, grid_p)

		#
		# Dust density setup:
		#
		RC  = 0.1*pc
		nC  = 6.6580e+03       # in cm^-3
		nC *= cli.opticaldepth # the optical depth at 1 micron
		nC *= m_h              # in g cm^-3
		nC /= 100.0            # converts from gas to dust density
	
		rho = np.zeros(model.grid.shape)
	
		#
		# n(r) = nC / [ 1.0 + (r/RC)**2.0 ]
		# x = -sin(2.0×pi×t) pc, y = +cos(2.0×pi×t) pc, z = 10.0×t pc, t = [0.0, 1.0]
		#  => t = m.grid.gz / (10*pc)
		#  => phi(t) = mod(360*t+270, 360)
		#
		for k in range(0, grid_Np):
			for j in range(0, grid_Nz):
				for i in range(0, grid_Nw):
				
					t = model.grid.gz[k,j,i] / (10*pc)
				
					if(cli.filament == "linear"):
						filament_center_x  = 0
						filament_center_y  = 0
					elif(cli.filament == "spiraling"):
						filament_center_x  = - math.sin(2*pi*t)*pc
						filament_center_y  = + math.cos(2*pi*t)*pc
				
					spherical_grid_r   = model.grid.gw[k,j,i]
					spherical_grid_phi = model.grid.gp[k,j,i]
				
					cartesian_grid_x   = spherical_grid_r * math.cos(spherical_grid_phi)
					cartesian_grid_y   = spherical_grid_r * math.sin(spherical_grid_phi)
				
					rsquared = (
								(cartesian_grid_x - filament_center_x)**2
								+
								(cartesian_grid_y - filament_center_y)**2
								)
				
					rho[k,j,i] = nC / (1.0 + (rsquared / (RC*RC)))
				
					if rsquared**0.5 > 3*pc:
						rho[k,j,i] = 0

		rho[model.grid.gw > grid_wmax] = 0
		rho[model.grid.gz < grid_zmin] = 0
		rho[model.grid.gz > grid_zmax] = 0

		model.add_density_grid(rho, 'dust_properties.hdf5')


		#
		# Check optical depth through the filament:
		#
		#  (y,z = 0, 2.5 pc goes through the filament center in all setups)
		
		#
		# Determine index of closest grid cell to z = 2.5 pc:
		#
		dz_last = 2*abs(grid_zmax-grid_zmin)
		for j in range(0, grid_Nz):
			dz = abs(model.grid.gz[0,j,0] - 2.5*pc)
			if(dz > dz_last):
				j=j-1
				break
			else:
				dz_last = dz

		#
		# Opacity at 1.0 micron (per gram dust):
		#
		chi = dust_properties.optical_properties.interp_chi_wav(1.0)

		tau_max = 0
		for k in range(0, grid_Np):
			tau = 0
			for i in range(0, grid_Nw):
				dr = model.grid.widths[0,k,j,i]
				dtau = dr * rho[k,j,i] * chi
				tau += dtau
			tau_max = max(tau_max, tau)

		if(cli.filament == "linear"):
			tau_max *= 2

		dev = 100 * abs(cli.opticaldepth - tau_max) / cli.opticaldepth

		if(cli.verbose):
			print("Check:")
			print(" Numerical integration of the optical depth through the filament center yields tau = ", tau_max)
			print(" This corresponds to a deviation to the chosen setup value of", dev, "percent")


		#
		# Source:
		#
		if(cli.sources == "external"):
		
			nu, jnu            = np.loadtxt('bg_intensity_modified.txt', unpack=True)
			source_R           = 5*pc
			source             = model.add_external_spherical_source()
			source.peeloff     = False
			source.position    = (0, 0, 5.0*pc) # in a Cartesian frame
			source.radius      = source_R
			source.spectrum    = (nu, jnu)
			#source_MeanIntensity_J = <integrate bg_intensity.txt>
			#source_Area        = 4.0 * pi * source_R*source_R
			source.luminosity  = 8237.0*lsun #source_Area * pi * source_MeanIntensity_J
		
		elif(cli.sources == "stellar"):

			source             = model.add_point_source()
			source.luminosity  = 3.839e35 # in ergs s^-1
			source.temperature = 10000.0 # in K
			if(cli.filament == "linear"):
				source.position    = (3.0*pc, 0, 5.0*pc)
			elif(cli.filament == "spiraling"):
				source.position    = (0     , 0, 3.0*pc)

		#
		# To compute total photon numbers:
		#
		grid_N = grid_Nw * grid_Nz * grid_Np
		if(cli.verbose):
			print("Radiation setup:")
			print(" photons_temperature / cell =", cli.photons_temperature)
			print(" photons_temperature total  =", grid_N * cli.photons_temperature)

		file = filename(cli, "temperature")
		file += ".rtin"

	else:
		file = filename(cli, "temperature")
		file += ".rtout"
	
		try:
			with open(file):
				if(cli.verbose):
					print("Using the specific energy distribution from file", file)
				model.use_geometry(file)
				model.use_quantities(file, only_initial=False, copy=False)
				model.use_sources(file)

		except IOError:
			print("ERROR: File '", file, "' cannot be found. \nERROR: This file, containing the specific energy density, has to be computed first via calling hyperion.")
			exit(2)

		#
		# To compute total photon numbers:
		#
		grid_Nw = len(model.grid.gw[0,0,:])
		grid_Nz = len(model.grid.gw[0,:,0])
		grid_Np = len(model.grid.gw[:,0,0])
		grid_N = grid_Nw * grid_Nz * grid_Np
		if(cli.verbose):
			print("Grid setup:")
			print(" grid_Nw =",grid_Nw)
			print(" grid_Nz =",grid_Nz)
			print(" grid_Np =",grid_Np)
			print("Radiation setup:")
			print(" photons_temperature / cell =", cli.photons_temperature)
			print(" photons_temperature total  =", grid_N * cli.photons_temperature)
			print(" photons_raytracing / cell  =", cli.photons_raytracing)
			print(" photons_raytracing total   =", grid_N * cli.photons_raytracing)
			print(" photons_imaging / cell     =", cli.photons_imaging)
			print(" photons_imaging total      =", grid_N * cli.photons_imaging)

		file = filename(cli, "")
		file += ".rtin"


	##
	## Temperature, Images, and SEDs:
	##
	if(cli.mode == "temperature"):

		model.set_raytracing(True)
		model.set_n_photons(
						initial            = grid_N * cli.photons_temperature,
						raytracing_sources = grid_N * cli.photons_raytracing,
						raytracing_dust    = grid_N * cli.photons_raytracing,
						imaging            = grid_N * cli.photons_imaging
						)
	
	elif(cli.mode == "images"):
	
		model.set_n_initial_iterations(0)
		model.set_raytracing(True)
		model.set_monochromatic(True, wavelengths=[100.0, 500.0, 0.55, 2.2])
		model.set_n_photons(
						raytracing_sources = grid_N * cli.photons_raytracing,
						raytracing_dust    = grid_N * cli.photons_raytracing,
						imaging_sources    = grid_N * cli.photons_imaging,
						imaging_dust       = grid_N * cli.photons_imaging
						)
	
		# group = 0
		image1x = model.add_peeled_images(sed=False, image=True)
		image1x.set_image_size(300, 300)
		image1x.set_image_limits(-5*pc, +5*pc, 0, 10*pc)
		image1x.set_viewing_angles([90],[0]) # along the x-direction
		image1x.set_uncertainties(True)
		image1x.set_output_bytes(8)
		image1x.set_track_origin('basic')
	
		# group = 1
		image1y = model.add_peeled_images(sed=False, image=True)
		image1y.set_image_size(300, 300)
		image1y.set_image_limits(-5*pc, +5*pc, 0, 10*pc)
		image1y.set_viewing_angles([90],[90]) # along the y-direction
		image1y.set_uncertainties(True)
		image1y.set_output_bytes(8)
		image1y.set_track_origin('basic')
	
		# group = 2
		image1z = model.add_peeled_images(sed=False, image=True)
		image1z.set_image_size(300, 300)
		image1z.set_image_limits(-5*pc, +5*pc, -5*pc, +5*pc)
		image1z.set_viewing_angles([0],[0]) # along the z-direction
		image1z.set_uncertainties(True)
		image1z.set_output_bytes(8)
		image1z.set_track_origin('basic')

	elif(cli.mode == "sed"):
	
		model.set_n_initial_iterations(0)
		model.set_raytracing(True)
		model.set_n_photons(
							raytracing_sources = grid_N * cli.photons_raytracing,
							raytracing_dust    = grid_N * cli.photons_raytracing,
							imaging            = grid_N * cli.photons_imaging
							)
	
		# group = 0
		sed1 = model.add_peeled_images(sed=True, image=False)
		sed1.set_wavelength_range(250, 0.01, 2000.0)
		sed1.set_viewing_angles([90],[0]) # along the x-direction
		sed1.set_peeloff_origin((0, 0, 2.5*pc))
		sed1.set_aperture_range(1, 0.3*pc, 0.3*pc)
		sed1.set_uncertainties(True)
		sed1.set_output_bytes(8)
		sed1.set_track_origin('basic')

		# group = 1
		sed2 = model.add_peeled_images(sed=True, image=False)
		sed2.set_wavelength_range(250, 0.01, 2000.0)
		sed2.set_viewing_angles([90],[0]) # along the x-direction
		sed2.set_peeloff_origin((0, 0, 5.0*pc))
		sed2.set_aperture_range(1, 0.3*pc, 0.3*pc)
		sed2.set_uncertainties(True)
		sed2.set_output_bytes(8)
		sed2.set_track_origin('basic')

		# group = 2
		sed3 = model.add_peeled_images(sed=True, image=False)
		sed3.set_wavelength_range(250, 0.01, 2000.0)
		sed3.set_viewing_angles([90],[0]) # along the x-direction
		sed3.set_peeloff_origin((0, 0, 7.5*pc))
		sed3.set_aperture_range(1, 0.3*pc, 0.3*pc)
		sed3.set_uncertainties(True)
		sed3.set_output_bytes(8)
		sed3.set_track_origin('basic')

	##
	## Write model for hyperion runs:
	##
	model.conf.output.output_density         = 'last'
	model.conf.output.output_specific_energy = 'last'
	model.conf.output.output_n_photons       = 'last'
	model.write(file)
	if(cli.verbose):
		print("The input file for hyperion was written to", file)
Example #34
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")
Example #35
0
def enzo_m_gen(fname,field_add):
    

    
    #add the fields in pd format
    pf = field_add(fname)
    ad = pf.all_data()
   
 

    #cutout
    center = pf.arr([cfg.model.x_cent,cfg.model.y_cent,cfg.model.z_cent],'code_length')
    
    box_len = pf.quan(cfg.par.zoom_box_len,'kpc').in_units('code_length')
   
    min_region = [center[0]-box_len,center[1]-box_len,center[2]-box_len]
    max_region = [center[0]+box_len,center[1]+box_len,center[2]+box_len]
    region = pf.region(center,min_region,max_region)
  
    pf = region.ds
  
    proj_plots(pf)
    #def. dust density
    def _dust_density(field, data):
        return data[('gas', 'metal_density')].in_units("g/cm**3")*cfg.par.dusttometals_ratio
    
    pf.add_field(('gas', 'dust_density'), function=_dust_density, units = 'g/cm**3')
       
    amr = AMRGrid.from_yt(pf, quantity_mapping={'density':('gas','dust_density')})
    


    '''
    levels = pf.index.max_level
    
    amr = AMRGrid()
    for ilevel in range(levels):
        level = amr.add_level()
        
    for igrid in pf.index.select_grids(ilevel):
        print igrid
        grid = level.add_grid()
        grid.xmin,grid.xmax = igrid.LeftEdge[0].in_units('cm'),igrid.RightEdge[0].in_units('cm')
        grid.ymin,grid.ymax = igrid.LeftEdge[1].in_units('cm'),igrid.RightEdge[1].in_units('cm')
        grid.zmin,grid.zmax = igrid.LeftEdge[2].in_units('cm'),igrid.RightEdge[2].in_units('cm')
        grid.quantities["density"] = np.transpose(np.array(igrid[("gas","metal_density")].in_units('g/cm**3')*cfg.par.dusttometals_ratio))
        grid.nx,grid.ny,grid.nz = igrid[("gas","metal_density")].shape
    '''


    m = Model()

    m.set_amr_grid(amr)

    energy_density_absorbed=energy_density_absorbed_by_CMB()
    energy_density_absorbed = np.repeat(energy_density_absorbed.value,amr['density'].shape)


    d = SphericalDust(cfg.par.dustdir+cfg.par.dustfile)
    if cfg.par.SUBLIMATION == True:
        d.set_sublimation_temperature('fast',temperature=cfg.par.SUBLIMATION_TEMPERATURE)
    m.add_density_grid(amr['density'],d,specific_energy=energy_density_absorbed)
    m.set_specific_energy_type('additional')
 #m.add_density_grid(amr['density'], cfg.par.dustdir+cfg.par.dustfile)
    

    #define the random things needed for parsing out the output args
    #center = pf.domain_center
    [xcent,ycent,zcent] = center
   
    boost = np.array([xcent,ycent,zcent])
    dx = pf.domain_width.in_units('cm')
    dy = pf.domain_width.in_units('cm')
    dz = pf.domain_width.in_units('cm')
    
    
    return m,xcent,ycent,zcent,dx,dy,dz,pf,boost

       
Example #36
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()
RSUB = 15 * au

def random_in_sphere(rmin, rmax, n):
    phi = np.random.uniform(0., 2 * np.pi, n)
    mu = np.random.uniform(-1., 1., n)
    sin_theta = (1 - mu*mu)**0.5
    r = (np.random.random(n) * (rmax ** 3 - rmin ** 3) + rmin ** 3) ** (1. / 3.)
    x = r * np.cos(phi) * sin_theta
    y = r * np.sin(phi) * sin_theta
    z = r * mu
    return x, y, z

# TODO: remove dust around source

m = Model()

# Grain Properties:

d = SphericalDust('../dust/integrated_hg_scattering.hdf5')
chi_v = d.optical_properties.interp_chi_wav(0.55)

# Determine density and density in ambient medium
tau_0 = 0.1
size = 60 * au
rho_0 = tau_0 / (size * chi_v)
mass_0 = rho_0 * size ** 3

# Determine density and mass in sphere 1
tau_1 = 1000
r_1 = 5 * au
from models import sky_model
from sources import setup_sources
from dust import setup_dust

if not os.path.exists("models"):
    os.mkdir("models")

if not os.path.exists("models/basic"):
    os.mkdir("models/basic")

seed = -43912

for model_name in sky_model:

    m = Model("models/basic/basic_%s" % model_name)

    seed += 1
    m.set_seed(seed)

    m = setup_sources(m, model_name, sky_model[model_name][0])
    m = setup_dust(m, **sky_model[model_name][1])

    image = m.add_peeled_images(image=True, sed=False)
    image.set_image_size(1, 1)
    image.set_viewing_angles([90.0], [180.0])
    image.set_image_limits(65.0, -65.0, -1.0, 1.0)
    image.set_inside_observer((8.5 * kpc, 0.0 * kpc, 0.015 * kpc))
    image.set_wavelength_range(160, 3.0, 140.0)
    image.set_track_origin("detailed")
Example #39
0
# A simple model with no dust, just sources, to check that the Doppler Shifting
# is working correctly. This model consists of a disk of sources that is
# rotating in a solid body fashion around the origin. All sources have a
# spectrum that consists of a single spectral line that is narrow enough that we
# can easily see the rotation in a multi-wavelength image. We check that the
# image looks sensible in both binned and peeled images.

import numpy as np
from hyperion.model import Model
from hyperion.util.constants import c

m = Model()

m.set_cartesian_grid([-1., 1], [-1., 1.], [-1., 1])

N = 1000
w = np.random.random(N)**0.5
p = np.random.uniform(0, 2 * np.pi, N)
z = np.random.uniform(-0.1, 0.1, N)

x = w * np.cos(p)
y = w * np.sin(p)

# solid body with 1000 km/s on the outside
v = w * 1e8  # cm / s

vx = -v * np.sin(p)
vy = +v * np.cos(p)
vz = np.repeat(0, N)

# narrow emission line spectrum at 1 micron
Example #40
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")
Example #41
0
def setup_model(outdir,record_dir,outname,params,dust_file,tsc=True,idl=False,plot=False,\
                low_res=True,flat=True,scale=1,radmc=False,mono=False,mono_wave=None,
                record=True,dstar=200.,aperture=None,dyn_cav=False,fix_params=None,
                power=2,better_im=False,ellipsoid=False,TSC_dir='~/programs/misc/TSC/',
                IDL_path='/Applications/exelis/idl83/bin/idl',auto_disk=0.25,fast_plot=False,
                image_only=False, tsc_com=False, ext_source=None):
    """
    params = dictionary of the model parameters
    'alma' keyword is obsoleted
    outdir: The directory for storing Hyperion input files
    record_dir: The directory contains "model_list.txt" for recording parameters
    TSC_dir: Path the TSC-related IDL routines
    IDL_path: The IDL executable
    fast_plot: Do not plot the polar plot of the density because the rendering
               takes quite a lot of time.
    mono: monochromatic radiative transfer mode (need to specify the wavelength
          or a list of wavelength with 'mono_wave')
    image_only: only run for images
    """
    import numpy as np
    import astropy.constants as const
    from astropy.io import ascii
    import scipy as sci
    # to avoid X server error
    import matplotlib as mpl
    mpl.use('Agg')
    #
    import matplotlib.pyplot as plt
    import os
    from matplotlib.colors import LogNorm
    from scipy.integrate import nquad
    from hyperion.model import Model
    from record_hyperion import record_hyperion
    from outflow_inner_edge import outflow_inner_edge
    from pprint import pprint

    # 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  [cm3/g/s^2]
    yr        = 60*60*24*365           # Years in seconds
    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
    g2d       = 100.
    mmw       = 2.37                   # Kauffmann 2008

    m = Model()

    # min and max wavelength to compute (need to define them first for checking dust properties)
    # !!!
    wav_min = 2.0
    wav_max = 1400.
    wav_num = 1400

    # Create dust properties
    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust = dict()
    [dust['nu'], dust['albedo'], dust['chi'], dust['g']] = np.genfromtxt(dust_file).T
    d = HenyeyGreensteinDust(dust['nu'], dust['albedo'], dust['chi'], dust['g'], dust['g']*0)
    # dust sublimation option
    d.set_sublimation_temperature('slow', temperature=1600.0)
    d.set_lte_emissivities(n_temp=3000,
                           temp_min=0.1,
                           temp_max=2000.)
    # if the min and/or max wavelength fall out of range
    if c/wav_min/1e-4 > dust['nu'].max():
        d.optical_properties.extrapolate_nu(dust['nu'].min(), c/wav_min/1e-4)
        print 'minimum wavelength is out of dust model.  The dust model is extrapolated.'
    if c/wav_max/1e-4 < dust['nu'].min():
        d.optical_properties.extrapolate_nu(c/wav_max/1e-4, dust['nu'].max())
        print 'maximum wavelength is out of dust model.  The dust model is extrapolated.'

    # try to solve the freq. problem
    d.optical_properties.extrapolate_nu(3.28e15, 5e15)
    #
    d.write(outdir+os.path.basename(dust_file).split('.')[0]+'.hdf5')
    d.plot(outdir+os.path.basename(dust_file).split('.')[0]+'.png')
    plt.clf()

    # Grids and Density

    # Grid Parameters
    nx        = 300L
    if low_res == True:
        nx    = 100L
    ny        = 400L
    nz        = 50L
    [nx, ny, nz] = [int(scale*nx), int(scale*ny), int(scale*nz)]

    # TSC model input setting
    dict_params = params
    # TSC model parameter
    cs        = dict_params['Cs']*1e5
    t         = dict_params['age']  # year
    omega     = dict_params['Omega0']
    # calculate related parameters
    M_env_dot = 0.975*cs**3/G
    mstar     = M_env_dot * t * yr
    R_cen     = omega**2 * G**3 * mstar**3 /(16*cs**8)
    R_inf     = cs * t * yr
    # protostar parameter
    tstar     = dict_params['tstar']
    R_env_max = dict_params['R_env_max']*AU
    theta_cav = dict_params['theta_cav']
    rho_cav_center = dict_params['rho_cav_center']
    rho_cav_edge   = dict_params['rho_cav_edge']*AU
    rstar     = dict_params['rstar']*RS
    # Mostly fixed parameter
    M_disk    = dict_params['M_disk']*MS
    beta      = dict_params['beta']
    h100      = dict_params['h100']*AU
    rho_cav   = dict_params['rho_cav']
    # make M_disk varies with mstar, which is the mass of star+disk
    if auto_disk != None:
        if M_disk != 0:
            print 'M_disk is reset to %4f of mstar (star+disk)' % auto_disk
            M_disk = mstar * auto_disk
        else:
            print 'M_disk = 0 is found.  M_disk is set to 0.'

    # ellipsoid cavity parameter
    if ellipsoid == True:
        # the numbers are given in arcsec
        a_out = 130 * dstar * AU
        b_out = 50  * dstar * AU
        z_out = a_out
        a_in  = dict_params['a_in'] * dstar * AU
        b_in  = a_in/a_out*b_out
        z_in  = a_in
        rho_cav_out = dict_params['rho_cav_out'] * mh
        rho_cav_in  = dict_params['rho_cav_in']  * mh
    # Calculate the dust sublimation radius
    T_sub = 1600
    a     = 1   # in micron
    # realistic dust
    # d_sub = 2.9388e7*(a/0.1)**-0.2 * (4*np.pi*rstar**2*sigma*tstar**4/LS)**0.5 / T_sub**3 *AU
    # black body dust
    d_sub = (LS/16./np.pi/sigma/AU**2*(4*np.pi*rstar**2*sigma*tstar**4/LS)/T_sub**4)**0.5 *AU
    # use the dust sublimation radius as the inner radius of disk and envelope
    R_disk_min = d_sub
    R_env_min  = d_sub
    rin        = rstar
    rout       = R_env_max
    R_disk_max = R_cen

    # print the variables
    print 'Dust sublimation radius %6f AU' % (d_sub/AU)
    print 'M_star %4f Solar mass' % (mstar/MS)
    print 'Infall radius %4f AU' % (R_inf / AU)

    # if there is any parameter found in fix_params, then fix them
    if fix_params != None:
        if 'R_min' in fix_params.keys():
            R_disk_min = fix_params['R_min']*AU
            R_env_min  = fix_params['R_min']*AU

    # Make the Coordinates
    #
    # if ext_source != None:
    #     rout = R_env_max*1.1
    ri           = rin * (rout/rin)**(np.arange(nx+1).astype(dtype='float')/float(nx))
    ri           = np.hstack((0.0, ri))
    thetai       = PI*np.arange(ny+1).astype(dtype='float')/float(ny)
    phii         = PI*2.0*np.arange(nz+1).astype(dtype='float')/float(nz)

    # Keep the constant cell size in r-direction at large radii
    #
    if flat == True:
        ri_cellsize = ri[1:-1]-ri[0:-2]
        ind = np.where(ri_cellsize/AU > 100.0)[0][0]       # The largest cell size is 100 AU
        ri = np.hstack((ri[0:ind],ri[ind]+np.arange(np.ceil((rout-ri[ind])/100/AU))*100*AU))
        nxx = nx
        nx = len(ri)-1
    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc           = 0.5*( ri[0:nx]     + ri[1:nx+1] )
    thetac       = 0.5*( thetai[0:ny] + thetai[1:ny+1] )
    phic         = 0.5*( phii[0:nz]   + phii[1:nz+1] )

    # for non-TSC model
    if tsc_com:
        import hyperion as hp
        from hyperion.model import AnalyticalYSOModel

        non_tsc = AnalyticalYSOModel()

        # Define the luminsoity source
        nt_source = non_tsc.add_spherical_source()
        nt_source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
        nt_source.radius = rstar  # [cm]
        nt_source.temperature = tstar  # [K]
        nt_source.position = (0., 0., 0.)
        nt_source.mass = mstar

        # Envelope structure
        #
        nt_envelope = non_tsc.add_ulrich_envelope()
        nt_envelope.mdot = M_env_dot    # Infall rate
        nt_envelope.rmin = rin          # Inner radius
        nt_envelope.rc   = R_cen        # Centrifugal radius
        nt_envelope.rmax = R_env_max    # Outer radius
        nt_envelope.star = nt_source

        nt_grid = hp.grid.SphericalPolarGrid(ri, thetai, phii)

        rho_env_ulrich = nt_envelope.density(nt_grid).T
        rho_env_ulrich2d = np.sum(rho_env_ulrich**2,axis=2)/np.sum(rho_env_ulrich,axis=2)

    # Make the dust density model
    # Make the density profile of the envelope
    #
    total_mass = 0
    if tsc == False:
        print 'Calculating the dust density profile with infall solution...'
        if theta_cav != 0:
            # using R = 10000 AU as the reference point
            c0 = (10000.*AU)**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
        else:
            c0 = 0
        rho_env  = np.zeros([len(rc),len(thetac),len(phic)])
        rho_disk = np.zeros([len(rc),len(thetac),len(phic)])
        rho      = np.zeros([len(rc),len(thetac),len(phic)])

        if dyn_cav == True:
            print 'WARNING: Calculation of interdependent cavity property has not implemented in infall-only solution!'
        # Normalization for the total disk mass
        def f(w,z,beta,rstar,h100):
            f = 2*PI*w*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/(w**beta*h100/100**beta))**2)
            return f

        rho_0 = M_disk/(nquad(f,[[R_disk_min,R_disk_max],[-R_env_max,R_env_max]], args=(beta,rstar,h100)))[0]
        i = 0
        j = 0
        if 'rho_cav_center' in locals() == False:
            rho_cav_center = 5e-19
            print 'Use 5.27e-18 as the default value for cavity center'
        if 'rho_cav_edge' in locals() == False:
            rho_cav_edge = 40*AU
            print 'Use 40 AU as the default value for size of the inner region'
        discont = 1
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > R_env_min:
                        # Envelope profile
                        w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])

                        if ellipsoid == False:
                            z_cav = c0*abs(w)**1.5
                            if z_cav == 0:
                                z_cav = R_env_max
                            cav_con = abs(z) > abs(z_cav)
                            if theta_cav == 90:
                                cav_con = True
                        else:
                            # condition for the outer ellipsoid
                            cav_con = (2*(w/b_out)**2 + ((abs(z)-z_out)/a_out)**2) < 1
                        if cav_con:
                            # open cavity
                            if ellipsoid == False:
                                if rho_cav_edge == 0:
                                    rho_cav_edge = R_env_min
                                if (rc[ir] <= rho_cav_edge) & (rc[ir] >= R_env_min):
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center
                                else:
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center*discont*(rho_cav_edge/rc[ir])**power
                                i += 1
                            else:
                                # condition for the inner ellipsoid
                                if (2*(w/b_in)**2 + ((abs(z)-z_in)/a_in)**2) > 1:
                                    rho_env[ir,itheta,iphi] = rho_cav_out
                                else:
                                    rho_env[ir,itheta,iphi] = rho_cav_in
                                i +=1
                        else:
                            j += 1
                            mu = abs(np.cos(thetac[itheta]))
                            # Implement new root finding algorithm
                            roots = np.roots(np.array([1.0, 0.0, rc[ir]/R_cen-1.0, -mu*rc[ir]/R_cen]))
                            if len(roots[roots.imag == 0]) == 1:
                                if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                                    mu_o_dum = roots[roots.imag == 0]
                                else:
                                    mu_o_dum = -0.5
                                    print 'Problem with cubic solving, cos(theta) = ', mu_o_dum
                                    print 'parameters are ', np.array([1.0, 0.0, rc[ir]/R_cen-1.0, -mu*rc[ir]/R_cen])
                            else:
                                mu_o_dum = -0.5
                                for imu in range(0, len(roots)):
                                    if roots[imu]*mu >= 0.0:
                                        if (abs((abs(roots[imu]) - 1.0)) <= 1e-5):
                                            mu_o_dum = 1.0 * np.sign(mu)
                                        else:
                                            mu_o_dum = roots[imu]
                                if mu_o_dum == -0.5:
                                    print 'Problem with cubic solving, roots are: ', roots
                            mu_o = mu_o_dum.real
                            rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*R_cen**3)**0.5)*(rc[ir]/R_cen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*R_cen/rc[ir])**(-1)
                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w/(100*AU))**beta)*h100
                            rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # Combine envelope and disk
                        rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-30
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1/3.)*(ri[ir+1]**3 - ri[ir]**3) * (phii[iphi+1]-phii[iphi]) * -(np.cos(thetai[itheta+1])-np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass

        rho_env  = rho_env  + 1e-40
        rho_disk = rho_disk + 1e-40
        rho      = rho      + 1e-40
    # TSC model
    else:
        print 'Calculating the dust density profile with TSC solution...'
        if theta_cav != 0:
            c0 = (1e4*AU)**(-0.5)*np.sqrt(1/np.sin(np.radians(theta_cav))**3-1/np.sin(np.radians(theta_cav)))
        else:
            c0 = 0
        # If needed, calculate the TSC model via IDL
        #
        if idl == True:
            print 'Using IDL to calculate the TSC model.  Make sure you are running this on mechine with IDL.'
            import pidly
            idl = pidly.IDL(IDL_path)
            idl('.r '+TSC_dir+'tsc.pro')
            idl('.r '+TSC_dir+'tsc_run.pro')
            #
            # only run TSC calculation within infall radius
            # modify the rc array
            ind_infall = np.where(rc >= R_inf)[0][0]
            if max(ri) > R_inf:
                rc_idl = rc[0:ind_infall+1]
            else:
                rc_idl = rc[rc < max(ri)]
            idl.pro('tsc_run', indir=TSC_dir, outdir=outdir, rc=rc_idl, thetac=thetac, time=t,
                    c_s=cs, omega=omega, renv_min=R_env_min)
            file_idl = 'rhoenv.dat'
        else:
            print 'Read the pre-computed TSC model.'
            ind_infall = np.where(rc >= R_inf)[0][0]
            if max(ri) > R_inf:
                rc_idl = rc[0:ind_infall+1]
            else:
                rc_idl = rc[rc < max(ri)]
            if idl != False:
                file_idl = idl

        # read in the exist file
        rho_env_tsc_idl = np.genfromtxt(outdir+file_idl).T
        # because only region within infall radius is calculated by IDL program,
        # need to project it to the original grid
        rho_env_tsc = np.zeros([len(rc), len(thetac)])
        for irc in range(len(rc)):
            if rc[irc] in rc_idl:
                rho_env_tsc[irc,:] = rho_env_tsc_idl[np.squeeze(np.where(rc_idl == rc[irc])),:]

        # extrapolate for the NaN values at the outer radius, usually at radius beyond the infall radius
        # using r^-2 profile at radius greater than infall radius
        # and map the 2d strcuture onto 3-D grid
        # map TSC solution from IDL to actual 2-D grid
        rho_env_tsc2d = np.empty((nx,ny))
        if max(ri) > R_inf:
            for i in range(0, len(rc)):
                if i <= ind_infall:
                    rho_env_tsc2d[i,:] = rho_env_tsc[i,:]
                else:
                    rho_env_tsc2d[i,:] =  10**(np.log10(rho_env_tsc[ind_infall,:]) - 2*(np.log10(rc[i]/rc[ind_infall])))
        else:
            rho_env_tsc2d = rho_env_tsc
        # map it to 3-D grid
        rho_env = np.empty((nx,ny,nz))
        for i in range(0, nz):
            rho_env[:,:,i] = rho_env_tsc2d

        # typical no used.  Just an approach I tried to make the size of the
        # constant desnity region self-consistent with the outflow cavity.
        if dyn_cav == True:
            print 'Calculate the cavity properties using the criteria that swept-up mass = outflowed mass'
            # using swept-up mass = flow mass to derive the edge of the extended flat density region
            v_outflow = 1e2 * 1e5
            rho_cav_edge = outflow_inner_edge(np.copy(rho_env), (ri,thetai,phii),M_env_dot,v_outflow,theta_cav, R_env_min)
            dict_params['rho_cav_edge'] = rho_cav_edge
            # assume gas-to-dust ratio = 100
            rho_cav_center = 0.01 * 0.1*M_env_dot*rho_cav_edge/v_outflow/2 / (2*np.pi/3*rho_cav_edge**3*(1-np.cos(np.radians(theta_cav))))
            dict_params['rho_cav_center'] = rho_cav_center
            print 'inner edge is %5f AU and density is %e g/cm3' % (rho_cav_edge/AU, rho_cav_center)

        # create the array of density of disk and the whole structure
        #
        rho_disk = np.zeros([len(rc),len(thetac),len(phic)])
        rho      = np.zeros([len(rc),len(thetac),len(phic)])

        # non-TSC option
        if tsc_com:
            rho_ulrich = np.zeros([len(rc),len(thetac),len(phic)])

        # Calculate the disk scale height by the normalization of h100
        def f(w,z,beta,rstar,h100):
            f = 2*PI*w*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/(w**beta*h100/100**beta))**2)
            return f
        # The function for calculating the normalization of disk using the total disk mass
        #
        rho_0 = M_disk/(nquad(f,[[R_disk_min,R_disk_max],[-R_env_max,R_env_max]], args=(beta,rstar,h100)))[0]
        i = 0
        j = 0

        # put in default outflow cavity setting if nothing is specified
        if 'rho_cav_center' in locals() == False:
            rho_cav_center = 5e-19
            print 'Use 5e-19 as the default value for cavity center'
        if 'rho_cav_edge' in locals() == False:
            rho_cav_edge = 40*AU
            print 'Use 40 AU as the default value for size of the inner region'
        discont = 1
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    # for external heating option
                    if (rc[ir] > R_env_min):
                        # Envelope profile
                        w = abs(rc[ir]*np.cos(np.pi/2 - thetac[itheta]))
                        z = rc[ir]*np.sin(np.pi/2 - thetac[itheta])

                        if ellipsoid == False:
                            z_cav = c0*abs(w)**1.5
                            if z_cav == 0:
                                z_cav = R_env_max
                            cav_con = abs(z) > abs(z_cav)
                        else:
                            # condition for the outer ellipsoid
                            cav_con = (2*(w/b_out)**2 + ((abs(z)-z_out)/a_out)**2) < 1
                        if cav_con:
                            # open cavity
                            if ellipsoid == False:
                                if rho_cav_edge == 0:
                                    rho_cav_edge = R_env_min
                                if (rc[ir] <= rho_cav_edge) & (rc[ir] >= R_env_min):
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center#*((rc[ir]/AU)**2)
                                    if tsc_com:
                                        rho_env_ulrich[ir,itheta,iphi] = rho_env[ir,itheta,iphi]
                                else:
                                    rho_env[ir,itheta,iphi] = g2d * rho_cav_center*discont*(rho_cav_edge/rc[ir])**power
                                    if tsc_com:
                                        rho_env_ulrich[ir,itheta,iphi] = rho_env[ir,itheta,iphi]
                                i += 1
                            else:
                                # condition for the inner ellipsoid
                                if (2*(w/b_in)**2 + ((abs(z)-z_in)/a_in)**2) > 1:
                                    rho_env[ir,itheta,iphi] = rho_cav_out
                                    if tsc_com:
                                        rho_env_ulrich[ir,itheta,iphi] = rho_env[ir,itheta,iphi]
                                else:
                                    rho_env[ir,itheta,iphi] = rho_cav_in
                                    if tsc_com:
                                        rho_env_ulrich[ir,itheta,iphi] = rho_env[ir,itheta,iphi]
                                i +=1

                        # Disk profile
                        if ((w >= R_disk_min) and (w <= R_disk_max)) == True:
                            h = ((w/(100*AU))**beta)*h100
                            rho_disk[ir,itheta,iphi] = rho_0*(1-np.sqrt(rstar/w))*(rstar/w)**(beta+1)*np.exp(-0.5*(z/h)**2)
                        # Combine envelope and disk
                        rho[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env[ir,itheta,iphi]
                        if tsc_com:
                            rho_ulrich[ir,itheta,iphi] = rho_disk[ir,itheta,iphi] + rho_env_ulrich[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-40
                        if tsc_com:
                            rho[ir,itheta,iphi] = 1e-40
                    # add the dust mass into the total count
                    cell_mass = rho[ir, itheta, iphi] * (1/3.)*(ri[ir+1]**3 - ri[ir]**3) * (phii[iphi+1]-phii[iphi]) * -(np.cos(thetai[itheta+1])-np.cos(thetai[itheta]))
                    total_mass = total_mass + cell_mass
    # apply gas-to-dust ratio of 100
    rho_dust = rho/g2d
    if tsc_com:
        rho_ulrich_dust = rho_ulrich/g2d
    total_mass_dust = total_mass/MS/g2d
    print 'Total dust mass = %f Solar mass' % total_mass_dust

    if record == True:
        # Record the input and calculated parameters
        params = dict_params.copy()
        params.update({'d_sub': d_sub/AU, 'M_env_dot': M_env_dot/MS*yr, 'R_inf': R_inf/AU, 'R_cen': R_cen/AU, 'mstar': mstar/MS, 'M_tot_gas': total_mass/MS})
        record_hyperion(params,record_dir)

    if plot == True:
        # rho2d is the 2-D projection of gas density
        # take the weighted average
        rho2d = np.sum(rho**2,axis=2)/np.sum(rho,axis=2)

        if tsc_com:
            rho2d = np.sum(rho_ulrich**2,axis=2)/np.sum(rho_ulrich,axis=2)

        if fast_plot == False:
            # Plot the azimuthal averaged density
            fig = plt.figure(figsize=(8,6))
            ax_env  = fig.add_subplot(111,projection='polar')

            zmin = 1e-22/mmw/mh
            zmin = 1e-1
            cmap = plt.cm.CMRmap
            rho2d_exp = np.hstack((rho2d,rho2d,rho2d[:,0:1]))
            thetac_exp = np.hstack((thetac-PI/2, thetac+PI/2, thetac[0]-PI/2))
            # plot the gas density
            img_env = ax_env.pcolormesh(thetac_exp,rc/AU,rho2d_exp/mmw/mh,cmap=cmap,norm=LogNorm(vmin=zmin,vmax=1e6)) # np.nanmax(rho2d_exp/mmw/mh)

            ax_env.set_xlabel(r'$\rm{Polar\,angle\,(Degree)}$',fontsize=20)
            ax_env.set_ylabel('',fontsize=20, labelpad=-140)
            ax_env.tick_params(labelsize=18)
            ax_env.set_yticks(np.hstack((np.arange(0,(int(R_env_max/AU/10000.)+1)*10000, 10000),R_env_max/AU)))
            ax_env.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}}$'])
            ax_env.set_yticklabels([])
            # fix the tick label font
            ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=20)
            for label in ax_env.get_yticklabels():
                label.set_fontproperties(ticks_font)

            ax_env.grid(True, color='LightGray', linewidth=1)
            cb = fig.colorbar(img_env, pad=0.1)
            cb.ax.set_ylabel(r'$\rm{Averaged\,Gas\,Density\,(cm^{-3})}$',fontsize=20)
            # cb.set_ticks([1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9])
            # cb.set_ticklabels([r'$\rm{10^{2}}$',r'$\rm{10^{3}}$',r'$\rm{10^{4}}$',r'$\rm{10^{5}}$',r'$\rm{10^{6}}$',\
            #                    r'$\rm{10^{7}}$',r'$\rm{10^{8}}$',r'$\rm{\geq 10^{9}}$'])
            # lower density ticks
            cb.set_ticks([1e-1,1e0,1e1,1e2,1e3,1e4,1e5,1e6])
            cb.set_ticklabels([r'$\rm{10^{-1}}$',r'$\rm{10^{0}}$',r'$\rm{10^{1}}$',r'$\rm{10^{2}}$',r'$\rm{10^{3}}$',
                               r'$\rm{10^{4}}$',r'$\rm{10^{5}}$',r'$\rm{\geq 10^{6}}$'])

            cb_obj = plt.getp(cb.ax.axes, 'yticklabels')
            plt.setp(cb_obj,fontsize=20)
            fig.savefig(outdir+outname+'_gas_density.png', format='png', dpi=300, bbox_inches='tight')
            fig.clf()

        # Plot the radial density profile
        fig = plt.figure(figsize=(12,9))
        ax = fig.add_subplot(111)

        plot_grid = [0,49,99,149,199]
        color_grid = ['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00']
        label = [r'$\rm{\theta='+str(int(np.degrees(thetai[plot_grid[0]])))+'^{\circ}}$',\
                 r'$\rm{\theta='+str(int(np.degrees(thetai[plot_grid[1]])))+'^{\circ}}$',\
                 r'$\rm{\theta='+str(1+int(np.degrees(thetai[plot_grid[2]])))+'^{\circ}}$',\
                 r'$\rm{\theta='+str(int(np.degrees(thetai[plot_grid[3]])))+'^{\circ}}$',\
                 r'$\rm{\theta='+str(1+int(np.degrees(thetai[plot_grid[4]])))+'^{\circ}}$']
        alpha = np.linspace(0.3,1.0,len(plot_grid))
        for i in plot_grid:
            ax.plot(np.log10(rc[rc > 0.14*AU]/AU), np.log10(rho2d[rc > 0.14*AU,i]/g2d/mmw/mh)+plot_grid[::-1].index(i)*-0.2,'-',color=color_grid[plot_grid.index(i)],mec='None',linewidth=2.5, \
                    markersize=3, label=label[plot_grid.index(i)]) # alpha=alpha[plot_grid.index(i)],
        ax.axvline(np.log10(R_inf/AU), linestyle='--', color='k', linewidth=1.5, label=r'$\rm{infall\,radius}$')
        ax.axvline(np.log10(R_cen/AU), linestyle=':', color='k', linewidth=1.5, label=r'$\rm{centrifugal\,radius}$')

        lg = plt.legend(fontsize=20, numpoints=1, ncol=2, framealpha=0.7, loc='upper right')

        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
        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)

        ax.set_ylim([0,11])
        fig.gca().set_xlim(left=np.log10(0.05))
        fig.savefig(outdir+outname+'_gas_radial.pdf',format='pdf',dpi=300,bbox_inches='tight')
        fig.clf()

    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)

    m.add_density_grid(rho_dust.T, d)
    # for non-TSC option
    if tsc_com:
        m.add_density_grid(rho_ulrich_dust.T, d)

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print 'L_center =  % 5.2f L_sun' % ((4*PI*rstar**2)*sigma*(tstar**4)/LS)

    # if ext_source != None:
    #     # add external heating - ISRF
    #     # use standard receipe from Hyperion doc
    #     isrf = ascii.read(ext_source, names=['wavelength', 'J_lambda'])
    #     isrf_nu = c/(isrf['wavelength']*1e-4)
    #     isrf_jnu = isrf['J_lambda']*isrf['wavelength']/isrf_nu
    #
    #     if 'mmp83' in ext_source:
    #         FOUR_PI_JNU = 0.0217
    #     else:
    #         FOUR_PI_JNU = raw_input('What is the FOUR_PI_JNU value?')
    #
    #     s_isrf = m.add_external_spherical_source()
    #     s_isrf.radius = R_env_max
    #     s_isrf.spectrum = (isrf_nu, isrf_jnu)
    #     s_isrf.luminosity = PI * R_env_max**2 * FOUR_PI_JNU

    m.set_raytracing(True)
    # option of using more photons for imaging
    if better_im == False:
        im_photon = 1e6
    else:
        im_photon = 5e7

    if mono == True:
        if (type(mono_wave) == int) or (type(mono_wave) == float) or (type(mono_wave) == str):
            mono_wave = float(mono_wave)
            mono_wave = [mono_wave]

        # Monochromatic radiative transfer setting
        m.set_monochromatic(True, wavelengths=mono_wave)
        m.set_n_photons(initial=1e6, imaging_sources=im_photon,
            imaging_dust=im_photon,raytracing_sources=1e6, raytracing_dust=1e6)
    else:
        # regular wavelength grid setting
        m.set_n_photons(initial=1e6, imaging=im_photon,raytracing_sources=1e6,
                        raytracing_dust=1e6)
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(20)
    m.set_convergence(True, percentile=dict_params['percentile'],
                            absolute=dict_params['absolute'],
                            relative=dict_params['relative'])
    m.set_mrw(True)   # Gamma = 1 by default

    # Setting up images and SEDs
    if not image_only:
        # SED setting
        # Infinite aperture
        syn_inf = m.add_peeled_images(image=False)
        # use the index of wavelength array used by the monochromatic radiative transfer
        if mono == False:
            syn_inf.set_wavelength_range(wav_num, wav_min, wav_max)
        syn_inf.set_viewing_angles([dict_params['view_angle']], [0.0])
        syn_inf.set_uncertainties(True)
        syn_inf.set_output_bytes(8)

        # aperture
        # 7.2 in 10 um scaled by lambda / 10
        # flatten beyond 20 um
        # default aperture (should always specify a set of apertures)
        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, 1300],\
                        '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, 101]}
        # 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)

        dict_peel_sed = {}
        for i in range(0, len(aper_reduced)):
            aper_dum = aper_reduced[i]/2 * (1/3600.*np.pi/180.)*dstar*pc
            dict_peel_sed[str(index_reduced[i])] = m.add_peeled_images(image=False)
            # use the index of wavelength array used by the monochromatic radiative transfer
            if mono == False:
                dict_peel_sed[str(index_reduced[i])].set_wavelength_range(wav_num, wav_min, wav_max)
            dict_peel_sed[str(index_reduced[i])].set_viewing_angles([dict_params['view_angle']], [0.0])
            # aperture should be given in cm and its the radius of the aperture
            dict_peel_sed[str(index_reduced[i])].set_aperture_range(1, aper_dum, aper_dum)
            dict_peel_sed[str(index_reduced[i])].set_uncertainties(True)
            dict_peel_sed[str(index_reduced[i])].set_output_bytes(8)

    # image setting
    syn_im = m.add_peeled_images(sed=False)
    # use the index of wavelength array used by the monochromatic radiative transfer
    if mono == False:
        syn_im.set_wavelength_range(wav_num, wav_min, wav_max)
    # pixel number
    # !!!
    if not mono:
        pix_num = 300
    else:
        pix_num = 8000
    #
    syn_im.set_image_size(pix_num, pix_num)
    syn_im.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    syn_im.set_viewing_angles([dict_params['view_angle']], [0.0])
    syn_im.set_uncertainties(True)
    syn_im.set_output_bytes(8)

    # Output setting
    # Density
    m.conf.output.output_density = 'last'

    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'

    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'

    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'

    m.write(outdir+outname+'.rtin')

    if radmc == True:
        # RADMC-3D still use a pre-defined aperture with lazy for-loop
        aper = np.zeros([len(lam)])
        ind = 0
        for wl in lam:
            if wl < 5:
                aper[ind] = 8.4
            elif wl >= 5 and wl < 14:
                aper[ind] = 1.8 * 4
            elif wl >= 14 and wl < 40:
                aper[ind] = 5.1 * 4
            else:
                aper[ind] = 24.5
            ind += 1

        # Write the wavelength_micron.inp file
        #
        f_wave = open(outdir+'wavelength_micron.inp','w')
        f_wave.write('%d \n' % int(nlam))
        for ilam in range(0,nlam):
            f_wave.write('%f \n' % lam[ilam])
        f_wave.close()

        # Write the camera_wavelength_micron.inp file
        #
        f_wave_cam = open(outdir+'camera_wavelength_micron.inp','w')
        f_wave_cam.write('%d \n' % int(nlam))
        for ilam in range(0,nlam):
            f_wave_cam.write('%f \n' % lam[ilam])
        f_wave_cam.close()

        # Write the aperture_info.inp
        #
        f_aper = open(outdir+'aperture_info.inp','w')
        f_aper.write('1 \n')
        f_aper.write('%d \n' % int(nlam))
        for iaper in range(0, len(aper)):
            f_aper.write('%f \t %f \n' % (lam[iaper],aper[iaper]/2))
        f_aper.close()

        # Write the stars.inp file
        #
        f_star = open(outdir+'stars.inp','w')
        f_star.write('2\n')
        f_star.write('1 \t %d \n' % int(nlam))
        f_star.write('\n')
        f_star.write('%e \t %e \t %e \t %e \t %e \n' % (rstar*0.9999,mstar,0,0,0))
        f_star.write('\n')
        for ilam in range(0,nlam):
            f_star.write('%f \n' % lam[ilam])
        f_star.write('\n')
        f_star.write('%f \n' % -tstar)
        f_star.close()

        # Write the grid file
        #
        f_grid = open(outdir+'amr_grid.inp','w')
        f_grid.write('1\n')                               # iformat
        f_grid.write('0\n')                               # AMR grid style  (0=regular grid, no AMR)
        f_grid.write('150\n')                             # Coordinate system  coordsystem<100: Cartisian; 100<=coordsystem<200: Spherical; 200<=coordsystem<300: Cylindrical
        f_grid.write('0\n')                               # gridinfo
        f_grid.write('1 \t 1 \t 1 \n')                    # Include x,y,z coordinate
        f_grid.write('%d \t %d \t %d \n' % (int(nx)-1,int(ny),int(nz)))    # Size of the grid
        [f_grid.write('%e \n' % ri[ir]) for ir in range(1,len(ri))]
        [f_grid.write('%f \n' % thetai[itheta]) for itheta in range(0,len(thetai))]
        [f_grid.write('%f \n' % phii[iphi]) for iphi in range(0,len(phii))]
        f_grid.close()

        # Write the density file
        #
        f_dust = open(outdir+'dust_density.inp','w')
        f_dust.write('1 \n')                      # format number
        f_dust.write('%d \n' % int((nx-1)*ny*nz)) # Nr of cells
        f_dust.write('1 \n')                      # Nr of dust species
        for iphi in range(0,len(phic)):
            for itheta in range(0,len(thetac)):
                for ir in range(1,len(rc)):
                    f_dust.write('%e \n' % rho_dust[ir,itheta,iphi])
        f_dust.close()

        # Write the dust opacity table
        f_dustkappa = open(outdir+'dustkappa_oh5_extended.inp','w')
        f_dustkappa.write('3 \n')                       # format index for including g-factor
        f_dustkappa.write('%d \n' % len(dust['nu']))    # number of wavlength/frequency in the table
        for i in range(len(dust['nu'])):
            f_dustkappa.write('%f \t %f \t %f \t %f \n' % (c/dust['nu'][i]*1e4, dust['chi'][i], dust['chi'][i]*dust['albedo'][i]/(1-dust['albedo'][i]), dust['g'][i]))
        f_dustkappa.close()

        # Write the Dust opacity control file
        #
        f_opac = open(outdir+'dustopac.inp','w')
        f_opac.write('2               Format number of this file\n')
        f_opac.write('1               Nr of dust species\n')
        f_opac.write('============================================================================\n')
        f_opac.write('1               Way in which this dust species is read\n')
        f_opac.write('0               0=Thermal grain\n')
        # f_opac.write('klaus           Extension of name of dustkappa_***.inp file\n')
        f_opac.write('oh5_extended    Extension of name of dustkappa_***.inp file\n')
        f_opac.write('----------------------------------------------------------------------------\n')
        f_opac.close()

        # Write the radmc3d.inp control file
        #
        f_control = open(outdir+'radmc3d.inp','w')
        f_control.write('nphot = %d \n' % 100000)
        f_control.write('scattering_mode_max = 2\n')
        f_control.write('camera_min_drr = 0.1\n')
        f_control.write('camera_min_dangle = 0.1\n')
        f_control.write('camera_spher_cavity_relres = 0.1\n')
        f_control.write('istar_sphere = 1\n')
        f_control.write('modified_random_walk = 1\n')
        f_control.close()

    return m
Example #42
0
#!/usr/bin/env python3

from numpy import loadtxt, zeros, ones, arange
from hyperion.model import Model
from hyperion.model import ModelOutput
from hyperion.dust import IsotropicDust
from hyperion.util.constants import *
import matplotlib.pyplot as plt

m = Model()

data = loadtxt('../dustkappa_yso.inp', skiprows=2)
nu = c / (data[:,0].copy()[::-1] * 1.0e-4)
kabs = data[:,1].copy()[::-1]
ksca = data[:,2].copy()[::-1]
chi = kabs + ksca
albedo = ksca / chi

d = IsotropicDust(nu, albedo, chi)

nr = 10
nt = 10
np = 10

r = arange(nr)*au/2
t = arange(nt)/(nt-1.)*pi
p = arange(np)/(np-1.)*2*pi

m.set_spherical_polar_grid(r, t, p)

dens = zeros((nr-1,nt-1,np-1)) + 1.0e-17
Example #43
0
# Resolution in pc:
cli.resolution          = 100.0

# Photons per grid cell:
cli.photons_temperature = 1
cli.photons_raytracing  = 1
cli.photons_imaging     = 1

for cli.case in [1, 2]:
    for cli.opticaldepth in [0.256434, 1.28217, 6.41084]:
        for cli.mode in ["temperature", "images", "seds"]:
            
            # Setup step:
            setup_model(cli);
            
            # Compute step:
            file = filename(cli, cli.mode)
            model = Model.read(file+".rtin")
            model.write("temp.rtin")
            model.run(filename=file+".rtout", logfile=file+".hyout", overwrite=True)
            
            # Plotting step:
            #TODO: if(cli.mode == "temperature"): visualize_setup + visualize_results => plot_setup
            if(cli.mode != "temperature"):
                plot_results(cli);
            
            # Export to fits format:
            if(cli.mode == "images"):
                export_to_fits(cli);
Example #44
0
import hyperion
import yt
from hyperion.model import Model
from hyperion.grid import AMRGrid

snapshot = '//ufrc/narayanan/desika.narayanan/yt_datasets/enzo_cosmology_plus/RD0009/RD0009'

ds = yt.load(snapshot)


def _dust_density(field, data):
    return (0.4 * data[('gas', 'metallicity')] *
            data[('gas', 'density')]).in_units('g/cm**3')


ds.add_field(('gas', 'dust_density'), function=_dust_density, units='g/cm**3')

m = Model()
#amr = AMRGrid.from_yt(ds,quantity_mapping={'density':('gas','density')})
amr = AMRGrid.from_yt(ds,
                      quantity_mapping={'density': ('gas', 'dust_density')})
m.set_amr_grid(amr)
Example #45
0
def sph_m_gen(fname,field_add):
    
    refined,dustdens,fc1,fw1,pf,ad = yt_octree_generate(fname,field_add)
    xmin = (fc1[:,0]-fw1[:,0]/2.).convert_to_units('cm') #in proper cm 
    xmax = (fc1[:,0]+fw1[:,0]/2.).convert_to_units('cm')
    ymin = (fc1[:,1]-fw1[:,1]/2.).convert_to_units('cm')
    ymax = (fc1[:,1]+fw1[:,1]/2.).convert_to_units('cm')
    zmin = (fc1[:,2]-fw1[:,2]/2.).convert_to_units('cm')
    zmax = (fc1[:,2]+fw1[:,2]/2.).convert_to_units('cm')
    

    #dx,dy,dz are the edges of the parent grid
    dx = (np.max(xmax)-np.min(xmin)).value
    dy = (np.max(ymax)-np.min(ymin)).value
    dz = (np.max(zmax)-np.min(zmin)).value


    xcent = np.mean([np.min(xmin),np.max(xmax)]) #kpc
    ycent = np.mean([np.min(ymin),np.max(ymax)])
    zcent = np.mean([np.min(zmin),np.max(zmax)])
    
    boost = np.array([xcent,ycent,zcent])
    print ('[pd_front end] boost = ',boost)

    
    #Tom Robitaille's conversion from z-first ordering (yt's default) to
    #x-first ordering (the script should work both ways)

    refined_array = np.array(refined)
    refined_array = np.squeeze(refined_array)
    
    order = find_order(refined_array)
    refined_reordered = []
    dustdens_reordered = np.zeros(len(order))
    
    
    
    for i in range(len(order)): 
        refined_reordered.append(refined[order[i]])
        dustdens_reordered[i] = dustdens[order[i]]


    refined = refined_reordered
    dustdens=dustdens_reordered

    #hyperion octree stats
    max_level = hos.hyperion_octree_stats(refined)


    pto.test_octree(refined,max_level)

    dump_cell_info(refined,fc1,fw1,xmin,xmax,ymin,ymax,zmin,zmax)
    np.save('refined.npy',refined)
    np.save('density.npy',dustdens)
    

    #========================================================================
    #Initialize Hyperion Model
    #========================================================================

    m = Model()
    
    if cfg.par.FORCE_RANDOM_SEED == True: m.set_seed(cfg.par.seed)

    print ('Setting Octree Grid with Parameters: ')



    #m.set_octree_grid(xcent,ycent,zcent,
    #                  dx,dy,dz,refined)
    m.set_octree_grid(0,0,0,dx/2,dy/2,dz/2,refined)    


    #get CMB:
    
    energy_density_absorbed=energy_density_absorbed_by_CMB()
    specific_energy = np.repeat(energy_density_absorbed.value,dustdens.shape)

    if cfg.par.PAH == True:
        
        # load PAH fractions for usg, vsg, and big (grain sizes)
        frac = cfg.par.PAH_frac

        # Normalize to 1
        total = np.sum(list(frac.values()))
        frac = {k: v / total for k, v in frac.items()}

        for size in frac.keys():
            d = SphericalDust(cfg.par.dustdir+'%s.hdf5'%size)
            if cfg.par.SUBLIMATION == True:
                d.set_sublimation_temperature('fast',temperature=cfg.par.SUBLIMATION_TEMPERATURE)
            #m.add_density_grid(dustdens * frac[size], cfg.par.dustdir+'%s.hdf5' % size)
            m.add_density_grid(dustdens*frac[size],d,specific_energy=specific_energy)
        m.set_enforce_energy_range(cfg.par.enforce_energy_range)
    else:
        d = SphericalDust(cfg.par.dustdir+cfg.par.dustfile)
        if cfg.par.SUBLIMATION == True:
            d.set_sublimation_temperature('fast',temperature=cfg.par.SUBLIMATION_TEMPERATURE)
        m.add_density_grid(dustdens,d,specific_energy=specific_energy)
        #m.add_density_grid(dustdens,cfg.par.dustdir+cfg.par.dustfile)  
    m.set_specific_energy_type('additional')








    return m,xcent,ycent,zcent,dx,dy,dz,pf,boost
Example #46
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()
Example #47
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()
Example #48
0
def _dust_density(field, data):
    return data[('gas', 'metal_density')].in_units("g/cm**3")*0.4

ds.add_field(('gas', 'dust_density'), function=_dust_density, units='g/cm**3')

amr = AMRGrid.from_yt(ds, quantity_mapping={'density':('gas','dust_density')})

# Set up Hyperion model

import numpy as np

from hyperion.model import Model
from hyperion.util.constants import pc

m = Model()

m.set_amr_grid(amr)

m.add_density_grid(amr['density'], '/Users/desika//pd/hyperion-dust-0.1.0/dust_files/d03_3.1_6.0_A.hdf5')

m.set_minimum_temperature(10)

m.set_n_initial_iterations(0)

m.set_raytracing(True)

# Add a point source in the center
s = m.add_point_source()
s.position = (0, 0., 0.)
s.luminosity = 1000 * lsun
Example #49
0
import numpy as np
from hyperion.model import Model
from hyperion.util.constants import au, lsun, rsun
from hyperion.dust import SphericalDust


# Model
m = Model()
dist = 20000 * au
x = np.linspace(-dist, dist, 101)
y = np.linspace(-dist, dist, 101)
z = np.linspace(-dist, dist, 101)
m.set_cartesian_grid(x,y,z)

# Dust
d = SphericalDust('kmh.hdf5')
d.set_sublimation_temperature('fast', temperature=1600.)
m.add_density_grid(np.ones((100,100,100)) * 1.e-18,'kmh.hdf5')

# Alpha centauri
sourceA = m.add_spherical_source()
sourceA.luminosity = 1.519 * lsun
sourceA.radius = 1.227 * rsun
sourceA.temperature = 5790.
sourceA.position = (0., 0., 0.)

# Beta centauri
sourceB = m.add_spherical_source()
sourceB.luminosity = 0.5 * lsun
sourceB.radius = 0.865 * rsun
sourceB.temperature = 5260.
from dust import setup_dust

if not os.path.exists("models"):
    os.mkdir("models")

if not os.path.exists("models/indiv"):
    os.mkdir("models/indiv")

seed = -69102

for model_name in ["sky_updarm_gaussian_hole_morepah"]:

    for spectral_type in spectral_types:

        print "Processing %s..." % spectral_type

        m = Model("models/indiv/energy_%s_%s" % (model_name, spectral_type.replace(" ", "_")))

        seed += 1
        m.set_seed(seed)

        m = setup_sources(m, model_name, sky_model[model_name][0], spectral_type=spectral_type)
        m = setup_dust(m, **sky_model[model_name][1])

        m.set_n_initial_iterations(3)
        m.set_n_photons(initial=100000000, imaging=0)

        m.set_enforce_energy_range(False)

        m.write()
Example #51
0
def setup_model_shell(indir,outdir,outname,rin_shell=None,denser_wall=False,tsc=True,idl=False,plot=False,low_res=False,flat=True,scale=1.0):
    import numpy as np
    import astropy.constants as const
    import scipy as sci
    import matplotlib.pyplot as plt
    import matplotlib as mat
    import os
    from matplotlib.colors import LogNorm
    from scipy.optimize import fsolve
    from scipy.optimize import newton
    from scipy.integrate import nquad
    from envelope_func import func
    import hyperion as hp
    from hyperion.model import Model
    from plot_density import plot_density

    # Constants setup
    c         = const.c.cgs.value
    AU        = 1.49598e13     # Astronomical Unit       [cm]
    pc        = 3.08572e18     # Parsec                  [cm]
    MS        = 1.98892e33     # Solar mass              [g]
    LS        = 3.8525e33      # Solar luminosity        [erg/s]
    RS        = 6.96e10        # Solar radius            [cm]
    G         = 6.67259e-8     # Gravitational constant  [cm3/g/s^2]
    yr        = 60*60*24*365   # Years in seconds
    PI        = np.pi          # PI constant
    sigma     = const.sigma_sb.cgs.value  # Stefan-Boltzmann constant 


    m = Model()

    # Create dust properties

    # Hyperion needs nu, albedo, chi, g, p_lin_max
    from hyperion.dust import HenyeyGreensteinDust
    # Read in the dust opacity table used by RADMC-3D
    dust_radmc = dict()
    [dust_radmc['wl'], dust_radmc['abs'], dust_radmc['scat'], dust_radmc['g']] = np.genfromtxt('dustkappa_oh5_extended.inp',skip_header=2).T
    # opacity per mass of dust?
    dust_hy = dict()
    dust_hy['nu'] = c/dust_radmc['wl']*1e4
    ind = np.argsort(dust_hy['nu'])
    dust_hy['nu'] = dust_hy['nu'][ind]
    dust_hy['albedo'] = (dust_radmc['scat']/(dust_radmc['abs']+dust_radmc['scat']))[ind]
    dust_hy['chi'] = (dust_radmc['abs']+dust_radmc['scat'])[ind]
    dust_hy['g'] = dust_radmc['g'][ind]
    dust_hy['p_lin_max'] = 0*dust_radmc['wl'][ind]     # assume no polarization

    d = HenyeyGreensteinDust(dust_hy['nu'], dust_hy['albedo'], dust_hy['chi'], dust_hy['g'], dust_hy['p_lin_max'])
    # dust sublimation does not occur
    # d.set_sublimation_temperature(None)
    d.write(outdir+'oh5.hdf5')
    d.plot(outdir+'oh5.png')

    # Grids and Density
    # Calculation inherited from the script used for RADMC-3D

    # Grid Parameters
    nx        = 300L
    if low_res == True:
        nx    = 100L
    ny        = 400L
    nz        = 50L
    [nx, ny, nz] = [scale*nx, scale*ny, scale*nz]

    if tsc == False:
        # Parameters setup
        # Import the model parameters from another file 
        #
        params     = np.genfromtxt(indir+'/params.dat',dtype=None)
        tstar      = params[0][1]
        mstar      = params[1][1]*MS
        rstar      = params[2][1]*RS
        M_env_dot  = params[3][1]*MS/yr
        M_disk_dot = params[4][1]*MS/yr
        R_env_max  = params[5][1]*AU
        R_env_min  = params[6][1]*AU
        theta_cav  = params[7][1]
        R_disk_max = params[8][1]*AU
        R_disk_min = params[9][1]*AU
        R_cen      = R_disk_max
        M_disk     = params[10][1]*MS
        beta       = params[11][1]
        h100       = params[12][1]*AU
        rho_cav    = params[13][1]
        if denser_wall == True:
            wall       = params[14][1]*AU
            rho_wall   = params[15][1]
        rho_cav_center = params[16][1]
        rho_cav_edge   = params[17][1]*AU

        # Model Parameters
        #
        rin       = rstar
        rout      = R_env_max
        rcen      = R_cen

        # Star Parameters
        #
        mstar    = mstar
        rstar    = rstar*0.9999
        tstar    = tstar
        pstar    = [0.,0.,0.]
    else:
        # TSC model input setting
        params    = np.genfromtxt(indir+'/tsc_params.dat', dtype=None)
        # TSC model parameter
        M_env_dot = params[0][1]*MS/yr
        R_cen     = params[1][1]*AU
        R_inf     = params[2][1]*AU
        # protostar parameter
        tstar     = params[3][1]
        R_env_max = params[4][1]*AU
        theta_cav = params[5][1]
        rho_cav_center = params[6][1]
        rho_cav_edge   = params[7][1]*AU
        rstar     = params[8][1]*RS
        # Calculate the dust sublimation radius
        T_sub = 2000
        a     = 1   #in micron
        d_sub = (306.86*(a/0.1)**-0.4 * (4*np.pi*rstar**2*sigma*tstar**4/LS) / T_sub)**0.5 *AU
        # use the dust sublimation radius as the inner radius of disk and envelope
        R_disk_min = d_sub
        R_env_min  = d_sub
        rin        = rstar
        rout       = R_env_max
        R_disk_max = R_cen
        # mostly fixed parameter
        M_disk    = 0.5*MS
        beta      = 1.093
        h100      = 8.123*AU
        rho_cav   = 1e-21

        # Do the variable conversion
        cs = (G * M_env_dot / 0.975)**(1/3.)  # cm/s
        t = R_inf / cs / yr   # in year
        mstar = M_env_dot * t * yr
        omega = (R_cen * 16*cs**8 / (G**3 * mstar**3))**0.5

    # Make the Coordinates
    #
    ri           = rin * (rout/rin)**(np.arange(nx+1).astype(dtype='float')/float(nx))
    ri           = np.hstack((0.0, ri))
    thetai       = PI*np.arange(ny+1).astype(dtype='float')/float(ny)
    phii         = PI*2.0*np.arange(nz+1).astype(dtype='float')/float(nz)
    # Keep the constant cell size in r-direction
    #
    if flat == True:
        ri_cellsize = ri[1:-1]-ri[0:-2]
        ind = np.where(ri_cellsize/AU > 100.0)[0][0]       # The largest cell size is 100 AU
        ri = np.hstack((ri[0:ind],ri[ind]+np.arange(np.ceil((rout-ri[ind])/100/AU))*100*AU))
        nxx = nx
        nx = len(ri)-1    

    # Assign the coordinates of the center of cell as its coordinates.
    #
    rc           = 0.5*( ri[0:nx]     + ri[1:nx+1] )
    thetac       = 0.5*( thetai[0:ny] + thetai[1:ny+1] )
    phic         = 0.5*( phii[0:nz]   + phii[1:nz+1] )
    # phic         = 0.5*( phii[0:nz-1]   + phii[1:nz] )

    # Make the dust density model
    # Make the density profile of the envelope
    #
    if rin_shell == None:
        rin_shell = 0.3*R_env_max
    if tsc == False:
        print 'Calculating the dust density profile with infall solution...'

        rho_env  = np.zeros([len(rc),len(thetac),len(phic)])
        rho      = np.zeros([len(rc),len(thetac),len(phic)])

        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > rin_shell:
                        # Envelope profile
                        mu = abs(np.cos(PI/2 - thetac[itheta]))
                        # Implement new root finding algorithm
                        roots = np.roots(np.array([1.0, 0.0, rc[ir]/rcen-1.0, -mu*rc[ir]/rcen]))
                        if len(roots[roots.imag == 0]) == 1:
                            if (abs(roots[roots.imag == 0]) - 1.0) <= 0.0:
                                mu_o_dum = roots[roots.imag == 0]
                            else:
                                mu_o_dum = -0.5
                                print 'Problem with cubic solving, cos(theta) = ', mu_o_dum
                                print 'parameters are ', np.array([1.0, 0.0, rc[ir]/rcen-1.0, -mu*rc[ir]/rcen])
                        else:
                            mu_o_dum = -0.5
                            for imu in range(0, len(roots)):
                                if roots[imu]*mu >= 0.0:
                                    if (abs((abs(roots[imu]) - 1.0)) <= 1e-5):
                                        mu_o_dum = 1.0 * np.sign(mu)
                                    else:
                                        mu_o_dum = roots[imu]
                            if mu_o_dum == -0.5:
                                print 'Problem with cubic solving, roots are: ', roots
                        mu_o = mu_o_dum.real
                        rho_env[ir,itheta,iphi] = M_env_dot/(4*PI*(G*mstar*rcen**3)**0.5)*(rc[ir]/rcen)**(-3./2)*(1+mu/mu_o)**(-0.5)*(mu/mu_o+2*mu_o**2*rcen/rc[ir])**(-1)
                        rho[ir,itheta,iphi] = rho_env[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-25
        rho_env  = rho_env  + 1e-40
        rho      = rho      + 1e-40
    # TSC model
    else:
        print 'Calculating the dust density profile with TSC solution...'
        # If needed, calculate the TSC model via IDL
        #
        if idl == True:
            print 'Using IDL to calculate the TSC model.  Make sure you are running this on mechine with IDL.'
            import pidly
            idl = pidly.IDL('/Applications/exelis/idl82/bin/idl')
            idl('.r ~/programs/misc/TSC/tsc.pro')
            idl.pro('tsc_run', outdir=outdir, grid=[nxx,ny,nz], time=t, c_s=cs, omega=omega, rstar=rstar, renv_min=R_env_min, renv_max=R_env_max)
        else:
            print 'Read the pre-computed TSC model.'
        # read in the exist file
        rho_env_tsc = np.genfromtxt(outdir+'rhoenv.dat').T
        # extrapolate for the NaN values at the outer radius, usually at radius beyond the infall radius
        # map the 2d strcuture onto 3d grid
        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
        rho_env_copy = np.array(rho_env_tsc)
        for ithetac in range(0, len(thetac)):
            rho_dum = np.log10(rho_env_copy[(rc > 1.1*R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == False),ithetac])
            rc_dum = np.log10(rc[(rc > 1.1*R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == False)])
            rc_dum_nan = np.log10(rc[(rc > 1.1*R_inf) & (np.isnan(rho_env_copy[:,ithetac]) == True)])
            for i in range(0, len(rc_dum_nan)):
                rho_extrapol = poly(rc_dum, rho_dum, rc_dum_nan[i])
                rho_env_copy[(np.log10(rc) == rc_dum_nan[i]),ithetac] = 10**rho_extrapol
        rho_env2d = rho_env_copy
        rho_env = np.empty((nx,ny,nz))
        for i in range(0, nz):
            rho_env[:,:,i] = rho_env2d
        # create the array of density of disk and the whole structure
        #
        rho      = np.zeros([len(rc),len(thetac),len(phic)])
        # The function for calculating the normalization of disk using the total disk mass
        #
        for ir in range(0,len(rc)):
            for itheta in range(0,len(thetac)):
                for iphi in range(0,len(phic)):
                    if rc[ir] > rin_shell:
                        # Envelope profile
                        rho[ir,itheta,iphi] = rho_env[ir,itheta,iphi]
                    else:
                        rho[ir,itheta,iphi] = 1e-25
        rho_env  = rho_env  + 1e-40
        rho      = rho      + 1e-40

    # Call function to plot the density
    plot_density(rho, rc, thetac,'/Users/yaolun/bhr71/hyperion/', plotname='shell')
    # Insert the calculated grid and dust density profile into hyperion
    m.set_spherical_polar_grid(ri, thetai, phii)
    m.add_density_grid(rho.T, outdir+'oh5.hdf5')    # numpy read the array in reverse order

    # Define the luminsoity source
    source = m.add_spherical_source()
    source.luminosity = (4*PI*rstar**2)*sigma*(tstar**4)  # [ergs/s]
    source.radius = rstar  # [cm]
    source.temperature = tstar  # [K]
    source.position = (0., 0., 0.)
    print 'L_center =  % 5.2f L_sun' % ((4*PI*rstar**2)*sigma*(tstar**4)/LS)

    # Setting up the wavelength for monochromatic radiative transfer
    lambda0 = 0.1
    lambda1 = 2.0
    lambda2 = 50.0
    lambda3 = 95.0
    lambda4 = 200.0
    lambda5 = 314.0
    lambda6 = 670.0
    n01     = 10.0
    n12     = 20.0
    n23     = (lambda3-lambda2)/0.02
    n34     = (lambda4-lambda3)/0.03
    n45     = (lambda5-lambda4)/0.1
    n56     = (lambda6-lambda5)/0.1

    lam01   = lambda0 * (lambda1/lambda0)**(np.arange(n01)/n01)
    lam12   = lambda1 * (lambda2/lambda1)**(np.arange(n12)/n12)
    lam23   = lambda2 * (lambda3/lambda2)**(np.arange(n23)/n23)
    lam34   = lambda3 * (lambda4/lambda3)**(np.arange(n34)/n34)
    lam45   = lambda4 * (lambda5/lambda4)**(np.arange(n45)/n45)
    lam56   = lambda5 * (lambda6/lambda5)**(np.arange(n56+1)/n56)

    lam     = np.concatenate([lam01,lam12,lam23,lam34,lam45,lam56])
    nlam    = len(lam)

    # Create camera wavelength points
    n12     = 70.0
    n23     = 70.0
    n34     = 70.0
    n45     = 50.0
    n56     = 50.0
    
    lam12   = lambda1 * (lambda2/lambda1)**(np.arange(n12)/n12)
    lam23   = lambda2 * (lambda3/lambda2)**(np.arange(n23)/n23)
    lam34   = lambda3 * (lambda4/lambda3)**(np.arange(n34)/n34)
    lam45   = lambda4 * (lambda5/lambda4)**(np.arange(n45)/n45)
    lam56   = lambda5 * (lambda6/lambda5)**(np.arange(n56+1)/n56)

    lam_cam = np.concatenate([lam12,lam23,lam34,lam45,lam56])
    n_lam_cam = len(lam_cam)

    # Radiative transfer setting

    # number of photons for temp and image
    m.set_raytracing(True)
    m.set_monochromatic(True, wavelengths=[3.6, 4.5, 5.8, 8.0, 24, 70, 100, 160, 250, 350, 500])
    m.set_n_photons(initial=1000000, imaging_sources=1000000, imaging_dust=1000000,raytracing_sources=1000000, raytracing_dust=1000000)
    # imaging=100000, raytracing_sources=100000, raytracing_dust=100000
    # number of iteration to compute dust specific energy (temperature)
    m.set_n_initial_iterations(5)
    m.set_convergence(True, percentile=99., absolute=1.5, relative=1.02)
    m.set_mrw(True)   # Gamma = 1 by default
    # m.set_forced_first_scattering(forced_first_scattering=True)
    # Setting up images and SEDs
    image = m.add_peeled_images()
    # image.set_wavelength_range(300, 2.0, 670.0)
    # use the index of wavelength array used by the monochromatic radiative transfer
    image.set_wavelength_index_range(2,12)
    # pixel number
    image.set_image_size(300, 300)
    image.set_image_limits(-R_env_max, R_env_max, -R_env_max, R_env_max)
    image.set_viewing_angles([82.0], [0.0])
    image.set_uncertainties(True)
    # output as 64-bit
    image.set_output_bytes(8)

    # Output setting
    # Density
    m.conf.output.output_density = 'last'

    # Density difference (shows where dust was destroyed)
    m.conf.output.output_density_diff = 'none'

    # Energy absorbed (using pathlengths)
    m.conf.output.output_specific_energy = 'last'

    # Number of unique photons that passed through the cell
    m.conf.output.output_n_photons = 'last'

    m.write(outdir+outname+'.rtin')
import yaml
settings = yaml.load(open('settings.yml'))

if not os.path.exists('models'):
    os.mkdir('models')

TAU_LABEL = {}
TAU_LABEL[0.01] = "1e-2"
TAU_LABEL[0.1] = "1e-1"
TAU_LABEL[1] = "1e+0"
TAU_LABEL[10] = "1e+1"


for tau_v in [0.01, 0.1, 1, 10]:

    m = Model()

    # Global geometry:
    #
    # * slab
    # * system size = 10x10x10 pc
    # * system coordinates (x,y,z min/max) = -5 to +5 pc
    # * slab z extent = -2 to -5 pc
    # * slab xy extend = -5 pc to 5 pc
    # * z optical depth @1um in slab = 0.01, 0.1, 1, 10
    # * optical depth outside slab = 0
    x = np.linspace(-5 * pc, 5 * pc, 100)
    y = np.linspace(-5 * pc, 5 * pc, 100)
    zfine = -2 - np.logspace(-3, np.log10(3), 200)
    z = np.hstack([zfine[::-1], -2, 5]) * pc
Example #53
0
import numpy as np

from hyperion.model import Model
from hyperion.util.constants import pc, lsun

# Initialize model
m = Model()

# Set one-cell cartesian grid
w = np.linspace(-pc, pc, 32)
m.set_cartesian_grid(w, w, w)

# Add density grid with constant density
m.add_density_grid(np.ones(m.grid.shape) * 4.e-20, 'kmh_lite.hdf5')

# Add a point source in the center
s = m.add_point_source()
s.luminosity = 1000 * lsun
s.temperature = 6000.

# Add 10 SEDs for different viewing angles
image = m.add_peeled_images(sed=True, image=False)
image.set_wavelength_range(250, 0.01, 5000.)
image.set_viewing_angles(np.linspace(0., 90., 10), np.repeat(20., 10))
image.set_track_origin('basic')

# Add multi-wavelength image for a single viewing angle
image = m.add_peeled_images(sed=False, image=True)
image.set_wavelength_range(30, 1., 1000.)
image.set_viewing_angles([30.], [20.])
image.set_image_size(200, 200)
Example #54
0
    def __init__(self, par, **kwargs):

        #Set up dust model for Hyperion
        # Initalize the model
        m = Model()
        
        #Set central source position
        m.add_spherical_source(luminosity  = par.get('lsource',1.0*lsun),
                               radius      = par.get('rsource',1.0*rsun),
                               mass        = par.get('msource',1.0*msun),
                               temperature = par.get('tsource',5784.0),
                               position    = par.get('position',(0.0,0.0,0.0))
        
        # Use raytracing to improve s/n of thermal/source emission
        m.set_raytracing(par.get('raytracing',True))
        
        # Use the modified random walk
        m.set_mrw(par.get('modrndwlk',True), gamma=par.get('mrw_gamma',2))

        # Set up spatial grid
        x = np.linspace(-1.5*par.get('rmax',100.*u.au), 1.5*par.get('rmax',100.*u.au), par.get('ngrid',257))
        y = np.linspace(-1.5*par.get('rmax',100.*u.au), 1.5*par.get('rmax',100.*u.au), par.get('ngrid',257))
        z = np.linspace(-1.5*par.get('rmax',100.*u.au), 1.5*par.get('rmax',100.*u.au), par.get('ngrid',257))
        m.set_cartesian_grid(x,y,z)

        print("Spatial grid set up.")

        #Set up density grid
        rho0 = par.get('rho0',1.5e-19)
        alpha_in = par.get('alpha_in',-5.)
        alpha_out = par.get('alpha_out',5.)
        scaleheight= par.get('scaleheight',0.1)
        rmin = par.get('rmin',70.*u.au)
        rmax = par.get('rmax',100.*u.au)
        rmid = (rmax + rmin) / 2
        rr = np.sqrt(m.grid.gx ** 2 + m.grid.gy ** 2 + m.grid.gz ** 2)
        
        #define density grid
        density = np.zeros(m.grid.shape)
        density = rho0 * ( (rr/rmid)**(2.*alpha_in) + (rr/rmid)**(2.*alpha_out) )**(-0.5) * np.exp(-((abs(m.grid.gz)/rr)**2/scaleheight**2)/2.0)
        m.add_density_grid(density, d)

        print("Density grid set up.")
        
        # Set up SED for 10 viewing angles
        sed = m.add_peeled_images(sed=par.get('api_sed',True), image=par.get('api_img',False))
        sed.set_viewing_angles(np.linspace(0., 90., 10), np.repeat(45., 10))
        sed.set_wavelength_range(par.get('nl',101), par.get('lmin',0.1), par.get('lmax',1000.))
        sed.set_track_origin('basic')
        
        # Set number of photons
        m.set_n_photons(initial=par.get('nph_initial',1e4), imaging=par.get('nph_imging',1e5),
                        raytracing_sources=par.get('nph_rtsrcs',1e5), raytracing_dust=par.get('nph_rtdust',1e5))
        
        # Set number of temperature iterations
        m.set_n_initial_iterations(par.get('niter',5))
        
        # Write out file
        m.write('HyperionRT_sed.rtin')
        print("Hyperion RT model created.")

    def __call__(self, *args, **kwargs):
        m.run('HyperionRT_sed.rtout', mpi=True,n_processes=6,overwrite=True)
        print("Hyperion RT model executed.")

        m = ModelOutput('HyperionRT_sed.rtout')

        self.modelFlux = sed.value

    def lnprior(self, theta, **kwargs):
        if self.flatprior:
            if (self.lims[0,0] < theta[0] < self.lims[0,1]) and \
               (self.lims[1,0] < theta[1] < self.lims[1,1]) and \
               (self.lims[2,0] < theta[2] < self.lims[2,1]) and \
               (self.lims[3,0] < theta[3] < self.lims[3,1]) and \
                np.sum(10**theta[4:]) <= 1. and np.all(theta[4:] < 0.): 
                return 0
            else:
                return -np.inf
        else:
            raise NotImplementedError()

    def __str__(self, **kwargs):
        raise NotImplementedError()

    def __repr__(self, **kwargs):
        raise NotImplementedError()

    def inputFile(self, **kwargs):
        #Create a dictionary with which to setup and run Hyperion RT models
        #Dust parameters
        par =   {'dust':'"astrosilicate"',
                 'format':2,
                 'size':0.5,
                 'amin':0.5,
                 'amax':1000.,
                 'na':101,
                 'nang':91,
                 'nanx':11,
                 'nchem':1,
                 'gtd':0,
                 'lmin':0.1,
                 'lmax':1000.0,
                 'nl':101,
                 'massfrac':1.0,
                 'rho0':1.5e-19,
                 'optconst':'"silicate_d03.lnk"',
                 'disttype':'power',
                 'q':3.5,
                 #Source parameters
                 'lsource': 1.0*u.lsun,
                 'rsource': 1.0*u.rsun,
                 'msource': 1.0*u.msun,
                 'tsource': 5784.,
                 'position':[0.0,0.0,0.0],
                 #Disc parameters
                 'rmin': 70.*u.au,
                 'rmax': 100.*u.au,
                 'alpha_in': -5.,
                 'alpha_out': 5.,
                 'scaleheight': 0.1,
                 #RT parameters
                 'niter':5,
                 'nph_initial':1e4,
                 'nph_imging':1e5,
                 'nph_rtsrcs':1e5,
                 'nph_rtdust':1e5,
                 #Peel photons to get images
                 'api_sed':True,
                 'api_img':False,
                 }

        return par

#convenience function to plot the SED of the Hyperion RT output
    def plot_sed(par):
        #Set up figure
        import matplotlib.pyplot as plt
        fig = plt.figure()
        ax = fig.add_subplot(1, 1, 1)
        
        #Read in Hyperion model output
        try:
            m = ModelOutput('HyperionRT_sed.rtout')

            #Total SED
            sed = m.get_sed(inclination=0, aperture=-1, distance=par['distance'],
                                    component='total',units='Jy')        
            ax.loglog(sed.wav, sed.val, color='black', lw=3, alpha=0.5)
            
            # Direct stellar photons
            sed = m.get_sed(inclination=0, aperture=-1, distance=par['distance'],
                                   component='source_emit',units='Jy')
            ax.loglog(sed.wav, sed.val, color='blue')
            # Scattered stellar photons
            sed = m.get_sed(inclination=0, aperture=-1, distance=par['distance'],
                                   component='source_scat',units='Jy')
            ax.loglog(sed.wav, sed.val, color='teal')
            # Direct dust photons
            sed = m.get_sed(inclination=0, aperture=-1, distance=par['distance'],
                                   component='dust_emit',units='Jy')
            ax.loglog(sed.wav, sed.val, color='red')        
            # Scattered dust photons
            sed = m.get_sed(inclination=0, aperture=-1, distance=par['distance'],
                                   component='dust_scat',units='Jy')
            ax.loglog(sed.wav, sed.val, color='orange')

            ax.set_xlabel(r'$\lambda$ [$\mu$m]')
            ax.set_ylabel(r'Flux [Jy]')
            ax.set_xlim(par.get('lmin',0.1), par.get('lmax',1000.))
            ax.set_ylim(1e-6*np.max(sed.value),1.1*np.max(sed.value))
            fig.savefig('HyperionRT_sed_plot_components.png')
            plt.close(fig)

        except IOError:
            print("No Hyperion RT output found, SED not plotted.")

#convenience function to write dust parameter file '<dust>.params' for Hyperion BHDust calculator (separate program)
    def write_bhmie_file(par):
        f=open(par.get('dust','astrosilicate')+'.params','w')
        f.write('"'+par['dust']+'_'+str(par['size'])+'"'+'\n')
        f.write(str(par['format'])+'\n')
        f.write(str(par['amin'])+'\n')
        f.write(str(par['amax'])+'\n')
        f.write(str(par['na'])+'\n')
        f.write(str(par['nang'])+'\n')
        f.write(str(par['nanx'])+'\n')
        f.write(str(par['nchem'])+'\n')
        f.write(str(par['gtd'])+'\n')
        f.write(str(par['lmin'])+' '+str(par['lmax'])+' '+str(par['nl'])+'\n')
        f.write(''+'\n')
        f.write(str(par['massfrac'])+'\n')
        f.write(str(par['rho0'])+'\n')
        f.write(str(par['optconst'])+'\n')
        f.write(str(par['disttype'])+'\n')
        f.write(str(par['amin'])+' '+str(par['amax'])+' '+str(par['q'])+'\n')
        f.close()

        print("BHMie dust input file created.")

        import subprocess
        subprocess.run(['bhmie',param_file])

        print("BHMie dust output file created")