生信入门实战 分子对接 · 组学分析 · 跑通为准
总览 / 模块 02

分子对接与虚拟筛选

本页目录 · 共 43 节
  1. 配套代码
  2. 0. 这份笔记怎么用
  3. 1. 原理最小集(先把这几个词搞懂)
  4. 1.1 分子对接在做什么
  5. 1.2 打分函数
  6. 1.3 结合口袋与搜索盒子(grid box)
  7. 1.4 文件格式:PDB / SDF / MOL2 / PDBQT
  8. 1.5 一次完整对接的流程
  9. 2. 资源总表
  10. T1 原理与总览
  11. T2 受体与配体准备
  12. T3 AutoDock Vina 官方教程与自带示例
  13. T4 批量虚拟筛选与化合物库
  14. T5 后处理(MM-GBSA 与分子动力学)
  15. T6 结果可视化
  16. 3. 零基础上手路径(按周推进)
  17. 第 1 周:跑通第一个对接(目标:拿到一个打分)
  18. 第 2 周:学会准备自己的输入(目标:用自己的体系跑通)
  19. 第 3 周:做一次小规模虚拟筛选(目标:一张排序表)
  20. 第 4 周:往后处理走(目标:给候选一个更可信的排名)
  21. 4. 最小可跑案例:把伊马替尼对接回 c-Abl(1IEP)
  22. 4.0 准备数据
  23. 4.1 装环境
  24. 4.2 准备受体
  25. 4.3 准备配体
  26. 4.4 可选:算 AutoDock4 的 affinity maps
  27. 4.5 跑对接
  28. 4.6 预期输出
  29. 4.7 导出结果、可视化
  30. 4.8 用 Python API 跑同一件事
  31. 5. 批量虚拟筛选:从化合物库到排序表
  32. 5.1 官方最小批量用法
  33. 5.2 用本包脚本跑批量筛选
  34. 5.3 配体从哪来
  35. 5.4 怎么判断筛选结果可信
  36. 5.5 规模上来之后的工具
  37. 6. 后处理:MM-GBSA 与分子动力学(简要路径)
  38. 6.1 思路
  39. 6.2 走一遍的步骤
  40. 6.3 注意事项
  41. 7. 结果可视化
  42. 8. 常见坑与自查清单
  43. 附录:资源与核验说明

配套代码

下面这些脚本随本模块一起提供,右边标的是它在真实环境里的验证状态;点「展开代码」可以直接在网页上看全文,点「下载」拿到原文件。

vs_batch.py已实测跑通下载

批量虚拟筛选模板:多进程并行调用 AutoDock Vina,把每个配体的最优打分汇总成一张排序 CSV。

展开代码
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
vs_batch.py —— 批量虚拟筛选模板(AutoDock Vina 命令行 + 多进程 + 结果汇总)

用途
    给一个已经准备好的受体(PDBQT)和一个装满配体(PDBQT)的文件夹,
    用多进程并行跑 Vina,把每个配体的最优打分汇总成一张 CSV。

依赖
    - AutoDock Vina 可执行文件(vina 或 vina.exe),并已加入 PATH
      安装:conda install -c conda-forge vina  或  pip install -U vina
    - 仅需 Python 标准库(subprocess / multiprocessing / csv / argparse / re)

典型用法
    # 1) 先准备受体(含极性氢)与配体(PDBQT)
    python vs_batch.py \
        --receptor 1iep_receptor.pdbqt \
        --ligands  ligands/ \
        --config   1iep_receptor.box.txt \
        --out-dir  poses \
        --csv      results.csv \
        --nproc    8 \
        --exhaustiveness 16 \
        --top-n    5

    # 2) 只想重新汇总已有结果,不重跑对接
    python vs_batch.py --ligands ligands/ --out-dir poses --csv results.csv --summarize-only

config 文件(--config 指向的文本文件)内容示例:
    center_x = 15.190
    center_y = 53.903
    center_z = 16.917
    size_x   = 20.0
    size_y   = 20.0
    size_z   = 20.0

若没有 config 文件,也可以直接用命令行给盒子:
    --center 15.190 53.903 16.917 --size 20 20 20
