Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions include/openmc/capi.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ int openmc_get_material_index(int32_t id, int32_t* index);
int openmc_get_mesh_index(int32_t id, int32_t* index);
int openmc_get_n_batches(int* n_batches, bool get_max_batches);
int openmc_get_nuclide_index(const char name[], int* index);
int openmc_add_unstructured_mesh(
const char filename[], const char library[], int* id);
int openmc_add_unstructured_mesh(const char filename[], const char library[],
double length_multiplier, const char options[], int32_t id, int32_t* index);
int64_t openmc_get_seed();
uint64_t openmc_get_stride();
int openmc_get_tally_index(int32_t id, int32_t* index);
Expand Down Expand Up @@ -140,12 +140,18 @@ int openmc_mesh_filter_get_translation(int32_t index, double translation[3]);
int openmc_mesh_filter_set_translation(int32_t index, double translation[3]);
int openmc_mesh_get_id(int32_t index, int32_t* id);
int openmc_mesh_set_id(int32_t index, int32_t id);
int openmc_mesh_get_name(int32_t index, const char** name);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some tests for these new functions in test_lib.py

int openmc_mesh_set_name(int32_t index, const char* name);
int openmc_mesh_get_n_elements(int32_t index, size_t* n);
int openmc_mesh_get_volumes(int32_t index, double* volumes);
int openmc_mesh_material_volumes(int32_t index, int nx, int ny, int nz,
int max_mats, int32_t* materials, double* volumes, double* bboxes);
int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh);
int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh);
int openmc_cylindrical_mesh_get_origin(int32_t index, double origin[3]);
int openmc_cylindrical_mesh_set_origin(int32_t index, const double origin[3]);
int openmc_spherical_mesh_get_origin(int32_t index, double origin[3]);
int openmc_spherical_mesh_set_origin(int32_t index, const double origin[3]);
int openmc_new_filter(const char* type, int32_t* index);
int openmc_next_batch(int* status);
int openmc_nuclide_name(int index, const char** name);
Expand Down
18 changes: 16 additions & 2 deletions include/openmc/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ class Mesh {

const std::string& name() const { return name_; }

void set_name(const std::string& name) { name_ = name; }

//! Set the mesh ID
void set_id(int32_t id = -1);

Expand Down Expand Up @@ -477,6 +479,16 @@ class PeriodicStructuredMesh : public StructuredMesh {
return r - origin_;
};

const Position& origin() const { return origin_; }

virtual int set_grid() = 0;

int set_origin(Position origin)
{
origin_ = origin;
return set_grid();
}

// Data members
Position origin_ {0.0, 0.0, 0.0}; //!< Origin of the mesh
};
Expand Down Expand Up @@ -834,7 +846,8 @@ class MOABMesh : public UnstructuredMesh {
MOABMesh() = default;
MOABMesh(pugi::xml_node);
MOABMesh(hid_t group);
MOABMesh(const std::string& filename, double length_multiplier = 1.0);
MOABMesh(const std::string& filename, double length_multiplier = 1.0,
const std::string& options = {});
MOABMesh(std::shared_ptr<moab::Interface> external_mbi);

static const std::string mesh_lib_type;
Expand Down Expand Up @@ -1004,7 +1017,8 @@ class LibMesh : public UnstructuredMesh {
// Constructors
LibMesh(pugi::xml_node node);
LibMesh(hid_t group);
LibMesh(const std::string& filename, double length_multiplier = 1.0);
LibMesh(const std::string& filename, double length_multiplier = 1.0,
const std::string& options = {});
LibMesh(libMesh::MeshBase& input_mesh, double length_multiplier = 1.0);

static const std::string mesh_lib_type;
Expand Down
3 changes: 3 additions & 0 deletions include/openmc/weight_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ class WeightWindows {
//! Ready the weight window class for use
void set_defaults();

//! Replace the energy grid with defaults for the selected particle type
void reset_energy_bounds();

//! Ensure the weight window lower bounds are properly allocated
void allocate_ww_bounds();

Expand Down
12 changes: 12 additions & 0 deletions openmc/checkvalue.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import copy
import os
from collections.abc import Iterable
from numbers import Real

import numpy as np

Expand Down Expand Up @@ -80,7 +81,18 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
max_depth : int
The maximum number of layers of nested iterables there should be before
reaching the ultimately contained items

Notes
-----
For NumPy floating-point arrays with an allowed number of dimensions, the
dtype guarantees the element type and the per-element scan is skipped when
*expected_type* is :class:`numbers.Real` or :class:`float`.
"""
if (isinstance(value, np.ndarray) and value.dtype.kind == 'f'
and min_depth <= value.ndim <= max_depth
and expected_type in (Real, float)):
return

# Initialize the tree at the very first item.
tree = [value]
index = [0]
Expand Down
95 changes: 94 additions & 1 deletion openmc/lib/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
_dll.openmc_mesh_set_id.argtypes = [c_int32, c_int32]
_dll.openmc_mesh_set_id.restype = c_int
_dll.openmc_mesh_set_id.errcheck = _error_handler
_dll.openmc_mesh_get_name.argtypes = [c_int32, POINTER(c_char_p)]
_dll.openmc_mesh_get_name.restype = c_int
_dll.openmc_mesh_get_name.errcheck = _error_handler
_dll.openmc_mesh_set_name.argtypes = [c_int32, c_char_p]
_dll.openmc_mesh_set_name.restype = c_int
_dll.openmc_mesh_set_name.errcheck = _error_handler
_dll.openmc_mesh_get_n_elements.argtypes = [c_int32, POINTER(c_size_t)]
_dll.openmc_mesh_get_n_elements.restype = c_int
_dll.openmc_mesh_get_n_elements.errcheck = _error_handler
Expand Down Expand Up @@ -97,6 +103,12 @@
c_int, POINTER(c_double), c_int, POINTER(c_double), c_int]
_dll.openmc_cylindrical_mesh_set_grid.restype = c_int
_dll.openmc_cylindrical_mesh_set_grid.errcheck = _error_handler
_dll.openmc_cylindrical_mesh_get_origin.argtypes = [c_int32, POINTER(c_double)]
_dll.openmc_cylindrical_mesh_get_origin.restype = c_int
_dll.openmc_cylindrical_mesh_get_origin.errcheck = _error_handler
_dll.openmc_cylindrical_mesh_set_origin.argtypes = [c_int32, POINTER(c_double)]
_dll.openmc_cylindrical_mesh_set_origin.restype = c_int
_dll.openmc_cylindrical_mesh_set_origin.errcheck = _error_handler

_dll.openmc_spherical_mesh_get_grid.argtypes = [c_int32,
POINTER(POINTER(c_double)), POINTER(c_int), POINTER(POINTER(c_double)),
Expand All @@ -107,6 +119,17 @@
c_int, POINTER(c_double), c_int, POINTER(c_double), c_int]
_dll.openmc_spherical_mesh_set_grid.restype = c_int
_dll.openmc_spherical_mesh_set_grid.errcheck = _error_handler
_dll.openmc_spherical_mesh_get_origin.argtypes = [c_int32, POINTER(c_double)]
_dll.openmc_spherical_mesh_get_origin.restype = c_int
_dll.openmc_spherical_mesh_get_origin.errcheck = _error_handler
_dll.openmc_spherical_mesh_set_origin.argtypes = [c_int32, POINTER(c_double)]
_dll.openmc_spherical_mesh_set_origin.restype = c_int
_dll.openmc_spherical_mesh_set_origin.errcheck = _error_handler

_dll.openmc_add_unstructured_mesh.argtypes = [
c_char_p, c_char_p, c_double, c_char_p, c_int32, POINTER(c_int32)]
_dll.openmc_add_unstructured_mesh.restype = c_int
_dll.openmc_add_unstructured_mesh.errcheck = _error_handler


class Mesh(_FortranObjectWithID):
Expand Down Expand Up @@ -155,6 +178,16 @@ def id(self):
def id(self, mesh_id):
_dll.openmc_mesh_set_id(self._index, mesh_id)

@property
def name(self):
name = c_char_p()
_dll.openmc_mesh_get_name(self._index, name)
return name.value.decode()

@name.setter
def name(self, name):
_dll.openmc_mesh_set_name(self._index, name.encode())

@property
def n_elements(self) -> int:
n = c_size_t()
Expand Down Expand Up @@ -621,6 +654,21 @@ def set_grid(self, r_grid, phi_grid, z_grid):
_dll.openmc_cylindrical_mesh_set_grid(self._index, r_grid, nr, phi_grid,
nphi, z_grid, nz)

@property
def origin(self):
origin = np.empty(3)
_dll.openmc_cylindrical_mesh_get_origin(
self._index, origin.ctypes.data_as(POINTER(c_double)))
return origin

@origin.setter
def origin(self, origin):
origin = np.ascontiguousarray(origin, dtype=np.float64)
if origin.shape != (3,):
raise ValueError('Mesh origin must have three coordinates')
_dll.openmc_cylindrical_mesh_set_origin(
self._index, origin.ctypes.data_as(POINTER(c_double)))


class SphericalMesh(Mesh):
"""SphericalMesh stored internally.
Expand Down Expand Up @@ -726,9 +774,54 @@ def set_grid(self, r_grid, theta_grid, phi_grid):
_dll.openmc_spherical_mesh_set_grid(self._index, r_grid, nr, theta_grid,
ntheta, phi_grid, nphi)

@property
def origin(self):
origin = np.empty(3)
_dll.openmc_spherical_mesh_get_origin(
self._index, origin.ctypes.data_as(POINTER(c_double)))
return origin

@origin.setter
def origin(self, origin):
origin = np.ascontiguousarray(origin, dtype=np.float64)
if origin.shape != (3,):
raise ValueError('Mesh origin must have three coordinates')
_dll.openmc_spherical_mesh_set_origin(
self._index, origin.ctypes.data_as(POINTER(c_double)))


class UnstructuredMesh(Mesh):
pass
@classmethod
def from_file(cls, filename, library, uid=None, length_multiplier=1.0,
options=None):
"""Create an unstructured mesh from a file.

Parameters
----------
filename : path-like
Path to the unstructured mesh file.
library : {'libmesh', 'moab'}
Library used to load the mesh.
uid : int, optional
Unique ID for the mesh. If omitted, an ID is assigned.
length_multiplier : float, optional
Multiplicative factor applied to mesh coordinates.
options : str, optional
Options used to construct spatial search data structures.

Returns
-------
openmc.lib.UnstructuredMesh
The newly allocated mesh.

"""
index = c_int32()
mesh_id = -1 if uid is None else uid
options = None if options is None else options.encode()
_dll.openmc_add_unstructured_mesh(
str(filename).encode(), library.encode(), length_multiplier,
options, mesh_id, index)
return cls(index=index.value)


_MESH_TYPE_MAP = {
Expand Down
15 changes: 10 additions & 5 deletions openmc/lib/weight_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,15 @@ def energy_bounds(self):

@energy_bounds.setter
def energy_bounds(self, e_bounds):
e_bounds_arr = np.asarray(e_bounds, dtype=float)
e_bounds_ptr = e_bounds_arr.ctypes.data_as(POINTER(c_double))
if e_bounds is None:
e_bounds_ptr = None
size = 0
else:
e_bounds_arr = np.ascontiguousarray(e_bounds, dtype=np.float64)
e_bounds_ptr = e_bounds_arr.ctypes.data_as(POINTER(c_double))
size = e_bounds_arr.size
_dll.openmc_weight_windows_set_energy_bounds(
self._index, e_bounds_ptr, e_bounds_arr.size)
self._index, e_bounds_ptr, size)

@property
def particle(self):
Expand All @@ -222,8 +227,8 @@ def bounds(self):

@bounds.setter
def bounds(self, bounds):
lower = np.asarray(bounds[0])
upper = np.asarray(bounds[1])
lower = np.ascontiguousarray(bounds[0], dtype=np.float64)
upper = np.ascontiguousarray(bounds[1], dtype=np.float64)

lower_p = lower.ctypes.data_as(POINTER(c_double))
upper_p = upper.ctypes.data_as(POINTER(c_double))
Expand Down
69 changes: 69 additions & 0 deletions openmc/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,75 @@ def axis_labels(self):
"""tuple of str : Names of the mesh axes, one per dimension."""
pass

def to_lib_object(self, uid: int | None = None,
base_dir: PathLike | None = None):
"""Create a corresponding :mod:`openmc.lib` mesh.

The OpenMC shared library must be initialized before calling this
method. The returned object is tied to the active library session and
becomes invalid when that session is finalized.

Parameters
----------
uid : int, optional
ID to assign to the library mesh. If omitted, the ID of this mesh
is used.
base_dir : path-like, optional
Directory used to resolve relative filenames for unstructured
meshes. If omitted, the current working directory is used.

Returns
-------
openmc.lib.Mesh
The corresponding library mesh. The concrete type depends on the
type of this mesh.

Raises
------
RuntimeError
If the OpenMC shared library has not been initialized.

"""
import openmc.lib

if not openmc.lib.is_initialized:
raise RuntimeError(
'The OpenMC shared library must be initialized before '
'creating a library mesh.')

if uid is None:
uid = self.id
base_dir = Path.cwd() if base_dir is None else Path(base_dir)

if isinstance(self, RegularMesh):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, each mesh subclass should implement how to convert itself to its lib counterpart.
That way we don't have to edit this function when implementing a new mesh type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to agree.

In the vein of my other comment, this also feels appropriate for a classmethod approach on the openmc.lib.Mesh object where each subclass handles its own property settings for the resulting openmc.lib object.

e.g.

lib_mesh = openmc.lib.Mesh.from_python_object(spherical_mesh).

This makes it more natural (to me) to ensure that the openmc.lib module is initialized before performing these operations.

lib_mesh = openmc.lib.RegularMesh(uid=uid)
lib_mesh.dimension = self.dimension
lib_mesh.set_parameters(
lower_left=self.lower_left, upper_right=self.upper_right)
elif isinstance(self, RectilinearMesh):
lib_mesh = openmc.lib.RectilinearMesh(uid=uid)
lib_mesh.set_grid(self.x_grid, self.y_grid, self.z_grid)
elif isinstance(self, CylindricalMesh):
lib_mesh = openmc.lib.CylindricalMesh(uid=uid)
lib_mesh.set_grid(self.r_grid, self.phi_grid, self.z_grid)
lib_mesh.origin = self.origin
elif isinstance(self, SphericalMesh):
lib_mesh = openmc.lib.SphericalMesh(uid=uid)
lib_mesh.set_grid(self.r_grid, self.theta_grid, self.phi_grid)
lib_mesh.origin = self.origin
elif isinstance(self, UnstructuredMesh):
filename = Path(self.filename)
if not filename.is_absolute():
filename = base_dir / filename
lib_mesh = openmc.lib.UnstructuredMesh.from_file(
filename.resolve(), self.library, uid=uid,
length_multiplier=self.length_multiplier, options=self.options)
else:
raise TypeError(f'Unsupported mesh type: {type(self)}')

lib_mesh.name = self.name
return lib_mesh

def __repr__(self):
string = type(self).__name__ + '\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
Expand Down
Loading
Loading