Tutorial¶
Continuum model¶
Model setup¶
First let us create a directory for our model. Then go to this directory and start python and
copy the dustkappa_silicate.inp
file from the python_examples/datafiles
directory to the
model directory. Then go to the model directory and start a python session.
Import the radmc3dPy package:
from radmc3dPy import *
This way we imported all core modules of the package: analyze
, image
,
setup
, natconst
.
The models
module contains all models (like a library). To check what models are available
we can use the getModelNames()
function:
>>> models.getModelNames()
['lines_nlte_lvg_1d_1', 'ppdisk', 'simple_1', 'sphere1d_1', 'sphere2d_1', 'test_scattering_1']
We can also get a brief description of any model with the getModelDesc()
function:
>>> models.getModelDesc('ppdisk')
'Generic protoplanetary disk model'
To create a model, we need to have a master parameter file containing all parameters of the model.
Every model in the library has a default set of parameters which can be used to, e.g. test the model.
We can use this default parameterset to create the parameter file with the writeDefaultParfile()
function.:
>>> analyze.writeDefaultParfile('ppdisk')
Writing problem_params.inp
Then we can read the parameters from this file...:
>>> par = analyze.readParams()
... and print them:
>>> par.printPar()
# -------------------------------------------------------------------------------------------------------------------------
# Block: Radiation sources
# -------------------------------------------------------------------------------------------------------------------------
incl_cont_stellarsrc = False # # Switches on (True) or off (False) continuous stellar sources )
incl_disc_stellarsrc = True # # Switches on (True) or off (False) discrete stellar sources)
mstar = [1.0*ms] # # Mass of the star(s)
pstar = [0.0, 0.0, 0.0] # # Position of the star(s) (cartesian coordinates)
rstar = [2.0*rs] # # Radius of the star(s)
staremis_type = ["blackbody"] # # Stellar emission type ("blackbody", "kurucz", "nextgen")
tstar = [4000.0] # # Effective temperature of the star(s) [K]
# -------------------------------------------------------------------------------------------------------------------------
# Block: Grid parameters
# -------------------------------------------------------------------------------------------------------------------------
crd_sys = 'sph' # Coordinate system used (car/cyl)
nw = [19, 50, 30] # Number of points in the wavelength grid
nx = [30,50] # Number of grid points in the first dimension
ny = [10,30,30,10] # Number of grid points in the second dimension
nz = 0 # Number of grid points in the third dimension
wbound = [0.1, 7.0, 25., 1e4] # Boundraries for the wavelength grid
xbound = [1.0*au,1.05*au, 100.0*au] # Boundaries for the x grid
xres_nlev = 3 # Number of refinement levels (spherical coordinates only
xres_nspan = 3 # Number of the original grid cells to refine (spherical coordinates only)
xres_nstep = 3 # Number of grid cells to create in a refinement level (spherical coordinates only)
ybound = [0., pi/3., pi/2., 2.*pi/3., pi] # Boundaries for the y grid
zbound = [0., 2.0*pi] # Boundraries for the z grid
# -------------------------------------------------------------------------------------------------------------------------
# Block: Dust opacity
# -------------------------------------------------------------------------------------------------------------------------
dustkappa_ext = ['silicate'] #
gdens = [3.6, 1.8] # Bulk density of the materials in g/cm^3
gsdist_powex = -3.5 # Grain size distribution power exponent
gsmax = 10.0 # Maximum grain size
gsmin = 0.1 # Minimum grain size
lnk_fname = ['/disk2/juhasz/Data/JPDOC/astrosil/astrosil_WD2001_new.lnk', '/disk2/juhasz/Data/JPDOC/carbon/A/cel600.lnk'] #
mixabun = [0.75, 0.25] # Mass fractions of the dust componetns to be mixed
ngs = 1 # Number of grain sizes
# -------------------------------------------------------------------------------------------------------------------------
# Block: Gas line RT
# -------------------------------------------------------------------------------------------------------------------------
gasspec_colpart_abun = [1e0] # Abundance of the molecule
gasspec_colpart_name = ['h2'] # Name of the gas species - the extension of the molecule_EXT.inp file
gasspec_mol_abun = [1e-4] # Abundance of the molecule
gasspec_mol_dbase_type = ['leiden'] # leiden or linelist
gasspec_mol_name = ['co'] # Name of the gas species - the extension of the molecule_EXT.inp file
# -------------------------------------------------------------------------------------------------------------------------
# Block: Code parameters
# -------------------------------------------------------------------------------------------------------------------------
istar_sphere = 0 # 1 - take into account the finite size of the star, 0 - take the star to be point-like
itempdecoup = 1 # Enable for different dust components to have different temperatures
lines_mode = -1 # Line raytracing mode
modified_random_walk = 0 # Switched on (1) and off (0) modified random walk
nphot = 1000000 # Nr of photons for the thermal Monte Carlo
nphot_scat = long(3e4) # Nr of photons for the scattering Monte Carlo (for images)
nphot_spec = long(1e5) # Nr of photons for the scattering Monte Carlo (for spectra)
rto_style = 3 # Format of outpuf files (1-ascii, 2-unformatted f77, 3-binary
scattering_mode_max = 1 # 0 - no scattering, 1 - isotropic scattering, 2 - anizotropic scattering
tgas_eq_tdust = 1 # Take the dust temperature to identical to the gas temperature
# -------------------------------------------------------------------------------------------------------------------------
# Block: Model ppdisk
# -------------------------------------------------------------------------------------------------------------------------
bgdens = 1e-30 # Background density (g/cm^3)
dusttogas = 0.01 # Dust-to-gas mass ratio
gap_drfact = [1e-5] # Density reduction factor in the gap
gap_rin = [10.0*au] # Inner radius of the gap
gap_rout = [40.*au] # Outer radius of the gap
gasspec_mol_dissoc_taulim = [1.0] # Continuum optical depth limit below which all molecules dissociate
gasspec_mol_freezeout_dfact = [1e-3] # Factor by which the molecular abundance should be decreased in the frezze-out zone
gasspec_mol_freezeout_temp = [19.0] # Freeze-out temperature of the molecules in Kelvin
gasspec_vturb = 0.2e5 # Microturbulent line width
hpr_prim_rout = 0.0 # Pressure scale height at rin
hrdisk = 0.1 # Ratio of the pressure scale height over radius at hrpivot
hrpivot = 100.0*au # Reference radius at which Hp/R is taken
mdisk = 9.9500000e+29 # Mass of the disk (either sig0 or mdisk should be set to zero or commented out)
plh = 1./7. # Flaring index
plsig1 = -1.0 # Power exponent of the surface density distribution as a function of radius
prim_rout = 0.0 # Outer boundary of the puffed-up inner rim in terms of rin
rdisk = 100.0*au # Outer radius of the disk
rin = 1.0*au # Inner radius of the disk
sig0 = 0.0 # Surface density at rdisk
srim_plsig = 0.0 # Power exponent of the density reduction inside of srim_rout*rin
srim_rout = 0.0 # Outer boundary of the smoothing in the inner rim in terms of rin
As can be seen, the parameters of the model are split into separate blocks to make it visually easy to recognise which parameters belong to the radiation sources, which to the grid, etc. For a detailed description of the structure and syntax of the master parameter file see Parameters.
Let us now set up the model, i.e. create all necessary input files for RADMC-3D. This can be done with the problemSetupDust()
method.
As the single mandatory argument we need to pass the name of the model. With the binary=True
keyword argument we can set the format of the
input files to be formatted ASCII (binary=False
) or C-style binary (binary=True
). The default is binary output.
If no other keyword argument is given the parameters in the problem_params.inp
file will be used to set up the model.
The following example will set the disk mass to \(10^{-5}{\rm M}_\odot\), introduce a gap between 10AU and 40AU where the density is reduced
by a factor of \(10^5\) and the azimuthal dimension will be switched off in the model, i.e. the model will be only 2D.
>>> setup.problemSetupDust('ppdisk', mdisk='1e-5*ms', gap_rin='[10.0*au]', gap_rout='[40.*au]', gap_drfact='[1e-5]', nz='0')
Writing problem_params.inp
Writing dustopac.inp
Writing wavelength_micron.inp
Writing amr_grid.inp
Writing stars.inp
-------------------------------------------------------------
Luminosities of radiation sources in the model :
As calculated from the input files :
Stars :
Star #0 + hotspot : 3.564346e+33
Continuous starlike source : 0.000000e+00
-------------------------------------------------------------
Writing dust_density.binp
Writing radmc3d.inp
If any keyword argument will be passed to problemSetupDust()
it will be used to override the parameters in problem_params.inp
.
For each setup first the parameters in the master parameter file are read. Then it will be checked whether or not keyword arguments have been passed
to problemSetupDust()
and if so, the value of keyword arguments will be taken and the master opacity file will be updated
with the new parameter value. The type of the value of the keyword argument can be int, float, list or string. int, float and list values can also
be given as strings and in this case this string will be explicitely written into the master parameter file, while for the model setup its numerical
value will be evaluated. E.g.::
>>> setup.problemSetupDust('ppdisk', mdisk='1e-4*ms')
Writing problem_params.inp
Writing dustopac.inp
Writing wavelength_micron.inp
Writing amr_grid.inp
Writing stars.inp
-------------------------------------------------------------
Luminosities of radiation sources in the model :
As calculated from the input files :
Stars :
Star #0 : 3.564346e+33
Continuous starlike source : 0.000000e+00
-------------------------------------------------------------
Writing dust_density.binp
Writing radmc3d.inp
>>> par = analyze.readParams()
>>> par.printPar()
...
mdisk = 1e-4*ms # Mass of the disk (either sig0 or mdisk should be set to zero or commented out)
...
If, on the other hand, python expressions are passed by their numerical values they will be written as such to the master parameter file:
>>> setup.problemSetupDust('ppdisk', mdisk=1e-4*natconst.ms)
Writing problem_params.inp
Writing dustopac.inp
Writing wavelength_micron.inp
Writing amr_grid.inp
Writing stars.inp
-------------------------------------------------------------
Luminosities of radiation sources in the model :
As calculated from the input files :
Stars :
Star #0 : 3.564346e+33
Continuous starlike source : 0.000000e+00
-------------------------------------------------------------
Writing dust_density.binp
Writing radmc3d.inp
>>> par = analyze.readParams()
>>> par.printPar()
...
mdisk = 1.9900000e+29 # Mass of the disk (either sig0 or mdisk should be set to zero or commented out)
...
Read the model structure¶
After the model is set up we can read the input density distribution with the readData()
method:
>>> data = analyze.readData(ddens=True)
Reading dust density
This method returns an instance of the radmc3dData
class. This class handles I/O of any physical variable
(dust density, dust temperature, gas density, gas temperature, gas velocity, microturbulent velocity). Whatever the methods of
radmc3dData
can read they can also write, too, both in formatted ASCII and in C-style binary streams as well.
Similar to RADMC-3D itself, it can also write Legacy VTK files (but currently only if the spherical coordinate system was used for the spatial mesh).
Diagnostic plots¶
First let us import the matplotlib library to be able to make any graphics.
>>> import matplotlib.pylab as plb
Let us also import numpy to be able to use arrays and mathematical functions.
>>> import numpy as np
Dust density contours¶
radmc3dData
stores not only the physical variables as data attributes, but also the wavelength and spatial grids
(radmc3dData.grid
attribute, which is an instance of the radmc3dGrid
class).
So we can make 2D density contour plot.
>>> c = plb.contourf(data.grid.x/natconst.au, np.pi/2.-data.grid.y, np.log10(data.rhodust[:,:,0,0].T), 30)
>>> plb.xlabel('r [AU]')
>>> plb.ylabel(r'$\pi/2-\theta$')
>>> plb.xscale('log')
Adding colorbars in matplotlib
is really easy:
>>> cb = plb.colorbar(c)
>>> cb.set_label(r'$\log_{10}{\rho}$', rotation=270.)
The end result should look like this:

Dust opacity¶
The readOpac()
function in the analyze
module can be used to read the dust opacity.
One can either pass the extension tag name of the dust opacity file (dustkappa_NAME.inp). We can read e.g. the dustkappa_silicate.inp
as:
>>> opac = analyze.readOpac(ext=['silicate'])
alternatively one can also pass the index of the dust component in the dust density array. The command to read the first dust species in the dust density distribution:
>>> opac = analyze.readOpac(idust=[0])
Note, that python also starts the array/list indices from zero, hence the first dust species in the dust density array will have the index of zero.
The readOpac()
function returns an instance of the radmc3dDustOpac
class.
The data attributes of this class are all lists, containing the opacity data of an individual dust component.
We can plot the absorption coefficient as a function of wavelength as:
>>> plb.loglog(opac.wav[0], opac.kabs[0])
>>> plb.xlabel(r'$\lambda$ [$\mu$m]')
>>> plb.ylabel(r'$\kappa_{\rm abs}$ [cm$^2$/g]')

As mentioned, the 0-index of the wav
and kabs
attributes means that we want to plot the wavelength and absorption coefficient
of the first dust species, that has actually been read. The indices in the data attributes mean only the sequential order as
the data have been read. The index of this dust species in the dust density array is given by radmc3dDustOpac.idust
, which is also
a list.
Optical depth¶
It is useful to display where the radial optical depth in the continuum at the peak of the stellar radiation field is located.
The getTau()
method of the radmc3dData
class calculates the
optical depth.:
>>> data.getTau(wav=0.5)
Opacity at 0.50um : 19625.9938111
The getTau()
method puts the optical depth into the radmc3dData.tauy
and
radmc3dData.tauy
attributes. So we can now also overplot the radial optical depth of unity contour:
>>> c = plb.contour(data.grid.x/natconst.au, np.pi/2.-data.grid.y, data.taux[:,:,0].T, [1.0], colors='w', linestyles='solid')
>>> plb.clabel(c, inline=1, fontsize=10)

