Skip to content
cccskills
FreePublished on GitHub

smina-molecular-docking

smina molecular docking CLI. AutoDock Vina fork with customizable scoring functions, native SDF/MOL2/PDB ligand input, autoboxing, local energy minimization, and per-atom score breakdowns. Pipeline: receptor PDBQT prep -> ligand prep (RDKit/OpenBabel) -> dock via autobox or explicit grid -> rescore/minimize with custom scoring -> rank poses by affinity. Choose smina over Vina when you need custom scoring terms (--custom_scoring), local optimization of an existing pose (--local_only), per-atom contributions (--atom_term_data), or SDF/MOL2 ligands without manual PDBQT conversion. For unknown binding sites use diffdock; for the Python-bindings/Vinardo workflow use autodock-vina-docking.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md14.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

smina Molecular Docking

Overview

smina is an AutoDock Vina 1.1.2 fork focused on flexible scoring and minimization. Accepts SDF/MOL2/PDB ligands directly (no manual PDBQT), autoboxes from a reference ligand, ships six built-in scoring functions plus arbitrary --custom_scoring terms, and prints per-atom score contributions. CLI-only — drive from Python via subprocess.

When to Use

  • Re-scoring or locally minimizing an existing pose (--local_only, --minimize) without a full search
  • Single-pose binding energy without docking (--score_only)
  • Docking with a custom or empirical scoring function tuned to a target class
  • SDF/MOL2/multi-ligand input without per-ligand PDBQT conversion
  • Autoboxing the grid around a co-crystallized reference ligand
  • Per-atom energy decomposition (--atom_term_data) for medchem analog design
  • Batch virtual screening that parallelizes well across nodes (one CLI process per ligand)
  • Use autodock-vina-docking instead when you need Vina Python bindings, Vinardo scoring, or Vina 1.2's expanded force field; use diffdock when the binding site is unknown

Prerequisites

  • smina binary — conda-forge or SourceForge build
  • Python: rdkit, openbabel-wheel (or system openbabel), prody, pandas, py3Dmol
  • ADFR Suite for prepare_receptor (receptor PDBQT only — ligands handled by smina)
  • Data: protein (PDB / PDB ID), ligand(s) as SMILES / SDF / MOL2

Check before installing — inside a pixi/conda env smina is usually already on PATH. If command -v smina succeeds, skip install; inside a pixi project invoke as pixi run smina ....

command -v smina || conda install -c conda-forge smina openbabel
pip install rdkit prody pandas py3Dmol
# ADFR Suite: https://ccsb.scripps.edu/adfr/downloads/

Quick Start

End-to-end docking using autobox from a reference ligand:

import subprocess

result = subprocess.run([
    "smina",
    "-r", "1hpv_receptor.pdbqt",
    "-l", "candidate.sdf",
    "--autobox_ligand", "1hpv_ref_ligand.pdb",
    "--autobox_add", "8",          # padding around reference (Å)
    "-o", "candidate_docked.sdf",
    "--exhaustiveness", "16",
    "--num_modes", "9",
    "--seed", "42",
], check=True, capture_output=True, text=True)

print(result.stdout.splitlines()[-15:])  # affinity table at stdout tail

Workflow

Step 1: Prepare the Receptor (PDBQT)

Strip waters/hetatms, then run ADFR Suite's prepare_receptor.

import subprocess, prody

pdb_id = "1HPV"
prody.fetchPDB(pdb_id, compressed=False)
protein = prody.parsePDB(f"{pdb_id}.pdb").select("protein")
prody.writePDB(f"{pdb_id}_protein.pdb", protein)

receptor_pdbqt = f"{pdb_id}_receptor.pdbqt"
subprocess.run([
    "prepare_receptor",
    "-r", f"{pdb_id}_protein.pdb",
    "-o", receptor_pdbqt,
    "-A", "hydrogens",
], check=True)
print(f"Receptor: {receptor_pdbqt} ({protein.numAtoms()} atoms)")

Step 2: Prepare the Ligand (SDF)

smina reads SDF directly. Generate 3D coords with RDKit.

from rdkit import Chem
from rdkit.Chem import AllChem

mol = Chem.MolFromSmiles("CC(C)(C)NC(=O)[C@@H]1CN(CCc2ccccc2)C[C@H]1O")
mol = Chem.AddHs(mol)
AllChem.EmbedMolecule(mol, randomSeed=42)
AllChem.MMFFOptimizeMolecule(mol)

w = Chem.SDWriter("candidate.sdf"); w.write(mol); w.close()
print(f"Ligand SDF: candidate.sdf ({mol.GetNumAtoms()} atoms)")

