Skip to content

A protein–ligand complex, end to end

Tested against md-tools 0.6.1. The structures, figures and numbers are from the prepared TYK2 fixture and its build records. The parameterisation and complex-build commands below are the recorded preparation of that fixture, with its ligand_catalog path adapted for a standalone working directory; they have not been re-executed for this page.

These pages all use one system, prepared once. Preparing a protein separately for each method is how four methods end up studying four slightly different proteins.

The system: TYK2 with ejm_31

TYK2 is a non-receptor tyrosine kinase of the JAK family, and the structure here is its kinase domain — the two-lobed fold every protein kinase shares, with the ligand bound in the cleft between the lobes where ATP normally sits.

The TYK2 kinase domain with ejm_31 bound in the ATP site

The prepared complex: 4,670 protein atoms in one chain, the ligand in orange, and the residues within 5 Å of it shown as a translucent surface. Rendered from prepared/complex_ejm_31.pdb.

ejm_31 is a small neutral inhibitor — 32 atoms, an amide linking a dichlorophenyl ring to a pyridine — from a congeneric series of sixteen with measured affinities. Its experimental binding free energy is −9.54 kcal/mol.

Zoomed into the site, the reason it binds where it does is visible: the ligand makes the two hinge hydrogen bonds that almost every ATP-competitive kinase inhibitor makes, to the backbone of the residues joining the two lobes.

The ATP site, with the hinge hydrogen bonds to Val981

The ligand (orange) against the residues within 4.5 Å (grey), hydrogens hidden. The dashed lines are the two hinge hydrogen bonds, 2.8 Å and 3.2 Å in the prepared structure. Distances are measured from the coordinates, not drawn by hand.

That pocket is why this system is used to demonstrate selective REST2: the interesting degrees of freedom — the ligand's own torsions, and the sidechains lining the site — are a few hundred atoms out of 53,030, and heating only those is the whole point of choosing a hot region.

Where the inputs come from

The protein and the ligands are from OpenFreeEnergy/openfe-benchmarks, openfe_benchmarks/data/benchmark_systems/jacs_set/tyk2/ — MIT licensed, Copyright (c) 2022 Irfan Alibay. Four files matter:

protein.pdb                      the kinase domain, ACE/NME capped
ligands.sdf                      sixteen congeneric ligands with bond orders
experimental_binding_data.json   the measured affinities
PREPARATION_DETAILS.md           what upstream did to the structure, and why
git clone https://github.com/OpenFreeEnergy/openfe-benchmarks
cd openfe-benchmarks/openfe_benchmarks/data/benchmark_systems/jacs_set/tyk2

Extract the one ligand you want from the multi-molecule SDF — the bond orders in that file are what parameterisation needs, and a PDB cannot supply them:

from rdkit import Chem

supplier = Chem.SDMolSupplier("ligands.sdf", removeHs=False)
ejm_31 = next(m for m in supplier if m.GetProp("_Name") == "ejm_31")
Chem.MolToMolFile(ejm_31, "ejm_31.sdf")

1. Build the ligand parameters, once

A ligand needs its own parameters before it can appear in any simulation, and MD-tools makes that a separate step with its own output: a parameter package, created once and reused in every environment — complex, solvent box, vacuum — so that every leg of every later calculation uses the identical intramolecular Hamiltonian.

mkdir -p tyk2 && cd tyk2
md-openmm build-top --parameterize -i ../ejm_31.sdf --config para.config \
    -op packages/L31.pdb -os packages/L31.xml \
    -log packages/parameterize.log --resname L31

It writes a package directory — that directory is the thing later builds point at:

tyk2/
└── packages/
    └── LOCAL-DKNAYSZNMZIMIZ/
        └── param_bd1388e5fe3e/
            ├── molecule.sdf       the exact chemical state
            ├── parameters.ffxml   the parameters themselves
            ├── metadata.json      identities, charges, provenance
            └── parameter.config   what a build must match to REUSE it

