Merge pull request #425 from smharper/pyapi_lattice_outer

Fix PyAPI HexLattice.get_unique_universes()
This commit is contained in:
Will Boyd 2015-08-07 21:51:21 -07:00
commit c982eb72ba
6 changed files with 250 additions and 74 deletions

View file

@ -1,3 +1,36 @@
from collections import Iterable
from numbers import Integral, Real
import numpy as np
def _isinstance(value, expected_type):
"""A Numpy-aware replacement for isinstance
This function will be obsolete when Numpy v. >= 1.9 is established.
"""
# Declare numpy numeric types.
np_ints = (np.int_, np.intc, np.intp, np.int8, np.int16, np.int32, np.int64,
np.uint8, np.uint16, np.uint32, np.uint64)
np_floats = (np.float_, np.float16, np.float32, np.float64)
# Include numpy integers, if necessary.
if type(expected_type) is tuple:
if Integral in expected_type:
expected_type = expected_type + np_ints
elif expected_type is Integral:
expected_type = (Integral, ) + np_ints
# Include numpy floats, if necessary.
if type(expected_type) is tuple:
if Real in expected_type:
expected_type = expected_type + np_floats
elif expected_type is Real:
expected_type = (Real, ) + np_floats
# Now, make the instance check.
return isinstance(value, expected_type)
def check_type(name, value, expected_type, expected_iter_type=None):
"""Ensure that an object is of an expected type. Optionally, if the object is
iterable, check that each element is of a particular type.
@ -16,20 +49,97 @@ def check_type(name, value, expected_type, expected_iter_type=None):
"""
if not isinstance(value, expected_type):
if not _isinstance(value, expected_type):
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
name, value, expected_type.__name__)
raise ValueError(msg)
if expected_iter_type:
for item in value:
if not isinstance(item, expected_iter_type):
if not _isinstance(item, expected_iter_type):
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
'of type "{2}"'.format(name, value,
expected_iter_type.__name__)
raise ValueError(msg)
def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
"""Ensure that an object is an iterable containing an expected type.
Parameters
----------
name : str
Description of value being checked
value : Iterable
Iterable, possibly of other iterables, that should ultimately contain
the expected type
expected_type : type
type that the iterable should contain
min_depth : int
The minimum number of layers of nested iterables there should be before
reaching the ultimately contained items
max_depth : int
The maximum number of layers of nested iterables there should be before
reaching the ultimately contained items
"""
# Initialize the tree at the very first item.
tree = [value]
index = [0]
# Traverse the tree.
while index[0] != len(tree[0]):
# If we are done with this level of the tree, go to the next branch on
# the level above this one.
if index[-1] == len(tree[-1]):
del index[-1]
del tree[-1]
index[-1] += 1
continue
# Get a string representation of the current index in case we raise an
# exception.
form = '[' + '{:d}, '*(len(index)-1) + '{:d}]'
ind_str = form.format(*index)
# What is the current item we are looking at?
current_item = tree[-1][index[-1]]
# If this item is of the expected type, then we've reached the bottom
# level of this branch.
if _isinstance(current_item, expected_type):
# Is this deep enough?
if len(tree) < min_depth:
msg = 'Error setting "{0}": The item at {1} does not meet the '\
'minimum depth of {2}'.format(name, ind_str, min_depth)
raise ValueError(msg)
# This item is okay. Move on to the next item.
index[-1] += 1
# If this item is not of the expected type, then it's either an error or
# another level of the tree that we need to pursue deeper.
else:
if isinstance(current_item, Iterable):
# The tree goes deeper here, let's explore it.
tree.append(current_item)
index.append(0)
# But first, have we exceeded the max depth?
if len(tree) > max_depth:
msg = 'Error setting {0}: Found an iterable at {1}, items '\
'in that iterable excceed the maximum depth of {2}' \
.format(name, ind_str, max_depth)
raise ValueError(msg)
else:
# This item is completely unexpected.
msg = "Error setting {0}: Items must be of type '{1}', but " \
"item at {2} is of type '{3}'"\
.format(name, expected_type.__name__, ind_str,
type(current_item).__name__)
raise ValueError(msg)
def check_length(name, value, length_min, length_max=None):
"""Ensure that a sized object has length within a given range.

View file

@ -6,7 +6,8 @@ import numpy as np
from openmc import Mesh
from openmc.constants import *
from openmc.checkvalue import check_type
from openmc.checkvalue import check_type, check_iterable_type, \
check_greater_than
class Filter(object):
"""A filter used to constrain a tally to a specific criterion, e.g. only tally
@ -134,15 +135,9 @@ class Filter(object):
if self.type in ['cell', 'cellborn', 'surface', 'material',
'universe', 'distribcell']:
check_iterable_type('filter bins', bins, Integral)
for edge in bins:
if not isinstance(edge, Integral):
msg = 'Unable to add bin "{0}" to a "{1}" Filter since ' \
'it is not an integer'.format(edge, self._type)
raise ValueError(msg)
elif edge < 0:
msg = 'Unable to add bin "{0}" to a "{1}" Filter since ' \
'it is negative'.format(edge, self._type)
raise ValueError(msg)
check_greater_than('filter bin', edge, 0, equality=True)
elif self._type in ['energy', 'energyout']:
for edge in bins:
@ -185,12 +180,8 @@ class Filter(object):
# FIXME
@num_bins.setter
def num_bins(self, num_bins):
if not isinstance(num_bins, Integral) 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)
check_type('filter num_bins', num_bins, Integral)
check_greater_than('filter num_bins', num_bins, 0, equality=True)
self._num_bins = num_bins
@mesh.setter