Run the thermal MC¶
To calculate the temperature distribution in the disk we need to run RADMC-3D in thermal Monte Carlo mode. This can be done from within the python shell:
>>> import os
>>> os.system('radmc3d mctherm')
Dust temperature contours¶
After the thermal Monte Carlo simulation has successfully finished we can read the calculated dust temperature:
>>> data = analyze.readData(dtemp=True)
Reading dust temperature
Alternatively we can also use the previous instance of the radmc3dData
we used to get the dust density,
to read the dust temperature. The readData()
function is only an interface to the methods of the
radmc3dData
class.:
>>> data = analyze.readData(dtemp=True)
Reading dust density
>>> data.readDustTemp()
Reading dust temperature
Then we can plot the 2D temperature contours:
>>> c = plb.contourf(data.grid.x/natconst.au, np.pi/2.-data.grid.y, data.dusttemp[:,:,0,0].T, 30)
>>> plb.xlabel('r [AU]')
>>> plb.ylabel(r'$\pi/2-\theta$')
>>> plb.xscale('log')
>>> cb = plb.colorbar(c)
>>> cb.set_label('T [K]', rotation=270.)
>>> c = plb.contour(data.grid.x/natconst.au, np.pi/2.-data.grid.y, data.dusttemp[:,:,0,0].T, 10, colors='k', linestyles='solid')
>>> plb.clabel(c, inline=1, fontsize=10)
Then the result should look like this:

Images¶
Make an image¶
Images can be calculated using the makeImage()
method
>>> image.makeImage(npix=300., wav=2.2, incl=20., phi=0., sizeau=300.)
================================================================
WELCOME TO RADMC-3D: A 3-D CONTINUUM AND LINE RT SOLVER
This is the 3-D reincarnation of the 2-D RADMC code
(c) 2010/2011 Cornelis Dullemond
************* NOTE: THIS IS STILL A BETA VERSION ***************
****** Some modes/capabilities are not yet ready/mature ********
Please feel free to ask questions. Also please report
bugs and/or suspicious behavior without hestitation.
The reliability of this code depends on your vigilance!
To keep up-to-date with bug-alarms and bugfixes, register to
the RADMC-3D mailing list by sending an email to me:
dullemon@mpia.de or dullemond@uni-heidelberg.de
Please visit the RADMC-3D home page at
http://www.ita.uni-heidelberg.de/~dullemond/software/radmc-3d/
================================================================
Note: T_gas taken to be equal to T_dust of dust species 1
Reading global frequencies/wavelengths...
Reading grid file and prepare grid tree...
Adjusting theta(ny+1) to exactly pi...
Adjusting theta(41) to exactly pi/2...
Reading star data...
Note: Please be aware that you treat the star(s) as
point source(s) while using spherical coordinate mode.
Since R_*<<R_in this is probably OK, but if you want
to play safe, then set istar_sphere = 1 in radmc3d.inp.
Note: Star 1 is taken to be a blackbody star
at a temperature T = 4000. Kelvin
Grid information (current status):
We have 6800 branches, of which 6800 are actual grid cells.
---> 100.000% mem use for branches, and 100.000% mem use for actual cells.
No grid refinement is active. The AMR tree is not allocated (this saves memory).
ALWAYS SELF-CHECK FOR NOW...
Starting procedure for rendering image...
--> Including dust
No lines included...
No gas continuum included...
Reading dust data...
Note: Opacity file dustkappa_silicate.inp does not cover the
complete wavelength range of the model (i.e. the range
in the file frequency.inp or wavelength_micron.inp).
Range of model: lambda = [ 0.10E+00, 0.10E+05]
Range in this file: lambda = [ 0.10E+00, 0.10E+05]
Simple extrapolations are used to extend this range.
Reading dust densities...
Dust mass total = 6.9721299190506066E-008 Msun
Reading dust temperatures...
Rendering image(s)...
Doing scattering Monte Carlo simulation for lambda = 2.2000000000000002 micron...
Using dust scattering mode 1
Wavelength nr 1 corresponding to lambda= 2.2000000000000002 micron
Photon nr 1000
.
.
.
Photon nr 30000
Average number of scattering events per photon package = 5.9100000000000000E-002
Ray-tracing image for lambda = 2.2000000000000002 micron...
Writing image to file...
Used scattering_mode=1, meaning: isotropic scattering approximation.
Diagnostics of flux-conservation (sub-pixeling):
Nr of main pixels (nx*ny) = 90000
Nr of (sub)pixels raytraced = 204616
Nr of (sub)pixels used = 175962
Increase of raytracing cost = 2.2735111111111110
Done...
Display images¶
Now we can read the image:
>>> im = image.readImage()
To display the images calculated by RADMC-3D we can use the plotImage()
method
>>> image.plotImage(im, au=True, log=True, maxlog=10, saturate=1e-5, cmap=plb.cm.gist_heat)
The results should look like this:

We can also display the images using angular coordinates for the image axis (Note that in this case the distance in pc needs also to be passed):
>>> image.plotImage(im, arcsec=True, dpc=140., log=True, maxlog=10, saturate=1e-5, bunit='snu', cmap=plb.cm.gist_heat)

Image manipulations¶
It is also easy to convolve the image with an arbitrary 2D gaussian beam:
>>> cim = im.imConv(fwhm=[0.06, 0.06], pa=0., dpc=140.)
>>> image.plotImage(cim, arcsec=True, dpc=140., log=True, maxlog=10, bunit='snu', cmap=plb.cm.gist_heat)

The effect of a coronographic mask can also be simulated. The cmask_rad keyword of the plotImage()
method sets the
intensity within the given radius to zero.:
>>> image.plotImage(cim, arcsec=True, dpc=140., log=True, maxlog=2.5, bunit='snu', cmask_rad=0.17, cmap=plb.cm.gist_heat)

Writing images to fits¶
We can also write the image to a fits file with the writeFits()
method:
im.writeFits('myimage.fits', dpc=140., coord='03h10m05s -10d05m30s')
It takes at very least two arguments, the name of the file and the distance in parsec.
Line model¶
Similarly to the dust setup we can use the problemSetupGas()
method to create all necessary input files for the line
simulations. In the following we will take the protoplanetary disk model as an example. It requires the dust model setup to be complete
and the dust temperature to be calculated (to set up a simple abundance model with photodissociation and freeze-out). To enhance the effect/visibility
of the freeze-out of the molecules close to the midplane rise the freeze-out temperature to 40K (Note, the real freeze-out temperature of
CO molecules is around 19K and the 40K used here is only for illustration purpose):
>>> setup.problemSetupGas('ppdisk', gasspec_mol_freezeout_temp=[40.])
The problemSetupGas()
method takes one mandatory argument, the name of the model.
Let us now see the gas structure in the disk. First we need to read the gas density:
>>> data = analyze.readData(gdens=True, ispec='co')
The readData()
method reads the content of the molecule_co.inp
and puts it into the
radmc3dData.ndens_mol
attribute. When reading gas density with the readData()
method
we always need to specify the name of the species with the ispec
keyword (i.e. the part of the filename between molecule_
and .inp
).
We can now make contour plots to display a vertical slice of the disk:
>>> sc = plb.contourf(data.grid.x/natconst.au, np.pi/2.-data.grid.y, np.log10(data.ndens_mol[:,:,0,0].T).clip(1e-3), 30)
>>> splb.xlabel('r [AU]')
>>> splb.ylabel(r'$\pi/2-\theta$')
>>> splb.xscale('log')
>>> scb = plb.colorbar(c)
>>> scb.set_label(r'$\log_{10}{n_{\rm CO}}$', rotation=270.)
We can also overplot the dust temperature to check whether freeze-out really happens at 40K as we specified:
>>> data.readDustTemp()
>>> sc = plb.contour(data.grid.x/natconst.au, np.pi/2.-data.grid.y, data.dusttemp[:,:,0,0].T, [40.], colors='w', linestyles='solid')
>>> splb.clabel(c, inline=1, fontsize=10)
The result should look like this:

