From 6eb26b80c6a8fe4613b2183df61d6c3c49856366 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Apr 2023 13:11:01 +0100 Subject: [PATCH 1/6] implemented BoundingBox class --- openmc/__init__.py | 1 + openmc/bounding_box.py | 44 +++++++++++ openmc/cell.py | 5 +- openmc/lib/cell.py | 4 +- openmc/region.py | 5 +- openmc/surface.py | 102 +++++++++++++++++--------- tests/unit_tests/test_bounding_box.py | 67 +++++++++++++++++ 7 files changed, 188 insertions(+), 40 deletions(-) create mode 100644 openmc/bounding_box.py create mode 100644 tests/unit_tests/test_bounding_box.py diff --git a/openmc/__init__.py b/openmc/__init__.py index ac9e02e16..5bb150fb0 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -1,4 +1,5 @@ from openmc.arithmetic import * +from openmc.bounding_box import * from openmc.cell import * from openmc.checkvalue import * from openmc.mesh import * diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py new file mode 100644 index 000000000..081893797 --- /dev/null +++ b/openmc/bounding_box.py @@ -0,0 +1,44 @@ +import numpy as np +from .checkvalue import check_type, check_length + + +class BoundingBox(tuple): + """Axis-aligned bounding box. + + Parameters + ---------- + corners : 2-tuple of numpy.array + Lower-left and upper-right coordinates of an axis-aligned bounding box + of the domain. + + Attributes + ---------- + volume: float + The volume of the bounding box in cm3 + center: numpy.array + x, y, z coordinates of the center of the bounding box in cm. + """ + + def __init__(self, corners): + + check_type("corners", corners, tuple) + check_type("corners", corners[0], np.ndarray) + check_type("corners", corners[1], np.ndarray) + check_length("corners", corners, 2, 2) + check_length("corners", corners[0], 3, 3) + check_length("corners", corners[1], 3, 3) + self.corners = corners + + @property + def center(self): + """The center x, y, z coordinates of the bounding box""" + # x_center = (self.corners[0][0] - self.corners[1][0]) / 2 + # y_center = (self.corners[0][1] - self.corners[1][1]) / 2 + # z_center = (self.corners[0][2] - self.corners[1][2]) / 2 + # return np.array([x_center, y_center, z_center]) + return (self.corners[0] + self.corners[1]) / 2 + + @property + def volume(self): + """The volume of the bounding box""" + return np.abs(np.prod(self.corners[1] - self.corners[0])) diff --git a/openmc/cell.py b/openmc/cell.py index a8e1178f4..db8ffd48b 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -13,6 +13,7 @@ from ._xml import get_text from .mixin import IDManagerMixin from .region import Region, Complement from .surface import Halfspace +from .bounding_box import BoundingBox class Cell(IDManagerMixin): @@ -249,8 +250,8 @@ class Cell(IDManagerMixin): if self.region is not None: return self.region.bounding_box else: - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox((np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]))) @property def num_instances(self): diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index b53a6be7a..c88d6da51 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -11,6 +11,8 @@ from . import _dll from .core import _FortranObjectWithID from .error import _error_handler from .material import Material +from ..bounding_box import BoundingBox + __all__ = ['Cell', 'cells'] @@ -289,7 +291,7 @@ class Cell(_FortranObjectWithID): llc[llc == -inf] = -np.inf urc[urc == -inf] = -np.inf - return llc, urc + return BoundingBox((llc, urc)) class _CellMapping(Mapping): diff --git a/openmc/region.py b/openmc/region.py index 5d0e68020..d8935a070 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -6,6 +6,7 @@ from copy import deepcopy import numpy as np from .checkvalue import check_type +from .bounding_box import BoundingBox class Region(ABC): @@ -418,7 +419,7 @@ class Intersection(Region, MutableSequence): lower_left_n, upper_right_n = n.bounding_box lower_left[:] = np.maximum(lower_left, lower_left_n) upper_right[:] = np.minimum(upper_right, upper_right_n) - return lower_left, upper_right + return BoundingBox((lower_left, upper_right)) class Union(Region, MutableSequence): @@ -506,7 +507,7 @@ class Union(Region, MutableSequence): lower_left_n, upper_right_n = n.bounding_box lower_left[:] = np.minimum(lower_left, lower_left_n) upper_right[:] = np.maximum(upper_right, upper_right_n) - return lower_left, upper_right + return BoundingBox((lower_left, upper_right)) class Complement(Region): diff --git a/openmc/surface.py b/openmc/surface.py index b8ea5479d..98ced13c6 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -12,6 +12,7 @@ import numpy as np from .checkvalue import check_type, check_value, check_length from .mixin import IDManagerMixin, IDWarning from .region import Region, Intersection, Union +from .bounding_box import BoundingBox _BOUNDARY_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic', 'white'] @@ -233,8 +234,10 @@ class Surface(IDManagerMixin, ABC): desired half-space """ - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) def clone(self, memo=None): """Create a copy of this surface with a new unique ID. @@ -539,7 +542,7 @@ class PlaneMixin: else: ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) - return (ll, ur) + return BoundingBox((ll, ur)) def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1196,8 +1199,10 @@ class Cylinder(QuadricMixin, Surface): 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])) + return BoundingBox(( + 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 @@ -1359,11 +1364,15 @@ class XCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), - np.array([np.inf, self.y0 + self.r, self.z0 + self.r])) + return BoundingBox(( + np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), + np.array([np.inf, self.y0 + self.r, self.z0 + self.r]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) def evaluate(self, point): y = point[1] - self.y0 @@ -1450,11 +1459,15 @@ class YCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), - np.array([self.x0 + self.r, np.inf, self.z0 + self.r])) + return BoundingBox(( + np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), + np.array([self.x0 + self.r, np.inf, self.z0 + self.r]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) def evaluate(self, point): x = point[0] - self.x0 @@ -1541,11 +1554,15 @@ class ZCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), - np.array([self.x0 + self.r, self.y0 + self.r, np.inf])) + return BoundingBox(( + np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), + np.array([self.x0 + self.r, self.y0 + self.r, np.inf]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) def evaluate(self, point): x = point[0] - self.x0 @@ -1631,13 +1648,15 @@ class Sphere(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return (np.array([self.x0 - self.r, self.y0 - self.r, - self.z0 - self.r]), - np.array([self.x0 + self.r, self.y0 + self.r, - self.z0 + self.r])) + return BoundingBox(( + np.array([self.x0 - self.r, self.y0 - self.r, self.z0 - self.r]), + np.array([self.x0 + self.r, self.y0 + self.r, self.z0 + self.r]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) def evaluate(self, point): x = point[0] - self.x0 @@ -2253,11 +2272,15 @@ class XTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return (np.array([x0 - b, y0 - a - c, z0 - a - c]), - np.array([x0 + b, y0 + a + c, z0 + a + c])) + return BoundingBox(( + np.array([x0 - b, y0 - a - c, z0 - a - c]), + np.array([x0 + b, y0 + a + c, z0 + a + c]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) class YTorus(TorusMixin, Surface): r"""A torus of the form :math:`(y - y_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (z - @@ -2324,11 +2347,16 @@ class YTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return (np.array([x0 - a - c, y0 - b, z0 - a - c]), - np.array([x0 + a + c, y0 + b, z0 + a + c])) + return BoundingBox(( + np.array([x0 - a - c, y0 - b, z0 - a - c]), + np.array([x0 + a + c, y0 + b, z0 + a + c]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) + class ZTorus(TorusMixin, Surface): r"""A torus of the form :math:`(z - z_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (y - @@ -2395,11 +2423,15 @@ class ZTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return (np.array([x0 - a - c, y0 - a - c, z0 - b]), - np.array([x0 + a + c, y0 + a + c, z0 + b])) + return BoundingBox(( + np.array([x0 - a - c, y0 - a - c, z0 - b]), + np.array([x0 + a + c, y0 + a + c, z0 + b]) + )) elif side == '+': - return (np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf])) + return BoundingBox(( + np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf]) + )) class Halfspace(Region): diff --git a/tests/unit_tests/test_bounding_box.py b/tests/unit_tests/test_bounding_box.py new file mode 100644 index 000000000..72c1baf48 --- /dev/null +++ b/tests/unit_tests/test_bounding_box.py @@ -0,0 +1,67 @@ +import numpy as np +import openmc +import pytest + + +test_bb_1 = openmc.BoundingBox( + (np.array([-10.0, -20.0, -30.0]), np.array([1.0, 2.0, 3.0])) +) +test_bb_2 = openmc.BoundingBox( + (np.array([1.0, 2.0, 3.0]), np.array([11.0, 22.0, 33.0])) +) +test_bb_3 = openmc.BoundingBox( + (np.array([-10.0, -20.0, -30.0]), np.array([-1.0, -2.0, -3.0])) +) + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, 7986), # 11 * 22 * 33 + (test_bb_2, 6000), # 10 * 20 * 30 + (test_bb_3, 4374), # 9 * 18 * 27 + ], +) +def test_bounding_box_volume(bb, expected): + assert bb.volume == expected + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, np.array([-4.5, -9.0, -13.5])), + (test_bb_2, np.array([6.0, 12.0, 18.0])), + (test_bb_3, np.array([-5.5, -11.0, -16.5])), + ], +) +def test_bounding_box_center(bb, expected): + assert np.array_equiv(expected, bb.center) + + +def test_bounding_box_input_checking(): + # checks that a list is not accepted instead of np.array + with pytest.raises(TypeError): + openmc.BoundingBox(([-10, -20, -30], [1, 2, 3])) + # checks that a list is not accepted instead of tuple + with pytest.raises(TypeError): + openmc.BoundingBox([np.array([-10, -20, -30]), np.array([1, 2, 3])]) + # checks that a tuple with one entry is not accepted + with pytest.raises(TypeError): + openmc.BoundingBox((np.array([-10, -20, -30]))) + # checks that a tuple with three entry is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox( + (np.array([-1, -2, -3]), np.array([-1, -2, -3]), np.array([-1, -2, -3])) + ) + # checks that a numpy array with two entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox((np.array([-10, -30]), np.array([1, 2, 3]))) + # checks that a numpy array with two entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox((np.array([-10, -20, -30]), np.array([1, 3]))) + # checks that a numpy array with four entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox((np.array([-10, -20, -3, -4]), np.array([1, 2, 3]))) + # checks that a numpy array with four entries is not accepted + with pytest.raises(ValueError): + openmc.BoundingBox((np.array([-10, -20, -4]), np.array([1, 2, 3, 4]))) From b3ff7f26e37675a193cccbb5b6f2a49a35b22b91 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Apr 2023 14:06:15 +0100 Subject: [PATCH 2/6] removed commented out code --- openmc/bounding_box.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py index 081893797..e9f225fe1 100644 --- a/openmc/bounding_box.py +++ b/openmc/bounding_box.py @@ -32,10 +32,6 @@ class BoundingBox(tuple): @property def center(self): """The center x, y, z coordinates of the bounding box""" - # x_center = (self.corners[0][0] - self.corners[1][0]) / 2 - # y_center = (self.corners[0][1] - self.corners[1][1]) / 2 - # z_center = (self.corners[0][2] - self.corners[1][2]) / 2 - # return np.array([x_center, y_center, z_center]) return (self.corners[0] + self.corners[1]) / 2 @property From c4aafdd5089a6b91e6f1985d5374a0e098c722e4 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Apr 2023 17:11:44 +0100 Subject: [PATCH 3/6] added ll and ur as suggested by @pshriwise --- openmc/bounding_box.py | 18 ++++++++++++++++-- tests/unit_tests/test_bounding_box.py | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py index e9f225fe1..e297cc225 100644 --- a/openmc/bounding_box.py +++ b/openmc/bounding_box.py @@ -13,10 +13,14 @@ class BoundingBox(tuple): Attributes ---------- - volume: float - The volume of the bounding box in cm3 center: numpy.array x, y, z coordinates of the center of the bounding box in cm. + lower_left: numpy.array + The x, y, z coordinates of the lower left corner of the bounding box + upper_right + The x, y, z coordinates of the upper right corner of the bounding box + volume: float + The volume of the bounding box in cm3 """ def __init__(self, corners): @@ -34,6 +38,16 @@ class BoundingBox(tuple): """The center x, y, z coordinates of the bounding box""" return (self.corners[0] + self.corners[1]) / 2 + @property + def lower_left(self): + """The x, y, z coordinates of the lower left corner of the bounding box""" + return self.corners[0] + + @property + def upper_right(self): + """The x, y, z coordinates of the upper right corner of the bounding box""" + return self.corners[1] + @property def volume(self): """The volume of the bounding box""" diff --git a/tests/unit_tests/test_bounding_box.py b/tests/unit_tests/test_bounding_box.py index 72c1baf48..419ada653 100644 --- a/tests/unit_tests/test_bounding_box.py +++ b/tests/unit_tests/test_bounding_box.py @@ -26,6 +26,30 @@ def test_bounding_box_volume(bb, expected): assert bb.volume == expected +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, np.array([-10.0, -20.0, -30.0])), + (test_bb_2, np.array([1.0, 2.0, 3.0])), + (test_bb_3, np.array([-10.0, -20.0, -30.0])), + ], +) +def test_bounding_lower_left(bb, expected): + assert np.array_equiv(expected, bb.lower_left) + + +@pytest.mark.parametrize( + "bb, expected", + [ + (test_bb_1, np.array([1.0, 2.0, 3.0])), + (test_bb_2, np.array([11.0, 22.0, 33.0])), + (test_bb_3, np.array([-1.0, -2.0, -3.0])), + ], +) +def test_bounding_upper_right(bb, expected): + assert np.array_equiv(expected, bb.upper_right) + + @pytest.mark.parametrize( "bb, expected", [ From b5d30ff553d2d5379635cee04bb21d70653d6f2e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 18 Apr 2023 22:04:46 -0500 Subject: [PATCH 4/6] Refactor BoundingBox implementation --- docs/source/pythonapi/base.rst | 1 + openmc/bounding_box.py | 67 +++++++++++++------------- openmc/cell.py | 6 ++- openmc/geometry.py | 2 +- openmc/lib/cell.py | 4 +- openmc/region.py | 10 ++-- openmc/surface.py | 68 +++++++++++++-------------- openmc/universe.py | 6 +-- tests/unit_tests/test_bounding_box.py | 36 +++++--------- 9 files changed, 96 insertions(+), 104 deletions(-) diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 72d2534ea..d379fb4bb 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -87,6 +87,7 @@ Building geometry openmc.Intersection openmc.Union openmc.Complement + openmc.BoundingBox openmc.Cell openmc.Universe openmc.DAGMCUniverse diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py index e297cc225..a4a695b99 100644 --- a/openmc/bounding_box.py +++ b/openmc/bounding_box.py @@ -1,54 +1,57 @@ +from typing import Iterable + import numpy as np -from .checkvalue import check_type, check_length + +from .checkvalue import check_length class BoundingBox(tuple): """Axis-aligned bounding box. + .. versionadded:: 0.13.4 + Parameters ---------- - corners : 2-tuple of numpy.array - Lower-left and upper-right coordinates of an axis-aligned bounding box - of the domain. + lower_left : iterable of float + The x, y, z coordinates of the lower left corner of the bounding box in [cm] + upper_right : iterable of float + The x, y, z coordinates of the upper right corner of the bounding box in [cm] Attributes ---------- - center: numpy.array - x, y, z coordinates of the center of the bounding box in cm. - lower_left: numpy.array - The x, y, z coordinates of the lower left corner of the bounding box - upper_right - The x, y, z coordinates of the upper right corner of the bounding box - volume: float - The volume of the bounding box in cm3 + center : numpy.ndarray + x, y, z coordinates of the center of the bounding box in [cm]. + lower_left : numpy.ndarray + The x, y, z coordinates of the lower left corner of the bounding box in [cm] + upper_right : numpy.ndarray + The x, y, z coordinates of the upper right corner of the bounding box in [cm] + volume : float + The volume of the bounding box in [cm^3] """ - def __init__(self, corners): + def __new__(cls, lower_left: Iterable[float], upper_right: Iterable[float]): + check_length("lower_left", lower_left, 3, 3) + check_length("upper_right", upper_right, 3, 3) + lower_left = np.array(lower_left, dtype=float) + upper_right = np.array(upper_right, dtype=float) + return tuple.__new__(cls, (lower_left, upper_right)) - check_type("corners", corners, tuple) - check_type("corners", corners[0], np.ndarray) - check_type("corners", corners[1], np.ndarray) - check_length("corners", corners, 2, 2) - check_length("corners", corners[0], 3, 3) - check_length("corners", corners[1], 3, 3) - self.corners = corners + def __repr__(self) -> str: + return "BoundingBox(lower_left={}, upper_right={})".format( + tuple(self.lower_left), tuple(self.upper_right)) @property - def center(self): - """The center x, y, z coordinates of the bounding box""" - return (self.corners[0] + self.corners[1]) / 2 + def center(self) -> np.ndarray: + return (self[0] + self[1]) / 2 @property - def lower_left(self): - """The x, y, z coordinates of the lower left corner of the bounding box""" - return self.corners[0] + def lower_left(self) -> np.ndarray: + return self[0] @property - def upper_right(self): - """The x, y, z coordinates of the upper right corner of the bounding box""" - return self.corners[1] + def upper_right(self) -> np.ndarray: + return self[1] @property - def volume(self): - """The volume of the bounding box""" - return np.abs(np.prod(self.corners[1] - self.corners[0])) + def volume(self) -> float: + return np.abs(np.prod(self[1] - self[0])) diff --git a/openmc/cell.py b/openmc/cell.py index db8ffd48b..67735ef5e 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -92,6 +92,8 @@ class Cell(IDManagerMixin): fill. For example, {'U235': 1.0e22, 'U238': 5.0e22, ...}. .. versionadded:: 0.12 + bounding_box : openmc.BoundingBox + Axis-aligned bounding box of the cell """ @@ -250,8 +252,8 @@ class Cell(IDManagerMixin): if self.region is not None: return self.region.bounding_box else: - return BoundingBox((np.array([-np.inf, -np.inf, -np.inf]), - np.array([np.inf, np.inf, np.inf]))) + return BoundingBox(np.array([-np.inf, -np.inf, -np.inf]), + np.array([np.inf, np.inf, np.inf])) @property def num_instances(self): diff --git a/openmc/geometry.py b/openmc/geometry.py index ad86cc56b..c3fd427e8 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -25,7 +25,7 @@ class Geometry: ---------- root_universe : openmc.UniverseBase Root universe which contains all others - bounding_box : 2-tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box of the universe. merge_surfaces : bool diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index c88d6da51..3394643ae 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -103,7 +103,7 @@ class Cell(_FortranObjectWithID): Name of the cell num_instances : int Number of unique cell instances - bounding_box : 2-tuple of numpy.ndarray + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of bounding box translation : Iterable of float 3-D coordinates of the translation vector @@ -291,7 +291,7 @@ class Cell(_FortranObjectWithID): llc[llc == -inf] = -np.inf urc[urc == -inf] = -np.inf - return BoundingBox((llc, urc)) + return BoundingBox(llc, urc) class _CellMapping(Mapping): diff --git a/openmc/region.py b/openmc/region.py index d8935a070..af9df1ba1 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -356,7 +356,7 @@ class Intersection(Region, MutableSequence): Attributes ---------- - bounding_box : tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box """ @@ -419,7 +419,7 @@ class Intersection(Region, MutableSequence): lower_left_n, upper_right_n = n.bounding_box lower_left[:] = np.maximum(lower_left, lower_left_n) upper_right[:] = np.minimum(upper_right, upper_right_n) - return BoundingBox((lower_left, upper_right)) + return BoundingBox(lower_left, upper_right) class Union(Region, MutableSequence): @@ -444,7 +444,7 @@ class Union(Region, MutableSequence): Attributes ---------- - bounding_box : 2-tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box """ @@ -507,7 +507,7 @@ class Union(Region, MutableSequence): lower_left_n, upper_right_n = n.bounding_box lower_left[:] = np.minimum(lower_left, lower_left_n) upper_right[:] = np.maximum(upper_right, upper_right_n) - return BoundingBox((lower_left, upper_right)) + return BoundingBox(lower_left, upper_right) class Complement(Region): @@ -534,7 +534,7 @@ class Complement(Region): ---------- node : openmc.Region Regions to take the complement of - bounding_box : tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box """ diff --git a/openmc/surface.py b/openmc/surface.py index 98ced13c6..2912cfd7e 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -234,10 +234,10 @@ class Surface(IDManagerMixin, ABC): desired half-space """ - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) def clone(self, memo=None): """Create a copy of this surface with a new unique ID. @@ -542,7 +542,7 @@ class PlaneMixin: else: ur = np.array([v if not np.isnan(v) else np.inf for v in vals]) - return BoundingBox((ll, ur)) + return BoundingBox(ll, ur) def evaluate(self, point): """Evaluate the surface equation at a given point. @@ -1199,10 +1199,10 @@ class Cylinder(QuadricMixin, Surface): return (np.array(ll), np.array(ur)) elif side == '+': - return BoundingBox(( + return BoundingBox( 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 @@ -1364,15 +1364,15 @@ class XCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]), np.array([np.inf, self.y0 + self.r, self.z0 + self.r]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) def evaluate(self, point): y = point[1] - self.y0 @@ -1459,15 +1459,15 @@ class YCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return BoundingBox(( + return BoundingBox( np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]), np.array([self.x0 + self.r, np.inf, self.z0 + self.r]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) def evaluate(self, point): x = point[0] - self.x0 @@ -1554,15 +1554,15 @@ class ZCylinder(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return BoundingBox(( + return BoundingBox( np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]), np.array([self.x0 + self.r, self.y0 + self.r, np.inf]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) def evaluate(self, point): x = point[0] - self.x0 @@ -1648,15 +1648,15 @@ class Sphere(QuadricMixin, Surface): def bounding_box(self, side): if side == '-': - return BoundingBox(( + return BoundingBox( np.array([self.x0 - self.r, self.y0 - self.r, self.z0 - self.r]), np.array([self.x0 + self.r, self.y0 + self.r, self.z0 + self.r]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) def evaluate(self, point): x = point[0] - self.x0 @@ -2272,15 +2272,15 @@ class XTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return BoundingBox(( + return BoundingBox( np.array([x0 - b, y0 - a - c, z0 - a - c]), np.array([x0 + b, y0 + a + c, z0 + a + c]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) class YTorus(TorusMixin, Surface): r"""A torus of the form :math:`(y - y_0)^2/B^2 + (\sqrt{(x - x_0)^2 + (z - @@ -2347,15 +2347,15 @@ class YTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return BoundingBox(( + return BoundingBox( np.array([x0 - a - c, y0 - b, z0 - a - c]), np.array([x0 + a + c, y0 + b, z0 + a + c]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) class ZTorus(TorusMixin, Surface): @@ -2423,15 +2423,15 @@ class ZTorus(TorusMixin, Surface): x0, y0, z0 = self.x0, self.y0, self.z0 a, b, c = self.a, self.b, self.c if side == '-': - return BoundingBox(( + return BoundingBox( np.array([x0 - a - c, y0 - a - c, z0 - b]), np.array([x0 + a + c, y0 + a + c, z0 + b]) - )) + ) elif side == '+': - return BoundingBox(( + return BoundingBox( np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf]) - )) + ) class Halfspace(Region): @@ -2466,7 +2466,7 @@ class Halfspace(Region): Surface which divides Euclidean space. side : {'+', '-'} Indicates whether the positive or negative half-space is used. - bounding_box : tuple of numpy.ndarray + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box """ diff --git a/openmc/universe.py b/openmc/universe.py index 2ed96f4bc..a8d0a0b3a 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -202,7 +202,7 @@ class Universe(UniverseBase): Volume of the universe in cm^3. This can either be set manually or calculated in a stochastic volume calculation and added via the :meth:`Universe.add_volume_information` method. - bounding_box : 2-tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box of the universe. @@ -662,7 +662,7 @@ class DAGMCUniverse(UniverseBase): auto_mat_ids : bool Set IDs automatically on initialization (True) or report overlaps in ID space between OpenMC and UWUW materials (False) - bounding_box : 2-tuple of numpy.array + bounding_box : openmc.BoundingBox Lower-left and upper-right coordinates of an axis-aligned bounding box of the universe. @@ -972,4 +972,4 @@ class DAGMCUniverse(UniverseBase): clone.volume = self.volume clone.auto_geom_ids = self.auto_geom_ids clone.auto_mat_ids = self.auto_mat_ids - return clone \ No newline at end of file + return clone diff --git a/tests/unit_tests/test_bounding_box.py b/tests/unit_tests/test_bounding_box.py index 419ada653..f053bb2a1 100644 --- a/tests/unit_tests/test_bounding_box.py +++ b/tests/unit_tests/test_bounding_box.py @@ -3,15 +3,9 @@ import openmc import pytest -test_bb_1 = openmc.BoundingBox( - (np.array([-10.0, -20.0, -30.0]), np.array([1.0, 2.0, 3.0])) -) -test_bb_2 = openmc.BoundingBox( - (np.array([1.0, 2.0, 3.0]), np.array([11.0, 22.0, 33.0])) -) -test_bb_3 = openmc.BoundingBox( - (np.array([-10.0, -20.0, -30.0]), np.array([-1.0, -2.0, -3.0])) -) +test_bb_1 = openmc.BoundingBox((-10.0, -20.0, -30.0), (1.0, 2.0, 3.0)) +test_bb_2 = openmc.BoundingBox((1.0, 2.0, 3.0), (11.0, 22.0, 33.0)) +test_bb_3 = openmc.BoundingBox((-10.0, -20.0, -30.0), (-1.0, -2.0, -3.0)) @pytest.mark.parametrize( @@ -63,29 +57,21 @@ def test_bounding_box_center(bb, expected): def test_bounding_box_input_checking(): - # checks that a list is not accepted instead of np.array + # checks that only passing lower_left is not accepted with pytest.raises(TypeError): - openmc.BoundingBox(([-10, -20, -30], [1, 2, 3])) - # checks that a list is not accepted instead of tuple - with pytest.raises(TypeError): - openmc.BoundingBox([np.array([-10, -20, -30]), np.array([1, 2, 3])]) - # checks that a tuple with one entry is not accepted - with pytest.raises(TypeError): - openmc.BoundingBox((np.array([-10, -20, -30]))) + openmc.BoundingBox((-10, -20, -3)) # checks that a tuple with three entry is not accepted - with pytest.raises(ValueError): - openmc.BoundingBox( - (np.array([-1, -2, -3]), np.array([-1, -2, -3]), np.array([-1, -2, -3])) - ) + with pytest.raises(TypeError): + openmc.BoundingBox((-1, -2, -3), (-1, -2, -3), (-1, -2, -3)) # checks that a numpy array with two entries is not accepted with pytest.raises(ValueError): - openmc.BoundingBox((np.array([-10, -30]), np.array([1, 2, 3]))) + openmc.BoundingBox(np.array([-10, -30]), np.array([1, 2, 3])) # checks that a numpy array with two entries is not accepted with pytest.raises(ValueError): - openmc.BoundingBox((np.array([-10, -20, -30]), np.array([1, 3]))) + openmc.BoundingBox(np.array([-10, -20, -30]), np.array([1, 3])) # checks that a numpy array with four entries is not accepted with pytest.raises(ValueError): - openmc.BoundingBox((np.array([-10, -20, -3, -4]), np.array([1, 2, 3]))) + openmc.BoundingBox(np.array([-10, -20, -3, -4]), np.array([1, 2, 3])) # checks that a numpy array with four entries is not accepted with pytest.raises(ValueError): - openmc.BoundingBox((np.array([-10, -20, -4]), np.array([1, 2, 3, 4]))) + openmc.BoundingBox(np.array([-10, -20, -4]), np.array([1, 2, 3, 4])) From 517c7436b0e61aca570d0f8eaf23d8c32f7a0171 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 25 Apr 2023 09:39:48 +0100 Subject: [PATCH 5/6] [skip ci] review improvment from @pshriwise Co-authored-by: Patrick Shriwise --- openmc/bounding_box.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/bounding_box.py b/openmc/bounding_box.py index a4a695b99..d2a499092 100644 --- a/openmc/bounding_box.py +++ b/openmc/bounding_box.py @@ -20,7 +20,7 @@ class BoundingBox(tuple): Attributes ---------- center : numpy.ndarray - x, y, z coordinates of the center of the bounding box in [cm]. + x, y, z coordinates of the center of the bounding box in [cm] lower_left : numpy.ndarray The x, y, z coordinates of the lower left corner of the bounding box in [cm] upper_right : numpy.ndarray From c06609cd8d9a5fb830102987e11cdb02de9855e5 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 25 Apr 2023 09:49:42 +0100 Subject: [PATCH 6/6] docstring lib.cell.py to match cell.py --- openmc/lib/cell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index 3394643ae..807fe73c7 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -104,7 +104,7 @@ class Cell(_FortranObjectWithID): num_instances : int Number of unique cell instances bounding_box : openmc.BoundingBox - Lower-left and upper-right coordinates of bounding box + Axis-aligned bounding box of the cell translation : Iterable of float 3-D coordinates of the translation vector rotation : Iterable of float