mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Force all filters to have a .bins attribute that makes sense
This commit is contained in:
parent
39bbb2de46
commit
d41ac48467
10 changed files with 350 additions and 736 deletions
|
|
@ -196,7 +196,7 @@ def check_less_than(name, value, maximum, equality=False):
|
|||
maximum : object
|
||||
Maximum value to check against
|
||||
equality : bool, optional
|
||||
Whether equality is allowed. Defaluts to False.
|
||||
Whether equality is allowed. Defaults to False.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ def check_greater_than(name, value, minimum, equality=False):
|
|||
minimum : object
|
||||
Minimum value to check against
|
||||
equality : bool, optional
|
||||
Whether equality is allowed. Defaluts to False.
|
||||
Whether equality is allowed. Defaults to False.
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
|||
695
openmc/filter.py
695
openmc/filter.py
|
|
@ -3,6 +3,7 @@ from collections import Iterable, OrderedDict
|
|||
import copy
|
||||
from functools import reduce
|
||||
import hashlib
|
||||
from itertools import product
|
||||
from numbers import Real, Integral
|
||||
import operator
|
||||
from xml.etree import ElementTree as ET
|
||||
|
|
@ -192,9 +193,6 @@ class Filter(IDManagerMixin, metaclass=FilterMeta):
|
|||
|
||||
@bins.setter
|
||||
def bins(self, bins):
|
||||
# Format the bins as a 1D numpy array.
|
||||
bins = np.atleast_1d(bins)
|
||||
|
||||
# Check the bin values.
|
||||
self.check_bins(bins)
|
||||
|
||||
|
|
@ -221,8 +219,6 @@ class Filter(IDManagerMixin, metaclass=FilterMeta):
|
|||
XML element containing filter data
|
||||
|
||||
"""
|
||||
|
||||
|
||||
element = ET.Element('filter')
|
||||
element.set('id', str(self.id))
|
||||
element.set('type', self.short_name.lower())
|
||||
|
|
@ -332,7 +328,10 @@ class Filter(IDManagerMixin, metaclass=FilterMeta):
|
|||
'is not one of the bins'.format(filter_bin)
|
||||
raise ValueError(msg)
|
||||
|
||||
return np.where(self.bins == filter_bin)[0][0]
|
||||
if isinstance(self.bins, np.ndarray):
|
||||
return np.where(self.bins == filter_bin)[0][0]
|
||||
else:
|
||||
return self.bins.index(filter_bin)
|
||||
|
||||
def get_bin(self, bin_index):
|
||||
"""Returns the filter bin for some filter bin index.
|
||||
|
|
@ -423,25 +422,24 @@ class Filter(IDManagerMixin, metaclass=FilterMeta):
|
|||
|
||||
|
||||
class WithIDFilter(Filter):
|
||||
"""Abstract parent for filters of types with ids (Cell, Material, etc.)."""
|
||||
|
||||
@Filter.bins.setter
|
||||
def bins(self, bins):
|
||||
# Format the bins as a 1D numpy array.
|
||||
"""Abstract parent for filters of types with IDs (Cell, Material, etc.)."""
|
||||
def __init__(self, bins, filter_id=None):
|
||||
bins = np.atleast_1d(bins)
|
||||
|
||||
# Check the bin values.
|
||||
# Make sure bins are either integers or appropriate objects
|
||||
cv.check_iterable_type('filter bins', bins,
|
||||
(Integral, self.expected_type))
|
||||
|
||||
# Extract ID values
|
||||
bins = np.array([b if isinstance(b, Integral) else b.id
|
||||
for b in bins])
|
||||
self.bins = bins
|
||||
self.id = filter_id
|
||||
|
||||
def check_bins(self, bins):
|
||||
# Check the bin values.
|
||||
for edge in bins:
|
||||
if isinstance(edge, Integral):
|
||||
cv.check_greater_than('filter bin', edge, 0, equality=True)
|
||||
|
||||
# Extract id values.
|
||||
bins = np.atleast_1d([b if isinstance(b, Integral) else b.id
|
||||
for b in bins])
|
||||
|
||||
self._bins = bins
|
||||
cv.check_greater_than('filter bin', edge, 0, equality=True)
|
||||
|
||||
|
||||
class UniverseFilter(WithIDFilter):
|
||||
|
|
@ -601,12 +599,13 @@ class MeshFilter(Filter):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
bins : Integral
|
||||
The Mesh ID
|
||||
mesh : openmc.Mesh
|
||||
The Mesh object that events will be tallied onto
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : list of tuple
|
||||
A list of mesh indices for each filter bin, e.g. [(1, 1, 1), (2, 1, 1),
|
||||
...]
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
|
||||
|
|
@ -614,7 +613,7 @@ class MeshFilter(Filter):
|
|||
|
||||
def __init__(self, mesh, filter_id=None):
|
||||
self.mesh = mesh
|
||||
super().__init__(mesh.id, filter_id)
|
||||
self.id = filter_id
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, **kwargs):
|
||||
|
|
@ -643,68 +642,14 @@ class MeshFilter(Filter):
|
|||
def mesh(self, mesh):
|
||||
cv.check_type('filter mesh', mesh, openmc.Mesh)
|
||||
self._mesh = mesh
|
||||
self.bins = mesh.id
|
||||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return reduce(operator.mul, self.mesh.dimension)
|
||||
|
||||
def check_bins(self, bins):
|
||||
if not len(bins) == 1:
|
||||
msg = 'Unable to add bins "{0}" to a MeshFilter since ' \
|
||||
'only a single mesh can be used per tally'.format(bins)
|
||||
raise ValueError(msg)
|
||||
elif not isinstance(bins[0], Integral):
|
||||
msg = 'Unable to add bin "{0}" to MeshFilter since it ' \
|
||||
'is a non-integer'.format(bins[0])
|
||||
raise ValueError(msg)
|
||||
elif bins[0] < 0:
|
||||
msg = 'Unable to add bin "{0}" to MeshFilter since it ' \
|
||||
'is a negative integer'.format(bins[0])
|
||||
raise ValueError(msg)
|
||||
self.bins = list(mesh.indices)
|
||||
|
||||
def can_merge(self, other):
|
||||
# Mesh filters cannot have more than one bin
|
||||
return False
|
||||
|
||||
def get_bin_index(self, filter_bin):
|
||||
# Filter bins for a mesh are an (x,y,z) tuple. Convert (x,y,z) to a
|
||||
# single bin -- this is similar to subroutine mesh_indices_to_bin in
|
||||
# openmc/src/mesh.F90.
|
||||
n_dim = len(self.mesh.dimension)
|
||||
if n_dim == 3:
|
||||
i, j, k = filter_bin
|
||||
nx, ny, nz = self.mesh.dimension
|
||||
return (i - 1) + (j - 1)*nx + (k - 1)*nx*ny
|
||||
elif n_dim == 2:
|
||||
i, j, *_ = filter_bin
|
||||
nx, ny = self.mesh.dimension
|
||||
return (i - 1) + (j - 1)*nx
|
||||
else:
|
||||
return filter_bin[0] - 1
|
||||
|
||||
def get_bin(self, bin_index):
|
||||
cv.check_type('bin_index', bin_index, Integral)
|
||||
cv.check_greater_than('bin_index', bin_index, 0, equality=True)
|
||||
cv.check_less_than('bin_index', bin_index, self.num_bins)
|
||||
|
||||
n_dim = len(self.mesh.dimension)
|
||||
if n_dim == 3:
|
||||
# Construct 3-tuple of x,y,z cell indices for a 3D mesh
|
||||
nx, ny, nz = self.mesh.dimension
|
||||
x = (bin_index % nx) + 1
|
||||
y = (bin_index % (nx * ny)) // nx + 1
|
||||
z = bin_index // (nx * ny) + 1
|
||||
return (x, y, z)
|
||||
|
||||
elif n_dim == 2:
|
||||
# Construct 2-tuple of x,y cell indices for a 2D mesh
|
||||
nx, ny = self.mesh.dimension
|
||||
x = (bin_index % nx) + 1
|
||||
y = bin_index // nx + 1
|
||||
return (x, y)
|
||||
else:
|
||||
return (bin_index + 1,)
|
||||
return self.bins[bin_index]
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
|
@ -783,6 +728,19 @@ class MeshFilter(Filter):
|
|||
|
||||
return df
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the Filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing filter data
|
||||
|
||||
"""
|
||||
element = super().to_xml_element()
|
||||
element[0].text = str(self.mesh.id)
|
||||
return element
|
||||
|
||||
|
||||
class MeshSurfaceFilter(MeshFilter):
|
||||
"""Filter events by surface crossings on a regular, rectangular mesh.
|
||||
|
|
@ -802,36 +760,25 @@ class MeshSurfaceFilter(MeshFilter):
|
|||
The Mesh object that events will be tallied onto
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : list of tuple
|
||||
|
||||
A list of mesh indices / surfaces for each filter bin, e.g. [(1, 1,
|
||||
'x-min out'), (1, 1, 'x-min in'), ...]
|
||||
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def num_bins(self):
|
||||
n_dim = len(self.mesh.dimension)
|
||||
return 4*n_dim*reduce(operator.mul, self.mesh.dimension)
|
||||
@MeshFilter.mesh.setter
|
||||
def mesh(self, mesh):
|
||||
cv.check_type('filter mesh', mesh, openmc.Mesh)
|
||||
self._mesh = mesh
|
||||
|
||||
def get_bin_index(self, filter_bin):
|
||||
# Split bin into mesh/surface parts
|
||||
*mesh_tuple, surf = filter_bin
|
||||
|
||||
# Get index for mesh alone
|
||||
mesh_index = super().get_bin_index(mesh_tuple)
|
||||
|
||||
# Combine surface and mesh index
|
||||
n_dim = len(self.mesh.dimension)
|
||||
for surf_index, name in enumerate(_CURRENT_NAMES):
|
||||
if surf == name:
|
||||
return 4*n_dim*mesh_index + surf_index
|
||||
else:
|
||||
raise ValueError("'{}' is not a valid mesh surface.".format(surf))
|
||||
|
||||
def get_bin(self, bin_index):
|
||||
n_dim = len(self.mesh.dimension)
|
||||
mesh_index, surf_index = divmod(bin_index, 4*n_dim)
|
||||
mesh_bin = super().get_bin(mesh_index)
|
||||
return mesh_bin + (_CURRENT_NAMES[surf_index],)
|
||||
# Take the product of mesh indices and current names
|
||||
n_dim = len(mesh.dimension)
|
||||
self.bins = [mesh_tuple + (surf,) for mesh_tuple, surf in
|
||||
product(mesh.indices, _CURRENT_NAMES[:4*n_dim])]
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
|
@ -920,42 +867,68 @@ class RealFilter(Filter):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
bins : Iterable of Real
|
||||
A grid of bin values.
|
||||
values : iterable of float
|
||||
A list of values for which each successive pair constitutes a range of
|
||||
values for a single bin
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Iterable of Real
|
||||
A grid of bin values.
|
||||
values : numpy.ndarray
|
||||
An array of values for which each successive pair constitutes a range of
|
||||
values for a single bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : numpy.ndarray
|
||||
An array of shape (N, 2) where each row is a pair of values indicating a
|
||||
filter bin range
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
def __init__(self, values, filter_id=None):
|
||||
self.values = np.asarray(values)
|
||||
self.bins = np.vstack((self.values[:-1], self.values[1:])).T
|
||||
self.id = filter_id
|
||||
|
||||
def __gt__(self, other):
|
||||
if type(self) is type(other):
|
||||
# Compare largest/smallest bin edges in filters
|
||||
# This logic is used when merging tallies with real filters
|
||||
return self.bins[0] >= other.bins[-1]
|
||||
return self.values[0] >= other.values[-1]
|
||||
else:
|
||||
return super().__gt__(other)
|
||||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return len(self.bins) - 1
|
||||
@Filter.bins.setter
|
||||
def bins(self, bins):
|
||||
Filter.bins.__set__(self, np.asarray(bins))
|
||||
|
||||
def check_bins(self, bins):
|
||||
for v0, v1 in bins:
|
||||
# Values should be real
|
||||
cv.check_type('filter value', v0, Real)
|
||||
cv.check_type('filter value', v1, Real)
|
||||
|
||||
# Make sure that each tuple has values that are increasing
|
||||
if v1 < v0:
|
||||
raise ValueError('Values {} and {} appear to be out of order'
|
||||
.format(v0, v1))
|
||||
|
||||
for pair0, pair1 in zip(bins[:-1], bins[1:]):
|
||||
# Successive pairs should be ordered
|
||||
if pair1[1] < pair0[1]:
|
||||
raise ValueError('Values {} and {} appear to be out of order'
|
||||
.format(pair1[1], pair0[1]))
|
||||
|
||||
def can_merge(self, other):
|
||||
if type(self) is not type(other):
|
||||
return False
|
||||
|
||||
if self.bins[0] == other.bins[-1]:
|
||||
if self.bins[0, 0] == other.bins[-1][1]:
|
||||
# This low edge coincides with other's high edge
|
||||
return True
|
||||
elif self.bins[-1] == other.bins[0]:
|
||||
elif self.bins[-1][1] == other.bins[0, 0]:
|
||||
# This high edge coincides with other's low edge
|
||||
return True
|
||||
else:
|
||||
|
|
@ -968,11 +941,11 @@ class RealFilter(Filter):
|
|||
raise ValueError(msg)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_bins = np.concatenate((self.bins, other.bins))
|
||||
merged_bins = np.unique(merged_bins)
|
||||
merged_values = np.concatenate((self.values, other.values))
|
||||
merged_values = np.unique(merged_values)
|
||||
|
||||
# Create a new filter with these bins and a new auto-generated ID
|
||||
return type(self)(sorted(merged_bins))
|
||||
return type(self)(sorted(merged_values))
|
||||
|
||||
def is_subset(self, other):
|
||||
"""Determine if another filter is a subset of this filter.
|
||||
|
|
@ -994,80 +967,19 @@ class RealFilter(Filter):
|
|||
|
||||
if type(self) is not type(other):
|
||||
return False
|
||||
elif len(self.bins) != len(other.bins):
|
||||
elif self.num_bins != other.num_bins:
|
||||
return False
|
||||
else:
|
||||
return np.allclose(self.bins, other.bins)
|
||||
return np.allclose(self.values, other.values)
|
||||
|
||||
def get_bin_index(self, filter_bin):
|
||||
i = np.where(self.bins == filter_bin[1])[0]
|
||||
i = np.where(self.bins[:, 1] == filter_bin[1])[0]
|
||||
if len(i) == 0:
|
||||
msg = 'Unable to get the bin index for Filter since "{0}" ' \
|
||||
'is not one of the bins'.format(filter_bin)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
return i[0] - 1
|
||||
|
||||
def get_bin(self, bin_index):
|
||||
cv.check_type('bin_index', bin_index, Integral)
|
||||
cv.check_greater_than('bin_index', bin_index, 0, equality=True)
|
||||
cv.check_less_than('bin_index', bin_index, self.num_bins)
|
||||
|
||||
# Construct 2-tuple of lower, upper bins for real-valued filters
|
||||
return (self.bins[bin_index], self.bins[bin_index + 1])
|
||||
|
||||
|
||||
class EnergyFilter(RealFilter):
|
||||
"""Bins tally events based on incident particle energy.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bins : Iterable of Real
|
||||
A grid of energy values in eV.
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Iterable of Real
|
||||
A grid of energy values in eV.
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
|
||||
def get_bin_index(self, filter_bin):
|
||||
# Use lower energy bound to find index for RealFilters
|
||||
deltas = np.abs(self.bins - filter_bin[1]) / filter_bin[1]
|
||||
min_delta = np.min(deltas)
|
||||
if min_delta < 1E-3:
|
||||
return deltas.argmin() - 1
|
||||
else:
|
||||
msg = 'Unable to get the bin index for Filter since "{0}" ' \
|
||||
'is not one of the bins'.format(filter_bin)
|
||||
raise ValueError(msg)
|
||||
|
||||
def check_bins(self, bins):
|
||||
for edge in bins:
|
||||
if not isinstance(edge, Real):
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is a non-integer or floating point ' \
|
||||
'value'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif edge < 0.:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is a negative value'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check that bin edges are monotonically increasing
|
||||
for index in range(1, len(bins)):
|
||||
if bins[index] < bins[index-1]:
|
||||
msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \
|
||||
'since they are not monotonically ' \
|
||||
'increasing'.format(bins, type(self))
|
||||
raise ValueError(msg)
|
||||
return i[0]
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
|
@ -1102,35 +1014,103 @@ class EnergyFilter(RealFilter):
|
|||
|
||||
# Extract the lower and upper energy bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], stride)
|
||||
hi_bins = np.repeat(self.bins[1:], stride)
|
||||
lo_bins = np.repeat(self.bins[:, 0], stride)
|
||||
hi_bins = np.repeat(self.bins[:, 1], stride)
|
||||
tile_factor = data_size // len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Add the new energy columns to the DataFrame.
|
||||
df.loc[:, self.short_name.lower() + ' low [eV]'] = lo_bins
|
||||
df.loc[:, self.short_name.lower() + ' high [eV]'] = hi_bins
|
||||
if hasattr(self, 'units'):
|
||||
units = ' [{}]'.format(self.units)
|
||||
else:
|
||||
units = ''
|
||||
|
||||
df.loc[:, self.short_name.lower() + ' low' + units] = lo_bins
|
||||
df.loc[:, self.short_name.lower() + ' high' + units] = hi_bins
|
||||
|
||||
return df
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the Filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing filter data
|
||||
|
||||
"""
|
||||
element = super().to_xml_element()
|
||||
element[0].text = ' '.join(str(x) for x in self.values)
|
||||
return element
|
||||
|
||||
|
||||
class EnergyFilter(RealFilter):
|
||||
"""Bins tally events based on incident particle energy.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
values : Iterable of Real
|
||||
A list of values for which each successive pair constitutes a range of
|
||||
energies in [eV] for a single bin
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
values : numpy.ndarray
|
||||
An array of values for which each successive pair constitutes a range of
|
||||
energies in [eV] for a single bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : numpy.ndarray
|
||||
An array of shape (N, 2) where each row is a pair of energies in [eV]
|
||||
for a single filter bin
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
units = 'eV'
|
||||
|
||||
def get_bin_index(self, filter_bin):
|
||||
# Use lower energy bound to find index for RealFilters
|
||||
deltas = np.abs(self.bins[:, 1] - filter_bin[1]) / filter_bin[1]
|
||||
min_delta = np.min(deltas)
|
||||
if min_delta < 1E-3:
|
||||
return deltas.argmin()
|
||||
else:
|
||||
msg = 'Unable to get the bin index for Filter since "{0}" ' \
|
||||
'is not one of the bins'.format(filter_bin)
|
||||
raise ValueError(msg)
|
||||
|
||||
def check_bins(self, bins):
|
||||
super().check_bins(bins)
|
||||
for v0, v1 in bins:
|
||||
cv.check_greater_than('filter value', v0, 0., equality=True)
|
||||
cv.check_greater_than('filter value', v1, 0., equality=True)
|
||||
|
||||
|
||||
class EnergyoutFilter(EnergyFilter):
|
||||
"""Bins tally events based on outgoing particle energy.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bins : Iterable of Real
|
||||
A grid of energy values in eV.
|
||||
values : Iterable of Real
|
||||
A list of values for which each successive pair constitutes a range of
|
||||
energies in [eV] for a single bin
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Iterable of Real
|
||||
A grid of energy values in eV.
|
||||
values : numpy.ndarray
|
||||
An array of values for which each successive pair constitutes a range of
|
||||
energies in [eV] for a single bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : numpy.ndarray
|
||||
An array of shape (N, 2) where each row is a pair of energies in [eV]
|
||||
for a single filter bin
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
|
|
@ -1412,98 +1392,41 @@ class MuFilter(RealFilter):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
bins : Iterable of Real or Integral
|
||||
A grid of scattering angles which events will binned into. Values
|
||||
represent the cosine of the scattering angle. If an Iterable is given,
|
||||
the values will be used explicitly as grid points. If a single Integral
|
||||
is given, the range [-1, 1] will be divided up equally into that number
|
||||
of bins.
|
||||
values : int or Iterable of Real
|
||||
A grid of scattering angles which events will binned into. Values
|
||||
represent the cosine of the scattering angle. If an iterable is given,
|
||||
the values will be used explicitly as grid points. If a single int is
|
||||
given, the range [-1, 1] will be divided up equally into that number of
|
||||
bins.
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Integral
|
||||
A grid of scattering angles which events will binned into. Values
|
||||
represent the cosine of the scattering angle. If an Iterable is given,
|
||||
the values will be used explicitly as grid points. If a single Integral
|
||||
is given, the range [-1, 1] will be divided up equally into that number
|
||||
of bins.
|
||||
values : numpy.ndarray
|
||||
An array of values for which each successive pair constitutes a range of
|
||||
scattering angle cosines for a single bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : numpy.ndarray
|
||||
An array of shape (N, 2) where each row is a pair of scattering angle
|
||||
cosines for a single filter bin
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
def __init__(self, values, filter_id=None):
|
||||
if isinstance(values, Integral):
|
||||
values = np.linspace(-1., 1., values + 1)
|
||||
super().__init__(values, filter_id)
|
||||
|
||||
def check_bins(self, bins):
|
||||
for edge in bins:
|
||||
if not isinstance(edge, Real):
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is a non-integer or floating point ' \
|
||||
'value'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif edge < -1.:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is less than -1'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif edge > 1.:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is greater than 1'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check that bin edges are monotonically increasing
|
||||
for index in range(1, len(bins)):
|
||||
if bins[index] < bins[index-1]:
|
||||
msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \
|
||||
'since they are not monotonically ' \
|
||||
'increasing'.format(bins, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method
|
||||
for :meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : int
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
stride : int
|
||||
Stride in memory for the filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with one column of the lower energy bound and one
|
||||
column of upper energy bound for each filter bin. The number of
|
||||
rows in the DataFrame is the same as the total number of bins in the
|
||||
corresponding tally, with the filter bin appropriately tiled to map
|
||||
to the corresponding tally bins.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
# Extract the lower and upper energy bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], stride)
|
||||
hi_bins = np.repeat(self.bins[1:], stride)
|
||||
tile_factor = data_size // len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Add the new energy columns to the DataFrame.
|
||||
df.loc[:, self.short_name.lower() + ' low'] = lo_bins
|
||||
df.loc[:, self.short_name.lower() + ' high'] = hi_bins
|
||||
|
||||
return df
|
||||
super().check_bins(bins)
|
||||
for x in np.ravel(bins):
|
||||
if not np.isclose(x, -1.):
|
||||
cv.check_greater_than('filter value', x, -1., equality=True)
|
||||
if not np.isclose(x, 1.):
|
||||
cv.check_less_than('filter value', x, 1., equality=True)
|
||||
|
||||
|
||||
class PolarFilter(RealFilter):
|
||||
|
|
@ -1511,98 +1434,44 @@ class PolarFilter(RealFilter):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
bins : Iterable of Real or Integral
|
||||
A grid of polar angles which events will binned into. Values represent
|
||||
an angle in radians relative to the z-axis. If an Iterable is given,
|
||||
the values will be used explicitly as grid points. If a single Integral
|
||||
is given, the range [0, pi] will be divided up equally into that number
|
||||
of bins.
|
||||
values : int or Iterable of Real
|
||||
A grid of polar angles which events will binned into. Values represent
|
||||
an angle in radians relative to the z-axis. If an iterable is given, the
|
||||
values will be used explicitly as grid points. If a single int is given,
|
||||
the range [0, pi] will be divided up equally into that number of bins.
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Iterable of Real or Integral
|
||||
A grid of polar angles which events will binned into. Values represent
|
||||
an angle in radians relative to the z-axis. If an Iterable is given,
|
||||
the values will be used explicitly as grid points. If a single Integral
|
||||
is given, the range [0, pi] will be divided up equally into that number
|
||||
of bins.
|
||||
values : numpy.ndarray
|
||||
An array of values for which each successive pair constitutes a range of
|
||||
polar angles in [rad] for a single bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : numpy.ndarray
|
||||
An array of shape (N, 2) where each row is a pair of polar angles for a
|
||||
single filter bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
units = 'rad'
|
||||
|
||||
def __init__(self, values, filter_id=None):
|
||||
if isinstance(values, Integral):
|
||||
values = np.linspace(0., np.pi, values + 1)
|
||||
super().__init__(values, filter_id)
|
||||
|
||||
def check_bins(self, bins):
|
||||
for edge in bins:
|
||||
if not isinstance(edge, Real):
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is a non-integer or floating point ' \
|
||||
'value'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif edge < 0.:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is less than 0'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif not np.isclose(edge, np.pi) and edge > np.pi:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is greater than pi'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check that bin edges are monotonically increasing
|
||||
for index in range(1, len(bins)):
|
||||
if bins[index] < bins[index-1]:
|
||||
msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \
|
||||
'since they are not monotonically ' \
|
||||
'increasing'.format(bins, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : int
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
stride : int
|
||||
Stride in memory for the filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with a column corresponding to the lower polar
|
||||
angle bound for each of the filter'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 bin appropriately tiled to map
|
||||
to the corresponding tally bins.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
# Extract the lower and upper angle bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], stride)
|
||||
hi_bins = np.repeat(self.bins[1:], stride)
|
||||
tile_factor = data_size // len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Add the new angle columns to the DataFrame.
|
||||
df.loc[:, 'polar low'] = lo_bins
|
||||
df.loc[:, 'polar high'] = hi_bins
|
||||
|
||||
return df
|
||||
super().check_bins(bins)
|
||||
for x in np.ravel(bins):
|
||||
if not np.isclose(x, 0.):
|
||||
cv.check_greater_than('filter value', x, 0., equality=True)
|
||||
if not np.isclose(x, np.pi):
|
||||
cv.check_less_than('filter value', x, np.pi, equality=True)
|
||||
|
||||
|
||||
class AzimuthalFilter(RealFilter):
|
||||
|
|
@ -1610,98 +1479,43 @@ class AzimuthalFilter(RealFilter):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
bins : Iterable of Real or Integral
|
||||
A grid of azimuthal angles which events will binned into. Values
|
||||
values : int or Iterable of Real
|
||||
A grid of azimuthal angles which events will binned into. Values
|
||||
represent an angle in radians relative to the x-axis and perpendicular
|
||||
to the z-axis. If an Iterable is given, the values will be used
|
||||
explicitly as grid points. If a single Integral is given, the range
|
||||
to the z-axis. If an iterable is given, the values will be used
|
||||
explicitly as grid points. If a single int is given, the range
|
||||
[-pi, pi) will be divided up equally into that number of bins.
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Iterable of Real or Integral
|
||||
A grid of azimuthal angles which events will binned into. Values
|
||||
represent an angle in radians relative to the x-axis and perpendicular
|
||||
to the z-axis. If an Iterable is given, the values will be used
|
||||
explicitly as grid points. If a single Integral is given, the range
|
||||
[-pi, pi) will be divided up equally into that number of bins.
|
||||
values : numpy.ndarray
|
||||
An array of values for which each successive pair constitutes a range of
|
||||
azimuthal angles in [rad] for a single bin
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
bins : numpy.ndarray
|
||||
An array of shape (N, 2) where each row is a pair of azimuthal angles
|
||||
for a single filter bin
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
units = 'rad'
|
||||
|
||||
def __init__(self, values, filter_id=None):
|
||||
if isinstance(values, Integral):
|
||||
values = np.linspace(-np.pi, np.pi, values + 1)
|
||||
super().__init__(values, filter_id)
|
||||
|
||||
def check_bins(self, bins):
|
||||
for edge in bins:
|
||||
if not isinstance(edge, Real):
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is a non-integer or floating point ' \
|
||||
'value'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif not np.isclose(edge, -np.pi) and edge < -np.pi:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is less than -pi'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
elif not np.isclose(edge, np.pi) and edge > np.pi:
|
||||
msg = 'Unable to add bin edge "{0}" to a "{1}" ' \
|
||||
'since it is greater than pi'.format(edge, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check that bin edges are monotonically increasing
|
||||
for index in range(1, len(bins)):
|
||||
if bins[index] < bins[index-1]:
|
||||
msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \
|
||||
'since they are not monotonically ' \
|
||||
'increasing'.format(bins, type(self))
|
||||
raise ValueError(msg)
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, paths=True):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : int
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
stride : int
|
||||
Stride in memory for the filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with a column corresponding to the lower
|
||||
azimuthal angle bound for each of the filter'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 bin appropriately tiled to map
|
||||
to the corresponding tally bins.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
# Extract the lower and upper angle bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], stride)
|
||||
hi_bins = np.repeat(self.bins[1:], stride)
|
||||
tile_factor = data_size // len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Add the new angle columns to the DataFrame.
|
||||
df.loc[:, 'azimuthal low'] = lo_bins
|
||||
df.loc[:, 'azimuthal high'] = hi_bins
|
||||
|
||||
return df
|
||||
super().check_bins(bins)
|
||||
for x in np.ravel(bins):
|
||||
if not np.isclose(x, -np.pi):
|
||||
cv.check_greater_than('filter value', x, -np.pi, equality=True)
|
||||
if not np.isclose(x, np.pi):
|
||||
cv.check_less_than('filter value', x, np.pi, equality=True)
|
||||
|
||||
|
||||
class DelayedGroupFilter(Filter):
|
||||
|
|
@ -1709,7 +1523,7 @@ class DelayedGroupFilter(Filter):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
bins : Integral or Iterable of Integral
|
||||
bins : iterable of int
|
||||
The delayed neutron precursor groups. For example, ENDF/B-VII.1 uses
|
||||
6 precursor groups so a tally with all groups will have bins =
|
||||
[1, 2, 3, 4, 5, 6].
|
||||
|
|
@ -1718,7 +1532,7 @@ class DelayedGroupFilter(Filter):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
bins : Integral or Iterable of Integral
|
||||
bins : iterable of int
|
||||
The delayed neutron precursor groups. For example, ENDF/B-VII.1 uses
|
||||
6 precursor groups so a tally with all groups will have bins =
|
||||
[1, 2, 3, 4, 5, 6].
|
||||
|
|
@ -1728,17 +1542,10 @@ class DelayedGroupFilter(Filter):
|
|||
The number of filter bins
|
||||
|
||||
"""
|
||||
@Filter.bins.setter
|
||||
def bins(self, bins):
|
||||
# Format the bins as a 1D numpy array.
|
||||
bins = np.atleast_1d(bins)
|
||||
|
||||
def check_bins(self, bins):
|
||||
# Check the bin values.
|
||||
cv.check_iterable_type('filter bins', bins, Integral)
|
||||
for edge in bins:
|
||||
cv.check_greater_than('filter bin', edge, 0, equality=True)
|
||||
|
||||
self._bins = bins
|
||||
for g in bins:
|
||||
cv.check_greater_than('delayed group', g, 0)
|
||||
|
||||
|
||||
class EnergyFunctionFilter(Filter):
|
||||
|
|
@ -1751,18 +1558,18 @@ class EnergyFunctionFilter(Filter):
|
|||
Parameters
|
||||
----------
|
||||
energy : Iterable of Real
|
||||
A grid of energy values in eV.
|
||||
A grid of energy values in [eV]
|
||||
y : iterable of Real
|
||||
A grid of interpolant values in eV.
|
||||
A grid of interpolant values in [eV]
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy : Iterable of Real
|
||||
A grid of energy values in eV.
|
||||
A grid of energy values in [eV]
|
||||
y : iterable of Real
|
||||
A grid of interpolant values in eV.
|
||||
A grid of interpolant values in [eV]
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : Integral
|
||||
|
|
@ -1903,6 +1710,14 @@ class EnergyFunctionFilter(Filter):
|
|||
raise RuntimeError('EnergyFunctionFilters have no bins.')
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the Filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing filter data
|
||||
|
||||
"""
|
||||
element = ET.Element('filter')
|
||||
element.set('id', str(self.id))
|
||||
element.set('type', self.short_name.lower())
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class LegendreFilter(Filter):
|
|||
|
||||
def __init__(self, order, filter_id=None):
|
||||
self.order = order
|
||||
self.bins = ['P{}'.format(i) for i in range(order + 1)]
|
||||
self.id = filter_id
|
||||
|
||||
def __hash__(self):
|
||||
|
|
@ -57,10 +58,6 @@ class LegendreFilter(Filter):
|
|||
cv.check_greater_than('Legendre order', order, 0, equality=True)
|
||||
self._order = order
|
||||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return self._order + 1
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, **kwargs):
|
||||
if group['type'].value.decode() != cls.short_name.lower():
|
||||
|
|
@ -74,44 +71,6 @@ class LegendreFilter(Filter):
|
|||
|
||||
return out
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
stride : int
|
||||
Stride in memory for the filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with a column that is filled with strings
|
||||
indicating Legendre orders. The number of rows in the DataFrame is
|
||||
the same as the total number of bins in the corresponding tally.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
bins = np.array(['P{}'.format(i) for i in range(self.order + 1)])
|
||||
filter_bins = np.repeat(bins, stride)
|
||||
tile_factor = data_size // len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
df = pd.concat([df, pd.DataFrame(
|
||||
{self.short_name.lower(): filter_bins})])
|
||||
|
||||
return df
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class SpatialLegendreFilter(Filter):
|
|||
self.axis = axis
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
self.bins = ['P{}'.format(i) for i in range(order + 1)]
|
||||
self.id = filter_id
|
||||
|
||||
def __hash__(self):
|
||||
|
|
@ -118,42 +119,6 @@ class SpatialLegendreFilter(Filter):
|
|||
|
||||
return cls(order, axis, min_, max_, filter_id)
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with a column that is filled with strings
|
||||
indicating Legendre orders. The number of rows in the DataFrame is
|
||||
the same as the total number of bins in the corresponding tally.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
bins = np.array(['P{}'.format(i) for i in range(self.order + 1)])
|
||||
filter_bins = np.repeat(bins, stride)
|
||||
tile_factor = data_size // len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
df = pd.concat([df, pd.DataFrame(
|
||||
{self.short_name.lower(): filter_bins})])
|
||||
|
||||
return df
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ class SphericalHarmonicsFilter(Filter):
|
|||
def __init__(self, order, filter_id=None):
|
||||
self.order = order
|
||||
self.id = filter_id
|
||||
self.bins = ['Y{},{}'.format(n, m)
|
||||
for n in range(order + 1)
|
||||
for m in range(-n, n + 1)]
|
||||
self._cosine = 'particle'
|
||||
|
||||
def __hash__(self):
|
||||
|
|
@ -87,49 +90,6 @@ class SphericalHarmonicsFilter(Filter):
|
|||
|
||||
return out
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
stride : int
|
||||
Stride in memory for the filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with a column that is filled with strings
|
||||
indicating spherical harmonics orders. The number of rows in the
|
||||
DataFrame is the same as the total number of bins in the
|
||||
corresponding tally.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
bins = []
|
||||
for n in range(self.order + 1):
|
||||
bins.extend('Y{},{}'.format(n, m) for m in range(-n, n + 1))
|
||||
bins = np.array(bins)
|
||||
|
||||
filter_bins = np.repeat(bins, stride)
|
||||
tile_factor = data_size // len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
df = pd.concat([df, pd.DataFrame(
|
||||
{self.short_name.lower(): filter_bins})])
|
||||
|
||||
return df
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ class ZernikeFilter(Filter):
|
|||
self.x = x
|
||||
self.y = y
|
||||
self.r = r
|
||||
self.bins = ['Z{},{}'.format(n, m)
|
||||
for n in range(order + 1)
|
||||
for m in range(-n, n + 1, 2)]
|
||||
self.id = filter_id
|
||||
|
||||
def __hash__(self):
|
||||
|
|
@ -116,48 +119,6 @@ class ZernikeFilter(Filter):
|
|||
|
||||
return cls(order, x, y, r, filter_id)
|
||||
|
||||
def get_pandas_dataframe(self, data_size, stride, **kwargs):
|
||||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
A Pandas DataFrame with a column that is filled with strings
|
||||
indicating Zernike orders. The number of rows in the DataFrame is
|
||||
the same as the total number of bins in the corresponding tally.
|
||||
|
||||
See also
|
||||
--------
|
||||
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
|
||||
|
||||
"""
|
||||
# Initialize Pandas DataFrame
|
||||
df = pd.DataFrame()
|
||||
|
||||
# Create list of strings for each order
|
||||
orders = []
|
||||
for n in range(self.order + 1):
|
||||
for m in range(-n, n + 1, 2):
|
||||
orders.append('Z{},{}'.format(n, m))
|
||||
|
||||
bins = np.array(orders)
|
||||
filter_bins = np.repeat(bins, stride)
|
||||
tile_factor = data_size // len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
df = pd.concat([df, pd.DataFrame(
|
||||
{self.short_name.lower(): filter_bins})])
|
||||
|
||||
return df
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ class Mesh(IDManagerMixin):
|
|||
are given, it is assumed that the mesh is an x-y mesh.
|
||||
width : Iterable of float
|
||||
The width of mesh cells in each direction.
|
||||
indices : list of tuple
|
||||
A list of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1,
|
||||
1), ...]
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1164,12 +1164,14 @@ class MGXS(metaclass=ABCMeta):
|
|||
if not isinstance(tally_filter, (openmc.EnergyFilter,
|
||||
openmc.EnergyoutFilter)):
|
||||
continue
|
||||
elif len(tally_filter.bins) != len(fine_edges):
|
||||
elif len(tally_filter.bins) != len(fine_edges) - 1:
|
||||
continue
|
||||
elif not np.allclose(tally_filter.bins, fine_edges):
|
||||
elif not np.allclose(tally_filter.bins[:, 0], fine_edges[:-1]):
|
||||
continue
|
||||
else:
|
||||
tally_filter.bins = coarse_groups.group_edges
|
||||
cedge = coarse_groups.group_edges
|
||||
tally_filter.values = cedge
|
||||
tally_filter.bins = np.vstack((cedge[:-1], cedge[1:])).T
|
||||
mean = np.add.reduceat(mean, energy_indices, axis=i)
|
||||
std_dev = np.add.reduceat(std_dev**2, energy_indices,
|
||||
axis=i)
|
||||
|
|
@ -2738,7 +2740,7 @@ class TransportXS(MGXS):
|
|||
if self._rxn_rate_tally is None:
|
||||
# Switch EnergyoutFilter to EnergyFilter.
|
||||
old_filt = self.tallies['scatter-1'].filters[-1]
|
||||
new_filt = openmc.EnergyFilter(old_filt.bins)
|
||||
new_filt = openmc.EnergyFilter(old_filt.values)
|
||||
self.tallies['scatter-1'].filters[-1] = new_filt
|
||||
|
||||
self._rxn_rate_tally = \
|
||||
|
|
@ -2757,7 +2759,7 @@ class TransportXS(MGXS):
|
|||
|
||||
# Switch EnergyoutFilter to EnergyFilter.
|
||||
old_filt = self.tallies['scatter-1'].filters[-1]
|
||||
new_filt = openmc.EnergyFilter(old_filt.bins)
|
||||
new_filt = openmc.EnergyFilter(old_filt.values)
|
||||
self.tallies['scatter-1'].filters[-1] = new_filt
|
||||
|
||||
# Compute total cross section
|
||||
|
|
|
|||
|
|
@ -218,10 +218,9 @@ class Tally(IDManagerMixin):
|
|||
f = h5py.File(self._sp_filename, 'r')
|
||||
|
||||
# Extract Tally data from the file
|
||||
data = f['tallies/tally {0}/results'.format(
|
||||
self.id)].value
|
||||
sum = data[:,:,0]
|
||||
sum_sq = data[:,:,1]
|
||||
data = f['tallies/tally {0}/results'.format(self.id)].value
|
||||
sum = data[:, :, 0]
|
||||
sum_sq = data[:, :, 1]
|
||||
|
||||
# Reshape the results arrays
|
||||
sum = np.reshape(sum, self.shape)
|
||||
|
|
@ -273,8 +272,8 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Convert NumPy array to SciPy sparse LIL matrix
|
||||
if self.sparse:
|
||||
self._mean = \
|
||||
sps.lil_matrix(self._mean.flatten(), self._mean.shape)
|
||||
self._mean = sps.lil_matrix(self._mean.flatten(),
|
||||
self._mean.shape)
|
||||
|
||||
if self.sparse:
|
||||
return np.reshape(self._mean.toarray(), self.shape)
|
||||
|
|
@ -295,8 +294,8 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Convert NumPy array to SciPy sparse LIL matrix
|
||||
if self.sparse:
|
||||
self._std_dev = \
|
||||
sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape)
|
||||
self._std_dev = sps.lil_matrix(self._std_dev.flatten(),
|
||||
self._std_dev.shape)
|
||||
|
||||
self.with_batch_statistics = True
|
||||
|
||||
|
|
@ -436,17 +435,16 @@ class Tally(IDManagerMixin):
|
|||
# Convert NumPy arrays to SciPy sparse LIL matrices
|
||||
if sparse and not self.sparse:
|
||||
if self._sum is not None:
|
||||
self._sum = \
|
||||
sps.lil_matrix(self._sum.flatten(), self._sum.shape)
|
||||
self._sum = sps.lil_matrix(self._sum.flatten(), self._sum.shape)
|
||||
if self._sum_sq is not None:
|
||||
self._sum_sq = \
|
||||
sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape)
|
||||
self._sum_sq = sps.lil_matrix(self._sum_sq.flatten(),
|
||||
self._sum_sq.shape)
|
||||
if self._mean is not None:
|
||||
self._mean = \
|
||||
sps.lil_matrix(self._mean.flatten(), self._mean.shape)
|
||||
self._mean = sps.lil_matrix(self._mean.flatten(),
|
||||
self._mean.shape)
|
||||
if self._std_dev is not None:
|
||||
self._std_dev = \
|
||||
sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape)
|
||||
self._std_dev = sps.lil_matrix(self._std_dev.flatten(),
|
||||
self._std_dev.shape)
|
||||
|
||||
self._sparse = True
|
||||
|
||||
|
|
@ -776,11 +774,11 @@ class Tally(IDManagerMixin):
|
|||
other_sum = other_copy.get_reshaped_data(value='sum')
|
||||
|
||||
if join_right:
|
||||
merged_sum = \
|
||||
np.concatenate((self_sum, other_sum), axis=merge_axis)
|
||||
merged_sum = np.concatenate((self_sum, other_sum),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_sum = \
|
||||
np.concatenate((other_sum, self_sum), axis=merge_axis)
|
||||
merged_sum = np.concatenate((other_sum, self_sum),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._sum = np.reshape(merged_sum, merged_tally.shape)
|
||||
|
||||
|
|
@ -790,11 +788,11 @@ class Tally(IDManagerMixin):
|
|||
other_sum_sq = other_copy.get_reshaped_data(value='sum_sq')
|
||||
|
||||
if join_right:
|
||||
merged_sum_sq = \
|
||||
np.concatenate((self_sum_sq, other_sum_sq), axis=merge_axis)
|
||||
merged_sum_sq = np.concatenate((self_sum_sq, other_sum_sq),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_sum_sq = \
|
||||
np.concatenate((other_sum_sq, self_sum_sq), axis=merge_axis)
|
||||
merged_sum_sq = np.concatenate((other_sum_sq, self_sum_sq),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._sum_sq = np.reshape(merged_sum_sq, merged_tally.shape)
|
||||
|
||||
|
|
@ -804,11 +802,11 @@ class Tally(IDManagerMixin):
|
|||
other_mean = other_copy.get_reshaped_data(value='mean')
|
||||
|
||||
if join_right:
|
||||
merged_mean = \
|
||||
np.concatenate((self_mean, other_mean), axis=merge_axis)
|
||||
merged_mean = np.concatenate((self_mean, other_mean),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_mean = \
|
||||
np.concatenate((other_mean, self_mean), axis=merge_axis)
|
||||
merged_mean = np.concatenate((other_mean, self_mean),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._mean = np.reshape(merged_mean, merged_tally.shape)
|
||||
|
||||
|
|
@ -818,11 +816,11 @@ class Tally(IDManagerMixin):
|
|||
other_std_dev = other_copy.get_reshaped_data(value='std_dev')
|
||||
|
||||
if join_right:
|
||||
merged_std_dev = \
|
||||
np.concatenate((self_std_dev, other_std_dev), axis=merge_axis)
|
||||
merged_std_dev = np.concatenate((self_std_dev, other_std_dev),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_std_dev = \
|
||||
np.concatenate((other_std_dev, self_std_dev), axis=merge_axis)
|
||||
merged_std_dev = np.concatenate((other_std_dev, self_std_dev),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._std_dev = np.reshape(merged_std_dev, merged_tally.shape)
|
||||
|
||||
|
|
@ -1003,35 +1001,6 @@ class Tally(IDManagerMixin):
|
|||
|
||||
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
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filter_type : openmc.FilterMeta
|
||||
Type of the filter, e.g. MeshFilter
|
||||
filter_bin : int or tuple
|
||||
The bin is an integer ID for 'material', 'surface', 'cell',
|
||||
'cellborn', and 'universe' Filters. The bin is an integer for the
|
||||
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
|
||||
-------
|
||||
The index in the Tally data array for this filter bin
|
||||
|
||||
"""
|
||||
|
||||
# Find the equivalent Filter in this Tally's list of Filters
|
||||
filter_found = self.find_filter(filter_type)
|
||||
|
||||
# Get the index for the requested bin from the Filter and return it
|
||||
filter_index = filter_found.get_bin_index(filter_bin)
|
||||
return filter_index
|
||||
|
||||
def get_nuclide_index(self, nuclide):
|
||||
"""Returns the index in the Tally's results array for a Nuclide bin
|
||||
|
||||
|
|
@ -1151,48 +1120,28 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Loop over all of the Tally's 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 type(self_filter) is test_filter:
|
||||
bins = filter_bins[j]
|
||||
user_filter = True
|
||||
break
|
||||
else:
|
||||
# If not a user-requested Filter, get all bins
|
||||
if isinstance(self_filter, openmc.DistribcellFilter):
|
||||
# Create list of cell instance IDs for distribcell Filters
|
||||
bins = list(range(self_filter.num_bins))
|
||||
|
||||
# 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 isinstance(self_filter, openmc.MeshFilter):
|
||||
bins = list(self_filter.mesh.indices)
|
||||
|
||||
# Create list of 2-tuples for energy boundary bins
|
||||
elif isinstance(self_filter, (openmc.EnergyFilter,
|
||||
openmc.EnergyoutFilter, openmc.MuFilter,
|
||||
openmc.PolarFilter, openmc.AzimuthalFilter)):
|
||||
bins = []
|
||||
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 isinstance(self_filter, openmc.DistribcellFilter):
|
||||
bins = [b for b in range(self_filter.num_bins)]
|
||||
|
||||
# EnergyFunctionFilters don't have bins so just add a None
|
||||
elif isinstance(self_filter, openmc.EnergyFunctionFilter):
|
||||
# EnergyFunctionFilters don't have bins so just add a None
|
||||
bins = [None]
|
||||
|
||||
# Create list of IDs for bins for all other filter types
|
||||
else:
|
||||
# Create list of IDs for bins for all other filter types
|
||||
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(type(self_filter), bin)
|
||||
filter_indices[i][j] = filter_index
|
||||
indices = np.array([self_filter.get_bin_index(b) for b in bins])
|
||||
filter_indices.append(indices)
|
||||
|
||||
# Account for stride in each of the previous filters
|
||||
for indices in filter_indices[:i]:
|
||||
|
|
@ -1956,14 +1905,14 @@ class Tally(IDManagerMixin):
|
|||
elif isinstance(filter1, openmc.EnergyFunctionFilter):
|
||||
filter1_bins = [None]
|
||||
else:
|
||||
filter1_bins = [filter1.get_bin(i) for i in range(filter1.num_bins)]
|
||||
filter1_bins = filter1.bins
|
||||
|
||||
if isinstance(filter2, openmc.DistribcellFilter):
|
||||
filter2_bins = [b for b in range(filter2.num_bins)]
|
||||
elif isinstance(filter2, openmc.EnergyFunctionFilter):
|
||||
filter2_bins = [None]
|
||||
else:
|
||||
filter2_bins = [filter2.get_bin(i) for i in range(filter2.num_bins)]
|
||||
filter2_bins = filter2.bins
|
||||
|
||||
# Create variables to store views of data in the misaligned structure
|
||||
mean = {}
|
||||
|
|
@ -2604,7 +2553,8 @@ class Tally(IDManagerMixin):
|
|||
new_tally = self * -1
|
||||
return new_tally
|
||||
|
||||
def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[]):
|
||||
def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[],
|
||||
squeeze=False):
|
||||
"""Build a sliced tally for the specified filters, scores and nuclides.
|
||||
|
||||
This method constructs a new tally to encapsulate a subset of the data
|
||||
|
|
@ -2615,26 +2565,26 @@ class Tally(IDManagerMixin):
|
|||
Parameters
|
||||
----------
|
||||
scores : list of str
|
||||
A list of one or more score strings
|
||||
(e.g., ['absorption', 'nu-fission']; default is [])
|
||||
A list of one or more score strings (e.g., ['absorption',
|
||||
'nu-fission']
|
||||
filters : Iterable of openmc.FilterMeta
|
||||
An iterable of filter types
|
||||
(e.g., [MeshFilter, EnergyFilter]; default is [])
|
||||
An iterable of filter types (e.g., [MeshFilter, EnergyFilter])
|
||||
filter_bins : list of Iterables
|
||||
A list of tuples of filter bins corresponding to the filter_types
|
||||
parameter (e.g., [(1,), ((0., 0.625e-6),)]; default is []). Each
|
||||
tuple contains bins to slice for the corresponding filter type in
|
||||
the filters parameter. Each bins is the integer ID for 'material',
|
||||
A list of iterables of filter bins corresponding to the specified
|
||||
filter types (e.g., [(1,), ((0., 0.625e-6),)]). Each iterable
|
||||
contains bins to slice for the corresponding filter type in the
|
||||
filters parameter. Each bin 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 floats for 'energy' and 'energyout' filters
|
||||
corresponding to the energy boundaries of the bin of interest. The
|
||||
bin is an (x,y,z) 3-tuple for 'mesh' filters corresponding to the
|
||||
mesh cell of interest. The order of the bins in the list must
|
||||
correspond to the filter_types parameter.
|
||||
correspond to the `filters` argument.
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U235', 'U238']; default is [])
|
||||
A list of nuclide name strings (e.g., ['U235', 'U238'])
|
||||
squeeze : bool
|
||||
Whether to remove filters with only a single bin in the sliced tally
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -2714,32 +2664,29 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Determine the filter indices from any of the requested filters
|
||||
for i, filter_type in enumerate(filters):
|
||||
find_filter = new_tally.find_filter(filter_type)
|
||||
f = new_tally.find_filter(filter_type)
|
||||
|
||||
# Remove filters with only a single bin if requested
|
||||
if squeeze:
|
||||
if len(filter_bins[i]) == 1:
|
||||
new_tally.filters.remove(f)
|
||||
continue
|
||||
else:
|
||||
raise RuntimeError('Cannot remove sliced filter with '
|
||||
'more than one bin.')
|
||||
|
||||
# Remove and/or reorder filter bins to user specifications
|
||||
bin_indices = []
|
||||
bin_indices = [f.get_bin_index(b)
|
||||
for b in filter_bins[i]]
|
||||
bin_indices = np.unique(bin_indices)
|
||||
|
||||
for filter_bin in filter_bins[i]:
|
||||
bin_index = find_filter.get_bin_index(filter_bin)
|
||||
if issubclass(filter_type, openmc.RealFilter):
|
||||
bin_indices.extend([bin_index, bin_index+1])
|
||||
else:
|
||||
bin_indices.append(bin_index)
|
||||
|
||||
# Set bins for mesh/distribcell filters apart from others
|
||||
if filter_type is openmc.MeshFilter:
|
||||
bins = find_filter.mesh
|
||||
elif filter_type is openmc.DistribcellFilter:
|
||||
bins = find_filter.bins
|
||||
else:
|
||||
bins = np.unique(find_filter.bins[bin_indices])
|
||||
|
||||
# Create new filter
|
||||
new_filter = filter_type(bins)
|
||||
# Set bins for sliced filter
|
||||
new_filter = copy.copy(f)
|
||||
new_filter.bins = [f.bins[i] for i in bin_indices]
|
||||
|
||||
# Set number of bins manually for mesh/distribcell filters
|
||||
if filter_type is openmc.DistribcellFilter:
|
||||
new_filter._num_bins = find_filter._num_bins
|
||||
new_filter._num_bins = f._num_bins
|
||||
|
||||
# Replace existing filter with new one
|
||||
for j, test_filter in enumerate(new_tally.filters):
|
||||
|
|
|
|||
|
|
@ -87,12 +87,14 @@ class TallySliceMergeTestHarness(PyAPITestHarness):
|
|||
# Slice the tallies by cell filter bins
|
||||
cell_filter_prod = itertools.product(tallies, self.cell_filters)
|
||||
tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])],
|
||||
filter_bins=[tf[1].get_bin(0)]), cell_filter_prod)
|
||||
filter_bins=[tf[1].get_bin(0)]),
|
||||
cell_filter_prod)
|
||||
|
||||
# Slice the tallies by energy filter bins
|
||||
energy_filter_prod = itertools.product(tallies, self.energy_filters)
|
||||
tallies = map(lambda tf: tf[0].get_slice(filters=[type(tf[1])],
|
||||
filter_bins=[(tf[1].get_bin(0),)]), energy_filter_prod)
|
||||
filter_bins=[tf[1].get_bin(0)]),
|
||||
energy_filter_prod)
|
||||
|
||||
# Slice the tallies by nuclide
|
||||
nuclide_prod = itertools.product(tallies, self.nuclides)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue