2019-07-05 13:09:30 -05:00
|
|
|
"""
|
2022-07-30 15:39:47 -05:00
|
|
|
Classes for collecting and calculating quantities for reaction rate operators
|
2019-07-05 13:09:30 -05:00
|
|
|
"""
|
2020-11-02 15:26:08 -06:00
|
|
|
import bisect
|
2022-06-14 12:29:20 -05:00
|
|
|
from abc import abstractmethod
|
2020-11-02 15:26:08 -06:00
|
|
|
from collections import defaultdict
|
2019-08-21 10:05:59 -05:00
|
|
|
from copy import deepcopy
|
2019-07-05 13:09:30 -05:00
|
|
|
from itertools import product
|
2019-08-13 15:39:13 -05:00
|
|
|
from numbers import Real
|
2020-11-02 15:26:08 -06:00
|
|
|
import sys
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2020-08-04 14:35:12 -05:00
|
|
|
from numpy import dot, zeros, newaxis, asarray
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2021-10-01 06:09:24 -05:00
|
|
|
from openmc.mpi import comm
|
2019-08-13 15:39:13 -05:00
|
|
|
from openmc.checkvalue import check_type, check_greater_than
|
2020-10-19 13:33:02 +01:00
|
|
|
from openmc.data import JOULE_PER_EV, REACTION_MT
|
2019-09-05 07:31:13 -05:00
|
|
|
from openmc.lib import (
|
2023-03-22 12:45:33 -05:00
|
|
|
Tally, MaterialFilter, EnergyFilter, EnergyFunctionFilter, load_nuclide)
|
2020-08-04 14:35:12 -05:00
|
|
|
import openmc.lib
|
2019-08-13 15:39:13 -05:00
|
|
|
from .abc import (
|
2022-06-14 12:29:20 -05:00
|
|
|
ReactionRateHelper, NormalizationHelper, FissionYieldHelper)
|
2019-08-13 15:39:13 -05:00
|
|
|
|
|
|
|
|
__all__ = (
|
2020-07-17 14:19:49 -05:00
|
|
|
"DirectReactionRateHelper", "ChainFissionHelper", "EnergyScoreHelper"
|
2022-06-14 12:29:20 -05:00
|
|
|
"SourceRateHelper", "TalliedFissionYieldHelper",
|
|
|
|
|
"ConstantFissionYieldHelper", "FissionYieldCutoffHelper",
|
2022-07-14 17:55:18 -05:00
|
|
|
"AveragedFissionYieldHelper", "FluxCollapseHelper")
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2023-06-15 00:15:22 -05:00
|
|
|
|
2022-06-14 12:29:20 -05:00
|
|
|
class TalliedFissionYieldHelper(FissionYieldHelper):
|
|
|
|
|
"""Abstract class for computing fission yields with tallies
|
|
|
|
|
|
|
|
|
|
Generates a basic fission rate tally in all burnable materials with
|
|
|
|
|
:meth:`generate_tallies`, and set nuclides to be tallied with
|
|
|
|
|
:meth:`update_tally_nuclides`. Subclasses will need to implement
|
|
|
|
|
:meth:`unpack` and :meth:`weighted_yields`.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
chain_nuclides : iterable of openmc.deplete.Nuclide
|
|
|
|
|
Nuclides tracked in the depletion chain. Not necessary
|
|
|
|
|
that all have yield data.
|
|
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
constant_yields : dict of str to :class:`openmc.deplete.FissionYield`
|
|
|
|
|
Fission yields for all nuclides that only have one set of
|
|
|
|
|
fission yield data. Can be accessed as ``{parent: {product: yield}}``
|
|
|
|
|
results : None or numpy.ndarray
|
|
|
|
|
Tally results shaped in a manner useful to this helper.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
_upper_energy = 20.0e6 # upper energy for tallies
|
|
|
|
|
|
|
|
|
|
def __init__(self, chain_nuclides):
|
|
|
|
|
super().__init__(chain_nuclides)
|
|
|
|
|
self._local_indexes = None
|
|
|
|
|
self._fission_rate_tally = None
|
|
|
|
|
self._tally_nucs = []
|
|
|
|
|
self.results = None
|
|
|
|
|
|
|
|
|
|
def generate_tallies(self, materials, mat_indexes):
|
|
|
|
|
"""Construct the fission rate tally
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
materials : iterable of :class:`openmc.lib.Material`
|
|
|
|
|
Materials to be used in :class:`openmc.lib.MaterialFilter`
|
|
|
|
|
mat_indexes : iterable of int
|
|
|
|
|
Indices of tallied materials that will have their fission
|
|
|
|
|
yields computed by this helper. Necessary as the
|
2022-07-30 16:46:31 -05:00
|
|
|
:class:`openmc.deplete.CoupledOperator` that uses this helper
|
2022-06-14 12:29:20 -05:00
|
|
|
may only burn a subset of all materials when running
|
|
|
|
|
in parallel mode.
|
|
|
|
|
"""
|
|
|
|
|
self._local_indexes = asarray(mat_indexes)
|
|
|
|
|
|
|
|
|
|
# Tally group-wise fission reaction rates
|
|
|
|
|
self._fission_rate_tally = Tally()
|
|
|
|
|
self._fission_rate_tally.writable = False
|
|
|
|
|
self._fission_rate_tally.scores = ['fission']
|
|
|
|
|
self._fission_rate_tally.filters = [MaterialFilter(materials)]
|
|
|
|
|
|
|
|
|
|
def update_tally_nuclides(self, nuclides):
|
|
|
|
|
"""Tally nuclides with non-zero density and multiple yields
|
|
|
|
|
|
|
|
|
|
Must be run after :meth:`generate_tallies`.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
nuclides : iterable of str
|
|
|
|
|
Potential nuclides to be tallied, such as those with
|
|
|
|
|
non-zero density at this stage.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
nuclides : list of str
|
|
|
|
|
Union of input nuclides and those that have multiple sets
|
|
|
|
|
of yield data. Sorted by nuclide name
|
|
|
|
|
|
|
|
|
|
Raises
|
|
|
|
|
------
|
|
|
|
|
AttributeError
|
|
|
|
|
If tallies not generated
|
|
|
|
|
"""
|
|
|
|
|
assert self._fission_rate_tally is not None, (
|
|
|
|
|
"Run generate_tallies first")
|
|
|
|
|
overlap = set(self._chain_nuclides).intersection(set(nuclides))
|
|
|
|
|
nuclides = sorted(overlap)
|
|
|
|
|
self._tally_nucs = [self._chain_nuclides[n] for n in nuclides]
|
|
|
|
|
self._fission_rate_tally.nuclides = nuclides
|
|
|
|
|
return nuclides
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def unpack(self):
|
|
|
|
|
"""Unpack tallies after a transport run.
|
|
|
|
|
|
|
|
|
|
Abstract because each subclass will need to arrange its
|
|
|
|
|
tally data.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
2019-07-05 13:09:30 -05:00
|
|
|
# -------------------------------------
|
|
|
|
|
# Helpers for generating reaction rates
|
|
|
|
|
# -------------------------------------
|
|
|
|
|
|
|
|
|
|
|
2019-07-15 11:14:16 -05:00
|
|
|
class DirectReactionRateHelper(ReactionRateHelper):
|
2020-09-15 10:22:57 -05:00
|
|
|
"""Class for generating one-group reaction rates with direct tallies
|
|
|
|
|
|
|
|
|
|
This class generates reaction rate tallies for each nuclide and
|
|
|
|
|
transmutation reaction relevant for a depletion calculation.
|
2019-07-23 08:29:37 -05:00
|
|
|
|
Fix bug when reaction rate nuclides change through depletion
The size of the Operator.reaction_rates does not change through
depletion, but the number of tallied nuclides for reaction
rates may or may not. The DirectReactionRateHelper returns
reaction rates according to the number of nuclides tallied,
a potential subset of all nuclides designated as burnable
by the Operator. This causes IndexErrors if a nuclide is not
tallied at a later step.
Example: 10 nuclides [0-9] are originally tracked by the Operator
and tallied by DirectReactionRateHelper. The Operator.reaction_rates
array will be of shape (n_mat, 10, n_react). Initially, the
DirectReactionRateHelper returns an array of size (10, n_react)
for each material. Then, if nuclide 5 is not in the list of nuclides
passed to the DirectReactionRateHelper at the next step, [decayed to
zero], DirectReactionRateHelper will return an array (9, n_react) and
try to pass tally data for nuclide 9 into row 9 of the reaction rate
array, causing an IndexError.
This commit instructs requires two integers, n_nucs and n_react, to be
passed to the initialization of any ReactionRateHelper, allocating
a single array for storing material-reaction rates. The method
get_material_rates uses this directly and does not re-allocate storage
if len(nuc_index) has changed [like if nuclide 9 has dropped out].
2019-07-25 09:32:13 -05:00
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
n_nucs : int
|
2022-07-30 16:46:31 -05:00
|
|
|
Number of burnable nuclides tracked by
|
|
|
|
|
:class:`openmc.deplete.CoupledOperator`
|
Fix bug when reaction rate nuclides change through depletion
The size of the Operator.reaction_rates does not change through
depletion, but the number of tallied nuclides for reaction
rates may or may not. The DirectReactionRateHelper returns
reaction rates according to the number of nuclides tallied,
a potential subset of all nuclides designated as burnable
by the Operator. This causes IndexErrors if a nuclide is not
tallied at a later step.
Example: 10 nuclides [0-9] are originally tracked by the Operator
and tallied by DirectReactionRateHelper. The Operator.reaction_rates
array will be of shape (n_mat, 10, n_react). Initially, the
DirectReactionRateHelper returns an array of size (10, n_react)
for each material. Then, if nuclide 5 is not in the list of nuclides
passed to the DirectReactionRateHelper at the next step, [decayed to
zero], DirectReactionRateHelper will return an array (9, n_react) and
try to pass tally data for nuclide 9 into row 9 of the reaction rate
array, causing an IndexError.
This commit instructs requires two integers, n_nucs and n_react, to be
passed to the initialization of any ReactionRateHelper, allocating
a single array for storing material-reaction rates. The method
get_material_rates uses this directly and does not re-allocate storage
if len(nuc_index) has changed [like if nuclide 9 has dropped out].
2019-07-25 09:32:13 -05:00
|
|
|
n_react : int
|
2022-07-30 16:46:31 -05:00
|
|
|
Number of reactions tracked by an instance of
|
|
|
|
|
:class:`openmc.deplete.CoupledOperator`
|
Fix bug when reaction rate nuclides change through depletion
The size of the Operator.reaction_rates does not change through
depletion, but the number of tallied nuclides for reaction
rates may or may not. The DirectReactionRateHelper returns
reaction rates according to the number of nuclides tallied,
a potential subset of all nuclides designated as burnable
by the Operator. This causes IndexErrors if a nuclide is not
tallied at a later step.
Example: 10 nuclides [0-9] are originally tracked by the Operator
and tallied by DirectReactionRateHelper. The Operator.reaction_rates
array will be of shape (n_mat, 10, n_react). Initially, the
DirectReactionRateHelper returns an array of size (10, n_react)
for each material. Then, if nuclide 5 is not in the list of nuclides
passed to the DirectReactionRateHelper at the next step, [decayed to
zero], DirectReactionRateHelper will return an array (9, n_react) and
try to pass tally data for nuclide 9 into row 9 of the reaction rate
array, causing an IndexError.
This commit instructs requires two integers, n_nucs and n_react, to be
passed to the initialization of any ReactionRateHelper, allocating
a single array for storing material-reaction rates. The method
get_material_rates uses this directly and does not re-allocate storage
if len(nuc_index) has changed [like if nuclide 9 has dropped out].
2019-07-25 09:32:13 -05:00
|
|
|
|
2019-07-23 08:29:37 -05:00
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
nuclides : list of str
|
Fix bug when reaction rate nuclides change through depletion
The size of the Operator.reaction_rates does not change through
depletion, but the number of tallied nuclides for reaction
rates may or may not. The DirectReactionRateHelper returns
reaction rates according to the number of nuclides tallied,
a potential subset of all nuclides designated as burnable
by the Operator. This causes IndexErrors if a nuclide is not
tallied at a later step.
Example: 10 nuclides [0-9] are originally tracked by the Operator
and tallied by DirectReactionRateHelper. The Operator.reaction_rates
array will be of shape (n_mat, 10, n_react). Initially, the
DirectReactionRateHelper returns an array of size (10, n_react)
for each material. Then, if nuclide 5 is not in the list of nuclides
passed to the DirectReactionRateHelper at the next step, [decayed to
zero], DirectReactionRateHelper will return an array (9, n_react) and
try to pass tally data for nuclide 9 into row 9 of the reaction rate
array, causing an IndexError.
This commit instructs requires two integers, n_nucs and n_react, to be
passed to the initialization of any ReactionRateHelper, allocating
a single array for storing material-reaction rates. The method
get_material_rates uses this directly and does not re-allocate storage
if len(nuc_index) has changed [like if nuclide 9 has dropped out].
2019-07-25 09:32:13 -05:00
|
|
|
All nuclides with desired reaction rates.
|
2019-07-23 08:29:37 -05:00
|
|
|
"""
|
2020-08-04 14:35:12 -05:00
|
|
|
def __init__(self, n_nuc, n_react):
|
|
|
|
|
super().__init__(n_nuc, n_react)
|
|
|
|
|
self._rate_tally = None
|
|
|
|
|
|
2020-08-24 13:48:21 -05:00
|
|
|
# Automatically pre-calculate reaction rates for depletion
|
|
|
|
|
openmc.lib.settings.need_depletion_rx = True
|
|
|
|
|
|
2020-08-04 14:35:12 -05:00
|
|
|
@ReactionRateHelper.nuclides.setter
|
|
|
|
|
def nuclides(self, nuclides):
|
|
|
|
|
ReactionRateHelper.nuclides.fset(self, nuclides)
|
|
|
|
|
self._rate_tally.nuclides = nuclides
|
2019-07-05 13:09:30 -05:00
|
|
|
|
|
|
|
|
def generate_tallies(self, materials, scores):
|
|
|
|
|
"""Produce one-group reaction rate tally
|
|
|
|
|
|
2023-06-15 00:15:22 -05:00
|
|
|
Uses the :mod:`openmc.lib` to generate a tally of relevant reactions
|
|
|
|
|
across all burnable materials.
|
2019-07-05 13:09:30 -05:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2023-06-15 00:15:22 -05:00
|
|
|
materials : iterable of :class:`openmc.lib.Material`
|
|
|
|
|
Burnable materials in the problem. Used to construct a
|
|
|
|
|
:class:`openmc.lib.MaterialFilter`
|
2019-07-05 13:09:30 -05:00
|
|
|
scores : iterable of str
|
2023-06-15 00:15:22 -05:00
|
|
|
Reaction identifiers, e.g. ``"(n, fission)"``, ``"(n, gamma)"``,
|
|
|
|
|
needed for the reaction rate tally.
|
2019-07-05 13:09:30 -05:00
|
|
|
"""
|
|
|
|
|
self._rate_tally = Tally()
|
2019-09-26 20:34:11 -04:00
|
|
|
self._rate_tally.writable = False
|
2019-07-05 13:09:30 -05:00
|
|
|
self._rate_tally.scores = scores
|
|
|
|
|
self._rate_tally.filters = [MaterialFilter(materials)]
|
2023-06-15 00:15:22 -05:00
|
|
|
self._rate_tally.multiply_density = False
|
2022-09-10 00:35:37 +00:00
|
|
|
self._rate_tally_means_cache = None
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2022-09-10 00:07:31 +00:00
|
|
|
@property
|
|
|
|
|
def rate_tally_means(self):
|
2022-09-12 17:08:41 +00:00
|
|
|
"""The mean results of the tally of every material's reaction rates for this cycle
|
|
|
|
|
"""
|
2022-09-10 00:07:31 +00:00
|
|
|
# If the mean cache is empty, fill it once with this transport cycle's results
|
2022-09-10 00:35:37 +00:00
|
|
|
if self._rate_tally_means_cache is None:
|
|
|
|
|
self._rate_tally_means_cache = self._rate_tally.mean
|
|
|
|
|
return self._rate_tally_means_cache
|
2022-09-10 00:07:31 +00:00
|
|
|
|
|
|
|
|
def reset_tally_means(self):
|
2022-09-10 00:35:37 +00:00
|
|
|
"""Reset the cached mean rate tallies.
|
2022-09-12 17:08:41 +00:00
|
|
|
.. note::
|
2023-03-22 12:45:33 -05:00
|
|
|
|
2022-09-12 17:08:41 +00:00
|
|
|
This step must be performed after each transport cycle
|
2022-09-10 00:07:31 +00:00
|
|
|
"""
|
2022-09-10 00:35:37 +00:00
|
|
|
self._rate_tally_means_cache = None
|
2022-09-10 00:07:31 +00:00
|
|
|
|
2023-08-31 09:55:21 -05:00
|
|
|
def get_material_rates(self, mat_index, nuc_index, rx_index):
|
2019-07-05 13:09:30 -05:00
|
|
|
"""Return an array of reaction rates for a material
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2023-08-31 09:55:21 -05:00
|
|
|
mat_index : int
|
|
|
|
|
Index for the material
|
2019-07-05 13:09:30 -05:00
|
|
|
nuc_index : iterable of int
|
|
|
|
|
Index for each nuclide in :attr:`nuclides` in the
|
|
|
|
|
desired reaction rate matrix
|
2023-06-15 00:15:22 -05:00
|
|
|
rx_index : iterable of int
|
2019-07-05 13:09:30 -05:00
|
|
|
Index for each reaction scored in the tally
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
2019-07-23 08:29:37 -05:00
|
|
|
rates : numpy.ndarray
|
|
|
|
|
Array with shape ``(n_nuclides, n_rxns)`` with the
|
2019-07-05 13:09:30 -05:00
|
|
|
reaction rates in this material
|
|
|
|
|
"""
|
Fix bug when reaction rate nuclides change through depletion
The size of the Operator.reaction_rates does not change through
depletion, but the number of tallied nuclides for reaction
rates may or may not. The DirectReactionRateHelper returns
reaction rates according to the number of nuclides tallied,
a potential subset of all nuclides designated as burnable
by the Operator. This causes IndexErrors if a nuclide is not
tallied at a later step.
Example: 10 nuclides [0-9] are originally tracked by the Operator
and tallied by DirectReactionRateHelper. The Operator.reaction_rates
array will be of shape (n_mat, 10, n_react). Initially, the
DirectReactionRateHelper returns an array of size (10, n_react)
for each material. Then, if nuclide 5 is not in the list of nuclides
passed to the DirectReactionRateHelper at the next step, [decayed to
zero], DirectReactionRateHelper will return an array (9, n_react) and
try to pass tally data for nuclide 9 into row 9 of the reaction rate
array, causing an IndexError.
This commit instructs requires two integers, n_nucs and n_react, to be
passed to the initialization of any ReactionRateHelper, allocating
a single array for storing material-reaction rates. The method
get_material_rates uses this directly and does not re-allocate storage
if len(nuc_index) has changed [like if nuclide 9 has dropped out].
2019-07-25 09:32:13 -05:00
|
|
|
self._results_cache.fill(0.0)
|
2023-08-31 09:55:21 -05:00
|
|
|
full_tally_res = self.rate_tally_means[mat_index]
|
2023-06-15 00:15:22 -05:00
|
|
|
for i_tally, (i_nuc, i_rx) in enumerate(product(nuc_index, rx_index)):
|
|
|
|
|
self._results_cache[i_nuc, i_rx] = full_tally_res[i_tally]
|
2019-07-05 13:09:30 -05:00
|
|
|
|
Fix bug when reaction rate nuclides change through depletion
The size of the Operator.reaction_rates does not change through
depletion, but the number of tallied nuclides for reaction
rates may or may not. The DirectReactionRateHelper returns
reaction rates according to the number of nuclides tallied,
a potential subset of all nuclides designated as burnable
by the Operator. This causes IndexErrors if a nuclide is not
tallied at a later step.
Example: 10 nuclides [0-9] are originally tracked by the Operator
and tallied by DirectReactionRateHelper. The Operator.reaction_rates
array will be of shape (n_mat, 10, n_react). Initially, the
DirectReactionRateHelper returns an array of size (10, n_react)
for each material. Then, if nuclide 5 is not in the list of nuclides
passed to the DirectReactionRateHelper at the next step, [decayed to
zero], DirectReactionRateHelper will return an array (9, n_react) and
try to pass tally data for nuclide 9 into row 9 of the reaction rate
array, causing an IndexError.
This commit instructs requires two integers, n_nucs and n_react, to be
passed to the initialization of any ReactionRateHelper, allocating
a single array for storing material-reaction rates. The method
get_material_rates uses this directly and does not re-allocate storage
if len(nuc_index) has changed [like if nuclide 9 has dropped out].
2019-07-25 09:32:13 -05:00
|
|
|
return self._results_cache
|
2019-07-05 13:09:30 -05:00
|
|
|
|
|
|
|
|
|
2020-08-04 14:35:12 -05:00
|
|
|
class FluxCollapseHelper(ReactionRateHelper):
|
2020-09-17 22:18:20 -05:00
|
|
|
"""Class that generates one-group reaction rates using multigroup flux
|
2020-09-15 10:22:57 -05:00
|
|
|
|
|
|
|
|
This class generates a multigroup flux tally that is used afterward to
|
|
|
|
|
calculate a one-group reaction rate by collapsing it with continuous-energy
|
|
|
|
|
cross section data. Additionally, select nuclides/reactions can be treated
|
|
|
|
|
with a direct reaction rate tally when using a multigroup flux spectrum
|
|
|
|
|
would not be sufficiently accurate. This is often the case for (n,gamma) and
|
|
|
|
|
fission reactions.
|
2020-08-24 10:46:05 -05:00
|
|
|
|
|
|
|
|
.. versionadded:: 0.12.1
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
n_nucs : int
|
2022-07-30 16:46:31 -05:00
|
|
|
Number of burnable nuclides tracked by
|
|
|
|
|
:class:`openmc.deplete.CoupledOperator`
|
2020-08-24 10:46:05 -05:00
|
|
|
n_react : int
|
2022-07-30 16:46:31 -05:00
|
|
|
Number of reactions tracked by :class:`openmc.deplete.CoupledOperator`
|
2020-08-24 10:46:05 -05:00
|
|
|
energies : iterable of float
|
|
|
|
|
Energy group boundaries for flux spectrum in [eV]
|
|
|
|
|
reactions : iterable of str
|
|
|
|
|
Reactions for which rates should be directly tallied
|
|
|
|
|
nuclides : iterable of str
|
|
|
|
|
Nuclides for which some reaction rates should be directly tallied. If
|
2020-09-15 10:22:57 -05:00
|
|
|
None, then ``reactions`` will be used for all nuclides.
|
2020-08-24 10:46:05 -05:00
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
nuclides : list of str
|
|
|
|
|
All nuclides with desired reaction rates.
|
|
|
|
|
|
|
|
|
|
"""
|
2020-09-03 10:11:04 -05:00
|
|
|
def __init__(self, n_nucs, n_reacts, energies, reactions=None, nuclides=None):
|
2020-08-24 10:46:05 -05:00
|
|
|
super().__init__(n_nucs, n_reacts)
|
|
|
|
|
self._energies = asarray(energies)
|
2020-09-03 10:11:04 -05:00
|
|
|
self._reactions_direct = list(reactions) if reactions is not None else []
|
2020-08-24 10:46:05 -05:00
|
|
|
self._nuclides_direct = list(nuclides) if nuclides is not None else None
|
|
|
|
|
|
|
|
|
|
@ReactionRateHelper.nuclides.setter
|
|
|
|
|
def nuclides(self, nuclides):
|
|
|
|
|
ReactionRateHelper.nuclides.fset(self, nuclides)
|
2020-09-03 10:11:04 -05:00
|
|
|
if self._reactions_direct and self._nuclides_direct is None:
|
2020-08-24 10:46:05 -05:00
|
|
|
self._rate_tally.nuclides = nuclides
|
|
|
|
|
|
2023-09-15 12:19:11 -05:00
|
|
|
# Make sure nuclide data is loaded
|
|
|
|
|
for nuclide in self.nuclides:
|
|
|
|
|
if nuclide not in openmc.lib.nuclides:
|
|
|
|
|
openmc.lib.load_nuclide(nuclide)
|
|
|
|
|
|
2020-08-24 10:46:05 -05:00
|
|
|
def generate_tallies(self, materials, scores):
|
|
|
|
|
"""Produce multigroup flux spectrum tally
|
|
|
|
|
|
|
|
|
|
Uses the :mod:`openmc.lib` module to generate a multigroup flux tally
|
|
|
|
|
for each burnable material.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
materials : iterable of :class:`openmc.Material`
|
|
|
|
|
Burnable materials in the problem. Used to construct a
|
|
|
|
|
:class:`openmc.MaterialFilter`
|
|
|
|
|
scores : iterable of str
|
|
|
|
|
Reaction identifiers, e.g. ``"(n, fission)"``, ``"(n, gamma)"``,
|
|
|
|
|
needed for the reaction rate tally.
|
|
|
|
|
"""
|
|
|
|
|
self._materials = materials
|
|
|
|
|
|
2020-10-08 17:08:24 +01:00
|
|
|
# adds an entry for fisson to the dictionary of reactions
|
2020-10-19 13:33:02 +01:00
|
|
|
self._mts = [REACTION_MT[x] for x in scores]
|
2020-08-24 10:46:05 -05:00
|
|
|
self._scores = scores
|
|
|
|
|
|
|
|
|
|
# Create flux tally with material and energy filters
|
|
|
|
|
self._flux_tally = Tally()
|
|
|
|
|
self._flux_tally.writable = False
|
|
|
|
|
self._flux_tally.filters = [
|
|
|
|
|
MaterialFilter(materials),
|
|
|
|
|
EnergyFilter(self._energies)
|
|
|
|
|
]
|
|
|
|
|
self._flux_tally.scores = ['flux']
|
2022-09-10 00:07:31 +00:00
|
|
|
self._flux_tally_means_cache = None
|
2020-08-24 10:46:05 -05:00
|
|
|
|
|
|
|
|
# Create reaction rate tally
|
2020-09-03 10:11:04 -05:00
|
|
|
if self._reactions_direct:
|
|
|
|
|
self._rate_tally = Tally()
|
|
|
|
|
self._rate_tally.writable = False
|
|
|
|
|
self._rate_tally.scores = self._reactions_direct
|
|
|
|
|
self._rate_tally.filters = [MaterialFilter(materials)]
|
2023-06-15 00:15:22 -05:00
|
|
|
self._rate_tally.multiply_density = False
|
2022-09-10 00:07:31 +00:00
|
|
|
self._rate_tally_means_cache = None
|
2020-09-03 10:11:04 -05:00
|
|
|
if self._nuclides_direct is not None:
|
2023-03-22 12:45:33 -05:00
|
|
|
# check if any direct tally nuclides are requested that are not
|
|
|
|
|
# already loaded with the materials. Load separately if so.
|
2023-03-23 15:07:32 -05:00
|
|
|
mat_nuclides = {n for mat in materials for n in mat.nuclides}
|
|
|
|
|
extra_nuclides = set(self._nuclides_direct) - mat_nuclides
|
2023-03-22 12:45:33 -05:00
|
|
|
for nuc in extra_nuclides:
|
|
|
|
|
load_nuclide(nuc)
|
2020-09-03 10:11:04 -05:00
|
|
|
self._rate_tally.nuclides = self._nuclides_direct
|
2020-08-24 10:46:05 -05:00
|
|
|
|
2022-09-10 00:07:31 +00:00
|
|
|
@property
|
|
|
|
|
def rate_tally_means(self):
|
2022-09-12 17:08:41 +00:00
|
|
|
"""The mean results of the tally of every material's reaction rates for this cycle
|
|
|
|
|
"""
|
2022-09-10 00:07:31 +00:00
|
|
|
# If the mean cache is empty, fill it once with this transport cycle's results
|
|
|
|
|
if self._rate_tally_means_cache is None:
|
|
|
|
|
self._rate_tally_means_cache = self._rate_tally.mean
|
|
|
|
|
return self._rate_tally_means_cache
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def flux_tally_means(self):
|
|
|
|
|
# If the mean cache is empty, fill it once for this transport cycle's results
|
|
|
|
|
if self._flux_tally_means_cache is None:
|
|
|
|
|
self._flux_tally_means_cache = self._flux_tally.mean
|
|
|
|
|
return self._flux_tally_means_cache
|
|
|
|
|
|
|
|
|
|
def reset_tally_means(self):
|
2022-09-10 00:35:37 +00:00
|
|
|
"""Reset the cached mean rate and flux tallies.
|
2022-09-12 17:08:41 +00:00
|
|
|
.. note::
|
2023-03-22 12:45:33 -05:00
|
|
|
|
2022-09-12 17:08:41 +00:00
|
|
|
This step must be performed after each transport cycle
|
2022-09-10 00:07:31 +00:00
|
|
|
"""
|
|
|
|
|
self._flux_tally_means_cache = None
|
|
|
|
|
if self._reactions_direct:
|
|
|
|
|
self._rate_tally_means_cache = None
|
|
|
|
|
|
2020-08-24 10:46:05 -05:00
|
|
|
def get_material_rates(self, mat_index, nuc_index, react_index):
|
|
|
|
|
"""Return an array of reaction rates for a material
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
mat_index : int
|
|
|
|
|
Index for material
|
|
|
|
|
nuc_index : iterable of int
|
|
|
|
|
Index for each nuclide in :attr:`nuclides` in the
|
|
|
|
|
desired reaction rate matrix
|
|
|
|
|
react_index : iterable of int
|
|
|
|
|
Index for each reaction scored in the tally
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
rates : numpy.ndarray
|
|
|
|
|
Array with shape ``(n_nuclides, n_rxns)`` with the reaction rates in
|
|
|
|
|
this material
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
self._results_cache.fill(0.0)
|
|
|
|
|
|
|
|
|
|
# Get flux for specified material
|
|
|
|
|
shape = (len(self._materials), len(self._energies) - 1)
|
2022-09-10 00:07:31 +00:00
|
|
|
mean_value = self.flux_tally_means.reshape(shape)
|
2020-08-24 10:46:05 -05:00
|
|
|
flux = mean_value[mat_index]
|
|
|
|
|
|
|
|
|
|
# Get direct reaction rates
|
2020-09-03 10:11:04 -05:00
|
|
|
if self._reactions_direct:
|
|
|
|
|
nuclides_direct = self._rate_tally.nuclides
|
|
|
|
|
shape = (len(nuclides_direct), len(self._reactions_direct))
|
2022-09-10 00:07:31 +00:00
|
|
|
rx_rates = self.rate_tally_means[mat_index].reshape(shape)
|
2026-01-13 21:34:07 +01:00
|
|
|
direct_rx_index = {score: i for i, score in enumerate(self._reactions_direct)}
|
|
|
|
|
direct_nuc_index = {nuc: i for i, nuc in enumerate(nuclides_direct)}
|
2020-08-24 10:46:05 -05:00
|
|
|
|
|
|
|
|
mat = self._materials[mat_index]
|
|
|
|
|
|
|
|
|
|
for name, i_nuc in zip(self.nuclides, nuc_index):
|
|
|
|
|
for mt, score, i_rx in zip(self._mts, self._scores, react_index):
|
|
|
|
|
if score in self._reactions_direct and name in nuclides_direct:
|
|
|
|
|
# Get reaction rate from tally
|
2026-01-13 21:34:07 +01:00
|
|
|
i_rx_direct = direct_rx_index[score]
|
|
|
|
|
i_nuc_direct = direct_nuc_index[name]
|
2020-08-24 10:46:05 -05:00
|
|
|
self._results_cache[i_nuc, i_rx] = rx_rates[i_nuc_direct, i_rx_direct]
|
|
|
|
|
else:
|
|
|
|
|
# Use flux to collapse reaction rate (per N)
|
|
|
|
|
nuc = openmc.lib.nuclides[name]
|
|
|
|
|
rate_per_nuc = nuc.collapse_rate(
|
|
|
|
|
mt, mat.temperature, self._energies, flux)
|
|
|
|
|
|
2023-06-15 00:15:22 -05:00
|
|
|
self._results_cache[i_nuc, i_rx] = rate_per_nuc
|
2020-08-24 10:46:05 -05:00
|
|
|
|
|
|
|
|
return self._results_cache
|
|
|
|
|
|
|
|
|
|
|
2020-07-13 16:15:14 -05:00
|
|
|
# ------------------------------------------
|
|
|
|
|
# Helpers for obtaining normalization factor
|
|
|
|
|
# ------------------------------------------
|
2019-07-05 13:09:30 -05:00
|
|
|
|
|
|
|
|
|
2020-08-03 14:25:58 -05:00
|
|
|
class EnergyNormalizationHelper(NormalizationHelper):
|
|
|
|
|
"""Compute energy-based normalization."""
|
|
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
"""Reset energy produced prior to unpacking tallies"""
|
|
|
|
|
self._energy = 0.0
|
|
|
|
|
|
|
|
|
|
def factor(self, source_rate):
|
|
|
|
|
# Reduce energy produced from all processes
|
|
|
|
|
# J / source neutron
|
|
|
|
|
energy = comm.allreduce(self._energy) * JOULE_PER_EV
|
|
|
|
|
|
|
|
|
|
# Guard against divide by zero
|
|
|
|
|
if energy == 0:
|
|
|
|
|
if comm.rank == 0:
|
|
|
|
|
sys.stderr.flush()
|
|
|
|
|
print("No energy reported from OpenMC tallies. Do your HDF5 "
|
|
|
|
|
"files have heating data?\n", file=sys.stderr, flush=True)
|
|
|
|
|
comm.barrier()
|
|
|
|
|
comm.Abort(1)
|
|
|
|
|
|
|
|
|
|
# Return normalization factor for scaling reaction rates. In this case,
|
|
|
|
|
# the source rate is the power in [W], so [W] / [J/src] = [src/s]
|
|
|
|
|
return source_rate / energy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChainFissionHelper(EnergyNormalizationHelper):
|
2020-07-13 16:15:14 -05:00
|
|
|
"""Computes normalization using fission Q values from depletion chain
|
2019-07-23 08:29:37 -05:00
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
nuclides : list of str
|
|
|
|
|
All nuclides with desired reaction rates. Ordered to be
|
2022-07-30 16:46:31 -05:00
|
|
|
consistent with :class:`openmc.deplete.CoupledOperator`
|
2019-07-23 08:29:37 -05:00
|
|
|
energy : float
|
|
|
|
|
Total energy [J/s/source neutron] produced in a transport simulation.
|
|
|
|
|
Updated in the material iteration with :meth:`update`.
|
|
|
|
|
"""
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2019-07-18 14:35:39 -05:00
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
self._fission_q_vector = None
|
|
|
|
|
|
2020-07-13 16:28:22 -05:00
|
|
|
def prepare(self, chain_nucs, rate_index):
|
2019-07-05 13:09:30 -05:00
|
|
|
"""Populate the fission Q value vector from a chain.
|
|
|
|
|
|
2019-07-23 08:29:37 -05:00
|
|
|
Parameters
|
|
|
|
|
----------
|
2019-07-05 13:09:30 -05:00
|
|
|
chain_nucs : iterable of :class:`openmc.deplete.Nuclide`
|
|
|
|
|
Nuclides used in this depletion chain. Do not need
|
|
|
|
|
to be ordered
|
|
|
|
|
rate_index : dict of str to int
|
|
|
|
|
Dictionary mapping names of nuclides, e.g. ``"U235"``,
|
|
|
|
|
to a corresponding index in the desired fission Q
|
|
|
|
|
vector.
|
|
|
|
|
"""
|
2019-07-18 14:35:39 -05:00
|
|
|
if (self._fission_q_vector is not None
|
|
|
|
|
and self._fission_q_vector.shape == (len(rate_index),)):
|
2019-07-05 13:09:30 -05:00
|
|
|
return
|
|
|
|
|
|
2019-07-18 14:35:39 -05:00
|
|
|
fission_qs = zeros(len(rate_index))
|
2019-07-05 13:09:30 -05:00
|
|
|
|
|
|
|
|
for nuclide in chain_nucs:
|
|
|
|
|
if nuclide.name in rate_index:
|
|
|
|
|
for rx in nuclide.reactions:
|
|
|
|
|
if rx.type == "fission":
|
2019-07-18 14:35:39 -05:00
|
|
|
fission_qs[rate_index[nuclide.name]] = rx.Q
|
2019-07-05 13:09:30 -05:00
|
|
|
break
|
|
|
|
|
|
2019-07-18 14:35:39 -05:00
|
|
|
self._fission_q_vector = fission_qs
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2022-08-10 15:53:40 -05:00
|
|
|
def update(self, fission_rates):
|
2019-07-18 14:35:39 -05:00
|
|
|
"""Update energy produced with fission rates in a material
|
2019-07-05 13:09:30 -05:00
|
|
|
|
2019-07-15 11:39:05 -05:00
|
|
|
Parameters
|
2019-07-05 13:09:30 -05:00
|
|
|
----------
|
|
|
|
|
fission_rates : numpy.ndarray
|
|
|
|
|
fission reaction rate for each isotope in the specified
|
2019-07-15 11:39:05 -05:00
|
|
|
material. Should be ordered corresponding to initial
|
|
|
|
|
``rate_index`` used in :meth:`prepare`
|
2019-07-05 13:09:30 -05:00
|
|
|
"""
|
2019-07-18 14:35:39 -05:00
|
|
|
self._energy += dot(fission_rates, self._fission_q_vector)
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
|
2020-08-03 14:25:58 -05:00
|
|
|
class EnergyScoreHelper(EnergyNormalizationHelper):
|
2019-09-10 15:22:23 -05:00
|
|
|
"""Class responsible for obtaining system energy via a tally score
|
|
|
|
|
|
2019-09-16 15:24:06 -05:00
|
|
|
Parameters
|
|
|
|
|
----------
|
2019-09-17 11:34:20 -05:00
|
|
|
score : string
|
|
|
|
|
Valid score to use when obtaining system energy from OpenMC.
|
|
|
|
|
Defaults to "heating-local"
|
2019-09-16 15:24:06 -05:00
|
|
|
|
2019-09-10 15:22:23 -05:00
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
nuclides : list of str
|
|
|
|
|
List of nuclides with reaction rates. Not needed, but provided
|
2020-07-13 16:15:14 -05:00
|
|
|
for a consistent API across other :class:`NormalizationHelper`
|
2019-09-10 15:22:23 -05:00
|
|
|
energy : float
|
|
|
|
|
System energy [eV] computed from the tally. Will be zero for
|
|
|
|
|
all MPI processes that are not the "master" process to avoid
|
|
|
|
|
artificially increasing the tallied energy.
|
2019-09-17 11:34:20 -05:00
|
|
|
score : str
|
|
|
|
|
Score used to obtain system energy
|
2019-09-10 15:22:23 -05:00
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
2019-09-17 11:34:20 -05:00
|
|
|
def __init__(self, score="heating-local"):
|
2019-09-10 15:22:23 -05:00
|
|
|
super().__init__()
|
2019-09-16 15:24:06 -05:00
|
|
|
self.score = score
|
2019-09-10 15:22:23 -05:00
|
|
|
self._tally = None
|
|
|
|
|
|
|
|
|
|
def prepare(self, *args, **kwargs):
|
|
|
|
|
"""Create a tally for system energy production
|
|
|
|
|
|
|
|
|
|
Input arguments are not used, as the only information needed
|
|
|
|
|
is :attr:`score`
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
self._tally = Tally()
|
2019-09-26 20:34:11 -04:00
|
|
|
self._tally.writable = False
|
2019-09-16 15:24:06 -05:00
|
|
|
self._tally.scores = [self.score]
|
2019-09-10 15:22:23 -05:00
|
|
|
|
|
|
|
|
def reset(self):
|
|
|
|
|
"""Obtain system energy from tally
|
|
|
|
|
|
|
|
|
|
Only the master process, ``comm.rank == 0`` will
|
|
|
|
|
have a non-zero :attr:`energy` taken from the tally.
|
|
|
|
|
This avoids accidentally scaling the system power by
|
|
|
|
|
the number of MPI processes
|
|
|
|
|
"""
|
|
|
|
|
super().reset()
|
2019-09-17 12:18:51 -05:00
|
|
|
if comm.rank == 0:
|
2020-07-22 22:50:05 -05:00
|
|
|
self._energy = self._tally.mean[0, 0]
|
2019-09-10 15:22:23 -05:00
|
|
|
|
2020-07-17 14:19:49 -05:00
|
|
|
|
|
|
|
|
class SourceRateHelper(NormalizationHelper):
|
|
|
|
|
def prepare(self, *args, **kwargs):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def factor(self, source_rate):
|
|
|
|
|
return source_rate
|
|
|
|
|
|
2019-08-02 17:51:33 -05:00
|
|
|
# ------------------------------------
|
|
|
|
|
# Helper for collapsing fission yields
|
|
|
|
|
# ------------------------------------
|
|
|
|
|
|
|
|
|
|
|
2019-08-13 15:39:13 -05:00
|
|
|
class ConstantFissionYieldHelper(FissionYieldHelper):
|
|
|
|
|
"""Class that uses a single set of fission yields on each isotope
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2019-08-08 08:50:32 -05:00
|
|
|
chain_nuclides : iterable of openmc.deplete.Nuclide
|
2019-08-21 09:34:44 -05:00
|
|
|
Nuclides tracked in the depletion chain. All nuclides are
|
|
|
|
|
not required to have fission yield data.
|
2019-08-13 15:39:13 -05:00
|
|
|
energy : float, optional
|
|
|
|
|
Key in :attr:`openmc.deplete.Nuclide.yield_data` corresponding
|
|
|
|
|
to the desired set of fission yield data. Typically one of
|
|
|
|
|
``{0.0253, 500000, 14000000}`` corresponding to 0.0253 eV,
|
|
|
|
|
500 keV, and 14 MeV yield libraries. If the specific key is not
|
|
|
|
|
found, will fall back to closest energy present.
|
|
|
|
|
Default: 0.0253 eV for thermal yields
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
2019-09-11 10:26:45 -05:00
|
|
|
constant_yields : collections.defaultdict
|
2019-08-13 15:39:13 -05:00
|
|
|
Fission yields for all nuclides that only have one set of
|
2019-09-11 10:26:45 -05:00
|
|
|
fission yield data. Dictionary of form ``{str: {str: float}}``
|
|
|
|
|
representing yields for ``{parent: {product: yield}}``. Default
|
|
|
|
|
return object is an empty dictionary
|
2019-08-13 15:39:13 -05:00
|
|
|
energy : float
|
|
|
|
|
Energy of fission yield libraries.
|
2019-08-02 17:51:33 -05:00
|
|
|
"""
|
|
|
|
|
|
2019-08-13 15:39:13 -05:00
|
|
|
def __init__(self, chain_nuclides, energy=0.0253):
|
|
|
|
|
check_type("energy", energy, Real)
|
|
|
|
|
check_greater_than("energy", energy, 0.0, equality=True)
|
|
|
|
|
self._energy = energy
|
|
|
|
|
super().__init__(chain_nuclides)
|
|
|
|
|
# Iterate over all nuclides with > 1 set of yields
|
|
|
|
|
for name, nuc in self._chain_nuclides.items():
|
|
|
|
|
yield_data = nuc.yield_data.get(energy)
|
|
|
|
|
if yield_data is not None:
|
|
|
|
|
self._constant_yields[name] = yield_data
|
2019-08-02 17:51:33 -05:00
|
|
|
continue
|
2019-08-13 15:39:13 -05:00
|
|
|
# Specific energy not found, use closest energy
|
2019-08-21 10:05:59 -05:00
|
|
|
min_E = min(nuc.yield_energies, key=lambda e: abs(e - energy))
|
|
|
|
|
self._constant_yields[name] = nuc.yield_data[min_E]
|
2019-08-02 17:51:33 -05:00
|
|
|
|
2019-08-13 17:47:34 -05:00
|
|
|
@classmethod
|
2019-08-21 09:34:44 -05:00
|
|
|
def from_operator(cls, operator, **kwargs):
|
2019-08-13 17:47:34 -05:00
|
|
|
"""Return a new ConstantFissionYieldHelper using operator data
|
|
|
|
|
|
2019-08-21 09:34:44 -05:00
|
|
|
All keyword arguments should be identical to their counterpart
|
|
|
|
|
in the main ``__init__`` method
|
|
|
|
|
|
2019-08-13 17:47:34 -05:00
|
|
|
Parameters
|
|
|
|
|
----------
|
2022-08-01 12:44:28 -05:00
|
|
|
operator : openmc.deplete.abc.TransportOperator
|
2019-08-13 17:47:34 -05:00
|
|
|
operator with a depletion chain
|
2019-08-21 09:34:44 -05:00
|
|
|
kwargs:
|
|
|
|
|
Additional keyword arguments to be used in construction
|
2019-08-13 17:47:34 -05:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
ConstantFissionYieldHelper
|
|
|
|
|
"""
|
2019-08-21 09:34:44 -05:00
|
|
|
return cls(operator.chain.nuclides, **kwargs)
|
2019-08-13 17:47:34 -05:00
|
|
|
|
2019-08-02 17:51:33 -05:00
|
|
|
@property
|
2019-08-13 15:39:13 -05:00
|
|
|
def energy(self):
|
|
|
|
|
return self._energy
|
2019-08-02 17:51:33 -05:00
|
|
|
|
2019-08-13 15:39:13 -05:00
|
|
|
def weighted_yields(self, _local_mat_index=None):
|
|
|
|
|
"""Return fission yields for all nuclides requested
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2019-08-13 15:39:13 -05:00
|
|
|
_local_mat_index : int, optional
|
|
|
|
|
Current material index. Not used since all yields are
|
|
|
|
|
constant
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
2019-09-11 10:26:45 -05:00
|
|
|
library : collections.defaultdict
|
2019-08-13 15:39:13 -05:00
|
|
|
Dictionary of ``{parent: {product: fyield}}``
|
2019-08-02 17:51:33 -05:00
|
|
|
"""
|
2019-08-13 15:39:13 -05:00
|
|
|
return self.constant_yields
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
|
2019-08-13 16:38:24 -05:00
|
|
|
class FissionYieldCutoffHelper(TalliedFissionYieldHelper):
|
|
|
|
|
"""Helper that computes fission yields based on a cutoff energy
|
|
|
|
|
|
|
|
|
|
Tally fission rates above and below the cutoff energy.
|
|
|
|
|
Assume that all fissions below cutoff energy have use thermal fission
|
|
|
|
|
product yield distributions, while all fissions above use a faster
|
|
|
|
|
set of yield distributions.
|
|
|
|
|
|
|
|
|
|
Uses a limit of 20 MeV for tallying fission.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
chain_nuclides : iterable of openmc.deplete.Nuclide
|
2019-08-21 09:34:44 -05:00
|
|
|
Nuclides tracked in the depletion chain. All nuclides are
|
|
|
|
|
not required to have fission yield data.
|
2019-08-13 16:57:27 -05:00
|
|
|
n_bmats : int, optional
|
2019-08-13 16:38:24 -05:00
|
|
|
Number of burnable materials tracked in the problem
|
|
|
|
|
cutoff : float, optional
|
|
|
|
|
Cutoff energy in [eV] below which all fissions will be
|
|
|
|
|
use thermal yields. All other fissions will use a
|
|
|
|
|
faster set of yields. Default: 112 [eV]
|
|
|
|
|
thermal_energy : float, optional
|
|
|
|
|
Energy of yield data corresponding to thermal yields.
|
|
|
|
|
Default: 0.0253 [eV]
|
|
|
|
|
fast_energy : float, optional
|
|
|
|
|
Energy of yield data corresponding to fast yields.
|
|
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
|
|
|
|
n_bmats : int
|
2019-08-13 16:57:27 -05:00
|
|
|
Number of burnable materials tracked in the problem.
|
|
|
|
|
Must be set prior to generating tallies
|
2019-08-13 16:38:24 -05:00
|
|
|
thermal_yields : dict
|
|
|
|
|
Dictionary of the form ``{parent: {product: yield}}``
|
|
|
|
|
with thermal yields
|
|
|
|
|
fast_yields : dict
|
|
|
|
|
Dictionary of the form ``{parent: {product: yield}}``
|
|
|
|
|
with fast yields
|
2019-09-11 10:26:45 -05:00
|
|
|
constant_yields : collections.defaultdict
|
|
|
|
|
Fission yields for all nuclides that only have one set of
|
|
|
|
|
fission yield data. Dictionary of form ``{str: {str: float}}``
|
|
|
|
|
representing yields for ``{parent: {product: yield}}``. Default
|
|
|
|
|
return object is an empty dictionary
|
2019-08-13 16:38:24 -05:00
|
|
|
results : numpy.ndarray
|
|
|
|
|
Array of fission rate fractions with shape
|
|
|
|
|
``(n_mats, 2, n_nucs)``. ``results[:, 0]``
|
|
|
|
|
corresponds to the fraction of all fissions
|
2022-07-27 12:27:16 -05:00
|
|
|
that occurred below ``cutoff``. The number
|
2019-08-13 16:38:24 -05:00
|
|
|
of materials in the first axis corresponds
|
|
|
|
|
to the number of materials burned by the
|
2022-07-30 16:46:31 -05:00
|
|
|
:class:`openmc.deplete.CoupledOperator`
|
2019-08-13 16:38:24 -05:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, chain_nuclides, n_bmats, cutoff=112.0,
|
|
|
|
|
thermal_energy=0.0253, fast_energy=500.0e3):
|
|
|
|
|
check_type("cutoff", cutoff, Real)
|
|
|
|
|
check_type("thermal_energy", thermal_energy, Real)
|
|
|
|
|
check_type("fast_energy", fast_energy, Real)
|
|
|
|
|
check_greater_than("thermal_energy", thermal_energy, 0.0, equality=True)
|
|
|
|
|
check_greater_than("cutoff", cutoff, thermal_energy, equality=False)
|
|
|
|
|
check_greater_than("fast_energy", fast_energy, cutoff, equality=False)
|
2019-08-15 11:20:04 -05:00
|
|
|
self.n_bmats = n_bmats
|
|
|
|
|
super().__init__(chain_nuclides)
|
2019-08-13 16:38:24 -05:00
|
|
|
self._cutoff = cutoff
|
|
|
|
|
self._thermal_yields = {}
|
|
|
|
|
self._fast_yields = {}
|
2019-08-22 11:24:39 -05:00
|
|
|
convert_to_constant = set()
|
2019-08-13 16:38:24 -05:00
|
|
|
for name, nuc in self._chain_nuclides.items():
|
|
|
|
|
yields = nuc.yield_data
|
|
|
|
|
energies = nuc.yield_energies
|
|
|
|
|
thermal = yields.get(thermal_energy)
|
|
|
|
|
fast = yields.get(fast_energy)
|
2019-08-21 10:05:59 -05:00
|
|
|
if thermal is None or fast is None:
|
2019-08-22 11:24:39 -05:00
|
|
|
if cutoff <= energies[0]:
|
|
|
|
|
# use lowest energy yields as constant
|
|
|
|
|
self._constant_yields[name] = yields[energies[0]]
|
|
|
|
|
convert_to_constant.add(name)
|
|
|
|
|
continue
|
|
|
|
|
if cutoff >= energies[-1]:
|
|
|
|
|
# use highest energy yields as constant
|
|
|
|
|
self._constant_yields[name] = yields[energies[-1]]
|
|
|
|
|
convert_to_constant.add(name)
|
|
|
|
|
continue
|
Improve testing for FY helpers using C API
Provide a module-scoped fixture that creates a temporary
directory, adds simple settings, geometry, and material files,
and initializes the openmc C API. This fixture also provides
two empty openmc.capi.Material objects to help the helpers
generate meaningful tallies.
The main tests for AveragedFissionYieldHelper and
FissionYieldCutoffHelper now go through the built tallies
and examine the filters, nuclides, and scores. A helper
function produces mocked-like tally data based on filters,
nuclides, and scores found on a tally.
Pu239 with 0.0253 eV, 500 keV, and 2 MeV yields is included in
the nuclide_bundle fixture. This provides some additional
heterogeneity in the results, as the 2 MeV data is not present
on U235.
2019-08-21 17:20:19 -05:00
|
|
|
cutoff_ix = bisect.bisect_left(energies, cutoff)
|
|
|
|
|
# find closest energy to requested thermal, fast energies
|
2019-08-21 10:05:59 -05:00
|
|
|
if thermal is None:
|
Improve testing for FY helpers using C API
Provide a module-scoped fixture that creates a temporary
directory, adds simple settings, geometry, and material files,
and initializes the openmc C API. This fixture also provides
two empty openmc.capi.Material objects to help the helpers
generate meaningful tallies.
The main tests for AveragedFissionYieldHelper and
FissionYieldCutoffHelper now go through the built tallies
and examine the filters, nuclides, and scores. A helper
function produces mocked-like tally data based on filters,
nuclides, and scores found on a tally.
Pu239 with 0.0253 eV, 500 keV, and 2 MeV yields is included in
the nuclide_bundle fixture. This provides some additional
heterogeneity in the results, as the 2 MeV data is not present
on U235.
2019-08-21 17:20:19 -05:00
|
|
|
min_E = min(energies[:cutoff_ix],
|
|
|
|
|
key=lambda e: abs(e - thermal_energy))
|
|
|
|
|
thermal = yields[min_E]
|
2019-08-21 10:05:59 -05:00
|
|
|
if fast is None:
|
Improve testing for FY helpers using C API
Provide a module-scoped fixture that creates a temporary
directory, adds simple settings, geometry, and material files,
and initializes the openmc C API. This fixture also provides
two empty openmc.capi.Material objects to help the helpers
generate meaningful tallies.
The main tests for AveragedFissionYieldHelper and
FissionYieldCutoffHelper now go through the built tallies
and examine the filters, nuclides, and scores. A helper
function produces mocked-like tally data based on filters,
nuclides, and scores found on a tally.
Pu239 with 0.0253 eV, 500 keV, and 2 MeV yields is included in
the nuclide_bundle fixture. This provides some additional
heterogeneity in the results, as the 2 MeV data is not present
on U235.
2019-08-21 17:20:19 -05:00
|
|
|
min_E = min(energies[cutoff_ix:],
|
|
|
|
|
key=lambda e: abs(e - fast_energy))
|
|
|
|
|
fast = yields[min_E]
|
2019-08-13 16:38:24 -05:00
|
|
|
self._thermal_yields[name] = thermal
|
|
|
|
|
self._fast_yields[name] = fast
|
2019-08-22 11:24:39 -05:00
|
|
|
for name in convert_to_constant:
|
|
|
|
|
self._chain_nuclides.pop(name)
|
2019-08-13 16:38:24 -05:00
|
|
|
|
2019-08-13 17:47:34 -05:00
|
|
|
@classmethod
|
2019-08-21 09:34:44 -05:00
|
|
|
def from_operator(cls, operator, **kwargs):
|
2019-08-13 17:47:34 -05:00
|
|
|
"""Construct a helper from an operator
|
|
|
|
|
|
2019-08-21 09:34:44 -05:00
|
|
|
All keyword arguments should be identical to their counterpart
|
2019-08-13 17:47:34 -05:00
|
|
|
in the main ``__init__`` method
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2022-07-30 16:46:31 -05:00
|
|
|
operator : openmc.deplete.CoupledOperator
|
2019-08-13 17:47:34 -05:00
|
|
|
Operator with a chain and burnable materials
|
2019-08-21 09:34:44 -05:00
|
|
|
kwargs:
|
|
|
|
|
Additional keyword arguments to be used in construction
|
2019-08-13 17:47:34 -05:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
FissionYieldCutoffHelper
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
return cls(operator.chain.nuclides, len(operator.burnable_mats),
|
2019-08-21 09:34:44 -05:00
|
|
|
**kwargs)
|
2019-08-13 17:47:34 -05:00
|
|
|
|
2019-08-13 16:38:24 -05:00
|
|
|
def generate_tallies(self, materials, mat_indexes):
|
|
|
|
|
"""Use C API to produce a fission rate tally in burnable materials
|
|
|
|
|
|
2019-09-05 07:31:13 -05:00
|
|
|
Include a :class:`openmc.lib.EnergyFilter` to tally fission rates
|
2019-08-13 16:38:24 -05:00
|
|
|
above and below cutoff energy.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2019-09-05 07:31:13 -05:00
|
|
|
materials : iterable of :class:`openmc.lib.Material`
|
|
|
|
|
Materials to be used in :class:`openmc.lib.MaterialFilter`
|
2019-08-13 16:38:24 -05:00
|
|
|
mat_indexes : iterable of int
|
2019-08-14 18:02:27 -05:00
|
|
|
Indices of tallied materials that will have their fission
|
|
|
|
|
yields computed by this helper. Necessary as the
|
2022-07-30 16:46:31 -05:00
|
|
|
:class:`openmc.deplete.CoupledOperator` that uses this helper
|
2019-08-14 18:02:27 -05:00
|
|
|
may only burn a subset of all materials when running
|
|
|
|
|
in parallel mode.
|
2019-08-13 16:38:24 -05:00
|
|
|
"""
|
|
|
|
|
super().generate_tallies(materials, mat_indexes)
|
Improve testing for FY helpers using C API
Provide a module-scoped fixture that creates a temporary
directory, adds simple settings, geometry, and material files,
and initializes the openmc C API. This fixture also provides
two empty openmc.capi.Material objects to help the helpers
generate meaningful tallies.
The main tests for AveragedFissionYieldHelper and
FissionYieldCutoffHelper now go through the built tallies
and examine the filters, nuclides, and scores. A helper
function produces mocked-like tally data based on filters,
nuclides, and scores found on a tally.
Pu239 with 0.0253 eV, 500 keV, and 2 MeV yields is included in
the nuclide_bundle fixture. This provides some additional
heterogeneity in the results, as the 2 MeV data is not present
on U235.
2019-08-21 17:20:19 -05:00
|
|
|
energy_filter = EnergyFilter([0.0, self._cutoff, self._upper_energy])
|
2019-08-21 09:45:09 -05:00
|
|
|
self._fission_rate_tally.filters = (
|
|
|
|
|
self._fission_rate_tally.filters + [energy_filter])
|
2019-08-13 16:38:24 -05:00
|
|
|
|
|
|
|
|
def unpack(self):
|
|
|
|
|
"""Obtain fast and thermal fission fractions from tally"""
|
2020-05-13 16:19:12 -05:00
|
|
|
if not self._tally_nucs or self._local_indexes.size == 0:
|
2019-08-16 16:13:33 -05:00
|
|
|
self.results = None
|
|
|
|
|
return
|
2020-07-22 22:50:05 -05:00
|
|
|
fission_rates = self._fission_rate_tally.mean.reshape(
|
2019-08-14 17:39:57 -05:00
|
|
|
self.n_bmats, 2, len(self._tally_nucs))
|
2019-08-13 16:38:24 -05:00
|
|
|
self.results = fission_rates[self._local_indexes]
|
|
|
|
|
total_fission = self.results.sum(axis=1)
|
|
|
|
|
nz_mat, nz_nuc = total_fission.nonzero()
|
|
|
|
|
self.results[nz_mat, :, nz_nuc] /= total_fission[nz_mat, newaxis, nz_nuc]
|
|
|
|
|
|
|
|
|
|
def weighted_yields(self, local_mat_index):
|
|
|
|
|
"""Return fission yields for a specific material
|
|
|
|
|
|
|
|
|
|
For nuclides with both yield data above and below
|
|
|
|
|
the cutoff energy, the effective yield for nuclide ``A``
|
|
|
|
|
will be a weighted sum of fast and thermal yields. The
|
2019-08-13 17:12:31 -05:00
|
|
|
weights will be the fraction of ``A`` fission events
|
2019-08-13 16:38:24 -05:00
|
|
|
in the above and below the cutoff energy.
|
|
|
|
|
|
|
|
|
|
If ``A`` has fission product distribution ``F``
|
|
|
|
|
for fast fissions and ``T`` for thermal fissions, and
|
2019-08-13 17:12:31 -05:00
|
|
|
70% of ``A`` fissions are considered thermal, then
|
2019-08-13 16:38:24 -05:00
|
|
|
the effective fission product yield distributions
|
|
|
|
|
for ``A`` is ``0.7 * T + 0.3 * F``
|
2019-08-02 17:51:33 -05:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
local_mat_index : int
|
2019-08-13 16:38:24 -05:00
|
|
|
Index for specific burnable material. Effective
|
|
|
|
|
yields will be produced using
|
|
|
|
|
``self.results[local_mat_index]``
|
2019-08-08 08:50:32 -05:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
2019-09-11 10:26:45 -05:00
|
|
|
library : collections.defaultdict
|
2019-08-08 08:50:32 -05:00
|
|
|
Dictionary of ``{parent: {product: fyield}}``
|
2019-08-02 17:51:33 -05:00
|
|
|
"""
|
2019-08-13 16:38:24 -05:00
|
|
|
yields = self.constant_yields
|
2019-08-16 16:13:33 -05:00
|
|
|
if not self._tally_nucs:
|
|
|
|
|
return yields
|
|
|
|
|
rates = self.results[local_mat_index]
|
2019-08-13 16:38:24 -05:00
|
|
|
# iterate over thermal then fast yields, prefer __mul__ to __rmul__
|
2019-08-30 09:36:28 -05:00
|
|
|
for therm_frac, fast_frac, nuc in zip(rates[0], rates[1], self._tally_nucs):
|
|
|
|
|
yields[nuc.name] = (self._thermal_yields[nuc.name] * therm_frac
|
|
|
|
|
+ self._fast_yields[nuc.name] * fast_frac)
|
2019-08-13 16:38:24 -05:00
|
|
|
return yields
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def thermal_yields(self):
|
2019-08-21 10:05:59 -05:00
|
|
|
return deepcopy(self._thermal_yields)
|
2019-08-13 16:38:24 -05:00
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def fast_yields(self):
|
2019-08-21 10:05:59 -05:00
|
|
|
return deepcopy(self._fast_yields)
|
2019-08-14 17:39:57 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AveragedFissionYieldHelper(TalliedFissionYieldHelper):
|
|
|
|
|
r"""Class that computes fission yields based on average fission energy
|
|
|
|
|
|
2022-07-27 12:27:16 -05:00
|
|
|
Computes average energy at which fission events occurred with
|
2019-08-14 17:39:57 -05:00
|
|
|
|
|
|
|
|
.. math::
|
|
|
|
|
|
|
|
|
|
\bar{E} = \frac{
|
|
|
|
|
\int_0^\infty E\sigma_f(E)\phi(E)dE
|
|
|
|
|
}{
|
|
|
|
|
\int_0^\infty\sigma_f(E)\phi(E)dE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
If the average energy for a nuclide is below the lowest energy
|
|
|
|
|
with yield data, that set of fission yields is taken.
|
|
|
|
|
Conversely, if the average energy is above the highest energy
|
|
|
|
|
with yield data, that set of fission yields is used.
|
|
|
|
|
For the case where the average energy is between two sets
|
2019-08-14 18:02:27 -05:00
|
|
|
of yields, the effective fission yield computed by
|
|
|
|
|
linearly interpolating between yields provided at the
|
|
|
|
|
nearest energies above and below the average.
|
2019-08-14 17:39:57 -05:00
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
chain_nuclides : iterable of openmc.deplete.Nuclide
|
2019-08-21 09:34:44 -05:00
|
|
|
Nuclides tracked in the depletion chain. All nuclides are
|
|
|
|
|
not required to have fission yield data.
|
2019-08-14 17:39:57 -05:00
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
|
----------
|
2019-09-11 10:26:45 -05:00
|
|
|
constant_yields : collections.defaultdict
|
2019-08-14 17:39:57 -05:00
|
|
|
Fission yields for all nuclides that only have one set of
|
2019-09-11 10:26:45 -05:00
|
|
|
fission yield data. Dictionary of form ``{str: {str: float}}``
|
|
|
|
|
representing yields for ``{parent: {product: yield}}``. Default
|
|
|
|
|
return object is an empty dictionary
|
2019-08-14 17:39:57 -05:00
|
|
|
results : None or numpy.ndarray
|
|
|
|
|
If tallies have been generated and unpacked, then the array will
|
|
|
|
|
have shape ``(n_mats, n_tnucs)``, where ``n_mats`` is the number
|
|
|
|
|
of materials where fission reactions were tallied and ``n_tnucs``
|
|
|
|
|
is the number of nuclides with multiple sets of fission yields.
|
2019-08-30 09:36:28 -05:00
|
|
|
Data in the array are the average energy of fission events for
|
|
|
|
|
tallied nuclides across burnable materials.
|
2019-08-14 17:39:57 -05:00
|
|
|
"""
|
|
|
|
|
|
2019-08-15 11:20:04 -05:00
|
|
|
def __init__(self, chain_nuclides):
|
|
|
|
|
super().__init__(chain_nuclides)
|
2019-08-14 17:39:57 -05:00
|
|
|
self._weighted_tally = None
|
|
|
|
|
|
|
|
|
|
def generate_tallies(self, materials, mat_indexes):
|
|
|
|
|
"""Construct tallies to determine average energy of fissions
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2019-09-05 07:31:13 -05:00
|
|
|
materials : iterable of :class:`openmc.lib.Material`
|
|
|
|
|
Materials to be used in :class:`openmc.lib.MaterialFilter`
|
2019-08-14 17:39:57 -05:00
|
|
|
mat_indexes : iterable of int
|
2019-08-14 18:02:27 -05:00
|
|
|
Indices of tallied materials that will have their fission
|
|
|
|
|
yields computed by this helper. Necessary as the
|
2022-07-30 16:46:31 -05:00
|
|
|
:class:`openmc.deplete.CoupledOperator` that uses this helper
|
2019-08-14 18:02:27 -05:00
|
|
|
may only burn a subset of all materials when running
|
|
|
|
|
in parallel mode.
|
2019-08-14 17:39:57 -05:00
|
|
|
"""
|
|
|
|
|
super().generate_tallies(materials, mat_indexes)
|
|
|
|
|
fission_tally = self._fission_rate_tally
|
2019-08-21 09:45:09 -05:00
|
|
|
filters = fission_tally.filters
|
2019-08-14 17:39:57 -05:00
|
|
|
|
Improve testing for FY helpers using C API
Provide a module-scoped fixture that creates a temporary
directory, adds simple settings, geometry, and material files,
and initializes the openmc C API. This fixture also provides
two empty openmc.capi.Material objects to help the helpers
generate meaningful tallies.
The main tests for AveragedFissionYieldHelper and
FissionYieldCutoffHelper now go through the built tallies
and examine the filters, nuclides, and scores. A helper
function produces mocked-like tally data based on filters,
nuclides, and scores found on a tally.
Pu239 with 0.0253 eV, 500 keV, and 2 MeV yields is included in
the nuclide_bundle fixture. This provides some additional
heterogeneity in the results, as the 2 MeV data is not present
on U235.
2019-08-21 17:20:19 -05:00
|
|
|
ene_filter = EnergyFilter([0, self._upper_energy])
|
2019-08-21 09:45:09 -05:00
|
|
|
fission_tally.filters = filters + [ene_filter]
|
|
|
|
|
|
|
|
|
|
func_filter = EnergyFunctionFilter()
|
|
|
|
|
func_filter.set_data((0, self._upper_energy), (0, self._upper_energy))
|
2019-08-14 17:39:57 -05:00
|
|
|
weighted_tally = Tally()
|
2019-09-26 20:34:11 -04:00
|
|
|
weighted_tally.writable = False
|
2019-08-14 17:39:57 -05:00
|
|
|
weighted_tally.scores = ['fission']
|
2019-08-21 09:45:09 -05:00
|
|
|
weighted_tally.filters = filters + [func_filter]
|
2019-08-14 17:39:57 -05:00
|
|
|
self._weighted_tally = weighted_tally
|
|
|
|
|
|
2019-08-19 09:43:40 -05:00
|
|
|
def update_tally_nuclides(self, nuclides):
|
|
|
|
|
"""Tally nuclides with non-zero density and multiple yields
|
|
|
|
|
|
|
|
|
|
Must be run after :meth:`generate_tallies`.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
nuclides : iterable of str
|
|
|
|
|
Potential nuclides to be tallied, such as those with
|
|
|
|
|
non-zero density at this stage.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
nuclides : tuple of str
|
|
|
|
|
Union of input nuclides and those that have multiple sets
|
|
|
|
|
of yield data. Sorted by nuclide name
|
|
|
|
|
|
|
|
|
|
Raises
|
|
|
|
|
------
|
|
|
|
|
AttributeError
|
|
|
|
|
If tallies not generated
|
|
|
|
|
"""
|
|
|
|
|
tally_nucs = super().update_tally_nuclides(nuclides)
|
|
|
|
|
self._weighted_tally.nuclides = tally_nucs
|
|
|
|
|
return tally_nucs
|
|
|
|
|
|
2019-08-14 17:39:57 -05:00
|
|
|
def unpack(self):
|
|
|
|
|
"""Unpack tallies and populate :attr:`results` with average energies"""
|
2020-05-13 16:19:12 -05:00
|
|
|
if not self._tally_nucs or self._local_indexes.size == 0:
|
2019-08-16 16:13:33 -05:00
|
|
|
self.results = None
|
|
|
|
|
return
|
2019-08-14 17:39:57 -05:00
|
|
|
fission_results = (
|
2020-07-22 22:50:05 -05:00
|
|
|
self._fission_rate_tally.mean[self._local_indexes])
|
2019-08-14 17:39:57 -05:00
|
|
|
self.results = (
|
2020-07-22 22:50:05 -05:00
|
|
|
self._weighted_tally.mean[self._local_indexes]).copy()
|
2019-08-14 17:39:57 -05:00
|
|
|
nz_mat, nz_nuc = fission_results.nonzero()
|
|
|
|
|
self.results[nz_mat, nz_nuc] /= fission_results[nz_mat, nz_nuc]
|
|
|
|
|
|
|
|
|
|
def weighted_yields(self, local_mat_index):
|
|
|
|
|
"""Return fission yields for a specific material
|
|
|
|
|
|
|
|
|
|
Use the computed average energy of fission
|
|
|
|
|
events to determine fission yields. If average
|
|
|
|
|
energy is between two sets of yields, linearly
|
2022-07-27 12:27:16 -05:00
|
|
|
interpolate between the two.
|
2019-08-14 17:39:57 -05:00
|
|
|
Otherwise take the closet set of yields.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
local_mat_index : int
|
|
|
|
|
Index for specific burnable material. Effective
|
|
|
|
|
yields will be produced using
|
|
|
|
|
``self.results[local_mat_index]``
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
2019-09-11 10:26:45 -05:00
|
|
|
library : collections.defaultdict
|
|
|
|
|
Dictionary of ``{parent: {product: fyield}}``. Default return
|
|
|
|
|
value is an empty dictionary
|
2019-08-14 17:39:57 -05:00
|
|
|
"""
|
2019-08-16 16:13:33 -05:00
|
|
|
if not self._tally_nucs:
|
|
|
|
|
return self.constant_yields
|
2019-09-11 10:26:45 -05:00
|
|
|
mat_yields = defaultdict(dict)
|
2019-08-14 17:39:57 -05:00
|
|
|
average_energies = self.results[local_mat_index]
|
|
|
|
|
for avg_e, nuc in zip(average_energies, self._tally_nucs):
|
|
|
|
|
nuc_energies = nuc.yield_energies
|
|
|
|
|
if avg_e <= nuc_energies[0]:
|
|
|
|
|
mat_yields[nuc.name] = nuc.yield_data[nuc_energies[0]]
|
|
|
|
|
continue
|
|
|
|
|
if avg_e >= nuc_energies[-1]:
|
|
|
|
|
mat_yields[nuc.name] = nuc.yield_data[nuc_energies[-1]]
|
|
|
|
|
continue
|
|
|
|
|
# in-between two energies
|
|
|
|
|
# linear search since there are usually ~3 energies
|
|
|
|
|
for ix, ene in enumerate(nuc_energies[:-1]):
|
|
|
|
|
if nuc_energies[ix + 1] > avg_e:
|
|
|
|
|
break
|
|
|
|
|
lower, upper = nuc_energies[ix:ix + 2]
|
|
|
|
|
fast_frac = (avg_e - lower) / (upper - lower)
|
|
|
|
|
mat_yields[nuc.name] = (
|
|
|
|
|
nuc.yield_data[lower] * (1 - fast_frac)
|
|
|
|
|
+ nuc.yield_data[upper] * fast_frac)
|
|
|
|
|
mat_yields.update(self.constant_yields)
|
|
|
|
|
return mat_yields
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2019-08-21 09:34:44 -05:00
|
|
|
def from_operator(cls, operator, **kwargs):
|
2019-08-14 17:39:57 -05:00
|
|
|
"""Return a new helper with data from an operator
|
|
|
|
|
|
2019-08-21 09:34:44 -05:00
|
|
|
All keyword arguments should be identical to their counterpart
|
|
|
|
|
in the main ``__init__`` method
|
|
|
|
|
|
2019-08-14 17:39:57 -05:00
|
|
|
Parameters
|
|
|
|
|
----------
|
2022-07-30 16:46:31 -05:00
|
|
|
operator : openmc.deplete.CoupledOperator
|
2019-08-14 17:39:57 -05:00
|
|
|
Operator with a depletion chain
|
2019-08-21 09:34:44 -05:00
|
|
|
kwargs :
|
|
|
|
|
Additional keyword arguments to be used in construction
|
2019-08-14 17:39:57 -05:00
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
AveragedFissionYieldHelper
|
|
|
|
|
"""
|
2019-08-15 11:20:04 -05:00
|
|
|
return cls(operator.chain.nuclides)
|