To calculate images/channel maps we need one additional data file with the molecular data (level energies, Einstein coefficients, etc.).
Such files can be downloaded e.g. from the Leiden Atomic and Molecular Database.
Note, that the files downloaded from the Leiden database need to be renamed to molecule_MOLECULENAME.inp
(e.g. co.dat -> molecule_co.inp).
For our example we can also use the molecule_co.inp
(12C16O data file downloaded from the Leiden database) in the python_examples/datafiles
directory and copy it to the model directory. Then we are ready to make line channel maps:
>>> image.makeImage(npix=300., incl=50., phi=0., sizeau=300., vkms=0.5, iline=3)
This command will calculate an image with 300 pixel and 300 AU each side, at an inclination of 50 degree. The image will be calculated for the
third line/transition int the molecular data file at a frequency/wavelength corresponding to 0.5 km/s assuming zero systemic velocity for the source.
To calculate a full 3D image qube with multiple frequencies/velocities at once, instead of the vkms
keyword one can use the widthkms
and linenlam
keywords. The widthkms
keyword will set the total width of the image qube in velocity (in the unit of km/s) while the linenlam
keyword will
set at how many linearly spaced velocity bins should images be calculated for that interval. For the details of how to calculate line channel maps
see the RADMC-3D manual.
Now we can read the calculate image (channel map):
>>> im = image.readImage()
And display it
>>> image.plotImage(im, arcsec=True, dpc=140., cmap=plb.cm.gist_heat)
