From 99db48bc8e48f41a8cd1e358f1311ba9ba72b5ee Mon Sep 17 00:00:00 2001 From: Will Boyd Date: Thu, 2 Mar 2017 17:16:32 -0500 Subject: [PATCH] Updated equations in new consistent scattering matrix --- docs/source/pythonapi/index.rst | 1 - openmc/mgxs/mgxs.py | 786 ++------------------------------ 2 files changed, 30 insertions(+), 757 deletions(-) diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index e622fb265..8b4b0b154 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -287,7 +287,6 @@ Multi-group Cross Sections openmc.mgxs.MGXS openmc.mgxs.AbsorptionXS openmc.mgxs.CaptureXS - openmc.mgxs.ConsistentScatterMatrixXS openmc.mgxs.Chi openmc.mgxs.FissionXS openmc.mgxs.InverseVelocity diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index aa2afb8cf..4b08cfb8f 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -3552,7 +3552,35 @@ class ScatterMatrixXS(MatrixMGXS): To incorporate the effect of neutron multiplication from (n,xn) reactions in the above relation, the `nu` parameter can be set to `True`. - # FIXME: Add equations to reflect alternative formulation + An alternative form of the scattering matrix is computed when the + `formulation` property is set to 'consistent' rather than the default + of 'simple'. This formulation computes the scattering matrix multi-group + cross section as the product of the scatter cross section and + group-to-group scattering probabilities. + + Unlike the default 'simple' formulation, the 'consistent' formulation + is computed from the groupwise cattering cross section which uses a + tracklength estimator. This ensures that reaction rate balance is exactly + preserved with a :class:`TotalXS` computed using a tracklength estimator. + + For a scattering probability matrix :math:`P_{s,\ell,g'\rightarrow g}` and + scattering cross section :math:`\sigma_s (r, E)` for incoming energy group + :math:`[E_{g'},E_{g'-1}]` and outgoing energy group :math:`[E_g,E_{g-1}]`, + the Legendre scattering moments are calculated as: + + .. math:: + + \sigma_{s,\ell,g'\rightarrow g} = \sigma_s (r, E) \times + P_{s,\ell,g'\rightarrow g} + + To incorporate the effect of neutron multiplication from (n,xn) reactions + in the 'consistent' scattering matrix, the `nu` parameter can be set to `True` + such that the Legendre scattering moments are calculated as: + + .. math:: + + \sigma_{s,\ell,g'\rightarrow g} = \upsilon_{g'\rightarrow g} \times + \sigma_s (r, E) \times P_{s,\ell,g'\rightarrow g} Parameters ---------- @@ -4786,7 +4814,7 @@ class ScatterProbabilityMatrix(MatrixMGXS): \sigma_{s,0,g'} \phi \rangle} To incorporate the effect of neutron multiplication from (n,xn) reactions - in the above probaility matrix, the `nu` parameter can be set to `True`. + in the above probability matrix, the `nu` parameter can be set to `True`. Parameters ---------- @@ -5581,760 +5609,6 @@ class ScatterProbabilityMatrix(MatrixMGXS): print(string) -@add_metaclass(ABCMeta) -class ConvolvedMGXS(MGXS): - """An abstract convolution of multiple multi-group cross sections for some - energy group structure within some spatial domain. - - This class can be used for both OpenMC input generation and tally data - post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for multi-group neutronics calculations. - - .. note:: Users should instantiate the subclasses of this abstract class. - - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - - Attributes - ---------- - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : {'tracklength', 'collision', 'analog'} - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file) and the number of mesh cells for - 'mesh' domain types. - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - mgxs : list of openmc.mgxs.MGXS - A list of MGXS to combine to compute this multi-group cross section - - """ - - def __init__(self, domain=None, domain_type=None, groups=None, - by_nuclide=False, name='', num_polar=1, num_azimuthal=1): - - self._mgxs = [] - super(ConvolvedMGXS, self).__init__( - domain, domain_type, groups, by_nuclide, - name, num_polar, num_azimuthal) - - def __deepcopy__(self, memo): - clone = super(ConvolvedMGXS, self).__deepcopy__(memo) - clone._mgxs = copy.deepcopy(self.mgxs) - return clone - - @property - def mgxs(self): - return self._mgxs - - @property - def scores(self): - scores = [] - for mgxs in self.mgxs: - scores += mgxs.scores - return scores - - @property - def filters(self): - filters = [] - for mgxs in self.mgxs: - filters.extend(mgxs.filters) - return filters - - @property - def tally_keys(self): - keys = [] - for mgxs in self.mgxs: - for tally_key in mgxs.tallies: - keys += ['{} : {}'.format(mgxs.hdf5_key, tally_key)] - return keys - - @property - def tallies(self): - - # Instantiate tallies if they do not exist - if self._tallies is None: - - # Initialize a collection of Tallies - self._tallies = OrderedDict() - - # Add tallies created for each MGXS - for mgxs in self.mgxs: - for tally_key in mgxs.tallies: - new_key = '{} : {}'.format(mgxs.hdf5_key, tally_key) - self._tallies[new_key] = mgxs.tallies[tally_key] - - return self._tallies - - @MGXS.name.setter - def name(self, name): - cv.check_type('name', name, string_types) - - self._name = name - for mgxs in self.mgxs: - mgxs.name = name - - @MGXS.by_nuclide.setter - def by_nuclide(self, by_nuclide): - cv.check_type('by_nuclide', by_nuclide, bool) - - self._by_nuclide = by_nuclide - for mgxs in self.mgxs: - mgxs.by_nuclide = by_nuclide - - @MGXS.nuclides.setter - def nuclides(self, nuclides): - cv.check_iterable_type('nuclides', nuclides, string_types) - - self._nuclides = nuclides - for mgxs in self.mgxs: - mgxs.nuclides = nuclides - - @MGXS.estimator.setter - def estimator(self, estimator): - raise ValueError('Unable to assign a tally estimator a ConvolvedMGXS') - - @MGXS.domain.setter - def domain(self, domain): - cv.check_type('domain', domain, _DOMAINS) - - self._domain = domain - for mgxs in self.mgxs: - mgxs.domain = domain - - # Assign a domain type - if self.domain_type is None: - if isinstance(domain, openmc.Material): - self._domain_type = 'material' - elif isinstance(domain, openmc.Cell): - self._domain_type = 'cell' - elif isinstance(domain, openmc.Universe): - self._domain_type = 'universe' - elif isinstance(domain, openmc.Mesh): - self._domain_type = 'mesh' - - @MGXS.domain_type.setter - def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, DOMAIN_TYPES) - - self._domain_type = domain_type - for mgxs in self.mgxs: - mgxs.domain_type = domain_type - - @MGXS.energy_groups.setter - def energy_groups(self, energy_groups): - cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) - - self._energy_groups = energy_groups - for mgxs in self.mgxs: - mgxs.energy_groups = energy_groups - - @MGXS.num_polar.setter - def num_polar(self, num_polar): - cv.check_type('num_polar', num_polar, Integral) - cv.check_greater_than('num_polar', num_polar, 0) - - self._num_polar = num_polar - for mgxs in self.mgxs: - mgxs.num_polar = num_polar - - @MGXS.num_azimuthal.setter - def num_azimuthal(self, num_azimuthal): - cv.check_type('num_azimuthal', num_azimuthal, Integral) - cv.check_greater_than('num_azimuthal', num_azimuthal, 0) - - self._num_azimuthal = num_azimuthal - for mgxs in self.mgxs: - mgxs.num_azimuthal = num_azimuthal - - @MGXS.tally_trigger.setter - def tally_trigger(self, tally_trigger): - cv.check_type('tally trigger', tally_trigger, openmc.Trigger) - - self._tally_trigger = tally_trigger - for mgxs in self.mgxs: - mgxs.tally_trigger = tally_trigger - - @MGXS.sparse.setter - def sparse(self, sparse): - """Convert tally data from NumPy arrays to SciPy list of lists (LIL) - sparse matrices, and vice versa. - - This property may be used to reduce the amount of data in memory during - tally data processing. The tally data will be stored as SciPy LIL - matrices internally within the Tally object. All tally data access - properties and methods will return data as a dense NumPy array. - - """ - - cv.check_type('sparse', sparse, bool) - - # Sparsify or densify the derived MGXS tallies and the base tallies - if self._xs_tally: - self.xs_tally.sparse = sparse - if self._rxn_rate_tally: - self.rxn_rate_tally.sparse = sparse - - for tally_name in self.tallies: - self.tallies[tally_name].sparse = sparse - - self._sparse = sparse - for mgxs in self.mgxs: - mgxs.sparse = sparse - - def load_from_statepoint(self, statepoint): - """Extracts tallies in an OpenMC StatePoint with the data needed to - compute multi-group cross sections. - - This method is needed to compute cross section data from tallies - in an OpenMC StatePoint object. - - .. note:: The statepoint must be linked with an OpenMC Summary object. - - Parameters - ---------- - statepoint : openmc.StatePoint - An OpenMC StatePoint object with tally data - - Raises - ------ - ValueError - When this method is called with a statepoint that has not been - linked with a summary object. - - """ - - cv.check_type('statepoint', statepoint, openmc.statepoint.StatePoint) - - if statepoint.summary is None: - msg = 'Unable to load data from a statepoint which has not been ' \ - 'linked with a summary file' - raise ValueError(msg) - - # Override the domain object that loaded from an OpenMC summary file - # NOTE: This is necessary for micro cross-sections which require - # the isotopic number densities as computed by OpenMC - geom = statepoint.summary.geometry - if self.domain_type in ('cell', 'distribcell'): - self.domain = geom.get_all_cells()[self.domain.id] - elif self.domain_type == 'universe': - self.domain = geom.get_all_universes()[self.domain.id] - elif self.domain_type == 'material': - self.domain = geom.get_all_materials()[self.domain.id] - elif self.domain_type == 'mesh': - self.domain = statepoint.meshes[self.domain.id] - else: - msg = 'Unable to load data from a statepoint for domain type {0} ' \ - 'which is not yet supported'.format(self.domain_type) - raise ValueError(msg) - - # Use tally "slicing" to ensure that tallies correspond to our domain - # NOTE: This is important if tally merging was used - if self.domain_type == 'mesh': - filters = [_DOMAIN_TO_FILTER[self.domain_type]] - xyz = [range(1, x + 1) for x in self.domain.dimension] - filter_bins = [tuple(itertools.product(*xyz))] - elif self.domain_type != 'distribcell': - filters = [_DOMAIN_TO_FILTER[self.domain_type]] - filter_bins = [(self.domain.id,)] - # Distribcell filters only accept single cell - neglect it when slicing - else: - filters = [] - filter_bins = [] - - # Clear any tallies previously loaded from a statepoint - if self.loaded_sp: - self._tallies = None - self._xs_tally = None - self._rxn_rate_tally = None - self._loaded_sp = False - - # Find, slice and store Tallies from StatePoint - # The tally slicing is needed if tally merging was used - for tally_type, tally in self.tallies.items(): - sp_tally = statepoint.get_tally( - tally.scores, tally.filters, tally.nuclides, - estimator=tally.estimator, exact_filters=True) - sp_tally = sp_tally.get_slice( - tally.scores, filters, filter_bins, tally.nuclides) - sp_tally.sparse = self.sparse - self._tallies[tally_type] = sp_tally - - # Load data for the tallies in each MGXS in the convolution - for mgxs in self.mgxs: - mgxs.load_from_statepoint(statepoint) - - self._loaded_sp = True - - -class ConsistentScatterMatrixXS(ConvolvedMGXS, ScatterMatrixXS): - r"""A scattering matrix multi-group cross section computed as the product - of the scatter cross section and group-to-group scattering probabilities. - - This class is a variation of the :class:`ScatterMatrixXS` which computes - the scattering matrix as the convolution product of :class:`ScatterXS` and - :class:`ScatterProbabilityMatrix`, and :class:`MultiplicityMatrix` if - multiplication from scattering multiplication is considered (optional). - Unlike the :class:`ScatterMatrixXS`, this scattering matrix is computed - from the scattering cross section which uses a tracklength estimator. - This ensures that reaction rate balance is exactly preserved with a - :class:`TotalXS` computed using a tracklength estimator. - - This class can be used for both OpenMC input generation and tally data - post-processing to compute spatially-homogenized and energy-integrated - multi-group cross sections for multi-group neutronics calculations. At a - minimum, one needs to set the - :attr:`ConsistentScatterMatrixXS.energy_groups` and - :attr:`ConsistentScatterMatrixXS.domain` properties. Tallies for the flux - and appropriate reaction rates over the specified domain are generated - automatically via the :attr:`ConsistentScatterMatrixXS.tallies` property, - which can then be appended to a :class:`openmc.Tallies` instance. - - For post-processing, the :meth:`MGXS.load_from_statepoint` will pull in the - necessary data to compute multi-group cross sections from a - :class:`openmc.StatePoint` instance. The derived multi-group cross section - can then be obtained from the :attr:`ConsistentScatterMatrixXS.xs_tally` - property. - - For a scattering probability matrix :math:`P_{s,\ell,g'\rightarrow g}` and - scattering cross section :math:`\sigma_s (r, E)` for incoming energy group - :math:`[E_{g'},E_{g'-1}]` and outgoing energy group :math:`[E_g,E_{g-1}]`, - the Legendre scattering moments are calculated as: - - .. math:: - - \sigma_{s,\ell,g'\rightarrow g} = \sigma_s (r, E) \times - P_{s,\ell,g'\rightarrow g} - - To incorporate the effect of neutron multiplication from (n,xn) reactions - in the above scattering matrix, the `nu` parameter can be set to `True` - such that the Legendre scattering moments are calculated as: - - .. math:: - - \sigma_{s,\ell,g'\rightarrow g} = \upsilon_{g'\rightarrow g} \times - \sigma_s (r, E) \times P_{s,\ell,g'\rightarrow g} - - Parameters - ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh - The domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - The domain type for spatial homogenization - groups : openmc.mgxs.EnergyGroups - The energy group structure for energy condensation - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - name : str, optional - Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC 'tallies.xml' file. - num_polar : Integral, optional - Number of equi-width polar angle bins for angle discretization; - defaults to one bin - num_azimuthal : Integral, optional - Number of equi-width azimuthal angle bins for angle discretization; - defaults to one bin - nu : bool - If True, the cross section data will include neutron multiplication; - defaults to False - - Attributes - ---------- - correction : 'P0' or None - Apply the P0 correction to scattering matrices if set to 'P0'; this is - used only if :attr:`ConsistentScatterMatrixXS.scatter_format` is - 'legendre' - scatter_format : {'legendre', or 'histogram'} - Representation of the angular scattering distribution (default is - 'legendre') - legendre_order : int - The highest Legendre moment in the scattering matrix; this is used if - :attr:`ConsistentScatterMatrixXS.scatter_format` is 'legendre'. - (default is 0) - histogram_bins : int - The number of equally-spaced bins for the histogram representation of - the angular scattering distribution; this is used if - :attr:`ConsistentScatterMatrixXS.scatter_format` is 'histogram'. - (default is 16) - name : str, optional - Name of the multi-group cross section - rxn_type : str - Reaction type (e.g., 'total', 'nu-fission', etc.) - nu : bool - If True, the cross section data will include neutron multiplication - by_nuclide : bool - If true, computes cross sections for each nuclide in domain - domain : Material or Cell or Universe or Mesh - Domain for spatial homogenization - domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} - Domain type for spatial homogenization - energy_groups : openmc.mgxs.EnergyGroups - Energy group structure for energy condensation - num_polar : Integral - Number of equi-width polar angle bins for angle discretization - num_azimuthal : Integral - Number of equi-width azimuthal angle bins for angle discretization - tally_trigger : openmc.Trigger - An (optional) tally precision trigger given to each tally used to - compute the cross section - scores : list of str - The scores in each tally used to compute the multi-group cross section - filters : list of openmc.Filter - The filters in each tally used to compute the multi-group cross section - tally_keys : list of str - The keys into the tallies dictionary for each tally used to compute - the multi-group cross section - estimator : 'analog' - The tally estimator used to compute the multi-group cross section - tallies : collections.OrderedDict - OpenMC tallies needed to compute the multi-group cross section. The keys - are strings listed in the :attr:`ConsistentScatterMatrixXS.tally_keys` - property - and values are instances of :class:`openmc.Tally`. - rxn_rate_tally : openmc.Tally - Derived tally for the reaction rate tally used in the numerator to - compute the multi-group cross section. This attribute is None - unless the multi-group cross section has been computed. - xs_tally : openmc.Tally - Derived tally for the multi-group cross section. This attribute - is None unless the multi-group cross section has been computed. - num_subdomains : int - The number of subdomains is unity for 'material', 'cell' and 'universe' - domain types. This is equal to the number of cell instances - for 'distribcell' domain types (it is equal to unity prior to loading - tally data from a statepoint file). - num_nuclides : int - The number of nuclides for which the multi-group cross section is - being tracked. This is unity if the by_nuclide attribute is False. - nuclides : Iterable of str or 'sum' - The optional user-specified nuclides for which to compute cross - sections (e.g., 'U238', 'O16'). If by_nuclide is True but nuclides - are not specified by the user, all nuclides in the spatial domain - are included. This attribute is 'sum' if by_nuclide is false. - sparse : bool - Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format - for compressed data storage - loaded_sp : bool - Whether or not a statepoint file has been loaded with tally data - derived : bool - Whether or not the MGXS is merged from one or more other MGXS - hdf5_key : str - The key used to index multi-group cross sections in an HDF5 data store - - """ - - def __init__(self, domain=None, domain_type=None, groups=None, - by_nuclide=False, name='', num_polar=1, - num_azimuthal=1, nu=False): - super(ConsistentScatterMatrixXS, self).__init__( - domain, domain_type, groups, by_nuclide=by_nuclide, - name=name, num_polar=num_polar, num_azimuthal=num_azimuthal) - - self.nu = nu - - # Initialize each MGXS used by the convolution - self._mgxs = \ - [ScatterXS(), ScatterProbabilityMatrix(), MultiplicityMatrixXS()] - - # Assign parameters to each MGXS in the convlution - for mgxs in self.mgxs: - mgxs.name = name - mgxs.by_nuclide = by_nuclide - mgxs.nu = nu - - if domain_type is not None: - mgxs.domain_type = domain_type - if domain is not None: - mgxs.domain = domain - if groups is not None: - mgxs.energy_groups = groups - mgxs.num_polar = num_polar - mgxs.num_azimuthal = num_azimuthal - - @property - def scores(self): - scores = super(ConsistentScatterMatrixXS, self).scores - - # Add scores for transport correction - if self.correction == 'P0' and self.legendre_order == 0: - scores += ['{}-1'.format(self.rxn_type)] - - return scores - - @property - def filters(self): - filters = super(ConsistentScatterMatrixXS, self).filters - - # Add filters for transport correction - if self.correction == 'P0' and self.legendre_order == 0: - group_edges = self.energy_groups.group_edges - energyout = openmc.EnergyoutFilter(group_edges) - filters += self._add_angle_filters([[energyout]]) - - return filters - - @property - def tally_keys(self): - tally_keys = super(ConsistentScatterMatrixXS, self).tally_keys - - # Add key for transport correction tally - if self.correction == 'P0' and self.legendre_order == 0: - tally_keys += ['{}-1'.format(self.rxn_type)] - - return tally_keys - - @property - def tallies(self): - - if self._tallies is None: - self._tallies = super(ConsistentScatterMatrixXS, self).tallies - - # Add in the transport correction tally - if self.correction == 'P0' and self.legendre_order == 0: - tally_key = '{}-1'.format(self.rxn_type) - - # Create a domain Filter object - filter_type = _DOMAIN_TO_FILTER[self.domain_type] - if self.domain_type == 'mesh': - domain_filter = filter_type(self.domain) - else: - domain_filter = filter_type(self.domain.id) - - # Create each Tally needed to compute the multi group cross section - self._tallies[tally_key] = openmc.Tally(name=self.name) - self._tallies[tally_key].estimator = 'analog' - self._tallies[tally_key].scores = [self.scores[-1]] - self._tallies[tally_key].filters = [domain_filter] - self._tallies[tally_key].filters.extend(self.filters[-1]) - - # If a tally trigger was specified, add it to each tally - if self.tally_trigger: - trigger_clone = copy.deepcopy(self.tally_trigger) - trigger_clone.scores = [self.scores[-1]] - self._tallies[tally_key].triggers.append(trigger_clone) - - # If this is a by-nuclide cross-section, add nuclides to Tally - if self.by_nuclide: - self._tallies[tally_key].nuclides += self.get_nuclides() - else: - self._tallies[tally_key].nuclides.append('total') - - return self._tallies - - @property - def scatter_xs(self): - return self.mgxs[0] - - @property - def probability_matrix(self): - return self.mgxs[1] - - @property - def multiplicity_matrix(self): - return self.mgxs[2] - - @property - def rxn_rate_tally(self): - raise NotImplementedError('The reaction rate tally is not well defined ') - - @property - def xs_tally(self): - if self._xs_tally is None: - if self.tallies is None: - msg = 'Unable to get xs_tally since tallies have ' \ - 'not been loaded from a statepoint' - raise ValueError(msg) - - # If using P0 correction subtract scatter-1 from the diagonal - if self.correction == 'P0' and self.legendre_order == 0: - flux = self.tallies['{} : flux'.format(self.rxn_type)] - scatter_p0 = self.tallies['{0} : {0}'.format(self.rxn_type)] - scatter_p1 = self.tallies['{}-1'.format(self.rxn_type)] - - energy_filter = scatter_p0.find_filter(openmc.EnergyFilter) - energy_filter = copy.deepcopy(energy_filter) - scatter_p1 = scatter_p1.diagonalize_filter(energy_filter) - correction = scatter_p1 / flux - else: - correction = 0. - - self._xs_tally = \ - self.scatter_xs.xs_tally * self.probability_matrix.xs_tally - - if self.nu: - self._xs_tally *= self.multiplicity_matrix.xs_tally - - self._xs_tally -= correction - self._compute_xs() - - return self._xs_tally - - @ScatterMatrixXS.correction.setter - def correction(self, correction): - ScatterMatrixXS.correction.fset(self, correction) - self.mgxs[2].correction = correction - - @ScatterMatrixXS.scatter_format.setter - def scatter_format(self, scatter_format): - ScatterMatrixXS.scatter_format.fset(self, scatter_format) - self.mgxs[1].scatter_format = scatter_format - - @ScatterMatrixXS.legendre_order.setter - def legendre_order(self, legendre_order): - ScatterMatrixXS.legendre_order.fset(self, legendre_order) - self.mgxs[1].legendre_order = legendre_order - - @ScatterMatrixXS.histogram_bins.setter - def histogram_bins(self, histogram_bins): - ScatterMatrixXS.histogram_bins.fset(self, histogram_bins) - self.mgxs[1].histogram_bins = histogram_bins - - @ScatterMatrixXS.nu.setter - def nu(self, nu): - cv.check_type('nu', nu, bool) - self._nu = nu - - if not nu: - self._rxn_type = 'scatter' - self._hdf5_key = 'consistent scatter matrix' - else: - self._rxn_type = 'nu-scatter' - self._hdf5_key = 'consistent nu-scatter matrix' - - for mgxs in self.mgxs: - mgxs.nu = nu - - def load_from_statepoint(self, statepoint): - """Extracts tallies in an OpenMC StatePoint with the data needed to - compute multi-group cross sections. - - This method is needed to compute cross section data from tallies - in an OpenMC StatePoint object. - - .. note:: The statepoint must be linked with an OpenMC Summary object. - - Parameters - ---------- - statepoint : openmc.StatePoint - An OpenMC StatePoint object with tally data - - Raises - ------ - ValueError - When this method is called with a statepoint that has not been - linked with a summary object. - - """ - - # Clear any tallies previously loaded from a statepoint - if self.loaded_sp: - self._tallies = None - self._xs_tally = None - self._rxn_rate_tally = None - self._loaded_sp = False - - if self.scatter_format == 'legendre': - # Expand scores to match the format in the statepoint - # e.g., "scatter-P2" -> "scatter-0", "scatter-1", "scatter-2" - tally_key = '{}-P{}'.format(self.rxn_type, self.legendre_order) - tally_key = \ - '{} probability matrix : {}'.format(self.rxn_type, tally_key) - self.tallies[tally_key].scores = \ - [self.rxn_type + '-{}'.format(i) - for i in range(self.legendre_order + 1)] - elif self.scatter_format == 'histogram': - self.tallies[self.rxn_type].scores = [self.rxn_type] - - super(ConsistentScatterMatrixXS, self).load_from_statepoint(statepoint) - - def get_condensed_xs(self, coarse_groups): - condense_xs = \ - super(ConsistentScatterMatrixXS, self).get_condensed_xs(coarse_groups) - - for i, mgxs in enumerate(condense_xs._mgxs): - condense_xs._mgxs[i] = mgxs.get_condensed_xs(coarse_groups) - - return condense_xs - - def get_subdomain_avg_xs(self, subdomains='all'): - avg_xs = \ - super(ConsistentScatterMatrixXS, self).get_subdomain_avg_xs(subdomains) - - for i, mgxs in enumerate(avg_xs._mgxs): - avg_xs._mgxs[i] = mgxs.get_subdomain_avg_xs(subdomains) - - return avg_xs - - class NuFissionMatrixXS(MatrixMGXS): r"""A fission production matrix multi-group cross section.