简介:围绕 Autodock Vina 批量分子对接与 Slurm 集群调度整理的实操文档,面向具备 Linux 基础、正在开展药物虚拟筛选或生物计算实验的高校学生与科研人员。内容以 Ubuntu 18.04 环境为线索,先说明 Open Babel、Autodock Vina 与 MGLTools 的依赖安装,再给出 prepare_receptor4.py、prepare_ligand4.py 预处理受体配体的做法,以及通过 conf.txt 控制 CPU 数量与搜索空间、用 obabel 转换格式并把 pdbqt 结果汇总为 sdf 的流程,同时记录了 vina.sh 中 PATH 环境变量的写法。随后延伸到 Slurm 的 munge 认证、slurm.conf 拓扑与资源分配、slurmctld 与 slurmd 服务启动,最终落到在调度系统上循环提交小分子库对接任务。资源包仅含 1 个 PDF 文件,约 1013KB,按步骤编排,便于对照服务器环境复现。已有 5264 人学习,可作为搭建批量对接流水线与排查依赖缺失、环境变量、作业提交问题的参考。
1. 跑完 500 个配体,单机串行要十几个小时,拆到三台机器上只要两小时——前提是先把几个基础环境问题解决清楚
真正让人卡住的往往不是分子对接本身,而是装完 MGLTools 后prepare_ligand4.py一跑就报错、sbatch提交完作业秒退、三台机器同时往同一个output.sdf里追加导致文件内容错乱。这份资料就是一个完整的实施记录:在 Ubuntu 18.04 上装齐 Open Babel、Autodock Vina、MGLTools,写一个vina.sh串行批处理,再叠加 Slurm,从单机sbatch一路扩到三节点分片并行分子对接。它适合手里有几台闲置服务器、正在做小分子虚拟筛选、又不想上重型平台的人,照着敲能把「一条命令跑一批小分子」的流程完整复现出来。
2. 环境准备:Ubuntu 18.04 上把 Open Babel、Vina、MGLTools 装进同一条 PATH
这一章解决的问题是「三个工具各自能跑,凑在一起就不能跑」。分子对接的链路很长:SDF 转 MOL2 靠 Open Babel,受体和配体转 PDBQT 靠 MGLTools 里的两个 Python 脚本,打分靠 Vina 二进制。三者版本之间没有严格的编译依赖,但路径依赖非常强,装错位置、PATH 少一层,后面所有脚本都会以各种看似不相关的方式报错。
2.1 依赖清单与目录约定
先看系统依赖。这些包装了,主要是为了 MGLTools 的 Python 扩展能编译通过、AutoDockTools 的 GUI 模块能被正常 import,以及csh这类老脚本的运行时。
sudo apt-get update sudo apt-get install -y build-essential libxt-dev libglu1-mesa-dev \ libxmu-dev libxi-dev libpng-dev libjpeg-dev libtiff-dev \ csh python-tk python-numpy python-dev python-pmw逐个说清用途,省得你删错包:
| 包名 | 用途 |
|---|---|
build-essential | install.py编译扩展时需要 gcc/make |
libxt-devlibxmu-devlibxi-dev | MGLTools 里 X11 相关模块的链接依赖 |
libglu1-mesa-devlibpng-devlibjpeg-devlibtiff-dev | 分子可视化的图形与图像库 |
csh | AutoDockTools 部分包装脚本的 shebang 是 csh |
python-tkpython-numpypython-devpython-pmw | MGLTools 的 Python 2 运行时与 PMV 模块 |
提示:
python-dev、python-numpy、python-tk这几个在 18.04 上指向 Python 2.7,在 20.04 之后的发行版里已经改名成python2-dev、python2-numpy,直接照抄会提示找不到包。这一点值得先确认,否则后面install.py一定会失败。
然后是目录约定。所有组件都放在同一个父目录下,后面写 PATH 时才能一次写完:
/home/autodock/ ├── autodock_vina_1_1_2_linux_x86/ # Vina 二进制 ├── MGLTools-1.5.6/ # MGLTools 安装目录 │ ├── bin/ # pythonsh、pmv 等 │ └── MGLToolsPckgs/ │ └── AutoDockTools/Utilities24/ # prepare_receptor4.py 等 ├── pro.pdb # 受体结构 ├── testset.sdf # 配体集合 ├── conf.txt # Vina 配置 └── vina.sh # 批处理脚本sudo mkdir -p /home/autodock && sudo chown -R $USER:$USER /home/autodock cd /home/autodock tar -xvf autodock_vina_1_1_2_linux_x86.tgz tar -xvf mgltools_x86_64Linux2_1.5.6.tar_.gz sudo apt-get install -y openbabelVina 是预编译的 tgz 包,解压后bin/下直接就是可执行文件,没有configure && make这一步,这也是它在教学和小规模筛选场景里流行起来的原因。
2.2 MGLTools 的 Python 2 依赖与 install.py
MGLTools 的安装脚本是个老式交互程序,问一句装到哪,回车确认即可。它本身是用 Python 2 写的,这一点决定了整个链路的版本基线。
cd /home/autodock/MGLTools-1.5.6 python2 install.py # 提示 Install directory 时直接回车,装到当前目录install.py干的事情不只是拷贝文件,它还会把MGLToolsPckgs下的 Python 源码做一次字节编译,并生成bin/pythonsh这个包装解释器。装完之后目录里应该能看到bin/和MGLToolsPckgs/两个子目录,缺一个都说明安装过程没走完。
注意:如果系统里
python已经指向 Python 3,install.py会在语法层面直接崩掉。稳妥的做法是明确写python2 install.py,不要图省事用python。
装完后可以顺手验证一下 Open Babel:
obabel -V能打印出版本号就行。它的角色是全流程的格式转换工具,SDF → MOL2、PDBQT → SDF 这两步都靠它,不需要额外配置。
2.3 PATH 拼装:三个来源目录缺一不可
这是整套方案里最容易漏的地方。Vina、MGLTools 主目录、Utilities24 是三个独立目录,必须同时进 PATH,少任何一个都会报command not found。
export PATH=/home/autodock/autodock_vina_1_1_2_linux_x86/bin:$PATH export PATH=/home/autodock/mgltools_x86_64Linux2_1.5.6/bin:$PATH export PATH=/home/autodock/mgltools_x86_64Linux2_1.5.6/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH- 第一行提供
vina这个可执行文件; - 第二行提供
pythonsh,是 MGLTools 内部脚本指定使用的 Python 解释器; - 第三行提供
prepare_receptor4.py和prepare_ligand4.py,这两个脚本本身不带可执行权限的话,还得chmod +x一下。
验证方式是逐条which,不要只看vina:
which vina prepare_receptor4.py prepare_ligand4.py三条路径都能打出来,环境算搭完。这里有个血泪经验:这三个 export 要写进作业脚本里,不能只写进~/.bashrc。Slurm 的sbatch用的是非登录 shell,~/.bashrc里在非交互判断之后的部分根本不会执行,作业里的 PATH 是干净的。这个问题在第 5 章还会再提一次。
3. 串行批处理拆解:vina.sh一个 for 循环里藏着的四步
单机版脚本只有二十来行,但它把整条流水线的形状定死了:受体处理一次、配体批量拆分、逐个对接、结果追加合并。后面上 Slurm 只是把这个循环拆开分给不同节点,核心逻辑一行没变,所以这一章值得逐段拆清楚。
3.1 受体预处理为什么只做一次
受体 PDBQT 只跟受体结构本身有关,跟你跑多少个配体完全无关。所以它应该在循环外做,也只做一次:
prepare_receptor4.py -r pro.pdb -e -o receptor.pdbqt-r指定输入受体结构,-o指定输出的 PDBQT;-e是让脚本遇到问题时直接打错误信息并退出,不同版本这个开关的行为略有差异,拿不准的时候敲prepare_receptor4.py -h看当前安装的说明,比照抄别人的命令稳;- 输出文件名固定叫
receptor.pdbqt,后面conf.txt会按这个名字引用。
这点在并行场景下非常关键:三台机器只需要共享同一份receptor.pdbqt,不用各自再算一遍。所以它应该放在 NFS 挂载的共享目录里,而不是每台机器本地各放一份——版本一旦不一致,打分结果就没法横向比较了。
3.2 多分子 SDF 拆分:obabel -m的命名规则
一个testset.sdf里通常摞着几百个分子,Vina 一次只吃一个配体,所以得先拆开:
# 先把多分子 SDF 转成多分子 MOL2,保留键级与电荷信息 obabel -isd testset.sdf -omol2 -O testset.mol2 mkdir -p mols && cd mols # -m 按分子逐个切分,生成 vs1.mol2、vs2.mol2 ... obabel ../testset.mol2 -O vs.mol2 -m这里有个常见的参数误用:-O(大写)是输出文件名,-o(小写)是输出格式。写成obabel ../testset.mol2 -o vs.mol2 -m不会报错,但结果会直接打到标准输出刷屏,你以为文件生成了,实际上ls出来是空的。加了-m之后文件名会带上序号,切出来的数量应该和原 SDF 里的分子数对得上,跑之前用ls *.mol2 | wc -l核一下,能省掉后面排查半天。
3.3 单个配体的对接与结果回写
循环体是整套脚本的重心,四步一循环:
cd mols for mol2 in `ls *.mol2` do # 1) 配体转 PDBQT prepare_ligand4.py -l $mol2 -o ligand.pdbqt # 2) 对接,日志和构象分别落盘 vina --config ../conf.txt --cpu 1 --out out.pdbqt --log log.txt # 3) PDBQT 转 SDF,方便下游用化学工具读 obabel -ipdbqt out.pdbqt -osd -O temp.sdf # 4) 追加到总结果文件,清掉中间产物 cat temp.sdf >> output.sdf rm -f log.txt ligand.pdbqt out.pdbqt temp.sdf done cd ..逐条说逻辑:
prepare_ligand4.py -l读单个 MOL2,输出ligand.pdbqt。它会补氢、算 Gasteiger 电荷、合并非极性氢,这几步直接决定打分是否合理,别跳过。vina --cpu 1把线程数锁到 1。单机上这么写是浪费,但在 Slurm 里每个任务只分到 1 个核,写成多线程会和其他任务争抢 CPU,反而整体变慢。这是个刻意的选择,不是笔误。--out和--log每次都被同名覆盖。串行跑没问题,因为上一轮的产物已经rm掉了;一旦多节点同时跑,这就是第一个出错的地方。cat temp.sdf >> output.sdf是唯一的汇总动作,整个流程只有一个写入口,串行时是安全的。
log.txt里其实有每个构象的亲和力明细,但脚本跑完就删了,只留下 SDF。这意味着你事后想按 RMSD 筛选或者看打分分布,数据已经没了——这是个设计上的取舍,第 6 章会给一个保留日志的改法。
3.4conf.txt里的盒子与穷举度
conf.txt决定了搜索空间,是影响结果质量最直接的一份配置:
receptor = ../receptor.pdbqt ligand = ligand.pdbqt center_x = 15.19 center_y = 53.90 center_z = 16.91 size_x = 22 size_y = 22 size_z = 22 exhaustiveness = 8 num_modes = 9 energy_range = 3| 参数 | 含义 | 调整建议 |
|---|---|---|
receptor/ligand | 输入文件路径,相对于 vina 的工作目录 | 在mols/里执行,所以受体要写../receptor.pdbqt |
center_* | 搜索盒中心坐标 | 取受体结合口袋的几何中心,别照抄别人的值 |
size_* | 搜索盒三边长度,单位埃 | 刚好包住口袋即可,盲目放大到 40 以上会显著拖慢且降低精度 |
exhaustiveness | 搜索穷举度,默认 8 | 精筛可上到 16~32,粗筛降到 4 换速度 |
num_modes | 最多输出多少个构象 | 一般 9 够用 |
energy_range | 输出构象与最优构象的能量差上限 | 默认 3,想要更多备选构象就放宽 |
参数表里的center_*是最值得花时间的一项。盒子对不准,后面再调exhaustiveness都是白费——我见过把盒子中心按蛋白质质心算的,跑完一堆构象全在溶剂区,纯属浪费机时。
4. Slurm 调度落地:从 munge 认证到第一个sbatch作业
单机跑通之后,瓶颈就变成「一台机器一次只能跑一个配体」。Slurm 要做的就是把那个 for 循环拆成 N 份,分给不同节点,同时保证作业排队、失败重试、资源不打架。整套配置其实不复杂,麻烦的是 Ubuntu 打包出来的路径和官方文档对不上。
4.1 munge 与依赖包安装
munge 是 Slurm 的认证基础,节点之间靠它确认彼此身份。它必须在所有节点上装着同一份密钥,否则sinfo会一直显示down。
sudo apt-get install -y make hwloc libhwloc-dev libmunge-dev libmunge2 munge \ mariadb-server libmysqlclient-dev sudo systemctl enable --now munge sudo systemctl status mungemariadb-server和libmysqlclient-dev是为作业记账(accounting)准备的。资料里的配置把AccountingStorageType设成了accounting_storage/none,也就是不记账,这时候数据库其实用不上,但装上不影响,后面想开sacct统计机时就不用再补了。
验证 munge 是否正常,最快的方式是自己跟自己握一次手:
munge -n | unmunge输出里能看到STATUS: Success就说明密钥和解码链路都通了。多节点集群里如果这一步失败,八成是/etc/munge/munge.key权限不对或者三台机器不一致。
4.2slurm.conf里真正要改的不到十个字段
官方 configurator 生成的配置文件有上百行,但真正影响能不能跑起来的就那么几个:
ClusterName=vina-cluster # 控制器所在节点,旧版本写作 ControlMachine/ControlAddr SlurmctldHost=node01(172.28.10.12) SlurmUser=root SlurmdUser=root AuthType=auth/munge CryptoType=crypto/munge StateSaveLocation=/var/spool/slurmctld SlurmdSpoolDir=/var/spool/slurmd SlurmctldPidFile=/var/run/slurmctld.pid SlurmdPidFile=/var/run/slurmd.pid SlurmctldPort=6817 SlurmdPort=6818 ProctrackType=proctrack/pgid TaskPlugin=task/none SwitchType=switch/none SelectType=select/linear SchedulerType=sched/backfill SlurmctldTimeout=120 SlurmdTimeout=300 ReturnToService=1 SlurmctldDebug=3 SlurmdDebug=3 SlurmctldLogFile=/var/log/slurm-llnl/slurmctld.log SlurmdLogFile=/var/log/slurm-llnl/slurmd.log AccountingStorageType=accounting_storage/none JobCompType=jobcomp/none JobAcctGatherType=jobacct_gather/none NodeName=node01 NodeAddr=172.28.10.12 CPUs=1 RealMemory=1800 State=UNKNOWN NodeName=node02 NodeAddr=172.28.10.13 CPUs=1 RealMemory=1800 State=UNKNOWN NodeName=node03 NodeAddr=172.28.10.14 CPUs=1 RealMemory=1800 State=UNKNOWN PartitionName=p1 Nodes=node01 Default=YES MaxTime=INFINITE State=UP PartitionName=p2 Nodes=node02 MaxTime=INFINITE State=UP PartitionName=p3 Nodes=node03 MaxTime=INFINITE State=UP几个字段值得单独拎出来讲:
SlurmctldHost:老版本(17.x 及更早)写的是ControlMachine加ControlAddr,照抄老配置到新版本会直接被拒绝启动,报Invalid generic type。SlurmUser=root:教学环境图省事,生产环境建议建独立用户。以 root 跑的前提是/var/spool下相关目录归属正确。SelectType=select/linear:按整节点分配资源,小集群够用。改成select/cons_tres才能按单核、单 GPU 粒度分配,那是后话。SchedulerType=sched/backfill:允许小作业插空跑,对一堆短时对接任务很友好。StateSaveLocation:目录必须提前存在,Slurm 不会帮你创建。这是新手最常见的一个问题。PartitionName的Default=YES:只能有一个分区带这个标记,三个都写YES会让slurmctld直接起不来。
节点名和地址建议同时写进/etc/hosts,别只依赖 DNS:
cat <<'EOF' | sudo tee -a /etc/hosts 172.28.10.12 node01 172.28.10.13 node02 172.28.10.14 node03 EOF4.3 目录、开机自启与hostname验证
配置文件就位后,补目录、起服务:
sudo mkdir -p /var/spool/slurmctld /var/spool/slurmd /var/log/slurm-llnl sudo chown -R root:root /var/spool/slurmctld /var/spool/slurmd sudo systemctl enable --now slurmctld sudo systemctl enable --now slurmd sinfosinfo里三个分区都显示idle,这场环境战役就算打赢了。接下来用最小作业验证调度链路,脚本越简单越好,别一上来就提交对接任务:
#!/bin/bash #SBATCH -o job.%j.out #SBATCH -p p1 #SBATCH -J myFirstJob #SBATCH --nodes=1 #SBATCH --ntasks-per-node=1 hostname-o job.%j.out让输出文件名带上作业 ID,避免多次提交互相覆盖;-p p1指定分区,不写会用默认分区;hostname是关键,它告诉你作业到底落在哪台机器上执行。
sbatch job1.sh squeue cat job.*.out输出里的主机名和你sbatch所在机器不一致,说明调度真的生效了。资料里那行--qos=low建议先删掉,QOS 没在配置里定义的话,提交会被直接拒绝。
5. 排障记录:批量对接在 Slurm 上最容易出问题的五个地方
下面五条都是这套环境里真实会遇到的问题,按「现象 → 原因 → 解决」写,遇到类似的直接对号入座。
5.1 作业提交成功,RUNNING两秒后变FAILED
现象:squeue里看到作业闪一下就消失,job.%j.out里只有一行prepare_ligand4.py: command not found,或者vina: command not found。
原因:PATH 是在交互式 shell 里 export 的,而sbatch启动的作业是非登录、非交互 shell,~/.bashrc里放在case $- in *i*)判断之后的语句根本不会执行。你的终端能跑,作业里跑不了,就这么简单。
解决:把三条export PATH=...原样搬进作业脚本开头,别偷懒依赖环境。
#!/bin/bash #SBATCH -p p1 #SBATCH -o logs/job.%j.out export PATH=/home/autodock/autodock_vina_1_1_2_linux_x86/bin:$PATH export PATH=/home/autodock/mgltools_x86_64Linux2_1.5.6/bin:$PATH export PATH=/home/autodock/mgltools_x86_64Linux2_1.5.6/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH5.2slurmctld起不来,日志里说配置找不到
现象:systemctl start slurmctld失败,journalctl -u slurmctld提示Couldn't find the slurm.conf file或Unable to read slurm.conf。
原因:Debian/Ubuntu 打包出来的默认路径是/etc/slurm-llnl/slurm.conf,而很多教程和手工建的目录是/etc/slurm/slurm.conf。你把文件放对了地方,但守护进程找的是另一个目录。
解决:确认实际搜索路径,然后两边都放一份,或者建软链:
sudo mkdir -p /etc/slurm-llnl sudo ln -sf /etc/slurm/slurm.conf /etc/slurm-llnl/slurm.conf sudo systemctl restart slurmctld5.3vina报Could not open receptor
现象:单机跑得好好的conf.txt,搬到作业脚本里就报找不到受体文件,或者报Parse error on line 2 in file ../conf.txt。
原因:conf.txt里的receptor是相对路径,相对的是vina 进程的当前目录,而不是配置文件所在目录。脚本里cd mols之后,receptor.pdbqt就跑到上一层去了;多节点场景下如果每台机器的目录布局不一致,问题会更隐蔽。
解决:共享路径一律写绝对路径,比如receptor = /vina/receptor.pdbqt,ligand同理。绝对路径不好看,但能省掉一整轮排查。
5.4 三个分区同时跑,output.sdf内容错乱
现象:三个节点的作业都显示COMPLETED,但汇总的output.sdf分子数和预期对不上,有的分子出现两次,有的记录被截断成半截。
原因:三个进程同时用cat temp.sdf >> output.sdf往同一个 NFS 文件追加。追加写入在 NFS 上不是原子操作,也没有锁,几个 write 交错在一起就成了这种需要排查的异常状态。更麻烦的是 NFS 客户端有属性缓存,ls -l看到的文件大小都可能滞后。
解决:改成「每人写自己的文件,事后统一合并」。作业里输出到out/${LIG}.pdbqt,全部跑完再一次性合并:
cat out/*.sdf > output.sdf合并动作只在登录节点做一次,没有并发,不会相互覆盖。
5.5prepare_ligand4.py报AttributeError或 import 失败
现象:脚本第一行就崩,报ImportError: No module named numpy,或者AttributeError: 'module' object has no attribute ...。
原因:MGLTools 依赖 Python 2,而系统里可能同时装了 Python 2 和 3 两套 numpy。prepare_ligand4.py的 shebang 指向pythonsh,但pythonsh会去系统 site-packages 里找模块,找错了版本就是这个结果。
解决:先确认解释器版本,再显式指定:
head -1 $(which prepare_ligand4.py) pythonsh -c "import numpy; print(numpy.__version__)"如果确实是 numpy 冲突,用python2 -m pip install numpy装一份到 Python 2 的环境里,或者干脆用python2 prepare_ligand4.py -l x.mol2 -o x.pdbqt显式调用,绕过 shebang。
6. 二次开发:把分片脚本改造成可复用的对接流水线
原始脚本能跑通,但要长期用,有三个地方必须改:输出文件名唯一化、日志保留、任务分发自动化。改完之后的作业脚本长这样:
#!/bin/bash #SBATCH -J vina-array #SBATCH -p p1 #SBATCH -o logs/job.%A_%a.out #SBATCH --nodes=1 #SBATCH --ntasks=1 #SBATCH --cpus-per-task=1 #SBATCH --array=1-500%6 export PATH=/home/autodock/autodock_vina_1_1_2_linux_x86/bin:$PATH export PATH=/home/autodock/mgltools_x86_64Linux2_1.5.6/bin:$PATH export PATH=/home/autodock/mgltools_x86_64Linux2_1.5.6/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH # 每个数组下标对应一行配体名 LIG=$(sed -n "${SLURM_ARRAY_TASK_ID}p" ligand.list) [ -z "$LIG" ] && exit 0 WORK=/vina/run/$LIG mkdir -p "$WORK" && cd "$WORK" || exit 1 prepare_ligand4.py -l /vina/mols/"$LIG".mol2 -o ligand.pdbqt vina --config /vina/conf.txt --cpu "$SLURM_CPUS_PER_TASK" \ --out out.pdbqt --log log.txt obabel -ipdbqt out.pdbqt -osd -O result.sdf 2>/dev/null # 保留最优亲和力,供后续汇总 grep -m1 "REMARK VINA RESULT" out.pdbqt > best.txt--array=1-500%6的%6是同时运行的任务数上限,按集群实际核数调;%A_%a让日志按「作业号_下标」命名,几百个任务的日志不会互相覆盖;--cpu "$SLURM_CPUS_PER_TASK"让 Vina 的线程数和 Slurm 分配到的核数自动对齐,改资源申请时不用再改脚本。
汇总环节从result.sdf里翻数据很痛苦,直接从 PDBQT 的REMARK VINA RESULT读第一行更省事,Vina 按亲和力降序输出,第一个 MODEL 就是最优构象:
import glob, os, re pat = re.compile(r"REMARK VINA RESULT:\s+(-?\d+\.\d+)") rows = [] for p in glob.glob("/vina/run/*/best.txt"): name = os.path.basename(os.path.dirname(p)) with open(p) as fh: m = pat.search(fh.read()) rows.append((name, float(m.group(1)) if m else None)) rows.sort(key=lambda r: (r[1] is None, r[1])) with open("summary.csv", "w") as fh: fh.write("ligand,best_affinity\n") for name, aff in rows: fh.write(f"{name},{'' if aff is None else aff}\n")验证方式很简单:先拿 3 个配体跑一遍数组作业,确认summary.csv行数对、亲和力量级合理(多数小分子在 -5 到 -10 kcal/mol 之间),再放大到全量。我踩过最不划算的一次是直接提交 800 个任务,跑到一半才发现conf.txt的盒子中心填错了,六小时机时全废。从那以后我每次上集群,都强制先用 3 个配体走通全链路再放大规模,这个习惯帮我省下的机时远比想象中多。希望帮到你。
本文还有配套的精品资源,点击获取