Step 3: Extract a Reference Ligand for Autoboxing

--autobox_ligand derives the grid from a reference structure.

import prody

ref = prody.parsePDB(f"{pdb_id}.pdb").select("hetero and not water and not ion")
if ref is None:
    raise RuntimeError("No reference ligand — supply explicit --center_x/--size_x")
prody.writePDB(f"{pdb_id}_ref_ligand.pdb", ref)
print(f"Ref ligand: {ref.numAtoms()} atoms, center {ref.getCoords().mean(axis=0).round(2)}")

Step 4: Run Docking with Autobox

Affinity table is printed to stdout — capture it.

import subprocess

proc = subprocess.run([
    "smina",
    "-r", receptor_pdbqt,
    "-l", "candidate.sdf",
    "--autobox_ligand", f"{pdb_id}_ref_ligand.pdb",
    "--autobox_add", "8",
    "-o", "candidate_docked.sdf",
    "--exhaustiveness", "16",
    "--num_modes", "9",
    "--energy_range", "3",
    "--cpu", "4",
    "--seed", "42",
], check=True, capture_output=True, text=True)

for line in proc.stdout.splitlines()[-15:]:
    print(line)

Step 5: Parse Poses and Affinities

Affinities go into the SDF <minimizedAffinity> property.

from rdkit import Chem
import pandas as pd

rows = []
for i, mol in enumerate(Chem.SDMolSupplier("candidate_docked.sdf", removeHs=False)):
    if mol is None:
        continue
    aff = float(mol.GetProp("minimizedAffinity")) if mol.HasProp("minimizedAffinity") else None
    rmsd = float(mol.GetProp("minimizedRMSD")) if mol.HasProp("minimizedRMSD") else None
    rows.append({"pose": i + 1, "affinity_kcal_mol": aff, "rmsd_to_best": rmsd})

df = pd.DataFrame(rows).sort_values("affinity_kcal_mol")
print(df.to_string(index=False))
print(f"Best: {df.iloc[0]['affinity_kcal_mol']:.2f} kcal/mol")

Step 6: Local Minimization of an Existing Pose

--local_only refines an input pose without global search.

import subprocess

subprocess.run([
    "smina",
    "-r", receptor_pdbqt,
    "-l", "candidate_pose.sdf",
    "--autobox_ligand", "candidate_pose.sdf",   # box around the pose itself
    "--autobox_add", "4",
    "-o", "candidate_min.sdf",
    "--local_only",
    "--minimize_iters", "1000",
], check=True, capture_output=True, text=True)
print("Local minimization complete: candidate_min.sdf")

Step 7: Visualize the Docked Complex

import py3Dmol

with open(f"{pdb_id}_protein.pdb") as f: rec = f.read()
with open("candidate_docked.sdf") as f: lig = f.read()

view = py3Dmol.view(width=800, height=600)
view.addModel(rec, "pdb"); view.setStyle({"model": 0}, {"cartoon": {}})
view.addModel(lig.split("$$$$")[0] + "$$$$", "sdf")
view.setStyle({"model": 1}, {"stick": {}})
view.zoomTo({"model": 1}); view.show()

Step 8: Batch Virtual Screening

One smina process per ligand parallelizes well across cores or cluster nodes.

import subprocess, pandas as pd
from rdkit import Chem
from rdkit.Chem import AllChem

library = pd.DataFrame({
    "name":   ["cpd_001", "cpd_002", "cpd_003"],
    "smiles": ["CC(=O)Oc1ccccc1C(=O)O",
               "CC(C)Cc1ccc(cc1)C(C)C(=O)O",
               "OC(=O)c1ccccc1O"],
})

results = []
for _, row in library.iterrows():
    lig_sdf, out_sdf = f"{row['name']}.sdf", f"{row['name']}_docked.sdf"
    mol = Chem.MolFromSmiles(row["smiles"]); mol = Chem.AddHs(mol)
    AllChem.EmbedMolecule(mol, randomSeed=42); AllChem.MMFFOptimizeMolecule(mol)
    w = Chem.SDWriter(lig_sdf); w.write(mol); w.close()

    proc = subprocess.run([
        "smina", "-r", receptor_pdbqt, "-l", lig_sdf,
        "--autobox_ligand", f"{pdb_id}_ref_ligand.pdb", "--autobox_add", "8",
        "-o", out_sdf, "--exhaustiveness", "8", "--num_modes", "1",
        "--cpu", "2", "--seed", "42",
    ], capture_output=True, text=True)

    if proc.returncode != 0:
        results.append({"name": row["name"], "affinity_kcal_mol": None})
        continue
    docked = next(iter(Chem.SDMolSupplier(out_sdf, removeHs=False)))
    aff = float(docked.GetProp("minimizedAffinity")) if docked and docked.HasProp("minimizedAffinity") else None
    results.append({"name": row["name"], "affinity_kcal_mol": aff})

