mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 06:05:58 -04:00
Address paulromano's comments
- various syntax adjustments and cleanups - remove unneeded import statements - add more information to new section in user's guide - typo fixes - remove dilute_initial - move _validate_micro_xs_inputs back into the class as a static method - update tests to reflect changes
This commit is contained in:
parent
af3d460665
commit
5f8e1ff9d9
4 changed files with 123 additions and 66 deletions
|
|
@ -181,25 +181,25 @@ Transport-independent depletion
|
|||
|
||||
.. note::
|
||||
|
||||
This is a brand-new feature and is under heavy development. API changes are
|
||||
This feature is still under heavy development. API changes are
|
||||
possible and likely in the near future.
|
||||
|
||||
OpenMC also supports transport-independent depletion calculations using the
|
||||
:class:`FluxDepletionOperator` class. Rather than taking a
|
||||
:class:`openmc.model.Model` object, this class accepts a volume,
|
||||
OpenMC supports running depletion calculations independent of the OpenMC
|
||||
transport solver using the :class:`FluxDepletionOperator` class. Rather than
|
||||
taking a :class:`openmc.model.Model` object, this class accepts a volume,
|
||||
a dictionary of nuclide concentrations, a flux spectra, and one-group
|
||||
microscopic cross sections as a pandas dataframe. The class includes
|
||||
helper functions to constructe the dataframe from a csv file or from
|
||||
microscopic cross sections as a :class:`pandas.DataFrame`. The class includes
|
||||
helper functions to construct the dataframe from a csv file or from
|
||||
data arrays::
|
||||
|
||||
...
|
||||
micro_xs = FluxDepletionOperator.create_micro_xs_from_csv(micro_xs_path)
|
||||
nuclides = {'U234':8.92e18,
|
||||
'U235':9.98e20,
|
||||
'U238':2.22e22,
|
||||
'U236':4.57e18,
|
||||
'O16':4.64e22,
|
||||
'O17':1.76e19}
|
||||
nuclides = {'U234': 8.92e18,
|
||||
'U235': 9.98e20,
|
||||
'U238': 2.22e22,
|
||||
'U236': 4.57e18,
|
||||
'O16': 4.64e22,
|
||||
'O17': 1.76e19}
|
||||
volume = 0.5
|
||||
flux = 1.16e15
|
||||
|
||||
|
|
@ -207,8 +207,10 @@ data arrays::
|
|||
|
||||
|
||||
A user can then define an integrator class as they would for a coupled
|
||||
transport-depletion calculation and follow the steps from there.
|
||||
present in the depletion chain.
|
||||
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.
|
||||
.. 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.
|
||||
|
|
|
|||
|
|
@ -6,36 +6,22 @@ and one-group cross sections.
|
|||
"""
|
||||
|
||||
|
||||
import copy
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
from warnings import warn
|
||||
|
||||
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.data import DataLibrary
|
||||
from openmc.exceptions import DataError
|
||||
from openmc.mpi import comm
|
||||
from .abc import TransportOperator, OperatorResult
|
||||
from .atom_number import AtomNumber
|
||||
from .chain import _find_chain_file, REACTIONS
|
||||
from .chain import REACTIONS
|
||||
from .reaction_rates import ReactionRates
|
||||
from .results import Results
|
||||
from .helpers import ConstantFissionYieldHelper
|
||||
|
||||
valid_rxns = list(REACTIONS.keys())
|
||||
valid_rxns += ['fission']
|
||||
|
||||
# Convenience function for the micro_xs static methods
|
||||
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)
|
||||
[check_value('reactions', reaction, valid_rxns) for reaction in reactions]
|
||||
valid_rxns = list(REACTIONS)
|
||||
valid_rxns.append('fission')
|
||||
|
||||
|
||||
|
||||
|
|
@ -51,10 +37,10 @@ class FluxDepletionOperator(TransportOperator):
|
|||
Parameters
|
||||
----------
|
||||
volume : float
|
||||
Volume of the material being depleted in cm^3
|
||||
Volume of the material being depleted in [cm^3]
|
||||
nuclides : dict of str to float
|
||||
Dictionary with nuclide names as keys and nuclide concentrations as
|
||||
values. Nuclide concentration units are [at/cm^3].
|
||||
values. Nuclide concentration units are [atom/cm^3].
|
||||
micro_xs : pandas.DataFrame
|
||||
DataFrame with nuclides names as index and microscopic cross section
|
||||
data in the columns. Cross section units are [cm^-2].
|
||||
|
|
@ -64,15 +50,10 @@ class FluxDepletionOperator(TransportOperator):
|
|||
Path to the depletion chain XML file.
|
||||
keff : 2-tuple of float, optional
|
||||
keff eigenvalue and uncertainty from transport calculation.
|
||||
Defualt is None.
|
||||
Default is None.
|
||||
fission_q : dict, optional
|
||||
Dictionary of nuclides and their fission Q values [eV]. If not given,
|
||||
values will be pulled from the ``chain_file``.
|
||||
dilute_initial : float, optional
|
||||
Initial atom density [atoms/cm^3] to add for nuclides that are zero
|
||||
in initial condition to ensure they exist in the decay chain.
|
||||
Only done for nuclides with reaction rates.
|
||||
Defaults to 1.0e3.
|
||||
prev_results : Results, optional
|
||||
Results from a previous depletion calculation.
|
||||
reduce_chain : bool, optional
|
||||
|
|
@ -86,10 +67,6 @@ class FluxDepletionOperator(TransportOperator):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
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 chain.
|
||||
Only done for nuclides with reaction rates.
|
||||
round_number : bool
|
||||
Whether or not to round output to OpenMC to 8 digits.
|
||||
Useful in testing, as OpenMC is incredibly sensitive to exact values.
|
||||
|
|
@ -117,12 +94,11 @@ class FluxDepletionOperator(TransportOperator):
|
|||
chain_file,
|
||||
keff=None,
|
||||
fission_q=None,
|
||||
dilute_initial=1.0e3,
|
||||
prev_results=None,
|
||||
reduce_chain=False,
|
||||
reduce_chain_level=None,
|
||||
fission_yield_opts=None):
|
||||
super().__init__(chain_file, fission_q, dilute_initial, prev_results)
|
||||
super().__init__(chain_file, fission_q, 0.0, prev_results)
|
||||
self.round_number = False
|
||||
self.flux_spectra = flux_spectra
|
||||
self._init_nuclides = nuclides
|
||||
|
|
@ -138,7 +114,7 @@ class FluxDepletionOperator(TransportOperator):
|
|||
check_type('micro_xs', micro_xs, pd.DataFrame)
|
||||
|
||||
self._micro_xs = micro_xs
|
||||
if not isinstance(keff, type(None)):
|
||||
if keff is not None:
|
||||
check_type('keff', keff, tuple, float)
|
||||
keff = ufloat(keff)
|
||||
|
||||
|
|
@ -221,7 +197,6 @@ class FluxDepletionOperator(TransportOperator):
|
|||
for nuc, i_nuc_results in zip(rxn_nuclides, nuc_ind):
|
||||
number[i_nuc_results] = self.number[0, nuc]
|
||||
|
||||
|
||||
# Calculate macroscopic cross sections and store them in rates array
|
||||
for nuc in rxn_nuclides:
|
||||
density = self.number.get_atom_density('0', nuc)
|
||||
|
|
@ -230,8 +205,7 @@ class FluxDepletionOperator(TransportOperator):
|
|||
'0',
|
||||
nuc,
|
||||
rxn,
|
||||
self._micro_xs[rxn].loc[nuc] *
|
||||
density)
|
||||
self._micro_xs[rxn, rxn] * density)
|
||||
|
||||
# Get reaction rate in reactions/sec
|
||||
rates *= self.flux_spectra
|
||||
|
|
@ -334,15 +308,13 @@ class FluxDepletionOperator(TransportOperator):
|
|||
"""
|
||||
|
||||
# Validate inputs
|
||||
try:
|
||||
assert data.shape == (len(nuclides), len(reactions))
|
||||
except AssertionError:
|
||||
raise SyntaxError(
|
||||
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}')
|
||||
|
||||
_validate_micro_xs_inputs(nuclides, reactions, data)
|
||||
FluxDepletionOperator._validate_micro_xs_inputs(nuclides, reactions, data)
|
||||
|
||||
# Convert to cm^2
|
||||
if units == 'barn':
|
||||
|
|
@ -371,7 +343,7 @@ class FluxDepletionOperator(TransportOperator):
|
|||
"""
|
||||
micro_xs = pd.read_csv(csv_file, index_col=0)
|
||||
|
||||
_validate_micro_xs_inputs(list(micro_xs.index),
|
||||
FluxDepletionOperator._validate_micro_xs_inputs(list(micro_xs.index),
|
||||
list(micro_xs.columns),
|
||||
micro_xs.to_numpy())
|
||||
|
||||
|
|
@ -380,6 +352,16 @@ class FluxDepletionOperator(TransportOperator):
|
|||
|
||||
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)
|
||||
|
||||
|
||||
def _update_materials(self):
|
||||
"""Updates material compositions in OpenMC on all processes."""
|
||||
|
||||
|
|
@ -464,7 +446,7 @@ class FluxDepletionOperator(TransportOperator):
|
|||
return [nuc for nuc in nuc_list if nuc in self.chain]
|
||||
|
||||
def _get_all_nuclides_in_simulation(self):
|
||||
"""Determine nuclides that will show up in the simulation.
|
||||
"""Determine nuclides that will show up in the depletion matrix.
|
||||
This is the union of the nuclides provided by the user and
|
||||
the nuclides present in the depletion chain.
|
||||
|
||||
|
|
@ -487,9 +469,8 @@ class FluxDepletionOperator(TransportOperator):
|
|||
|
||||
def _get_nuclides_with_data(self):
|
||||
"""Finds nuclides with cross section data"""
|
||||
nuclides_with_data = set(self._micro_xs.index)
|
||||
return set(self._micro_xs.index)
|
||||
|
||||
return nuclides_with_data
|
||||
|
||||
def _extract_number(self, local_mats, volume, nuclides, prev_res=None):
|
||||
"""Construct AtomNumber using geometry
|
||||
|
|
@ -508,11 +489,6 @@ class FluxDepletionOperator(TransportOperator):
|
|||
"""
|
||||
self.number = AtomNumber(local_mats, nuclides, volume, len(self.chain))
|
||||
|
||||
if self.dilute_initial != 0.0:
|
||||
for nuc in self._burnable_nucs:
|
||||
self.number.set_atom_density(
|
||||
np.s_[:], nuc, self.dilute_initial)
|
||||
|
||||
# Now extract and store the number densities
|
||||
# From the geometry if no previous depletion results
|
||||
if prev_res is None:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
Class for normalizing fission energy deposition
|
||||
"""
|
||||
import bisect
|
||||
import pandas as pd
|
||||
from abc import abstractmethod
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
|
|
@ -124,6 +125,84 @@ class TalliedFissionYieldHelper(FissionYieldHelper):
|
|||
# -------------------------------------
|
||||
|
||||
|
||||
class FluxReactionRateHelper(ReactionRateHelper):
|
||||
"""Class for generating one-group reaction rates with flux and
|
||||
one-group cross sections.
|
||||
|
||||
This class does not generate tallies, and instead stores cross sections
|
||||
for each nuclides and transmutation reaction relevant for a depletion
|
||||
calculation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_nucs : int
|
||||
Number of burnable nuclides tracked by :class:`openmc.deplete.Operator`
|
||||
n_react : int
|
||||
Number of reactions tracked by :class:`openmc.deplete.Operator`
|
||||
|
||||
Attributes
|
||||
----------
|
||||
flux :
|
||||
|
||||
xs :
|
||||
|
||||
|
||||
"""
|
||||
def __init__(self, n_nuc, n_react):
|
||||
super().__init__(n_nuc, n_react)
|
||||
self._flux = None
|
||||
self._micro_xs = None
|
||||
|
||||
def generate_tallies(self, materials, scores):
|
||||
"""Unused in this case"""
|
||||
|
||||
|
||||
@property
|
||||
def flux(self):
|
||||
"""Flux in n cm^-2 s^-1"""
|
||||
return self._flux
|
||||
|
||||
@flux.setter
|
||||
def flux(self, flux):
|
||||
check_type("flux", flux, float)
|
||||
self._flux = flux
|
||||
|
||||
@property
|
||||
def micro_xs(self):
|
||||
"""DataFrame of microscopic cross sections with requested reaction
|
||||
for all nuclides"""
|
||||
return self._micro_xs
|
||||
|
||||
@micro_xs.setter
|
||||
def micro_xs(self, micro_xs):
|
||||
# TODO : validate micro_xs
|
||||
self._micro_xs = micro_xs
|
||||
|
||||
def get_material_rates(self, mat_id, nuc_index, react_index):
|
||||
"""Return 2D array of [nuclide, reaction] reaction rates
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mat_id : int
|
||||
Unique ID for the requested material
|
||||
nuc_index : list of str
|
||||
Ordering of desired nuclides
|
||||
react_index : list of str
|
||||
Ordering of reactions
|
||||
"""
|
||||
self._results_cache.fill(0.0)
|
||||
full_tally_res = self._rate_tally.mean[mat_id]
|
||||
for i_tally, (i_nuc, i_react) in enumerate(
|
||||
product(nuc_index, react_index)):
|
||||
self._results_cache[i_nuc, i_react] = full_tally_res[i_tally]
|
||||
|
||||
return self._results_cache
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DirectReactionRateHelper(ReactionRateHelper):
|
||||
"""Class for generating one-group reaction rates with direct tallies
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ def test_create_micro_xs_from_data_array():
|
|||
|
||||
FluxDepletionOperator.create_micro_xs_from_data_array(
|
||||
nuclides, reactions, data)
|
||||
with pytest.raises(SyntaxError, match=r'Nuclides list of length \d* and '
|
||||
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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue