Implemented Tally.summation(...) routine

This commit is contained in:
Will Boyd 2015-09-06 18:51:43 -04:00
parent bcc9ded29b
commit efb3c0488f
2 changed files with 126 additions and 38 deletions

View file

@ -114,6 +114,7 @@ class MultiGroupXS(object):
def __init__(self, domain=None, domain_type=None,
energy_groups=None, name=''):
self._name = ''
self._xs_type = None
self._domain = None
@ -141,7 +142,7 @@ class MultiGroupXS(object):
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
# If this is the first time we have tried to copy this object, copy it
if existing is None:
clone = type(self).__new__(type(self))
clone._name = self.name
@ -151,7 +152,7 @@ class MultiGroupXS(object):
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
clone._num_groups = self.num_groups
clone._xs_tally = copy.deepcopy(self.xs_tally, memo)
clone._subdomain_offsets = copy.deepcopy(self.subdomain_indices, memo)
clone._subdomain_indices = copy.deepcopy(self.subdomain_indices, memo)
clone._offset = copy.deepcopy(self.offset, memo)
clone._tallies = dict()
@ -227,15 +228,15 @@ class MultiGroupXS(object):
def _find_domain_offset(self):
"""Finds and stores the offset of the domain tally filter"""
tally = self.tallies[self.tallies.keys()[0]]
tally = self.tallies.values()[0]
filter = tally.find_filter(self.domain_type, [self.domain.id])
self._offset = filter.offset
def set_subdomain_index(self, subdomain_id, offset):
def set_subdomain_index(self, subdomain_id, index):
"""Set the filter bin index for a subdomain of the domain.
This is primary useful when the domain type is 'distribcell', in which
case it can be useful to map each subdomain (a cell instance) to its
This is primarily useful when the domain type is 'distribcell', in
which case one may wish to map each subdomain (a cell instance) to its
filter bin in the derived multi-group cross-section tally data array.
Parameters
@ -248,10 +249,10 @@ class MultiGroupXS(object):
"""
cv.check_type('subdomain id', subdomain_id, Integral)
cv.check_type('subdomain offset', offset, Integral)
cv.check_type('subdomain offset', index, Integral)
cv.check_greater_than('subdomain id', subdomain_id, 0, True)
cv.check_greater_than('subdomain offset', subdomain_id, 0, True)
self._subdomain_indices[subdomain_id] = offset
self._subdomain_indices[subdomain_id] = index
@abc.abstractmethod
def _create_tallies(self, scores, all_filters, keys, estimator):
@ -274,8 +275,7 @@ class MultiGroupXS(object):
"""
cv.check_value('scores', scores, openmc.SCORE_TYPES)
# FIXME : Use @smharper's recursive iterable checker
# cv.check_type('filters', all_filters, openmc.Filter)
cv.check_iterable_type('filters', all_filters, openmc.Filter, 1, 2)
cv.check_type('keys', keys, Iterable, basestring)
cv.check_length('scores', scores, len(keys))
cv.check_value('estimator', estimator, ['analog', 'tracklength'])
@ -299,14 +299,17 @@ class MultiGroupXS(object):
This method can be used to extract the indices into the multi-group
cross-section tally data array for a subdomain (i.e., cell instance).
See also : get_subdomains
Parameters
----------
subdomains : Iterable of Integral or 'all'
Subdomain IDs of interest
Returns
indices : NumPy ndarray
Array of subdomain indices indexed in the order of the subdomains
----------
indices : ndarray
The subdomain indices indexed in the order of the subdomains
Raises
------
@ -319,8 +322,8 @@ class MultiGroupXS(object):
cv.check_type('subdomains', subdomains, Iterable, Integral)
if subdomains == 'all':
# FIXME: This isn't correct any more!!
# indices = np.arange(self.xs.shape[1])
num_subdomains = len(self.subdomain_indices)
indices = np.arange(num_subdomains)
else:
indices = np.zeros(len(subdomains), dtype=np.int64)
@ -340,7 +343,7 @@ class MultiGroupXS(object):
This method can be used to extract the subdomains for the multi-group
cross-section from their indices in the tally data array.
See also : get_subdomain_offsets
See also : get_subdomain_indices
Parameters
----------
@ -348,7 +351,8 @@ class MultiGroupXS(object):
Subdomain indices of interest
Returns
subdomains : NumPy ndarray
----------
subdomains : ndarray
Array of subdomain IDs indexed in the order of the indices
Raises
@ -370,9 +374,9 @@ class MultiGroupXS(object):
for i, index in enumerate(indices):
if index in values:
subdomains[i] = keys[values.index(indices)]
subdomains[i] = keys[values.index(index)]
else:
msg = 'Unable to get subdomain for offset "{0}" since it ' \
msg = 'Unable to get subdomain for index "{0}" since it ' \
'is not a valid index'.format(index)
raise ValueError(msg)
@ -408,21 +412,20 @@ class MultiGroupXS(object):
if self.xs_tally is None:
msg = 'Unable to get cross-section since it has not been computed'
raise ValueError(msg)
if groups != 'all':
cv.check_value('groups', groups, Iterable, Integral)
if subdomains != 'all':
cv.check_value('subdomains', subdomains, Iterable, Integral)
filters = []
filter_bins = []
# Construct a collection of the domain filter bins
filters.append(self.domain_type)
filter_bins.append(subdomains)
if subdomains != 'all':
cv.check_value('subdomains', subdomains, Iterable, Integral)
filters.append(self.domain_type)
filter_bins.append(tuple(subdomains))
# Construct a collection of the energy group filter bins
filters.append('energy')
filter_bins.append(self.energy_groups.get_group_bounds(groups))
if groups != 'all':
cv.check_value('groups', groups, Iterable, Integral)
filters.append('energy')
filter_bins.append(self.energy_groups.get_group_bounds(groups))
# Query the multi-group cross-section tally for the data
xs = self.xs_tally.get_values(filters=filters,
@ -465,9 +468,9 @@ class MultiGroupXS(object):
for key, old_tally in avg_xs.tallies.items():
# FIXME: Need to create Tally.mean(...)
slice_tally = old_tally.slice(filters=[avg_xs.domain_type],
filter_bins=subdomains)
filter_bins=[tuple(subdomains)])
avg_tally = slice_tally.mean(filters=[avg_xs.domain_type],
filter_bins=subdomains)
filter_bins=[tuple(subdomains)])
avg_xs.tallies[key] = avg_tally
avg_xs.compute_xs()

View file

@ -3,6 +3,8 @@ import copy
import os
import pickle
import itertools
import functools
from operator import mul
from numbers import Integral, Real
from xml.etree import ElementTree as ET
import sys
@ -1677,8 +1679,8 @@ class Tally(object):
new_tally._derived = True
new_tally.with_batch_statistics = True
new_tally.name = self.name
new_tally._mean = self._mean + other
new_tally._std_dev = self._std_dev
new_tally._mean = self.mean + other
new_tally._std_dev = self.std_dev
new_tally.estimator = self.estimator
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
@ -1746,8 +1748,8 @@ class Tally(object):
new_tally = Tally(name='derived')
new_tally._derived = True
new_tally.name = self.name
new_tally._mean = self._mean - other
new_tally._std_dev = self._std_dev
new_tally._mean = self.mean - other
new_tally._std_dev = self.std_dev
new_tally.estimator = self.estimator
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
@ -1816,8 +1818,8 @@ class Tally(object):
new_tally = Tally(name='derived')
new_tally._derived = True
new_tally.name = self.name
new_tally._mean = self._mean * other
new_tally._std_dev = self._std_dev * np.abs(other)
new_tally._mean = self.mean * other
new_tally._std_dev = self.std_dev * np.abs(other)
new_tally.estimator = self.estimator
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
@ -1886,8 +1888,8 @@ class Tally(object):
new_tally = Tally(name='derived')
new_tally._derived = True
new_tally.name = self.name
new_tally._mean = self._mean / other
new_tally._std_dev = self._std_dev * np.abs(1. / other)
new_tally._mean = self.mean / other
new_tally._std_dev = self.std_dev * np.abs(1. / other)
new_tally.estimator = self.estimator
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
@ -2085,7 +2087,7 @@ class Tally(object):
"""Build a sliced tally for the specified filters, scores and nuclides.
This method constructs a new tally to encapsulate a subset of the data
represented by this tally. The subset of data to included in the tally
represented by this tally. The subset of data to include in the tally
slice is determined by the scores, filters and nuclides specified in
the input parameters.
@ -2200,6 +2202,89 @@ class Tally(object):
return new_tally
def summation(self, scores=[], filters=[], filter_bins=[], nuclides=[]):
"""Build a sliced tally for the specified filters, scores and nuclides.
This method constructs a new tally to encapsulate a subset of the data
represented by this tally. The subset of data to include in the tally
slice is determined by the scores, filters and nuclides specified in
the input parameters.
Parameters
----------
scores : list
A list of one or more score strings to sum across
(e.g., ['absorption', 'nu-fission']; default is [])
filters : list
A list of filter type strings to sum across
(e.g., ['mesh', 'energy']; default is [])
filter_bins : list of Iterables
A list of the filter bins corresponding to the filter_types
parameter (e.g., [(1,), (0., 0.625e-6)]; default is []). Each bin
in the list is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. Each bin is an integer for the
cell instance ID for 'distribcell Filters. Each bin is a 2-tuple of
floats for 'energy' and 'energyout' filters corresponding to the
energy boundaries of the bin of interest. The bin is a (x,y,z)
3-tuple for 'mesh' filters corresponding to the mesh cell of
interest. The order of the bins in the list must correspond of the
filter_types parameter.
nuclides : list
A list of nuclide name strings to sum across
(e.g., ['U-235', 'U-238']; default is [])
Returns
-------
Tally
A new tally which encapsulates the sum of data requested.
"""
# If user did not specify any scores, do not sum across scores
if len(scores) == 0:
scores = [[]]
# Sum across any scores specified by the user
else:
scores = [[score] for score in scores]
# If user did not specify any nuclides, do not sum across nuclides
if len(nuclides) == 0:
nuclides = [[]]
# Sum across any nuclides specified by the user
else:
nuclides = [[nuclide] for nuclide in nuclides]
# If user did not specify any filter bins, do not sum across filter bins
if len(filters) == 0:
filter_bins = [[]]
filters = [[]]
# Sum across any filter bins specified by the user
else:
filter_bins = list(itertools.product(*filter_bins))
filter_bins = [list(filter_bin) for filter_bin in filter_bins]
filters = [filters]
# Initialize Tally sum
tally_sum = 0
# Iterate over all Tally slice operands in summation
prod = [scores, filters, filter_bins, nuclides]
for scores, filters, filter_bins, nuclides in itertools.product(*prod):
tally_slice = self.get_slice(scores, filters, filter_bins, nuclides)
# Remove filters summed across to avoid bulky CrossFilters
for filter in reversed(tally_slice.filters):
if filter.type in filters:
tally_slice.remove_filter(filter)
# Accumulate this Tally slice into the Tally sum
tally_sum += tally_slice
return tally_sum
class TalliesFile(object):
"""Tallies file used for an OpenMC simulation. Corresponds directly to the