10. Depletion and Transmutation — OpenMC Documentation (2024)

OpenMC supports transport-coupled and transport-independent depletion, orburnup, calculations through the openmc.deplete Python module. OpenMCuses transmutation reaction rates to solve a set of transmutation equationsthat determine the evolution of nuclide densities within a material. Thenuclide densities predicted at some future time are then used to determineupdated reaction rates, and the process is repeated for as many timesteps asare requested.

The depletion module is designed such that the reaction rate solution (thetransport “operator”) is completely isolated from the solution of thetransmutation equations and the method used for advancing time.

openmc.deplete supports multiple time-integration methods for determiningmaterial compositions over time. Each method appears as a different class.For example, openmc.deplete.CECMIntegrator runs a depletion calculationusing the CE/CM algorithm (deplete over a timestep using the middle-of-stepreaction rates). An instance of TransportOperatoris passed to one of these Integrator classes along with the timesteps and powerlevel:

power = 1200.0e6 # wattstimesteps = [10.0, 10.0, 10.0] # daysopenmc.deplete.CECMIntegrator(op, timesteps, power, timestep_units='d').integrate()

The depletion problem is executed, and once it is done adepletion_results.h5 file is written. The results can be analyzed using theopenmc.deplete.Results class. This class has methods that allow foreasy retrieval of k-effective, nuclide concentrations, and reaction rates overtime:

results = openmc.deplete.Results("depletion_results.h5")time, keff = results.get_keff()

Note that the coupling between the reaction rate solver and the transmutationsolver happens in-memory rather than by reading/writing files on disk. OpenMChas two categories of transport operators for obtaining transmutation reactionrates.

10.1. Transport-coupled depletion

This category of operator solves the transport equation to obtain transmutationreaction rates. At present, the openmc.deplete module offers a singletransport-coupled operator, openmc.deplete.CoupledOperator (which usesthe OpenMC transport solver), but in principle additional transport-coupledoperator classes based on other transport codes could be implemented and nochanges to the depletion solver itself would be needed. Theopenmc.deplete.CoupledOperator class requires a Modelinstance containing material, geometry, and settings information:

model = openmc.Model()...op = openmc.deplete.CoupledOperator(model)

Any material that contains a fissionable nuclide is depleted by default, butthis can behavior can be changed with the Material.depletable attribute.

Important

The volume must be specified for each material that is depleted by settingthe Material.volume attribute. This is necessary in order tocalculate the proper normalization of tally results based on the source rate.

10.1.1. Fixed-Source Transmutation

When the power or power_density argument is used for one of theIntegrator classes, it is assumed that OpenMC is running in k-eigenvalue mode,and normalization of tally results is performed based on energy deposition. Itis also possible to run a fixed-source simulation and perform normalizationbased on a known source rate. First, as with all fixed-source calculations, weneed to set the run mode:

settings.run_mode = 'fixed source'

Additionally, all materials that you wish to deplete need to be marked as suchusing the Material.depletable attribute:

mat = openmc.Material()mat.depletable = True

When constructing the CoupledOperator, you shouldindicate that normalization of tally results will be done based on the sourcerate rather than a power or power density:

Finally, when creating a depletion integrator, use the source_rates argument:

integrator = openmc.deplete.PredictorIntegrator(op, timesteps, sources_rates=...)

As with the power argument, you can provide a different source rate for eachtimestep in the calculation. A zero source rate for a given timestep will resultin a decay-only step, where all reaction rates are zero.

10.1.2. Caveats

10.1.2.1. Energy Deposition

The default energy deposition mode, "fission-q", instructs theCoupledOperator to normalize reaction rates using theproduct of fission reaction rates and fission Q values taken from the depletionchain. This approach does not consider indirect contributions to energydeposition, such as neutron heating and energy from secondary photons. In doingthis, the energy deposited during a transport calculation will be lower thanexpected. This causes the reaction rates to be over-adjusted to hit theuser-specific power, or power density, leading to an over-depletion of burnablematerials.

There are some remedies. First, the fission Q values can be directly set in avariety of ways. This requires knowing what the total fission energy releaseshould be, including indirect components. Some examples are provided below:

# use a dictionary of fission_q valuesfission_q = {"U235": 202e+6} # energy in eV# create a Model objectmodel = openmc.Model(geometry, settings)# create a modified chain and write it to a new filechain = openmc.deplete.Chain.from_xml("chain.xml", fission_q)chain.export_to_xml("chain_mod_q.xml")op = openmc.deplete.CoupledOperator(model, "chain_mod_q.xml")# alternatively, pass the modified fission Q directly to the operatorop = openmc.deplete.CoupledOperator(model, "chain.xml", fission_q=fission_q)

A more complete way to model the energy deposition is to use the modifiedheating reactions described in Heating and Energy Deposition. These values can be usedto normalize reaction rates instead of using the fission reaction rates with:

op = openmc.deplete.CoupledOperator(model, "chain.xml", normalization_mode="energy-deposition")

These modified heating libraries can be generated by running the latest versionof openmc.data.IncidentNeutron.from_njoy(), and will eventually be bundledinto the distributed libraries.

10.1.2.2. Local Spectra and Repeated Materials

It is not uncommon to explicitly create a single burnable material across manylocations. From a pure transport perspective, there is nothing wrong withcreating a single 3.5 wt.% enriched fuel fuel_3, and placing that fuel inevery fuel pin in an assembly or even full core problem. This certainlyexpedites the model making process, but can pose issues with depletion. Underthis setup, openmc.deplete will deplete a single fuel_3 materialusing a single set of reaction rates, and produce a single new composition forthe next time step. This can be problematic if the same fuel_3 is used invery different regions of the problem.

As an example, consider a full-scale power reactor core with vacuum boundaryconditions, and with fuel pins solely composed of the same fuel_3 material.The fuel pins towards the center of the problem will surely experience a moreintense neutron flux and greater reaction rates than those towards the edge ofthe domain. This indicates that the fuel in the center should be at a moredepleted state than periphery pins, at least for the fist depletion step.However, without any other instructions, OpenMC will deplete fuel_3 as asingle material, and all of the fuel pins will have an identical composition atthe next transport step.

This can be countered by instructing the operator to treat repeated instancesof the same material as a unique material definition with:

op = openmc.deplete.CoupledOperator(model, chain_file, diff_burnable_mats=True)

For our example problem, this would deplete fuel on the outer region of theproblem with different reaction rates than those in the center. Materials willbe depleted corresponding to their local neutron spectra, and have uniquecompositions at each transport step. The volume of the original fuel_3material must represent the volume of all the fuel_3 in the problem.When creating the unique materials, this volume will be equally distributedacross all material instances.

Note

This will increase the total memory usage and run time due to an increasednumber of tallies and material definitions.

10.2. Transport-independent depletion

This category of operator uses multigroup microscopic cross sections along withmultigroup flux spectra to obtain transmutation reaction rates. The crosssections are pre-calculated, so there is no need for direct coupling between atransport-independent operator and a transport solver. The openmc.depletemodule offers a single transport-independent operator,IndependentOperator, and only one operator is neededsince, in theory, any transport code could calculate the multigroup microscopiccross sections. The IndependentOperator class has twoconstructors. The default constructor requires a openmc.Materialsinstance, a list of multigroup flux arrays, and a list ofMicroXS instances containing multigroup microscopiccross sections in units of barns. This might look like the following:

materials = openmc.Materials([m1, m2, m3])...# Assign fluxes (generated from any code)flux_m1 = numpy.array([...])flux_m2 = numpy.array([...])flux_m3 = numpy.array([...])fluxes = [flux_m1, flux_m2, flux_m3]# Assign microscopic cross sectionsmicro_m1 = openmc.deplete.MicroXS.from_csv('xs_m1.csv')micro_m2 = openmc.deplete.MicroXS.from_csv('xs_m2.csv')micro_m3 = openmc.deplete.MicroXS.from_csv('xs_m3.csv')micros = [micro_m1, micro_m2, micro_m3]# Create operatorop = openmc.deplete.IndependentOperator(materials, fluxes, micros)