View file

@ -411,16 +411,70 @@ class Summary(object):
if outer != -22:
lattice.outer = self.universes[outer]
# Build array of Universe pointers for the Lattice
universes = \
np.ndarray(tuple(universe_ids.shape), dtype=openmc.Universe)
# Build array of Universe pointers for the Lattice. Note that
# we need to convert between the HDF5's square array of
# (x, alpha, z) to the Python API's format of a ragged nested
# list of (z, ring, theta).
universes = []
for z in range(lattice.num_axial):
# Add a list for this axial level.
universes.append([])
x = lattice.num_rings - 1
a = 2*lattice.num_rings - 2
for r in range(lattice.num_rings - 1, 0, -1):
# Add a list for this ring.
universes[-1].append([])
for i in range(universe_ids.shape[0]):
for j in range(universe_ids.shape[1]):
for k in range(universe_ids.shape[2]):
if universe_ids[i, j, k] != -1:
universes[i, j, k] = self.get_universe_by_id(
universe_ids[i, j, k])
# Climb down the top-right.
for i in range(r):
universes[-1][-1].append(universe_ids[z, a, x])
x += 1
a -= 1
# Climb down the right.
for i in range(r):
universes[-1][-1].append(universe_ids[z, a, x])
a -= 1
# Climb down the bottom-right.
for i in range(r):
universes[-1][-1].append(universe_ids[z, a, x])
x -= 1
# Climb up the bottom-left.
for i in range(r):
universes[-1][-1].append(universe_ids[z, a, x])
x -= 1
a += 1
# Climb up the left.
for i in range(r):
universes[-1][-1].append(universe_ids[z, a, x])
a += 1
# Climb up the top-left.
for i in range(r):
universes[-1][-1].append(universe_ids[z, a, x])
x += 1
# Move down to the next ring.
a -= 1
# Convert the ids into Universe objects.
universes[-1][-1] = [self.get_universe_by_id(u_id)
for u_id in universes[-1][-1]]
# Handle the degenerate center ring separately.
u_id = universe_ids[z, a, x]
universes[-1].append([self.get_universe_by_id(u_id)])
# Add the universes to the lattice.
if len(pitch) == 2:
# Lattice is 3D
lattice.universes = universes
else:
# Lattice is 2D; extract the only axial level
lattice.universes = universes[0]
if offset_size > 0:
lattice.offsets = offsets

12
openmc/temp.py Normal file
View file

@ -0,0 +1,12 @@
from checkvalue import *
from checkvalue import _isinstance
import numpy as np
zs = np.zeros((2,))
print _isinstance(zs[0], Integral)
print _isinstance(zs[0], Real)
print _isinstance(zs[0], (Integral, Real))
print check_iterable_type('thing', zs, (Real, Integral))

View file

