added prompt and delayed nu-fission matrices

This commit is contained in:
Sam Shaner 2016-10-13 17:50:43 -04:00
parent 01c28052da
commit 80a6095cfe
5 changed files with 995 additions and 36 deletions

View file

@ -527,7 +527,7 @@ class Library(object):
----------
domain : Material or Cell or Universe or Integral
The material, cell, or universe object of interest (or its ID)
mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'delayed-nu-fission', 'chi-delayed', 'beta'}
mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'prompt-nu-fission matrix', 'delayed-nu-fission', 'delayed-nu-fission matrix', 'chi-delayed', 'beta'}
The type of multi-group cross section object to return
Returns
@ -987,12 +987,24 @@ class Library(object):
nuclide=[nuclide],
subdomain=subdomain)
if 'prompt-nu-fission matrix' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'prompt-nu-fission matrix')
xsdata.set_prompt_nu_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide],
subdomain=subdomain)
if 'delayed-nu-fission' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'delayed-nu-fission')
xsdata.set_delayed_nu_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide],
subdomain=subdomain)
if 'delayed-nu-fission matrix' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'delayed-nu-fission matrix')
xsdata.set_delayed_nu_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide],
subdomain=subdomain)
if 'beta' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'nu-fission')
xsdata.set_beta_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide],

View file

