A single protein–ligand prediction, run twice: once plain, once with chemical
steering. Two files: run_of3_steered.py and query_protein_ligand.json.
- OpenFold3 installed in the same environment as foldsteer
(
pip install -e .from the OF3 checkout, thenpip install -e ..here); - model parameters under
$OPENFOLD_CACHE(setup_openfold --non-interactive); - a GPU.
The query runs in single-sequence mode — no MSA server, no template cache, no data root — so nothing else has to be staged. It is PDB 9EHA: a 109-residue SH2 domain plus phosphotyrosine, which has a chiral centre, an aromatic ring and a phosphate, i.e. all three things steering restrains.
cd examples
# baseline
python run_of3_steered.py --no-steer -- \
--query-json query_protein_ligand.json \
--output-dir /tmp/fs_base --num-diffusion-samples 1 --num-model-seeds 1
# steered (guidance only)
python run_of3_steered.py --steer -- \
--query-json query_protein_ligand.json \
--output-dir /tmp/fs_steer --num-diffusion-samples 1 --num-model-seeds 1Everything after -- goes to run_openfold predict unchanged, so the two arms
take identical OpenFold3 arguments and only steering differs. Expect a few
minutes on one GPU.
Steering that finds no ligand is indistinguishable from steering that works, looked at from the outside — so check the counters the script prints at the end:
foldsteer: 1 ligand chain(s); {'n_atoms': 918, 'n_bounds': 231, 'n_chiral': 1, ...}
[foldsteer] targets=1 ligand_chains=1 guided_steps=200
ligand_chains=0 means the chemistry never reached the engine and the run was
unsteered; the script warns when that happens. Use --stats-json out.json to
keep the per-target breakdown.
patch_sample_diffusion_class patches the SampleDiffusion class, and
Lightning builds the model inside the runner. So the OF3 CLI has to be invoked
in the same interpreter — which is what this script does, rather than shelling
out to run_openfold. A subprocess would run unsteered, quietly.
Guidance only is the default: it is the cheaper half, carries most of the
benefit, and leaves the sample count alone. --fk turns on resampling as well,
but it reuses OF3's rollout-sample axis as the particle axis, so
--num-diffusion-samples must be a multiple of --num-particles (default 3)
and each particle group collapses to one survivor — fewer structures come back
than were sampled, and per-sample compute goes up.
python run_of3_steered.py --steer --fk --num-particles 3 -- \
--query-json query_protein_ligand.json \
--output-dir /tmp/fs_fk --num-diffusion-samples 3 --num-model-seeds 1Any OF3 query JSON works — see
examples/example_inference_inputs/ in the OpenFold3 repo for MSAs, templates,
CCD-code ligands and multi-chain complexes. Steering restrains non-polymer
chemistry only, so a target with no ligand chain runs unsteered by design.