ranked = pd.DataFrame(results).sort_values("affinity_kcal_mol")
ranked.to_csv("screening_results.csv", index=False)
print(ranked.to_string(index=False))

Key Parameters

ParameterDefaultRange / OptionsEffect
--exhaustiveness81-128MC search effort; 16-32 production, 64+ publication
--num_modes91-20Poses written to output SDF
--energy_range31-5 kcal/molMax ΔE from best pose in output
--scoringdefaultdefault, vina, vinardo, dkoes_scoring, dkoes_scoring_old, ad4_scoringBuilt-in scoring (default ≈ Vina)
--custom_scoring—pathUser-defined weighted terms; overrides --scoring
--autobox_ligand—PDB/SDF pathDerive grid from reference
--autobox_add42-12 ÅPadding around autobox extent
--center_x/y/z, --size_x/y/z—ÅManual grid (alternative to autobox)
--local_onlyoffflagSkip global search; local optimization only
--score_onlyoffflagEnergy without minimization or search
--minimizeoffflagEnergy-minimize without scoring search
--minimize_iters0 (auto)0-100000Local minimizer iterations
--atom_term_dataoffflagPer-atom score contributions in output
--cpuall1-NThreads per job
--seedrandomintRNG seed for reproducibility
--no_ligoffflagScore receptor-only as baseline

Common Recipes

Recipe: Score-Only (Single-Point Energy)

Compare pose energy between scoring functions without re-docking.

import subprocess

proc = subprocess.run([
    "smina", "-r", "receptor.pdbqt", "-l", "pose.sdf",
    "--score_only", "--scoring", "vinardo",   # try vina, vinardo, dkoes_scoring
], capture_output=True, text=True, check=True)

for line in proc.stdout.splitlines():
    if line.startswith("Affinity:"):
        print(line)

Recipe: Custom Scoring Function

Target-class-tuned empirical scoring; reproduce dkoes terms or a custom-fit set.

import subprocess

with open("my_scoring.txt", "w") as f:
    f.write("""\
-0.035579    gauss(o=0,_w=0.5,_c=8)
-0.005156    gauss(o=3,_w=2,_c=8)
0.840245     repulsion(o=0,_c=8)
-0.035069    hydrophobic(g=0.5,_b=1.5,_c=8)
-0.587439    non_dir_h_bond(g=-0.7,_b=0,_c=8)
1.923        num_tors_div
""")

subprocess.run([
    "smina", "-r", "receptor.pdbqt", "-l", "candidate.sdf",
    "--custom_scoring", "my_scoring.txt",
    "--autobox_ligand", "ref.pdb", "--autobox_add", "8",
    "-o", "custom_docked.sdf", "--exhaustiveness", "16",
], check=True)

Recipe: Per-Atom Score Decomposition

Identify which ligand atoms drive binding.

import subprocess
from rdkit import Chem

subprocess.run([
    "smina", "-r", "receptor.pdbqt", "-l", "pose.sdf",
    "--score_only", "--atom_term_data", "-o", "pose_decomp.sdf",
], check=True)

mol = next(iter(Chem.SDMolSupplier("pose_decomp.sdf", removeHs=False)))
for prop in mol.GetPropNames():
    if "atom_term" in prop:
        print(f"{prop}: {mol.GetProp(prop)[:120]}")

Recipe: Re-Docking Validation

Re-dock the co-crystallized ligand; confirm RMSD < 2.0 Å.

import subprocess
from rdkit import Chem
from rdkit.Chem import AllChem

subprocess.run([
    "smina", "-r", "receptor.pdbqt", "-l", "ref_ligand.sdf",
    "--autobox_ligand", "ref_ligand.sdf", "--autobox_add", "8",
    "-o", "redocked.sdf", "--exhaustiveness", "32",
    "--num_modes", "1", "--seed", "42",
], check=True)

ref = next(iter(Chem.SDMolSupplier("ref_ligand.sdf", removeHs=False)))
docked = next(iter(Chem.SDMolSupplier("redocked.sdf", removeHs=False)))
rmsd = AllChem.GetBestRMS(ref, docked)
print(f"Re-docking RMSD: {rmsd:.2f} Å  ->  {'PASS' if rmsd < 2.0 else 'FAIL'}")

