name: Test Matrix

on:
  push:
    branches: [ master ]
    paths-ignore: 'doc/**'
  pull_request:
    branches: [ master ]
    paths-ignore: 'doc/**'

jobs:
  testallconfigs:
    name: "CMake Matrix Test."
    runs-on: ${{ matrix.config.os }}
    strategy:
      fail-fast: false
      matrix:
        config:
        - {
            name:  "Linux Min GCC",
            os:    "ubuntu-22.04",
            cc:    "gcc-12",
            cxx:   "g++-12",
            py:    "3.10",
            cmake: "3.19.x",
            mpi:   "ON",
            simd:  "OFF"
          }
        - {
            name:  "Linux Min Clang",
            os:    "ubuntu-22.04",
            cc:    "clang-13",
            cxx:   "clang++-13",
            py:    "3.10",
            cmake: "3.19.x",
            mpi:   "ON",
            simd:  "OFF"
          }
        - {
            name:  "MacOS Min",
            os:    "macos-13",
            cc:    "clang",
            cxx:   "clang++",
            py:    "3.10",
            cmake: "3.19.x",
            mpi:   "ON",
            simd:  "OFF"
          }
        - {
            name:  "Linux Max GCC",
            os:    "ubuntu-24.04",
            cc:    "gcc-14",
            cxx:   "g++-14",
            py:    "3.13",
            cmake: "3.22.x",
            mpi:   "ON",
            simd:  "OFF"
          }
        - {
            name:  "Linux SIMD",
            os:    "ubuntu-24.04",
            cc:    "gcc-14",
            cxx:   "g++-14",
            py:    "3.13",
            cmake: "3.22.x",
            mpi:   "OFF",
            simd:  "ON"
          }
        - {
            name:  "Linux Max Clang",
            os:    "ubuntu-24.04",
            cc:    "clang-18",
            cxx:   "clang++-18",
            py:    "3.13",
            cmake: "3.22.x",
            mpi:   "ON",
            simd:  "OFF"
          }
        - {
            name:  "MacOS Max",
            os:    "macos-14",
            cc:    "clang",
            cxx:   "clang++",
            py:    "3.13",
            cmake: "3.22.x",
            mpi:   "ON",
            simd:  "OFF"
          }
        variant: [static, shared]
    env:
        CC:         ${{ matrix.config.cc }}
        CXX:        ${{ matrix.config.cxx }}
        # We set PYTHONPATH instead of installing arbor to avoid distribution/OS specific behaviour.
        PYTHONPATH: ${{ github.workspace }}/build/python
        # This is a workaround for the unfortunate interaction of MacOS and OpenMPI 4
        # See https://github.com/open-mpi/ompi/issues/6518
        OMPI_MCA_btl: "self,tcp"
    steps:
      - name: "Linux: get build dependencies"
        if: ${{ startsWith(matrix.config.os, 'ubuntu') }}
        run: |
          sudo apt-get update
          sudo apt-get install -y libopenmpi-dev ninja-build ccache ${{ matrix.config.cc }}
      - name: "MacOS: get build dependencies"
        if: ${{ startsWith(matrix.config.os, 'macos') }}
        run: |
          brew install openmpi ninja ccache
      - name: Set up cmake
        uses: jwlawson/actions-setup-cmake@v2.0.0
        with:
          cmake-version: ${{ matrix.config.cmake }}
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.config.py }}
      - name: Update pip
        run: python -m pip install --upgrade pip
      - name: Install Python packages
        run:  pip install numpy sphinx svgwrite sphinx-rtd-theme mpi4py pandas seaborn pybind11-stubgen
      - name: Clone w/ submodules
        uses: actions/checkout@v4
        with:
          submodules: recursive
      # figure out vector extensions for ccache key
      - if: ${{ contains(matrix.config.os, 'macos') }}
        name: Check vector extensions
        # assume uniform hardware for macos
        run: |
          echo "VECTOR_EXTENSIONS=" >> $GITHUB_ENV
      - if: ${{ contains(matrix.config.os, 'ubuntu') }}
        name: Check vector extensions
        run: |
          HAS_AVX512F=$([[ $(lscpu | grep "avx512f" | wc -l) -eq 1 ]] && echo "_avx512f" || echo "")
          HAS_AVX2=$([[ $(lscpu | grep "avx2" | wc -l) -eq 1 ]] && echo "_avx2" || echo "")
          HAS_FMA=$([[ $(lscpu | grep "fma" | wc -l) -eq 1 ]] && echo "_fma" || echo "")
          HAS_AVX=$([[ $(lscpu | grep "avx" | wc -l) -eq 1 ]] && echo "_avx" || echo "")
          VECTOR_EXTENSIONS=${HAS_AVX512F}${HAS_AVX2}${HAS_FMA}${HAS_AVX}
          echo "VECTOR_EXTENSIONS=$VECTOR_EXTENSIONS" >> $GITHUB_ENV
      - name: Setup ccache
        uses: hendrikmuhs/ccache-action@v1.2
        with:
          key: ${{ matrix.config.os }}-${{ matrix.config.cxx }}-${{ matrix.variant }}-${{ env.VECTOR_EXTENSIONS }}
      - name: Check config
        run: |
          $CC --version
          $CXX --version
          python --version
          mpic++ --show
          mpicc --show
          echo $PYTHONPATH
      - if:   ${{ matrix.variant == 'static' }}
        name: Build arbor
        run: |
          export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
          mkdir build
          cd build
          cmake .. -GNinja -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_WITH_PYTHON=ON -DARB_VECTORIZE=${{ matrix.config.simd }} -DPython3_EXECUTABLE=`which python` -DARB_WITH_MPI=${{ matrix.config.mpi }} -DARB_WITH_ASSERTIONS=ON -DARB_WITH_PROFILING=ON
          ninja -j4 tests examples pyarb
          cd -
      - if:   ${{ matrix.variant == 'shared' }}
        name: Build arbor
        run: |
          export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
          mkdir build
          cd build
          cmake .. -GNinja -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_WITH_PYTHON=ON -DARB_VECTORIZE=${{ matrix.config.simd }} -DPython3_EXECUTABLE=`which python` -DARB_WITH_MPI=${{ matrix.config.mpi }} -DARB_WITH_ASSERTIONS=ON -DBUILD_SHARED_LIBS=ON -DARB_WITH_PROFILING=ON
          ninja -j4 tests examples pyarb
          cd -
      - name: Install arbor
        run: |
          cd build
          sudo ninja install
          cd -
      - name: Run unit tests
        run: |
          build/bin/unit
          build/bin/unit-modcc
      - if:   ${{ matrix.config.mpi == 'ON' }}
        name: Run MPI tests
        run:  mpirun -n 4 -oversubscribe build/bin/unit-mpi
      - if:   ${{ matrix.config.mpi == 'OFF' }}
        name: Run examples
        run: scripts/run_cpp_examples.sh
      - if:   ${{ matrix.config.mpi == 'ON' }}
        name: Run examples with MPI
        run: scripts/run_cpp_examples.sh "mpirun -n 4 -oversubscribe"
      - name: Run python tests
        run: |
          python -m unittest discover -v -s python
      - if:   ${{ matrix.config.mpi == 'ON' }}
        name: Run python+MPI tests
        run:  mpirun -n 4 -oversubscribe python -m unittest discover -v -s python
      # - name: Run Python examples (plotting leads to time outs on macos, the step under testpip is enough)
      #   run: scripts/run_python_examples.sh
      - name: Test executables
        run: ./scripts/test_executables.sh