"""

import argparse
import csv
import os
import re
import shutil
import subprocess
import sys
from collections import Counter
from concurrent.futures import ProcessPoolExecutor, as_completed
from glob import glob

# 匹配输出 PDBQT 里的结果行,例如:
# REMARK VINA RESULT:    -13.2      0.000      0.000
_RESULT_RE = re.compile(
    r"^REMARK\s+VINA RESULT:\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)"
)


def parse_scores(pdbqt_path):
    """读取一个 Vina 输出 PDBQT,返回 [(affinity, rmsd_lb, rmsd_ub), ...](按出现顺序=按打分排序)。"""
    scores = []
    try:
        with open(pdbqt_path, "r", errors="ignore") as fh:
            for line in fh:
                m = _RESULT_RE.match(line.strip())
                if m:
                    scores.append((float(m.group(1)), float(m.group(2)), float(m.group(3))))
    except OSError:
        return []
    return scores


def find_ligands(paths):
    """把 --ligands 传进来的目录 / glob / 文件统一展开成 PDBQT 文件绝对路径列表。"""
    files = []
    for p in paths:
        if os.path.isdir(p):
            files.extend(glob(os.path.join(p, "*.pdbqt")))
        elif any(ch in p for ch in "*?["):
            files.extend(glob(p))
        elif os.path.isfile(p):
            files.append(p)
    # 去重并排序,保证可复现
    files = sorted(set(os.path.abspath(f) for f in files))
    # 排除已经带 _out 的结果文件,避免重复对接
    return [f for f in files if not os.path.basename(f).endswith("_out.pdbqt")]


def assign_stems(ligands):
    """给每个配体分配唯一的输出名(stem),避免不同目录下的同名配体互相覆盖。

    基线是文件名去掉扩展名;同一个 basename 出现多次时,统一追加 __1、__2 … 后缀。
    返回 (stems, dups):
        stems —— {配体绝对路径: 输出名}
        dups  —— 出现冲突的 basename 列表(排序后),供提示用
    """
    bases = [os.path.splitext(os.path.basename(f))[0] for f in ligands]
    total = Counter(bases)
    seen = Counter()
    stems = {}
    for f, b in zip(ligands, bases):
        if total[b] == 1:
            stems[f] = b
        else:
            seen[b] += 1
            stems[f] = "%s__%d" % (b, seen[b])
    dups = sorted(b for b, c in total.items() if c > 1)
    return stems, dups


def build_box_config(box_path, center, size):
    """没有现成 config 时,按 center/size 生成一个临时 config 文件。"""
    with open(box_path, "w") as fh:
        fh.write("center_x = %.3f\n" % center[0])
        fh.write("center_y = %.3f\n" % center[1])
        fh.write("center_z = %.3f\n" % center[2])
        fh.write("size_x = %.1f\n" % size[0])
        fh.write("size_y = %.1f\n" % size[1])
        fh.write("size_z = %.1f\n" % size[2])
    return box_path


def run_one(job):
    """单个配体对接。job 是一个 dict,方便进程池序列化。"""
    ligand = job["ligand"]
    name = job.get("stem") or os.path.splitext(os.path.basename(ligand))[0]
    out_file = os.path.join(job["out_dir"], name + "_out.pdbqt")
    log_file = os.path.join(job["out_dir"], name + ".log")

    cmd = [
        job["vina"],
        "--receptor", job["receptor"],
        "--ligand", ligand,
        "--config", job["config"],
        "--out", out_file,
        "--exhaustiveness", str(job["exhaustiveness"]),
        "--cpu", str(job["cpu_per_job"]),
    ]
    if job["scoring"]:
        cmd += ["--scoring", job["scoring"]]
    if job["seed"] is not None:
        cmd += ["--seed", str(job["seed"])]

    try:
        with open(log_file, "w") as log:
            proc = subprocess.run(
                cmd, stdout=log, stderr=subprocess.STDOUT,
                timeout=job["timeout"], check=False,
            )
    except subprocess.TimeoutExpired:
        return {"ligand": name, "status": "timeout", "out_file": out_file}
    except FileNotFoundError:
        return {"ligand": name, "status": "vina-not-found", "out_file": out_file}

    if proc.returncode != 0 or not os.path.isfile(out_file):
        return {"ligand": name, "status": "failed(rc=%d)" % proc.returncode, "out_file": out_file}

    scores = parse_scores(out_file)
    if not scores:
        return {"ligand": name, "status": "no-result", "out_file": out_file}

    return {
        "ligand": name,
        "status": "ok",
        "out_file": out_file,
        "best_affinity": scores[0][0],
        "best_rmsd_lb": scores[0][1],
        "best_rmsd_ub": scores[0][2],
        "n_poses": len(scores),
    }


def summarize(out_dir, csv_path, top_n):
    """扫描 out_dir 下所有 *_out.pdbqt,汇总最优打分到 CSV。"""
    rows = []
    for out_file in sorted(glob(os.path.join(out_dir, "*_out.pdbqt"))):
        name = os.path.basename(out_file)[:-len("_out.pdbqt")]
        scores = parse_scores(out_file)
        if scores:
            best = scores[0]
            rows.append({
                "ligand": name,
                "best_affinity": best[0],
                "best_rmsd_lb": best[1],
                "best_rmsd_ub": best[2],
                "n_poses": len(scores),
                "out_file": out_file,
            })
    rows.sort(key=lambda r: r["best_affinity"])  # 越负越靠前

    with open(csv_path, "w", newline="") as fh:
        writer = csv.writer(fh)
        writer.writerow(["rank", "ligand", "best_affinity_kcal_mol",
                         "rmsd_lb", "rmsd_ub", "n_poses", "out_file"])
        for i, r in enumerate(rows, 1):
            writer.writerow([i, r["ligand"], r["best_affinity"],
                             r["best_rmsd_lb"], r["best_rmsd_ub"],
                             r["n_poses"], r["out_file"]])
    return rows


def main():
    ap = argparse.ArgumentParser(description="AutoDock Vina 批量虚拟筛选模板")
    ap.add_argument("--receptor", help="受体 PDBQT 文件")
    ap.add_argument("--ligands", nargs="+", required=True,
                    help="配体 PDBQT:目录 / glob / 文件,可给多个")
    ap.add_argument("--config", help="Vina config 文本(含 center/size)")
    ap.add_argument("--center", nargs=3, type=float, metavar=("X", "Y", "Z"),
                    help="盒子中心;与 --size 搭配,替代 --config")
    ap.add_argument("--size", nargs=3, type=float, metavar=("X", "Y", "Z"),
                    help="盒子边长(埃);与 --center 搭配")
    ap.add_argument("--out-dir", default="poses", help="输出目录(默认 poses)")
    ap.add_argument("--csv", default="vs_results.csv", help="汇总 CSV 路径")
    ap.add_argument("--nproc", type=int, default=os.cpu_count() or 1,
                    help="并行进程数(同时跑多少个配体)")
    ap.add_argument("--cpu-per-job", type=int, default=1,
                    help="每个 vina 进程内部使用几个 CPU 核(默认 1)")
    ap.add_argument("--exhaustiveness", type=int, default=16,
                    help="搜索强度;越大越慢越稳(默认 16,正式筛选可用 32)")
    ap.add_argument("--scoring", default=None, choices=["vina", "ad4", "vinardo"],
                    help="打分函数;默认用 vina。用 ad4 时需先算 affinity maps 并改用 --maps")
    ap.add_argument("--seed", type=int, default=None, help="随机种子,固定可复现")
    ap.add_argument("--timeout", type=int, default=3600, help="单个配体超时秒数")
    ap.add_argument("--top-n", type=int, default=0, help="打印前 N 名(0=不打印)")
    ap.add_argument("--summarize-only", action="store_true",
                    help="跳过对接,只重新汇总 out-dir 里已有结果")
    ap.add_argument("--vina", default=None, help="vina 可执行文件路径(默认从 PATH 找)")
    args = ap.parse_args()

    os.makedirs(args.out_dir, exist_ok=True)
    vina_bin = args.vina or shutil.which("vina") or "vina"

    if args.summarize_only:
        rows = summarize(args.out_dir, args.csv, args.top_n)
        print("汇总完成:%d 条结果 -> %s" % (len(rows), args.csv))
        _print_top(rows, args.top_n)
        return

    if not args.receptor:
        ap.error("需要 --receptor(受体 PDBQT)")
    if not args.config:
        if not (args.center and args.size):
            ap.error("需要 --config,或同时给出 --center 与 --size")
        args.config = build_box_config(os.path.join(args.out_dir, "_box.txt"),
                                       args.center, args.size)

    ligands = find_ligands(args.ligands)
    if not ligands:
        print("没有找到任何配体 PDBQT,检查 --ligands 路径。", file=sys.stderr)
        sys.exit(1)

    # 提前校验输入:这些问题逐个配体报一遍既费时间又难定位,直接拦在前面
    if not os.path.isfile(args.receptor):
        ap.error("找不到受体文件:%s" % args.receptor)
    if not os.path.isfile(args.config):
        ap.error("找不到 config 文件:%s" % args.config)
    if not (os.path.isfile(vina_bin) or shutil.which(vina_bin)):
        ap.error("找不到 vina 可执行文件(%s)。请先 conda install -c conda-forge vina,"
                 "或用 --vina 指定完整路径。" % vina_bin)

    # 不同目录下的同名配体输出会互相覆盖,这里统一改名规避
    stems, dups = assign_stems(ligands)
    if dups:
        print("提示:以下配体重名,输出名已自动加 __N 后缀避免覆盖:%s"
              % ", ".join(dups))

    print("vina        : %s" % vina_bin)
    print("受体        : %s" % os.path.abspath(args.receptor))
    print("config      : %s" % os.path.abspath(args.config))
    print("配体数量    : %d" % len(ligands))
    print("并行进程    : %d(每进程 %d 核)" % (args.nproc, args.cpu_per_job))
    print("搜索强度    : %d" % args.exhaustiveness)

    jobs = [{
        "ligand": lig,
        "stem": stems[lig],
        "receptor": os.path.abspath(args.receptor),
        "config": os.path.abspath(args.config),
        "out_dir": os.path.abspath(args.out_dir),
        "vina": vina_bin,
        "exhaustiveness": args.exhaustiveness,
        "cpu_per_job": args.cpu_per_job,
        "scoring": args.scoring,
        "seed": args.seed,
        "timeout": args.timeout,
    } for lig in ligands]

    done = 0
    results = []
    with ProcessPoolExecutor(max_workers=args.nproc) as pool:
        futures = {pool.submit(run_one, j): j for j in jobs}
        for fut in as_completed(futures):
            r = fut.result()
            results.append(r)
            done += 1
            tag = "%s(%s)" % (r["ligand"], r["status"])
            print("[%d/%d] %s" % (done, len(jobs), tag))

    rows = summarize(args.out_dir, args.csv, args.top_n)
    ok = sum(1 for r in results if r["status"] == "ok")
    print("\n对接结束:成功 %d / 共 %d,汇总 -> %s" % (ok, len(jobs), args.csv))
    _print_top(rows, args.top_n)


def _print_top(rows, top_n):
    if top_n and rows:
        print("\n打分最优的前 %d 个配体(kcal/mol,越负越可能结合):" % top_n)
        for i, r in enumerate(rows[:top_n], 1):
            print("  %2d. %-30s %8.2f" % (i, r["ligand"], r["best_affinity"]))


if __name__ == "__main__":
    main()
requirements.txt已按实测环境核对下载

Python 依赖清单(vina / meeko / rdkit / openbabel 等)。

展开代码
text
# ---------------------------------------------------------------
# 环境依赖(批量虚拟筛选)
# 建议用 conda / mamba 建独立环境,避免与系统 Python 冲突
# ---------------------------------------------------------------

# 对接引擎(同时提供命令行 vina 和 Python API)
# conda 更稳;也可 pip install -U vina
vina>=1.2.5

# 分子读写与构象
numpy
scipy
rdkit

# 受体 / 配体 PDBQT 准备(提供 mk_prepare_ligand.py、mk_prepare_receptor.py、mk_export.py)
meeko>=0.6

# 给配体加氢 / 生成 3D 构象 / 枚举质子化与互变异构(提供 scrub.py)
# 注意:scrub.py 内部 import joblib,但 molscrub 没把它写进依赖,必须单独装,
#       否则跑 scrub.py 会报 ModuleNotFoundError: No module named 'joblib'
molscrub
joblib

# 蛋白结构处理(官方 Colab 工作流里用到)
gemmi
prody

# 可视化(可选,用于看结果)
py3Dmol

# 说明:
# - AutoDock-GPU(GPU 加速大规模对接):https://github.com/ccsb-scripps/AutoDock-GPU
# - Ringtail(虚拟筛选结果入库与富集分析):https://github.com/forlilab/ringtail
# - ad4 打分需要 autogrid4 预计算 affinity maps:conda install -c conda-forge autogrid
README.md安装步骤已实测下载

脚本使用说明:装环境、准备输入、跑流程、怎么读结果。

这份笔记是“生信带教包”的第二模块。目标很明确:让零基础的人从装环境开始, 一周能跑出第一个对接结果,一个月能独立完成一次小规模虚拟筛选。 里面每一条资源都是真实存在、能打开的(除了个别被站点反爬拦住、已单独标注的), 不是凑数用的链接,跟着点就行。


0. 这份笔记怎么用

  • 第 1 章把必须懂的概念压到最小,看不懂先跳过,跑完第 4 章再回来读。
  • 第 2 章是资源总表,按主题分好,实验时按需查。
  • 第 3 章是按周推进的路径,照着走不会迷路。
  • 第 4 章是最小可跑案例,照着敲命令,能拿到一个真实结果。
  • 第 5、6、7 章是往上游和下游延伸:批量筛选、后处理、可视化。
  • scripts/ 目录里有一个批量筛选脚本模板,第 5 章讲怎么用。

1. 原理最小集(先把这几个词搞懂)

1.1 分子对接在做什么

一句话:把一个小分子(配体,ligand)塞进蛋白质(受体,receptor)的某个口袋里, 反复调整它的位置和姿态,找让两者结合最稳的那个构象。

对接软件做的其实是两件事:搜索(构象采样)和打分(评价好坏)。 搜索常用蒙特卡洛 / 遗传算法,打分靠打分函数。我们后面要调的所有参数, 本质都在这两件事上做取舍:exhaustiveness 控制搜索多狠,力场选择控制怎么打分。

对接跟另外两个概念别搞混:

  • 分子动力学(MD):让配体和蛋白在模拟的溶剂里都动起来,看一段时间的演化。
  • 基于配体的筛选:不看受体结构,只比化合物的相似性。

前两个是基于结构的(structure-based),也是这个模块的重点。

1.2 打分函数

打分函数给一个蛋白-配体构象打一个分,单位通常是 kcal/mol,数值越负表示预测结合越强。 AutoDock 家族常用的有三套:

力场 特点 是否需要预计算
vina Vina 默认,速度快,是现在虚拟筛选的主力 不需要,Vina 内部自己算格点
ad4 老牌 AutoDock4 力场,更依赖格点 需要先用 autogrid4 算 affinity maps
vinardo 基于 Vina 改的,某些体系更准 不需要

一个必须记住的坑:不同力场打出来的分不能互相比较。用 vina 跑的分和 ad4 跑的分放一起排名是没有意义的。一次筛选从头到尾只用一套力场。

Vina 打分函数大致由几项加和:空间排斥、疏水接触、氢键、高斯项(惩罚偏离理想距离)。 关于它在虚拟筛选里的实际表现和局限,可以读 arXiv:2006.16955(见资源表 T1-4)。

1.3 结合口袋与搜索盒子(grid box)

对接不会让配体在整个蛋白上乱跑,而是限定在一个长方体区域里,叫搜索盒子。 盒子用中心坐标(center_x/y/z)和边长(size_x/y/z)定义,单位是埃(Å)。

盒子怎么定:

  • 有共晶配体(蛋白里本来带着的小分子)时,盒子中心对准它,边长比它大一圈(20 Å 起步)。
  • 没有共晶配体时,用工具找口袋(例如 AutoSite、fpocket),或者把整个蛋白设为搜索空间。
  • 盒子太小会漏掉真实结合位点,太大又不准又慢,这是新手最容易犯的错。

1.4 文件格式:PDB / SDF / MOL2 / PDBQT

对接流程里会反复遇到这几种格式,分清它们能少走很多弯路:

  • PDB:蛋白结构的通用格式,从 Protein Data Bank 下载的就是它。
  • SDF / MOL2:小分子格式,带键的信息,是配体准备的首选输入。
  • SMILES:一串文本表示分子结构,适合批量。
  • PDBQT:AutoDock 家族的专用格式,在 PDB 基础上加了两样东西—— 极性氢部分电荷,还有一个标记原子类型和可旋转键的字段。

关键提醒:不要用 PDB 格式准备小分子。PDB 里没有键的信息,软件只能猜键级, 对复杂分子经常猜错。配体一律从 SDF 或 MOL2 走。

1.5 一次完整对接的流程

text
        PDB 结构                       SDF / SMILES
           │                              │
     去水/去杂/加氢                  加氢/生成3D/定质子化态
           │                              │
    mk_prepare_receptor.py         mk_prepare_ligand.py
           │                              │
     receptor.pdbqt                  ligand.pdbqt
           └──────────┬───────────────────┘
                  vina(+ 盒子 config)
                       │
                  poses(打分排序)
                       │
            导出 SDF → PyMOL 看结合模式
                       │
              (可选)MM-GBSA / MD 复算

2. 资源总表

字段说明:名称 / 链接 / 类型 / 语言 / 难度 / 是否可直接跑通 / 一句话说明。 “可否直接跑通”指跟着做能不能在本机复现结果;纯文档类标“阅读”。

T1 原理与总览

名称 链接 类型 语言 难度 可否直接跑通 说明
AutoDock Vina 官方文档站 https://autodock-vina.readthedocs.io/en/latest/ 文档·教程 入门 阅读 主入口,安装、基础对接、批量对接、Python 脚本全在这
Forli 等 2016, Nature Protocols https://pmc.ncbi.nlm.nih.gov/articles/PMC4868550/ 论文·教程 进阶 阅读 AutoDock 官方授权流程论文,讲透打分、口袋、exhaustiveness 的含义
Vina 软件需求与安装页 https://autodock-vina.readthedocs.io/en/latest/docking_requirements.html 文档 入门 一行命令装齐 Meeko、AutoGrid、ADFR 等依赖
arXiv:2006.16955(SMINA docking benchmark) https://arxiv.org/abs/2006.16955 论文 进阶 阅读 从另一个角度讨论对接打分在分子设计里的可信度,适合进阶读

T2 受体与配体准备

名称 链接 类型 语言 难度 可否直接跑通 说明
Meeko(AutoDock 接口) https://meeko.readthedocs.io/en/develop/ 工具·文档 入门 Vina 官方指定的 PDBQT 准备工具,提供 mk_prepare_ligand / receptor 脚本
Meeko Basic Docking 教程 https://meeko.readthedocs.io/en/develop/tutorial1.html 教程·案例 入门 单个配体准备 + 批量准备 + 结果处理,一页讲完主流程
Molscrub https://github.com/forlilab/molscrub 工具 入门 提供 scrub.py,给分子加氢、生成 3D 构象、枚举质子化与互变异构
RDKit https://rdkit.org/ 工具·文档 入门 开源化学信息学工具包,配体处理、相似性、指纹都靠它
RDKit Cookbook https://rdkit.org/docs/Cookbook.html 教程·代码 进阶 一堆可复制的 RDKit 代码片段,遇到具体问题翻这里
Open Babel https://openbabel.org/ 工具 入门 支持 110 多种化学文件格式互转的老牌工具箱
PDB2PQR https://pdb2pqr.readthedocs.io/en/latest/ 工具·文档 入门 补缺失重原子、估质子化态、分配电荷与半径

T3 AutoDock Vina 官方教程与自带示例

名称 链接 类型 语言 难度 可否直接跑通 说明
Basic docking(1IEP 重对接) https://autodock-vina.readthedocs.io/en/latest/docking_basic.html 教程·案例 入门 本模块第 4 章的蓝本,伊马替尼对接回 c-Abl,含预期打分
Docking in batch mode https://autodock-vina.readthedocs.io/en/latest/docking_in_batch.html 教程 入门 一条命令批量对接一批配体,虚拟筛选的官方最小用法
Multiple ligands docking https://autodock-vina.readthedocs.io/en/latest/docking_multiple_ligands.html 教程·案例 进阶 同时对接两个配体(碎片设计场景),5x72 示例
Python scripting https://autodock-vina.readthedocs.io/en/latest/docking_python.html 教程·代码 进阶 用 vina 的 Python API 写脚本,批量筛选的核心接口
Colab Examples https://autodock-vina.readthedocs.io/en/latest/colab_examples.html 课程·案例 入门 免安装,浏览器里跑完整对接,含免费 GPU,最省事的上手方式
官方示例数据(GitHub) https://github.com/ccsb-scripps/AutoDock-Vina/tree/develop/example/basic_docking 代码·数据 入门 1IEP 例子的输入与预期输出文件都在这里。该链接引自官方文档原文,GitHub 对自动抓取有限制,未能单独打开核验,使用时直接在浏览器打开即可。

T4 批量虚拟筛选与化合物库

名称 链接 类型 语言 难度 可否直接跑通 说明
AutoDock-GPU https://github.com/ccsb-scripps/AutoDock-GPU 工具 进阶 GPU 加速对接,配体上万时的首选,实现 ad4 力场
GNINA https://github.com/gnina/gnina 工具 进阶 基于 Vina 加了 CNN 打分,能自动定盒子,适合大规模筛选
GNINA 文档站 https://gnina.github.io/gnina/ 文档·课程 进阶 阅读 GNINA 用法与 workshop 材料
smina https://sourceforge.net/projects/smina/ 工具 进阶 Vina 的分支,专注打分函数开发和能量最小化,脚本化友好
Dockey https://github.com/lmdu/dockey 工具·论文 进阶 图形界面全流程大规模对接,自动检测相互作用,对新手友好
Ringtail https://github.com/forlilab/ringtail 工具 进阶 把虚拟筛选结果存进 SQLite 并做富集分析,配体上万后的刚需
DUD-E https://dude.docking.org/ 数据集 入门 102 个靶点的活性物 + 诱饵,对接程序基准测试的标准集
DUDE-Z https://dudez.docking.org/ 数据集 进阶 DUD-E 的升级版诱饵集,更贴近真实筛选难度
ZINC20 https://zinc20.docking.org/ 数据集 入门 可直接下载的 ready-to-dock 化合物库,虚拟筛选配体来源
PubChem https://pubchem.ncbi.nlm.nih.gov/ 数据集 入门 全球最大的免费化学信息库,查结构、下 SDF、批量拿 CID 都在这
docking.org https://docking.org 数据集·工具索引 入门 Shoichet / Irwin 实验室资源总入口,ZINC、DUD-E、DOCK 都从这进
阿里云 E-HPC:用 AutoDock Vina 做虚拟筛选 https://help.aliyun.com/zh/e-hpc/e-hpc-1-0/use-cases/use-autodock-vina-to-screen-potential-drugs 教程·案例 进阶 中文实战,讲怎么把 Vina 放到集群上跑作业数组

T5 后处理(MM-GBSA 与分子动力学)

名称 链接 类型 语言 难度 可否直接跑通 说明
gmx_MMPBSA 文档 https://valdes-tresanco-ms.github.io/gmx_MMPBSA/dev/ 工具·文档 进阶 用 GROMACS 轨迹做 MM/PB(GB)SA,开源且更新活跃
gmx_MMPBSA:AMBER 输入示例 https://valdes-tresanco-ms.github.io/gmx_MMPBSA/dev/examples/AMBER/ 案例 进阶 RAS-RAF 结合自由能完整命令,可照抄
gmx_MMPBSA 输入文件说明 https://valdes-tresanco-ms.github.io/gmx_MMPBSA/v1.5.6/input_file/ 文档 进阶 阅读 gb / pb / rism / decomp 各 namelist 怎么写
GROMACS 官网 https://www.gromacs.org/ 工具·文档 入门 开源 MD 主力软件,下载、手册、教程入口
GROMACS 入门教程 https://tutorials.gromacs.org/md-intro-tutorial.html 教程 入门 官方入门课,从零跑一个蛋白在水里的模拟
Justin Lemkul 的 GROMACS 系列教程 https://www.mdtutorials.com/gmx/ 教程 入门 公认最好的 GROMACS 教学系列,Tutorial 3 是蛋白-配体复合物
MM(PB/GB)SA 快速上手指南 https://www.blopig.com/blog/2022/05/mmpb-gbsa-a-quick-start-guide/ 教程 进阶 用纯开源工具从 PDB 走到结合自由能的完整流程
MMPBSA.py 官方手册 http://archive.ambermd.org/201005/att-0941/MMPBSA_Python_Manual.pdf 文档 进阶 阅读 讲清 MM/PB(GB)SA 的原理、拓扑构建和常见报错
g_mmpbsa https://g-mmpbsa.readthedocs.io/en/stable/ 工具·文档 进阶 另一款 GROMACS 上的 MM-PBSA 工具,纯 Python 实现

T6 结果可视化

名称 链接 类型 语言 难度 可否直接跑通 说明
PyMOL 官方文档 https://pymol.org/dokuwiki/ 文档·教程 入门 阅读 命令参考、教程、示例图都在这
PyMOL Wiki https://pymolwiki.org/ 教程·文档 入门 阅读 社区维护的支持站,问题基本都能搜到
Practical PyMOL for Beginners https://pymolwiki.org/index.php/Practical_Pymol_for_Beginners 教程 入门 从界面讲到出图,最友好的 PyMOL 上手文
py3Dmol https://3dmol.org/ 工具·文档 入门 在网页或 Jupyter 里直接展示 3D 结构,官方 Colab 示例用的就是它

3. 零基础上手路径(按周推进)

给一个四周的节奏,每周有明确产出物。动手时间按每天 2 小时估。

第 1 周:跑通第一个对接(目标:拿到一个打分)

做什么 用什么 产出
D1 读第 1 章 + Vina 官方文档安装页,把环境装好 T1-1、T1-3 命令行敲 vina 有输出
D2 读 Basic docking 教程,理解每步在干嘛 T3-1 笔记:受体准备/配体准备/盒子各做什么
D3 跟第 4 章走完 1IEP 案例 T3-1 1iep_ligand_vina_out.pdbqt
D4 装 PyMOL,把受体和结果姿态叠一起看 T6-1、T6-3 一张结合模式截图
D5 换个盒子大小重跑,看打分怎么变 T3-1 一段结论:盒子大小对结果的影响
D6–7 读 Forli 2016 论文的前半部分 T1-2 笔记:打分函数在筛选中能信到什么程度

第 2 周:学会准备自己的输入(目标:用自己的体系跑通)

做什么 用什么 产出
D1 读 Meeko Basic Docking,练配体准备 T2-2 一批 SDF → PDBQT
D2 练受体准备,处理去水、去配体、补氢 T2-1、T2-7 自己的 receptor.pdbqt
D3 学 RDKit 处理分子、看 SMILES T2-4、T2-5 一段能跑的 RDKit 小脚本
D4 用 scrub.py 给 SMILES 生成构象并枚举质子化态 T2-3 理解为什么质子化态不能凑合
D5 找一个自己感兴趣或本课题的靶点,定盒子 T1-1 一份 config 文件
D6–7 完成一次单配体对接并复盘 前面全部 一次完整流程记录

第 3 周:做一次小规模虚拟筛选(目标:一张排序表)

做什么 用什么 产出
D1 读 batch mode 教程 + Python scripting T3-2、T3-4 明白批量模式的输入输出
D2 从 ZINC 或 PubChem 下一小批配体(先 50–100 个练手) T4-9、T4-10 ligands/ 一堆 PDBQT
D3 scripts/vs_batch.py,拿到 results.csv 本包脚本 results.csv
D4 读 DUD-E 的构造,理解诱饵和富集因子 T4-7 笔记:怎么判断筛选靠不靠谱
D5 挑前 10 个候选,用 PyMOL 逐个看结合模式 T6-1 一张候选清单
D6–7 用 Ringtail 或自己写脚本统计打分分布 T4-6 打分分布图

第 4 周:往后处理走(目标:给候选一个更可信的排名)

做什么 用什么 产出
D1 读 MM(PB/GB)SA 快速上手 T5-7 明白后处理在算什么
D2 读 GROMACS 入门教程,跑一个小体系 T5-5、T5-6 一次 MD 跑通
D3–4 对手上最好的几个候选建拓扑、跑短 MD T5-1、T5-4 拓扑与轨迹文件
D5 用 gmx_MMPBSA 算结合自由能 T5-1、T5-2 FINAL_RESULTS_MMPBSA.dat
D6 对比对接打分和 MM-GBSA 的排序是否一致 一段分析
D7 整体复盘,把流程写成自己的 SOP 一份可供师姐复用的流程文档

4. 最小可跑案例:把伊马替尼对接回 c-Abl(1IEP)

这个案例来自 AutoDock Vina 官方教程(T3-1)。用抗癌药伊马替尼(imatinib)重对接回 它的靶点 c-Abl 激酶域,看能不能把配体放回晶体结构里原来的位置(这叫重对接/redocking, 是检验对接流程有没有搭对的黄金标准)。

4.0 准备数据

  • 受体坐标:1iep_receptorH.pdb(已加氢,去掉了原配体)
  • 配体坐标:1iep_ligand.sdf
  • 两个文件都在 AutoDock-Vina 官方仓库的 example/basic_docking 目录里(见 T3-6)。

自检习惯:每次做对接都应该设一个阳性对照——对接一个已知能结合的分子, 跑通、打分合理,再做未知分子。否则算出来的东西没法判断真假。

4.1 装环境

bash
# 方式一:pip(最快)
pip install -U numpy scipy rdkit vina meeko gemmi prody

# 方式二:conda(更稳,推荐)
conda create -n vina python=3.10 -y
conda activate vina
conda install -c conda-forge numpy scipy rdkit vina meeko gemmi autogrid -y
pip install prody

装好后这几个命令应该都能用:vinamk_prepare_receptor.pymk_prepare_ligand.pymk_export.py

4.2 准备受体

bash
mk_prepare_receptor.py -i 1iep_receptorH.pdb -o 1iep_receptor -p -v \
    --box_size 20 20 20 --box_center 15.190 53.903 16.917

参数含义:-p 生成受体 PDBQT;-v 连同盒子信息生成 config 与盒子 PDB; --box_size / --box_center 就是搜索盒子。跑完会得到:

text
1iep_receptor.pdbqt      受体(含极性氢和部分电荷)
1iep_receptor.box.txt    盒子配置,可直接当 config 用
1iep_receptor.box.pdb    盒子可视化文件,拖进 PyMOL 能看

4.3 准备配体

bash
mk_prepare_ligand.py -i 1iep_ligand.sdf -o 1iep_ligand.pdbqt

4.4 可选:算 AutoDock4 的 affinity maps

只有想用 ad4 力场时才需要。在 4.2 的命令里加 -g 生成 GPF,再跑 autogrid4:

bash
mk_prepare_receptor.py -i 1iep_receptorH.pdb -o 1iep_receptor -p -v -g \
    --box_size 20 20 20 --box_center 15.190 53.903 16.917
autogrid4 -p 1iep_receptor.gpf -l 1iep_receptor.glg

会生成 1iep_receptor.*.map(各原子类型)、.d.map(去溶剂化)、.e.map(静电)。

4.5 跑对接

用 Vina 力场(推荐先跑这个)1iep_receptor.box.txt 内容如下:

text
center_x = 15.190
center_y = 53.903
center_z = 16.917
size_x = 20.0
size_y = 20.0
size_z = 20.0
bash
vina --receptor 1iep_receptor.pdbqt --ligand 1iep_ligand.pdbqt \
     --config 1iep_receptor.box.txt --exhaustiveness=32 \
     --out 1iep_ligand_vina_out.pdbqt

用 AutoDock4 力场(需要 4.4 的 maps):

bash
vina --ligand 1iep_ligand.pdbqt --maps 1iep_receptor --scoring ad4 \
     --exhaustiveness 32 --out 1iep_ligand_ad4_out.pdbqt

关于 exhaustiveness:默认值只有 8,官方明确说这个体系用默认参数有时找不到正确姿态, 建议提到 32。这个值越大搜索越充分,也越慢。

4.6 预期输出

Vina 力场下,最佳打分大约 −13 kcal/mol

text
Scoring function : vina
Rigid receptor: 1iep_receptor.pdbqt
Ligand: 1iep_ligand.pdbqt
Grid center: X 15.19 Y 53.903 Z 16.917
Grid size  : X 20 Y 20 Z 20
Grid space : 0.375
Exhaustiveness: 32
CPU: 0

AutoDock4 力场下,最佳打分大约 −14 kcal/mol(两套力场的分不能互相比)。

看到接近这个数量级、且最优姿态能叠回晶体结构原配体的位置,就说明流程搭对了。

4.7 导出结果、可视化

bash
mk_export.py 1iep_ligand_vina_out.pdbqt -s 1iep_ligand_vina_out.sdf

为什么用 Meeko 的 mk_export.py 而不是别家工具:PDBQT 里没存完整的键级信息, 从 PDBQT 反推键级对复杂分子经常出错。Meeko 在写 PDBQT 时把 SMILES 塞进了文件头, 导出时能还原成正确的键级和电荷。

可视化:PyMOL 里先载入 1iep_receptor.pdbqt,再载入导出的 SDF,就能看到配体 落在口袋里的姿态,以及周围残基。

4.8 用 Python API 跑同一件事

python
from vina import Vina

v = Vina(sf_name='vina')
v.set_receptor('1iep_receptor.pdbqt')
v.set_ligand_from_file('1iep_ligand.pdbqt')
v.compute_vina_maps(center=[15.190, 53.903, 16.917], box_size=[20, 20, 20])

energy = v.score()
print('打分(优化前):%.3f kcal/mol' % energy[0])
energy_min = v.optimize()
print('打分(局部优化后):%.3f kcal/mol' % energy_min[0])

v.dock(exhaustiveness=32, n_poses=20)
v.write_poses('1iep_ligand_vina_out.pdbqt', n_poses=5, overwrite=True)

这段脚本在官方教材里也有,对应仓库的 example/python_scripting


5. 批量虚拟筛选:从化合物库到排序表

单配体对接是学流程,真正做事要面对成百上千个分子。这一步的核心是并行—— 不同配体之间互不依赖,天然适合多核或 GPU 并行。

5.1 官方最小批量用法

Vina 自带批量模式:

bash
vina --receptor 1iep_receptor.pdbqt --batch ligands/*.pdbqt \
     --config config.txt --dir poses

输出会写到 poses/ 下,命名为 <配体名>_out.pdbqt,重名时自动加索引。 分子上千时,更推荐用 Python 脚本管理并行(下一节),或者干脆换 AutoDock-GPU。

5.2 用本包脚本跑批量筛选

scripts/vs_batch.py 是一个可直接复用的模板,用多进程并行调用 Vina 命令行, 把最优打分会总成 CSV。完整用法见 scripts/README.md,最短示例:

bash
python scripts/vs_batch.py \
    --receptor 1iep_receptor.pdbqt \
    --ligands  ligands/ \
    --config   1iep_receptor.box.txt \
    --out-dir  poses \
    --csv      results.csv \
    --nproc    8 \
    --exhaustiveness 16 \
    --seed 42 \
    --top-n 20

跑完得到 results.csv,按最优打分升序排列,字段为 rank, ligand, best_affinity_kcal_mol, rmsd_lb, rmsd_ub, n_poses, out_file

它做了几件实用的事:自动展开配体目录、跳过已完成的 *_out.pdbqt、 单个配体可设超时、固定随机种子保证可复现、--summarize-only 支持断点续跑汇总。

5.3 配体从哪来

  • ZINC20(T4-9):直接下 ready-to-dock 的库,选 lead-like / fragment-like 子集。
  • PubChem(T4-10):按靶点或性质查到一批化合物,拿 CID 批量下 SDF。
  • DUD-E(T4-7):想验证方法靠不靠谱,用它自带的活性物 + 诱饵做基准。

拿到 SDF 后统一走 scrub.py 加氢生成构象,再 mk_prepare_ligand.py 批量转 PDBQT(命令见 scripts/README.md)。

5.4 怎么判断筛选结果可信

打分排名只是第一步,别直接拿 best_affinity 当结论。三个动作能让结果靠谱很多:

  1. 看关键相互作用:目标口袋里的关键残基有没有形成氢键或疏水接触。 打分好但一个关键接触都没打上,多半是假阳性。
  2. 看富集:有活性/诱饵标签的数据集(如 DUD-E)可以算富集因子, 检验打分能不能把活性物排到前面。
  3. 后处理复算:挑前几十个候选做 MM-GBSA 或短 MD(第 6 章), 让排名稳一稳。

5.5 规模上来之后的工具

  • 配体上万、有 GPU:换 AutoDock-GPU(T4-1)或 GNINA(T4-2)。
  • 结果太多要管理:用 Ringtail(T4-6)入库并算富集。
  • 想少写代码:用 Dockey(T4-5)的图形界面。
  • 上集群:参考阿里云那篇中文实战(T4-12),把每个配体拆成作业数组。

6. 后处理:MM-GBSA 与分子动力学(简要路径)

对接打分是快但粗的估计。想给候选一个更可信的结合自由能,常见做法是: 对接 → 分子动力学(MD)→ MM-GBSA 复算

6.1 思路

  • MD:把对接出来的复合物放进水盒子里,跑几百纳秒,看结合是否稳定 (配体有没有跑掉、有没有变形)。
  • MM-GBSA / MM-PBSA:从 MD 轨迹里抽一批快照,算复合物、受体、配体三者的 能量差,用隐式溶剂近似溶剂效应,得到 ΔG_bind 的估计。

6.2 走一遍的步骤

  1. 建拓扑:用 tleap(Amber)或 GROMACS 的 pdb2gmx 给复合物建力场拓扑, 注意受体、配体、复合物的拓扑必须同源同力场、同 PBRadii 设置。
  2. 跑 MD:最小化 → 加热 → 平衡 → 生产。GROMACS 的入门教程(T5-5、T5-6)可以照着做。
  3. 抽帧去水:把轨迹里的水去掉,只留复合物。
  4. 算能量:用 gmx_MMPBSA(T5-1),它能在 GROMACS 轨迹上跑 MM/PB(GB)SA, 输入文件怎么写在 T5-3,示例命令在 T5-2。

一条典型的 gmx_MMPBSA 命令结构(来自官方 AMBER 示例):

bash
gmx_MMPBSA -O \
  -i mmpbsa.in \
  -cs complex.tpr -ct traj.xtc \
  -ci index.ndx \
  -cg 1 13 \
  -o FINAL_RESULTS_MMPBSA.dat \
  -eo FINAL_RESULTS_MMPBSA.csv

6.3 注意事项

  • MM-GBSA 对同一批配体做相对排序比较可靠,绝对值误差大,别当 Kd 用。
  • 拓扑别搞错:受体、配体、复合物三个拓扑必须一致,否则能量差全是噪声。
  • 熵项(−TS)计算代价高,很多流程直接省略,报告结果时要说清楚。
  • 嫌 gmx_MMPBSA 重,可以用纯 Python 的 g_mmpbsa(T5-9)。

7. 结果可视化

对接做完,最终是要看图讲故事的。常用工具:

  • PyMOL(T6-1):主流选择。标准动作是——载入受体显示 cartoon, 载入配体显示 sticks,指着口袋里的关键残基标出来,调好视角出图。 PyMOL Wiki 的 Practical PyMOL(T6-3)够新手用。
  • py3Dmol(T6-4):想在网页或 Jupyter 里交互看结构就用它, 官方 Colab 示例用的就是它。
  • VMD:MD 轨迹分析更强,做后处理动画时用得上。

出图建议:受体用浅灰 cartoon(低调),配体用 sticks + 明显的元素配色, 关键残基单独高亮并标名字,最后叠上对接盒子的 PDB(1iep_receptor.box.pdb) 能直观说明搜索范围。


8. 常见坑与自查清单

跑之前对一遍,能省很多调错时间。

  1. 配体用了 PDB 格式 → 键级会被猜错。配体一定要从 SDF / MOL2 走。
  2. 受体没加氢 → PDBQT 需要全氢坐标,先补氢(REDUCE、PDB2PQR 都行)。
  3. 盒子定小了或偏了 → 配体被塞在不该在的位置。有共晶配体就对准它。
  4. exhaustiveness 太低 → 结果不稳定,同一配体跑两次分差很大。正式筛选用 32。
  5. 混用两套力场比较打分 → vina 和 ad4 的分不能放一起排名。
  6. 质子化态没管 → 一个氢的位置能把对接结果带偏,用 scrub.py 枚举再定。
  7. 只看打分不看相互作用 → 假阳性高。关键残基接触是第二条过滤线。
  8. 忘记阳性对照 → 没有已知结合分子做参照,结果好坏无从判断。
  9. 并行时把内存跑爆--nproc 别超过核数,--cpu-per-job 适当降。
  10. 结果不可复现 → 固定 --seed,记录版本号(vina、meeko、力场)。

附录:资源与核验说明

  • 资源条目:共 42 条,分布在 6 个主题(原理 4、受体配体准备 7、 Vina 教程 6、批量筛选与库 12、后处理 9、可视化 4,其中 1 条中文实战归入批量筛选)。
  • 核验方式(如实标注):42 条均经联网检索核验,其中约 30 条用 web_fetch 直接打开网页正文、确认存在且内容相关(AutoDock Vina、Meeko、GROMACS、 gmx_MMPBSA、PyMOL、DUD-E、smina 等为逐页精读);其余条目为官方文档站原文 直接给出的仓库页或数据页,或经检索返回完整内容的页面交叉确认,链接均指向 真实项目。
  • 需在浏览器手动打开的一条:T3-6(GitHub 官方示例目录),因 GitHub 对自动 抓取有限制,未能单独打开正文;该链接引自 AutoDock Vina 官方文档原文,已在表中标注。
  • 因无法可靠核验而舍弃的条目4 条,分别是——
  • RCSB PDB 首页(rcsb.org):被站点安全策略拦截,未能打开,故不单列。
  • Amber 官方 MM-PBSA 教程页(ambermd.org/.../tutorial3):robots.txt 禁止自动访问,舍弃。
  • smina 的 GitHub 仓库页:robots.txt 禁止,改用 SourceForge 官方页(T4-7 已核验)。
  • molscrub 的 PyPI 页:被反爬拦截,改用 GitHub 仓库页(T2-3 已核验)。
  • GitHub 说明:本模块涉及的多个仓库(AutoDock-Vina、Meeko、RDKit 等)首页都会被 GitHub 的 robots.txt 拒绝自动抓取,因此这些资源是通过其官方文档站 (readthedocs / 官网)完成的存在性与内容核验,链接指向的仓库均为真实项目。