mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 22:26:08 -04:00
Made Python API MultiGroupXS object xs_type attribute by_nuclide; nuclides paramter can now be all, sum or a list of nuclides
This commit is contained in:
parent
8f19a3fdc3
commit
25e67baa32
1 changed files with 309 additions and 191 deletions
|
|
@ -44,6 +44,8 @@ class MultiGroupXS(object):
|
|||
The domain type for spatial homogenization
|
||||
energy_groups : EnergyGroups
|
||||
The energy group structure for energy condensation
|
||||
by_nuclide : bool
|
||||
If true, computes multi-group 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.
|
||||
|
|
@ -54,6 +56,8 @@ class MultiGroupXS(object):
|
|||
Name of the multi-group cross section
|
||||
rxn_type : str
|
||||
Reaction type (e.g., 'total', 'nu-fission', etc.)
|
||||
by_nuclide : bool
|
||||
If true, computes multi-group cross sections for each nuclide in domain
|
||||
domain : Material or Cell or Universe
|
||||
Domain for spatial homogenization
|
||||
domain_type : {'material', 'cell', 'distribcell', 'universe'}
|
||||
|
|
@ -74,11 +78,11 @@ class MultiGroupXS(object):
|
|||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
energy_groups=None, xs_type='macro', name=''):
|
||||
energy_groups=None, by_nuclide=False, name=''):
|
||||
|
||||
self._name = ''
|
||||
self._rxn_type = None
|
||||
self._xs_type = None
|
||||
self._by_nuclide = None
|
||||
self._domain = None
|
||||
self._domain_type = None
|
||||
self._energy_groups = None
|
||||
|
|
@ -87,7 +91,7 @@ class MultiGroupXS(object):
|
|||
self._xs_tally = None
|
||||
|
||||
self.name = name
|
||||
self.xs_type = xs_type
|
||||
self.by_nuclide = by_nuclide
|
||||
if domain_type is not None:
|
||||
self.domain_type = domain_type
|
||||
if domain is not None:
|
||||
|
|
@ -103,7 +107,7 @@ class MultiGroupXS(object):
|
|||
clone = type(self).__new__(type(self))
|
||||
clone._name = self.name
|
||||
clone._rxn_type = self.rxn_type
|
||||
clone._xs_type = self.xs_type
|
||||
clone._by_nuclide = self.by_nuclide
|
||||
clone._domain = self.domain
|
||||
clone._domain_type = self.domain_type
|
||||
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
|
||||
|
|
@ -131,8 +135,8 @@ class MultiGroupXS(object):
|
|||
return self._rxn_type
|
||||
|
||||
@property
|
||||
def xs_type(self):
|
||||
return self._xs_type
|
||||
def by_nuclide(self):
|
||||
return self._by_nuclide
|
||||
|
||||
@property
|
||||
def domain(self):
|
||||
|
|
@ -169,10 +173,10 @@ class MultiGroupXS(object):
|
|||
cv.check_type('name', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@xs_type.setter
|
||||
def xs_type(self, xs_type):
|
||||
cv.check_value('xs_type', xs_type, ('macro', 'micro'))
|
||||
self._xs_type = xs_type
|
||||
@by_nuclide.setter
|
||||
def by_nuclide(self, by_nuclide):
|
||||
cv.check_type('by_nuclide', by_nuclide, bool)
|
||||
self._by_nuclide = by_nuclide
|
||||
|
||||
@domain.setter
|
||||
def domain(self, domain):
|
||||
|
|
@ -213,16 +217,18 @@ class MultiGroupXS(object):
|
|||
return nuclides.keys()
|
||||
|
||||
def get_nuclide_density(self, nuclide):
|
||||
"""Get the atomic number density for a nuclide in the cross section's
|
||||
spatial domain.
|
||||
"""Get the atomic number density in units of atoms/b-cm for a nuclide
|
||||
in the cross section's spatial domain.
|
||||
|
||||
Paramters
|
||||
---------
|
||||
nuclide : str
|
||||
A nuclide name string (e.g., 'U-235')
|
||||
|
||||
Returns
|
||||
-------
|
||||
density : Real
|
||||
The atomic number density for the nuclide of interest
|
||||
The atomic number density (atom/b-cm) for the nuclide of interest
|
||||
|
||||
Raises
|
||||
------
|
||||
|
|
@ -246,17 +252,22 @@ class MultiGroupXS(object):
|
|||
return density
|
||||
|
||||
def get_nuclide_densities(self, nuclides='all'):
|
||||
"""Get all atomic number densities in the cross section's spatial domain.
|
||||
"""Get an array of atomic number densities in units of atom/b-cm for all
|
||||
nuclides in the cross section's spatial domain.
|
||||
|
||||
nuclides : Iterable of str or 'all'
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is 'all')
|
||||
Paramters
|
||||
---------
|
||||
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 atom densities for all nuclides
|
||||
in the spatial domain. The special string 'sum' will return the atom
|
||||
density summed across all nuclides in the spatial domain.
|
||||
|
||||
Returns
|
||||
-------
|
||||
densities : ndarray of float
|
||||
The atomic number densities corresponding to each of the nuclides
|
||||
in the problem domain
|
||||
An array of the atomic number densities (atom/b-cm) for each of the
|
||||
nuclides in the problem domain
|
||||
|
||||
Raises
|
||||
------
|
||||
|
|
@ -268,15 +279,21 @@ class MultiGroupXS(object):
|
|||
if self.domain is None:
|
||||
raise ValueError('Unable to get nuclide densities without a domain')
|
||||
|
||||
# If the user requested the densities for all nuclides, get a list of
|
||||
# all of the nuclide name strings
|
||||
if nuclides == 'all':
|
||||
nuclides =self.domain.get_all_nuclides()
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
|
||||
# Loop over each nuclide and find and store its atomic number density
|
||||
densities = np.zeros(len(nuclides), dtype=np.float)
|
||||
for i, nuclide in enumerate(nuclides):
|
||||
densities[i] = self.get_nuclide_density(nuclide)
|
||||
# Sum the atomic number densities for all nuclides
|
||||
elif nuclides == 'sum':
|
||||
nuclides = self.get_all_nuclides()
|
||||
densities = np.zeros(1, dtype=np.float)
|
||||
for i, nuclide in enumerate(nuclides):
|
||||
densities[0] += self.get_nuclide_density(nuclide)
|
||||
|
||||
# Store each nuclide's atomic number density in an array
|
||||
else:
|
||||
densities = np.zeros(len(nuclides), dtype=np.float)
|
||||
for i, nuclide in enumerate(nuclides):
|
||||
densities[i] = self.get_nuclide_density(nuclide)
|
||||
|
||||
return densities
|
||||
|
||||
|
|
@ -323,23 +340,25 @@ class MultiGroupXS(object):
|
|||
for filter in filters:
|
||||
self.tallies[key].add_filter(filter)
|
||||
|
||||
# If this is a microscopic cross-section, add all nuclides to tally
|
||||
if self.xs_type == 'micro' and score != 'flux':
|
||||
# If this is a by nuclide cross-section, add all nuclides to Tally
|
||||
if self.by_nuclide and score != 'flux':
|
||||
all_nuclides = self.domain.get_all_nuclides()
|
||||
for nuclide in all_nuclides:
|
||||
self.tallies[key].add_nuclide(nuclide)
|
||||
|
||||
@abc.abstractmethod
|
||||
def compute_xs(self):
|
||||
"""Computes multi-group cross sections using OpenMC tally arithmetic."""
|
||||
"""Performs generic cleanup after a subclass' uses tally arithmetic to
|
||||
compute a multi-group cross section as a derived tally."""
|
||||
|
||||
# If a microscopic cross-section, replace CrossNuclides with originals
|
||||
if self.xs_type == 'micro':
|
||||
# If computing xs for each nuclide, replace CrossNuclides with originals
|
||||
if self.by_nuclide:
|
||||
self.xs_tally._nuclides = []
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
for nuclide in nuclides:
|
||||
self.xs_tally.add_nuclide(nuclide)
|
||||
self.xs_tally.add_nuclide(openmc.Nuclide(nuclide))
|
||||
|
||||
# Remove NaNs which may have resulted from divide-by-zero operations
|
||||
self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean)
|
||||
self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev)
|
||||
|
||||
|
|
@ -350,6 +369,8 @@ class MultiGroupXS(object):
|
|||
This method is needed to compute cross section data from tallies
|
||||
in an OpenMC StatePoint object.
|
||||
|
||||
NOTE: The statepoint must first be linked with an OpenMC Summary object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
statepoint : openmc.StatePoint
|
||||
|
|
@ -413,9 +434,11 @@ class MultiGroupXS(object):
|
|||
subdomains : Iterable of Integral or 'all'
|
||||
Subdomain IDs of interest
|
||||
|
||||
nuclides : Iterable of str or 'all'
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is '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' (default) 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.
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Return the macro or micro cross section in units of cm^-1 or barns
|
||||
|
|
@ -462,20 +485,32 @@ class MultiGroupXS(object):
|
|||
filters.append('energy')
|
||||
filter_bins.append((self.energy_groups.get_group_bounds(group),))
|
||||
|
||||
# Construct list of nuclides for all requested nuclides
|
||||
if nuclides != 'all' and nuclides != ['total']:
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
# Construct a collection of the nuclides to retrieve from the xs tally
|
||||
# NOTE: We must not override the "nuclides" parameter since it is used
|
||||
# to retrieve atomic number densities for micro xs
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all' or nuclides == 'sum':
|
||||
query_nuclides = self.get_all_nuclides()
|
||||
else:
|
||||
query_nuclides = nuclides
|
||||
else:
|
||||
nuclides = []
|
||||
query_nuclides = ['total']
|
||||
|
||||
# Query the multi-group cross section tally for the data
|
||||
xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins,
|
||||
nuclides=nuclides, value=value)
|
||||
# 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)
|
||||
|
||||
# If user requested microscopic cross sections from an object with
|
||||
# microscopic cross sections, divide by atom number densities
|
||||
if self.xs_type == 'micro' and xs_type == 'micro':
|
||||
densities = self.get_nuclide_densities(nuclides)
|
||||
# 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]
|
||||
|
||||
|
|
@ -559,8 +594,8 @@ class MultiGroupXS(object):
|
|||
"""Construct a subdomain-averaged version of this cross section.
|
||||
|
||||
This is primarily useful for averaging across distribcell instances.
|
||||
This routine performs spatial homogenization to compute the
|
||||
scalar flux-weighted average cross section across the subdomains.
|
||||
This routine performs spatial homogenization to compute the scalar
|
||||
flux-weighted average cross section across the subdomains.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -586,13 +621,12 @@ class MultiGroupXS(object):
|
|||
raise ValueError(msg)
|
||||
|
||||
# Construct a collection of the subdomain filter bins to average across
|
||||
if subdomains == 'all':
|
||||
if self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
else:
|
||||
if subdomains != 'all':
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
# Clone this MultiGroupXS to initialize the subdomain-averaged version
|
||||
avg_xs = copy.deepcopy(self)
|
||||
|
|
@ -644,25 +678,38 @@ class MultiGroupXS(object):
|
|||
subdomains : Iterable of Integral or 'all'
|
||||
The subdomain IDs of the cross sections to include in the report
|
||||
|
||||
nuclides : Iterable of str or 'all'
|
||||
The nuclides of the cross-sections to include in the report
|
||||
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' (default) 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.
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Return the macro or micro cross section in units of cm^-1 or barns
|
||||
|
||||
"""
|
||||
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
|
||||
# Construct a collection of the subdomains to report
|
||||
if subdomains != 'all':
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
if nuclides != 'all':
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
else:
|
||||
if self.xs_type == 'micro':
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
# Construct a collection of the nuclides to report
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
if nuclides == 'sum':
|
||||
nuclides = ['sum']
|
||||
else:
|
||||
nuclides = ['total']
|
||||
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-Group XS\n'
|
||||
|
|
@ -675,12 +722,6 @@ class MultiGroupXS(object):
|
|||
print(string)
|
||||
return
|
||||
|
||||
if subdomains == 'all':
|
||||
if self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
# Loop over all subdomains
|
||||
for subdomain in subdomains:
|
||||
|
||||
|
|
@ -691,7 +732,7 @@ class MultiGroupXS(object):
|
|||
for nuclide in nuclides:
|
||||
|
||||
# Build header for nuclide type
|
||||
if xs_type != 'total':
|
||||
if nuclide != 'sum':
|
||||
string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide)
|
||||
|
||||
# Build header for cross section type
|
||||
|
|
@ -719,7 +760,8 @@ class MultiGroupXS(object):
|
|||
|
||||
print(string)
|
||||
|
||||
def build_hdf5_store(self, filename='mgxs', directory='mgxs', append=True):
|
||||
def build_hdf5_store(self, filename='mgxs', directory='mgxs',
|
||||
xs_type='macro', append=True):
|
||||
"""Export the multi-group cross section data into an HDF5 binary file.
|
||||
|
||||
This routine constructs an HDF5 file which stores the multi-group
|
||||
|
|
@ -738,6 +780,9 @@ class MultiGroupXS(object):
|
|||
directory : str
|
||||
Directory for the HDF5 file (default is 'mgxs')
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Store the macro or micro cross section in units of cm^-1 or barns
|
||||
|
||||
append : boolean
|
||||
If true, appends to an existing HDF5 file with the same filename
|
||||
directory (if one exists)
|
||||
|
|
@ -776,13 +821,15 @@ class MultiGroupXS(object):
|
|||
else:
|
||||
xs_results = h5py.File(filename, 'w')
|
||||
|
||||
if self.xs_type == 'micro':
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
|
||||
if self.by_nuclide:
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
densities = np.zeros(len(nuclides), dtype=np.float)
|
||||
for i, nuclide in enumerate(nuclides):
|
||||
densities[i] = nuclides[nuclide][1]
|
||||
else:
|
||||
nuclides = ['total']
|
||||
nuclides = ['sum']
|
||||
|
||||
# Create an HDF5 group within the file for the domain
|
||||
domain_type_group = xs_results.require_group(self.domain_type)
|
||||
|
|
@ -813,7 +860,7 @@ class MultiGroupXS(object):
|
|||
# Create a separate HDF5 group for each nuclide
|
||||
for j, nuclide in enumerate(nuclides):
|
||||
|
||||
if nuclide != 'total':
|
||||
if nuclide != 'sum':
|
||||
nuclide_group = rxn_group.require_group(nuclide)
|
||||
nuclide_group.require_dataset('density', dtype=np.float64,
|
||||
data=[densities[j]], shape=(1,))
|
||||
|
|
@ -822,9 +869,9 @@ class MultiGroupXS(object):
|
|||
|
||||
# Extract the cross section for this subdomain and nuclide
|
||||
average = self.get_xs(subdomains=[subdomain], nuclides=[nuclide],
|
||||
xs_type=self.xs_type, value='mean')
|
||||
xs_type=xs_type, value='mean')
|
||||
std_dev = self.get_xs(subdomains=[subdomain], nuclides=[nuclide],
|
||||
xs_type=self.xs_type, value='std_dev')
|
||||
xs_type=xs_type, value='std_dev')
|
||||
average = average.squeeze()
|
||||
std_dev = std_dev.squeeze()
|
||||
|
||||
|
|
@ -837,7 +884,8 @@ class MultiGroupXS(object):
|
|||
# Close the MultiGroup results HDF5 file
|
||||
xs_results.close()
|
||||
|
||||
def export_xs_data(self, filename='mgxs', directory='mgxs', format='csv'):
|
||||
def export_xs_data(self, filename='mgxs', directory='mgxs',
|
||||
format='csv', groups='all', xs_type='macro'):
|
||||
"""Export the multi-group cross section data to a file.
|
||||
|
||||
This routine leverages the functionality in the Pandas library to
|
||||
|
|
@ -855,23 +903,18 @@ class MultiGroupXS(object):
|
|||
format : {'csv', 'excel', 'pickle', 'latex'}
|
||||
The format for the exported data file
|
||||
|
||||
groups : {'indices' or 'bounds'}
|
||||
When set to 'indices' (default), integer group indices are inserted
|
||||
in the energy in/out column(s) of the DataFrame. When it is 'bounds'
|
||||
the lower and upper energy bounds are used.
|
||||
groups : Iterable of Integral or 'all'
|
||||
Energy groups of interest
|
||||
|
||||
summary : None or Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
information in the Summary object is embedded into a Multi-index
|
||||
column with a geometric "path" to each distribcell intance.
|
||||
NOTE: This option requires the OpenCG Python package.
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Store the macro or micro cross section in units of cm^-1 or barns
|
||||
|
||||
"""
|
||||
|
||||
cv.check_type('filename', filename, basestring)
|
||||
cv.check_type('directory', directory, basestring)
|
||||
cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex'])
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
|
||||
# Make directory if it does not exist
|
||||
if not os.path.exists(directory):
|
||||
|
|
@ -881,7 +924,7 @@ class MultiGroupXS(object):
|
|||
filename = filename.replace(' ', '-')
|
||||
|
||||
# Get a Pandas DataFrame for the data
|
||||
df = self.get_pandas_dataframe()
|
||||
df = self.get_pandas_dataframe(groups=groups, xs_type=xs_type)
|
||||
|
||||
# Capitalize column label strings
|
||||
df.columns = df.columns.astype(str)
|
||||
|
|
@ -916,7 +959,8 @@ class MultiGroupXS(object):
|
|||
modified.write(data)
|
||||
modified.write('\n\\end{document}')
|
||||
|
||||
def get_pandas_dataframe(self, groups='indices', summary=None):
|
||||
def get_pandas_dataframe(self, groups='all', nuclides='all',
|
||||
xs_type='macro', summary=None):
|
||||
"""Build a Pandas DataFrame for the MultiGroupXS data.
|
||||
|
||||
This routine leverages the Tally.get_pandas_dataframe(...) routine, but
|
||||
|
|
@ -924,15 +968,23 @@ class MultiGroupXS(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
groups : {'indices' or 'bounds'}
|
||||
When set to 'indices' (default), integer group indices are inserted
|
||||
in the energy in/out column(s) of the DataFrame. When it is 'bounds'
|
||||
the lower and upper energy bounds are used.
|
||||
groups : Iterable of Integral or 'all'
|
||||
Energy groups of interest
|
||||
|
||||
nuclides : Iterable of str or 'all' or 'sum'
|
||||
The nuclides of the cross-sections to include in the dataframe. This
|
||||
may be a list of nuclide name strings (e.g., ['U-235', 'U-238']).
|
||||
The special string 'all' (default) will include the cross sections
|
||||
for all nuclides in the spatial domain. The special string 'sum'
|
||||
will include the cross sections summed over all nuclides.
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Return macro or micro cross section in units of cm^-1 or barns
|
||||
|
||||
summary : None or Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
information in the Summary object is embedded into a Multi-index
|
||||
information in the Summary object is embedded into a multi-index
|
||||
column with a geometric "path" to each distribcell intance.
|
||||
NOTE: This option requires the OpenCG Python package.
|
||||
|
||||
|
|
@ -954,6 +1006,12 @@ class MultiGroupXS(object):
|
|||
'cross section has not been computed'
|
||||
raise ValueError(msg)
|
||||
|
||||
if groups != 'all':
|
||||
cv.check_iterable_type('groups', groups, Integral)
|
||||
if nuclides != 'all' and nuclides != 'sum':
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
|
||||
# Get a Pandas DataFrame from the derived xs tally
|
||||
df = self.xs_tally.get_pandas_dataframe(summary=summary)
|
||||
|
||||
|
|
@ -963,30 +1021,51 @@ class MultiGroupXS(object):
|
|||
else:
|
||||
df = df.drop('score', axis=1)
|
||||
|
||||
# Use group indices in place of energy bounds ("1" for fastest group)
|
||||
if groups == 'indices':
|
||||
# Rename energy(out) columns
|
||||
columns = []
|
||||
if 'energy [MeV]' in df:
|
||||
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
|
||||
columns.append('group in')
|
||||
if 'energyout [MeV]' in df:
|
||||
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
|
||||
columns.append('group out')
|
||||
|
||||
# Rename the column label for energy in the dataframe
|
||||
columns = []
|
||||
if 'energy [MeV]' in df:
|
||||
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
|
||||
columns.append('group in')
|
||||
if 'energyout [MeV]' in df:
|
||||
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
|
||||
columns.append('group out')
|
||||
# Loop over all energy groups and override the bounds with indices
|
||||
template = '({0:.1e} - {1:.1e})'
|
||||
bins = self.energy_groups.group_edges
|
||||
for column in columns:
|
||||
for i in range(self.num_groups):
|
||||
group = template.format(bins[i], bins[i+1])
|
||||
row_indices = df[column] == group
|
||||
df.loc[row_indices, column] = self.num_groups - i
|
||||
|
||||
# Loop over all energy groups and override the bounds with indices
|
||||
template = '({0:.1e} - {1:.1e})'
|
||||
bins = self.energy_groups.group_edges
|
||||
for column in columns:
|
||||
for i in range(self.num_groups):
|
||||
group = template.format(bins[i], bins[i+1])
|
||||
row_indices = df[column] == group
|
||||
df.loc[row_indices, column] = self.num_groups - i
|
||||
# Select out those groups the user requested
|
||||
if groups != 'all':
|
||||
if 'group in' in df:
|
||||
df = df[df['group in'].isin(groups)]
|
||||
if 'group out' in df:
|
||||
df = df[df['group out'].isin(groups)]
|
||||
|
||||
# Sort the dataframe by domain type id (e.g., distribcell id) and
|
||||
# energy groups such that data is from fast to thermal
|
||||
df.sort([self.domain_type] + columns, inplace=True)
|
||||
# Sum up cross sections across nuclides if requested
|
||||
if self.by_nuclide and nuclides == 'sum':
|
||||
non_nuclide_cols = list(df.columns[df.columns != 'nuclide'])
|
||||
df = df.groupby(non_nuclide_cols, as_index=False)['nuclide'].sum()
|
||||
# If the user requested specific nuclides, remove others from dataframe
|
||||
elif nuclides != 'all' and nuclides != 'sum':
|
||||
df = df[df.nuclide.isin(nuclides)]
|
||||
|
||||
# If user requested micro cross sections, divide out the atom densities
|
||||
if xs_type == 'micro':
|
||||
if self.by_nuclide:
|
||||
densities = self.get_nuclide_densities(nuclides)
|
||||
else:
|
||||
densities = self.get_nuclide_densities('sum')
|
||||
df['mean'] /= densities
|
||||
df['std. dev.'] /= densities
|
||||
|
||||
# Sort the dataframe by domain type id (e.g., distribcell id) and
|
||||
# energy groups such that data is from fast to thermal
|
||||
df.sort([self.domain_type] + columns, inplace=True)
|
||||
|
||||
return df
|
||||
|
||||
|
|
@ -994,8 +1073,8 @@ class MultiGroupXS(object):
|
|||
class TotalXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(TotalXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(TotalXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'total'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1025,8 +1104,8 @@ class TotalXS(MultiGroupXS):
|
|||
class TransportXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(TransportXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(TransportXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'transport'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1064,8 +1143,8 @@ class TransportXS(MultiGroupXS):
|
|||
class AbsorptionXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(AbsorptionXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(AbsorptionXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'absorption'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1095,8 +1174,8 @@ class AbsorptionXS(MultiGroupXS):
|
|||
class CaptureXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(CaptureXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(CaptureXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'capture'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1127,8 +1206,8 @@ class CaptureXS(MultiGroupXS):
|
|||
class FissionXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(FissionXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(FissionXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'fission'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1158,8 +1237,8 @@ class FissionXS(MultiGroupXS):
|
|||
class NuFissionXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(NuFissionXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(NuFissionXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-fission'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1189,8 +1268,8 @@ class NuFissionXS(MultiGroupXS):
|
|||
class ScatterXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(ScatterXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(ScatterXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'scatter'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1220,8 +1299,8 @@ class ScatterXS(MultiGroupXS):
|
|||
class NuScatterXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(NuScatterXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(NuScatterXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-scatter'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1251,8 +1330,8 @@ class NuScatterXS(MultiGroupXS):
|
|||
class ScatterMatrixXS(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(ScatterMatrixXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(ScatterMatrixXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'scatter matrix'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1316,9 +1395,11 @@ class ScatterMatrixXS(MultiGroupXS):
|
|||
subdomains : Iterable of Integral or 'all'
|
||||
Subdomain IDs of interest
|
||||
|
||||
nuclides : Iterable of str or 'all'
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is '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' (default) 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.
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Return the macro or micro cross section in units of cm^-1 or barns
|
||||
|
|
@ -1372,21 +1453,34 @@ class ScatterMatrixXS(MultiGroupXS):
|
|||
filters.append('energyout')
|
||||
filter_bins.append((self.energy_groups.get_group_bounds(group),))
|
||||
|
||||
# Construct list of nuclides for all requested nuclides
|
||||
if nuclides != 'all' and nuclides != ['total']:
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
# Construct a collection of the nuclides to retrieve from the xs tally
|
||||
# NOTE: We must not override the "nuclides" parameter since it is used
|
||||
# to retrieve atomic number densities for micro xs
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all' or nuclides == 'sum':
|
||||
query_nuclides = self.get_all_nuclides()
|
||||
else:
|
||||
query_nuclides = nuclides
|
||||
else:
|
||||
nuclides = []
|
||||
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)
|
||||
|
||||
# Query the multi-group cross section tally for the data
|
||||
xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins,
|
||||
nuclides=nuclides, value=value)
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
# If user requested microscopic cross sections from an object with
|
||||
# microscopic cross sections, divide by atom number densities
|
||||
if self.xs_type == 'micro' and xs_type == 'micro':
|
||||
densities = self.get_nuclide_densities(nuclides)
|
||||
# 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]
|
||||
|
||||
|
|
@ -1400,25 +1494,38 @@ class ScatterMatrixXS(MultiGroupXS):
|
|||
subdomains : Iterable of Integral or 'all'
|
||||
The subdomain IDs of the cross sections to include in the report
|
||||
|
||||
nuclides : Iterable of str or 'all'
|
||||
The nuclides of the cross-sections to include in the report
|
||||
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' (default) 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.
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
Return the macro or micro cross section in units of cm^-1 or barns
|
||||
|
||||
"""
|
||||
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
|
||||
# Construct a collection of the subdomains to report
|
||||
if subdomains != 'all':
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
if nuclides != 'all':
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
else:
|
||||
if self.xs_type == 'micro':
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
# Construct a collection of the nuclides to report
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
if nuclides == 'sum':
|
||||
nuclides = ['sum']
|
||||
else:
|
||||
nuclides = ['total']
|
||||
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-Group XS\n'
|
||||
|
|
@ -1456,7 +1563,7 @@ class ScatterMatrixXS(MultiGroupXS):
|
|||
for nuclide in nuclides:
|
||||
|
||||
# Build header for nuclide type
|
||||
if xs_type != 'total':
|
||||
if xs_type != 'sum':
|
||||
string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide)
|
||||
|
||||
# Build header for cross section type
|
||||
|
|
@ -1491,8 +1598,8 @@ class ScatterMatrixXS(MultiGroupXS):
|
|||
class NuScatterMatrixXS(ScatterMatrixXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(NuScatterMatrixXS, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(NuScatterMatrixXS, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-scatter matrix'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1541,8 +1648,8 @@ class NuScatterMatrixXS(ScatterMatrixXS):
|
|||
class Chi(MultiGroupXS):
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, xs_type='macro', name=''):
|
||||
super(Chi, self).__init__(domain, domain_type, groups, xs_type, name)
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(Chi, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'chi'
|
||||
|
||||
def create_tallies(self):
|
||||
|
|
@ -1593,9 +1700,9 @@ class Chi(MultiGroupXS):
|
|||
|
||||
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-section for all nuclides
|
||||
in the spatial domain. The special string 'sum' will return the sum
|
||||
across all nuclides weighted by the isotope-specific fission source.
|
||||
special string 'all' (default) 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.
|
||||
|
||||
xs_type: {'macro' or 'micro'}
|
||||
This parameter is not relevant for chi but is included here to
|
||||
|
|
@ -1624,6 +1731,7 @@ class Chi(MultiGroupXS):
|
|||
raise ValueError(msg)
|
||||
|
||||
cv.check_value('value', value, ['mean', 'std_dev', 'rel_err'])
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
|
||||
filters = []
|
||||
filter_bins = []
|
||||
|
|
@ -1642,33 +1750,43 @@ class Chi(MultiGroupXS):
|
|||
filters.append('energyout')
|
||||
filter_bins.append((self.energy_groups.get_group_bounds(group),))
|
||||
|
||||
# Construct list of nuclides for all requested nuclides
|
||||
if nuclides != 'all' and nuclides != ['total']:
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
# If chi 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':
|
||||
|
||||
# Retrieve the fission production tallies
|
||||
nu_fission_in = self.tallies['nu-fission-in']
|
||||
nu_fission_out = self.tallies['nu-fission-out']
|
||||
|
||||
# Sum out all nuclides
|
||||
nuclides = self.get_all_nuclides()
|
||||
nu_fission_in = nu_fission_in.summation(nuclides=nuclides)
|
||||
nu_fission_out = nu_fission_out.summation(nuclides=nuclides)
|
||||
|
||||
# Compute chi and store it as the xs_tally attribute so we can use
|
||||
# the generic get_xs routine
|
||||
xs_tally = nu_fission_out / nu_fission_in
|
||||
xs = xs_tally.get_values(filters=filters,
|
||||
filter_bins=filter_bins, value=value)
|
||||
|
||||
# Get chi 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 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 was computed as an average of nuclides in the domain
|
||||
else:
|
||||
nuclides = []
|
||||
|
||||
# Special case for the "macroscopic-from-microscopic" chi
|
||||
# This is needed since chi is fission source rather than flux-weighted
|
||||
if nuclides == 'sum' and self.xs_type == 'micro':
|
||||
|
||||
# Retrieve the fission production tallies
|
||||
nu_fission_in = self.tallies['nu-fission-in']
|
||||
nu_fission_out = self.tallies['nu-fission-out']
|
||||
|
||||
# Sum out all nuclides
|
||||
nuclides = self.get_all_nuclides()
|
||||
nu_fission_in = nu_fission_in.summation(nuclides=nuclides)
|
||||
nu_fission_out = nu_fission_out.summation(nuclides=nuclides)
|
||||
|
||||
# Compute chi and store it as the xs_tally attribute so we can use
|
||||
# the generic get_xs routine
|
||||
xs_tally = nu_fission_out / nu_fission_in
|
||||
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=nuclides, value=value)
|
||||
xs = self.xs_tally.get_values(filters=filters,
|
||||
filter_bins=filter_bins, value=value)
|
||||
|
||||
return xs
|
||||
Loading…
Add table
Add a link
Reference in a new issue