Merge pull request #550 from wbinventor/distribcell-tally-sum

Fixed Distribcell Tally Summation
This commit is contained in:
Paul Romano 2016-01-18 06:29:33 -06:00
commit bd89b423bd
11 changed files with 818 additions and 219 deletions

409
openmc/aggregate.py Normal file
View file

@ -0,0 +1,409 @@
import sys
from numbers import Integral
import numpy as np
from openmc import Filter, Nuclide
from openmc.cross import CrossScore, CrossNuclide, CrossFilter
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# Acceptable tally aggregation operations
_TALLY_AGGREGATE_OPS = ['sum', 'mean']
class AggregateScore(object):
"""A special-purpose tally score used to encapsulate an aggregate of a
subset or all of tally's scores for tally aggregation.
Parameters
----------
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
to aggregate across a tally's scores with this AggregateScore
Attributes
----------
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
to aggregate across a tally's scores with this AggregateScore
"""
def __init__(self, scores=None, aggregate_op=None):
self._scores = None
self._aggregate_op = None
if scores is not None:
self.scores = scores
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._scores = self.scores
clone._aggregate_op = self.aggregate_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
string = ', '.join(map(str, self.scores))
string = '{0}({1})'.format(self.aggregate_op, string)
return string
@property
def scores(self):
return self._scores
@property
def aggregate_op(self):
return self._aggregate_op
@scores.setter
def scores(self, scores):
cv.check_iterable_type('scores', scores, basestring)
self._scores = scores
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, (basestring, CrossScore))
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
class AggregateNuclide(object):
"""A special-purpose tally nuclide used to encapsulate an aggregate of a
subset or all of tally's nuclides for tally aggregation.
Parameters
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
Attributes
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
"""
def __init__(self, nuclides=None, aggregate_op=None):
self._nuclides = None
self._aggregate_op = None
if nuclides is not None:
self.nuclides = nuclides
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._nuclides = self.nuclides
clone._aggregate_op = self._aggregate_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
# Append each nuclide in the aggregate to the string
string = '{0}('.format(self.aggregate_op)
names = [nuclide.name if isinstance(nuclide, Nuclide) else str(nuclide)
for nuclide in self.nuclides]
string += ', '.join(map(str, names)) + ')'
return string
@property
def nuclides(self):
return self._nuclides
@property
def aggregate_op(self):
return self._aggregate_op
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides,
(basestring, Nuclide, CrossNuclide))
self._nuclides = nuclides
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
class AggregateFilter(object):
"""A special-purpose tally filter used to encapsulate an aggregate of a
subset or all of a tally filter's bins for tally aggregation.
Parameters
----------
aggregate_filter : Filter or CrossFilter
The filter included in the aggregation
bins : Iterable of tuple
The filter bins included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
Attributes
----------
type : str
The type of the aggregatefilter (e.g., 'sum(energy)', 'sum(cell)')
aggregate_filter : filter
The filter included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
bins : Iterable of tuple
The filter bins included in the aggregation
num_bins : Integral
The number of filter bins (always 1 if aggregate_filter is defined)
stride : Integral
The number of filter, nuclide and score bins within each of this
aggregatefilter's bins.
"""
def __init__(self, aggregate_filter=None, bins=None, aggregate_op=None):
self._type = '{0}({1})'.format(aggregate_op, aggregate_filter.type)
self._bins = None
self._stride = None
self._aggregate_filter = None
self._aggregate_op = None
if aggregate_filter is not None:
self.aggregate_filter = aggregate_filter
if bins is not None:
self.bins = bins
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash((self.type, self.bins, self.aggregate_op))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __repr__(self):
string = 'AggregateFilter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self.bins)
return string
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._type = self.type
clone._aggregate_filter = self.aggregate_filter
clone._aggregate_op = self.aggregate_op
clone._bins = self._bins
clone._stride = self.stride
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
@property
def aggregate_filter(self):
return self._aggregate_filter
@property
def aggregate_op(self):
return self._aggregate_op
@property
def type(self):
return self._type
@property
def bins(self):
return self._bins
@property
def num_bins(self):
return 1 if self.aggregate_filter else 0
@property
def stride(self):
return self._stride
@type.setter
def type(self, filter_type):
if filter_type not in _FILTER_TYPES.values():
msg = 'Unable to set AggregateFilter type to "{0}" since it ' \
'is not one of the supported types'.format(filter_type)
raise ValueError(msg)
self._type = filter_type
@aggregate_filter.setter
def aggregate_filter(self, aggregate_filter):
cv.check_type('aggregate_filter', aggregate_filter, (Filter, CrossFilter))
self._aggregate_filter = aggregate_filter
@bins.setter
def bins(self, bins):
cv.check_iterable_type('bins', bins, (Integral, tuple))
self._bins = bins
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
@stride.setter
def stride(self, stride):
self._stride = stride
def get_bin_index(self, filter_bin):
"""Returns the index in the AggregateFilter for some bin.
Parameters
----------
filter_bin : Integral or tuple of Real
A tuple of value(s) corresponding to the bin of interest in
the aggregated filter. The bin is the integer ID for 'material',
'surface', 'cell', 'cellborn', and 'universe' Filters. The bin
is the integer cell instance ID for 'distribcell' Filters. The
bin is a 2-tuple of floats for 'energy' and 'energyout' filters
corresponding to the energy boundaries of the bin of interest.
The bin is a (x,y,z) 3-tuple for 'mesh' filters corresponding to
the mesh cell of interest.
Returns
-------
filter_index : Integral
The index in the Tally data array for this filter bin. For an
AggregateTally the filter bin index is always unity.
Raises
------
ValueError
When the filter_bin is not part of the aggregated filter's bins
"""
if filter_bin not in self.bins:
msg = 'Unable to get the bin index for AggregateFilter since ' \
'"{0}" is not one of the bins'.format(filter_bin)
raise ValueError(msg)
else:
return 0
def get_pandas_dataframe(self, datasize, summary=None):
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
This method constructs a Pandas DataFrame object for the AggregateFilter
with columns annotated by filter bin information. This is a helper
method for the Tally.get_pandas_dataframe(...) method.
Parameters
----------
datasize : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). NOTE: This parameter
is not used by the AggregateFilter and simply mirrors the method
signature for the CrossFilter.
Returns
-------
pandas.DataFrame
A Pandas DataFrame with columns of strings that characterize the
aggregatefilter's bins. Each entry in the DataFrame will include
one or more aggregation operations used to construct the
aggregatefilter's bins. The number of rows in the DataFrame is the
same as the total number of bins in the corresponding tally, with
the filter bins appropriately tiled to map to the corresponding
tally bins.
See also
--------
Tally.get_pandas_dataframe(), Filter.get_pandas_dataframe(),
CrossFilter.get_pandas_dataframe()
"""
import pandas as pd
# Construct a sring representing the filter aggregation
aggregate_bin = '{0}('.format(self.aggregate_op)
aggregate_bin += ', '.join(map(str, self.bins)) + ')'
# Construct NumPy array of bin repeated for each element in dataframe
aggregate_bin_array = np.array([aggregate_bin])
aggregate_bin_array = np.repeat(aggregate_bin_array, datasize)
# Construct Pandas DataFrame for the AggregateFilter
df = pd.DataFrame({self.aggregate_filter.type: aggregate_bin_array})
return df

View file

@ -34,7 +34,7 @@ class CrossScore(object):
The right score in the outer product
binary_op : str
The tally arithmetic binary operator (e.g., '+', '-', etc.) used to
combine two tally's scores with this CrossNuclide
combine two tally's scores with this CrossScore
"""
@ -245,6 +245,8 @@ class CrossFilter(object):
Attributes
----------
type : str
The type of the crossfilter (e.g., 'energy / energy')
left_filter : Filter or CrossFilter
The left filter in the outer product
right_filter : Filter or CrossFilter
@ -252,6 +254,14 @@ class CrossFilter(object):
binary_op : str
The tally arithmetic binary operator (e.g., '+', '-', etc.) used to
combine two tally's filter bins with this CrossFilter
bins : dict of Iterable
A dictionary of the bins from each filter keyed by the types of the
left / right filters
num_bins : Integral
The number of filter bins (always 1 if aggregate_filter is defined)
stride : Integral
The number of filter, nuclide and score bins within each of this
crossfilter's bins.
"""
@ -310,7 +320,6 @@ class CrossFilter(object):
clone._binary_op = self.binary_op
clone._type = self.type
clone._bins = self._bins
clone._num_bins = self.num_bins
clone._stride = self.stride
memo[id(self)] = clone
@ -355,8 +364,8 @@ class CrossFilter(object):
@type.setter
def type(self, filter_type):
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(filter_type)
msg = 'Unable to set CrossFilter type to "{0}" since it ' \
'is not one of the supported types'.format(filter_type)
raise ValueError(msg)
self._type = filter_type

View file

@ -35,8 +35,8 @@ class Filter(object):
Attributes
----------
type : str
The type of the tally filter.
bins : Integral or Iterable of Integral or Iterable of Real
The type of the tally filter
bins : Integral or Iterable of Real
The bins for the filter
num_bins : Integral
The number of filter bins
@ -227,12 +227,12 @@ class Filter(object):
self._stride = stride
def can_merge(self, filter):
def can_merge(self, other):
"""Determine if filter can be merged with another.
Parameters
----------
filter : Filter
other : Filter
Filter to compare with
Returns
@ -242,11 +242,11 @@ class Filter(object):
"""
if not isinstance(filter, Filter):
if not isinstance(other, Filter):
return False
# Filters must be of the same type
elif self.type != filter.type:
elif self.type != other.type:
return False
# Distribcell filters cannot have more than one bin
@ -264,12 +264,12 @@ class Filter(object):
else:
return True
def merge(self, filter):
def merge(self, other):
"""Merge this filter with another.
Parameters
----------
filter : Filter
other : Filter
Filter to merge with
Returns
@ -279,16 +279,16 @@ class Filter(object):
"""
if not self.can_merge(filter):
if not self.can_merge(other):
msg = 'Unable to merge "{0}" with "{1}" ' \
'filters'.format(self.type, filter.type)
'filters'.format(self.type, other.type)
raise ValueError(msg)
# Create deep copy of filter to return as merged filter
merged_filter = copy.deepcopy(self)
# Merge unique filter bins
merged_bins = list(set(np.concatenate((self.bins, filter.bins))))
merged_bins = list(set(np.concatenate((self.bins, other.bins))))
merged_filter.bins = merged_bins
merged_filter.num_bins = len(merged_bins)

View file

@ -521,8 +521,8 @@ class MGXS(object):
self.tallies[key].add_trigger(trigger_clone)
# Add all non-domain specific Filters (e.g., 'energy') to the Tally
for filter in filters:
self.tallies[key].add_filter(filter)
for add_filter in filters:
self.tallies[key].add_filter(add_filter)
# If this is a by-nuclide cross-section, add all nuclides to Tally
if self.by_nuclide and score != 'flux':
@ -787,15 +787,15 @@ class MGXS(object):
std_dev = tally.get_reshaped_data(value='std_dev')
# Sum across all applicable fine energy group filters
for i, filter in enumerate(tally.filters):
if 'energy' not in filter.type:
for i, tally_filter in enumerate(tally.filters):
if 'energy' not in tally_filter.type:
continue
elif len(filter.bins) != len(fine_edges):
elif len(tally_filter.bins) != len(fine_edges):
continue
elif not np.allclose(filter.bins, fine_edges):
elif not np.allclose(tally_filter.bins, fine_edges):
continue
else:
filter.bins = coarse_groups.group_edges
tally_filter.bins = coarse_groups.group_edges
mean = np.add.reduceat(mean, energy_indices, axis=i)
std_dev = np.add.reduceat(std_dev**2, energy_indices, axis=i)
std_dev = np.sqrt(std_dev)

View file

@ -3,6 +3,7 @@ import re
import numpy as np
import openmc
import openmc.checkvalue as cv
if sys.version > '3':
long = int
@ -377,18 +378,18 @@ class StatePoint(object):
bins = self._f['{0}{1}/bins'.format(subbase, j)].value
# Create Filter object
filter = openmc.Filter(filter_type, bins)
filter.num_bins = n_bins
new_filter = openmc.Filter(filter_type, bins)
new_filter.num_bins = n_bins
if filter_type == 'mesh':
mesh_ids = self._f['tallies/meshes/ids'].value
mesh_keys = self._f['tallies/meshes/keys'].value
key = mesh_keys[mesh_ids == bins][0]
filter.mesh = self.meshes[key]
new_filter.mesh = self.meshes[key]
# Add Filter to the Tally
tally.add_filter(filter)
tally.add_filter(new_filter)
# Read Nuclide bins
nuclide_names = \
@ -406,11 +407,11 @@ class StatePoint(object):
# Compute and set the filter strides
for i in range(n_filters):
filter = tally.filters[i]
filter.stride = n_score_bins * len(nuclide_names)
tally_filter = tally.filters[i]
tally_filter.stride = n_score_bins * len(nuclide_names)
for j in range(i+1, n_filters):
filter.stride *= tally.filters[j].num_bins
tally_filter.stride *= tally.filters[j].num_bins
# Read scattering moment order strings (e.g., P3, Y1,2, etc.)
moments = self._f['{0}{1}/moment_orders'.format(
@ -544,13 +545,13 @@ class StatePoint(object):
contains_filters = True
# Iterate over the Filters requested by the user
for filter in filters:
for outer_filter in filters:
contains_filters = False
# Test if requested filter is a subset of any of the test
# tally's filters and if so continue to next filter
for test_filter in test_tally.filters:
if test_filter.is_subset(filter):
for inner_filter in test_tally.filters:
if inner_filter.is_subset(outer_filter):
contains_filters = True
break
@ -616,29 +617,29 @@ class StatePoint(object):
tally.name = summary.tallies[tally_id].name
tally.with_summary = True
for filter in tally.filters:
if filter.type == 'surface':
for tally_filter in tally.filters:
if tally_filter.type == 'surface':
surface_ids = []
for bin in filter.bins:
for bin in tally_filter.bins:
surface_ids.append(summary.surfaces[bin].id)
filter.bins = surface_ids
tally_filter.bins = surface_ids
if filter.type in ['cell', 'distribcell']:
if tally_filter.type in ['cell', 'distribcell']:
distribcell_ids = []
for bin in filter.bins:
for bin in tally_filter.bins:
distribcell_ids.append(summary.cells[bin].id)
filter.bins = distribcell_ids
tally_filter.bins = distribcell_ids
if filter.type == 'universe':
if tally_filter.type == 'universe':
universe_ids = []
for bin in filter.bins:
for bin in tally_filter.bins:
universe_ids.append(summary.universes[bin].id)
filter.bins = universe_ids
tally_filter.bins = universe_ids
if filter.type == 'material':
if tally_filter.type == 'material':
material_ids = []
for bin in filter.bins:
for bin in tally_filter.bins:
material_ids.append(summary.materials[bin].id)
filter.bins = material_ids
tally_filter.bins = material_ids
self._summary = summary

View file

@ -556,11 +556,11 @@ class Summary(object):
bins = self._f['{0}/bins'.format(subsubbase)][...]
# Create Filter object
filter = openmc.Filter(filter_type, bins)
filter.num_bins = num_bins
new_filter = openmc.Filter(filter_type, bins)
new_filter.num_bins = num_bins
# Add Filter to the Tally
tally.add_filter(filter)
tally.add_filter(new_filter)
# Add Tally to the global dictionary of all Tallies
self.tallies[tally_id] = tally

View file

@ -13,6 +13,7 @@ import numpy as np
from openmc import Mesh, Filter, Trigger, Nuclide
from openmc.cross import CrossScore, CrossNuclide, CrossFilter
from openmc.aggregate import AggregateScore, AggregateNuclide, AggregateFilter
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
from openmc.clean_xml import *
@ -143,8 +144,8 @@ class Tally(object):
clone._results_read = self._results_read
clone._filters = []
for filter in self.filters:
clone.add_filter(copy.deepcopy(filter, memo))
for self_filter in self.filters:
clone.add_filter(copy.deepcopy(self_filter, memo))
clone._nuclides = []
for nuclide in self.nuclides:
@ -174,8 +175,8 @@ class Tally(object):
if len(self.filters) != len(other.filters):
return False
for filter in self.filters:
if filter not in other.filters:
for self_filter in self.filters:
if self_filter not in other.filters:
return False
# Check all nuclides
@ -212,9 +213,9 @@ class Tally(object):
string += '{0: <16}{1}\n'.format('\tFilters', '=\t')
for filter in self.filters:
string += '{0: <16}\t\t{1}\t{2}\n'.format('', filter.type,
filter.bins)
for self_filter in self.filters:
string += '{0: <16}\t\t{1}\t{2}\n'.format('', self_filter.type,
self_filter.bins)
string += '{0: <16}{1}'.format('\tNuclides', '=\t')
@ -259,12 +260,16 @@ class Tally(object):
def num_scores(self):
return len(self._scores)
@property
def num_filters(self):
return len(self.filters)
@property
def num_filter_bins(self):
num_bins = 1
for filter in self.filters:
num_bins *= filter.num_bins
for self_filter in self.filters:
num_bins *= self_filter.num_bins
return num_bins
@ -455,33 +460,63 @@ class Tally(object):
else:
self._name = ''
def add_filter(self, filter):
def add_filter(self, new_filter):
"""Add a filter to the tally
Parameters
----------
filter : openmc.filter.Filter
Filter to add
new_filter : Filter, CrossFilter or AggregateFilter
A filter to specify a discretization of the tally across some
dimension (e.g., 'energy', 'cell'). The filter should be a Filter
object when a user is adding filters to a Tally for input file
generation or when the Tally is created from a StatePoint. The
filter may be a CrossFilter or AggregateFilter for derived tallies
created by tally arithmetic.
"""
if not isinstance(filter, (Filter, CrossFilter)):
if not isinstance(new_filter, (Filter, CrossFilter, AggregateFilter)):
msg = 'Unable to add Filter "{0}" to Tally ID="{1}" since it is ' \
'not a Filter object'.format(filter, self.id)
'not a Filter object'.format(new_filter, self.id)
raise ValueError(msg)
self._filters.append(filter)
# If the filter is already in the Tally, raise an error
if new_filter in self.filters:
msg = 'Unable to add a duplicate filter "{0}" to Tally ID="{1}" ' \
'since duplicate filters are not supported in the OpenMC ' \
'Python API'.format(new_filter, self.id)
raise ValueError(msg)
self._filters.append(new_filter)
def add_nuclide(self, nuclide):
"""Specify that scores for a particular nuclide should be accumulated
Parameters
----------
nuclide : openmc.nuclide.Nuclide
Nuclide to add
nuclide : str, Nuclide, CrossNuclide or AggregateNuclide
Nuclide to add to the tally. The nuclide should be a Nuclide object
when a user is adding nuclides to a Tally for input file generation.
The nuclide is a str when a Tally is created from a StatePoint file
(e.g., 'H-1', 'U-235') unless a Summary has been linked with the
StatePoint. The nuclide may be a CrossNuclide or AggregateNuclide
for derived tallies created by tally arithmetic.
"""
if not isinstance(nuclide, (basestring, Nuclide,
CrossNuclide, AggregateNuclide)):
msg = 'Unable to add nuclide "{0}" to Tally ID="{1}" since it is ' \
'not a Nuclide object'.format(nuclide)
raise ValueError(msg)
# If the nuclide is already in the Tally, raise an error
if nuclide in self.nuclides:
msg = 'Unable to add a duplicate nuclide "{0}" to Tally ID="{1}" ' \
'since duplicate nuclides are not supported in the OpenMC ' \
'Python API'.format(nuclide, self.id)
raise ValueError(msg)
self._nuclides.append(nuclide)
def add_score(self, score):
@ -489,19 +524,23 @@ class Tally(object):
Parameters
----------
score : str
Score to be accumulated, e.g. 'flux'
score : str, CrossScore or AggregateScore
A score to be accumulated (e.g., 'flux', 'nu-fission'). The score
should be a str when a user is adding scores to a Tally for input
file generation or when the Tally is created from a StatePoint. The
score may be a CrossScore or AggregateScore for derived tallies
created by tally arithmetic.
"""
if not isinstance(score, (basestring, CrossScore)):
if not isinstance(score, (basestring, CrossScore, AggregateScore)):
msg = 'Unable to add score "{0}" to Tally ID="{1}" since it is ' \
'not a string'.format(score, self.id)
raise ValueError(msg)
# If the score is already in the Tally, raise an error
if score in self.scores:
msg = 'Unable to add a duplicate score {0} to Tally ID="{1}" ' \
msg = 'Unable to add a duplicate score "{0}" to Tally ID="{1}" ' \
'since duplicate scores are not supported in the OpenMC ' \
'Python API'.format(score, self.id)
raise ValueError(msg)
@ -509,7 +548,7 @@ class Tally(object):
# Normal score strings
if isinstance(score, basestring):
self._scores.append(score.strip())
# CrossScores
# CrossScores and AggrgateScore
else:
self._scores.append(score)
@ -601,22 +640,22 @@ class Tally(object):
self._scores.remove(score)
def remove_filter(self, filter):
def remove_filter(self, old_filter):
"""Remove a filter from the tally
Parameters
----------
filter : openmc.filter.Filter
old_filter : openmc.filter.Filter
Filter to remove
"""
if filter not in self.filters:
if old_filter not in self.filters:
msg = 'Unable to remove filter "{0}" from Tally ID="{1}" since the ' \
'Tally does not contain this filter'.format(filter, self.id)
'Tally does not contain this filter'.format(old_filter, self.id)
ValueError(msg)
self._filters.remove(filter)
self._filters.remove(old_filter)
def remove_nuclide(self, nuclide):
"""Remove a nuclide from the tally
@ -760,13 +799,13 @@ class Tally(object):
element.set("name", self.name)
# Optional Tally filters
for filter in self.filters:
for self_filter in self.filters:
subelement = ET.SubElement(element, "filter")
subelement.set("type", str(filter.type))
subelement.set("type", str(self_filter.type))
if filter.bins is not None:
if self_filter.bins is not None:
bins = ''
for bin in filter.bins:
for bin in self_filter.bins:
bins += '{0} '.format(bin)
subelement.set("bins", bins.rstrip(' '))
@ -818,7 +857,7 @@ class Tally(object):
Returns
-------
filter : openmc.filter.Filter
filter_found : openmc.filter.Filter
Filter from this tally with matching type, or None if no matching
Filter is found
@ -829,21 +868,21 @@ class Tally(object):
"""
filter = None
filter_found = None
# Look through all of this Tally's Filters for the type requested
for test_filter in self.filters:
if test_filter.type == filter_type:
filter = test_filter
filter_found = test_filter
break
# If we did not find the Filter, throw an Exception
if filter is None:
if filter_found is None:
msg = 'Unable to find filter type "{0}" in ' \
'Tally ID="{1}"'.format(filter_type, self.id)
raise ValueError(msg)
return filter
return filter_found
def get_filter_index(self, filter_type, filter_bin):
"""Returns the index in the Tally's results array for a Filter bin
@ -868,10 +907,10 @@ class Tally(object):
"""
# Find the equivalent Filter in this Tally's list of Filters
filter = self.find_filter(filter_type)
filter_found = self.find_filter(filter_type)
# Get the index for the requested bin from the Filter and return it
filter_index = filter.get_bin_index(filter_bin)
filter_index = filter_found.get_bin_index(filter_bin)
return filter_index
def get_nuclide_index(self, nuclide):
@ -991,12 +1030,12 @@ class Tally(object):
filter_indices = []
# Loop over all of the Tally's Filters
for i, filter in enumerate(self.filters):
for i, self_filter in enumerate(self.filters):
user_filter = False
# If a user-requested Filter, get the user-requested bins
for j, test_filter in enumerate(filters):
if filter.type == test_filter:
if self_filter.type == test_filter:
bins = filter_bins[j]
user_filter = True
break
@ -1004,36 +1043,36 @@ class Tally(object):
# If not a user-requested Filter, get all bins
if not user_filter:
# Create list of 2- or 3-tuples tuples for mesh cell bins
if filter.type == 'mesh':
dimension = filter.mesh.dimension
if self_filter.type == 'mesh':
dimension = self_filter.mesh.dimension
xyz = map(lambda x: np.arange(1, x+1), dimension)
bins = list(itertools.product(*xyz))
# Create list of 2-tuples for energy boundary bins
elif filter.type in ['energy', 'energyout']:
elif self_filter.type in ['energy', 'energyout']:
bins = []
for k in range(filter.num_bins):
bins.append((filter.bins[k], filter.bins[k+1]))
for k in range(self_filter.num_bins):
bins.append((self_filter.bins[k], self_filter.bins[k+1]))
# Create list of cell instance IDs for distribcell Filters
elif filter.type == 'distribcell':
bins = np.arange(filter.num_bins)
elif self_filter.type == 'distribcell':
bins = np.arange(self_filter.num_bins)
# Create list of IDs for bins for all other filter types
else:
bins = filter.bins
bins = self_filter.bins
# Initialize a NumPy array for the Filter bin indices
filter_indices.append(np.zeros(len(bins), dtype=np.int))
# Add indices for each bin in this Filter to the list
for j, bin in enumerate(bins):
filter_index = self.get_filter_index(filter.type, bin)
filter_index = self.get_filter_index(self_filter.type, bin)
filter_indices[i][j] = filter_index
# Account for stride in each of the previous filters
for indices in filter_indices[:i]:
indices *= filter.num_bins
indices *= self_filter.num_bins
# Apply outer product sum between all filter bin indices
filter_indices = list(map(sum, itertools.product(*filter_indices)))
@ -1275,8 +1314,8 @@ class Tally(object):
if filters:
# Append each Filter's DataFrame to the overall DataFrame
for filter in self.filters:
filter_df = filter.get_pandas_dataframe(data_size, summary)
for self_filter in self.filters:
filter_df = self_filter.get_pandas_dataframe(data_size, summary)
df = pd.concat([df, filter_df], axis=1)
# Include DataFrame column for nuclides if user requested it
@ -1365,8 +1404,8 @@ class Tally(object):
# Build a new array shape with one dimension per filter
new_shape = ()
for filter in self.filters:
new_shape += (filter.num_bins, )
for self_filter in self.filters:
new_shape += (self_filter.num_bins, )
new_shape += (self.num_nuclides,)
new_shape += (self.num_scores,)
@ -1459,8 +1498,9 @@ class Tally(object):
# Create an HDF5 sub-group for the Filters
filter_group = tally_group.create_group('filters')
for filter in self.filters:
filter_group.create_dataset(filter.type, data=filter.bins)
for self_filter in self.filters:
filter_group.create_dataset(self_filter.type,
filter=self_filter.bins)
# Add all results to the main HDF5 group for the Tally
tally_group.create_dataset('sum', data=self.sum)
@ -1503,8 +1543,8 @@ class Tally(object):
tally_group['filters'] = {}
filter_group = tally_group['filters']
for filter in self.filters:
filter_group[filter.type] = filter.bins
for self_filter in self.filters:
filter_group[self_filter.type] = self_filter.bins
# Add all results to the main sub-dictionary for the Tally
tally_group['sum'] = self.sum
@ -1599,8 +1639,12 @@ class Tally(object):
raise ValueError(msg)
new_tally = Tally()
new_tally.with_batch_statistics = True
new_tally._derived = True
new_tally.with_batch_statistics = True
new_tally._num_realizations = self.num_realizations
new_tally._estimator = self.estimator
new_tally._with_summary = self.with_summary
new_tally._sp_filename = self._sp_filename
# Construct a combined derived name from the two tally operands
if self.name != '' and other.name != '':
@ -1698,14 +1742,21 @@ class Tally(object):
new_score = CrossScore(self_score, other_score, binary_op)
new_tally.add_score(new_score)
# Correct each Filter's stride
stride = new_tally.num_nuclides * new_tally.num_scores
for filter in reversed(new_tally.filters):
filter.stride = stride
stride *= filter.num_bins
# Update the new tally's filter strides
new_tally._update_filter_strides()
return new_tally
def _update_filter_strides(self):
"""Update each filter's stride based on the tally's nuclides and scores
for derived tallies created by tally arithmetic.
"""
stride = self.num_nuclides * self.num_scores
for self_filter in reversed(self.filters):
self_filter.stride = stride
stride *= self_filter.num_bins
def _align_tally_data(self, other, filter_product, nuclide_product,
score_product):
"""Aligns data from two tallies for tally arithmetic.
@ -1748,26 +1799,26 @@ class Tally(object):
set(other.filters).difference(set(self.filters))
# Add filters present in self but not in other to other
for filter in other_missing_filters:
filter = copy.deepcopy(filter)
other._mean = np.repeat(other.mean, filter.num_bins, axis=0)
other._std_dev = np.repeat(other.std_dev, filter.num_bins, axis=0)
other.add_filter(filter)
for other_filter in other_missing_filters:
filter_copy = copy.deepcopy(other_filter)
other._mean = np.repeat(other.mean, filter_copy.num_bins, axis=0)
other._std_dev = np.repeat(other.std_dev, filter_copy.num_bins, axis=0)
other.add_filter(filter_copy)
# Add filters present in other but not in self to self
for filter in self_missing_filters:
filter = copy.deepcopy(filter)
self._mean = np.repeat(self.mean, filter.num_bins, axis=0)
self._std_dev = np.repeat(self.std_dev, filter.num_bins, axis=0)
self.add_filter(filter)
for self_filter in self_missing_filters:
filter_copy = copy.deepcopy(self_filter)
self._mean = np.repeat(self.mean, filter_copy.num_bins, axis=0)
self._std_dev = np.repeat(self.std_dev, filter_copy.num_bins, axis=0)
self.add_filter(filter_copy)
# Align other filters with self filters
for i, filter in enumerate(self.filters):
other_index = other.filters.index(filter)
for i, self_filter in enumerate(self.filters):
other_index = other.filters.index(self_filter)
# If necessary, swap other filter
if other_index != i:
other._swap_filters(filter, other.filters[i])
other._swap_filters(self_filter, other.filters[i])
# Repeat and tile the data by nuclide in preparation for performing
# the tensor product across nuclides.
@ -1855,17 +1906,9 @@ class Tally(object):
if other_index != i:
other._swap_scores(score, other.scores[i])
# Correct the stride for other filters
stride = other.num_nuclides * other.num_scores
for filter in reversed(other.filters):
filter.stride = stride
stride *= filter.num_bins
# Correct the stride for self filters
stride = self.num_nuclides * self.num_scores
for filter in reversed(self.filters):
filter.stride = stride
stride *= filter.num_bins
# Update the tallies' filter strides
other._update_filter_strides()
self._update_filter_strides()
data = {}
data['self'] = {}
@ -1927,16 +1970,13 @@ class Tally(object):
self.filters[filter1_index] = filter2
self.filters[filter2_index] = filter1
# Update the strides for each of the filters
stride = self.num_nuclides * self.num_scores
for filter in reversed(self.filters):
filter.stride = stride
stride *= filter.num_bins
# Update the tally's filter strides
self._update_filter_strides()
# Construct lists of tuples for the bins in each of the two filters
filters = [filter1.type, filter2.type]
if filter1.type == 'distribcell':
filter1_bins = np.arange(filter.num_bins)
filter1_bins = np.arange(filter1.num_bins)
else:
filter1_bins = [(filter1.get_bin(i)) for i in range(filter1.num_bins)]
@ -2161,8 +2201,8 @@ class Tally(object):
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
for filter in self.filters:
new_tally.add_filter(filter)
for self_filter in self.filters:
new_tally.add_filter(self_filter)
for nuclide in self.nuclides:
new_tally.add_nuclide(nuclide)
for score in self.scores:
@ -2235,8 +2275,8 @@ class Tally(object):
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
for filter in self.filters:
new_tally.add_filter(filter)
for self_filter in self.filters:
new_tally.add_filter(self_filter)
for nuclide in self.nuclides:
new_tally.add_nuclide(nuclide)
for score in self.scores:
@ -2310,8 +2350,8 @@ class Tally(object):
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
for filter in self.filters:
new_tally.add_filter(filter)
for self_filter in self.filters:
new_tally.add_filter(self_filter)
for nuclide in self.nuclides:
new_tally.add_nuclide(nuclide)
for score in self.scores:
@ -2385,8 +2425,8 @@ class Tally(object):
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
for filter in self.filters:
new_tally.add_filter(filter)
for self_filter in self.filters:
new_tally.add_filter(self_filter)
for nuclide in self.nuclides:
new_tally.add_nuclide(nuclide)
for score in self.scores:
@ -2449,8 +2489,8 @@ 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:
# If original tally operand was sparse, sparsify the new tally
if self.sparse:
new_tally.sparse = True
elif isinstance(power, Real):
@ -2464,8 +2504,8 @@ class Tally(object):
new_tally.with_summary = self.with_summary
new_tally.num_realization = self.num_realizations
for filter in self.filters:
new_tally.add_filter(filter)
for self_filter in self.filters:
new_tally.add_filter(self_filter)
for nuclide in self.nuclides:
new_tally.add_nuclide(nuclide)
for score in self.scores:
@ -2605,7 +2645,7 @@ class Tally(object):
parameter (e.g., [(1,), (0., 0.625e-6)]; default is []). Each bin
in the list is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. Each bin is an integer for the
cell instance ID for 'distribcell Filters. Each bin is a 2-tuple of
cell instance ID for 'distribcell' Filters. Each bin is a 2-tuple of
floats for 'energy' and 'energyout' filters corresponding to the
energy boundaries of the bin of interest. The bin is a (x,y,z)
3-tuple for 'mesh' filters corresponding to the mesh cell of
@ -2687,29 +2727,29 @@ class Tally(object):
# Determine the filter indices from any of the requested filters
for i, filter_type in enumerate(filters):
filter = new_tally.find_filter(filter_type)
find_filter = new_tally.find_filter(filter_type)
# Remove and/or reorder filter bins to user specifications
bin_indices = []
num_bins = 0
for filter_bin in filter_bins[i]:
bin_index = filter.get_bin_index(filter_bin)
bin_index = find_filter.get_bin_index(filter_bin)
if filter_type in ['energy', 'energyout']:
bin_indices.extend([bin_index, bin_index+1])
num_bins += 1
elif filter_type == 'distribcell':
indices = [(bin,) for bin in range(filter.num_bins)]
bin_indices.extend(indices)
bin_indices = [0]
num_bins = find_filter.num_bins
else:
bin_indices.append(bin_index)
num_bins += 1
filter.bins = filter.bins[bin_indices]
filter.num_bins = len(filter_bins[i])
find_filter.bins = find_filter.bins[bin_indices]
find_filter.num_bins = num_bins
# Correct each Filter's stride
stride = new_tally.num_nuclides * new_tally.num_scores
for filter in reversed(new_tally.filters):
filter.stride = stride
stride *= filter.num_bins
# Update the new tally's filter strides
new_tally._update_filter_strides()
# If original tally was sparse, sparsify the sliced tally
new_tally.sparse = self.sparse
@ -2737,7 +2777,7 @@ class Tally(object):
A list of the filter bins corresponding to the filter_type parameter
Each bin in the list is the integer ID for 'material', 'surface',
'cell', 'cellborn', and 'universe' Filters. Each bin is an integer
for the cell instance ID for 'distribcell Filters. Each bin is a
for the cell instance ID for 'distribcell' Filters. Each bin is a
2-tuple of floats for 'energy' and 'energyout' filters corresponding
to the energy boundaries of the bin of interest. Each bin is an
(x,y,z) 3-tuple for 'mesh' filters corresponding to the mesh cell of
@ -2755,65 +2795,109 @@ class Tally(object):
A new tally which encapsulates the sum of data requested.
"""
# If user did not specify any scores, do not sum across scores
if len(scores) == 0:
scores = [[]]
# Sum across any scores specified by the user
else:
scores = [[score] for score in scores]
# Create new derived Tally for summation
tally_sum = Tally()
tally_sum._derived = True
tally_sum._estimator = self.estimator
tally_sum._num_realizations = self.num_realizations
tally_sum.with_batch_statistics = self.with_batch_statistics
tally_sum._with_summary = self.with_summary
tally_sum._sp_filename = self._sp_filename
tally_sum._results_read = self._results_read
# If user did not specify any nuclides, do not sum across nuclides
if len(nuclides) == 0:
nuclides = [[]]
# Sum across any nuclides specified by the user
else:
nuclides = [[nuclide] for nuclide in nuclides]
# Get tally data arrays reshaped with one dimension per filter
mean = self.get_reshaped_data(value='mean')
std_dev = self.get_reshaped_data(value='std_dev')
# Sum across any filter bins specified by the user
if filter_type in _FILTER_TYPES:
find_filter = self.find_filter(filter_type)
# If user did not specify filter bins, sum across all bins
if len(filter_bins) == 0:
filter = self.find_filter(filter_type)
filter_bins = [[(filter.get_bin(i),)] for i in range(filter.num_bins)]
bin_indices = np.arange(find_filter.num_bins)
if filter_type == 'distribcell':
filter_bins = np.arange(find_filter.num_bins)
else:
num_bins = find_filter.num_bins
filter_bins = \
[(find_filter.get_bin(i)) for i in range(num_bins)]
# Only sum across bins specified by the user
else:
filter_bins = [[(filter_bin,)] for filter_bin in filter_bins]
bin_indices = \
[find_filter.get_bin_index(bin) for bin in filter_bins]
filters = [[filter_type]]
# If user did not specify a filter type, do not sum across filter bins
# Sum across the bins in the user-specified filter
for i, self_filter in enumerate(self.filters):
if self_filter.type == filter_type:
mean = np.take(mean, indices=bin_indices, axis=i)
std_dev = np.take(std_dev, indices=bin_indices, axis=i)
mean = np.sum(mean, axis=i, keepdims=True)
std_dev = np.sum(std_dev**2, axis=i, keepdims=True)
std_dev = np.sqrt(std_dev)
# Add AggregateFilter to the tally sum
if not remove_filter:
filter_sum = \
AggregateFilter(self_filter, filter_bins, 'sum')
tally_sum.add_filter(filter_sum)
# Add a copy of each filter not summed across to the tally sum
else:
tally_sum.add_filter(copy.deepcopy(self_filter))
# Add a copy of this tally's filters to the tally sum
else:
filter_bins = [[]]
filters = [[]]
tally_sum._filters = copy.deepcopy(self.filters)
# Initialize Tally sum
tally_sum = 0
# Sum across any nuclides specified by the user
if len(nuclides) != 0:
nuclide_bins = [self.get_nuclide_index(nuclide) for nuclide in nuclides]
axis_index = self.num_filters
mean = np.take(mean, indices=nuclide_bins, axis=axis_index)
std_dev = np.take(std_dev, indices=nuclide_bins, axis=axis_index)
mean = np.sum(mean, axis=axis_index, keepdims=True)
std_dev = np.sum(std_dev**2, axis=axis_index, keepdims=True)
std_dev = np.sqrt(std_dev)
# Iterate over all Tally slice operands in summation
prod = [scores, filters, filter_bins, nuclides]
summed_filters = defaultdict(list)
for scores, filters, filter_bins, nuclides in itertools.product(*prod):
tally_slice = self.get_slice(scores, filters, filter_bins, nuclides)
# Add AggregateNuclide to the tally sum
nuclide_sum = AggregateNuclide(nuclides, 'sum')
tally_sum.add_nuclide(nuclide_sum)
# Remove filters summed across to avoid bulky CrossFilters
if filter_type:
filter = tally_slice.find_filter(filter_type)
tally_slice.remove_filter(filter)
summed_filters[filter_type].append(filter)
# Accumulate this Tally slice into the Tally sum
tally_sum += tally_slice
# Add back the filter(s) which were summed across to derived tally,
# if filter bins were input; otherwise, leave out summed filter(s)
if remove_filter and filter_type is not None:
# Rename tally sum indicating a summation over a particular filter
tally_sum.name = 'sum({0}, {1})'.format(self.name, filter_type)
# Add a copy of this tally's nuclides to the tally sum
else:
for summed_filter_type in summed_filters:
filters = summed_filters[summed_filter_type]
for i in range(1, len(filters)):
filters[i] = CrossFilter(filters[i-1], filters[i], '+')
tally_sum.add_filter(filters[-1])
tally_sum._nuclides = copy.deepcopy(self.nuclides)
# Sum across any scores specified by the user
if len(scores) != 0:
score_bins = [self.get_score_index(score) for score in scores]
axis_index = self.num_filters + 1
mean = np.take(mean, indices=score_bins, axis=axis_index)
std_dev = np.take(std_dev, indices=score_bins, axis=axis_index)
mean = np.sum(mean, axis=axis_index, keepdims=True)
std_dev = np.sum(std_dev**2, axis=axis_index, keepdims=True)
std_dev = np.sqrt(std_dev)
# Add AggregateScore to the tally sum
score_sum = AggregateScore(scores, 'sum')
tally_sum.add_score(score_sum)
# Add a copy of this tally's scores to the tally sum
else:
tally_sum._scores = copy.deepcopy(self.scores)
# Update the tally sum's filter strides
tally_sum._update_filter_strides()
# Reshape condensed data arrays with one dimension for all filters
mean = np.reshape(mean, tally_sum.shape)
std_dev = np.reshape(std_dev, tally_sum.shape)
# Assign tally sum's data with the new arrays
tally_sum._mean = mean
tally_sum._std_dev = std_dev
# If original tally was sparse, sparsify the tally summation
tally_sum.sparse = self.sparse
@ -2880,11 +2964,8 @@ class Tally(object):
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
stride = new_tally.num_nuclides * new_tally.num_scores
for filter in reversed(new_tally.filters):
filter.stride = stride
stride *= filter.num_bins
# Update the new tally's filter strides
new_tally._update_filter_strides()
# If original tally was sparse, sparsify the diagonalized tally
new_tally.sparse = self.sparse

View file

@ -0,0 +1 @@
530a5e969901e153531f74aed46246b1e8783a0e2f347e472f7554c9970152f45d85499f17d7df9c35c74fed6f78d449aa70bf0c1f8947cd34d3a829483a0055

View file

@ -0,0 +1 @@
ba8bfe764fcc0484a4fdab8fdc4ff8ad0e4a98b1ff33e8687899c8cc6bf80cb28b3a59aeaec84bd74681b8b5f19f714292ccaa9c9d4ba852b2cc29872f612e10

View file

@ -0,0 +1,99 @@
#!/usr/bin/env python
import os
import sys
import glob
import hashlib
sys.path.insert(0, os.pardir)
from testing_harness import PyAPITestHarness
import openmc
class TallyAggregationTestHarness(PyAPITestHarness):
def _build_inputs(self):
# The summary.h5 file needs to be created to read in the tallies
self._input_set.settings.output = {'summary': True}
# Initialize the tallies file
tallies_file = openmc.TalliesFile()
# Initialize the nuclides
u235 = openmc.Nuclide('U-235')
u238 = openmc.Nuclide('U-238')
pu239 = openmc.Nuclide('Pu-239')
# Initialize the filters
energy_filter = openmc.Filter(type='energy', bins=[0.0, 0.253e-6,
1.0e-3, 1.0, 20.0])
distrib_filter = openmc.Filter(type='distribcell', bins=[60])
# Initialized the tallies
tally = openmc.Tally(name='distribcell tally')
tally.add_filter(energy_filter)
tally.add_filter(distrib_filter)
tally.add_score('nu-fission')
tally.add_score('total')
tally.add_nuclide(u235)
tally.add_nuclide(u238)
tally.add_nuclide(pu239)
tallies_file.add_tally(tally)
# Export tallies to file
self._input_set.tallies = tallies_file
super(TallyAggregationTestHarness, self)._build_inputs()
def _get_results(self, hash_output=True):
"""Digest info in the statepoint and return as a string."""
# Read the statepoint file.
statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0]
sp = openmc.StatePoint(statepoint)
# Read the summary file.
summary = glob.glob(os.path.join(os.getcwd(), 'summary.h5'))[0]
su = openmc.Summary(summary)
sp.link_with_summary(su)
# Extract the tally of interest
tally = sp.get_tally(name='distribcell tally')
# Perform tally aggregations across filter bins, nuclides and scores
outstr = ''
# Sum across all energy filter bins
tally_sum = tally.summation(filter_type='energy')
outstr += ', '.join(map(str, tally_sum.mean))
outstr += ', '.join(map(str, tally_sum.std_dev))
# Sum across all distribcell filter bins
tally_sum = tally.summation(filter_type='distribcell')
outstr += ', '.join(map(str, tally_sum.mean))
outstr += ', '.join(map(str, tally_sum.std_dev))
# Sum across all nuclides
tally_sum = tally.summation(nuclides=['U-235', 'U-238', 'Pu-239'])
outstr += ', '.join(map(str, tally_sum.mean))
outstr += ', '.join(map(str, tally_sum.std_dev))
# Sum across all scores
tally_sum = tally.summation(scores=['nu-fission', 'total'])
outstr += ', '.join(map(str, tally_sum.mean))
outstr += ', '.join(map(str, tally_sum.std_dev))
# Hash the results if necessary
if hash_output:
sha512 = hashlib.sha512()
sha512.update(outstr.encode('utf-8'))
outstr = sha512.hexdigest()
return outstr
def _cleanup(self):
super(TallyAggregationTestHarness, self)._cleanup()
f = os.path.join(os.getcwd(), 'tallies.xml')
if os.path.exists(f): os.remove(f)
if __name__ == '__main__':
harness = TallyAggregationTestHarness('statepoint.10.h5', True)
harness.main()

View file

@ -100,8 +100,6 @@ class TallyArithmeticTestHarness(PyAPITestHarness):
'entrywise')
outstr += str(tally_3.mean)
print(outstr)
# Hash the results if necessary
if hash_output:
sha512 = hashlib.sha512()