Merge pull request #528 from wbinventor/sparse-tallies

Sparse Tallies
This commit is contained in:
Paul Romano 2016-01-06 09:07:53 -06:00
commit 9fa31bbeba
7 changed files with 328 additions and 94 deletions

View file

@ -73,6 +73,9 @@ class Library(object):
name : str, optional
Name of the multi-group cross section library. Used as a label to
identify tallies in OpenMC 'tallies.xml' file.
sparse : bool
Whether or not the Library's tallies use SciPy's LIL sparse matrix
format for compressed data storage
"""
@ -92,6 +95,7 @@ class Library(object):
self._all_mgxs = OrderedDict()
self._sp_filename = None
self._keff = None
self._sparse = False
self.name = name
self.openmc_geometry = openmc_geometry
@ -119,6 +123,7 @@ class Library(object):
clone._all_mgxs = self.all_mgxs
clone._sp_filename = self._sp_filename
clone._keff = self._keff
clone._sparse = self.sparse
clone._all_mgxs = OrderedDict()
for domain in self.domains:
@ -208,6 +213,10 @@ class Library(object):
def keff(self):
return self._keff
@property
def sparse(self):
return self._sparse
@openmc_geometry.setter
def openmc_geometry(self, openmc_geometry):
cv.check_type('openmc_geometry', openmc_geometry, openmc.Geometry)
@ -286,6 +295,28 @@ class Library(object):
cv.check_type('tally trigger', tally_trigger, openmc.Trigger)
self._tally_trigger = tally_trigger
@sparse.setter
def sparse(self, sparse):
"""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
tally data processing. The tally data will be stored as SciPy LIL
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)
# Sparsify or densify each MGXS in the Library
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = self.get_mgxs(domain, mgxs_type)
mgxs.sparse = self.sparse
self._sparse = sparse
def build_library(self):
"""Initialize MGXS objects in each domain and for each reaction type
in the library.
@ -381,6 +412,7 @@ class Library(object):
for mgxs_type in self.mgxs_types:
mgxs = self.get_mgxs(domain, mgxs_type)
mgxs.load_from_statepoint(statepoint)
mgxs.sparse = self.sparse
def get_mgxs(self, domain, mgxs_type):
"""Return the MGXS object for some domain and reaction rate type.

View file

