Skip to content

CI (Spack-based)

CI (Spack-based) #3631

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:
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:
# PETSc solver sets mirror the per-arch '--download-' packages in
# docker/Dockerfile.test-env, so this workflow and ccpp.yml build
# PETSc the same way. Two of those packages have no Spack variant
# in petsc: 'spai' (real64-32) and serial 'superlu' (all int32
# arches); everything else matches. Note the pattern the
# Dockerfile encodes: hypre is double-precision only, SuperLU_DIST
# has no single-precision complex, and the int64 arches drop metis.
- {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 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
- name: Get FEniCS Spack packages
uses: actions/checkout@v7
with:
repository: fenics/spack-fenics
ref: main
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
spack config get repos
spack repo list
- name: Get DOLFINx code
uses: actions/checkout@v7
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@v7
with:
repository: ${{ env.ufl_repository }}
ref: ${{ env.ufl_ref }}
path: ufl-src
- name: Get FFCx code
uses: actions/checkout@v7
with:
repository: ${{ env.ffcx_repository }}
ref: ${{ env.ffcx_ref }}
path: ffcx-src
- name: Get Basix code
uses: actions/checkout@v7
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 C++ environment
run: |
spack info fenics-dolfinx
spack env create cxx dolfinx-src/.github/workflows/spack-config/gh-actions-env-test.yml
spack -e cxx config add "packages:mpi:require:${{ matrix.mpi }}"
spack -e cxx develop --no-clone --path $GITHUB_WORKSPACE/basix-src fenics-basix@main
spack -e cxx develop --no-clone --path $GITHUB_WORKSPACE/basix-src py-fenics-basix@main
spack -e cxx develop --no-clone --path $GITHUB_WORKSPACE/ufl-src py-fenics-ufl@main
spack -e cxx develop --no-clone --path $GITHUB_WORKSPACE/ffcx-src py-fenics-ffcx@main
spack -e cxx develop --no-clone --path $GITHUB_WORKSPACE/ffcx-src fenics-ufcx@main
spack -e cxx develop --no-clone --path $GITHUB_WORKSPACE/dolfinx-src fenics-dolfinx@main
- name: Build library (C++)
run: |
# Ensure build provenance by adding manually and providing git sha
spack -e cxx add fenics-basix@main commit=$BASIX_SHA build_type=Developer
spack -e cxx add py-fenics-basix@main commit=$BASIX_SHA
spack -e cxx add py-fenics-ufl@main commit=$UFL_SHA
spack -e cxx add fenics-ufcx@main commit=$FFCX_SHA
spack -e cxx 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 cxx 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 cxx add catch2 ninja
spack -e cxx concretize -j 4
spack -e cxx install -j 4 --use-buildcache auto
- name: Push C++ packages to cache
run: |
spack -e cxx buildcache push --base-image ubuntu:26.04 --only dependencies \
--with-build-dependencies --update-index --allow-missing local-buildcache
if: ${{ !cancelled() }}
- name: Build and run C++ unit tests (serial and MPI)
shell: spack-bash {0}
run: |
spack env activate cxx
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 -V --output-on-failure -R unittests_np_1
ctest -V --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 cxx
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 -V --output-on-failure -R "demo_.*_np_1"
ctest -V --output-on-failure -R "demo_.*_np_2"
- name: Prepare Python environment
run: |
spack info py-fenics-dolfinx
spack env create py dolfinx-src/.github/workflows/spack-config/gh-actions-env-test.yml
spack -e py config add "packages:mpi:require:${{ matrix.mpi }}"
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/basix-src fenics-basix@main
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/basix-src py-fenics-basix@main
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/ufl-src py-fenics-ufl@main
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/ffcx-src py-fenics-ffcx@main
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/ffcx-src fenics-ufcx@main
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/dolfinx-src fenics-dolfinx@main
spack -e py develop --no-clone --path $GITHUB_WORKSPACE/dolfinx-src py-fenics-dolfinx@main
- name: Build library (Python)
run: |
# Ensure build provenance by adding manually and providing git sha
spack -e py add fenics-basix@main commit=$BASIX_SHA build_type=Developer
spack -e py add py-fenics-basix@main commit=$BASIX_SHA
spack -e py add py-fenics-ufl@main commit=$UFL_SHA
spack -e py add fenics-ufcx@main commit=$FFCX_SHA
spack -e py add py-fenics-ffcx@main commit=$FFCX_SHA
# NOTE: see the 'Build library (C++)' step for why adios2~sz is
# required -- without it the PyVista demos segfault.
spack -e py 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 py add py-fenics-dolfinx@main${{ matrix.petsc && '+petsc4py+slepc4py' || '~petsc4py~slepc4py' }} commit=${{ github.sha }} build_type=Developer
spack -e py 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 py concretize -j 4
spack -e py install -j 4 --use-buildcache auto
# 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 py
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 py
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
- name: Run Python unit tests (serial and MPI)
shell: spack-bash {0}
run: |
spack env activate py
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/
- name: Push dependency packages to cache and update index
run: |
spack -e py buildcache push --base-image ubuntu:26.04 --allow-missing --only dependencies \
--with-build-dependencies --update-index local-buildcache
if: ${{ !cancelled() }}
- name: Push test dependency packages to cache and update index
run: |
spack -e py 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
if: ${{ !cancelled() }}