Docking¶
Dock ligands to a Protein with the Deep Origin docking tool.
Prerequisites¶
- You have a prepared Protein
- You have a Ligand or LigandSet
- You have protonated your ligands
- You have a
Pocketthat defines the binding site
Docking does not search for a binding site: you always pass the pocket
explicitly. For a known site, use the crystal-ligand pocket from
Protein Prep
(find_pockets="from-crystal-ligand"). For a target without a known site,
discover candidates with find_pockets="novel" or
PocketFinder and choose one. The
docking tutorial walks through both.
from deeporigin.drug_discovery import Protein, ProteinPrep
protein = Protein.from_pdb_id("1EBY")
protein.sync()
prep = ProteinPrep(
protein,
find_pockets="from-crystal-ligand",
component_id="ligand:B:BEB:501:",
)
prep.recommend()
protein = prep.run() # the prepared protein
pocket = prep.get_pockets()[0]
Docking a single Ligand¶
Use Docking with your protein, pocket, and ligand. Both Docking.run() and Docking.start() use client.executions.create: run() sets sync=True for one ligand (one blocking request until completion), and start() sets sync=False for a single persisted async job with two or more ligands. Docking.run() returns a PoseSet of poses.
from deeporigin.drug_discovery import Docking
docking = Docking(protein=protein, pocket=pocket, ligand=ligand)
poses = docking.run()
Estimating cost¶
To get a cost estimate without running the docking, pass quote=True to run():
docking = Docking(protein=protein, pocket=pocket, ligand=ligand)
docking.run(quote=True) # populates docking.estimate
docking.estimate # estimated cost in dollars
docking.cost # None until a billable run completes
After a completed run, the actual cost is on the same object:
docking = Docking(protein=protein, pocket=pocket, ligand=ligand)
poses = docking.run()
docking.cost # actual cost in dollars
Viewing docked poses¶
Docked poses for that ligand can be viewed using:
protein.show(poses=poses)
You will see something similar to the following. Use the arrows to inspect individual poses.
Viewing pose scores and binding energy¶
Every pose is assigned a pose score and a binding energy. These can be viewed using:
poses
A widget similar to the following will be shown:
LigandSet with 15 poses
SMILES: Cc1[nH]c2cc(Cl)cc(Cl)c2c1CCN
Properties: Binding Energy, POSE SCORE, SMILES, initial_smiles
Use .to_dataframe() to convert to a dataframe, .show_df() to view dataframewith structures, or .show() for 3D visualization
To work with a dataframe containing this data, use:
df = poses.to_dataframe()
Exporting poses to SDF¶
Poses can be saved to a SDF file using:
poses.to_sdf()
Docking many ligands¶
Several ligands in a LigandSet can be docked by passing ligands to Docking, then Docking.start() (async). Poll with sync() or Jupyter helpers until the job completes, then call get_results() to retrieve a PoseSet of poses.
docking = Docking(protein=protein, pocket=pocket, ligands=ligands)
docking.start()
# … wait for completion (docking.sync() in a loop, or watch() in notebooks) …
poses = docking.get_results()
poses is a PoseSet containing the docked poses. To work with a DataFrame:
df = poses.to_dataframe()
To filter poses to keep only top poses, use:
top_poses = poses.filter_top_poses()
These poses can be visualized as before:
protein.show(poses=poses)
If you need SDF files for the poses (e.g. for export), use get_poses() instead, which downloads the SDF files from the platform:
poses = docking.get_poses()
poses.to_sdf("my_poses.sdf")
To estimate the cost of docking a full LigandSet without running it:
docking = Docking(protein=protein, pocket=pocket, ligands=ligands)
docking.start(quote=True)
docking.estimate # total estimated cost across all ligands
Working with existing runs¶
Reconnect to an asynchronous docking run started earlier, in this or a previous session, instead of re-submitting it:
# By execution id:
docking = Docking.from_id("<executionId>")
# Or the most recently created Docking run:
docking = Docking.from_last_run()
docking.sync() # refresh status from the platform
poses = docking.get_results()
from_last_run() returns the newest execution of that tool type in your
current project, so call it on the class you submitted with —
ConstrainedDocking.from_last_run() for a constrained run.
Constrained docking¶
Use ConstrainedDocking to dock test ligands while
preserving a reference binding mode. The platform derives harmonic constraints
server-side from MCS alignment between each test ligand and a supplied
reference pose. Callers provide reference_ligand (scaffold identity) and
reference_pose (required 3D SDF coordinates); they do not pass constraint
atom lists.
Crystal pose as the reference¶
When the pocket comes from a crystal ligand, Protein Prep already has the
reference pose: use the extracted crystal pose from get_crystal_poses() and
the extracted ligand (pocket.ligand_id):
from deeporigin.drug_discovery import ConstrainedDocking, Ligand
reference_ligand = Ligand.from_id(pocket.ligand_id)
crystal_pose = prep.get_crystal_poses().poses[0]
crystal_pose.download()
cd = ConstrainedDocking(
protein=protein, # the prepared protein
pocket=pocket,
reference_ligand=reference_ligand,
reference_pose=crystal_pose.to_ligand(),
ligand=query_ligand,
)
poses = cd.run()
Dock-then-constrain pipeline¶
Without a crystal pose, dock a reference ligand, upload the best pose SDF, then constrained-dock analogs:
from deeporigin.drug_discovery import ConstrainedDocking, Docking
ref_poses = Docking(protein=protein, pocket=pocket, ligand=reference_ligand).run()
reference_pose = ref_poses.ligands[0]
reference_pose.sync(remote_path="testing/reference-pose.sdf")
cd = ConstrainedDocking(
protein=protein,
pocket=pocket,
reference_ligand=reference_ligand,
reference_pose=reference_pose,
ligand=query_ligand,
)
poses = cd.run()
Each test ligand must have SMILES or a structure file on the platform (load from
SDF/MOL2 and call ligand.sync(), or use Ligand.from_smiles and call
ligand.sync()). The reference pose must have 3D coordinates.
SMILES-only test ligands¶
When a test ligand has no structure file, provide SMILES and sync the entity:
from deeporigin.drug_discovery import ConstrainedDocking, Ligand
test_ligand = Ligand.from_smiles("CC(C)O")
test_ligand.sync()
cd = ConstrainedDocking(
protein=protein,
pocket=pocket,
reference_ligand=reference_ligand,
reference_pose=reference_pose,
ligand=test_ligand,
)
poses = cd.run()
The platform embeds the SMILES to an ephemeral 3D structure for MCS alignment. Reference ligand and reference pose still require structure files on the platform.
To view new poses together with the reference pose:
protein.show(poses=reference_pose + poses)
Inspect whether harmonic constraints were applied via the constrained property
on each pose in poses.to_dataframe(). When MCS cannot match a test ligand,
the tool free-docks that ligand and sets constrained=False.
Retrieve the reference pose echoed by the tool:
reported_reference = cd.get_reference_pose()
Multiple test ligands (async)¶
Pass ligands= and use start() / watch() for batch constrained docking:
cd = ConstrainedDocking(
protein=protein,
pocket=pocket,
reference_ligand=reference_ligand,
reference_pose=reference_pose,
ligands=analogs,
)
cd.start()
# … wait for completion …
poses = cd.get_results()
MCS override¶
Force a common scaffold with mcs_smarts or mcs_smiles (mutually exclusive):
cd = ConstrainedDocking(
protein=protein,
pocket=pocket,
reference_ligand=reference_ligand,
reference_pose=reference_pose,
ligand=query_ligand,
mcs_smarts="C(=O)",
)
poses = cd.run()
Estimating cost¶
cd = ConstrainedDocking(
protein=protein,
pocket=pocket,
reference_ligand=reference_ligand,
reference_pose=reference_pose,
ligand=query_ligand,
)
cd.run(quote=True)
cd.estimate
Filtering docking outputs¶
Filter docking results by score and related properties. Here we assume that
you have run Docking on many ligands with a Docking
object called docking.
Fetch docked poses¶
First, fetch the docked poses:
poses = docking.get_poses()
poses object shows us:
LigandSet with 2246 ligands
157 unique SMILES
Properties: Binding Energy, POSE SCORE, SCORE, SMILES, initial_smiles
Use .to_dataframe() to convert to a dataframe, .show_df() to view dataframewith structures, or .show() for 3D visualization
Plot docking results¶
The metrics of all docked poses can be plotted in a scatter plot using:
poses.plot()
Pick top results¶
We can pick the top pose for each SMILES string using:
poses.filter_top_poses()