From 756a43f1c563276af124b520a974389f8b286114 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 28 Jan 2017 11:34:29 -0500 Subject: [PATCH 01/16] Added ability to convert the isotropic or angle representation from one to the other for XSdata objects and MGXSLibrary objects --- openmc/mgxs/mgxs.py | 2 +- openmc/mgxs_library.py | 196 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 4 deletions(-) diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index de90d4397a..9f1ac59d1c 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -205,7 +205,7 @@ class MGXS(object): clone._name = self.name clone._rxn_type = self.rxn_type clone._by_nuclide = self.by_nuclide - clone._nuclides = copy.deepcopy(self._nuclides) + clone._nuclides = copy.deepcopy(self._nuclides, memo) clone._domain = self.domain clone._domain_type = self.domain_type clone._energy_groups = copy.deepcopy(self.energy_groups, memo) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 4fb347b73c..09f7e421bb 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1,4 +1,4 @@ -from collections import Iterable +import copy from numbers import Real, Integral import os @@ -185,6 +185,52 @@ class XSdata(object): self._inverse_velocity = len(temperatures) * [None] self._xs_shapes = None + def __deepcopy__(self, memo): + existing = memo.get(id(self)) + + # If this is the first time we have tried to copy this object, copy it + if existing is None: + clone = type(self).__new__(type(self)) + clone._name = self.name + clone._energy_groups = copy.deepcopy(self.energy_groups, memo) + clone._num_delayed_groups = self.num_delayed_groups + clone._temperatures = copy.deepcopy(self.temperatures, memo) + clone._drepresentation = self.representation + clone._atomic_weight_ratio = self._atomic_weight_ratio + clone._fissionable = self._fissionable + clone._scatter_format = self._scatter_format + clone._order = self._order + clone._num_polar = self._num_polar + clone._num_azimuthal = self._num_azimuthal + clone._total = copy.deepcopy(self._total, memo) + clone._absorption = copy.deepcopy(self._absorption, memo) + clone._scatter_matrix = copy.deepcopy(self._scatter_matrix, memo) + clone._multiplicity_matrix = \ + copy.deepcopy(self._multiplicity_matrix, memo) + clone._fission = copy.deepcopy(self._fission, memo) + clone._nu_fission = copy.deepcopy(self._nu_fission, memo) + clone._prompt_nu_fission = \ + copy.deepcopy(self._prompt_nu_fission, memo) + clone._delayed_nu_fission = \ + copy.deepcopy(self._delayed_nu_fission, memo) + clone._kappa_fission = copy.deepcopy(self._kappa_fission, memo) + clone._chi = copy.deepcopy(self._chi, memo) + clone._chi_prompt = copy.deepcopy(self._chi_prompt, memo) + clone._chi_delayed = copy.deepcopy(self._chi_delayed, memo) + clone._beta = copy.deepcopy(self._beta, memo) + clone._decay_rate = copy.deepcopy(self._decay_rate, memo) + clone._inverse_velocity = \ + copy.deepcopy(self._inverse_velocity, memo) + clone._xs_shapes = copy.deepcopy(self._xs_shapes, memo) + + memo[id(self)] = clone + + return clone + + # If this object has been copied before, return the first copy made + else: + return existing + @property def name(self): return self._name @@ -348,7 +394,7 @@ class XSdata(object): @representation.setter def representation(self, representation): - # Check it is of valid type. + # Check it is of valid value. check_value('representation', representation, _REPRESENTATIONS) self._representation = representation @@ -1622,7 +1668,8 @@ class XSdata(object): """ - check_type('inverse_velocity', inverse_velocity, openmc.mgxs.InverseVelocity) + check_type('inverse_velocity', inverse_velocity, + openmc.mgxs.InverseVelocity) check_value('energy_groups', inverse_velocity.energy_groups, [self.energy_groups]) check_value('domain_type', inverse_velocity.domain_type, @@ -1634,6 +1681,89 @@ class XSdata(object): self._inverse_velocity[i] = inverse_velocity.get_xs( nuclides=nuclide, xs_type=xs_type, subdomains=subdomain) + def convert_representation(self, target_representation, num_polar=None, + num_azimuthal=None): + """Produce a new XSdata object with the same data, but converted to the + new representation + + Parameters + ---------- + target_representation : {'isotropic', 'angle'} + Representation of the MGXS (isotropic or angle-dependent flux + weighting). + num_polar : int, optional + Number of equal width angular bins that the polar angular + domain is subdivided into. This is required when + :param:`target_representation` is "angle". + num_azimuthal : int, optional + Number of equal width angular bins that the azimuthal angular + domain is subdivided into. This is required when + :param:`target_representation` is "angle". + + Returns + ------- + openmc.XSdata + Multi-group cross section data with the same data as self, but + represented as specified in :param:`target_representation`. + + """ + + check_value('target_representation', target_representation, + _REPRESENTATIONS) + if target_representation == 'angle': + check_type('num_polar', num_polar, Integral) + check_type('num_azimuthal', num_azimuthal, Integral) + check_greater_than('num_polar', num_polar, 0) + check_greater_than('num_azimuthal', num_azimuthal, 0) + + xsdata = copy.deepcopy(self) + if target_representation == self.representation: + # Check to make sure the num_polar and num_azimuthal values match + if target_representation == 'angle': + if num_polar != self.num_polar or num_azimuthal != self.num_azimuthal: + raise NotImplementedError("XSdata.convert_representation " + "cannot translate between " + "`angle` representations with " + "different angle bin structures") + # Nothing to do + return xsdata + + types = ['total', 'absorption', 'fission', 'nu_fission', + 'scatter_matrix', 'multiplicity_matrix', 'prompt_nu_fission', + 'delayed_nu_fission', 'kappa_fission', 'chi', 'chi_prompt', + 'chi_delayed', 'beta', 'decay_rate', 'inverse_velocity'] + + xsdata.representation = target_representation + # We have different actions depending on the representation conversion + if target_representation == 'isotropic': + xsdata.num_polar = None + xsdata.num_azimuthal = None + for i, temp in enumerate(xsdata.temperatures): + for xs in types: + # Get the original data + orig_data = getattr(self, '_' + xs)[i] + # Since we are going from angle to isotropic, the current + # data should be just averaged over the angle bins + new_data = orig_data.mean(axis=(0, 1)) + setter = getattr(xsdata, 'set_' + xs) + setter(new_data, temp) + + elif target_representation == 'angle': + xsdata.num_polar = num_polar + xsdata.num_azimuthal = num_azimuthal + for i, temp in enumerate(xsdata.temperatures): + for xs in types: + # Get the original data + orig_data = getattr(self, '_' + xs)[i] + # Since we are going from isotropic to angle, the current + # data should be just copied for every polar/azimuthal bin + new_shape = (num_polar, num_azimuthal) + orig_data.shape + new_data = np.resize(orig_data, new_shape) + setter = getattr(xsdata, 'set_' + xs) + setter(new_data, temp) + + return xsdata + def to_hdf5(self, file): """Write XSdata to an HDF5 file @@ -1995,6 +2125,24 @@ class MGXSLibrary(object): self.num_delayed_groups = num_delayed_groups self._xsdatas = [] + def __deepcopy__(self, memo): + existing = memo.get(id(self)) + + # If this is the first time we have tried to copy this object, copy it + if existing is None: + clone = type(self).__new__(type(self)) + clone._energy_groups = copy.deepcopy(self.energy_groups, memo) + clone._num_delayed_groups = self.num_delayed_groups + clone._xsdatas = copy.deepcopy(self.xsdatas, memo) + + memo[id(self)] = clone + + return clone + + # If this object has been copied before, return the first copy made + else: + return existing + @property def energy_groups(self): return self._energy_groups @@ -2099,6 +2247,48 @@ class MGXSLibrary(object): result = xsdata return result + def convert_representation(self, target_representation, num_polar=None, + num_azimuthal=None): + """Produce a new MGXSLibrary object with the same data, but converted + to the new representation + + Parameters + ---------- + target_representation : {'isotropic', 'angle'} + Representation of the MGXS (isotropic or angle-dependent flux + weighting). + num_polar : int, optional + Number of equal width angular bins that the polar angular + domain is subdivided into. This is required when + :param:`target_representation` is "angle". + num_azimuthal : int, optional + Number of equal width angular bins that the azimuthal angular + domain is subdivided into. This is required when + :param:`target_representation` is "angle". + + Returns + ------- + openmc.MGXSLibrary + Multi-group Library with the same data as self, but represented as + specified in :param:`target_representation`. + + """ + + check_value('target_representation', target_representation, + _REPRESENTATIONS) + if target_representation == 'angle': + check_type('num_polar', num_polar, Integral) + check_type('num_azimuthal', num_azimuthal, Integral) + check_greater_than('num_polar', num_polar, 0) + check_greater_than('num_azimuthal', num_azimuthal, 0) + + library = copy.deepcopy(self) + for i, xsdata in enumerate(self.xsdatas): + library.xsdatas[i] = \ + xsdata.convert_representation(target_representation, + num_polar, num_azimuthal) + return library + def export_to_hdf5(self, filename='mgxs.h5'): """Create an hdf5 file that can be used for a simulation. From 1cd15696c17eb1c4bb63926aad5b0a4efc2a7ed3 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 28 Jan 2017 13:44:38 -0500 Subject: [PATCH 02/16] fixes to convert_representation and MGXSLibrary implementation of convert_scatter_format --- openmc/mgxs_library.py | 83 ++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 09f7e421bb..5a23e6dbe6 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1736,29 +1736,33 @@ class XSdata(object): xsdata.representation = target_representation # We have different actions depending on the representation conversion if target_representation == 'isotropic': - xsdata.num_polar = None - xsdata.num_azimuthal = None - for i, temp in enumerate(xsdata.temperatures): - for xs in types: - # Get the original data - orig_data = getattr(self, '_' + xs)[i] - # Since we are going from angle to isotropic, the current - # data should be just averaged over the angle bins - new_data = orig_data.mean(axis=(0, 1)) - setter = getattr(xsdata, 'set_' + xs) - setter(new_data, temp) - + # This is not needed for the correct functionality, but these + # values are changed back to None for clarity + xsdata._num_polar = None + xsdata._num_azimuthal = None elif target_representation == 'angle': xsdata.num_polar = num_polar xsdata.num_azimuthal = num_azimuthal - for i, temp in enumerate(xsdata.temperatures): - for xs in types: - # Get the original data - orig_data = getattr(self, '_' + xs)[i] - # Since we are going from isotropic to angle, the current - # data should be just copied for every polar/azimuthal bin - new_shape = (num_polar, num_azimuthal) + orig_data.shape - new_data = np.resize(orig_data, new_shape) + + # Reset XSdata.xs_shapes to accomodate the new shape + # import pdb; pdb.set_trace() + xsdata._xs_shapes = None + xsdata.xs_shapes + + for i, temp in enumerate(xsdata.temperatures): + for xs in types: + # Get the original data + orig_data = getattr(self, '_' + xs)[i] + if orig_data is not None: + if target_representation == 'isotropic': + # Since we are going from angle to isotropic, the + # current data is just averaged over the angle bins + new_data = orig_data.mean(axis=(0, 1)) + elif target_representation == 'angle': + # Since we are going from isotropic to angle, the + # current data is just copied for every angle bin + new_shape = (num_polar, num_azimuthal) + orig_data.shape + new_data = np.resize(orig_data, new_shape) setter = getattr(xsdata, 'set_' + xs) setter(new_data, temp) @@ -2289,6 +2293,45 @@ class MGXSLibrary(object): num_polar, num_azimuthal) return library + def convert_scatter_format(self, target_format, target_order): + """Produce a new MGXSLibrary object with the same data, but converted + to the new scatter format and order + + Parameters + ---------- + target_format : {'isotropic', 'angle'} + Representation of the MGXS (isotropic or angle-dependent flux + weighting). + target_order : int + Either the Legendre target_order, number of bins, or number of + points used to describe the angular distribution associated with + each group-to-group transfer probability. + + Returns + ------- + openmc.MGXSLibrary + Multi-group Library with the same data as self, but with the + scatter format represented as specified in :param:`target_format` + and :param:`target_order`. + + """ + + check_value('target_format', target_format, _SCATTER_TYPES) + check_type('target_order', target_order, Integral) + if target_format == 'legendre': + check_greater_than('target_order', target_order, Integral, 0, + equality=True) + else: + check_greater_than('target_order', target_order, Integral, 0, + equality=True) + + library = copy.deepcopy(self) + for i, xsdata in enumerate(self.xsdatas): + library.xsdatas[i] = \ + xsdata.convert_scatter_format(target_format, target_order) + + return library + def export_to_hdf5(self, filename='mgxs.h5'): """Create an hdf5 file that can be used for a simulation. From 1c367b049c77cf945565187fb384a5a1b49be0dc Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 29 Jan 2017 15:25:00 -0500 Subject: [PATCH 03/16] Finished convert_scatter_format; tested, seems to be great! --- openmc/mgxs_library.py | 206 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 193 insertions(+), 13 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 5a23e6dbe6..b740b4dae9 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -14,9 +14,13 @@ from openmc.checkvalue import check_type, check_value, check_greater_than, \ # Supported incoming particle MGXS angular treatment representations _REPRESENTATIONS = ['isotropic', 'angle'] +# Supported scattering angular distribution representations _SCATTER_TYPES = ['tabular', 'legendre', 'histogram'] +# List of MGXS dimension types _XS_SHAPES = ["[G][G'][Order]", "[G]", "[G']", "[G][G']", "[DG]", "[DG][G]", "[DG][G']", "[DG][G][G']"] +# Number of mu points for conversion between scattering formats +_NMU = 257 class XSdata(object): @@ -195,7 +199,7 @@ class XSdata(object): clone._energy_groups = copy.deepcopy(self.energy_groups, memo) clone._num_delayed_groups = self.num_delayed_groups clone._temperatures = copy.deepcopy(self.temperatures, memo) - clone._drepresentation = self.representation + clone._representation = self.representation clone._atomic_weight_ratio = self._atomic_weight_ratio clone._fissionable = self._fissionable clone._scatter_format = self._scatter_format @@ -1745,9 +1749,7 @@ class XSdata(object): xsdata.num_azimuthal = num_azimuthal # Reset XSdata.xs_shapes to accomodate the new shape - # import pdb; pdb.set_trace() xsdata._xs_shapes = None - xsdata.xs_shapes for i, temp in enumerate(xsdata.temperatures): for xs in types: @@ -1761,13 +1763,194 @@ class XSdata(object): elif target_representation == 'angle': # Since we are going from isotropic to angle, the # current data is just copied for every angle bin - new_shape = (num_polar, num_azimuthal) + orig_data.shape + new_shape = (num_polar, num_azimuthal) + \ + orig_data.shape new_data = np.resize(orig_data, new_shape) setter = getattr(xsdata, 'set_' + xs) setter(new_data, temp) return xsdata + def convert_scatter_format(self, target_format, target_order=None): + """Produce a new MGXSLibrary object with the same data, but converted + to the new scatter format and order + + Parameters + ---------- + target_format : {'tabular', 'legendre', 'histogram'} + Representation of the scattering angle distribution + target_order : int + Either the Legendre target_order, number of bins, or number of + points used to describe the angular distribution associated with + each group-to-group transfer probability + + Returns + ------- + openmc.XSdata + Multi-group cross section data with the same data as self, but + represented as specified in :param:`target_format`. + + """ + + from scipy.interpolate import interp1d + from scipy.integrate import quad, simps + from scipy.special import eval_legendre + + check_value('target_format', target_format, _SCATTER_TYPES) + check_type('target_order', target_order, Integral) + if target_format == 'legendre': + check_greater_than('target_order', target_order, 0, equality=True) + else: + check_greater_than('target_order', target_order, 0) + + xsdata = copy.deepcopy(self) + xsdata.scatter_format = target_format + xsdata.order = target_order + # Reset XSdata.xs_shapes to accomodate the new shape + xsdata._xs_shapes = None + xsdata.xs_shapes + + # We have to accomodate the following possibilities: + # histogram -> tabular w/ same or diff order + # histogram -> legendre + # histogram -> histogram w/ same or diff order + + for i, temp in enumerate(xsdata.temperatures): + orig_data = self._scatter_matrix[i] + new_shape = orig_data.shape[:-1] + (xsdata.num_orders,) + new_data = np.zeros(new_shape) + if self.scatter_format == 'legendre': + if target_format == 'legendre': + # Then we are changing orders and only need to change + # dimensionality of the mu data and pad/truncate as needed + order = min(xsdata.num_orders, self.num_orders) + new_data[..., :order] = orig_data[..., :order] + elif target_format == 'tabular': + mu = np.linspace(-1, 1, xsdata.num_orders) + # Create the Legendre series and either do a point-wise + # evaluation (tabular) or bin-wise integral (histogram) + new_data[..., :] = \ + np.sum((l + 0.5) * eval_legendre(l, mu) * orig_data + for l in range(self.num_orders)) + elif target_format == 'histogram': + mu = np.linspace(-1, 1, xsdata.num_orders + 1) + # This code will be written to utilize the vectorized + # integration capabilities instead of having an isotropic + # and angle representation path. + for h_bin in range(xsdata.num_orders): + mu_fine = np.linspace(mu[i], mu[i + 1], _NMU) + new_data[..., h_bin] = \ + simps(np.sum((l + 0.5) * + eval_legendre(l, mu_fine) * + orig_data + for l in range(self.num_orders)), + mu_fine) + + # Remove the very small results from numerical precision + # issues (allowing conversions to be reproduced exactly) + new_data[..., np.abs(new_data) < 1.E-10] = 0. + + elif self.scatter_format == 'tabular': + mu_self = np.linspace(-1, 1, self.num_orders) + if target_format == 'legendre': + # find the legendre coefficients via integration. To best + # use the vectorized integration capabilities of scipy, + # this will be done with fixed sample integration routines. + mu_fine = np.linspace(-1, 1, _NMU) + y = [interp1d(mu_self, orig_data)(mu_fine) * + eval_legendre(l, mu_fine) + for l in range(xsdata.num_orders)] + for l in range(xsdata.num_orders): + new_data[..., l] = (l + 0.5) * simps(y[l], mu_fine) + + # Remove the very small results from numerical precision + # issues (allowing conversions to be reproduced exactly) + new_data[..., np.abs(new_data) < 1.E-10] = 0. + elif target_format == 'tabular': + # Simply use an interpolating function to get the new data + mu = np.linspace(-1, 1, xsdata.num_orders) + new_data[..., :] = interp1d(mu_self, orig_data)(mu) + elif target_format == 'histogram': + # Use an interpolating function to do the bin-wise + # integrals + mu = np.linspace(-1, 1, xsdata.num_orders + 1) + + # Like the tabular -> legendre path above, this code will + # be written to utilize the vectorized integration + # capabilities instead of having an isotropic and + # angle representation path. + interp = interp1d(mu_self, orig_data) + for h_bin in range(xsdata.num_orders): + mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) + new_data[..., h_bin] = simps(interp(mu_fine), mu_fine) + + # Remove the very small results from numerical precision + # issues (allowing conversions to be reproduced exactly) + new_data[..., np.abs(new_data) < 1.E-10] = 0. + elif self.scatter_format == 'histogram': + # The histogram format does not have enough information to + # convert to the other forms without inducing an error. + # We will make the assumption that the left-edge of the bin + # has the value of the bin. The mu=1 value will be extrapolated + # from the previous two points. + mu_self = np.linspace(-1, 1, self.num_orders + 1) + tab_data = np.zeros(orig_data.shape[:-1] + + (orig_data.shape[-1] + 1,)) + tab_data[..., :-1] = orig_data + tab_data[..., -1] = (orig_data[..., -2] - + orig_data[..., -3]) + orig_data[..., -2] + # Now get the distribution normalization factor.such + # This factor will be such that its average value is the + # group -> group cross section (which is the sum of the + # original data) + norm = np.sum(orig_data, axis=-1) / np.mean(tab_data, axis=-1) + norm = np.nan_to_num(norm) + for i in range(tab_data.shape[-1]): + tab_data[..., i] *= norm[...] + + # We now have a tabular distribution in tab_data on mu_self + # with a normalization in norm. We now proceed just like the + # tabular branch above. + if target_format == 'legendre': + # find the legendre coefficients via integration. To best + # use the vectorized integration capabilities of scipy, + # this will be done with fixed sample integration routines. + mu_fine = np.linspace(-1, 1, _NMU) + y = [interp1d(mu_self, tab_data)(mu_fine) * + eval_legendre(l, mu_fine) + for l in range(xsdata.num_orders)] + for l in range(xsdata.num_orders): + new_data[..., l] = (l + 0.5) * simps(y[l], mu_fine) + + # Remove the very small results from numerical precision + # issues (allowing conversions to be reproduced exactly) + new_data[..., np.abs(new_data) < 1.E-10] = 0. + elif target_format == 'tabular': + # Simply use an interpolating function to get the new data + mu = np.linspace(-1, 1, xsdata.num_orders) + new_data[..., :] = interp1d(mu_self, tab_data)(mu) + elif target_format == 'histogram': + # Use an interpolating function to do the bin-wise + # integrals + mu = np.linspace(-1, 1, xsdata.num_orders + 1) + + # Like the tabular -> legendre path above, this code will + # be written to utilize the vectorized integration + # capabilities instead of having an isotropic and + # angle representation path. + interp = interp1d(mu_self, tab_data) + for h_bin in range(xsdata.num_orders): + mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) + new_data[..., h_bin] = simps(interp(mu_fine), mu_fine) + + # Remove the very small results from numerical precision + # issues (allowing conversions to be reproduced exactly) + new_data[..., np.abs(new_data) < 1.E-10] = 0. + + xsdata.set_scatter_matrix(new_data, temp) + + return xsdata + def to_hdf5(self, file): """Write XSdata to an HDF5 file @@ -1891,7 +2074,7 @@ class XSdata(object): elif self.representation == 'angle': matrix = \ self._scatter_matrix[i][p, a, g_in, :, 0] - elif self.scatter_format == 'histogram': + else: if self.representation == 'isotropic': matrix = \ np.sum(self._scatter_matrix[i][g_in, :, :], @@ -2299,13 +2482,12 @@ class MGXSLibrary(object): Parameters ---------- - target_format : {'isotropic', 'angle'} - Representation of the MGXS (isotropic or angle-dependent flux - weighting). + target_format : {'tabular', 'legendre', 'histogram'} + Representation of the scattering angle distribution target_order : int Either the Legendre target_order, number of bins, or number of points used to describe the angular distribution associated with - each group-to-group transfer probability. + each group-to-group transfer probability Returns ------- @@ -2319,11 +2501,9 @@ class MGXSLibrary(object): check_value('target_format', target_format, _SCATTER_TYPES) check_type('target_order', target_order, Integral) if target_format == 'legendre': - check_greater_than('target_order', target_order, Integral, 0, - equality=True) + check_greater_than('target_order', target_order, 0, equality=True) else: - check_greater_than('target_order', target_order, Integral, 0, - equality=True) + check_greater_than('target_order', target_order, 0) library = copy.deepcopy(self) for i, xsdata in enumerate(self.xsdatas): From 7e1700227c6bf7dc43e122813f2f12423e71f234 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 5 Feb 2017 15:56:23 -0500 Subject: [PATCH 04/16] Fixes to scatter format conversion as well as initial implementation of conversion of MG library to CE --- openmc/mgxs_library.py | 253 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 239 insertions(+), 14 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index b740b4dae9..6f46f48875 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1793,7 +1793,7 @@ class XSdata(object): """ from scipy.interpolate import interp1d - from scipy.integrate import quad, simps + from scipy.integrate import simps from scipy.special import eval_legendre check_value('target_format', target_format, _SCATTER_TYPES) @@ -1827,24 +1827,34 @@ class XSdata(object): new_data[..., :order] = orig_data[..., :order] elif target_format == 'tabular': mu = np.linspace(-1, 1, xsdata.num_orders) - # Create the Legendre series and either do a point-wise - # evaluation (tabular) or bin-wise integral (histogram) - new_data[..., :] = \ - np.sum((l + 0.5) * eval_legendre(l, mu) * orig_data - for l in range(self.num_orders)) + # Evaluate the legendre on the tabular grid within mu + for imu in range(len(mu)): + new_data[..., imu] = \ + np.sum((l + 0.5) * eval_legendre(l, mu[imu]) * + orig_data[..., l] + for l in range(self.num_orders)) elif target_format == 'histogram': mu = np.linspace(-1, 1, xsdata.num_orders + 1) # This code will be written to utilize the vectorized # integration capabilities instead of having an isotropic # and angle representation path. for h_bin in range(xsdata.num_orders): - mu_fine = np.linspace(mu[i], mu[i + 1], _NMU) - new_data[..., h_bin] = \ - simps(np.sum((l + 0.5) * - eval_legendre(l, mu_fine) * - orig_data - for l in range(self.num_orders)), - mu_fine) + mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) + table_shape = new_data.shape[:-1] + (_NMU,) + table_fine = np.zeros(table_shape) + for imu in range(len(mu_fine)): + table_fine[..., imu] = \ + np.sum((l + 0.5) * + eval_legendre(l, mu_fine[imu]) * + orig_data[..., l] + for l in range(self.num_orders)) + new_data[..., h_bin] = simps(table_fine, mu_fine) + # new_data[..., h_bin] = \ + # simps(np.sum((l + 0.5) * + # eval_legendre(l, mu_fine) * + # orig_data[..., l] + # for l in range(self.num_orders)), + # mu_fine) # Remove the very small results from numerical precision # issues (allowing conversions to be reproduced exactly) @@ -1899,7 +1909,7 @@ class XSdata(object): tab_data[..., :-1] = orig_data tab_data[..., -1] = (orig_data[..., -2] - orig_data[..., -3]) + orig_data[..., -2] - # Now get the distribution normalization factor.such + # Now get the distribution normalization factor. # This factor will be such that its average value is the # group -> group cross section (which is the sum of the # original data) @@ -1951,6 +1961,198 @@ class XSdata(object): return xsdata + def convert_to_continuous_energy(self, temperature=294.): + """Converts the XSdata object to an equivalent + openmc.data.IncidentNeutron object. + + Parameters + ---------- + temperature : float + Temperature of dataset to print; defaults to 294K + + Returns + ------- + openmc.data.IncidentNeutron + The continuous-energy IncidentNeutron data library equivalent + to the MGXS data in self + """ + + # Check if this can be performed successfully + if self.representation == 'angle': + raise ValueError("Cannot convert angle-dependent MGXS; convert to " + "an isotropic representation first") + required_types = ['absorption', 'scatter_matrix'] + for type_check in required_types: + if getattr(self, '_' + type_check)[0] is None: + raise ValueError(type_check + ' data is required') + if temperature not in self.temperatures: + raise ValueError("Invalid temperature") + + def convert_xs(group_edges, values): + cexs = openmc.data.Tabulated1D(group_edges, + np.append(values[::-1], + [values[0]]), + breakpoints=[len(group_edges)], + interpolation=[1]) + return cexs + + # Build required metadata + kTs = self.temperatures[:] * openmc.data.K_BOLTZMANN + if self.atomic_weight_ratio: + awr = self.atomic_weight_ratio + else: + awr = 1. + + # Get the temperature index + iT = np.where(self.temperatures == temperature)[0][0] + data = openmc.data.IncidentNeutron(self.name, 1, 1, 0, awr, kTs) + strT = "{}K".format(int(round(self.temperatures[iT]))) + data.energy = {strT: self.energy_groups.group_edges} + energy_midpoints = (self.energy_groups.group_edges[1:] + + self.energy_groups.group_edges[:-1]) / 2. + + # Elastic MGXS: must explicitly be 0 barns to avoid incorrect + # elastic_scatter calculation with data available to us in MG library. + el_rxn = openmc.data.Reaction(2) + el_rxn.xs[strT] = \ + convert_xs(self.energy_groups.group_edges, + np.zeros(self.energy_groups.num_groups)) + data.reactions[2] = el_rxn + + # Absorption MGXS + abs_rxn = openmc.data.Reaction(102) + abs_rxn.xs[strT] = convert_xs(self.energy_groups.group_edges, + np.subtract(self._absorption[iT], + self._fission[iT])) + data.reactions[102] = abs_rxn + + if self._nu_fission[iT] is not None and self._fission[iT] is not None: + fiss_rxn = openmc.data.Reaction(18) + fiss_rxn.xs[strT] = convert_xs(self.energy_groups.group_edges, + self._fission[iT]) + + # Get nu_fission and chi from the presence of both, or the + # nu_fission matrix + if self._nu_fission[iT].shape == self.xs_shapes["[G][G']"]: + nu_fiss = np.sum(self._nu_fission[iT], axis=1)[::-1] + chi = self._nu_fission[iT][::-1] / nu_fiss + else: + nu_fiss = self._nu_fission[iT][::-1] + chi = np.reshape(np.tile(self._chi[iT][::-1], + self.energy_groups.num_groups), + (self.energy_groups.num_groups, + self.energy_groups.num_groups)) + nu = np.divide(nu_fiss, self._fission[iT][::-1]) + + # Build a histogram distribution to represent the yield for the + # incoming groups + prod = openmc.data.Product() + prod.yield_ = \ + openmc.data.Tabulated1D(self.energy_groups.group_edges[:-1], + nu, breakpoints=[len(nu)], + interpolation=[1]) + + # Now build the outgoing energy distribution using discrete energy + # lines within each of the outgoing groups + chi_eouts = [] + for g in range(self.energy_groups.num_groups): + chi_eouts.append(openmc.stats.Discrete(energy_midpoints, + chi[g, :])) + # Ensure the distribution CDF starts with 0 + chi_eouts[-1].c = np.cumsum(chi[g, :]) - chi[g, 0] + + # Create the continuous distribution for the fission + # The angular distribution will remain None (thus isotropic) + chi_distrib = openmc.data.ContinuousTabular( + [len(chi)], [1], self.energy_groups.group_edges[:-1], + chi_eouts) + prod.distribution = \ + [openmc.data.UncorrelatedAngleEnergy(energy=chi_distrib)] + fiss_rxn.products = [prod] + fiss_rxn.center_of_mass = False + data.reactions[18] = fiss_rxn + + # Scattering Data + # First convert the data to a tabular representation + if self.scatter_format == 'tabular': + tabular = self + else: + tabular = self.convert_scatter_format('tabular', 33) + + # Calculate the isotropic scattering matrix and use that to find the + # total scattering x/s and outgoing energy distributions + isotropic_matrix = np.mean(tabular._scatter_matrix[iT], axis=-1)[::-1, + ::-1] + scatt_xs = np.sum(isotropic_matrix, axis=1) + energy = np.zeros((self.energy_groups.num_groups, + self.energy_groups.num_groups)) + for gin in range(self.energy_groups.num_groups): + energy[gin, :] = isotropic_matrix[gin, :] / scatt_xs[gin] + + # Get the anisotropic but normalized angular distribution + distrib = np.zeros((self.energy_groups.num_groups, + self.energy_groups.num_groups, + tabular.num_orders)) + for gin in range(self.energy_groups.num_groups): + for gout in range(self.energy_groups.num_groups): + distrib[gin, gout, :] = \ + np.divide(tabular._scatter_matrix[iT][gin, gout, :], + isotropic_matrix[gin, gout]) + distrib = np.nan_to_num(distrib) + + # Incorporate the scattering multiplication, if required + scatt_prod = openmc.data.Product() + if self._multiplicity_matrix[iT]: + yield_ = self._multiplicity_matrix[iT][::-1, ::-1] + scatt_prod.yield_ = \ + openmc.data.Tabulated1D(self.energy_groups.group_edges[:-1], + yield_, breakpoints=[len(yield_)], + interpolation=[1]) + + # Now build the outgoing energy distribution using discrete energy + # lines within each of the outgoing groups + scatt_eouts = [] + for g in range(self.energy_groups.num_groups): + scatt_eouts.append( + openmc.stats.Discrete(energy_midpoints, energy[g, :])) + # Ensure the distribution CDF starts with 0 + scatt_eouts[-1].c = np.cumsum(energy[g, :]) - energy[g, 0] + scatt_eouts.append(scatt_eouts[-1]) + + # Build the angular distributions associated with each outgoing energy + # group + mu = np.linspace(-1., 1., tabular.num_orders) + scatt_angles = [] + for gin in range(self.energy_groups.num_groups): + scatt_angles.append([]) + for gout in range(self.energy_groups.num_groups): + scatt_angles[gin].append( + openmc.stats.Tabular(mu, distrib[gin, gout, :])) + # Ensure the distribution CDF starts with 0 + scatt_angles[gin][-1].c = \ + np.cumsum(distrib[gin, gout, :]) - distrib[gin, gout, 0] + scatt_angles.append(scatt_angles[-1]) + + # Combine the energy and angle distributions in to a correlated + # angle/energy object + scatt_prod.distribution = \ + [openmc.data.CorrelatedAngleEnergy( + breakpoints=[len(scatt_eouts)], interpolation=[1], + energy=self.energy_groups.group_edges[:-1], + energy_out=scatt_eouts, mu=scatt_angles)] + + # Finally build the reaction with the just-calculated information + # This will be set to the (n,2n) reaction, though any scattering + # reaction would suffice + scatt_rxn = openmc.data.Reaction(16) + scatt_rxn.xs[strT] = \ + convert_xs(self.energy_groups.group_edges, scatt_xs[::-1]) + scatt_rxn.products = [scatt_prod] + scatt_rxn.center_of_mass = False + data.reactions[16] = scatt_rxn + + return data + def to_hdf5(self, file): """Write XSdata to an HDF5 file @@ -2512,6 +2714,29 @@ class MGXSLibrary(object): return library + def convert_to_continuous_energy(self, h5_filename='ce_mgxs.h5', + library_filename='ce_mgxs.xml'): + """Converts the MGXSLibrary object to an equivalent + library of openmc.data.IncidentNeutron objects + + Parameters + ---------- + h5_filename : str + HDF5 file to write with all the files + library_filename : str + cross_sections.xml file describing the HDF5 file + + """ + + library = openmc.data.DataLibrary() + data = [] + for i, xsdata in enumerate(self.xsdatas): + data.append(xsdata.convert_to_continuous_energy()) + data[-1].export_to_hdf5(h5_filename) + + library.register_file(h5_filename) + library.export_to_xml(library_filename) + def export_to_hdf5(self, filename='mgxs.h5'): """Create an hdf5 file that can be used for a simulation. From 2987ed88221663400dba872878b5178118021021 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 12 Feb 2017 13:18:52 -0500 Subject: [PATCH 05/16] Fix for floating point errors (rarely) causing crashes as no outgoing group is found --- openmc/mgxs_library.py | 6 ------ src/scattdata_header.F90 | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 6f46f48875..b2dee39489 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1849,12 +1849,6 @@ class XSdata(object): orig_data[..., l] for l in range(self.num_orders)) new_data[..., h_bin] = simps(table_fine, mu_fine) - # new_data[..., h_bin] = \ - # simps(np.sum((l + 0.5) * - # eval_legendre(l, mu_fine) * - # orig_data[..., l] - # for l in range(self.num_orders)), - # mu_fine) # Remove the very small results from numerical precision # issues (allowing conversions to be reproduced exactly) diff --git a/src/scattdata_header.F90 b/src/scattdata_header.F90 index 3d9df0bbf0..511e2a2376 100644 --- a/src/scattdata_header.F90 +++ b/src/scattdata_header.F90 @@ -522,7 +522,7 @@ contains gout = this % gmin(gin) prob = this % energy(gin) % data(gout) - do while (prob < xi) + do while ((prob < xi) .and. (gout < this % gmax(gin))) gout = gout + 1 prob = prob + this % energy(gin) % data(gout) end do @@ -568,7 +568,7 @@ contains gout = this % gmin(gin) prob = this % energy(gin) % data(gout) - do while (prob < xi) + do while ((prob < xi) .and. (gout < this % gmax(gin))) gout = gout + 1 prob = prob + this % energy(gin) % data(gout) end do @@ -605,7 +605,7 @@ contains gout = this % gmin(gin) prob = this % energy(gin) % data(gout) - do while (prob < xi) + do while ((prob < xi) .and. (gout < this % gmax(gin))) gout = gout + 1 prob = prob + this % energy(gin) % data(gout) end do From 8093f3654de5decaf4f90ec18a429a49f2bfcf64 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 12 Feb 2017 20:36:39 -0500 Subject: [PATCH 06/16] Tested scattering rigorously which resulted in some changes --- openmc/mgxs_library.py | 73 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index b2dee39489..14fb3dddc9 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1865,7 +1865,7 @@ class XSdata(object): eval_legendre(l, mu_fine) for l in range(xsdata.num_orders)] for l in range(xsdata.num_orders): - new_data[..., l] = (l + 0.5) * simps(y[l], mu_fine) + new_data[..., l] = simps(y[l], mu_fine) # Remove the very small results from numerical precision # issues (allowing conversions to be reproduced exactly) @@ -1893,38 +1893,30 @@ class XSdata(object): new_data[..., np.abs(new_data) < 1.E-10] = 0. elif self.scatter_format == 'histogram': # The histogram format does not have enough information to - # convert to the other forms without inducing an error. - # We will make the assumption that the left-edge of the bin - # has the value of the bin. The mu=1 value will be extrapolated - # from the previous two points. - mu_self = np.linspace(-1, 1, self.num_orders + 1) - tab_data = np.zeros(orig_data.shape[:-1] + - (orig_data.shape[-1] + 1,)) - tab_data[..., :-1] = orig_data - tab_data[..., -1] = (orig_data[..., -2] - - orig_data[..., -3]) + orig_data[..., -2] - # Now get the distribution normalization factor. - # This factor will be such that its average value is the - # group -> group cross section (which is the sum of the - # original data) - norm = np.sum(orig_data, axis=-1) / np.mean(tab_data, axis=-1) - norm = np.nan_to_num(norm) - for i in range(tab_data.shape[-1]): - tab_data[..., i] *= norm[...] + # convert to the other forms without inducing some amount of + # error. We will make the assumption that the center of the bin + # has the value of the bin. The mu=-1 and 1 points will be + # extrapolated from the shape. + mu_midpoint = np.linspace(-1, 1, self.num_orders, + endpoint=False) + mu_midpoint += (mu_midpoint[1] - mu_midpoint[0]) * 0.5 + interp = interp1d(mu_midpoint, orig_data, + fill_value='extrapolate') + # Now get the distribution normalization factor to take from + # an integral quantity to a point-wise quantity + norm = float(self.num_orders) / 2.0 - # We now have a tabular distribution in tab_data on mu_self - # with a normalization in norm. We now proceed just like the - # tabular branch above. + # We now have a tabular distribution in tab_data on mu_self. + # We now proceed just like the tabular branch above. if target_format == 'legendre': # find the legendre coefficients via integration. To best # use the vectorized integration capabilities of scipy, # this will be done with fixed sample integration routines. mu_fine = np.linspace(-1, 1, _NMU) - y = [interp1d(mu_self, tab_data)(mu_fine) * - eval_legendre(l, mu_fine) + y = [interp(mu_fine) * norm * eval_legendre(l, mu_fine) for l in range(xsdata.num_orders)] for l in range(xsdata.num_orders): - new_data[..., l] = (l + 0.5) * simps(y[l], mu_fine) + new_data[..., l] = simps(y[l], mu_fine) # Remove the very small results from numerical precision # issues (allowing conversions to be reproduced exactly) @@ -1932,7 +1924,7 @@ class XSdata(object): elif target_format == 'tabular': # Simply use an interpolating function to get the new data mu = np.linspace(-1, 1, xsdata.num_orders) - new_data[..., :] = interp1d(mu_self, tab_data)(mu) + new_data[..., :] = interp(mu) * norm elif target_format == 'histogram': # Use an interpolating function to do the bin-wise # integrals @@ -1942,15 +1934,14 @@ class XSdata(object): # be written to utilize the vectorized integration # capabilities instead of having an isotropic and # angle representation path. - interp = interp1d(mu_self, tab_data) for h_bin in range(xsdata.num_orders): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) - new_data[..., h_bin] = simps(interp(mu_fine), mu_fine) + new_data[..., h_bin] = \ + norm * simps(interp(mu_fine), mu_fine) # Remove the very small results from numerical precision # issues (allowing conversions to be reproduced exactly) new_data[..., np.abs(new_data) < 1.E-10] = 0. - xsdata.set_scatter_matrix(new_data, temp) return xsdata @@ -2107,10 +2098,18 @@ class XSdata(object): # lines within each of the outgoing groups scatt_eouts = [] for g in range(self.energy_groups.num_groups): + nz = np.nonzero(energy[g, :]) + lo = nz[0][0] + hi = nz[0][-1] + 1 + # scatt_eouts.append( + # openmc.stats.Discrete(energy_midpoints[lo: hi], + # energy[g, lo:hi])) scatt_eouts.append( - openmc.stats.Discrete(energy_midpoints, energy[g, :])) + openmc.stats.Tabular(self.energy_groups.group_edges[:-1], + energy[g, :], + interpolation='histogram')) # Ensure the distribution CDF starts with 0 - scatt_eouts[-1].c = np.cumsum(energy[g, :]) - energy[g, 0] + scatt_eouts[-1].c = np.cumsum(energy[g, lo:hi]) - energy[g, lo] scatt_eouts.append(scatt_eouts[-1]) # Build the angular distributions associated with each outgoing energy @@ -2120,11 +2119,13 @@ class XSdata(object): for gin in range(self.energy_groups.num_groups): scatt_angles.append([]) for gout in range(self.energy_groups.num_groups): - scatt_angles[gin].append( - openmc.stats.Tabular(mu, distrib[gin, gout, :])) - # Ensure the distribution CDF starts with 0 - scatt_angles[gin][-1].c = \ - np.cumsum(distrib[gin, gout, :]) - distrib[gin, gout, 0] + if energy[gin, gout] > 0.: + scatt_angles[gin].append( + openmc.stats.Tabular(mu, distrib[gin, gout, :])) + # Ensure the distribution CDF starts with 0 + scatt_angles[gin][-1].c = \ + np.cumsum(distrib[gin, gout, :]) - \ + distrib[gin, gout, 0] scatt_angles.append(scatt_angles[-1]) # Combine the energy and angle distributions in to a correlated From 13bbabd8cfa388593ea51ee2ab256480b66b8d22 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Mon, 13 Feb 2017 19:51:25 -0500 Subject: [PATCH 07/16] old code cleanup --- openmc/mgxs_library.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 14fb3dddc9..b4482d60b9 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -2098,18 +2098,12 @@ class XSdata(object): # lines within each of the outgoing groups scatt_eouts = [] for g in range(self.energy_groups.num_groups): - nz = np.nonzero(energy[g, :]) - lo = nz[0][0] - hi = nz[0][-1] + 1 - # scatt_eouts.append( - # openmc.stats.Discrete(energy_midpoints[lo: hi], - # energy[g, lo:hi])) scatt_eouts.append( openmc.stats.Tabular(self.energy_groups.group_edges[:-1], energy[g, :], interpolation='histogram')) # Ensure the distribution CDF starts with 0 - scatt_eouts[-1].c = np.cumsum(energy[g, lo:hi]) - energy[g, lo] + scatt_eouts[-1].c = np.cumsum(energy[g, :]) - energy[g, 0] scatt_eouts.append(scatt_eouts[-1]) # Build the angular distributions associated with each outgoing energy From 130db90c2a74f5a24e4248ef2d0bed8f7b81e4fa Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Wed, 15 Feb 2017 20:27:31 -0500 Subject: [PATCH 08/16] cleaned up comments and removed the conversion to CE --- openmc/mgxs_library.py | 268 ++++++----------------------------------- 1 file changed, 36 insertions(+), 232 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index b4482d60b9..e3a8ad4d85 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1721,22 +1721,19 @@ class XSdata(object): check_greater_than('num_azimuthal', num_azimuthal, 0) xsdata = copy.deepcopy(self) + + # First handle the case where the current and requested + # representations are the same if target_representation == self.representation: # Check to make sure the num_polar and num_azimuthal values match if target_representation == 'angle': if num_polar != self.num_polar or num_azimuthal != self.num_azimuthal: - raise NotImplementedError("XSdata.convert_representation " - "cannot translate between " + raise NotImplementedError("XCannot translate between " "`angle` representations with " "different angle bin structures") - # Nothing to do + # Nothing to do as the same structure was requested return xsdata - types = ['total', 'absorption', 'fission', 'nu_fission', - 'scatter_matrix', 'multiplicity_matrix', 'prompt_nu_fission', - 'delayed_nu_fission', 'kappa_fission', 'chi', 'chi_prompt', - 'chi_delayed', 'beta', 'decay_rate', 'inverse_velocity'] - xsdata.representation = target_representation # We have different actions depending on the representation conversion if target_representation == 'isotropic': @@ -1748,17 +1745,21 @@ class XSdata(object): xsdata.num_polar = num_polar xsdata.num_azimuthal = num_azimuthal - # Reset XSdata.xs_shapes to accomodate the new shape + # Reset xs_shapes so it is recalculated the next time it is needed xsdata._xs_shapes = None for i, temp in enumerate(xsdata.temperatures): - for xs in types: + for xs in ['total', 'absorption', 'fission', 'nu_fission', + 'scatter_matrix', 'multiplicity_matrix', + 'prompt_nu_fission', 'delayed_nu_fission', + 'kappa_fission', 'chi', 'chi_prompt', 'chi_delayed', + 'beta', 'decay_rate', 'inverse_velocity']: # Get the original data orig_data = getattr(self, '_' + xs)[i] if orig_data is not None: if target_representation == 'isotropic': # Since we are going from angle to isotropic, the - # current data is just averaged over the angle bins + # current data is just the average over the angle bins new_data = orig_data.mean(axis=(0, 1)) elif target_representation == 'angle': # Since we are going from isotropic to angle, the @@ -1787,7 +1788,7 @@ class XSdata(object): Returns ------- openmc.XSdata - Multi-group cross section data with the same data as self, but + Multi-group cross section data with the same data as in self, but represented as specified in :param:`target_format`. """ @@ -1806,15 +1807,11 @@ class XSdata(object): xsdata = copy.deepcopy(self) xsdata.scatter_format = target_format xsdata.order = target_order - # Reset XSdata.xs_shapes to accomodate the new shape + + # Reset and re-generate XSdata.xs_shapes with the new scattering format xsdata._xs_shapes = None xsdata.xs_shapes - # We have to accomodate the following possibilities: - # histogram -> tabular w/ same or diff order - # histogram -> legendre - # histogram -> histogram w/ same or diff order - for i, temp in enumerate(xsdata.temperatures): orig_data = self._scatter_matrix[i] new_shape = orig_data.shape[:-1] + (xsdata.num_orders,) @@ -1827,21 +1824,23 @@ class XSdata(object): new_data[..., :order] = orig_data[..., :order] elif target_format == 'tabular': mu = np.linspace(-1, 1, xsdata.num_orders) - # Evaluate the legendre on the tabular grid within mu + # Evaluate the legendre on the mu grid for imu in range(len(mu)): new_data[..., imu] = \ np.sum((l + 0.5) * eval_legendre(l, mu[imu]) * orig_data[..., l] for l in range(self.num_orders)) elif target_format == 'histogram': + # This code uses the vectorized integration capabilities + # instead of having an isotropic and angle representation + # path. + # Set the histogram mu grid mu = np.linspace(-1, 1, xsdata.num_orders + 1) - # This code will be written to utilize the vectorized - # integration capabilities instead of having an isotropic - # and angle representation path. + # For every bin perform simpson integration of a finely + # sampled orig_data for h_bin in range(xsdata.num_orders): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) - table_shape = new_data.shape[:-1] + (_NMU,) - table_fine = np.zeros(table_shape) + table_fine = np.zeros(new_data.shape[:-1] + (_NMU,)) for imu in range(len(mu_fine)): table_fine[..., imu] = \ np.sum((l + 0.5) * @@ -1850,16 +1849,17 @@ class XSdata(object): for l in range(self.num_orders)) new_data[..., h_bin] = simps(table_fine, mu_fine) - # Remove the very small results from numerical precision - # issues (allowing conversions to be reproduced exactly) + # Remove the very small values resulting from numerical + # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. elif self.scatter_format == 'tabular': + # Calculate the mu points of the current data mu_self = np.linspace(-1, 1, self.num_orders) if target_format == 'legendre': - # find the legendre coefficients via integration. To best + # Find the Legendre coefficients via integration. To best # use the vectorized integration capabilities of scipy, - # this will be done with fixed sample integration routines. + # this is done with fixed sample integration routines. mu_fine = np.linspace(-1, 1, _NMU) y = [interp1d(mu_self, orig_data)(mu_fine) * eval_legendre(l, mu_fine) @@ -1867,8 +1867,8 @@ class XSdata(object): for l in range(xsdata.num_orders): new_data[..., l] = simps(y[l], mu_fine) - # Remove the very small results from numerical precision - # issues (allowing conversions to be reproduced exactly) + # Remove the very small values resulting from numerical + # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. elif target_format == 'tabular': # Simply use an interpolating function to get the new data @@ -1888,8 +1888,8 @@ class XSdata(object): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) new_data[..., h_bin] = simps(interp(mu_fine), mu_fine) - # Remove the very small results from numerical precision - # issues (allowing conversions to be reproduced exactly) + # Remove the very small values resulting from numerical + # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. elif self.scatter_format == 'histogram': # The histogram format does not have enough information to @@ -1918,8 +1918,8 @@ class XSdata(object): for l in range(xsdata.num_orders): new_data[..., l] = simps(y[l], mu_fine) - # Remove the very small results from numerical precision - # issues (allowing conversions to be reproduced exactly) + # Remove the very small values resulting from numerical + # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. elif target_format == 'tabular': # Simply use an interpolating function to get the new data @@ -1939,209 +1939,13 @@ class XSdata(object): new_data[..., h_bin] = \ norm * simps(interp(mu_fine), mu_fine) - # Remove the very small results from numerical precision - # issues (allowing conversions to be reproduced exactly) + # Remove the very small values resulting from numerical + # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. xsdata.set_scatter_matrix(new_data, temp) return xsdata - def convert_to_continuous_energy(self, temperature=294.): - """Converts the XSdata object to an equivalent - openmc.data.IncidentNeutron object. - - Parameters - ---------- - temperature : float - Temperature of dataset to print; defaults to 294K - - Returns - ------- - openmc.data.IncidentNeutron - The continuous-energy IncidentNeutron data library equivalent - to the MGXS data in self - """ - - # Check if this can be performed successfully - if self.representation == 'angle': - raise ValueError("Cannot convert angle-dependent MGXS; convert to " - "an isotropic representation first") - required_types = ['absorption', 'scatter_matrix'] - for type_check in required_types: - if getattr(self, '_' + type_check)[0] is None: - raise ValueError(type_check + ' data is required') - if temperature not in self.temperatures: - raise ValueError("Invalid temperature") - - def convert_xs(group_edges, values): - cexs = openmc.data.Tabulated1D(group_edges, - np.append(values[::-1], - [values[0]]), - breakpoints=[len(group_edges)], - interpolation=[1]) - return cexs - - # Build required metadata - kTs = self.temperatures[:] * openmc.data.K_BOLTZMANN - if self.atomic_weight_ratio: - awr = self.atomic_weight_ratio - else: - awr = 1. - - # Get the temperature index - iT = np.where(self.temperatures == temperature)[0][0] - data = openmc.data.IncidentNeutron(self.name, 1, 1, 0, awr, kTs) - strT = "{}K".format(int(round(self.temperatures[iT]))) - data.energy = {strT: self.energy_groups.group_edges} - energy_midpoints = (self.energy_groups.group_edges[1:] + - self.energy_groups.group_edges[:-1]) / 2. - - # Elastic MGXS: must explicitly be 0 barns to avoid incorrect - # elastic_scatter calculation with data available to us in MG library. - el_rxn = openmc.data.Reaction(2) - el_rxn.xs[strT] = \ - convert_xs(self.energy_groups.group_edges, - np.zeros(self.energy_groups.num_groups)) - data.reactions[2] = el_rxn - - # Absorption MGXS - abs_rxn = openmc.data.Reaction(102) - abs_rxn.xs[strT] = convert_xs(self.energy_groups.group_edges, - np.subtract(self._absorption[iT], - self._fission[iT])) - data.reactions[102] = abs_rxn - - if self._nu_fission[iT] is not None and self._fission[iT] is not None: - fiss_rxn = openmc.data.Reaction(18) - fiss_rxn.xs[strT] = convert_xs(self.energy_groups.group_edges, - self._fission[iT]) - - # Get nu_fission and chi from the presence of both, or the - # nu_fission matrix - if self._nu_fission[iT].shape == self.xs_shapes["[G][G']"]: - nu_fiss = np.sum(self._nu_fission[iT], axis=1)[::-1] - chi = self._nu_fission[iT][::-1] / nu_fiss - else: - nu_fiss = self._nu_fission[iT][::-1] - chi = np.reshape(np.tile(self._chi[iT][::-1], - self.energy_groups.num_groups), - (self.energy_groups.num_groups, - self.energy_groups.num_groups)) - nu = np.divide(nu_fiss, self._fission[iT][::-1]) - - # Build a histogram distribution to represent the yield for the - # incoming groups - prod = openmc.data.Product() - prod.yield_ = \ - openmc.data.Tabulated1D(self.energy_groups.group_edges[:-1], - nu, breakpoints=[len(nu)], - interpolation=[1]) - - # Now build the outgoing energy distribution using discrete energy - # lines within each of the outgoing groups - chi_eouts = [] - for g in range(self.energy_groups.num_groups): - chi_eouts.append(openmc.stats.Discrete(energy_midpoints, - chi[g, :])) - # Ensure the distribution CDF starts with 0 - chi_eouts[-1].c = np.cumsum(chi[g, :]) - chi[g, 0] - - # Create the continuous distribution for the fission - # The angular distribution will remain None (thus isotropic) - chi_distrib = openmc.data.ContinuousTabular( - [len(chi)], [1], self.energy_groups.group_edges[:-1], - chi_eouts) - prod.distribution = \ - [openmc.data.UncorrelatedAngleEnergy(energy=chi_distrib)] - fiss_rxn.products = [prod] - fiss_rxn.center_of_mass = False - data.reactions[18] = fiss_rxn - - # Scattering Data - # First convert the data to a tabular representation - if self.scatter_format == 'tabular': - tabular = self - else: - tabular = self.convert_scatter_format('tabular', 33) - - # Calculate the isotropic scattering matrix and use that to find the - # total scattering x/s and outgoing energy distributions - isotropic_matrix = np.mean(tabular._scatter_matrix[iT], axis=-1)[::-1, - ::-1] - scatt_xs = np.sum(isotropic_matrix, axis=1) - energy = np.zeros((self.energy_groups.num_groups, - self.energy_groups.num_groups)) - for gin in range(self.energy_groups.num_groups): - energy[gin, :] = isotropic_matrix[gin, :] / scatt_xs[gin] - - # Get the anisotropic but normalized angular distribution - distrib = np.zeros((self.energy_groups.num_groups, - self.energy_groups.num_groups, - tabular.num_orders)) - for gin in range(self.energy_groups.num_groups): - for gout in range(self.energy_groups.num_groups): - distrib[gin, gout, :] = \ - np.divide(tabular._scatter_matrix[iT][gin, gout, :], - isotropic_matrix[gin, gout]) - distrib = np.nan_to_num(distrib) - - # Incorporate the scattering multiplication, if required - scatt_prod = openmc.data.Product() - if self._multiplicity_matrix[iT]: - yield_ = self._multiplicity_matrix[iT][::-1, ::-1] - scatt_prod.yield_ = \ - openmc.data.Tabulated1D(self.energy_groups.group_edges[:-1], - yield_, breakpoints=[len(yield_)], - interpolation=[1]) - - # Now build the outgoing energy distribution using discrete energy - # lines within each of the outgoing groups - scatt_eouts = [] - for g in range(self.energy_groups.num_groups): - scatt_eouts.append( - openmc.stats.Tabular(self.energy_groups.group_edges[:-1], - energy[g, :], - interpolation='histogram')) - # Ensure the distribution CDF starts with 0 - scatt_eouts[-1].c = np.cumsum(energy[g, :]) - energy[g, 0] - scatt_eouts.append(scatt_eouts[-1]) - - # Build the angular distributions associated with each outgoing energy - # group - mu = np.linspace(-1., 1., tabular.num_orders) - scatt_angles = [] - for gin in range(self.energy_groups.num_groups): - scatt_angles.append([]) - for gout in range(self.energy_groups.num_groups): - if energy[gin, gout] > 0.: - scatt_angles[gin].append( - openmc.stats.Tabular(mu, distrib[gin, gout, :])) - # Ensure the distribution CDF starts with 0 - scatt_angles[gin][-1].c = \ - np.cumsum(distrib[gin, gout, :]) - \ - distrib[gin, gout, 0] - scatt_angles.append(scatt_angles[-1]) - - # Combine the energy and angle distributions in to a correlated - # angle/energy object - scatt_prod.distribution = \ - [openmc.data.CorrelatedAngleEnergy( - breakpoints=[len(scatt_eouts)], interpolation=[1], - energy=self.energy_groups.group_edges[:-1], - energy_out=scatt_eouts, mu=scatt_angles)] - - # Finally build the reaction with the just-calculated information - # This will be set to the (n,2n) reaction, though any scattering - # reaction would suffice - scatt_rxn = openmc.data.Reaction(16) - scatt_rxn.xs[strT] = \ - convert_xs(self.energy_groups.group_edges, scatt_xs[::-1]) - scatt_rxn.products = [scatt_prod] - scatt_rxn.center_of_mass = False - data.reactions[16] = scatt_rxn - - return data - def to_hdf5(self, file): """Write XSdata to an HDF5 file From 384a08956af7a92ae3fc70292b27aac698f091ca Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Thu, 16 Feb 2017 21:02:34 -0500 Subject: [PATCH 09/16] Added test of convert_* methods; --- openmc/mgxs_library.py | 92 +++++----- tests/test_mg_convert/inputs_true.dat | 30 ++++ tests/test_mg_convert/results_true.dat | 28 +++ tests/test_mg_convert/test_mg_convert.py | 207 +++++++++++++++++++++++ 4 files changed, 303 insertions(+), 54 deletions(-) create mode 100644 tests/test_mg_convert/inputs_true.dat create mode 100644 tests/test_mg_convert/results_true.dat create mode 100755 tests/test_mg_convert/test_mg_convert.py diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index e3a8ad4d85..d441e1bbbf 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -14,11 +14,14 @@ from openmc.checkvalue import check_type, check_value, check_greater_than, \ # Supported incoming particle MGXS angular treatment representations _REPRESENTATIONS = ['isotropic', 'angle'] + # Supported scattering angular distribution representations _SCATTER_TYPES = ['tabular', 'legendre', 'histogram'] -# List of MGXS dimension types + +# List of MGXS indexing schemes _XS_SHAPES = ["[G][G'][Order]", "[G]", "[G']", "[G][G']", "[DG]", "[DG][G]", "[DG][G']", "[DG][G][G']"] + # Number of mu points for conversion between scattering formats _NMU = 257 @@ -368,15 +371,14 @@ class XSdata(object): @name.setter def name(self, name): + check_type('name for XSdata', name, string_types) self._name = name @energy_groups.setter def energy_groups(self, energy_groups): - # Check validity of energy_groups check_type('energy_groups', energy_groups, openmc.mgxs.EnergyGroups) - if energy_groups.group_edges is None: msg = 'Unable to assign an EnergyGroups object ' \ 'with uninitialized group edges' @@ -387,7 +389,6 @@ class XSdata(object): @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): - # Check validity of num_delayed_groups check_type('num_delayed_groups', num_delayed_groups, Integral) check_less_than('num_delayed_groups', num_delayed_groups, openmc.mgxs.MAX_DELAYED_GROUPS, equality=True) @@ -398,14 +399,12 @@ class XSdata(object): @representation.setter def representation(self, representation): - # Check it is of valid value. check_value('representation', representation, _REPRESENTATIONS) self._representation = representation @atomic_weight_ratio.setter def atomic_weight_ratio(self, atomic_weight_ratio): - # Check validity of type and that the atomic_weight_ratio value is > 0 check_type('atomic_weight_ratio', atomic_weight_ratio, Real) check_greater_than('atomic_weight_ratio', atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio @@ -419,14 +418,12 @@ class XSdata(object): @scatter_format.setter def scatter_format(self, scatter_format): - # check to see it is of a valid type and value check_value('scatter_format', scatter_format, _SCATTER_TYPES) self._scatter_format = scatter_format @order.setter def order(self, order): - # Check type and value check_type('order', order, Integral) check_greater_than('order', order, 0, equality=True) self._order = order @@ -434,7 +431,6 @@ class XSdata(object): @num_polar.setter def num_polar(self, num_polar): - # Make sure we have positive ints check_type('num_polar', num_polar, Integral) check_greater_than('num_polar', num_polar, 0) self._num_polar = num_polar @@ -1688,7 +1684,14 @@ class XSdata(object): def convert_representation(self, target_representation, num_polar=None, num_azimuthal=None): """Produce a new XSdata object with the same data, but converted to the - new representation + new representation (isotropic or angle-dependent). + + This method cannot be used to change the number of polar or + azimuthal bins of an XSdata object that already uses an angular + representation. Finally, this method simply uses an arithmetic mean to + convert from an angular to isotropic representation; no flux-weighting + is applied and therefore the correctness of the solution is not + guaranteed. Parameters ---------- @@ -1728,9 +1731,9 @@ class XSdata(object): # Check to make sure the num_polar and num_azimuthal values match if target_representation == 'angle': if num_polar != self.num_polar or num_azimuthal != self.num_azimuthal: - raise NotImplementedError("XCannot translate between " - "`angle` representations with " - "different angle bin structures") + raise ValueError("Cannot translate between `angle`" + " representations with different angle" + " bin structures") # Nothing to do as the same structure was requested return xsdata @@ -1741,6 +1744,7 @@ class XSdata(object): # values are changed back to None for clarity xsdata._num_polar = None xsdata._num_azimuthal = None + elif target_representation == 'angle': xsdata.num_polar = num_polar xsdata.num_azimuthal = num_azimuthal @@ -1757,16 +1761,19 @@ class XSdata(object): # Get the original data orig_data = getattr(self, '_' + xs)[i] if orig_data is not None: + if target_representation == 'isotropic': # Since we are going from angle to isotropic, the # current data is just the average over the angle bins new_data = orig_data.mean(axis=(0, 1)) + elif target_representation == 'angle': # Since we are going from isotropic to angle, the # current data is just copied for every angle bin new_shape = (num_polar, num_azimuthal) + \ orig_data.shape new_data = np.resize(orig_data, new_shape) + setter = getattr(xsdata, 'set_' + xs) setter(new_data, temp) @@ -1810,18 +1817,19 @@ class XSdata(object): # Reset and re-generate XSdata.xs_shapes with the new scattering format xsdata._xs_shapes = None - xsdata.xs_shapes for i, temp in enumerate(xsdata.temperatures): orig_data = self._scatter_matrix[i] new_shape = orig_data.shape[:-1] + (xsdata.num_orders,) new_data = np.zeros(new_shape) + if self.scatter_format == 'legendre': if target_format == 'legendre': # Then we are changing orders and only need to change # dimensionality of the mu data and pad/truncate as needed order = min(xsdata.num_orders, self.num_orders) new_data[..., :order] = orig_data[..., :order] + elif target_format == 'tabular': mu = np.linspace(-1, 1, xsdata.num_orders) # Evaluate the legendre on the mu grid @@ -1856,6 +1864,7 @@ class XSdata(object): elif self.scatter_format == 'tabular': # Calculate the mu points of the current data mu_self = np.linspace(-1, 1, self.num_orders) + if target_format == 'legendre': # Find the Legendre coefficients via integration. To best # use the vectorized integration capabilities of scipy, @@ -1870,10 +1879,12 @@ class XSdata(object): # Remove the very small values resulting from numerical # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. + elif target_format == 'tabular': # Simply use an interpolating function to get the new data mu = np.linspace(-1, 1, xsdata.num_orders) new_data[..., :] = interp1d(mu_self, orig_data)(mu) + elif target_format == 'histogram': # Use an interpolating function to do the bin-wise # integrals @@ -1891,6 +1902,7 @@ class XSdata(object): # Remove the very small values resulting from numerical # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. + elif self.scatter_format == 'histogram': # The histogram format does not have enough information to # convert to the other forms without inducing some amount of @@ -1921,10 +1933,12 @@ class XSdata(object): # Remove the very small values resulting from numerical # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. + elif target_format == 'tabular': # Simply use an interpolating function to get the new data mu = np.linspace(-1, 1, xsdata.num_orders) new_data[..., :] = interp(mu) * norm + elif target_format == 'histogram': # Use an interpolating function to do the bin-wise # integrals @@ -1942,6 +1956,7 @@ class XSdata(object): # Remove the very small values resulting from numerical # precision issues new_data[..., np.abs(new_data) < 1.E-10] = 0. + xsdata.set_scatter_matrix(new_data, temp) return xsdata @@ -2431,8 +2446,15 @@ class MGXSLibrary(object): def convert_representation(self, target_representation, num_polar=None, num_azimuthal=None): - """Produce a new MGXSLibrary object with the same data, but converted - to the new representation + """Produce a new XSdata object with the same data, but converted to the + new representation (isotropic or angle-dependent). + + This method cannot be used to change the number of polar or + azimuthal bins of an XSdata object that already uses an angular + representation. Finally, this method simply uses an arithmetic mean to + convert from an angular to isotropic representation; no flux-weighting + is applied and therefore the correctness of the solution is not + guaranteed. Parameters ---------- @@ -2456,14 +2478,6 @@ class MGXSLibrary(object): """ - check_value('target_representation', target_representation, - _REPRESENTATIONS) - if target_representation == 'angle': - check_type('num_polar', num_polar, Integral) - check_type('num_azimuthal', num_azimuthal, Integral) - check_greater_than('num_polar', num_polar, 0) - check_greater_than('num_azimuthal', num_azimuthal, 0) - library = copy.deepcopy(self) for i, xsdata in enumerate(self.xsdatas): library.xsdatas[i] = \ @@ -2493,13 +2507,6 @@ class MGXSLibrary(object): """ - check_value('target_format', target_format, _SCATTER_TYPES) - check_type('target_order', target_order, Integral) - if target_format == 'legendre': - check_greater_than('target_order', target_order, 0, equality=True) - else: - check_greater_than('target_order', target_order, 0) - library = copy.deepcopy(self) for i, xsdata in enumerate(self.xsdatas): library.xsdatas[i] = \ @@ -2507,29 +2514,6 @@ class MGXSLibrary(object): return library - def convert_to_continuous_energy(self, h5_filename='ce_mgxs.h5', - library_filename='ce_mgxs.xml'): - """Converts the MGXSLibrary object to an equivalent - library of openmc.data.IncidentNeutron objects - - Parameters - ---------- - h5_filename : str - HDF5 file to write with all the files - library_filename : str - cross_sections.xml file describing the HDF5 file - - """ - - library = openmc.data.DataLibrary() - data = [] - for i, xsdata in enumerate(self.xsdatas): - data.append(xsdata.convert_to_continuous_energy()) - data[-1].export_to_hdf5(h5_filename) - - library.register_file(h5_filename) - library.export_to_xml(library_filename) - def export_to_hdf5(self, filename='mgxs.h5'): """Create an hdf5 file that can be used for a simulation. diff --git a/tests/test_mg_convert/inputs_true.dat b/tests/test_mg_convert/inputs_true.dat new file mode 100644 index 0000000000..9b0b0f4b17 --- /dev/null +++ b/tests/test_mg_convert/inputs_true.dat @@ -0,0 +1,30 @@ + + + + + + + + + + + ./mgxs.h5 + + + + + + + + + 1000 + 2000 + 100 + + + + -5 -5 -5 5 5 5 + + + multi-group + diff --git a/tests/test_mg_convert/results_true.dat b/tests/test_mg_convert/results_true.dat new file mode 100644 index 0000000000..5cf0eb6d26 --- /dev/null +++ b/tests/test_mg_convert/results_true.dat @@ -0,0 +1,28 @@ +k-combined: +9.957263E-01 5.469041E-05 +k-combined: +9.957263E-01 5.469041E-05 +k-combined: +9.963652E-01 5.444550E-05 +k-combined: +9.957263E-01 5.469041E-05 +k-combined: +9.955157E-01 5.785520E-05 +k-combined: +9.954023E-01 5.694553E-05 +k-combined: +9.955722E-01 6.075634E-05 +k-combined: +9.955692E-01 6.094480E-05 +k-combined: +9.955154E-01 5.775805E-05 +k-combined: +9.956787E-01 5.915112E-05 +k-combined: +9.957022E-01 5.952369E-05 +k-combined: +9.953517E-01 5.920431E-05 +k-combined: +9.957263E-01 5.469041E-05 +k-combined: +9.957263E-01 5.469041E-05 diff --git a/tests/test_mg_convert/test_mg_convert.py b/tests/test_mg_convert/test_mg_convert.py new file mode 100755 index 0000000000..c5c0ac16c7 --- /dev/null +++ b/tests/test_mg_convert/test_mg_convert.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python + +import os +import sys +import hashlib +sys.path.insert(0, os.pardir) + +import numpy as np + +from testing_harness import PyAPITestHarness +import openmc + +# OpenMC simulation parameters +batches = 2000 +inactive = 100 +particles = 1000 + + +def build_mgxs_library(convert): + # Instantiate the energy group data + groups = openmc.mgxs.EnergyGroups(group_edges=[1e-5, 0.625, 20.0e6]) + + # Instantiate the 7-group (C5G7) cross section data + uo2_xsdata = openmc.XSdata('UO2', groups) + uo2_xsdata.order = 2 + uo2_xsdata.set_total([2., 2.]) + uo2_xsdata.set_absorption([1., 1.]) + scatter_matrix = np.array([[[0.75, 0.25], + [0.00, 1.00]], + [[0.75 / 3., 0.25 / 3.], + [0.00 / 3., 1.00 / 3.]], + [[0.75 / 4., 0.25 / 4.], + [0.00 / 4., 1.00 / 4.]]]) + scatter_matrix = np.rollaxis(scatter_matrix, 0, 3) + uo2_xsdata.set_scatter_matrix(scatter_matrix) + uo2_xsdata.set_fission([0.5, 0.5]) + uo2_xsdata.set_nu_fission([1., 1.]) + uo2_xsdata.set_chi([1., 0.]) + + mg_cross_sections_file = openmc.MGXSLibrary(groups) + mg_cross_sections_file.add_xsdatas([uo2_xsdata]) + + if convert is not None: + if isinstance(convert[0], list): + for conv in convert: + if conv[0] in ['legendre', 'tabular', 'histogram']: + mg_cross_sections_file = \ + mg_cross_sections_file.convert_scatter_format( + conv[0], conv[1]) + elif conv[0] in ['angle', 'isotropic']: + mg_cross_sections_file = \ + mg_cross_sections_file.convert_representation( + conv[0], conv[1], conv[1]) + elif convert[0] in ['legendre', 'tabular', 'histogram']: + mg_cross_sections_file = \ + mg_cross_sections_file.convert_scatter_format( + convert[0], convert[1]) + elif convert[0] in ['angle', 'isotropic']: + mg_cross_sections_file = \ + mg_cross_sections_file.convert_representation( + convert[0], convert[1], convert[1]) + + mg_cross_sections_file.export_to_hdf5() + + +class MGXSTestHarness(PyAPITestHarness): + def _build_inputs(self): + # Instantiate some Macroscopic Data + uo2_data = openmc.Macroscopic('UO2') + + # Instantiate some Materials and register the appropriate objects + mat = openmc.Material(material_id=1, name='UO2 fuel') + mat.set_density('macro', 1.0) + mat.add_macroscopic(uo2_data) + + # Instantiate a Materials collection and export to XML + materials_file = openmc.Materials([mat]) + materials_file.cross_sections = "./mgxs.h5" + materials_file.export_to_xml() + + # Instantiate ZCylinder surfaces + left = openmc.XPlane(surface_id=4, x0=-5., name='left') + right = openmc.XPlane(surface_id=5, x0=5., name='right') + bottom = openmc.YPlane(surface_id=6, y0=-5., name='bottom') + top = openmc.YPlane(surface_id=7, y0=5., name='top') + + left.boundary_type = 'reflective' + right.boundary_type = 'vacuum' + top.boundary_type = 'reflective' + bottom.boundary_type = 'reflective' + + # Instantiate Cells + fuel = openmc.Cell(cell_id=1, name='cell 1') + + # Use surface half-spaces to define regions + fuel.region = +left & -right & +bottom & -top + + # Register Materials with Cells + fuel.fill = mat + + # Instantiate Universe + root = openmc.Universe(universe_id=0, name='root universe') + + # Register Cells with Universe + root.add_cells([fuel]) + + # Instantiate a Geometry, register the root Universe, and export to XML + geometry = openmc.Geometry(root) + geometry.export_to_xml() + + settings_file = openmc.Settings() + settings_file.energy_mode = "multi-group" + settings_file.batches = batches + settings_file.inactive = inactive + settings_file.particles = particles + + # Create an initial uniform spatial source distribution + bounds = [-5, -5, -5, 5, 5, 5] + uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:]) + settings_file.source = openmc.source.Source(space=uniform_dist) + + settings_file.export_to_xml() + + def _run_openmc(self): + # Run multiple conversions to compare results + cases = [None, ['legendre', 2], ['legendre', 0], + ['tabular', 33], ['histogram', 32], + [['tabular', 33], ['legendre', 1]], + [['tabular', 33], ['tabular', 3]], + [['tabular', 33], ['histogram', 32]], + [['histogram', 32], ['legendre', 1]], + [['histogram', 32], ['tabular', 3]], + [['histogram', 32], ['histogram', 16]], + [['histogram', 32], ['histogram', 128]], + ['angle', 2], [['angle', 2], ['isotropic', None]]] + + outstr = '' + for case in cases: + build_mgxs_library(case) + + if self._opts.mpi_exec is not None: + returncode = openmc.run(mpi_procs=self._opts.mpi_np, + openmc_exec=self._opts.exe, + mpi_exec=self._opts.mpi_exec) + + else: + returncode = openmc.run(openmc_exec=self._opts.exe) + + assert returncode == 0, 'OpenMC did not exit successfully.' + + sp = openmc.StatePoint('statepoint.' + str(batches) + '.h5') + + # Write out k-combined. + outstr += 'k-combined:\n' + form = '{0:12.6E} {1:12.6E}\n' + outstr += form.format(sp.k_combined[0], sp.k_combined[1]) + sp.close() + + return outstr + + def _get_results(self, outstr, hash_output=False): + # Hash the results if necessary. + if hash_output: + sha512 = hashlib.sha512() + sha512.update(outstr.encode('utf-8')) + outstr = sha512.hexdigest() + + return outstr + + def _cleanup(self): + super(MGXSTestHarness, self)._cleanup() + f = os.path.join(os.getcwd(), 'mgxs.h5') + if os.path.exists(f): + os.remove(f) + + def execute_test(self): + """Build input XMLs, run OpenMC, and verify correct results.""" + try: + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._compare_inputs() + outstr = self._run_openmc() + results = self._get_results(outstr) + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + + def update_results(self): + """Update results_true.dat and inputs_true.dat""" + try: + self._build_inputs() + inputs = self._get_inputs() + self._write_inputs(inputs) + self._overwrite_inputs() + outstr = self._run_openmc() + results = self._get_results(outstr) + self._write_results(results) + self._overwrite_results() + finally: + self._cleanup() + + +if __name__ == '__main__': + harness = MGXSTestHarness('statepoint.10.*', False) + harness.main() From 7069c5db315be9f2a756d6e5d8cc295d7be474a0 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Fri, 17 Feb 2017 07:48:45 -0500 Subject: [PATCH 10/16] lessened the number of particles used in test_mg_convert and reduced the number of cases by 2 --- tests/test_mg_convert/inputs_true.dat | 2 +- tests/test_mg_convert/results_true.dat | 28 ++++++++++-------------- tests/test_mg_convert/test_mg_convert.py | 5 ++--- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/tests/test_mg_convert/inputs_true.dat b/tests/test_mg_convert/inputs_true.dat index 9b0b0f4b17..95ad615867 100644 --- a/tests/test_mg_convert/inputs_true.dat +++ b/tests/test_mg_convert/inputs_true.dat @@ -18,7 +18,7 @@ 1000 - 2000 + 1000 100 diff --git a/tests/test_mg_convert/results_true.dat b/tests/test_mg_convert/results_true.dat index 5cf0eb6d26..cf4777b8eb 100644 --- a/tests/test_mg_convert/results_true.dat +++ b/tests/test_mg_convert/results_true.dat @@ -1,28 +1,24 @@ k-combined: -9.957263E-01 5.469041E-05 +9.957366E-01 7.836427E-05 k-combined: -9.957263E-01 5.469041E-05 +9.963227E-01 8.010344E-05 k-combined: -9.963652E-01 5.444550E-05 +9.957366E-01 7.836427E-05 k-combined: -9.957263E-01 5.469041E-05 +9.955152E-01 8.132242E-05 k-combined: -9.955157E-01 5.785520E-05 +9.953937E-01 8.452497E-05 k-combined: -9.954023E-01 5.694553E-05 +9.957744E-01 8.816057E-05 k-combined: -9.955722E-01 6.075634E-05 +9.955332E-01 8.894390E-05 k-combined: -9.955692E-01 6.094480E-05 +9.956463E-01 8.378533E-05 k-combined: -9.955154E-01 5.775805E-05 +9.955875E-01 8.818927E-05 k-combined: -9.956787E-01 5.915112E-05 +9.957017E-01 8.726677E-05 k-combined: -9.957022E-01 5.952369E-05 +9.957366E-01 7.836427E-05 k-combined: -9.953517E-01 5.920431E-05 -k-combined: -9.957263E-01 5.469041E-05 -k-combined: -9.957263E-01 5.469041E-05 +9.957366E-01 7.836427E-05 diff --git a/tests/test_mg_convert/test_mg_convert.py b/tests/test_mg_convert/test_mg_convert.py index c5c0ac16c7..ad86e5a508 100755 --- a/tests/test_mg_convert/test_mg_convert.py +++ b/tests/test_mg_convert/test_mg_convert.py @@ -11,7 +11,7 @@ from testing_harness import PyAPITestHarness import openmc # OpenMC simulation parameters -batches = 2000 +batches = 1000 inactive = 100 particles = 1000 @@ -123,7 +123,7 @@ class MGXSTestHarness(PyAPITestHarness): def _run_openmc(self): # Run multiple conversions to compare results - cases = [None, ['legendre', 2], ['legendre', 0], + cases = [['legendre', 2], ['legendre', 0], ['tabular', 33], ['histogram', 32], [['tabular', 33], ['legendre', 1]], [['tabular', 33], ['tabular', 3]], @@ -131,7 +131,6 @@ class MGXSTestHarness(PyAPITestHarness): [['histogram', 32], ['legendre', 1]], [['histogram', 32], ['tabular', 3]], [['histogram', 32], ['histogram', 16]], - [['histogram', 32], ['histogram', 128]], ['angle', 2], [['angle', 2], ['isotropic', None]]] outstr = '' From e6a3c04817126a524549b82ed95dffdc2a6bdb8a Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 18 Feb 2017 05:20:30 -0500 Subject: [PATCH 11/16] docstring modifications and reduced histories in test_mg_convert in an attempt to bring its runtime down --- openmc/mgxs_library.py | 6 ++---- tests/test_mg_convert/inputs_true.dat | 6 +++--- tests/test_mg_convert/results_true.dat | 24 ++++++++++++------------ tests/test_mg_convert/test_mg_convert.py | 6 +++--- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index d441e1bbbf..deef7e82d0 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1690,8 +1690,7 @@ class XSdata(object): azimuthal bins of an XSdata object that already uses an angular representation. Finally, this method simply uses an arithmetic mean to convert from an angular to isotropic representation; no flux-weighting - is applied and therefore the correctness of the solution is not - guaranteed. + is applied and therefore reaction rates will not be preserved. Parameters ---------- @@ -2453,8 +2452,7 @@ class MGXSLibrary(object): azimuthal bins of an XSdata object that already uses an angular representation. Finally, this method simply uses an arithmetic mean to convert from an angular to isotropic representation; no flux-weighting - is applied and therefore the correctness of the solution is not - guaranteed. + is applied and therefore the reaction rates will not be preserved. Parameters ---------- diff --git a/tests/test_mg_convert/inputs_true.dat b/tests/test_mg_convert/inputs_true.dat index 95ad615867..9bfb1dfbae 100644 --- a/tests/test_mg_convert/inputs_true.dat +++ b/tests/test_mg_convert/inputs_true.dat @@ -17,9 +17,9 @@ - 1000 - 1000 - 100 + 100 + 10 + 5 diff --git a/tests/test_mg_convert/results_true.dat b/tests/test_mg_convert/results_true.dat index cf4777b8eb..a52b929dcf 100644 --- a/tests/test_mg_convert/results_true.dat +++ b/tests/test_mg_convert/results_true.dat @@ -1,24 +1,24 @@ k-combined: -9.957366E-01 7.836427E-05 +9.930873E-01 2.221904E-03 k-combined: -9.963227E-01 8.010344E-05 +9.948148E-01 1.216270E-03 k-combined: -9.957366E-01 7.836427E-05 +9.930873E-01 2.221904E-03 k-combined: -9.955152E-01 8.132242E-05 +9.755034E-01 6.178296E-03 k-combined: -9.953937E-01 8.452497E-05 +9.738059E-01 4.529068E-03 k-combined: -9.957744E-01 8.816057E-05 +9.866847E-01 9.485912E-03 k-combined: -9.955332E-01 8.894390E-05 +9.755024E-01 6.179047E-03 k-combined: -9.956463E-01 8.378533E-05 +9.738061E-01 4.529462E-03 k-combined: -9.955875E-01 8.818927E-05 +9.866835E-01 9.485832E-03 k-combined: -9.957017E-01 8.726677E-05 +9.719024E-01 4.213166E-03 k-combined: -9.957366E-01 7.836427E-05 +9.930873E-01 2.221904E-03 k-combined: -9.957366E-01 7.836427E-05 +9.930873E-01 2.221904E-03 diff --git a/tests/test_mg_convert/test_mg_convert.py b/tests/test_mg_convert/test_mg_convert.py index ad86e5a508..607195a82b 100755 --- a/tests/test_mg_convert/test_mg_convert.py +++ b/tests/test_mg_convert/test_mg_convert.py @@ -11,9 +11,9 @@ from testing_harness import PyAPITestHarness import openmc # OpenMC simulation parameters -batches = 1000 -inactive = 100 -particles = 1000 +batches = 10 +inactive = 5 +particles = 100 def build_mgxs_library(convert): From ca746424e1646d7ef39d119c3127a2fa4583a866 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 18 Feb 2017 12:15:26 -0500 Subject: [PATCH 12/16] Closed StatePoint inbetween reads and writes in the test_mgxs_library_ce_to_mg; this was failing on my machine, but apparently not elsewhere. I thought I fixed it with a recompile of HDF5 which stopped the non-sensical error messages; turns out it came back and this is actually the culprit --- .../test_mgxs_library_ce_to_mg.py | 3 ++ tests/test_salphabeta/geometry.xml | 15 ------- tests/test_salphabeta/materials.xml | 41 ------------------- tests/test_salphabeta/settings.xml | 16 -------- 4 files changed, 3 insertions(+), 72 deletions(-) delete mode 100644 tests/test_salphabeta/geometry.xml delete mode 100644 tests/test_salphabeta/materials.xml delete mode 100644 tests/test_salphabeta/settings.xml diff --git a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py b/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py index 7dc679b1fe..e6511fd623 100644 --- a/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py +++ b/tests/test_mgxs_library_ce_to_mg/test_mgxs_library_ce_to_mg.py @@ -72,6 +72,9 @@ class MGXSTestHarness(PyAPITestHarness): if os.path.exists('./tallies.xml'): os.remove('./tallies.xml') + # Close the statepoint to allow writing + sp.close() + # Re-run MG mode. if self._opts.mpi_exec is not None: returncode = openmc.run(mpi_procs=self._opts.mpi_np, diff --git a/tests/test_salphabeta/geometry.xml b/tests/test_salphabeta/geometry.xml deleted file mode 100644 index 2b978b9148..0000000000 --- a/tests/test_salphabeta/geometry.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/tests/test_salphabeta/materials.xml b/tests/test_salphabeta/materials.xml deleted file mode 100644 index bfe0a6224d..0000000000 --- a/tests/test_salphabeta/materials.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/test_salphabeta/settings.xml b/tests/test_salphabeta/settings.xml deleted file mode 100644 index a6fd5da19e..0000000000 --- a/tests/test_salphabeta/settings.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - 10 - 5 - 1000 - - - - - -4 -4 -4 4 4 4 - - - - From a7b174aff623825c1e07b5c0ddc54692f5fcffce Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 18 Feb 2017 12:16:58 -0500 Subject: [PATCH 13/16] Ahh!! I deleted the XML files! forgot that this was a legacy test --- tests/test_salphabeta/geometry.xml | 15 +++++++++++ tests/test_salphabeta/materials.xml | 41 +++++++++++++++++++++++++++++ tests/test_salphabeta/settings.xml | 16 +++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/test_salphabeta/geometry.xml create mode 100644 tests/test_salphabeta/materials.xml create mode 100644 tests/test_salphabeta/settings.xml diff --git a/tests/test_salphabeta/geometry.xml b/tests/test_salphabeta/geometry.xml new file mode 100644 index 0000000000..2b978b9148 --- /dev/null +++ b/tests/test_salphabeta/geometry.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tests/test_salphabeta/materials.xml b/tests/test_salphabeta/materials.xml new file mode 100644 index 0000000000..bfe0a6224d --- /dev/null +++ b/tests/test_salphabeta/materials.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_salphabeta/settings.xml b/tests/test_salphabeta/settings.xml new file mode 100644 index 0000000000..a6fd5da19e --- /dev/null +++ b/tests/test_salphabeta/settings.xml @@ -0,0 +1,16 @@ + + + + + 10 + 5 + 1000 + + + + + -4 -4 -4 4 4 4 + + + + From db33004a0f2c7db8eb8a67d92c09f25cc3a1ef93 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sat, 18 Feb 2017 14:51:12 -0500 Subject: [PATCH 14/16] Added test for Legendre scattering - current tests converted all legenres to tabular distributions, this avoids that --- tests/test_mg_legendre/inputs_true.dat | 47 ++++++++++++++++++++++ tests/test_mg_legendre/results_true.dat | 2 + tests/test_mg_legendre/test_mg_legendre.py | 28 +++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 tests/test_mg_legendre/inputs_true.dat create mode 100644 tests/test_mg_legendre/results_true.dat create mode 100644 tests/test_mg_legendre/test_mg_legendre.py diff --git a/tests/test_mg_legendre/inputs_true.dat b/tests/test_mg_legendre/inputs_true.dat new file mode 100644 index 0000000000..fe0b5cf16d --- /dev/null +++ b/tests/test_mg_legendre/inputs_true.dat @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + ../1d_mgxs.h5 + + + + + + + + + + + + + + + + + 100 + 10 + 5 + + + + 0.0 0.0 0.0 10.0 10.0 5.0 + + + multi-group + + false + + diff --git a/tests/test_mg_legendre/results_true.dat b/tests/test_mg_legendre/results_true.dat new file mode 100644 index 0000000000..d0f8c319e7 --- /dev/null +++ b/tests/test_mg_legendre/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.110122E+00 2.549637E-02 diff --git a/tests/test_mg_legendre/test_mg_legendre.py b/tests/test_mg_legendre/test_mg_legendre.py new file mode 100644 index 0000000000..a16912c5db --- /dev/null +++ b/tests/test_mg_legendre/test_mg_legendre.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import os +import sys + +sys.path.insert(0, os.pardir) +from testing_harness import PyAPITestHarness +from input_set import MGInputSet + + +class MGMaxOrderTestHarness(PyAPITestHarness): + def __init__(self, statepoint_name, tallies_present, mg=False): + PyAPITestHarness.__init__(self, statepoint_name, tallies_present) + self._input_set = MGInputSet() + + def _build_inputs(self): + """Write input XML files.""" + reps = ['iso'] + self._input_set.build_default_materials_and_geometry(reps=reps) + self._input_set.build_default_settings() + # Enforce Legendre scattering + self._input_set.settings.tabular_legendre = {'enable': False} + self._input_set.export() + + +if __name__ == '__main__': + harness = MGMaxOrderTestHarness('statepoint.10.*', False, mg=True) + harness.main() From 471ebbfa891fc7df8721969f0c16839315294c04 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 19 Feb 2017 06:10:20 -0500 Subject: [PATCH 15/16] Moved check for small values to end of convert_scatter_format and added blank line --- openmc/mgxs_library.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index deef7e82d0..af6dc8f98c 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1837,6 +1837,7 @@ class XSdata(object): np.sum((l + 0.5) * eval_legendre(l, mu[imu]) * orig_data[..., l] for l in range(self.num_orders)) + elif target_format == 'histogram': # This code uses the vectorized integration capabilities # instead of having an isotropic and angle representation @@ -1856,10 +1857,6 @@ class XSdata(object): for l in range(self.num_orders)) new_data[..., h_bin] = simps(table_fine, mu_fine) - # Remove the very small values resulting from numerical - # precision issues - new_data[..., np.abs(new_data) < 1.E-10] = 0. - elif self.scatter_format == 'tabular': # Calculate the mu points of the current data mu_self = np.linspace(-1, 1, self.num_orders) @@ -1875,10 +1872,6 @@ class XSdata(object): for l in range(xsdata.num_orders): new_data[..., l] = simps(y[l], mu_fine) - # Remove the very small values resulting from numerical - # precision issues - new_data[..., np.abs(new_data) < 1.E-10] = 0. - elif target_format == 'tabular': # Simply use an interpolating function to get the new data mu = np.linspace(-1, 1, xsdata.num_orders) @@ -1898,10 +1891,6 @@ class XSdata(object): mu_fine = np.linspace(mu[h_bin], mu[h_bin + 1], _NMU) new_data[..., h_bin] = simps(interp(mu_fine), mu_fine) - # Remove the very small values resulting from numerical - # precision issues - new_data[..., np.abs(new_data) < 1.E-10] = 0. - elif self.scatter_format == 'histogram': # The histogram format does not have enough information to # convert to the other forms without inducing some amount of @@ -1929,10 +1918,6 @@ class XSdata(object): for l in range(xsdata.num_orders): new_data[..., l] = simps(y[l], mu_fine) - # Remove the very small values resulting from numerical - # precision issues - new_data[..., np.abs(new_data) < 1.E-10] = 0. - elif target_format == 'tabular': # Simply use an interpolating function to get the new data mu = np.linspace(-1, 1, xsdata.num_orders) @@ -1952,9 +1937,8 @@ class XSdata(object): new_data[..., h_bin] = \ norm * simps(interp(mu_fine), mu_fine) - # Remove the very small values resulting from numerical - # precision issues - new_data[..., np.abs(new_data) < 1.E-10] = 0. + # Remove small values resulting from numerical precision issues + new_data[..., np.abs(new_data) < 1.E-10] = 0. xsdata.set_scatter_matrix(new_data, temp) From 38ce9acdfbc13b5aac8b9b1370d71eb12525d4a8 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 19 Feb 2017 09:43:57 -0500 Subject: [PATCH 16/16] Updated openmc.run commands and input files to reflect the new run modes --- tests/test_mg_convert/inputs_true.dat | 9 ++++----- tests/test_mg_convert/test_mg_convert.py | 6 +++--- tests/test_mg_legendre/inputs_true.dat | 9 ++++----- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/test_mg_convert/inputs_true.dat b/tests/test_mg_convert/inputs_true.dat index 9bfb1dfbae..85eeb3a24a 100644 --- a/tests/test_mg_convert/inputs_true.dat +++ b/tests/test_mg_convert/inputs_true.dat @@ -16,11 +16,10 @@ - - 100 - 10 - 5 - + eigenvalue + 100 + 10 + 5 -5 -5 -5 5 5 5 diff --git a/tests/test_mg_convert/test_mg_convert.py b/tests/test_mg_convert/test_mg_convert.py index 607195a82b..f4cd90e713 100755 --- a/tests/test_mg_convert/test_mg_convert.py +++ b/tests/test_mg_convert/test_mg_convert.py @@ -138,9 +138,9 @@ class MGXSTestHarness(PyAPITestHarness): build_mgxs_library(case) if self._opts.mpi_exec is not None: - returncode = openmc.run(mpi_procs=self._opts.mpi_np, - openmc_exec=self._opts.exe, - mpi_exec=self._opts.mpi_exec) + mpi_args = [self._opts.mpi_exec, '-n', self._opts.mpi_np] + returncode = openmc.run(openmc_exec=self._opts.exe, + mpi_args=mpi_args) else: returncode = openmc.run(openmc_exec=self._opts.exe) diff --git a/tests/test_mg_legendre/inputs_true.dat b/tests/test_mg_legendre/inputs_true.dat index fe0b5cf16d..ca24a0dcf7 100644 --- a/tests/test_mg_legendre/inputs_true.dat +++ b/tests/test_mg_legendre/inputs_true.dat @@ -30,11 +30,10 @@ - - 100 - 10 - 5 - + eigenvalue + 100 + 10 + 5 0.0 0.0 0.0 10.0 10.0 5.0