Skip to content

Commit 5fc289b

Browse files
paulromanoeepetersonshimwell
authored
Automate workflow for mesh- or cell-based R2S calculations (#3508)
Co-authored-by: Ethan Peterson <[email protected]> Co-authored-by: Jonathan Shimwell <[email protected]>
1 parent 4c41766 commit 5fc289b

File tree

9 files changed

+1176
-51
lines changed

9 files changed

+1176
-51
lines changed

docs/source/pythonapi/deplete.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,16 @@ the following abstract base classes:
287287
abc.SIIntegrator
288288
abc.DepSystemSolver
289289

290+
R2S Automation
291+
--------------
292+
293+
.. autosummary::
294+
:toctree: generated
295+
:nosignatures:
296+
:template: myclass.rst
297+
298+
R2SManager
299+
290300
D1S Functions
291301
-------------
292302

docs/source/usersguide/decay_sources.rst

Lines changed: 179 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,189 @@ Decay Sources
66

77
Through the :ref:`depletion <usersguide_depletion>` capabilities in OpenMC, it
88
is possible to simulate radiation emitted from the decay of activated materials.
9-
For fusion energy systems, this is commonly done using what is known as the
10-
`rigorous 2-step <https://doi.org/10.1016/S0920-3796(02)00144-8>`_ (R2S) method.
11-
In this method, a neutron transport calculation is used to determine the neutron
12-
flux and reaction rates over a cell- or mesh-based spatial discretization of the
13-
model. Then, the neutron flux in each discrete region is used to predict the
14-
activated material composition using a depletion solver. Finally, a photon
15-
transport calculation with a source based on the activity and energy spectrum of
16-
the activated materials is used to determine a desired physical response (e.g.,
17-
a dose rate) at one or more locations of interest.
18-
19-
Once a depletion simulation has been completed in OpenMC, the intrinsic decay
20-
source can be determined as follows. First the activated material composition
21-
can be determined using the :class:`openmc.deplete.Results` object. Indexing an
22-
instance of this class with the timestep index returns a
23-
:class:`~openmc.deplete.StepResult` object, which itself has a
24-
:meth:`~openmc.deplete.StepResult.get_material` method. Once the activated
25-
:class:`~openmc.Material` has been obtained, the
26-
:meth:`~openmc.Material.get_decay_photon_energy` method will give the energy
27-
spectrum of the decay photon source. The integral of the spectrum also indicates
28-
the intensity of the source in units of [Bq]. Altogether, the workflow looks as
29-
follows::
30-
9+
For fusion energy systems, this is commonly done using either the `rigorous
10+
2-step <https://doi.org/10.1016/S0920-3796(02)00144-8>`_ (R2S) method or the
11+
`direct 1-step <https://doi.org/10.1016/S0920-3796(01)00188-0>`_ (D1S) method.
12+
In the R2S method, a neutron transport calculation is used to determine the
13+
neutron flux and reaction rates over a cell- or mesh-based spatial
14+
discretization of the model. Then, the neutron flux in each discrete region is
15+
used to predict the activated material composition using a depletion solver.
16+
Finally, a photon transport calculation with a source based on the activity and
17+
energy spectrum of the activated materials is used to determine a desired
18+
physical response (e.g., a dose rate) at one or more locations of interest.
19+
OpenMC includes automation for both the R2S and D1S methods as described in the
20+
following sections.
21+
22+
Rigorous 2-Step (R2S) Calculations
23+
==================================
24+
25+
OpenMC includes an :class:`openmc.deplete.R2SManager` class that fully automates
26+
cell- and mesh-based R2S calculations. Before we describe this class, it is
27+
useful to understand the basic mechanics of how an R2S calculation works.
28+
Generally, it involves the following steps:
29+
30+
1. The :meth:`openmc.deplete.get_microxs_and_flux` function is called to run a
31+
neutron transport calculation that determines fluxes and microscopic cross
32+
sections in each activation region.
33+
2. The :class:`openmc.deplete.IndependentOperator` and
34+
:class:`openmc.deplete.PredictorIntegrator` classes are used to carry out a
35+
depletion (activation) calculation in order to determine predicted material
36+
compositions based on a set of timesteps and source rates.
37+
3. The activated material composition is determined using the
38+
:class:`openmc.deplete.Results` class. Indexing an instance of this class
39+
with the timestep index returns a :class:`~openmc.deplete.StepResult` object,
40+
which itself has a :meth:`~openmc.deplete.StepResult.get_material` method
41+
returning an activated material.
42+
4. The :meth:`openmc.Material.get_decay_photon_energy` method is used to obtain
43+
the energy spectrum of the decay photon source. The integral of the spectrum
44+
also indicates the intensity of the source in units of [Bq].
45+
5. A new photon source is defined using one of OpenMC's source classes with the
46+
energy distribution set equal to the object returned by the
47+
:meth:`openmc.Material.get_decay_photon_energy` method. The source is then
48+
assigned to a photon :class:`~openmc.Model`.
49+
6. A photon transport calculation is run with ``model.run()``.
50+
51+
Altogether, the workflow looks as follows::
52+
53+
# Run neutron transport calculation
54+
fluxes, micros = openmc.deplete.get_microxs_and_flux(model, domains)
55+
56+
# Run activation calculation
57+
op = openmc.deplete.IndependentOperator(mats, fluxes, micros)
58+
timesteps = ...
59+
source_rates = ...
60+
integrator = openmc.deplete.Integrator(op, timesteps, source_rates)
61+
integrator.integrate()
62+
63+
# Get decay photon source at last timestep
3164
results = openmc.deplete.Results("depletion_results.h5")
32-
33-
# Get results at last timestep
3465
step = results[-1]
35-
36-
# Get activated material composition for ID=1
3766
activated_mat = step.get_material('1')
38-
39-
# Determine photon source
4067
photon_energy = activated_mat.get_decay_photon_energy()
41-
42-
By default, the :meth:`~openmc.Material.get_decay_photon_energy` method will
43-
eliminate spectral lines with very low intensity, but this behavior can be
44-
configured with the ``clip_tolerance`` argument.
68+
photon_source = openmc.IndependentSource(
69+
space=...,
70+
energy=photon_energy,
71+
particle='photon',
72+
strength=photon_energy.integral()
73+
)
74+
75+
# Run photon transport calculation
76+
model.settings.source = photon_source
77+
model.run()
78+
79+
Note that by default, the :meth:`~openmc.Material.get_decay_photon_energy`
80+
method will eliminate spectral lines with very low intensity, but this behavior
81+
can be configured with the ``clip_tolerance`` argument.
82+
83+
Cell-based R2S
84+
--------------
85+
86+
In practice, users do not need to manually go through each of the steps in an R2S
87+
calculation described above. The :class:`~openmc.deplete.R2SManager` fully
88+
automates the execution of neutron transport, depletion, decay source
89+
generation, and photon transport. For a cell-based R2S calculation, once you
90+
have a :class:`~openmc.Model` that has been defined, simply create an instance
91+
of :class:`~openmc.deplete.R2SManager` by passing the model and a list of cells
92+
to activate::
93+
94+
r2s = openmc.deplete.R2SManager(model, [cell1, cell2, cell3])
95+
96+
Note that the ``volume`` attribute must be set for any cell that is to be
97+
activated. The :class:`~openmc.deplete.R2SManager` class allows you to
98+
optionally specify a separate photon model; if not given as an argument, it will
99+
create a shallow copy of the original neutron model (available as the
100+
``neutron_model`` attribute) and store it in the ``photon_model`` attribute. We
101+
can use this to define tallies specific to the photon model::
102+
103+
dose_tally = openmc.Tally()
104+
...
105+
r2s.photon_model.tallies = [dose_tally]
106+
107+
Next, define the timesteps and source rates for the activation calculation::
108+
109+
timesteps = [(3.0, 'd'), (5.0, 'h')]
110+
source_rates = [1e12, 0.0]
111+
112+
In this case, the model is irradiated for 3 days with a source rate of
113+
:math:`10^{12}` neutron/sec and then the source is turned off and the activated
114+
materials are allowed to decay for 5 hours. These parameters should be passed to
115+
the :meth:`~openmc.deplete.R2SManager.run` method to execute the full R2S
116+
calculation. Before we can do that though, for a cell-based calculation, the one
117+
other piece of information that is needed is bounding boxes of the activated
118+
cells::
119+
120+
bounding_boxes = {
121+
cell1.id: cell1.bounding_box,
122+
cell2.id: cell2.bounding_box,
123+
cell3.id: cell3.bounding_box
124+
}
125+
126+
Note that calling the ``bounding_box`` attribute may not work for all
127+
constructive solid geometry regions (for example, a cell that uses a
128+
non-axis-aligned plane). In these cases, the bounding box will need to be
129+
specified manually. Once you have a set of bounding boxes, the R2S calculation
130+
can be run::
131+
132+
r2s.run(timesteps, source_rates, bounding_boxes=bounding_boxes)
133+
134+
If not specified otherwise, a photon transport calculation is run at each time
135+
in the depletion schedule. That means in the case above, we would see three
136+
photon transport calculations. To specify specific times at which photon
137+
transport calculations should be run, pass the ``photon_time_indices`` argument.
138+
For example, if we wanted to run a photon transport calculation only on the last
139+
time (after the 5 hour decay), we would run::
140+
141+
r2s.run(timesteps, source_rates, bounding_boxes=bounding_boxes,
142+
photon_time_indices=[2])
143+
144+
After an R2S calculation has been run, the :class:`~openmc.deplete.R2SManager`
145+
instance will have a ``results`` dictionary that allows you to directly access
146+
results from each of the steps. It will also write out all the output files into
147+
a directory that is named "r2s_<timestamp>/". The ``output_dir`` argument to the
148+
:meth:`~openmc.deplete.R2SManager.run` method enables you to override the
149+
default output directory name if desired.
150+
151+
The :meth:`~openmc.deplete.R2SManager.run` method actually runs three
152+
lower-level methods under the hood::
153+
154+
r2s.step1_neutron_transport(...)
155+
r2s.step2_activation(...)
156+
r2s.step3_photon_transport(...)
157+
158+
For users looking for more control over the calculation, these lower-level
159+
methods can be used in lieu of the :meth:`openmc.deplete.R2SManager.run` method.
160+
161+
Mesh-based R2S
162+
--------------
163+
164+
Executing a mesh-based R2S calculation looks nearly identical to the cell-based
165+
R2S workflow described above. The only difference is that instead of passing a
166+
list of cells to the ``domains`` argument of
167+
:class:`~openmc.deplete.R2SManager`, you need to define a mesh object and pass
168+
that instead. This might look like the following::
169+
170+
# Define a regular Cartesian mesh
171+
mesh = openmc.RegularMesh()
172+
mesh.lower_left = (-50., -50., 0.)
173+
mesh.upper_right = (50., 50., 75.)
174+
mesh.dimension = (10, 10, 5)
175+
176+
r2s = openmc.deplete.R2SManager(model, mesh)
177+
178+
Executing the R2S calculation is then performed by adding photon tallies and
179+
calling the :meth:`~openmc.deplete.R2SManager.run` method with the appropriate
180+
timesteps and source rates. Note that in this case we do not need to define cell
181+
volumes or bounding boxes as is required for a cell-based R2S calculation.
182+
Instead, during the neutron transport step, OpenMC will run a raytracing
183+
calculation to determine material volume fractions within each mesh element
184+
using the :meth:`openmc.MeshBase.material_volumes` method. Arguments to this
185+
method can be customized via the ``mat_vol_kwargs`` argument to the
186+
:meth:`~openmc.deplete.R2SManager.run` method. Most often, this would involve
187+
customizing the number of rays traced to obtain better estimates of volumes. As
188+
an example, if we wanted to run the raytracing calculation with 10 million rays,
189+
we would run::
190+
191+
r2s.run(timesteps, source_rates, mat_vol_kwargs={'n_samples': 10_000_000})
45192

46193
Direct 1-Step (D1S) Calculations
47194
================================

openmc/deplete/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .results import *
1818
from .integrators import *
1919
from .transfer_rates import *
20+
from .r2s import *
2021
from . import abc
2122
from . import cram
2223
from . import helpers

0 commit comments

Comments
 (0)