Skip to content

Docking

This document describes how to dock ligands to a protein using Deep Origin tools.

Prerequisites

First, import the necessary classes:

from deeporigin.drug_discovery import (
    BRD_DATA_DIR,
    Docking,
    Ligand,
    Pocket,
    PocketFinder,
    Protein,
)

Load protein

Load a protein from a PDB file and remove water molecules:

protein = Protein.from_file(BRD_DATA_DIR / "brd.pdb")
protein.remove_water()

We can view the protein to see that waters have been removed:

protein.show()

Sync the protein to the platform so it can be used for docking:

protein.sync()

Load ligand

Load a ligand from an SDF file:

ligand = Ligand.from_sdf(BRD_DATA_DIR / "brd-2.sdf")

Sync the ligand to the platform and to your project:

ligand.sync()

Find pockets in Protein

Run Pocket Finder on the synced protein to detect binding sites. Create a PocketFinder with that protein, then call run() to execute the tool and get a list of Pocket objects:

pf = PocketFinder(protein, pocket_count=1)
pockets = pf.run()
pocket = pockets[0]

We can visualize the pocket using:

protein.show(pockets=pockets)

You should see something along the lines of:

We can see that the protein is shown together with the identified pocket in red.

Oriented search box

New pocket-finder runs include a nested box with PCA-aligned sizes and rotation_deg. When you build a Docking from that pocket, show_box() and docking runs use the inferred orientation automatically. See Interactive rotation below to override it in the notebook.

The Pocket Finder Function

For more details on how to use the Pocket Finder, look at PocketFinder.

Preview the docking search box (protein plus wireframe box from pocket center and box size) before running:

docking = Docking(protein=protein, pocket=pocket, ligand=ligand)
docking.show_box()

When the pocket row includes nested box from pocket-finder, the wireframe defaults to that inferred orientation (PCA-aligned sizes and rotation_deg).

To overlay docked poses with the search box (e.g. after run() / get_poses()):

poses = docking.get_poses()
docking.show_box(poses=poses)

Rotating the search box interactively

In JupyterLab, pass interactive=True to rotate the search box with molstar's Docking Box controls (open Settings in the viewer). Releasing a slider or ending a canvas drag stores the orientation on the Docking instance; subsequent run() or start() calls include rotation_deg in the pocket sent to the platform. The overlay shows the last synced rotation. Printed cell output does not update until you re-run the cell.

handle = docking.show_box(interactive=True)
docking.rotation_deg  # after a rotation gesture — e.g. [0.0, 45.0, 0.0]

Interactive mode requires JupyterLab with the project kernel (make jupyter). See the Interactive docking box notebook for a full walkthrough. Rotated docking on the platform requires toolbox docking 3.4.32+ on your org.

When the pocket came from pocket-finder with a nested box, show_box() already applies that inferred orientation. Interactive rotation replaces it only after you commit a gesture (session rotation overrides inferred orientation on subsequent run() / start() calls).

You should see something like this:

The pocket object can be inspected, too:

pocket

Expected Output

Pocket:
╭─────────────────────────┬──────────────╮
│ Name                    │ pocket_1     │
├─────────────────────────┼──────────────┤
│ Color                   │ red          │
├─────────────────────────┼──────────────┤
│ Volume                  │ 545.0 ų     │
├─────────────────────────┼──────────────┤
│ Total SASA              │ 1560.474 Ų  │
├─────────────────────────┼──────────────┤
│ Polar SASA              │ 762.11224 Ų │
├─────────────────────────┼──────────────┤
│ Polar/Apolar SASA ratio │ 0.95459515   │
├─────────────────────────┼──────────────┤
│ Hydrophobicity          │ 15.903226    │
├─────────────────────────┼──────────────┤
│ Polarity                │ 17.0         │
├─────────────────────────┼──────────────┤
│ Drugability score       │ 0.83243614   │
╰─────────────────────────┴──────────────╯

Estimate the cost of a docking run

Create a Docking object with the protein, pocket, and ligand, then get a cost estimate:

docking = Docking(protein=protein, pocket=pocket, ligand=ligand)
docking.start(quote=True)
docking.estimate

The estimate property shows the predicted cost in dollars for the docking run.

Controlling effort

By default, the docking effort level is 3 (on a scale of 1–5). Lower effort is faster, higher effort is more thorough.

docking.effort = 1

Start the docking run

To dock this one ligand to the protein, use:

poses = docking.run()

The returned poses is a LigandSet containing the docked poses.

Monitoring jobs

For more details about how to monitor jobs, look at this How To section.

Results

Viewing results

Each docked pose is assigned a Pose Score and a Binding Energy.

  • The pose_score is a score that evaluates the quality of each ligand's pose, where higher scores indicate better predicted binding poses. This score can be more informative than binding energy for identifying the optimal conformation.
  • The binding_energy is the predicted binding energy typically used to estimate the strength of interaction between the ligand and the protein. The units are in kcal/mol and generally the lower energy scores (more negative values) mean higher chances that the ligand would bind to the protein strongly.

When you load tabular results with docking.get_results(), each row includes a best_pose boolean: True for the single pose with the highest pose_score among poses for that ligand (for example, one of sixteen poses per ligand), and False for the others.

You can inspect the properties of individual poses:

poses[0].properties

Viewing docked poses

To visualize the docked poses on the protein, use:

poses.download()
protein.show(poses=poses)

All poses are loaded and overlaid on the protein. When there is more than one pose, a navigation bar appears at the bottom of the viewer: use its ◀ / ▶ buttons or the Left/Right arrow keys to cycle through an "all poses" view and each individual pose one at a time.

Retrieving poses from the platform

For async docking runs, you can retrieve docked poses after completion:

poses = docking.get_poses()

Exporting for further analysis

Poses can be converted into a dataframe for further analysis or export:

df = poses.to_dataframe()

Filtering poses

You typically want to filter these poses to only retain the top pose for each ligand. To do that, use:

poses = poses.filter_top_poses()