diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index dfa55b3a59..991a98f62c 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -15,14 +15,9 @@ import openmc.checkvalue as cv if sys.version_info[0] >= 3: basestring = str -# The following represent the most accurate MGXS generation strategy -# for use in the MG mode of OpenMC. -OPENMC_MG_MGXS_TYPES = ['transport', 'absorption', 'nu-fission', 'chi', - 'scatter matrix', 'nu-scatter matrix'] - class Library(object): - """A multi-group cross section library for some energy group structure. + '''A multi-group cross section library for some energy group structure. This class can be used for both OpenMC input generation and tally data post-processing to compute spatially-homogenized and energy-integrated @@ -84,7 +79,7 @@ class Library(object): Whether or not the Library's tallies use SciPy's LIL sparse matrix format for compressed data storage - """ + ''' def __init__(self, openmc_geometry, by_nuclide=False, mgxs_types=None, name=''): @@ -252,7 +247,8 @@ class Library(object): @domain_type.setter def domain_type(self, domain_type): - cv.check_value('domain type', domain_type, tuple(openmc.mgxs.DOMAIN_TYPES)) + cv.check_value('domain type', domain_type, + tuple(openmc.mgxs.DOMAIN_TYPES)) self._domain_type = domain_type @domains.setter @@ -304,7 +300,7 @@ class Library(object): @sparse.setter def sparse(self, sparse): - """Convert tally data from NumPy arrays to SciPy list of lists (LIL) + '''Convert tally data from NumPy arrays to SciPy list of lists (LIL) sparse matrices, and vice versa. This property may be used to reduce the amount of data in memory during @@ -312,7 +308,7 @@ class Library(object): matrices internally within the Tally object. All tally data access properties and methods will return data as a dense NumPy array. - """ + ''' cv.check_type('sparse', sparse, bool) @@ -325,14 +321,14 @@ class Library(object): self._sparse = sparse def build_library(self): - """Initialize MGXS objects in each domain and for each reaction type + '''Initialize MGXS objects in each domain and for each reaction type in the library. This routine will populate the all_mgxs instance attribute dictionary with MGXS subclass objects keyed by each domain ID (e.g., Material IDs) and cross section type (e.g., 'nu-fission', 'total', etc.). - """ + ''' # Initialize MGXS for each domain and mgxs type and store in dictionary for domain in self.domains: @@ -355,7 +351,7 @@ class Library(object): self.all_mgxs[domain.id][mgxs_type] = mgxs def add_to_tallies_file(self, tallies_file, merge=True): - """Add all tallies from all MGXS objects to a tallies file. + '''Add all tallies from all MGXS objects to a tallies file. NOTE: This assumes that :meth:`Library.build_library` has been called @@ -363,12 +359,12 @@ class Library(object): ---------- tallies_file : openmc.Tallies A Tallies collection to add each MGXS' tallies to generate a - "tallies.xml" input file for OpenMC + 'tallies.xml' input file for OpenMC merge : bool Indicate whether tallies should be merged when possible. Defaults to True. - """ + ''' cv.check_type('tallies_file', tallies_file, openmc.Tallies) @@ -380,7 +376,7 @@ class Library(object): tallies_file.append(tally, merge=merge) def load_from_statepoint(self, statepoint): - """Extracts tallies in an OpenMC StatePoint with the data needed to + '''Extracts tallies in an OpenMC StatePoint with the data needed to compute multi-group cross sections. This method is needed to compute cross section data from tallies @@ -399,7 +395,7 @@ class Library(object): When this method is called with a statepoint that has not been linked with a summary object. - """ + ''' cv.check_type('statepoint', statepoint, openmc.StatePoint) @@ -423,7 +419,7 @@ class Library(object): mgxs.sparse = self.sparse def get_mgxs(self, domain, mgxs_type): - """Return the MGXS object for some domain and reaction rate type. + '''Return the MGXS object for some domain and reaction rate type. This routine searches the library for an MGXS object for the spatial domain and reaction rate type requested by the user. @@ -448,7 +444,7 @@ class Library(object): If no MGXS object can be found for the requested domain or multi-group cross section type - """ + ''' if self.domain_type == 'material': cv.check_type('domain', domain, (openmc.Material, Integral)) @@ -464,7 +460,7 @@ class Library(object): if domain_id == domain.id: break else: - msg = 'Unable to find MGXS for {0} "{1}" in ' \ + msg = 'Unable to find MGXS for "{0}" "{1}" in ' \ 'library'.format(self.domain_type, domain_id) raise ValueError(msg) else: @@ -478,7 +474,7 @@ class Library(object): return self.all_mgxs[domain_id][mgxs_type] def get_condensed_library(self, coarse_groups): - """Construct an energy-condensed version of this library. + '''Construct an energy-condensed version of this library. This routine condenses each of the multi-group cross sections in the library to a coarse energy group structure. NOTE: This routine must @@ -505,7 +501,7 @@ class Library(object): -------- MGXS.get_condensed_xs(coarse_groups) - """ + ''' if self.sp_filename is None: msg = 'Unable to get a condensed coarse group cross section ' \ @@ -534,7 +530,7 @@ class Library(object): return condensed_library def get_subdomain_avg_library(self): - """Construct a subdomain-averaged version of this library. + '''Construct a subdomain-averaged version of this library. This routine averages each multi-group cross section across distribcell instances. The method performs spatial homogenization to compute the @@ -557,7 +553,7 @@ class Library(object): -------- MGXS.get_subdomain_avg_xs(subdomains) - """ + ''' if self.sp_filename is None: msg = 'Unable to get a subdomain-averaged cross section ' \ @@ -585,7 +581,7 @@ class Library(object): def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs', subdomains='all', nuclides='all', xs_type='macro', row_column='inout'): - """Export the multi-group cross section library to an HDF5 binary file. + '''Export the multi-group cross section library to an HDF5 binary file. This method constructs an HDF5 file which stores the library's multi-group cross section data. The data is stored in a hierarchy of @@ -628,7 +624,7 @@ class Library(object): -------- MGXS.build_hdf5_store(filename, directory, xs_type) - """ + ''' if self.sp_filename is None: msg = 'Unable to export multi-group cross section library ' \ @@ -648,7 +644,7 @@ class Library(object): full_filename = os.path.join(directory, filename) full_filename = full_filename.replace(' ', '-') f = h5py.File(full_filename, 'w') - f.attrs["# groups"] = self.num_groups + f.attrs['# groups'] = self.num_groups f.close() # Export MGXS for each domain and mgxs type to an HDF5 file @@ -663,7 +659,7 @@ class Library(object): nuclides=nuclides, row_column=row_column) def dump_to_file(self, filename='mgxs', directory='mgxs'): - """Store this Library object in a pickle binary file. + '''Store this Library object in a pickle binary file. Parameters ---------- @@ -676,7 +672,7 @@ class Library(object): -------- Library.load_from_file(filename, directory) - """ + ''' cv.check_type('filename', filename, basestring) cv.check_type('directory', directory, basestring) @@ -693,7 +689,7 @@ class Library(object): @staticmethod def load_from_file(filename='mgxs', directory='mgxs'): - """Load a Library object from a pickle binary file. + '''Load a Library object from a pickle binary file. Parameters ---------- @@ -711,7 +707,7 @@ class Library(object): -------- Library.dump_to_file(mgxs_lib, filename, directory) - """ + ''' cv.check_type('filename', filename, basestring) cv.check_type('directory', directory, basestring) @@ -729,7 +725,7 @@ class Library(object): def write_mg_library(self, xs_type='macro', domain_names=None, xs_ids=None, filename='mg_cross_sections', directory='./', return_names=True): - """Creates a cross-section data library file for the Multi-Group + '''Creates a cross-section data library file for the Multi-Group mode of OpenMC. Parameters @@ -740,11 +736,11 @@ class Library(object): 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", ... + resultant mgxs data file. Defaults to 'set1', 'set2', ... xs_ids : str or Iterable of str - Cross section set identifier (i.e., "71c") for all + 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' + (if iterable of str). Defaults to '1m'. filename : str Filename for the pickle file. Defaults to 'mg_cross_sections'. directory : str @@ -772,7 +768,11 @@ class Library(object): -------- Library.dump_to_file(mgxs_lib, filename, directory) - """ + ''' + + # Check to ensure the Library contains the correct + # multi-group cross section types + self.check_library_for_openmc_mgxs() # Check the provided parameters cv.check_value('xs_type', xs_type, ['macro', 'micro']) @@ -786,14 +786,18 @@ class Library(object): else: cv.check_iterable_type('xs_ids', xs_ids, basestring) else: - xs_ids = ['1g' for i in range(len(self.domains))] + xs_ids = ['1m' for i in range(len(self.domains))] cv.check_type('filename', filename, basestring) cv.check_type('directory', directory, basestring) + # Make sure statepoint has been loaded + if self._sp_filename is None: + msg = 'A StatePoint must be loaded before calling ' \ + 'the write_mg_library() function' + raise ValueError(msg) + # Construct the collection of the nuclides to report - if self.by_nuclide: - nuclides = self.all_mgxs[1][self.mgxs_types[-1]].get_all_nuclides() - else: + if not self.by_nuclide: xs_type = 'macro' # Make directory if it does not exist and build our filename @@ -813,213 +817,98 @@ class Library(object): xsdatas = [] mat_names = {} - for i in range(len(self.domains)): + for i, domain in enumerate(self.domains): - id = self.domains[i].id - if not self.by_nuclide: + mat_names[domain.id] = {} + if self.by_nuclide: + nuclides = list(domain.get_all_nuclides().keys()) + else: + nuclides = ['total'] + for nuclide in nuclides: # 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] + if nuclide is not 'total': + name += '_' + nuclide name += '.' + xs_ids[i] + + # Store the name + mat_names[domain.id][nuclide] = name + xsdata = openmc.XSdata(name, self.energy_groups) xsdata.order = order + if nuclide is not 'total': + xsdata.zaid = self._nuclides[nuclide][0] + xsdata.awr = self._nuclides[nuclide][1] - mat_names[id] = name - + nuclide = [nuclide] # Now get xs data itself if 'transport' in self.mgxs_types: - if self.correction == 'P0': - xsdata.set_total(self.all_mgxs[id]['transport'], - xs_type=xs_type, subdomains=(id,)) - else: - msg = "The use of a transport cross section " + \ - "requires the correction attribute to be" + \ - "set to 'P0' to produce valid cross " + \ - "section libraries" - raise ValueError(msg) + mymgxs = self.get_mgxs(domain, 'transport') + xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) elif 'total' in self.mgxs_types: - xsdata.set_total(self.all_mgxs[id]['total'], - xs_type=xs_type, subdomains=(id,)) + mymgxs = self.get_mgxs(domain, 'total') + xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) if 'absorption' in self.mgxs_types: - xsdata.set_absorption(self.all_mgxs[id]['absorption'], - xs_type=xs_type, - subdomains=(id,)) + mymgxs = self.get_mgxs(domain, 'absorption') + xsdata.set_absorption_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) if 'fission' in self.mgxs_types: - xsdata.set_fission(self.all_mgxs[id]['fission'], - xs_type=xs_type, subdomains=(id,)) + mymgxs = self.get_mgxs(domain, 'fission') + xsdata.set_fission_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) if 'kappa-fission' in self.mgxs_types: - xsdata.set_k_fission(self.all_mgxs[id]['kappa-fission'], - xs_type=xs_type, subdomains=(id,)) + mymgxs = self.get_mgxs(domain, 'kappa-fission') + xsdata.set_kappa_fission_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) if 'chi' in self.mgxs_types: - xsdata.set_chi(self.all_mgxs[id]['chi'], - xs_type=xs_type, subdomains=(id,)) + mymgxs = self.get_mgxs(domain, 'chi') + xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) if 'nu-fission' in self.mgxs_types: - xsdata.set_nu_fission(self.all_mgxs[id]['nu-fission'], - xs_type=xs_type, - subdomains=(id,)) + mymgxs = self.get_mgxs(domain, 'nu-fission') + xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type, + nuclide=nuclide) # multiplicity requires scatter and nu-scatter if ((('scatter matrix' in self.mgxs_types) and ('nu-scatter matrix' in self.mgxs_types))): - xsdata.set_multiplicity( - self.all_mgxs[id]['nu-scatter matrix'], - self.all_mgxs[id]['scatter matrix'], - xs_type=xs_type, subdomains=(id,)) - xsdata.multiplicity = np.nan_to_num(xsdata.multiplicity) + scatt_mgxs = self.get_mgxs(domain, + 'scatter matrix') + nuscatt_mgxs = self.get_mgxs(domain, + 'nu-scatter matrix') + xsdata.set_multiplicity_mgxs(nuscatt_mgxs, scatt_mgxs, + xs_type=xs_type, + nuclide=nuclide) using_multiplicity = True else: using_multiplicity = False if using_multiplicity: - xsdata.set_scatter(self.all_mgxs[id]['nu-scatter matrix'], - xs_type=xs_type, - subdomains=(id,)) + nuscatt_mgxs = self.get_mgxs(domain, + 'nu-scatter matrix') + xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type, + nuclide=nuclide) else: if 'nu-scatter matrix' in self.mgxs_types: - xsdata.set_scatter( - self.all_mgxs[id]['nu-scatter matrix'], - xs_type=xs_type, subdomains=(id,)) + nuscatt_mgxs = self.get_mgxs(domain, + 'nu-scatter matrix') + xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type, + nuclide=nuclide) + # Since we are not using multiplicity, then # scattering multiplication (nu-scatter) must be # accounted for approximately by using an adjusted # absorption cross section. - # We can not do this with a transport x/s so check - # for that. if 'total' in self.mgxs_types: xsdata.absorption = \ np.subtract(xsdata.total, np.sum(xsdata.scatter[0, :, :], axis=1)) - else: - msg = "Absorption cross section must be " + \ - "provided if using a transport cross" + \ - " section and while not providing a " + \ - "scattering matrix" - raise ValueError(msg) - else: - msg = "No nu-scatter matrix data was provided. " + \ - "This means neutron balance cannot be " + \ - "achieved since (n,xn) multiplication is " + \ - "ignored." - warn(msg) - xsdata.set_scatter(self.all_mgxs[id]['scatter matrix'], - xs_type=xs_type, - subdomains=(id,)) - xsdatas.append(xsdata) - else: - mat_names[id] = {} - for nuclide in nuclides: - # Build & add metadata to XSdata object - if domain_names is None: - name = 'set' + str(i + 1) - else: - name = domain_names[i] - name += '_' + nuclide - name += '.' + xs_ids[i] - - mat_names[id][nuclide] = name - - xsdata = openmc.XSdata(name, self.energy_groups) - xsdata.order = order - xsdata.zaid = self._nuclides[nuclide][0] - xsdata.awr = self._nuclides[nuclide][1] - - # Now get xs data itself - if 'transport' in self.mgxs_types: - if self.correction == 'P0': - xsdata.set_total(self.all_mgxs[id]['transport'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - else: - msg = "The use of a transport cross section " + \ - "requires the correction attribute to be" + \ - "set to 'P0' to produce valid cross " + \ - "section libraries" - raise ValueError(msg) - elif 'total' in self.mgxs_types: - xsdata.set_total(self.all_mgxs[id]['total'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - if 'absorption' in self.mgxs_types: - xsdata.set_absorption(self.all_mgxs[id]['absorption'], - xs_type=xs_type, - subdomains=(id,), - nuclides=[nuclide]) - if 'fission' in self.mgxs_types: - xsdata.set_fission(self.all_mgxs[id]['fission'], - xs_type=xs_type, - subdomains=(id,), - nuclides=[nuclide]) - if 'kappa-fission' in self.mgxs_types: - xsdata.set_k_fission( - self.all_mgxs[id]['kappa-fission'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - if 'chi' in self.mgxs_types: - xsdata.set_chi(self.all_mgxs[id]['chi'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - if 'nu-fission' in self.mgxs_types: - xsdata.set_nu_fission(self.all_mgxs[id]['nu-fission'], - xs_type=xs_type, - subdomains=(id,), - nuclides=[nuclide]) - # multiplicity requires scatter and nu-scatter - if ((('scatter matrix' in self.mgxs_types) and - ('nu-scatter matrix' in self.mgxs_types))): - xsdata.set_multiplicity( - self.all_mgxs[id]['nu-scatter matrix'], - self.all_mgxs[id]['scatter matrix'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - xsdata.multiplicity = \ - np.nan_to_num(xsdata.multiplicity) - using_multiplicity = True - else: - using_multiplicity = False - - if using_multiplicity: - xsdata.set_scatter( - self.all_mgxs[id]['nu-scatter matrix'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - else: - if 'nu-scatter matrix' in self.mgxs_types: - xsdata.set_scatter( - self.all_mgxs[id]['nu-scatter matrix'], - xs_type=xs_type, subdomains=(id,), - nuclides=[nuclide]) - # Since we are not using multiplicity, then - # scattering multiplication (nu-scatter) must be - # accounted for approximately by using an adjusted - # absorption cross section. - if 'total' in self.mgxs_types: - xsdata.absorption = \ - np.subtract(xsdata.total, - np.sum(xsdata.scatter[0, :, :], - axis=1)) - else: - msg = "Absorption cross section must be " + \ - "provided if using a transport cross" + \ - " section and while not providing a " + \ - "scattering matrix" - raise ValueError(msg) - else: - msg = "No nu-scatter matrix data was provided. " +\ - "This means neutron balance cannot be " + \ - "achieved since (n,xn) multiplication is " +\ - "ignored." - warn(msg) - xsdata.set_scatter( - self.all_mgxs[id]['scatter matrix'], - xs_type=xs_type, - subdomains=(id,), - nuclides=[nuclide]) - - xsdatas.append(xsdata) # Add XSdatas to file mgxs_file.add_xsdatas(xsdatas) @@ -1029,3 +918,68 @@ class Library(object): if return_names: return mat_names + + def check_library_for_openmc_mgxs(self): + """This routine will check the MGXS Types within the provided + Library to ensure the data types provided can be used to create + a MGXS Library for OpenMC's Multi-Group mode via the + `Library.write_mg_library` method. + The rules to check include: + - Fission is not required as a fixed source problem could be + the target. + - Absorption is required. + - A nu-scatter matrix is required. + - Having both nu-scatter (of any order) and scatter + (at least isotropic) matrices is preferred + - If only nu-scatter, need total (not transport), to + be used in adjusting absorption + (i.e., reduced_abs = tot - nuscatt) + - Either total or transport should be present. + - Both can be available if one wants, but we should + use whatever corresponds to Library.correction (if P0: transport) + + Raises + ------ + ValueError + When the Library object is initialized with insufficient types of + cross sections for the Library. + + See also + -------- + Library.write_mg_library(...) + + """ + + error_flag = False + # Ensure absorption is present + if 'absorption' not in self.mgxs_types: + error_flag = True + msg = 'Absorption MGXS type is required but not provided.' + warn(msg) + # Ensure nu-scattering matrix is required + if 'nu-scatter matrix' not in self.mgxs_types: + error_flag = True + msg = 'Nu-Scatter Matrix MGXS type is required but not provided.' + warn(msg) + else: + # Ok, now see the status of scatter + if 'scatter matrix' not in self.mgxs_types: + # We dont have both nu-scatter and scatter, therefore + # we need total, and not transport. + if 'total' not in self.mgxs_types: + error_flag = True + msg = 'Total MGXS type is required if a ' \ + 'scattering matrix is not provided.' + warn(msg) + # Total or transport can be present, but if using + # self.correction=="P0", then we should use transport. + if (((self.correction is "P0") and + ('transport' not in self.mgxs_types))): + error_flag = True + msg = 'Transport MGXS type is required since a "P0" correction ' \ + 'is applied, but a Transport MGXS is not provided.' + warn(msg) + + if error_flag: + msg = "Invalid MGXS configuration encountered." + raise ValueError(msg) diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index ba9ba75b05..f9353f9cc7 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -122,12 +122,23 @@ class XSdata(object): Legendre polynomial form). Dict contains two keys: 'enable' and 'num_points'. 'enable' is a boolean and 'num_points' is the number of points to use, if 'enable' is True. + representation : {'isotropic', 'angle'} + Method used in generating the MGXS (isotropic or angle-dependent flux + weighting). num_azimuthal : int Number of equal width angular bins that the azimuthal angular domain is subdivided into. This only applies when ``representation`` is "angle". num_polar : int Number of equal width angular bins that the polar angular domain is subdivided into. This only applies when ``representation`` is "angle". + vector_shape : iterable of int + Dimensionality of vector multi-group cross sections (e.g., the total + cross section). The return result depends on the value of + ``representation``. + matrix_shape : iterable of int + Dimensionality of matrix multi-group cross sections (e.g., the + scattering matrix cross section). The return result depends on the + value of ``representation``. total : numpy.ndarray Group-wise total cross section ordered by increasing group index (i.e., fast to thermal). If ``representation`` is "isotropic", then the length @@ -173,7 +184,7 @@ class XSdata(object): azimuthal angles times the number of polar angles, with the inner-dimension being groups, intermediate-dimension being azimuthal angles and outer-dimension being the polar angles. - k_fission : numpy.ndarray + kappa_fission : numpy.ndarray Group-wise kappa-fission cross section ordered by increasing group index (i.e., fast to thermal). If ``representation`` is "isotropic", then the length of this list should equal the number of groups in the @@ -225,7 +236,7 @@ class XSdata(object): self._multiplicity = None self._fission = None self._nu_fission = None - self._k_fission = None + self._kappa_fission = None self._chi = None self._use_chi = None @@ -302,8 +313,8 @@ class XSdata(object): return self._nu_fission @property - def k_fission(self): - return self._k_fission + def kappa_fission(self): + return self._kappa_fission @property def chi(self): @@ -317,6 +328,24 @@ class XSdata(object): else: return self._order + @property + def vector_shape(self): + if self.representation is 'isotropic': + return (self.energy_groups.num_groups,) + elif self.representation is 'angle': + return (self.num_polar, self.num_azimuthal, + self.energy_groups.num_groups) + + @property + def matrix_shape(self): + if self.representation is 'isotropic': + return (self.energy_groups.num_groups, + self.energy_groups.num_groups) + elif self.representation is 'angle': + return (self.num_polar, self.num_azimuthal, + self.energy_groups.num_groups, + self.energy_groups.num_groups) + @name.setter def name(self, name): check_type('name for XSdata', name, basestring) @@ -326,6 +355,11 @@ class XSdata(object): def energy_groups(self, energy_groups): # Check validity of energy_groups check_type('energy_groups', energy_groups, openmc.mgxs.EnergyGroups) + + if energy_group.group_edges is None: + msg = 'Unable to assign an EnergyGroups object ' + \ + 'with uninitialized group edges' + raise ValueError(msg) self._energy_groups = energy_groups @representation.setter @@ -414,141 +448,196 @@ class XSdata(object): @total.setter def total(self, total): - if self._representation is 'isotropic': - shape = (self._energy_groups.num_groups,) - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups) + """This method sets the total cross section by performing a + deep-copy of the provided ndarray. + + Parameters + ---------- + total: ndarray + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ + # check we have a numpy list check_type('total', total, np.ndarray, expected_iter_type=Real) - if total.shape == shape: - self._total = np.copy(total) - else: - msg = 'Shape of provided total "{0}" does not match shape ' \ - 'required, "{1}"'.format(total.shape, shape) - raise ValueError(msg) + # Check the dimensions of the data + check_value('total shape', total.shape, self.vector_shape) + + self._total = np.copy(total) @absorption.setter def absorption(self, absorption): - if self._representation is 'isotropic': - shape = (self._energy_groups.num_groups,) - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups) + """This method sets the absorption cross section by performing a + deep-copy of the provided ndarray. + + Parameters + ---------- + absorption: ndarray + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ # check we have a numpy list check_type('absorption', absorption, np.ndarray, expected_iter_type=Real) - if absorption.shape == shape: - self._absorption = np.copy(absorption) - else: - msg = 'Shape of provided absorption "{0}" does not match shape ' \ - 'required, "{1}"'.format(absorption.shape, shape) - raise ValueError(msg) + # Check the dimensions of the data + check_value('absorption shape', absorption.shape, self.vector_shape) + + self._absorption = np.copy(absorption) @fission.setter def fission(self, fission): - if self._representation is 'isotropic': - shape = (self._energy_groups.num_groups,) - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups) - # check we have a numpy list - check_type('fission', fission, np.ndarray, expected_iter_type=Real) - if fission.shape == shape: - self._fission = np.copy(fission) - if np.sum(self._fission) > 0.0: - self._fissionable = True - else: - msg = 'Shape of provided fission "{0}" does not match shape ' \ - 'required, "{1}"'.format(fission.shape, shape) - raise ValueError(msg) + """This method sets the fission cross section by performing a + deep-copy of the provided ndarray. - @k_fission.setter - def k_fission(self, k_fission): - if self._representation is 'isotropic': - shape = (self._energy_groups.num_groups,) - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups) + Parameters + ---------- + fission: ndarray + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ # check we have a numpy list - check_type('k_fission', k_fission, np.ndarray, + check_type('fission', fission, np.ndarray, expected_iter_type=Real) - if k_fission.shape == shape: - self._k_fission = np.copy(k_fission) - if np.sum(self._k_fission) > 0.0: - self._fissionable = True - else: - msg = 'Shape of provided k_fission "{0}" does not match ' \ - 'shape required, "{1}"'.format(k_fission.shape, shape) - raise ValueError(msg) + # Check the dimensions of the data + check_value('fission shape', fission.shape, self.vector_shape) + + self._fission = np.copy(fission) + + if np.sum(self._fission) > 0.0: + self._fissionable = True + + @kappa_fission.setter + def kappa_fission(self, kappa_fission): + """This method sets the kappa_fission cross section by performing a + deep-copy of the provided ndarray. + + Parameters + ---------- + kappa_fission: ndarray + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ + # check we have a numpy list + check_type('kappa_fission', fission, np.ndarray, + expected_iter_type=Real) + # Check the dimensions of the data + check_value('kappa fission shape', kappa_fission.shape, + self.vector_shape) + + self._kappa_fission = np.copy(fission) + + if np.sum(self._kappa_fission) > 0.0: + self._fissionable = True @chi.setter def chi(self, chi): + """This method sets the chi cross section by performing a + deep-copy of the provided ndarray. + + Parameters + ---------- + chi: ndarray + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if self._use_chi is not None: if not self._use_chi: - msg = 'Providing chi when nu_fission already provided as matrix!' + msg = 'Providing chi when nu_fission already provided as a' \ + 'matrix' raise ValueError(msg) - if self._representation is 'isotropic': - shape = (self._energy_groups.num_groups,) - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups) # check we have a numpy list check_type('chi', chi, np.ndarray, expected_iter_type=Real) - if chi.shape == shape: - self._chi = np.copy(chi) - else: - msg = 'Shape of provided chi "{0}" does not match shape ' \ - 'required, "{1}"'.format(chi.shape, shape) - raise ValueError(msg) + # Check the dimensions of the data + check_value('chi shape', chi.shape, self.vector_shape) + + self._chi = np.copy(chi) + if self._use_chi is not None: self._use_chi = True @scatter.setter def scatter(self, scatter): - if self._representation is 'isotropic': - shape = (self.num_orders, self._energy_groups.num_groups, - self._energy_groups.num_groups) - max_depth = 3 - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, self.num_orders, - self._energy_groups.num_groups, - self._energy_groups.num_groups) - max_depth = 5 + """This method sets the scattering matrix cross sections + by performing a deep-copy of the provided ndarray. + + Parameters + ---------- + scatter : ndarrays + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ # check we have a numpy list - check_iterable_type('scatter', scatter, expected_type=Real, - max_depth=max_depth) - if scatter.shape == shape: - self._scatter = np.copy(scatter) - else: - msg = 'Shape of provided scatter "{0}" does not match shape ' \ - 'required, "{1}"'.format(scatter.shape, shape) - raise ValueError(msg) + check_type('scatter', scatter, np.ndarray, expected_iter_type=Real, + max_depth=len(scatter.shape)) + # Check the dimensions of the data + check_value('scatter shape', scatter.shape, self.matrix_shape) + + self._scatter = np.copy(scatter) @multiplicity.setter def multiplicity(self, multiplicity): - if self._representation is 'isotropic': - shape = (self._energy_groups.num_groups, - self._energy_groups.num_groups) - max_depth = 2 - elif self._representation is 'angle': - shape = (self._num_polar, self._num_azimuthal, - self._energy_groups.num_groups, - self._energy_groups.num_groups) - max_depth = 4 + """This method sets the scattering multiplicity matrix cross sections + by performing a deep-copy of the provided ndarray. + + Parameters + ---------- + multiplicity : ndarrays + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ # check we have a numpy list - check_iterable_type('multiplicity', multiplicity, expected_type=Real, - max_depth=max_depth) - if multiplicity.shape == shape: - self._multiplicity = np.copy(multiplicity) - else: - msg = 'Shape of provided multiplicity "{0}" does not match shape' \ - ' required, "{1}"'.format(multiplicity.shape, shape) - raise ValueError(msg) + check_type('multiplicity', multiplicity, np.ndarray, + expected_iter_type=Real, max_depth=len(multiplicity.shape)) + # Check the dimensions of the data + check_value('multiplicity shape', multiplicity.shape, + self.matrix_shape) + + self._multiplicity = np.copy(multiplicity) @nu_fission.setter def nu_fission(self, nu_fission): + """This method sets the nu_fission cross section by performing a + deep-copy of the provided ndarray. + + Parameters + ---------- + nu_fission: ndarray + Array of group-wise cross sections to apply + + Raises + ------ + ValueError + When invalid parameters are passed. + """ # 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. @@ -559,47 +648,54 @@ class XSdata(object): # 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 + # First, check we have a numpy list check_type('nu_fission', nu_fission, np.ndarray, - expected_iter_type=Real) + expected_iter_type=Real, max_depth=len(nu_fission.shape)) + + if self._use_chi is not None: + # Check the dimensions of the data + if self._use_chi: + check_value('nu_fission shape', nu_fission.shape, + self.vector_shape) + else: + check_value('nu_fission shape', nu_fission.shape, + self.matrix_shape) + else: + # Make sure the dimensions are at least right + check_value('nu_fission shape', nu_fission.shape, + (self.vector_shape, self.matrix_shape)) + # Then find out which one we have so we can set use_chi + if nu_fission.shape == self.vector_shape: + self._use_chi = True + else: + self._use_chi = False + self._nu_fission = np.copy(nu_fission) if np.sum(self._nu_fission) > 0.0: self._fissionable = True - def set_total(self, total, subdomain, nuclide='sum', xs_type='macro'): + def set_total_mgxs(self, total, nuclide='total', xs_type='macro'): + """This method allows for an openmc.mgxs.TotalXS or + openmc.mgxs.TransportXS to be used to set the total cross section + for this XSdata object. + + Parameters + ---------- + total: {openmc.mgxs.TotalXS, openmc.mgxs.TransportXS} + MGXS Object containing the total or transport cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if not isinstance(total, (openmc.mgxs.TotalXS, openmc.mgxs.TransportXS)): msg = 'Method must be passed an openmc.mgxs.TotalXS or ' \ @@ -607,59 +703,119 @@ class XSdata(object): raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != total.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', total.energy_groups, [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', total.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._total = total.get_xs(subdomain=subdomains, nuclides=nuclide, - xs_type=xs_type) + self._total = total.get_xs(nuclides=nuclide, xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - def set_absorption(self, absorption, subdomain, nuclide='sum', - xs_type='macro'): + def set_absorption_mgxs(self, absorption, nuclide='total', xs_type='macro'): + """This method allows for an openmc.mgxs.AbsorptionXS + to be used to set the absorption cross section for this XSdata object. + + Parameters + ---------- + absorption: openmc.mgxs.AbsorptionXS + MGXS Object containing the absorption cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if not isinstance(absorption, openmc.mgxs.AbsorptionXS): msg = 'Method must be passed an openmc.mgxs.AbsorptionXS' raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != absorption.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', absorption.energy_groups, + [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', absorption.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._absorption = absorption.get_xs(subdomains=subdomain, - nuclides=nuclide, + self._absorption = absorption.get_xs(nuclides=nuclide, xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - def set_fission(self, fission, subdomain, nuclide='sum', xs_type='macro'): + def set_fission_mgxs(self, fission, nuclide='total', xs_type='macro'): + """This method allows for an openmc.mgxs.FissionXS + to be used to set the fission cross section for this XSdata object. + + Parameters + ---------- + fission: openmc.mgxs.FissionXS + MGXS Object containing the fission cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if not isinstance(fission, openmc.mgxs.FissionXS): msg = 'Method must be passed an openmc.mgxs.FissionXS' raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != fission.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', fission.energy_groups, + [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', fission.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._fission = fission.get_xs(subdomains=subdomain, - nuclides=nuclide, + self._fission = fission.get_xs(nuclides=nuclide, xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - def set_nu_fission(self, nu_fission, subdomain, nuclide='sum', - xs_type='macro'): + def set_nu_fission_mgxs(self, nu_fission, nuclide='total', xs_type='macro'): + """This method allows for an openmc.mgxs.NuFissionXS + to be used to set the nu-fission cross section for this XSdata object. + + Parameters + ---------- + nu_fission: openmc.mgxs.NuFissionXS + MGXS Object containing the nu-fission cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ # 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. @@ -668,14 +824,15 @@ class XSdata(object): raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != nu_fission.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', nu_fission.energy_groups, + [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', nu_fission.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._nu_fission = nu_fission.get_xs(subdomains=subdomain, - nuclides=nuclide, + self._nu_fission = nu_fission.get_xs(nuclides=nuclide, xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' @@ -686,27 +843,68 @@ class XSdata(object): if np.sum(self._nu_fission) > 0.0: self._fissionable = True - def set_k_fission(self, k_fission, subdomain, nuclide='sum', - xs_type='macro'): + def set_kappa_fission_mgxs(self, k_fission, nuclide='total', + xs_type='macro'): + """This method allows for an openmc.mgxs.KappaFissionXS + to be used to set the kappa-fission cross section for this XSdata + object. + + Parameters + ---------- + kappa_fission: openmc.mgxs.KappaFissionXS + MGXS Object containing the kappa-fission cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if not isinstance(k_fission, openmc.mgxs.KappaFissionXS): msg = 'Method must be passed an openmc.mgxs.KappaFissionXS' raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != k_fission.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', k_fission.energy_groups, + [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', k_fission.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._k_fission = k_fission.get_xs(subdomains=subdomain, - nuclides=nuclide, - xs_type=xs_type) + self._kappa_fission = k_fission.get_xs(nuclides=nuclide, + xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - def set_chi(self, chi, subdomain, nuclide='sum', xs_type='macro'): + def set_chi_mgxs(self, chi, nuclide='total', xs_type='macro'): + """This method allows for an openmc.mgxs.Chi + to be used to set chi for this XSdata object. + + Parameters + ---------- + chi: openmc.mgxs.Chi + MGXS Object containing chi for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if self._use_chi is not None: if not self._use_chi: msg = 'Providing chi when nu_fission already provided as a ' \ @@ -718,14 +916,14 @@ class XSdata(object): raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != chi.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', chi.energy_groups, [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', chi.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._chi = chi.get_xs(subdomains=subdomain, - nuclides=nuclide, + self._chi = chi.get_xs(nuclides=nuclide, xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' @@ -734,55 +932,102 @@ class XSdata(object): if self._use_chi is not None: self._use_chi = True - def set_scatter(self, scatter, subdomain, nuclide='sum', xs_type='macro'): + def set_scatter_mgxs(self, scatter, nuclide='total', xs_type='macro'): + """This method allows for an openmc.mgxs.ScatterMatrixXS + to be used to set the scatter matrix cross section for this XSdata + object. + + Parameters + ---------- + scatter: openmc.mgxs.ScatterMatrixXS + MGXS Object containing the scatter matrix cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ if not isinstance(scatter, openmc.mgxs.ScatterMatrixXS): msg = 'Method must be passed an openmc.mgxs.ScatterMatrixXS' raise TypeError(msg) # Make sure passed MGXS object contains correct group structure - if self.energy_groups != scatter.energy_groups: - msg = 'Group structure of provided data does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + check_value('energy_groups', scatter.energy_groups, + [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', scatter.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - self._scatter = scatter.get_xs(subdomains=subdomain, - nuclides=nuclide, + self._scatter = scatter.get_xs(nuclides=nuclide, xs_type=xs_type) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) - def set_multiplicity(self, multiplicity, scatter, subdomain, - nuclide='sum', xs_type='macro'): - if not isinstance(multiplicity, openmc.mgxs.ScatterMatrixXS): + def set_multiplicity_mgxs(self, nuscatter, scatter, nuclide='total', + xs_type='macro'): + """This method allows for an openmc.mgxs.NuScatterMatrixXS and + openmc.mgxs.ScatterMatrixXS to be used to set the scattering + multiplicity for this XSdata object. + + Parameters + ---------- + nuscatter: openmc.mgxs.NuScatterMatrixXS + MGXS Object containing the nu-scattering matrix cross section + for the domain of interest. + scatter: openmc.mgxs.ScatterMatrixXS + MGXS Object containing the scattering matrix cross section + for the domain of interest. + nuclide : str + Individual nuclide (or 'total' if obtaining material-wise data) + to gather data for. Defaults to 'total'. + xs_type: {'macro', 'micro'} + Provide the macro or micro cross section in units of cm^-1 or + barns. Defaults to 'macro'. + + Raises + ------ + ValueError + When invalid parameters are passed. + """ + if not isinstance(nuscatter, openmc.mgxs.NuScatterMatrixXS): msg = 'Method must be passed an openmc.mgxs.ScatterMatrixXS' raise TypeError(msg) if not isinstance(scatter, openmc.mgxs.ScatterMatrixXS): msg = 'Method must be passed an openmc.mgxs.ScatterMatrixXS' raise TypeError(msg) - # Make sure passed MGXS objects contain correct group structure - if self.energy_groups != multiplicity.energy_groups: - msg = 'Group structure of "multiplicity" does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) - if self.energy_groups != scatter.energy_groups: - msg = 'Group structure of "scatter" does not match' \ - ' group structure of XSdata object' - raise ValueError(msg) + # Make sure passed MGXS object contains correct group structure + check_value('energy_groups', nuscatter.energy_groups, + [self.energy_groups]) + check_value('energy_groups', scatter.energy_groups, + [self.energy_groups]) + + # Make sure passed MGXS object has correct domain type + check_value('domain_type', nuscatter.domain_type, + ['universe', 'cell', 'material']) + check_value('domain_type', scatter.domain_type, + ['universe', 'cell', 'material']) if self._representation is 'isotropic': - nuscatt = multiplicity.get_xs(subdomains=subdomain, - nuclides=nuclide, - xs_type=xs_type) - scatt = scatter.get_xs(subdomains=subdomain, - nuclides=nuclide, + nuscatt = nuscatter.get_xs(nuclides=nuclide, + xs_type=xs_type) + scatt = scatter.get_xs(nuclides=nuclide, xs_type=xs_type) self._multiplicity = np.divide(nuscatt, scatt) elif self._representation is 'angle': msg = 'Angular-Dependent MGXS have not yet been implemented' raise ValueError(msg) + self._multiplicity = np.nan_to_num(self._multiplicity) def _get_xsdata_xml(self): element = ET.Element('xsdata') @@ -858,9 +1103,9 @@ class XSdata(object): subelement = ET.SubElement(element, 'fission') subelement.text = ndarray_to_string(self._fission) - if self._k_fission is not None: + if self._kappa_fission is not None: subelement = ET.SubElement(element, 'k_fission') - subelement.text = ndarray_to_string(self._k_fission) + subelement.text = ndarray_to_string(self._kappa_fission) if self._nu_fission is not None: subelement = ET.SubElement(element, 'nu_fission') @@ -947,10 +1192,8 @@ class MGXSLibrary(object): """ - if not isinstance(xsdatas, Iterable): - msg = 'Unable to create OpenMC xsdatas.xml file from "{0}" which' \ - ' is not iterable'.format(xsdatas) - raise ValueError(msg) + # Check we have an iterable of XSdatas + check_iterable_type('xsdatas', xsdatas, XSdata) for xsdata in xsdatas: self.add_xsdata(xsdata)