diff --git a/CMakeLists.txt b/CMakeLists.txt index 6665682dd05..364f108adc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) message(STATUS "OPENMC_USE_OPENMP ${OPENMC_USE_OPENMP}") @@ -46,7 +45,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}") # Warnings for deprecated options @@ -189,22 +187,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 #=============================================================================== @@ -535,11 +517,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 #=============================================================================== diff --git a/cmake/OpenMCConfig.cmake.in b/cmake/OpenMCConfig.cmake.in index 7416013fbc6..3fe0c1bcd16 100644 --- a/cmake/OpenMCConfig.cmake.in +++ b/cmake/OpenMCConfig.cmake.in @@ -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() diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 7a8cb80204e..8d217139699 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -368,10 +368,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) diff --git a/include/openmc/mcpl_interface.h b/include/openmc/mcpl_interface.h index e5c182280c8..e0315b739cf 100644 --- a/include/openmc/mcpl_interface.h +++ b/include/openmc/mcpl_interface.h @@ -9,12 +9,6 @@ namespace openmc { -//============================================================================== -// Constants -//============================================================================== - -extern "C" const bool MCPL_ENABLED; - //============================================================================== // Functions //============================================================================== @@ -29,14 +23,18 @@ vector 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 source_bank, const vector& 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 +#endif // OPENMC_MCPL_INTERFACE_H \ No newline at end of file diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 15642b42be3..d2a794eb158 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -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 diff --git a/src/mcpl_interface.cpp b/src/mcpl_interface.cpp index 83ef6332097..e90fc9d102a 100644 --- a/src/mcpl_interface.cpp +++ b/src/mcpl_interface.cpp @@ -11,32 +11,309 @@ #include -#ifdef OPENMC_MCPL -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include #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 openmc_local_mcpl_particle_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) + +typedef struct openmc_local_mcpl_particle_t mcpl_particle_repr_t; + +typedef void* mcpl_file_handle_repr_t; +typedef void* mcpl_outfile_handle_repr_t; + +typedef mcpl_file_handle_repr_t (*mcpl_open_file_fpt)(const char* filename); +typedef uint64_t (*mcpl_hdr_nparticles_fpt)( + mcpl_file_handle_repr_t file_handle); +typedef const mcpl_particle_repr_t* (*mcpl_read_fpt)( + mcpl_file_handle_repr_t file_handle); +typedef void (*mcpl_close_file_fpt)(mcpl_file_handle_repr_t file_handle); + +typedef mcpl_outfile_handle_repr_t (*mcpl_create_outfile_fpt)( + const char* filename); +typedef void (*mcpl_hdr_set_srcname_fpt)( + mcpl_outfile_handle_repr_t outfile_handle, const char* srcname); +typedef void (*mcpl_add_particle_fpt)(mcpl_outfile_handle_repr_t outfile_handle, + const mcpl_particle_repr_t* particle); +typedef void (*mcpl_close_outfile_fpt)( + mcpl_outfile_handle_repr_t outfile_handle); + namespace openmc { -//============================================================================== -// Constants -//============================================================================== +#ifdef _WIN32 +typedef HMODULE LibraryHandleType; +#else +typedef void* LibraryHandleType; +#endif -#ifdef OPENMC_MCPL -const bool MCPL_ENABLED = true; +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 bool MCPL_ENABLED = false; + const char* err = dlerror(); + return err ? std::string(err) : "No error reported by dlerror."; #endif +} -//============================================================================== -// Functions -//============================================================================== +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( + load_symbol_platform("mcpl_open_file")); + hdr_nparticles = reinterpret_cast( + load_symbol_platform("mcpl_hdr_nparticles")); + read = reinterpret_cast(load_symbol_platform("mcpl_read")); + close_file = reinterpret_cast( + load_symbol_platform("mcpl_close_file")); + create_outfile = reinterpret_cast( + load_symbol_platform("mcpl_create_outfile")); + hdr_set_srcname = reinterpret_cast( + load_symbol_platform("mcpl_hdr_set_srcname")); + add_particle = reinterpret_cast( + load_symbol_platform("mcpl_add_particle")); + close_outfile = reinterpret_cast( + load_symbol_platform("mcpl_close_outfile")); + } +}; + +static LibraryHandleType g_mcpl_lib_handle = nullptr; +static std::unique_ptr 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; -#ifdef OPENMC_MCPL -SourceSite mcpl_particle_to_site(const mcpl_particle_t* particle) +void append_error(std::string& existing_msg, const std::string& new_error) { - SourceSite site; + if (!existing_msg.empty()) { + existing_msg += "; "; + } + existing_msg += new_error; +} + +void mcpl_library_cleanup() +{ + g_mcpl_api.reset(); + if (g_mcpl_lib_handle) { +#ifdef _WIN32 + FreeLibrary(g_mcpl_lib_handle); +#else + dlclose(g_mcpl_lib_handle); +#endif + g_mcpl_lib_handle = nullptr; + } + g_mcpl_successfully_loaded = false; + g_mcpl_init_attempted = false; +} + +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(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(g_mcpl_lib_handle); + g_mcpl_successfully_loaded = true; + std::atexit(mcpl_library_cleanup); + } 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; + } +} - switch (particle->pdgcode) { +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(fmt::format( + "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'. " + "Last error(s): {}", + g_mcpl_load_error_msg.empty() ? "No specific error during load." + : g_mcpl_load_error_msg)); + } +} + +SourceSite mcpl_particle_to_site(const mcpl_particle_repr_t* particle_repr) +{ + SourceSite site; + switch (particle_repr->pdgcode) { case 2112: site.particle = ParticleType::neutron; break; @@ -49,179 +326,191 @@ 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 mcpl_source_sites(std::string path) { + ensure_mcpl_ready_or_fatal(); vector 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); - - 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; - } + mcpl_file_handle_repr_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)); + } - // Convert to source site and add to vector - sites.push_back(mcpl_particle_to_site(particle)); + size_t n_particles_in_file = g_mcpl_api->hdr_nparticles(mcpl_file); + if (n_particles_in_file > 0) { + sites.reserve(n_particles_in_file); } - // Check that some sites were read - if (sites.empty()) { - fatal_error("MCPL file contained no neutron, photon, electron, or positron " - "source particles."); + 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)); + } } - mcpl_close_file(mcpl_file); -#else - fatal_error( - "Your build of OpenMC does not support reading MCPL source files."); -#endif + g_mcpl_api->close_file(mcpl_file); + if (sites.empty()) { + 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).", + path, n_particles_in_file)); + } else { + fatal_error(fmt::format( + "MCPL file '{}' is empty or contains no particle data.", path)); + } + } return sites; } -//============================================================================== - -#ifdef OPENMC_MCPL -void write_mcpl_source_bank(mcpl_outfile_t file_id, - span source_bank, const vector& bank_index) +void write_mcpl_source_bank_internal(mcpl_outfile_handle_repr_t file_id, + span local_source_bank, + const vector& 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 - - // Save source bank sites since the array is overwritten below -#ifdef OPENMC_MPI - vector temp_source {source_bank.begin(), source_bank.end()}; -#endif + if (!file_id) { + fatal_error("MCPL: Internal error - master rank called " + "write_mcpl_source_bank_internal with null file_id."); + } + vector receive_buffer; - // 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(bank_index[i + 1] - bank_index[i])}; + for (int rank_idx = 0; rank_idx < mpi::n_procs; ++rank_idx) { + size_t num_sites_on_rank = static_cast( + bank_index_all_ranks[rank_idx + 1] - bank_index_all_ranks[rank_idx]); + if (num_sites_on_rank == 0) + continue; + span sites_to_write; #ifdef OPENMC_MPI - if (i > 0) - MPI_Recv(source_bank.data(), count[0], mpi::source_site, i, i, - mpi::intracomm, MPI_STATUS_IGNORE); + if (rank_idx == mpi::rank) { + sites_to_write = openmc::span( + 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( + receive_buffer.data(), num_sites_on_rank); + } +#else + sites_to_write = openmc::span( + local_source_bank.data(), num_sites_on_rank); #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 source_bank, const vector& 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_handle_repr_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 +} // namespace openmc \ No newline at end of file diff --git a/src/settings.cpp b/src/settings.cpp index 9afe743614e..afa42a3c5e6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -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")) { diff --git a/tests/regression_tests/source_mcpl_file/test.py b/tests/regression_tests/source_mcpl_file/test.py index 84ec005ae16..a4bdb2eb04c 100644 --- a/tests/regression_tests/source_mcpl_file/test.py +++ b/tests/regression_tests/source_mcpl_file/test.py @@ -3,11 +3,13 @@ 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="""