Skip to content

Use system MPI in Dockerfile #3727

Use system MPI in Dockerfile

Use system MPI in Dockerfile #3727

Workflow file for this run

name: CI (with Spack)
on:
# Uncomment the below 'push' to trigger on push
push:
branches:
- release
pull_request:
branches:
- main
workflow_dispatch:
env:
GITHUB_USER: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OMPI_ALLOW_RUN_AS_ROOT: 1
OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1
PRTE_MCA_rmaps_default_mapping_policy: :oversubscribe
jobs:
ci-spack-build:
name: ci-spack-build (${{ matrix.mpi }}${{ matrix.petsc && format(', petsc {0}/{1}', matrix.scalar, matrix.index) || ', no petsc' }})
runs-on: ubuntu-26.04
container: ubuntu:26.04
permissions:
contents: read
packages: write
strategy:
fail-fast: false
# PETSc scalar/index combinations are covered on OpenMPI; MPICH
# repeats the two most common ones, as it exercises the MPI layer
# rather than the scalar type. The PETSc-free build tests the
# optional-PETSc code paths and needs only one MPI.
matrix:
include:
- {mpi: openmpi, petsc: true, scalar: float32, index: int32,
petsc_dep: "^petsc+mpi+metis+mumps+ptscotch+scalapack+superlu-dist~hypre~suite-sparse~double~complex~int64"}
- {mpi: openmpi, petsc: true, scalar: float64, index: int32,
petsc_dep: "^petsc+mpi+hypre+metis+mumps+ptscotch+scalapack+suite-sparse+superlu-dist+double~complex~int64"}
- {mpi: openmpi, petsc: true, scalar: complex64, index: int32,
petsc_dep: "^petsc+mpi+metis+mumps+ptscotch+scalapack~hypre~suite-sparse~superlu-dist~double+complex~int64"}
- {mpi: openmpi, petsc: true, scalar: complex128, index: int32,
petsc_dep: "^petsc+mpi+hypre+metis+mumps+ptscotch+scalapack+suite-sparse+superlu-dist+double+complex~int64"}
- {mpi: openmpi, petsc: true, scalar: float64, index: int64,
petsc_dep: "^petsc+mpi+hypre+mumps+ptscotch+scalapack+suite-sparse+superlu-dist~metis+double~complex+int64"}
- {mpi: openmpi, petsc: true, scalar: complex128, index: int64,
petsc_dep: "^petsc+mpi+hypre+mumps+ptscotch+scalapack+suite-sparse+superlu-dist~metis+double+complex+int64"}
- {mpi: mpich, petsc: true, scalar: float64, index: int32,
petsc_dep: "^petsc+mpi+hypre+metis+mumps+ptscotch+scalapack+suite-sparse+superlu-dist+double~complex~int64"}
- {mpi: mpich, petsc: true, scalar: complex128, index: int32,
petsc_dep: "^petsc+mpi+hypre+metis+mumps+ptscotch+scalapack+suite-sparse+superlu-dist+double+complex~int64"}
- {mpi: openmpi, petsc: false}
steps:
- name: Branch and commit SHA
run: |
echo "Branch and commit SHA: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}, ${{ github.sha }}"
- name: Install Spack requirements
run: |
apt-get -y update
apt-get install -y --no-install-recommends file bzip2 ca-certificates g++ gcc gfortran git gzip \
libgl-dev libosmesa6-dev lsb-release patch python3 tar unzip xz-utils zstd
- name: Set up Spack
uses: spack/setup-spack@16b756799ede8b28951287f89bcd3fdb32ec1ca3 # v3.0.0
with:
spack_ref: v1.2.2
packages_ref: d342cee94fb90fb020776c8bd8500b54a19c63cf
# packages_ref: v2026.06.0
# Pinned, like spack itself and its packages repo above: an unpinned
# 'main' changes what CI builds with no commit here, which makes a
# failure impossible to attribute. Bump deliberately.
- name: Get FEniCS Spack packages
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: fenics/spack-fenics
ref: 05ea6cd78f5fde9c580fe29cb6dbb040d05f74ee # main, 2026-08-12
path: spack-fenics
# Add FEniCS Spack packages on top of the upstream Spack packages
- name: Add FEniCS Spack packages repo
run: |
spack repo add --name fenics $GITHUB_WORKSPACE/spack-fenics/spack_repo/fenics
- name: Get DOLFINx code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: dolfinx-src
- name: Load dev branch environment variables
run: cat dolfinx-src/.github/workflows/fenicsx-refs.env >> $GITHUB_ENV
- name: Get UFL code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ${{ env.ufl_repository }}
ref: ${{ env.ufl_ref }}
path: ufl-src
- name: Get FFCx code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ${{ env.ffcx_repository }}
ref: ${{ env.ffcx_ref }}
path: ffcx-src
- name: Get Basix code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ${{ env.basix_repository }}
ref: ${{ env.basix_ref }}
path: basix-src
- name: Record source commit SHAs
run: |
echo "BASIX_SHA=$(git -C $GITHUB_WORKSPACE/basix-src rev-parse HEAD)" >> $GITHUB_ENV
echo "UFL_SHA=$(git -C $GITHUB_WORKSPACE/ufl-src rev-parse HEAD)" >> $GITHUB_ENV
echo "FFCX_SHA=$(git -C $GITHUB_WORKSPACE/ffcx-src rev-parse HEAD)" >> $GITHUB_ENV
- name: Prepare Spack environment
run: |
spack env create ci dolfinx-src/.github/workflows/spack-config/gh-actions-env-test.yml
spack -e ci config add "packages:mpi:require:${{ matrix.mpi }}"
for pkg in fenics-basix:basix py-fenics-basix:basix py-fenics-ufl:ufl \
py-fenics-ffcx:ffcx fenics-ufcx:ffcx fenics-dolfinx:dolfinx py-fenics-dolfinx:dolfinx; do
spack -e ci develop --no-clone --path $GITHUB_WORKSPACE/${pkg#*:}-src ${pkg%%:*}@main
done
# A single environment builds DOLFINx once for both the C++ and the
# Python test steps. Separate environments resolve to an identical
# runtime spec and differ only in build-time dependencies, so the
# second build produces the same library at a different hash.
- name: Build DOLFINx and test dependencies
run: |
# Ensure build provenance by adding manually and providing git sha
spack -e ci add fenics-basix@main commit=$BASIX_SHA build_type=Developer
spack -e ci add py-fenics-basix@main commit=$BASIX_SHA
spack -e ci add py-fenics-ufl@main commit=$UFL_SHA
spack -e ci add fenics-ufcx@main commit=$FFCX_SHA
spack -e ci add py-fenics-ffcx@main commit=$FFCX_SHA
# NOTE: adios2~sz drops the SZ2 compression dependency. 'sz'
# installs its own unversioned libzstd.so, which interposes on the
# real zstd once libdolfinx is loaded; a ZSTD_DCtx then gets
# allocated in one zstd and freed in the other. This segfaults
# Mesa's llvmpipe shader disk cache, so any PyVista render
# following 'import dolfinx' crashes (see 'Run Python demos').
spack -e ci add fenics-dolfinx@main+adios2${{ matrix.petsc && '+petsc+slepc' || '~petsc~slepc+superlu-dist' }} commit=${{ github.sha }} build_type=Developer partitioners=parmetis \
${{ matrix.petsc_dep }} ^adios2~libcatalyst~fortran~sz
spack -e ci add py-fenics-dolfinx@main${{ matrix.petsc && '+petsc4py+slepc4py' || '~petsc4py~slepc4py' }} commit=${{ github.sha }} build_type=Developer
spack -e ci add py-gmsh ^gmsh~med~fltk+external ^opencascade~draw py-pyamg py-matplotlib \
py-networkx \
py-numba ^llvm~clang~lldb~lld~offload~libomptarget~polly targets=x86 \
py-pytest py-pytest-xdist py-scipy \
"py-pyvista ^[virtuals=gl] osmesa"
spack -e ci add catch2 ninja
spack -e ci concretize -j $(nproc)
spack -e ci install -j $(nproc) --use-buildcache auto
# Pushed before the test steps so the artifacts are cached even if a
# later step fails. The index is rebuilt by the final push.
#
# Both pushes are skipped for pull requests from forks: those run
# with a read-only GITHUB_TOKEN whatever 'permissions' asks for, so
# the push would 401 as soon as it had anything to upload, failing a
# contributor's job on a step that tests nothing. Reads are
# anonymous, so their builds still take from the cache.
- name: Push packages to cache
if: >-
${{ !cancelled() && (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name == github.repository) }}
run: |
spack -e ci buildcache push --base-image ubuntu:26.04 --only dependencies \
--with-build-dependencies --allow-missing local-buildcache
- name: Build and run C++ unit tests (serial and MPI)
shell: spack-bash {0}
run: |
spack env activate ci
spack load catch2 cmake ninja py-fenics-ffcx
cmake -Werror=dev --warn-uninitialized -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build/test/ -S dolfinx-src/cpp/test/
cmake --build build/test
cd build/test
ctest --output-on-failure -R unittests_np_1
ctest --output-on-failure -R unittests_np_3
# Skipped without PETSc: several demo CMakeLists reference
# PETSC_INCLUDE_DIRS unconditionally, which --warn-uninitialized
# turns into an error, and the top-level demo CMakeLists adds every
# subdirectory, so configuration fails as a whole.
- name: Build and run C++ demos (serial and MPI)
if: ${{ matrix.petsc }}
shell: spack-bash {0}
run: |
spack env activate ci
spack load cmake ninja py-fenics-ffcx
cmake -Werror=dev --warn-uninitialized -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build/demo/ -S dolfinx-src/cpp/demo/
cmake --build build/demo
cd build/demo
ctest --output-on-failure -R "demo_.*_np_1"
ctest --output-on-failure -R "demo_.*_np_2"
# Guard against a mis-specified variant silently building PETSc into
# the PETSc-free leg, which would make it test nothing new.
- name: Check PETSc configuration matches the matrix
shell: spack-bash {0}
run: |
spack env activate ci
python -c "import dolfinx; assert dolfinx.has_petsc is ${{ matrix.petsc && 'True' || 'False' }}, dolfinx.has_petsc"
- name: Set default DOLFINx JIT options
run: |
mkdir -p ~/.config/dolfinx
echo '{ "cffi_extra_compile_args": ["-g0", "-O0" ] }' > ~/.config/dolfinx/dolfinx_jit_options.json
- name: Run Python demos (serial and MPI)
shell: spack-bash {0}
run: |
spack env activate ci
spack load gcc
python -m pytest -n=auto -m serial --durations=10 dolfinx-src/python/demo/test.py
python -m pytest -m mpi --num-proc=3 dolfinx-src/python/demo/test.py
# NOTE: the marker expression is written '!matrix.petsc && X || Y'
# rather than 'matrix.petsc && Y || X'. GitHub expressions treat the
# empty string as false, so the latter form falls through to X on
# every leg and silently skips the petsc4py tests everywhere.
- name: Run Python unit tests (serial and MPI)
shell: spack-bash {0}
run: |
spack env activate ci
spack load gcc
python -m pytest -n=auto --durations=50 ${{ !matrix.petsc && '-m "not petsc4py"' || '' }} dolfinx-src/python/test/unit/
mpiexec -np 3 python -m pytest --durations=50 ${{ !matrix.petsc && '-m "not petsc4py"' || '' }} dolfinx-src/python/test/unit/
# The test tools are environment roots, so '--only dependencies'
# skips them and they need naming explicitly. Only the final push
# rebuilds the index, which is the expensive part.
- name: Push test packages to cache and update index
if: >-
${{ !cancelled() && (github.event_name != 'pull_request'
|| github.event.pull_request.head.repo.full_name == github.repository) }}
run: |
spack -e ci buildcache push --base-image ubuntu:26.04 --allow-missing --update-index local-buildcache \
py-gmsh py-matplotlib py-networkx py-numba py-pytest py-pytest-xdist py-scipy py-pyvista