diff --git a/openmc/cross.py b/openmc/cross.py index fa1ce6e63..e2281142c 100644 --- a/openmc/cross.py +++ b/openmc/cross.py @@ -1,7 +1,7 @@ import sys from openmc import Filter, Nuclide -from openmc.constants import FILTER_TYPES +from openmc.filter import _FILTER_TYPES import openmc.checkvalue as cv @@ -345,7 +345,7 @@ class CrossFilter(object): @type.setter def type(self, filter_type): - if filter_type not in FILTER_TYPES.values(): + if filter_type not in _FILTER_TYPES.values(): msg = 'Unable to set Filter type to "{0}" since it is not one ' \ 'of the supported types'.format(type) raise ValueError(msg) diff --git a/openmc/filter.py b/openmc/filter.py index b8865a184..2f09d93f2 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -7,7 +7,6 @@ import numpy as np from openmc import Mesh from openmc.summary import Summary -from openmc.constants import * import openmc.checkvalue as cv diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 91e1635fe..f4c569e16 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -190,6 +190,96 @@ class MultiGroupXS(object): self._energy_groups = energy_groups self._num_groups = energy_groups.num_groups + def get_all_nuclides(self): + """Get all nuclides in the cross section's spatial domain. + + Returns + ------- + nuclides : list of str + A list of the string names for each nuclide in the problem domain + (e.g., ['U-235', 'U-238', 'O-16']) + + Raises + ------ + ValueError + When this method is called before the spatial domain has been set. + + """ + + if self.domain is None: + raise ValueError('Unable to get all nuclides without a domain') + + nuclides = self.domain.get_all_nuclides() + return nuclides.keys() + + def get_nuclide_density(self, nuclide): + """Get the atomic number density for a nuclide in the cross section's + spatial domain. + + nuclide : str + A nuclide name string (e.g., 'U-235') + + Returns + ------- + density : Real + The atomic number density for the nuclide of interest + + Raises + ------ + ValueError + When the density is requested for a nuclide which is not found in + the spatial domain. + + """ + + cv.check_type('nuclide', nuclide, basestring) + + # Get list of all nuclides in the spatial domain + nuclides = self.domain.get_all_nuclides() + + if nuclide not in nuclides: + msg = 'Unable to get density for nuclide "{0}" which is not in ' \ + '{1} "{2}"'.format(nuclide, self.domain_type, self.domain.id) + ValueError(msg) + + density = nuclides[nuclide][1] + return density + + def get_nuclide_densities(self, nuclides='all'): + """Get all atomic number densities in the cross section's spatial domain. + + nuclides : Iterable of str or 'all' + A list of nuclide name strings + (e.g., ['U-235', 'U-238']; default is 'all') + + Returns + ------- + densities : ndarray of float + The atomic number densities corresponding to each of the nuclides + in the problem domain + + Raises + ------ + ValueError + When this method is called before the spatial domain has been set. + + """ + + if self.domain is None: + raise ValueError('Unable to get nuclide densities without a domain') + + # If the user requested the densities for all nuclides, get a list of + # all of the nuclide name strings + if nuclides == 'all': + nuclides =self.domain.get_all_nuclides() + + # Loop over each nuclide and find and store its atomic number density + densities = np.zeros(len(nuclides), dtype=np.float) + for i, nuclide in enumerate(nuclides): + densities[i] = self.get_nuclide_density(nuclide) + + return densities + @abc.abstractmethod def create_tallies(self, scores, all_filters, keys, estimator): """Instantiates tallies needed to compute the multi-group cross section. @@ -243,7 +333,15 @@ class MultiGroupXS(object): def compute_xs(self): """Computes multi-group cross sections using OpenMC tally arithmetic.""" - return + # If a microscopic cross-section, replace CrossNuclides with originals + if self.xs_type == 'micro': + self.xs_tally._nuclides = [] + nuclides = self.domain.get_all_nuclides() + for nuclide in nuclides: + self.xs_tally.add_nuclide(nuclide) + + self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) + self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) def load_from_statepoint(self, statepoint): """Extracts tallies in an OpenMC StatePoint with the data needed to @@ -321,8 +419,6 @@ class MultiGroupXS(object): """ - # TODO: Multiply by densities - if self.xs_tally is None: msg = 'Unable to get cross section since it has not been computed' raise ValueError(msg) @@ -355,6 +451,13 @@ class MultiGroupXS(object): # Query the multi-group cross section tally for the data xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins, nuclides=nuclides, value=value) + + # If user requested microscopic cross sections from an object with + # microscopic cross sections, divide by atom number densities + if self.xs_type == 'micro' and xs_type == 'micro': + densities = self.get_nuclide_densities(nuclides) + if value == 'mean' or value == 'std_dev': + xs /= densities[np.newaxis, :, np.newaxis] return xs def get_condensed_xs(self, coarse_groups): @@ -512,7 +615,7 @@ class MultiGroupXS(object): return avg_xs - def print_xs(self, subdomains='all', nuclides='all'): + def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): """Prints a string representation for the multi-group cross section. Parameters @@ -523,6 +626,9 @@ class MultiGroupXS(object): nuclides : Iterable of str or 'all' The nuclides of the cross-sections to include in the report + xs_type: {'macro' or 'micro'} + Return the macro or micro cross section in units of cm^-1 or barns + """ if subdomains != 'all': @@ -561,11 +667,14 @@ class MultiGroupXS(object): # Loop over all Nuclides for nuclide in nuclides: - # Build header for cross section type based on the nuclide - if nuclide == 'total': + # Build header for nuclide type + if xs_type != 'total': + string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + + # Build header for cross section type + if xs_type == 'macro': string += '{0: <16}\n'.format('\tCross Sections [cm^-1]:') else: - string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) string += '{0: <16}\n'.format('\tCross Sections [barns]:') template = '{0: <12}Group {1} [{2: <10} - {3: <10}MeV]:\t' @@ -575,9 +684,9 @@ class MultiGroupXS(object): bounds = self.energy_groups.get_group_bounds(group) string += template.format('', group, bounds[0], bounds[1]) average = self.get_xs([group], [subdomain], - [nuclide], 'mean') + [nuclide], xs_type, 'mean') rel_err = self.get_xs([group], [subdomain], - [nuclide], 'rel_err') * 100. + [nuclide], xs_type, 'rel_err') * 100 average = np.nan_to_num(average.flatten())[0] rel_err = np.nan_to_num(rel_err.flatten())[0] string += '{:.2e} +/- {:1.2e}%'.format(average, rel_err) @@ -646,9 +755,9 @@ class MultiGroupXS(object): if self.xs_type == 'micro': nuclides = self.domain.get_all_nuclides() - densities = [] - for nuclide in nuclides: - densities.append(nuclides[nuclide][1]) + densities = np.zeros(len(nuclides), dtype=np.float) + for i, nuclide in enumerate(nuclides): + densities[i] = nuclides[nuclide][1] else: nuclides = ['total'] @@ -887,8 +996,7 @@ class TotalXS(MultiGroupXS): tally arithmetic.""" self._xs_tally = self.tallies['total'] / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(TotalXS, self).compute_xs() class TransportXS(MultiGroupXS): @@ -927,8 +1035,7 @@ class TransportXS(MultiGroupXS): self._xs_tally = self.tallies['total'] - self.tallies['scatter-P1'] self._xs_tally /= self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(TotalXS, self).compute_xs() class AbsorptionXS(MultiGroupXS): @@ -959,8 +1066,7 @@ class AbsorptionXS(MultiGroupXS): tally arithmetic.""" self._xs_tally = self.tallies['absorption'] / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(AbsorptionXS, self).compute_xs() class CaptureXS(MultiGroupXS): @@ -992,8 +1098,7 @@ class CaptureXS(MultiGroupXS): self._xs_tally = self.tallies['absorption'] - self.tallies['fission'] self._xs_tally /= self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(CaptureXS, self).compute_xs() class FissionXS(MultiGroupXS): @@ -1024,8 +1129,7 @@ class FissionXS(MultiGroupXS): tally arithmetic.""" self._xs_tally = self.tallies['fission'] / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(FissionXS, self).compute_xs() class NuFissionXS(MultiGroupXS): @@ -1056,8 +1160,7 @@ class NuFissionXS(MultiGroupXS): tally arithmetic.""" self._xs_tally = self.tallies['nu-fission'] / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(NuFissionXS, self).compute_xs() class ScatterXS(MultiGroupXS): @@ -1088,8 +1191,7 @@ class ScatterXS(MultiGroupXS): OpenMC tally arithmetic.""" self._xs_tally = self.tallies['scatter'] / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(ScatterXS, self).compute_xs() class NuScatterXS(MultiGroupXS): @@ -1120,8 +1222,7 @@ class NuScatterXS(MultiGroupXS): tally arithmetic.""" self._xs_tally = self.tallies['nu-scatter'] / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(NuScatterXS, self).compute_xs() class ScatterMatrixXS(MultiGroupXS): @@ -1172,8 +1273,7 @@ class ScatterMatrixXS(MultiGroupXS): rxn_tally = self.tallies['scatter'] self._xs_tally = rxn_tally / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(ScatterMatrixXS, self).compute_xs() def get_xs(self, in_groups='all', out_groups='all', subdomains='all', nuclides='all', xs_type='macro', value='mean'): @@ -1218,8 +1318,6 @@ class ScatterMatrixXS(MultiGroupXS): """ - # TODO: Deal with xs_type and micros - if self.xs_tally is None: msg = 'Unable to get cross section since it has not been computed' raise ValueError(msg) @@ -1260,9 +1358,17 @@ class ScatterMatrixXS(MultiGroupXS): xs = self.xs_tally.get_values(filters=filters, filter_bins=filter_bins, nuclides=nuclides, value=value) xs = np.nan_to_num(xs) + + # If user requested microscopic cross sections from an object with + # microscopic cross sections, divide by atom number densities + if self.xs_type == 'micro' and xs_type == 'micro': + densities = self.get_nuclide_densities(nuclides) + if value == 'mean' or value == 'std_dev': + xs /= densities[np.newaxis, :, np.newaxis] + return xs - def print_xs(self, subdomains='all', nuclides='all'): + def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'): """Prints a string representation for the multi-group cross section. Parameters @@ -1273,6 +1379,9 @@ class ScatterMatrixXS(MultiGroupXS): nuclides : Iterable of str or 'all' The nuclides of the cross-sections to include in the report + xs_type: {'macro' or 'micro'} + Return the macro or micro cross section in units of cm^-1 or barns + """ if subdomains != 'all': @@ -1320,11 +1429,14 @@ class ScatterMatrixXS(MultiGroupXS): # Loop over all Nuclides for nuclide in nuclides: - # Build header for cross section type based on the nuclide - if nuclide == 'total': + # Build header for nuclide type + if xs_type != 'total': + string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) + + # Build header for cross section type + if xs_type == 'macro': string += '{0: <16}\n'.format('\tCross Sections [cm^-1]:') else: - string += '{0: <16}=\t{1}\n'.format('\tNuclide', nuclide) string += '{0: <16}\n'.format('\tCross Sections [barns]:') template = '{0: <12}Group {1} -> Group {2}:\t\t' @@ -1334,11 +1446,11 @@ class ScatterMatrixXS(MultiGroupXS): for out_group in range(1, self.num_groups+1): string += template.format('', in_group, out_group) average = \ - self.get_xs([in_group], [out_group], - [subdomain], [nuclide], 'mean') + self.get_xs([in_group], [out_group], [subdomain], + [nuclide], xs_type, 'mean') rel_err = \ - self.get_xs([in_group], [out_group], - [subdomain], [nuclide],'rel_err') * 100 + self.get_xs([in_group], [out_group], [subdomain], + [nuclide], xs_type, 'rel_err') * 100 average = np.nan_to_num(average.flatten())[0] rel_err = np.nan_to_num(rel_err.flatten())[0] string += '{:1.2e} +/- {:1.2e}%'.format(average, rel_err) @@ -1398,8 +1510,7 @@ class NuScatterMatrixXS(ScatterMatrixXS): rxn_tally = self.tallies['nu-scatter'] self._xs_tally = rxn_tally / self.tallies['flux'] - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(ScatterMatrixXS, self).compute_xs() class Chi(MultiGroupXS): @@ -1434,8 +1545,7 @@ class Chi(MultiGroupXS): nu_fission_out = self.tallies['nu-fission-out'] nu_fission_in.remove_filter(nu_fission_in.filters[-1]) self._xs_tally = nu_fission_out / nu_fission_in - self._xs_tally._mean = np.nan_to_num(self.xs_tally.mean) - self._xs_tally._std_dev = np.nan_to_num(self.xs_tally.std_dev) + super(Chi, self).compute_xs() def get_xs(self, groups='all', subdomains='all', nuclides='all', xs_type='macro', value='mean'): diff --git a/openmc/tallies.py b/openmc/tallies.py index 9b753fe49..6429d8529 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -1,4 +1,4 @@ -from collections import Iterable, defaultdict +from collections import Iterable import copy import os import pickle @@ -9,9 +9,8 @@ import sys import numpy as np -from openmc import Mesh, Filter, Trigger, Nuclide, FILTER_TYPES +from openmc import Mesh, Filter, Trigger, Nuclide from openmc.cross import CrossScore, CrossNuclide, CrossFilter -from openmc.summary import Summary import openmc.checkvalue as cv from openmc.clean_xml import *