finished unit tests

This commit is contained in:
Ethan Peterson 2020-02-27 12:31:15 -05:00
parent a25882e76f
commit c88bd49597
4 changed files with 174 additions and 139 deletions

View file

@ -225,7 +225,6 @@ class Region(metaclass=ABCMeta):
# at the end
return output[0]
@abstractmethod
def clone(self, memo=None):
"""Create a copy of this region - each of the surfaces in the
region's nodes will be cloned and will have new unique IDs.
@ -242,9 +241,14 @@ class Region(metaclass=ABCMeta):
The clone of this region
"""
pass
@abstractmethod
if memo is None:
memo = {}
clone = deepcopy(self)
clone[:] = [n.clone(memo) for n in self]
return clone
def translate(self, vector, memo=None):
"""Translate region in given direction
@ -262,7 +266,52 @@ class Region(metaclass=ABCMeta):
Translated region
"""
pass
if memo is None:
memo = {}
return type(self)(n.translate(vector, memo) for n in self)
def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
memo=None):
r"""Rotate surface by angles provided or by applying matrix directly.
Parameters
----------
rotation : 3-tuple of float, or 3x3 iterable
A 3-tuple of angles :math:`(\phi, \theta, \psi)` in degrees where
the first element is the rotation about the x-axis in the fixed
laboratory frame, the second element is the rotation about the
y-axis in the fixed laboratory frame, and the third element is the
rotation about the z-axis in the fixed laboratory frame. The
rotations are active rotations. Additionally a 3x3 rotation matrix
can be specified directly either as a nested iterable or array.
pivot : iterable of float, optional
(x, y, z) coordinates for the point to rotate about. Defaults to
(0., 0., 0.)
order : str, optional
A string of 'x', 'y', and 'z' in some order specifying which
rotation to perform first, second, and third. Defaults to 'xyz'
which means, the rotation by angle :math:`\phi` about x will be
applied first, followed by :math:`\theta` about y and then
:math:`\psi` about z. This corresponds to an x-y-z extrinsic
rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
angles :math:`(\phi, \theta, \psi)`.
inplace : boolean
Whether or not to return a new instance of Surface or to modify the
coefficients of this Surface in place. Defaults to False.
memo : dict or None
Dictionary used for memoization
Returns
-------
openmc.Region
Translated region
"""
if memo is None:
memo = {}
return type(self)(n.rotate(rotation, pivot=pivot, order=order,
inplace=inplace, memo=memo) for n in self)
class Intersection(Region, MutableSequence):
@ -354,51 +403,6 @@ class Intersection(Region, MutableSequence):
upper_right[:] = np.minimum(upper_right, upper_right_n)
return lower_left, upper_right
def clone(self, memo=None):
"""Create a copy of this region - each of the surfaces in the
intersection's nodes will be cloned and will have new unique IDs.
Parameters
----------
memo : dict or None
A nested dictionary of previously cloned objects. This parameter
is used internally and should not be specified by the user.
Returns
-------
clone : openmc.Intersection
The clone of this intersection
"""
if memo is None:
memo = {}
clone = deepcopy(self)
clone[:] = [n.clone(memo) for n in self]
return clone
def translate(self, vector, memo=None):
"""Translate region in given direction
Parameters
----------
vector : iterable of float
Direction in which region should be translated
memo : dict or None
Dictionary used for memoization. This parameter is used internally
and should not be specified by the user.
Returns
-------
openmc.Intersection
Translated region
"""
if memo is None:
memo = {}
return type(self)(n.translate(vector, memo) for n in self)
class Union(Region, MutableSequence):
r"""Union of two or more regions.
@ -487,51 +491,6 @@ class Union(Region, MutableSequence):
upper_right[:] = np.maximum(upper_right, upper_right_n)
return lower_left, upper_right
def clone(self, memo=None):
"""Create a copy of this region - each of the surfaces in the
union's nodes will be cloned and will have new unique IDs.
Parameters
----------
memo : dict or None
A nested dictionary of previously cloned objects. This parameter
is used internally and should not be specified by the user.
Returns
-------
clone : openmc.Union
The clone of this union
"""
if memo is None:
memo = {}
clone = deepcopy(self)
clone[:] = [n.clone(memo) for n in self]
return clone
def translate(self, vector, memo=None):
"""Translate region in given direction
Parameters
----------
vector : iterable of float
Direction in which region should be translated
memo : dict or None
Dictionary used for memoization. This parameter is used internally
and should not be specified by the user.
Returns
-------
openmc.Union
Translated region
"""
if memo is None:
memo = {}
return type(self)(n.translate(vector, memo) for n in self)
class Complement(Region):
"""Complement of a region.
@ -642,22 +601,6 @@ class Complement(Region):
region.remove_redundant_surfaces(redundant_surfaces)
def clone(self, memo=None):
"""Create a copy of this region - each of the surfaces in the
complement's node will be cloned and will have new unique IDs.
Parameters
----------
memo : dict or None
A nested dictionary of previously cloned objects. This parameter
is used internally and should not be specified by the user.
Returns
-------
clone : openmc.Complement
The clone of this complement
"""
if memo is None:
memo = {}
@ -666,22 +609,13 @@ class Complement(Region):
return clone
def translate(self, vector, memo=None):
"""Translate region in given direction
Parameters
----------
vector : iterable of float
Direction in which region should be translated
memo : dict or None
Dictionary used for memoization. This parameter is used internally
and should not be specified by the user.
Returns
-------
openmc.Complement
Translated region
"""
if memo is None:
memo = {}
return type(self)(self.node.translate(vector, memo))
def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
memo=None):
if memo is None:
memo = {}
return type(self)(self.node.rotate(rotation, pivot=pivot, order=order,
inplace=inplace, memo=memo))

