From 02c30fd5de7f4c26f5e97a6f2103a4736af9a226 Mon Sep 17 00:00:00 2001 From: Will Boyd Date: Sat, 26 Sep 2015 17:12:03 -0400 Subject: [PATCH] Removed Tally.summation(...) routine in place of explicit tally summations in openmc.mgxs module for subdomain averaging --- openmc/filter.py | 5 ++- openmc/mgxs/mgxs.py | 53 +++++++++++++++++++++----- openmc/tallies.py | 90 --------------------------------------------- 3 files changed, 47 insertions(+), 101 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index f740247242..b8865a1846 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -318,6 +318,7 @@ class Filter(object): boolean Whether or not the other filter is a subset of this filter """ + if not isinstance(other, Filter): return False elif self.type != other.type: @@ -325,8 +326,8 @@ class Filter(object): elif self.type in ['energy', 'energyout']: return np.all(self.bins == other.bins) - for bin in other.bins: - if bin not in self.bins: + for bin in self.bins: + if bin not in other.bins: return False return True diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index a0e52ab5f2..fc763592f6 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -403,7 +403,9 @@ class MultiGroupXS(object): def get_subdomain_avg_xs(self, subdomains='all'): """Construct a subdomain-averaged version of this cross-section. - This is primarily useful for averaging across distribcell instances. + This is primarily useful for averaging across distribcell instances or + mesh cells. This routine performs spatial homogenization to compute the + scalar flux-weighted average cross-section across the subdomains. Parameters ---------- @@ -440,17 +442,49 @@ class MultiGroupXS(object): # Clone this MultiGroupXS to initialize the subdomain-averaged version avg_xs = copy.deepcopy(self) - # Reset subdomain indices and offsets for distribcell domains + # If domain is distribcell, make subdomain-averaged a 'cell' domain if self.domain_type == 'distribcell': + avg_xs.domain_type = 'cell' avg_xs._offset = 0 + # TODO: Implement this for mesh tallies + elif self.domain_type == 'mesh': + raise NotImplementedError('Average mesh xs are not yet implemented') - # Overwrite tallies with new subdomain-averaged versions - avg_xs._tallies = {} - for tally_type, tally in self.tallies.items(): - tally_sum = tally.summation(filter_type=self.domain_type, - filter_bins=subdomains) - tally_sum /= len(subdomains) - avg_xs.tallies[tally_type] = tally_sum + # Average each of the tallies across subdomains + for tally_type, tally in avg_xs.tallies.items(): + + # Make condensed tally derived and null out sum, sum_sq + tally._derived = True + tally._sum = None + tally._sum_sq = None + + # Get tally data arrays reshaped with one dimension per filter + mean = tally.get_reshaped_data(value='mean') + std_dev = tally.get_reshaped_data(value='std_dev') + + # Get the mean of the mean, std. dev. across requested subdomains + mean = np.mean(mean[subdomains, ...], axis=0) + std_dev = np.mean(std_dev[subdomains, ...]**2, axis=0) + std_dev = np.sqrt(std_dev) + + # If domain is distribcell, make subdomain-averaged a 'cell' domain + domain_filter = tally.find_filter(self._domain_type) + if domain_filter.type == 'distribcell': + domain_filter.type = 'cell' + domain_filter.num_bins = 1 + # TODO: Implement this for mesh tallies + elif domain_filter.type == 'mesh': + raise NotImplementedError('Average mesh xs are not yet implemented') + + # Reshape averaged data arrays with one dimension for all filters + new_shape = \ + (tally.num_filter_bins, tally.num_nuclides, tally.num_score_bins,) + mean = np.reshape(mean, new_shape) + std_dev = np.reshape(std_dev, new_shape) + + # Override tally's data with the new condensed data + tally._mean = mean + tally._std_dev = std_dev # Compute the subdomain-averaged multi-group cross-section avg_xs.compute_xs() @@ -733,6 +767,7 @@ class MultiGroupXS(object): df = self.get_pandas_dataframe() # Capitalize column label strings + df.columns = df.columns.astype(str) df.columns = map(str.title, df.columns) # Export the data using Pandas IO API diff --git a/openmc/tallies.py b/openmc/tallies.py index 785fdb9ddc..a5b55d810e 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -2316,96 +2316,6 @@ class Tally(object): return new_tally - def summation(self, scores=[], filter_type=None, - filter_bins=[], nuclides=[]): - """Build a sliced tally for the specified filter bins, nuclides, scores. - - 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, filter bins 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 []) - - filter_type : str - A filter type string (e.g., 'cell', 'energy') corresponding to the - filter bins to sum across - - filter_bins : Iterable of Integral or tuple - A list of the filter bins corresponding to the filters parameter - 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. Each bin is an - (x,y,z) 3-tuple for 'mesh' filters corresponding to the mesh cell of - interest. - - 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] - - # Sum across any filter bins specified by the user - if filter_type in FILTER_TYPES.values(): - filter_bins = [[(filter_bin,)] for filter_bin in filter_bins] - filters = [[filter_type]] - # If user did not specify a filter type, do not sum across filter bins - else: - filter_bins = [[]] - filters = [[]] - - # Initialize Tally sum - tally_sum = 0 - - # Iterate over all Tally slice operands in summation - prod = [scores, filters, filter_bins, nuclides] - summed_filters = defaultdict(list) - 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 - if filter_type: - filter = tally_slice.find_filter(filter_type) - tally_slice.remove_filter(filter) - summed_filters[filter_type].append(filter) - - # Accumulate this Tally slice into the Tally sum - tally_sum += tally_slice - - # FIXME: test if this works for filter - for filter_type in summed_filters: - filters = summed_filters[filter_type] - for i in range(1, len(filters)): - filters[i] = CrossFilter(filters[i-1], filters[i], '+') - tally_sum.add_filter(filters[-1]) - - return tally_sum - def tile_filter(self, new_filter): """Combines filters, scores and nuclides with another tally.