@ -22,7 +22,8 @@ if sys.version_info[0] >= 3:
MDGXS_TYPES = ['delayed-nu-fission',
'chi-delayed',
'beta',
'decay-rate']
'decay-rate',
'delayed-nu-fission matrix']
# Maximum number of delayed groups, from src/constants.F90
MAX_DELAYED_GROUPS = 8
@ -215,7 +216,7 @@ class MDGXS(MGXS):
Parameters
----------
mdgxs_type : {'delayed-nu-fission', 'chi-delayed', 'beta', 'decay-rate'}
mdgxs_type : {'delayed-nu-fission', 'chi-delayed', 'beta', 'decay-rate', 'delayed-nu-fission matrix'}
The type of multi-delayed-group cross section object to return
domain : openmc.Material or openmc.Cell or openmc.Universe or
openmc.Mesh
@ -253,6 +254,9 @@ class MDGXS(MGXS):
mdgxs = Beta(domain, domain_type, energy_groups, delayed_groups)
elif mdgxs_type == 'decay-rate':
mdgxs = DecayRate(domain, domain_type, energy_groups, delayed_groups)
elif mdgxs_type == 'delayed-nu-fission matrix':
mdgxs = DelayedNuFissionMatrixXS(domain, domain_type, energy_groups,
delayed_groups)
mdgxs.by_nuclide = by_nuclide
mdgxs.name = name
@ -1732,3 +1736,616 @@ class DecayRate(MDGXS):
super(DecayRate, self)._compute_xs()
return self._xs_tally
class MatrixMDGXS(MDGXS):
"""An abstract multi-delayed-group cross section for some energy group and
delayed group structure within some spatial domain. This class is
specifically intended for cross sections which depend on both the incoming
and outgoing energy groups and are therefore represented by matrices.
An example of this is the delayed-nu-fission matrix.
This class can be used for both OpenMC input generation and tally data
post-processing to compute spatially-homogenized and energy-integrated
multi-group and multi-delayed-group cross sections for downstream neutronics
calculations.
NOTE: Users should instantiate the subclasses of this abstract class.
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
The domain type for spatial homogenization
energy_groups : openmc.mgxs.EnergyGroups
The energy group structure for energy condensation
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
name : str, optional
Name of the multi-group cross section. Used as a label to identify
tallies in OpenMC 'tallies.xml' file.
delayed_groups : list of int
Delayed groups to filter out the xs
Attributes
----------
name : str, optional
Name of the multi-group cross section
rxn_type : str
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure for energy condensation
delayed_groups : list of int
Delayed groups to filter out the xs
tally_trigger : openmc.Trigger
An (optional) tally precision trigger given to each tally used to
compute the cross section
scores : list of str
The scores in each tally used to compute the multi-group cross section
filters : list of openmc.Filter
The filters in each tally used to compute the multi-group cross section
tally_keys : list of str
The keys into the tallies dictionary for each tally used to compute
the multi-group cross section
estimator : {'tracklength', 'collision', 'analog'}
The tally estimator used to compute the multi-group cross section
tallies : collections.OrderedDict
OpenMC tallies needed to compute the multi-group cross section
rxn_rate_tally : openmc.Tally
Derived tally for the reaction rate tally used in the numerator to
compute the multi-group cross section. This attribute is None
unless the multi-group cross section has been computed.
xs_tally : openmc.Tally
Derived tally for the multi-group cross section. This attribute
is None unless the multi-group cross section has been computed.
num_subdomains : int
The number of subdomains is unity for 'material', 'cell' and 'universe'
domain types. This is equal to the number of cell instances
for 'distribcell' domain types (it is equal to unity prior to loading
tally data from a statepoint file) and the number of mesh cells for
'mesh' domain types.
num_nuclides : int
The number of nuclides for which the multi-group cross section is
being tracked. This is unity if the by_nuclide attribute is False.
nuclides : Iterable of str or 'sum'
The optional user-specified nuclides for which to compute cross
sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides
are not specified by the user, all nuclides in the spatial domain
are included. This attribute is 'sum' if by_nuclide is false.
sparse : bool
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
for compressed data storage
loaded_sp : bool
Whether or not a statepoint file has been loaded with tally data
derived : bool
Whether or not the MGXS is merged from one or more other MGXS
hdf5_key : str
The key used to index multi-group cross sections in an HDF5 data store
"""
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
@property
def filters(self):
# Create the non-domain specific Filters for the Tallies
group_edges = self.energy_groups.group_edges
energy = openmc.EnergyFilter(group_edges)
energyout = openmc.EnergyoutFilter(group_edges)
if self.delayed_groups != None:
delayed = openmc.DelayedGroupFilter(self.delayed_groups)
return [[energy], [delayed, energy, energyout]]
else:
return [[energy], [energy, energyout]]
def get_xs(self, in_groups='all', out_groups='all',
subdomains='all', nuclides='all',
xs_type='macro', order_groups='increasing',
row_column='inout', value='mean', delayed_groups='all',
squeeze=True, **kwargs):
"""Returns an array of multi-group cross sections.
This method constructs a 4D NumPy array for the requested
multi-group cross section data for one or more subdomains
(1st dimension), delayed groups (2nd dimension), energy groups in
(3rd dimension), energy groups out (4th dimension), and nuclides
(5th dimension).
Parameters
----------
in_groups : Iterable of Integral or 'all'
Incoming energy groups of interest. Defaults to 'all'.
out_groups : Iterable of Integral or 'all'
Outgoing energy groups of interest. Defaults to 'all'.
subdomains : Iterable of Integral or 'all'
Subdomain IDs of interest. Defaults to 'all'.
nuclides : Iterable of str or 'all' or 'sum'
A list of nuclide name strings (e.g., ['U235', 'U238']). The
special string 'all' will return the cross sections for all
nuclides in the spatial domain. The special string 'sum' will
return the cross section summed over all nuclides. Defaults to
'all'.
xs_type: {'macro', 'micro'}
Return the macro or micro cross section in units of cm^-1 or barns.
Defaults to 'macro'.
order_groups: {'increasing', 'decreasing'}
Return the cross section indexed according to increasing or
decreasing energy groups (decreasing or increasing energies).
Defaults to 'increasing'.
row_column: {'inout', 'outin'}
Return the cross section indexed first by incoming group and
second by outgoing group ('inout'), or vice versa ('outin').
Defaults to 'inout'.
value : {'mean', 'std_dev', 'rel_err'}
A string for the type of value to return. Defaults to 'mean'.
delayed_groups : list of int or 'all'
Delayed groups of interest. Defaults to 'all'.
squeeze : bool
A boolean representing whether to eliminate the extra dimensions
of the multi-dimensional array to be returned. Defaults to True.
Returns
-------
ndarray
A NumPy array of the multi-group cross section indexed in the order
each group and subdomain is listed in the parameters.
Raises
------
ValueError
When this method is called before the multi-group cross section is
computed from tally data.
"""
cv.check_value('value', value, ['mean', 'std_dev', 'rel_err'])
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
# FIXME: Unable to get microscopic xs for mesh domain because the mesh
# cells do not know the nuclide densities in each mesh cell.
if self.domain_type == 'mesh' and xs_type == 'micro':
msg = 'Unable to get micro xs for mesh domain since the mesh ' \
'cells do not know the nuclide densities in each mesh cell.'
raise ValueError(msg)
filters = []
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral,
max_depth=3)
for subdomain in subdomains:
filters.append(_DOMAIN_TO_FILTER[self.domain_type])
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(in_groups, basestring):
cv.check_iterable_type('groups', in_groups, Integral)
for group in in_groups:
filters.append(openmc.EnergyFilter)
filter_bins.append((
self.energy_groups.get_group_bounds(group),))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(out_groups, basestring):
cv.check_iterable_type('groups', out_groups, Integral)
for group in out_groups:
filters.append(openmc.EnergyoutFilter)
filter_bins.append((
self.energy_groups.get_group_bounds(group),))
# Construct list of delayed group tuples for all requested groups
if not isinstance(delayed_groups, basestring):
cv.check_type('delayed groups', delayed_groups, list, int)
for delayed_group in delayed_groups:
filters.append(openmc.DelayedGroupFilter)
filter_bins.append((delayed_group,))
# Construct a collection of the nuclides to retrieve from the xs tally
if self.by_nuclide:
if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']:
query_nuclides = self.get_nuclides()
else:
query_nuclides = nuclides
else:
query_nuclides = ['total']
# Use tally summation if user requested the sum for all nuclides
if nuclides == 'sum' or nuclides == ['sum']:
xs_tally = self.xs_tally.summation(nuclides=query_nuclides)
xs = xs_tally.get_values(filters=filters, filter_bins=filter_bins,
value=value)
else:
xs = self.xs_tally.get_values(filters=filters,
filter_bins=filter_bins,
nuclides=query_nuclides, value=value)
# Divide by atom number densities for microscopic cross sections
if xs_type == 'micro':
if self.by_nuclide:
densities = self.get_nuclide_densities(nuclides)
else:
densities = self.get_nuclide_densities('sum')
if value == 'mean' or value == 'std_dev':
xs /= densities[np.newaxis, :, np.newaxis]
# Eliminate the trivial score dimension
xs = np.squeeze(xs, axis=len(xs.shape) - 1)
xs = np.nan_to_num(xs)
if in_groups == 'all':
num_in_groups = self.num_groups
else:
num_in_groups = len(in_groups)
if out_groups == 'all':
num_out_groups = self.num_groups
else:
num_out_groups = len(out_groups)
if delayed_groups == 'all':
num_delayed_groups = self.num_delayed_groups
else:
num_delayed_groups = len(delayed_groups)
# Reshape tally data array with separate axes for domain and energy
num_subdomains = int(xs.shape[0] / (num_in_groups * num_out_groups *
num_delayed_groups))
new_shape = (num_subdomains, num_delayed_groups, num_in_groups,
num_out_groups)
new_shape += xs.shape[1:]
xs = np.reshape(xs, new_shape)
# Transpose the matrix if requested by user
if row_column == 'outin':
xs = np.swapaxes(xs, 2, 3)
# Reverse data if user requested increasing energy groups since
# tally data is stored in order of increasing energies
if order_groups == 'increasing':
xs = xs[:, :, ::-1, ::-1, :]
if squeeze:
xs = np.squeeze(xs)
xs = np.atleast_2d(xs)
return xs
def get_slice(self, nuclides=[], in_groups=[], out_groups=[],
delayed_groups=[]):
"""Build a sliced MatrixMDGXS object for the specified nuclides and
energy groups.
This method constructs a new MdGXS to encapsulate a subset of the data
represented by this MdGXS. The subset of data to include in the tally
slice is determined by the nuclides, energy groups, and delayed groups
specified in the input parameters.
Parameters
----------
nuclides : list of str
A list of nuclide name strings
(e.g., ['U235', 'U238']; default is [])
in_groups : list of int
A list of incoming energy group indices starting at 1 for the high
energies (e.g., [1, 2, 3]; default is [])
out_groups : list of int
A list of outgoing energy group indices starting at 1 for the high
energies (e.g., [1, 2, 3]; default is [])
delayed_groups : list of int
A list of delayed group indices
(e.g., [1, 2, 3]; default is [])
Returns
-------
openmc.mgxs.MatrixMDGXS
A new MatrixMDGXS object which encapsulates the subset of data
requested for the nuclide(s) and/or energy group(s) requested in
the parameters.
"""
# Call super class method and null out derived tallies
slice_xs = super(MatrixMDGXS, self).get_slice(nuclides, in_groups,
delayed_groups)
slice_xs._rxn_rate_tally = None
slice_xs._xs_tally = None
# Slice outgoing energy groups if needed
if len(out_groups) != 0:
filter_bins = []
for group in out_groups:
group_bounds = self.energy_groups.get_group_bounds(group)
filter_bins.append(group_bounds)
filter_bins = [tuple(filter_bins)]
# Slice each of the tallies across energyout groups
for tally_type, tally in slice_xs.tallies.items():
if tally.contains_filter(openmc.EnergyoutFilter):
tally_slice = tally.get_slice(
filters=[openmc.EnergyoutFilter],
filter_bins=filter_bins)
slice_xs.tallies[tally_type] = tally_slice
slice_xs.sparse = self.sparse
return slice_xs
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):
"""Prints a string representation for the multi-group cross section.
Parameters
----------
subdomains : Iterable of Integral or 'all'
The subdomain IDs of the cross sections to include in the report.
Defaults to 'all'.
nuclides : Iterable of str or 'all' or 'sum'
The nuclides of the cross-sections to include in the report. This
may be a list of nuclide name strings (e.g., ['U235', 'U238']).
The special string 'all' will report the cross sections for all
nuclides in the spatial domain. The special string 'sum' will
report the cross sections summed over all nuclides. Defaults to
'all'.
xs_type: {'macro', 'micro'}
Return the macro or micro cross section in units of cm^-1 or barns.
Defaults to 'macro'.
"""
# Construct a collection of the subdomains to report
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
elif self.domain_type == 'mesh':
xyz = [range(1, x+1) for x in self.domain.dimension]
subdomains = list(itertools.product(*xyz))
else:
subdomains = [self.domain.id]
# Construct a collection of the nuclides to report
if self.by_nuclide:
if nuclides == 'all':
nuclides = self.get_nuclides()
if nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
else:
nuclides = ['sum']
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
# Build header for string with type and domain info
string = 'Multi-Delayed-Group XS\n'
string += '{0: <16}=\t{1}\n'.format('\tReaction Type', self.rxn_type)
string += '{0: <16}=\t{1}\n'.format('\tDomain Type', self.domain_type)
string += '{0: <16}=\t{1}\n'.format('\tDomain ID', self.domain.id)
# Generate the header for an individual XS
xs_header = '\tCross Sections [{0}]:'.format(self.get_units(xs_type))
# If cross section data has not been computed, only print string header
if self.tallies is None:
print(string)
return
string += '{0: <16}\n'.format('\tEnergy Groups:')
template = '{0: <12}Group {1} [{2: <10} - {3: <10}MeV]\n'
# Loop over energy groups ranges
for group in range(1, self.num_groups + 1):
bounds = self.energy_groups.get_group_bounds(group)
string += template.format('', group, bounds[0], bounds[1])
# Loop over all subdomains
for subdomain in subdomains:
if self.domain_type == 'distribcell':
string += \
'{0: <16}=\t{1}\n'.format('\tSubdomain', subdomain)
# Loop over all Nuclides
for nuclide in nuclides:
# Build header for nuclide type
if xs_type != 'sum':
string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide)
# Build header for cross section type
string += '{0: <16}\n'.format(xs_header)
if self.delayed_groups != None:
for delayed_group in self.delayed_groups:
template = '{0: <12}Delayed Group {1}:\t'
string += template.format('', delayed_group)
string += '\n'
template = '{0: <12}Group {1} -> Group {2}:\t\t'
# Loop over incoming/outgoing energy groups ranges
for in_group in range(1, self.num_groups + 1):
for out_group in range(1, self.num_groups + 1):
string += template.format('', in_group, out_group)
average = \
self.get_xs([in_group], [out_group],
[subdomain], [nuclide],
xs_type=xs_type,
value='mean',
delayed_groups=[delayed_group])
rel_err = \
self.get_xs([in_group], [out_group],
[subdomain], [nuclide],
xs_type=xs_type,
value='rel_err',
delayed_groups=[delayed_group])
average = average.flatten()[0]
rel_err = rel_err.flatten()[0] * 100.
string += '{:1.2e} +/- {:1.2e}%'.format(average,
rel_err)
string += '\n'
string += '\n'
string += '\n'
else:
template = '{0: <12}Group {1} -> Group {2}:\t\t'
# Loop over incoming/outgoing energy groups ranges
for in_group in range(1, self.num_groups + 1):
for out_group in range(1, self.num_groups + 1):
string += template.format('', in_group, out_group)
average = \
self.get_xs([in_group], [out_group],
[subdomain], [nuclide],
xs_type=xs_type, value='mean')
rel_err = \
self.get_xs([in_group], [out_group],
[subdomain], [nuclide],
xs_type=xs_type, value='rel_err')
average = average.flatten()[0]
rel_err = rel_err.flatten()[0] * 100.
string += '{:1.2e} +/- {:1.2e}%'.format(average,
rel_err)
string += '\n'
string += '\n'
string += '\n'
string += '\n'
print(string)
class DelayedNuFissionMatrixXS(MatrixMDGXS):
r"""A fission delayed neutron production matrix multi-group cross section.
This class can be used for both OpenMC input generation and tally data
post-processing to compute spatially-homogenized and energy-integrated
multi-group fission neutron production cross sections for multi-group
neutronics calculations. At a minimum, one needs to set the
:attr:`DelayedNuFissionMatrixXS.energy_groups` and
:attr:`DelayedNuFissionMatrixXS.domain` properties. Tallies for the flux and
appropriate reaction rates over the specified domain are generated
automatically via the :attr:`DelayedNuFissionMatrixXS.tallies` property,
which can then be appended to a :class:`openmc.Tallies` instance.
For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the
necessary data to compute multi-group cross sections from a
:class:`openmc.StatePoint` instance. The derived multi-group cross section
can then be obtained from the :attr:`DelayedNuFissionMatrixXS.xs_tally`
property.
For a spatial domain :math:`V`, energy group :math:`[E_g,E_{g-1}]`, and
delayed group :math:`d`, the fission delayed neutron production cross
section is calculated as:
.. math::
\langle \nu\sigma_{f,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr
\int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{E_g}^{E_{g-1}} dE
\; \chi(E) \nu\sigma_f^d (r, E') \psi(r, E', \Omega')\\
\langle \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega
\int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\
\nu\sigma_{f,g'\rightarrow g} &= \frac{\langle \nu\sigma_{f,g'\rightarrow
g}^d \phi \rangle}{\langle \phi \rangle}
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
The domain type for spatial homogenization
groups : openmc.mgxs.EnergyGroups
The energy group structure for energy condensation
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
name : str, optional
Name of the multi-group cross section. Used as a label to identify
tallies in OpenMC 'tallies.xml' file.
delayed_groups : list of int
Delayed groups to filter out the xs
Attributes
----------
name : str, optional
Name of the multi-group cross section
rxn_type : str
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure for energy condensation
delayed_groups : list of int
Delayed groups to filter out the xs
tally_trigger : openmc.Trigger
An (optional) tally precision trigger given to each tally used to
compute the cross section
scores : list of str
The scores in each tally used to compute the multi-group cross section
filters : list of openmc.Filter
The filters in each tally used to compute the multi-group cross section
tally_keys : list of str
The keys into the tallies dictionary for each tally used to compute
the multi-group cross section
estimator : {'tracklength', 'analog'}
The tally estimator used to compute the multi-group cross section
tallies : collections.OrderedDict
OpenMC tallies needed to compute the multi-group cross section. The keys
are strings listed in the :attr:`DelayedNuFissionXS.tally_keys` property
and values are instances of :class:`openmc.Tally`.
rxn_rate_tally : openmc.Tally
Derived tally for the reaction rate tally used in the numerator to
compute the multi-group cross section. This attribute is None
unless the multi-group cross section has been computed.
xs_tally : openmc.Tally
Derived tally for the multi-group cross section. This attribute
is None unless the multi-group cross section has been computed.
num_subdomains : int
The number of subdomains is unity for 'material', 'cell' and 'universe'
domain types. When the This is equal to the number of cell instances
for 'distribcell' domain types (it is equal to unity prior to loading
tally data from a statepoint file).
num_nuclides : int
The number of nuclides for which the multi-group cross section is
being tracked. This is unity if the by_nuclide attribute is False.
nuclides : Iterable of str or 'sum'
The optional user-specified nuclides for which to compute cross
sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides
are not specified by the user, all nuclides in the spatial domain
are included. This attribute is 'sum' if by_nuclide is false.
sparse : bool
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
for compressed data storage
loaded_sp : bool
Whether or not a statepoint file has been loaded with tally data
derived : bool
Whether or not the MGXS is merged from one or more other MGXS
hdf5_key : str
The key used to index multi-group cross sections in an HDF5 data store
"""
def __init__(self, domain=None, domain_type=None, energy_groups=None,
delayed_groups=None, by_nuclide=False, name=''):
super(DelayedNuFissionMatrixXS, self).__init__(domain, domain_type,
energy_groups,
delayed_groups,
by_nuclide, name)
self._rxn_type = 'delayed-nu-fission'
self._hdf5_key = 'delayed-nu-fission matrix'
self._estimator = 'analog'
self._valid_estimators = ['analog']

