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]
|
mpi: [n, y]
|
||||||
omp: [n, y]
|
omp: [n, y]
|
||||||
dagmc: [n]
|
dagmc: [n]
|
||||||
ncrystal: [n]
|
|
||||||
libmesh: [n]
|
libmesh: [n]
|
||||||
event: [n]
|
event: [n]
|
||||||
vectfit: [n]
|
vectfit: [n]
|
||||||
|
|
@ -45,10 +44,6 @@ jobs:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
mpi: y
|
mpi: y
|
||||||
omp: y
|
omp: y
|
||||||
- ncrystal: y
|
|
||||||
python-version: "3.11"
|
|
||||||
mpi: n
|
|
||||||
omp: n
|
|
||||||
- libmesh: y
|
- libmesh: y
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
mpi: y
|
mpi: y
|
||||||
|
|
@ -66,7 +61,7 @@ jobs:
|
||||||
omp: n
|
omp: n
|
||||||
mpi: y
|
mpi: y
|
||||||
name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }},
|
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 }}
|
libmesh=${{ matrix.libmesh }}, event=${{ matrix.event }}
|
||||||
vectfit=${{ matrix.vectfit }})"
|
vectfit=${{ matrix.vectfit }})"
|
||||||
|
|
||||||
|
|
@ -75,7 +70,6 @@ jobs:
|
||||||
PHDF5: ${{ matrix.mpi }}
|
PHDF5: ${{ matrix.mpi }}
|
||||||
OMP: ${{ matrix.omp }}
|
OMP: ${{ matrix.omp }}
|
||||||
DAGMC: ${{ matrix.dagmc }}
|
DAGMC: ${{ matrix.dagmc }}
|
||||||
NCRYSTAL: ${{ matrix.ncrystal }}
|
|
||||||
EVENT: ${{ matrix.event }}
|
EVENT: ${{ matrix.event }}
|
||||||
VECTFIT: ${{ matrix.vectfit }}
|
VECTFIT: ${{ matrix.vectfit }}
|
||||||
LIBMESH: ${{ matrix.libmesh }}
|
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_LIBMESH "Enable support for libMesh unstructured mesh tallies" OFF)
|
||||||
option(OPENMC_USE_MPI "Enable MPI" OFF)
|
option(OPENMC_USE_MPI "Enable MPI" OFF)
|
||||||
option(OPENMC_USE_MCPL "Enable MCPL" 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)
|
option(OPENMC_USE_UWUW "Enable UWUW" OFF)
|
||||||
|
|
||||||
message(STATUS "OPENMC_USE_OPENMP ${OPENMC_USE_OPENMP}")
|
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_LIBMESH ${OPENMC_USE_LIBMESH}")
|
||||||
message(STATUS "OPENMC_USE_MPI ${OPENMC_USE_MPI}")
|
message(STATUS "OPENMC_USE_MPI ${OPENMC_USE_MPI}")
|
||||||
message(STATUS "OPENMC_USE_MCPL ${OPENMC_USE_MCPL}")
|
message(STATUS "OPENMC_USE_MCPL ${OPENMC_USE_MCPL}")
|
||||||
message(STATUS "OPENMC_USE_NCRYSTAL ${OPENMC_USE_NCRYSTAL}")
|
|
||||||
message(STATUS "OPENMC_USE_UWUW ${OPENMC_USE_UWUW}")
|
message(STATUS "OPENMC_USE_UWUW ${OPENMC_USE_UWUW}")
|
||||||
|
|
||||||
# Warnings for deprecated options
|
# Warnings for deprecated options
|
||||||
|
|
@ -120,23 +118,6 @@ macro(find_package_write_status pkg)
|
||||||
endif()
|
endif()
|
||||||
endmacro()
|
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
|
# DAGMC Geometry Support - need DAGMC/MOAB
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
|
@ -372,6 +353,7 @@ list(APPEND libopenmc_SOURCES
|
||||||
src/mgxs.cpp
|
src/mgxs.cpp
|
||||||
src/mgxs_interface.cpp
|
src/mgxs_interface.cpp
|
||||||
src/ncrystal_interface.cpp
|
src/ncrystal_interface.cpp
|
||||||
|
src/ncrystal_load.cpp
|
||||||
src/nuclide.cpp
|
src/nuclide.cpp
|
||||||
src/output.cpp
|
src/output.cpp
|
||||||
src/particle.cpp
|
src/particle.cpp
|
||||||
|
|
@ -548,11 +530,6 @@ if (OPENMC_USE_MCPL)
|
||||||
target_link_libraries(libopenmc MCPL::mcpl)
|
target_link_libraries(libopenmc MCPL::mcpl)
|
||||||
endif()
|
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
|
# Log build info that this executable can report later
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ Dockerfile @shimwell
|
||||||
src/random_ray/ @jtramm
|
src/random_ray/ @jtramm
|
||||||
|
|
||||||
# NCrystal interface
|
# NCrystal interface
|
||||||
src/ncrystal_interface.cpp @marquezj
|
src/ncrystal_interface.cpp @marquezj @tkittel
|
||||||
|
src/ncrystal_load.cpp @marquezj @tkittel
|
||||||
|
|
||||||
# MCPL interface
|
# MCPL interface
|
||||||
src/mcpl_interface.cpp @ebknudsen
|
src/mcpl_interface.cpp @ebknudsen
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,6 @@ if(@OPENMC_USE_DAGMC@)
|
||||||
find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@)
|
find_package(DAGMC REQUIRED HINTS @DAGMC_DIR@)
|
||||||
endif()
|
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@)
|
if(@OPENMC_USE_LIBMESH@)
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
list(APPEND CMAKE_PREFIX_PATH @LIBMESH_PREFIX@)
|
list(APPEND CMAKE_PREFIX_PATH @LIBMESH_PREFIX@)
|
||||||
|
|
|
||||||
|
|
@ -284,13 +284,13 @@ Prerequisites
|
||||||
|
|
||||||
* NCrystal_ library for defining materials with enhanced thermal neutron transport
|
* NCrystal_ library for defining materials with enhanced thermal neutron transport
|
||||||
|
|
||||||
Adding this option allows the creation of materials from NCrystal, which
|
OpenMC supports the creation of materials from NCrystal, which replaces
|
||||||
replaces the scattering kernel treatment of ACE files with a modular,
|
the scattering kernel treatment of ACE files with a modular, on-the-fly
|
||||||
on-the-fly approach. To use it `install
|
approach. OpenMC does not need any particular build option to use this,
|
||||||
<https://github.com/mctools/ncrystal/wiki/Get-NCrystal>`_ NCrystal and
|
but NCrystal must be installed on the system. Refer to `NCrystal
|
||||||
turn on the option in the CMake configuration step::
|
documentation
|
||||||
|
<https://github.com/mctools/ncrystal/wiki/Get-NCrystal>`_ for how this is
|
||||||
cmake -DOPENMC_USE_NCRYSTAL=on ..
|
achieved.
|
||||||
|
|
||||||
* libMesh_ mesh library framework for numerical simulations of partial differential equations
|
* 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
|
Turns on support for reading MCPL_ source files and writing MCPL source points
|
||||||
and surface sources. (Default: off)
|
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
|
OPENMC_USE_LIBMESH
|
||||||
Enables the use of unstructured mesh tallies with libMesh_. (Default: off)
|
Enables the use of unstructured mesh tallies with libMesh_. (Default: off)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
#ifndef OPENMC_NCRYSTAL_INTERFACE_H
|
#ifndef OPENMC_NCRYSTAL_INTERFACE_H
|
||||||
#define OPENMC_NCRYSTAL_INTERFACE_H
|
#define OPENMC_NCRYSTAL_INTERFACE_H
|
||||||
|
|
||||||
#ifdef NCRYSTAL
|
#include "openmc/ncrystal_load.h"
|
||||||
#include "NCrystal/NCrystal.hh"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "openmc/particle.h"
|
#include "openmc/particle.h"
|
||||||
|
|
||||||
#include <cstdint> // for uint64_t
|
#include <cstdint> // for uint64_t
|
||||||
|
|
@ -17,28 +14,25 @@ namespace openmc {
|
||||||
// Constants
|
// Constants
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
extern "C" const bool NCRYSTAL_ENABLED;
|
|
||||||
|
|
||||||
//! Energy in [eV] to switch between NCrystal and ENDF
|
//! Energy in [eV] to switch between NCrystal and ENDF
|
||||||
constexpr double NCRYSTAL_MAX_ENERGY {5.0};
|
constexpr double NCRYSTAL_MAX_ENERGY {5.0};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Wrapper class an NCrystal material
|
// Wrapper class for an NCrystal material
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
class NCrystalMat {
|
class NCrystalMat {
|
||||||
public:
|
public:
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Constructors
|
// Constructors
|
||||||
NCrystalMat() = default;
|
NCrystalMat() = default; // empty object
|
||||||
explicit NCrystalMat(const std::string& cfg);
|
explicit NCrystalMat(const std::string& cfg);
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Methods
|
// Methods
|
||||||
|
|
||||||
#ifdef NCRYSTAL
|
//! Return configuration string:
|
||||||
//! Return configuration string
|
const std::string& cfg() const { return cfg_; }
|
||||||
std::string cfg() const;
|
|
||||||
|
|
||||||
//! Get cross section from NCrystal material
|
//! Get cross section from NCrystal material
|
||||||
//
|
//
|
||||||
|
|
@ -52,25 +46,21 @@ public:
|
||||||
void scatter(Particle& p) const;
|
void scatter(Particle& p) const;
|
||||||
|
|
||||||
//! Whether the object holds a valid NCrystal material
|
//! Whether the object holds a valid NCrystal material
|
||||||
operator bool() const;
|
operator bool() const { return !cfg_.empty(); }
|
||||||
#else
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
NCrystalMat clone() const
|
||||||
// Trivial methods when compiling without NCRYSTAL
|
{
|
||||||
std::string cfg() const { return ""; }
|
NCrystalMat c;
|
||||||
double xs(const Particle& p) const { return -1.0; }
|
c.cfg_ = cfg_;
|
||||||
void scatter(Particle& p) const {}
|
c.proc_ = proc_.clone();
|
||||||
operator bool() const { return false; }
|
return c;
|
||||||
#endif
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// Data members (only present when compiling with NCrystal support)
|
// Data members (only present when compiling with NCrystal support)
|
||||||
#ifdef NCRYSTAL
|
std::string cfg_; //!< NCrystal configuration string
|
||||||
std::string cfg_; //!< NCrystal configuration string
|
NCrystalScatProc proc_; //!< NCrystal scatter process
|
||||||
std::shared_ptr<const NCrystal::ProcImpl::Process>
|
|
||||||
ptr_; //!< Pointer to NCrystal material object
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
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():
|
def _dagmc_enabled():
|
||||||
return c_bool.in_dll(_dll, "DAGMC_ENABLED").value
|
return c_bool.in_dll(_dll, "DAGMC_ENABLED").value
|
||||||
|
|
||||||
def _ncrystal_enabled():
|
|
||||||
return c_bool.in_dll(_dll, "NCRYSTAL_ENABLED").value
|
|
||||||
|
|
||||||
def _coord_levels():
|
def _coord_levels():
|
||||||
return c_int.in_dll(_dll, "n_coord_levels").value
|
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)
|
nc_mat = NCrystal.createInfo(cfg)
|
||||||
|
|
||||||
def openmc_natabund(Z):
|
def openmc_natabund(Z):
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ Material& Material::clone()
|
||||||
mat->name_ = name_;
|
mat->name_ = name_;
|
||||||
mat->nuclide_ = nuclide_;
|
mat->nuclide_ = nuclide_;
|
||||||
mat->element_ = element_;
|
mat->element_ = element_;
|
||||||
mat->ncrystal_mat_ = ncrystal_mat_;
|
mat->ncrystal_mat_ = ncrystal_mat_.clone();
|
||||||
mat->atom_density_ = atom_density_;
|
mat->atom_density_ = atom_density_;
|
||||||
mat->density_ = density_;
|
mat->density_ = density_;
|
||||||
mat->density_gpcc_ = density_gpcc_;
|
mat->density_gpcc_ = density_gpcc_;
|
||||||
|
|
|
||||||
|
|
@ -7,89 +7,34 @@
|
||||||
namespace openmc {
|
namespace openmc {
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Constants
|
// NCrystalMat implementation
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifdef NCRYSTAL
|
NCrystalMat::NCrystalMat(const std::string& cfg) : cfg_(cfg), proc_(cfg.c_str())
|
||||||
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_;
|
|
||||||
}
|
|
||||||
|
|
||||||
double NCrystalMat::xs(const Particle& p) const
|
double NCrystalMat::xs(const Particle& p) const
|
||||||
{
|
{
|
||||||
// Calculate scattering XS per atom with NCrystal, only once per material
|
// Calculate scattering XS per atom with NCrystal, only once per material
|
||||||
NCrystal::CachePtr dummy_cache;
|
double neutron_state[4] = {p.E(), p.u().x, p.u().y, p.u().z};
|
||||||
auto nc_energy = NCrystal::NeutronEnergy {p.E()};
|
return proc_.cross_section(neutron_state);
|
||||||
return ptr_->crossSection(dummy_cache, nc_energy, {p.u().x, p.u().y, p.u().z})
|
|
||||||
.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NCrystalMat::scatter(Particle& p) const
|
void NCrystalMat::scatter(Particle& p) const
|
||||||
{
|
{
|
||||||
NCrystalRNGWrapper rng(p.current_seed()); // Initialize RNG
|
// Scatter with NCrystal, using the OpenMC RNG stream:
|
||||||
// create a cache pointer for multi thread physics
|
uint64_t* seed = p.current_seed();
|
||||||
NCrystal::CachePtr dummy_cache;
|
std::function<double()> rng = [&seed]() { return prn(seed); };
|
||||||
auto nc_energy = NCrystal::NeutronEnergy {p.E()};
|
double neutron_state[4] = {p.E(), p.u().x, p.u().y, p.u().z};
|
||||||
auto outcome = ptr_->sampleScatter(
|
proc_.scatter(rng, neutron_state);
|
||||||
dummy_cache, rng, nc_energy, {p.u().x, p.u().y, p.u().z});
|
|
||||||
|
|
||||||
// Modify attributes of particle
|
// Modify attributes of particle
|
||||||
p.E() = outcome.ekin.get();
|
p.E() = neutron_state[0];
|
||||||
Direction u_old {p.u()};
|
Direction u_old {p.u()};
|
||||||
p.u() =
|
p.u() = Direction(neutron_state[1], neutron_state[2], neutron_state[3]);
|
||||||
Direction(outcome.direction[0], outcome.direction[1], outcome.direction[2]);
|
|
||||||
p.mu() = u_old.dot(p.u());
|
p.mu() = u_old.dot(p.u());
|
||||||
p.event_mt() = ELASTIC;
|
p.event_mt() = ELASTIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
NCrystalMat::operator bool() const
|
|
||||||
{
|
|
||||||
return ptr_.get();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// Functions
|
// 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 profiling(n);
|
||||||
std::string coverage(n);
|
std::string coverage(n);
|
||||||
std::string mcpl(n);
|
std::string mcpl(n);
|
||||||
std::string ncrystal(n);
|
|
||||||
std::string uwuw(n);
|
std::string uwuw(n);
|
||||||
|
|
||||||
#ifdef PHDF5
|
#ifdef PHDF5
|
||||||
|
|
@ -332,9 +331,6 @@ void print_build_info()
|
||||||
#ifdef OPENMC_MCPL
|
#ifdef OPENMC_MCPL
|
||||||
mcpl = y;
|
mcpl = y;
|
||||||
#endif
|
#endif
|
||||||
#ifdef NCRYSTAL
|
|
||||||
ncrystal = y;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_LIBPNG
|
#ifdef USE_LIBPNG
|
||||||
png = y;
|
png = y;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -362,7 +358,6 @@ void print_build_info()
|
||||||
fmt::print("DAGMC support: {}\n", dagmc);
|
fmt::print("DAGMC support: {}\n", dagmc);
|
||||||
fmt::print("libMesh support: {}\n", libmesh);
|
fmt::print("libMesh support: {}\n", libmesh);
|
||||||
fmt::print("MCPL support: {}\n", mcpl);
|
fmt::print("MCPL support: {}\n", mcpl);
|
||||||
fmt::print("NCrystal support: {}\n", ncrystal);
|
|
||||||
fmt::print("Coverage testing: {}\n", coverage);
|
fmt::print("Coverage testing: {}\n", coverage);
|
||||||
fmt::print("Profiling flags: {}\n", profiling);
|
fmt::print("Profiling flags: {}\n", profiling);
|
||||||
fmt::print("UWUW support: {}\n", uwuw);
|
fmt::print("UWUW support: {}\n", uwuw);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#define HAS_DYNAMIC_LINKING
|
#define HAS_DYNAMIC_LINKING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <algorithm> // for move
|
#include <utility> // for move
|
||||||
|
|
||||||
#ifdef HAS_DYNAMIC_LINKING
|
#ifdef HAS_DYNAMIC_LINKING
|
||||||
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
|
#include <dlfcn.h> // for dlopen, dlsym, dlclose, dlerror
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import numpy as np
|
||||||
import openmc
|
import openmc
|
||||||
import openmc.lib
|
import openmc.lib
|
||||||
import pytest
|
import pytest
|
||||||
|
import shutil
|
||||||
|
|
||||||
from tests.testing_harness import PyAPITestHarness
|
from tests.testing_harness import PyAPITestHarness
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipif(
|
pytestmark = pytest.mark.skipif(
|
||||||
not openmc.lib._ncrystal_enabled(),
|
not shutil.which('ncrystal-config'),
|
||||||
reason="NCrystal materials are not enabled.")
|
reason="NCrystal is not installed.")
|
||||||
|
|
||||||
|
|
||||||
def pencil_beam_model(cfg, E0, N):
|
def pencil_beam_model(cfg, E0, N):
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import shutil
|
||||||
import subprocess
|
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
|
# Create build directory and change to it
|
||||||
shutil.rmtree('build', ignore_errors=True)
|
shutil.rmtree('build', ignore_errors=True)
|
||||||
os.mkdir('build')
|
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'
|
libmesh_path = os.environ.get('HOME') + '/LIBMESH'
|
||||||
cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path)
|
cmake_cmd.append('-DCMAKE_PREFIX_PATH=' + libmesh_path)
|
||||||
|
|
||||||
if ncrystal:
|
|
||||||
cmake_cmd.append('-DOPENMC_USE_NCRYSTAL=ON')
|
|
||||||
|
|
||||||
# Build in coverage mode for coverage testing
|
# Build in coverage mode for coverage testing
|
||||||
cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on')
|
cmake_cmd.append('-DOPENMC_ENABLE_COVERAGE=on')
|
||||||
|
|
||||||
|
|
@ -59,11 +56,10 @@ def main():
|
||||||
mpi = (os.environ.get('MPI') == 'y')
|
mpi = (os.environ.get('MPI') == 'y')
|
||||||
phdf5 = (os.environ.get('PHDF5') == 'y')
|
phdf5 = (os.environ.get('PHDF5') == 'y')
|
||||||
dagmc = (os.environ.get('DAGMC') == 'y')
|
dagmc = (os.environ.get('DAGMC') == 'y')
|
||||||
ncrystal = (os.environ.get('NCRYSTAL') == 'y')
|
|
||||||
libmesh = (os.environ.get('LIBMESH') == 'y')
|
libmesh = (os.environ.get('LIBMESH') == 'y')
|
||||||
|
|
||||||
# Build and install
|
# Build and install
|
||||||
install(omp, mpi, phdf5, dagmc, libmesh, ncrystal)
|
install(omp, mpi, phdf5, dagmc, libmesh)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,9 @@ if [[ $DAGMC = 'y' ]]; then
|
||||||
./tools/ci/gha-install-dagmc.sh
|
./tools/ci/gha-install-dagmc.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install NCrystal if needed
|
# Install NCrystal and verify installation
|
||||||
if [[ $NCRYSTAL = 'y' ]]; then
|
pip install 'ncrystal>=4.1.0'
|
||||||
pip install 'ncrystal>=4.0.0'
|
nctool --test
|
||||||
#Basic quick verification:
|
|
||||||
nctool --test
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install vectfit for WMP generation if needed
|
# Install vectfit for WMP generation if needed
|
||||||
if [[ $VECTFIT = 'y' ]]; then
|
if [[ $VECTFIT = 'y' ]]; then
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,5 @@ if [[ $EVENT == 'y' ]]; then
|
||||||
args="${args} --event "
|
args="${args} --event "
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check NCrystal installation
|
|
||||||
if [[ $NCRYSTAL = 'y' ]]; then
|
|
||||||
nctool --test
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run regression and unit tests
|
# Run regression and unit tests
|
||||||
pytest --cov=openmc -v $args tests
|
pytest --cov=openmc -v $args tests
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue