mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-21 06:25:30 -04:00
NCrystal becomes runtime rather than buildtime dependency (#3328)
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
parent
239f7fed5e
commit
ced8929128
18 changed files with 331 additions and 180 deletions
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#===============================================================================
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ Dockerfile @shimwell
|
|||
src/random_ray/ @jtramm
|
||||
|
||||
# NCrystal interface
|
||||
src/ncrystal_interface.cpp @marquezj
|
||||
src/ncrystal_interface.cpp @marquezj @tkittel
|
||||
src/ncrystal_load.cpp @marquezj @tkittel
|
||||
|
||||
# MCPL interface
|
||||
src/mcpl_interface.cpp @ebknudsen
|
||||
|
|
|
|||
|
|
@ -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@)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
<https://github.com/mctools/ncrystal/wiki/Get-NCrystal>`_ 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
|
||||
<https://github.com/mctools/ncrystal/wiki/Get-NCrystal>`_ for how this is
|
||||
achieved.
|
||||
|
||||
* libMesh_ mesh library framework for numerical simulations of partial differential equations
|
||||
|
||||
|
|
@ -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
|
||||
<https://github.com/mctools/ncrystal/wiki/Get-NCrystal>`_ and `initialized
|
||||
<https://github.com/mctools/ncrystal/wiki/Using-NCrystal#setting-up>`_.
|
||||
(Default: off)
|
||||
|
||||
OPENMC_USE_LIBMESH
|
||||
Enables the use of unstructured mesh tallies with libMesh_. (Default: off)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <cstdint> // for uint64_t
|
||||
|
|
@ -17,28 +14,25 @@ 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};
|
||||
|
||||
//==============================================================================
|
||||
// Wrapper class an NCrystal material
|
||||
// Wrapper class for an NCrystal material
|
||||
//==============================================================================
|
||||
|
||||
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 @@ public:
|
|||
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<const NCrystal::ProcImpl::Process>
|
||||
ptr_; //!< Pointer to NCrystal material object
|
||||
#endif
|
||||
std::string cfg_; //!< NCrystal configuration string
|
||||
NCrystalScatProc proc_; //!< NCrystal scatter process
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
127
include/openmc/ncrystal_load.h
Normal file
127
include/openmc/ncrystal_load.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
//! \file ncrystal_load.h
|
||||
//! \brief Helper class taking care of loading NCrystal at runtime.
|
||||
|
||||
#ifndef OPENMC_NCRYSTAL_LOAD_H
|
||||
#define OPENMC_NCRYSTAL_LOAD_H
|
||||
|
||||
#include <algorithm> // for swap
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr
|
||||
#include <utility> // for move
|
||||
|
||||
namespace NCrystalVirtualAPI {
|
||||
|
||||
// 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.
|
||||
|
||||
//! 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}
|
||||
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<double()>& 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 NCrystalVirtualAPI
|
||||
|
||||
namespace openmc {
|
||||
|
||||
using NCrystalAPI = NCrystalVirtualAPI::VirtAPI_Type1_v1;
|
||||
|
||||
//! Function which locates and loads NCrystal at runtime using the virtual API
|
||||
std::shared_ptr<const NCrystalAPI> 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<double()>& rng, double* neutron_state) const
|
||||
{
|
||||
api_->sampleScatterUncached(*p_, rng, neutron_state);
|
||||
}
|
||||
|
||||
//! Clones the object which is otherwise move-only
|
||||
NCrystalScatProc clone() const
|
||||
{
|
||||
NCrystalScatProc c;
|
||||
if (p_) {
|
||||
c.api_ = api_;
|
||||
c.p_ = api_->cloneScatter(p_);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
// Plumbing (move-only semantics, but supports explicit clone):
|
||||
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<const NCrystalAPI> api_;
|
||||
const NCrystalAPI::ScatterProcess* p_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif
|
||||
|
|
@ -40,9 +40,6 @@ else:
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -429,7 +429,11 @@ class Material(IDManagerMixin):
|
|||
|
||||
"""
|
||||
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -7,89 +7,34 @@
|
|||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Constants
|
||||
// NCrystalMat implementation
|
||||
//==============================================================================
|
||||
|
||||
#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<double>(
|
||||
std::numeric_limits<double>::min(), prn(openmc_seed_));
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t* openmc_seed_;
|
||||
};
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
// NCrystal implementation
|
||||
//==============================================================================
|
||||
|
||||
NCrystalMat::NCrystalMat(const std::string& cfg)
|
||||
{
|
||||
#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_;
|
||||
}
|
||||
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
|
||||
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<double()> 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
|
||||
//==============================================================================
|
||||
|
|
|
|||
151
src/ncrystal_load.cpp
Normal file
151
src/ncrystal_load.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
#include "openmc/ncrystal_load.h"
|
||||
|
||||
#include <cctype> // for isspace
|
||||
#include <cstdlib> // for strtoul
|
||||
#include <memory> // for shared_ptr
|
||||
#include <mutex> // for mutex, lock_guard
|
||||
#include <string>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <stdio.h> // for popen, pclose
|
||||
|
||||
#include "openmc/error.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h> // for LoadLibrary, GetProcAddress
|
||||
#else
|
||||
#include <dlfcn.h> // for dlopen, dlsym, dlerror
|
||||
#endif
|
||||
|
||||
namespace openmc {
|
||||
namespace {
|
||||
|
||||
struct NCrystalConfig {
|
||||
std::string shlibpath;
|
||||
unsigned long intversion = 0;
|
||||
std::string symbol_namespace;
|
||||
};
|
||||
|
||||
NCrystalConfig query_ncrystal_config()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
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;
|
||||
if (!readLine(res.shlibpath) ||
|
||||
!(res.intversion = parseIntVersion(res.shlibpath)) ||
|
||||
!readLine(res.shlibpath) || res.shlibpath.empty() ||
|
||||
!readLine(res.symbol_namespace)) {
|
||||
res.intversion = 0; // failure
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
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<const NCrystalAPI> api;
|
||||
using FctSignature = void* (*)(int);
|
||||
FctSignature ncrystal_access_virtapi_fct = nullptr;
|
||||
};
|
||||
|
||||
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:
|
||||
fatal_error("Could not locate a functioning and recent enough"
|
||||
" NCrystal installation (required since geometry"
|
||||
" contains NCrystal materials).");
|
||||
}
|
||||
#ifdef _WIN32
|
||||
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)
|
||||
fatal_error("Loading of the NCrystal library failed");
|
||||
|
||||
std::string symbol =
|
||||
fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace);
|
||||
|
||||
#ifdef _WIN32
|
||||
void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str());
|
||||
if (!addr)
|
||||
fatal_error("GetProcAddress("
|
||||
"ncrystal_access_virtual_api) failed");
|
||||
#else
|
||||
dlerror(); // clear previous errors
|
||||
void* addr = dlsym(handle, symbol.c_str());
|
||||
if (!addr)
|
||||
fatal_error("dlsym(ncrystal_access_virtual_api) failed");
|
||||
#endif
|
||||
db.ncrystal_access_virtapi_fct =
|
||||
reinterpret_cast<NCrystalAPIDB::FctSignature>(addr);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
static NCrystalAPIDB db;
|
||||
return db;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<const NCrystalAPI> load_ncrystal_api()
|
||||
{
|
||||
auto& db = get_ncrystal_api_db();
|
||||
std::lock_guard<std::mutex> lock(db.mtx);
|
||||
if (!db.api) {
|
||||
void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db);
|
||||
if (!raw_api)
|
||||
fatal_error("Problems loading NCrystal.");
|
||||
db.api = *reinterpret_cast<std::shared_ptr<const NCrystalAPI>*>(raw_api);
|
||||
}
|
||||
return db.api;
|
||||
}
|
||||
} // namespace openmc
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#define HAS_DYNAMIC_LINKING
|
||||
#endif
|
||||
|
||||
#include <algorithm> // for move
|
||||
#include <utility> // for move
|
||||
|
||||
#ifdef HAS_DYNAMIC_LINKING
|
||||
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import numpy as np
|
|||
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):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import shutil
|
|||
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()
|
||||
|
|
|
|||
|
|
@ -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.0.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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue