Quickstart

Installation

pip install -e ".[all]"

Core dependencies are only rdkit, networkx, numpy, and scipy. The amber, kartograf, and amber-mol2 extras pull in optional backends.

Example data

No binary files are checked in. Regenerate the benzamide example ligands from their SMILES:

python examples/data/make_conformers.py

The 16-ligand Tyk2 series used by The Tyk2 variant matrix is checked in directly, as co-posed mol2 under examples/data/tyk2/, because the comparison it supports is only meaningful on a real, consistently posed set.

Plan a network

rbfenet plan --ligands examples/data/benzamides.sdf \
             --edges-per-ligand 2 --min-cycle-coverage 1.0 \
             --max-softcore-atoms 12 --show-rejected \
             --out network.json

From Python:

from rbfenetmap.core.options import NetworkOptions, SoftcorePolicy
from rbfenetmap.core.pipeline import build_network
from rbfenetmap.io.loaders import load_ligands

network = build_network(
    load_ligands(["examples/data/benzamides.sdf"]),
    mapper="mcss-e2",
    network_options=NetworkOptions(
        edges_per_ligand=2, softcore=SoftcorePolicy(max_softcore_atoms=12)
    ),
)
network.validate()

Ligands must be co-posed

Warning

Ligands must be supplied in a common binding-site frame. The core_rmsd descriptor measures in-place deviation without superposition, precisely so that it detects a mapping pairing atoms that occupy different parts of the pocket.

Independently embedded conformers share no frame, so every candidate edge between them is rejected with core_geometry_mismatch – correctly, but uselessly. examples/data/make_conformers.py shows the constrained-embedding pattern.

If your structures were prepared separately – set up individually for ABFE runs, say, and written to mol2 from their own Amber topologies – then their conformers are real bound poses and only the frames disagree. Aligning ligands recovers the common frame rather than asking you to re-prepare them.

Aligning ligands

--align rigidly superposes the set into a common frame before anything is mapped. It is off by default, and should stay off for ligands that were prepared together: those are already co-posed, and aligning them would paper over a genuine pose problem instead of revealing it.

rbfenet plan --ligands prepared_from_abfe/ --align \
             --write-aligned aligned/ --out network.json

Each ligand is fitted onto an already-aligned neighbour through their maximum common substructure and moved there rigidly. Neighbour rather than root: the walk order is a maximum spanning tree over fingerprint similarity, so a ligand is fitted onto the one it most resembles. For a set too diverse for any substructure to bite on, --align o3a switches to Open3DAlign, which needs no shared substructure but gives no auditable set of paired atoms in exchange.

The report tells you whether it worked, and goes to stderr so a --format json run stays machine-readable:

ligand   reference  fit atoms  rmsd   note
-------  ---------  ---------  -----  ---------
bza_CF3  -          0          -      reference
bza_Cl   bza_F      10         0.132
bza_H    bza_Cl     9          0.030

Read both columns. A small fit atoms count deserves suspicion even when the RMSD looks good – three atoms determine a rigid body, but only just – and a ligand that could not be fitted at all is reported separately and left in its own frame, where its edges will still be rejected for geometry.

Then look at the structures, because a plausible table can still hide a flipped ring:

pymol aligned/*.sdf   # one object per ligand, not one object with N states

Note

Rigid alignment recovers a common frame. It cannot recover a common conformation. Each independently relaxed structure keeps its own ring puckers, exocyclic torsions, and bond-length noise, so a residual core_rmsd survives alignment and should – expect more of it than a constrained-embedded series would show, and expect the default --core-rmsd-threshold 2.0 to be doing real work rather than waving everything through. Raise it only after looking at the actual distribution:

rbfenet score --ligands prepared_from_abfe/ --align --show-rejected --explain

What alignment does not touch is the atom mapping. Mappings are index-based, so Amber masks, exported edge lists, and every depiction are identical with and without --align; it cannot corrupt a downstream setup. What it does change is every quantity measured from coordinates – core_rmsd, the geometry gate, the RMSD terms in both scorers – and the molecules embedded in network.json, which carry the aligned frame from then on. Each ligand records what was done to it under metadata["alignment"], and that survives the network round trip.