make changes from @paulromano's 3rd review

- syntax fixes and adjustments
- change name of FluxDepletionOperator to IndependentOperator
- flux_operator.py -> independent_operator.py
- new class, MicroXS, for creating (for now) one-group microscopic
  cross section DataFrames. This class takes the functionality that was
  previously in static functions in IndependentOperator
- Associated changes to the test suite and online docs
This commit is contained in:
yardasol 2022-07-28 15:55:07 -05:00
parent 4818a296d4
commit 3e827530ba
15 changed files with 437 additions and 319 deletions

View file

@ -49,9 +49,9 @@ specific to OpenMC are available using the following classes:
:template: mycallable.rst
Operator
FluxDepletionOperator
IndependentOperator
The :class:`Operator` and :class:`FluxDepletionOperator` classes must also have
The :class:`Operator` and :class:`IndependentOperator` classes must also have
some knowledge of how nuclides transmute and decay. This is handled by the
:class:`Chain`.
@ -134,6 +134,7 @@ data, such as number densities and reaction rates for each material.
:template: myclass.rst
AtomNumber
MicroXS
OperatorResult
ReactionRates
Results

View file

@ -97,7 +97,7 @@ Energy Deposition
-----------------
The default energy deposition mode, ``"fission-q"``, instructs the
:class:`openmc.deplete.Operator` to normalize reaction rates using the product
:class:`~openmc.deplete.Operator` to normalize reaction rates using the product
of fission reaction rates and fission Q values taken from the depletion chain.
This approach does not consider indirect contributions to energy deposition,
such as neutron heating and energy from secondary photons. In doing this, the
@ -113,7 +113,7 @@ should be, including indirect components. Some examples are provided below::
fission_q = {"U235": 202e+6} # energy in eV
# create a Model object
model = openmc.Model(geometry, settings)
model = openmc.Model(geometry, settings)
# create a modified chain and write it to a new file
chain = openmc.deplete.Chain.from_xml("chain.xml", fission_q)
@ -133,7 +133,7 @@ to normalize reaction rates instead of using the fission reaction rates with::
normalization_mode="energy-deposition")
These modified heating libraries can be generated by running the latest version
of :meth:`openmc.data.IncidentNeutron.from_njoy`, and will eventually be bundled
of :meth:`openmc.data.IncidentNeutron.from_njoy()`, and will eventually be bundled
into the distributed libraries.
Local Spectra and Repeated Materials
@ -188,31 +188,57 @@ Transport-independent depletion
possible and likely in the near future.
OpenMC supports running depletion calculations independent of the OpenMC
transport solver using the :class:`~openmc.deplete.FluxDepletionOperator` class.
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).
.. 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``.
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``.
.. 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.
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.
This class has two ways to initialize it: the default constructor accepts an
:class:`openmc.Materials` object and one-group microscopic
cross sections as a :class:`pandas.DataFrame`, while the ``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.
The class includes helper functions to construct the dataframe from a csv file
or from data arrays::
: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
: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.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::
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.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::
...
# load in the microscopic cross sections
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(micro_xs_path)
flux = 1.16e15
op = FluxDepletionOperator(materials, micro_xs, chain_file)
op = IndependentOperator(materials, micro_xs, chain_file)
# alternate construtor
nuclides = {'U234': 8.92e18,
@ -222,8 +248,8 @@ or from data arrays::
'O16': 4.64e22,
'O17': 1.76e19}
volume = 0.5
op = FluxDepletionOperator.from_nuclides(volume, nuclides, 'atom/cm3',
micro_xs, flux, chain_file)
op = 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.

View file

@ -9,7 +9,8 @@ from .nuclide import *
from .chain import *
from .openmc_operator import *
from .operator import *
from .flux_operator import *
from .independent_operator import *
from .microxs import *
from .reaction_rates import *
from .atom_number import *
from .stepresult import *

View file

@ -526,7 +526,7 @@ class Integrator(ABC):
initial heavy metal inventory to get total power if ``power``
is not specified.
source_rates : float or iterable of float, optional
Source rate in [neutron/sec] or neutron flux in [neut/s-cm^2] for each
Source rate in [neutron/sec] or neutron flux in [neut/cm^2-s] for each
interval in :attr:`timesteps`
.. versionadded:: 0.12.1
@ -864,7 +864,7 @@ class SIIntegrator(Integrator):
initial heavy metal inventory to get total power if ``power``
is not specified.
source_rates : float or iterable of float, optional
Source rate in [neutron/sec] or neutron flux in [neut/s-cm^2] for each
Source rate in [neutron/sec] or neutron flux in [neutron/s-cm^2] for each
interval in :attr:`timesteps`
.. versionadded:: 0.12.1

