From 692765ef4ac641276a16a4db153a33e6e9240423 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Sat, 29 Jul 2017 08:43:48 -0500 Subject: [PATCH] Move material/nuclide related C API bindings into separate files --- openmc/capi/__init__.py | 261 ++++------------------------------------ openmc/capi/material.py | 147 ++++++++++++++++++++++ openmc/capi/nuclide.py | 86 +++++++++++++ 3 files changed, 256 insertions(+), 238 deletions(-) create mode 100644 openmc/capi/material.py create mode 100644 openmc/capi/nuclide.py diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 0706d4c774..f8beb2fd7f 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -10,21 +10,36 @@ automatically loaded. Calls to the OpenMC library can then be made, for example: """ -from collections import Mapping from contextlib import contextmanager from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER import sys from warnings import warn -from weakref import WeakValueDictionary -import numpy as np from numpy.ctypeslib import as_array import pkg_resources + +# Determine shared-library suffix +if sys.platform == 'darwin': + _suffix = 'dylib' +else: + _suffix = 'so' + +# Open shared library +_filename = pkg_resources.resource_filename( + __name__, '_libopenmc.{}'.format(_suffix)) +try: + _dll = CDLL(_filename) + _available = True +except OSError: + warn("OpenMC shared library is not available from the Python API. This " + "means you will not be able to use openmc.capi to make in-memory " + "calls to OpenMC.") + _available = False + + _int3 = c_int*3 _double3 = c_double*3 -_int_array = POINTER(POINTER(c_int)) -_double_array = POINTER(POINTER(c_double)) class GeometryError(Exception): @@ -77,172 +92,6 @@ def _error_handler(err, func, args): raise Exception("Unknown error encountered (code {}).".format(err)) - - -class MaterialView(object): - __instances = WeakValueDictionary() - def __new__(cls, *args): - if args not in cls.__instances: - instance = super().__new__(cls) - cls.__instances[args] = instance - return cls.__instances[args] - - def __init__(self, index): - self._index = index - - @property - def id(self): - mat_id = c_int32() - _dll.openmc_material_id(self._index, mat_id) - return mat_id.value - - @property - def nuclides(self): - return self._get_densities()[0] - return nuclides - - @property - def densities(self): - return self._get_densities()[1] - - def _get_densities(self): - """Get atom densities in a material. - - Returns - ------- - list of string - List of nuclide names - numpy.ndarray - Array of densities in atom/b-cm - - """ - # Allocate memory for arguments that are written to - nuclides = POINTER(c_int)() - densities = POINTER(c_double)() - n = c_int() - - # Get nuclide names and densities - _dll.openmc_material_get_densities(self._index, nuclides, densities, n) - - # Convert to appropriate types and return - nuclide_list = [NuclideView(nuclides[i]).name for i in range(n.value)] - density_array = as_array(densities, (n.value,)) - return nuclide_list, density_array - - def add_nuclide(name, density): - """Add a nuclide to a material. - - Parameters - ---------- - name : str - Name of nuclide, e.g. 'U235' - density : float - Density in atom/b-cm - - """ - _dll.openmc_material_add_nuclide(self._index, name.encode(), density) - - def set_density(self, density): - """Set density of a material. - - Parameters - ---------- - density : float - Density in atom/b-cm - - """ - _dll.openmc_material_set_density(self._index, density) - - def set_densities(self, nuclides, densities): - """Set the densities of a list of nuclides in a material - - Parameters - ---------- - nuclides : iterable of str - Nuclide names - densities : iterable of float - Corresponding densities in atom/b-cm - - """ - # Convert strings to an array of char* - nucs = (c_char_p * len(nuclides))() - nucs[:] = [x.encode() for x in nuclides] - - # Get numpy array as a double* - d = np.asarray(densities) - dp = d.ctypes.data_as(POINTER(c_double)) - - _dll.openmc_material_set_densities(self._index, len(nuclides), nucs, dp) - - -class _MaterialMapping(Mapping): - def __getitem__(self, key): - index = c_int32() - _dll.openmc_get_material(key, index) - return MaterialView(index.value) - - def __iter__(self): - for i in range(len(self)): - yield MaterialView(i + 1).id - - def __len__(self): - return c_int32.in_dll(_dll, 'n_materials').value - -materials = _MaterialMapping() - - -class NuclideView(object): - __instances = WeakValueDictionary() - def __new__(cls, *args): - if args not in cls.__instances: - instance = super().__new__(cls) - cls.__instances[args] = instance - return cls.__instances[args] - - def __init__(self, index): - self._index = index - - @property - def name(self): - """Name of nuclide with given index - - Parameter - --------- - index : int - Index in internal nuclides array - - Returns - ------- - str - Name of nuclide - - """ - name = c_char_p() - _dll.openmc_nuclide_name(self._index, name) - - # Find blank in name - i = 0 - while name.value[i:i+1] != b' ': - i += 1 - return name.value[:i].decode() - - -class _NuclideMapping(Mapping): - def __getitem__(self, key): - index = c_int() - _dll.openmc_get_nuclide(key.encode(), index) - return NuclideView(index) - - def __iter__(self): - for i in range(len(self)): - yield NuclideView(i + 1).name - - def __len__(self): - return c_int.in_dll(_dll, 'n_nuclides').value - -nuclides = _NuclideMapping() - - def calculate_volumes(): """Run stochastic volume calculation""" _dll.openmc_calculate_volumes() @@ -264,7 +113,6 @@ def cell_set_temperature(cell_id, T, instance=None): _dll.openmc_cell_set_temperature(cell_id, T, instance) - def finalize(): """Finalize simulation and free memory""" _dll.openmc_finalize() @@ -340,20 +188,6 @@ def keff(): return tuple(k) -def load_nuclide(name): - """Load cross section data for a nuclide. - - Parameters - ---------- - name : str - Name of nuclide, e.g. 'U235' - - """ - _dll.openmc_load_nuclide(name.encode()) - - - - def plot_geometry(): """Plot geometry""" _dll.openmc_plot_geometry() @@ -415,24 +249,6 @@ def run_in_memory(intracomm=None): finalize() -# Determine shared-library suffix -if sys.platform == 'darwin': - _suffix = 'dylib' -else: - _suffix = 'so' - -# Open shared library -_filename = pkg_resources.resource_filename( - __name__, '_libopenmc.{}'.format(_suffix)) -try: - _dll = CDLL(_filename) - _available = True -except OSError: - warn("OpenMC shared library is not available from the Python API. This " - "means you will not be able to use openmc.capi to make in-memory " - "calls to OpenMC.") - _available = False - # Set argument/return types if _available: _dll.openmc_calculate_volumes.restype = None @@ -453,40 +269,6 @@ if _available: _dll.openmc_cell_set_temperature.restype = c_int _dll.openmc_cell_set_temperature.errcheck = _error_handler - # Material functions - _dll.openmc_get_material.argtypes = [c_int32, POINTER(c_int32)] - _dll.openmc_get_material.restype = c_int - _dll.openmc_get_material.errcheck = _error_handler - _dll.openmc_material_add_nuclide.argtypes = [ - c_int32, c_char_p, c_double] - _dll.openmc_material_add_nuclide.restype = c_int - _dll.openmc_material_add_nuclide.errcheck = _error_handler - _dll.openmc_material_id.argtypes = [c_int32, POINTER(c_int32)] - _dll.openmc_material_id.restype = c_int - _dll.openmc_material_id.errcheck = _error_handler - _dll.openmc_material_get_densities.argtypes = [ - c_int32, _int_array, _double_array, POINTER(c_int)] - _dll.openmc_material_get_densities.restype = c_int - _dll.openmc_material_get_densities.errcheck = _error_handler - _dll.openmc_material_set_density.argtypes = [c_int32, c_double] - _dll.openmc_material_set_density.restype = c_int - _dll.openmc_material_set_density.errcheck = _error_handler - _dll.openmc_material_set_densities.argtypes = [ - c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] - _dll.openmc_material_set_densities.restype = c_int - _dll.openmc_material_set_densities.errcheck = _error_handler - - # Nuclide functions - _dll.openmc_get_nuclide.argtypes = [c_char_p, POINTER(c_int)] - _dll.openmc_get_nuclide.restype = c_int - _dll.openmc_get_nuclide.errcheck = _error_handler - _dll.openmc_load_nuclide.argtypes = [c_char_p] - _dll.openmc_load_nuclide.restype = c_int - _dll.openmc_load_nuclide.errcheck = _error_handler - _dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] - _dll.openmc_nuclide_name.restype = c_int - _dll.openmc_nuclide_name.errcheck = _error_handler - _dll.openmc_plot_geometry.restype = None _dll.openmc_run.restype = None _dll.openmc_reset.restype = None @@ -496,3 +278,6 @@ if _available: c_int32, _double_array, POINTER(_int3)] _dll.openmc_tally_results.restype = c_int _dll.openmc_tally_results.errcheck = _error_handler + + from .nuclide import * + from .material import * diff --git a/openmc/capi/material.py b/openmc/capi/material.py new file mode 100644 index 0000000000..1863957882 --- /dev/null +++ b/openmc/capi/material.py @@ -0,0 +1,147 @@ +from collections import Mapping +from ctypes import c_int, c_int32, c_double, c_char_p, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from openmc.capi import _dll, _error_handler, NuclideView + +__all__ = ['MaterialView', 'materials'] + + +# Material functions +_dll.openmc_get_material.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_get_material.restype = c_int +_dll.openmc_get_material.errcheck = _error_handler +_dll.openmc_material_add_nuclide.argtypes = [ + c_int32, c_char_p, c_double] +_dll.openmc_material_add_nuclide.restype = c_int +_dll.openmc_material_add_nuclide.errcheck = _error_handler +_dll.openmc_material_id.argtypes = [c_int32, POINTER(c_int32)] +_dll.openmc_material_id.restype = c_int +_dll.openmc_material_id.errcheck = _error_handler +_dll.openmc_material_get_densities.argtypes = [ + c_int32, POINTER(POINTER(c_int)), POINTER(POINTER(c_double)), + POINTER(c_int)] +_dll.openmc_material_get_densities.restype = c_int +_dll.openmc_material_get_densities.errcheck = _error_handler +_dll.openmc_material_set_density.argtypes = [c_int32, c_double] +_dll.openmc_material_set_density.restype = c_int +_dll.openmc_material_set_density.errcheck = _error_handler +_dll.openmc_material_set_densities.argtypes = [ + c_int32, c_int, POINTER(c_char_p), POINTER(c_double)] +_dll.openmc_material_set_densities.restype = c_int +_dll.openmc_material_set_densities.errcheck = _error_handler + + +class MaterialView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def id(self): + mat_id = c_int32() + _dll.openmc_material_id(self._index, mat_id) + return mat_id.value + + @property + def nuclides(self): + return self._get_densities()[0] + return nuclides + + @property + def densities(self): + return self._get_densities()[1] + + def _get_densities(self): + """Get atom densities in a material. + + Returns + ------- + list of string + List of nuclide names + numpy.ndarray + Array of densities in atom/b-cm + + """ + # Allocate memory for arguments that are written to + nuclides = POINTER(c_int)() + densities = POINTER(c_double)() + n = c_int() + + # Get nuclide names and densities + _dll.openmc_material_get_densities(self._index, nuclides, densities, n) + + # Convert to appropriate types and return + nuclide_list = [NuclideView(nuclides[i]).name for i in range(n.value)] + density_array = as_array(densities, (n.value,)) + return nuclide_list, density_array + + def add_nuclide(name, density): + """Add a nuclide to a material. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_add_nuclide(self._index, name.encode(), density) + + def set_density(self, density): + """Set density of a material. + + Parameters + ---------- + density : float + Density in atom/b-cm + + """ + _dll.openmc_material_set_density(self._index, density) + + def set_densities(self, nuclides, densities): + """Set the densities of a list of nuclides in a material + + Parameters + ---------- + nuclides : iterable of str + Nuclide names + densities : iterable of float + Corresponding densities in atom/b-cm + + """ + # Convert strings to an array of char* + nucs = (c_char_p * len(nuclides))() + nucs[:] = [x.encode() for x in nuclides] + + # Get numpy array as a double* + d = np.asarray(densities) + dp = d.ctypes.data_as(POINTER(c_double)) + + _dll.openmc_material_set_densities(self._index, len(nuclides), nucs, dp) + + +class _MaterialMapping(Mapping): + def __getitem__(self, key): + index = c_int32() + _dll.openmc_get_material(key, index) + return MaterialView(index.value) + + def __iter__(self): + for i in range(len(self)): + yield MaterialView(i + 1).id + + def __len__(self): + return c_int32.in_dll(_dll, 'n_materials').value + +materials = _MaterialMapping() diff --git a/openmc/capi/nuclide.py b/openmc/capi/nuclide.py new file mode 100644 index 0000000000..7a2f7a37b4 --- /dev/null +++ b/openmc/capi/nuclide.py @@ -0,0 +1,86 @@ +from collections import Mapping +from ctypes import c_int, c_char_p, POINTER +from weakref import WeakValueDictionary + +import numpy as np +from numpy.ctypeslib import as_array + +from openmc.capi import _dll, _error_handler + + +__all__ = ['NuclideView', 'nuclides', 'load_nuclide'] + +# Nuclide functions +_dll.openmc_get_nuclide.argtypes = [c_char_p, POINTER(c_int)] +_dll.openmc_get_nuclide.restype = c_int +_dll.openmc_get_nuclide.errcheck = _error_handler +_dll.openmc_load_nuclide.argtypes = [c_char_p] +_dll.openmc_load_nuclide.restype = c_int +_dll.openmc_load_nuclide.errcheck = _error_handler +_dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)] +_dll.openmc_nuclide_name.restype = c_int +_dll.openmc_nuclide_name.errcheck = _error_handler + + +def load_nuclide(name): + """Load cross section data for a nuclide. + + Parameters + ---------- + name : str + Name of nuclide, e.g. 'U235' + + """ + _dll.openmc_load_nuclide(name.encode()) + + +class NuclideView(object): + __instances = WeakValueDictionary() + def __new__(cls, *args): + if args not in cls.__instances: + instance = super().__new__(cls) + cls.__instances[args] = instance + return cls.__instances[args] + + def __init__(self, index): + self._index = index + + @property + def name(self): + """Name of nuclide with given index + + Parameter + --------- + index : int + Index in internal nuclides array + + Returns + ------- + str + Name of nuclide + + """ + name = c_char_p() + _dll.openmc_nuclide_name(self._index, name) + + # Find blank in name + i = 0 + while name.value[i:i+1] != b' ': + i += 1 + return name.value[:i].decode() + + +class _NuclideMapping(Mapping): + def __getitem__(self, key): + index = c_int() + _dll.openmc_get_nuclide(key.encode(), index) + return NuclideView(index) + + def __iter__(self): + for i in range(len(self)): + yield NuclideView(i + 1).name + + def __len__(self): + return c_int.in_dll(_dll, 'n_nuclides').value + +nuclides = _NuclideMapping()