bio-pose-validation · git:20260821.2d1b678 · 2026-08-21 · sha256 7c796c1375827c65
bio-pose-validation git:20260821.2d1b678A
Immutable. This exact content is served forever at /api/v1/blob/7c796c1375827c65.
---
name: bio-pose-validation
description: Validates docked / generated protein-ligand poses using PoseBusters physical-validity tests, strain energy quantification, geometric checks (planarity, vdW overlap, bond/angle distortion), and pose-energy reasonableness. Use when QC-ing docking results, comparing classical vs ML docking outputs, or filtering pose lists before SAR analysis.
origin: openai4s
category: bioskills/chemoinformatics
metadata:
tool_type: python
primary_tool: PoseBusters
third_party:
name: GPTomics/bioSkills
repository: https://github.com/GPTomics/bioSkills
commit: d91ed3d563019e649dc854c56ccd62551359488a
license: MIT
---
## Version Compatibility
Reference examples tested with: PoseBusters 0.6+, RDKit 2024.09+, pandas 2.2+, posecheck 0.5+ (optional).
Before using code patterns, verify installed versions match. If versions differ:
- Python: `pip show <package>` then `help(module.function)` to check signatures
If code throws ImportError, AttributeError, or TypeError, introspect the installed
package and adapt the example to match the actual API rather than retrying.
# Pose Validation
Test docked or AI-generated protein-ligand poses for physical plausibility. PoseBusters (Buttenschoen et al. 2024) provides geometric, chemical, and energetic checks that flag implausible poses, including non-planar aromatic rings, van der Waals clashes, broken bonds, altered stereochemistry, and unfavorable internal energies. On the Astex Diverse Set, DiffDock achieved 72% RMSD success but only 47% combined RMSD-and-PB-valid success; the size of this gap is dataset- and method-dependent. PB-valid status complements RMSD for downstream SAR, FEP setup, or generative-model training.
For docking, see `chemoinformatics/virtual-screening`. For ML docking specifically, see `chemoinformatics/ml-docking-rescoring`.
## PoseBusters Test Suite
PoseBusters runs ~20 individual checks grouped into:
The thresholds below are the benchmark criteria reported by Buttenschoen et al. (2024). Installed PoseBusters defaults may differ by version and configuration, so record the package version and resolved configuration.
| Check group | What it tests | 2024 benchmark criterion |
|-------------|---------------|-----------|
| Sanity | Ligand chemical sanity | RDKit sanitization passes |
| Bond lengths | Bond lengths within reference | 0.75–1.25 times RDKit distance-geometry bounds |
| Bond angles | 1–3 distances within reference | 0.75–1.25 times RDKit distance-geometry bounds |
| Internal steric | No intra-ligand clash | Pair distance > 0.70 times the RDKit lower bound |
| Aromatic ring planarity | Aromatic rings planar | Maximum deviation from fitted plane <= 0.25 Å |
| Double-bond stereo | Z/E preserved | Match input SMILES |
| Internal energy | Energy relative to generated conformers | UFF energy ratio <= 100 versus the mean of 50 generated, relaxed conformers |
| Volume overlap | vdW overlap with protein | < 7.5% of ligand vdW volume |
| Minimum distance | No severe protein-ligand clash | Distance >= 0.75 times the sum of vdW radii |
| Chirality | R/S preserved from input | Match input SMILES |
A pose passing ALL tests is "PB-valid". Combined PB-valid + RMSD <= 2 Å is the modern criterion.
## When to Apply PoseBusters
| Workflow | PoseBusters use | Action |
|----------|-----------------|--------|
| Self-docking (validating method) | Required | Compare PB-valid + RMSD <= 2A |
| Cross-docking | Required | PB-valid + RMSD <= 2A; account for protein flexibility |
| Virtual screening top hits | Required | Filter to PB-valid before MM/GBSA / FEP |
| AI docking (DiffDock, etc.) | Required for a fair benchmark | Report the dataset-specific PB-valid and combined success rates |
| Generated ligand poses | Recommended | Measure chemical and geometric validity rather than assuming it |
| Boltz-2 / AlphaFold3 ligand poses | Recommended | Benchmark validity on the relevant complexes; do not infer a failure frequency from DiffDock |
| Production FEP setup | Required | Inspect pose validity and ligand strain before system preparation |
## PoseBusters Usage
```python
from posebusters import PoseBusters
bust = PoseBusters(config='redock')
results = bust.bust(
mol_pred='predicted.sdf',
mol_true='reference.sdf',
mol_cond='receptor.pdb',
)
```
Common configurations and their included checks are:
| Config | Includes | When to use |
|--------|----------|-------------|
| `redock` | All checks + RMSD vs reference + protein vdW overlap | Self-docking benchmarks, retrospective validation |
| `dock` | All checks except RMSD reference | Blind docking, prospective virtual screening |
| `mol` | Intra-ligand only (sanity, bonds, angles, rings, stereo, energy) | Conformer QC; no protein context |
PoseBusters also ships additional and faster configurations in some releases. Treat the table as a workflow guide, not an exhaustive registry, and inspect the configurations available in the installed version.
Output: a DataFrame with one row per pose, metadata columns, and boolean pass/fail columns for the checks enabled by the selected configuration. Reference-dependent fields such as RMSD and the exact check-column names vary by configuration and version; inspect `results.columns` rather than relying on a fixed exhaustive list.
## Python Library API
**Goal:** Programmatically validate a docked-pose SDF against a receptor PDB and produce a PB-valid filter.
**Approach:** Instantiate `PoseBusters(config='dock')`, call `bust()` on the SDF + PDB pair, and AND-aggregate all boolean check columns into a single `pb_valid` flag.
```python
from posebusters import PoseBusters
import pandas as pd
bust = PoseBusters(config='dock')
results = bust.bust(
mol_pred='/path/to/docked_poses.sdf',
mol_cond='/path/to/receptor.pdb',
)
check_cols = [
col for col in results.select_dtypes(include='bool').columns
if not col.lower().startswith('rmsd')
]
results['pb_valid'] = results[check_cols].all(axis=1)
valid = results[results['pb_valid']]
print(f'{len(valid)} / {len(results)} poses are PB-valid')
```
## Strain Energy Quantification
Beyond binary PB-valid, quantitative strain energy distinguishes "marginal" from "egregious" poses.
**Goal:** Quantify how far each docked pose is from its lowest-energy free conformer in MMFF94 energy units.
**Approach:** Generate a reference conformer ensemble (ETKDGv3 + MMFF94), make the docked and reference molecules chemically consistent by adding explicit hydrogens to both, relax only the added docked-pose hydrogens while fixing all heavy atoms, take the lowest sampled reference energy as baseline, and report `docked_energy - min_ref_energy` as a relative strain diagnostic. This is not a rigorous solution-phase conformational free energy.
```python
from rdkit import Chem
from rdkit.Chem import AllChem
def ligand_strain(docked_sdf, n_ref=20):
suppl = Chem.SDMolSupplier(docked_sdf, removeHs=False)
strains = []
for docked in suppl:
if docked is None:
continue
smi = Chem.MolToSmiles(docked)
ref = Chem.MolFromSmiles(smi)
if ref is None:
strains.append({'strain': None, 'note': 'reference_parse_failed'})
continue
ref = Chem.AddHs(ref)
props_ref = AllChem.MMFFGetMoleculeProperties(ref)
if props_ref is None:
strains.append({'strain': None, 'note': 'no_reference_mmff_parameters'})
continue
conf_ids = list(AllChem.EmbedMultipleConfs(
ref, numConfs=n_ref, params=AllChem.ETKDGv3()
))
if not conf_ids:
strains.append({'strain': None, 'note': 'reference_embedding_failed'})
continue
AllChem.MMFFOptimizeMoleculeConfs(ref)
ref_energies = []
for c in conf_ids:
ff = AllChem.MMFFGetMoleculeForceField(
ref, props_ref, confId=c
)
if ff is not None:
ref_energies.append(ff.CalcEnergy())
if not ref_energies:
strains.append({'strain': None, 'note': 'reference_force_field_failed'})
continue
min_ref = min(ref_energies)
# MMFF energies are comparable only for the same explicit atom system.
# Add any missing H coordinates, then relax H atoms while preserving the
# docked heavy-atom pose.
docked_h = Chem.AddHs(Chem.Mol(docked), addCoords=True)
if docked_h.GetNumAtoms() != ref.GetNumAtoms():
strains.append({'strain': None, 'note': 'atom_system_mismatch'})
continue
props_docked = AllChem.MMFFGetMoleculeProperties(docked_h)
docked_ff = AllChem.MMFFGetMoleculeForceField(
docked_h, props_docked
) if props_docked is not None else None
if docked_ff is not None:
for atom in docked_h.GetAtoms():
if atom.GetAtomicNum() != 1:
docked_ff.AddFixedPoint(atom.GetIdx())
docked_ff.Minimize(maxIts=200)
docked_e = docked_ff.CalcEnergy() if docked_ff else None
strains.append({
'min_ref_energy': min_ref,
'docked_energy': docked_e,
'strain': docked_e - min_ref if docked_e is not None else None,
'note': 'ok' if docked_e is not None else 'docked_force_field_failed',
})
return strains
```
Interpret relative MMFF strain in the context of ligand chemistry, conformer-sampling coverage, and force-field support. Boström et al. (1998) found a conformational energy penalty of no more than 3 kcal/mol for about 70% of 33 protein-bound ligands; that result does not establish a universal acceptance cutoff. Treat unusually high values as a prompt for inspection or use a project-defined threshold validated for the series.
## vdW Overlap with Protein
The 2024 benchmark criterion limits protein-ligand overlap to 7.5% of the ligand vdW volume, using protein radii scaled by 0.8. PoseBusters' `bust(...)` computes this check; do not substitute an unvalidated pairwise-distance sketch for its volume calculation.
## Aromatic Ring Planarity
```python
import numpy as np
def aromatic_planarity(mol):
deviations = []
for ring in mol.GetRingInfo().AtomRings():
ring_atoms = [mol.GetAtomWithIdx(i) for i in ring]
if not all(a.GetIsAromatic() for a in ring_atoms):
continue
coords = np.array([mol.GetConformer().GetAtomPosition(i)
for i in ring])
centroid = coords.mean(axis=0)
centered = coords - centroid
_, s, vh = np.linalg.svd(centered)
normal = vh[-1]
deviation = np.abs(centered @ normal).max()
deviations.append(deviation)
return max(deviations) if deviations else 0
```
Aromatic ring deviation > 0.25 Å is implausible; flag.
## Model-Specific Failure Diagnosis
Do not assign a mechanism from the model name or a failed PoseBusters column alone. For DiffDock-L, EquiBind, TANKBind, Boltz, AlphaFold3, or another pose generator, report the observed failed checks on the evaluated dataset, inspect the structures, and compare against the method's documented constraints. A chirality, planarity, bond-geometry, or clash failure may justify filtering or a validated constrained-relaxation protocol, but relaxation must be checked for displacement of the binding mode.
### High strain after Vina docking
**Trigger:** Highly constrained pocket; flexible ligand.
**Symptom:** Relative strain is an outlier for the chemical series even though the pose passes the enabled geometric checks.
**Fix:** Inspect conformer-sampling coverage and force-field support. Compare additional docking or constrained-relaxation settings under a project-validated protocol rather than applying a universal strain or exhaustiveness cutoff.
## Reconciliation: PoseBusters vs RMSD
| RMSD <= 2A | PB-valid | Action |
|------------|----------|--------|
| Yes | Yes | Physically plausible and close to the reference; still validate suitability for the downstream task |
| Yes | No | Close to the reference but fails an enabled plausibility check; inspect the failure and any validated relaxation |
| No | Yes | Physically plausible but different from the reference; investigate alignment, protein state, and alternative binding modes |
| No | No | Different from the reference and fails an enabled plausibility check; inspect both causes before deciding whether to reject |
On the Astex Diverse Set reported by Buttenschoen et al. (2024), DiffDock's top-pose success fell from 72% by RMSD <= 2 Å alone to 47% when PB-validity was also required: a 25-percentage-point gap. Do not generalize that result to a fixed failure rate on other datasets.
## Integration into VS Pipeline
```python
import pandas as pd
from posebusters import PoseBusters
def pose_qc_pipeline(docked_sdfs, receptor_pdb):
bust = PoseBusters(config='dock')
all_results = []
for sdf in docked_sdfs:
r = bust.bust(mol_pred=sdf, mol_cond=receptor_pdb)
check_cols = [
col for col in r.select_dtypes(include='bool').columns
if not col.lower().startswith('rmsd')
]
r['pb_valid'] = r[check_cols].all(axis=1)
r['source'] = sdf
all_results.append(r)
df = pd.concat(all_results)
df['rank'] = df.groupby('source')['pb_valid'].cumsum()
valid_top = df[df['pb_valid']].groupby('source').head(1)
return valid_top
```
## Common Errors
| Symptom | Cause | Fix |
|---------|-------|-----|
| Rows or expected checks are missing | Input loading failed or the selected configuration omits those checks | Inspect the returned DataFrame, loading-status columns, input format, and installed configuration |
| RMSD not computed | No reference provided | Pass `mol_true` parameter |
| All checks pass for invalid pose | Wrong receptor file format | Use PDB with hydrogens; PDBQT may not work |
| vdW overlap false positive on covalent | Covalent bond counted as clash | Use covalent docking-specific validation |
| Strain calculation slow | Too many reference conformers | Reduce `n_ref` to 5-10 |
| PoseBusters config error | Wrong or version-incompatible config name | Inspect the installed configuration registry; `redock`, `dock`, and `mol` are common configurations |
| posecheck unavailable | Different tool, similar purpose | `pip install posecheck` for alternative |
## References
- Buttenschoen M, Morris GM, Deane CM. "PoseBusters: AI-based docking methods fail to generate physically valid poses or generalise to novel sequences." *Chem. Sci.* 15:3130–3139 (2024). DOI: 10.1039/D3SC04185A.
- Boström J, Norrby PO, Liljefors T. "Conformational energy penalties of protein-bound ligands." *J. Comput.-Aided Mol. Des.* 12:383–396 (1998). DOI: 10.1023/A:1008007507641.
- Corso G et al. "DiffDock: Diffusion Steps, Twists, and Turns for Molecular Docking." *ICLR* (2023). OpenReview: https://openreview.net/forum?id=kKF8_K-mBbS.
- Stärk H et al. "EquiBind: Geometric Deep Learning for Drug Binding Structure Prediction." *PMLR* 162:20503–20521 (2022). https://proceedings.mlr.press/v162/stark22b.html.
- Lu W et al. "TankBind: Trigonometry-Aware Neural NetworKs for Drug-Protein Binding Structure Prediction." *NeurIPS* 35 (2022). Official repository: https://github.com/luwei0917/TankBind.
- Abramson J et al. "Accurate structure prediction of biomolecular interactions with AlphaFold 3." *Nature* 630:493–500 (2024). DOI: 10.1038/s41586-024-07487-w.
- Boltz official repository and documentation: https://github.com/jwohlwend/boltz.
- PoseBusters documentation, Python API: https://posebusters.readthedocs.io/en/latest/api.html.
## Related Skills
- chemoinformatics/virtual-screening - Source of poses to validate
- chemoinformatics/ml-docking-rescoring - DiffDock, EquiBind, TANKBind validation
- chemoinformatics/molecular-io - SDF format handling
- chemoinformatics/conformer-generation - Generate reference conformer ensemble for strain
- chemoinformatics/free-energy-calculations - PoseBusters-valid poses for FEP input
- chemoinformatics/covalent-design - Covalent pose validation