Contributing to DisVis
Bug reports, questions and pull requests are welcome; use the issue templates
on GitHub. This file describes how to set up a development environment, check
your changes and get them merged. For using DisVis, see README.md; for GPU
acceleration, INSTALLATION.md.
Development setup
DisVis requires Python 3.11 or newer and NumPy 2. It contains two compiled extensions (Cython and C), so it must be built and installed; it is not a pure-Python package.
git clone https://github.com/haddocking/disvis.git
cd disvis
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]' # with ruff and cython-lint; '.[dev,gpu]' for the GPU
The package metadata and dependencies are in pyproject.toml; setup.py
only defines the compiled extensions.
This is an editable install: changes to the Python code in src/disvis/ take
effect without reinstalling. After changing the compiled extensions
(libdisvis.pyx or _extensions.c), rebuild them with pip install -e ..
The build leaves build/, src/disvis.egg-info/ and the compiled *.so
files in src/disvis/; git ignores them.
The gpu extra installs the GPU requirements; pyvkfft is built from source
and needs a C++ compiler and, on Linux, the OpenCL headers (see
INSTALLATION.md).
Code layout
The package is in src/disvis/:
main.py: command-line interface. Parses the arguments, input files and restraints, runs the search on the CPU (multiprocessing over subsets of the rotations) or GPU, and writes the output files.disvis.py: theDisVisclass, the search itself, with a CPU implementation (NumPy FFT or pyFFTW) and a GPU implementation (OpenCL).helpers.py: parsing of restraints, interaction selections and interaction files, and other small utilities.rotations.pyanddata/*.npy: rotation matrices and the precomputed rotational sampling sets with their weights.volume.py: theVolumeclass (3D grid with spacing and origin).pdb.py,IO/: PDB/mmCIF and MRC input and output.kernels.py,kernels.cl: OpenCL kernels for the GPU.gpufft.py: real 3D FFTs on the GPU with VkFFT, through pyvkfft.libdisvis.pyx(Cython) and_extensions.c(C): the CPU grid operations (restraint spaces, violations, interactions, grid rotation).
Outside the package: tests/ (unit tests), scripts/ (analysis scripts for
DisVis output), test-cases/ (test cases with reference results, see
test-cases/README.md) and docs/ (the documentation site, see
Documentation).
All grids are indexed as [z, y, x]; coordinates are (x, y, z).
Code style
The code is linted and formatted with ruff,
configured in pyproject.toml, and the Cython code is linted with
cython-lint:
ruff check .
ruff format . # or ruff format --check . to only check
cython-lint --max-line-length 79 src/disvis/libdisvis.pyx
Every module, public class and function has a docstring (PEP 257, summary on
the first line). src/disvis/elements.py is third-party code (Christoph Gohlke):
it is excluded from formatting and from most lint rules; keep it as it is.
Functions are limited to a McCabe complexity of 10 (ruff rule C901); the few
older functions above it are marked with # noqa: C901 until they are split
up. Prefer fixing an issue in the code; disable a rule in pyproject.toml
only for a deliberate style choice, with a comment saying why.
Tests
Run the unit tests from the repository root; they test the installed package:
python -W error -m unittest # all tests
python -W error -m unittest tests/test_disvis.py # CPU only
python -W error -m unittest tests/test_kernels.py tests/test_cpu_vs_gpu.py # GPU
The GPU tests run on a GPU if there is one, and otherwise on any other OpenCL
device, such as pocl on the CPU (pip install 'pyopencl[pocl]', Linux only).
They are skipped when the GPU packages or an OpenCL device are missing, or when
the device cannot build a kernel (e.g. pocl on some recent CPUs).
Reference results
test-cases/*/example-results/ contains CPU and GPU reference results (see
test-cases/README.md). The CPU output files (accessible_complexes.out,
violations.out, z-score.out and accessible_interaction_space.mrc) must
stay byte-identical to the references. After any change to the search,
run at least the coarse runs of both test cases, which
test-cases/check_test_cases.py runs and compares with the references:
python test-cases/check_test_cases.py -p 8 # coarse, on the CPU
python test-cases/check_test_cases.py --fine # fine: takes from minutes to hours
python test-cases/check_test_cases.py --gpu # compared with the GPU references
GPU results agree with the CPU references to about 1e-5 (single precision), with identical z-scores.
If a change intentionally alters the output, explain why in the pull request,
and update the reference results and test-cases/README.md.
Documentation
The documentation site, https://www.bonvinlab.org/disvis, is built with
MkDocs Material (mkdocs.yml,
docs/) and deployed to GitHub Pages by .github/workflows/pages.yml on every
push to main. Its pages include sections of README.md, INSTALLATION.md,
CONTRIBUTING.md and test-cases/README.md, marked with
files, not a copy in docs/. The CLI reference is generated from the disvis
arguments, and the example reports are those of the CPU reference results.
The "Try it in your browser" page runs docs/lite/content/disvis_demo.ipynb in
JupyterLite, with a WebAssembly wheel of DisVis built from the source. To build
and view the site (the wheel is built from the last commit):
pip install -e '.[docs]'
make -C docs # wheel, MkDocs and JupyterLite, into site/
make -C docs serve # http://localhost:8000
Pull requests
Work on a branch and open a pull request into main; fill in the pull-request
template. Before it can be merged, a pull request needs:
- an approving review (and a review from CSBcore for the files listed in
.github/CODEOWNERS), - all review conversations resolved,
- the GitHub workflow passing (its
ci-passedcheck), on a branch that is up to date withmain.
The GitHub workflow (.github/workflows/ci.yml) runs on every pull request:
ruff and cython-lint, the unit tests on Linux, macOS and Windows with Python
3.11–3.14, the coarse test cases with test-cases/check_test_cases.py, and the GPU
unit tests on the CPU with pocl. Run the same checks locally before pushing.
These jobs are skipped when a pull request changes only *.md files;
ci-passed then still reports success.
If AI tools played a meaningful role, say so in the pull request (the template
has a section for it), and update aidecl.yaml, the project's AI declaration,
when a new tool or kind of use is added.
Checklist:
- Tests added for new code.
- Documentation added: docstrings, and
README.md/INSTALLATION.mdwhen the usage or installation changes (the documentation site includes them). CHANGELOG.mdupdated.- Reference results unchanged, or updated with an explanation.
- No new dependencies without discussion.
- No new lint errors.
- Prefer small, incremental changes, with passing tests at each step, over large rewrites.
Releases
The version is set in pyproject.toml only; disvis.__version__ reads it
from the installed package metadata. To release, update the version and
CHANGELOG.md, merge, then tag main as vX.Y.Z and create a GitHub release.
Pushing a v* tag runs .github/workflows/docker-publish.yml, which publishes
the Docker image to ghcr.io. Publishing the GitHub release runs
.github/workflows/pypi-publish.yml, which builds the wheels and the sdist
and publishes them to PyPI. Only CSBcore can create v* tags.