diff --git a/openmc/cross.py b/openmc/cross.py index 60a524b512..45c7ce2d8f 100644 --- a/openmc/cross.py +++ b/openmc/cross.py @@ -1,30 +1,29 @@ from openmc import Filter, Nuclide -from openmc.checkvalue import check_type class _CrossScore(object): """A special-purpose tally score used to encapsulate all combinations of two - tally's scores as a cross product for tally arithmetic. + tally's scores as a outer product for tally arithmetic. Parameters ---------- left_score : str or _CrossScore - The left score in the cross product. + The left score in the outer product right_score : str or _CrossScore - The right score in the cross product. + The right score in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to - combine two tally's scores with this _CrossNuclide. + combine two tally's scores with this _CrossNuclide Attributes ---------- left_score : str or _CrossScore - The left score in the cross product. + The left score in the outer product right_score : str or _CrossScore - The right score in the cross product. + The right score in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to - combine two tally's scores with this _CrossNuclide. + combine two tally's scores with this _CrossNuclide """ @@ -55,17 +54,14 @@ class _CrossScore(object): @left_score.setter def left_score(self, left_score): - check_type('left score', left_score, (str, _CrossScore)) self._left_score = left_score @right_score.setter def right_score(self, right_score): - check_type('right score', right_score, (str, _CrossScore)) self._right_score = right_score @binary_op.setter def binary_op(self, binary_op): - check_type('binary op', binary_op, str) self._binary_op = binary_op def __repr__(self): @@ -76,27 +72,27 @@ class _CrossScore(object): class _CrossNuclide(object): """A special-purpose nuclide used to encapsulate all combinations of two - tally's nuclides as a cross product for tally arithmetic. + tally's nuclides as a outer product for tally arithmetic. Parameters ---------- left_nuclide : Nuclide or _CrossNuclide - The left nuclide in the cross product. + The left nuclide in the outer product right_nuclide : Nuclide or _CrossNuclide - The right nuclide in the cross product. + The right nuclide in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to - combine two tally's nuclides with this _CrossNuclide. + combine two tally's nuclides with this _CrossNuclide Attributes ---------- left_nuclide : Nuclide or _CrossNuclide - The left nuclide in the cross product. + The left nuclide in the outer product right_nuclide : Nuclide or _CrossNuclide - The right nuclide in the cross product. + The right nuclide in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to - combine two tally's nuclides with this _CrossNuclide. + combine two tally's nuclides with this _CrossNuclide """ @@ -127,17 +123,14 @@ class _CrossNuclide(object): @left_nuclide.setter def left_nuclide(self, left_nuclide): - check_type('left nuclide', left_nuclide, (Nuclide, _CrossNuclide)) self._left_nuclide = left_nuclide @right_nuclide.setter def right_nuclide(self, right_nuclide): - check_type('right nuclide', right_nuclide, (Nuclide, _CrossNuclide)) self._right_nuclide = right_nuclide @binary_op.setter def binary_op(self, binary_op): - check_type('binary op', binary_op, str) self._binary_op = binary_op def __repr__(self): @@ -165,32 +158,41 @@ class _CrossNuclide(object): class _CrossFilter(object): """A special-purpose filter used to encapsulate all combinations of two - tally's filter bins as a cross product for tally arithmetic. + tally's filter bins as a outer product for tally arithmetic. Parameters ---------- left_filter : Filter or _CrossFilter - The left filter in the cross product. + The left filter in the outer product right_filter : Filter or _CrossFilter - The right filter in the cross product. + The right filter in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to - combine two tally's filter bins with this _CrossFilter. + combine two tally's filter bins with this _CrossFilter Attributes ---------- left_filter : Filter or _CrossFilter - The left filter in the cross product. + The left filter in the outer product right_filter : Filter or _CrossFilter - The right filter in the cross product. + The right filter in the outer product binary_op : str The tally arithmetic binary operator (e.g., '+', '-', etc.) used to - combine two tally's filter bins with this _CrossFilter. + combine two tally's filter bins with this _CrossFilter """ def __init__(self, left_filter=None, right_filter=None, binary_op=None): + left_type = left_filter.type + right_type = right_filter.type + self.type = '({0} {1} {2})'.format(left_type, binary_op, right_type) + + self._bins = {} + self._bins['left'] = left_filter.bins + self._bins['right'] = right_filter.bins + self._num_bins = left_filter.num_bins * right_filter.num_bins + self._left_filter = None self._right_filter = None self._binary_op = None @@ -216,33 +218,34 @@ class _CrossFilter(object): @property def type(self): - return (self.right_filter.type, self.left_filter.type) + return self._type @property def bins(self): - return (self.right_filter.bins, self.left_filter.bins) + return (self._bins['left'], self._bins['right']) @property def num_bins(self): - return self.left_filter.num_bins * self.right_filter.num_bins + return self._num_bins @property def stride(self): return self.left_filter.stride * self.right_filter.stride + @type.setter + def type(self, filter_type): + self._type = filter_type + @left_filter.setter def left_filter(self, left_filter): - check_type('left filter', left_filter, (Filter, _CrossFilter)) self._left_filter = left_filter @right_filter.setter def right_filter(self, right_filter): - check_type('right filter', right_filter, (Filter, _CrossFilter)) self._right_filter = right_filter @binary_op.setter def binary_op(self, binary_op): - check_type('binary op', binary_op, str) self._binary_op = binary_op def __repr__(self): diff --git a/openmc/tallies.py b/openmc/tallies.py index db2f897ff6..85f2ecbd69 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -479,7 +479,7 @@ class Tally(object): string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self.id) string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self.name) - string += '{0: <16}\n'.format('\tFilters') + string += '{0: <16}{1}\n'.format('\tFilters', '=\t') for filter in self.filters: string += '{0: <16}\t\t{1}\t{2}\n'.format('', filter.type, @@ -864,10 +864,6 @@ class Tally(object): 'Tally.get_values(...)'.format(self.id) raise ValueError(msg) - # Compute batch statistics if not yet computed - if not self.with_batch_statistics: - self.compute_std_dev() - ############################ FILTERS ######################### # Determine the score indices from any of the requested scores if filters: @@ -1027,10 +1023,6 @@ class Tally(object): msg = 'The pandas Python package must be installed on your system' raise ImportError(msg) - # Compute batch statistics if not yet computed - if not self.with_batch_statistics: - self.compute_std_dev() - # Initialize a pandas dataframe for the tally data df = pd.DataFrame() @@ -1452,7 +1444,7 @@ class Tally(object): """ - new_tally.name = '({0} {1} {2})'.format(self.name, other.name, binary_op) + new_tally.name = '({0} {1} {2})'.format(self.name, binary_op, other.name) if self.estimator == other.estimator: new_tally.estimator = self.estimator @@ -1464,12 +1456,12 @@ class Tally(object): # Generate filter "outer products" if self.filters == other.filters: for self_filter in self.filters: - new_filter = _CrossScore(self_filter, self_filter, binary_op) + new_filter = _CrossFilter(self_filter, self_filter, binary_op) new_tally.add_filter(new_filter) else: all_filters = [self.filters, other.filters] for self_filter, other_filter in itertools.product(*all_filters): - new_filter = _CrossScore(self_filter, other_filter, binary_op) + new_filter = _CrossFilter(self_filter, other_filter, binary_op) new_tally.add_filter(new_filter) # Generate score "outer products" @@ -1487,7 +1479,7 @@ class Tally(object): if self.nuclides == other.nuclides: for self_nuclide in self.nuclides: new_nuclide = _CrossNuclide(self_nuclide, self_nuclide, binary_op) - new_tally.add_nuclide(new_nuclide) + new_tally.nuclides.append(new_nuclide) else: all_nuclides = [self.nuclides, other.nuclides] for self_nuclide, other_nuclide in itertools.product(*all_nuclides): @@ -1612,7 +1604,7 @@ class Tally(object): """ # Check that results have been read - if self.mean is None: + if self.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(self.id) raise ValueError(msg) @@ -1623,7 +1615,7 @@ class Tally(object): if isinstance(other, Tally): # Check that results have been read - if other.mean is None: + if other.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(other.id) raise ValueError(msg) @@ -1691,7 +1683,7 @@ class Tally(object): """ # Check that results have been read - if self.mean is None: + if self.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(self.id) raise ValueError(msg) @@ -1702,7 +1694,7 @@ class Tally(object): if isinstance(other, Tally): # Check that results have been read - if other.mean is None: + if other.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(other.id) raise ValueError(msg) @@ -1771,7 +1763,7 @@ class Tally(object): """ # Check that results have been read - if self.mean is None: + if self.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(self.id) raise ValueError(msg) @@ -1782,7 +1774,7 @@ class Tally(object): if isinstance(other, Tally): # Check that results have been read - if other.mean is None: + if other.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(other.id) raise ValueError(msg) @@ -1853,7 +1845,7 @@ class Tally(object): """ # Check that results have been read - if self.mean is None: + if self.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(self.id) raise ValueError(msg) @@ -1864,7 +1856,7 @@ class Tally(object): if isinstance(other, Tally): # Check that results have been read - if other.mean is None: + if other.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(other.id) raise ValueError(msg) @@ -1935,7 +1927,7 @@ class Tally(object): """ # Check that results have been read - if self.mean is None: + if self.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(self.id) raise ValueError(msg) @@ -1946,7 +1938,7 @@ class Tally(object): if isinstance(power, Tally): # Check that results have been read - if power.mean is None: + if power.sum is None: msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ 'since it does not contain any results.'.format(power.id) raise ValueError(msg) @@ -2081,153 +2073,16 @@ class Tally(object): new_tally = self * -1 return new_tally - def minimum(self, scores=[], filters=[], filter_bins=[], - nuclides=[], value='mean'): - """Returns the minimum of a slice of the Tally's data. - - Parameters - ---------- - scores : list - A list of one or more score strings - (e.g., ['absorption', 'nu-fission']; default is []) - - filters : list - A list of filter type strings - (e.g., ['mesh', 'energy']; default is []) - - filter_bins : list - 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 - (e.g., ['U-235', 'U-238']; default is []) - - value : str - A string for the type of value to return - 'mean' (default), - 'std_dev', 'rel_err', 'sum', or 'sum_sq' are accepted - - Returns - ------- - float - A scalar float of the minimum value in the Tally data indexed by - each filter, nuclide and score as listed in the parameters. - """ - - data = self.get_values(scores, filters, filter_bins, nuclides, value) - return np.min(data) - - def maximum(self, scores=[], filters=[], filter_bins=[], - nuclides=[], value='mean'): - """Returns the maximum of a slice of the Tally's data. - - Parameters - ---------- - scores : list - A list of one or more score strings - (e.g., ['absorption', 'nu-fission']; default is []) - - filters : list - A list of filter type strings - (e.g., ['mesh', 'energy']; default is []) - - filter_bins : list - 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 - (e.g., ['U-235', 'U-238']; default is []) - - value : str - A string for the type of value to return - 'mean' (default), - 'std_dev', 'rel_err', 'sum', or 'sum_sq' are accepted - - Returns - ------- - float - A scalar float of the maximum value in the Tally data indexed by - each filter, nuclide and score as listed in the parameters. - """ - - data = self.get_values(scores, filters, filter_bins, nuclides, value) - return np.max(data) - - def summation(self, scores=[], filters=[], filter_bins=[], - nuclides=[], value='mean'): - """Returns the sum of a slice of the Tally's data. - - Parameters - ---------- - scores : list - A list of one or more score strings - (e.g., ['absorption', 'nu-fission']; default is []) - - filters : list - A list of filter type strings - (e.g., ['mesh', 'energy']; default is []) - - filter_bins : list - 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 - (e.g., ['U-235', 'U-238']; default is []) - - value : str - A string for the type of value to return - 'mean' (default), - 'std_dev', 'rel_err', 'sum', or 'sum_sq' are accepted - - Returns - ------- - float - A scalar float of the sum of the Tally data indexed by - each filter, nuclide and score as listed in the parameters. - """ - - data = self.get_values(scores, filters, filter_bins, nuclides, value) - return np.sum(data) - def slice(self, scores=[], filters=[], filter_bins=[], nuclides=[]): """ """ # Ensure that StatePoint.read_results() was called first - if (self.mean is None) or (self.std_dev is None): - msg = 'The Tally ID={0} has no data to slice. Call the ' \ - 'StatePoint.read_results() method before using ' \ - 'Tally.slice(...)'.format(self.id) + if self.sum is None: + msg = 'Unable to use tally arithmetic with Tally ID={0} ' \ + 'since it does not contain any results.'.format(self.id) raise ValueError(msg) - # Compute batch statistics if not yet computed - if not self.with_batch_statistics: - self.compute_std_dev() - new_tally = copy.deepcopy(self) new_sum = self.get_values(scores, filters, filter_bins, nuclides, 'sum') @@ -2264,10 +2119,8 @@ class Tally(object): # Create a list of the first energy edge bins = [filter_bin[0] for filter_bin in filter_bins] - - - ############################ NUCLIDES ######################## - # Determine the score indices from any of the requested scores + ############################ NUCLIDES ######################### + # Determine the nuclide indices from any of the requested nuclides if nuclides: # Initialize list of indices to Nuclides to exclude from slice @@ -2288,15 +2141,14 @@ class Tally(object): for nuclide_index in nuclide_indices[::-1]: new_tally.remove_nuclide(self.nuclides[nuclide_index]) - ############################# SCORES ######################### + ############################ SCORES ########################## # Determine the score indices from any of the requested scores if scores: - # Initialize list of indices to Nuclides to exclude from slice + # Initialize list of indices to scores to exclude from slice score_indices = [] for score in self.scores: - if score not in scores: score_index = self.get_score_index(score) score_indices.append(score_index)