Expected Outputs

  • *_docked.sdf — multi-model SDF, one molecule per pose; affinity in <minimizedAffinity>, RMSD-to-best in <minimizedRMSD>
  • screening_results.csv — name, SMILES, affinity (kcal/mol)
  • *_receptor.pdbqt — prepared receptor
  • stdout — affinity table: pose, affinity (kcal/mol), rmsd lower/upper bound

Troubleshooting

ProblemCauseSolution
smina: command not foundBinary not on PATHconda install -c conda-forge smina; in pixi env use pixi run smina
Parse error on line ... (ligand)Malformed SDF/MOL2Re-export with RDKit SDWriter after AddHs + EmbedMolecule
Could not figure out box dimensionsMissing both --autobox_ligand and --center_x/--size_xSupply autobox reference or explicit grid
Highly positive affinities (>0)Ligand outside grid or steric clash on inputRaise --autobox_add; verify reference pose is inside binding site
--local_only returns input unchanged--minimize_iters 0 = "no minimization" in some buildsSet --minimize_iters 1000 explicitly
Identical poses across runsFixed --seed + low --exhaustivenessRaise to 32+; vary --seed for ensemble
Receptor PDBQT generation failsprepare_receptor not on PATHInstall ADFR Suite, add <adfr>/bin to PATH
Empty SDF after docksmina aborted silentlyDrop capture_output, inspect proc.stderr; check receptor charges
Score differs between --score_only and --local_onlyLocal opt moves the poseExpected — score-only for fixed pose, local-only for refined energy
--custom_scoring file rejectedTerm typo or missing weight columnEach line: <weight><whitespace><term_name(args)> — whitespace strict
Per-atom decomposition emptyUsed with --minimize / dock, not --score_only--atom_term_data most reliable with --score_only

References

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

API + Python SDK for ordering cell-free protein expression and binding assays. Submit sequences for expression (10–100 µg), measure binding affinity (KD) against targets, track status, and retrieve results programmatically — no wet-lab setup. Built for ML-guided directed evolution and antibody/nanobody optimization. Requires Adaptyv account and API key.

The English summary is being prepared. Showing the original description.

jaechang-hits/SciAgent-Skills381Updated Oct 16, 2026

aeon

Free

scikit-learn compatible Python toolkit for time series ML: classify, cluster, regress, segment, transform with 30+ algorithms (ROCKET, InceptionTime, KNN-DTW, HIVE-COTE, WEASEL). Handles panel, multivariate, and unequal-length series. Maintained successor to sktime. Alternatives: sktime (larger ecosystem), tslearn (fewer algorithms), catch22 (features only).

The English summary is being prepared. Showing the original description.

jaechang-hits/SciAgent-Skills381Updated Oct 16, 2026

AiZynthFinder retrosynthetic route planning (CASP) from AstraZeneca Molecular AI. Monte Carlo tree search guided by a template-based neural expansion policy recursively disconnects a target SMILES until precursors are found in a purchasable stock. Covers config.yml (v4 format), aizynthcli batch screening, the AiZynthFinder/AiZynthExpander Python API, one-step disconnections, custom stocks via smiles2stock, scorers, Retro*/breadth-first/DFPN search alternatives, and reading output.json.gz / trees.json. Use for synthesis route planning, synthesizability screening, and building-block/precursor search. For reaction barriers use neb-irc-activation-energy; for 2D reaction scheme drawing use rdkit-chemdraw-cdxml.

The English summary is being prepared. Showing the original description.

jaechang-hits/SciAgent-Skills381Updated Oct 16, 2026

Access AlphaFold DB's 200M+ predicted structures by UniProt ID. Download PDB/mmCIF, analyze pLDDT/PAE, bulk-fetch proteomes via Google Cloud. For experimental structures use PDB; for prediction use ColabFold or ESMFold.

The English summary is being prepared. Showing the original description.

jaechang-hits/SciAgent-Skills381Updated Oct 16, 2026

Annotated matrices for single-cell genomics. Stores X with obs/var metadata, layers, embeddings (obsm/varm), graphs (obsp/varp), uns. Use for .h5ad/.zarr I/O, concatenation, scverse integration. For analysis use scanpy; for probabilistic models use scvi-tools.

The English summary is being prepared. Showing the original description.

jaechang-hits/SciAgent-Skills381Updated Oct 16, 2026

GRN inference from expression via GRNBoost2 (gradient boosting) or GENIE3 (Random Forest). Load matrix, filter by TFs, infer TF-target-importance links, save network. Dask-parallelized to single-cell scale. Core SCENIC component.

The English summary is being prepared. Showing the original description.

jaechang-hits/SciAgent-Skills381Updated Oct 16, 2026

See all skills by jaechang-hits

Report a problem with this skill