diff --git a/openmc/cross.py b/openmc/cross.py index 975d5cb363..9b8a1d240d 100644 --- a/openmc/cross.py +++ b/openmc/cross.py @@ -405,7 +405,7 @@ class CrossFilter(object): This method constructs a Pandas DataFrame object for the CrossFilter with columns annotated by filter bin information. This is a helper - method for the Tally.get_pandas_dataframe(...) routine. This method + method for the Tally.get_pandas_dataframe(...) method. This method recursively builds and concatenates Pandas DataFrames for the left and right filters and crossfilters. diff --git a/openmc/filter.py b/openmc/filter.py index 3bc0c866e5..eaa30d30e5 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -466,7 +466,7 @@ class Filter(object): This method constructs a Pandas DataFrame object for the filter with columns annotated by filter bin information. This is a helper method - for the Tally.get_pandas_dataframe(...) routine. + for the Tally.get_pandas_dataframe(...) method. This capability has been tested for Pandas >=0.13.1. However, it is recommended to use v0.16 or newer versions of Pandas since this method diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index e6ea7564cb..171718bc8d 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -17,12 +17,14 @@ if sys.version_info[0] >= 3: # Supported domain types +# TODO: Implement Mesh domains DOMAIN_TYPES = ['cell', 'distribcell', 'universe', 'material'] -# Supported domain objects +# Supported domain classes +# TODO: Implement Mesh domains DOMAINS = [openmc.Cell, openmc.Universe, openmc.Material] @@ -48,7 +50,7 @@ class MultiGroupXS(object): If true, computes multi-group cross sections for each nuclide in domain name : str, optional Name of the multi-group cross section. Used as a label to identify - tallies in OpenMC tallies.xml file. + tallies in OpenMC 'tallies.xml' file. Attributes ---------- @@ -92,6 +94,7 @@ class MultiGroupXS(object): self.name = name self.by_nuclide = by_nuclide + if domain_type is not None: self.domain_type = domain_type if domain is not None: @@ -213,7 +216,7 @@ class MultiGroupXS(object): Returns ------- - nuclides : list of str + list of str A list of the string names for each nuclide in the problem domain (e.g., ['U-235', 'U-238', 'O-16']) @@ -241,7 +244,7 @@ class MultiGroupXS(object): Returns ------- - density : Real + Real The atomic number density (atom/b-cm) for the nuclide of interest Raises @@ -279,7 +282,7 @@ class MultiGroupXS(object): Returns ------- - densities : ndarray of float + ndarray of Real An array of the atomic number densities (atom/b-cm) for each of the nuclides in the problem domain @@ -300,14 +303,14 @@ class MultiGroupXS(object): for nuclide in nuclides: densities[0] += self.get_nuclide_density(nuclide) - # Sum the atomic number densities for all nuclides + # Tabulate the atomic number densities for all nuclides elif nuclides == 'all': nuclides = self.get_all_nuclides() densities = np.zeros(self.num_nuclides, dtype=np.float) for i, nuclide in enumerate(nuclides): densities[i] += self.get_nuclide_density(nuclide) - # Store each nuclide's atomic number density in an array + # Tabulate the atomic number densities for each specified nuclide else: densities = np.zeros(len(nuclides), dtype=np.float) for i, nuclide in enumerate(nuclides): @@ -321,6 +324,8 @@ class MultiGroupXS(object): This is a helper method for MultiGroupXS subclasses to create tallies for input file generation. The tallies are stored in the tallies dict. + This method is called by each subclass' create_tallies(...) method + which define the parameters given to this parent class method. Parameters ---------- @@ -343,8 +348,8 @@ class MultiGroupXS(object): # Create a domain Filter object domain_filter = openmc.Filter(self.domain_type, self.domain.id) - domain_filter.num_bins = 1 + # Create each Tally needed to compute the multi group cross section for score, key, filters in zip(scores, keys, all_filters): self.tallies[key] = openmc.Tally(name=self.name) self.tallies[key].add_score(score) @@ -355,7 +360,7 @@ class MultiGroupXS(object): for filter in filters: self.tallies[key].add_filter(filter) - # If this is a by nuclide cross-section, add all nuclides to Tally + # If this is a by-nuclide cross-section, add all nuclides to Tally if self.by_nuclide and score != 'flux': all_nuclides = self.domain.get_all_nuclides() for nuclide in all_nuclides: @@ -366,7 +371,18 @@ class MultiGroupXS(object): @abc.abstractmethod def compute_xs(self): """Performs generic cleanup after a subclass' uses tally arithmetic to - compute a multi-group cross section as a derived tally.""" + compute a multi-group cross section as a derived tally. + + This method replaces CrossNuclides generated by tally arithmetic with + the original Nuclide objects in the xs_tally instance attribute. The + simple Nuclides allow for cleaner output through Pandas DataFrames as + well as simpler data access through the get_xs(...) class method. + + In addition, this routine resets NaNs in the multi group cross section + array to 0.0. This may be needed occur if no events were scored in + certain tally bins, which will lead to a divide-by-zero situation. + + """ # If computing xs for each nuclide, replace CrossNuclides with originals if self.by_nuclide: @@ -393,6 +409,12 @@ class MultiGroupXS(object): statepoint : openmc.StatePoint An OpenMC StatePoint object with tally data + Raises + ------ + ValueError + When this method is called with a statepoint that has not been + linked with a summary object. + """ cv.check_type('statepoint', statepoint, openmc.statepoint.StatePoint) @@ -419,21 +441,13 @@ class MultiGroupXS(object): # Create Tallies to search for in StatePoint self.create_tallies() - if self.domain_type == 'distribcell': - filters = [] - filter_bins = [] - else: - filters = [self.domain_type] - filter_bins = [(self.domain.id,)] - # Find, slice and store Tallies from StatePoint # The tally slicing is needed if tally merging was used for tally_type, tally in self.tallies.items(): sp_tally = statepoint.get_tally(tally.scores, tally.filters, tally.nuclides, estimator=tally.estimator) - sp_tally = sp_tally.get_slice(tally.scores, filters, - filter_bins, tally.nuclides) + sp_tally = sp_tally.get_slice(tally.scores, nuclides=tally.nuclides) self.tallies[tally_type] = sp_tally def get_xs(self, groups='all', subdomains='all', nuclides='all', @@ -465,7 +479,7 @@ class MultiGroupXS(object): Returns ------- - xs : ndarray + ndarray A NumPy array of the multi-group cross section indexed in the order each group, subdomain and nuclide is listed in the parameters. @@ -502,8 +516,6 @@ class MultiGroupXS(object): filter_bins.append((self.energy_groups.get_group_bounds(group),)) # Construct a collection of the nuclides to retrieve from the xs tally - # NOTE: We must not override the "nuclides" parameter since it is used - # to retrieve atomic number densities for micro xs if self.by_nuclide: if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']: query_nuclides = self.get_all_nuclides() @@ -512,14 +524,14 @@ class MultiGroupXS(object): else: query_nuclides = ['total'] - # Use tally summation if user requested the sum for all nuclides + # If user requested the sum for all nuclides, use tally summation if nuclides == 'sum' or nuclides == ['sum']: xs_tally = self.xs_tally.summation(nuclides=query_nuclides) xs = xs_tally.get_values(filters=filters, filter_bins=filter_bins, value=value) else: xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins, - nuclides=query_nuclides, value=value) + nuclides=query_nuclides, value=value) # Divide by atom number densities for microscopic cross sections if xs_type == 'micro': @@ -533,11 +545,12 @@ class MultiGroupXS(object): # Reverse data if user requested increasing energy groups since # tally data is stored in order of increasing energies if order_groups == 'increasing': - # Reshape tally data array with separate axes for domain and energy if groups == 'all': num_groups = self.num_groups else: num_groups = len(groups) + + # Reshape tally data array with separate axes for domain and energy num_subdomains = xs.shape[0] / num_groups new_shape = (num_subdomains, num_groups) + xs.shape[1:] xs = np.reshape(xs, new_shape) @@ -583,7 +596,7 @@ class MultiGroupXS(object): condensed_xs = copy.deepcopy(self) condensed_xs.energy_groups = coarse_groups - # Build indices to sum up over + # Build energy indices to sum across energy_indices = [] for group in range(coarse_groups.num_groups, 0, -1): low, high = coarse_groups.get_group_bounds(group) @@ -629,9 +642,9 @@ class MultiGroupXS(object): def get_subdomain_avg_xs(self, subdomains='all'): """Construct a subdomain-averaged version of this cross section. - This is primarily useful for averaging across distribcell instances. - This routine performs spatial homogenization to compute the scalar - flux-weighted average cross section across the subdomains. + This method is useful for averaging cross sections across distribcell + instances. The method performs spatial homogenization to compute the + scalar flux-weighted average cross section across the subdomains. Parameters ---------- @@ -707,7 +720,7 @@ class MultiGroupXS(object): return avg_xs def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): - """Prints a string representation for the multi-group cross section. + """Print a string representation for the multi-group cross section. Parameters ---------- @@ -736,7 +749,7 @@ class MultiGroupXS(object): if self.by_nuclide: if nuclides == 'all': nuclides = self.get_all_nuclides() - if nuclides == 'sum': + elif nuclides == 'sum': nuclides = ['sum'] else: cv.check_iterable_type('nuclides', nuclides, basestring) @@ -784,9 +797,9 @@ class MultiGroupXS(object): average = self.get_xs([group], [subdomain], [nuclide], xs_type=xs_type, value='mean') rel_err = self.get_xs([group], [subdomain], [nuclide], - xs_type=xs_type, value='rel_err')*100 - average = np.nan_to_num(average.flatten())[0] - rel_err = np.nan_to_num(rel_err.flatten())[0] + xs_type=xs_type, value='rel_err') + average = average.flatten()[0] + rel_err = rel_err.flatten()[0] * 100. string += '{:.2e} +/- {:1.2e}%'.format(average, rel_err) string += '\n' string += '\n' @@ -796,13 +809,13 @@ class MultiGroupXS(object): def build_hdf5_store(self, filename='mgxs', directory='mgxs', xs_type='macro', append=True): - """Export the multi-group cross section data into an HDF5 binary file. + """Export the multi-group cross section data to an HDF5 binary file. - This routine constructs an HDF5 file which stores the multi-group - cross section data. The data is be stored in a hierarchy of HDF5 groups + This method constructs an HDF5 file which stores the multi-group + cross section data. The data is stored in a hierarchy of HDF5 groups from the domain type, domain id, subdomain id (for distribcell domains), - and cross section type. Two datasets for the mean and standard deviation - are stored for each subddomain entry in the HDF5 file. + nuclides and cross section type. Two datasets for the mean and standard + deviation are stored for each subdomain entry in the HDF5 file. NOTE: This requires the h5py Python package. @@ -892,9 +905,10 @@ class MultiGroupXS(object): for j, nuclide in enumerate(nuclides): if nuclide != 'sum': + density = densities[j] nuclide_group = rxn_group.require_group(nuclide) nuclide_group.require_dataset('density', dtype=np.float64, - data=[densities[j]], shape=(1,)) + data=[density], shape=(1,)) else: nuclide_group = rxn_group @@ -919,9 +933,9 @@ class MultiGroupXS(object): format='csv', groups='all', xs_type='macro'): """Export the multi-group cross section data to a file. - This routine leverages the functionality in the Pandas library to - export the multi-group cross section data in a variety of output - file formats for storage and/or post-processing. + This method leverages the functionality in the Pandas library to export + the multi-group cross section data in a variety of output file formats + for storage and/or post-processing. Parameters ---------- @@ -990,7 +1004,7 @@ class MultiGroupXS(object): xs_type='macro', summary=None): """Build a Pandas DataFrame for the MultiGroupXS data. - This routine leverages the Tally.get_pandas_dataframe(...) routine, but + This method leverages the Tally.get_pandas_dataframe(...) method, but renames the columns with terminology appropriate for cross section data. Parameters @@ -1799,7 +1813,7 @@ class Chi(MultiGroupXS): nu_fission_out = nu_fission_out.summation(nuclides=nuclides) # Compute chi and store it as the xs_tally attribute so we can use - # the generic get_xs routine + # the generic get_xs(...) method xs_tally = nu_fission_out / nu_fission_in xs = xs_tally.get_values(filters=filters, filter_bins=filter_bins, value=value) @@ -1848,7 +1862,7 @@ class Chi(MultiGroupXS): xs_type='macro', summary=None): """Build a Pandas DataFrame for the MultiGroupXS data. - This routine leverages the Tally.get_pandas_dataframe(...) routine, but + This method leverages the Tally.get_pandas_dataframe(...) method, but renames the columns with terminology appropriate for cross section data. Parameters @@ -1883,12 +1897,12 @@ class Chi(MultiGroupXS): """ - # Build the dataframe using the parent class routine + # Build the dataframe using the parent class method df = super(Chi, self).get_pandas_dataframe(groups, nuclides, xs_type, summary) # If user requested micro cross sections, multiply by the atom - # densities to cancel out division made by the parent class routine + # densities to cancel out division made by the parent class method if xs_type == 'micro': if self.by_nuclide: densities = self.get_nuclide_densities(nuclides) diff --git a/openmc/tallies.py b/openmc/tallies.py index 3e0d70409d..dc58c2f6d7 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -843,8 +843,8 @@ class Tally(object): def get_filter_indices(self, filters=[], filter_bins=[]): """Get indices into the filter axis of this tally's data arrays. - This is a helper routine for the Tally.get_values(...) routine to - extract tally data. This routine returns the indices into the filter + This is a helper method for the Tally.get_values(...) method to + extract tally data. This method returns the indices into the filter axis of the tally's data array (axis=0) for particular combinations of filters and their corresponding bins. @@ -937,8 +937,8 @@ class Tally(object): def get_nuclide_indices(self, nuclides): """Get indices into the nuclide axis of this tally's data arrays. - This is a helper routine for the Tally.get_values(...) routine to - extract tally data. This routine returns the indices into the nuclide + This is a helper method for the Tally.get_values(...) method to + extract tally data. This method returns the indices into the nuclide axis of the tally's data array (axis=1) for one or more nuclides. Parameters @@ -971,8 +971,8 @@ class Tally(object): def get_score_indices(self, scores): """Get indices into the score axis of this tally's data arrays. - This is a helper routine for the Tally.get_values(...) routine to - extract tally data. This routine returns the indices into the score + This is a helper method for the Tally.get_values(...) method to + extract tally data. This method returns the indices into the score axis of the tally's data array (axis=2) for one or more scores. Parameters @@ -1227,7 +1227,7 @@ class Tally(object): The tally data in OpenMC is stored as a 3D array with the dimensions corresponding to filters, nuclides and scores. As a result, tally data can be opaque for a user to directly index (i.e., without use of the - Tally.get_values(...) routine) since one must know how to properly use + Tally.get_values(...) method) since one must know how to properly use the number of bins and strides for each filter to index into the first (filter) dimension. @@ -1235,7 +1235,7 @@ class Tally(object): unique dimensions corresponding to each tally filter. For example, suppose this tally has arrays of data with shape (8,5,5) corresponding to two filters (2 and 4 bins, respectively), five nuclides and five - scores. This routine will return a version of the data array with the + scores. This method will return a version of the data array with the with a new shape of (2,4,5,5) such that the first two dimensions correspond directly to the two filters with two and four bins. @@ -1293,7 +1293,7 @@ class Tally(object): # Ensure that StatePoint.read_results() was called first if self._sum is None or self._sum_sq is None and not self.derived: msg = 'The Tally ID="{0}" has no data to export. Call the ' \ - 'StatePoint.read_results() routine before using ' \ + 'StatePoint.read_results() method before using ' \ 'Tally.export_results(...)'.format(self.id) raise KeyError(msg) @@ -1688,8 +1688,8 @@ class Tally(object): def swap_filters(self, filter1, filter2): """Reverse the ordering of two filters in this tally - This is a helper routine for tally arithmetic which helps align the data - in two tallies with shared filters. This routine copies this tally and + This is a helper method for tally arithmetic which helps align the data + in two tallies with shared filters. This method copies this tally and reverses the order of the two filters. Parameters @@ -2471,7 +2471,7 @@ class Tally(object): def diagonalize_filter(self, new_filter): """Diagonalize the tally data array along a new axis of filter bins. - This is a helper method for the tally arithmetic methods. This routine + This is a helper method for the tally arithmetic methods. This method adds the new filter to a derived tally constructed copied from this one. The data in the derived tally arrays is "diagonalized" along the bins in the new filter. This functionality is used by the openmc.mgxs module; to