Skip to content

pyscf: Add version 2.14.0 #5

pyscf: Add version 2.14.0

pyscf: Add version 2.14.0 #5

Workflow file for this run

# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow follows upstream's own release-pypi-aarch64 job:
# https://github.com/pyscf/pyscf/blob/master/.github/workflows/publish.yml
name: Build pyscf wheels (riscv64)
on:
workflow_dispatch:
inputs:
version:
description: 'Version glob to (re)build; empty builds every version of docs/packages/pyscf.yaml not released yet'
required: false
default: ''
pull_request:
branches: [main]
paths:
- '.github/workflows/build-pyscf.yml'
- 'docs/packages/pyscf.yaml'
push:
branches: [main]
paths:
- '.github/workflows/build-pyscf.yml'
- 'docs/packages/pyscf.yaml'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
env:
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
LIBCINT_VERSION: v6.1.3
LIBXC_VERSION: '7.0.0'
XCFUN_REF: a89b783
jobs:
setup:
uses: $/.github/workflows/_setup.yml
with:
package: pyscf
version: ${{ inputs.version }}
build_wheels:
needs: [setup]
if: needs.setup.outputs.versions != '[]'
name: Build pyscf ${{ matrix.version }} py3-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 1440
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
env:
PYSCF_VERSION: ${{ matrix.version }}
steps:
- name: Checkout pyscf v${{ env.PYSCF_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: pyscf/pyscf
ref: v${{ env.PYSCF_VERSION }}
path: pyscf
persist-credentials: false
- name: Build wheel
run: |
mkdir -p wheelhouse
set -o pipefail
docker run --rm -i --network=host \
-v "${GITHUB_WORKSPACE}:/work" -w /work/pyscf \
-e LIBCINT_VERSION -e LIBXC_VERSION -e XCFUN_REF \
"${MANYLINUX_RISCV64_IMAGE}" \
bash <<'SCRIPT' 2>&1 | tee build.log
set -eux
dnf -y install openblas-devel gcc
# Upstream's own official Linux wheels ship no licence for any of these;
# send this fix upstream too.
curl -fsSLo LICENSE.libcint "https://raw.githubusercontent.com/sunqm/libcint/${LIBCINT_VERSION}/LICENSE"
curl -fsSLo LICENSE.libxc "https://gitlab.com/libxc/libxc/-/raw/${LIBXC_VERSION}/COPYING"
curl -fsSLo LICENSE.xcfun "https://raw.githubusercontent.com/dftlibs/xcfun/${XCFUN_REF}/LICENSE.md"
cp /usr/share/licenses/openblas/LICENSE LICENSE.openblas
PYTHON_BIN=/opt/python/cp312-cp312/bin/python
export PATH="/opt/python/cp312-cp312/bin:${PATH}"
"${PYTHON_BIN}" -m pip install -q -U pip setuptools wheel auditwheel
export PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
export PIP_ONLY_BINARY=cmake
# WITH_F12/ENABLE_SMD=OFF matches upstream's own macOS build; the Linux
# jobs turn ENABLE_SMD on, which also links in pyscf's own GPLv3-licensed
# MNSOL solvent code (pyscf/lib/solvent), kept out of scope here.
export CMAKE_CONFIGURE_ARGS="-DWITH_F12=OFF -DENABLE_SMD=OFF"
export CMAKE_BUILD_PARALLEL_LEVEL=4
"${PYTHON_BIN}" -m pip wheel -v --no-deps --no-clean -w /tmp/wheelhouse .
auditwheel repair /tmp/wheelhouse/pyscf-*.whl --lib-sdir /lib -w /work/wheelhouse
SCRIPT
- name: Upload build log
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pyscf-${{ env.PYSCF_VERSION }}-build-log
path: build.log
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pyscf-${{ env.PYSCF_VERSION }}-py3-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error
- name: Test wheel
run: |
docker run --rm -i --network=host \
-v "${GITHUB_WORKSPACE}:/work" -w /work/pyscf \
-e PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ \
"${MANYLINUX_RISCV64_IMAGE}" \
bash <<'SCRIPT'
set -eux
PYTHON_BIN=/opt/python/cp312-cp312/bin/python
"${PYTHON_BIN}" -m pip install -q --only-binary=:all: /work/wheelhouse/*.whl
"${PYTHON_BIN}" -m pip install -q pytest
# cd out of /work/pyscf first (gotcha 612): the build step's --no-clean
# pip wheel leaves pyscf.egg-info there, and `python -c` puts cwd on
# sys.path[0], so importlib.metadata resolves that stale egg-info instead
# of the installed wheel and the licence check below sees an empty set.
cd /tmp
licences=$("${PYTHON_BIN}" -c "
import importlib.metadata as m
print(sorted(p.name for p in m.files('pyscf') if '.dist-info/licenses/' in str(p)))
")
echo "$licences"
"${PYTHON_BIN}" -c "
import importlib.metadata as m
names = {p.name for p in m.files('pyscf') if '.dist-info/licenses/' in str(p)}
expected = {'LICENSE', 'NOTICE', 'LICENSE.libcint', 'LICENSE.libxc', 'LICENSE.xcfun', 'LICENSE.openblas'}
assert expected <= names, names
"
echo 'pbc_tools_pbc_fft_engine = "NUMPY"' > .pyscf_conf.py
echo 'scf_hf_SCF_mute_chkfile = True' >> .pyscf_conf.py
export PYSCF_CONFIG_FILE=/tmp/.pyscf_conf.py
export OMP_NUM_THREADS=1
ulimit -s 20000
"${PYTHON_BIN}" -c "import pyscf; from pyscf import gto, scf
mol = gto.M(atom='H 0 0 0; H 0 0 0.74', basis='sto-3g')
mf = scf.RHF(mol).run()
assert abs(mf.e_tot - (-1.1167)) < 1e-3, mf.e_tot
print('HF energy ok:', mf.e_tot)"
# Rename the checkout's package dir before collection (gotcha 616): even under
# pytest.ini's --import-mode=importlib, a test nested inside the package gets its
# ancestor packages imported straight off disk by walking up __init__.py files, so
# pyscf itself loads from this in-place build, not the installed wheel. Renaming
# breaks the name match and pyscf/__init__.py's own `from pyscf import lib` then
# falls through to the installed copy via normal sys.path.
mv /work/pyscf/pyscf /work/pyscf/pyscf-src
"${PYTHON_BIN}" -m pytest /work/pyscf/pyscf-src -c /work/pyscf/pytest.ini \
--ignore=/work/pyscf/pyscf-src/adc \
--ignore=/work/pyscf/pyscf-src/pbc/df \
--ignore=/work/pyscf/pyscf-src/pbc/cc \
--ignore=/work/pyscf/pyscf-src/solvent \
--ignore=/work/pyscf/pyscf-src/grad/test/test_ucasci.py
SCRIPT
gpl_sources:
needs: [setup]
if: needs.setup.outputs.versions != '[]'
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
name: Collect GPL sources
runs-on: ubuntu-24.04-riscv
env:
PYSCF_VERSION: ${{ matrix.version }}
steps:
- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# OpenBLAS is compiled with gfortran and pyscf links OpenMP, so
# auditwheel vendors the image's libgfortran and libgomp.
- uses: ./actions/collect-gpl-sources
with:
image: ${{ env.MANYLINUX_RISCV64_IMAGE }}
packages: gcc
output: gpl-sources.tar
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pyscf-${{ env.PYSCF_VERSION }}-gpl-sources
path: gpl-sources.tar
if-no-files-found: error
publish:
name: Publish pyscf ${{ matrix.version }}
needs: [setup, build_wheels, gpl_sources]
if: needs.setup.outputs.versions != '[]'
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
secrets:
app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }}
with:
artifact-pattern: pyscf-${{ matrix.version }}-py3-manylinux_riscv64
gpl-sources-artifact: pyscf-${{ matrix.version }}-gpl-sources
gpl-sources-description: gcc