From eb916839d1abd97f565efbcf358b63950c53d2cb Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Sun, 14 Jun 2026 08:11:01 +0200 Subject: [PATCH 1/6] build: bump CMake abseil pin to 20260107.1 for Clang >= 21 The CMake FetchContent abseil pin (2024-08) fails to compile under Clang >= 21: absl::Nonnull SFINAE in absl/strings/ascii.cc and the numbers.cc nullability annotations are rejected by the newer frontend. Bump to the 20260107.1 LTS, which compiles cleanly under Clang 21.1.8 and GCC. Clang is the compiler required for the Enzyme autodiff build. The Bazel build keeps its own (BCR) abseil pin and is unaffected. --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be3c3255b..9a65418c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,10 @@ find_package(LAPACK REQUIRED) FetchContent_Declare( abseil-cpp GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git - GIT_TAG 4447c7562e3bc702ade25105912dce503f0c4010 + # 20260107.1 LTS: required for Clang >= 21, where the 2024 pin fails to + # compile (absl::Nonnull SFINAE in absl/strings/ascii.cc). Clang is the + # compiler used for the Enzyme autodiff build. + GIT_TAG 20260107.1 GIT_SHALLOW TRUE ) FetchContent_Declare( From 804b792be266a107fba7cb5adc23362cfa9b47a0 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Sun, 14 Jun 2026 17:31:45 +0200 Subject: [PATCH 2/6] ci: skip benchmark result upload on fork PRs (token is read-only) The 'Compare benchmark result' step uses github-action-benchmark with comment-on-alert and the GITHUB_TOKEN, which is read-only for pull requests from forks -> 'Resource not accessible by integration'. Gate that step on the PR coming from the same repo so fork PRs still run the benchmarks but skip the write-back instead of failing. --- .github/workflows/benchmarks.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yaml b/.github/workflows/benchmarks.yaml index c2f3fdf4e..54326ec6a 100644 --- a/.github/workflows/benchmarks.yaml +++ b/.github/workflows/benchmarks.yaml @@ -36,7 +36,11 @@ jobs: pytest benchmarks/test_benchmarks.py --benchmark-json=benchmark_results.json - name: Compare benchmark result - if: github.event_name == 'pull_request' + # Skip the result upload/compare for fork PRs: their GITHUB_TOKEN is + # read-only, so comment-on-alert/auto-push hit 'Resource not accessible + # by integration'. The benchmarks still run above; only the write-back + # is skipped for forks. + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository uses: benchmark-action/github-action-benchmark@v1.21.0 with: tool: "pytest" From 5688994340240ff081dd4451f2cb31f2449c2789 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Sun, 14 Jun 2026 18:12:42 +0200 Subject: [PATCH 3/6] ci: build VMEC2000 from source so the compat test runs on numpy 2 The pinned vmec-0.0.6 cp310 wheel was f90wrapped against numpy 1.x. Under the numpy 2.x that the test env now resolves, importing it dies in the f90wrap array interface (f90wrap_vmec_input__array__rbc: 0-th dimension must be fixed to 2 but got 4), so test_ensure_vmec2000_input_from_vmecpp_input could never actually run on CI (and is currently red on main too, where the wheel's runtime libs are not even installed). Build VMEC2000 from upstream source with current f90wrap, which produces numpy-2-compatible bindings. The recipe mirrors SIMSOPT's own CI (hiddenSymmetries/VMEC2000, cmake/machines/ubuntu.json). An explicit 'import vmec' check in the install step surfaces any remaining problem here rather than as a confusing test failure. --- .github/workflows/tests.yaml | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index a22e0cbdc..4a7964059 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -53,12 +53,27 @@ jobs: - name: Also install VMEC2000 (only on Ubuntu 22.04) if: ${{ matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.10' }} run: | - # mpi4py is needed for VMEC2000 - sudo apt-get install -y libopenmpi-dev - python -m pip install mpi4py - # custom wheel for VMEC2000, needed for some VMEC++/VMEC2000 compatibility tests - # NOTE: this wheel is only guaranteed to work on Ubuntu 22.04 - python -m pip install vmec@https://anaconda.org/eguiraud-pf/vmec/0.0.6/download/vmec-0.0.6-cp310-cp310-linux_x86_64.whl + # Build VMEC2000 from source instead of the old prebuilt wheel. The + # pinned vmec-0.0.6 cp310 wheel was f90wrapped against numpy 1.x and + # fails to import under numpy 2 (f90wrap array-interface break), so the + # compatibility test could never run. Building from source with current + # f90wrap produces numpy-2-compatible bindings. Recipe mirrors SIMSOPT's + # own CI (hiddenSymmetries/VMEC2000 cmake/machines/ubuntu.json). + sudo apt-get install -y libopenmpi-dev libnetcdff-dev libscalapack-mpi-dev \ + libhdf5-dev libhdf5-serial-dev libfftw3-dev libopenblas-dev ninja-build + # Build VMEC2000 with the SYSTEM cmake + ninja. Do not pip-install the + # cmake/ninja wheels: they would shadow the system cmake that + # scikit-build-core's editable vmecpp rebuild records, breaking its + # verify_globs step in the editable matrix job. + python -m pip install mpi4py numpy scikit-build f90wrap setuptools wheel + git clone --depth 1 https://github.com/hiddenSymmetries/VMEC2000.git /tmp/VMEC2000 + cd /tmp/VMEC2000 + cp cmake/machines/ubuntu.json cmake_config_file.json + LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu \ + python -m pip install -v --no-build-isolation . + cd - + # fail loudly here if the binding is still broken, not in the test step + python -c "import vmec; print('VMEC2000 import OK')" - name: Install package run: | # on Ubuntu we would not need this, but on MacOS we need to point CMake to gfortran-14 and gcc-14 From b205eb2ad93d15e06d7f82cd2f0115aa9893706f Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Sun, 14 Jun 2026 18:20:09 +0200 Subject: [PATCH 4/6] test: skip vmecpp-only indata fields in the VMEC2000 compat subset With VMEC2000 built from current upstream source, the compatibility test runs for the first time and hits vmecpp indata fields that have no counterpart in the legacy VMEC2000 INDATA namelist (e.g. free_boundary_method), which raised AttributeError. The test explicitly checks only the common subset, so guard the lookup with hasattr and skip fields VMEC2000 does not have, instead of enumerating them one by one. --- tests/test_simsopt_compat.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_simsopt_compat.py b/tests/test_simsopt_compat.py index 609dffea2..1443d8cbc 100644 --- a/tests/test_simsopt_compat.py +++ b/tests/test_simsopt_compat.py @@ -303,6 +303,11 @@ def test_ensure_vmec2000_input_from_vmecpp_input(): if varname[1:-1] == "axis_": # these are called differently in VMEC2000, e.g. raxis_c -> raxis_cc varname_vmec2000 = f"{varname[:-1]}c{varname[-1]}" + if not hasattr(vmec2000.indata, varname_vmec2000): + # vmecpp-only field (e.g. free_boundary_method) with no counterpart + # in the legacy VMEC2000 INDATA namelist; not part of the common + # subset under test. + continue vmec2000_var = getattr(vmec2000.indata, varname_vmec2000) if vmecpp_var is None: From a17697081dbc9e84998f40124f4725383ab5bf3d Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Mon, 15 Jun 2026 06:58:03 +0200 Subject: [PATCH 5/6] build: pin abseil to the 20260107.1 commit hash Pin the FetchContent abseil dependency to commit 255c84d (the exact commit behind the 20260107.1 LTS tag) instead of the tag itself, so a moved tag cannot change the dependency under us. --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a65418c6..8e07b1753 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,10 +66,9 @@ find_package(LAPACK REQUIRED) FetchContent_Declare( abseil-cpp GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git - # 20260107.1 LTS: required for Clang >= 21, where the 2024 pin fails to - # compile (absl::Nonnull SFINAE in absl/strings/ascii.cc). Clang is the - # compiler used for the Enzyme autodiff build. - GIT_TAG 20260107.1 + # 20260107.1 LTS: older abseil fails to compile under Clang >= 21 (the + # Enzyme build) on absl::Nonnull SFINAE in absl/strings/ascii.cc. + GIT_TAG 255c84dadd029fd8ad25c5efb5933e47beaa00c7 GIT_SHALLOW TRUE ) FetchContent_Declare( From f14b26c6fa9ced36eb5206fa60ff1f226ec15d77 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Mon, 15 Jun 2026 08:03:05 +0200 Subject: [PATCH 6/6] ci: cache and pin the VMEC2000-from-source build Use the canonical recipe (cache the built wheel keyed on the pinned source commit 728af8b, drop the unused FFTW/HDF5 dev packages) instead of rebuilding VMEC2000 unpinned on every run. --- .github/workflows/tests.yaml | 39 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4a7964059..0f68ab6e9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -50,28 +50,31 @@ jobs: run: | # install VMEC++ deps as well as VMEC2000 deps (we need to import VMEC2000 in a test) sudo apt-get update && sudo apt-get install -y build-essential cmake libnetcdf-dev liblapack-dev libomp-dev + - name: Cache the VMEC2000 wheel + if: ${{ matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.10' }} + uses: actions/cache@v4 + with: + path: /tmp/vmec2000-wheel + # Keyed on the pinned VMEC2000 source commit: rebuild only when it changes. + key: vmec2000-728af8bd6c79-cp310-ubuntu22.04 - name: Also install VMEC2000 (only on Ubuntu 22.04) if: ${{ matrix.os == 'ubuntu-22.04' && matrix.python-version == '3.10' }} run: | - # Build VMEC2000 from source instead of the old prebuilt wheel. The - # pinned vmec-0.0.6 cp310 wheel was f90wrapped against numpy 1.x and - # fails to import under numpy 2 (f90wrap array-interface break), so the - # compatibility test could never run. Building from source with current - # f90wrap produces numpy-2-compatible bindings. Recipe mirrors SIMSOPT's - # own CI (hiddenSymmetries/VMEC2000 cmake/machines/ubuntu.json). sudo apt-get install -y libopenmpi-dev libnetcdff-dev libscalapack-mpi-dev \ - libhdf5-dev libhdf5-serial-dev libfftw3-dev libopenblas-dev ninja-build - # Build VMEC2000 with the SYSTEM cmake + ninja. Do not pip-install the - # cmake/ninja wheels: they would shadow the system cmake that - # scikit-build-core's editable vmecpp rebuild records, breaking its - # verify_globs step in the editable matrix job. - python -m pip install mpi4py numpy scikit-build f90wrap setuptools wheel - git clone --depth 1 https://github.com/hiddenSymmetries/VMEC2000.git /tmp/VMEC2000 - cd /tmp/VMEC2000 - cp cmake/machines/ubuntu.json cmake_config_file.json - LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu \ - python -m pip install -v --no-build-isolation . - cd - + libopenblas-dev ninja-build + python -m pip install mpi4py + if ! ls /tmp/vmec2000-wheel/vmec-*.whl >/dev/null 2>&1; then + # Build with the SYSTEM cmake + ninja; do NOT pip-install the + # cmake/ninja wheels (they shadow the system cmake that + # scikit-build-core's editable vmecpp rebuild records). + python -m pip install numpy scikit-build f90wrap setuptools wheel + git clone https://github.com/hiddenSymmetries/VMEC2000.git /tmp/VMEC2000 + git -C /tmp/VMEC2000 checkout 728af8bd6c796b36a0aa85fe298e507791e57c6e + cp /tmp/VMEC2000/cmake/machines/ubuntu.json /tmp/VMEC2000/cmake_config_file.json + LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu \ + python -m pip wheel /tmp/VMEC2000 --no-build-isolation -w /tmp/vmec2000-wheel + fi + python -m pip install /tmp/vmec2000-wheel/vmec-*.whl # fail loudly here if the binding is still broken, not in the test step python -c "import vmec; print('VMEC2000 import OK')" - name: Install package