Drug discovery
DrugFlow + Boltz-2 Affinity on HPC or Any Cloud
Generate pocket ligands with DrugFlow, score each with Boltz-2 affinity, rank by ΔG. The GPU stage runs remotely; prep and ranking stay local.
What this workflow does
This workflow takes a target protein in PDB format and a reference ligand. The reference ligand defines the binding pocket. DrugFlow generates novel drug-like molecules for that pocket. Boltz-2 then scores every generated molecule for binding affinity. The workflow returns a ΔG table ranked best first.
It is a full de-novo design loop: propose, then score. The generation step is structure-based, so the molecules are built for the pocket you give it, not pulled from a library.
The ΔG column is an approximation. Boltz-2 reports affinity_pred_value as
about log10(IC50) with IC50 in µM. Treating IC50 as a Kd proxy at 298 K gives
ΔG ≈ 1.364 · (affinity_pred_value − 6) in kcal/mol. Use it for ranking
candidates, not as an exact free energy.
The compute problem
The pipeline has six stages and they do not want the same hardware.
Two setup stages only fetch data. One downloads the DrugFlow checkpoint, about 170 MB, from Zenodo. The other clones the DrugFlow source at a pinned commit. DrugFlow is not published as a package, so the code has to come from a checkout even when the environment is already built. Both stages need one CPU and a network connection, and both are skipped once their output exists.
The generation stage wants a GPU. It runs DrugFlow's generate.py over the
pocket, and it is the stage that defines the hardware bill.
The next three stages are CPU work. One extracts the target sequence from the
PDB and writes one Boltz affinity YAML per molecule. One runs boltz predict
over the whole input folder. One parses the affinity JSON files, converts each
to ΔG and sorts the table.
The dependency sets also conflict. DrugFlow needs its own environment, and its
conda recipe has no osx-arm64 solution: pyg=2.5.1, ProDy=2.4.0 and
pytorch=2.2.1 have no arm64 builds. The prep stage needs RDKit and Biopython.
The predict stage needs boltz. The ranking stage needs nothing but a stdlib
python3. Putting all of that in one environment is a solve that does not
resolve.
How Horus solves it
Horus assigns an executor and a target to each stage. The generation stage uses
the singularity executor on a slurm target, so the container runs inside an
sbatch job with gres: gpu:1. The other five stages run locally. The prep and
predict stages each get their own uv environment, so RDKit, Biopython and
boltz never have to coexist.
The .sif image is built once, off-workflow, from the published DrugFlow Docker
image. The executor has no build or pull step, so the image must already exist
on the target. Its path is declared in the artifacts: block as drugflow_sif,
next to the protein and the reference ligand, and the generation task reads it
as ${drugflow_sif}. The default is relative, so it resolves in the run
directory. Point it at an absolute path to share one build across runs. Every
path an operator has to set lives in one block.
Singularity does no path translation. A bound host path is visible inside the
container at the same location, so every ${...} in the command is valid on
both sides and needs no rewriting. The executor auto-binds each input and output
artifact's parent directory, which is how the DrugFlow checkout becomes visible
to the container that supplies only its dependencies.
To move a stage to another machine, change the executor: field. The
runtime.command string does not change. Horus moves the data across each
boundary for you.
Pipeline
download_checkpoint (local, CPU) Zenodo ──► drugflow.ckpt
fetch_source (local, CPU) pinned commit ──► drugflow_src/
generate (GPU, .sif) protein + ref ligand ──► results/samples.sdf
prepare_inputs (local, CPU) RDKit + Biopython ──► boltz_inputs/ + smiles.json
predict (local, CPU) boltz predict ──► predictions/affinity_*.json
rank (local, CPU) affinity → ΔG, sort ──► results/deltaG_table.csv
Inputs and outputs
Inputs
examples/kras.pdb: the target protein structure. The prep stage takes the sequence of its longest protein chain.examples/kras_ref_ligand.sdf: the reference ligand. Its binding site is the pocket DrugFlow designs into.
Outputs land in horus_workflow_results/:
drugflow.ckpt: the checkpoint the workflow downloaded.drugflow_src/: the DrugFlow source at the pinned commit.results/samples.sdf: the generated molecules.results/boltz_inputs/: one Boltz affinity YAML per molecule.results/smiles.json: a{molecule_name: canonical_smiles}map.results/predictions/: oneaffinity_*.jsonper molecule.results/deltaG_table.csv: columnsmolecule, smiles, affinity_pred_value, binding_probability, deltaG_kcal_per_mol, sorted by ascending ΔG.
A lower ΔG value means stronger predicted binding.
Each molecule's YAML is named after the molecule, because the YAML stem becomes the Boltz prediction name and is the key the ranking stage joins against.
Run the workflow
Install the horus-runtime and the plugins one time:
uv sync
If you do not have uv, install it first:
curl -LsSf https://astral.sh/uv/install.sh | sh
You can also install the packages with pip:
pip install horus-runtime horus-environments horus-singularity horus-slurm
Build the .sif image once on the cluster, then run the workflow from a login
node where sbatch and singularity (or apptainer) are reachable:
horus run workflow.yaml
To set the number of molecules, edit the command: of the generate stage.
--n_samples defaults to 10 and --batch_size to 32. The
--pocket_distance_cutoff value defaults to 8.0 ångström. Add
--molecule_size for a maximum atom count or a range, --n_steps to override
the denoising default, and --filter to resample until the quality filters
pass.
For a CPU-only generation run, set the executor's nv: false and
--device cpu. The two have to agree, or the container either cannot see the
driver stack or does not ask for it.
The predict stage defaults to --accelerator cpu so it runs anywhere. It
also passes --use_msa_server, which is the public ColabFold server, so the
machine running that stage needs outbound network access and is subject to that
server's rate limits. Both the generation and the prediction stages are slow on
CPU.
References
Run this workflow
The workflow is open source. Clone the pantheon repository and run it with the horus-runtime engine. To run it on managed compute without a cluster of your own, open it in Temple Compute OS.