From 2a594cc7041c89ed9dc45858816b22d329faae86 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Tue, 29 Nov 2016 18:46:09 -0500 Subject: [PATCH] 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 08930fa65c..bb976f9fa5 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 4d35891417..f2521c4acf 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 58b8a32c25..6f8abefd17 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 347c015e79..70ff7a91b6 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