From 077eff7f19bbab9a411d5c3356a93b9b7c9279b2 Mon Sep 17 00:00:00 2001 From: "wbinventor@gmail.com" Date: Sun, 7 Feb 2016 13:24:14 -0500 Subject: [PATCH] Fixed some bugs in tally merging with introduction of comparison operators for filers and nuclides --- openmc/element.py | 6 ++++ openmc/filter.py | 25 ++++++++++++- openmc/nuclide.py | 6 ++++ openmc/tallies.py | 92 +++++++++++++++++++++++++++++++++++------------ 4 files changed, 106 insertions(+), 23 deletions(-) diff --git a/openmc/element.py b/openmc/element.py index 9f04abfdab..4880dfaf23 100644 --- a/openmc/element.py +++ b/openmc/element.py @@ -57,6 +57,12 @@ class Element(object): def __ne__(self, other): return not self == other + def __lt__(self, other): + return not self > other + + def __hash__(self): + return hash(repr(self)) + def __hash__(self): return hash(repr(self)) diff --git a/openmc/filter.py b/openmc/filter.py index 430b2415f0..21797264b3 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -77,6 +77,23 @@ class Filter(object): def __ne__(self, other): return not self == other + def __gt__(self, other): + if self.type != other.type: + if self.type in _FILTER_TYPES and other.type in _FILTER_TYPES: + delta = _FILTER_TYPES.index(self.type) - \ + _FILTER_TYPES.index(other.type) + return True if delta > 0 else False + else: + return False + else: + if 'energy' in self.type and 'energy' in other.type: + return self.bins[0] >= other.bins[-1] + else: + return max(self.bins) > max(other.bins) + + def __lt__(self, other): + return not self > other + def __hash__(self): return hash(repr(self)) @@ -297,7 +314,13 @@ class Filter(object): # Merge unique filter bins merged_bins = set(np.concatenate((self.bins, other.bins))) - merged_filter.bins = list(sorted(merged_bins)) + + # Sort energy bin edges + if 'energy' in self.type: + merged_bins = sorted(merged_bins) + + # Assign merged bins to merged filter + merged_filter.bins = list(merged_bins) # Count bins in the merged filter if 'energy' in merged_filter.type: diff --git a/openmc/nuclide.py b/openmc/nuclide.py index 01fb2aa459..8e97f1a1c6 100644 --- a/openmc/nuclide.py +++ b/openmc/nuclide.py @@ -60,6 +60,12 @@ class Nuclide(object): def __ne__(self, other): return not self == other + def __gt__(self, other): + return repr(self) > repr(other) + + def __lt__(self, other): + return not self > other + def __hash__(self): return hash(repr(self)) diff --git a/openmc/tallies.py b/openmc/tallies.py index c966fbcf88..d88519ba3b 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -826,18 +826,30 @@ class Tally(object): if self.estimator != other.estimator: return False + equal_filters = sorted(self.filters) == sorted(other.filters) + equal_nuclides = sorted(self.nuclides) == sorted(other.nuclides) + equal_scores = sorted(self.scores) == sorted(other.scores) + equality = [equal_filters, equal_nuclides, equal_scores] + + # If all filters, nuclides and scores match then tallies are mergeable + if equal_filters and equal_nuclides and equal_scores: + return True + # Variables to indicate matching filter bins, nuclides and scores merge_filters = self._can_merge_filters(other) merge_nuclides = self._can_merge_nuclides(other) merge_scores = self._can_merge_scores(other) mergeability = [merge_filters, merge_nuclides, merge_scores] + if not all(mergeability): + return False + # If the tally results have been read from the statepoint, we can only - # merge along one of filter bins, scores or nuclides - if self._results_read and sum(mergeability) > 1: + # at least two of filters, nuclides and scores must match + elif self._results_read and sum(equality) < 2: return False else: - return all([merge_filters, merge_nuclides, merge_scores]) + return True def merge(self, other): """Merge another tally with this one @@ -871,8 +883,16 @@ class Tally(object): # Create deep copy of other tally to use for array concatenation other_copy = copy.deepcopy(other) + # FIXME: document and create vars for merge_filters, etc. + merge_filters = self._can_merge_filters(other) + merge_nuclides = self._can_merge_nuclides(other) + merge_scores = self._can_merge_scores(other) + equal_filters = sorted(self.filters) == sorted(other.filters) + equal_nuclides = sorted(self.nuclides) == sorted(other.nuclides) + equal_scores = sorted(self.scores) == sorted(other.scores) + # If two tallies can be merged along a filter's bins - if self._can_merge_filters(other): + if merge_filters and not equal_filters: # Search for mergeable filters for i, filter1 in enumerate(self.filters): @@ -880,12 +900,14 @@ class Tally(object): if filter1 != filter2 and filter1.can_merge(filter2): other_copy._swap_filters(other_copy.filters[i], filter2) merged_tally.filters[i] = filter1.merge(filter2) + join_right = filter1 < filter2 merge_axis = i break # If two tallies can be merged along nuclide bins - if self._can_merge_nuclides(other): + if merge_nuclides and not equal_nuclides: merge_axis = self.num_filters + join_right = True # Add unique nuclides from other tally to merged tally for nuclide in other.nuclides: @@ -893,8 +915,9 @@ class Tally(object): merged_tally.add_nuclide(nuclide) # If two tallies can be merged along score bins - if self._can_merge_scores(other): + if merge_scores and not equal_scores: merge_axis = self.num_filters + 1 + join_right = True # Add unique scores from other tally to merged tally for score in other.scores: @@ -908,37 +931,57 @@ class Tally(object): if self.sum is not None and other_copy.sum is not None: self_sum = self.get_reshaped_data(value='sum') other_sum = other_copy.get_reshaped_data(value='sum') - merged_tally._sum = \ - np.concatenate((self_sum, other_sum), axis=merge_axis) - merged_tally._sum = \ - np.reshape(merged_tally._sum, merged_tally.shape) + + if join_right: + merged_sum = \ + np.concatenate((self_sum, other_sum), axis=merge_axis) + else: + merged_sum = \ + np.concatenate((other_sum, self_sum), axis=merge_axis) + + merged_tally._sum = np.reshape(merged_sum, merged_tally.shape) # Concatenate sum_sq arrays if present in both tallies if self.sum_sq is not None and other.sum_sq is not None: self_sum_sq = self.get_reshaped_data(value='sum_sq') other_sum_sq = other_copy.get_reshaped_data(value='sum_sq') - merged_tally._sum_sq = \ - np.concatenate((self_sum_sq, other_sum_sq), axis=merge_axis) - merged_tally._sum_sq = \ - np.reshape(merged_tally._sum_sq, merged_tally.shape) + + if join_right: + merged_sum_sq = \ + np.concatenate((self_sum_sq, other_sum_sq), axis=merge_axis) + else: + merged_sum_sq = \ + np.concatenate((other_sum_sq, self_sum_sq), axis=merge_axis) + + merged_tally._sum_sq = np.reshape(merged_sum_sq, merged_tally.shape) # Concatenate mean arrays if present in both tallies if self.mean is not None and other.mean is not None: self_mean = self.get_reshaped_data(value='mean') other_mean = other_copy.get_reshaped_data(value='mean') - merged_tally._mean = \ - np.concatenate((self_mean, other_mean), axis=merge_axis) - merged_tally._mean = \ - np.reshape(merged_tally._mean, merged_tally.shape) + + if join_right: + merged_mean = \ + np.concatenate((self_mean, other_mean), axis=merge_axis) + else: + merged_mean = \ + np.concatenate((other_mean, self_mean), axis=merge_axis) + + merged_tally._mean = np.reshape(merged_mean, merged_tally.shape) # Concatenate std. dev. arrays if present in both tallies if self.std_dev is not None and other.std_dev is not None: self_std_dev = self.get_reshaped_data(value='std_dev') other_std_dev = other_copy.get_reshaped_data(value='std_dev') - merged_tally._std_dev = \ - np.concatenate((self_std_dev, other_std_dev), axis=merge_axis) - merged_tally._std_dev = \ - np.reshape(merged_tally._std_dev, merged_tally.shape) + + if join_right: + merged_std_dev = \ + np.concatenate((self_std_dev, other_std_dev), axis=merge_axis) + else: + merged_std_dev = \ + np.concatenate((other_std_dev, self_std_dev), axis=merge_axis) + + merged_tally._std_dev = np.reshape(merged_std_dev, merged_tally.shape) # Sparsify merged tally if both tallies are sparse merged_tally.sparse = self.sparse and other.sparse @@ -2878,7 +2921,12 @@ class Tally(object): 'since it does not contain any results.'.format(self.id) raise ValueError(msg) + # Create deep copy of tally to return as sliced tally new_tally = copy.deepcopy(self) + + # Differentiate Tally with a new auto-generated Tally ID + new_tally.id = None + new_tally.sparse = False if not self.derived and self.sum is not None: