Incompatibility of mpi4py and multiprocessing in depletion
The openmc.deplete module relies on calls to the OpenMC shared library via openmc.lib in order to perform neutron transport, update material compositions, etc., which means parallel execution has to be coordinated using mpi4py and the Python script itself should be called with mpiexec. The actual solve of the Bateman equations is done using the CRAM method, which is coded entirely in Python (using scipy.sparse); the parallelization strategy there is to break up the full list of materials being depleted over MPI processes (via mpi4py) and then over thread via multiprocessing. The default configuration of mpi4py and multiprocessing do not play well together due to the following sequence of events:
- Importing
mpi4pywill result inMPI_Initgetting called - For most Python versions, using a multiprocessing pool will create multiple processes via an OS fork
- MPI processes that are already initialized generally should not be forked as this results in implementation-dependent behavior and can result in deadlocks.
A few workarounds currently are to:
- Disable use of multiprocessing in depletion
- Use a start method other than "fork" in
multiprocessing - Disable automatic initialization/finalization from mpi4py
Further down the line, we may want to consider using free-threading in Python 3.14+ as this should allow us to get parallelization without creating new processes that cause problems with MPI.
Further down the line, we may want to consider using free-threading in Python 3.14+ as this should allow us to get parallelization without creating new processes that cause problems with MPI.
I have been thinking about how to best tackle this as a python library. As you probably know free-threading is a compile time option for Cpython introduced with 3.13, and so really now there are two versions of every minor python release. The biggest thing is you can't guarantee that free-threading will or will not be available.
I think it's then only worthwhile using free-threading if there is a good wrapper interface that lets you deduplicate code if you are using multi-threading or multiprocessing. Concurrent.futures provides this interface for vanilla python, but I don't know how mpi4py will handle this.
I have looked into supporting free-threading in Python 3.13. I ran into the issue that lxml does not explicitly support 3.13 free-threading. It might be possible if you build the package from source. This got me investigating further on the positions of people on supporting free-threading. I came across PEP 779. Per this PEP 3.13 free-threading is experimental, and 3.14 free-threading is officially supported but not mandatory. lxml does support free-threading 3.14.
I propose then that only 3.14 free-threading be supported, and not 3.13. This will depend a bit on vtk and in the issue above they seem to be going back and forth on this idea.