View file

@ -38,7 +38,8 @@ MGXS_TYPES = ['total',
'chi',
'chi-prompt',
'inverse-velocity',
'prompt-nu-fission']
'prompt-nu-fission',
'prompt-nu-fission matrix']
# Supported domain types
DOMAIN_TYPES = ['cell',
@ -453,7 +454,7 @@ class MGXS(object):
Parameters
----------
mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', 'chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission'}
mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', 'chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'prompt-nu-fission matrix'}
The type of multi-group cross section object to return
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
The domain for spatial homogenization
@ -514,6 +515,8 @@ class MGXS(object):
mgxs = InverseVelocity(domain, domain_type, energy_groups)
elif mgxs_type == 'prompt-nu-fission':
mgxs = PromptNuFissionXS(domain, domain_type, energy_groups)
elif mgxs_type == 'prompt-nu-fission matrix':
mgxs = PromptNuFissionMatrixXS(domain, domain_type, energy_groups)
mgxs.by_nuclide = by_nuclide
mgxs.name = name
@ -1530,7 +1533,7 @@ class MGXS(object):
# Override energy groups bounds with indices
all_groups = np.arange(self.num_groups, 0, -1, dtype=np.int)
all_groups = np.repeat(all_groups, len(query_nuclides))
all_groups = np.repeat(all_groups, len(query_nuclides))
if 'energy low [MeV]' in df and 'energyout low [MeV]' in df:
df.rename(columns={'energy low [MeV]': 'group in'},
inplace=True)
@ -5140,3 +5143,120 @@ class PromptNuFissionXS(MGXS):
super(PromptNuFissionXS, self).__init__(domain, domain_type, groups,
by_nuclide, name)
self._rxn_type = 'prompt-nu-fission'
class PromptNuFissionMatrixXS(MatrixMGXS):
r"""A prompt fission neutron production matrix multi-group cross section.
This class can be used for both OpenMC input generation and tally data
post-processing to compute spatially-homogenized and energy-integrated
multi-group cross sections for multi-group neutronics calculations. At a
minimum, one needs to set the :attr:`PromptNuFissionMatrixXS.energy_groups`
and :attr:`PromptNuFissionMatrixXS.domain` properties. Tallies for the flux
and appropriate reaction rates over the specified domain are generated
automatically via the :attr:`PromptNuFissionMatrixXS.tallies` property,
which can then be appended to a :class:`openmc.Tallies` instance.
For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the
necessary data to compute multi-group cross sections from a
:class:`openmc.StatePoint` instance. The derived multi-group cross section
can then be obtained from the :attr:`PromptNuFissionMatrixXS.xs_tally`
property.
For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the
fission spectrum is calculated as:
.. math::
\langle \nu\sigma_{f,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr
\int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{E_g}^{E_{g-1}} dE
\; \chi(E) \nu\sigma_f^p (r, E') \psi(r, E', \Omega')\\
\langle \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega
\int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\
\nu\sigma_{f,g'\rightarrow g} &= \frac{\langle \nu\sigma_{f,g'\rightarrow
g}^p \phi \rangle}{\langle \phi \rangle}
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
The domain type for spatial homogenization
groups : openmc.mgxs.EnergyGroups
The energy group structure for energy condensation
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
name : str, optional
Name of the multi-group cross section. Used as a label to identify
tallies in OpenMC 'tallies.xml' file.
Attributes
----------
name : str, optional
Name of the multi-group cross section
rxn_type : str
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure for energy condensation
tally_trigger : openmc.Trigger
An (optional) tally precision trigger given to each tally used to
compute the cross section
scores : list of str
The scores in each tally used to compute the multi-group cross section
filters : list of openmc.Filter
The filters in each tally used to compute the multi-group cross section
tally_keys : list of str
The keys into the tallies dictionary for each tally used to compute
the multi-group cross section
estimator : {'tracklength', 'collision', 'analog'}
The tally estimator used to compute the multi-group cross section
tallies : collections.OrderedDict
OpenMC tallies needed to compute the multi-group cross section. The keys
are strings listed in the :attr:`PromptNuFissionXS.tally_keys` property
and values are instances of :class:`openmc.Tally`.
rxn_rate_tally : openmc.Tally
Derived tally for the reaction rate tally used in the numerator to
compute the multi-group cross section. This attribute is None
unless the multi-group cross section has been computed.
xs_tally : openmc.Tally
Derived tally for the multi-group cross section. This attribute
is None unless the multi-group cross section has been computed.
num_subdomains : int
The number of subdomains is unity for 'material', 'cell' and 'universe'
domain types. This is equal to the number of cell instances
for 'distribcell' domain types (it is equal to unity prior to loading
tally data from a statepoint file).
num_nuclides : int
The number of nuclides for which the multi-group cross section is
being tracked. This is unity if the by_nuclide attribute is False.
nuclides : Iterable of str or 'sum'
The optional user-specified nuclides for which to compute cross
sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides
are not specified by the user, all nuclides in the spatial domain
are included. This attribute is 'sum' if by_nuclide is false.
sparse : bool
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
for compressed data storage
loaded_sp : bool
Whether or not a statepoint file has been loaded with tally data
derived : bool
Whether or not the MGXS is merged from one or more other MGXS
hdf5_key : str
The key used to index multi-group cross sections in an HDF5 data store
"""
def __init__(self, domain=None, domain_type=None,
groups=None, by_nuclide=False, name=''):
super(PromptNuFissionMatrixXS, self).__init__(domain, domain_type,
groups, by_nuclide, name)
self._rxn_type = 'prompt-nu-fission'
self._hdf5_key = 'prompt-nu-fission matrix'
self._estimator = 'analog'
self._valid_estimators = ['analog']

