From 5db54fc814bea764fdb1463d2c6661f332622cc2 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 14 Oct 2026 12:49:56 +0000 Subject: [PATCH] hydride: Add version 1.2.3 Build hydride's Cython relax extension for riscv64, mirroring upstream's cibuildwheel test_and_deploy.yml. Two patches: a backport of upstream's setup.py fix so the build-time fragment library compiles against biotite >= 1.0, and a cast at hydride's biotite CellList boundary so relax_hydrogen() works with biotite 1.7 (hydride issue #29). --- .github/workflows/build-hydride.yml | 111 ++++++++++++++++++ docs/packages/hydride.yaml | 6 + ...-components-with-missing-coordinates.patch | 42 +++++++ ...lList-inputs-outputs-for-biotite-1.7.patch | 56 +++++++++ 4 files changed, 215 insertions(+) create mode 100644 .github/workflows/build-hydride.yml create mode 100644 docs/packages/hydride.yaml create mode 100644 patches/hydride/1.2.3/0001-Fix-setup-for-CCD-components-with-missing-coordinates.patch create mode 100644 patches/hydride/1.2.3/0002-relax-cast-biotite-CellList-inputs-outputs-for-biotite-1.7.patch diff --git a/.github/workflows/build-hydride.yml b/.github/workflows/build-hydride.yml new file mode 100644 index 000000000000..0c2f6bd19bf6 --- /dev/null +++ b/.github/workflows/build-hydride.yml @@ -0,0 +1,111 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# Based on upstream's CI: +# https://github.com/biotite-dev/hydride/blob/v1.2.3/.github/workflows/test_and_deploy.yml +name: Build hydride wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/hydride.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-hydride.yml' + - 'docs/packages/hydride.yaml' + - 'patches/hydride/**' + push: + branches: [main] + paths: + - '.github/workflows/build-hydride.yml' + - 'docs/packages/hydride.yaml' + - 'patches/hydride/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: hydride + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build hydride ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 720 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # setup.py imports biotite at build time; biotite has no free-threaded wheel. + python: ["cp312", "cp313", "cp314"] + env: + HYDRIDE_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout hydride v${{ env.HYDRIDE_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: biotite-dev/hydride + ref: v${{ env.HYDRIDE_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Apply patches + run: git apply -v python-wheels/patches/hydride/${{ env.HYDRIDE_VERSION }}/*.patch + + - name: Build and test wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + output-dir: wheelhouse/ + only: ${{ matrix.python }}-manylinux_riscv64 + env: + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + CIBW_DEPENDENCY_VERSIONS: pinned + CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + CIBW_TEST_EXTRAS: test + CIBW_TEST_SOURCES: tests + CIBW_TEST_COMMAND: pytest tests --durations=50 + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: hydride-${{ env.HYDRIDE_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: ./wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish hydride ${{ matrix.version }} + needs: [setup, build_wheels] + 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: hydride-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/hydride.yaml b/docs/packages/hydride.yaml new file mode 100644 index 000000000000..64c383ceafc4 --- /dev/null +++ b/docs/packages/hydride.yaml @@ -0,0 +1,6 @@ +package-name: hydride +source-code: https://github.com/biotite-dev/hydride +license: BSD-3-Clause +versions: +- version: 1.2.3 + patched: true diff --git a/patches/hydride/1.2.3/0001-Fix-setup-for-CCD-components-with-missing-coordinates.patch b/patches/hydride/1.2.3/0001-Fix-setup-for-CCD-components-with-missing-coordinates.patch new file mode 100644 index 000000000000..0263dcfd18cc --- /dev/null +++ b/patches/hydride/1.2.3/0001-Fix-setup-for-CCD-components-with-missing-coordinates.patch @@ -0,0 +1,42 @@ +From bba37a77d314a02dce0e4f11fefd325455931a07 Mon Sep 17 00:00:00 2001 +From: Patrick Kunzmann +Date: Thu, 25 Sep 2025 12:17:30 +0200 +Subject: [PATCH] Fix setup for CCD components with missing coordinates + +Upstream-Status: Backport [https://github.com/biotite-dev/hydride/commit/bba37a77d314a02dce0e4f11fefd325455931a07] + +setup.py compiles hydride's fragment library at build time by calling +biotite.structure.info.residue() on every component of the Chemical +Component Dictionary, skipping the ones it cannot load. The v1.2.3 tag +only skips KeyError, but biotite >= 1.0 raises ValueError for a CCD +component with missing coordinates (residue(allow_missing_coord=False)), +so with the biotite 1.7.1 that build isolation pulls in from +pypi.riseproject.dev the wheel build aborts in setup.py before any +compiler runs. This is not riscv64-specific: any v1.2.3 build against a +current biotite fails the same way. + +Upstream fixed it in bba37a7, which is on the v1.2.4 tag; that tag still +declares __version__ = "1.2.3" and was never uploaded to PyPI, so 1.2.3 +remains the latest release and this one-line change is carried onto it. + +--- + setup.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index e854b94..141f6c0 100644 +--- a/setup.py ++++ b/setup.py +@@ -148,7 +148,8 @@ if not isfile(fragment_file_path): + print(f"Compiling fragment library... ({i}/{len(mol_names)})", end="\r") + try: + mol = info.residue(mol_name) +- except KeyError: ++ except ValueError: ++ # Molecule has missing coordinates + continue + std_fragment_library.add_molecule(mol) + print("Compiling fragment library... Done" + " " * 20) +-- +2.43.0 + diff --git a/patches/hydride/1.2.3/0002-relax-cast-biotite-CellList-inputs-outputs-for-biotite-1.7.patch b/patches/hydride/1.2.3/0002-relax-cast-biotite-CellList-inputs-outputs-for-biotite-1.7.patch new file mode 100644 index 000000000000..2b78bc2979ad --- /dev/null +++ b/patches/hydride/1.2.3/0002-relax-cast-biotite-CellList-inputs-outputs-for-biotite-1.7.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Wed, 14 Oct 2026 12:00:00 +0000 +Subject: [PATCH] relax: cast biotite CellList inputs/outputs for biotite 1.7 + +Upstream-Status: Issue [https://github.com/biotite-dev/hydride/issues/29] + +biotite 1.7 reimplemented structure.CellList in Rust, which changed two +things MinimumFinder.__init__ relies on: + +- CellList.get_atoms() now returns int64 indices, so assigning the + result to the `cdef int32[:,:] adj_indices` memoryview raises + "ValueError: Buffer dtype mismatch, expected 'int32' but got 'long'" + for every relax_hydrogen() call (hydride issues #29 and #30). +- CellList(..., periodic=True, box=...) only accepts a float32 box; the + float64 np.identity(3)-style boxes callers pass raise + "TypeError: 'ndarray' object cannot be cast as 'ndarray'". + +Cast the box to float32 and the returned indices to int32 at the call +site. With biotite 1.7.1 (the only biotite on pypi.riseproject.dev) the +unpatched wheel fails 141 of the 1327 tests on any architecture +(reproduced on x86_64); with this patch they all pass. biotite 1.6.0 +and older already return int32 and accept a float32 box, so the casts +are no-ops there. + +Signed-off-by: Ludovic Henry +--- + src/hydride/relax.pyx | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/hydride/relax.pyx b/src/hydride/relax.pyx +index 3c937bd..6c94593 100644 +--- a/src/hydride/relax.pyx ++++ b/src/hydride/relax.pyx +@@ -218,7 +218,8 @@ class MinimumFinder: + ) + else: + cell_list = struc.CellList( +- atoms, cell_size=force_cutoff, periodic=True, box=self._box ++ atoms, cell_size=force_cutoff, periodic=True, ++ box=np.asarray(self._box, dtype=np.float32) + ) + relevant_mask = (np.asarray(groups) != -1) + cdef int32[:] relevant_indices = np.where(relevant_mask)[0] \ +@@ -226,7 +227,7 @@ class MinimumFinder: + cdef int32[:,:] adj_indices = cell_list.get_atoms( + atoms.coord[np.asarray(relevant_indices)], + radius=force_cutoff +- ) ++ ).astype(np.int32, copy=False) + + + cdef int32[:,:] bond_indices = atoms.bonds.get_all_bonds()[0] +-- +2.43.0 +