diff --git a/docs/source/methods/depletion.rst b/docs/source/methods/depletion.rst index dce1f2503..0e06b3a50 100644 --- a/docs/source/methods/depletion.rst +++ b/docs/source/methods/depletion.rst @@ -103,16 +103,16 @@ integrate over the entire timestep. Our aim here is not to exhaustively describe all integration methods but rather to give a few examples that elucidate the main considerations one must take into account when choosing a method. Generally, there is a tradeoff between the -accuracy of the method and its computational expense. The expense is driven -almost entirely by the time to compute a transport solution, i.e., to evaluate -:math:`\mathbf{A}` for a given :math:`\mathbf{n}`. Thus, the cost of a method -scales with the number of :math:`\mathbf{A}` evaluations that are performed per -timestep. On the other hand, methods that require more evaluations generally -achieve higher accuracy. The predictor method only requires one evaluation and -its error converges as :math:`\mathcal{O}(h)`. The CE/CM method requires two -evaluations and is thus twice as expensive as the predictor method, but achieves -an error of :math:`\mathcal{O}(h^2)`. An exhaustive description of time -integration methods and their merits can be found in the `thesis of Colin Josey +accuracy of the method and its computational expense. In the case of +transport-coupled depletion, the expense is driven almost entirely by the time +to compute a transport solution, i.e., to evaluate :math:`\mathbf{A}` for a +given :math:`\mathbf{n}`. Thus, the cost of a method scales with the number of +:math:`\mathbf{A}` evaluations that are performed per timestep. On the other +hand, methods that require more evaluations generally achieve higher accuracy. The predictor method only requires one evaluation and its error converges as +:math:`\mathcal{O}(h)`. The CE/CM method requires two evaluations and is thus +twice as expensive as the predictor method, but achieves an error of +:math:`\mathcal{O}(h^2)`. An exhaustive description of time integration methods +and their merits can be found in the `thesis of Colin Josey `_. OpenMC does not rely on a single time integration method but rather has several @@ -169,12 +169,14 @@ Data Considerations In principle, solving Eq. :eq:`depletion-matrix` using CRAM is fairly simple: just construct the burnup matrix at various times and solve a set of sparse -linear systems. However, constructing the burnup matrix itself involves not only -solving the transport equation to estimate transmutation reaction rates but also -a series of choices about what data to include. In OpenMC, the burnup matrix is -constructed based on data inside of a *depletion chain* file, which includes -fundamental data gathered from ENDF incident neutron, decay, and fission product -yield sublibraries. For each nuclide, this file includes: +linear systems. However, constructing the burnup matrix itself involves not +only solving the transport equation to estimate transmutation reaction rates +(in the case of transport-coupled depletion) or to obtain microscopic cross +sections (in the case of transport-independent depletion), but also a series of +choices about what data to include. In OpenMC, the burnup matrix is constructed +based on data inside of a *depletion chain* file, which includes fundamental +data gathered from ENDF incident neutron, decay, and fission product yield +sublibraries. For each nuclide, this file includes: - What transmutation reactions are possible, their Q values, and their products; - If a nuclide is not stable, what decay modes are possible, their branching @@ -185,9 +187,12 @@ yield sublibraries. For each nuclide, this file includes: Transmutation Reactions ----------------------- -OpenMC will setup tallies in a problem based on what transmutation reactions are -available in a depletion chain file, so any arbitrary number of transmutation -reactions can be tracked. The pregenerated chain files that are available on +In transport-coupled depletion, OpenMC will setup tallies in a problem based on +what transmutation reactions are available in a depletion chain file, so any +arbitrary number of transmutation reactions can be tracked. In +transport-independent depletion, OpenMC will calculate reaction rates for every +reaction that is present in both the available cross sections and the depletion +chain file. The pregenerated chain files that are available on https://openmc.org include the following transmutation reactions: fission, (n,\ :math:`\gamma`\ ), (n,2n), (n,3n), (n,4n), (n,p), and (n,\ :math:`\alpha`\ ). @@ -202,11 +207,12 @@ accurately model the branching of the capture reaction in Am241. This is complicated by the fact that the branching ratio may depend on the incident neutron energy causing capture. -OpenMC does not currently allow energy-dependent capture branching ratios. -However, the depletion chain file does allow a transmutation reaction to be -listed multiple times with different branching ratios resulting in different -products. Spectrum-averaged capture branching ratios have been computed in LWR -and SFR spectra and are available at https://openmc.org/depletion-chains. +OpenMC's transport solver does not currently allow energy-dependent capture +branching ratios. However, the depletion chain file does allow a transmutation +reaction to be listed multiple times with different branching ratios resulting +in different products. Spectrum-averaged capture branching ratios have been +computed in LWR and SFR spectra and are available at +https://openmc.org/depletion-chains. Fission Product Yields ---------------------- @@ -217,26 +223,31 @@ energies. It is an open question as to what the best way to handle this energy dependence is. OpenMC includes three methods for treating the energy dependence of FPY: -1. Use FPY data corresponding to a specified energy. +1. Use FPY data corresponding to a specified energy. This is used by default in + both transport-coupled and transport-independent depletion. 2. Tally fission rates above and below a specified cutoff energy. Assume that all fissions below the cutoff energy correspond to thermal FPY data and all - fission above the cutoff energy correspond to fast FPY data. + fission above the cutoff energy correspond to fast FPY data. Only applicable + to transport-coupled depletion. 3. Compute the average energy at which fission events occur and use an effective FPY by linearly interpolating between FPY provided at neighboring energies. + Only applicable to transport-coupled depletion -The method can be selected through the ``fission_yield_mode`` argument to the -:class:`openmc.deplete.Operator` constructor. +The method for transport-coupled depletion can be selected through the +``fission_yield_mode`` argument to the :class:`openmc.deplete.Operator` +constructor. Power Normalization ------------------- -The reaction rates provided OpenMC are given in units of reactions per source -particle. For depletion, it is necessary to compute an absolute reaction rate in -reactions per second. To do so, the reaction rates are normalized based on a -specified power. A complete description of how this normalization can be -performed is described in :ref:`usersguide_tally_normalization`. Here, we simply -note that the main depletion class, :class:`openmc.deplete.Operator`, allows the -user to choose one of two methods for estimating the heating rate, including: +In transport-coupled depletion, the reaction rates provided OpenMC are given in +units of reactions per source particle. For depletion, it is necessary to +compute an absolute reaction rate in reactions per second. To do so, the +reaction rates are normalized based on a specified power. A complete +description of how this normalization can be performed is described in +:ref:`usersguide_tally_normalization`. Here, we simply note that the main +depletion class, :class:`openmc.deplete.Operator`, allows the user to choose +one of two methods for estimating the heating rate, including: 1. Using fixed Q values from a depletion chain file (useful for comparisons to other codes that use fixed Q values), or diff --git a/docs/source/pythonapi/deplete.rst b/docs/source/pythonapi/deplete.rst index 246cea59f..cc36a17c7 100644 --- a/docs/source/pythonapi/deplete.rst +++ b/docs/source/pythonapi/deplete.rst @@ -12,19 +12,20 @@ Primary API The two primary requirements to perform depletion with :mod:`openmc.deplete` are: - 1) A transport operator + 1) A reaction rate operator 2) A time-integration scheme -The former is responsible for executing a transport code, like OpenMC, -and retaining important information required for depletion. The most common examples -are reaction rates and power normalization data. The latter is responsible for -projecting reaction rates and compositions forward in calendar time across -some step size :math:`\Delta t`, and obtaining new compositions given a power -or power density. The :class:`Operator` is provided to handle communicating with -OpenMC. Several classes are provided that implement different time-integration -algorithms for depletion calculations, which are described in detail in Colin -Josey's thesis, `Development and analysis of high order neutron -transport-depletion coupling algorithms `_. +The former is responsible for obtaining transmuation reaction rates. The latter +is responsible for projecting reaction rates and compositions forward in +calendar time across some step size :math:`\Delta t`, and obtaining new +compositions given a power or power density. The :class:`Operator` class is +provided to obtain reaction rates via tallies through OpenMC's transport +solver, and the :class:`IndependentOperator` class is provided to obtain +reaction rates from cross-section data. Several classes are provided that +implement different time-integration algorithms for depletion calculations, +which are described in detail in Colin Josey's thesis, `Development and +analysis of high order neutron transport-depletion coupling algorithms +`_. .. autosummary:: :toctree: generated @@ -40,8 +41,8 @@ transport-depletion coupling algorithms `_. SICELIIntegrator SILEQIIntegrator -Each of these classes expects a "transport operator" to be passed. Operators -specific to OpenMC are available using the following classes: +Each of these classes expects a "reaction rate operator" to be passed. Operators +provided by OpenMC are available using the following classes: .. autosummary:: :toctree: generated diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index 0bc2a5af9..7409c6610 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -4,23 +4,55 @@ Depletion and Transmutation =========================== -OpenMC supports coupled depletion, or burnup, calculations through the -:mod:`openmc.deplete` Python module. OpenMC solves the transport equation to -obtain transmutation reaction rates, and then the reaction rates are used to -solve a set of transmutation equations that determine the evolution of nuclide -densities within a material. The nuclide densities predicted as some future time -are then used to determine updated reaction rates, and the process is repeated -for as many timesteps as are requested. +OpenMC supports transport-coupled and transport-independent depletion, or +burnup, calculations through the :mod:`openmc.deplete` Python module. OpenMC +uses transmutation reaction rates to solve a set of transmutation equations +that determine the evolution of nuclide densities within a material. The +nuclide densities predicted as some future time are then used to determine +updated reaction rates, and the process is repeated for as many timesteps as +are requested. -The depletion module is designed such that the flux/reaction rate solution (the -transport "operator") is completely isolated from the solution of the -transmutation equations and the method used for advancing time. At present, the -:mod:`openmc.deplete` module offers a single transport operator, -:class:`openmc.deplete.Operator` (which uses the OpenMC transport solver), but -in principle additional operator classes based on other transport codes could be -implemented and no changes to the depletion solver itself would be needed. The -operator class requires a :class:`~openmc.Model` instance containing -material, geometry, and settings information:: +The depletion module is designed such that the reaction rate solution (the +"operator") is completely isolated from the solution of the transmutation +equations and the method used for advancing time. + +:mod:`openmc.deplete` supports multiple time-integration methods for determining +material compositions over time. Each method appears as a different class. +For example, :class:`openmc.deplete.CECMIntegrator` runs a depletion calculation +using the CE/CM algorithm (deplete over a timestep using the middle-of-step +reaction rates). An instance of :class:`~openmc.deplete.abc.TransportOperator` +is passed to one of these functions along with the timesteps and power level:: + + power = 1200.0e6 # watts + timesteps = [10.0, 10.0, 10.0] # days + openmc.deplete.CECMIntegrator(op, timesteps, power, timestep_units='d').integrate() + +The depletion problem is executed, and once it is done a +``depletion_results.h5`` file is written. The results can be analyzed using the +:class:`openmc.deplete.Results` class. This class has methods that allow for +easy retrieval of k-effective, nuclide concentrations, and reaction rates over +time:: + + results = openmc.deplete.Results("depletion_results.h5") + time, keff = results.get_keff() + +Note that the coupling between the reaction rate solver and the transmutation +solver happens in-memory rather than by reading/writing files on disk. OpenMC has two categories of +operators for obtaining transmutation reaction rates. + +.. _coupled-depletion: + +Transport-coupled depletion +=========================== + +This category of operator solves the transport equation to obtain transmutation +reaction rates. At present, the :mod:`openmc.deplete` module offers a single +transport-coupled operator, :class:`openmc.deplete.Operator` (which uses the +OpenMC transport solver), but in principle additional transport-coupled operator +classes based on other transport codes could be implemented and no changes to +the depletion solver itself would be needed. The +:class:`openmc.deplete.Operator` class requires a :class:`~openmc.Model` +instance containing material, geometry, and settings information:: model = openmc.Model() ... @@ -30,36 +62,14 @@ material, geometry, and settings information:: Any material that contains a fissionable nuclide is depleted by default, but this can behavior can be changed with the :attr:`Material.depletable` attribute. -.. important:: The volume must be specified for each material that is depleted by - setting the :attr:`Material.volume` attribute. This is necessary - in order to calculate the proper normalization of tally results - based on the source rate. - -:mod:`openmc.deplete` supports multiple time-integration methods for determining -material compositions over time. Each method appears as a different class. -For example, :class:`openmc.deplete.CECMIntegrator` runs a depletion calculation -using the CE/CM algorithm (deplete over a timestep using the middle-of-step -reaction rates). An instance of :class:`openmc.deplete.Operator` is passed to -one of these functions along with the timesteps and power level:: - - power = 1200.0e6 # watts - timesteps = [10.0, 10.0, 10.0] # days - openmc.deplete.CECMIntegrator(op, timesteps, power, timestep_units='d').integrate() - -The coupled transport-depletion problem is executed, and once it is done a -``depletion_results.h5`` file is written. The results can be analyzed using the -:class:`openmc.deplete.Results` class. This class has methods that allow for -easy retrieval of k-effective, nuclide concentrations, and reaction rates over -time:: - - results = openmc.deplete.Results("depletion_results.h5") - time, keff = results.get_keff() - -Note that the coupling between the transport solver and the transmutation solver -happens in-memory rather than by reading/writing files on disk. +.. important:: + + The volume must be specified for each material that is depleted by setting + the :attr:`Material.volume` attribute. This is necessary in order to + calculate the proper normalization of tally results based on the source rate. Fixed-Source Transmutation -========================== +-------------------------- When the ``power`` or ``power_density`` argument is used for one of the Integrator classes, it is assumed that OpenMC is running in k-eigenvalue mode, @@ -91,10 +101,12 @@ timestep in the calculation. A zero source rate for a given timestep will result in a decay-only step, where all reaction rates are zero. Caveats -======= +------- + +.. _energy-deposition: Energy Deposition ------------------ +~~~~~~~~~~~~~~~~~ The default energy deposition mode, ``"fission-q"``, instructs the :class:`~openmc.deplete.Operator` to normalize reaction rates using the product @@ -126,7 +138,7 @@ should be, including indirect components. Some examples are provided below:: A more complete way to model the energy deposition is to use the modified -heating reactions described in :ref:`methods_heating`. These values can be used +heating reactions described in :ref:`methods_heating`. These values can be used to normalize reaction rates instead of using the fission reaction rates with:: op = openmc.deplete.Operator(model, "chain.xml", @@ -137,7 +149,7 @@ of :meth:`openmc.data.IncidentNeutron.from_njoy()`, and will eventually be bundl into the distributed libraries. Local Spectra and Repeated Materials ------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is not uncommon to explicitly create a single burnable material across many locations. From a pure transport perspective, there is nothing wrong with @@ -184,42 +196,81 @@ Transport-independent depletion .. note:: - This feature is still under heavy development. API changes are - possible and likely in the near future. + This feature is still under heavy development and has yet to be verifed + code-to-code . API changes and feature additions are possible and likely in + the near future. -OpenMC supports running depletion calculations independent of the OpenMC -transport solver using the :class:`~openmc.deplete.IndependentOperator` class. -This class supports both constant-flux (``source-rate`` normalization) and -constant-power depletion (``fission-q`` normalization). +This category of operator uses pre-calculated one-group microscopic cross +sections to obtain transmutation reaction rates. OpenMC provides the +:class:`~openmc.deplete.IndependentOperator` for this method of calculation. +While the one-group microscopic cross sections can be calculated using a +transport solver, :class:`~openmc.deplete.IndependentOperator` is not directly +coupled to any transport solver. The +:class:`~openmc.deplete.IndependentOperator` class requires a +:class:`openmc.Materials` object, a :class:`~openmc.deplete.MicroXS` object, +and a path to a depletion chain file:: -.. important:: + # load in the microscopic cross sections + materials = openmc.Materials() + ... - Make sure you set the correct parameter in the :class:`openmc.abc.Integrator` - class. Use the ``source_rates`` parameter when - ``normalization_mode == source-rate``, and use ``power`` or ``power_density`` - when ``normalization_mode == fission-q``. + micro_xs = openmc.deplete.MicroXS() + ... -.. warning:: + op = IndependentOperator(materials, micro_xs, chain_file) - The accuracy of results when using ``fission-q`` is entirely dependent on - your depletion chain. Make sure it has sufficient data to resolve the - dynamics of your particular scenario. +.. note:: -:class:`~openmc.deplete.IndependentOperator` class uses one-group microscopic cross sections to calculate reaction -rates. Users can generate one-group microscopic cross sections using the + The same statements from :ref:`coupled-depletion` about which + materials are depleted and the requirement for depletable materials to have + a specified volume also apply here. + +An alternate constructor, +:meth:`~openmc.deplete.IndependentOperator.from_nuclides`, accepts a volume and +dictionary of nuclide concentrations in place of the :class:`openmc.Materials` +object:: + + nuclides = {'U234': 8.92e18, + 'U235': 9.98e20, + 'U238': 2.22e22, + 'U236': 4.57e18, + 'O16': 4.64e22, + 'O17': 1.76e19} + volume = 0.5 + op = openmc.deplete.IndependentOperator.from_nuclides(volume, + nuclides, + micro_xs, + chain_file, + nuc_units='atom/cm3') + +A user can then define an integrator class as they would for a coupled +transport-depletion calculation and follow the same steps from there. + +.. note:: + + Ideally, one-group cross section data should be available for every + reaction in the depletion chain. If a nuclide that has a reaction + associated with it in the depletion chain is present in the `nuclides` + parameter but not the cross section data, that reaction will not be + simulated. + +Generating Microscopic Cross Sections +------------------------------------- + +Users can generate the one-group microscopic cross sections needed by +:class:`~openmc.deplete.IndependentOperator` using the :class:`~openmc.deplete.MicroXS` class:: import openmc - from openmc.deplete import MicroXS model = openmc.Model.from_xml() - micro_xs = MicroXS.from_model(model, model.materials[0]) + micro_xs = openmc.deplete.MicroXS.from_model(model, model.materials[0]) - micro_xs.to_csv(micro_xs_path) - -:class:`~openmc.deplete.MicroXS` also includes functions to read in cross -section data directly from a ``.csv`` file or from data arrays:: +The :meth:`~openmc.deplete.MicroXS.from_model()` method will produce a +:class:`~openmc.deplete.MicroXS` object with microscopic cross section data in +units of ``b``, which is what :class:`~openmc.deplete.IndependentOperator` +expects the units to be. The :class:`~openmc.deplete.MicroXS` class also includes functions to read in cross section data directly from a ``.csv`` file or from data arrays:: micro_xs = MicroXS.from_csv(micro_xs_path) @@ -230,32 +281,73 @@ section data directly from a ``.csv`` file or from data arrays:: [0.01, 0.5]]) micro_xs = MicroXS.from_array(nuclides, reactions, data) -:class:`~openmc.deplete.IndependentOperator` has two ways to initialize it: -the default constructor accepts an :class:`openmc.Materials` object and -one-group microscopic cross sections as a :class:`~openmc.deplete.MicroXS` -object, while the :meth:`~openmc.deplete.IndependentOperator.from_nuclides` -method accepts a volume and dictionary of nuclide concentrations in place of the -:class:`openmc.Materials` object in addition to the other parameters:: +.. important :: - # load in the microscopic cross sections - op = IndependentOperator(materials, micro_xs, chain_file) + Both :meth:`~openmc.deplete.MicroXS.from_csv()` and + :meth:`~openmc.deplete.MicroXS.from_array()` assume the cross section values + provided are in barns by defualt, but have no way of verifying this. Make + sure your cross sections are in the correct units before passing to a + :class:`~openmc.deplete.IndependentOperator` object. - # alternate construtor - nuclides = {'U234': 8.92e18, - 'U235': 9.98e20, - 'U238': 2.22e22, - 'U236': 4.57e18, - 'O16': 4.64e22, - 'O17': 1.76e19} - volume = 0.5 - op = IndependentOperator.from_nuclides(volume, nuclides, micro_xs, - chain_file, nuc_units='atom/cm3') +Caveats +------- -A user can then define an integrator class as they would for a coupled -transport-depletion calculation and follow the same steps from there. +Reaction Rate Normalization +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. note:: Ideally, one-group cross section data should be available for every - reaction in the depletion chain. If a nuclide that has a reaction - associated with it in the depletion chain is present in the `nuclides` - parameter but not the cross section data, that reaction will not be - simulated. +The :class:`~openmc.deplete.IndependentOperator` class supports two methods for +normalizing reaction rates: + +.. important:: + + Make sure you set the correct parameter in the :class:`openmc.abc.Integrator` + class. Use the ``source_rates`` parameter when + ``normalization_mode == source-rate``, and use ``power`` or ``power_density`` + when ``normalization_mode == fission-q``. + +1. ``soure-rate`` normalization, which assumes the ``source-rate`` provided by + the time integrator is a flux, and obtains the reaction rates by multiplying + the cross-sections by the ``source-rate``. +2. ``fission-q`` normalization, which assumes the ``source-rate`` provided by + the time integrator is a power, and obtains the reaction rates by computing a + value for the flux based on this power. The general equation for the flux is + + .. math:: + + \phi = \frac{P}{V \cdot \sum_i (Q_i \cdot \Sigma^f_i \cdot \rho_i)} + + where :math:`\sum_i` is the sum over all nuclides :math:`i`. This equation + makes the same assumptions and issues as discussed in + :ref:`energy-deposition`. Unfortunately, the proposed solution in that + section does not apply here since we are decoupled from transport code. + However, there is a method to converge to a more accurate value for flux by + using substeps during time integration. + `This paper `_ provides a + good discussion of this method. Hopefully such a method will be implemented + in OpenMC in the near future. + +.. warning:: + + The accuracy of results when using ``fission-q`` is entirely dependent on + your depletion chain. Make sure it has sufficient data to resolve the + dynamics of your particular scenario. + +Multiple Materials +~~~~~~~~~~~~~~~~~~ + +Running a depletion simulation with multiple materials using the +``source-rate`` normalization method treats each material as completely +separate with respect to reaction rates. This can be useful for running many +different cases of a particular scenario. However, running a depletion +simulation with multiple materials using the ``fission-q`` normalization method +treats each material as part of the same "reactor" due to how ``fission-q`` +normalization conglomerates energy values from each material to a single value. +This behavior may change in the future. + +Time integration +~~~~~~~~~~~~~~~~ + +The one-group microscopic cross sections passed to +:class:`openmc.deplete.IndependentOperator` are fixed values for the entire +depletion simulation. This implicit assumption may produce inaccurate results +for certain scenarios.