From 7bb140c8ecf3346a5545789b58a869076437da8b Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Mon, 24 Feb 2025 21:34:06 +0100 Subject: [PATCH 01/21] add ncrystal_load.h/.cpp infrastructure for accessing NCrystal at runtime rather than buildtime --- src/ncrystal_load.cpp | 146 ++++++++++++++++++++++++++++++++++++++++++ src/ncrystal_load.h | 102 +++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 src/ncrystal_load.cpp create mode 100644 src/ncrystal_load.h diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp new file mode 100644 index 00000000000..57f7a72802d --- /dev/null +++ b/src/ncrystal_load.cpp @@ -0,0 +1,146 @@ +#include "ncrystal_load.hh" +#include +#include +#include +#include + +#if !defined(NCLOAD_WINDOWS) && ( defined (_WIN32) || defined (WIN32) ) +# define NCLOAD_WINDOWS +#endif +#ifdef NCLOAD_WINDOWS +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +#else +# include +#endif + +namespace openmc { + namespace { + + struct NCrystalConfig { + std::string shlibpath; + unsigned long intversion = 0; + std::string symbol_namespace; + }; + + NCrystalConfig query_ncrystal_config() + { + char buffer[4096]; +#ifdef NCLOAD_WINDOWS + FILE* pipe = _popen("ncrystal-config --show " + "intversion shlibpath namespace", "r"); +#else + FILE* pipe = popen("ncrystal-config --show " + "intversion shlibpath namespace 2>/dev/null", "r"); +#endif + if (!pipe) + return {};//failure + auto readLine = [pipe]( std::string& tgt ) -> bool + { + //Read line and discard trailing whitespace (including newline chars). + char buffer[4096]; + if (fgets(buffer, sizeof(buffer), pipe) == NULL) + return false; + tgt = buffer; + while( !tgt.empty() && std::isspace( tgt.back() ) ) + tgt.pop_back(); + return true; + }; + auto parseIntVersion = []( const std::string& s ) + { + char * str_end = nullptr; + unsigned long v = std::strtoul( s.c_str(), &str_end, 10 ); + return ( v >= 2002000 + && v < 999999999 + && str_end == s.c_str() + s.size() ) ? v : 0; + }; + + NCrystalConfig res; + bool all_ok(true); + if ( !readLine(res.shlibpath) + || !(res.intversion = parseIntVersion( res.shlibpath )) + || !readLine( res.shlibpath ) + || res.shlibpath.empty() + || !readLine(res.symbol_namespace ) ) { + res.intversion = 0;//failure + } + +#ifdef NCLOAD_WINDOWS + auto returnCode = _pclose(pipe); +#else + auto returnCode = pclose(pipe); +#endif + if ( returnCode == 0 && res.intversion >= 2002000 ) + return res; + return {};//failure + } + + struct NCrystalAPIDB { + std::mutex mtx; + std::shared_ptr api; + typedef void* (*FctSignature)(int); + FctSignature ncrystal_access_dynapi_fct = nullptr; + }; + + void* load_dynapi_raw( unsigned interface_id, NCrystalAPIDB& db ) + { + if ( !db.ncrystal_access_dynapi_fct ) { + auto cfg = query_ncrystal_config(); + if ( !cfg.intversion >= 4000003 ) + throw std::runtime_error("Could not locate a functioning and" + " recent enough NCrystal installation."); +#ifdef NCLOAD_WINDOWS + auto handle = LoadLibrary(cfg.shlibpath.c_str()); +#else + dlerror();//clear previous errors + void * handle = dlopen( cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY ); +#endif + if (!handle) + throw std::runtime_error("Loading of the NCrystal library failed"); + + std::string symbol("ncrystal"); + symbol += cfg.symbol_namespace; + symbol += "_access_dynamic_api"; + +#ifdef NCLOAD_WINDOWS + FARPROC fproc; + void * addr = (void *)(intptr_t)GetProcAddress(handle, + symbol.c_str()); + if (!addr) + throw std::runtime_error("GetProcAddress(" + "ncrystal_access_dynamic_api) failed"); +#else + dlerror();//clear previous errors + void * addr = dlsym( handle, symbol.c_str()); + if ( !addr ) + throw std::runtime_error("dlsym(ncrystal_access_dynamic_api) failed"); +#endif + db.ncrystal_access_dynapi_fct + = reinterpret_cast(addr); + } + + return (*db.ncrystal_access_dynapi_fct)( interface_id ); + } + + NCrystalAPIDB& get_ncrystal_api_db() + { + static NCrystalAPIDB db; + return db; + } + } + + std::shared_ptr load_ncrystal_api() + { + auto& db = get_ncrystal_api_db(); + std::lock_guard lock(db.mtx); + if ( ! db.api ) { + void * raw_api = load_dynapi_raw(NCrystalAPI::interface_id,db); + if (!raw_api) + throw std::runtime_error("Failed to access required NCrystal interface."); + db.api = *reinterpret_cast*>(raw_api); + } + return db.api; + } +} diff --git a/src/ncrystal_load.h b/src/ncrystal_load.h new file mode 100644 index 00000000000..65f68065a61 --- /dev/null +++ b/src/ncrystal_load.h @@ -0,0 +1,102 @@ +#ifndef ncrystal_load +#define ncrystal_load + +#include +#include + +namespace NCrystalDynamicAPI { + + class DynAPI_Type1_v1 { + public: + + //NOTICE: Do NOT make ANY changes in the DynAPI_Type1_v1 class, it is + //required to stay exactly constant over time and compatible with the same + //definition used to compile the NCrystal library! But changes to white + //space, comments, and formatting is of course allowed. + //This API was introduced in NCrystal 4.1.0. + + static constexpr unsigned interface_id = 1001;//1000*typenumber+version + + class ScatterProcess; + virtual const ScatterProcess * createScatter( const char * cfgstr ) const = 0; + virtual void deallocateScatter( const ScatterProcess * ) const = 0; + + //NB: Cross section units returned are barn/atom: + virtual double crossSectionUncached( const ScatterProcess&, + double neutron_ekin_eV, + double neutron_dir_ux, + double neutron_dir_uy, + double neutron_dir_uz ) const = 0; + virtual void sampleScatterUncached( const ScatterProcess&, + std::function& rng, + double& neutron_ekin_eV, + double& neutron_dir_ux, + double& neutron_dir_uy, + double& neutron_dir_uz ) const = 0; + + virtual ~DynAPI_Type1_v1() = default; + DynAPI_Type1_v1() = default; + DynAPI_Type1_v1( const DynAPI_Type1_v1& ) = delete; + DynAPI_Type1_v1& operator=( const DynAPI_Type1_v1& ) = delete; + DynAPI_Type1_v1( DynAPI_Type1_v1&& ) = delete; + DynAPI_Type1_v1& operator=( DynAPI_Type1_v1&& ) = delete; + }; + +} + +namespace openmc { + + using NCrystalAPI = NCrystalDynamicAPI::DynAPI_Type1_v1; + + std::shared_ptr load_ncrystal_api(); + + class NCrystalScatProc final { + public: + NCrystalScatProc( const char * cfgstr ) + : m_api( load_ncrystal_api() ), + m_p( m_api->createScatter( cfgstr ) ) + { + } + ~NCrystalScatProc() + { + if ( m_p ) + m_api->deallocateScatter( m_p ); + } + + struct NeutronState { double ekin, ux, uy, uz; }; + + double cross_section( const NeutronState& n ) const + { + return m_api->crossSectionUncached( *m_p, n.ekin, n.ux, n.uy, n.uz ); + } + void scatter( std::function& rng, NeutronState& n ) const + { + m_api->sampleScatterUncached( *m_p, rng, n.ekin, n.ux, n.uy, n.uz ); + } + + NCrystalScatProc( const NCrystalScatProc& ) = delete; + NCrystalScatProc& operator=( const NCrystalScatProc& ) = delete; + NCrystalScatProc( NCrystalScatProc&& o ) + : m_api(std::move(o.m_api)), m_p(nullptr) + { + std::swap( m_p, o.m_p ); + } + NCrystalScatProc& operator=( NCrystalScatProc&& o ) + { + if ( m_p ) { + m_api->deallocateScatter( m_p ); + m_p = nullptr; + } + std::swap( m_api, o.m_api ); + std::swap( m_p, o.m_p ); + return *this; + } + + private: + std::shared_ptr m_api; + const NCrystalAPI::ScatterProcess * m_p; + }; + +} + +#endif From 262881384d529cd240ec94cd13563a9f428344cc Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Mon, 24 Feb 2025 21:39:56 +0100 Subject: [PATCH 02/21] Try to follow openmc coding style in ncrystal_load.h/.cpp files --- src/ncrystal_load.cpp | 218 +++++++++++++++++++++--------------------- src/ncrystal_load.h | 166 ++++++++++++++++---------------- 2 files changed, 190 insertions(+), 194 deletions(-) diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 57f7a72802d..653e385ecb1 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -1,146 +1,144 @@ #include "ncrystal_load.hh" -#include #include #include #include +#include -#if !defined(NCLOAD_WINDOWS) && ( defined (_WIN32) || defined (WIN32) ) -# define NCLOAD_WINDOWS +#if !defined(NCLOAD_WINDOWS) && (defined(_WIN32) || defined(WIN32)) +#define NCLOAD_WINDOWS #endif #ifdef NCLOAD_WINDOWS -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include #else -# include +#include #endif namespace openmc { - namespace { +namespace { - struct NCrystalConfig { - std::string shlibpath; - unsigned long intversion = 0; - std::string symbol_namespace; - }; +struct NCrystalConfig { + std::string shlibpath; + unsigned long intversion = 0; + std::string symbol_namespace; +}; - NCrystalConfig query_ncrystal_config() - { - char buffer[4096]; +NCrystalConfig query_ncrystal_config() +{ + char buffer[4096]; #ifdef NCLOAD_WINDOWS - FILE* pipe = _popen("ncrystal-config --show " - "intversion shlibpath namespace", "r"); + FILE* pipe = _popen("ncrystal-config --show " + "intversion shlibpath namespace", + "r"); #else - FILE* pipe = popen("ncrystal-config --show " - "intversion shlibpath namespace 2>/dev/null", "r"); + FILE* pipe = popen("ncrystal-config --show " + "intversion shlibpath namespace 2>/dev/null", + "r"); #endif - if (!pipe) - return {};//failure - auto readLine = [pipe]( std::string& tgt ) -> bool - { - //Read line and discard trailing whitespace (including newline chars). - char buffer[4096]; - if (fgets(buffer, sizeof(buffer), pipe) == NULL) - return false; - tgt = buffer; - while( !tgt.empty() && std::isspace( tgt.back() ) ) - tgt.pop_back(); - return true; - }; - auto parseIntVersion = []( const std::string& s ) - { - char * str_end = nullptr; - unsigned long v = std::strtoul( s.c_str(), &str_end, 10 ); - return ( v >= 2002000 - && v < 999999999 - && str_end == s.c_str() + s.size() ) ? v : 0; - }; + if (!pipe) + return {}; // failure + auto readLine = [pipe](std::string& tgt) -> bool { + // Read line and discard trailing whitespace (including newline chars). + char buffer[4096]; + if (fgets(buffer, sizeof(buffer), pipe) == NULL) + return false; + tgt = buffer; + while (!tgt.empty() && std::isspace(tgt.back())) + tgt.pop_back(); + return true; + }; + auto parseIntVersion = [](const std::string& s) { + char* str_end = nullptr; + unsigned long v = std::strtoul(s.c_str(), &str_end, 10); + return (v >= 2002000 && v < 999999999 && str_end == s.c_str() + s.size()) + ? v + : 0; + }; - NCrystalConfig res; - bool all_ok(true); - if ( !readLine(res.shlibpath) - || !(res.intversion = parseIntVersion( res.shlibpath )) - || !readLine( res.shlibpath ) - || res.shlibpath.empty() - || !readLine(res.symbol_namespace ) ) { - res.intversion = 0;//failure - } + NCrystalConfig res; + bool all_ok(true); + if (!readLine(res.shlibpath) || + !(res.intversion = parseIntVersion(res.shlibpath)) || + !readLine(res.shlibpath) || res.shlibpath.empty() || + !readLine(res.symbol_namespace)) { + res.intversion = 0; // failure + } #ifdef NCLOAD_WINDOWS - auto returnCode = _pclose(pipe); + auto returnCode = _pclose(pipe); #else - auto returnCode = pclose(pipe); + auto returnCode = pclose(pipe); #endif - if ( returnCode == 0 && res.intversion >= 2002000 ) - return res; - return {};//failure - } + if (returnCode == 0 && res.intversion >= 2002000) + return res; + return {}; // failure +} - struct NCrystalAPIDB { - std::mutex mtx; - std::shared_ptr api; - typedef void* (*FctSignature)(int); - FctSignature ncrystal_access_dynapi_fct = nullptr; - }; +struct NCrystalAPIDB { + std::mutex mtx; + std::shared_ptr api; + typedef void* (*FctSignature)(int); + FctSignature ncrystal_access_dynapi_fct = nullptr; +}; - void* load_dynapi_raw( unsigned interface_id, NCrystalAPIDB& db ) - { - if ( !db.ncrystal_access_dynapi_fct ) { - auto cfg = query_ncrystal_config(); - if ( !cfg.intversion >= 4000003 ) - throw std::runtime_error("Could not locate a functioning and" - " recent enough NCrystal installation."); +void* load_dynapi_raw(unsigned interface_id, NCrystalAPIDB& db) +{ + if (!db.ncrystal_access_dynapi_fct) { + auto cfg = query_ncrystal_config(); + if (!cfg.intversion >= 4000003) + throw std::runtime_error("Could not locate a functioning and" + " recent enough NCrystal installation."); #ifdef NCLOAD_WINDOWS - auto handle = LoadLibrary(cfg.shlibpath.c_str()); + auto handle = LoadLibrary(cfg.shlibpath.c_str()); #else - dlerror();//clear previous errors - void * handle = dlopen( cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY ); + dlerror(); // clear previous errors + void* handle = dlopen(cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY); #endif - if (!handle) - throw std::runtime_error("Loading of the NCrystal library failed"); + if (!handle) + throw std::runtime_error("Loading of the NCrystal library failed"); - std::string symbol("ncrystal"); - symbol += cfg.symbol_namespace; - symbol += "_access_dynamic_api"; + std::string symbol("ncrystal"); + symbol += cfg.symbol_namespace; + symbol += "_access_dynamic_api"; #ifdef NCLOAD_WINDOWS - FARPROC fproc; - void * addr = (void *)(intptr_t)GetProcAddress(handle, - symbol.c_str()); - if (!addr) - throw std::runtime_error("GetProcAddress(" - "ncrystal_access_dynamic_api) failed"); + FARPROC fproc; + void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str()); + if (!addr) + throw std::runtime_error("GetProcAddress(" + "ncrystal_access_dynamic_api) failed"); #else - dlerror();//clear previous errors - void * addr = dlsym( handle, symbol.c_str()); - if ( !addr ) - throw std::runtime_error("dlsym(ncrystal_access_dynamic_api) failed"); + dlerror(); // clear previous errors + void* addr = dlsym(handle, symbol.c_str()); + if (!addr) + throw std::runtime_error("dlsym(ncrystal_access_dynamic_api) failed"); #endif - db.ncrystal_access_dynapi_fct - = reinterpret_cast(addr); - } + db.ncrystal_access_dynapi_fct = + reinterpret_cast(addr); + } - return (*db.ncrystal_access_dynapi_fct)( interface_id ); - } + return (*db.ncrystal_access_dynapi_fct)(interface_id); +} - NCrystalAPIDB& get_ncrystal_api_db() - { - static NCrystalAPIDB db; - return db; - } - } +NCrystalAPIDB& get_ncrystal_api_db() +{ + static NCrystalAPIDB db; + return db; +} +} // namespace - std::shared_ptr load_ncrystal_api() - { - auto& db = get_ncrystal_api_db(); - std::lock_guard lock(db.mtx); - if ( ! db.api ) { - void * raw_api = load_dynapi_raw(NCrystalAPI::interface_id,db); - if (!raw_api) - throw std::runtime_error("Failed to access required NCrystal interface."); - db.api = *reinterpret_cast*>(raw_api); - } - return db.api; +std::shared_ptr load_ncrystal_api() +{ + auto& db = get_ncrystal_api_db(); + std::lock_guard lock(db.mtx); + if (!db.api) { + void* raw_api = load_dynapi_raw(NCrystalAPI::interface_id, db); + if (!raw_api) + throw std::runtime_error("Failed to access required NCrystal interface."); + db.api = *reinterpret_cast*>(raw_api); } + return db.api; } +} // namespace openmc diff --git a/src/ncrystal_load.h b/src/ncrystal_load.h index 65f68065a61..7f827cd033b 100644 --- a/src/ncrystal_load.h +++ b/src/ncrystal_load.h @@ -6,97 +6,95 @@ namespace NCrystalDynamicAPI { - class DynAPI_Type1_v1 { - public: - - //NOTICE: Do NOT make ANY changes in the DynAPI_Type1_v1 class, it is - //required to stay exactly constant over time and compatible with the same - //definition used to compile the NCrystal library! But changes to white - //space, comments, and formatting is of course allowed. - //This API was introduced in NCrystal 4.1.0. - - static constexpr unsigned interface_id = 1001;//1000*typenumber+version - - class ScatterProcess; - virtual const ScatterProcess * createScatter( const char * cfgstr ) const = 0; - virtual void deallocateScatter( const ScatterProcess * ) const = 0; - - //NB: Cross section units returned are barn/atom: - virtual double crossSectionUncached( const ScatterProcess&, - double neutron_ekin_eV, - double neutron_dir_ux, - double neutron_dir_uy, - double neutron_dir_uz ) const = 0; - virtual void sampleScatterUncached( const ScatterProcess&, - std::function& rng, - double& neutron_ekin_eV, - double& neutron_dir_ux, - double& neutron_dir_uy, - double& neutron_dir_uz ) const = 0; - - virtual ~DynAPI_Type1_v1() = default; - DynAPI_Type1_v1() = default; - DynAPI_Type1_v1( const DynAPI_Type1_v1& ) = delete; - DynAPI_Type1_v1& operator=( const DynAPI_Type1_v1& ) = delete; - DynAPI_Type1_v1( DynAPI_Type1_v1&& ) = delete; - DynAPI_Type1_v1& operator=( DynAPI_Type1_v1&& ) = delete; - }; - -} +// NOTICE: Do NOT make ANY changes in the NCrystalDynamicAPI::DynAPI_Type1_v1 +// class, it is required to stay exactly constant over time and compatible +// with the same definition used to compile the NCrystal library! But changes +// to white space, comments, and formatting is of course allowed. This API +// was introduced in NCrystal 4.1.0. + +class DynAPI_Type1_v1 { +public: + static constexpr unsigned interface_id = 1001; // 1000*typenumber+version + + class ScatterProcess; + virtual const ScatterProcess* createScatter(const char* cfgstr) const = 0; + virtual void deallocateScatter(const ScatterProcess*) const = 0; + + // NB: Cross section units returned are barn/atom: + virtual double crossSectionUncached(const ScatterProcess&, + double neutron_ekin_eV, double neutron_dir_ux, double neutron_dir_uy, + double neutron_dir_uz) const = 0; + virtual void sampleScatterUncached(const ScatterProcess&, + std::function& rng, double& neutron_ekin_eV, + double& neutron_dir_ux, double& neutron_dir_uy, + double& neutron_dir_uz) const = 0; + + virtual ~DynAPI_Type1_v1() = default; + DynAPI_Type1_v1() = default; + DynAPI_Type1_v1(const DynAPI_Type1_v1&) = delete; + DynAPI_Type1_v1& operator=(const DynAPI_Type1_v1&) = delete; + DynAPI_Type1_v1(DynAPI_Type1_v1&&) = delete; + DynAPI_Type1_v1& operator=(DynAPI_Type1_v1&&) = delete; +}; + +} // namespace NCrystalDynamicAPI namespace openmc { - using NCrystalAPI = NCrystalDynamicAPI::DynAPI_Type1_v1; +using NCrystalAPI = NCrystalDynamicAPI::DynAPI_Type1_v1; - std::shared_ptr load_ncrystal_api(); +std::shared_ptr load_ncrystal_api(); - class NCrystalScatProc final { - public: - NCrystalScatProc( const char * cfgstr ) - : m_api( load_ncrystal_api() ), - m_p( m_api->createScatter( cfgstr ) ) - { - } - ~NCrystalScatProc() - { - if ( m_p ) - m_api->deallocateScatter( m_p ); - } +class NCrystalScatProc final { +public: + NCrystalScatProc(const char* cfgstr) + : api_(load_ncrystal_api()), p_(api_->createScatter(cfgstr)) + { + } - struct NeutronState { double ekin, ux, uy, uz; }; + struct NeutronState { double ekin, ux, uy, uz; }; - double cross_section( const NeutronState& n ) const - { - return m_api->crossSectionUncached( *m_p, n.ekin, n.ux, n.uy, n.uz ); - } - void scatter( std::function& rng, NeutronState& n ) const - { - m_api->sampleScatterUncached( *m_p, rng, n.ekin, n.ux, n.uy, n.uz ); - } + double cross_section(const NeutronState& n) const + { + return api_->crossSectionUncached(*p_, n.ekin, n.ux, n.uy, n.uz); + } + + void scatter(std::function& rng, NeutronState& n) const + { + api_->sampleScatterUncached(*p_, rng, n.ekin, n.ux, n.uy, n.uz); + } - NCrystalScatProc( const NCrystalScatProc& ) = delete; - NCrystalScatProc& operator=( const NCrystalScatProc& ) = delete; - NCrystalScatProc( NCrystalScatProc&& o ) - : m_api(std::move(o.m_api)), m_p(nullptr) - { - std::swap( m_p, o.m_p ); - } - NCrystalScatProc& operator=( NCrystalScatProc&& o ) - { - if ( m_p ) { - m_api->deallocateScatter( m_p ); - m_p = nullptr; - } - std::swap( m_api, o.m_api ); - std::swap( m_p, o.m_p ); - return *this; - } - - private: - std::shared_ptr m_api; - const NCrystalAPI::ScatterProcess * m_p; - }; - -} + //Plumbing (move-only semantics): + NCrystalScatProc(const NCrystalScatProc&) = delete; + NCrystalScatProc& operator=(const NCrystalScatProc&) = delete; + + NCrystalScatProc(NCrystalScatProc&& o) : api_(std::move(o.api_)), p_(nullptr) + { + std::swap(p_, o.p_); + } + + NCrystalScatProc& operator=(NCrystalScatProc&& o) + { + if (p_) { + api_->deallocateScatter(p_); + p_ = nullptr; + } + std::swap(api_, o.api_); + std::swap(p_, o.p_); + return *this; + } + + ~NCrystalScatProc() + { + if (p_) + api_->deallocateScatter(p_); + } + +private: + std::shared_ptr api_; + const NCrystalAPI::ScatterProcess* p_; +}; + +} // namespace openmc #endif From 2c21caf53385b4a31e54657f9048002291a170e9 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Mon, 24 Feb 2025 21:47:40 +0100 Subject: [PATCH 03/21] Move ncrystal_load.h to include folder --- {src => include/openmc}/ncrystal_load.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src => include/openmc}/ncrystal_load.h (100%) diff --git a/src/ncrystal_load.h b/include/openmc/ncrystal_load.h similarity index 100% rename from src/ncrystal_load.h rename to include/openmc/ncrystal_load.h From a7c59edebd39ba2b51d3f48b238656f19322ae22 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Tue, 25 Feb 2025 11:39:13 +0100 Subject: [PATCH 04/21] More fixes --- CMakeLists.txt | 25 +------- CODEOWNERS | 2 +- cmake/OpenMCConfig.cmake.in | 13 ---- include/openmc/ncrystal_interface.h | 36 ++++------- include/openmc/ncrystal_load.h | 84 ++++++++++++++----------- openmc/lib/__init__.py | 3 - src/material.cpp | 2 +- src/ncrystal_interface.cpp | 75 ++++------------------ src/ncrystal_load.cpp | 20 +++--- tests/regression_tests/ncrystal/test.py | 5 +- 10 files changed, 86 insertions(+), 179 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 385ce85aaaf..461abe508ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,6 @@ option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" option(OPENMC_USE_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF) option(OPENMC_USE_MPI "Enable MPI" OFF) option(OPENMC_USE_MCPL "Enable MCPL" OFF) -option(OPENMC_USE_NCRYSTAL "Enable support for NCrystal scattering" OFF) option(OPENMC_USE_UWUW "Enable UWUW" OFF) message(STATUS "OPENMC_USE_OPENMP ${OPENMC_USE_OPENMP}") @@ -48,7 +47,6 @@ message(STATUS "OPENMC_USE_DAGMC ${OPENMC_USE_DAGMC}") message(STATUS "OPENMC_USE_LIBMESH ${OPENMC_USE_LIBMESH}") message(STATUS "OPENMC_USE_MPI ${OPENMC_USE_MPI}") message(STATUS "OPENMC_USE_MCPL ${OPENMC_USE_MCPL}") -message(STATUS "OPENMC_USE_NCRYSTAL ${OPENMC_USE_NCRYSTAL}") message(STATUS "OPENMC_USE_UWUW ${OPENMC_USE_UWUW}") # Warnings for deprecated options @@ -120,23 +118,6 @@ macro(find_package_write_status pkg) endif() endmacro() -#=============================================================================== -# NCrystal Scattering Support -#=============================================================================== - -if(OPENMC_USE_NCRYSTAL) - if(NOT DEFINED "NCrystal_DIR") - #Invocation of "ncrystal-config --show cmakedir" is needed to find NCrystal - #when it is installed from Python wheels: - execute_process( - COMMAND "ncrystal-config" "--show" "cmakedir" - OUTPUT_VARIABLE "NCrystal_DIR" OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() - find_package(NCrystal 3.8.0 REQUIRED) - message(STATUS "Found NCrystal: ${NCrystal_DIR} (version ${NCrystal_VERSION})") -endif() - #=============================================================================== # DAGMC Geometry Support - need DAGMC/MOAB #=============================================================================== @@ -372,6 +353,7 @@ list(APPEND libopenmc_SOURCES src/mgxs.cpp src/mgxs_interface.cpp src/ncrystal_interface.cpp + src/ncrystal_load.cpp src/nuclide.cpp src/output.cpp src/particle.cpp @@ -548,11 +530,6 @@ if (OPENMC_USE_MCPL) target_link_libraries(libopenmc MCPL::mcpl) endif() -if(OPENMC_USE_NCRYSTAL) - target_compile_definitions(libopenmc PRIVATE NCRYSTAL) - target_link_libraries(libopenmc NCrystal::NCrystal) -endif() - #=============================================================================== # Log build info that this executable can report later #=============================================================================== diff --git a/CODEOWNERS b/CODEOWNERS index 6d452e36653..090305fd092 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -60,7 +60,7 @@ Dockerfile @shimwell src/random_ray/ @jtramm # NCrystal interface -src/ncrystal_interface.cpp @marquezj +src/ncrystal_interface.cpp @marquezj @tkittel # MCPL interface src/mcpl_interface.cpp @ebknudsen diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index 21c55230334..5cab4790abc 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -8,19 +8,6 @@ if(@OPENMC_USE_DAGMC@) find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@) endif() -if(@OPENMC_USE_NCRYSTAL@) - if(NOT DEFINED "NCrystal_DIR") - #Invocation of "ncrystal-config --show cmakedir" is needed to find NCrystal - #when it is installed from Python wheels: - execute_process( - COMMAND "ncrystal-config" "--show" "cmakedir" - OUTPUT_VARIABLE "NCrystal_DIR" OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() - find_package(NCrystal REQUIRED) - message(STATUS "Found NCrystal: ${NCrystal_DIR} (version ${NCrystal_VERSION})") -endif() - if(@OPENMC_USE_LIBMESH@) include(FindPkgConfig) list(APPEND CMAKE_PREFIX_PATH @LIBMESH_PREFIX@) diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h index 22422e378cf..1c8db85f19c 100644 --- a/include/openmc/ncrystal_interface.h +++ b/include/openmc/ncrystal_interface.h @@ -1,10 +1,7 @@ #ifndef OPENMC_NCRYSTAL_INTERFACE_H #define OPENMC_NCRYSTAL_INTERFACE_H -#ifdef NCRYSTAL -#include "NCrystal/NCrystal.hh" -#endif - +#include "openmc/ncrystal_load.h" #include "openmc/particle.h" #include // for uint64_t @@ -17,8 +14,6 @@ namespace openmc { // Constants //============================================================================== -extern "C" const bool NCRYSTAL_ENABLED; - //! Energy in [eV] to switch between NCrystal and ENDF constexpr double NCRYSTAL_MAX_ENERGY {5.0}; @@ -30,15 +25,14 @@ class NCrystalMat { public: //---------------------------------------------------------------------------- // Constructors - NCrystalMat() = default; + NCrystalMat() = default;//empty object explicit NCrystalMat(const std::string& cfg); //---------------------------------------------------------------------------- // Methods -#ifdef NCRYSTAL - //! Return configuration string - std::string cfg() const; + //! Return configuration string: + const std::string& cfg() const { return cfg_; } //! Get cross section from NCrystal material // @@ -52,25 +46,21 @@ class NCrystalMat { void scatter(Particle& p) const; //! Whether the object holds a valid NCrystal material - operator bool() const; -#else + operator bool() const { return !cfg_.empty(); } - //---------------------------------------------------------------------------- - // Trivial methods when compiling without NCRYSTAL - std::string cfg() const { return ""; } - double xs(const Particle& p) const { return -1.0; } - void scatter(Particle& p) const {} - operator bool() const { return false; } -#endif + NCrystalMat clone() const + { + NCrystalMat c; + c.cfg_ = cfg_; + c.proc_ = proc_.clone(); + return c; + } private: //---------------------------------------------------------------------------- // Data members (only present when compiling with NCrystal support) -#ifdef NCRYSTAL std::string cfg_; //!< NCrystal configuration string - std::shared_ptr - ptr_; //!< Pointer to NCrystal material object -#endif + NCrystalScatProc proc_; //!< NCrystal scatter process }; //============================================================================== diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index 7f827cd033b..107999c4fa2 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -1,70 +1,78 @@ -#ifndef ncrystal_load -#define ncrystal_load +#ifndef OPENMC_NCRYSTAL_LOAD_H +#define OPENMC_NCRYSTAL_LOAD_H #include #include -namespace NCrystalDynamicAPI { +namespace NCrystalVirtualAPI { -// NOTICE: Do NOT make ANY changes in the NCrystalDynamicAPI::DynAPI_Type1_v1 -// class, it is required to stay exactly constant over time and compatible -// with the same definition used to compile the NCrystal library! But changes -// to white space, comments, and formatting is of course allowed. This API -// was introduced in NCrystal 4.1.0. +// NOTICE: Do NOT make ANY changes in the NCrystalVirtualAPI::VirtAPI_Type1_v1 +// class, it is required to stay exactly constant over time and compatible with +// the same definition used to compile the NCrystal library! But changes to +// white space, comments, and formatting is of course allowed. This API was +// introduced in NCrystal 4.1.0. -class DynAPI_Type1_v1 { +class VirtAPI_Type1_v1 { public: - static constexpr unsigned interface_id = 1001; // 1000*typenumber+version - class ScatterProcess; - virtual const ScatterProcess* createScatter(const char* cfgstr) const = 0; - virtual void deallocateScatter(const ScatterProcess*) const = 0; - - // NB: Cross section units returned are barn/atom: - virtual double crossSectionUncached(const ScatterProcess&, - double neutron_ekin_eV, double neutron_dir_ux, double neutron_dir_uy, - double neutron_dir_uz) const = 0; - virtual void sampleScatterUncached(const ScatterProcess&, - std::function& rng, double& neutron_ekin_eV, - double& neutron_dir_ux, double& neutron_dir_uy, - double& neutron_dir_uz) const = 0; - - virtual ~DynAPI_Type1_v1() = default; - DynAPI_Type1_v1() = default; - DynAPI_Type1_v1(const DynAPI_Type1_v1&) = delete; - DynAPI_Type1_v1& operator=(const DynAPI_Type1_v1&) = delete; - DynAPI_Type1_v1(DynAPI_Type1_v1&&) = delete; - DynAPI_Type1_v1& operator=(DynAPI_Type1_v1&&) = delete; + virtual const ScatterProcess * createScatter( const char * cfgstr ) const = 0; + virtual const ScatterProcess * cloneScatter( const ScatterProcess * ) const = 0; + virtual void deallocateScatter( const ScatterProcess * ) const = 0; + virtual double crossSectionUncached( const ScatterProcess&, + const double* neutron ) const = 0; + virtual void sampleScatterUncached( const ScatterProcess&, + std::function& rng, + double* neutron ) const = 0; + //Plumbing: + static constexpr unsigned interface_id = 1001; + virtual ~VirtAPI_Type1_v1() = default; + VirtAPI_Type1_v1() = default; + VirtAPI_Type1_v1( const VirtAPI_Type1_v1& ) = delete; + VirtAPI_Type1_v1& operator=( const VirtAPI_Type1_v1& ) = delete; + VirtAPI_Type1_v1( VirtAPI_Type1_v1&& ) = delete; + VirtAPI_Type1_v1& operator=( VirtAPI_Type1_v1&& ) = delete; }; -} // namespace NCrystalDynamicAPI +} // namespace NCrystalVirtualAPI namespace openmc { -using NCrystalAPI = NCrystalDynamicAPI::DynAPI_Type1_v1; +using NCrystalAPI = NCrystalVirtualAPI::VirtAPI_Type1_v1; std::shared_ptr load_ncrystal_api(); class NCrystalScatProc final { public: + NCrystalScatProc() {} + NCrystalScatProc(const char* cfgstr) : api_(load_ncrystal_api()), p_(api_->createScatter(cfgstr)) { } - struct NeutronState { double ekin, ux, uy, uz; }; + //Note: Neutron state array is {ekin,ux,uy,uz} + + double cross_section(const double* neutron_state) const + { + return api_->crossSectionUncached(*p_, neutron_state); + } - double cross_section(const NeutronState& n) const + void scatter(std::function& rng, double* neutron_state) const { - return api_->crossSectionUncached(*p_, n.ekin, n.ux, n.uy, n.uz); + api_->sampleScatterUncached(*p_, rng, neutron_state); } - void scatter(std::function& rng, NeutronState& n) const + NCrystalScatProc clone() const { - api_->sampleScatterUncached(*p_, rng, n.ekin, n.ux, n.uy, n.uz); + NCrystalScatProc c; + if ( p_ ) { + c.api_ = api_; + c.p_ = api_->cloneScatter( p_ ); + } + return c; } - //Plumbing (move-only semantics): + //Plumbing (move-only semantics, but supports explicit clone): NCrystalScatProc(const NCrystalScatProc&) = delete; NCrystalScatProc& operator=(const NCrystalScatProc&) = delete; @@ -92,7 +100,7 @@ class NCrystalScatProc final { private: std::shared_ptr api_; - const NCrystalAPI::ScatterProcess* p_; + const NCrystalAPI::ScatterProcess* p_ = nullptr; }; } // namespace openmc diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 5fe35b9745d..15642b42be3 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -40,9 +40,6 @@ def _dagmc_enabled(): return c_bool.in_dll(_dll, "DAGMC_ENABLED").value -def _ncrystal_enabled(): - return c_bool.in_dll(_dll, "NCRYSTAL_ENABLED").value - def _coord_levels(): return c_int.in_dll(_dll, "n_coord_levels").value diff --git a/src/material.cpp b/src/material.cpp index 3ba9ab96c00..a1937aca459 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -368,7 +368,7 @@ Material& Material::clone() mat->name_ = name_; mat->nuclide_ = nuclide_; mat->element_ = element_; - mat->ncrystal_mat_ = ncrystal_mat_; + mat->ncrystal_mat_ = ncrystal_mat_.clone(); mat->atom_density_ = atom_density_; mat->density_ = density_; mat->density_gpcc_ = density_gpcc_; diff --git a/src/ncrystal_interface.cpp b/src/ncrystal_interface.cpp index b39f62d9020..3a4e161bec0 100644 --- a/src/ncrystal_interface.cpp +++ b/src/ncrystal_interface.cpp @@ -7,89 +7,36 @@ namespace openmc { //============================================================================== -// Constants -//============================================================================== - -#ifdef NCRYSTAL -const bool NCRYSTAL_ENABLED = true; -#else -const bool NCRYSTAL_ENABLED = false; -#endif - -//============================================================================== -// NCrystal wrapper class for the OpenMC random number generator -//============================================================================== - -#ifdef NCRYSTAL -class NCrystalRNGWrapper : public NCrystal::RNGStream { -public: - constexpr NCrystalRNGWrapper(uint64_t* seed) noexcept : openmc_seed_(seed) {} - -protected: - double actualGenerate() override - { - return std::max( - std::numeric_limits::min(), prn(openmc_seed_)); - } - -private: - uint64_t* openmc_seed_; -}; -#endif - -//============================================================================== -// NCrystal implementation +// NCrystalMat implementation //============================================================================== NCrystalMat::NCrystalMat(const std::string& cfg) + : cfg_( cfg ), proc_( cfg.c_str() ) { -#ifdef NCRYSTAL - cfg_ = cfg; - ptr_ = NCrystal::FactImpl::createScatter(cfg); -#else - fatal_error("Your build of OpenMC does not support NCrystal materials."); -#endif -} - -#ifdef NCRYSTAL -std::string NCrystalMat::cfg() const -{ - return cfg_; } double NCrystalMat::xs(const Particle& p) const { // Calculate scattering XS per atom with NCrystal, only once per material - NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy {p.E()}; - return ptr_->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z}) - .get(); + double neutron_state[4] = { p.E(), p.u().x, p.u().y, p.u().z }; + return proc_.cross_section( neutron_state ); } void NCrystalMat::scatter(Particle& p) const { - NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG - // create a cache pointer for multi thread physics - NCrystal::CachePtr dummy_cache; - auto nc_energy = NCrystal::NeutronEnergy {p.E()}; - auto outcome = ptr_->sampleScatter( - dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z}); - + // Scatter with NCrystal, using the OpenMC RNG stream: + uint64_t* seed = p.current_seed(); + std::function rng = [&seed]() { return prn(seed); }; + double neutron_state[4] = { p.E(), p.u().x, p.u().y, p.u().z }; + proc_.scatter(rng,neutron_state); // Modify attributes of particle - p.E() = outcome.ekin.get(); + p.E() = neutron_state[0]; Direction u_old {p.u()}; - p.u() = - Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]); + p.u() = Direction(neutron_state[1],neutron_state[2],neutron_state[3]); p.mu() = u_old.dot(p.u()); p.event_mt() = ELASTIC; } -NCrystalMat::operator bool() const -{ - return ptr_.get(); -} -#endif - //============================================================================== // Functions //============================================================================== diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 653e385ecb1..8a531660bd2 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -1,4 +1,4 @@ -#include "ncrystal_load.hh" +#include "openmc/ncrystal_load.h" #include #include #include @@ -80,12 +80,12 @@ struct NCrystalAPIDB { std::mutex mtx; std::shared_ptr api; typedef void* (*FctSignature)(int); - FctSignature ncrystal_access_dynapi_fct = nullptr; + FctSignature ncrystal_access_virtapi_fct = nullptr; }; -void* load_dynapi_raw(unsigned interface_id, NCrystalAPIDB& db) +void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) { - if (!db.ncrystal_access_dynapi_fct) { + if (!db.ncrystal_access_virtapi_fct) { auto cfg = query_ncrystal_config(); if (!cfg.intversion >= 4000003) throw std::runtime_error("Could not locate a functioning and" @@ -101,25 +101,25 @@ void* load_dynapi_raw(unsigned interface_id, NCrystalAPIDB& db) std::string symbol("ncrystal"); symbol += cfg.symbol_namespace; - symbol += "_access_dynamic_api"; + symbol += "_access_virtual_api"; #ifdef NCLOAD_WINDOWS FARPROC fproc; void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str()); if (!addr) throw std::runtime_error("GetProcAddress(" - "ncrystal_access_dynamic_api) failed"); + "ncrystal_access_virtual_api) failed"); #else dlerror(); // clear previous errors void* addr = dlsym(handle, symbol.c_str()); if (!addr) - throw std::runtime_error("dlsym(ncrystal_access_dynamic_api) failed"); + throw std::runtime_error("dlsym(ncrystal_access_virtual_api) failed"); #endif - db.ncrystal_access_dynapi_fct = + db.ncrystal_access_virtapi_fct = reinterpret_cast(addr); } - return (*db.ncrystal_access_dynapi_fct)(interface_id); + return (*db.ncrystal_access_virtapi_fct)(interface_id); } NCrystalAPIDB& get_ncrystal_api_db() @@ -134,7 +134,7 @@ std::shared_ptr load_ncrystal_api() auto& db = get_ncrystal_api_db(); std::lock_guard lock(db.mtx); if (!db.api) { - void* raw_api = load_dynapi_raw(NCrystalAPI::interface_id, db); + void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db); if (!raw_api) throw std::runtime_error("Failed to access required NCrystal interface."); db.api = *reinterpret_cast*>(raw_api); diff --git a/tests/regression_tests/ncrystal/test.py b/tests/regression_tests/ncrystal/test.py index e1c1e5ed6f0..8da05e1cfd7 100644 --- a/tests/regression_tests/ncrystal/test.py +++ b/tests/regression_tests/ncrystal/test.py @@ -6,12 +6,13 @@ import openmc import openmc.lib import pytest +import shutil from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.lib._ncrystal_enabled(), - reason="NCrystal materials are not enabled.") + not shutil.which('ncrystal-config'), + reason="NCrystal is not installed.") def pencil_beam_model(cfg, E0, N): From bfdcb71e6f606210daa17a48d61794e6cc560402 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Tue, 25 Feb 2025 11:40:27 +0100 Subject: [PATCH 05/21] clang-format --- include/openmc/ncrystal_interface.h | 4 ++-- include/openmc/ncrystal_load.h | 36 ++++++++++++++--------------- src/ncrystal_interface.cpp | 16 ++++++------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h index 1c8db85f19c..31ff81ec04c 100644 --- a/include/openmc/ncrystal_interface.h +++ b/include/openmc/ncrystal_interface.h @@ -25,7 +25,7 @@ class NCrystalMat { public: //---------------------------------------------------------------------------- // Constructors - NCrystalMat() = default;//empty object + NCrystalMat() = default; // empty object explicit NCrystalMat(const std::string& cfg); //---------------------------------------------------------------------------- @@ -59,7 +59,7 @@ class NCrystalMat { private: //---------------------------------------------------------------------------- // Data members (only present when compiling with NCrystal support) - std::string cfg_; //!< NCrystal configuration string + std::string cfg_; //!< NCrystal configuration string NCrystalScatProc proc_; //!< NCrystal scatter process }; diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index 107999c4fa2..39bae64fc1f 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -15,22 +15,21 @@ namespace NCrystalVirtualAPI { class VirtAPI_Type1_v1 { public: class ScatterProcess; - virtual const ScatterProcess * createScatter( const char * cfgstr ) const = 0; - virtual const ScatterProcess * cloneScatter( const ScatterProcess * ) const = 0; - virtual void deallocateScatter( const ScatterProcess * ) const = 0; - virtual double crossSectionUncached( const ScatterProcess&, - const double* neutron ) const = 0; - virtual void sampleScatterUncached( const ScatterProcess&, - std::function& rng, - double* neutron ) const = 0; - //Plumbing: + virtual const ScatterProcess* createScatter(const char* cfgstr) const = 0; + virtual const ScatterProcess* cloneScatter(const ScatterProcess*) const = 0; + virtual void deallocateScatter(const ScatterProcess*) const = 0; + virtual double crossSectionUncached( + const ScatterProcess&, const double* neutron) const = 0; + virtual void sampleScatterUncached(const ScatterProcess&, + std::function& rng, double* neutron) const = 0; + // Plumbing: static constexpr unsigned interface_id = 1001; virtual ~VirtAPI_Type1_v1() = default; VirtAPI_Type1_v1() = default; - VirtAPI_Type1_v1( const VirtAPI_Type1_v1& ) = delete; - VirtAPI_Type1_v1& operator=( const VirtAPI_Type1_v1& ) = delete; - VirtAPI_Type1_v1( VirtAPI_Type1_v1&& ) = delete; - VirtAPI_Type1_v1& operator=( VirtAPI_Type1_v1&& ) = delete; + VirtAPI_Type1_v1(const VirtAPI_Type1_v1&) = delete; + VirtAPI_Type1_v1& operator=(const VirtAPI_Type1_v1&) = delete; + VirtAPI_Type1_v1(VirtAPI_Type1_v1&&) = delete; + VirtAPI_Type1_v1& operator=(VirtAPI_Type1_v1&&) = delete; }; } // namespace NCrystalVirtualAPI @@ -47,10 +46,9 @@ class NCrystalScatProc final { NCrystalScatProc(const char* cfgstr) : api_(load_ncrystal_api()), p_(api_->createScatter(cfgstr)) - { - } + {} - //Note: Neutron state array is {ekin,ux,uy,uz} + // Note: Neutron state array is {ekin,ux,uy,uz} double cross_section(const double* neutron_state) const { @@ -65,14 +63,14 @@ class NCrystalScatProc final { NCrystalScatProc clone() const { NCrystalScatProc c; - if ( p_ ) { + if (p_) { c.api_ = api_; - c.p_ = api_->cloneScatter( p_ ); + c.p_ = api_->cloneScatter(p_); } return c; } - //Plumbing (move-only semantics, but supports explicit clone): + // Plumbing (move-only semantics, but supports explicit clone): NCrystalScatProc(const NCrystalScatProc&) = delete; NCrystalScatProc& operator=(const NCrystalScatProc&) = delete; diff --git a/src/ncrystal_interface.cpp b/src/ncrystal_interface.cpp index 3a4e161bec0..935d2b8850a 100644 --- a/src/ncrystal_interface.cpp +++ b/src/ncrystal_interface.cpp @@ -10,16 +10,14 @@ namespace openmc { // NCrystalMat implementation //============================================================================== -NCrystalMat::NCrystalMat(const std::string& cfg) - : cfg_( cfg ), proc_( cfg.c_str() ) -{ -} +NCrystalMat::NCrystalMat(const std::string& cfg) : cfg_(cfg), proc_(cfg.c_str()) +{} double NCrystalMat::xs(const Particle& p) const { // Calculate scattering XS per atom with NCrystal, only once per material - double neutron_state[4] = { p.E(), p.u().x, p.u().y, p.u().z }; - return proc_.cross_section( neutron_state ); + double neutron_state[4] = {p.E(), p.u().x, p.u().y, p.u().z}; + return proc_.cross_section(neutron_state); } void NCrystalMat::scatter(Particle& p) const @@ -27,12 +25,12 @@ void NCrystalMat::scatter(Particle& p) const // Scatter with NCrystal, using the OpenMC RNG stream: uint64_t* seed = p.current_seed(); std::function rng = [&seed]() { return prn(seed); }; - double neutron_state[4] = { p.E(), p.u().x, p.u().y, p.u().z }; - proc_.scatter(rng,neutron_state); + double neutron_state[4] = {p.E(), p.u().x, p.u().y, p.u().z}; + proc_.scatter(rng, neutron_state); // Modify attributes of particle p.E() = neutron_state[0]; Direction u_old {p.u()}; - p.u() = Direction(neutron_state[1],neutron_state[2],neutron_state[3]); + p.u() = Direction(neutron_state[1], neutron_state[2], neutron_state[3]); p.mu() = u_old.dot(p.u()); p.event_mt() = ELASTIC; } From 11e0bce334be0dc4178a66513c06e6ceec1cff53 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Tue, 25 Feb 2025 12:19:29 +0100 Subject: [PATCH 06/21] Better error messages --- openmc/material.py | 6 +++++- src/ncrystal_load.cpp | 26 +++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/openmc/material.py b/openmc/material.py index 41780ce3607..c1276dc6bb3 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -429,7 +429,11 @@ def from_ncrystal(cls, cfg, **kwargs) -> Material: """ - import NCrystal + try: + import NCrystal + except ModuleNotFoundError as e + raise RuntimeError('The .from_ncrystal method requires' + ' NCrystal to be installed.') from e nc_mat = NCrystal.createInfo(cfg) def openmc_natabund(Z): diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 8a531660bd2..9f4bc9688bd 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -1,4 +1,5 @@ #include "openmc/ncrystal_load.h" +#include "openmc/error.h" #include #include #include @@ -87,9 +88,12 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) { if (!db.ncrystal_access_virtapi_fct) { auto cfg = query_ncrystal_config(); - if (!cfg.intversion >= 4000003) - throw std::runtime_error("Could not locate a functioning and" - " recent enough NCrystal installation."); + if (! (cfg.intversion >= 4000003) ) { + //This is the most likely error message people will see: + fatal_error("Could not locate a functioning and recent enough" + " NCrystal installation (required since geometry" + " contains NCrystal materials)."); + } #ifdef NCLOAD_WINDOWS auto handle = LoadLibrary(cfg.shlibpath.c_str()); #else @@ -97,7 +101,7 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) void* handle = dlopen(cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY); #endif if (!handle) - throw std::runtime_error("Loading of the NCrystal library failed"); + fatal_error("Loading of the NCrystal library failed"); std::string symbol("ncrystal"); symbol += cfg.symbol_namespace; @@ -107,19 +111,23 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) FARPROC fproc; void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str()); if (!addr) - throw std::runtime_error("GetProcAddress(" - "ncrystal_access_virtual_api) failed"); + fatal_error("GetProcAddress(" + "ncrystal_access_virtual_api) failed"); #else dlerror(); // clear previous errors void* addr = dlsym(handle, symbol.c_str()); if (!addr) - throw std::runtime_error("dlsym(ncrystal_access_virtual_api) failed"); + fatal_error("dlsym(ncrystal_access_virtual_api) failed"); #endif db.ncrystal_access_virtapi_fct = reinterpret_cast(addr); } - return (*db.ncrystal_access_virtapi_fct)(interface_id); + void * result = (*db.ncrystal_access_virtapi_fct)(interface_id); + if (!result) + fatal_error("NCrystal installation does not support required interface."); + + return result; } NCrystalAPIDB& get_ncrystal_api_db() @@ -136,7 +144,7 @@ std::shared_ptr load_ncrystal_api() if (!db.api) { void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db); if (!raw_api) - throw std::runtime_error("Failed to access required NCrystal interface."); + fatal_error("Problems loading NCrystal."); db.api = *reinterpret_cast*>(raw_api); } return db.api; From 0041ed361dca5b7106d6ce332e68e637b07c3cfa Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Tue, 25 Feb 2025 14:31:51 +0100 Subject: [PATCH 07/21] Require NCrystal 4.1.0 or later --- include/openmc/ncrystal_load.h | 1 + src/ncrystal_load.cpp | 2 +- tools/ci/gha-install.sh | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index 39bae64fc1f..465db30d4c3 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -14,6 +14,7 @@ namespace NCrystalVirtualAPI { class VirtAPI_Type1_v1 { public: + //Note: neutron must be an array of length 4 with values {ekin,ux,uy,uz} class ScatterProcess; virtual const ScatterProcess* createScatter(const char* cfgstr) const = 0; virtual const ScatterProcess* cloneScatter(const ScatterProcess*) const = 0; diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 9f4bc9688bd..c2a37492205 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -88,7 +88,7 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) { if (!db.ncrystal_access_virtapi_fct) { auto cfg = query_ncrystal_config(); - if (! (cfg.intversion >= 4000003) ) { + if (! (cfg.intversion >= 4001000) ) { //This is the most likely error message people will see: fatal_error("Could not locate a functioning and recent enough" " NCrystal installation (required since geometry" diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index 50f110ed4e6..4106fd13608 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -16,7 +16,7 @@ fi # Install NCrystal if needed if [[ $NCRYSTAL = 'y' ]]; then - pip install 'ncrystal>=4.0.0' + pip install 'ncrystal>=4.1.0' #Basic quick verification: nctool --test fi From 611e9232b95d3e846c457b70cab72c6d35209cf7 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 26 Feb 2025 11:21:35 +0100 Subject: [PATCH 08/21] Fix syntax error --- openmc/material.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/material.py b/openmc/material.py index c1276dc6bb3..b104b374e6c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -431,7 +431,7 @@ def from_ncrystal(cls, cfg, **kwargs) -> Material: try: import NCrystal - except ModuleNotFoundError as e + except ModuleNotFoundError as e: raise RuntimeError('The .from_ncrystal method requires' ' NCrystal to be installed.') from e nc_mat = NCrystal.createInfo(cfg) From f70c1bb9f4e841a26da54b580043bbdbb3dcea18 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 26 Feb 2025 11:25:43 +0100 Subject: [PATCH 09/21] Fix lint --- include/openmc/ncrystal_load.h | 2 +- src/ncrystal_load.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index 465db30d4c3..d9287bd3f4c 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -14,7 +14,7 @@ namespace NCrystalVirtualAPI { class VirtAPI_Type1_v1 { public: - //Note: neutron must be an array of length 4 with values {ekin,ux,uy,uz} + // Note: neutron must be an array of length 4 with values {ekin,ux,uy,uz} class ScatterProcess; virtual const ScatterProcess* createScatter(const char* cfgstr) const = 0; virtual const ScatterProcess* cloneScatter(const ScatterProcess*) const = 0; diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index c2a37492205..12ee83e02d0 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -88,8 +88,8 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) { if (!db.ncrystal_access_virtapi_fct) { auto cfg = query_ncrystal_config(); - if (! (cfg.intversion >= 4001000) ) { - //This is the most likely error message people will see: + if (!(cfg.intversion >= 4001000)) { + // This is the most likely error message people will see: fatal_error("Could not locate a functioning and recent enough" " NCrystal installation (required since geometry" " contains NCrystal materials)."); @@ -123,7 +123,7 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) reinterpret_cast(addr); } - void * result = (*db.ncrystal_access_virtapi_fct)(interface_id); + void* result = (*db.ncrystal_access_virtapi_fct)(interface_id); if (!result) fatal_error("NCrystal installation does not support required interface."); From bf4f72cf3d9e26371a506e905da9d97035317335 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 26 Feb 2025 14:49:57 +0100 Subject: [PATCH 10/21] Remove a few lingering ncrystal=y/n flags --- src/output.cpp | 5 ----- tools/ci/gha-install.py | 8 ++------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index 63e7fe55331..e20868efbb1 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -314,7 +314,6 @@ void print_build_info() std::string profiling(n); std::string coverage(n); std::string mcpl(n); - std::string ncrystal(n); std::string uwuw(n); #ifdef PHDF5 @@ -332,9 +331,6 @@ void print_build_info() #ifdef OPENMC_MCPL mcpl = y; #endif -#ifdef NCRYSTAL - ncrystal = y; -#endif #ifdef USE_LIBPNG png = y; #endif @@ -362,7 +358,6 @@ void print_build_info() fmt::print("DAGMC support: {}\n", dagmc); fmt::print("libMesh support: {}\n", libmesh); fmt::print("MCPL support: {}\n", mcpl); - fmt::print("NCrystal support: {}\n", ncrystal); fmt::print("Coverage testing: {}\n", coverage); fmt::print("Profiling flags: {}\n", profiling); fmt::print("UWUW support: {}\n", uwuw); diff --git a/tools/ci/gha-install.py b/tools/ci/gha-install.py index 1eb9a55b4dc..1cc792f8d78 100644 --- a/tools/ci/gha-install.py +++ b/tools/ci/gha-install.py @@ -3,7 +3,7 @@ import subprocess -def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrystal=False): +def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False): # Create build directory and change to it shutil.rmtree('build', ignore_errors=True) os.mkdir('build') @@ -40,9 +40,6 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False, libmesh=False, ncrys libmesh_path = os.environ.get('HOME') + '/LIBMESH' cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path) - if ncrystal: - cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON') - # Build in coverage mode for coverage testing cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on') @@ -59,11 +56,10 @@ def main(): mpi = (os.environ.get('MPI') == 'y') phdf5 = (os.environ.get('PHDF5') == 'y') dagmc = (os.environ.get('DAGMC') == 'y') - ncrystal = (os.environ.get('NCRYSTAL') == 'y') libmesh = (os.environ.get('LIBMESH') == 'y') # Build and install - install(omp, mpi, phdf5, dagmc, libmesh, ncrystal) + install(omp, mpi, phdf5, dagmc, libmesh) if __name__ == '__main__': main() From b4a70bf9cccd84fc91409a02f02ab0b74f7387b6 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 26 Feb 2025 14:54:17 +0100 Subject: [PATCH 11/21] update install.rst concerning NCrystal --- docs/source/usersguide/install.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index bb54594defb..9a7d4f15eba 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -284,13 +284,13 @@ Prerequisites * NCrystal_ library for defining materials with enhanced thermal neutron transport - Adding this option allows the creation of materials from NCrystal, which - replaces the scattering kernel treatment of ACE files with a modular, - on-the-fly approach. To use it `install - `_ NCrystal and - turn on the option in the CMake configuration step:: - - cmake -DOPENMC_USE_NCRYSTAL=on .. + OpenMC supports the creation of materials from NCrystal, which replaces + the scattering kernel treatment of ACE files with a modular, on-the-fly + approach. OpenMC does not need any particular build option to use this, + but NCrystal must be installed on the system. Refer to `NCrystal + documentation + `_ for how this is + achieved. * libMesh_ mesh library framework for numerical simulations of partial differential equations From 6d7036a6ad6c458232ee6f8a0604df970884de5f Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 26 Feb 2025 15:19:19 +0100 Subject: [PATCH 12/21] Add doxygen comments --- include/openmc/ncrystal_interface.h | 2 +- include/openmc/ncrystal_load.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/openmc/ncrystal_interface.h b/include/openmc/ncrystal_interface.h index 31ff81ec04c..e9bd8ae79b9 100644 --- a/include/openmc/ncrystal_interface.h +++ b/include/openmc/ncrystal_interface.h @@ -18,7 +18,7 @@ namespace openmc { constexpr double NCRYSTAL_MAX_ENERGY {5.0}; //============================================================================== -// Wrapper class an NCrystal material +// Wrapper class for an NCrystal material //============================================================================== class NCrystalMat { diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index d9287bd3f4c..6848feca675 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -1,3 +1,6 @@ +//! \file ncrystal_load.h +//! \brief Helper class taking care of loading NCrystal at runtime. + #ifndef OPENMC_NCRYSTAL_LOAD_H #define OPENMC_NCRYSTAL_LOAD_H @@ -12,6 +15,9 @@ namespace NCrystalVirtualAPI { // white space, comments, and formatting is of course allowed. This API was // introduced in NCrystal 4.1.0. +//! Abstract base class for NCrystal interface which must be declared exactly as +// it is in NCrystal itself. + class VirtAPI_Type1_v1 { public: // Note: neutron must be an array of length 4 with values {ekin,ux,uy,uz} @@ -39,28 +45,41 @@ namespace openmc { using NCrystalAPI = NCrystalVirtualAPI::VirtAPI_Type1_v1; +//! Function which locates and loads NCrystal at runtime using the virtual API std::shared_ptr load_ncrystal_api(); + +//! Class encapsulating exactly the parts of NCrystal needed by OpenMC + class NCrystalScatProc final { public: + //! Empty constructor which does not load NCrystal NCrystalScatProc() {} + //! Load NCrystal and instantiate a scattering process + //! \param cfgstr NCrystal cfg-string defining the material. NCrystalScatProc(const char* cfgstr) : api_(load_ncrystal_api()), p_(api_->createScatter(cfgstr)) {} // Note: Neutron state array is {ekin,ux,uy,uz} + //! Returns total scattering cross section in units of barns per atom. + //! \param neutron_state array {ekin,ux,uy,uz} with ekin (eV) and direction. double cross_section(const double* neutron_state) const { return api_->crossSectionUncached(*p_, neutron_state); } + //! Returns total scattering cross section in units of barns per atom. + //! \param rng function returning random numbers in the unit interval + //! \param neutron_state array {ekin,ux,uy,uz} with ekin (eV) and direction. void scatter(std::function& rng, double* neutron_state) const { api_->sampleScatterUncached(*p_, rng, neutron_state); } + //! Clones the object which is otherwise move-only NCrystalScatProc clone() const { NCrystalScatProc c; From 7f600afc8074ba528dabb540c4f8ae68335e6a0e Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 26 Feb 2025 18:06:57 +0100 Subject: [PATCH 13/21] fix lint --- include/openmc/ncrystal_load.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index 6848feca675..fb36cc3b192 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -48,7 +48,6 @@ using NCrystalAPI = NCrystalVirtualAPI::VirtAPI_Type1_v1; //! Function which locates and loads NCrystal at runtime using the virtual API std::shared_ptr load_ncrystal_api(); - //! Class encapsulating exactly the parts of NCrystal needed by OpenMC class NCrystalScatProc final { From 165e582b0f067ff1a81776d88a2e48b4dabdf354 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Sat, 1 Mar 2025 11:03:03 +0100 Subject: [PATCH 14/21] Add src/ncrystal_load.cpp to CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 090305fd092..c77366de78d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -61,6 +61,7 @@ src/random_ray/ @jtramm # NCrystal interface src/ncrystal_interface.cpp @marquezj @tkittel +src/ncrystal_load.cpp @marquezj @tkittel # MCPL interface src/mcpl_interface.cpp @ebknudsen From 42e96ea59fee080bb298ed60f10c5027ce5f7425 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Mar 2025 09:15:26 -0600 Subject: [PATCH 15/21] Remove one mention of OPENMC_USE_NCRYSTAL --- docs/source/usersguide/install.rst | 6 ------ include/openmc/ncrystal_load.h | 6 ++++-- src/source.cpp | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 9a7d4f15eba..0aa561ee3d7 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -393,12 +393,6 @@ OPENMC_USE_MCPL Turns on support for reading MCPL_ source files and writing MCPL source points and surface sources. (Default: off) -OPENMC_USE_NCRYSTAL - Turns on support for NCrystal materials. NCrystal must be `installed - `_ and `initialized - `_. - (Default: off) - OPENMC_USE_LIBMESH Enables the use of unstructured mesh tallies with libMesh_. (Default: off) diff --git a/include/openmc/ncrystal_load.h b/include/openmc/ncrystal_load.h index fb36cc3b192..d85f240906c 100644 --- a/include/openmc/ncrystal_load.h +++ b/include/openmc/ncrystal_load.h @@ -4,8 +4,10 @@ #ifndef OPENMC_NCRYSTAL_LOAD_H #define OPENMC_NCRYSTAL_LOAD_H -#include -#include +#include // for swap +#include // for function +#include // for shared_ptr +#include // for move namespace NCrystalVirtualAPI { diff --git a/src/source.cpp b/src/source.cpp index 2116809f2dd..c1b4ce260a3 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -4,7 +4,7 @@ #define HAS_DYNAMIC_LINKING #endif -#include // for move +#include // for move #ifdef HAS_DYNAMIC_LINKING #include // for dlopen, dlsym, dlclose, dlerror From 15aa74a51dc8bc61a7400b5babf2074e62afc6e9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 3 Mar 2025 11:56:29 -0600 Subject: [PATCH 16/21] Use fmt::format in one place --- src/ncrystal_load.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 12ee83e02d0..33c61e042aa 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -1,5 +1,8 @@ #include "openmc/ncrystal_load.h" #include "openmc/error.h" + +#include + #include #include #include @@ -103,9 +106,8 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) if (!handle) fatal_error("Loading of the NCrystal library failed"); - std::string symbol("ncrystal"); - symbol += cfg.symbol_namespace; - symbol += "_access_virtual_api"; + std::string symbol = + fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace); #ifdef NCLOAD_WINDOWS FARPROC fproc; From 78e75fa31f003a4ae48de0b7a94c2dd423296a02 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Mon, 3 Mar 2025 20:08:45 +0100 Subject: [PATCH 17/21] Only check WIN32 not _WIN32 in ncrystal_load.cpp --- src/ncrystal_load.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 33c61e042aa..0c3f4498f99 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -8,7 +8,7 @@ #include #include -#if !defined(NCLOAD_WINDOWS) && (defined(_WIN32) || defined(WIN32)) +#if !defined(NCLOAD_WINDOWS) && defined(_WIN32) #define NCLOAD_WINDOWS #endif #ifdef NCLOAD_WINDOWS From 14e741a63f3f61aa88a5498bb6ffc9677ee9e6fc Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Mon, 3 Mar 2025 20:08:58 +0100 Subject: [PATCH 18/21] Remove unused variable in Windows path --- src/ncrystal_load.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 0c3f4498f99..715cc83be6d 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -110,7 +110,6 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace); #ifdef NCLOAD_WINDOWS - FARPROC fproc; void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str()); if (!addr) fatal_error("GetProcAddress(" From d01339e6af4d97f5d05fb21aeae141f49b4b721c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Mar 2025 16:24:29 -0600 Subject: [PATCH 19/21] Simplify ifdefs in ncrystal_load.cpp --- src/ncrystal_load.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 715cc83be6d..3970028703d 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -1,23 +1,23 @@ #include "openmc/ncrystal_load.h" -#include "openmc/error.h" + +#include // for isspace +#include // for strtoul +#include // for shared_ptr +#include // for mutex, lock_guard +#include #include +#include // for popen, pclose -#include -#include -#include -#include +#include "openmc/error.h" -#if !defined(NCLOAD_WINDOWS) && defined(_WIN32) -#define NCLOAD_WINDOWS -#endif -#ifdef NCLOAD_WINDOWS +#ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif -#include +#include // for LoadLibrary, GetProcAddress #else -#include +#include // for dlopen, dlsym, dlerror #endif namespace openmc { @@ -31,8 +31,7 @@ struct NCrystalConfig { NCrystalConfig query_ncrystal_config() { - char buffer[4096]; -#ifdef NCLOAD_WINDOWS +#ifdef _WIN32 FILE* pipe = _popen("ncrystal-config --show " "intversion shlibpath namespace", "r"); @@ -70,7 +69,7 @@ NCrystalConfig query_ncrystal_config() res.intversion = 0; // failure } -#ifdef NCLOAD_WINDOWS +#ifdef _WIN32 auto returnCode = _pclose(pipe); #else auto returnCode = pclose(pipe); @@ -83,7 +82,7 @@ NCrystalConfig query_ncrystal_config() struct NCrystalAPIDB { std::mutex mtx; std::shared_ptr api; - typedef void* (*FctSignature)(int); + using FctSignature = void* (*)(int); FctSignature ncrystal_access_virtapi_fct = nullptr; }; @@ -97,7 +96,7 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) " NCrystal installation (required since geometry" " contains NCrystal materials)."); } -#ifdef NCLOAD_WINDOWS +#ifdef _WIN32 auto handle = LoadLibrary(cfg.shlibpath.c_str()); #else dlerror(); // clear previous errors @@ -109,7 +108,7 @@ void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db) std::string symbol = fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace); -#ifdef NCLOAD_WINDOWS +#ifdef _WIN32 void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str()); if (!addr) fatal_error("GetProcAddress(" From cb131d2bf711a196fc2a987c51c0044e5029f0bd Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 4 Mar 2025 16:27:50 -0600 Subject: [PATCH 20/21] Remove ncrystal-specific build config --- .github/workflows/ci.yml | 8 +------- tools/ci/gha-install.sh | 9 +++------ tools/ci/gha-script.sh | 5 ----- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fe3e1557b6..877ec68338e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,6 @@ jobs: mpi: [n, y] omp: [n, y] dagmc: [n] - ncrystal: [n] libmesh: [n] event: [n] vectfit: [n] @@ -45,10 +44,6 @@ jobs: python-version: "3.11" mpi: y omp: y - - ncrystal: y - python-version: "3.11" - mpi: n - omp: n - libmesh: y python-version: "3.11" mpi: y @@ -66,7 +61,7 @@ jobs: omp: n mpi: y name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }}, - mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }}, ncrystal=${{ matrix.ncrystal }}, + mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }}, libmesh=${{ matrix.libmesh }}, event=${{ matrix.event }} vectfit=${{ matrix.vectfit }})" @@ -75,7 +70,6 @@ jobs: PHDF5: ${{ matrix.mpi }} OMP: ${{ matrix.omp }} DAGMC: ${{ matrix.dagmc }} - NCRYSTAL: ${{ matrix.ncrystal }} EVENT: ${{ matrix.event }} VECTFIT: ${{ matrix.vectfit }} LIBMESH: ${{ matrix.libmesh }} diff --git a/tools/ci/gha-install.sh b/tools/ci/gha-install.sh index 4106fd13608..d8a3a7600e4 100755 --- a/tools/ci/gha-install.sh +++ b/tools/ci/gha-install.sh @@ -14,12 +14,9 @@ if [[ $DAGMC = 'y' ]]; then ./tools/ci/gha-install-dagmc.sh fi -# Install NCrystal if needed -if [[ $NCRYSTAL = 'y' ]]; then - pip install 'ncrystal>=4.1.0' - #Basic quick verification: - nctool --test -fi +# Install NCrystal and verify installation +pip install 'ncrystal>=4.1.0' +nctool --test # Install vectfit for WMP generation if needed if [[ $VECTFIT = 'y' ]]; then diff --git a/tools/ci/gha-script.sh b/tools/ci/gha-script.sh index 4733907eb25..c7c634ffa33 100755 --- a/tools/ci/gha-script.sh +++ b/tools/ci/gha-script.sh @@ -14,10 +14,5 @@ if [[ $EVENT == 'y' ]]; then args="${args} --event " fi -# Check NCrystal installation -if [[ $NCRYSTAL = 'y' ]]; then - nctool --test -fi - # Run regression and unit tests pytest --cov=openmc -v $args tests From c34b79aa0d109a40f8591b70323aaddff68c6871 Mon Sep 17 00:00:00 2001 From: Thomas Kittelmann Date: Wed, 5 Mar 2025 08:16:13 +0100 Subject: [PATCH 21/21] Remove leftover unused variable --- src/ncrystal_load.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ncrystal_load.cpp b/src/ncrystal_load.cpp index 3970028703d..b69f3a27f4e 100644 --- a/src/ncrystal_load.cpp +++ b/src/ncrystal_load.cpp @@ -61,7 +61,6 @@ NCrystalConfig query_ncrystal_config() }; NCrystalConfig res; - bool all_ok(true); if (!readLine(res.shlibpath) || !(res.intversion = parseIntVersion(res.shlibpath)) || !readLine(res.shlibpath) || res.shlibpath.empty() ||