From e1f70b40d40fcc302c833bb6a1349846f4cbcd65 Mon Sep 17 00:00:00 2001 From: Adam Nelson Date: Sun, 1 May 2016 10:23:20 -0400 Subject: [PATCH] Incorporated ability to transfer from Library to MGXS_Library. Didnt test yet. --- openmc/mgxs/library.py | 168 +++++++++++++++++++++++++++++++++++++ openmc/mgxs_library.py | 184 ++++++++++++++++++++--------------------- 2 files changed, 260 insertions(+), 92 deletions(-) diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index f3bf2018dc..7e6f07ce21 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -4,6 +4,8 @@ import copy import pickle from numbers import Integral from collections import OrderedDict +import numpy as np +import warnings.warn as warn import openmc import openmc.mgxs @@ -712,3 +714,169 @@ class Library(object): # Load and return pickled Library object return pickle.load(open(full_filename, 'rb')) + + def write_mg_library(self, xs_type='micro', domain_names=None, xs_ids=None, + filename='mg_cross_sections', directory='./'): + """Create a cross-section data library file for the Multi-Group + mode of OpenMC. + + Parameters + ---------- + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. If the Library object is not tallied by + nuclide this will be set to 'macro' regardless + domain_names : Iterable of str + List of names to apply to the xsdata entries in the + resultant mgxs data file. Defaults to "set1", "set2", ... + xs_ids : str or Iterable of str + Cross section set identifier (i.e., "71c") for all + data sets (if only str) or for each individual one + (if iterable of str). Defaults to '1g' + filename : str + Filename for the pickle file. Defaults to 'mg_cross_sections'. + directory : str + Directory for the pickle file. Defaults to './' (the + current working directory). + + See also + -------- + Library.dump_to_file(mgxs_lib, filename, directory) + + """ + + # Check data types provided + + cv.check_value('xs_type', xs_type, ['macro', 'micro']) + if not self.by_nuclide: + xs_type = 'macro' + if domain_names is not None: + cv.check_iterable_type('domain_names', filename, basestring) + if xs_ids is not None: + if isinstance(xs_ids, basestring): + # If we only have a string lets convert it now to a list + # of strings. + xs_ids = [xs_ids for i in range(len(self.domains))] + else: + cv.check_iterable_type('xs_ids', xs_ids, basestring) + else: + xs_ids = ['.1g'] + cv.check_type('filename', filename, basestring) + cv.check_type('directory', directory, basestring) + + # Make directory if it does not exist + if not os.path.exists(directory): + os.makedirs(directory) + + full_filename = os.path.join(directory, filename + '.xml') + full_filename = full_filename.replace(' ', '-') + + # Initialize file + mgxs_file = openmc.MGXSLibrary(self.energy_groups) + + # Set the scattering order as isotropic until + # support for higher orders are included + order = 0 + + # Build XSdata objects + xsdatas = [] + for i in range(len(self.domains)): + id = self.domains[i].id + if not self.by_nuclide: + # Use k instead of i simply because k will be used for + # the nuclide index in the else part of this conditional + # and using k allows us to use the same code. + k = i + # Build & add metadata to XSdata object + # (Use i here because k in nuclides will add chars to this) + if domain_names is None: + name = 'set' + str(i + 1) + else: + name = domain_names[i] + name += xs_ids[k] + xsdata = openmc.XSdata(name, self.energy_groups) + xsdata.order = order + + # Now get xs data itself + if 'total' in self.mgxs_types: + xsdata.set_total(self.all_mgxs[id]['total'], + xs_type=xs_type, subdomains=(k + 1,)) + if 'absorption' in self.mgxs_types: + xsdata.set_absorption(self.all_mgxs[id]['absorption'], + xs_type=xs_type, subdomains=(k + 1,)) + if 'fission' in self.mgxs_types: + xsdata.set_fission(self.all_mgxs[id]['fission'], + xs_type=xs_type, subdomains=(k + 1,)) + if 'kappa-fission' in self.mgxs_types: + xsdata.set_k_fission(self.all_mgxs[id]['kappa-fission'], + xs_type=xs_type, subdomains=(k + 1,)) + if 'chi' in self.mgxs_types: + xsdata.set_chi(self.all_mgxs[id]['chi'], + xs_type=xs_type, subdomains=(k + 1,)) + if 'nu-fission' in self.mgxs_types: + xsdata.set_nu_fission(self.all_mgxs[id]['nu-fission'], + xs_type=xs_type, subdomains=(k + 1,)) + # multiplicity requires scatter and nu-scatter + if (('scatter' in self.mgxs_types) and ('nu-scatter' in + self.mgxs_types)): + xsdata.set_multiplicity(self.all_mgxs[id]['nu-scatter'], + self.all_mgxs[id]['scatter'], + xs_type=xs_type, + subdomains=(k + 1,)) + using_multiplicity = True + else: + using_multiplicity = False + + if using_multiplicity: + xsdata.set_scatter(self.all_mgxs[id]['scatter'], + xs_type=xs_type, + subdomains=(k + 1,)) + else: + if 'nu-scatter' in self.mgxs_types: + xsdata.set_scatter(self.all_mgxs[id]['nu-scatter'], + xs_type=xs_type, + subdomains=(k + 1,)) + # Since we are not using multiplicity, then + # scattering multiplication (nu-scatter) must be + # accounted for approximately by using an adjusted + # absorption cross section. + if self.total is not None: + xsdata.absorption = \ + np.subtract(xsdata.total, + np.sum(xsdata.scatter[0, :, :], + axis=1)) + else: + # Total isnt included so we cant do the above + # approximation w/out changing absorption instead. + # That can be done with: + # SigA' = SigA - (nuSigS - SigS) + # Doing so would mean essentially duplicating + # set_scatter from MGXSLibrary to obtain the + # SigS, which would be big and ugly once + # angle filters are available. + # Instead, raise a warning about the + # lack of neutron balance and then use scatter + # instead of nu-scatter + if 'scatter' in self.mgxs_types: + msg = "To properly use the 'nu-scatter' " + \ + "MGXS type and maintain neutron " + \ + "balance, a 'total' MGXS type " + \ + "should be provided." + warn(msg) + xsdata.set_scatter( + self.all_mgxs[id]['scatter'], + xs_type=xs_type, subdomains=(k + 1,)) + else: + # Welp, cant do that either. Quit while ahead. + msg = "Total X/S must be provided if using" + \ + " nu-scatter as the scattering data" + raise ValueError(msg) + + xsdatas.append(xsdata) + else: + pass + + # Add XSdatas to file + + # Finally, write the file + mgxs_file.export_to_xml(full_filename) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index b41a2030c4..eae6aa4c15 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -739,6 +739,98 @@ class XSdata(object): 'required, "{1}"'.format(fission.shape, shape) raise ValueError(msg) + def set_nu_fission(self, nu_fission, **kwargs): + # The NuFissionXS class does not have the capability to produce + # a fission matrix and therefore if this path is pursued, we know + # chi must be used. + if isinstance(nu_fission, openmc.mgxs.NuFissionXS): + # Make sure passed MGXS object contains correct group structure + if self.energy_groups != nu_fission.energy_groups: + msg = 'Group structure of provided NuFissionXS does not match'\ + ' group structure of XSdata object' + raise ValueError(msg) + # Get openmc.mgxs.get_xs() arguments from kwargs + # nuclides, xs_type, and value will have sane defaults but can be + # overridden by kwards + if 'nuclides' in kwargs: + nuclides = kwargs['nuclides'] + else: + nuclides = 'sum' + if 'xs_type' in kwargs: + xs_type = kwargs['xs_type'] + else: + xs_type = 'macro' + if 'value' in kwargs: + value = kwargs['value'] + else: + value = 'mean' + # subdomains is required from the kwargs as this is specific to + # this XSdata object. + if 'subdomains' in kwargs: + subdomains = kwargs['subdomains'] + else: + msg = "Argument 'subdomains' is required" + raise ValueError(msg) + + if self._representation is 'isotropic': + self._nu_fission = nu_fission.get_xs(subdomains=subdomains, + nuclides=nuclides, + xs_type=xs_type, + value=value) + elif self._representation is 'angle': + # Not yet implemented as MGXS do not yet support this + pass + + self._use_chi = True + + else: + # nu_fission can be given as a vector or a matrix + # Vector is used when chi also exists. + # Matrix is used when chi does not exist. + # We have to check that the correct form is given, but only if + # chi already has been set. If not, we just check that this is OK + # and set the use_chi flag accordingly + + # First lets set our dimensions here since they get used repeatedly + # throughout this code. + if self._representation is 'isotropic': + shape_vec = (self._energy_groups.num_groups,) + shape_mat = (self._energy_groups.num_groups, + self._energy_groups.num_groups) + elif self._representation is 'angle': + shape_vec = (self._num_polar, self._num_azimuthal, + self._energy_groups.num_groups) + shape_mat = (self._num_polar, self._num_azimuthal, + self._energy_groups.num_groups, + self._energy_groups.num_groups) + + # Begin by checking the case when chi has already been given and + # thus the rules for filling in nu_fission are set. + if self._use_chi is not None: + if self._use_chi: + shape = shape_vec + else: + shape = shape_mat + if nu_fission.shape != shape: + msg = "Invalid Shape of Nu_fission!" + raise ValueError(msg) + else: + # Get shape of nu_fission to determine if we need chi or not + if nu_fission.shape == shape_vec: + self._use_chi = True + elif nu_fission.shape == shape_mat: + self._use_chi = False + else: + msg = "Invalid Shape of Nu_fission!" + raise ValueError(msg) + + # check we have a numpy list + check_type("nu_fission", nu_fission, np.ndarray, + expected_iter_type=Real) + self._nu_fission = np.copy(nu_fission) + if np.sum(self._nu_fission) > 0.0: + self._fissionable = True + def set_k_fission(self, k_fission, **kwargs): if isinstance(k_fission, openmc.mgxs.KappaFissionXS): # Make sure passed MGXS object contains correct group structure @@ -986,98 +1078,6 @@ class XSdata(object): ' required, "{1}"'.format(multiplicity.shape, shape) raise ValueError(msg) - def set_nu_fission(self, nu_fission, **kwargs): - # The NuFissionXS class does not have the capability to produce - # a fission matrix and therefore if this path is pursued, we know - # chi must be used. - if isinstance(nu_fission, openmc.mgxs.NuFissionXS): - # Make sure passed MGXS object contains correct group structure - if self.energy_groups != nu_fission.energy_groups: - msg = 'Group structure of provided NuFissionXS does not match'\ - ' group structure of XSdata object' - raise ValueError(msg) - # Get openmc.mgxs.get_xs() arguments from kwargs - # nuclides, xs_type, and value will have sane defaults but can be - # overridden by kwards - if 'nuclides' in kwargs: - nuclides = kwargs['nuclides'] - else: - nuclides = 'sum' - if 'xs_type' in kwargs: - xs_type = kwargs['xs_type'] - else: - xs_type = 'macro' - if 'value' in kwargs: - value = kwargs['value'] - else: - value = 'mean' - # subdomains is required from the kwargs as this is specific to - # this XSdata object. - if 'subdomains' in kwargs: - subdomains = kwargs['subdomains'] - else: - msg = "Argument 'subdomains' is required" - raise ValueError(msg) - - if self._representation is 'isotropic': - self._nu_fission = nu_fission.get_xs(subdomains=subdomains, - nuclides=nuclides, - xs_type=xs_type, - value=value) - elif self._representation is 'angle': - # Not yet implemented as MGXS do not yet support this - pass - - self._use_chi = True - - else: - # nu_fission can be given as a vector or a matrix - # Vector is used when chi also exists. - # Matrix is used when chi does not exist. - # We have to check that the correct form is given, but only if - # chi already has been set. If not, we just check that this is OK - # and set the use_chi flag accordingly - - # First lets set our dimensions here since they get used repeatedly - # throughout this code. - if self._representation is 'isotropic': - shape_vec = (self._energy_groups.num_groups,) - shape_mat = (self._energy_groups.num_groups, - self._energy_groups.num_groups) - elif self._representation is 'angle': - shape_vec = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups) - shape_mat = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups, - self._energy_groups.num_groups) - - # Begin by checking the case when chi has already been given and - # thus the rules for filling in nu_fission are set. - if self._use_chi is not None: - if self._use_chi: - shape = shape_vec - else: - shape = shape_mat - if nu_fission.shape != shape: - msg = "Invalid Shape of Nu_fission!" - raise ValueError(msg) - else: - # Get shape of nu_fission to determine if we need chi or not - if nu_fission.shape == shape_vec: - self._use_chi = True - elif nu_fission.shape == shape_mat: - self._use_chi = False - else: - msg = "Invalid Shape of Nu_fission!" - raise ValueError(msg) - - # check we have a numpy list - check_type("nu_fission", nu_fission, np.ndarray, - expected_iter_type=Real) - self._nu_fission = np.copy(nu_fission) - if np.sum(self._nu_fission) > 0.0: - self._fissionable = True - def _get_xsdata_xml(self): element = ET.Element("xsdata") element.set("name", self._name)