added chi-prompt, velocity, and prompt-neutron-lifetime mgxs

This commit is contained in:
Sam Shaner 2016-07-04 17:15:51 -04:00
parent 4198b6b514
commit 710f8a75b8
3 changed files with 865 additions and 19 deletions

View file

@ -213,7 +213,7 @@ def check_less_than(name, value, maximum, equality=False):
raise ValueError(msg)
def check_greater_than(name, value, minimum, equality=False):
"""Ensure that an object's value is less than a given value.
"""Ensure that an object's value is greater than a given value.
Parameters
----------

View file

@ -564,7 +564,7 @@ class Filter(object):
# Initialize dictionary to build Pandas Multi-index column
filter_dict = {}
# Append Mesh ID as outermost index of mult-index
# Append Mesh ID as outermost index of multi-index
mesh_key = 'mesh {0}'.format(self.mesh.id)
# Find mesh dimensions - use 3D indices for simplicity

View file

@ -34,7 +34,10 @@ MGXS_TYPES = ['total',
'nu-scatter matrix',
'multiplicity matrix',
'nu-fission matrix',
'chi']
'chi',
'chi-prompt',
'velocity',
'prompt-neutron-lifetime']
# Supported domain types
@ -427,7 +430,11 @@ 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'}
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',
'velocity', 'prompt-neutron-lifetime'}
The type of multi-group cross section object to return
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
@ -482,6 +489,12 @@ class MGXS(object):
mgxs = NuFissionMatrixXS(domain, domain_type, energy_groups)
elif mgxs_type == 'chi':
mgxs = Chi(domain, domain_type, energy_groups)
elif mgxs_type == 'chi-prompt':
mgxs = ChiPrompt(domain, domain_type, energy_groups)
elif mgxs_type == 'velocity':
mgxs = Velocity(domain, domain_type, energy_groups)
elif mgxs_type == 'prompt-neutron-lifetime':
mgxs = PromptNeutronLifetime(domain, domain_type, energy_groups)
mgxs.by_nuclide = by_nuclide
mgxs.name = name
@ -1935,7 +1948,7 @@ class MatrixMGXS(MGXS):
class TotalXS(MGXS):
r"""A total multi-group cross section.
"""A total 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
@ -2044,7 +2057,7 @@ class TotalXS(MGXS):
class TransportXS(MGXS):
r"""A transport-corrected total multi-group cross section.
"""A transport-corrected total 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
@ -2187,7 +2200,7 @@ class TransportXS(MGXS):
class NuTransportXS(TransportXS):
r"""A transport-corrected total multi-group cross section which
"""A transport-corrected total multi-group cross section which
accounts for neutron multiplicity in scattering reactions.
This class can be used for both OpenMC input generation and tally data
@ -2300,7 +2313,7 @@ class NuTransportXS(TransportXS):
class AbsorptionXS(MGXS):
r"""An absorption multi-group cross section.
"""An absorption multi-group cross section.
Absorption is defined as all reactions that do not produce secondary
neutrons (disappearance) plus fission reactions.
@ -2413,7 +2426,7 @@ class AbsorptionXS(MGXS):
class CaptureXS(MGXS):
r"""A capture multi-group cross section.
"""A capture multi-group cross section.
The neutron capture reaction rate is defined as the difference between
OpenMC's 'absorption' and 'fission' reaction rate score types. This includes
@ -2541,7 +2554,7 @@ class CaptureXS(MGXS):
class FissionXS(MGXS):
r"""A fission multi-group cross section.
"""A fission 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
@ -2651,7 +2664,7 @@ class FissionXS(MGXS):
class NuFissionXS(MGXS):
r"""A fission neutron production multi-group cross section.
"""A fission neutron production 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
@ -2762,7 +2775,7 @@ class NuFissionXS(MGXS):
class KappaFissionXS(MGXS):
r"""A recoverable fission energy production rate multi-group cross section.
"""A recoverable fission energy production rate multi-group cross section.
The recoverable energy per fission, :math:`\kappa`, is defined as the
fission product kinetic energy, prompt and delayed neutron kinetic energies,
@ -2878,7 +2891,7 @@ class KappaFissionXS(MGXS):
class ScatterXS(MGXS):
r"""A scattering multi-group cross section.
"""A scattering multi-group cross section.
The scattering cross section is defined as the difference between the total
and absorption cross sections.
@ -2991,7 +3004,7 @@ class ScatterXS(MGXS):
class NuScatterXS(MGXS):
r"""A scattering neutron production multi-group cross section.
"""A scattering neutron production multi-group cross section.
The neutron production from scattering is defined as the average number of
neutrons produced from all neutron-producing reactions except for fission.
@ -3110,7 +3123,7 @@ class NuScatterXS(MGXS):
class ScatterMatrixXS(MatrixMGXS):
r"""A scattering matrix multi-group cross section for one or more Legendre
"""A scattering matrix multi-group cross section for one or more Legendre
moments.
This class can be used for both OpenMC input generation and tally data
@ -3444,7 +3457,7 @@ class ScatterMatrixXS(MatrixMGXS):
subdomains='all', nuclides='all', moment='all',
xs_type='macro', order_groups='increasing',
row_column='inout', value='mean', **kwargs):
r"""Returns an array of multi-group cross sections.
"""Returns an array of multi-group cross sections.
This method constructs a 2D NumPy array for the requested scattering
matrix data data for one or more energy groups and subdomains.
@ -3890,7 +3903,7 @@ class NuScatterMatrixXS(ScatterMatrixXS):
class MultiplicityMatrixXS(MatrixMGXS):
r"""The scattering multiplicity matrix.
"""The scattering multiplicity matrix.
This class can be used for both OpenMC input generation and tally data
post-processing to compute spatially-homogenized and energy-integrated
@ -4044,7 +4057,7 @@ class MultiplicityMatrixXS(MatrixMGXS):
class NuFissionMatrixXS(MatrixMGXS):
r"""A fission production matrix multi-group cross section.
"""A fission 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
@ -4159,7 +4172,7 @@ class NuFissionMatrixXS(MatrixMGXS):
class Chi(MGXS):
r"""The fission spectrum.
"""The fission spectrum.
This class can be used for both OpenMC input generation and tally data
post-processing to compute spatially-homogenized and energy-integrated
@ -4615,3 +4628,836 @@ class Chi(MGXS):
df['std. dev.'] *= np.tile(densities, tile_factor)
return df
class ChiPrompt(Chi):
"""The prompt fission spectrum.
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:`ChiPrompt.energy_groups` and
:attr:`ChiPrompt.domain` properties. Tallies for the flux and appropriate
reaction rates over the specified domain are generated automatically via the
:attr:`ChiPrompt.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:`ChiPrompt.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,\rightarrow g}^p \phi \rangle &= \int_{r \in V} dr
\int_{4\pi} d\Omega' \int_0^\infty dE' \int_{E_g}^{E_{g-1}} dE \; \chi(E)
\nu\sigma_f (r, E') \psi(r, E', \Omega')\\
\langle \nu\sigma_f^p \phi \rangle &= \int_{r \in V} dr \int_{4\pi}
d\Omega' \int_0^\infty dE' \int_0^\infty dE \; \chi(E) \nu\sigma_f^p (r,
E') \psi(r, E', \Omega') \\
\chi_g^p &= \frac{\langle \nu\sigma_{f,\rightarrow g}^p \phi \rangle}{\langle
\nu\sigma_f^p \phi \rangle}
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
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
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
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', '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:`ChiPrompt.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,
groups=None, by_nuclide=False, name=''):
super(ChiPrompt, self).__init__(domain, domain_type, groups, by_nuclide, name)
self._rxn_type = 'chi-prompt'
@property
def scores(self):
return ['delayed-nu-fission', 'delayed-nu-fission',
'nu-fission', 'nu-fission']
@property
def filters(self):
# Create the non-domain specific Filters for the Tallies
group_edges = self.energy_groups.group_edges
energyout = openmc.Filter('energyout', group_edges)
energyin = openmc.Filter('energy', [group_edges[0], group_edges[-1]])
return [[energyin], [energyout], [energyin], [energyout]]
@property
def tally_keys(self):
return ['delayed-nu-fission-in', 'delayed-nu-fission-out',
'nu-fission-in', 'nu-fission-out']
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
self._rxn_rate_tally = self.tallies['nu-fission-out'] - \
self.tallies['delayed-nu-fission-out']
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
@property
def xs_tally(self):
if self._xs_tally is None:
delayed_nu_fission_in = self.tallies['delayed-nu-fission-in']
nu_fission_in = self.tallies['nu-fission-in']
prompt_nu_fission_in = nu_fission_in - delayed_nu_fission_in
# Remove coarse energy filter to keep it out of tally arithmetic
energy_filter = prompt_nu_fission_in.find_filter('energy')
prompt_nu_fission_in.remove_filter(energy_filter)
# Compute chi
self._xs_tally = self.rxn_rate_tally / prompt_nu_fission_in
super(ChiPrompt, self)._compute_xs()
# Add the coarse energy filter back to the nu-fission tally
prompt_nu_fission_in.filters.append(energy_filter)
return self._xs_tally
def get_slice(self, nuclides=[], groups=[]):
"""Build a sliced ChiDelayed for the specified nuclides and energy
groups.
This method constructs a new MGXS to encapsulate a subset of the data
represented by this MGXS. The subset of data to include in the tally
slice is determined by the nuclides and energy groups specified in
the input parameters.
Parameters
----------
nuclides : list of str
A list of nuclide name strings
(e.g., ['U-235', 'U-238']; default is [])
groups : list of Integral
A list of energy group indices starting at 1 for the high energies
(e.g., [1, 2, 3]; default is [])
Returns
-------
openmc.mgxs.MGXS
A new MGXS which encapsulates the subset of data requested
for the nuclide(s) and/or energy group(s) requested in the
parameters.
"""
# Temporarily remove energy filter from delayed-nu-fission-in since its
# group structure will work in super MGXS.get_slice(...) method
delayed_nu_fission_in = self.tallies['delayed-nu-fission-in']
nu_fission_in = self.tallies['nu-fission-in']
prompt_nu_fission_in = nu_fission_in - delayed_nu_fission_in
energy_filter = prompt_nu_fission_in.find_filter('energy')
prompt_nu_fission_in.remove_filter(energy_filter)
# Call super class method and null out derived tallies
slice_xs = super(ChiPrompt, self).get_slice(nuclides, groups)
slice_xs._rxn_rate_tally = None
slice_xs._xs_tally = None
# Slice energy groups if needed
if len(groups) != 0:
filter_bins = []
for group in groups:
group_bounds = self.energy_groups.get_group_bounds(group)
filter_bins.append(group_bounds)
filter_bins = [tuple(filter_bins)]
# Slice nu-fission-out tally along energyout filter
prompt_nu_fission_out = slice_xs.tallies['nu-fission-out'] - \
slice_xs.tallies['delayed-nu-fission-out']
tally_slice = prompt_nu_fission_out\
.get_slice(filters=['energyout'],
filter_bins=filter_bins)
slice_xs._tallies['prompt-nu-fission-out'] = tally_slice
# Add energy filter back to nu-fission-in tallies
slice_xs._tallies['prompt-nu-fission-in'].add_filter(energy_filter)
slice_xs.sparse = self.sparse
return slice_xs
def merge(self, other):
"""Merge another ChiPrompt with this one
If results have been loaded from a statepoint, then ChiPrompt are only
mergeable along one and only one of energy groups or nuclides.
Parameters
----------
other : openmc.mgxs.MGXS
MGXS to merge with this one
Returns
-------
merged_mgxs : openmc.mgxs.MGXS
Merged MGXS
"""
if not self.can_merge(other):
raise ValueError('Unable to merge ChiPrompt')
return super(ChiPrompt, self).merge(other)
def get_xs(self, groups='all', subdomains='all', nuclides='all',
xs_type='macro', order_groups='increasing',
value='mean', **kwargs):
"""Returns an array of the fission spectrum.
This method constructs a 2D NumPy array for the requested multi-group
cross section data data for one or more energy groups and subdomains.
Parameters
----------
groups : Iterable of Integral or 'all'
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., ['U-235', 'U-238']). 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'}
This parameter is not relevant for chi but is included here to
mirror the parent MGXS.get_xs(...) class method
order_groups: {'increasing', 'decreasing'}
Return the cross section indexed according to increasing or
decreasing energy groups (decreasing or increasing energies).
Defaults to 'increasing'.
value : {'mean', 'std_dev', 'rel_err'}
A string for the type of value to return. Defaults to 'mean'.
Returns
-------
numpy.ndarray
A NumPy array of the multi-group cross section indexed in the order
each group, subdomain and nuclide 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'])
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=2)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(groups, basestring):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append('energyout')
filter_bins.append((self.energy_groups.get_group_bounds(group),))
# If chi delayed was computed for each nuclide in the domain
if self.by_nuclide:
# Get the sum as the fission source weighted average chi for all
# nuclides in the domain
if nuclides == 'sum' or nuclides == ['sum']:
# Retrieve the fission production tallies
prompt_nu_fission_in = self.tallies['nu-fission-in'] - \
self.tallies['delayed-nu-fission-in']
prompt_nu_fission_out = self.tallies['nu-fission-out'] - \
self.tallies['delayed-nu-fission-out']
# Sum out all nuclides
nuclides = self.get_all_nuclides()
prompt_nu_fission_in = prompt_nu_fission_in.summation\
(nuclides=nuclides)
prompt_nu_fission_out = prompt_nu_fission_out.summation\
(nuclides=nuclides)
# Remove coarse energy filter to keep it out of tally arithmetic
energy_filter = prompt_nu_fission_in.find_filter('energy')
prompt_nu_fission_in.remove_filter(energy_filter)
# Compute chi and store it as the xs_tally attribute so we can
# use the generic get_xs(...) method
xs_tally = prompt_nu_fission_out / prompt_nu_fission_in
# Add the coarse energy filter back to the nu-fission tally
prompt_nu_fission_in.filters.append(energy_filter)
xs = xs_tally.get_values(filters=filters,
filter_bins=filter_bins, value=value)
# Get chi delayed for all nuclides in the domain
elif nuclides == 'all':
nuclides = self.get_all_nuclides()
xs = self.xs_tally.get_values(filters=filters,
filter_bins=filter_bins,
nuclides=nuclides, value=value)
# Get chi prompt for user-specified nuclides in the domain
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
xs = self.xs_tally.get_values(filters=filters,
filter_bins=filter_bins,
nuclides=nuclides, value=value)
# If chi prompt was computed as an average of nuclides in the domain
else:
xs = self.xs_tally.get_values(filters=filters,
filter_bins=filter_bins, value=value)
# Reverse data if user requested increasing energy groups since
# tally data is stored in order of increasing energies
if order_groups == 'increasing':
# Reshape tally data array with separate axes for domain and energy
if groups == 'all':
num_groups = self.num_groups
else:
num_groups = len(groups)
num_subdomains = int(xs.shape[0] / num_groups)
new_shape = (num_subdomains, num_groups) + xs.shape[1:]
xs = np.reshape(xs, new_shape)
# Reverse energies to align with increasing energy groups
xs = xs[:, ::-1, :]
# Eliminate trivial dimensions
xs = np.squeeze(xs)
xs = np.atleast_1d(xs)
xs = np.nan_to_num(xs)
return xs
class Velocity(MGXS):
"""A velocity 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 velocity cross sections for multi-group neutronics
calculations. At a minimum, one needs to set the
:attr:`Velocity.energy_groups` and :attr:`Velocity.domain`
properties. Tallies for the flux and appropriate reaction rates over the
specified domain are generated automatically via the
:attr:`Velocity.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:`Velocity.xs_tally` property.
For a spatial domain :math:`V` and energy group :math:`[E_g,E_{g-1}]`, the
velocity cross section is calculated as:
.. math::
\frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \;
\psi (r, E, \Omega)}{\int_{r \in V} dr \int_{4\pi}
d\Omega \int_{E_g}^{E_{g-1}} dE \; \frac{\psi (r, E, \Omega)}{v (r, E)}}.
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
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
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
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', '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:`AbsorptionXS.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(Velocity, self).__init__(domain, domain_type,
groups, by_nuclide, name)
self._rxn_type = 'velocity'
@property
def scores(self):
return ['inverse-velocity', 'flux']
@property
def tally_keys(self):
return ['inverse-velocity', 'flux']
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
self._rxn_rate_tally = self.tallies['flux']
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
@property
def xs_tally(self):
if self._xs_tally is None:
inverse_velocity = self.tallies['inverse-velocity']
# Compute the velocity
self._xs_tally = self.rxn_rate_tally / inverse_velocity
super(Velocity, self)._compute_xs()
return self._xs_tally
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):
"""Print 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., ['U-235', 'U-238']).
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)
else:
subdomains = [self.domain.id]
# Construct a collection of the nuclides to report
if self.by_nuclide:
if nuclides == 'all':
nuclides = self.get_all_nuclides()
elif nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
else:
nuclides = ['sum']
cv.check_value('xs_type', xs_type, ['macro'])
# Build header for string with type and domain info
string = 'Multi-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)
# If cross section data has not been computed, only print string header
if self.tallies is None:
print(string)
return
# 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 nuclide != 'sum':
string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide)
# Build header for cross section type
string += '{0: <16}\n'.format\
('\tVelocity [cm/second]:')
template = '{0: <12}Group {1} [{2: <10} - {3: <10}MeV]:\t'
# 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])
average = self.get_xs([group], [subdomain], [nuclide],
xs_type=xs_type, value='mean')
rel_err = self.get_xs([group], [subdomain], [nuclide],
xs_type=xs_type, value='rel_err')
average = average.flatten()[0]
rel_err = rel_err.flatten()[0] * 100.
string += '{:.2e} +/- {:1.2e}%'.format(average, rel_err)
string += '\n'
string += '\n'
string += '\n'
print(string)
class PromptNeutronLifetime(MGXS):
"""The prompt neutron lifetime.
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:`PromptNeutronLifetime.energy_groups`
and :attr:`PromptNeutronLifetime.domain` properties. Tallies for the flux
and appropriate reaction rates over the specified domain are generated
automatically via the :attr:`PromptNeutronLifetime.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:`PromptNeutronLifetime.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::
\frac{\int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \;
\frac{\psi (r, E, \Omega)}{v (r, E)}}{\int_{r \in V} dr \int_{4\pi}
d\Omega \int_{E_g}^{E_{g-1}} dE \; \nu\sigma_f (r, E') \psi(r, E', \Omega')}.
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
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
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
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', '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:`ChiDelayed.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,
groups=None, by_nuclide=False, name=''):
super(PromptNeutronLifetime, self).__init__(domain, domain_type, groups,
by_nuclide, name)
self._rxn_type = 'prompt-neutron-lifetime'
@property
def scores(self):
return ['nu-fission', 'inverse-velocity']
@property
def tally_keys(self):
return ['nu-fission', 'inverse-velocity']
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
self._rxn_rate_tally = self.tallies['inverse-velocity']
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
@property
def xs_tally(self):
if self._xs_tally is None:
nu_fission = self.tallies['nu-fission']
# Compute the prompt neutron lifetime
self._xs_tally = self.rxn_rate_tally / nu_fission
super(PromptNeutronLifetime, self)._compute_xs()
return self._xs_tally
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):
"""Print 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., ['U-235', 'U-238']).
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)
else:
subdomains = [self.domain.id]
# Construct a collection of the nuclides to report
if self.by_nuclide:
if nuclides == 'all':
nuclides = self.get_all_nuclides()
elif nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
else:
nuclides = ['sum']
cv.check_value('xs_type', xs_type, ['macro'])
# Build header for string with type and domain info
string = 'Multi-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)
# If cross section data has not been computed, only print string header
if self.tallies is None:
print(string)
return
# 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 nuclide != 'sum':
string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide)
# Build header for cross section type
string += '{0: <16}\n'.format\
('\tPrompt Neutron Lifetime [seconds]:')
template = '{0: <12}Group {1} [{2: <10} - {3: <10}MeV]:\t'
# 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])
average = self.get_xs([group], [subdomain], [nuclide],
xs_type=xs_type, value='mean')
rel_err = self.get_xs([group], [subdomain], [nuclide],
xs_type=xs_type, value='rel_err')
average = average.flatten()[0]
rel_err = rel_err.flatten()[0] * 100.
string += '{:.2e} +/- {:1.2e}%'.format(average, rel_err)
string += '\n'
string += '\n'
string += '\n'
print(string)