View file

@ -1,7 +1,7 @@
"""Pure depletion operator
"""Independent depletion operator
This module implements a pure depletion operator that user-provided one-group
cross sections.
This module implements a depletion operator that runs independently of any
transport solver by using user-provided one-group cross sections.
"""
@ -11,110 +11,33 @@ from warnings import warn
from itertools import product
import numpy as np
import pandas as pd
from uncertainties import ufloat
import openmc
from openmc.checkvalue import check_type, check_value, check_iterable_type
from openmc.checkvalue import check_type
from openmc.mpi import comm
from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS
from .abc import ReactionRateHelper, OperatorResult
from .chain import REACTIONS
from .openmc_operator import OpenMCOperator, _distribute
from .microxs import MicroXS
from .results import Results
from .helpers import ChainFissionHelper, ConstantFissionYieldHelper, SourceRateHelper
_valid_rxns = list(REACTIONS)
_valid_rxns.append('fission')
def generate_1g_cross_sections(model,
reaction_domain,
reactions=['(n,gamma)',
'(n,2n)',
'(n,p)',
'(n,a)',
'(n,3n)',
'(n,4n)',
'fission'],
energy_bounds=(0, 20e6),
write_to_csv=False,
filename='micro_xs.csv'):
"""Helper function to generate a one-group cross-section dataframe using
OpenMC. Note that the ``openmc`` executable must be compiled.
Parameters
----------
model : openmc.model.Model
OpenMC model object. Must contain geometry, materials, and settings.
reaction_domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh
Domain in which to tally reaction rates.
reactions : list of str, optional
Reaction names to tally
energy_bound : 2-tuple of float, optional
Bounds for the energy group.
write_to_csv : bool, optional
Option to write the DataFrame to a `.csv` file.
filename : str
Name for csv file. Only applicable if ``write_to_csv == True``
Returns
-------
None or pandas.DataFrame
"""
groups = EnergyGroups(energy_bounds)
# Set up the reaction tallies
original_tallies = model.tallies
tallies = openmc.Tallies()
xs = {}
for rxn in reactions:
if rxn == 'fission':
xs[rxn] = FissionXS(domain=reaction_domain, groups=groups, by_nuclide=True)
else:
xs[rxn] = ArbitraryXS(rxn, domain=reaction_domain, groups=groups, by_nuclide=True)
tallies += xs[rxn].tallies.values()
model.tallies = tallies
statepoint_path = model.run()
# Revert to the original tallies
model.tallies = old_tallies
with openmc.StatePoint(statepoint_path) as sp:
for rxn in xs:
xs[rxn].load_from_statepoint(sp)
# Build the DataFrame
micro_xs = pd.DataFrame()
for rxn in xs:
df = xs[rxn].get_pandas_dataframe(xs_type='micro')
df.index = df['nuclide']
df.drop(['nuclide', xs[rxn].domain_type, 'group in', 'std. dev.'], axis=1, inplace=True)
df.rename({'mean':rxn}, axis=1, inplace=True)
micro_xs = pd.concat([micro_xs, df], axis=1)
if write_to_csv:
micro_xs.to_csv(filename)
return micro_xs
class FluxDepletionOperator(OpenMCOperator):
"""Depletion operator that uses one-group
cross sections to calculate reaction rates.
class IndependentOperator(OpenMCOperator):
"""Depletion operator that uses one-group cross sections to calculate
reaction rates.
Instances of this class can be used to perform depletion using one-group
cross sections and constant flux or constant power. Normally, a user needn't call methods of
this class directly. Instead, an instance of this class is passed to an
integrator class, such as :class:`openmc.deplete.CECMIntegrator`.
cross sections and constant flux or constant power. Normally, a user needn't
call methods of this class directly. Instead, an instance of this class is
passed to an integrator class, such as
:class:`openmc.deplete.CECMIntegrator`.
Parameters
----------
materials : openmc.Materials
Materials to deplete.
micro_xs : pandas.DataFrame
DataFrame with nuclides names as index and microscopic cross section
data in the columns. Cross section units are [cm^-2].
micro_xs : MicroXS
One-group microscopic cross sections in [b] .
chain_file : str
Path to the depletion chain XML file.
keff : 2-tuple of float, optional
@ -134,23 +57,23 @@ class FluxDepletionOperator(OpenMCOperator):
if ``"normalization_mode" == "fission-q"``.
reduce_chain : bool, optional
If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the
depletion chain up to ``reduce_chain_level``. Default is False.
depletion chain up to ``reduce_chain_level``.
reduce_chain_level : int, optional
Depth of the search when reducing the depletion chain. Only used
if ``reduce_chain`` evaluates to true. The default value of
``None`` implies no limit on the depth.
fission_yield_opts : dict of str to option, optional
Optional arguments to pass to the `FissionYieldHelper`. Will be
Optional arguments to pass to the
:class:`openmc.deplete.helpers.FissionYieldHelper` object. Will be
passed directly on to the helper. Passing a value of None will use
the defaults for the associated helper.
Attributes
----------
materials : openmc.Materials
All materials present in the model
cross_sections : pandas.DataFrame
Object containing one-group cross-sections.
cross_sections : MicroXS
Object containing one-group cross-sections in [cm^2].
dilute_initial : float
Initial atom density [atoms/cm^3] to add for nuclides that
are zero in initial condition to ensure they exist in the decay
@ -193,7 +116,7 @@ class FluxDepletionOperator(OpenMCOperator):
fission_yield_opts=None):
# Validate micro-xs parameters
check_type('materials', materials, openmc.Materials)
check_type('micro_xs', micro_xs, pd.DataFrame)
check_type('micro_xs', micro_xs, MicroXS)
if keff is not None:
check_type('keff', keff, tuple, float)
keff = ufloat(*keff)
@ -205,9 +128,11 @@ class FluxDepletionOperator(OpenMCOperator):
helper_kwargs = {'normalization_mode': normalization_mode,
'fission_yield_opts': fission_yield_opts}
cross_sections = micro_xs * 1e-24
cross_sections._units = 'cm^2'
super().__init__(
materials,
micro_xs,
cross_sections,
chain_file,
prev_results,
fission_q=fission_q,
@ -216,9 +141,10 @@ class FluxDepletionOperator(OpenMCOperator):
reduce_chain_level=reduce_chain_level)
@classmethod
def from_nuclides(cls, volume, nuclides, nuc_units,
def from_nuclides(cls, volume, nuclides,
micro_xs,
chain_file,
nuc_units='atom/b-cm',
keff=None,
normalization_mode='source-rate',
fission_q=None,
@ -234,13 +160,12 @@ class FluxDepletionOperator(OpenMCOperator):
nuclides : dict of str to float
Dictionary with nuclide names as keys and nuclide concentrations as
values.
nuc_units : {'atom/cm3', 'atom/b-cm'}
Units for nuclide concentration.
micro_xs : pandas.DataFrame
DataFrame with nuclides names as index and microscopic cross section
data in the columns. Cross section units are [cm^-2].
micro_xs : MicroXS
One-group microscopic cross sections.
chain_file : str
Path to the depletion chain XML file.
nuc_units : {'atom/cm3', 'atom/b-cm'}
Units for nuclide concentration.
keff : 2-tuple of float, optional
keff eigenvalue and uncertainty from transport calculation.
Default is None.
@ -325,17 +250,16 @@ class FluxDepletionOperator(OpenMCOperator):
"""Finds nuclides with cross section data"""
return set(cross_sections.index)
class _FluxDepletionNormalizationHelper(ChainFissionHelper):
"""Class for calculating one-group flux
based on a power.
class _IndependentNormalizationHelper(ChainFissionHelper):
"""Class for calculating one-group flux based on a power.
flux = Power / X, where X = volume * sum_i(Q_i * fission_micro_xs_i * density_i)
Parameters
----------
op : openmc.deplete.FluxDepletionOperator
Reference to the object encapsulate _FluxDepletionNormalizationHelper.
We pass this so we don't have to duplicate the ``number`` object.
op : openmc.deplete.IndependentOperator
Reference to the object encapsulating _IndependentNormalizationHelper.
We pass this so we don't have to duplicate :attr:`IndependentOperator.number`.
"""
@ -367,7 +291,7 @@ class FluxDepletionOperator(OpenMCOperator):
super().update(fission_rates)
class _FluxDepletionRateHelper(ReactionRateHelper):
class _IndependentRateHelper(ReactionRateHelper):
"""Class for generating one-group reaction rates with flux and
one-group cross sections.
@ -378,9 +302,9 @@ class FluxDepletionOperator(OpenMCOperator):
Parameters
----------
op : openmc.deplete.FluxDepletionOperator
Reference to the object encapsulate _FluxDepletionRateHelper.
We pass this so we don't have to duplicate the ``number`` object.
op : openmc.deplete.IndependentOperator
Reference to the object encapsulate _IndependentRateHelper.
We pass this so we don't have to duplicate the :attr:`IndependentOperator.number` object.
Attributes
@ -441,9 +365,9 @@ class FluxDepletionOperator(OpenMCOperator):
normalization_mode = helper_kwargs['normalization_mode']
fission_yield_opts = helper_kwargs['fission_yield_opts']
self._rate_helper = self._FluxDepletionRateHelper(self)
self._rate_helper = self._IndependentRateHelper(self)
if normalization_mode == "fission-q":
self._normalization_helper = self._FluxDepletionNormalizationHelper(self)
self._normalization_helper = self._IndependentNormalizationHelper(self)
else:
self._normalization_helper = SourceRateHelper()
@ -472,7 +396,7 @@ class FluxDepletionOperator(OpenMCOperator):
vec : list of numpy.ndarray
Total atoms to be used in function.
source_rate : float
Power in [W] or flux in [neut/s-cm^2]
Power in [W] or flux in [neutron/cm^2-s]
Returns
-------
@ -523,79 +447,3 @@ class FluxDepletionOperator(OpenMCOperator):
' atom/b-cm)')
number_i[mat, nuc] = 0.0
@staticmethod
def create_micro_xs_from_data_array(nuclides, reactions, data):
"""
Creates a ``micro_xs`` parameter from a dictionary.
Parameters
----------
nuclides : list of str
List of nuclide symbols for that have data for at least one
reaction.
reactions : list of str
List of reactions. All reactions must match those in ``chain.REACTIONS``
data : ndarray of floats
Array containing one-group microscopic cross section values, in
[barn], for each nuclide and reaction.
Returns
-------
micro_xs : pandas.DataFrame
A DataFrame object correctly formatted for use in ``FluxOperator``.
Cross section data is in [cm^2]
"""
# Validate inputs
if data.shape != (len(nuclides), len(reactions)):
raise ValueError(
f'Nuclides list of length {len(nuclides)} and '
f'reactions array of length {len(reactions)} do not '
f'match dimensions of data array of shape {data.shape}')
FluxDepletionOperator._validate_micro_xs_inputs(
nuclides, reactions, data)
# Convert to cm^2
data *= 1e-24
return pd.DataFrame(index=nuclides, columns=reactions, data=data)
@staticmethod
def create_micro_xs_from_csv(csv_file, units='barn'):
"""
Create the ``micro_xs`` parameter from a ``.csv`` file.
Parameters
----------
csv_file : str
Relative path to csv-file containing microscopic cross section
data. Cross section values should be in [barn].
Returns
-------
micro_xs : pandas.DataFrame
A DataFrame object correctly formatted for use in ``FluxOperator``.
Cross section data is in [cm^2]
"""
micro_xs = pd.read_csv(csv_file, index_col=0)
FluxDepletionOperator._validate_micro_xs_inputs(list(micro_xs.index),
list(micro_xs.columns),
micro_xs.to_numpy())
micro_xs *= 1e-24
return micro_xs
# Convenience function for the micro_xs static methods
@staticmethod
def _validate_micro_xs_inputs(nuclides, reactions, data):
check_iterable_type('nuclides', nuclides, str)
check_iterable_type('reactions', reactions, str)
check_type('data', data, np.ndarray, expected_iter_type=float)
for reaction in reactions:
check_value('reactions', reaction, _valid_rxns)

178
openmc/deplete/microxs.py Normal file
View file

@ -0,0 +1,178 @@
"""MicroXS module
A pandas.DataFrame storing microscopic cross section data with
nuclides names as row indices and reaction names as column indices.
"""
import tempfile
from pathlib import Path
from os import chdir
from pandas import DataFrame, read_csv, concat
from numpy import ndarray
from openmc.checkvalue import check_type, check_value, check_iterable_type
from openmc.mgxs import EnergyGroups, ArbitraryXS, FissionXS
from openmc import Tallies, StatePoint
from .chain import REACTIONS
_valid_rxns = list(REACTIONS)
_valid_rxns.append('fission')
class MicroXS(DataFrame):
"""Stores microscopic cross section data for use in
independent depletion.
"""
@classmethod
def from_model(cls,
model,
reaction_domain,
reactions=['(n,gamma)',
'(n,2n)',
'(n,p)',
'(n,a)',
'(n,3n)',
'(n,4n)',
'fission'],
energy_bounds=(0, 20e6)):
"""Generate a one-group cross-section dataframe using
OpenMC. Note that the ``openmc`` executable must be compiled.
Parameters
----------
model : openmc.Model
OpenMC model object. Must contain geometry, materials, and settings.
reaction_domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh
Domain in which to tally reaction rates.
reactions : list of str, optional
Reaction names to tally
energy_bound : 2-tuple of float, optional
Bounds for the energy group.
Returns
-------
MicroXS, cross section data in [b]
"""
groups = EnergyGroups(energy_bounds)
# Set up the reaction tallies
original_tallies = model.tallies
tallies = Tallies()
xs = {}
for rxn in reactions:
if rxn == 'fission':
xs[rxn] = FissionXS(domain=reaction_domain, groups=groups, by_nuclide=True)
else:
xs[rxn] = ArbitraryXS(rxn, domain=reaction_domain, groups=groups, by_nuclide=True)
tallies += xs[rxn].tallies.values()
model.tallies = tallies
# create temporary run
original_dir = Path.cwd()
temp_dir = tempfile.TemporaryDirectory()
chdir(tempfile.gettempdir())
statepoint_path = model.run()
chdir(original_dir)
with StatePoint(statepoint_path) as sp:
for rxn in xs:
xs[rxn].load_from_statepoint(sp)
# Build the DataFrame
micro_xs = cls()
for rxn in xs:
df = xs[rxn].get_pandas_dataframe(xs_type='micro')
df.index = df['nuclide']
df.drop(['nuclide', xs[rxn].domain_type, 'group in', 'std. dev.'], axis=1, inplace=True)
df.rename({'mean':rxn}, axis=1, inplace=True)
micro_xs = concat([micro_xs, df], axis=1)
micro_xs._units = 'b'
# Revert to the original tallies
model.tallies = original_tallies
return micro_xs
@classmethod
def from_array(cls, nuclides, reactions, data, units='b'):
"""
Creates a ``MicroXS`` object from arrays.
Parameters
----------
nuclides : list of str
List of nuclide symbols for that have data for at least one
reaction.
reactions : list of str
List of reactions. All reactions must match those in
:data:`openmc.deplete.chain.REACTIONS`
data : ndarray of floats
Array containing one-group microscopic cross section values for
each nuclide and reaction
units : {'b', 'cm^2'}
Units of the cross section values in ``data``
Returns
-------
MicroXS
"""
# Validate inputs
if data.shape != (len(nuclides), len(reactions)):
raise ValueError(
f'Nuclides list of length {len(nuclides)} and '
f'reactions array of length {len(reactions)} do not '
f'match dimensions of data array of shape {data.shape}')
cls._validate_micro_xs_inputs(
nuclides, reactions, data)
micro_xs = cls(index=nuclides, columns=reactions, data=data)
micro_xs._units = units
return micro_xs
@classmethod
def from_csv(cls, csv_file, units='b', **kwargs):
"""
Load a ``MicroXS`` object from a ``.csv`` file.
Parameters
----------
csv_file : str
Relative path to csv-file containing microscopic cross section
data.
units : {'b', 'cm^2'}
Units of the cross section values in the ``.csv`` file
**kwargs : dict
Keyword arguments to pass to :func:`pandas.read_csv()`.
Returns
-------
MicroXS
"""
if 'float_precision' not in kwargs:
kwargs['float_precision'] = 'round_trip'
micro_xs = cls(read_csv(csv_file, index_col=0, **kwargs))
cls._validate_micro_xs_inputs(list(micro_xs.index),
list(micro_xs.columns),
micro_xs.to_numpy())
micro_xs._units = units
return micro_xs
@staticmethod
def _validate_micro_xs_inputs(nuclides, reactions, data):
check_iterable_type('nuclides', nuclides, str)
check_iterable_type('reactions', reactions, str)
check_type('data', data, ndarray, expected_iter_type=float)
for reaction in reactions:
check_value('reactions', reaction, _valid_rxns)

View file

@ -1,6 +1,6 @@
"""OpenMC transport operator
This module implements functions used by both OpenMC transport operators as well as pure depletion operators.
This module implements functions shared by both OpenMC transport operators as well as indepenedent depletion operators.
"""
@ -75,8 +75,8 @@ class OpenMCOperator(TransportOperator):
helper_kwargs : dict
Keyword arguments for helper classes
reduce_chain : bool, optional
If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the
depletion chain up to ``reduce_chain_level``. Default is False.
If True, use :meth:`openmc.deplete.Chain.reduce()` to reduce the
depletion chain up to ``reduce_chain_level``.
reduce_chain_level : int, optional
Depth of the search when reducing the depletion chain. Only used
if ``reduce_chain`` evaluates to true. The default value of
@ -87,7 +87,7 @@ class OpenMCOperator(TransportOperator):
----------
materials : openmc.Materials
All materials present in the model
cross_sections : str or pandas.DataFrame
cross_sections : str or MicroXS
Path to continuous energy cross section library, or object
containing one-group cross-sections.
dilute_initial : float

View file

@ -131,7 +131,7 @@ class Operator(OpenMCOperator):
.. versionadded:: 0.12.1
reduce_chain : bool, optional
If True, use :meth:`openmc.deplete.Chain.reduce` to reduce the
depletion chain up to ``reduce_chain_level``. Default is False.
depletion chain up to ``reduce_chain_level``.
.. versionadded:: 0.12
reduce_chain_level : int, optional
@ -226,7 +226,7 @@ class Operator(OpenMCOperator):
self.cleanup_when_done = True
if reaction_rate_opts is None:
reaction_rate_opts = {}
reaction_rate_opts = {}
if fission_yield_opts is None:
fission_yield_opts = {}
helper_kwargs = {

View file

@ -1,19 +1,12 @@
""" Transport-free depletion test suite """
from math import floor
import shutil
from pathlib import Path
from difflib import unified_diff
import numpy as np
import pytest
import openmc
from openmc.data import JOULE_PER_EV
import openmc.deplete
from openmc.deplete import FluxDepletionOperator
from tests.regression_tests import config
from openmc.deplete import IndependentOperator, MicroXS
@pytest.fixture(scope="module")
def fuel():
@ -27,7 +20,6 @@ def fuel():
return fuel
@pytest.mark.parametrize("multiproc, from_nuclides, normalization_mode, power, flux", [
(True, True,'source-rate', None, 1164719970082145.0),
(False, True, 'source-rate', None, 1164719970082145.0),
@ -46,7 +38,7 @@ def test_no_transport_from_nuclides(run_in_tmpdir, fuel, multiproc, from_nuclide
"""
# Create operator
micro_xs_file = Path(__file__).parents[2] / 'micro_xs_simple.csv'
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(micro_xs_file)
micro_xs = MicroXS.from_csv(micro_xs_file)
chain_file = Path(__file__).parents[2] / 'chain_simple.xml'
if from_nuclides:
@ -54,11 +46,11 @@ def test_no_transport_from_nuclides(run_in_tmpdir, fuel, multiproc, from_nuclide
for nuc, dens in fuel.get_nuclide_atom_densities().items():
nuclides[nuc] = dens
op = FluxDepletionOperator.from_nuclides(
fuel.volume, nuclides,'atom/b-cm', micro_xs, chain_file, normalization_mode=normalization_mode)
op = IndependentOperator.from_nuclides(
fuel.volume, nuclides, micro_xs, chain_file, normalization_mode=normalization_mode)
else:
op = FluxDepletionOperator(openmc.Materials([fuel]), micro_xs, chain_file, normalization_mode=normalization_mode)
op = IndependentOperator(openmc.Materials([fuel]), micro_xs, chain_file, normalization_mode=normalization_mode)
# Power and timesteps
dt = [30] # single step

View file

@ -0,0 +1,52 @@
"""Test one-group cross section generation"""
import numpy as np
import pytest
import openmc
from openmc.deplete import MicroXS
@pytest.fixture(scope="module")
def model():
fuel = openmc.Material(name="uo2")
fuel.add_element("U", 1, percent_type="ao", enrichment=4.25)
fuel.add_element("O", 2)
fuel.set_density("g/cc", 10.4)
clad = openmc.Material(name="clad")
clad.add_element("Zr", 1)
clad.set_density("g/cc", 6)
water = openmc.Material(name="water")
water.add_element("O", 1)
water.add_element("H", 2)
water.set_density("g/cc", 1.0)
water.add_s_alpha_beta("c_H_in_H2O")
radii = [0.42, 0.45]
fuel.volume = np.pi * radii[0] ** 2
clad.volume = np.pi * (radii[1]**2 - radii[0]**2)
water.volume = 1.24**2 - (np.pi * radii[1]**2)
materials = openmc.Materials([fuel, clad, water])
pin_surfaces = [openmc.ZCylinder(r=r) for r in radii]
pin_univ = openmc.model.pin(pin_surfaces, materials)
bound_box = openmc.rectangular_prism(1.24, 1.24, boundary_type="reflective")
root_cell = openmc.Cell(fill=pin_univ, region=bound_box)
geometry = openmc.Geometry([root_cell])
settings = openmc.Settings()
settings.particles = 1000
settings.inactive = 10
settings.batches = 50
return openmc.Model(geometry, materials, settings)
def test_from_model(model):
ref_xs = MicroXS.from_csv('test_reference.csv')
test_xs = MicroXS.from_model(model, model.materials[0])
assert ref_xs._units == test_xs._units
np.testing.assert_allclose(test_xs, ref_xs, rtol=1e-11)

View file

@ -0,0 +1,7 @@
nuclide,"(n,gamma)","(n,2n)","(n,p)","(n,a)","(n,3n)","(n,4n)",fission
U234,23.518634203050674,0.0008255330840536984,0.0,0.0,9.404554397521455e-07,0.0,0.49531930067650964
U235,10.621118186344795,0.004359401013759254,0.0,0.0,7.2974901306692395e-06,0.0,49.10955932965902
U238,0.8652742788116055,0.005661917442209096,0.0,0.0,4.92273921631416e-05,0.0,0.10579281644765708
U236,9.095623870006163,0.0024373322002926834,0.0,0.0,1.966889146690413e-05,0.0,0.3231539233923791
O16,7.511380881289377e-05,0.0,1.3764104470470622e-05,0.002862620940027927,0.0,0.0,0.0
O17,0.00041221042693945085,1.9826700699084533e-05,7.764409141772239e-06,0.05938517754333328,0.0,0.0,0.0
1 nuclide (n,gamma) (n,2n) (n,p) (n,a) (n,3n) (n,4n) fission
2 U234 23.518634203050674 0.0008255330840536984 0.0 0.0 9.404554397521455e-07 0.0 0.49531930067650964
3 U235 10.621118186344795 0.004359401013759254 0.0 0.0 7.2974901306692395e-06 0.0 49.10955932965902
4 U238 0.8652742788116055 0.005661917442209096 0.0 0.0 4.92273921631416e-05 0.0 0.10579281644765708
5 U236 9.095623870006163 0.0024373322002926834 0.0 0.0 1.966889146690413e-05 0.0 0.3231539233923791
6 O16 7.511380881289377e-05 0.0 1.3764104470470622e-05 0.002862620940027927 0.0 0.0 0.0
7 O17 0.00041221042693945085 1.9826700699084533e-05 7.764409141772239e-06 0.05938517754333328 0.0 0.0 0.0

View file

@ -1,83 +0,0 @@
"""Basic unit tests for openmc.deplete.FluxDepletionOperator instantiation
Modifies and resets environment variable OPENMC_CROSS_SECTIONS
to a custom file with new depletion_chain node
"""
from pathlib import Path
import pytest
from openmc.deplete.flux_operator import FluxDepletionOperator
from openmc import Material, Materials
import pandas as pd
import numpy as np
CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml"
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
def test_create_micro_xs_from_data_array():
nuclides = [
'U234',
'U235',
'U238',
'U236',
'O16',
'O17',
'I135',
'Xe135',
'Xe136',
'Cs135',
'Gd157',
'Gd156']
reactions = ['fission', '(n,gamma)']
# These values are placeholders and are not at all
# physically meaningful.
data = np.array([[0.1, 0.],
[0.1, 0.],
[0.9, 0.],
[0.4, 0.],
[0., 0.],
[0., 0.],
[0., 0.1],
[0., 0.9],
[0., 0.],
[0., 0.],
[0., 0.1],
[0., 0.1]])
FluxDepletionOperator.create_micro_xs_from_data_array(
nuclides, reactions, data)
with pytest.raises(ValueError, match=r'Nuclides list of length \d* and '
r'reactions array of length \d* do not '
r'match dimensions of data array of shape \(\d*\,d*\)'):
FluxDepletionOperator.create_micro_xs_from_data_array(
nuclides, reactions, data[:, 0])
def test_create_micro_xs_from_csv():
FluxDepletionOperator.create_micro_xs_from_csv(ONE_GROUP_XS)
def test_operator_init():
"""The test uses a temporary dummy chain. This file will be removed
at the end of the test, and only contains a depletion_chain node."""
volume = 1
nuclides = {'U234': 8.922411359424315e+18,
'U235': 9.98240191860822e+20,
'U238': 2.2192386373095893e+22,
'U236': 4.5724195495061115e+18,
'O16': 4.639065406771322e+22,
'O17': 1.7588724018066158e+19}
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(ONE_GROUP_XS)
nuclide_flux_operator = FluxDepletionOperator.from_nuclides(
volume, nuclides, 'atom/cm3', micro_xs, CHAIN_PATH)
fuel = Material(name="uo2")
fuel.add_element("U", 1, percent_type="ao", enrichment=4.25)
fuel.add_element("O", 2)
fuel.set_density("g/cc", 10.4)
fuel.depletable=True
fuel.volume = 1
materials = Materials([fuel])
nuclide_flux_operator = FluxDepletionOperator(materials, micro_xs, CHAIN_PATH)

View file

@ -0,0 +1,36 @@
"""Basic unit tests for openmc.deplete.IndependentOperator instantiation
"""
from pathlib import Path
import pytest
from openmc.deplete import IndependentOperator, MicroXS
from openmc import Material, Materials
import numpy as np
CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml"
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
def test_operator_init():
"""The test uses a temporary dummy chain. This file will be removed
at the end of the test, and only contains a depletion_chain node."""
volume = 1
nuclides = {'U234': 8.922411359424315e+18,
'U235': 9.98240191860822e+20,
'U238': 2.2192386373095893e+22,
'U236': 4.5724195495061115e+18,
'O16': 4.639065406771322e+22,
'O17': 1.7588724018066158e+19}
micro_xs = MicroXS.from_csv(ONE_GROUP_XS)
IndependentOperator.from_nuclides(
volume, nuclides, micro_xs, CHAIN_PATH, nuc_units='atom/cm3')
fuel = Material(name="uo2")
fuel.add_element("U", 1, percent_type="ao", enrichment=4.25)
fuel.add_element("O", 2)
fuel.set_density("g/cc", 10.4)
fuel.depletable=True
fuel.volume = 1
materials = Materials([fuel])
IndependentOperator(materials, micro_xs, CHAIN_PATH)

View file

@ -0,0 +1,60 @@
"""Basic unit tests for openmc.deplete.IndependentOperator instantiation
Modifies and resets environment variable OPENMC_CROSS_SECTIONS
to a custom file with new depletion_chain node
"""
from os import remove
from pathlib import Path
import pytest
from openmc.deplete import MicroXS
import numpy as np
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
def test_from_array():
nuclides = [
'U234',
'U235',
'U238',
'U236',
'O16',
'O17',
'I135',
'Xe135',
'Xe136',
'Cs135',
'Gd157',
'Gd156']
reactions = ['fission', '(n,gamma)']
# These values are placeholders and are not at all
# physically meaningful.
data = np.array([[0.1, 0.],
[0.1, 0.],
[0.9, 0.],
[0.4, 0.],
[0., 0.],
[0., 0.],
[0., 0.1],
[0., 0.9],
[0., 0.],
[0., 0.],
[0., 0.1],
[0., 0.1]])
MicroXS.from_array(nuclides, reactions, data)
with pytest.raises(ValueError, match=r'Nuclides list of length \d* and '
r'reactions array of length \d* do not '
r'match dimensions of data array of shape \(\d*\,d*\)'):
MicroXS.from_array(nuclides, reactions, data[:, 0])
def test_csv():
ref_xs = MicroXS.from_csv(ONE_GROUP_XS)
ref_xs.to_csv('temp_xs.csv')
temp_xs = MicroXS.from_csv('temp_xs.csv')
assert ref_xs._units == temp_xs._units
assert np.all(ref_xs == temp_xs)
remove('temp_xs.csv')