diff --git a/openmc/mesh.py b/openmc/mesh.py index 946b90fbf6..3f0802d6be 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -1188,7 +1188,7 @@ class RegularMesh(StructuredMesh): @classmethod def from_domain( cls, - domain: HasBoundingBox, + domain: HasBoundingBox | BoundingBox, dimension: Sequence[int] | int = 1000, mesh_id: int | None = None, name: str = '' @@ -1197,10 +1197,12 @@ class RegularMesh(StructuredMesh): Parameters ---------- - domain : HasBoundingBox + domain : HasBoundingBox | openmc.BoundingBox The object passed in will be used as a template for this mesh. The bounding box of the property of the object passed will be used to - set the lower_left and upper_right and of the mesh instance + set the lower_left and upper_right and of the mesh instance. + Alternatively, a :class:`openmc.BoundingBox` can be passed + directly. dimension : Iterable of int | int The number of mesh cells in total or number of mesh cells in each direction (x, y, z). If a single integer is provided, the domain @@ -1217,21 +1219,26 @@ class RegularMesh(StructuredMesh): RegularMesh instance """ - if not hasattr(domain, 'bounding_box'): - raise TypeError("Domain must have a bounding_box property") + if isinstance(domain, BoundingBox): + bb = domain + elif hasattr(domain, 'bounding_box'): + bb = domain.bounding_box + else: + raise TypeError("Domain must be a BoundingBox or have a " + "bounding_box property") mesh = cls(mesh_id=mesh_id, name=name) - mesh.lower_left = domain.bounding_box[0] - mesh.upper_right = domain.bounding_box[1] + mesh.lower_left = bb[0] + mesh.upper_right = bb[1] if isinstance(dimension, int): cv.check_greater_than("dimension", dimension, 1, equality=True) # If a single integer is provided, divide the domain into that many # mesh cells with roughly equal lengths in each direction - ideal_cube_volume = domain.bounding_box.volume / dimension + ideal_cube_volume = bb.volume / dimension ideal_cube_size = ideal_cube_volume ** (1 / 3) dimension = [ max(1, int(round(side / ideal_cube_size))) - for side in domain.bounding_box.width + for side in bb.width ] mesh.dimension = dimension @@ -1904,7 +1911,7 @@ class CylindricalMesh(StructuredMesh): @classmethod def from_domain( cls, - domain: HasBoundingBox, + domain: HasBoundingBox | BoundingBox, dimension: Sequence[int] = (10, 10, 10), mesh_id: int | None = None, phi_grid_bounds: Sequence[float] = (0.0, 2*pi), @@ -1915,10 +1922,11 @@ class CylindricalMesh(StructuredMesh): Parameters ---------- - domain : HasBoundingBox + domain : HasBoundingBox | openmc.BoundingBox The object passed in will be used as a template for this mesh. The bounding box of the property of the object passed will be used to - set the r_grid, z_grid ranges. + set the r_grid, z_grid ranges. Alternatively, a + :class:`openmc.BoundingBox` can be passed directly. dimension : Iterable of int The number of equally spaced mesh cells in each direction (r_grid, phi_grid, z_grid) @@ -1939,11 +1947,13 @@ class CylindricalMesh(StructuredMesh): CylindricalMesh instance """ - if not hasattr(domain, 'bounding_box'): - raise TypeError("Domain must have a bounding_box property") - - # loaded once to avoid recalculating bounding box - cached_bb = domain.bounding_box + if isinstance(domain, BoundingBox): + cached_bb = domain + elif hasattr(domain, 'bounding_box'): + cached_bb = domain.bounding_box + else: + raise TypeError("Domain must be a BoundingBox or have a " + "bounding_box property") if enclose_domain: outer_radius = 0.5 * np.linalg.norm(cached_bb.width[:2]) @@ -2290,7 +2300,7 @@ class SphericalMesh(StructuredMesh): @classmethod def from_domain( cls, - domain: HasBoundingBox, + domain: HasBoundingBox | BoundingBox, dimension: Sequence[int] = (10, 10, 10), mesh_id: int | None = None, phi_grid_bounds: Sequence[float] = (0.0, 2*pi), @@ -2302,10 +2312,11 @@ class SphericalMesh(StructuredMesh): Parameters ---------- - domain : HasBoundingBox + domain : HasBoundingBox | openmc.BoundingBox The object passed in will be used as a template for this mesh. The bounding box of the property of the object passed will be used to - set the r_grid, phi_grid, and theta_grid ranges. + set the r_grid, phi_grid, and theta_grid ranges. Alternatively, a + :class:`openmc.BoundingBox` can be passed directly. dimension : Iterable of int The number of equally spaced mesh cells in each direction (r_grid, phi_grid, theta_grid). Spacing is in angular space (radians) for @@ -2330,11 +2341,13 @@ class SphericalMesh(StructuredMesh): SphericalMesh instance """ - if not hasattr(domain, 'bounding_box'): - raise TypeError("Domain must have a bounding_box property") - - # loaded once to avoid recalculating bounding box - cached_bb = domain.bounding_box + if isinstance(domain, BoundingBox): + cached_bb = domain + elif hasattr(domain, 'bounding_box'): + cached_bb = domain.bounding_box + else: + raise TypeError("Domain must be a BoundingBox or have a " + "bounding_box property") if enclose_domain: outer_radius = 0.5 * np.linalg.norm(cached_bb.width) diff --git a/tests/unit_tests/test_mesh_from_domain.py b/tests/unit_tests/test_mesh_from_domain.py index 5b1173126f..2b02921b5e 100644 --- a/tests/unit_tests/test_mesh_from_domain.py +++ b/tests/unit_tests/test_mesh_from_domain.py @@ -16,6 +16,17 @@ def test_reg_mesh_from_cell(): assert np.array_equal(mesh.upper_right, cell.bounding_box[1]) +def test_reg_mesh_from_bounding_box(): + """Tests a RegularMesh can be made from a BoundingBox directly.""" + bb = openmc.BoundingBox([-8, -7, -5], [12, 13, 15]) + + mesh = openmc.RegularMesh.from_domain(domain=bb, dimension=[7, 11, 13]) + assert isinstance(mesh, openmc.RegularMesh) + assert np.array_equal(mesh.dimension, (7, 11, 13)) + assert np.array_equal(mesh.lower_left, bb[0]) + assert np.array_equal(mesh.upper_right, bb[1]) + + def test_cylindrical_mesh_from_cell(): """Tests a CylindricalMesh can be made from a Cell and the specified dimensions are propagated through."""