The two names are derived, not chosen: LOCAL-… is the compound id from the molecule's InChIKey, and param_… is a digest of the parameters. Yours will match these if the chemistry and the settings match.

para.config states the small-molecule force field and the charge method:

# para.config
solute:
  kind: ligand
  ligand_forcefield: openff-2.2.1        # SMIRNOFF; the default is sage-2.2.1
  ligand_charge_method: am1bcc           # through AmberTools sqm
  aliases: [ejm_31]                      # searchable names — set them now, see below

The package records the force field, the charge method and the implementation that produced the charges (backend_id: ambertools-sqm), so a later build can tell whether a package it found was made the same way it would make one.

AM1-BCC through AmberTools sqm takes about 70 seconds for a ligand this size. The package that comes out is identified by its chemical state and its parameters, not by its conformer — the same molecule from a different pose produces the same package id.

Give the package aliases before you register it

solute.aliases defaults to empty and registration is write-once. A package registered without aliases can only ever be found by its compound id, never by the name you think of it by, and that cannot be corrected afterwards. See ligand packages.

2. Build the complex

With the package in hand, the complex build reuses it rather than re-parameterising:

# complex.config -- written in tyk2/, beside packages/
solute:
  kind: complex
ligands:
  - select: {chain: B, resid: "1"}
    parameters: LOCAL-DKNAYSZNMZIMIZ/param_bd1388e5fe3e   # <compound-id>/<parameter-id>
ligand_catalog:
  path: ./packages        # the directory step 1 wrote into, relative to THIS file
forcefield:
  protein: ff14SB
solvent:
  model: TIP3P
  padding_nm: 1.2
  box_shape: cube
  cutoff_nm: 0.9
constraints:
  type: HBonds
  rigid_water: true
md-openmm build-top -i ../complex_ejm_31.pdb --config complex.config \
    -os build/built.xml -op build/built.pdb -log build/built.log

The two lines work together. ligand_catalog.path says which directory to look in — here the one step 1 wrote, relative to complex.config itself — and parameters names the package inside it as <compound-id>/<parameter-id>, the two directory levels in the tree above. If the package is already in the machine catalog under $MD_DATA, drop ligand_catalog and keep the parameters line; if you would rather not name the id at all, write parameters: search and let the build find a package whose recorded criteria match.

built.log says which route ran — reused (stated reference), reused (catalog search) or created — so no build can quietly regenerate charges without saying so.

Built this way the system is 53,030 particles, 16,462 residues, ff14SB and TIP3P in a 1.2 nm cubic box. Everything downstream — plain MD, selective REST2, and later the alchemical methods — starts from exactly these files.

Why ff14SB and TIP3P rather than ff19SB and OPC

OPC applies its own rounded 1-4 electrostatic scale to every pair, the ligand's included, while a vacuum build applies the package's exact value. Alchemical cycles pair a solvated leg with a vacuum leg and refuse that mismatch, so the choice is made here, once, for every method that will use this system.

Where to go next

method what it is for
Selective REST2 choosing the hot region: the ligand alone, or the ligand plus the sidechains lining the pocket published, 0.6.1
cMD a long unbiased simulation of the same complex, as the reference every enhanced method is compared against published, 0.6.1 — 1 µs
AIS annealing a weakened pocket back to full strength published, 0.6.1 — usable for the ligand alone, not for the ligand plus its shell
Umbrella sampling a potential of mean force along the ligand–protein centre-of-mass distance page planned, 0.6.5
Alchemical — ligand to ligand absolute and relative binding free energies on this complex planned, 0.6.5

Umbrella sampling arrives in 0.6.3 and the alchemical pages in 0.7.0. They are listed here so the shape of the set is visible; neither is written yet, and neither is linked to a page that does not exist.

Every page here copies its numbers from the files a real run produced, which is why the cMD page appeared only once its 1 µs run had finished — 4 days 6 h on one card.