Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .github/workflows/build-hydride.yml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions docs/packages/hydride.yaml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
From bba37a77d314a02dce0e4f11fefd325455931a07 Mon Sep 17 00:00:00 2001
From: Patrick Kunzmann <padix.key@gmail.com>
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

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
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 <git@ludovic.dev>
---
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

Loading