From ef60d9d2f5e22a5d23654f23906a553c52d79c81 Mon Sep 17 00:00:00 2001 From: "wbinventor@gmail.com" Date: Mon, 8 Feb 2016 15:40:58 -0500 Subject: [PATCH] Implemented MGXS merging --- openmc/filter.py | 18 --------- openmc/mgxs/groups.py | 67 +++++++++++++++++++++++++++++++- openmc/mgxs/mgxs.py | 90 +++++++++++++++++++++++++++++++++++++++++-- openmc/tallies.py | 6 ++- 4 files changed, 157 insertions(+), 24 deletions(-) diff --git a/openmc/filter.py b/openmc/filter.py index f8484ec767..2c310bebb4 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -289,24 +289,6 @@ class Filter(object): else: return True - ''' - # FIXME: Should all bins be completely separate??? - # FIMXE: This is necessary if merging will choose out unique bins - else: - # None of the bins in this filter should match in the other filter - for bin in self.bins: - if bin in other.bins: - return False - - # None of the bins in the other filter should match in this filter - for bin in other.bins: - if bin in self.bins: - return False - - # If all conditional checks pass then filters are mergeable - return True - ''' - def merge(self, other): """Merge this filter with another. diff --git a/openmc/mgxs/groups.py b/openmc/mgxs/groups.py index 3436c0e037..16e94ee451 100644 --- a/openmc/mgxs/groups.py +++ b/openmc/mgxs/groups.py @@ -54,10 +54,12 @@ class EnergyGroups(object): def __eq__(self, other): if not isinstance(other, EnergyGroups): return False - elif self.group_edges != other.group_edges: + elif self.num_groups != other.num_groups: return False - else: + elif np.allclose(self.group_edges, other.group_edges): return True + else: + return False def __ne__(self, other): return not self == other @@ -236,3 +238,64 @@ class EnergyGroups(object): condensed_groups.group_edges = group_edges return condensed_groups + + def can_merge(self, other): + """Determine if energy groups can be merged with another. + + Parameters + ---------- + other : EnergyGroups + EnergyGroups to compare with + + Returns + ------- + bool + Whether the energy groups can be merged + + """ + + if not isinstance(other, EnergyGroups): + return False + + # If the energy group structures match then groups are mergeable + if self == other: + return True + + # This low energy edge coincides with other's high energy edge + if self.group_edges[0] == other.group_edges[-1]: + return True + # This high energy edge coincides with other's low energy edge + elif self.group_edges[-1] == other.group_edges[0]: + return True + else: + return False + + def merge(self, other): + """Merge this energy groups with another. + + Parameters + ---------- + other : EnergyGroups + EnergyGroups to merge with + + Returns + ------- + merged_groups : EnergyGroups + EnergyGroups resulting from the merge + + """ + + if not self.can_merge(other): + raise ValueError('Unable to merge energy groups') + + # Create deep copy to return as merged energy groups + merged_groups = copy.deepcopy(self) + + # Merge unique filter bins + merged_edges = np.concatenate((self.group_edges, other.group_edges)) + merged_edges = np.unique(merged_edges) + merged_edges = sorted(merged_edges) + + # Assign merged edges to merged groups + merged_groups.group_edges = list(merged_edges) + return merged_groups \ No newline at end of file diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index b82426e7df..b4c107139d 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -155,7 +155,7 @@ class MGXS(object): clone._name = self.name clone._rxn_type = self.rxn_type clone._by_nuclide = self.by_nuclide - clone._nuclides = self._nuclides + clone._nuclides = copy.deepcopy(self._nuclides) clone._domain = self.domain clone._domain_type = self.domain_type clone._energy_groups = copy.deepcopy(self.energy_groups, memo) @@ -953,9 +953,93 @@ class MGXS(object): slice_xs.sparse = self.sparse return slice_xs - # FIXME + def can_merge(self, other): + """Determine if another MGXS can be merged with this one + + If results have been loaded from a statepoint, then MGXS are only + mergeable along one and only one of enegy groups or nuclides. + + Parameters + ---------- + other : MGXS + MGXS to check for merging + + """ + + if not isinstance(other, type(self)): + return False + + # Compare reaction type, energy groups, nuclides, domain type + if self.rxn_type != other.rxn_type: + return False + elif not self.energy_groups.can_merge(other.energy_groups): + return False + elif self.by_nuclide != other.by_nuclide: + return False + elif self.domain_type != other.domain_type: + return False + elif 'distribcell' not in self.domain_type and self.domain != other.domain: + return False + elif len(self.tallies) != len(other.tallies): + return False + + # See if each individual tally is mergeable + for tally_key in self.tallies: + if not self.tallies[tally_key].can_merge(other.tallies[tally_key]): + return False + + # If all conditionals pass then MGXS are mergeable + return True + def merge(self, other): - raise NotImplementedError('not yet implemented') + """Merge another MGXS with this one + + If results have been loaded from a statepoint, then MGXS are only + mergeable along one and only one of energy groups or nuclides. + + Parameters + ---------- + other : MGXS + MGXS to merge with this one + + Returns + ------- + merged_mgxs : MGXS + Merged MGXS + + """ + + if not self.can_merge(other): + raise ValueError('Unable to merge MGXS') + + # Create deep copy of tally to return as merged tally + merged_mgxs = copy.deepcopy(self) + merged_mgxs._rxn_rate_tally = None + merged_mgxs._xs_tally = None + + # Merge energy groups + if self.energy_groups != other.energy_groups: + merged_groups = self.energy_groups.merge(other.energy_groups) + merged_mgxs.energy_groups = merged_groups + + # Merge nuclides + if self.nuclides != other.nuclides: + + # The nuclides must be mutually exclusive + for nuclide in self.nuclides: + if nuclide in other.nuclides: + msg = 'Unable to merge MGXS with shared nuclides' + raise ValueError(msg) + + # Concatenate lists of nuclides for the merged MGXS + merged_mgxs.nuclides = self.nuclides + other.nuclides + + # Merge tallies + for tally_key in self.tallies: + merged_tally = self.tallies[tally_key].merge(other.tallies[tally_key]) + merged_mgxs.tallies[tally_key] = merged_tally + + return merged_mgxs def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): """Print a string representation for the multi-group cross section. diff --git a/openmc/tallies.py b/openmc/tallies.py index c04ee001a7..6b04403329 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -859,7 +859,7 @@ class Tally(object): Parameters ---------- - tally : Tally + other : Tally Tally to merge with this one Returns @@ -880,6 +880,10 @@ class Tally(object): # Differentiate Tally with a new auto-generated Tally ID merged_tally.id = None + # If the two tallies are equal, simpy return copy + if self == other: + return merged_tally + # Create deep copy of other tally to use for array concatenation other_copy = copy.deepcopy(other)