mcpField
S
smina-molecular-docking

smina-molecular-docking

github.com/jaechang-hitsその他38453 回インストール1 週間前に更新
リポジトリ

smina-molecular-docking について

smina Molecular Dockingは、sminaで小分子のドッキングやポーズ解析を行う研究者向けのコマンドラインスキルです。SDFやMOL2を直接読み込み、柔軟なスコアリングと全体探索なしの局所最適化に対応します。

主な機能

  1. 01SDF、MOL2、PDBリガンドの入力
  2. 026種類の組み込み・カスタムスコアリング
  3. 03参照リガンドからのグリッド自動設定
  4. 04ポーズのスコアリング、最小化、局所最適化
  5. 05原子ごとのスコア寄与の出力
  6. 06並列バッチスクリーニング

活用シーン

  1. 01既存リガンドポーズの再スコアリング
  2. 02共結晶リガンドを基準にしたドッキング
  3. 03カスタムスコアリング関数の比較
  4. 04計算ノードを使ったリガンドライブラリのスクリーニング

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill smina-molecular-docking

インストール後、Claude Code で/smina-molecular-dockingを入力して読み込まれたか確認します。

詳細

smina 分子ドッキング

概要

smina は、柔軟なスコアリングと最小化に重点を置いた AutoDock Vina 1.1.2 のフォークです。SDF/MOL2/PDB リガンドを直接受け付けるため、手動で PDBQT に変換する必要はありません。参照リガンドからオートボックスを設定でき、6 種類の組み込みスコアリング関数と任意の --custom_scoring 項目が用意されており、原子ごとのスコア寄与を出力します。CLI 専用のため、Python からは subprocess 経由で実行します。

使用する場面

  • 完全な探索を行わず、既存のポーズを再スコアリングまたは局所最小化する場合(--local_only、--minimize)
  • ドッキングせずに単一ポーズの結合エネルギーを求める場合(--score_only)
  • 標的クラスに合わせて調整したカスタムまたは経験的スコアリング関数でドッキングする場合
  • リガンドごとに PDBQT へ変換せず、SDF/MOL2/複数リガンドを入力する場合
  • 共結晶化した参照リガンドを囲むようにグリッドを自動設定する場合
  • メディシナルケミストリーの類縁体設計に向けて、原子ごとのエネルギーを分解する場合(--atom_term_data)
  • ノード間で効率よく並列化できるバッチ仮想スクリーニングを行う場合(リガンドごとに CLI プロセスを 1 つ実行)
  • Vina の Python バインディング、Vinardo スコアリング、または Vina 1.2 の拡張フォースフィールドが必要な場合は、代わりに autodock-vina-docking を使用してください。結合部位が不明な場合は diffdock を使用してください。

前提条件

  • smina バイナリ — conda-forge または SourceForge のビルド
  • Python:rdkit、openbabel-wheel(またはシステムの openbabel)、prody、pandas、py3Dmol
  • ADFR Suite:prepare_receptor 用(受容体の PDBQT のみ。リガンドは smina が処理します)
  • データ:タンパク質(PDB/PDB ID)、SMILES/SDF/MOL2 形式のリガンド

インストール前に確認してください。pixi/conda 環境では、smina がすでに PATH に含まれていることがよくあります。command -v smina が成功する場合は、インストールを省略してください。pixi プロジェクト内では 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/

クイックスタート

参照リガンドからオートボックスを設定して、ドッキングを最初から最後まで実行します。

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

ワークフロー

ステップ 1:受容体を準備する(PDBQT)

水分子とヘテロ原子を除去してから、ADFR Suite の 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)")
ステップ 2:リガンドを準備する(SDF)

smina は SDF を直接読み取ります。RDKit で 3D 座標を生成します。

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)")
ステップ 3:オートボックス用の参照リガンドを抽出する

--autobox_ligand を使うと、参照構造からグリッドが設定されます。

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)}")
ステップ 4:オートボックスでドッキングを実行する

親和性の表は標準出力に表示されるため、取得してください。

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)
ステップ 5:ポーズと親和性を解析する

親和性は SDF の <minimizedAffinity> プロパティに格納されます。

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")
ステップ 6:既存のポーズを局所最小化する

--local_only を指定すると、グローバル探索を行わずに入力ポーズを精密化します。

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")
ステップ 7:ドッキング複合体を可視化する
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()
ステップ 8:バッチ仮想スクリーニング

