mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-23 19:45:34 -04:00
339 lines
9.2 KiB
Python
339 lines
9.2 KiB
Python
from contextlib import contextmanager
|
|
from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, c_char,
|
|
POINTER, Structure, c_void_p, create_string_buffer)
|
|
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
|
|
import openmc.capi
|
|
|
|
|
|
class _Bank(Structure):
|
|
_fields_ = [('wgt', c_double),
|
|
('xyz', c_double*3),
|
|
('uvw', c_double*3),
|
|
('E', c_double),
|
|
('delayed_group', c_int)]
|
|
|
|
|
|
_dll.openmc_calculate_volumes.restype = c_int
|
|
_dll.openmc_calculate_volumes.errcheck = _error_handler
|
|
_dll.openmc_finalize.restype = c_int
|
|
_dll.openmc_finalize.errcheck = _error_handler
|
|
_dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32),
|
|
POINTER(c_int32)]
|
|
_dll.openmc_find.restype = c_int
|
|
_dll.openmc_find.errcheck = _error_handler
|
|
_dll.openmc_hard_reset.restype = c_int
|
|
_dll.openmc_hard_reset.errcheck = _error_handler
|
|
_dll.openmc_init.argtypes = [c_int, POINTER(POINTER(c_char)), c_void_p]
|
|
_dll.openmc_init.restype = c_int
|
|
_dll.openmc_init.errcheck = _error_handler
|
|
_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 = c_int
|
|
_dll.openmc_plot_geometry.restype = _error_handler
|
|
_dll.openmc_run.restype = c_int
|
|
_dll.openmc_run.errcheck = _error_handler
|
|
_dll.openmc_reset.restype = c_int
|
|
_dll.openmc_reset.errcheck = _error_handler
|
|
_dll.openmc_source_bank.argtypes = [POINTER(POINTER(_Bank)), POINTER(c_int64)]
|
|
_dll.openmc_source_bank.restype = c_int
|
|
_dll.openmc_source_bank.errcheck = _error_handler
|
|
_dll.openmc_simulation_init.restype = c_int
|
|
_dll.openmc_simulation_init.errcheck = _error_handler
|
|
_dll.openmc_simulation_finalize.restype = c_int
|
|
_dll.openmc_simulation_finalize.errcheck = _error_handler
|
|
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p)]
|
|
_dll.openmc_statepoint_write.restype = c_int
|
|
_dll.openmc_statepoint_write.errcheck = _error_handler
|
|
|
|
|
|
def calculate_volumes():
|
|
"""Run stochastic volume calculation"""
|
|
_dll.openmc_calculate_volumes()
|
|
|
|
|
|
def finalize():
|
|
"""Finalize simulation and free memory"""
|
|
_dll.openmc_finalize()
|
|
|
|
|
|
def find_cell(xyz):
|
|
"""Find the cell at a given point
|
|
|
|
Parameters
|
|
----------
|
|
xyz : iterable of float
|
|
Cartesian coordinates of position
|
|
|
|
Returns
|
|
-------
|
|
openmc.capi.Cell
|
|
Cell containing the point
|
|
int
|
|
If the cell at the given point is repeated in the geometry, this
|
|
indicates which instance it is, i.e., 0 would be the first instance.
|
|
|
|
"""
|
|
uid = c_int32()
|
|
instance = c_int32()
|
|
_dll.openmc_find((c_double*3)(*xyz), 1, uid, instance)
|
|
return openmc.capi.cells[uid.value], instance.value
|
|
|
|
|
|
def find_material(xyz):
|
|
"""Find the material at a given point
|
|
|
|
Parameters
|
|
----------
|
|
xyz : iterable of float
|
|
Cartesian coordinates of position
|
|
|
|
Returns
|
|
-------
|
|
openmc.capi.Material or None
|
|
Material containing the point, or None is no material is found
|
|
|
|
"""
|
|
uid = c_int32()
|
|
instance = c_int32()
|
|
_dll.openmc_find((c_double*3)(*xyz), 2, uid, instance)
|
|
return openmc.capi.materials[uid.value] if uid != 0 else None
|
|
|
|
|
|
def hard_reset():
|
|
"""Reset tallies, timers, and pseudo-random number generator state."""
|
|
_dll.openmc_hard_reset()
|
|
|
|
|
|
def init(args=None, intracomm=None):
|
|
"""Initialize OpenMC
|
|
|
|
Parameters
|
|
----------
|
|
args : list of str
|
|
Command-line arguments
|
|
intracomm : mpi4py.MPI.Intracomm or None
|
|
MPI intracommunicator
|
|
|
|
"""
|
|
if args is not None:
|
|
args = ['openmc'] + list(args)
|
|
argc = len(args)
|
|
|
|
# Create the argv array. Note that it is actually expected to be of
|
|
# length argc + 1 with the final item being a null pointer.
|
|
argv = (POINTER(c_char) * (argc + 1))()
|
|
for i, arg in enumerate(args):
|
|
argv[i] = create_string_buffer(arg.encode())
|
|
else:
|
|
argc = 0
|
|
argv = None
|
|
|
|
if intracomm is not None:
|
|
# If an mpi4py communicator was passed, convert it to void* to be passed
|
|
# to openmc_init
|
|
try:
|
|
from mpi4py import MPI
|
|
except ImportError:
|
|
intracomm = None
|
|
else:
|
|
address = MPI._addressof(intracomm)
|
|
intracomm = c_void_p(address)
|
|
|
|
_dll.openmc_init(argc, argv, intracomm)
|
|
|
|
|
|
def iter_batches():
|
|
"""Iterator over batches.
|
|
|
|
This function returns a generator-iterator that allows Python code to be run
|
|
between batches in an OpenMC simulation. It should be used in conjunction
|
|
with :func:`openmc.capi.simulation_init` and
|
|
:func:`openmc.capi.simulation_finalize`. For example:
|
|
|
|
.. code-block:: Python
|
|
|
|
with openmc.capi.run_in_memory():
|
|
openmc.capi.simulation_init()
|
|
for _ in openmc.capi.iter_batches():
|
|
# Look at convergence of tallies, for example
|
|
...
|
|
openmc.capi.simulation_finalize()
|
|
|
|
See Also
|
|
--------
|
|
openmc.capi.next_batch
|
|
|
|
"""
|
|
while True:
|
|
# Run next batch
|
|
status = next_batch()
|
|
|
|
# Provide opportunity for user to perform action between batches
|
|
yield
|
|
|
|
# End the iteration
|
|
if status != 0:
|
|
break
|
|
|
|
|
|
def keff():
|
|
"""Return the calculated k-eigenvalue and its standard deviation.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
Mean k-eigenvalue and standard deviation of the mean
|
|
|
|
"""
|
|
n = openmc.capi.num_realizations()
|
|
if n > 3:
|
|
# Use the combined estimator if there are enough realizations
|
|
k = (c_double*2)()
|
|
_dll.openmc_get_keff(k)
|
|
return tuple(k)
|
|
else:
|
|
# Otherwise, return the tracklength estimator
|
|
mean = c_double.in_dll(_dll, 'openmc_keff').value
|
|
std_dev = c_double.in_dll(_dll, 'openmc_keff_std').value \
|
|
if n > 1 else np.inf
|
|
return (mean, std_dev)
|
|
|
|
|
|
def next_batch():
|
|
"""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():
|
|
"""Plot geometry"""
|
|
_dll.openmc_plot_geometry()
|
|
|
|
|
|
def reset():
|
|
"""Reset tallies and timers."""
|
|
_dll.openmc_reset()
|
|
|
|
|
|
def run():
|
|
"""Run simulation"""
|
|
_dll.openmc_run()
|
|
|
|
|
|
def simulation_init():
|
|
"""Initialize simulation"""
|
|
_dll.openmc_simulation_init()
|
|
|
|
|
|
def simulation_finalize():
|
|
"""Finalize simulation"""
|
|
_dll.openmc_simulation_finalize()
|
|
|
|
|
|
def source_bank():
|
|
"""Return source bank as NumPy array
|
|
|
|
Returns
|
|
-------
|
|
numpy.ndarray
|
|
Source sites
|
|
|
|
"""
|
|
# Get pointer to source bank
|
|
ptr = POINTER(_Bank)()
|
|
n = c_int64()
|
|
_dll.openmc_source_bank(ptr, n)
|
|
|
|
# Convert to numpy array with appropriate datatype
|
|
bank_dtype = np.dtype(_Bank)
|
|
return as_array(ptr, (n.value,)).view(bank_dtype)
|
|
|
|
|
|
def statepoint_write(filename=None):
|
|
"""Write a statepoint file.
|
|
|
|
Parameters
|
|
----------
|
|
filename : str or None
|
|
Path to the statepoint to write. If None is passed, a default name that
|
|
contains the current batch will be written.
|
|
|
|
"""
|
|
if filename is not None:
|
|
filename = c_char_p(filename.encode())
|
|
_dll.openmc_statepoint_write(filename)
|
|
|
|
|
|
@contextmanager
|
|
def run_in_memory(intracomm=None):
|
|
"""Provides context manager for calling OpenMC shared library functions.
|
|
|
|
This function is intended to be used in a 'with' statement and ensures that
|
|
OpenMC is properly initialized/finalized. At the completion of the 'with'
|
|
block, all memory that was allocated during the block is freed. For
|
|
example::
|
|
|
|
with openmc.capi.run_in_memory():
|
|
for i in range(n_iters):
|
|
openmc.capi.reset()
|
|
do_stuff()
|
|
openmc.capi.run()
|
|
|
|
Parameters
|
|
----------
|
|
intracomm : mpi4py.MPI.Intracomm or None
|
|
MPI intracommunicator
|
|
|
|
"""
|
|
init(intracomm)
|
|
try:
|
|
yield
|
|
finally:
|
|
finalize()
|
|
|
|
|
|
class _DLLGlobal(object):
|
|
"""Data descriptor that exposes global variables from libopenmc."""
|
|
def __init__(self, ctype, name):
|
|
self.ctype = ctype
|
|
self.name = name
|
|
|
|
def __get__(self, instance, owner):
|
|
return self.ctype.in_dll(_dll, self.name).value
|
|
|
|
def __set__(self, instance, value):
|
|
self.ctype.in_dll(_dll, self.name).value = value
|
|
|
|
|
|
class _FortranObject(object):
|
|
def __repr__(self):
|
|
return "{}[{}]".format(type(self).__name__, self._index)
|
|
|
|
|
|
class _FortranObjectWithID(_FortranObject):
|
|
def __init__(self, uid=None, new=True, index=None):
|
|
# Creating the object has already been handled by __new__. In the
|
|
# initializer, all we do is make sure that the object returned has an ID
|
|
# assigned. If the array index of the object is out of bounds, an
|
|
# OutOfBoundsError will be raised here by virtue of referencing self.id
|
|
self.id
|