mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Merge branch 'develop' into iso-lab
This commit is contained in:
commit
42a8dc3c05
5 changed files with 874 additions and 682 deletions
|
|
@ -6,10 +6,12 @@ from openmc.plots import *
|
|||
from openmc.settings import *
|
||||
from openmc.surface import *
|
||||
from openmc.universe import *
|
||||
from openmc.mesh import *
|
||||
from openmc.filter import *
|
||||
from openmc.trigger import *
|
||||
from openmc.tallies import *
|
||||
from openmc.cmfd import *
|
||||
from openmc.executor import *
|
||||
#from statepoint import *
|
||||
|
||||
try:
|
||||
from openmc.opencg_compatible import *
|
||||
|
|
|
|||
310
src/utils/openmc/filter.py
Normal file
310
src/utils/openmc/filter.py
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
import copy
|
||||
|
||||
from openmc import Mesh
|
||||
from openmc.checkvalue import *
|
||||
from openmc.constants import *
|
||||
|
||||
|
||||
class Filter(object):
|
||||
|
||||
# Initialize Filter class attributes
|
||||
def __init__(self, type=None, bins=None):
|
||||
|
||||
self.type = type
|
||||
self._num_bins = 0
|
||||
self.bins = bins
|
||||
self._mesh = None
|
||||
self._offset = -1
|
||||
self._stride = None
|
||||
|
||||
|
||||
def __eq__(self, filter2):
|
||||
|
||||
# Check type
|
||||
if self._type != filter2._type:
|
||||
return False
|
||||
|
||||
# Check number of bins
|
||||
elif len(self._bins) != len(filter2._bins):
|
||||
return False
|
||||
|
||||
# Check bin edges
|
||||
elif not np.allclose(self._bins, filter2._bins):
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def __hash__(self):
|
||||
hashable = []
|
||||
hashable.append(self._type)
|
||||
hashable.append(self._bins)
|
||||
return hash(tuple(hashable))
|
||||
|
||||
|
||||
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._bins = copy.deepcopy(self._bins, memo)
|
||||
clone._num_bins = self._num_bins
|
||||
clone._mesh = copy.deepcopy(self._mesh, memo)
|
||||
clone._offset = self._offset
|
||||
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 type(self):
|
||||
return self._type
|
||||
|
||||
|
||||
@property
|
||||
def bins(self):
|
||||
return self._bins
|
||||
|
||||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return self._num_bins
|
||||
|
||||
|
||||
@property
|
||||
def mesh(self):
|
||||
return self._mesh
|
||||
|
||||
|
||||
@property
|
||||
def offset(self):
|
||||
return self._offset
|
||||
|
||||
|
||||
@property
|
||||
def stride(self):
|
||||
return self._stride
|
||||
|
||||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
|
||||
if type is None:
|
||||
self._type = type
|
||||
|
||||
elif not type in FILTER_TYPES.values():
|
||||
msg = 'Unable to set Filter type to {0} since it is not one ' \
|
||||
'of the supported types'.format(type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._type = type
|
||||
|
||||
|
||||
@bins.setter
|
||||
def bins(self, bins):
|
||||
|
||||
if bins is None:
|
||||
self.num_bins = 0
|
||||
|
||||
elif self._type is None:
|
||||
msg = 'Unable to set bins for Filter to {0} since ' \
|
||||
'the Filter type has not yet been set'.format(bins)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the bin edge is a single value, it is a Cell, Material, etc. ID
|
||||
if not isinstance(bins, (tuple, list, np.ndarray)):
|
||||
bins = [bins]
|
||||
|
||||
# If the bins are in a collection, convert it to a list
|
||||
else:
|
||||
bins = list(bins)
|
||||
|
||||
if self._type in ['cell', 'cellborn', 'surface', 'material',
|
||||
'universe', 'distribcell']:
|
||||
|
||||
for edge in bins:
|
||||
|
||||
if not is_integer(edge):
|
||||
msg = 'Unable to add bin {0} to a {1} Filter since ' \
|
||||
'it is a non-integer'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif edge < 0:
|
||||
msg = 'Unable to add bin {0} to a {1} Filter since ' \
|
||||
'it is a negative integer'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
elif self._type in ['energy', 'energyout']:
|
||||
|
||||
for edge in bins:
|
||||
|
||||
if not is_integer(edge) and not is_float(edge):
|
||||
msg = 'Unable to add bin edge {0} to {1} Filter since ' \
|
||||
'it is a non-integer or floating point ' \
|
||||
'value'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif edge < 0.:
|
||||
msg = 'Unable to add bin edge {0} to {1} Filter since it ' \
|
||||
'is a negative value'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check that bin edges are monotonically increasing
|
||||
for index in range(len(bins)):
|
||||
|
||||
if index > 0 and bins[index] < bins[index-1]:
|
||||
msg = 'Unable to add bin edges {0} to {1} Filter since ' \
|
||||
'they are not monotonically ' \
|
||||
'increasing'.format(bins, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# mesh filters
|
||||
elif self._type == 'mesh':
|
||||
|
||||
if not len(bins) == 1:
|
||||
msg = 'Unable to add bins {0} to a mesh Filter since ' \
|
||||
'only a single mesh can be used per tally'.format(bins)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not is_integer(bins[0]):
|
||||
msg = 'Unable to add bin {0} to mesh Filter since it ' \
|
||||
'is a non-integer'.format(bins[0])
|
||||
raise ValueError(msg)
|
||||
|
||||
elif bins[0] < 0:
|
||||
msg = 'Unable to add bin {0} to mesh Filter since it ' \
|
||||
'is a negative integer'.format(bins[0])
|
||||
raise ValueError(msg)
|
||||
|
||||
# If all error checks passed, add bin edges
|
||||
self._bins = bins
|
||||
|
||||
|
||||
# FIXME
|
||||
@num_bins.setter
|
||||
def num_bins(self, num_bins):
|
||||
|
||||
if not is_integer(num_bins) or num_bins < 0:
|
||||
msg = 'Unable to set the number of bins {0} for a {1} Filter ' \
|
||||
'since it is not a positive ' \
|
||||
'integer'.format(num_bins, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._num_bins = num_bins
|
||||
|
||||
|
||||
@mesh.setter
|
||||
def mesh(self, mesh):
|
||||
|
||||
if not isinstance(mesh, Mesh):
|
||||
msg = 'Unable to set Mesh to {0} for Filter since it is not a ' \
|
||||
'Mesh object'.format(mesh)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._mesh = mesh
|
||||
self.type = 'mesh'
|
||||
self.bins = self._mesh._id
|
||||
|
||||
|
||||
@offset.setter
|
||||
def offset(self, offset):
|
||||
|
||||
if not is_integer(offset):
|
||||
msg = 'Unable to set offset {0} for a {1} Filter since it is a ' \
|
||||
'non-integer value'.format(offset, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._offset = offset
|
||||
|
||||
|
||||
@stride.setter
|
||||
def stride(self, stride):
|
||||
|
||||
if not is_integer(stride):
|
||||
msg = 'Unable to set stride {0} for a {1} Filter since it is a ' \
|
||||
'non-integer value'.format(stride, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
if stride < 0:
|
||||
msg = 'Unable to set stride {0} for a {1} Filter since it is a ' \
|
||||
'negative value'.format(stride, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._stride = stride
|
||||
|
||||
|
||||
def can_merge(self, filter):
|
||||
|
||||
if not isinstance(filter, Filter):
|
||||
return False
|
||||
|
||||
# Filters must be of the same type
|
||||
elif self.type != filter.type:
|
||||
return False
|
||||
|
||||
# Distribcell filters cannot have more than one bin
|
||||
elif self.type == 'distribcell':
|
||||
return False
|
||||
|
||||
# Mesh filters cannot have more than one bin
|
||||
elif self.type == 'mesh':
|
||||
return False
|
||||
|
||||
# Different energy bins are not mergeable
|
||||
elif 'energy' in self.type:
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def merge(self, filter):
|
||||
|
||||
if not self.can_merge(filter):
|
||||
msg = 'Unable to merge {0} with {1} filters'.format(self._type, filter._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(self._bins + filter._bins))
|
||||
merged_filter.bins = merged_bins
|
||||
merged_filter.num_bins = len(merged_bins)
|
||||
|
||||
return merged_filter
|
||||
|
||||
|
||||
def get_bin_index(self, bin):
|
||||
|
||||
try:
|
||||
index = self._bins.index(bin)
|
||||
|
||||
except ValueError:
|
||||
msg = 'Unable to get the bin index for Filter since {0} ' \
|
||||
'is not one of the bins'.format(bin)
|
||||
raise ValueError(msg)
|
||||
|
||||
return index
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
string = 'Filter\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self._bins)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offset)
|
||||
return string
|
||||
331
src/utils/openmc/mesh.py
Normal file
331
src/utils/openmc/mesh.py
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
import copy
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from openmc.checkvalue import *
|
||||
|
||||
|
||||
# "Static" variable for auto-generated and Mesh IDs
|
||||
AUTO_MESH_ID = 10000
|
||||
|
||||
|
||||
def reset_auto_mesh_id():
|
||||
global AUTO_MESH_ID
|
||||
AUTO_MESH_ID = 10000
|
||||
|
||||
|
||||
class Mesh(object):
|
||||
|
||||
def __init__(self, mesh_id=None, name=''):
|
||||
|
||||
# Initialize Mesh class attributes
|
||||
self.id = mesh_id
|
||||
self.name = name
|
||||
self._type = 'rectangular'
|
||||
self._dimension = None
|
||||
self._lower_left = None
|
||||
self._upper_right = None
|
||||
self._width = None
|
||||
|
||||
|
||||
def __eq__(self, mesh2):
|
||||
|
||||
# Check type
|
||||
if self._type != mesh2._type:
|
||||
return False
|
||||
|
||||
# Check dimension
|
||||
elif self._dimension != mesh2._dimension:
|
||||
return False
|
||||
|
||||
# Check width
|
||||
elif self._width != mesh2._width:
|
||||
return False
|
||||
|
||||
# Check lower left / upper right
|
||||
elif self._lower_left != mesh2._lower_left and \
|
||||
self._upper_right != mesh2._upper_right:
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
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._id = self._id
|
||||
clone._name = self._name
|
||||
clone._type = self._type
|
||||
clone._dimension = copy.deepcopy(self._dimension, memo)
|
||||
clone._lower_left = copy.deepcopy(self._lower_left, memo)
|
||||
clone._upper_right = copy.deepcopy(self._upper_right, memo)
|
||||
clone._width = copy.deepcopy(self._width, memo)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
return clone
|
||||
|
||||
# If this object has been copied before, return the first copy made
|
||||
else:
|
||||
return existing
|
||||
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self._type
|
||||
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self._dimension
|
||||
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return self._lower_left
|
||||
|
||||
|
||||
@property
|
||||
def upper_right(self):
|
||||
return self._upper_right
|
||||
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self._width
|
||||
|
||||
|
||||
@property
|
||||
def num_mesh_cells(self):
|
||||
return np.prod(self._dimension)
|
||||
|
||||
|
||||
@id.setter
|
||||
def id(self, mesh_id):
|
||||
|
||||
if mesh_id is None:
|
||||
global AUTO_MESH_ID
|
||||
self._id = AUTO_MESH_ID
|
||||
AUTO_MESH_ID += 1
|
||||
|
||||
# Check that the ID is an integer and wasn't already used
|
||||
elif not is_integer(mesh_id):
|
||||
msg = 'Unable to set a non-integer Mesh ID {0}'.format(mesh_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif mesh_id < 0:
|
||||
msg = 'Unable to set Mesh ID to {0} since it must be a ' \
|
||||
'non-negative integer'.format(mesh_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
self._id = mesh_id
|
||||
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
|
||||
if not is_string(name):
|
||||
msg = 'Unable to set name for Mesh ID={0} with a non-string ' \
|
||||
'value {1}'.format(self._id, name)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
self._name = name
|
||||
|
||||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
|
||||
if not is_string(type):
|
||||
msg = 'Unable to set Mesh ID={0} for type {1} which is not ' \
|
||||
'a string'.format(self._id, type)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not type in ['rectangular', 'hexagonal']:
|
||||
msg = 'Unable to set Mesh ID={0} for type {1} which since ' \
|
||||
'only rectangular and hexagonal meshes are ' \
|
||||
'supported '.format(self._id, type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._type = type
|
||||
|
||||
|
||||
@dimension.setter
|
||||
def dimension(self, dimension):
|
||||
|
||||
if not isinstance(dimension, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with dimension {1} which is ' \
|
||||
'not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, dimension)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif len(dimension) != 2 and len(dimension) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with dimension {1} since it ' \
|
||||
'must include 2 or 3 dimensions'.format(self._id, dimension)
|
||||
raise ValueError(msg)
|
||||
|
||||
for dim in dimension:
|
||||
|
||||
if not is_integer(dim):
|
||||
msg = 'Unable to set Mesh ID={0} with dimension {1} which ' \
|
||||
'is a non-integer'.format(self._id, dim)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._dimension = dimension
|
||||
|
||||
|
||||
@lower_left.setter
|
||||
def lower_left(self, lower_left):
|
||||
|
||||
if not isinstance(lower_left, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with lower_left {1} which is ' \
|
||||
'not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, lower_left)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif len(lower_left) != 2 and len(lower_left) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with lower_left {1} since it ' \
|
||||
'must include 2 or 3 dimensions'.format(self._id, lower_left)
|
||||
raise ValueError(msg)
|
||||
|
||||
for coord in lower_left:
|
||||
|
||||
if not is_integer(coord) and not is_float(coord):
|
||||
msg = 'Unable to set Mesh ID={0} with lower_left {1} which ' \
|
||||
'is neither neither an integer nor a floating point ' \
|
||||
'value'.format(self._id, coord)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._lower_left = lower_left
|
||||
|
||||
|
||||
@upper_right.setter
|
||||
def upper_right(self, upper_right):
|
||||
|
||||
if not isinstance(upper_right, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
|
||||
'is not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, upper_right)
|
||||
raise ValueError(msg)
|
||||
|
||||
if len(upper_right) != 2 and len(upper_right) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with upper_right {1} since it ' \
|
||||
'must include 2 or 3 dimensions'.format(self._id, upper_right)
|
||||
raise ValueError(msg)
|
||||
|
||||
for coord in upper_right:
|
||||
|
||||
if not is_integer(coord) and not is_float(coord):
|
||||
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
|
||||
'is neither an integer nor a floating point ' \
|
||||
'value'.format(self._id, coord)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._upper_right = upper_right
|
||||
|
||||
|
||||
@width.setter
|
||||
def width(self, width):
|
||||
|
||||
if not width is None:
|
||||
|
||||
if not isinstance(width, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with width {1} which ' \
|
||||
'is not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, width)
|
||||
raise ValueError(msg)
|
||||
|
||||
if len(width) != 2 and len(width) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with width {1} since it must ' \
|
||||
'include 2 or 3 dimensions'.format(self._id, width)
|
||||
raise ValueError(msg)
|
||||
|
||||
for dim in width:
|
||||
|
||||
if not is_integer(dim) and not is_float(dim):
|
||||
msg = 'Unable to set Mesh ID={0} with width {1} which is ' \
|
||||
'neither an integer nor a floating point ' \
|
||||
'value'.format(self._id, width)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._width = width
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
string = 'Mesh\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tBasis', '=\t', self._dimension)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tWidth', '=\t', self._lower_left)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOrigin', '=\t', self._upper_right)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._width)
|
||||
return string
|
||||
|
||||
|
||||
def get_mesh_xml(self):
|
||||
|
||||
element = ET.Element("mesh")
|
||||
element.set("id", str(self._id))
|
||||
element.set("type", self._type)
|
||||
|
||||
if len(self._dimension) == 2:
|
||||
subelement = ET.SubElement(element, "dimension")
|
||||
subelement.text = '{0} {1}'.format(self._dimension[0],
|
||||
self._dimension[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "dimension")
|
||||
subelement.text = '{0} {1} {2}'.format(self._dimension[0],
|
||||
self._dimension[1],
|
||||
self._dimension[2])
|
||||
|
||||
if len(self._lower_left) == 2:
|
||||
subelement = ET.SubElement(element, "lower_left")
|
||||
subelement.text = '{0} {1}'.format(self._lower_left[0],
|
||||
self._lower_left[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "lower_left")
|
||||
subelement.text = '{0} {1} {2}'.format(self._lower_left[0],
|
||||
self._lower_left[1],
|
||||
self._lower_left[2])
|
||||
|
||||
if not self._upper_right is None:
|
||||
if len(self._upper_right) == 2:
|
||||
subelement = ET.SubElement(element, "upper_right")
|
||||
subelement.text = '{0} {1}'.format(self._upper_right[0],
|
||||
self._upper_right[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "upper_right")
|
||||
subelement.text = '{0} {1} {2}'.format(self._upper_right[0],
|
||||
self._upper_right[1],
|
||||
self._upper_right[2])
|
||||
|
||||
if not self._width is None:
|
||||
if len(self._width) == 2:
|
||||
subelement = ET.SubElement(element, "width")
|
||||
subelement.text = '{0} {1}'.format(self._width[0],
|
||||
self._width[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "width")
|
||||
subelement.text = '{0} {1} {2}'.format(self._width[0],
|
||||
self._width[1],
|
||||
self._width[2])
|
||||
|
||||
return element
|
||||
|
|
@ -2,15 +2,13 @@ import copy
|
|||
import os
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from openmc import Nuclide
|
||||
from openmc import Mesh, Filter, Trigger, Nuclide
|
||||
from openmc.clean_xml import *
|
||||
from openmc.checkvalue import *
|
||||
from openmc.constants import *
|
||||
|
||||
|
||||
# "Static" variables for auto-generated Tally and Mesh IDs
|
||||
# "Static" variable for auto-generated Tally IDs
|
||||
AUTO_TALLY_ID = 10000
|
||||
AUTO_MESH_ID = 10000
|
||||
|
||||
|
||||
def reset_auto_tally_id():
|
||||
|
|
@ -18,680 +16,6 @@ def reset_auto_tally_id():
|
|||
AUTO_TALLY_ID = 10000
|
||||
|
||||
|
||||
def reset_auto_mesh_id():
|
||||
global AUTO_MESH_ID
|
||||
AUTO_MESH_ID = 10000
|
||||
|
||||
|
||||
class Filter(object):
|
||||
|
||||
# Initialize Filter class attributes
|
||||
def __init__(self, type=None, bins=None):
|
||||
|
||||
self.type = type
|
||||
self.bins = bins
|
||||
self._mesh = None
|
||||
self._offset = -1
|
||||
self._stride = None
|
||||
|
||||
|
||||
def __eq__(self, filter2):
|
||||
|
||||
# Check type
|
||||
if self._type != filter2._type:
|
||||
return False
|
||||
|
||||
# Check number of bins
|
||||
elif len(self._bins) != len(filter2._bins):
|
||||
return False
|
||||
|
||||
# Check bin edges
|
||||
elif not np.allclose(self._bins, filter2._bins):
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def __hash__(self):
|
||||
hashable = []
|
||||
hashable.append(self._type)
|
||||
hashable.append(self._bins)
|
||||
return hash(tuple(hashable))
|
||||
|
||||
|
||||
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._bins = copy.deepcopy(self._bins, memo)
|
||||
clone._num_bins = self._num_bins
|
||||
clone._mesh = copy.deepcopy(self._mesh, memo)
|
||||
clone._offset = self._offset
|
||||
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 type(self):
|
||||
return self._type
|
||||
|
||||
|
||||
@property
|
||||
def bins(self):
|
||||
return self._bins
|
||||
|
||||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return self._num_bins
|
||||
|
||||
|
||||
@property
|
||||
def mesh(self):
|
||||
return self._mesh
|
||||
|
||||
|
||||
@property
|
||||
def offset(self):
|
||||
return self._offset
|
||||
|
||||
|
||||
@property
|
||||
def stride(self):
|
||||
return self._stride
|
||||
|
||||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
|
||||
if type is None:
|
||||
self._type = type
|
||||
|
||||
elif not type in FILTER_TYPES.values():
|
||||
msg = 'Unable to set Filter type to {0} since it is not one ' \
|
||||
'of the supported types'.format(type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._type = type
|
||||
|
||||
|
||||
@bins.setter
|
||||
def bins(self, bins):
|
||||
|
||||
if bins is None:
|
||||
self.num_bins = 0
|
||||
|
||||
elif self._type is None:
|
||||
msg = 'Unable to set bins for Filter to {0} since ' \
|
||||
'the Filter type has not yet been set'.format(bins)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the bin edge is a single value, it is a Cell, Material, etc. ID
|
||||
if not isinstance(bins, (tuple, list, np.ndarray)):
|
||||
bins = [bins]
|
||||
|
||||
# If the bins are in a collection, convert it to a list
|
||||
else:
|
||||
bins = list(bins)
|
||||
|
||||
if self._type in ['cell', 'cellborn', 'surface', 'material',
|
||||
'universe', 'distribcell']:
|
||||
|
||||
for edge in bins:
|
||||
|
||||
if not is_integer(edge):
|
||||
msg = 'Unable to add bin {0} to a {1} Filter since ' \
|
||||
'it is a non-integer'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif edge < 0:
|
||||
msg = 'Unable to add bin {0} to a {1} Filter since ' \
|
||||
'it is a negative integer'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
elif self._type in ['energy', 'energyout']:
|
||||
|
||||
for edge in bins:
|
||||
|
||||
if not is_integer(edge) and not is_float(edge):
|
||||
msg = 'Unable to add bin edge {0} to {1} Filter since ' \
|
||||
'it is a non-integer or floating point ' \
|
||||
'value'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif edge < 0.:
|
||||
msg = 'Unable to add bin edge {0} to {1} Filter since it ' \
|
||||
'is a negative value'.format(edge, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check that bin edges are monotonically increasing
|
||||
for index in range(len(bins)):
|
||||
|
||||
if index > 0 and bins[index] < bins[index-1]:
|
||||
msg = 'Unable to add bin edges {0} to {1} Filter since ' \
|
||||
'they are not monotonically ' \
|
||||
'increasing'.format(bins, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
# mesh filters
|
||||
elif self._type == 'mesh':
|
||||
|
||||
if not len(bins) == 1:
|
||||
msg = 'Unable to add bins {0} to a mesh Filter since ' \
|
||||
'only a single mesh can be used per tally'.format(bins)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not is_integer(bins[0]):
|
||||
msg = 'Unable to add bin {0} to mesh Filter since it ' \
|
||||
'is a non-integer'.format(bins[0])
|
||||
raise ValueError(msg)
|
||||
|
||||
elif bins[0] < 0:
|
||||
msg = 'Unable to add bin {0} to mesh Filter since it ' \
|
||||
'is a negative integer'.format(bins[0])
|
||||
raise ValueError(msg)
|
||||
|
||||
# If all error checks passed, add bin edges
|
||||
self._bins = bins
|
||||
|
||||
|
||||
# FIXME
|
||||
@num_bins.setter
|
||||
def num_bins(self, num_bins):
|
||||
|
||||
if not is_integer(num_bins) or num_bins < 0:
|
||||
msg = 'Unable to set the number of bins {0} for a {1} Filter ' \
|
||||
'since it is not a positive ' \
|
||||
'integer'.format(num_bins, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._num_bins = num_bins
|
||||
|
||||
|
||||
@mesh.setter
|
||||
def mesh(self, mesh):
|
||||
|
||||
if not isinstance(mesh, Mesh):
|
||||
msg = 'Unable to set Mesh to {0} for Filter since it is not a ' \
|
||||
'Mesh object'.format(mesh)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._mesh = mesh
|
||||
self.type = 'mesh'
|
||||
self.bins = self._mesh._id
|
||||
|
||||
|
||||
@offset.setter
|
||||
def offset(self, offset):
|
||||
|
||||
if not is_integer(offset):
|
||||
msg = 'Unable to set offset {0} for a {1} Filter since it is a ' \
|
||||
'non-integer value'.format(offset, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._offset = offset
|
||||
|
||||
|
||||
@stride.setter
|
||||
def stride(self, stride):
|
||||
|
||||
if not is_integer(stride):
|
||||
msg = 'Unable to set stride {0} for a {1} Filter since it is a ' \
|
||||
'non-integer value'.format(stride, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
if stride < 0:
|
||||
msg = 'Unable to set stride {0} for a {1} Filter since it is a ' \
|
||||
'negative value'.format(stride, self._type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._stride = stride
|
||||
|
||||
|
||||
def get_bin_index(self, bin):
|
||||
|
||||
try:
|
||||
index = self._bins.index(bin)
|
||||
|
||||
except ValueError:
|
||||
msg = 'Unable to get the bin index for Filter since {0} ' \
|
||||
'is not one of the bins'.format(bin)
|
||||
raise ValueError(msg)
|
||||
|
||||
return index
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
string = 'Filter\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self._bins)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offset)
|
||||
return string
|
||||
|
||||
|
||||
|
||||
class Mesh(object):
|
||||
|
||||
def __init__(self, mesh_id=None, name=''):
|
||||
|
||||
# Initialize Mesh class attributes
|
||||
self.id = mesh_id
|
||||
self.name = name
|
||||
self._type = 'rectangular'
|
||||
self._dimension = None
|
||||
self._lower_left = None
|
||||
self._upper_right = None
|
||||
self._width = None
|
||||
|
||||
|
||||
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._id = self._id
|
||||
clone._name = self._name
|
||||
clone._type = self._type
|
||||
clone._dimension = copy.deepcopy(self._dimension, memo)
|
||||
clone._lower_left = copy.deepcopy(self._lower_left, memo)
|
||||
clone._upper_right = copy.deepcopy(self._upper_right, memo)
|
||||
clone._width = copy.deepcopy(self._width, memo)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
return clone
|
||||
|
||||
# If this object has been copied before, return the first copy made
|
||||
else:
|
||||
return existing
|
||||
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self._type
|
||||
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self._dimension
|
||||
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return self._lower_left
|
||||
|
||||
|
||||
@property
|
||||
def upper_right(self):
|
||||
return self._upper_right
|
||||
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self._width
|
||||
|
||||
|
||||
@property
|
||||
def num_mesh_cells(self):
|
||||
return np.prod(self._dimension)
|
||||
|
||||
|
||||
@id.setter
|
||||
def id(self, mesh_id):
|
||||
|
||||
if mesh_id is None:
|
||||
global AUTO_MESH_ID
|
||||
self._id = AUTO_MESH_ID
|
||||
AUTO_MESH_ID += 1
|
||||
|
||||
# Check that the ID is an integer and wasn't already used
|
||||
elif not is_integer(mesh_id):
|
||||
msg = 'Unable to set a non-integer Mesh ID {0}'.format(mesh_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif mesh_id < 0:
|
||||
msg = 'Unable to set Mesh ID to {0} since it must be a ' \
|
||||
'non-negative integer'.format(mesh_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
self._id = mesh_id
|
||||
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
|
||||
if not is_string(name):
|
||||
msg = 'Unable to set name for Mesh ID={0} with a non-string ' \
|
||||
'value {1}'.format(self._id, name)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
self._name = name
|
||||
|
||||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
|
||||
if not is_string(type):
|
||||
msg = 'Unable to set Mesh ID={0} for type {1} which is not ' \
|
||||
'a string'.format(self._id, type)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not type in ['rectangular', 'hexagonal']:
|
||||
msg = 'Unable to set Mesh ID={0} for type {1} which since ' \
|
||||
'only rectangular and hexagonal meshes are ' \
|
||||
'supported '.format(self._id, type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._type = type
|
||||
|
||||
|
||||
@dimension.setter
|
||||
def dimension(self, dimension):
|
||||
|
||||
if not isinstance(dimension, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with dimension {1} which is ' \
|
||||
'not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, dimension)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif len(dimension) != 2 and len(dimension) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with dimension {1} since it ' \
|
||||
'must include 2 or 3 dimensions'.format(self._id, dimension)
|
||||
raise ValueError(msg)
|
||||
|
||||
for dim in dimension:
|
||||
|
||||
if not is_integer(dim):
|
||||
msg = 'Unable to set Mesh ID={0} with dimension {1} which ' \
|
||||
'is a non-integer'.format(self._id, dim)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._dimension = dimension
|
||||
|
||||
|
||||
@lower_left.setter
|
||||
def lower_left(self, lower_left):
|
||||
|
||||
if not isinstance(lower_left, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with lower_left {1} which is ' \
|
||||
'not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, lower_left)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif len(lower_left) != 2 and len(lower_left) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with lower_left {1} since it ' \
|
||||
'must include 2 or 3 dimensions'.format(self._id, lower_left)
|
||||
raise ValueError(msg)
|
||||
|
||||
for coord in lower_left:
|
||||
|
||||
if not is_integer(coord) and not is_float(coord):
|
||||
msg = 'Unable to set Mesh ID={0} with lower_left {1} which ' \
|
||||
'is neither neither an integer nor a floating point ' \
|
||||
'value'.format(self._id, coord)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._lower_left = lower_left
|
||||
|
||||
|
||||
@upper_right.setter
|
||||
def upper_right(self, upper_right):
|
||||
|
||||
if not isinstance(upper_right, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
|
||||
'is not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, upper_right)
|
||||
raise ValueError(msg)
|
||||
|
||||
if len(upper_right) != 2 and len(upper_right) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with upper_right {1} since it ' \
|
||||
'must include 2 or 3 dimensions'.format(self._id, upper_right)
|
||||
raise ValueError(msg)
|
||||
|
||||
for coord in upper_right:
|
||||
|
||||
if not is_integer(coord) and not is_float(coord):
|
||||
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
|
||||
'is neither an integer nor a floating point ' \
|
||||
'value'.format(self._id, coord)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._upper_right = upper_right
|
||||
|
||||
|
||||
@width.setter
|
||||
def width(self, width):
|
||||
|
||||
if not width is None:
|
||||
|
||||
if not isinstance(width, (tuple, list, np.ndarray)):
|
||||
msg = 'Unable to set Mesh ID={0} with width {1} which ' \
|
||||
'is not a Python list, tuple or NumPy ' \
|
||||
'array'.format(self._id, width)
|
||||
raise ValueError(msg)
|
||||
|
||||
if len(width) != 2 and len(width) != 3:
|
||||
msg = 'Unable to set Mesh ID={0} with width {1} since it must ' \
|
||||
'include 2 or 3 dimensions'.format(self._id, width)
|
||||
raise ValueError(msg)
|
||||
|
||||
for dim in width:
|
||||
|
||||
if not is_integer(dim) and not is_float(dim):
|
||||
msg = 'Unable to set Mesh ID={0} with width {1} which is ' \
|
||||
'neither an integer nor a floating point ' \
|
||||
'value'.format(self._id, width)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._width = width
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
string = 'Mesh\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tBasis', '=\t', self._dimension)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tWidth', '=\t', self._lower_left)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOrigin', '=\t', self._upper_right)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._width)
|
||||
return string
|
||||
|
||||
|
||||
def get_mesh_xml(self):
|
||||
|
||||
element = ET.Element("mesh")
|
||||
element.set("id", str(self._id))
|
||||
element.set("type", self._type)
|
||||
|
||||
if len(self._dimension) == 2:
|
||||
subelement = ET.SubElement(element, "dimension")
|
||||
subelement.text = '{0} {1}'.format(self._dimension[0],
|
||||
self._dimension[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "dimension")
|
||||
subelement.text = '{0} {1} {2}'.format(self._dimension[0],
|
||||
self._dimension[1],
|
||||
self._dimension[2])
|
||||
|
||||
if len(self._lower_left) == 2:
|
||||
subelement = ET.SubElement(element, "lower_left")
|
||||
subelement.text = '{0} {1}'.format(self._lower_left[0],
|
||||
self._lower_left[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "lower_left")
|
||||
subelement.text = '{0} {1} {2}'.format(self._lower_left[0],
|
||||
self._lower_left[1],
|
||||
self._lower_left[2])
|
||||
|
||||
if not self._upper_right is None:
|
||||
if len(self._upper_right) == 2:
|
||||
subelement = ET.SubElement(element, "upper_right")
|
||||
subelement.text = '{0} {1}'.format(self._upper_right[0],
|
||||
self._upper_right[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "upper_right")
|
||||
subelement.text = '{0} {1} {2}'.format(self._upper_right[0],
|
||||
self._upper_right[1],
|
||||
self._upper_right[2])
|
||||
|
||||
if not self._width is None:
|
||||
if len(self._width) == 2:
|
||||
subelement = ET.SubElement(element, "width")
|
||||
subelement.text = '{0} {1}'.format(self._width[0],
|
||||
self._width[1])
|
||||
else:
|
||||
subelement = ET.SubElement(element, "width")
|
||||
subelement.text = '{0} {1} {2}'.format(self._width[0],
|
||||
self._width[1],
|
||||
self._width[2])
|
||||
|
||||
return element
|
||||
|
||||
|
||||
class Trigger(object):
|
||||
|
||||
def __init__(self, trigger_type, threshold):
|
||||
|
||||
# Initialize Mesh class attributes
|
||||
self.trigger_type = trigger_type
|
||||
self.threshold = threshold
|
||||
self._scores = []
|
||||
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
|
||||
existing = memo.get(id(self))
|
||||
|
||||
# If this is first time we have tried to copy this object, create a copy
|
||||
if existing is None:
|
||||
|
||||
clone = type(self).__new__(type(self))
|
||||
clone._trigger_type = self._trigger_type
|
||||
clone._threshold = self._threshold
|
||||
|
||||
clone._scores = []
|
||||
for score in self._scores:
|
||||
clone.add_score(score)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
return clone
|
||||
|
||||
# If this object has been copied before, return the first copy made
|
||||
else:
|
||||
return existing
|
||||
|
||||
|
||||
@property
|
||||
def trigger_type(self):
|
||||
return self._trigger_type
|
||||
|
||||
|
||||
@property
|
||||
def threshold(self):
|
||||
return self._threshold
|
||||
|
||||
|
||||
@property
|
||||
def scores(self):
|
||||
return self._scores
|
||||
|
||||
|
||||
@trigger_type.setter
|
||||
def trigger_type(self, trigger_type):
|
||||
|
||||
if not trigger_type in ['variance', 'std_dev', 'rel_err']:
|
||||
msg = 'Unable to create a tally trigger with ' \
|
||||
'type {0}'.format(trigger_type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._trigger_type = trigger_type
|
||||
|
||||
|
||||
@threshold.setter
|
||||
def threshold(self, threshold):
|
||||
|
||||
if not is_float(threshold):
|
||||
msg = 'Unable to set a tally trigger threshold with ' \
|
||||
'threshold {0}'.format(threshold)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._threshold = threshold
|
||||
|
||||
|
||||
def add_score(self, score):
|
||||
|
||||
if not is_string(score):
|
||||
msg = 'Unable to add score {0} to tally trigger since ' \
|
||||
'it is not a string'.format(score)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the score is already in the Tally, don't add it again
|
||||
if score in self._scores:
|
||||
return
|
||||
else:
|
||||
self._scores.append(score)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
string = 'Trigger\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._trigger_type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tThreshold', '=\t', self._threshold)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tScores', '=\t', self._scores)
|
||||
return string
|
||||
|
||||
|
||||
def get_trigger_xml(self, element):
|
||||
|
||||
subelement = ET.SubElement(element, "trigger")
|
||||
subelement.set("type", self._trigger_type)
|
||||
subelement.set("threshold", str(self._threshold))
|
||||
|
||||
# Scores
|
||||
if len(self._scores) != 0:
|
||||
|
||||
scores = ''
|
||||
for score in self._scores:
|
||||
scores += '{0} '.format(score)
|
||||
|
||||
scores.rstrip(' ')
|
||||
subelement.set("scores", scores)
|
||||
|
||||
|
||||
class Tally(object):
|
||||
|
||||
def __init__(self, tally_id=None, name=''):
|
||||
|
|
@ -746,7 +70,7 @@ class Tally(object):
|
|||
|
||||
clone._triggers = []
|
||||
for trigger in self._triggers:
|
||||
trigger.add_trigger(trigger)
|
||||
clone.add_trigger(trigger)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
|
|
@ -1100,6 +424,74 @@ class Tally(object):
|
|||
return string
|
||||
|
||||
|
||||
def can_merge(self, tally):
|
||||
|
||||
if not isinstance(tally, Tally):
|
||||
return False
|
||||
|
||||
# Must have same estimator
|
||||
if self._estimator != tally._estimator:
|
||||
return False
|
||||
|
||||
# Must have same nuclides
|
||||
if len(self._nuclides) != len(tally._nuclides):
|
||||
return False
|
||||
|
||||
for nuclide in self._nuclides:
|
||||
if not nuclide in tally._nuclides:
|
||||
return False
|
||||
|
||||
# Must have same or mergeable filters
|
||||
if len(self._filters) != len(tally._filters):
|
||||
return False
|
||||
|
||||
# Look to see if all filters are the same, or one or more can be merged
|
||||
for filter1 in self._filters:
|
||||
mergeable_filter = False
|
||||
|
||||
for filter2 in tally._filters:
|
||||
if filter1 == filter2 or filter1.can_merge(filter2):
|
||||
mergeable_filter = True
|
||||
break
|
||||
|
||||
# If no mergeable filter was found, the tallies are not mergable
|
||||
if not mergeable_filter:
|
||||
return False
|
||||
|
||||
# Tallies are mergeable if all conditional checks passed
|
||||
return True
|
||||
|
||||
|
||||
def merge(self, tally):
|
||||
|
||||
if not self.can_merge(tally):
|
||||
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
|
||||
merged_tally = copy.deepcopy(self)
|
||||
|
||||
# Differentiate Tally with a new auto-generated Tally ID
|
||||
merged_tally.id = None
|
||||
|
||||
# Merge filters
|
||||
for i, filter1 in enumerate(merged_tally._filters):
|
||||
for filter2 in tally._filters:
|
||||
if filter1 != filter2 and filter1.can_merge(filter2):
|
||||
merged_filter = filter1.merge(filter2)
|
||||
merged_tally._filters[i] = merged_filter
|
||||
break
|
||||
|
||||
# Add scores from second tally to merged tally
|
||||
for score in tally._scores:
|
||||
merged_tally.add_score(score)
|
||||
|
||||
# Add triggers from second tally to merged tally
|
||||
for trigger in tally._triggers:
|
||||
merged_tally.add_trigger(trigger)
|
||||
|
||||
return merged_tally
|
||||
|
||||
|
||||
def get_tally_xml(self):
|
||||
|
||||
|
|
@ -1443,19 +835,62 @@ class TalliesFile(object):
|
|||
self._tallies_file = ET.Element("tallies")
|
||||
|
||||
|
||||
def add_tally(self, tally):
|
||||
def add_tally(self, tally, merge=False):
|
||||
|
||||
if not isinstance(tally, Tally):
|
||||
msg = 'Unable to add a non-Tally {0} to the TalliesFile'.format(tally)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._tallies.append(tally)
|
||||
if merge:
|
||||
merged = False
|
||||
|
||||
# Look for a tally to merge with this one
|
||||
for i, tally2 in enumerate(self._tallies):
|
||||
|
||||
# If a mergeable tally is found
|
||||
if tally2.can_merge(tally):
|
||||
|
||||
# Replace tally 2 with the merged tally
|
||||
merged_tally = tally2.merge(tally)
|
||||
self._tallies[i] = merged_tally
|
||||
merged = True
|
||||
break
|
||||
|
||||
# If not mergeable tally was found, simply add this tally
|
||||
if not merged:
|
||||
self._tallies.append(tally)
|
||||
|
||||
else:
|
||||
self._tallies.append(tally)
|
||||
|
||||
|
||||
def remove_tally(self, tally):
|
||||
self._tallies.remove(tally)
|
||||
|
||||
|
||||
def merge_tallies(self):
|
||||
|
||||
for i, tally1 in enumerate(self._tallies):
|
||||
for j, tally2 in enumerate(self._tallies):
|
||||
|
||||
# Do not merge the same tally with itself
|
||||
if i == j:
|
||||
continue
|
||||
|
||||
# If the two tallies are mergeable
|
||||
if tally1.can_merge(tally2):
|
||||
|
||||
# Replace tally 1 with the merged tally
|
||||
merged_tally = tally1.merge(tally2)
|
||||
self._tallies[i] = merged_tally
|
||||
|
||||
# Remove tally 2 since it is no longer needed
|
||||
self._tallies.pop(j)
|
||||
|
||||
# Continue iterating from the first loop
|
||||
break
|
||||
|
||||
|
||||
def add_mesh(self, mesh):
|
||||
|
||||
if not isinstance(mesh, Mesh):
|
||||
|
|
|
|||
114
src/utils/openmc/trigger.py
Normal file
114
src/utils/openmc/trigger.py
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
from xml.etree import ElementTree as ET
|
||||
|
||||
from openmc.checkvalue import *
|
||||
|
||||
|
||||
class Trigger(object):
|
||||
|
||||
def __init__(self, trigger_type, threshold):
|
||||
|
||||
# Initialize Mesh class attributes
|
||||
self.trigger_type = trigger_type
|
||||
self.threshold = threshold
|
||||
self._scores = []
|
||||
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
|
||||
existing = memo.get(id(self))
|
||||
|
||||
# If this is first time we have tried to copy this object, create a copy
|
||||
if existing is None:
|
||||
|
||||
clone = type(self).__new__(type(self))
|
||||
clone._trigger_type = self._trigger_type
|
||||
clone._threshold = self._threshold
|
||||
|
||||
clone._scores = []
|
||||
for score in self._scores:
|
||||
clone.add_score(score)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
return clone
|
||||
|
||||
# If this object has been copied before, return the first copy made
|
||||
else:
|
||||
return existing
|
||||
|
||||
|
||||
@property
|
||||
def trigger_type(self):
|
||||
return self._trigger_type
|
||||
|
||||
|
||||
@property
|
||||
def threshold(self):
|
||||
return self._threshold
|
||||
|
||||
|
||||
@property
|
||||
def scores(self):
|
||||
return self._scores
|
||||
|
||||
|
||||
@trigger_type.setter
|
||||
def trigger_type(self, trigger_type):
|
||||
|
||||
if not trigger_type in ['variance', 'std_dev', 'rel_err']:
|
||||
msg = 'Unable to create a tally trigger with ' \
|
||||
'type {0}'.format(trigger_type)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._trigger_type = trigger_type
|
||||
|
||||
|
||||
@threshold.setter
|
||||
def threshold(self, threshold):
|
||||
|
||||
if not is_float(threshold):
|
||||
msg = 'Unable to set a tally trigger threshold with ' \
|
||||
'threshold {0}'.format(threshold)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._threshold = threshold
|
||||
|
||||
|
||||
def add_score(self, score):
|
||||
|
||||
if not is_string(score):
|
||||
msg = 'Unable to add score {0} to tally trigger since ' \
|
||||
'it is not a string'.format(score)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the score is already in the Tally, don't add it again
|
||||
if score in self._scores:
|
||||
return
|
||||
else:
|
||||
self._scores.append(score)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
string = 'Trigger\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._trigger_type)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tThreshold', '=\t', self._threshold)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tScores', '=\t', self._scores)
|
||||
return string
|
||||
|
||||
|
||||
def get_trigger_xml(self, element):
|
||||
|
||||
subelement = ET.SubElement(element, "trigger")
|
||||
subelement.set("type", self._trigger_type)
|
||||
subelement.set("threshold", str(self._threshold))
|
||||
|
||||
# Scores
|
||||
if len(self._scores) != 0:
|
||||
|
||||
scores = ''
|
||||
for score in self._scores:
|
||||
scores += '{0} '.format(score)
|
||||
|
||||
scores.rstrip(' ')
|
||||
subelement.set("scores", scores)
|
||||
Loading…
Add table
Add a link
Reference in a new issue