diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 0aa058cdb..d2cb3b125 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -93,29 +93,13 @@ or ``multi-group``. *Default*: continuous-energy ---------------------- -```` Element ---------------------- +-------------------------- +```` Element +-------------------------- -The ```` element describes a mesh that is used for calculating Shannon -entropy. This mesh should cover all possible fissionable materials in the -problem. It has the following attributes/sub-elements: - - :dimension: - The number of mesh cells in the x, y, and z directions, respectively. - - *Default*: If this tag is not present, the number of mesh cells is - automatically determined by the code. - - :lower_left: - The Cartesian coordinates of the lower-left corner of the mesh. - - *Default*: None - - :upper_right: - The Cartesian coordinates of the upper-right corner of the mesh. - - *Default*: None +The ```` element indicates the ID of a mesh that is to be used for +calculating Shannon entropy. The mesh should cover all possible fissionable +materials in the problem and is specified using a :ref:`mesh_element`. ----------------------------------- ```` Element @@ -199,6 +183,36 @@ then, OpenMC will only use up to the :math:`P_1` data. .. note:: This element is not used in the continuous-energy :ref:`energy_mode`. +.. _mesh_element: + +------------------ +```` Element +------------------ + +The ```` element describes a mesh that is used either for calculating +Shannon entropy, applying the uniform fission site method, or in tallies. For +Shannon entropy meshes, the mesh should cover all possible fissionable materials +in the problem. It has the following attributes/sub-elements: + + :id: + A unique integer that is used to identify the mesh. + + :dimension: + The number of mesh cells in the x, y, and z directions, respectively. + + *Default*: If this tag is not present, the number of mesh cells is + automatically determined by the code. + + :lower_left: + The Cartesian coordinates of the lower-left corner of the mesh. + + *Default*: None + + :upper_right: + The Cartesian coordinates of the upper-right corner of the mesh. + + *Default*: None + ----------------------- ```` Element ----------------------- @@ -765,30 +779,15 @@ has the following attributes/sub-elements: ------------------------ -```` Element +```` Element ------------------------ -The ```` element describes a mesh that is used for re-weighting -source sites at every generation based on the uniform fission site methodology -described in Kelly et al., "MC21 Analysis of the Nuclear Energy Agency Monte -Carlo Performance Benchmark Problem," Proceedings of *Physor 2012*, Knoxville, -TN (2012). This mesh should cover all possible fissionable materials in the -problem. It has the following attributes/sub-elements: - - :dimension: - The number of mesh cells in the x, y, and z directions, respectively. - - *Default*: None - - :lower_left: - The Cartesian coordinates of the lower-left corner of the mesh. - - *Default*: None - - :upper_right: - The Cartesian coordinates of the upper-right corner of the mesh. - - *Default*: None +The ```` element indicates the ID of a mesh that is used for +re-weighting source sites at every generation based on the uniform fission site +methodology described in Kelly et al., "MC21 Analysis of the Nuclear Energy +Agency Monte Carlo Performance Benchmark Problem," Proceedings of *Physor 2012*, +Knoxville, TN (2012). The mesh should cover all possible fissionable materials +in the problem and is specified using a :ref:`mesh_element`. .. _verbosity: diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index f35823399..38ce61fb3 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -44,5 +44,8 @@ Classes EnergyFilter MaterialFilter Material + Mesh + MeshFilter + MeshSurfaceFilter Nuclide Tally diff --git a/include/openmc.h b/include/openmc.h index 1c2106432..083788120 100644 --- a/include/openmc.h +++ b/include/openmc.h @@ -16,7 +16,7 @@ extern "C" { int delayed_group; }; - void openmc_calculate_voumes(); + void openmc_calculate_volumes(); int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n); int openmc_cell_get_id(int32_t index, int32_t* id); int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices); @@ -27,10 +27,13 @@ extern "C" { int openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_filters(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_materials(int32_t n, int32_t* index_start, int32_t* index_end); + int openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); + int openmc_filter_get_type(int32_t index, const char** type); int openmc_filter_set_id(int32_t index, int32_t id); + int openmc_filter_set_type(int32_t index, const char* type); void openmc_finalize(); int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance); int openmc_get_cell_index(int32_t id, int32_t* index); @@ -38,7 +41,9 @@ extern "C" { void openmc_get_filter_next_id(int32_t* id); int openmc_get_keff(double k_combined[]); 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_nuclide_index(const char name[], int* index); + int64_t openmc_get_seed(); int openmc_get_tally_index(int32_t id, int32_t* index); void openmc_hard_reset(); void openmc_init(const int* intracomm); @@ -53,13 +58,22 @@ extern "C" { int openmc_material_set_id(int32_t index, int32_t id); int openmc_material_filter_get_bins(int32_t index, int32_t** bins, int32_t* n); int openmc_material_filter_set_bins(int32_t index, int32_t n, const int32_t* bins); + int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_mesh_filter_set_mesh(int32_t index, int32_t index_mesh); + int openmc_mesh_get_id(int32_t index, int32_t* id); + int openmc_mesh_get_dimension(int32_t index, int** id, int* n); + int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n); + int openmc_mesh_set_id(int32_t index, int32_t id); + int openmc_mesh_set_dimension(int32_t index, int n, const int* dims); + int openmc_mesh_set_params(int32_t index, const double* ll, const double* ur, const double* width, int n); + 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_next_batch(); + int openmc_next_batch(int* status); int openmc_nuclide_name(int index, char** name); void openmc_plot_geometry(); void openmc_reset(); - void openmc_run(); + int openmc_run(); + void openmc_set_seed(int64_t new_seed); void openmc_simulation_finalize(); void openmc_simulation_init(); int openmc_source_bank(struct Bank** ptr, int64_t* n); @@ -83,7 +97,8 @@ extern "C" { int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices); int openmc_tally_set_id(int32_t index, int32_t id); int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); - int openmc_tally_set_scores(int32_t index, int n, const int* scores); + int openmc_tally_set_scores(int32_t index, int n, const char** scores); + int openmc_tally_set_type(int32_t index, const char* type); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r); int openmc_zernike_filter_set_order(int32_t index, int order); @@ -91,17 +106,17 @@ extern "C" { const double* y, const double* r); // Error codes - extern int E_UNASSIGNED; - extern int E_ALLOCATE; - extern int E_OUT_OF_BOUNDS; - extern int E_INVALID_SIZE; - extern int E_INVALID_ARGUMENT; - extern int E_INVALID_TYPE; - extern int E_INVALID_ID; - extern int E_GEOMETRY; - extern int E_DATA; - extern int E_PHYSICS; - extern int E_WARNING; + extern int OPENMC_E_UNASSIGNED; + extern int OPENMC_E_ALLOCATE; + extern int OPENMC_E_OUT_OF_BOUNDS; + extern int OPENMC_E_INVALID_SIZE; + extern int OPENMC_E_INVALID_ARGUMENT; + extern int OPENMC_E_INVALID_TYPE; + extern int OPENMC_E_INVALID_ID; + extern int OPENMC_E_GEOMETRY; + extern int OPENMC_E_DATA; + extern int OPENMC_E_PHYSICS; + extern int OPENMC_E_WARNING; // Global variables extern char openmc_err_msg[256]; diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index bc173f994..217e782a8 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -43,6 +43,7 @@ from .core import * from .nuclide import * from .material import * from .cell import * +from .mesh import * from .filter import * from .tally import * from .settings import settings diff --git a/openmc/capi/cell.py b/openmc/capi/cell.py index 0ab3f2583..258e773dc 100644 --- a/openmc/capi/cell.py +++ b/openmc/capi/cell.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .material import Material __all__ = ['Cell', 'cells'] @@ -44,7 +45,7 @@ class Cell(_FortranObjectWithID): This class exposes a cell that is stored internally in the OpenMC library. To obtain a view of a cell with a given ID, use the - :data:`openmc.capi.nuclides` mapping. + :data:`openmc.capi.cells` mapping. Parameters ---------- @@ -115,14 +116,15 @@ class Cell(_FortranObjectWithID): def fill(self, fill): if isinstance(fill, Iterable): n = len(fill) - indices = (c_int*n)(*(m._index for m in fill)) - _dll.openmc_cell_set_fill(self._index, 1, 1, indices) + indices = (c_int32*n)(*(m._index if m is not None else -1 + for m in fill)) + _dll.openmc_cell_set_fill(self._index, 1, n, indices) elif isinstance(fill, Material): - materials = [fill] - indices = (c_int*1)(fill._index) + indices = (c_int32*1)(fill._index) + _dll.openmc_cell_set_fill(self._index, 1, 1, indices) + elif fill is None: + indices = (c_int32*1)(-1) _dll.openmc_cell_set_fill(self._index, 1, 1, indices) - else: - raise NotImplementedError def set_temperature(self, T, instance=None): """Set the temperature of a cell diff --git a/openmc/capi/core.py b/openmc/capi/core.py index 415fa2e93..f80b5fe54 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -6,8 +6,9 @@ from warnings import warn import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError from . import _dll -from .error import _error_handler, AllocationError +from .error import _error_handler import openmc.capi @@ -31,9 +32,12 @@ _dll.openmc_init.restype = None _dll.openmc_get_keff.argtypes = [POINTER(c_double*2)] _dll.openmc_get_keff.restype = c_int _dll.openmc_get_keff.errcheck = _error_handler +_dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int +_dll.openmc_next_batch.errcheck = _error_handler _dll.openmc_plot_geometry.restype = None -_dll.openmc_run.restype = None +_dll.openmc_run.restype = c_int +_dll.openmc_run.errcheck = _error_handler _dll.openmc_reset.restype = None _dll.openmc_source_bank.argtypes = [POINTER(POINTER(_Bank)), POINTER(c_int64)] _dll.openmc_source_bank.restype = c_int @@ -147,13 +151,13 @@ def iter_batches(): """ while True: # Run next batch - retval = next_batch() + status = next_batch() # Provide opportunity for user to perform action between batches yield # End the iteration - if retval < 0: + if status != 0: break @@ -180,12 +184,18 @@ def keff(): def next_batch(): - """Run next batch.""" - retval = _dll.openmc_next_batch() - if retval == -3: - raise AllocationError('Simulation has not been initialized. You must call ' - 'openmc.capi.simulation_init() first.') - return retval + """Run next batch. + + Returns + ------- + int + Status after running a batch (0=normal, 1=reached maximum number of + batches, 2=tally triggers reached) + + """ + status = c_int() + _dll.openmc_next_batch(status) + return status.value def plot_geometry(): diff --git a/openmc/capi/error.py b/openmc/capi/error.py index a11d6ea87..b35de4e60 100644 --- a/openmc/capi/error.py +++ b/openmc/capi/error.py @@ -1,45 +1,10 @@ from ctypes import c_int, c_char from warnings import warn +import openmc.exceptions as exc from . import _dll -class OpenMCError(Exception): - """Root exception class for OpenMC.""" - - -class GeometryError(OpenMCError): - """Geometry-related error""" - - -class InvalidIDError(OpenMCError): - """Use of an ID that is invalid.""" - - -class AllocationError(OpenMCError): - """Error related to memory allocation.""" - - -class OutOfBoundsError(OpenMCError): - """Index in array out of bounds.""" - - -class DataError(OpenMCError): - """Error relating to nuclear data.""" - - -class PhysicsError(OpenMCError): - """Error relating to performing physics.""" - - -class InvalidArgumentError(OpenMCError): - """Argument passed was invalid.""" - - -class InvalidTypeError(OpenMCError): - """Tried to perform an operation on the wrong type.""" - - def _error_handler(err, func, args): """Raise exception according to error code.""" @@ -52,23 +17,23 @@ def _error_handler(err, func, args): msg = errmsg.value.decode() # Raise exception type corresponding to error code - if err == errcode('e_allocate'): - raise AllocationError(msg) - elif err == errcode('e_out_of_bounds'): - raise OutOfBoundsError(msg) - elif err == errcode('e_invalid_argument'): - raise InvalidArgumentError(msg) - elif err == errcode('e_invalid_type'): - raise InvalidTypeError(msg) - if err == errcode('e_invalid_id'): - raise InvalidIDError(msg) - elif err == errcode('e_geometry'): - raise GeometryError(msg) - elif err == errcode('e_data'): - raise DataError(msg) - elif err == errcode('e_physics'): - raise PhysicsError(msg) - elif err == errcode('e_warning'): + if err == errcode('OPENMC_E_ALLOCATE'): + raise exc.AllocationError(msg) + elif err == errcode('OPENMC_E_OUT_OF_BOUNDS'): + raise exc.OutOfBoundsError(msg) + elif err == errcode('OPENMC_E_INVALID_ARGUMENT'): + raise exc.InvalidArgumentError(msg) + elif err == errcode('OPENMC_E_INVALID_TYPE'): + raise exc.InvalidTypeError(msg) + if err == errcode('OPENMC_E_INVALID_ID'): + raise exc.InvalidIDError(msg) + elif err == errcode('OPENMC_E_GEOMETRY'): + raise exc.GeometryError(msg) + elif err == errcode('OPENMC_E_DATA'): + raise exc.DataError(msg) + elif err == errcode('OPENMC_E_PHYSICS'): + raise exc.PhysicsError(msg) + elif err == errcode('OPENMC_E_WARNING'): warn(msg) elif err < 0: - raise OpenMCError("Unknown error encountered (code {}).".format(err)) + raise exc.OpenMCError("Unknown error encountered (code {}).".format(err)) diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 691ecc09c..5c21c3ef4 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -6,17 +6,19 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .material import Material +from .mesh import Mesh __all__ = ['Filter', 'AzimuthalFilter', 'CellFilter', 'CellbornFilter', 'CellfromFilter', 'DistribcellFilter', 'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter', 'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter', - 'MuFilter', 'PolarFilter', 'SurfaceFilter', + 'MeshSurfaceFilter', 'MuFilter', 'PolarFilter', 'SurfaceFilter', 'UniverseFilter', 'filters'] # Tally functions @@ -52,9 +54,15 @@ _dll.openmc_material_filter_get_bins.errcheck = _error_handler _dll.openmc_material_filter_set_bins.argtypes = [c_int32, c_int32, POINTER(c_int32)] _dll.openmc_material_filter_set_bins.restype = c_int _dll.openmc_material_filter_set_bins.errcheck = _error_handler +_dll.openmc_mesh_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_mesh_filter_get_mesh.restype = c_int +_dll.openmc_mesh_filter_get_mesh.errcheck = _error_handler _dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_mesh_filter_set_mesh.restype = c_int _dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler +_dll.openmc_meshsurface_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_meshsurface_filter_get_mesh.restype = c_int +_dll.openmc_meshsurface_filter_get_mesh.errcheck = _error_handler _dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32] _dll.openmc_meshsurface_filter_set_mesh.restype = c_int _dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler @@ -190,10 +198,40 @@ class MaterialFilter(Filter): class MeshFilter(Filter): filter_type = 'mesh' + def __init__(self, mesh=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if mesh is not None: + self.mesh = mesh + + @property + def mesh(self): + index_mesh = c_int32() + _dll.openmc_mesh_filter_get_mesh(self._index, index_mesh) + return Mesh(index=index_mesh.value) + + @mesh.setter + def mesh(self, mesh): + _dll.openmc_mesh_filter_set_mesh(self._index, mesh._index) + class MeshSurfaceFilter(Filter): filter_type = 'meshsurface' + def __init__(self, mesh=None, uid=None, new=True, index=None): + super().__init__(uid, new, index) + if mesh is not None: + self.mesh = mesh + + @property + def mesh(self): + index_mesh = c_int32() + _dll.openmc_meshsurface_filter_get_mesh(self._index, index_mesh) + return Mesh(index=index_mesh.value) + + @mesh.setter + def mesh(self, mesh): + _dll.openmc_meshsurface_filter_set_mesh(self._index, mesh._index) + class MuFilter(Filter): filter_type = 'mu' diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 62d6df012..a6c29a375 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import AllocationError, InvalidIDError from . import _dll, Nuclide from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler __all__ = ['Material', 'materials'] @@ -89,6 +90,9 @@ class Material(_FortranObjectWithID): index = index.value else: index = mapping[uid]._index + elif index == -1: + # Special value indicates void material + return None if index not in cls.__instances: instance = super(Material, cls).__new__(cls) diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py new file mode 100644 index 000000000..091c5194b --- /dev/null +++ b/openmc/capi/mesh.py @@ -0,0 +1,183 @@ +from collections.abc import Mapping, Iterable +from ctypes import c_int, c_int32, c_double, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from openmc.exceptions import AllocationError, InvalidIDError +from . import _dll +from .core import _FortranObjectWithID +from .error import _error_handler +from .material import Material + +__all__ = ['Mesh', 'meshes'] + +# Mesh functions +_dll.openmc_extend_meshes.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] +_dll.openmc_extend_meshes.restype = c_int +_dll.openmc_extend_meshes.errcheck = _error_handler +_dll.openmc_mesh_get_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_mesh_get_id.restype = c_int +_dll.openmc_mesh_get_id.errcheck = _error_handler +_dll.openmc_mesh_get_dimension.argtypes = [c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] +_dll.openmc_mesh_get_dimension.restype = c_int +_dll.openmc_mesh_get_dimension.errcheck = _error_handler +_dll.openmc_mesh_get_params.argtypes = [ + c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), + POINTER(POINTER(c_double)), POINTER(c_int)] +_dll.openmc_mesh_get_params.restype = c_int +_dll.openmc_mesh_get_params.errcheck = _error_handler +_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_set_dimension.argtypes = [c_int32, c_int, POINTER(c_int)] +_dll.openmc_mesh_set_dimension.restype = c_int +_dll.openmc_mesh_set_dimension.errcheck = _error_handler +_dll.openmc_mesh_set_params.argtypes = [ + c_int32, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)] +_dll.openmc_mesh_set_params.restype = c_int +_dll.openmc_mesh_set_params.errcheck = _error_handler +_dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_mesh_index.restype = c_int +_dll.openmc_get_mesh_index.errcheck = _error_handler + + +class Mesh(_FortranObjectWithID): + """Mesh stored internally. + + This class exposes a mesh that is stored internally in the OpenMC + library. To obtain a view of a mesh with a given ID, use the + :data:`openmc.capi.meshes` mapping. + + Parameters + ---------- + index : int + Index in the `meshes` array. + + Attributes + ---------- + id : int + ID of the mesh + dimension : iterable of int + The number of mesh cells in each direction. + lower_left : numpy.ndarray + The lower-left corner of the structured mesh. If only two coordinate are + given, it is assumed that the mesh is an x-y mesh. + upper_right : numpy.ndarray + The upper-right corner of the structrued mesh. If only two coordinate + are given, it is assumed that the mesh is an x-y mesh. + width : numpy.ndarray + The width of mesh cells in each direction. + + """ + __instances = WeakValueDictionary() + + def __new__(cls, uid=None, new=True, index=None): + mapping = meshes + if index is None: + if new: + # Determine ID to assign + if uid is None: + uid = max(mapping, default=0) + 1 + else: + if uid in mapping: + raise AllocationError('A mesh with ID={} has already ' + 'been allocated.'.format(uid)) + + index = c_int32() + _dll.openmc_extend_meshes(1, index, None) + index = index.value + else: + index = mapping[uid]._index + + if index not in cls.__instances: + instance = super().__new__(cls) + instance._index = index + if uid is not None: + instance.id = uid + cls.__instances[index] = instance + + return cls.__instances[index] + + @property + def id(self): + mesh_id = c_int32() + _dll.openmc_mesh_get_id(self._index, mesh_id) + return mesh_id.value + + @id.setter + def id(self, mesh_id): + _dll.openmc_mesh_set_id(self._index, mesh_id) + + @property + def dimension(self): + dims = POINTER(c_int)() + n = c_int() + _dll.openmc_mesh_get_dimension(self._index, dims, n) + return tuple(as_array(dims, (n.value,))) + + @dimension.setter + def dimension(self, dimension): + n = len(dimension) + dimension = (c_int*n)(*dimension) + _dll.openmc_mesh_set_dimension(self._index, n, dimension) + + @property + def lower_left(self): + return self._get_parameters()[0] + + @property + def upper_right(self): + return self._get_parameters()[1] + + @property + def width(self): + return self._get_parameters()[2] + + def _get_parameters(self): + ll = POINTER(c_double)() + ur = POINTER(c_double)() + w = POINTER(c_double)() + n = c_int() + _dll.openmc_mesh_get_params(self._index, ll, ur, w, n) + return ( + as_array(ll, (n.value,)), + as_array(ur, (n.value,)), + as_array(w, (n.value,)) + ) + + def set_parameters(self, lower_left=None, upper_right=None, width=None): + if lower_left is not None: + n = len(lower_left) + lower_left = (c_double*n)(*lower_left) + if upper_right is not None: + n = len(upper_right) + upper_right = (c_double*n)(*upper_right) + if width is not None: + n = len(width) + width = (c_double*n)(*width) + _dll.openmc_mesh_set_params(self._index, n, lower_left, upper_right, width) + + +class _MeshMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + try: + _dll.openmc_get_mesh_index(key, index) + except (AllocationError, InvalidIDError) as e: + # __contains__ expects a KeyError to work correctly + raise KeyError(str(e)) + return Mesh(index=index.value) + + def __iter__(self): + for i in range(len(self)): + yield Mesh(index=i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_meshes').value + + def __repr__(self): + return repr(dict(self)) + +meshes = _MeshMapping() diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py index f66212c97..e57653c64 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/capi/nuclide.py @@ -5,9 +5,10 @@ from weakref import WeakValueDictionary import numpy as np from numpy.ctypeslib import as_array +from openmc.exceptions import DataError, AllocationError from . import _dll from .core import _FortranObject -from .error import _error_handler, DataError, AllocationError +from .error import _error_handler __all__ = ['Nuclide', 'nuclides', 'load_nuclide'] diff --git a/openmc/capi/tally.py b/openmc/capi/tally.py index a78347177..46a967f69 100644 --- a/openmc/capi/tally.py +++ b/openmc/capi/tally.py @@ -6,10 +6,11 @@ import numpy as np from numpy.ctypeslib import as_array import scipy.stats +from openmc.exceptions import AllocationError, InvalidIDError from openmc.data.reaction import REACTION_NAME from . import _dll, Nuclide from .core import _FortranObjectWithID -from .error import _error_handler, AllocationError, InvalidIDError +from .error import _error_handler from .filter import _get_filter diff --git a/openmc/data/library.py b/openmc/data/library.py index 34cd380a5..adfa3e4cb 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -23,9 +23,14 @@ class DataLibrary(EqualityMixin): def __init__(self): self.libraries = [] - def get_by_material(self, value): + def get_by_material(self, name): """Return the library dictionary containing a given material. + Parameters + ---------- + name : str + Name of material, e.g. 'Am241' + Returns ------- library : dict or None @@ -34,7 +39,7 @@ class DataLibrary(EqualityMixin): """ for library in self.libraries: - if value in library['materials']: + if name in library['materials']: return library return None diff --git a/openmc/exceptions.py b/openmc/exceptions.py new file mode 100644 index 000000000..c87bfc82c --- /dev/null +++ b/openmc/exceptions.py @@ -0,0 +1,38 @@ +class OpenMCError(Exception): + """Root exception class for OpenMC.""" + + +class GeometryError(OpenMCError): + """Geometry-related error""" + + +class InvalidIDError(OpenMCError): + """Use of an ID that is invalid.""" + + +class AllocationError(OpenMCError): + """Error related to memory allocation.""" + + +class OutOfBoundsError(OpenMCError): + """Index in array out of bounds.""" + + +class DataError(OpenMCError): + """Error relating to nuclear data.""" + + +class PhysicsError(OpenMCError): + """Error relating to performing physics.""" + + +class InvalidArgumentError(OpenMCError): + """Argument passed was invalid.""" + + +class InvalidTypeError(OpenMCError): + """Tried to perform an operation on the wrong type.""" + + +class SetupError(OpenMCError): + """Error while setting up a problem.""" diff --git a/src/cmfd_input.F90 b/src/cmfd_input.F90 index 86f4e2400..40b4abf57 100644 --- a/src/cmfd_input.F90 +++ b/src/cmfd_input.F90 @@ -278,7 +278,7 @@ contains m % id = i_start ! Set mesh type to rectangular - m % type = LATTICE_RECT + m % type = MESH_REGULAR ! Get pointer to mesh XML node node_mesh = root % child("mesh") diff --git a/src/error.F90 b/src/error.F90 index 0a5af302d..f4fb34173 100644 --- a/src/error.F90 +++ b/src/error.F90 @@ -15,19 +15,19 @@ module error public :: write_message ! Error codes - integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1 - integer(C_INT), public, bind(C) :: E_ALLOCATE = -2 - integer(C_INT), public, bind(C) :: E_OUT_OF_BOUNDS = -3 - integer(C_INT), public, bind(C) :: E_INVALID_SIZE = -4 - integer(C_INT), public, bind(C) :: E_INVALID_ARGUMENT = -5 - integer(C_INT), public, bind(C) :: E_INVALID_TYPE = -6 - integer(C_INT), public, bind(C) :: E_INVALID_ID = -7 - integer(C_INT), public, bind(C) :: E_GEOMETRY = -8 - integer(C_INT), public, bind(C) :: E_DATA = -9 - integer(C_INT), public, bind(C) :: E_PHYSICS = -10 + integer(C_INT), public, bind(C, name='OPENMC_E_UNASSIGNED') :: E_UNASSIGNED = -1 + integer(C_INT), public, bind(C, name='OPENMC_E_ALLOCATE') :: E_ALLOCATE = -2 + integer(C_INT), public, bind(C, name='OPENMC_E_OUT_OF_BOUNDS') :: E_OUT_OF_BOUNDS = -3 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_SIZE') :: E_INVALID_SIZE = -4 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_ARGUMENT') :: E_INVALID_ARGUMENT = -5 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_TYPE') :: E_INVALID_TYPE = -6 + integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_ID') :: E_INVALID_ID = -7 + integer(C_INT), public, bind(C, name='OPENMC_E_GEOMETRY') :: E_GEOMETRY = -8 + integer(C_INT), public, bind(C, name='OPENMC_E_DATA') :: E_DATA = -9 + integer(C_INT), public, bind(C, name='OPENMC_E_PHYSICS') :: E_PHYSICS = -10 ! Warning codes - integer(C_INT), public, bind(C) :: E_WARNING = 1 + integer(C_INT), public, bind(C, name='OPENMC_E_WARNING') :: E_WARNING = 1 ! Error message character(kind=C_CHAR), public, bind(C) :: openmc_err_msg(256) diff --git a/src/geometry_header.F90 b/src/geometry_header.F90 index e71916d09..f6206adf9 100644 --- a/src/geometry_header.F90 +++ b/src/geometry_header.F90 @@ -544,16 +544,12 @@ contains c % type = FILL_MATERIAL do i = 1, n j = indices(i) - if (j == 0) then - c % material(i) = MATERIAL_VOID + if ((j >= 1 .and. j <= n_materials) .or. j == MATERIAL_VOID) then + c % material(i) = j else - if (j >= 1 .and. j <= n_materials) then - c % material(i) = j - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index " // trim(to_str(j)) // " in the & - &materials array is out of bounds.") - end if + err = E_OUT_OF_BOUNDS + call set_errmsg("Index " // trim(to_str(j)) // " in the & + &materials array is out of bounds.") end if end do case (FILL_UNIVERSE) diff --git a/src/main.F90 b/src/main.F90 index 120bdd1e0..372e993f5 100644 --- a/src/main.F90 +++ b/src/main.F90 @@ -1,5 +1,7 @@ program main + use, intrinsic :: ISO_C_BINDING + use constants use message_passing use openmc_api, only: openmc_init, openmc_finalize, openmc_run, & @@ -9,6 +11,7 @@ program main implicit none + integer(C_INT) :: err #ifdef OPENMC_MPI integer :: mpi_err ! MPI error code #endif @@ -27,7 +30,7 @@ program main ! start problem based on mode select case (run_mode) case (MODE_FIXEDSOURCE, MODE_EIGENVALUE) - call openmc_run() + err = openmc_run() case (MODE_PLOTTING) call openmc_plot_geometry() case (MODE_PARTICLE) diff --git a/src/mesh_header.F90 b/src/mesh_header.F90 index 8a2d61fd9..fc13a868e 100644 --- a/src/mesh_header.F90 +++ b/src/mesh_header.F90 @@ -6,7 +6,7 @@ module mesh_header use constants use dict_header, only: DictIntInt - use error, only: warning, fatal_error + use error use hdf5_interface use string, only: to_str, to_lower use xml_interface @@ -15,6 +15,13 @@ module mesh_header private public :: free_memory_mesh public :: openmc_extend_meshes + public :: openmc_get_mesh_index + public :: openmc_mesh_get_id + public :: openmc_mesh_get_dimension + public :: openmc_mesh_get_params + public :: openmc_mesh_set_id + public :: openmc_mesh_set_dimension + public :: openmc_mesh_set_params !=============================================================================== ! STRUCTUREDMESH represents a tessellation of n-dimensional Euclidean space by @@ -23,13 +30,13 @@ module mesh_header type, public :: RegularMesh integer :: id = -1 ! user-specified id - integer :: type ! rectangular, hexagonal - integer :: n_dimension ! rank of mesh + integer :: type = MESH_REGULAR ! rectangular, hexagonal + integer(C_INT) :: n_dimension ! rank of mesh real(8) :: volume_frac ! volume fraction of each cell - integer, allocatable :: dimension(:) ! number of cells in each direction - real(8), allocatable :: lower_left(:) ! lower-left corner of mesh - real(8), allocatable :: upper_right(:) ! upper-right corner of mesh - real(8), allocatable :: width(:) ! width of each mesh cell + integer(C_INT), allocatable :: dimension(:) ! number of cells in each direction + real(C_DOUBLE), allocatable :: lower_left(:) ! lower-left corner of mesh + real(C_DOUBLE), allocatable :: upper_right(:) ! upper-right corner of mesh + real(C_DOUBLE), allocatable :: width(:) ! width of each mesh cell contains procedure :: from_xml => regular_from_xml procedure :: get_bin => regular_get_bin @@ -592,4 +599,180 @@ contains err = 0 end function openmc_extend_meshes + + function openmc_get_mesh_index(id, index) result(err) bind(C) + ! Return the index in the meshes array of a mesh with a given ID + integer(C_INT32_T), value :: id + integer(C_INT32_T), intent(out) :: index + integer(C_INT) :: err + + if (allocated(meshes)) then + if (mesh_dict % has(id)) then + index = mesh_dict % get(id) + err = 0 + else + err = E_INVALID_ID + call set_errmsg("No mesh exists with ID=" // trim(to_str(id)) // ".") + end if + else + err = E_ALLOCATE + call set_errmsg("Memory has not been allocated for meshes.") + end if + end function openmc_get_mesh_index + + + function openmc_mesh_get_id(index, id) result(err) bind(C) + ! Return the ID of a mesh + integer(C_INT32_T), value :: index + integer(C_INT32_T), intent(out) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= size(meshes)) then + id = meshes(index) % id + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_get_id + + + function openmc_mesh_set_id(index, id) result(err) bind(C) + ! Set the ID of a mesh + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), value, intent(in) :: id + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_meshes) then + meshes(index) % id = id + call mesh_dict % set(id, index) + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_set_id + + + function openmc_mesh_get_dimension(index, dims, n) result(err) bind(C) + ! Get the dimension of a mesh + integer(C_INT32_T), value, intent(in) :: index + type(C_PTR), intent(out) :: dims + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_meshes) then + dims = C_LOC(meshes(index) % dimension) + n = meshes(index) % n_dimension + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_get_dimension + + + function openmc_mesh_set_dimension(index, n, dims) result(err) bind(C) + ! Set the dimension of a mesh + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + integer(C_INT), intent(in) :: dims(n) + integer(C_INT) :: err + + if (index >= 1 .and. index <= n_meshes) then + associate (m => meshes(index)) + if (allocated(m % dimension)) deallocate (m % dimension) + if (allocated(m % lower_left)) deallocate (m % lower_left) + if (allocated(m % upper_right)) deallocate (m % upper_right) + if (allocated(m % width)) deallocate (m % width) + + m % n_dimension = n + allocate(m % dimension(n)) + allocate(m % lower_left(n)) + allocate(m % upper_right(n)) + allocate(m % width(n)) + + ! Copy dimension + m % dimension(:) = dims + end associate + err = 0 + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_set_dimension + + + function openmc_mesh_get_params(index, ll, ur, width, n) result(err) bind(C) + ! Get the mesh parameters + integer(C_INT32_T), value, intent(in) :: index + type(C_PTR), intent(out) :: ll + type(C_PTR), intent(out) :: ur + type(C_PTR), intent(out) :: width + integer(C_INT), intent(out) :: n + integer(C_INT) :: err + + err = 0 + if (index >= 1 .and. index <= n_meshes) then + associate (m => meshes(index)) + if (allocated(m % lower_left)) then + ll = C_LOC(m % lower_left(1)) + ur = C_LOC(m % upper_right(1)) + width = C_LOC(m % width(1)) + n = m % n_dimension + else + err = E_ALLOCATE + call set_errmsg("Mesh parameters have not been set.") + end if + end associate + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_get_params + + + function openmc_mesh_set_params(index, n, ll, ur, width) result(err) bind(C) + ! Set the mesh parameters + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT), value, intent(in) :: n + real(C_DOUBLE), intent(in), optional :: ll(n) + real(C_DOUBLE), intent(in), optional :: ur(n) + real(C_DOUBLE), intent(in), optional :: width(n) + integer(C_INT) :: err + + err = 0 + if (index >= 1 .and. index <= n_meshes) then + associate (m => meshes(index)) + if (allocated(m % lower_left)) deallocate (m % lower_left) + if (allocated(m % upper_right)) deallocate (m % upper_right) + if (allocated(m % width)) deallocate (m % width) + + allocate(m % lower_left(n)) + allocate(m % upper_right(n)) + allocate(m % width(n)) + + if (present(ll) .and. present(ur)) then + m % lower_left(:) = ll + m % upper_right(:) = ur + m % width(:) = (ur - ll) / m % dimension + elseif (present(ll) .and. present(width)) then + m % lower_left(:) = ll + m % width(:) = width + m % upper_right(:) = ll + width * m % dimension + elseif (present(ur) .and. present(width)) then + m % upper_right(:) = ur + m % width(:) = width + m % lower_left(:) = ur - width * m % dimension + else + err = E_INVALID_ARGUMENT + call set_errmsg("At least two parameters must be specified.") + end if + end associate + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in meshes array is out of bounds.") + end if + end function openmc_mesh_set_params + end module mesh_header diff --git a/src/simulation.F90 b/src/simulation.F90 index 8fe4f025c..68debe52b 100644 --- a/src/simulation.F90 +++ b/src/simulation.F90 @@ -48,6 +48,10 @@ module simulation public :: openmc_simulation_init public :: openmc_simulation_finalize + integer(C_INT), parameter :: STATUS_EXIT_NORMAL = 0 + integer(C_INT), parameter :: STATUS_EXIT_MAX_BATCH = 1 + integer(C_INT), parameter :: STATUS_EXIT_ON_TRIGGER = 2 + contains !=============================================================================== @@ -56,28 +60,36 @@ contains ! calculation. !=============================================================================== - subroutine openmc_run() bind(C) + function openmc_run() result(err) bind(C) + integer(C_INT) :: err + integer(C_INT) :: status call openmc_simulation_init() - do while (openmc_next_batch() == 0) + do + err = openmc_next_batch(status) + if (status /= 0 .or. err < 0) exit end do call openmc_simulation_finalize() - end subroutine openmc_run + end function openmc_run !=============================================================================== ! OPENMC_NEXT_BATCH !=============================================================================== - function openmc_next_batch() result(retval) bind(C) - integer(C_INT) :: retval + function openmc_next_batch(status) result(err) bind(C) + integer(C_INT), intent(out), optional :: status + integer(C_INT) :: err type(Particle) :: p integer(8) :: i_work + err = 0 + ! Make sure simulation has been initialized if (.not. simulation_initialized) then - retval = -3 + err = E_ALLOCATE + call set_errmsg("Simulation has not been initialized yet.") return end if @@ -86,7 +98,7 @@ contains ! Handle restart runs if (restart_run .and. current_batch <= restart_batch) then call replay_batch_history() - retval = 0 + status = STATUS_EXIT_NORMAL return end if @@ -124,12 +136,14 @@ contains call finalize_batch() ! Check simulation ending criteria - if (current_batch == n_max_batches) then - retval = -1 - elseif (satisfy_triggers) then - retval = -2 - else - retval = 0 + if (present(status)) then + if (current_batch == n_max_batches) then + status = STATUS_EXIT_MAX_BATCH + elseif (satisfy_triggers) then + status = STATUS_EXIT_ON_TRIGGER + else + status = STATUS_EXIT_NORMAL + end if end if end function openmc_next_batch diff --git a/src/tallies/tally_filter_mesh.F90 b/src/tallies/tally_filter_mesh.F90 index 3c0e6250c..d2acdc4c8 100644 --- a/src/tallies/tally_filter_mesh.F90 +++ b/src/tallies/tally_filter_mesh.F90 @@ -16,6 +16,7 @@ module tally_filter_mesh implicit none private + public :: openmc_mesh_filter_get_mesh public :: openmc_mesh_filter_set_mesh !=============================================================================== @@ -280,35 +281,46 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_mesh_filter_get_mesh(index, index_mesh) result(err) bind(C) + ! Get the mesh for a mesh filter + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), intent(out) :: index_mesh + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshFilter) + index_mesh = f % mesh + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select + end if + end function openmc_mesh_filter_get_mesh + + function openmc_mesh_filter_set_mesh(index, index_mesh) result(err) bind(C) ! Set the mesh for a mesh filter integer(C_INT32_T), value, intent(in) :: index integer(C_INT32_T), value, intent(in) :: index_mesh integer(C_INT) :: err - err = 0 - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (MeshFilter) - if (index_mesh >= 1 .and. index_mesh <= n_meshes) then - f % mesh = index_mesh - f % n_bins = product(meshes(index_mesh) % dimension) - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in 'meshes' array is out of bounds.") - end if + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshFilter) + if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + f % mesh = index_mesh + f % n_bins = product(meshes(index_mesh) % dimension) + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in 'meshes' array is out of bounds.") + end if class default - err = E_INVALID_TYPE - call set_errmsg("Tried to set mesh on a non-mesh filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select end if end function openmc_mesh_filter_set_mesh diff --git a/src/tallies/tally_filter_meshsurface.F90 b/src/tallies/tally_filter_meshsurface.F90 index e1e8c4803..d90b63664 100644 --- a/src/tallies/tally_filter_meshsurface.F90 +++ b/src/tallies/tally_filter_meshsurface.F90 @@ -16,6 +16,7 @@ module tally_filter_meshsurface implicit none private + public :: openmc_meshsurface_filter_get_mesh public :: openmc_meshsurface_filter_set_mesh !=============================================================================== @@ -296,6 +297,25 @@ contains ! C API FUNCTIONS !=============================================================================== + function openmc_meshsurface_filter_get_mesh(index, index_mesh) result(err) bind(C) + ! Get the mesh for a mesh surface filter + integer(C_INT32_T), value, intent(in) :: index + integer(C_INT32_T), intent(out) :: index_mesh + integer(C_INT) :: err + + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshSurfaceFilter) + index_mesh = f % mesh + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select + end if + end function openmc_meshsurface_filter_get_mesh + + function openmc_meshsurface_filter_set_mesh(index, index_mesh) result(err) bind(C) ! Set the mesh for a mesh surface filter integer(C_INT32_T), value, intent(in) :: index @@ -304,30 +324,22 @@ contains integer :: n_dim - err = 0 - if (index >= 1 .and. index <= n_filters) then - if (allocated(filters(index) % obj)) then - select type (f => filters(index) % obj) - type is (MeshSurfaceFilter) - if (index_mesh >= 1 .and. index_mesh <= n_meshes) then - f % mesh = index_mesh - n_dim = meshes(index_mesh) % n_dimension - f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension) - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in 'meshes' array is out of bounds.") - end if - class default - err = E_INVALID_TYPE - call set_errmsg("Tried to set mesh on a non-mesh filter.") - end select - else - err = E_ALLOCATE - call set_errmsg("Filter type has not been set yet.") - end if - else - err = E_OUT_OF_BOUNDS - call set_errmsg("Index in filters array out of bounds.") + err = verify_filter(index) + if (err == 0) then + select type (f => filters(index) % obj) + type is (MeshSurfaceFilter) + if (index_mesh >= 1 .and. index_mesh <= n_meshes) then + f % mesh = index_mesh + n_dim = meshes(index_mesh) % n_dimension + f % n_bins = 4*n_dim*product(meshes(index_mesh) % dimension) + else + err = E_OUT_OF_BOUNDS + call set_errmsg("Index in 'meshes' array is out of bounds.") + end if + class default + err = E_INVALID_TYPE + call set_errmsg("Tried to set mesh on a non-mesh filter.") + end select end if end function openmc_meshsurface_filter_set_mesh diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 8bc6c3d15..6c05f5d3e 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -4,6 +4,7 @@ import os import numpy as np import pytest import openmc +import openmc.exceptions as exc import openmc.capi from tests import cdtemp @@ -60,7 +61,7 @@ def test_cell(capi_init): def test_new_cell(capi_init): - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.Cell(1) new_cell = openmc.capi.Cell() new_cell_with_id = openmc.capi.Cell(10) @@ -91,7 +92,7 @@ def test_material(capi_init): def test_new_material(capi_init): - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.Material(1) new_mat = openmc.capi.Material() new_mat_with_id = openmc.capi.Material(10) @@ -109,7 +110,7 @@ def test_nuclide_mapping(capi_init): def test_load_nuclide(capi_init): openmc.capi.load_nuclide('Pu239') - with pytest.raises(openmc.capi.DataError): + with pytest.raises(exc.DataError): openmc.capi.load_nuclide('Pu3') @@ -145,7 +146,7 @@ def test_tally(capi_init): assert isinstance(t.filters[1], openmc.capi.EnergyFilter) # Create new filter and replace existing - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.MaterialFilter(uid=1) mats = openmc.capi.materials f = openmc.capi.MaterialFilter([mats[2], mats[1]]) @@ -153,7 +154,7 @@ def test_tally(capi_init): assert t.filters == [f] assert t.nuclides == ['U235', 'U238'] - with pytest.raises(openmc.capi.DataError): + with pytest.raises(exc.DataError): t.nuclides = ['Zr2'] t.nuclides = ['U234', 'Zr90'] assert t.nuclides == ['U234', 'Zr90'] @@ -165,7 +166,7 @@ def test_tally(capi_init): def test_new_tally(capi_init): - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.Material(1) new_tally = openmc.capi.Tally() new_tally.scores = ['flux'] @@ -206,7 +207,7 @@ def test_by_batch(capi_run): # Running next batch before simulation is initialized should raise an # exception - with pytest.raises(openmc.capi.AllocationError): + with pytest.raises(exc.AllocationError): openmc.capi.next_batch() openmc.capi.simulation_init() @@ -241,7 +242,7 @@ def test_find_cell(capi_init): assert cell is openmc.capi.cells[1] cell, instance = openmc.capi.find_cell((0.4, 0., 0.)) assert cell is openmc.capi.cells[2] - with pytest.raises(openmc.capi.GeometryError): + with pytest.raises(exc.GeometryError): openmc.capi.find_cell((100., 100., 100.)) @@ -250,3 +251,38 @@ def test_find_material(capi_init): assert mat is openmc.capi.materials[1] mat = openmc.capi.find_material((0.4, 0., 0.)) assert mat is openmc.capi.materials[2] + + +def test_mesh(capi_init): + mesh = openmc.capi.Mesh() + mesh.dimension = (2, 3, 4) + assert mesh.dimension == (2, 3, 4) + with pytest.raises(exc.AllocationError): + mesh2 = openmc.capi.Mesh(mesh.id) + + # Make sure each combination of parameters works + ll = (0., 0., 0.) + ur = (10., 10., 10.) + width = (1., 1., 1.) + mesh.set_parameters(lower_left=ll, upper_right=ur) + assert mesh.lower_left == pytest.approx(ll) + assert mesh.upper_right == pytest.approx(ur) + mesh.set_parameters(lower_left=ll, width=width) + assert mesh.lower_left == pytest.approx(ll) + assert mesh.width == pytest.approx(width) + mesh.set_parameters(upper_right=ur, width=width) + assert mesh.upper_right == pytest.approx(ur) + assert mesh.width == pytest.approx(width) + + meshes = openmc.capi.meshes + assert isinstance(meshes, Mapping) + assert len(meshes) == 1 + for mesh_id, mesh in meshes.items(): + assert isinstance(mesh, openmc.capi.Mesh) + assert mesh_id == mesh.id + + mf = openmc.capi.MeshFilter(mesh) + assert mf.mesh == mesh + + msf = openmc.capi.MeshSurfaceFilter(mesh) + assert msf.mesh == mesh