Skip to content

Commit f7a7341

Browse files
authored
Optionally compute bounding boxes in Mesh.material_volumes (#3731)
1 parent 008d584 commit f7a7341

7 files changed

Lines changed: 414 additions & 60 deletions

File tree

include/openmc/capi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ int openmc_mesh_set_id(int32_t index, int32_t id);
116116
int openmc_mesh_get_n_elements(int32_t index, size_t* n);
117117
int openmc_mesh_get_volumes(int32_t index, double* volumes);
118118
int openmc_mesh_material_volumes(int32_t index, int nx, int ny, int nz,
119-
int max_mats, int32_t* materials, double* volumes);
119+
int max_mats, int32_t* materials, double* volumes, double* bboxes);
120120
int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh);
121121
int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh);
122122
int openmc_new_filter(const char* type, int32_t* index);

include/openmc/mesh.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,24 @@ namespace detail {
8888

8989
class MaterialVolumes {
9090
public:
91+
MaterialVolumes(int32_t* mats, double* vols, double* bboxes, int table_size)
92+
: materials_(mats), volumes_(vols), bboxes_(bboxes), table_size_(table_size)
93+
{}
94+
9195
MaterialVolumes(int32_t* mats, double* vols, int table_size)
92-
: materials_(mats), volumes_(vols), table_size_(table_size)
96+
: MaterialVolumes(mats, vols, nullptr, table_size)
9397
{}
9498

9599
//! Add volume for a given material in a mesh element
96100
//
97101
//! \param[in] index_elem Index of the mesh element
98102
//! \param[in] index_material Index of the material within the model
99103
//! \param[in] volume Volume to add
100-
void add_volume(int index_elem, int index_material, double volume);
101-
void add_volume_unsafe(int index_elem, int index_material, double volume);
104+
//! \param[in] bbox Bounding box to union into the result (optional)
105+
void add_volume(int index_elem, int index_material, double volume,
106+
const BoundingBox* bbox = nullptr);
107+
void add_volume_unsafe(int index_elem, int index_material, double volume,
108+
const BoundingBox* bbox = nullptr);
102109

103110
// Accessors
104111
int32_t& materials(int i, int j) { return materials_[i * table_size_ + j]; }
@@ -113,11 +120,23 @@ class MaterialVolumes {
113120
return volumes_[i * table_size_ + j];
114121
}
115122

123+
double& bboxes(int i, int j, int k)
124+
{
125+
return bboxes_[(i * table_size_ + j) * 6 + k];
126+
}
127+
const double& bboxes(int i, int j, int k) const
128+
{
129+
return bboxes_[(i * table_size_ + j) * 6 + k];
130+
}
131+
132+
bool has_bboxes() const { return bboxes_ != nullptr; }
133+
116134
bool table_full() const { return table_full_; }
117135

118136
private:
119137
int32_t* materials_; //!< material index (bins, table_size)
120138
double* volumes_; //!< volume in [cm^3] (bins, table_size)
139+
double* bboxes_; //!< bounding boxes (bins, table_size, 6)
121140
int table_size_; //!< Size of hash table for each mesh element
122141
bool table_full_ {false}; //!< Whether the hash table is full
123142
};
@@ -240,6 +259,19 @@ class Mesh {
240259
void material_volumes(int nx, int ny, int nz, int max_materials,
241260
int32_t* materials, double* volumes) const;
242261

262+
//! Determine volume and bounding boxes of materials within each mesh element
263+
//
264+
//! \param[in] nx Number of samples in x direction
265+
//! \param[in] ny Number of samples in y direction
266+
//! \param[in] nz Number of samples in z direction
267+
//! \param[in] max_materials Maximum number of materials in a single mesh
268+
//! element
269+
//! \param[inout] materials Array storing material indices
270+
//! \param[inout] volumes Array storing volumes
271+
//! \param[inout] bboxes Array storing bounding boxes (n_elems, table_size, 6)
272+
void material_volumes(int nx, int ny, int nz, int max_materials,
273+
int32_t* materials, double* volumes, double* bboxes) const;
274+
243275
//! Determine bounding box of mesh
244276
//
245277
//! \return Bounding box of mesh

openmc/deplete/r2s.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def step1_neutron_transport(
269269
# Compute material volume fractions on the mesh
270270
if mat_vol_kwargs is None:
271271
mat_vol_kwargs = {}
272+
mat_vol_kwargs.setdefault('bounding_boxes', True)
272273
self.results['mesh_material_volumes'] = mmv = comm.bcast(
273274
self.domains.material_volumes(self.neutron_model, **mat_vol_kwargs))
274275

@@ -539,14 +540,15 @@ def step3_photon_transport(
539540
def get_decay_photon_source_mesh(
540541
self,
541542
time_index: int = -1
542-
) -> list[openmc.MeshSource]:
543+
) -> list[openmc.IndependentSource]:
543544
"""Create decay photon source for a mesh-based calculation.
544545
545-
This function creates N :class:`MeshSource` objects where N is the
546-
maximum number of unique materials that appears in a single mesh
547-
element. For each mesh element-material combination, and
548-
IndependentSource instance is created with a spatial constraint limited
549-
the sampled decay photons to the correct region.
546+
For each mesh element-material combination, an
547+
:class:`~openmc.IndependentSource` is created with a
548+
:class:`~openmc.stats.Box` spatial distribution based on the bounding
549+
box of the material within the mesh element. A material constraint is
550+
also applied so that sampled source sites are limited to the correct
551+
region.
550552
551553
When the photon transport model is different from the neutron model, the
552554
photon MeshMaterialVolumes is used to determine whether an (element,
@@ -559,19 +561,15 @@ def get_decay_photon_source_mesh(
559561
560562
Returns
561563
-------
562-
list of openmc.MeshSource
563-
A list of MeshSource objects, each containing IndependentSource
564-
instances for the decay photons in the corresponding mesh element.
564+
list of openmc.IndependentSource
565+
A list of IndependentSource objects for the decay photons, one for
566+
each mesh element-material combination with non-zero source strength.
565567
566568
"""
567569
mat_dict = self.neutron_model._get_all_materials()
568570

569-
# Some MeshSource objects will have empty positions; create a "null source"
570-
# that is used for this case
571-
null_source = openmc.IndependentSource(particle='photon', strength=0.0)
572-
573-
# List to hold sources for each MeshSource (length = N)
574-
source_lists = []
571+
# List to hold all sources
572+
sources = []
575573

576574
# Index in the overall list of activated materials
577575
index_mat = 0
@@ -594,7 +592,7 @@ def get_decay_photon_source_mesh(
594592
if mat_id is not None
595593
}
596594

597-
for j, (mat_id, _) in enumerate(mat_vols.by_element(index_elem)):
595+
for mat_id, _, bbox in mat_vols.by_element(index_elem, include_bboxes=True):
598596
# Skip void volume
599597
if mat_id is None:
600598
continue
@@ -604,30 +602,27 @@ def get_decay_photon_source_mesh(
604602
index_mat += 1
605603
continue
606604

607-
# Check whether a new MeshSource object is needed
608-
if j >= len(source_lists):
609-
source_lists.append([null_source]*n_elements)
610-
611605
# Get activated material composition
612606
original_mat = materials[index_mat]
613607
activated_mat = results[time_index].get_material(str(original_mat.id))
614608

615-
# Create decay photon source source
609+
# Create decay photon source
616610
energy = activated_mat.get_decay_photon_energy()
617611
if energy is not None:
618612
strength = energy.integral()
619-
source_lists[j][index_elem] = openmc.IndependentSource(
613+
space = openmc.stats.Box(*bbox)
614+
sources.append(openmc.IndependentSource(
615+
space=space,
620616
energy=energy,
621617
particle='photon',
622618
strength=strength,
623619
constraints={'domains': [mat_dict[mat_id]]}
624-
)
620+
))
625621

626622
# Increment index of activated material
627623
index_mat += 1
628624

629-
# Return list of mesh sources
630-
return [openmc.MeshSource(self.domains, sources) for sources in source_lists]
625+
return sources
631626

632627
def load_results(self, path: PathLike):
633628
"""Load results from a previous R2S calculation.

openmc/lib/mesh.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections.abc import Mapping, Sequence
2-
from ctypes import (c_int, c_int32, c_char_p, c_double, POINTER,
2+
from ctypes import (c_int, c_int32, c_char_p, c_double, POINTER, c_void_p,
33
create_string_buffer, c_size_t)
44
from math import sqrt
55
import sys
@@ -47,7 +47,8 @@
4747
_dll.openmc_mesh_bounding_box.restype = c_int
4848
_dll.openmc_mesh_bounding_box.errcheck = _error_handler
4949
_dll.openmc_mesh_material_volumes.argtypes = [
50-
c_int32, c_int, c_int, c_int, c_int, arr_2d_int32, arr_2d_double]
50+
c_int32, c_int, c_int, c_int, c_int, arr_2d_int32, arr_2d_double,
51+
c_void_p]
5152
_dll.openmc_mesh_material_volumes.restype = c_int
5253
_dll.openmc_mesh_material_volumes.errcheck = _error_handler
5354
_dll.openmc_mesh_get_plot_bins.argtypes = [
@@ -188,6 +189,7 @@ def material_volumes(
188189
n_samples: int | tuple[int, int, int] = 10_000,
189190
max_materials: int = 4,
190191
output: bool = True,
192+
bounding_boxes: bool = False,
191193
) -> MeshMaterialVolumes:
192194
"""Determine volume of materials in each mesh element.
193195
@@ -213,6 +215,11 @@ def material_volumes(
213215
Estimated maximum number of materials in any given mesh element.
214216
output : bool, optional
215217
Whether or not to show output.
218+
bounding_boxes : bool, optional
219+
Whether or not to compute an axis-aligned bounding box for each
220+
(mesh element, material) combination. When enabled, the bounding
221+
box encloses the ray-estimator prisms used for the volume
222+
estimation.
216223
217224
Returns
218225
-------
@@ -243,23 +250,36 @@ def material_volumes(
243250
table_size = slot_factor*max_materials
244251
materials = np.full((n, table_size), EMPTY_SLOT, dtype=np.int32)
245252
volumes = np.zeros((n, table_size), dtype=np.float64)
253+
bboxes = None
254+
if bounding_boxes:
255+
bboxes = np.empty((n, table_size, 6), dtype=np.float64)
256+
bboxes[..., 0:3] = np.inf
257+
bboxes[..., 3:6] = -np.inf
246258

247259
# Run material volume calculation
248260
while True:
249261
try:
262+
bboxes_ptr = None
263+
if bboxes is not None:
264+
bboxes_ptr = bboxes.ctypes.data_as(POINTER(c_double))
250265
with quiet_dll(output):
251266
_dll.openmc_mesh_material_volumes(
252-
self._index, nx, ny, nz, table_size, materials, volumes)
267+
self._index, nx, ny, nz, table_size, materials,
268+
volumes, bboxes_ptr)
253269
except AllocationError:
254270
# Increase size of result array and try again
255271
table_size *= 2
256272
materials = np.full((n, table_size), EMPTY_SLOT, dtype=np.int32)
257273
volumes = np.zeros((n, table_size), dtype=np.float64)
274+
if bounding_boxes:
275+
bboxes = np.empty((n, table_size, 6), dtype=np.float64)
276+
bboxes[..., 0:3] = np.inf
277+
bboxes[..., 3:6] = -np.inf
258278
else:
259279
# If no error, break out of loop
260280
break
261281

262-
return MeshMaterialVolumes(materials, volumes)
282+
return MeshMaterialVolumes(materials, volumes, bboxes)
263283

264284
def get_plot_bins(
265285
self,

0 commit comments

Comments
 (0)