View file

@ -839,9 +839,14 @@ class XSdata(object):
# Get the accepted shapes for this xs
if self.representation is 'isotropic':
shapes = [(self.energy_groups.num_groups,)]
shapes = [(self.energy_groups.num_groups,),
(self.energy_groups.num_groups,
self.energy_groups.num_groups)]
else:
shapes = [(self.num_polar, self.num_azimuthal,
self.energy_groups.num_groups),
(self.num_polar, self.num_azimuthal,
self.energy_groups.num_groups,
self.energy_groups.num_groups)]
# Convert to a numpy array so we can easily get the shape for checking
@ -877,9 +882,14 @@ class XSdata(object):
# Get the accepted shapes for this xs
if self.representation is 'isotropic':
shapes = [(self.delayed_groups, self.energy_groups.num_groups,)]
shapes = [(self.delayed_groups, self.energy_groups.num_groups,),
(self.delayed_groups, self.energy_groups.num_groups,
self.energy_groups.num_groups)]
else:
shapes = [(self.delayed_groups, self.num_polar, self.num_azimuthal,
self.energy_groups.num_groups),
(self.delayed_groups, self.num_polar, self.num_azimuthal,
self.energy_groups.num_groups,
self.energy_groups.num_groups)]
# Convert to a numpy array so we can easily get the shape for checking
@ -1124,12 +1134,13 @@ class XSdata(object):
def set_prompt_nu_fission_mgxs(self, prompt_nu_fission, temperature=294.,
nuclide='total', xs_type='macro',
subdomain=None):
"""This method allows for an openmc.mgxs.PromptNuFissionXS to be used to
set the prompt-nu-fission cross section for this XSdata object.
"""This method allows for an openmc.mgxs.PromptNuFissionXS or
openmc.mgxs.PromptNuFissionMatrixXS to be used to set the
prompt-nu-fission cross section for this XSdata object.
Parameters
----------
prompt_nu_fission: openmc.mgxs.PromptNuFissionXS
prompt_nu_fission: openmc.mgxs.PromptNuFissionXS or openmc.mgxs.PromptNuFissionMatrixXS
MGXS Object containing the prompt-nu-fission cross section
for the domain of interest.
temperature : float
@ -1153,7 +1164,8 @@ class XSdata(object):
"""
check_type('prompt_nu_fission', prompt_nu_fission,
(openmc.mgxs.PromptNuFissionXS,))
(openmc.mgxs.PromptNuFissionXS,
openmc.mgxs.PromptNuFissionMatrixXS))
check_value('energy_groups', prompt_nu_fission.energy_groups,
[self.energy_groups])
check_value('domain_type', prompt_nu_fission.domain_type,
@ -1177,12 +1189,13 @@ class XSdata(object):
def set_delayed_nu_fission_mgxs(self, delayed_nu_fission, temperature=294.,
nuclide='total', xs_type='macro',
subdomain=None):
"""This method allows for an openmc.mgxs.DelayedNuFissionXS to be used
to set the delayed-nu-fission cross section for this XSdata object.
"""This method allows for an openmc.mgxs.DelayedNuFissionXS or
openmc.mgxs.DelayedNuFissionMatrixXS to be used to set the
delayed-nu-fission cross section for this XSdata object.
Parameters
----------
delayed_nu_fission: openmc.mgxs.DelayedNuFissionXS
delayed_nu_fission: openmc.mgxs.DelayedNuFissionXS or openmc.mgxs.DelayedNuFissionMatrixXS
MGXS Object containing the delayed-nu-fission cross section
for the domain of interest.
temperature : float
@ -1206,7 +1219,8 @@ class XSdata(object):
"""
check_type('delayed_nu_fission', delayed_nu_fission,
(openmc.mgxs.DelayedNuFissionXS,))
(openmc.mgxs.DelayedNuFissionXS,
openmc.mgxs.DelayedNuFissionMatrixXS))
check_value('energy_groups', delayed_nu_fission.energy_groups,
[self.energy_groups])
check_value('delayed_groups', delayed_nu_fission.num_delayed_groups,

View file

@ -425,7 +425,7 @@ module mgxs_header
integer(HID_T) :: xsdata, xsdata_grp, scatt_grp, ndims
integer(HSIZE_T) :: dims(2)
real(8), allocatable :: temp_arr(:), temp_2d(:, :)
real(8), allocatable :: temp_beta(:, :)
real(8), allocatable :: temp_beta(:, :), temp_3d(:, :, :)
real(8) :: dmu, mu, norm, chi_sum
integer :: order, order_dim, gin, gout, l, imu, length
type(VectorInt) :: temps_to_read
@ -642,11 +642,6 @@ module mgxs_header
call fatal_error("nu-fission must be provided as a 1D or 2D &
&array")
end if
print *, xs % prompt_nu_fission
print *, xs % delayed_nu_fission
print *, xs % chi_prompt
print *, xs % chi_delayed
end if
! If chi_prompt provided, set chi_prompt
@ -755,9 +750,57 @@ module mgxs_header
! If prompt-nu-fission present, set prompt_nu_fission
if (check_dataset(xsdata_grp, "prompt-nu-fission")) then
! Set prompt_nu_fission
call read_dataset(xs % prompt_nu_fission, xsdata_grp, &
"prompt-nu-fission")
! Get the dimensions of the prompt-nu-fission dataset
xsdata = open_dataset(xsdata_grp, "prompt-nu-fission")
call get_ndims(xsdata, ndims)
! If prompt-nu-fission is a vector
if (ndims == 1) then
! Set prompt_nu_fission
call read_dataset(xs % prompt_nu_fission, xsdata_grp, &
"prompt-nu-fission")
! If prompt-nu-fission is a matrix, set prompt_nu_fission and
! chi_prompt.
else if (ndims == 2) then
! chi_prompt is embedded in prompt_nu_fission -> extract
! chi_prompt
allocate(temp_arr(energy_groups * energy_groups))
call read_dataset(temp_arr, xsdata_grp, "prompt-nu-fission")
allocate(temp_2d(energy_groups, energy_groups))
temp_2d = reshape(temp_arr, (/energy_groups, energy_groups/))
! Deallocate temporary 1D array for prompt_nu_fission matrix
deallocate(temp_arr)
! Set the vector prompt-nu-fission from the matrix
! prompt-nu-fission
do gin = 1, energy_groups
xs % prompt_nu_fission(gin) = sum(temp_2d(:, gin))
end do
! Now pull out information needed for chi
xs % chi_prompt(:, :) = temp_2d
! Deallocate temporary 2D array for nu_fission matrix
deallocate(temp_2d)
! Normalize chi so its CDF goes to 1
do gin = 1, energy_groups
chi_sum = sum(xs % chi_prompt(:, gin))
if (chi_sum == ZERO) then
call fatal_error("Encountered chi prompt for a group &
&that sums to zero")
else
xs % chi_prompt(:, gin) = xs % chi_prompt(:, gin) / chi_sum
end if
end do
else
call fatal_error("prompt-nu-fission must be provided as a 1D &
&or 2D array")
end if
end if
! If delayed-nu-fission provided, set delayed_nu_fission. If
@ -836,9 +879,52 @@ module mgxs_header
! Deallocate temporary array for delayed_nu_fission matrix
deallocate(temp_arr)
! If delayed nu-fission is a 3D matrix, set delayed_nu_fission
! and chi_delayed.
else if (ndims == 3) then
! chi_delayed is embedded in delayed_nu_fission -> extract
! chi_delayed
allocate(temp_arr(delayed_groups * energy_groups * &
energy_groups))
call read_dataset(temp_arr, xsdata_grp, "delayed-nu-fission")
allocate(temp_3d(energy_groups, energy_groups, delayed_groups))
temp_3d = reshape(temp_arr, (/energy_groups, energy_groups, &
delayed_groups/))
! Deallocate temporary 1D array for delayed_nu_fission matrix
deallocate(temp_arr)
! Set the 2D delayed-nu-fission matrix and 3D chi_dealyed matrix
! from the 3D delayed-nu-fission matrix
do dg = 1, delayed_groups
do gin = 1, energy_groups
xs % delayed_nu_fission(gin, dg) = sum(temp_3d(:, gin, dg))
do gout = 1, energy_groups
xs % chi_delayed(gout, gin, dg) = temp_3d(gout, gin, dg)
end do
end do
end do
! Normalize chi_delayed so its CDF goes to 1
do dg = 1, delayed_groups
do gin = 1, energy_groups
chi_sum = sum(xs % chi_delayed(:, gin, dg))
if (chi_sum == ZERO) then
call fatal_error("Encountered chi delayed for a group &
&that sums to zero")
else
xs % chi_delayed(:, gin, dg) = &
xs % chi_delayed(:, gin, dg) / chi_sum
end if
end do
end do
! Deallocate temporary 3D matrix for delayed_nu_fission
deallocate(temp_3d)
else
call fatal_error("delayed-nu-fission must be provided as a &
&1D or 2D array")
&1D, 2D, or 3D array")
end if
end if
@ -1107,7 +1193,8 @@ module mgxs_header
integer(HSIZE_T) :: dims(4)
integer, allocatable :: int_arr(:)
real(8), allocatable :: temp_1d(:), temp_3d(:, :, :)
real(8), allocatable :: temp_4d(:, :, :, :), temp_beta(:, :, :, :)
real(8), allocatable :: temp_4d(:, :, :, :), temp_5d(:, :, :, :, :)
real(8), allocatable :: temp_beta(:, :, :, :)
real(8) :: dmu, mu, norm
integer :: order, order_dim, gin, gout, l, imu, dg
type(VectorInt) :: temps_to_read
@ -1401,16 +1488,70 @@ module mgxs_header
! If prompt-nu-fission present, set prompt_nu_fission
if (check_dataset(xsdata_grp, "prompt-nu-fission")) then
! Allocate temporary array for prompt-nu-fission
allocate(temp_1d(energy_groups * this % n_azi * this % n_pol))
! Get the dimensions of the prompt-nu-fission dataset
xsdata = open_dataset(xsdata_grp, "prompt-nu-fission")
call get_ndims(xsdata, ndims)
! Read prompt-nu-fission
call read_dataset(temp_1d, xsdata_grp, "prompt-nu-fission")
xs % prompt_nu_fission = reshape(temp_1d, (/energy_groups, &
this % n_azi, this % n_pol/))
! If prompt-nu-fission is a vector for each azi and pol
if (ndims == 3) then
! Deallocate temporary array for prompt_nu_fission
deallocate(temp_1d)
! Set prompt_nu_fission
call read_dataset(xs % prompt_nu_fission, xsdata_grp, &
"prompt-nu-fission")
! If prompt-nu-fission is a matrix for each azi and pol,
! set prompt_nu_fission and chi_prompt.
else if (ndims == 4) then
! chi_prompt is embedded in prompt_nu_fission -> extract
! chi_prompt
allocate(temp_1d(energy_groups * energy_groups &
* this % n_azi * this % n_pol))
allocate(temp_4d(energy_groups, energy_groups, this % n_azi, &
this % n_pol))
call read_dataset(temp_1d, xsdata_grp, "prompt-nu-fission")
temp_4d = reshape(temp_1d, (/energy_groups, energy_groups, &
this % n_azi, this % n_pol/))
! Deallocate temporary 1D array for prompt_nu_fission matrix
deallocate(temp_1d)
! Set the vector prompt-nu-fission from the matrix
! prompt-nu-fission
do ipol = 1, this % n_pol
do iazi = 1, this % n_azi
do gin = 1, energy_groups
xs % prompt_nu_fission(gin, iazi, ipol) = &
sum(temp_4d(:, gin, iazi, ipol))
end do
end do
end do
! Now pull out information needed for chi
xs % chi_prompt(:, :, :, :) = temp_4d
! Deallocate temporary 4D array for nu_fission matrix
deallocate(temp_4d)
! Normalize chi so its CDF goes to 1
do ipol = 1, this % n_pol
do iazi = 1, this % n_azi
do gin = 1, energy_groups
chi_sum = sum(xs % chi_prompt(:, gin, iazi, ipol))
if (chi_sum == ZERO) then
call fatal_error("Encountered chi prompt for a group &
&that sums to zero")
else
xs % chi_prompt(:, gin, iazi, ipol) = &
xs % chi_prompt(:, gin, iazi, ipol) / chi_sum
end if
end do
end do
end do
else
call fatal_error("prompt-nu-fission must be provided as a 3D &
&or 4D array")
end if
end if
! If delayed-nu-fission provided, set delayed_nu_fission. If
@ -1500,9 +1641,64 @@ module mgxs_header
! Deallocate temporary array for delayed_nu_fission matrix
deallocate(temp_1d)
! If delayed nu-fission is a 5D matrix, set delayed_nu_fission
! and chi_delayed.
else if (ndims == 5) then
! chi_delayed is embedded in delayed_nu_fission -> extract
! chi_delayed
allocate(temp_1d(delayed_groups * energy_groups * &
energy_groups * this % n_azi * this % n_pol))
allocate(temp_5d(energy_groups, energy_groups, delayed_groups, &
this % n_azi, this % n_pol))
call read_dataset(temp_1d, xsdata_grp, "delayed-nu-fission")
temp_5d = reshape(temp_1d, (/energy_groups, energy_groups, &
delayed_groups, this % n_azi, this % n_pol/))
! Deallocate temporary 1D array for delayed_nu_fission matrix
deallocate(temp_1d)
! Set the 4D delayed-nu-fission matrix and 5D chi_dealyed matrix
! from the 5D delayed-nu-fission matrix
do ipol = 1, this % n_pol
do iazi = 1, this % n_azi
do dg = 1, delayed_groups
do gin = 1, energy_groups
xs % delayed_nu_fission(gin, dg, iazi, ipol) = &
sum(temp_5d(:, gin, dg, iazi, ipol))
do gout = 1, energy_groups
xs % chi_delayed(gout, gin, dg, iazi, ipol) = &
temp_5d(gout, gin, dg, iazi, ipol)
end do
end do
end do
end do
end do
! Normalize chi_delayed so its CDF goes to 1
do ipol = 1, this % n_pol
do iazi = 1, this % n_azi
do dg = 1, delayed_groups
do gin = 1, energy_groups
chi_sum = sum(xs % chi_delayed(:, gin, dg, iazi, ipol))
if (chi_sum == ZERO) then
call fatal_error("Encountered chi delayed for a group&
& that sums to zero")
else
xs % chi_delayed(:, gin, dg, iazi, ipol) = &
xs % chi_delayed(:, gin, dg, iazi, ipol) / &
chi_sum
end if
end do
end do
end do
end do
! Deallocate temporary 5D matrix for delayed_nu_fission
deallocate(temp_5d)
else
call fatal_error("delayed-nu-fission must be provided as a &
&1D or 2D array")
&3D, 4D, or 5D array")
end if
end if