リガンドごとに smina プロセスを 1 つ実行すると、コア間またはクラスターノード間で効率よく並列化できます。

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))

主なパラメーター

パラメーターデフォルト範囲/オプション効果
--exhaustiveness81-128MC 探索の試行量。実運用では 16-32、論文発表向けには 64 以上
--num_modes91-20出力 SDF に書き込むポーズ数
--energy_range31-5 kcal/mol出力する最良ポーズからの最大 ΔE
--scoringdefaultdefault、vina、vinardo、dkoes_scoring、dkoes_scoring_old、ad4_scoring組み込みスコアリング(default は約 Vina 相当)
--custom_scoring—pathユーザー定義の重み付き項目。--scoring を上書きします
--autobox_ligand—PDB/SDF path参照構造からグリッドを設定
--autobox_add42-12 Åオートボックス範囲の周囲に追加するパディング
--center_x/y/z、--size_x/y/z—Å手動で設定するグリッド(オートボックスの代替)
--local_onlyoffflagグローバル探索をスキップし、局所最適化のみ実行
--score_onlyoffflag最小化や探索を行わずにエネルギーを算出
--minimizeoffflagスコアリング探索を行わずにエネルギーを最小化
--minimize_iters0 (auto)0-100000局所最小化の反復回数
--atom_term_dataoffflag出力に原子ごとのスコア寄与を含める
--cpuall1-Nジョブごとのスレッド数
--seedrandomint再現性を確保するための RNG シード
--no_ligoffflagベースラインとして受容体のみをスコアリング

よく使うレシピ

レシピ:スコアのみ(単一点エネルギー)

再ドッキングせずに、スコアリング関数間でポーズのエネルギーを比較します。

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)
レシピ:カスタムスコアリング関数

ターゲットクラスに合わせて調整した経験的スコアリングです。dkoes の項、またはカスタムフィットした項のセットを再現します。

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)
レシピ:原子ごとのスコア分解

どのリガンド原子が結合に寄与しているかを特定します。

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]}")
レシピ:再ドッキングによる検証

共結晶化したリガンドを再ドッキングし、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'}")

期待される出力

  • *_docked.sdf — マルチモデル SDF。ポーズごとに1分子を格納し、結合親和性は <minimizedAffinity>、最良ポーズに対する RMSD は <minimizedRMSD> に格納
  • screening_results.csv — 名前、SMILES、結合親和性(kcal/mol)
  • *_receptor.pdbqt — 調製済み受容体
  • stdout — 結合親和性テーブル:ポーズ、結合親和性(kcal/mol)、RMSD の下限/上限

トラブルシューティング

問題原因解決策
smina: command not foundバイナリが PATH にないconda install -c conda-forge smina を実行します。pixi 環境では pixi run smina を使用します
Parse error on line ...(リガンド)SDF/MOL2 の形式が不正AddHs と EmbedMolecule の実行後、RDKit の SDWriter で再エクスポートします
Could not figure out box dimensions--autobox_ligand と --center_x/--size_x の両方が指定されていないオートボックスの参照または明示的なグリッドを指定します
結合親和性が非常に正の値(>0)リガンドがグリッドの外にある、または入力に立体障害がある--autobox_add の値を上げ、参照ポーズが結合部位内にあることを確認します
--local_only が入力を変更せずに返す一部のビルドでは --minimize_iters 0 は「最小化しない」という意味です--minimize_iters 1000 を明示的に設定します
実行ごとに同一のポーズになる--seed が固定されており、--exhaustiveness の値が低い32 以上に上げ、アンサンブルには --seed を変えます
受容体の PDBQT 生成に失敗するprepare_receptor が PATH にないADFR Suite をインストールし、<adfr>/bin を PATH に追加します
ドッキング後の SDF が空smina が警告なく中断したcapture_output を削除して proc.stderr を確認し、受容体の電荷をチェックします
--score_only と --local_only でスコアが異なるローカル最適化でポーズが移動する想定どおりです。固定ポーズには score-only、精密化したエネルギーには local-only を使用します
--custom_scoring ファイルが拒否される項の名前の誤り、または重みの列がない各行は <weight><whitespace><term_name(args)> の形式です。空白の形式は厳密です
原子ごとの分解結果が空--score_only ではなく --minimize/ドッキングと併用した--atom_term_data は --score_only との併用が最も確実です

参考資料

コメント