@ -7,7 +7,7 @@ import sys
import numpy as np
import openmc
from openmc.checkvalue import check_type, check_length, check_greater_than
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
@ -111,13 +111,13 @@ class Cell(object):
self._id = AUTO_CELL_ID
AUTO_CELL_ID += 1
else:
check_type('cell ID', cell_id, Integral)
check_greater_than('cell ID', cell_id, 0)
cv.check_type('cell ID', cell_id, Integral)
cv.check_greater_than('cell ID', cell_id, 0)
self._id = cell_id
@name.setter
def name(self, name):
check_type('cell name', name, basestring)
cv.check_type('cell name', name, basestring)
self._name = name
@fill.setter
@ -148,19 +148,19 @@ class Cell(object):
@rotation.setter
def rotation(self, rotation):
check_type('cell rotation', rotation, Iterable, Real)
check_length('cell rotation', rotation, 3)
cv.check_type('cell rotation', rotation, Iterable, Real)
cv.check_length('cell rotation', rotation, 3)
self._rotation = rotation
@translation.setter
def translation(self, translation):
check_type('cell translation', translation, Iterable, Real)
check_length('cell translation', translation, 3)
cv.check_type('cell translation', translation, Iterable, Real)
cv.check_length('cell translation', translation, 3)
self._translation = translation
@offsets.setter
def offsets(self, offsets):
check_type('cell offsets', offsets, Iterable)
cv.check_type('cell offsets', offsets, Iterable)
self._offsets = offsets
def add_surface(self, surface, halfspace):
@ -432,13 +432,13 @@ class Universe(object):
self._id = AUTO_UNIVERSE_ID
AUTO_UNIVERSE_ID += 1
else:
check_type('universe ID', universe_id, Integral)
check_greater_than('universe ID', universe_id, 0, True)
cv.check_type('universe ID', universe_id, Integral)
cv.check_greater_than('universe ID', universe_id, 0, True)
self._id = universe_id
@name.setter
def name(self, name):
check_type('universe name', name, basestring)
cv.check_type('universe name', name, basestring)
self._name = name
def add_cell(self, cell):
@ -671,24 +671,25 @@ class Lattice(object):
self._id = AUTO_UNIVERSE_ID
AUTO_UNIVERSE_ID += 1
else:
check_type('lattice ID', lattice_id, Integral)
check_greater_than('lattice ID', lattice_id, 0)
cv.check_type('lattice ID', lattice_id, Integral)
cv.check_greater_than('lattice ID', lattice_id, 0)
self._id = lattice_id
@name.setter
def name(self, name):
check_type('lattice name', name, basestring)
cv.check_type('lattice name', name, basestring)
self._name = name
@outer.setter
def outer(self, outer):
check_type('outer universe', outer, Universe)
cv.check_type('outer universe', outer, Universe)
self._outer = outer
@universes.setter
def universes(self, universes):
check_type('lattice universes', universes, Iterable)
self._universes = np.asarray(universes, dtype=Universe)
cv.check_iterable_type('lattice universes', universes, Universe,
min_depth=2, max_depth=3)
self._universes = universes
def get_unique_universes(self):
"""Determine all unique universes in the lattice
@ -701,13 +702,21 @@ class Lattice(object):
"""
unique_universes = np.unique(self._universes.ravel())
universes = {}
univs = dict()
for k in range(len(self._universes)):
for j in range(len(self._universes[k])):
if isinstance(self._universes[k][j], Universe):
u = self._universes[k][j]
univs[u._id] = u
else:
for i in range(len(self._universes[k][j])):
u = self._universes[k][j][i]
assert isinstance(u, Universe)
univs[u._id] = u
for universe in unique_universes:
universes[universe._id] = universe
univs[self._outer._id] = self._outer
return universes
return univs
def get_all_nuclides(self):
"""Return all nuclides contained in the lattice
@ -825,29 +834,29 @@ class RectLattice(Lattice):
@dimension.setter
def dimension(self, dimension):
check_type('lattice dimension', dimension, Iterable, Integral)
check_length('lattice dimension', dimension, 2, 3)
cv.check_type('lattice dimension', dimension, Iterable, Integral)
cv.check_length('lattice dimension', dimension, 2, 3)
for dim in dimension:
check_greater_than('lattice dimension', dim, 0)
cv.check_greater_than('lattice dimension', dim, 0)
self._dimension = dimension
@lower_left.setter
def lower_left(self, lower_left):
check_type('lattice lower left corner', lower_left, Iterable, Real)
check_length('lattice lower left corner', lower_left, 2, 3)
cv.check_type('lattice lower left corner', lower_left, Iterable, Real)
cv.check_length('lattice lower left corner', lower_left, 2, 3)
self._lower_left = lower_left
@offsets.setter
def offsets(self, offsets):
check_type('lattice offsets', offsets, Iterable)
cv.check_type('lattice offsets', offsets, Iterable)
self._offsets = offsets
@Lattice.pitch.setter
def pitch(self, pitch):
check_type('lattice pitch', pitch, Iterable, Real)
check_length('lattice pitch', pitch, 2, 3)
cv.check_type('lattice pitch', pitch, Iterable, Real)
cv.check_length('lattice pitch', pitch, 2, 3)
for dim in pitch:
check_greater_than('lattice pitch', dim, 0.0)
cv.check_greater_than('lattice pitch', dim, 0.0)
self._pitch = pitch
def get_offset(self, path, filter_offset):
@ -1041,28 +1050,28 @@ class HexLattice(Lattice):
@num_rings.setter
def num_rings(self, num_rings):
check_type('number of rings', num_rings, Integral)
check_greater_than('number of rings', num_rings, 0)
cv.check_type('number of rings', num_rings, Integral)
cv.check_greater_than('number of rings', num_rings, 0)
self._num_rings = num_rings
@num_axial.setter
def num_axial(self, num_axial):
check_type('number of axial', num_axial, Integral)
check_greater_than('number of axial', num_axial, 0)
cv.check_type('number of axial', num_axial, Integral)
cv.check_greater_than('number of axial', num_axial, 0)
self._num_axial = num_axial
@center.setter
def center(self, center):
check_type('lattice center', center, Iterable, Real)
check_length('lattice center', center, 2, 3)
cv.check_type('lattice center', center, Iterable, Real)
cv.check_length('lattice center', center, 2, 3)
self._center = center
@Lattice.pitch.setter
def pitch(self, pitch):
check_type('lattice pitch', pitch, Iterable, Real)
check_length('lattice pitch', pitch, 1, 2)
cv.check_type('lattice pitch', pitch, Iterable, Real)
cv.check_length('lattice pitch', pitch, 1, 2)
for dim in pitch:
check_greater_than('lattice pitch', dim, 0)
cv.check_greater_than('lattice pitch', dim, 0)
self._pitch = pitch
@Lattice.universes.setter
@ -1091,14 +1100,14 @@ class HexLattice(Lattice):
# Set the number of axial positions.
if n_dims == 3:
self.num_axial = self._universes.shape[0]
self.num_axial = len(self._universes)
else:
self._num_axial = None
# Set the number of rings and make sure this number is consistent for
# all axial positions.
if n_dims == 3:
self.num_rings = len(self._universes[0])
self.num_rings = len(self._universes)
for rings in self._universes:
if len(rings) != self._num_rings:
msg = 'HexLattice ID={0:d} has an inconsistent number of ' \
@ -1106,7 +1115,7 @@ class HexLattice(Lattice):
raise ValueError(msg)
else:
self.num_rings = self._universes.shape[0]
self.num_rings = len(self._universes)
# Make sure there are the correct number of elements in each ring.
if n_dims == 3:

View file

@ -394,7 +394,7 @@ contains
! Write number of lattice cells.
call su % write_data(lat % n_rings, "n_rings", &
group="geometry/lattices/lattice " // trim(to_str(lat % id)))
call su % write_data(lat % n_rings, "n_axial", &
call su % write_data(lat % n_axial, "n_axial", &
group="geometry/lattices/lattice " // trim(to_str(lat % id)))
! Write lattice center, pitch and outer universe.
@ -407,10 +407,10 @@ contains
end if
if (lat % is_3d) then
call su % write_data(lat % pitch, "pitch", length=3, &
call su % write_data(lat % pitch, "pitch", length=2, &
group="geometry/lattices/lattice " // trim(to_str(lat % id)))
else
call su % write_data(lat % pitch, "pitch", length=2, &
call su % write_data(lat % pitch, "pitch", length=1, &
group="geometry/lattices/lattice " // trim(to_str(lat % id)))
end if
@ -447,7 +447,7 @@ contains
end do
end do
call su % write_data(lattice_universes, "universes", &
&length=(/lat % n_axial, 2*lat % n_rings-1, 2*lat % n_rings-1/), &
&length=(/2*lat % n_rings-1, 2*lat % n_rings-1, lat % n_axial/), &
&group="geometry/lattices/lattice " // trim(to_str(lat % id)))
deallocate(lattice_universes)
end select