mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Refactored Tally._outer_product(...) and operator overloads per @paulromano suggestion
This commit is contained in:
parent
9db3a343ca
commit
1154c07438
1 changed files with 125 additions and 114 deletions
|
|
@ -101,6 +101,7 @@ class Tally(object):
|
|||
self._mean = None
|
||||
self._std_dev = None
|
||||
self._with_batch_statistics = False
|
||||
self._derived = False
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
existing = memo.get(id(self))
|
||||
|
|
@ -119,6 +120,7 @@ class Tally(object):
|
|||
clone._std_dev = copy.deepcopy(self.std_dev, memo)
|
||||
clone._with_summary = self.with_summary
|
||||
clone._with_batch_statistics = self.with_batch_statistics
|
||||
clone._derived = self.derived
|
||||
|
||||
clone._filters = []
|
||||
for filter in self.filters:
|
||||
|
|
@ -281,6 +283,10 @@ class Tally(object):
|
|||
def with_batch_statistics(self):
|
||||
return self._with_batch_statistics
|
||||
|
||||
@property
|
||||
def derived(self):
|
||||
return self._derived
|
||||
|
||||
@estimator.setter
|
||||
def estimator(self, estimator):
|
||||
check_value('estimator', estimator, ['analog', 'tracklength'])
|
||||
|
|
@ -410,8 +416,8 @@ class Tally(object):
|
|||
"""
|
||||
|
||||
if score not in self.scores:
|
||||
msg = 'Unable to remove score "{0}" from Tally ID="{1}" since the ' \
|
||||
'Tally does not contain this score'.format(score, self.id)
|
||||
msg = 'Unable to remove score "{0}" from Tally ID="{1}" since ' \
|
||||
'the Tally does not contain this score'.format(score, self.id)
|
||||
ValueError(msg)
|
||||
|
||||
self._scores.remove(score)
|
||||
|
|
@ -1006,7 +1012,7 @@ class Tally(object):
|
|||
|
||||
# Ensure that StatePoint.read_results() was called first
|
||||
if self.mean is None or self.std_dev is None:
|
||||
msg = 'The Tally ID={0} has no data to return. Call the ' \
|
||||
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)
|
||||
raise KeyError(msg)
|
||||
|
|
@ -1427,7 +1433,7 @@ class Tally(object):
|
|||
# Pickle the Tally results to a file
|
||||
pickle.dump(tally_results, open(filename, 'wb'))
|
||||
|
||||
def _outer_product(self, other, new_tally, binary_op):
|
||||
def _outer_product(self, other, binary_op):
|
||||
"""Combines filters, scores and nuclides with another tally.
|
||||
|
||||
This is a helper method for the tally arithmetic methods. The filters,
|
||||
|
|
@ -1439,14 +1445,66 @@ class Tally(object):
|
|||
----------
|
||||
other : Tally
|
||||
The tally on the right hand side of the outer product
|
||||
new_tally: Tally
|
||||
The new tally to represent the outer product
|
||||
binary_op : {'+', '-', '*', '/', '^'}
|
||||
The binary operation in the outer product
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tally
|
||||
A new Tally outer that is the outer product with this one.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
When this method is called before the other tally is populated
|
||||
with data by the StatePoint.read_results() method.
|
||||
|
||||
"""
|
||||
|
||||
new_tally.name = '({0} {1} {2})'.format(self.name, binary_op, other.name)
|
||||
# Check that results have been read
|
||||
if not other.derived and other.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID="{0}" ' \
|
||||
'since it does not contain any results.'.format(other.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
new_name = '({0} {1} {2})'.format(self.name, binary_op, other.name)
|
||||
new_tally = Tally(name=new_name)
|
||||
new_tally.with_batch_statistics = True
|
||||
|
||||
data = self._align_tally_data(other)
|
||||
|
||||
if binary_op == '+':
|
||||
new_tally._mean = data['self']['mean'] + data['other']['mean']
|
||||
new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 +
|
||||
data['other']['std. dev.']**2)
|
||||
elif binary_op == '-':
|
||||
data = self._align_tally_data(other)
|
||||
new_tally._mean = data['self']['mean'] - data['other']['mean']
|
||||
new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 +
|
||||
data['other']['std. dev.']**2)
|
||||
elif binary_op == '*':
|
||||
data = self._align_tally_data(other)
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] * data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(self_rel_err**2 + other_rel_err**2)
|
||||
elif binary_op == '/':
|
||||
data = self._align_tally_data(other)
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] / data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(self_rel_err**2 + other_rel_err**2)
|
||||
elif binary_op == '^':
|
||||
data = self._align_tally_data(power)
|
||||
mean_ratio = data['other']['mean'] / data['self']['mean']
|
||||
first_term = mean_ratio * data['self']['std. dev.']
|
||||
second_term = \
|
||||
np.log(data['self']['mean']) * data['other']['std. dev.']
|
||||
new_tally._mean = data['self']['mean'] ** data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(first_term**2 + second_term**2)
|
||||
|
||||
if self.estimator == other.estimator:
|
||||
new_tally.estimator = self.estimator
|
||||
|
|
@ -1485,6 +1543,8 @@ class Tally(object):
|
|||
new_nuclide = CrossNuclide(self_nuclide, other_nuclide, binary_op)
|
||||
new_tally.add_nuclide(new_nuclide)
|
||||
|
||||
return new_tally
|
||||
|
||||
def _align_tally_data(self, other):
|
||||
"""Aligns data from two tallies for tally arithmetic.
|
||||
|
||||
|
|
@ -1508,6 +1568,7 @@ class Tally(object):
|
|||
A dictionary of dictionaries to "aligned" 'mean' and 'std. dev'
|
||||
NumPy arrays for each tally's data.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
self_mean = copy.deepcopy(self.mean)
|
||||
|
|
@ -1591,33 +1652,26 @@ class Tally(object):
|
|||
A new derived tally which is the sum of this tally and the other
|
||||
tally or scalar value in the addition.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
When this method is called before the Tally is populated with data
|
||||
by the StatePoint.read_results() method.
|
||||
|
||||
"""
|
||||
|
||||
# Check that results have been read
|
||||
if self.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
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 = Tally(name='derived')
|
||||
new_tally.with_batch_statistics = True
|
||||
|
||||
if isinstance(other, Tally):
|
||||
|
||||
# Check that results have been read
|
||||
if other.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
'since it does not contain any results.'.format(other.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._outer_product(other, new_tally, binary_op='+')
|
||||
data = self._align_tally_data(other)
|
||||
new_tally._mean = data['self']['mean'] + data['other']['mean']
|
||||
new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 +
|
||||
data['other']['std. dev.']**2)
|
||||
new_tally = self._outer_product(other, binary_op='+')
|
||||
|
||||
elif isinstance(other, Real):
|
||||
|
||||
new_tally = Tally(name='derived')
|
||||
new_tally.with_batch_statistics = True
|
||||
new_tally.name = self.name
|
||||
new_tally._mean = self._mean + other
|
||||
new_tally._std_dev = self._std_dev
|
||||
|
|
@ -1634,7 +1688,7 @@ class Tally(object):
|
|||
new_tally.add_score(score)
|
||||
|
||||
else:
|
||||
msg = 'Unable to add {0} to Tally ID={1}'.format(other, self.id)
|
||||
msg = 'Unable to add "{0}" to Tally ID="{1}"'.format(other, self.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
return new_tally
|
||||
|
|
@ -1667,33 +1721,24 @@ class Tally(object):
|
|||
A new derived tally which is the difference of this tally and the
|
||||
other tally or scalar value in the subtraction.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
When this method is called before the Tally is populated with data
|
||||
by the StatePoint.read_results() method.
|
||||
|
||||
"""
|
||||
|
||||
# Check that results have been read
|
||||
if self.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
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 = Tally(name='derived')
|
||||
new_tally.with_batch_statistics = True
|
||||
|
||||
if isinstance(other, Tally):
|
||||
|
||||
# Check that results have been read
|
||||
if other.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
'since it does not contain any results.'.format(other.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._outer_product(other, new_tally, binary_op='-')
|
||||
data = self._align_tally_data(other)
|
||||
new_tally._mean = data['self']['mean'] - data['other']['mean']
|
||||
new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 +
|
||||
data['other']['std. dev.']**2)
|
||||
new_tally = self._outer_product(other, binary_op='-')
|
||||
|
||||
elif isinstance(other, Real):
|
||||
|
||||
new_tally.name = self.name
|
||||
new_tally._mean = self._mean - other
|
||||
new_tally._std_dev = self._std_dev
|
||||
|
|
@ -1710,8 +1755,8 @@ class Tally(object):
|
|||
new_tally.add_score(score)
|
||||
|
||||
else:
|
||||
msg = 'Unable to subtract {0} from Tally ' \
|
||||
'ID={1}'.format(other, self.id)
|
||||
msg = 'Unable to subtract "{0}" from Tally ' \
|
||||
'ID="{1}"'.format(other, self.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
return new_tally
|
||||
|
|
@ -1744,35 +1789,24 @@ class Tally(object):
|
|||
A new derived tally which is the product of this tally and the
|
||||
other tally or scalar value in the multiplication.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
When this method is called before the Tally is populated with data
|
||||
by the StatePoint.read_results() method.
|
||||
|
||||
"""
|
||||
|
||||
# Check that results have been read
|
||||
if self.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
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 = Tally(name='derived')
|
||||
new_tally.with_batch_statistics = True
|
||||
|
||||
if isinstance(other, Tally):
|
||||
|
||||
# Check that results have been read
|
||||
if other.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
'since it does not contain any results.'.format(other.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._outer_product(other, new_tally, binary_op='*')
|
||||
data = self._align_tally_data(other)
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] * data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(self_rel_err**2 + other_rel_err**2)
|
||||
new_tally = self._outer_product(other, binary_op='*')
|
||||
|
||||
elif isinstance(other, Real):
|
||||
|
||||
new_tally.name = self.name
|
||||
new_tally._mean = self._mean * other
|
||||
new_tally._std_dev = self._std_dev * np.abs(other)
|
||||
|
|
@ -1789,8 +1823,8 @@ class Tally(object):
|
|||
new_tally.add_score(score)
|
||||
|
||||
else:
|
||||
msg = 'Unable to multiply Tally ID={1} ' \
|
||||
'by {0}'.format(self.id, other)
|
||||
msg = 'Unable to multiply Tally ID="{0}" ' \
|
||||
'by "{1}"'.format(self.id, other)
|
||||
raise ValueError(msg)
|
||||
|
||||
return new_tally
|
||||
|
|
@ -1823,35 +1857,24 @@ class Tally(object):
|
|||
A new derived tally which is the dividend of this tally and the
|
||||
other tally or scalar value in the division.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
When this method is called before the Tally is populated with data
|
||||
by the StatePoint.read_results() method.
|
||||
|
||||
"""
|
||||
|
||||
# Check that results have been read
|
||||
if self.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
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 = Tally(name='derived')
|
||||
new_tally.with_batch_statistics = True
|
||||
|
||||
if isinstance(other, Tally):
|
||||
|
||||
# Check that results have been read
|
||||
if other.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
'since it does not contain any results.'.format(other.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._outer_product(other, new_tally, binary_op='/')
|
||||
data = self._align_tally_data(other)
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] / data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(self_rel_err**2 + other_rel_err**2)
|
||||
new_tally = self._outer_product(other, binary_op='/')
|
||||
|
||||
elif isinstance(other, Real):
|
||||
|
||||
new_tally.name = self.name
|
||||
new_tally._mean = self._mean / other
|
||||
new_tally._std_dev = self._std_dev * np.abs(1. / other)
|
||||
|
|
@ -1868,8 +1891,8 @@ class Tally(object):
|
|||
new_tally.add_score(score)
|
||||
|
||||
else:
|
||||
msg = 'Unable to divide Tally ID={0} ' \
|
||||
'by {1}'.format(self.id, other)
|
||||
msg = 'Unable to divide Tally ID="{0}" ' \
|
||||
'by "{1}"'.format(self.id, other)
|
||||
raise ValueError(msg)
|
||||
|
||||
return new_tally
|
||||
|
|
@ -1902,36 +1925,24 @@ class Tally(object):
|
|||
A new derived tally which is this tally raised to the power of the
|
||||
other tally or scalar value in the exponentiation.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
When this method is called before the Tally is populated with data
|
||||
by the StatePoint.read_results() method.
|
||||
|
||||
"""
|
||||
|
||||
# Check that results have been read
|
||||
if self.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
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 = Tally(name='derived')
|
||||
new_tally.with_batch_statistics = True
|
||||
|
||||
if isinstance(power, Tally):
|
||||
|
||||
# Check that results have been read
|
||||
if power.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
'since it does not contain any results.'.format(power.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._outer_product(power, new_tally, binary_op='^')
|
||||
data = self._align_tally_data(power)
|
||||
mean_ratio = data['other']['mean'] / data['self']['mean']
|
||||
first_term = mean_ratio * data['self']['std. dev.']
|
||||
second_term = np.log(data['self']['mean']) * data['other']['std. dev.']
|
||||
new_tally._mean = data['self']['mean'] ** data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(first_term**2 + second_term**2)
|
||||
new_tally = self._outer_product(power, binary_op='^')
|
||||
|
||||
elif isinstance(power, Real):
|
||||
|
||||
new_tally.name = self.name
|
||||
new_tally._mean = self._mean ** power
|
||||
self_rel_err = self.std_dev / self.mean
|
||||
|
|
@ -1949,8 +1960,8 @@ class Tally(object):
|
|||
new_tally.add_score(score)
|
||||
|
||||
else:
|
||||
msg = 'Unable to raise Tally ID={0} to ' \
|
||||
'power {1}'.format(self.id, power)
|
||||
msg = 'Unable to raise Tally ID="{0}" to ' \
|
||||
'power "{1}"'.format(self.id, power)
|
||||
raise ValueError(msg)
|
||||
|
||||
return new_tally
|
||||
|
|
@ -2108,7 +2119,7 @@ class Tally(object):
|
|||
|
||||
# Ensure that StatePoint.read_results() was called first
|
||||
if self.sum is None:
|
||||
msg = 'Unable to use tally arithmetic with Tally ID={0} ' \
|
||||
msg = 'Unable to use tally arithmetic with Tally ID="{0}" ' \
|
||||
'since it does not contain any results.'.format(self.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue