diff --git a/openmc/universe.py b/openmc/universe.py index b65263ef9c..78f75bbfc1 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -12,9 +12,12 @@ import numpy as np import openmc import openmc.checkvalue as cv + from ._xml import get_text +from .checkvalue import check_type, check_value from .mixin import IDManagerMixin from .plots import _SVG_COLORS +from .surface import _BOUNDARY_TYPES class UniverseBase(ABC, IDManagerMixin): @@ -713,6 +716,78 @@ class DAGMCUniverse(UniverseBase): dagmc_element.set('filename', self.filename) xml_element.append(dagmc_element) + def bounding_region(self, bounded_type='box', boundary_type='vacuum'): + """Creates a either a spherical or box shaped bounding region around + the DAGMC geometry. + Parameters + ---------- + bounded_type : str + The type of bounding surface(s) to use when constructing the region. + Options include a single spherical surface (sphere) or a rectangle + made from six planes (box). + boundary_type : str + Boundary condition that defines the behavior for particles hitting + the surface. Defaults to vacuum boundary condition. Passed into the + surface construction. + Returns + ------- + openmc.Region + Region instance + """ + + check_type('boundary type', boundary_type, str) + check_value('boundary type', boundary_type, _BOUNDARY_TYPES) + check_type('bounded type', bounded_type, str) + check_value('bounded type', bounded_type, ('box', 'sphere')) + + bounding_box = self.bounding_box + + if bounded_type == 'sphere': + import math + bounding_box_center = (bounding_box[0] + bounding_box[1])/2 + radius = math.dist(bounding_box[0], bounding_box[1]) + bounding_surface = openmc.Sphere( + x0=bounding_box_center[0], + y0=bounding_box_center[1], + z0=bounding_box_center[2], + boundary_type=boundary_type, + r=radius, + ) + + return -bounding_surface + + if bounded_type == 'box': + # defines plane surfaces for all six faces of the bounding box + lower_x = openmc.XPlane(bounding_box[0][0], boundary_type=boundary_type) + upper_x = openmc.XPlane(bounding_box[1][0], boundary_type=boundary_type) + lower_y = openmc.YPlane(bounding_box[0][1], boundary_type=boundary_type) + upper_y = openmc.YPlane(bounding_box[1][1], boundary_type=boundary_type) + lower_z = openmc.ZPlane(bounding_box[0][2], boundary_type=boundary_type) + upper_z = openmc.ZPlane(bounding_box[1][2], boundary_type=boundary_type) + + return +lower_x & -upper_x & +lower_y & -upper_y & +lower_z & -upper_z + + def bounded_universe(self, bounding_cell_id=10000, **kwargs): + """Returns an openmc.Universe filled with this DAGMCUniverse and bounded + with a cell. Defaults to a box cell with a vacuum surface however this + can be changed using the kwargs which are passed directly to + DAGMCUniverse.bounding_region(). + + Parameters + ---------- + bounding_cell_id : int + The cell ID number to use for the bounding cell, defaults to 1000 to reduce + the chance of overlapping ID numbers with the DAGMC geometry. + + Returns + ------- + openmc.Universe + Universe instance + """ + + bounding_cell = openmc.Cell(fill=self, cell_id =bounding_cell_id, region=self.bounding_region(**kwargs)) + return openmc.Universe(cells=[bounding_cell]) + @classmethod def from_hdf5(cls, group): """Create DAGMC universe from HDF5 group diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 8ce5aabdad..dc3bc2b2ce 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -183,7 +183,9 @@ void DAGUniverse::init_geometry() } else { warning(fmt::format("DAGMC Cell IDs: {}", dagmc_ids_for_dim(3))); fatal_error(fmt::format("Cell ID {} exists in both DAGMC Universe {} " - "and the CSG geometry.", + "and the CSG geometry. Setting auto_geom_ids " + "to True when initiating the DAGMC Universe may " + "resolve this issue", c->id_, this->id_)); } diff --git a/tests/regression_tests/dagmc/universes/inputs_true.dat b/tests/regression_tests/dagmc/universes/inputs_true.dat index 4443f9a35d..2165a78b6c 100644 --- a/tests/regression_tests/dagmc/universes/inputs_true.dat +++ b/tests/regression_tests/dagmc/universes/inputs_true.dat @@ -1,8 +1,8 @@ - + - + 24.0 24.0 2 2 -24.0 -24.0 @@ -10,12 +10,12 @@ 9 9 9 9 - - - - - - + + + + + + diff --git a/tests/regression_tests/dagmc/universes/test.py b/tests/regression_tests/dagmc/universes/test.py index 6726b4b448..f2eb3882f2 100644 --- a/tests/regression_tests/dagmc/universes/test.py +++ b/tests/regression_tests/dagmc/universes/test.py @@ -46,11 +46,32 @@ class DAGMCUniverseTest(PyAPITestHarness): # create the DAGMC universe pincell_univ = openmc.DAGMCUniverse(filename='dagmc.h5m', auto_geom_ids=True) + # creates another DAGMC universe, this time with within a bounded cell + bound_pincell_universe = openmc.DAGMCUniverse(filename='dagmc.h5m').bounded_universe() + # uses the bound_dag_cell as the root argument to test the type checks in openmc.Geometry + bound_pincell_geometry = openmc.Geometry(root=bound_pincell_universe) + # assigns the bound_dag_geometry to the model to test the type checks in model.Geometry setter + model.Geometry = bound_pincell_geometry + # checks that the bounding box is calculated correctly bounding_box = pincell_univ.bounding_box assert bounding_box[0].tolist() == [-25., -25., -25.] assert bounding_box[1].tolist() == [25., 25., 25.] + # checks that the bounding region is six surfaces each with a vacuum boundary type + b_region = pincell_univ.bounding_region(bounded_type='box', boundary_type='vacuum') + assert isinstance(b_region, openmc.Region) + assert len(b_region.get_surfaces()) == 6 + for surface in list(b_region.get_surfaces().values()): + assert surface.boundary_type == 'vacuum' + + # checks that the bounding region is a single surface with a reflective boundary type + b_region = pincell_univ.bounding_region(bounded_type='sphere', boundary_type='reflective') + assert isinstance(b_region, openmc.Region) + assert len(b_region.get_surfaces()) == 1 + for surface in list(b_region.get_surfaces().values()): + assert surface.boundary_type == 'reflective' + # create a 2 x 2 lattice using the DAGMC pincell pitch = np.asarray((24.0, 24.0)) lattice = openmc.RectLattice()