@@ -48,24 +48,30 @@ struct openmc_local_mcpl_particle_t {
4848
4949typedef struct openmc_local_mcpl_particle_t mcpl_particle_repr_t ;
5050
51- typedef void * mcpl_file_handle_repr_t ;
52- typedef void * mcpl_outfile_handle_repr_t ;
53-
54- typedef mcpl_file_handle_repr_t (*mcpl_open_file_fpt)(const char * filename);
55- typedef uint64_t (*mcpl_hdr_nparticles_fpt)(
56- mcpl_file_handle_repr_t file_handle);
57- typedef const mcpl_particle_repr_t * (*mcpl_read_fpt)(
58- mcpl_file_handle_repr_t file_handle);
59- typedef void (*mcpl_close_file_fpt)(mcpl_file_handle_repr_t file_handle);
60-
61- typedef mcpl_outfile_handle_repr_t (*mcpl_create_outfile_fpt)(
62- const char * filename);
51+ // Opaque struct definitions replicating the MCPL C-API to ensure ABI
52+ // compatibility without including mcpl.h. These must be kept in sync.
53+ struct mcpl_file_t_ {
54+ void * internal;
55+ };
56+ struct mcpl_outfile_t_ {
57+ void * internal;
58+ };
59+
60+ typedef struct mcpl_file_t_ mcpl_file_t ;
61+ typedef struct mcpl_outfile_t_ mcpl_outfile_t ;
62+
63+ // Function pointer types for the dynamically loaded MCPL library
64+ typedef mcpl_file_t * (*mcpl_open_file_fpt)(const char * filename);
65+ typedef uint64_t (*mcpl_hdr_nparticles_fpt)(mcpl_file_t * file_handle);
66+ typedef const mcpl_particle_repr_t * (*mcpl_read_fpt)(mcpl_file_t * file_handle);
67+ typedef void (*mcpl_close_file_fpt)(mcpl_file_t * file_handle);
68+
69+ typedef mcpl_outfile_t * (*mcpl_create_outfile_fpt)(const char * filename);
6370typedef void (*mcpl_hdr_set_srcname_fpt)(
64- mcpl_outfile_handle_repr_t outfile_handle, const char * srcname);
65- typedef void (*mcpl_add_particle_fpt)(mcpl_outfile_handle_repr_t outfile_handle,
66- const mcpl_particle_repr_t * particle);
67- typedef void (*mcpl_close_outfile_fpt)(
68- mcpl_outfile_handle_repr_t outfile_handle);
71+ mcpl_outfile_t * outfile_handle, const char * srcname);
72+ typedef void (*mcpl_add_particle_fpt)(
73+ mcpl_outfile_t * outfile_handle, const mcpl_particle_repr_t * particle);
74+ typedef void (*mcpl_close_outfile_fpt)(mcpl_outfile_t * outfile_handle);
6975
7076namespace openmc {
7177
@@ -164,21 +170,6 @@ void append_error(std::string& existing_msg, const std::string& new_error)
164170 existing_msg += new_error;
165171}
166172
167- void mcpl_library_cleanup ()
168- {
169- g_mcpl_api.reset ();
170- if (g_mcpl_lib_handle) {
171- #ifdef _WIN32
172- FreeLibrary (g_mcpl_lib_handle);
173- #else
174- dlclose (g_mcpl_lib_handle);
175- #endif
176- g_mcpl_lib_handle = nullptr ;
177- }
178- g_mcpl_successfully_loaded = false ;
179- g_mcpl_init_attempted = false ;
180- }
181-
182173void initialize_mcpl_interface_impl ()
183174{
184175 g_mcpl_init_attempted = true ;
@@ -264,7 +255,8 @@ void initialize_mcpl_interface_impl()
264255 try {
265256 g_mcpl_api = std::make_unique<McplApi>(g_mcpl_lib_handle);
266257 g_mcpl_successfully_loaded = true ;
267- std::atexit (mcpl_library_cleanup);
258+ // Do not call dlclose/FreeLibrary at exit. Leaking the handle is safer
259+ // and standard practice for libraries used for the application's lifetime.
268260 } catch (const std::runtime_error& e) {
269261 append_error (g_mcpl_load_error_msg,
270262 fmt::format (
@@ -297,16 +289,12 @@ inline void ensure_mcpl_ready_or_fatal()
297289{
298290 initialize_mcpl_interface_if_needed ();
299291 if (!g_mcpl_successfully_loaded) {
300- fatal_error (fmt::format (
301- " MCPL functionality is required, but the MCPL library is not available "
302- " or failed to initialize. "
303- " Please ensure MCPL is installed and its library can be found (e.g., via "
304- " PATH on Windows, LD_LIBRARY_PATH on Linux, or DYLD_LIBRARY_PATH on "
305- " macOS). "
306- " You can often install MCPL with 'pip install mcpl'. "
307- " Last error(s): {}" ,
308- g_mcpl_load_error_msg.empty () ? " No specific error during load."
309- : g_mcpl_load_error_msg));
292+ fatal_error (" MCPL functionality is required, but the MCPL library is not "
293+ " available or failed to initialize. Please ensure MCPL is "
294+ " installed and its library can be found (e.g., via PATH on "
295+ " Windows, LD_LIBRARY_PATH on Linux, or DYLD_LIBRARY_PATH on "
296+ " macOS). You can often install MCPL with 'pip install mcpl' or "
297+ " 'conda install mcpl'." );
310298 }
311299}
312300
@@ -352,14 +340,15 @@ vector<SourceSite> mcpl_source_sites(std::string path)
352340 ensure_mcpl_ready_or_fatal ();
353341 vector<SourceSite> sites;
354342
355- mcpl_file_handle_repr_t mcpl_file = g_mcpl_api->open_file (path.c_str ());
343+ mcpl_file_t * mcpl_file = g_mcpl_api->open_file (path.c_str ());
356344 if (!mcpl_file) {
357345 fatal_error (fmt::format (" MCPL: Could not open file '{}'. It might be "
358346 " missing, inaccessible, or not a valid MCPL file." ,
359347 path));
360348 }
361349
362350 size_t n_particles_in_file = g_mcpl_api->hdr_nparticles (mcpl_file);
351+ size_t n_skipped = 0 ;
363352 if (n_particles_in_file > 0 ) {
364353 sites.reserve (n_particles_in_file);
365354 }
@@ -375,16 +364,28 @@ vector<SourceSite> mcpl_source_sites(std::string path)
375364 if (p_repr->pdgcode == 2112 || p_repr->pdgcode == 22 ||
376365 p_repr->pdgcode == 11 || p_repr->pdgcode == -11 ) {
377366 sites.push_back (mcpl_particle_to_site (p_repr));
367+ } else {
368+ n_skipped++;
378369 }
379370 }
380371
381372 g_mcpl_api->close_file (mcpl_file);
382373
374+ if (n_skipped > 0 && n_particles_in_file > 0 ) {
375+ double percent_skipped =
376+ 100.0 * static_cast <double >(n_skipped) / n_particles_in_file;
377+ warning (fmt::format (
378+ " MCPL: Skipped {} of {} total particles ({:.1f}%) in file '{}' because "
379+ " their type is not supported by OpenMC." ,
380+ n_skipped, n_particles_in_file, percent_skipped, path));
381+ }
382+
383383 if (sites.empty ()) {
384384 if (n_particles_in_file > 0 ) {
385- fatal_error (fmt::format (" MCPL file '{}' contained {} particles, but none "
386- " were of the supported types "
387- " (neutron, photon, electron, positron)." ,
385+ fatal_error (fmt::format (
386+ " MCPL file '{}' contained {} particles, but none were of the supported "
387+ " types (neutron, photon, electron, positron). OpenMC cannot proceed "
388+ " without source particles." ,
388389 path, n_particles_in_file));
389390 } else {
390391 fatal_error (fmt::format (
@@ -394,7 +395,7 @@ vector<SourceSite> mcpl_source_sites(std::string path)
394395 return sites;
395396}
396397
397- void write_mcpl_source_bank_internal (mcpl_outfile_handle_repr_t file_id,
398+ void write_mcpl_source_bank_internal (mcpl_outfile_t * file_id,
398399 span<SourceSite> local_source_bank,
399400 const vector<int64_t >& bank_index_all_ranks)
400401{
@@ -484,7 +485,7 @@ void write_mcpl_source_point(const char* filename, span<SourceSite> source_bank,
484485 filename, extension));
485486 }
486487
487- mcpl_outfile_handle_repr_t file_id = nullptr ;
488+ mcpl_outfile_t * file_id = nullptr ;
488489
489490 if (mpi::master) {
490491 file_id = g_mcpl_api->create_outfile (filename_.c_str ());
0 commit comments