@ -103,6 +103,9 @@ class MGXS(object):
nuclides : list of str or 'sum'
A list of nuclide string names (e.g., 'U-238', 'O-16') when by_nuclide
is True and 'sum' when by_nuclide is False.
sparse : bool
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
for compressed data storage
"""
@ -121,6 +124,7 @@ class MGXS(object):
self._tally_trigger = None
self._tallies = None
self._xs_tally = None
self._sparse = False
self.name = name
self.by_nuclide = by_nuclide
@ -146,6 +150,7 @@ class MGXS(object):
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
clone._xs_tally = copy.deepcopy(self.xs_tally, memo)
clone._sparse = self.sparse
clone._tallies = OrderedDict()
for tally_type, tally in self.tallies.items():
@ -199,6 +204,10 @@ class MGXS(object):
def xs_tally(self):
return self._xs_tally
@property
def sparse(self):
return self._sparse
@property
def num_subdomains(self):
tally = list(self.tallies.values())[0]
@ -249,6 +258,29 @@ class MGXS(object):
cv.check_type('tally trigger', tally_trigger, openmc.Trigger)
self._tally_trigger = tally_trigger
@sparse.setter
def sparse(self, sparse):
"""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
tally data processing. The tally data will be stored as SciPy LIL
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)
# Sparsify or densify the derived MGXS tally and its base tallies
if self.xs_tally:
self.xs_tally.sparse = sparse
for tally_name in self.tallies:
self.tallies[tally_name].sparse = sparse
self._sparse = sparse
@staticmethod
def get_mgxs(mgxs_type, domain=None, domain_type=None,
energy_groups=None, by_nuclide=False, name=''):
@ -503,6 +535,7 @@ class MGXS(object):
# Remove NaNs which may have resulted from divide-by-zero operations
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)
self.xs_tally.sparse = self.sparse
def load_from_statepoint(self, statepoint):
"""Extracts tallies in an OpenMC StatePoint with the data needed to
@ -565,6 +598,7 @@ class MGXS(object):
estimator=tally.estimator)
sp_tally = sp_tally.get_slice(tally.scores, filters,
filter_bins, tally.nuclides)
sp_tally.sparse = self.sparse
self.tallies[tally_type] = sp_tally
# Compute the cross section from the tallies
@ -716,6 +750,7 @@ class MGXS(object):
# Clone this MGXS to initialize the condensed version
condensed_xs = copy.deepcopy(self)
condensed_xs.sparse = False
condensed_xs.energy_groups = coarse_groups
# Build energy indices to sum across
@ -754,10 +789,8 @@ class MGXS(object):
std_dev = np.sqrt(std_dev)
# Reshape condensed data arrays with one dimension for all filters
new_shape = \
(tally.num_filter_bins, tally.num_nuclides, tally.num_scores,)
mean = np.reshape(mean, new_shape)
std_dev = np.reshape(std_dev, new_shape)
mean = np.reshape(mean, tally.shape)
std_dev = np.reshape(std_dev, tally.shape)
# Override tally's data with the new condensed data
tally._mean = mean
@ -765,6 +798,7 @@ class MGXS(object):
# Compute the energy condensed multi-group cross section
condensed_xs.compute_xs()
condensed_xs.sparse = self.sparse
return condensed_xs
def get_subdomain_avg_xs(self, subdomains='all'):
@ -807,8 +841,9 @@ class MGXS(object):
# Clone this MGXS to initialize the subdomain-averaged version
avg_xs = copy.deepcopy(self)
avg_xs.sparse = False
# If domain is distribcell, make the new domain 'cell'
# If domain is distribcell, make the new domain 'cell'
if self.domain_type == 'distribcell':
avg_xs.domain_type = 'cell'
@ -836,10 +871,8 @@ class MGXS(object):
domain_filter.num_bins = 1
# Reshape averaged data arrays with one dimension for all filters
new_shape = \
(tally.num_filter_bins, tally.num_nuclides, tally.num_scores,)
mean = np.reshape(mean, new_shape)
std_dev = np.reshape(std_dev, new_shape)
mean = np.reshape(mean, tally.shape)
std_dev = np.reshape(std_dev, tally.shape)
# Override tally's data with the new condensed data
tally._mean = mean
@ -847,7 +880,7 @@ class MGXS(object):
# Compute the subdomain-averaged multi-group cross section
avg_xs.compute_xs()
avg_xs.sparse = self.sparse
return avg_xs
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):

View file

@ -901,7 +901,9 @@ def get_opencg_lattice(openmc_lattice):
# Convert 2D universes array to 3D for OpenCG
if len(universes.shape) == 2:
universes.shape = (1,) + universes.shape
new_universes = universes.copy()
new_universes.shape = (1,) + universes.shape
universes = new_universes
# Initialize an empty array for the OpenCG nested Universes in this Lattice
universe_array = np.ndarray(tuple(np.array(dimension)[::-1]),

View file

@ -75,6 +75,9 @@ class StatePoint(object):
energy of the source site.
source_present : bool
Indicate whether source sites are present
sparse : bool
Whether or not the tallies uses SciPy's LIL sparse matrix format for
compressed data storage
tallies : dict
Dictionary whose keys are tally IDs and whose values are Tally objects
tallies_present : bool
@ -110,6 +113,7 @@ class StatePoint(object):
self._tallies_read = False
self._summary = False
self._global_tallies = None
self._sparse = False
def close(self):
self._f.close()
@ -318,6 +322,10 @@ class StatePoint(object):
def source_present(self):
return self._f['source_present'].value > 0
@property
def sparse(self):
return self._sparse
@property
def tallies(self):
if not self._tallies_read:
@ -340,7 +348,8 @@ class StatePoint(object):
for tally_key in tally_keys:
# Read the Tally size specifications
n_realizations = self._f['{0}{1}/n_realizations'.format(base, tally_key)].value
n_realizations = \
self._f['{0}{1}/n_realizations'.format(base, tally_key)].value
# Create Tally object and assign basic properties
tally = openmc.Tally(tally_id=tally_key)
@ -350,7 +359,8 @@ class StatePoint(object):
tally.num_realizations = n_realizations
# Read the number of Filters
n_filters = self._f['{0}{1}/n_filters'.format(base, tally_key)].value
n_filters = \
self._f['{0}{1}/n_filters'.format(base, tally_key)].value
subbase = '{0}{1}/filter '.format(base, tally_key)
@ -358,7 +368,8 @@ class StatePoint(object):
for j in range(1, n_filters+1):
# Read the Filter type
filter_type = self._f['{0}{1}/type'.format(subbase, j)].value.decode()
filter_type = \
self._f['{0}{1}/type'.format(subbase, j)].value.decode()
n_bins = self._f['{0}{1}/n_bins'.format(subbase, j)].value
@ -380,7 +391,8 @@ class StatePoint(object):
tally.add_filter(filter)
# Read Nuclide bins
nuclide_names = self._f['{0}{1}/nuclides'.format(base, tally_key)].value
nuclide_names = \
self._f['{0}{1}/nuclides'.format(base, tally_key)].value
# Add all Nuclides to the Tally
for name in nuclide_names:
@ -415,6 +427,7 @@ class StatePoint(object):
tally.add_score(score)
# Add Tally to the global dictionary of all Tallies
tally.sparse = self.sparse
self._tallies[tally_key] = tally
self._tallies_read = True
@ -439,6 +452,26 @@ class StatePoint(object):
def with_summary(self):
return False if self.summary is None else True
@sparse.setter
def sparse(self, sparse):
"""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
tally data processing. The tally data will be stored as SciPy LIL
matrices internally within each Tally object. All tally data access
properties and methods will return data as a dense NumPy array.
"""
cv.check_type('sparse', sparse, bool)
self._sparse = sparse
# Update tally sparsities
if self._tallies_read:
for tally_id in self.tallies:
self.tallies[tally_id].sparse = self.sparse
def get_tally(self, scores=[], filters=[], nuclides=[],
name=None, id=None, estimator=None):
"""Finds and returns a Tally object with certain properties.

View file

@ -73,6 +73,9 @@ class Tally(object):
Total number of filter bins accounting for all filters
num_bins : Integral
Total number of bins for the tally
shape : 3-tuple of Integral
The shape of the tally data array ordered as the number of filter bins,
nuclide bins and score bins
num_realizations : Integral
Total number of realizations
with_summary : bool
@ -86,6 +89,11 @@ class Tally(object):
An array containing the sample mean for each bin
std_dev : ndarray
An array containing the sample standard deviation for each bin
derived : bool
Whether or not the tally is derived from one or more other tallies
sparse : bool
Whether or not the tally uses SciPy's LIL sparse matrix format for
compressed data storage
"""
@ -108,6 +116,7 @@ class Tally(object):
self._std_dev = None
self._with_batch_statistics = False
self._derived = False
self._sparse = False
self._sp_filename = None
self._results_read = False
@ -129,6 +138,7 @@ class Tally(object):
clone._with_summary = self.with_summary
clone._with_batch_statistics = self.with_batch_statistics
clone._derived = self.derived
clone._sparse = self.sparse
clone._sp_filename = self._sp_filename
clone._results_read = self._results_read
@ -265,6 +275,10 @@ class Tally(object):
num_bins *= self.num_scores
return num_bins
@property
def shape(self):
return (self.num_filter_bins, self.num_nuclides, self.num_scores)
@property
def estimator(self):
return self._estimator
@ -298,29 +312,33 @@ class Tally(object):
sum = data['sum']
sum_sq = data['sum_sq']
# Define a routine to convert 0 to 1
def nonzero(val):
return 1 if not val else val
# Reshape the results arrays
new_shape = (nonzero(self.num_filter_bins),
nonzero(self.num_nuclides),
nonzero(self.num_scores))
sum = np.reshape(sum, new_shape)
sum_sq = np.reshape(sum_sq, new_shape)
sum = np.reshape(sum, self.shape)
sum_sq = np.reshape(sum_sq, self.shape)
# Set the data for this Tally
self._sum = sum
self._sum_sq = sum_sq
# Convert NumPy arrays to SciPy sparse LIL matrices
if self.sparse:
import scipy.sparse as sps
self._sum = \
sps.lil_matrix(self._sum.flatten(), self._sum.shape)
self._sum_sq = \
sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape)
# Indicate that Tally results have been read
self._results_read = True
# Close the HDF5 statepoint file
f.close()
return self._sum
if self.sparse:
return np.reshape(self._sum.toarray(), self.shape)
else:
return self._sum
@property
def sum_sq(self):
@ -331,7 +349,10 @@ class Tally(object):
# Force reading of sum and sum_sq
self.sum
return self._sum_sq
if self.sparse:
return np.reshape(self._sum_sq.toarray(), self.shape)
else:
return self._sum_sq
@property
def mean(self):
@ -340,7 +361,18 @@ class Tally(object):
return None
self._mean = self.sum / self.num_realizations
return self._mean
# Convert NumPy array to SciPy sparse LIL matrix
if self.sparse:
import scipy.sparse as sps
self._mean = \
sps.lil_matrix(self._mean.flatten(), self._mean.shape)
if self.sparse:
return np.reshape(self._mean.toarray(), self.shape)
else:
return self._mean
@property
def std_dev(self):
@ -353,8 +385,20 @@ class Tally(object):
self._std_dev = np.zeros_like(self.mean)
self._std_dev[nonzero] = np.sqrt((self.sum_sq[nonzero]/n -
self.mean[nonzero]**2)/(n - 1))
# Convert NumPy array to SciPy sparse LIL matrix
if self.sparse:
import scipy.sparse as sps
self._std_dev = \
sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape)
self.with_batch_statistics = True
return self._std_dev
if self.sparse:
return np.reshape(self._std_dev.toarray(), self.shape)
else:
return self._std_dev
@property
def with_batch_statistics(self):
@ -364,6 +408,10 @@ class Tally(object):
def derived(self):
return self._derived
@property
def sparse(self):
return self._sparse
@estimator.setter
def estimator(self, estimator):
cv.check_value('estimator', estimator,
@ -491,6 +539,51 @@ class Tally(object):
cv.check_type('sum_sq', sum_sq, Iterable)
self._sum_sq = sum_sq
@sparse.setter
def sparse(self, sparse):
"""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
tally data processing. The tally data will be stored as SciPy LIL
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)
# Convert NumPy arrays to SciPy sparse LIL matrices
if sparse and not self.sparse:
import scipy.sparse as sps
if self._sum is not None:
self._sum = \
sps.lil_matrix(self._sum.flatten(), self._sum.shape)
if self._sum_sq is not None:
self._sum_sq = \
sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape)
if self._mean is not None:
self._mean = \
sps.lil_matrix(self._mean.flatten(), self._mean.shape)
if self._std_dev is not None:
self._std_dev = \
sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape)
self._sparse = True
# Convert SciPy sparse LIL matrices to NumPy arrays
elif not sparse and self.sparse:
if self._sum is not None:
self._sum = np.reshape(self._sum.toarray(), self.shape)
if self._sum_sq is not None:
self._sum_sq = np.reshape(self._sum_sq.toarray(), self.shape)
if self._mean is not None:
self._mean = np.reshape(self._mean.toarray(), self.shape)
if self._std_dev is not None:
self._std_dev = np.reshape(self._std_dev.toarray(), self.shape)
self._sparse = False
def remove_score(self, score):
"""Remove a score from the tally
@ -618,7 +711,8 @@ class Tally(object):
"""
if not self.can_merge(tally):
msg = 'Unable to merge tally ID="{0}" with "{1}"'.format(tally.id, self.id)
msg = 'Unable to merge tally ID="{0}" with ' + \
'"{1}"'.format(tally.id, self.id)
raise ValueError(msg)
# Create deep copy of tally to return as merged tally
@ -1067,22 +1161,19 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method. ValueError is also thrown
if the input parameters do not correspond to the Tally's attributes,
When this method is called before the Tally is populated with data,
or the input parameters do not correspond to the Tally's attributes,
e.g., if the score(s) do not match those in the Tally.
"""
# Ensure that StatePoint.read_results() was called first
# Ensure that the tally has data
if (value == 'mean' and self.mean is None) or \
(value == 'std_dev' and self.std_dev is None) or \
(value == 'rel_err' and self.mean is None) or \
(value == 'sum' and self.sum is None) or \
(value == 'sum_sq' and self.sum_sq is None):
msg = 'The Tally ID="{0}" has no data to return. Call the ' \
'StatePoint.read_results() method before using ' \
'Tally.get_values(...)'.format(self.id)
msg = 'The Tally ID="{0}" has no data to return'.format(self.id)
raise ValueError(msg)
# Get filter, nuclide and score indices
@ -1149,17 +1240,14 @@ class Tally(object):
------
KeyError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
ImportError
When Pandas can not be found on the caller's system
"""
# Ensure that StatePoint.read_results() was called first
# Ensure that the tally has data
if self.mean is None or self.std_dev is None:
msg = 'The Tally ID="{0}" has no data to return. Call the ' \
'StatePoint.read_results() method before using ' \
'Tally.get_pandas_dataframe(...)'.format(self.id)
msg = 'The Tally ID="{0}" has no data to return'.format(self.id)
raise KeyError(msg)
# If using Summary, ensure StatePoint.link_with_summary(...) was called
@ -1305,16 +1393,13 @@ class Tally(object):
Raises
------
KeyError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
# Ensure that StatePoint.read_results() was called first
# Ensure that the tally has data
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() method before using ' \
'Tally.export_results(...)'.format(self.id)
msg = 'The Tally ID="{0}" has no data to export'.format(self.id)
raise KeyError(msg)
if not isinstance(filename, basestring):
@ -1476,7 +1561,7 @@ class Tally(object):
------
ValueError
When this method is called before the other tally is populated
with data by the StatePoint.read_results() method.
with data.
"""
@ -1531,6 +1616,9 @@ class Tally(object):
self_copy = copy.deepcopy(self)
other_copy = copy.deepcopy(other)
self_copy.sparse = False
other_copy.sparse = False
# Align the tally data based on desired hybrid product
data = self_copy._align_tally_data(other_copy, filter_product,
nuclide_product, score_product)
@ -1596,7 +1684,8 @@ class Tally(object):
else:
all_nuclides = [self_copy.nuclides, other_copy.nuclides]
for self_nuclide, other_nuclide in itertools.product(*all_nuclides):
new_nuclide = CrossNuclide(self_nuclide, other_nuclide, binary_op)
new_nuclide = \
CrossNuclide(self_nuclide, other_nuclide, binary_op)
new_tally.add_nuclide(new_nuclide)
# Add scores to the new tally
@ -1653,8 +1742,10 @@ class Tally(object):
"""
# Get the set of filters that each tally is missing
other_missing_filters = set(self.filters).difference(set(other.filters))
self_missing_filters = set(other.filters).difference(set(self.filters))
other_missing_filters = \
set(self.filters).difference(set(other.filters))
self_missing_filters = \
set(other.filters).difference(set(self.filters))
# Add filters present in self but not in other to other
for filter in other_missing_filters:
@ -1681,30 +1772,40 @@ class Tally(object):
# Repeat and tile the data by nuclide in preparation for performing
# the tensor product across nuclides.
if nuclide_product == 'tensor':
self._mean = np.repeat(self.mean, other.num_nuclides, axis=1)
self._std_dev = np.repeat(self.std_dev, other.num_nuclides, axis=1)
other._mean = np.tile(other.mean, (1, self.num_nuclides, 1))
other._std_dev = np.tile(other.std_dev, (1, self.num_nuclides, 1))
self._mean = \
np.repeat(self.mean, other.num_nuclides, axis=1)
self._std_dev = \
np.repeat(self.std_dev, other.num_nuclides, axis=1)
other._mean = \
np.tile(other.mean, (1, self.num_nuclides, 1))
other._std_dev = \
np.tile(other.std_dev, (1, self.num_nuclides, 1))
# Add nuclides to each tally such that each tally contains the complete
# set of nuclides necessary to perform an entrywise product. New nuclides
# added to a tally will have all their scores set to zero.
# set of nuclides necessary to perform an entrywise product. New
# nuclides added to a tally will have all their scores set to zero.
else:
# Get the set of nuclides that each tally is missing
other_missing_nuclides = set(self.nuclides).difference(set(other.nuclides))
self_missing_nuclides = set(other.nuclides).difference(set(self.nuclides))
other_missing_nuclides = \
set(self.nuclides).difference(set(other.nuclides))
self_missing_nuclides = \
set(other.nuclides).difference(set(self.nuclides))
# Add nuclides present in self but not in other to other
for nuclide in other_missing_nuclides:
other._mean = np.insert(other.mean, other.num_nuclides, 0, axis=1)
other._std_dev = np.insert(other.std_dev, other.num_nuclides, 0, axis=1)
other._mean = \
np.insert(other.mean, other.num_nuclides, 0, axis=1)
other._std_dev = \
np.insert(other.std_dev, other.num_nuclides, 0, axis=1)
other.add_nuclide(nuclide)
# Add nuclides present in other but not in self to self
for nuclide in self_missing_nuclides:
self._mean = np.insert(self.mean, self.num_nuclides, 0, axis=1)
self._std_dev = np.insert(self.std_dev, self.num_nuclides, 0, axis=1)
self._mean = \
np.insert(self.mean, self.num_nuclides, 0, axis=1)
self._std_dev = \
np.insert(self.std_dev, self.num_nuclides, 0, axis=1)
self.add_nuclide(nuclide)
# Align other nuclides with self nuclides
@ -1729,8 +1830,10 @@ class Tally(object):
else:
# Get the set of scores that each tally is missing
other_missing_scores = set(self.scores).difference(set(other.scores))
self_missing_scores = set(other.scores).difference(set(self.scores))
other_missing_scores = \
set(self.scores).difference(set(other.scores))
self_missing_scores = \
set(other.scores).difference(set(self.scores))
# Add scores present in self but not in other to other
for score in other_missing_scores:
@ -1792,7 +1895,7 @@ class Tally(object):
------
ValueError
If this is a derived tally or this method is called before the tally
is populated with data by the StatePoint.read_results() method.
is populated with data.
"""
@ -1879,7 +1982,7 @@ class Tally(object):
------
ValueError
If this is a derived tally or this method is called before the tally
is populated with data by the StatePoint.read_results() method.
is populated with data.
"""
@ -1946,7 +2049,7 @@ class Tally(object):
------
ValueError
If this is a derived tally or this method is called before the tally
is populated with data by the StatePoint.read_results() method.
is populated with data.
"""
@ -2030,8 +2133,7 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
@ -2044,6 +2146,10 @@ class Tally(object):
if isinstance(other, Tally):
new_tally = self.hybrid_product(other, binary_op='+')
# If both tally operands were sparse, sparsify the new tally
if self.sparse and other.sparse:
new_tally.sparse = True
elif isinstance(other, Real):
new_tally = Tally(name='derived')
new_tally._derived = True
@ -2062,6 +2168,9 @@ class Tally(object):
for score in self.scores:
new_tally.add_score(score)
# If this tally operand is sparse, sparsify the new tally
new_tally.sparse = self.sparse
else:
msg = 'Unable to add "{0}" to Tally ID="{1}"'.format(other, self.id)
raise ValueError(msg)
@ -2099,8 +2208,7 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
@ -2113,6 +2221,10 @@ class Tally(object):
if isinstance(other, Tally):
new_tally = self.hybrid_product(other, binary_op='-')
# If both tally operands were sparse, sparsify the new tally
if self.sparse and other.sparse:
new_tally.sparse = True
elif isinstance(other, Real):
new_tally = Tally(name='derived')
new_tally._derived = True
@ -2130,6 +2242,9 @@ class Tally(object):
for score in self.scores:
new_tally.add_score(score)
# If this tally operand is sparse, sparsify the new tally
new_tally.sparse = self.sparse
else:
msg = 'Unable to subtract "{0}" from Tally ' \
'ID="{1}"'.format(other, self.id)
@ -2168,8 +2283,7 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
@ -2182,6 +2296,10 @@ class Tally(object):
if isinstance(other, Tally):
new_tally = self.hybrid_product(other, binary_op='*')
# If original tally operands were sparse, sparsify the new tally
if self.sparse and other.sparse:
new_tally.sparse = True
elif isinstance(other, Real):
new_tally = Tally(name='derived')
new_tally._derived = True
@ -2199,6 +2317,9 @@ class Tally(object):
for score in self.scores:
new_tally.add_score(score)
# If this tally operand is sparse, sparsify the new tally
new_tally.sparse = self.sparse
else:
msg = 'Unable to multiply Tally ID="{0}" ' \
'by "{1}"'.format(self.id, other)
@ -2237,8 +2358,7 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
@ -2251,6 +2371,10 @@ class Tally(object):
if isinstance(other, Tally):
new_tally = self.hybrid_product(other, binary_op='/')
# If original tally operands were sparse, sparsify the new tally
if self.sparse and other.sparse:
new_tally.sparse = True
elif isinstance(other, Real):
new_tally = Tally(name='derived')
new_tally._derived = True
@ -2268,6 +2392,9 @@ class Tally(object):
for score in self.scores:
new_tally.add_score(score)
# If this tally operand is sparse, sparsify the new tally
new_tally.sparse = self.sparse
else:
msg = 'Unable to divide Tally ID="{0}" ' \
'by "{1}"'.format(self.id, other)
@ -2309,8 +2436,7 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
@ -2323,6 +2449,10 @@ class Tally(object):
if isinstance(power, Tally):
new_tally = self.hybrid_product(power, binary_op='^')
# If original tally operands were sparse, sparsify the new tally
if self.sparse and other.sparse:
new_tally.sparse = True
elif isinstance(power, Real):
new_tally = Tally(name='derived')
new_tally._derived = True
@ -2341,6 +2471,9 @@ class Tally(object):
for score in self.scores:
new_tally.add_score(score)
# If original tally was sparse, sparsify the exponentiated tally
new_tally.sparse = self.sparse
else:
msg = 'Unable to raise Tally ID="{0}" to ' \
'power "{1}"'.format(self.id, power)
@ -2491,18 +2624,18 @@ class Tally(object):
Raises
------
ValueError
When this method is called before the Tally is populated with data
by the StatePoint.read_results() method.
When this method is called before the Tally is populated with data.
"""
# Ensure that StatePoint.read_results() was called first
# Ensure that the tally has data
if not self.derived and self.sum is None:
msg = 'Unable to use tally arithmetic with Tally ID="{0}" ' \
'since it does not contain any results.'.format(self.id)
raise ValueError(msg)
new_tally = copy.deepcopy(self)
new_tally.sparse = False
if self.sum is not None:
new_sum = self.get_values(scores, filters, filter_bins,
@ -2518,7 +2651,7 @@ class Tally(object):
new_tally._mean = new_mean
if self.std_dev is not None:
new_std_dev = self.get_values(scores, filters, filter_bins,
nuclides, 'std_dev')
nuclides, 'std_dev')
new_tally._std_dev = new_std_dev
# SCORES
@ -2578,6 +2711,8 @@ class Tally(object):
filter.stride = stride
stride *= filter.num_bins
# If original tally was sparse, sparsify the sliced tally
new_tally.sparse = self.sparse
return new_tally
def summation(self, scores=[], filter_type=None,
@ -2680,6 +2815,8 @@ class Tally(object):
filters[i] = CrossFilter(filters[i-1], filters[i], '+')
tally_sum.add_filter(filters[-1])
# If original tally was sparse, sparsify the tally summation
tally_sum.sparse = self.sparse
return tally_sum
def diagonalize_filter(self, new_filter):
@ -2716,12 +2853,6 @@ class Tally(object):
new_tally = copy.deepcopy(self)
new_tally.add_filter(new_filter)
# Determine the shape of data in the new diagonalized Tally
num_filter_bins = new_tally.num_filter_bins
num_nuclides = new_tally.num_nuclides
num_scores = new_tally.num_scores
new_shape = (num_filter_bins, num_nuclides, num_scores)
# Determine "base" indices along the new "diagonal", and the factor
# by which the "base" indices should be repeated to account for all
# other filter bins in the diagonalized tally
@ -2737,16 +2868,16 @@ class Tally(object):
# Inject this Tally's data along the diagonal of the diagonalized Tally
if self.sum is not None:
new_tally._sum = np.zeros(new_shape, dtype=np.float64)
new_tally._sum = np.zeros(new_tally.shape, dtype=np.float64)
new_tally._sum[diag_indices, :, :] = self.sum
if self.sum_sq is not None:
new_tally._sum_sq = np.zeros(new_shape, dtype=np.float64)
new_tally._sum_sq = np.zeros(new_tally.shape, dtype=np.float64)
new_tally._sum_sq[diag_indices, :, :] = self.sum_sq
if self.mean is not None:
new_tally._mean = np.zeros(new_shape, dtype=np.float64)
new_tally._mean = np.zeros(new_tally.shape, dtype=np.float64)
new_tally._mean[diag_indices, :, :] = self.mean
if self.std_dev is not None:
new_tally._std_dev = np.zeros(new_shape, dtype=np.float64)
new_tally._std_dev = np.zeros(new_tally.shape, dtype=np.float64)
new_tally._std_dev[diag_indices, :, :] = self.std_dev
# Correct each Filter's stride
@ -2755,6 +2886,8 @@ class Tally(object):
filter.stride = stride
stride *= filter.num_bins
# If original tally was sparse, sparsify the diagonalized tally
new_tally.sparse = self.sparse
return new_tally

View file

@ -1081,7 +1081,7 @@ class RectLattice(Lattice):
# For 2D Lattices
if len(self._dimension) == 2:
offset = self._offsets[i[1]-1, i[2]-1, 0, distribcell_index-1]
offset += self._universes[i[1]][i[2]].get_cell_instance(path,
offset += self._universes[i[1]-1][i[2]-1].get_cell_instance(path,
distribcell_index)
# For 3D Lattices

View file

@ -37,6 +37,7 @@ if have_setuptools:
# Optional dependencies
'extras_require': {
'pandas': ['pandas'],
'sparse' : ['scipy'],
'vtk': ['vtk', 'silomesh'],
'validate': ['lxml']
}})