1. Basic usage
DustPy
is using on the simframe
framework for running scientific simulations. For a detailed description of the usage of simframe
please have a look at the Simframe Documentation.
This notebook demonstrates how to run the most simple DustPy
model, i.e., the model that is run by default, how to plot data, and how to resume simulations from dump files.
The Simulation Frame
To set up a model we have to import the Simulation
class from the DustPy
package.
[1]:
from dustpy import Simulation
We can now create an instance of this class.
[2]:
sim = Simulation()
At this stage sim
is an empty simulation object that controls our simulation.
[3]:
sim
[3]:
DustPy
------
dust : Group (Dust quantities)
gas : Group (Gas quantities)
grid : Group (Grid quantities)
star : Group (Stellar quantities)
-----
t : NoneType
-----
Integrator : not specified
Writer : not specified
All the fields are initialized with None
. All attributes can be easiliy addressed via e.g.
[4]:
sim.gas
[4]:
Group (Gas quantities)
----------------------
boundary : Group (Boundary conditions)
S : Group (Source terms)
v : Group (Velocities)
-----
alpha : NoneType
cs : NoneType
eta : NoneType
Fi : NoneType
gamma : NoneType
Hp : NoneType
mfp : NoneType
mu : NoneType
n : NoneType
nu : NoneType
P : NoneType
rho : NoneType
Sigma : NoneType
SigmaFloor : NoneType
T : NoneType
-----
Initializing
We can now initialize the sim
object with Simulation.initialize()
. DustPy
will then fill all the fields with default values.
[5]:
sim.initialize()
sim
object has now values assigned to its fields.[6]:
sim.gas
[6]:
Group (Gas quantities)
----------------------
boundary : Group (Boundary conditions)
S : Group (Source terms)
v : Group (Velocities)
-----
alpha : Field (Turbulent alpha parameter)
cs : Field (Sound speed [cm/s])
eta : Field (Pressure gradient parameter)
Fi : Field (Gas flux interfaces [g/cm/s])
gamma : Field (Adiabatic index)
Hp : Field (Pressure scale height [cm])
mfp : Field (Midplane mean free path [cm])
mu : Field (Mean molecular weight [g])
n : Field (Miplane number density [1/cm³])
nu : Field (Kinematic viscosity [cm²/s])
P : Field (Midplane pressure [g/cm/s²])
rho : Field (Miplane mass density [g/cm³])
Sigma : Field (Surface density [g/cm²])
SigmaFloor : Field (Floor value of surface density [g/cm²])
T : Field (Temperature [K])
-----
You can also display the full table of contents of the sim
object.
[7]:
sim.toc
DustPy
- dust: Group (Dust quantities)
- a: Field (Particle size [cm])
- backreaction: Group (Backreaction coefficients)
- A: Field (Pull factor)
- B: Field (Push factor)
- boundary: Group (Boundary conditions)
- inner: Constant gradient
- outer: Value
- coagulation: Group (Coagulation quantities)
- A: Field (Fragment normalization factors), constant
- eps: Field (Remnant mass distribution), constant
- lf_ind: Field (Index of largest fragment), constant
- phi: Field (Fragment distribution), constant
- rm_ind: Field (Smaller index of remnant), constant
- stick: Field (Sticking matrix), constant
- stick_ind: Field (Non-zero elements of sticking matrix), constant
- D: Field (Diffusivity [cm²/s])
- delta: Group (Mixing parameters)
- rad: Field (Radial mixing parameter)
- turb: Field (Turbulent mixing parameter)
- vert: Field (Vertical mixing parameter)
- eps: Field (Dust-to-gas ratio)
- Fi: Group (Fluxes)
- adv: Field (Advective flux [g/cm/s])
- diff: Field (Diffusive flux [g/cm/s])
- tot: Field (Total flux [g/cm/s])
- fill: Field (Filling factor)
- H: Field (Scale heights [cm])
- kernel: Field (Collision kernel [cm²/s])
- p: Group (Probabilities)
- frag: Field (Fragmentation probability)
- stick: Field (Sticking probability)
- rho: Field (Midplane mass density per mass bin [g/cm³])
- rhos: Field (Solid state density [g/cm³])
- S: Group (Sources)
- coag: Field (Coagulation sources [g/cm²/s])
- ext: Field (External sources [g/cm²/s])
- hyd: Field (Hydrodynamic sources [g/cm²/s])
- tot: Field (Tot sources [g/cm²/s])
- Sigma: Field (Surface density per mass bin [g/cm²])
- SigmaFloor: Field (Floor value of surface density [g/cm²])
- St: Field (Stokes number)
- v: Group (Velocities)
- driftmax: Field (Maximum drift velocity [cm/s])
- frag: Field (Fragmentation velocity [cm/s])
- rad: Field (Radial velocity [cm/s])
- rel: Group (Relative velocities)
- azi: Field (Relative azimuthal velocity [cm/s])
- brown: Field (Relative Brownian motion velocity [cm/s])
- rad: Field (Relative radial velocity [cm/s])
- tot: Field (Total relative velocity [cm/s])
- turb: Field (Relative turbulent velocity [cm/s])
- vert: Field (Relative vertical settling velocity [cm/s])
- gas: Group (Gas quantities)
- alpha: Field (Turbulent alpha parameter)
- boundary: Group (Boundary conditions)
- inner: Constant gradient
- outer: Value
- cs: Field (Sound speed [cm/s])
- eta: Field (Pressure gradient parameter)
- Fi: Field (Gas flux interfaces [g/cm/s])
- gamma: Field (Adiabatic index)
- Hp: Field (Pressure scale height [cm])
- mfp: Field (Midplane mean free path [cm])
- mu: Field (Mean molecular weight [g])
- n: Field (Miplane number density [1/cm³])
- nu: Field (Kinematic viscosity [cm²/s])
- P: Field (Midplane pressure [g/cm/s²])
- rho: Field (Miplane mass density [g/cm³])
- S: Group (Source terms)
- ext: Field (External sources [g/cm²/s])
- hyd: Field (Hydrodynamic sources [g/cm²/s])
- tot: Field (Total sources [g/cm²/s])
- Sigma: Field (Surface density [g/cm²])
- SigmaFloor: Field (Floor value of surface density [g/cm²])
- T: Field (Temperature [K])
- v: Group (Velocities)
- rad: Field (Radial velocity [cm/s])
- visc: Field (Viscous accretion velocity [cm/s])
- grid: Group (Grid quantities)
- A: Field (Radial grid annulus area [cm²]), constant
- m: Field (Mass grid [g]), constant
- Nm: Field (# of mass bins), constant
- Nr: Field (# of radial grid cells), constant
- OmegaK: Field (Keplerian frequency [1/s])
- r: Field (Radial grid cell centers [cm]), constant
- ri: Field (Radial grid cell interfaces [cm]), constant
- star: Group (Stellar quantities)
- L: Field (Luminosity [erg/s])
- M: Field (Mass [g])
- R: Field (Radius [cm])
- T: Field (Effective temperature [K])
- t: IntVar (Time [s]), Integration variable
Running a Simulation
The simulation is now ready to go. This will take a few minutes. The default simulation is running for 100,000 years.
[8]:
sim.run()
DustPy v1.0.6
Documentation: https://stammler.github.io/dustpy/
PyPI: https://pypi.org/project/dustpy/
GitHub: https://github.com/stammler/dustpy/
Please cite Stammler & Birnstiel (2022).
Checking for mass conservation...
- Sticking:
max. rel. error: 2.75e-14
for particle collision
m[114] = 1.93e+04 g with
m[116] = 3.73e+04 g
- Full fragmentation:
max. rel. error: 5.55e-16
for particle collision
m[55] = 7.20e-05 g with
m[55] = 7.20e-05 g
- Erosion:
max. rel. error: 1.78e-15
for particle collision
m[110] = 5.18e+03 g with
m[118] = 7.20e+04 g
Creating data directory data.
Writing file data/data0000.hdf5
Writing dump file data/frame.dmp
Writing file data/data0001.hdf5
Writing dump file data/frame.dmp
Writing file data/data0002.hdf5
Writing dump file data/frame.dmp
Writing file data/data0003.hdf5
Writing dump file data/frame.dmp
Writing file data/data0004.hdf5
Writing dump file data/frame.dmp
Writing file data/data0005.hdf5
Writing dump file data/frame.dmp
Writing file data/data0006.hdf5
Writing dump file data/frame.dmp
Writing file data/data0007.hdf5
Writing dump file data/frame.dmp
Writing file data/data0008.hdf5
Writing dump file data/frame.dmp
Writing file data/data0009.hdf5
Writing dump file data/frame.dmp
Writing file data/data0010.hdf5
Writing dump file data/frame.dmp
Writing file data/data0011.hdf5
Writing dump file data/frame.dmp
Writing file data/data0012.hdf5
Writing dump file data/frame.dmp
Writing file data/data0013.hdf5
Writing dump file data/frame.dmp
Writing file data/data0014.hdf5
Writing dump file data/frame.dmp
Writing file data/data0015.hdf5
Writing dump file data/frame.dmp
Writing file data/data0016.hdf5
Writing dump file data/frame.dmp
Writing file data/data0017.hdf5
Writing dump file data/frame.dmp
Writing file data/data0018.hdf5
Writing dump file data/frame.dmp
Writing file data/data0019.hdf5
Writing dump file data/frame.dmp
Writing file data/data0020.hdf5
Writing dump file data/frame.dmp
Writing file data/data0021.hdf5
Writing dump file data/frame.dmp
Execution time: 0:25:57
By default DustPy
has written output files into the data/
directory.
Plotting
DustPy
is coming with a simple plotting script that can be used to check the status of a simulation.
[9]:
from dustpy import plot
The plotting script does either take the simulation object as argument or a data directory.
If the argument is a simulation object the script is only plotting the current state.
[10]:
plot.panel(sim)
The blue and the green lines in the top left panel are analytical estimates for the fragmentation and drift barrier taken from Birnstiel et al. (2012).
it
, radial ir
, or mass im
index.[11]:
plot.panel("data", it=10, ir=10, im=5)
The top middle and bottom left panels show the dust density distribution along the gray dashed lines in the top left panel.
It’s furthermore possible to use an interactive plotting script.
[12]:
plot.ipanel("data")
Code Units
The dust density distribution plotted here is given by
\(\Sigma_\mathrm{d} \left( r \right) = \int\limits_0^\infty \sigma \left(r, m \right) \mathrm{d} \log m\).
In this way the distribution is independent of the mass grid.
The code units DustPy
uses are \(\Sigma_{\mathrm{d},\,i} \left( r \right) \equiv \Sigma_\mathrm{d} \left(r, m_i \right)\) with
\(\Sigma_\mathrm{d} \left( r \right) = \sum\limits_i \Sigma_{\mathrm{d},\,i} \left( r \right)\),
meaning the numerical sum over the mass dimension is the dust surface density.
[13]:
sim.dust.Sigma.sum(-1)
[13]:
[1.10557336e+01 1.02992403e+01 9.59191143e+00 8.93895173e+00
8.32865715e+00 7.75850789e+00 7.22599249e+00 6.72858135e+00
6.26420956e+00 5.83081478e+00 5.42620080e+00 5.04864335e+00
4.69645672e+00 4.36777515e+00 4.06118902e+00 3.77532591e+00
3.50861087e+00 3.25989842e+00 3.02807253e+00 2.81181963e+00
2.61021307e+00 2.42234147e+00 2.24712516e+00 2.08381579e+00
1.93166212e+00 1.78979290e+00 1.65760286e+00 1.53446406e+00
1.41968875e+00 1.31278546e+00 1.21322061e+00 1.12046562e+00
1.03411972e+00 9.53723000e-01 8.78885950e-01 8.09291321e-01
7.44487702e-01 6.84220071e-01 6.28190605e-01 5.76091230e-01
5.27711760e-01 4.82757140e-01 4.41035921e-01 4.02334717e-01
3.66446537e-01 3.33222129e-01 3.02442339e-01 2.73987432e-01
2.47687148e-01 2.23422946e-01 2.01051129e-01 1.80463176e-01
1.61537112e-01 1.44176170e-01 1.28274293e-01 1.13748337e-01
1.00501698e-01 8.84615589e-02 7.75367958e-02 6.76595237e-02
5.87679684e-02 5.07937245e-02 4.36879524e-02 3.74072233e-02
3.18783737e-02 2.70175128e-02 2.27460076e-02 1.90031682e-02
1.57421892e-02 1.29229042e-02 1.05062053e-02 8.44924723e-03
6.69827665e-03 5.17305825e-03 3.75786050e-03 2.35751219e-03
1.08630361e-03 3.51144052e-04 7.97325012e-05 1.26505270e-05
1.33385792e-06 1.07474163e-07 6.75578366e-09 3.34961791e-10
1.31247630e-11 4.03047526e-13 9.49677567e-15 1.65313317e-16
2.00675100e-18 1.57276265e-20 7.28047481e-23 1.83036618e-25
3.38627923e-28 9.02283461e-29 7.84588510e-29 6.83347698e-29
5.95170965e-29 5.18372240e-29 4.51483347e-29 3.93225557e-29]
To convert this into \(\sigma\) we have to divide the code density distribution by \(B = \frac{\Delta m}{m}\), where \(\Delta m\) is the width of the mass bin centered on \(m\).
Since the mass grid is strictly logarithmic the following relation holds
\(m_{i+1} = A \cdot m_i\).
The grid constant \(A\) can be easily calculated.
[14]:
import numpy as np
A = np.mean(sim.grid.m[1:]/sim.grid.m[:-1])
A
[14]:
1.3894954943731377
We further assume that the mass bin center is exactly in the middle between the mass bin interfaces
\(m_i = \frac{1}{2} \left( m_{i-\frac{1}{2}} + m_{i+\frac{1}{2}} \right)\).
Solving for the interfaces yields
\(\begin{split} m_{i-\frac{1}{2}} &= \frac{2}{A+1} m_i \\ m_{i+\frac{1}{2}} &= \frac{2}{A+1} A\cdot m_i. \end{split}\)
We therefore have
\(\Delta m_i = m_{i+\frac{1}{2}} - m_{i-\frac{1}{2}} = 2\frac{A-1}{A+1}m_i\)
and
\(B = \frac{\Delta m_i}{m_i} = 2\frac{A-1}{A+1}\).
[15]:
B = 2 * (A-1) / (A+1)
[16]:
sim.dust.Sigma / B
[16]:
[[1.35791789e-01 1.11472047e-01 1.16092272e-01 ... 1.52526930e-23
2.11935481e-23 2.94483396e-23]
[1.29819811e-01 1.06226080e-01 1.10385025e-01 ... 1.32845402e-23
1.84588088e-23 2.56484316e-23]
[1.24467541e-01 1.01487194e-01 1.05204713e-01 ... 1.15703508e-23
1.60769503e-23 2.23388501e-23]
...
[4.45718657e-46 6.19324066e-46 8.60547999e-46 ... 2.30858850e-29
3.20777332e-29 4.45718657e-29]
[3.88204722e-46 5.39408712e-46 7.49505975e-46 ... 2.01069653e-29
2.79385376e-29 3.88204722e-29]
[3.38112178e-46 4.69805348e-46 6.52792414e-46 ... 1.75124347e-29
2.43334490e-29 3.38112178e-29]]
Reading data files
If you want to read data files you can use the read/writer module provided by simframe
, that is used to write the data.
[17]:
from dustpy import hdf5writer
wrtr = hdf5writer()
Make sure that the correct data directory is assigned to the writer.
[18]:
wrtr
[18]:
Writer (HDF5 file format using h5py)
------------------------------------
Data directory : data
File names : data/data0000.hdf5
Overwrite : False
Dumping : True
Options : {'com': 'lzf', 'comopts': None}
Verbosity : 1
You can now read a single data file with
[19]:
data0003 = wrtr.read.output(3)
This function returns a namespace and the data can simply be accessed in the same way as for the Simulation
object.
[20]:
data0003.gas.Sigma
[20]:
array([1.11442758e+003, 1.03950016e+003, 9.69534899e+002, 9.03996575e+002,
8.42652573e+002, 7.85281679e+002, 7.31666653e+002, 6.81591015e+002,
6.34839857e+002, 5.91202935e+002, 5.50478001e+002, 5.12473131e+002,
4.77007790e+002, 4.43912953e+002, 4.13030706e+002, 3.84213637e+002,
3.57324174e+002, 3.32233942e+002, 3.08823154e+002, 2.86980035e+002,
2.66600291e+002, 2.47586611e+002, 2.29848199e+002, 2.13300346e+002,
1.97864019e+002, 1.83465484e+002, 1.70035958e+002, 1.57511273e+002,
1.45831575e+002, 1.34941033e+002, 1.24787571e+002, 1.15322622e+002,
1.06500891e+002, 9.82801375e+001, 9.06209726e+001, 8.34866689e+001,
7.68429821e+001, 7.06579849e+001, 6.49019115e+001, 5.95470123e+001,
5.45674173e+001, 4.99390088e+001, 4.56393015e+001, 4.16473303e+001,
3.79435448e+001, 3.45097100e+001, 3.13288134e+001, 2.83849764e+001,
2.56633720e+001, 2.31501460e+001, 2.08323432e+001, 1.86978378e+001,
1.67352666e+001, 1.49339667e+001, 1.32839164e+001, 1.17756796e+001,
1.04003528e+001, 9.14951632e+000, 8.01518836e+000, 6.98978248e+000,
6.06606887e+000, 5.23713926e+000, 4.49637584e+000, 3.83742439e+000,
3.25417178e+000, 2.74072812e+000, 2.29141364e+000, 1.90075039e+000,
1.56345878e+000, 1.27445866e+000, 1.02887484e+000, 8.22046251e-001,
6.49538378e-001, 5.07157848e-001, 3.90968245e-001, 2.97305957e-001,
2.22794764e-001, 1.64357939e-001, 1.19226636e-001, 8.49435663e-002,
5.93611962e-002, 4.06340475e-002, 2.72050959e-002, 1.77866714e-002,
1.13366732e-002, 7.03125139e-003, 4.23534403e-003, 2.47255688e-003,
1.39583152e-003, 7.60163304e-004, 3.98337858e-004, 2.00295348e-004,
9.63569541e-005, 4.42094325e-005, 1.92794721e-005, 7.96245913e-006,
3.10228830e-006, 1.13550420e-006, 3.87735143e-007, 1.00000000e-100])
You can also read the full data directory with
[21]:
data = wrtr.read.all()
The data has now an additional dimension for time.
[22]:
data.gas.Sigma.shape
[22]:
(22, 100)
Data files can be quite large and reading the entire data set can take some time. Is is also possible to only read a single field from the data files instead of the entire data set.
[23]:
SigmaGas = wrtr.read.sequence("gas.Sigma")
[24]:
SigmaGas.shape
[24]:
(22, 100)
It is also possible to exclude certain fields from being written into the data files to save memory by setting their save
attribute to False
.
[25]:
sim.dust.kernel.save = False
Reading Dump Files
The data files are only containing the pure data, but no information about the operations DustPy
has to perform, like customized functions. Therefore, it’s not possible to directly restart a simulation from data files.
simframe
is saving by default the most recent dump file, from which a simulation can be restarted.
Attention: Malware can be injected with dump files, which are pickled objects. Only read dump files that you have created yourself or from sources that you trust! Dump files have to be read with the same version of DustPy
as they were written. Otherwise, it is not guaranteed to work.
[26]:
from dustpy import readdump
[27]:
sim_restart = readdump("data/frame.dmp")
This is now a simulation frame that should be identical to our previous object.
[28]:
sim.gas.Sigma == sim_restart.gas.Sigma
[28]:
[ True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True True True True True True True True True
True True True True]
We can now, for example, add more snapshots and restart the simulation. Here we just want to extend the run by one year.
[29]:
from dustpy import constants as c
sim_restart.t.snapshots = np.hstack((sim_restart.t.snapshots, [100001.*c.year]))
The current time is
[30]:
sim_restart.t / c.year
[30]:
100000.0
We can now restart the simulation for another year.
[31]:
sim_restart.run()
DustPy v1.0.6
Documentation: https://stammler.github.io/dustpy/
PyPI: https://pypi.org/project/dustpy/
GitHub: https://github.com/stammler/dustpy/
Please cite Stammler & Birnstiel (2022).
Checking for mass conservation...
- Sticking:
max. rel. error: 2.75e-14
for particle collision
m[114] = 1.93e+04 g with
m[116] = 3.73e+04 g
- Full fragmentation:
max. rel. error: 5.55e-16
for particle collision
m[55] = 7.20e-05 g with
m[55] = 7.20e-05 g
- Erosion:
max. rel. error: 1.78e-15
for particle collision
m[110] = 5.18e+03 g with
m[118] = 7.20e+04 g
Writing file data/data0022.hdf5
Writing dump file data/frame.dmp
Execution time: 0:00:01
Another file was written and the current time is now
[32]:
sim_restart.t / c.year
[32]:
100001.0