From 42e329cd444e18f1cf9640f027d597fe6ced8219 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 14 Aug 2026 18:02:58 -0500 Subject: [PATCH] Replace exported settings globals with typed accessors --- CMakeLists.txt | 1 + docs/source/capi/index.rst | 28 ++++- include/openmc/capi.h | 97 ++++++++++++++++ include/openmc/geometry.h | 4 +- include/openmc/settings.h | 49 ++++---- include/openmc/simulation.h | 36 +++--- include/openmc/tallies/tally.h | 2 +- openmc/lib/__init__.py | 4 +- openmc/lib/core.py | 16 +-- openmc/lib/settings.py | 103 +++++++++++++---- openmc/lib/tally.py | 3 +- src/geometry.cpp | 5 + src/initialize.cpp | 1 - src/main.cpp | 67 +---------- src/openmc.cpp | 75 +++++++++++++ src/settings.cpp | 199 ++++++++++++++++++++++++++++++++- src/simulation.cpp | 5 + src/state_point.cpp | 2 +- src/tallies/tally.cpp | 5 + tests/unit_tests/test_lib.py | 40 +++++++ 20 files changed, 580 insertions(+), 162 deletions(-) create mode 100644 src/openmc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 62c2ac8a151..74263a6b929 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,6 +409,7 @@ list(APPEND libopenmc_SOURCES src/ncrystal_interface.cpp src/ncrystal_load.cpp src/nuclide.cpp + src/openmc.cpp src/output.cpp src/particle.cpp src/particle_data.cpp diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index e924aa6e2a9..87ada334d13 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -5,10 +5,10 @@ C/C++ API ========= The libopenmc shared library that is built when installing OpenMC exports a -number of C interoperable functions and global variables that can be used for -in-memory coupling. While it is possible to directly use the C/C++ API as -documented here for coupling, most advanced users will find it easier to work -with the Python bindings in the :py:mod:`openmc.lib` module. +number of C interoperable functions that can be used for in-memory coupling. +While it is possible to directly use the C/C++ API as documented here for +coupling, most advanced users will find it easier to work with the Python +bindings in the :py:mod:`openmc.lib` module. .. warning:: The C/C++ API is still experimental and may undergo substantial changes in future releases. @@ -329,6 +329,8 @@ Functions :return: Return status (negative if an error occurs) :rtype: int +.. doxygenfunction:: openmc_main + .. c:function:: int openmc_material_add_nuclide(int32_t index, const char name[], double density) Add a nuclide to an existing material. If the nuclide already exists, the @@ -865,6 +867,24 @@ Functions :return: Return status (negative if an error occurred) :rtype: int +.. doxygenfunction:: openmc_setting_get_bool + +.. doxygenfunction:: openmc_setting_get_double + +.. doxygenfunction:: openmc_setting_get_int32 + +.. doxygenfunction:: openmc_setting_get_int64 + +.. doxygenfunction:: openmc_setting_get_string + +.. doxygenfunction:: openmc_setting_set_bool + +.. doxygenfunction:: openmc_setting_set_double + +.. doxygenfunction:: openmc_setting_set_int32 + +.. doxygenfunction:: openmc_setting_set_int64 + .. c:function:: int openmc_simulation_finalize() Finalize a simulation. diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 7fb736a4f10..20be2b20ef5 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -108,6 +108,16 @@ void openmc_get_tally_next_id(int32_t* id); int openmc_global_tallies(double** ptr); int openmc_hard_reset(); int openmc_init(int argc, char* argv[], const void* intracomm); + +//! Run OpenMC as a command-line application. +//! +//! This function initializes OpenMC, executes the requested run mode, and +//! finalizes the library. +//! \param argc Number of command-line arguments (including command) +//! \param argv Command-line arguments +//! \return Exit status +int openmc_main(int argc, char* argv[]); + bool openmc_is_statepoint_batch(); int openmc_legendre_filter_get_order(int32_t index, int* order); int openmc_legendre_filter_set_order(int32_t index, int order); @@ -361,6 +371,93 @@ int openmc_properties_import(const char* filename); //! \return Error code int openmc_get_feature_enabled(const char* feature, bool* enabled); +// Simulation state + +//! Get the current batch number. +//! +//! \return Current batch number +int openmc_get_current_batch(); + +//! Get the number of coordinate levels in the geometry. +//! +//! \return Number of coordinate levels +int openmc_get_n_coord_levels(); + +//! Get the number of realizations in the global tally results. +//! +//! \return Number of realizations +int32_t openmc_get_n_realizations(); + +//! Determine whether the current process is the master process. +//! +//! \return True if the current process is the master process +bool openmc_master(); + +// Settings + +//! Get a boolean setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_get_bool(const char* name, bool* value); + +//! Get a double-precision floating-point setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_get_double(const char* name, double* value); + +//! Get a 32-bit integer setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_get_int32(const char* name, int32_t* value); + +//! Get a 64-bit integer setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_get_int64(const char* name, int64_t* value); + +//! Get a string setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_get_string(const char* name, const char** value); + +//! Set a boolean setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_set_bool(const char* name, bool value); + +//! Set a double-precision floating-point setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_set_double(const char* name, double value); + +//! Set a 32-bit integer setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_set_int32(const char* name, int32_t value); + +//! Set a 64-bit integer setting. +//! +//! \param name Name of the setting +//! \param value Value of the setting +//! \return Status (negative if an error occurred) +int openmc_setting_set_int64(const char* name, int64_t value); + //! Return the message associated with the most recent C API error. //! //! The returned pointer is valid until the next error message is set. diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index 314e87dafe9..e8504d48261 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -50,8 +50,8 @@ struct OverlapKeyHash { namespace model { -extern int root_universe; //!< Index of root universe -extern "C" int n_coord_levels; //!< Number of CSG coordinate levels +extern int root_universe; //!< Index of root universe +extern int n_coord_levels; //!< Number of CSG coordinate levels extern vector overlap_check_count; diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 3ad5f502c95..cc58537ee76 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -64,24 +64,23 @@ extern bool confidence_intervals; //!< use confidence intervals for results? extern bool create_fission_neutrons; //!< create fission neutrons (fixed source)? extern bool create_delayed_neutrons; //!< create delayed fission neutrons? -extern "C" bool cmfd_run; //!< is a CMFD run? +extern bool cmfd_run; //!< is a CMFD run? extern bool - delayed_photon_scaling; //!< Scale fission photon yield to include delayed -extern "C" bool entropy_on; //!< calculate Shannon entropy? -extern "C" bool - event_based; //!< use event-based mode (instead of history-based) -extern bool ifp_on; //!< Use IFP for kinetics parameters? + delayed_photon_scaling; //!< Scale fission photon yield to include delayed +extern bool entropy_on; //!< calculate Shannon entropy? +extern bool event_based; //!< use event-based mode (instead of history-based) +extern bool ifp_on; //!< Use IFP for kinetics parameters? extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? extern bool material_cell_offsets; //!< create material cells offsets? -extern "C" bool output_summary; //!< write summary.h5? +extern bool output_summary; //!< write summary.h5? extern bool output_tallies; //!< write tallies.out? extern bool particle_restart_run; //!< particle restart run? -extern "C" bool photon_transport; //!< photon transport turned on? +extern bool photon_transport; //!< photon transport turned on? extern bool atomic_relaxation; //!< atomic relaxation enabled? -extern "C" bool reduce_tallies; //!< reduce tallies at end of batch? +extern bool reduce_tallies; //!< reduce tallies at end of batch? extern bool res_scat_on; //!< use resonance upscattering method? -extern "C" bool restart_run; //!< restart run? -extern "C" bool run_CE; //!< run with continuous-energy data? +extern bool restart_run; //!< restart run? +extern bool run_CE; //!< run with continuous-energy data? extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? @@ -92,14 +91,14 @@ extern bool surf_source_read; //!< read surface source file? extern bool survival_biasing; //!< use survival biasing? extern bool survival_normalization; //!< use survival normalization? extern bool temperature_multipole; //!< use multipole data? -extern "C" bool trigger_on; //!< tally triggers enabled? +extern bool trigger_on; //!< tally triggers enabled? extern bool trigger_predict; //!< predict batches for triggers? extern bool uniform_source_sampling; //!< sample sources uniformly? extern bool ufs_on; //!< uniform fission site method on? extern bool urr_ptables_on; //!< use unresolved resonance prob. tables? extern bool use_decay_photons; //!< use decay photons for D1S extern bool use_shared_secondary_bank; //!< Use shared bank for secondaries -extern "C" bool weight_windows_on; //!< are weight windows are enabled? +extern bool weight_windows_on; //!< are weight windows are enabled? extern bool weight_window_checkpoint_surface; //!< enable weight window check //!< upon surface crossing? extern bool weight_window_checkpoint_collision; //!< enable weight window check @@ -119,21 +118,15 @@ extern std::string weight_windows_file; //!< Location of weight window file to extern std::string properties_file; //!< Location of properties file to //!< load on simulation initialization -// This is required because the c_str() may not be the first thing in -// std::string. Sometimes it is, but it seems libc++ may not be like that -// on some computers, like the intel Mac. -extern "C" const char* path_statepoint_c; //!< C pointer to statepoint file name - -extern "C" int32_t n_inactive; //!< number of inactive batches -extern "C" int32_t max_lost_particles; //!< maximum number of lost particles -extern "C" double +extern int32_t n_inactive; //!< number of inactive batches +extern int32_t max_lost_particles; //!< maximum number of lost particles +extern double rel_max_lost_particles; //!< maximum number of lost particles, relative to the //!< total number of particles -extern "C" int32_t - max_write_lost_particles; //!< maximum number of lost particles - //!< to be written to files -extern "C" int32_t gen_per_batch; //!< number of generations per batch -extern "C" int64_t n_particles; //!< number of particles per generation +extern int32_t max_write_lost_particles; //!< maximum number of lost particles + //!< to be written to files +extern int32_t gen_per_batch; //!< number of generations per batch +extern int64_t n_particles; //!< number of particles per generation extern int64_t max_particles_in_flight; //!< Max num. event-based particles in flight @@ -160,7 +153,7 @@ extern double res_scat_energy_min; //!< Min energy in [eV] for res. upscattering extern double res_scat_energy_max; //!< Max energy in [eV] for res. upscattering extern vector res_scat_nuclides; //!< Nuclides using res. upscattering treatment -extern "C" RunMode run_mode; //!< Run mode (eigenvalue, fixed src, etc.) +extern RunMode run_mode; //!< Run mode (eigenvalue, fixed src, etc.) extern SolverType solver_type; //!< Solver Type (Monte Carlo or Random Ray) extern std::unordered_set sourcepoint_batch; //!< Batches when source should be written @@ -200,7 +193,7 @@ extern int64_t trace_particle; //!< Particle ID to enable trace on extern vector> track_identifiers; //!< Particle numbers for writing tracks extern int trigger_batch_interval; //!< Batch interval for triggers -extern "C" int verbosity; //!< How verbose to make output +extern int verbosity; //!< How verbose to make output extern double weight_cutoff; //!< Weight cutoff for Russian roulette extern double weight_survive; //!< Survival weight after Russian roulette diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index 454752cd271..b5c26d807d6 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -22,26 +22,24 @@ constexpr int STATUS_EXIT_ON_TRIGGER {2}; namespace simulation { -extern int ct_current_file; //!< current collision track file index -extern "C" int current_batch; //!< current batch -extern "C" int current_gen; //!< current fission generation -extern "C" bool initialized; //!< has simulation been initialized? -extern "C" double keff; //!< average k over batches -extern "C" double keff_std; //!< standard deviation of average k -extern "C" double k_col_abs; //!< sum over batches of k_collision * k_absorption -extern "C" double - k_col_tra; //!< sum over batches of k_collision * k_tracklength -extern "C" double - k_abs_tra; //!< sum over batches of k_absorption * k_tracklength +extern int ct_current_file; //!< current collision track file index +extern int current_batch; //!< current batch +extern int current_gen; //!< current fission generation +extern bool initialized; //!< has simulation been initialized? +extern double keff; //!< average k over batches +extern double keff_std; //!< standard deviation of average k +extern double k_col_abs; //!< sum over batches of k_collision * k_absorption +extern double k_col_tra; //!< sum over batches of k_collision * k_tracklength +extern double k_abs_tra; //!< sum over batches of k_absorption * k_tracklength extern double log_spacing; //!< lethargy spacing for energy grid searches -extern "C" int n_lost_particles; //!< cumulative number of lost particles -extern "C" bool need_depletion_rx; //!< need to calculate depletion rx? -extern "C" int restart_batch; //!< batch at which a restart job resumed -extern "C" bool satisfy_triggers; //!< have tally triggers been satisfied? -extern int ssw_current_file; //!< current surface source file -extern "C" int total_gen; //!< total number of generations simulated -extern double total_weight; //!< Total source weight in a batch -extern int64_t work_per_rank; //!< number of particles per MPI rank +extern int n_lost_particles; //!< cumulative number of lost particles +extern bool need_depletion_rx; //!< need to calculate depletion rx? +extern int restart_batch; //!< batch at which a restart job resumed +extern bool satisfy_triggers; //!< have tally triggers been satisfied? +extern int ssw_current_file; //!< current surface source file +extern int total_gen; //!< total number of generations simulated +extern double total_weight; //!< Total source weight in a batch +extern int64_t work_per_rank; //!< number of particles per MPI rank extern const RegularMesh* entropy_mesh; extern const RegularMesh* ufs_mesh; diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index ae604b732de..f9affa16736 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -222,7 +222,7 @@ namespace simulation { extern tensor::StaticTensor2D global_tallies; //! Number of realizations for global tallies -extern "C" int32_t n_realizations; +extern int32_t n_realizations; } // namespace simulation extern double global_tally_absorption; diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 4d79622447a..fa793768ff7 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -42,6 +42,8 @@ _dll.openmc_get_feature_enabled.restype = c_int _dll.openmc_get_feature_enabled.errcheck = _error_handler +_dll.openmc_get_n_coord_levels.restype = c_int + def feature_enabled(feature: str) -> bool: """Return whether OpenMC was built with an optional feature. @@ -67,7 +69,7 @@ def feature_enabled(feature: str) -> bool: def _coord_levels(): - return c_int.in_dll(_dll, "n_coord_levels").value + return _dll.openmc_get_n_coord_levels() from .error import * diff --git a/openmc/lib/core.py b/openmc/lib/core.py index 22580d52a46..72901008712 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -62,6 +62,7 @@ class _SourceSite(Structure): _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_get_current_batch.restype = c_int _dll.openmc_initialize_mesh_egrid.argtypes = [ c_int, _array_1d_int, c_double ] @@ -158,7 +159,7 @@ def current_batch(): Current batch of the simulation """ - return c_int.in_dll(_dll, 'current_batch').value + return _dll.openmc_get_current_batch() def export_properties(filename=None, output=True): @@ -759,19 +760,6 @@ def __exit__(self, exc_type, exc_value, traceback): self._tmp_dir.cleanup() -class _DLLGlobal: - """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: def __repr__(self): return f"<{type(self).__name__}(index={self._index})>" diff --git a/openmc/lib/settings.py b/openmc/lib/settings.py index 4fba8d48b6e..8cfbad3a469 100644 --- a/openmc/lib/settings.py +++ b/openmc/lib/settings.py @@ -1,7 +1,8 @@ -from ctypes import c_int, c_int32, c_int64, c_double, c_char_p, c_bool, POINTER +from ctypes import ( + byref, c_bool, c_char_p, c_double, c_int, c_int32, c_int64, POINTER +) from . import _dll -from .core import _DLLGlobal from .error import _error_handler _RUN_MODES = {1: 'fixed source', @@ -22,26 +23,86 @@ _dll.openmc_set_n_batches.errcheck = _error_handler +_SETTING_ACCESSORS = {} +for _suffix, _ctype in ( + ('bool', c_bool), + ('int32', c_int32), + ('int64', c_int64), + ('double', c_double), +): + _getter = getattr(_dll, f'openmc_setting_get_{_suffix}') + _getter.argtypes = [c_char_p, POINTER(_ctype)] + _getter.restype = c_int + _getter.errcheck = _error_handler + + _setter = getattr(_dll, f'openmc_setting_set_{_suffix}') + _setter.argtypes = [c_char_p, _ctype] + _setter.restype = c_int + _setter.errcheck = _error_handler + + _SETTING_ACCESSORS[_ctype] = (_getter, _setter) + +_dll.openmc_setting_get_string.argtypes = [c_char_p, POINTER(c_char_p)] +_dll.openmc_setting_get_string.restype = c_int +_dll.openmc_setting_get_string.errcheck = _error_handler + + +def _get_setting(ctype, name): + value = ctype() + getter, _ = _SETTING_ACCESSORS[ctype] + getter(name.encode(), byref(value)) + return value.value + + +def _set_setting(ctype, name, value): + _, setter = _SETTING_ACCESSORS[ctype] + setter(name.encode(), value) + + +class _DLLFunctionProperty: + """Data descriptor backed by type-specific C API setting functions.""" + + def __init__(self, ctype, name): + self.ctype = ctype + self.name = name + + def __get__(self, instance, owner): + if instance is None: + return self + return _get_setting(self.ctype, self.name) + + def __set__(self, instance, value): + _set_setting(self.ctype, self.name, value) + + class _Settings: # Attributes that are accessed through a descriptor - cmfd_run = _DLLGlobal(c_bool, 'cmfd_run') - entropy_on = _DLLGlobal(c_bool, 'entropy_on') - generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') - inactive = _DLLGlobal(c_int32, 'n_inactive') - max_lost_particles = _DLLGlobal(c_int32, 'max_lost_particles') - need_depletion_rx = _DLLGlobal(c_bool, 'need_depletion_rx') - output_summary = _DLLGlobal(c_bool, 'output_summary') - particles = _DLLGlobal(c_int64, 'n_particles') - rel_max_lost_particles = _DLLGlobal(c_double, 'rel_max_lost_particles') - restart_run = _DLLGlobal(c_bool, 'restart_run') - run_CE = _DLLGlobal(c_bool, 'run_CE') - verbosity = _DLLGlobal(c_int, 'verbosity') - event_based = _DLLGlobal(c_bool, 'event_based') - weight_windows_on = _DLLGlobal(c_bool, 'weight_windows_on') + cmfd_run = _DLLFunctionProperty(c_bool, 'cmfd_run') + entropy_on = _DLLFunctionProperty(c_bool, 'entropy_on') + event_based = _DLLFunctionProperty(c_bool, 'event_based') + generations_per_batch = _DLLFunctionProperty(c_int32, 'gen_per_batch') + inactive = _DLLFunctionProperty(c_int32, 'n_inactive') + max_lost_particles = _DLLFunctionProperty(c_int32, 'max_lost_particles') + max_write_lost_particles = _DLLFunctionProperty( + c_int32, 'max_write_lost_particles' + ) + need_depletion_rx = _DLLFunctionProperty(c_bool, 'need_depletion_rx') + output_summary = _DLLFunctionProperty(c_bool, 'output_summary') + particles = _DLLFunctionProperty(c_int64, 'n_particles') + photon_transport = _DLLFunctionProperty(c_bool, 'photon_transport') + rel_max_lost_particles = _DLLFunctionProperty( + c_double, 'rel_max_lost_particles' + ) + reduce_tallies = _DLLFunctionProperty(c_bool, 'reduce_tallies') + restart_run = _DLLFunctionProperty(c_bool, 'restart_run') + run_CE = _DLLFunctionProperty(c_bool, 'run_ce') + trigger_on = _DLLFunctionProperty(c_bool, 'trigger_on') + verbosity = _DLLFunctionProperty(c_int32, 'verbosity') + weight_windows_on = _DLLFunctionProperty(c_bool, 'weight_windows_on') @property def run_mode(self): - i = c_int.in_dll(_dll, 'run_mode').value + i = _get_setting(c_int32, 'run_mode') try: return _RUN_MODES[i] except KeyError: @@ -49,18 +110,18 @@ def run_mode(self): @run_mode.setter def run_mode(self, mode): - current_idx = c_int.in_dll(_dll, 'run_mode') for idx, mode_value in _RUN_MODES.items(): if mode_value == mode: - current_idx.value = idx + _set_setting(c_int32, 'run_mode', idx) break else: raise ValueError(f'Invalid run mode: {mode}') @property def path_statepoint(self): - path = c_char_p.in_dll(_dll, 'path_statepoint_c').value - return path.decode() + path = c_char_p() + _dll.openmc_setting_get_string(b'path_statepoint', byref(path)) + return path.value.decode() @property def seed(self): diff --git a/openmc/lib/tally.py b/openmc/lib/tally.py index c17b16597f9..a4c7b05df21 100644 --- a/openmc/lib/tally.py +++ b/openmc/lib/tally.py @@ -26,6 +26,7 @@ _dll.openmc_global_tallies.argtypes = [POINTER(POINTER(c_double))] _dll.openmc_global_tallies.restype = c_int _dll.openmc_global_tallies.errcheck = _error_handler +_dll.openmc_get_n_realizations.restype = c_int32 _dll.openmc_tally_get_active.argtypes = [c_int32, POINTER(c_bool)] _dll.openmc_tally_get_active.restype = c_int _dll.openmc_tally_get_active.errcheck = _error_handler @@ -152,7 +153,7 @@ def global_tallies(): def num_realizations(): """Number of realizations of global tallies.""" - return c_int32.in_dll(_dll, 'n_realizations').value + return _dll.openmc_get_n_realizations() class Tally(_FortranObjectWithID): diff --git a/src/geometry.cpp b/src/geometry.cpp index 687be57fee9..c694e7e46f6 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -533,6 +533,11 @@ extern "C" int openmc_find_cell( return 0; } +extern "C" int openmc_get_n_coord_levels() +{ + return model::n_coord_levels; +} + extern "C" int openmc_global_bounding_box(double* llc, double* urc) { auto bbox = model::universes.at(model::root_universe)->bounding_box(); diff --git a/src/initialize.cpp b/src/initialize.cpp index 33dfeca0e51..b154cfecb03 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -275,7 +275,6 @@ int parse_command_line(int argc, char* argv[]) // Set path and flag for type of run if (filetype == "statepoint") { settings::path_statepoint = argv[i]; - settings::path_statepoint_c = settings::path_statepoint.c_str(); settings::restart_run = true; } else if (filetype == "particle restart") { settings::path_particle_restart = argv[i]; diff --git a/src/main.cpp b/src/main.cpp index 87a493882d9..f20e5e3b581 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,71 +1,6 @@ -#ifdef OPENMC_MPI -#include -#endif #include "openmc/capi.h" -#include "openmc/constants.h" -#include "openmc/error.h" -#include "openmc/message_passing.h" -#include "openmc/particle_restart.h" -#include "openmc/random_ray/random_ray_simulation.h" -#include "openmc/settings.h" int main(int argc, char* argv[]) { - using namespace openmc; - int err; - - // Initialize run -- when run with MPI, pass communicator -#ifdef OPENMC_MPI - MPI_Comm world {MPI_COMM_WORLD}; - err = openmc_init(argc, argv, &world); -#else - err = openmc_init(argc, argv, nullptr); -#endif - if (err == -1) { - // This happens for the -h and -v flags - return 0; - } else if (err) { - fatal_error(openmc_get_err_msg()); - } - - // start problem based on mode - switch (settings::run_mode) { - case RunMode::FIXED_SOURCE: - case RunMode::EIGENVALUE: - switch (settings::solver_type) { - case SolverType::MONTE_CARLO: - err = openmc_run(); - break; - case SolverType::RANDOM_RAY: - openmc_run_random_ray(); - err = 0; - break; - } - break; - case RunMode::PLOTTING: - err = openmc_plot_geometry(); - break; - case RunMode::PARTICLE: - if (mpi::master) - run_particle_restart(); - err = 0; - break; - case RunMode::VOLUME: - err = openmc_calculate_volumes(); - break; - default: - break; - } - if (err) - fatal_error(openmc_get_err_msg()); - - // Finalize and free up memory - err = openmc_finalize(); - if (err) - fatal_error(openmc_get_err_msg()); - - // If MPI is in use and enabled, terminate it -#ifdef OPENMC_MPI - MPI_Finalize(); -#endif + return openmc_main(argc, argv); } diff --git a/src/openmc.cpp b/src/openmc.cpp new file mode 100644 index 00000000000..237fb2f672e --- /dev/null +++ b/src/openmc.cpp @@ -0,0 +1,75 @@ +#include "openmc/capi.h" + +#ifdef OPENMC_MPI +#include +#endif + +#include "openmc/constants.h" +#include "openmc/error.h" +#include "openmc/message_passing.h" +#include "openmc/particle_restart.h" +#include "openmc/random_ray/random_ray_simulation.h" +#include "openmc/settings.h" + +extern "C" int openmc_main(int argc, char* argv[]) +{ + using namespace openmc; + int err; + + // Initialize run -- when run with MPI, pass communicator +#ifdef OPENMC_MPI + MPI_Comm world {MPI_COMM_WORLD}; + err = openmc_init(argc, argv, &world); +#else + err = openmc_init(argc, argv, nullptr); +#endif + if (err == -1) { + // This happens for the -h and -v flags + return 0; + } else if (err) { + fatal_error(get_errmsg()); + } + + // Start problem based on mode + switch (settings::run_mode) { + case RunMode::FIXED_SOURCE: + case RunMode::EIGENVALUE: + switch (settings::solver_type) { + case SolverType::MONTE_CARLO: + err = openmc_run(); + break; + case SolverType::RANDOM_RAY: + openmc_run_random_ray(); + err = 0; + break; + } + break; + case RunMode::PLOTTING: + err = openmc_plot_geometry(); + break; + case RunMode::PARTICLE: + if (mpi::master) + run_particle_restart(); + err = 0; + break; + case RunMode::VOLUME: + err = openmc_calculate_volumes(); + break; + default: + break; + } + if (err) + fatal_error(get_errmsg()); + + // Finalize and free up memory + err = openmc_finalize(); + if (err) + fatal_error(get_errmsg()); + + // If MPI is in use and enabled, terminate it +#ifdef OPENMC_MPI + MPI_Finalize(); +#endif + + return 0; +} diff --git a/src/settings.cpp b/src/settings.cpp index 15a7b9c27c9..423deb3a47a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,8 +1,9 @@ #include "openmc/settings.h" #include "openmc/random_ray/flat_source_domain.h" -#include // for ceil, pow -#include // for numeric_limits +#include // for ceil, pow +#include // for strcmp +#include // for numeric_limits #include #include @@ -96,7 +97,6 @@ std::string path_output; std::string path_particle_restart; std::string path_sourcepoint; std::string path_statepoint; -const char* path_statepoint_c {path_statepoint.c_str()}; std::string weight_windows_file; std::string properties_file; @@ -1357,6 +1357,199 @@ void free_memory_settings() // C API functions //============================================================================== +namespace { + +int invalid_setting(const char* type, const char* name) +{ + set_errmsg(fmt::format("Unknown {} setting '{}'.", type, name)); + return OPENMC_E_INVALID_ARGUMENT; +} + +bool* bool_setting(const char* name) +{ + if (std::strcmp(name, "cmfd_run") == 0) { + return &settings::cmfd_run; + } else if (std::strcmp(name, "entropy_on") == 0) { + return &settings::entropy_on; + } else if (std::strcmp(name, "event_based") == 0) { + return &settings::event_based; + } else if (std::strcmp(name, "need_depletion_rx") == 0) { + return &simulation::need_depletion_rx; + } else if (std::strcmp(name, "photon_transport") == 0) { + return &settings::photon_transport; + } else if (std::strcmp(name, "output_summary") == 0) { + return &settings::output_summary; + } else if (std::strcmp(name, "reduce_tallies") == 0) { + return &settings::reduce_tallies; + } else if (std::strcmp(name, "restart_run") == 0) { + return &settings::restart_run; + } else if (std::strcmp(name, "run_ce") == 0) { + return &settings::run_CE; + } else if (std::strcmp(name, "trigger_on") == 0) { + return &settings::trigger_on; + } else if (std::strcmp(name, "weight_windows_on") == 0) { + return &settings::weight_windows_on; + } + return nullptr; +} + +} // namespace + +extern "C" int openmc_setting_get_bool(const char* name, bool* value) +{ + if (!name || !value) { + set_errmsg("Setting name and output pointer must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + bool* setting = bool_setting(name); + if (!setting) + return invalid_setting("boolean", name); + + *value = *setting; + return 0; +} + +extern "C" int openmc_setting_set_bool(const char* name, bool value) +{ + if (!name) { + set_errmsg("Setting name must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + bool* setting = bool_setting(name); + if (!setting) + return invalid_setting("boolean", name); + + *setting = value; + return 0; +} + +extern "C" int openmc_setting_get_int32(const char* name, int32_t* value) +{ + if (!name || !value) { + set_errmsg("Setting name and output pointer must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "gen_per_batch") == 0) { + *value = settings::gen_per_batch; + } else if (std::strcmp(name, "max_lost_particles") == 0) { + *value = settings::max_lost_particles; + } else if (std::strcmp(name, "max_write_lost_particles") == 0) { + *value = settings::max_write_lost_particles; + } else if (std::strcmp(name, "n_inactive") == 0) { + *value = settings::n_inactive; + } else if (std::strcmp(name, "run_mode") == 0) { + *value = static_cast(settings::run_mode); + } else if (std::strcmp(name, "verbosity") == 0) { + *value = settings::verbosity; + } else { + return invalid_setting("int32", name); + } + return 0; +} + +extern "C" int openmc_setting_set_int32(const char* name, int32_t value) +{ + if (!name) { + set_errmsg("Setting name must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "gen_per_batch") == 0) { + settings::gen_per_batch = value; + } else if (std::strcmp(name, "max_lost_particles") == 0) { + settings::max_lost_particles = value; + } else if (std::strcmp(name, "max_write_lost_particles") == 0) { + settings::max_write_lost_particles = value; + } else if (std::strcmp(name, "n_inactive") == 0) { + settings::n_inactive = value; + } else if (std::strcmp(name, "run_mode") == 0) { + if (value < static_cast(RunMode::UNSET) || + value > static_cast(RunMode::VOLUME)) { + set_errmsg(fmt::format("Invalid run mode: {}.", value)); + return OPENMC_E_INVALID_ARGUMENT; + } + settings::run_mode = static_cast(value); + } else if (std::strcmp(name, "verbosity") == 0) { + settings::verbosity = value; + } else { + return invalid_setting("int32", name); + } + return 0; +} + +extern "C" int openmc_setting_get_int64(const char* name, int64_t* value) +{ + if (!name || !value) { + set_errmsg("Setting name and output pointer must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "n_particles") != 0) + return invalid_setting("int64", name); + + *value = settings::n_particles; + return 0; +} + +extern "C" int openmc_setting_set_int64(const char* name, int64_t value) +{ + if (!name) { + set_errmsg("Setting name must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "n_particles") != 0) + return invalid_setting("int64", name); + + settings::n_particles = value; + return 0; +} + +extern "C" int openmc_setting_get_double(const char* name, double* value) +{ + if (!name || !value) { + set_errmsg("Setting name and output pointer must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "rel_max_lost_particles") != 0) + return invalid_setting("double", name); + + *value = settings::rel_max_lost_particles; + return 0; +} + +extern "C" int openmc_setting_set_double(const char* name, double value) +{ + if (!name) { + set_errmsg("Setting name must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "rel_max_lost_particles") != 0) + return invalid_setting("double", name); + + settings::rel_max_lost_particles = value; + return 0; +} + +extern "C" int openmc_setting_get_string(const char* name, const char** value) +{ + if (!name || !value) { + set_errmsg("Setting name and output pointer must not be null."); + return OPENMC_E_INVALID_ARGUMENT; + } + + if (std::strcmp(name, "path_statepoint") != 0) + return invalid_setting("string", name); + + *value = settings::path_statepoint.c_str(); + return 0; +} + extern "C" int openmc_set_n_batches( int32_t n_batches, bool set_max_batches, bool add_statepoint_batch) { diff --git a/src/simulation.cpp b/src/simulation.cpp index 03f40a726eb..b1025a35677 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -1235,4 +1235,9 @@ void transport_event_based_shared_secondary() calculate_work(settings::n_particles); } +extern "C" int openmc_get_current_batch() +{ + return simulation::current_batch; +} + } // namespace openmc diff --git a/src/state_point.cpp b/src/state_point.cpp index 608918cf428..cbfb13f7670 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -382,7 +382,7 @@ void restart_set_keff() void load_state_point() { write_message( - fmt::format("Loading state point {}...", settings::path_statepoint_c), 5); + fmt::format("Loading state point {}...", settings::path_statepoint), 5); openmc_statepoint_load(settings::path_statepoint.c_str()); } diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 5ffa1b074c6..c944b9c50b4 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -1618,6 +1618,11 @@ extern "C" int openmc_global_tallies(double** ptr) return 0; } +extern "C" int32_t openmc_get_n_realizations() +{ + return simulation::n_realizations; +} + extern "C" size_t tallies_size() { return model::tallies.size(); diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 8f1900eaa87..d33f69f224e 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -314,6 +314,46 @@ def test_settings(lib_init): assert settings.event_based is False settings.seed = 11 + new_values = { + 'cmfd_run': not settings.cmfd_run, + 'entropy_on': not settings.entropy_on, + 'generations_per_batch': settings.generations_per_batch + 1, + 'inactive': settings.inactive + 1, + 'max_lost_particles': settings.max_lost_particles + 1, + 'max_write_lost_particles': settings.max_write_lost_particles + 1, + 'need_depletion_rx': not settings.need_depletion_rx, + 'output_summary': not settings.output_summary, + 'particles': settings.particles + 1, + 'photon_transport': not settings.photon_transport, + 'rel_max_lost_particles': settings.rel_max_lost_particles + 0.01, + 'reduce_tallies': not settings.reduce_tallies, + 'restart_run': not settings.restart_run, + 'run_CE': not settings.run_CE, + 'trigger_on': not settings.trigger_on, + 'verbosity': settings.verbosity + 1, + 'event_based': not settings.event_based, + 'weight_windows_on': not settings.weight_windows_on, + } + original_values = { + name: getattr(settings, name) for name in new_values + } + original_run_mode = settings.run_mode + + try: + for name, value in new_values.items(): + setattr(settings, name, value) + assert getattr(settings, name) == value + + settings.run_mode = 'plot' + assert settings.run_mode == 'plot' + assert isinstance(settings.path_statepoint, str) + finally: + for name, value in original_values.items(): + setattr(settings, name, value) + settings.run_mode = original_run_mode + + assert isinstance(openmc.lib._coord_levels(), int) + def test_feature_enabled(): assert isinstance(openmc.lib.feature_enabled('dagmc'), bool)