For more details on the MicroXS class, including how touse OpenMC’s transport solver to generate microscopic cross sections and fluxesfor use with IndependentOperator, see Loading and Generating Microscopic Cross Sections.

Note

The same statements from Transport-coupled depletion about which materials aredepleted and the requirement for depletable materials to have a specifiedvolume also apply here.

An alternate constructor,from_nuclides(), accepts a volume anddictionary of nuclide concentrations in place of the openmc.Materialsinstance. Note that while the normal constructor allows multiple materials to bedepleted with a single operator, thefrom_nuclides() classmethod only worksfor a single material:

nuclides = {'U234': 8.92e18, 'U235': 9.98e20, 'U238': 2.22e22, 'U236': 4.57e18, 'O16': 4.64e22, 'O17': 1.76e19}volume = 0.5op = openmc.deplete.IndependentOperator.from_nuclides(volume, nuclides, flux, micro_xs, chain_file, nuc_units='atom/cm3')

A user can then define an integrator class as they would for a coupledtransport-depletion calculation and follow the same steps from there.

Note

Ideally, multigroup cross section data should be available for every reactionin the depletion chain. If cross section data is not present for a nuclide inthe depletion chain with at least one reaction, that reaction will not besimulated.

10.2.1. Loading and Generating Microscopic Cross Sections

As mentioned above, any transport code could be used to calculate multigroupmicroscopic cross sections and fluxes. The openmc.deplete module providesthe MicroXS class, which can either be instantiatedfrom pre-calculated cross sections in a .csv file or from data arraysdirectly:

micro_xs = MicroXS.from_csv(micro_xs_path)nuclides = ['U234', 'U235', 'U238']reactions = ['fission', '(n,gamma)']data = np.array([[0.1, 0.2], [0.3, 0.4], [0.01, 0.5]])micro_xs = MicroXS(data, nuclides, reactions)

Important

The cross section values are assumed to be in units of barns. Make sure yourcross sections are in the correct units before passing to aIndependentOperator object.

Additionally, a convenience function,get_microxs_and_flux(), can provide the needed fluxes andcross sections using OpenMC’s transport solver:

model = openmc.Model()...fluxes, micros = openmc.deplete.get_microxs_and_flux(model, materials)

If you are running get_microxs_and_flux() on a clusterwhere temporary files are created on a local filesystem that is not sharedacross nodes, you’ll need to set an environment variable pointing to a localdirectoy so that each MPI process knows where to store output files used tocalculate the microscopic cross sections. In order of priority, they areTMPDIR. TEMP, and TMP. Users interested in furtherdetails can read the documentation for the tempfile module.

10.2.2. Caveats

10.2.2.1. Reaction Rate Normalization

The IndependentOperator class supports two methods fornormalizing reaction rates:

Important

Make sure you set the correct parameter in the openmc.abc.Integratorclass. Use the source_rates parameter whennormalization_mode == source-rate, and use power or power_densitywhen normalization_mode == fission-q.

  1. source-rate normalization, which assumes the source_rate provided bythe time integrator is a flux, and obtains the reaction rates by multiplyingthe cross sections by the source-rate.

  2. fission-q normalization, which uses the power or power_densityprovided by the time integrator to obtain normalized reaction rates bycomputing a normalization factor as the ratio of the user-specified power tothe “observed” power based on fission reaction rates. The equation for thenormalization factor is

    (1)\[f = \frac{P}{\sum\limits_m \sum\limits_i \left(Q_i N_{i,m} \sum\limits_g\sigma^f_{i,g,m} \phi_{g,m} \right)}\]

    where \(P\) is the power, \(Q_i\) is the fission Q value for nuclide\(i\), \(\sigma_{i,g,m}^f\) is the microscopic fission cross sectionfor nuclide \(i\) in energy group \(g\) for material \(m\),\(\phi_{g,m}\) is the neutron flux in group \(g\) for material\(m\), and \(N_{i,m}\) is the number of atoms of nuclide \(i\)for material \(m\). Reaction rates are then multiplied by \(f\) sothat the total fission power matches \(P\). This equation makes the sameassumptions and issues as discussed in Energy Deposition.Unfortunately, the proposed solution in that section does not apply heresince we are decoupled from transport code. However, there is a method toconverge to a more accurate value for flux by using substeps during timeintegration. This paperprovides a good discussion of this method.

