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
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
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 m.add_density_grid(dens, d) source = m.add_spherical_source() source.luminosity = lsun source.radius = rsun source.temperature = 4000. m.set_n_photons(initial=1000000, imaging=0) m.set_convergence(True, percentile=99., absolute=2., relative=1.02) m.write("test_spherical.rtin") m.run("test_spherical.rtout", mpi=False) n = ModelOutput('test_spherical.rtout')
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. sourceB.position = (-11.2 * au, 0., 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
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.0) m.add_density_grid(np.ones((100, 100, 100)) * 1.0e-18, "kmh.hdf5") # Alpha centauri sourceA = m.add_spherical_source() sourceA.luminosity = 1.519 * lsun sourceA.radius = 1.227 * rsun sourceA.temperature = 5790.0 sourceA.position = (0.0, 0.0, 0.0) # Beta centauri sourceB = m.add_spherical_source() sourceB.luminosity = 0.5 * lsun sourceB.radius = 0.865 * rsun sourceB.temperature = 5260.0 sourceB.position = (-11.2 * au, 0.0, 0.0)
# 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 # Set up images
in_box = (x > 0.) & (x < 60 * au) & (y > 0.) & (y < 60 * au) & (z > 0.) & (z < 60 * au) density[in_box] = rho_0 # Set up sphere 1 in_sphere_1 = (x - 10 * au) ** 2 + (y - 15 * au) ** 2 + (z - 20 * au) ** 2 < r_1 ** 2 density[in_sphere_1] = rho_1 # Set up sphere 2 in_sphere_2 = (x - 26.666667 * au) ** 2 + (y - 31.666667 * au) ** 2 + (z - 28.333333 * au) ** 2 < r_2 ** 2 density[in_sphere_2] = rho_2 # Remove dust close to source in_rsub = np.sqrt(x * x + y * y + z * z) < RSUB density[in_rsub] = 0. m.add_density_grid(density, d) # m.set_propagation_check_frequency(1.0) # Set up illuminating source: s = m.add_spherical_source() s.radius = 6.6 * rsun s.temperature = 33000. s.luminosity = 4 * pi * s.radius ** 2 * sigma * s.temperature ** 4 # Set up number of photons m.set_n_photons(initial=NPHOTONS, imaging=0) # Write out and run m.write(os.path.join('models', 'bm2_eff_vor_temperature.rtin'), overwrite=True) m.run(os.path.join('models', 'bm2_eff_vor_temperature.rtout'), mpi=True, overwrite=True)
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
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'
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
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.0e-20, "kmh_lite.hdf5") # Add a point source in the center s = m.add_point_source() s.luminosity = 1000 * lsun s.temperature = 6000.0 # Add 10 SEDs for different viewing angles image = m.add_peeled_images(sed=True, image=False) image.set_wavelength_range(250, 0.01, 5000.0) image.set_viewing_angles(np.linspace(0.0, 90.0, 10), np.repeat(20.0, 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.0, 1000.0) image.set_viewing_angles([30.0], [20.0]) image.set_image_size(200, 200)
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)
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. s.spectrum = nu[::-1], fnu[::-1] s.luminosity = 1 s.radius = 0.1 # Set up images
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')
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)
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) # Set runtime parameters. We turn off scattering for the imaging since it is not
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)
# 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 # Set up images
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)
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')
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
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")
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
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 s.temperature = 6000. i = m.add_peeled_images(sed=False, image=True) i.set_image_limits(-1 * pc, 1 * pc, -1 * pc, 1 * pc)
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
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')
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
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)
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")