From d58810bd8abc40f23b4e2834d68fbf2e2bdcd8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Di=20Remigio=20Eik=C3=A5s?= Date: Fri, 30 Oct 2026 13:12:07 +0100 Subject: [PATCH 1/3] =?UTF-8?q?test(container):=20=E2=9C=85=20check=20that?= =?UTF-8?q?=20mpi4py=20loads=20the=20MPI=20ABI=20monoprop=20was=20built=20?= =?UTF-8?q?for?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since #426 monoprop loads its MPI extension for the ABI family of the MPI library mpi4py uses and raises MPIAbiMismatchError for any other. The smoke test now compares the family recorded in _mpi_abis.py with the mpi4py variant, reading both from files so that monoprop is still not imported. Assisted-by: Pi:claude-opus-5-5 --- tools/container/smoke-test.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tools/container/smoke-test.sh b/tools/container/smoke-test.sh index 13d5841bb..f25584ad5 100755 --- a/tools/container/smoke-test.sh +++ b/tools/container/smoke-test.sh @@ -53,6 +53,36 @@ echo "$mpi4py_library" [[ "$mpi4py_library" == *"Open MPI v${ompi_version}"* ]] \ || fail "mpi4py did not load the image's Open MPI v$ompi_version" +echo "== MPI ABI of monoprop's backend" +# monoprop loads its MPI extension for the ABI family mpi4py uses, and raises if it was not +# built for that family. Both sides are read from files, so monoprop is still not imported. +abi_check="$(python3 - "$package_dir" <<'PY' +import ast +import pathlib +import re +import sys + +import mpi4py + +mpi4py.rc.initialize = False +from mpi4py import MPI + +source = (pathlib.Path(sys.argv[1]) / "_mpi_abis.py").read_text() +built = next( + ast.literal_eval(node.value) + for node in ast.parse(source).body + if isinstance(node, ast.AnnAssign) and getattr(node.target, "id", "") == "ABIS" +) +variant = re.match(r"MPI\.(mpich|openmpi|mpiabi)\.", pathlib.Path(MPI.__spec__.origin).name) +print(" ".join(built), variant[1] if variant else "unknown", sep="|") +PY +)" +built_abis="${abi_check%|*}" +mpi4py_abi="${abi_check#*|}" +echo "built for: ${built_abis:-none}; mpi4py uses: $mpi4py_abi" +[[ " $built_abis " == *" $mpi4py_abi "* ]] \ + || fail "mpi4py uses the $mpi4py_abi MPI ABI, but monoprop was built for: ${built_abis:-none}" + echo "== Open MPI build" grep -q 'MCA pml: ucx' <<< "$ompi_details" || fail "Open MPI was built without the UCX PML" # The base image builds PMIx into /usr/local/pmix; srun --mpi=pmix talks to it. From d7175a1d8f473c0212997c2a978ead3b809c2933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Di=20Remigio=20Eik=C3=A5s?= Date: Fri, 30 Oct 2026 13:12:07 +0100 Subject: [PATCH 2/3] =?UTF-8?q?docs(clusters):=20=F0=9F=93=9D=20follow=20t?= =?UTF-8?q?he=20MPI=5FABIS=20API=20and=20the=20MPI-enabled=20wheels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit monoprop.has_mpi no longer exists; the MareNostrum5 check prints MPI_ABIS. Point users with network access to the MPI-enabled PyPI wheels, and say that the image's source build has one MPI backend, which the smoke test checks. Assisted-by: Pi:claude-opus-5-5 --- docs/content/docs/building.mdx | 3 +++ docs/content/docs/clusters/index.mdx | 5 +++-- docs/content/docs/clusters/marenostrum5.mdx | 11 +++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/content/docs/building.mdx b/docs/content/docs/building.mdx index 3310a4ee9..24c099ae3 100644 --- a/docs/content/docs/building.mdx +++ b/docs/content/docs/building.mdx @@ -440,6 +440,9 @@ requests that touch `tools/container/` or the workflow. `tools/container/smoke-t checks the monoprop image before it is pushed. It never imports monoprop, because the CI machine's CPU may lack the instructions `-march` enables, so check a new image on the target machine as well (see [MareNostrum5](/clusters/marenostrum5#check-the-image)). +Being a source build, the image has a single MPI backend, for the ABI family of the base +image's Open MPI; the smoke test checks that mpi4py loads that same family, without which +propagators given a communicator raise `MPIAbiMismatchError`. To reproduce the build locally: diff --git a/docs/content/docs/clusters/index.mdx b/docs/content/docs/clusters/index.mdx index acb66b839..c69f7f010 100644 --- a/docs/content/docs/clusters/index.mdx +++ b/docs/content/docs/clusters/index.mdx @@ -3,8 +3,9 @@ title: HPC clusters description: Running monoprop on HPC systems from its MPI-enabled container image. --- -Some HPC systems give their login nodes no outbound network, so monoprop cannot be -installed there with `pip`. For those, every release, the `main` branch and pull requests that +Where `pip` works, the PyPI wheels already run on the cluster's own MPI; see +[Enabling MPI](/features/parallelism#enabling-mpi). Some HPC systems, however, give their login +nodes no outbound network, so monoprop cannot be installed there with `pip`. For those, every release, the `main` branch and pull requests that change the container recipes publish an MPI-enabled container image to the GitHub Container Registry: ```text diff --git a/docs/content/docs/clusters/marenostrum5.mdx b/docs/content/docs/clusters/marenostrum5.mdx index 4c77ecbb1..e50a1708b 100644 --- a/docs/content/docs/clusters/marenostrum5.mdx +++ b/docs/content/docs/clusters/marenostrum5.mdx @@ -36,12 +36,15 @@ MPI on two ranks: ```bash salloc --nodes=1 --account= -q gp_debug --exclusive --time=00:10:00 module load singularity -srun -n 2 --mpi=pmix singularity exec monoprop_.sif \ - python3 -c 'import monoprop; print(monoprop.__version__, monoprop.has_mpi)' +srun -n 2 --mpi=pmix singularity exec monoprop_.sif python3 -c ' +import monoprop +from mpi4py import MPI +print(MPI.COMM_WORLD.Get_rank(), monoprop.__version__, monoprop.MPI_ABIS)' ``` -Both ranks should print the image's version and `True`. This is the check CI cannot run: -the CI machines may lack the Sapphire Rapids instructions the image is compiled for. +Each rank should print its rank, the image's version and `('openmpi',)`, the MPI ABI family +monoprop was built for in the image. This is the check CI cannot run: the CI machines may lack +the Sapphire Rapids instructions the image is compiled for. MN5's Slurm offers munge authentication to PMIx, which the container's PMIx is built without. The image sets `PMIX_MCA_psec=native`, so the ranks use PMIx's native From 444a84588354448eadbedd190f17934b8fe57188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Di=20Remigio=20Eik=C3=A5s?= Date: Fri, 30 Oct 2026 14:58:36 +0100 Subject: [PATCH 3/3] =?UTF-8?q?docs(clusters):=20=F0=9F=93=9D=20make=20the?= =?UTF-8?q?=20image=20check=20load=20the=20MPI=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MPI_ABIS only lists the families the image was built for, so printing it did not show that monoprop can run on the MPI library mpi4py loads. The check now builds a propagator on a duplicated COMM_WORLD, which loads the MPI backend and raises MPIAbiMismatchError on a mismatch. Also qualify which MPI the PyPI wheels support and link MPIAbiMismatchError. Assisted-by: Pi:claude-opus-5-5 --- docs/content/docs/building.mdx | 3 ++- docs/content/docs/clusters/index.mdx | 5 +++-- docs/content/docs/clusters/marenostrum5.mdx | 19 +++++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/content/docs/building.mdx b/docs/content/docs/building.mdx index 24c099ae3..8544121be 100644 --- a/docs/content/docs/building.mdx +++ b/docs/content/docs/building.mdx @@ -442,7 +442,8 @@ machine's CPU may lack the instructions `-march` enables, so check a new image o target machine as well (see [MareNostrum5](/clusters/marenostrum5#check-the-image)). Being a source build, the image has a single MPI backend, for the ABI family of the base image's Open MPI; the smoke test checks that mpi4py loads that same family, without which -propagators given a communicator raise `MPIAbiMismatchError`. +propagators given a communicator raise +[MPIAbiMismatchError][monoprop.exceptions.MPIAbiMismatchError]. To reproduce the build locally: diff --git a/docs/content/docs/clusters/index.mdx b/docs/content/docs/clusters/index.mdx index c69f7f010..f97d65e43 100644 --- a/docs/content/docs/clusters/index.mdx +++ b/docs/content/docs/clusters/index.mdx @@ -3,8 +3,9 @@ title: HPC clusters description: Running monoprop on HPC systems from its MPI-enabled container image. --- -Where `pip` works, the PyPI wheels already run on the cluster's own MPI; see -[Enabling MPI](/features/parallelism#enabling-mpi). Some HPC systems, however, give their login +Where `pip` works, `pip install "monoprop[mpi]"` installs wheels that run on the cluster's own +MPI, if it is from the MPICH family (MPICH, Intel MPI, MVAPICH, Cray MPICH) or Open MPI and +not older than the versions listed in [Enabling MPI](/features/parallelism#enabling-mpi). Some HPC systems, however, give their login nodes no outbound network, so monoprop cannot be installed there with `pip`. For those, every release, the `main` branch and pull requests that change the container recipes publish an MPI-enabled container image to the GitHub Container Registry: diff --git a/docs/content/docs/clusters/marenostrum5.mdx b/docs/content/docs/clusters/marenostrum5.mdx index e50a1708b..b7895662e 100644 --- a/docs/content/docs/clusters/marenostrum5.mdx +++ b/docs/content/docs/clusters/marenostrum5.mdx @@ -30,8 +30,8 @@ rsync -P monoprop_.sif @:/ ## Check the image -Get an interactive allocation on a compute node, then check that monoprop imports and sees -MPI on two ranks: +Get an interactive allocation on a compute node, then check that monoprop runs a propagator +over MPI on two ranks: ```bash salloc --nodes=1 --account= -q gp_debug --exclusive --time=00:10:00 @@ -39,12 +39,19 @@ module load singularity srun -n 2 --mpi=pmix singularity exec monoprop_.sif python3 -c ' import monoprop from mpi4py import MPI -print(MPI.COMM_WORLD.Get_rank(), monoprop.__version__, monoprop.MPI_ABIS)' +comm = MPI.COMM_WORLD.Dup() +operator = monoprop.PauliOperator({monoprop.Pauli("Z", (0,)): 1.0}, 2) +propagator = monoprop.PauliPropagator(operator, [0], cutoff=2, comm=comm) +print(comm.Get_rank(), monoprop.__version__, "MPI backend loaded") +del propagator +comm.Free()' ``` -Each rank should print its rank, the image's version and `('openmpi',)`, the MPI ABI family -monoprop was built for in the image. This is the check CI cannot run: the CI machines may lack -the Sapphire Rapids instructions the image is compiled for. +Each rank should print its rank, the image's version and `MPI backend loaded`. Building a +propagator with a communicator loads monoprop's MPI backend for the MPI library mpi4py uses, +so this also checks that the image's mpi4py and monoprop agree on it; if they did not, it would +raise [MPIAbiMismatchError][monoprop.exceptions.MPIAbiMismatchError]. This is the check CI +cannot run: the CI machines may lack the Sapphire Rapids instructions the image is compiled for. MN5's Slurm offers munge authentication to PMIx, which the container's PMIx is built without. The image sets `PMIX_MCA_psec=native`, so the ranks use PMIx's native