简介:这份资料面向生物信息学、计算化学方向的初学者与科研人员,聚焦分子对接在集群环境下的批量执行问题。内容围绕 Autodock Vina 与 Slurm 两条主线展开:一方面梳理 Ubuntu 18.04 下 Open Babel、Autodock Vina、MGLTools 的安装与依赖配置,说明受体与配体的预处理流程、conf.txt 参数控制以及 pdbqt 到 sdf 的结果转换;另一方面讲解 munge 服务的启动、slurm-wlm/slurmd/slurmctld 的部署与 slurm.conf 的关键配置,最终把单机对接脚本迁移到调度系统上批量运行。资源包为 1 个 PDF 文件,约 1013KB,以图文步骤与命令清单形式呈现,便于对照操作与复盘。目前已有 5264 人学习,适合需要快速搭建对接流程、理解作业调度写法、减少环境配置踩坑的读者参考。
1. 一百个配体排成一队:Autodock Vina 批量分子对接卡在哪
单个分子跑一次 Autodock Vina,从准备受体到出打分,熟练的人十分钟能搞定。真正让人卡住的是第一百个:手工改 conf.txt、手工换配体名、手工把 out.pdbqt 转成 sdf,中间任何一次路径写错,前面几小时的等待全白费。Autodock Vina 批量分子对接要解决的就是这件事——把预处理、对接、格式回收串成一条不用人盯的流水线,再把这套流水线交给 Slurm 调度到多台机器上。适合做虚拟筛选初筛、课程设计要交几十个构象、或者实验室只有几台空闲机器想凑出算力的人。前提是你愿意接受命令行,愿意为一个受体盒子反复调参数。
2. 环境先立住:Ubuntu 18.04 上把 Vina、MGLTools、Open Babel 装成一条链
这套工具链最反直觉的地方是:三个软件单装都能跑,装在一起就跑不动。原因是它们互相依赖的运行时版本不一样——Vina 是静态编译的二进制,几乎不挑环境;Open Babel 走系统包管理,吃的是系统库;MGLTools 是 Python 2 时代的东西,还带 Tk 图形界面依赖。Ubuntu 18.04 恰好卡在 Python 2 到 3 的过渡期,python命令默认不存在,这是后面所有报错的源头。
我不建议在 20.04 及以上版本硬套这套流程,MGLTools 1.5.6 的安装脚本对 Python 3 支持很差,改起来比换台 18.04 更费时间。用虚拟机或者容器固定一个 18.04 镜像,把这套环境做成一次性的,后面复制节点时直接克隆,比逐台重装省事得多。
2.1 依赖项不是凑数:libxt、csh、python-tk 各自在替谁干活
那一长串apt-get install里,每一个都有明确归属,装之前心里有数,装完出问题才知道该查谁:
| 依赖包 | 谁在用 | 缺了会怎样 |
|---|---|---|
| libxt-dev / libxmu-dev / libxi-dev | MGLTools 的 Tk 界面组件 | 运行 prepare_ligand4.py 时报共享库缺失 |
| libglu1-mesa-dev / libpng-dev / libjpeg-dev / libtiff-dev | Open Babel 的图形与图像后端 | obabel 转换时报无法加载格式插件 |
| csh | AutoDockTools 的部分包装脚本 | 脚本第一行#!/bin/csh直接找不到解释器 |
| python-tk / python-numpy / python-dev | MGLTools 安装脚本与 AutoDockTools | install.py 中途退出,或 numpy 导入失败 |
| build-essential | 少数需要现场编译的组件 | 后续 pip 或源码安装全线失败 |
一次性装完,别分几次:
# 全部依赖一次装齐,避免装到一半发现缺库再回头补 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 python-tk python-numpy python-dev python-pmw这里的python是关键。18.04 官方源里它指向 Python 2.7,装完which python应该有输出。如果源里没有这个包(部分精简镜像会剔除),退而求其次用apt install python2.7再手工建软链,但软链要建在/usr/bin/python,不要建在用户目录,因为 MGLTools 的脚本 shebang 写死的是#!/usr/bin/env python,走的是 PATH 查找。
2.2 三个软件的落位顺序与解压目录约定
目录结构先定死,后面所有脚本里的路径都从这里派生,改一处不用改十处。我一般这样放:
# 统一工作根目录,所有软件与数据都挂在这下面 sudo mkdir -p /home/autodock cd /home/autodock # 1) Open Babel 走系统包,二进制落在 /usr/bin/obabel sudo apt-get install -y openbabel # 2) Vina 是免安装二进制包,解压即用 tar -zxvf autodock_vina_1_1_2_linux_x86_64.tgz # 3) MGLTools 需要跑一次安装脚本 tar -zxvf mgltools_x86_64Linux2_1.5.6.tar.gz cd mgltools_x86_64Linux2_1.5.6 python install.pytar的参数顺序是血泪经验:-f后面必须紧跟文件名,写成tar -xvfz xxx.tgz会被解释成"从名为 z 的文件里读取",然后报z: Cannot open: No such file or directory,而xxx.tgz被当成要提取的成员名,错误信息完全指不到真正的问题上。养成-zxvf或者-xzvf的习惯。
MGLTools 的install.py全程交互很少,但会在同目录下生成bin/和MGLToolsPckgs/。注意bin/里的可执行文件只是入口,真正的 Python 脚本在MGLToolsPckgs/AutoDockTools/Utilities24/下面——这两个路径都要进 PATH,只加bin会出现"命令找不到"的假象,因为prepare_receptor4.py这类脚本压根不在bin里。
2.3 PATH 收敛到一个 env.sh,避免每次开终端重配
三个软件、四个路径,每次手敲export一定会漏。写成一个文件,所有脚本 source 它:
# /home/autodock/env.sh —— 所有批处理脚本的第一行都 source 这个文件 export AUTODOCK_HOME=/home/autodock export MGL=$AUTODOCK_HOME/mgltools_x86_64Linux2_1.5.6 # Vina 二进制 export PATH=$AUTODOCK_HOME/autodock_vina_1_1_2_linux_x86_64/bin:$PATH # MGLTools 启动脚本 export PATH=$MGL/bin:$PATH # AutoDockTools 的 Python 工具集,prepare_* 系列都在这里 export PATH=$MGL/MGLToolsPckgs/AutoDockTools/Utilities24:$PATH # MGLTools 依赖的 Python 包路径,不设会在 import 时失败 export PYTHONPATH=$MGL/MGLToolsPckgs:$PYTHONPATHPYTHONPATH这一行原文脚本里没有,但实际用的时候大概率会撞上ImportError: No module named MolKit。原因是 MGLTools 的部分脚本以模块方式互相引用,只靠 PATH 找到入口不够,得让解释器也能找到包目录。加上这一行之后,交互式python -c "import MolKit"能过,才说明环境真的通了。
2.4 装完先验:四条命令确认链路通
不要一装完就扔进去跑批量,先用四条命令把每一环单独点一遍:
source /home/autodock/env.sh vina --version # 期望输出 1.1.2 版本号 obabel -V # 期望输出 Open Babel 版本 prepare_receptor4.py -h # 期望打印参数帮助,不报 ImportError prepare_ligand4.py -h # 同上 python -c "import MolKit; print('ok')"哪一条报错就停在哪一步查,别往下走。常见的三种失败:prepare_ligand4.py: not found说明 Utilities24 没进 PATH;bad interpreter: No such file or directory说明python命令不存在;ImportError说明 PYTHONPATH 没设。这三类问题占了环境搭建失败的九成,逐条对应上去,比盲目重装快得多。
3. 脚本拆开看:从 testset.sdf 到 output.sdf 的六步流水线
批量脚本表面上是个 for 循环,实际上有六个必须按序执行的阶段:受体预处理一次、配体集合转格式、配体拆分成单文件、逐个配体预处理、逐个对接、结果回收集合。任何一步的顺序反了,或者中间产物命名冲突,最后拿到的 output.sdf 就是脏数据——最坏的情况不是报错,而是静默地少几个分子或者串了几个分子的构象,等你拿去分析才发现。
先看完整骨架,再逐段拆:
#!/bin/bash # vina_batch.sh —— 单机串行批量对接主脚本 set -e # 任何一步失败立即退出,避免脏数据继续流转 source /home/autodock/env.sh cd /home/autodock # 阶段一:受体只准备一次 prepare_receptor4.py -r pro.pdb -e -o receptor.pdbqt # 阶段二:整份 SDF 转成 mol2 obabel -isd testset.sdf -omol2 -O testset.mol2 # 阶段三:按记录拆成单分子文件 mkdir -p mols && cd mols obabel ../testset.mol2 -O vs.mol2 -m # 阶段四到六:准备、对接、回收 : > output.sdf # 清空结果文件,避免追加到上一次的残留 for mol2 in $(ls *.mol2 | sort -V); do base=${mol2%.mol2} # 保留分子名,用于区分产物 prepare_ligand4.py -l "$mol2" -o "${base}.pdbqt" vina --config ../conf.txt --cpu 1 \ --ligand "${base}.pdbqt" \ --out "${base}_out.pdbqt" \ --log "${base}.log" obabel -ipdbqt "${base}_out.pdbqt" -osd -O "${base}.sdf" cat "${base}.sdf" >> output.sdf rm -f "${base}.pdbqt" "${base}_out.pdbqt" "${base}.sdf" "${base}.log" doneset -e这一行值得单说。分子对接单次失败很常见(配体太大、盒子没罩住、电荷异常),没有set -e时脚本会带着错误继续跑,后面的cat把空文件追加进去,你最后得到的是一个分子数对不上、还查不出是哪一步丢的 output.sdf。加上它,出问题立刻停在原地,日志就是现场。
3.1 受体只准备一次:prepare_receptor4.py 的输出要留档
受体预处理在整个流程里只做一次,但它的输出质量决定后面所有对接的可信度。核心命令是:
prepare_receptor4.py -r pro.pdb -e -o receptor.pdbqt # 检查输出是否合理:原子数、残基是否有明显缺失 grep -c "^ATOM" receptor.pdbqt grep "^ATOM" receptor.pdbqt | awk '{print $4}' | sort -u | wc -l-r是输入受体 PDB,-o是输出 pdbqt。-e这个开关在不同 MGLTools 打包版本上行为不完全一致,作用范围是受体里保留哪些原子(水、非标准残基、氢原子),所以我一般不盲信它——跑完之后拿原子行数和唯一残基数跟原始 PDB 对一遍,确认关键残基没被清掉。
判断标准很朴素:原始pro.pdb里有多少个不同残基编号,pdbqt 里应该基本对得上,差几个通常是水和杂原子,属于正常清理。如果差了几十个,说明非标准残基被整段删了,对接盒子很可能落在空腔上,跑出来的结合能好看但没意义。
另外,受体 pdbqt 一定要改成一个固定的、不会在循环里被覆盖的名字。它属于"一次准备、多次复用"的资产,跟每个配体一一对应的中间文件不是一类东西。
3.2 配体拆分:obabel 的 -m 到底生成了什么文件名
obabel -isd testset.sdf -omol2 -O testset.mol2是把整份多记录 SDF 合并成一个 mol2 文件。这一步其实可以跳过,但中间多一次转换有个好处:mol2 里保留了键级和电荷信息,后面 prepare_ligand4.py 处理时比直接吃 sdf 稳。
真正拆分成单文件的是-m开关:
cd mols obabel ../testset.mol2 -O vs.mol2 -m ls # 期望看到 vs1.mol2 vs2.mol2 vs3.mol2 ...-m的含义是"按记录拆分成多个文件",输出文件名规则是:把-O指定名字里的扩展名之前插入序号。vs.mol2就变成vs1.mol2、vs2.mol2,依次递增。序号从 1 开始,不加前导零——这一点直接影响排序,ls默认按字典序排会得到vs1, vs10, vs100, vs11...,跟 SDF 里记录的顺序完全对不上。
这就是脚本里ls *.mol2 | sort -V的作用:-V是版本号排序,能正确处理数字大小,让 vs2 排在 vs10 前面。如果你后续要靠序号反查原始 SDF 里的分子名,这个顺序必须是对的,否则编号和结构对不上,分析结论全废。
还有一点:拆分后 mol2 文件里的分子名不一定保留原始 SDF 里的_Name字段。常见做法是提前把配体名写进文件名,或者在拆分后用grep从 mol2 第一行把名字抓出来做映射表,别指望序号能反推。
3.3 conf.txt 里六个盒子参数怎么定
Vina 的配置文件是整条流水线的核心参数,改错一个数字,跑十个小时也是白跑:
# conf.txt —— 对接参数配置 receptor = /home/autodock/receptor.pdbqt ligand = ligand.pdbqt # 搜索盒中心,通常取受体结合口袋的几何中心 center_x = 12.5 center_y = -3.2 center_z = 18.7 # 搜索盒边长,单位埃,三边都要能罩住整个口袋 size_x = 22 size_y = 22 size_z = 22 # 搜索强度,默认 8,批量初筛常用 8~16 exhaustiveness = 8 # 最多保留几个构象 num_modes = 9 # 输出构象之间的能量差上限,单位 kcal/mol energy_range = 3 # 固定随机种子,保证同一输入可复现 seed = 42盒子的定法没有万能公式。常规做法是从共晶结构里取原配体的几何中心,没有共晶结构就查文献里的关键残基,取这些残基的侧链原子坐标平均值作为中心,再把三边设成能覆盖整个口袋的尺寸。盒子太小,配体伸不进去,Vina 会在日志里提示部分原子超出搜索空间;盒子太大,搜索空间爆炸,耗时线性上涨而命中率下降。
exhaustiveness是最容易被滥用的一项。调到 32 以上确实能提高找到最优构象的概率,但耗时也翻几倍。批量初筛阶段我用 8,把上千个分子过一遍筛掉明显不结合的;对筛出来的几十个再单独用 16 到 32 精算。一次就上 32,机器跑不完,进度还看不到。
seed固定住很重要。同一个配体、同一个盒子,换随机种子得到的最优打分可能差 0.5 kcal/mol 以上。批量场景里如果你要比较不同分子的得分排序,种子必须一致,否则排序里混进了随机噪声。
3.4 循环体内部:vina 调用、pdbqt 转 sdf 与结果追加
循环里每一轮做四件事:准备配体、对接、转格式、追加结果。逐条看参数:
prepare_ligand4.py -l vs1.mol2 -o vs1.pdbqt # -l 输入配体,-o 输出。输出名跟输入名绑死, # 是为了让后面每一步都能从名字反推是哪个分子 vina --config ../conf.txt \ --cpu 1 \ --ligand vs1.pdbqt \ --out vs1_out.pdbqt \ --log vs1.log # --config 指定参数文件;命令行参数优先级高于配置文件 # --ligand 覆盖 conf.txt 里的 ligand 行 # --cpu 1 表示单核,批量场景下靠多节点并行而不是单任务多核 # --out 输出构象集合,--log 输出打分与运行信息 obabel -ipdbqt vs1_out.pdbqt -osd -O vs1.sdf # pdbqt 转 sdf,保留构象坐标,方便后续用 PyMOL 或 RDKit 读 cat vs1.sdf >> output.sdf--cpu 1这个设置要理解清楚:Vina 的多线程是在单个任务内部并行搜索,对单次对接有加速,但批量场景下更好的策略是每个任务单核,靠调度器把几百个任务铺到多台机器上。如果一边给每个任务开 8 核,一边又想并行几十个任务,机器会严重超卖,每个任务都变慢,整体吞吐反而下降。
--log文件不要急着删。它里面记录了每个构象的打分和 RMSD 上下界,批量跑完之后排查"为什么这个分子没结果"全靠它。我在循环里保留了删日志的动作是为了控制磁盘,实际生产里会把它统一 mv 到一个 logs 目录,出问题还能翻。
结果汇总用cat >>追加有个前提:脚本开头必须把 output.sdf 清空(: > output.sdf),否则第二次运行会追加到上一次的结果后面,分子数翻倍。这个坑非常隐蔽,因为 SDF 本身允许多记录,文件不会报错,只有你去数分子数才会发现不对。
4. Slurm 单机跑通:munge 认证、slurm.conf 八个必改项与 sbatch 验证
单机上的 Slurm 看起来没什么意义——一个节点调度给谁看?但它是整个集群方案的验证台阶。单机能跑通,说明 munge 认证、配置文件、目录权限、服务启动顺序这条链是通的,后面加节点只是复制和改地址;单机跑不通就直接上三节点,你会同时面对认证、网络、配置同步三类问题,排查成本翻好几倍。
4.1 munge 是认证底座,它挂了 Slurm 一定起不来
munge 提供节点之间的身份认证,Slurm 的控制进程和计算进程全靠它互相确认身份。它的失败模式很典型:服务显示 active,但 Slurm 一启动就报认证错误,因为两端用的密钥不一样。
# 安装 munge 与 Slurm 组件 sudo apt-get install -y munge libmunge-dev libmunge2 \ slurm-wlm slurmd slurmctld # 启动并设为开机自启 sudo systemctl enable munge sudo systemctl start munge sudo systemctl status munge --no-pager # 验证 munge 本身能否正常工作 munge -n | unmunge最后一条命令是判断 munge 是否健康的唯一可靠方式:munge -n生成一个凭证,unmunge解出来,能看到解码后的 UID 和 GID 就说明密钥和 socket 都正常。只看systemctl status是不够的,服务活着但密钥权限不对的情况太常见了。
密钥文件的权限必须是400,属主是 munge 用户。权限放宽到 644 之后 munge 会拒绝启动或者启动后认证失败,而报错信息往往指向 Slurm 而不是 munge。多节点场景下,所有节点必须用同一份/etc/munge/munge.key,拷贝过去之后重新设权限、重启服务。
4.2 slurm.conf 里必须自己确认的八个字段
配置文件很长,但真正需要按环境改的只有八处,其余保持注释或默认值即可:
| 字段 | 作用 | 单机取值示例 | 改错的后果 |
|---|---|---|---|
| ClusterName | 集群标识 | cluster | 多集群混用时作业投错地方 |
| 控制节点地址 | 控制进程所在主机 | node01 / 192.168.56.11 | 计算节点连不上控制器 |
| SlurmUser | 运行守护进程的用户 | root | 权限不足,状态目录写不进 |
| StateSaveLocation | 控制器状态持久化目录 | /var/lib/slurm-llnl/slurmctld | 目录不存在则 slurmctld 起不来 |
| SlurmdSpoolDir | 计算节点暂存目录 | /var/lib/slurm-llnl/slurmd | 与创建目录不一致时节点 Drain |
| SelectType | 资源分配粒度 | select/linear | 与分区定义不匹配导致分配失败 |
| SchedulerType | 调度算法 | sched/backfill | 回填失效,短作业排队变长 |
| NodeName / PartitionName | 节点与分区映射 | node01 / debug | 分区名写错,sbatch 直接拒收 |
StateSaveLocation和SlurmdSpoolDir这两项跟实际创建的目录必须严格一致,这是单机部署最常见的翻车点。配置文件里写/var/lib/slurm-llnl/slurmctld,手工mkdir的是/var/spool/slurmctld,服务启动时不会报"目录不存在",而是静默失败或者启动后立刻退出,日志里只有一行很含糊的权限错误。稳妥做法是先建目录、再写配置、最后启动,三步顺序不能乱。
# 目录先建好,属主对齐配置里的 SlurmUser sudo mkdir -p /var/lib/slurm-llnl/slurmctld sudo mkdir -p /var/lib/slurm-llnl/slurmd sudo mkdir -p /var/log/slurm-llnl # 配置文件放在发行版约定的位置 sudo tee /etc/slurm-llnl/slurm.conf > /dev/null <<'EOF' ClusterName=cluster SlurmctldHost=node01 SlurmUser=root StateSaveLocation=/var/lib/slurm-llnl/slurmctld SlurmdSpoolDir=/var/lib/slurm-llnl/slurmd SlurmctldPort=6817 SlurmdPort=6818 AuthType=auth/munge CryptoType=crypto/munge SelectType=select/linear SchedulerType=sched/backfill SlurmctldLogFile=/var/log/slurm-llnl/slurmctld.log SlurmdLogFile=/var/log/slurm-llnl/slurmd.log SlurmctldDebug=3 SlurmdDebug=3 ReturnToService=1 NodeName=node01 NodeAddr=192.168.56.11 CPUs=4 CoresPerSocket=1 ThreadsPerCore=1 PartitionName=debug Nodes=node01 Default=YES MaxTime=INFINITE State=UP EOF这里有两个版本差异要留神:较新的 Slurm 用SlurmctldHost取代了老的ControlMachine,配置文件写老字段名,新版本会直接忽略并报"没有控制节点";反过来,18.04 源里的 Slurm 版本对SlurmctldHost的解析也可能不完整。最稳的判断方法不是背版本号,而是启动失败时先看slurmctld -C或者日志里的第一行报错,它会明确告诉你哪个字段不认识。
CPUs和CoresPerSocket要跟实际机器核数对上。写多了,调度器会把超量的任务压给节点,结果是内存打爆、进程被 OOM 杀掉;写少了,机器的算力用不满。虚拟机环境里nproc的输出就是最可靠的依据。
4.3 目录、权限与启动顺序
启动顺序有讲究:munge 必须在两个 Slurm 服务之前起,slurmctld 要在 slurmd 之前起。反过来做,计算节点会先尝试注册然后失败,节点状态卡在DOWN,即使后面服务起来了也要手工scontrol update才能恢复。
# 严格按顺序:认证 -> 控制器 -> 计算节点 sudo systemctl restart munge sleep 1 sudo systemctl restart slurmctld sleep 2 sudo systemctl restart slurmd # 检查两个服务 systemctl is-active munge slurmctld slurmd # 看节点是否注册成功 sinfo scontrol show node node01sinfo输出里节点状态应该是idle,分区状态是up。如果显示down或者drain,scontrol show node里的Reason字段会写原因,多半是"无法与控制器通信"或者"spool 目录不可写"。这两个原因分别对应 munge 和目录权限,按前面的检查项回去对。
4.4 用一个 hostname 作业验证调度链路
别一上来就提交对接任务。先提交一个只会打印主机名的最小作业,把"提交、排队、分配、执行、回收输出"这条链单独验证一遍:
# job1.sh —— 最小验证作业 #!/bin/bash #SBATCH -o job.%j.out # 输出文件名,%j 自动替换成作业号 #SBATCH -p debug # 分区名,必须与 slurm.conf 里一致 #SBATCH -J first_job # 作业名,squeue 里显示 #SBATCH --nodes=1 # 节点数 #SBATCH --ntasks-per-node=1 # 每节点任务数 hostname echo "SLURM_JOB_ID=$SLURM_JOB_ID" echo "SLURM_JOB_NODELIST=$SLURM_JOB_NODELIST"sbatch job1.sh squeue # 应能看到作业短暂出现在 R 状态 cat job.*.out # 期望输出节点名与作业号作业能在几秒内跑完、输出文件里有节点名,说明调度链路完全通了。这一步失败的话,squeue里作业会很快消失,sacct -j <作业号>能看到退出码,最常见的是1:0或127——分别是脚本本身报错和命令找不到。127 基本可以断定是计算节点上 PATH 没设置好,后面讲并行改造时会专门处理。
5. 常见问题与排查:五个现场,从格式报错到节点 Drain
前面四章是理想路径,这一章是我实际踩过的坑。每一条都按"看到什么、为什么、怎么修"写,遇到同类现象直接对号入座,比从头重装快得多。
5.1 预处理阶段的三个坑
坑一:prepare_ligand4.py报bad interpreter或者SyntaxError: invalid syntax。
现象是脚本能被执行到,但立刻停在第一行,报解释器不存在或者语法错误指向一些看似正常的 Python 语句。原因是 Ubuntu 18.04 默认没有python命令,只有python3,而 MGLTools 的脚本 shebang 写的是#!/usr/bin/env python,PATH 里找不到就退化成用 sh 执行,然后被 Python 语法卡住。
解决办法不是改每个脚本的 shebang(几十个文件改不完),而是在系统层补一个 Python 2 入口:
# 优先用源里的 python 包,它会建立 /usr/bin/python -> python2.7 的链接 sudo apt-get install -y python # 源里没有时的手工兜底 sudo ln -sf /usr/bin/python2.7 /usr/bin/python python --version # 必须显示 Python 2.7.x补完之后先跑一次prepare_ligand4.py -h,能打印帮助就说明这一层通了。注意别把python指向 Python 3,MGLTools 1.5.6 的代码里有大量 Python 2 专有语法,指向 3 会报一堆print语句相关的语法错误。
坑二:受体 pdbqt 的原子数比原始 PDB 少了一大截。
现象是对接能跑完,但结合能异常漂亮,盒子位置看起来也没错。把 receptor.pdbqt 和 pro.pdb 的原子行数一比,少了几百行。原因是prepare_receptor4.py会按默认规则清理非标准残基、水分子和部分氢原子,参数没显式指定时,清理范围可能超出预期。
解决办法是跑完立刻做一次对比,把差异量化:
grep -c "^ATOM" pro.pdb receptor.pdbqt # 残基编号集合对比,看少了哪些残基 grep "^ATOM" pro.pdb | cut -c18-21 | sort -u > /tmp/res_pdb.txt grep "^ATOM" receptor.pdbqt | cut -c18-21 | sort -u > /tmp/res_pdbqt.txt comm -23 /tmp/res_pdb.txt /tmp/res_pdbqt.txtcomm -23输出的是只在前一个文件里出现的残基,也就是被清理掉的。如果里面有金属离子、辅因子或者关键残基,就要在预处理阶段显式保留,或者在对接盒子里手动补上这些坐标。少几十个水分子是正常的,少一个辅因子可能导致整个口袋构象失真。
坑三:拆分出来的 mol2 文件顺序跟原始 SDF 对不上。
现象是最后 output.sdf 里第 7 个分子的构象,明显配不上原始 SDF 第 7 个记录。原因就是前面说的字典序问题:ls把vs10.mol2排在vs2.mol2前面,序号和原始记录顺序错位。
修复方式有两种,按场景选:
# 方式一:循环时按版本号排序,编号顺序与 SDF 记录顺序一致 for mol2 in $(ls *.mol2 | sort -V); do ... done # 方式二:拆分时就补零,让字典序等于数字序 obabel ../testset.mol2 -O vs.mol2 -m for f in vs*.mol2; do n=$(echo "$f" | grep -o '[0-9]*') mv "$f" "$(printf 'vs%04d.mol2' "$n")" done第二种方式更彻底,因为后面所有基于文件名的排序、脚本遍历、人工核对都自动对齐了。批量任务里文件名一旦乱序,排查成本比多写十行代码高得多。
5.2 调度与节点阶段的两个坑
坑四:sbatch 提交后作业立即失败,输出文件为空或者只有一行command not found。
现象是squeue里作业闪一下就没了,job.%j.out要么是空文件,要么写着vina: command not found。原因很明确:sbatch 执行时是一个非交互式 shell,它不会加载你.bashrc里的 PATH,你在登录终端里能跑的vina,在作业里找不到。
解决办法是把环境初始化写进作业脚本本身,而不是依赖登录环境:
#!/bin/bash #SBATCH -o job.%j.out #SBATCH -p debug #SBATCH -J vina_test #SBATCH --nodes=1 #SBATCH --ntasks-per-node=1 # 关键:所有 PATH 都在脚本内显式加载 source /home/autodock/env.sh # 打印实际环境,出问题时用于核对 echo "PATH=$PATH" echo "HOST=$(hostname)" cd /home/autodock/mols vina --versionsource env.sh这一行必须放在#SBATCH注释之后、所有实际命令之前。另外,如果环境变量文件里引用了交互式 shell 才有的变量(比如HISTFILE),在作业里会报警告,那些行要么条件判断,要么直接删掉。
坑五:sinfo里节点状态变成drain或者down,但服务是 active 的。
现象是多节点场景下,某个计算节点一直分不到任务,scontrol show node显示的 Reason 是"Not responding"或者"Slurmd spool directory inaccessible"。原因有三种,按出现频率排序:munge 密钥与其他节点不一致、spool 目录不存在或权限不对、控制节点的地址在计算节点上不可达。
排查步骤是固定的三段式:
# 1) 认证通不通,这条能过说明 munge 没问题 munge -n | ssh node02 unmunge | head -5 # 2) spool 目录对不对,必须与 slurm.conf 里的 SlurmdSpoolDir 一致 grep SlurmdSpoolDir /etc/slurm-llnl/slurm.conf ls -ld /var/lib/slurm-llnl/slurmd # 3) 网络通不通 ping -c 2 <控制节点地址>三条里任何一条失败,原因就定位到了。修完之后节点不会自动恢复,需要手工唤醒:
sudo scontrol update NodeName=node02 State=RESUME sinfo -N -l值得说一句的是ReturnToService=1这个配置项,它让节点在故障恢复后自动回到服务状态,省去手工唤醒。但自动恢复有个副作用:如果故障是间歇性的,节点会在 drain 和 idle 之间反复横跳,作业断断续续失败,反而更难排查。我一般先手工确认根因修掉,再打开这个开关。
6. 三节点并行收尾:NFS 共享、分区划分与结果校验
三节点跟单机的差别不在软件,在两个地方:所有节点要看到同一份输入和输出(NFS),以及每个节点要能独立分到一个分区的作业(分区划分)。少一样,三个节点就是三台各跑各的机器。
NFS 的做法是把控制节点上的/home/autodock导出,计算节点挂到同一路径。关键是挂载点必须完全一致——如果控制节点写/home/autodock,计算节点挂/vina,脚本里的相对路径就全崩了。
# 控制节点 /etc/exports:只对集群内网开放 /home/autodock 192.168.56.0/24(rw,sync,no_subtree_check,no_root_squash) # 计算节点挂载,路径必须与控制节点一致 sudo mount -t nfs 192.168.56.11:/home/autodock /home/autodock echo "192.168.56.11:/home/autodock /home/autodock nfs defaults 0 0" | sudo tee -a /etc/fstabno_root_squash在多节点跑 root 作业时必须加,否则计算节点上的 root 会被降权成 nobody,写结果文件直接失败。写完/etc/fstab之后重启一次计算节点,确认自动挂载生效,比手工 mount 一次就上生产靠谱得多。
分区划分上,我给每个节点单独一个分区(p1、p2、p3),作业脚本里把配体列表按序号均分,每个分区提交一批:
# 把 vs*.mol2 按序号取模分到三个目录,每个目录对应一个分区 mkdir -p molsp1 molsp2 molsp3 i=1 for f in $(ls mols/vs*.mol2 | sort -V); do case $((i % 3)) in 1) cp "$f" molsp1/ ;; 2) cp "$f" molsp2/ ;; 0) cp "$f" molsp3/ ;; esac i=$((i + 1)) done均分看起来简单粗暴,但比"一次性 sbatch 几百个任务"更好控制:每个分区的脚本能单独重跑,某一批失败不用全部重来,输出文件也按分区隔离,不会互相覆盖。
结果校验我固定用下面这条,比肉眼翻 SDF 可靠:
# 统计每个配体的最优结合能(Vina 输出的第一行 REMARK 就是最优构象) grep -h "REMARK VINA RESULT" output.sdf | awk '{print $4}' | sort -n | head -20 # 分子数核对:SDF 以 $$$$ 结尾,统计分隔符数量就是分子数 grep -c '^\$\$\$\$' output.sdf第二个数字必须等于输入配体数,少一个就说明某个分子在某一步被静默丢弃了。这时候回到对应分区的日志里 grep 分子名,基本都能定位到是预处理失败还是盒子没罩住。
| 校验项 | 命令 | 期望结果 |
|---|---|---|
| 结果分子数 | grep -c '^\$\$\$\$' output.sdf | 等于输入配体数 |
| 打分分布 | 上面那条 awk | 前 20 个值里不应出现正数大值 |
| 单分子日志 | grep -i error <分子名>.log | 无输出 |
| 分区任务数 | sacct -a -X -o JobName,State | 全部 COMPLETED |
有一次我少核对了分子数这一项,跑了六个小时,最后拿去分析时才发现两百个配体只回来了 187 个,差的那 13 个全是分子量偏大的——盒子边长设成 22 埃根本罩不住。从那以后我每次批量跑完,第一件事就是把$$$$数量和输入分子数对一遍,对不上就先查盒子,不往下走。希望帮到你。
本文还有配套的精品资源,点击获取