Warning

The accuracy of results when using fission-q is entirely dependent onyour depletion chain. Make sure it has sufficient data to resolve thedynamics of your particular scenario.

10.2.2.2. Multiple Materials

A transport-independent depletion simulation using source-rate normalizationwill calculate reaction rates for each material independently. This can beuseful for running many different cases of a particular scenario. Atransport-independent depletion simulation using fission-q normalizationwill sum the fission energy values across all materials into \(Q_i\) inEquation (1), and Equation (1)provides the normalization factor applied to reaction rates in each material.This can be useful for running a scenario with multiple depletable materialsthat are part of the same reactor. This behavior may change in the future.

10.2.2.3. Time integration

The values of the microscopic cross sections passed toopenmc.deplete.IndependentOperator are fixed for the entire depletionsimulation. This implicit assumption may produce inaccurate results for certainscenarios.

10.3. Transfer Rates

Transfer rates define removal or feed of nuclides to or from one or moredepletable materials. This can be useful to model continuous fuel reprocessing,online fission products separation, etc.

Transfer rates are defined by calling theadd_transfer_rate() method directly fromone of the Integrator classes:

...integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power)integrator.add_transfer_rate(...)

10.3.1. Defining transfer rates

The add_transfer_rate() method requires aMaterial instance (alternatively, a material id orthe name) as the depletable material from which nuclides are processed,a list of elements that share the same transfer rate, and a transfer rate itself.

Caution

Make sure you set the transfer rate value with the right sign.A positive transfer rate assumes removal, while a negative one assumes feed.

The transfer_rate_units argument specifies the units for the transfer rate.The default is 1/s, but ‘1/min’, ‘1/h’, ‘1/d’ and ‘1/a’ are also validoptions.

For example, to define continuous removal of xenon from one material with aremoval rate value of 0.1 s-1 (or a cycle time of 10 s), you’d use:

mat1 = openmc.Material(material_id=1, name='fuel')...integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power)# by openmc.Material objectintegrator.add_transfer_rate(mat1, ['Xe'], 0.1)# or by material idintegrator.add_transfer_rate(1, ['Xe'], 0.1)# or by material nameintegrator.add_transfer_rate('fuel', ['Xe'], 0.1)

Note that in this case the xenon isotopes that are removed will not be tracked.

10.3.2. Defining a destination material

To transfer elements from one depletable material to another, thedestination_material parameter needs to be passed to theadd_transfer_rate() method. For example,to transfer xenon from one material to another, you’d use:

...mat2 = openmc.Material(name='storage')...integrator.add_transfer_rate(mat1, ['Xe'], 0.1, destination_material=mat2)
10. Depletion and Transmutation — OpenMC Documentation (2024)
Top Articles
Latest Posts
Article information

Author: The Hon. Margery Christiansen

Last Updated:

Views: 5859

Rating: 5 / 5 (70 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: The Hon. Margery Christiansen

Birthday: 2000-07-07

Address: 5050 Breitenberg Knoll, New Robert, MI 45409

Phone: +2556892639372

Job: Investor Mining Engineer

Hobby: Sketching, Cosplaying, Glassblowing, Genealogy, Crocheting, Archery, Skateboarding

Introduction: My name is The Hon. Margery Christiansen, I am a bright, adorable, precious, inexpensive, gorgeous, comfortable, happy person who loves writing and wants to share my knowledge and understanding with you.