Make MCPL a Runtime Optional Dependency (#3429)

Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
Ahnaf Tahmid Chowdhury 2025-07-17 08:46:39 +06:00 committed by GitHub
parent 24f78e6e92
commit 5318ea6e2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 438 additions and 203 deletions

View file

@ -36,7 +36,6 @@ option(OPENMC_ENABLE_COVERAGE "Compile with coverage analysis flags"
option(OPENMC_USE_DAGMC "Enable support for DAGMC (CAD) geometry" OFF)
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_UWUW "Enable UWUW" OFF)
option(OPENMC_FORCE_VENDORED_LIBS "Explicitly use submodules defined in 'vendor'" OFF)
@ -47,7 +46,6 @@ message(STATUS "OPENMC_ENABLE_COVERAGE ${OPENMC_ENABLE_COVERAGE}")
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_UWUW ${OPENMC_USE_UWUW}")
message(STATUS "OPENMC_FORCE_VENDORED_LIBS ${OPENMC_FORCE_VENDORED_LIBS}")
@ -191,22 +189,6 @@ if(${HDF5_VERSION} VERSION_GREATER_EQUAL 1.12.0)
list(APPEND cxxflags -DH5Oget_info_by_idx_vers=1 -DH5O_info_t_vers=1)
endif()
#===============================================================================
# MCPL
#===============================================================================
if (OPENMC_USE_MCPL)
if (NOT DEFINED MCPL_DIR)
execute_process(
COMMAND mcpl-config --show cmakedir
OUTPUT_VARIABLE MCPL_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
find_package(MCPL REQUIRED)
message(STATUS "Found MCPL: ${MCPL_DIR} (found version \"${MCPL_VERSION}\")")
endif()
#===============================================================================
# Set compile/link flags based on which compiler is being used
#===============================================================================
@ -557,11 +539,6 @@ if (OPENMC_BUILD_TESTS)
add_subdirectory(tests/cpp_unit_tests)
endif()
if (OPENMC_USE_MCPL)
target_compile_definitions(libopenmc PUBLIC OPENMC_MCPL)
target_link_libraries(libopenmc MCPL::mcpl)
endif()
#===============================================================================
# Log build info that this executable can report later
#===============================================================================

View file

@ -29,10 +29,6 @@ if(@OPENMC_USE_OPENMP@)
find_package(OpenMP REQUIRED)
endif()
if(@OPENMC_USE_MCPL@)
find_package(MCPL REQUIRED HINTS @MCPL_DIR@)
endif()
if(@OPENMC_USE_UWUW@ AND NOT ${DAGMC_BUILD_UWUW})
message(FATAL_ERROR "UWUW is enabled in OpenMC but the DAGMC installation discovered was not configured with UWUW.")
endif()

View file

@ -375,10 +375,6 @@ OPENMC_USE_DAGMC
should also be defined as `DAGMC_ROOT` in the CMake configuration command.
(Default: off)
OPENMC_USE_MCPL
Turns on support for reading MCPL_ source files and writing MCPL source points
and surface sources. (Default: off)
OPENMC_USE_LIBMESH
Enables the use of unstructured mesh tallies with libMesh_. (Default: off)

View file

@ -9,12 +9,6 @@
namespace openmc {
//==============================================================================
// Constants
//==============================================================================
extern "C" const bool MCPL_ENABLED;
//==============================================================================
// Functions
//==============================================================================
@ -29,14 +23,18 @@ vector<SourceSite> mcpl_source_sites(std::string path);
//
//! \param[in] filename Path to MCPL file
//! \param[in] source_bank Vector of SourceSites to write to file for this
//! MPI rank. Note that this can't be const due to
//! it being used as work space by MPI.
//! \param[in] bank_indx Pointer to vector of site index ranges over all
//! MPI ranks. This can be computed by calling
//! calculate_parallel_index_vector on
//! source_bank.size().
//! MPI rank.
//! \param[in] bank_index Pointer to vector of site index ranges over all
//! MPI ranks.
void write_mcpl_source_point(const char* filename, span<SourceSite> source_bank,
const vector<int64_t>& bank_index);
//! Check if MCPL functionality is available
bool is_mcpl_interface_available();
//! Initialize the MCPL interface
void initialize_mcpl_interface_if_needed();
} // namespace openmc
#endif // OPENMC_MCPL_INTERFACE_H

View file

@ -46,9 +46,6 @@ def _coord_levels():
def _libmesh_enabled():
return c_bool.in_dll(_dll, "LIBMESH_ENABLED").value
def _mcpl_enabled():
return c_bool.in_dll(_dll, "MCPL_ENABLED").value
def _uwuw_enabled():
return c_bool.in_dll(_dll, "UWUW_ENABLED").value

View file

@ -11,32 +11,292 @@
#include <fmt/core.h>
#ifdef OPENMC_MCPL
#include <mcpl.h>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dlfcn.h>
#endif
// WARNING: These declarations MUST EXACTLY MATCH the structure and function
// signatures of the libmcpl being loaded at runtime. Any discrepancy will
// likely lead to crashes or incorrect behavior. This is a maintenance risk.
// MCPL 2.2.0
#pragma pack(push, 1)
struct mcpl_particle_repr_t {
double ekin;
double polarisation[3];
double position[3];
double direction[3];
double time;
double weight;
int32_t pdgcode;
uint32_t userflags;
};
#pragma pack(pop)
// Opaque struct definitions replicating the MCPL C-API to ensure ABI
// compatibility without including mcpl.h. These must be kept in sync.
struct mcpl_file_t {
void* internal;
};
struct mcpl_outfile_t {
void* internal;
};
// Function pointer types for the dynamically loaded MCPL library
using mcpl_open_file_fpt = mcpl_file_t* (*)(const char* filename);
using mcpl_hdr_nparticles_fpt = uint64_t (*)(mcpl_file_t* file_handle);
using mcpl_read_fpt = const mcpl_particle_repr_t* (*)(mcpl_file_t* file_handle);
using mcpl_close_file_fpt = void (*)(mcpl_file_t* file_handle);
using mcpl_create_outfile_fpt = mcpl_outfile_t* (*)(const char* filename);
using mcpl_hdr_set_srcname_fpt = void (*)(
mcpl_outfile_t* outfile_handle, const char* srcname);
using mcpl_add_particle_fpt = void (*)(
mcpl_outfile_t* outfile_handle, const mcpl_particle_repr_t* particle);
using mcpl_close_outfile_fpt = void (*)(mcpl_outfile_t* outfile_handle);
namespace openmc {
//==============================================================================
// Constants
//==============================================================================
#ifdef OPENMC_MCPL
const bool MCPL_ENABLED = true;
#ifdef _WIN32
using LibraryHandleType = HMODULE;
#else
const bool MCPL_ENABLED = false;
using LibraryHandleType = void*;
#endif
//==============================================================================
// Functions
//==============================================================================
std::string get_last_library_error()
{
#ifdef _WIN32
DWORD error_code = GetLastError();
if (error_code == 0)
return "No error reported by system."; // More accurate than "No error."
LPSTR message_buffer = nullptr;
size_t size =
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&message_buffer, 0, NULL);
std::string message(message_buffer, size);
LocalFree(message_buffer);
while (
!message.empty() && (message.back() == '\n' || message.back() == '\r')) {
message.pop_back();
}
return message;
#else
const char* err = dlerror();
return err ? std::string(err) : "No error reported by dlerror.";
#endif
}
#ifdef OPENMC_MCPL
SourceSite mcpl_particle_to_site(const mcpl_particle_t* particle)
struct McplApi {
mcpl_open_file_fpt open_file;
mcpl_hdr_nparticles_fpt hdr_nparticles;
mcpl_read_fpt read;
mcpl_close_file_fpt close_file;
mcpl_create_outfile_fpt create_outfile;
mcpl_hdr_set_srcname_fpt hdr_set_srcname;
mcpl_add_particle_fpt add_particle;
mcpl_close_outfile_fpt close_outfile;
explicit McplApi(LibraryHandleType lib_handle)
{
if (!lib_handle)
throw std::runtime_error(
"MCPL library handle is null during API binding.");
auto load_symbol_platform = [lib_handle](const char* name) {
void* sym = nullptr;
#ifdef _WIN32
sym = (void*)GetProcAddress(lib_handle, name);
#else
sym = dlsym(lib_handle, name);
#endif
if (!sym) {
throw std::runtime_error(
fmt::format("Failed to load MCPL symbol '{}': {}", name,
get_last_library_error()));
}
return sym;
};
open_file = reinterpret_cast<mcpl_open_file_fpt>(
load_symbol_platform("mcpl_open_file"));
hdr_nparticles = reinterpret_cast<mcpl_hdr_nparticles_fpt>(
load_symbol_platform("mcpl_hdr_nparticles"));
read = reinterpret_cast<mcpl_read_fpt>(load_symbol_platform("mcpl_read"));
close_file = reinterpret_cast<mcpl_close_file_fpt>(
load_symbol_platform("mcpl_close_file"));
create_outfile = reinterpret_cast<mcpl_create_outfile_fpt>(
load_symbol_platform("mcpl_create_outfile"));
hdr_set_srcname = reinterpret_cast<mcpl_hdr_set_srcname_fpt>(
load_symbol_platform("mcpl_hdr_set_srcname"));
add_particle = reinterpret_cast<mcpl_add_particle_fpt>(
load_symbol_platform("mcpl_add_particle"));
close_outfile = reinterpret_cast<mcpl_close_outfile_fpt>(
load_symbol_platform("mcpl_close_outfile"));
}
};
static LibraryHandleType g_mcpl_lib_handle = nullptr;
static std::unique_ptr<McplApi> g_mcpl_api;
static bool g_mcpl_init_attempted = false;
static bool g_mcpl_successfully_loaded = false;
static std::string g_mcpl_load_error_msg;
static std::once_flag g_mcpl_init_flag;
void append_error(std::string& existing_msg, const std::string& new_error)
{
if (!existing_msg.empty()) {
existing_msg += "; ";
}
existing_msg += new_error;
}
void initialize_mcpl_interface_impl()
{
g_mcpl_init_attempted = true;
g_mcpl_load_error_msg.clear();
// Try mcpl-config
if (!g_mcpl_lib_handle) {
FILE* pipe = nullptr;
#ifdef _WIN32
pipe = _popen("mcpl-config --show libpath", "r");
#else
pipe = popen("mcpl-config --show libpath 2>/dev/null", "r");
#endif
if (pipe) {
char buffer[512];
if (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
std::string shlibpath = buffer;
// Remove trailing whitespace
while (!shlibpath.empty() &&
std::isspace(static_cast<unsigned char>(shlibpath.back()))) {
shlibpath.pop_back();
}
if (!shlibpath.empty()) {
#ifdef _WIN32
g_mcpl_lib_handle = LoadLibraryA(shlibpath.c_str());
#else
g_mcpl_lib_handle = dlopen(shlibpath.c_str(), RTLD_LAZY);
#endif
if (!g_mcpl_lib_handle) {
append_error(
g_mcpl_load_error_msg, fmt::format("From mcpl-config ({}): {}",
shlibpath, get_last_library_error()));
}
}
}
#ifdef _WIN32
_pclose(pipe);
#else
pclose(pipe);
#endif
} else { // pipe failed to open
append_error(g_mcpl_load_error_msg,
"mcpl-config command not found or failed to execute");
}
}
// Try standard library names
if (!g_mcpl_lib_handle) {
#ifdef _WIN32
const char* standard_names[] = {"mcpl.dll", "libmcpl.dll"};
#else
const char* standard_names[] = {"libmcpl.so", "libmcpl.dylib"};
#endif
for (const char* name : standard_names) {
#ifdef _WIN32
g_mcpl_lib_handle = LoadLibraryA(name);
#else
g_mcpl_lib_handle = dlopen(name, RTLD_LAZY);
#endif
if (g_mcpl_lib_handle)
break;
}
if (!g_mcpl_lib_handle) {
append_error(
g_mcpl_load_error_msg, fmt::format("Using standard names (e.g. {}): {}",
standard_names[0], get_last_library_error()));
}
}
if (!g_mcpl_lib_handle) {
if (mpi::master) {
warning(fmt::format("MCPL library could not be loaded. MCPL-dependent "
"features will be unavailable. Load attempts: {}",
g_mcpl_load_error_msg.empty()
? "No specific error during load attempts."
: g_mcpl_load_error_msg));
}
g_mcpl_successfully_loaded = false;
return;
}
try {
g_mcpl_api = std::make_unique<McplApi>(g_mcpl_lib_handle);
g_mcpl_successfully_loaded = true;
// Do not call dlclose/FreeLibrary at exit. Leaking the handle is safer
// and standard practice for libraries used for the application's lifetime.
} catch (const std::runtime_error& e) {
append_error(g_mcpl_load_error_msg,
fmt::format(
"MCPL library loaded, but failed to bind symbols: {}", e.what()));
if (mpi::master) {
warning(g_mcpl_load_error_msg);
}
#ifdef _WIN32
FreeLibrary(g_mcpl_lib_handle);
#else
dlclose(g_mcpl_lib_handle);
#endif
g_mcpl_lib_handle = nullptr;
g_mcpl_successfully_loaded = false;
}
}
void initialize_mcpl_interface_if_needed()
{
std::call_once(g_mcpl_init_flag, initialize_mcpl_interface_impl);
}
bool is_mcpl_interface_available()
{
initialize_mcpl_interface_if_needed();
return g_mcpl_successfully_loaded;
}
inline void ensure_mcpl_ready_or_fatal()
{
initialize_mcpl_interface_if_needed();
if (!g_mcpl_successfully_loaded) {
fatal_error("MCPL functionality is required, but the MCPL library is not "
"available or failed to initialize. Please ensure MCPL is "
"installed and its library can be found (e.g., via PATH on "
"Windows, LD_LIBRARY_PATH on Linux, or DYLD_LIBRARY_PATH on "
"macOS). You can often install MCPL with 'pip install mcpl' or "
"'conda install mcpl'.");
}
}
SourceSite mcpl_particle_to_site(const mcpl_particle_repr_t* particle_repr)
{
SourceSite site;
switch (particle->pdgcode) {
switch (particle_repr->pdgcode) {
case 2112:
site.particle = ParticleType::neutron;
break;
@ -49,179 +309,204 @@ SourceSite mcpl_particle_to_site(const mcpl_particle_t* particle)
case -11:
site.particle = ParticleType::positron;
break;
default:
fatal_error(fmt::format(
"MCPL: Encountered unexpected PDG code {} when converting to SourceSite.",
particle_repr->pdgcode));
break;
}
// Copy position and direction
site.r.x = particle->position[0];
site.r.y = particle->position[1];
site.r.z = particle->position[2];
site.u.x = particle->direction[0];
site.u.y = particle->direction[1];
site.u.z = particle->direction[2];
site.r.x = particle_repr->position[0];
site.r.y = particle_repr->position[1];
site.r.z = particle_repr->position[2];
site.u.x = particle_repr->direction[0];
site.u.y = particle_repr->direction[1];
site.u.z = particle_repr->direction[2];
// MCPL stores kinetic energy in [MeV], time in [ms]
site.E = particle->ekin * 1e6;
site.time = particle->time * 1e-3;
site.wgt = particle->weight;
site.E = particle_repr->ekin * 1e6;
site.time = particle_repr->time * 1e-3;
site.wgt = particle_repr->weight;
return site;
}
#endif
//==============================================================================
vector<SourceSite> mcpl_source_sites(std::string path)
{
ensure_mcpl_ready_or_fatal();
vector<SourceSite> sites;
#ifdef OPENMC_MCPL
// Open MCPL file and determine number of particles
auto mcpl_file = mcpl_open_file(path.c_str());
size_t n_sites = mcpl_hdr_nparticles(mcpl_file);
mcpl_file_t* mcpl_file = g_mcpl_api->open_file(path.c_str());
if (!mcpl_file) {
fatal_error(fmt::format("MCPL: Could not open file '{}'. It might be "
"missing, inaccessible, or not a valid MCPL file.",
path));
}
for (int i = 0; i < n_sites; i++) {
// Extract particle from mcpl-file, checking if it is a neutron, photon,
// electron, or positron. Otherwise skip.
const mcpl_particle_t* particle;
int pdg = 0;
while (pdg != 2112 && pdg != 22 && pdg != 11 && pdg != -11) {
particle = mcpl_read(mcpl_file);
pdg = particle->pdgcode;
size_t n_particles_in_file = g_mcpl_api->hdr_nparticles(mcpl_file);
size_t n_skipped = 0;
if (n_particles_in_file > 0) {
sites.reserve(n_particles_in_file);
}
for (size_t i = 0; i < n_particles_in_file; ++i) {
const mcpl_particle_repr_t* p_repr = g_mcpl_api->read(mcpl_file);
if (!p_repr) {
warning(fmt::format("MCPL: Read error or unexpected end of file '{}' "
"after reading {} of {} expected particles.",
path, sites.size(), n_particles_in_file));
break;
}
if (p_repr->pdgcode == 2112 || p_repr->pdgcode == 22 ||
p_repr->pdgcode == 11 || p_repr->pdgcode == -11) {
sites.push_back(mcpl_particle_to_site(p_repr));
} else {
n_skipped++;
}
// Convert to source site and add to vector
sites.push_back(mcpl_particle_to_site(particle));
}
// Check that some sites were read
g_mcpl_api->close_file(mcpl_file);
if (n_skipped > 0 && n_particles_in_file > 0) {
double percent_skipped =
100.0 * static_cast<double>(n_skipped) / n_particles_in_file;
warning(fmt::format(
"MCPL: Skipped {} of {} total particles ({:.1f}%) in file '{}' because "
"their type is not supported by OpenMC.",
n_skipped, n_particles_in_file, percent_skipped, path));
}
if (sites.empty()) {
fatal_error("MCPL file contained no neutron, photon, electron, or positron "
"source particles.");
if (n_particles_in_file > 0) {
fatal_error(fmt::format(
"MCPL file '{}' contained {} particles, but none were of the supported "
"types (neutron, photon, electron, positron). OpenMC cannot proceed "
"without source particles.",
path, n_particles_in_file));
} else {
fatal_error(fmt::format(
"MCPL file '{}' is empty or contains no particle data.", path));
}
}
mcpl_close_file(mcpl_file);
#else
fatal_error(
"Your build of OpenMC does not support reading MCPL source files.");
#endif
return sites;
}
//==============================================================================
#ifdef OPENMC_MCPL
void write_mcpl_source_bank(mcpl_outfile_t file_id,
span<SourceSite> source_bank, const vector<int64_t>& bank_index)
void write_mcpl_source_bank_internal(mcpl_outfile_t* file_id,
span<SourceSite> local_source_bank,
const vector<int64_t>& bank_index_all_ranks)
{
int64_t dims_size = settings::n_particles;
int64_t count_size = simulation::work_per_rank;
if (mpi::master) {
// Particles are writeen to disk from the master node only
if (!file_id) {
fatal_error("MCPL: Internal error - master rank called "
"write_mcpl_source_bank_internal with null file_id.");
}
vector<SourceSite> receive_buffer;
// Save source bank sites since the array is overwritten below
for (int rank_idx = 0; rank_idx < mpi::n_procs; ++rank_idx) {
size_t num_sites_on_rank = static_cast<size_t>(
bank_index_all_ranks[rank_idx + 1] - bank_index_all_ranks[rank_idx]);
if (num_sites_on_rank == 0)
continue;
span<const SourceSite> sites_to_write;
#ifdef OPENMC_MPI
vector<SourceSite> temp_source {source_bank.begin(), source_bank.end()};
if (rank_idx == mpi::rank) {
sites_to_write = openmc::span<const SourceSite>(
local_source_bank.data(), num_sites_on_rank);
} else {
if (receive_buffer.size() < num_sites_on_rank) {
receive_buffer.resize(num_sites_on_rank);
}
MPI_Recv(receive_buffer.data(), num_sites_on_rank, mpi::source_site,
rank_idx, rank_idx, mpi::intracomm, MPI_STATUS_IGNORE);
sites_to_write = openmc::span<const SourceSite>(
receive_buffer.data(), num_sites_on_rank);
}
#else
sites_to_write = openmc::span<const SourceSite>(
local_source_bank.data(), num_sites_on_rank);
#endif
// loop over the other nodes and receive data - then write those.
for (int i = 0; i < mpi::n_procs; ++i) {
// number of particles for node node i
size_t count[] {static_cast<size_t>(bank_index[i + 1] - bank_index[i])};
#ifdef OPENMC_MPI
if (i > 0)
MPI_Recv(source_bank.data(), count[0], mpi::source_site, i, i,
mpi::intracomm, MPI_STATUS_IGNORE);
#endif
// now write the source_bank data again.
for (const auto& site : source_bank) {
// particle is now at the iterator
// write it to the mcpl-file
mcpl_particle_t p;
p.position[0] = site.r.x;
p.position[1] = site.r.y;
p.position[2] = site.r.z;
// mcpl requires that the direction vector is unit length
// which is also the case in openmc
p.direction[0] = site.u.x;
p.direction[1] = site.u.y;
p.direction[2] = site.u.z;
// MCPL stores kinetic energy in [MeV], time in [ms]
p.ekin = site.E * 1e-6;
p.time = site.time * 1e3;
p.weight = site.wgt;
for (const auto& site : sites_to_write) {
mcpl_particle_repr_t p_repr {};
p_repr.position[0] = site.r.x;
p_repr.position[1] = site.r.y;
p_repr.position[2] = site.r.z;
p_repr.direction[0] = site.u.x;
p_repr.direction[1] = site.u.y;
p_repr.direction[2] = site.u.z;
p_repr.ekin = site.E * 1e-6;
p_repr.time = site.time * 1e3;
p_repr.weight = site.wgt;
switch (site.particle) {
case ParticleType::neutron:
p.pdgcode = 2112;
p_repr.pdgcode = 2112;
break;
case ParticleType::photon:
p.pdgcode = 22;
p_repr.pdgcode = 22;
break;
case ParticleType::electron:
p.pdgcode = 11;
p_repr.pdgcode = 11;
break;
case ParticleType::positron:
p.pdgcode = -11;
p_repr.pdgcode = -11;
break;
default:
continue;
}
mcpl_add_particle(file_id, &p);
g_mcpl_api->add_particle(file_id, &p_repr);
}
}
#ifdef OPENMC_MPI
// Restore state of source bank
std::copy(temp_source.begin(), temp_source.end(), source_bank.begin());
#endif
} else {
#ifdef OPENMC_MPI
MPI_Send(source_bank.data(), count_size, mpi::source_site, 0, mpi::rank,
mpi::intracomm);
if (!local_source_bank.empty()) {
MPI_Send(local_source_bank.data(), local_source_bank.size(),
mpi::source_site, 0, mpi::rank, mpi::intracomm);
}
#endif
}
}
#endif
//==============================================================================
void write_mcpl_source_point(const char* filename, span<SourceSite> source_bank,
const vector<int64_t>& bank_index)
{
ensure_mcpl_ready_or_fatal();
std::string filename_(filename);
const auto extension = get_file_extension(filename_);
if (extension == "") {
if (extension.empty()) {
filename_.append(".mcpl");
} else if (extension != "mcpl") {
warning("write_mcpl_source_point was passed a file extension differing "
"from .mcpl, but an mcpl file will be written.");
warning(fmt::format("Specified filename '{}' has an extension '.{}', but "
"an MCPL file (.mcpl) will be written using this name.",
filename, extension));
}
#ifdef OPENMC_MCPL
mcpl_outfile_t file_id;
mcpl_outfile_t* file_id = nullptr;
std::string line;
if (mpi::master) {
file_id = mcpl_create_outfile(filename_.c_str());
file_id = g_mcpl_api->create_outfile(filename_.c_str());
if (!file_id) {
fatal_error(fmt::format(
"MCPL: Failed to create output file '{}'. Check permissions and path.",
filename_));
}
std::string src_line;
if (VERSION_DEV) {
line = fmt::format("OpenMC {0}.{1}.{2}-dev{3}", VERSION_MAJOR,
src_line = fmt::format("OpenMC {}.{}.{}-dev{}", VERSION_MAJOR,
VERSION_MINOR, VERSION_RELEASE, VERSION_COMMIT_COUNT);
} else {
line = fmt::format(
"OpenMC {0}.{1}.{2}", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE);
src_line = fmt::format(
"OpenMC {}.{}.{}", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE);
}
mcpl_hdr_set_srcname(file_id, line.c_str());
g_mcpl_api->hdr_set_srcname(file_id, src_line.c_str());
}
write_mcpl_source_bank(file_id, source_bank, bank_index);
write_mcpl_source_bank_internal(file_id, source_bank, bank_index);
if (mpi::master) {
mcpl_close_outfile(file_id);
if (file_id) {
g_mcpl_api->close_outfile(file_id);
}
}
#endif
}
} // namespace openmc

View file

@ -844,12 +844,6 @@ void read_settings_xml(pugi::xml_node root)
}
if (check_for_node(node_sp, "mcpl")) {
source_mcpl_write = get_node_value_bool(node_sp, "mcpl");
// Make sure MCPL support is enabled
if (source_mcpl_write && !MCPL_ENABLED) {
fatal_error(
"Your build of OpenMC does not support writing MCPL source files.");
}
}
if (check_for_node(node_sp, "overwrite_latest")) {
source_latest = get_node_value_bool(node_sp, "overwrite_latest");
@ -902,12 +896,6 @@ void read_settings_xml(pugi::xml_node root)
if (check_for_node(node_ssw, "mcpl")) {
surf_mcpl_write = get_node_value_bool(node_ssw, "mcpl");
// Make sure MCPL support is enabled
if (surf_mcpl_write && !MCPL_ENABLED) {
fatal_error("Your build of OpenMC does not support writing MCPL "
"surface source files.");
}
}
// Get cell information
if (check_for_node(node_ssw, "cell")) {

View file

@ -1,12 +1,11 @@
<?xml version="1.0"?>
<settings>
<run_mode>eigenvalue</run_mode>
<state_point batches="10" />
<source_point mcpl="true" separate="true" />
<eigenvalue>
<batches>10</batches>
<inactive>5</inactive>
<particles>1000</particles>
</eigenvalue>
<batches>10</batches>
<inactive>5</inactive>
<particles>1000</particles>
<source>
<space type="box">
<parameters>-4 -4 -4 4 4 4</parameters>

View file

@ -1,23 +1,23 @@
#!/usr/bin/env python
import openmc.lib
import pytest
import glob
import os
import shutil
from tests.testing_harness import *
pytestmark = pytest.mark.skipif(
not openmc.lib._mcpl_enabled(),
reason="MCPL is not enabled.")
shutil.which("mcpl-config") is None,
reason="mcpl-config command not found in PATH; MCPL is likely not available."
)
settings1="""<?xml version="1.0"?>
<settings>
<run_mode>eigenvalue</run_mode>
<state_point batches="10" />
<source_point mcpl="true" separate="true" />
<eigenvalue>
<batches>10</batches>
<inactive>5</inactive>
<particles>1000</particles>
</eigenvalue>
<batches>10</batches>
<inactive>5</inactive>
<particles>1000</particles>
<source>
<space type="box">
<parameters>-4 -4 -4 4 4 4</parameters>
@ -28,11 +28,10 @@ settings1="""<?xml version="1.0"?>
settings2 = """<?xml version="1.0"?>
<settings>
<eigenvalue>
<batches>10</batches>
<inactive>5</inactive>
<particles>1000</particles>
</eigenvalue>
<run_mode>eigenvalue</run_mode>
<batches>10</batches>
<inactive>5</inactive>
<particles>1000</particles>
<source>
<file>source.10.{}</file>
</source>