From 2a594cc7041c89ed9dc45858816b22d329faae86 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Tue, 29 Nov 2016 18:46:09 -0500 Subject: [PATCH 1/2] Added from_hdf5 for MGXSLibrary class and some miscellaneous fixes' --- openmc/data/reaction.py | 2 +- openmc/macroscopic.py | 4 +- openmc/mgxs/mgxs.py | 6 +- openmc/mgxs_library.py | 239 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 231 insertions(+), 20 deletions(-) diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index 08930fa65..bb976f9fa 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -818,7 +818,7 @@ class Reaction(EqualityMixin): Parameters ---------- group : h5py.Group - HDF5 group to write to + HDF5 group to read from energy : dict Dictionary whose keys are temperatures (e.g., '300K') and values are arrays of energies at which cross sections are tabulated at. diff --git a/openmc/macroscopic.py b/openmc/macroscopic.py index 4d3589141..f2521c4ac 100644 --- a/openmc/macroscopic.py +++ b/openmc/macroscopic.py @@ -1,5 +1,3 @@ -import sys - from six import string_types from openmc.checkvalue import check_type @@ -45,7 +43,7 @@ class Macroscopic(object): return hash((self._name)) def __repr__(self): - string = 'Nuclide - {0}\n'.format(self._name) + string = 'Macroscopic - {0}\n'.format(self._name) return string @property diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 58b8a32c2..6f8abefd1 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -62,6 +62,9 @@ _DOMAINS = (openmc.Cell, # Supported ScatterMatrixXS and NuScatterMatrixXS angular distribution types MU_TREATMENTS = ('legendre', 'histogram') +# Maximum Legendre order supported by OpenMC +MAX_LEGENDRE = 10 + @add_metaclass(ABCMeta) class MGXS(object): @@ -3475,7 +3478,8 @@ class ScatterMatrixXS(MatrixMGXS): cv.check_type('legendre_order', legendre_order, Integral) cv.check_greater_than('legendre_order', legendre_order, 0, equality=True) - cv.check_less_than('legendre_order', legendre_order, 10, equality=True) + cv.check_less_than('legendre_order', legendre_order, MAX_LEGENDRE, + equality=True) if self.scatter_format == 'legendre': if self.correction == 'P0' and legendre_order > 0: diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 347c015e7..70ff7a91b 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -1,6 +1,6 @@ from collections import Iterable from numbers import Real, Integral -import sys +import os from six import string_types import numpy as np @@ -16,7 +16,7 @@ from openmc.checkvalue import check_type, check_value, check_greater_than, \ _REPRESENTATIONS = ['isotropic', 'angle'] _SCATTER_TYPES = ['tabular', 'legendre', 'histogram'] _XS_SHAPES = ["[G][G'][Order]", "[G]", "[G']", "[G][G']", "[DG]", "[G][DG]", - "[G'][DG]"] + "[G'][DG]", "[G][G'][DG]"] class XSdata(object): @@ -42,7 +42,7 @@ class XSdata(object): ---------- name : str Unique identifier for the xsdata object - aromic_weight_ratio : float + atomic_weight_ratio : float Atomic weight ratio of an isotope. That is, the ratio of the mass of the isotope to the mass of a single neutron. temperatures : numpy.ndarray @@ -303,13 +303,14 @@ class XSdata(object): self._xs_shapes["[G][G'][DG]"] = (self.energy_groups.num_groups, self.energy_groups.num_groups, self.num_delayed_groups) + self._xs_shapes["[G][G'][Order]"] \ = (self.energy_groups.num_groups, self.energy_groups.num_groups, self.num_orders) # If representation is by angle prepend num polar and num azim if self.representation == 'angle': - for key,shapes in self._xs_shapes.items(): + for key, shapes in self._xs_shapes.items(): self._xs_shapes[key] \ = (self.num_polar, self.num_azimuthal) + shapes @@ -337,7 +338,7 @@ class XSdata(object): def num_delayed_groups(self, num_delayed_groups): # Check validity of num_delayed_groups - check_type('num_delayed_groups', num_delayed_groups, int) + 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) check_greater_than('num_delayed_groups', num_delayed_groups, 0, @@ -359,6 +360,13 @@ class XSdata(object): check_greater_than('atomic_weight_ratio', atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio + @fissionable.setter + def fissionable(self, fissionable): + + # Check validity of type + check_type('fissionable', fissionable, bool) + self._fissionable = fissionable + @temperatures.setter def temperatures(self, temperatures): @@ -1652,6 +1660,7 @@ class XSdata(object): HDF5 File (a root Group) to write to """ + grp = file.create_group(self.name) if self.atomic_weight_ratio is not None: grp.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio @@ -1719,11 +1728,12 @@ class XSdata(object): (self._delayed_nu_fission[i] is None or \ self._prompt_nu_fission[i] is None): raise ValueError('nu-fission or prompt-nu-fission and ' - 'delayed-nu-fission data must be provided ' - 'when writing the HDF5 library') + 'delayed-nu-fission data must be ' + 'provided when writing the HDF5 library') if self._nu_fission[i] is not None: - xs_grp.create_dataset("nu-fission", data=self._nu_fission[i]) + xs_grp.create_dataset("nu-fission", + data=self._nu_fission[i]) if self._prompt_nu_fission[i] is not None: xs_grp.create_dataset("prompt-nu-fission", @@ -1737,7 +1747,8 @@ class XSdata(object): xs_grp.create_dataset("beta", data=self._beta[i]) if self._decay_rate[i] is not None: - xs_grp.create_dataset("decay rate", data=self._decay_rate[i]) + xs_grp.create_dataset("decay rate", + data=self._decay_rate[i]) if self._scatter_matrix[i] is None: raise ValueError('Scatter matrix must be provided when ' @@ -1836,6 +1847,143 @@ class XSdata(object): xs_grp.create_dataset("inverse-velocity", data=self._inverse_velocity[i]) + @classmethod + def from_hdf5(cls, group, name, energy_groups, num_delayed_groups): + """Generate XSdata object from an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + name : str + Name of the mgxs data set. + energy_groups : openmc.mgxs.EnergyGroups + Energy group structure + num_delayed_groups : int + Number of delayed groups + + Returns + ------- + openmc.XSdata + Multi-group cross section data + + """ + + # Get a list of all the subgroups which will contain our temperature + # strings + subgroups = group.keys() + temperatures = [] + for subgroup in subgroups: + if subgroup != 'kTs': + temperatures.append(subgroup) + + # To ensure the actual floating point temperature used when creating + # the new library is consistent with that used when originally creating + # the file, get the floating point temperatures straight from the kTs + # group. + kTs_group = group['kTs'] + float_temperatures = [] + for temperature in temperatures: + kT = kTs_group[temperature].value + float_temperatures.append(kT / openmc.data.K_BOLTZMANN) + + attrs = group.attrs.keys() + if 'representation' in attrs: + representation = group.attrs['representation'].decode() + else: + representation = 'isotropic' + + data = cls(name, energy_groups, float_temperatures, representation, + num_delayed_groups) + + if 'scatter_format' in attrs: + data.scatter_format = group.attrs['scatter_format'].decode() + + # Get the remaining optional attributes + if 'atomic_weight_ratio' in attrs: + data.atomic_weight_ratio = group.attrs['atomic_weight_ratio'] + if 'order' in attrs: + data.order = group.attrs['order'] + if data.representation == 'angle': + data.num_azimuthal = group.attrs['num_azimuthal'] + data.num_polar = group.attrs['num_polar'] + + # Read the temperature-dependent datasets + for temp, float_temp in zip(temperatures, float_temperatures): + xs_types = ['total', 'absorption', 'fission', 'kappa-fission', + 'chi', 'chi-prompt', 'chi-delayed', 'nu-fission', + 'prompt-nu-fission', 'delayed-nu-fission', 'beta', + 'decay rate', 'inverse-velocity'] + + temperature_group = group[temp] + + for xs_type in xs_types: + set_func = 'set_' + xs_type.replace(' ', '_').replace('-', '_') + if xs_type in temperature_group: + getattr(data, set_func)(temperature_group[xs_type].value, + float_temp) + + scatt_group = temperature_group['scatter_data'] + + # Get scatter matrix and 'un-flatten' it + g_max = scatt_group['g_max'] + g_min = scatt_group['g_min'] + flat_scatter = scatt_group['scatter_matrix'].value + scatter_matrix = np.zeros(data.xs_shapes["[G][G'][Order]"]) + G = data.energy_groups.num_groups + if data.representation == 'isotropic': + Np = 1 + Na = 1 + elif data.representation == 'angle': + Np = data.num_polar + Na = data.num_azimuthal + flat_index = 0 + for p in range(Np): + for a in range(Na): + for g_in in range(G): + if data.representation == 'isotropic': + g_mins = g_min[g_in] + g_maxs = g_max[g_in] + elif data.representation == 'angle': + g_mins = g_min[p, a, g_in] + g_maxs = g_max[p, a, g_in] + for g_out in range(g_mins - 1, g_maxs): + for ang in range(data.num_orders): + if data.representation == 'isotropic': + scatter_matrix[g_in, g_out, ang] = \ + flat_scatter[flat_index] + elif data.representation == 'angle': + scatter_matrix[p, a, g_in, g_out, ang] = \ + flat_scatter[flat_index] + flat_index += 1 + data.set_scatter_matrix(scatter_matrix, float_temp) + + # Repeat for multiplicity + if 'multiplicity_matrix' in scatt_group: + flat_mult = scatt_group['multiplicity_matrix'].value + mult_matrix = np.zeros(data.xs_shapes["[G][G']"]) + flat_index = 0 + for p in range(Np): + for a in range(Na): + for g_in in range(G): + if data.representation == 'isotropic': + g_mins = g_min[g_in] + g_maxs = g_max[g_in] + elif data.representation == 'angle': + g_mins = g_min[p, a, g_in] + g_maxs = g_max[p, a, g_in] + for g_out in range(g_mins - 1, g_maxs): + if data.representation == 'isotropic': + mult_matrix[g_in, g_out] = \ + flat_mult[flat_index] + elif data.representation == 'angle': + mult_matrix[p, a, g_in, g_out] = \ + flat_mult[flat_index] + flat_index += 1 + data.set_multiplicity_matrix(mult_matrix, float_temp) + + return data + class MGXSLibrary(object): """Multi-Group Cross Sections file used for an OpenMC simulation. @@ -1872,14 +2020,14 @@ class MGXSLibrary(object): def num_delayed_groups(self): return self._num_delayed_groups - @property - def temperatures(self): - return self._temperatures - @property def xsdatas(self): return self._xsdatas + @property + def names(self): + return [xsdata.name for xsdata in self.xsdatas] + @energy_groups.setter def energy_groups(self, energy_groups): check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups) @@ -1887,7 +2035,7 @@ class MGXSLibrary(object): @num_delayed_groups.setter def num_delayed_groups(self, num_delayed_groups): - check_type('num_delayed_groups', num_delayed_groups, int) + check_type('num_delayed_groups', num_delayed_groups, Integral) check_greater_than('num_delayed_groups', num_delayed_groups, 0, equality=True) check_less_than('num_delayed_groups', num_delayed_groups, @@ -1916,7 +2064,7 @@ class MGXSLibrary(object): self._xsdatas.append(xsdata) def add_xsdatas(self, xsdatas): - """Add multiple xsdatas to the file. + """Add multiple XSdatas to the file. Parameters ---------- @@ -1947,6 +2095,27 @@ class MGXSLibrary(object): self._xsdatas.remove(xsdata) + def get_by_name(self, name): + """Access the XSdata objects by name + + Parameters + ---------- + name : str + Name of openmc.XSdata object to obtain + + Returns + ------- + result : openmc.XSdata or None + Provides the matching XSdata object or None, if not found + + """ + check_type("name", name, str) + result = None + for xsdata in self.xsdatas: + if name == xsdata.name: + result = xsdata + return result + def export_to_hdf5(self, filename='mgxs.h5'): """Create an hdf5 file that can be used for a simulation. @@ -1969,3 +2138,43 @@ class MGXSLibrary(object): xsdata.to_hdf5(file) file.close() + + @classmethod + def from_hdf5(cls, filename=None): + """Generate an MGXS Library from an HDF5 group or file + Parameters + ---------- + filename : str, optional + Name of HDF5 file containing MGXS data. Default is None. + If not provided, the value of the OPENMC_MG_CROSS_SECTIONS + environmental variable will be used + Returns + ------- + openmc.MGXSLibrary + Multi-group cross section data object. + """ + + # If filename is None, get the cross sections from the + # OPENMC_CROSS_SECTIONS environment variable + if filename is None: + filename = os.environ.get('OPENMC_MG_CROSS_SECTIONS') + + # Check to make sure there was an environmental variable. + if filename is None: + raise ValueError("Either path or OPENMC_MG_CROSS_SECTIONS " + "environmental variable must be set") + + check_type('filename', filename, str) + file = h5py.File(filename, 'r') + + group_structure = file.attrs['group structure'] + num_delayed_groups = file.attrs['delayed_groups'] + energy_groups = openmc.mgxs.EnergyGroups(group_structure) + data = cls(energy_groups, num_delayed_groups) + + for group_name, group in file.items(): + data.add_xsdata(openmc.XSdata.from_hdf5(group, group_name, + energy_groups, + num_delayed_groups)) + + return data From c423975189d897925f7e316b822649b49812a9f0 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Wed, 30 Nov 2016 19:48:30 -0500 Subject: [PATCH 2/2] Modified XSData docstring, removed XSData.fissionable setter and made openmc.mgxs.MAX_LEGENDRE private. --- openmc/mgxs/mgxs.py | 4 ++-- openmc/mgxs_library.py | 37 +++++++++++++++---------------------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 6f8abefd1..ce7511193 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -63,7 +63,7 @@ _DOMAINS = (openmc.Cell, MU_TREATMENTS = ('legendre', 'histogram') # Maximum Legendre order supported by OpenMC -MAX_LEGENDRE = 10 +_MAX_LEGENDRE = 10 @add_metaclass(ABCMeta) @@ -3478,7 +3478,7 @@ class ScatterMatrixXS(MatrixMGXS): cv.check_type('legendre_order', legendre_order, Integral) cv.check_greater_than('legendre_order', legendre_order, 0, equality=True) - cv.check_less_than('legendre_order', legendre_order, MAX_LEGENDRE, + cv.check_less_than('legendre_order', legendre_order, _MAX_LEGENDRE, equality=True) if self.scatter_format == 'legendre': diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 70ff7a91b..10fda4593 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -71,50 +71,50 @@ class XSdata(object): Number of equal width angular bins that the polar angular domain is subdivided into. This only applies when :attr:`XSdata.representation` is "angle". - total : dict of numpy.ndarray + total : list of numpy.ndarray Group-wise total cross section. - absorption : dict of numpy.ndarray + absorption : list of numpy.ndarray Group-wise absorption cross section. - scatter_matrix : dict of numpy.ndarray + scatter_matrix : list of numpy.ndarray Scattering moment matrices presented with the columns representing incoming group and rows representing the outgoing group. That is, down-scatter will be above the diagonal of the resultant matrix. - multiplicity_matrix : dict of numpy.ndarray + multiplicity_matrix : list of numpy.ndarray Ratio of neutrons produced in scattering collisions to the neutrons which undergo scattering collisions; that is, the multiplicity provides the code with a scaling factor to account for neutrons produced in (n,xn) reactions. - fission : dict of numpy.ndarray + fission : list of numpy.ndarray Group-wise fission cross section. - kappa_fission : dict of numpy.ndarray + kappa_fission : list of numpy.ndarray Group-wise kappa_fission cross section. - chi : dict of numpy.ndarray + chi : list of numpy.ndarray Group-wise fission spectra ordered by increasing group index (i.e., fast to thermal). This attribute should be used if making the common approximation that the fission spectra does not depend on incoming energy. If the user does not wish to make this approximation, then this should not be provided and this information included in the :attr:`XSdata.nu_fission` attribute instead. - chi_prompt : dict of numpy.ndarray + chi_prompt : list of numpy.ndarray Group-wise prompt fission spectra ordered by increasing group index (i.e., fast to thermal). This attribute should be used if chi from prompt and delayed neutrons is being set separately. - chi_delayed : dict of numpy.ndarray + chi_delayed : list of numpy.ndarray Group-wise delayed fission spectra ordered by increasing group index (i.e., fast to thermal). This attribute should be used if chi from prompt and delayed neutrons is being set separately. - nu_fission : dict of numpy.ndarray + nu_fission : list of numpy.ndarray Group-wise fission production cross section vector (i.e., if ``chi`` is provided), or is the group-wise fission production matrix. - prompt_nu_fission : dict of numpy.ndarray + prompt_nu_fission : list of numpy.ndarray Group-wise prompt fission production cross section vector. - delayed_nu_fission : dict of numpy.ndarray + delayed_nu_fission : list of numpy.ndarray Group-wise delayed fission production cross section vector. - beta : dict of numpy.ndarray + beta : list of numpy.ndarray Delayed-group-wise delayed neutron fraction cross section vector. - decay_rate : dict of numpy.ndarray + decay_rate : list of numpy.ndarray Delayed-group-wise decay rate vector. - inverse_velocity : dict of numpy.ndarray + inverse_velocity : list of numpy.ndarray Inverse of velocity, in units of sec/cm. xs_shapes : dict of iterable of int Dictionary with keys of _XS_SHAPES and iterable of int values with the @@ -360,13 +360,6 @@ class XSdata(object): check_greater_than('atomic_weight_ratio', atomic_weight_ratio, 0.0) self._atomic_weight_ratio = atomic_weight_ratio - @fissionable.setter - def fissionable(self, fissionable): - - # Check validity of type - check_type('fissionable', fissionable, bool) - self._fissionable = fissionable - @temperatures.setter def temperatures(self, temperatures):