View file

@ -39,13 +39,13 @@ def get_rotation_matrix(rotation, order='xyz'):
laboratory frame, and the third element is the rotation about the
z-axis in the fixed laboratory frame. The rotations are active
rotations.
order : str, optinoal
order : str, optional
A string of 'x', 'y', and 'z' in some order specifying which rotation
to perform first, second, and third. Defaults to 'xyz' which means, the
rotation by angle phi about x will be applied first, followed by theta
about y and then phi about z. This corresponds to an x-y-z extrinsic
rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
angles :math:`(\phi, \theta, \psi)`.
rotation by angle :math:`\phi` about x will be applied first, followed
by :math:`\theta` about y and then :math:`\psi` about z. This
corresponds to an x-y-z extrinsic rotation as well as a z-y'-x''
intrinsic rotation using Tait-Bryan angles :math:`(\phi, \theta, \psi)`.
"""
check_type('surface rotation', rotation, Iterable, Real)
@ -307,22 +307,29 @@ class Surface(IDManagerMixin, metaclass=ABCMeta):
@abstractmethod
def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False):
r"""Rotate surface by given Tait-Bryan angles
r"""Rotate surface by angles provided or by applying matrix directly.
Parameters
----------
rotation : iterable of float
Intrinsic Tait-Bryan angles in degrees used to rotate the surface
rotation : 3-tuple of float, or 3x3 iterable
A 3-tuple of angles :math:`(\phi, \theta, \psi)` in degrees where
the first element is the rotation about the x-axis in the fixed
laboratory frame, the second element is the rotation about the
y-axis in the fixed laboratory frame, and the third element is the
rotation about the z-axis in the fixed laboratory frame. The
rotations are active rotations. Additionally a 3x3 rotation matrix
can be specified directly either as a nested iterable or array.
pivot : iterable of float, optional
(x, y, z) coordinates for the point to rotate about. Defaults to
(0., 0., 0.)
order : str, optional
A string of 'x', 'y', and 'z' in some order specifying which
rotation to perform first, second, and third. Defaults to 'xyz'
which means, the rotation by angle phi about x will be applied
first, followed by theta about y and then phi about z. This
corresponds to an x-y-z extrinsic rotation as well as a z-y'-x''
intrinsic rotation using Tait-Bryan angles :math:`(\phi, \theta, \psi)`.
which means, the rotation by angle :math:`\phi` about x will be
applied first, followed by :math:`\theta` about y and then
:math:`\psi` about z. This corresponds to an x-y-z extrinsic
rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
angles :math:`(\phi, \theta, \psi)`.
inplace : boolean
Whether or not to return a new instance of Surface or to modify the
coefficients of this Surface in place. Defaults to False.
@ -1291,6 +1298,19 @@ class Cylinder(QuadricMixin, Surface):
check_type('dz coefficient', dz, Real)
self._coefficients['dz'] = dz
def bounding_box(self, side):
if side == '-':
r = self.r
ll = [xi - r if np.isclose(dxi, 0., rtol=0., atol=self._atol)
else -np.inf for xi, dxi in zip(self._origin, self._axis)]
ur = [xi + r if np.isclose(dxi, 0., rtol=0., atol=self._atol)
else np.inf for xi, dxi in zip(self._origin, self._axis)]
return (np.array(ll), np.array(ur))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
def _get_base_coeffs(self):
# Get x, y, z coordinates of two points
x1, y1, z1 = self._origin
@ -2772,9 +2792,59 @@ class Halfspace(Region):
if key not in memo:
memo[key] = self.surface.translate(vector)
# Return translated surface
# Return translated half-space
return type(self)(memo[key], self.side)
def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
memo=None):
r"""Rotate surface by angles provided or by applying matrix directly.
Parameters
----------
rotation : 3-tuple of float, or 3x3 iterable
A 3-tuple of angles :math:`(\phi, \theta, \psi)` in degrees where
the first element is the rotation about the x-axis in the fixed
laboratory frame, the second element is the rotation about the
y-axis in the fixed laboratory frame, and the third element is the
rotation about the z-axis in the fixed laboratory frame. The
rotations are active rotations. Additionally a 3x3 rotation matrix
can be specified directly either as a nested iterable or array.
pivot : iterable of float, optional
(x, y, z) coordinates for the point to rotate about. Defaults to
(0., 0., 0.)
order : str, optional
A string of 'x', 'y', and 'z' in some order specifying which
rotation to perform first, second, and third. Defaults to 'xyz'
which means, the rotation by angle :math:`\phi` about x will be
applied first, followed by :math:`\theta` about y and then
:math:`\psi` about z. This corresponds to an x-y-z extrinsic
rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
angles :math:`(\phi, \theta, \psi)`.
inplace : boolean
Whether or not to return a new instance of Surface or to modify the
coefficients of this Surface in place. Defaults to False.
memo : dict or None
Dictionary used for memoization
Returns
-------
openmc.Halfspace
Translated half-space
"""
if memo is None:
memo = {}
# If rotated surface not in memo, add it
key = (self.surface, tuple(rotation), tuple(pivot), order, inplace)
if key not in memo:
memo[key] = self.surface.rotate(rotation, pivot=pivot, order=order,
inplace=inplace)
# Return rotated half-space
return type(self)(memo[key], self.side)
_SURFACE_CLASSES = {cls._type: cls for cls in Surface.__subclasses__()}

View file

@ -40,6 +40,13 @@ def test_union(reset):
assert (6, 0, 0) not in regt
assert (8, 0, 0) in regt
# rotate method
regr = region.rotate((0., 90., 0.))
assert (-4, 0, 0) not in regr
assert (0, 0, 6) in regr
assert (0, 0, -6) in regr
assert (0, 0, 3) not in regr
def test_intersection(reset):
s1 = openmc.XPlane(x0=5, surface_id=1)
@ -73,6 +80,13 @@ def test_intersection(reset):
assert (6, 0, 0) in regt
assert (8, 0, 0) not in regt
# rotate method
regr = region.rotate((0., 90., 0.))
assert (-4, 0, 0) in regr
assert (0, 0, 6) not in regr
assert (0, 0, -6) not in regr
assert (0, 0, 3) in regr
def test_complement(reset):
zcyl = openmc.ZCylinder(r=1., surface_id=1)
@ -106,6 +120,14 @@ def test_complement(reset):
assert ll == pytest.approx((0., 0., -4.))
assert ur == pytest.approx((2., 2., 6.))
# rotate method
inside_r = inside.rotate((90., 0., 0.))
ll, ur = inside_r.bounding_box
assert (.5, 2, 0) in inside_r
assert (0, 0, 6) not in inside_r
assert ll == pytest.approx((-1., -5., -1.))
assert ur == pytest.approx((1., 5., 1.))
def test_get_surfaces():
s1 = openmc.XPlane()

View file

@ -533,6 +533,15 @@ def test_quadric():
assert (st.g, st.h, st.j) == (-2, -2, -2)
assert st.k == s.k + 3
# rotate method
x0, y0, z0, r2 = 2, 3, 4, 4
dx, dy, dz = 1, -1, 1
s = openmc.Cone(x0=x0, y0=y0, z0=z0, dx=dx, dy=dy, dz=dz, r2=r2)
q = openmc.Quadric(*s._get_base_coeffs())
qr = q.rotate((45, 60, 30))
sr = s.rotate((45, 60, 30))
assert qr.is_equal(sr)
def test_cylinder_from_points():
seed(1) # Make random numbers reproducible