简介:本资源是一套面向计算化学、材料科学领域科研人员与高年级研究生的批量分子动力学(MD)模拟自动化工具集,旨在解决重复性MD任务设置繁琐、参数管理低效、多条件对比分析困难等实际问题。压缩包共427个文件,14.62MB,涵盖130个AMBER输入控制文件(.in)、37个分子结构文件(.pdb)、24组力场参数体系(含.prmtop、.frcmod、.lib、.inpcrd、.mol2等),以及17个Shell脚本(.sh)和1个核心Python脚本(.py),完整支撑从分子预处理(如ANTECHAMBER电荷生成)、系统构建、GROMACS/LAMMPS兼容参数转换到批量提交与日志解析的全流程。已有694人学习下载,资源以virtual_MD为核心目录组织,结构清晰、示例即用,附带典型log输出与inf状态监控文件,便于快速上手、调试验证及拓展定制,是开展温度/压力扫描、构象采样或溶剂化效应研究的高效实践基线。
1. 批量分子动力学模拟脚本和示例:不是“一键跑完”,而是把 200 个体系从建模→提交→解析结果全链路串起来的工程化工具包
你有没有试过:手头有 37 个配体-蛋白复合物,每个要扫 5 个温度点、3 种离子浓度,光是写 LAMMPS 或 GROMACS 的 input 文件就花掉两天?改一个参数就得重命名 15 个目录、手动改 42 行 mdp 文件、再逐个检查 log 里有没有Step 0后的报错?这不是做科研,是在当人肉 Makefile。这个「批量分子动力学模拟脚本和示例」包,就是为这种场景生的——它不封装底层引擎(不碰 LAMMPS/GROMACS 二进制),但用 Python + Shell 把建模(packmol/tleap)、拓扑生成(acpype/parmed)、任务调度(slurm/pbs)、日志解析(energy/temperature/RMSD 提取)、失败自动重试这五层胶水全焊死了。适合正在做构象稳定性筛选、溶剂化自由能粗筛、或需要横向对比力场参数的计算化学实践者。它不是教学 demo,是某高校计算平台运维组压箱底的生产级脚本集,已支撑过单次超 800 个独立 MD 任务的并发调度。
2. 脚本架构与核心模块拆解:为什么选 Python + Bash 组合而不是纯 Python 或纯 Shell?
2.1 整体分层设计:从输入配置到结果归档的六层流水线
整个工具包采用清晰的职责分离:
- 输入层:YAML 配置文件(
config.yaml)定义体系列表、参数网格、集群资源策略; - 建模层:调用 packmol 构建溶剂盒,或 tleap 生成 AMBER 拓扑,支持 PDB/SMILES 双入口;
- 模板层:Jinja2 渲染
.in(LAMMPS)或.mdp(GROMACS)模板,变量如{{ temperature }}、{{ nsteps }}全局注入; - 调度层:生成 slurm 脚本(
sbatch.sh)并自动sbatch提交,支持依赖队列(--dependency=afterok:); - 监控层:后台轮询
jobid状态,超时自动 kill 并标记FAILED_TIMEOUT; - 解析层:用
gmx energy/lammps -log提取关键指标,输出 CSV+PNG 可视化。
提示:所有模块通过
run_pipeline.py统一入口驱动,不强制要求环境变量,所有路径均相对project_root/解析,避免硬编码路径翻车。
2.2 为什么坚持 Python + Bash 混合?血泪经验告诉你边界在哪
纯 Python 调用subprocess.run()管理 HPC 作业看似优雅,但实际踩过三个深坑:
- 信号传递失效:Python 进程被
Ctrl+C中断时,子进程(如sbatch提交的 job)常变成僵尸,集群资源卡死; - 日志流阻塞:
gmx mdrun输出含 ANSI 转义符,Python 的stdout.read()会卡在\x1b[?25l(隐藏光标)处; - 权限继承异常:某些 HPC 环境中,Python 子进程无法继承
module load gromacs/2022.5的环境,导致gmx命令未找到。
而 Bash 天然适配 HPC 场景:trap 'kill -- -$$' EXIT可靠清理子进程树,gmx energy -f edr.xvg 2>/dev/null | tail -n +18直接管道解析,module load在sbatch.sh中原生生效。我们只用 Python 做高阶逻辑(YAML 解析、参数组合、失败分类),Bash 做原子操作(建模、运行、提取),二者通过 JSON 中间文件通信(如job_config.json写入当前任务参数,Bash 脚本读取后渲染模板)。这种切分让每个环节可单独测试——你可以bash scripts/build_box.sh job_001单独验证建模,不用启动整条流水线。
2.3 核心脚本功能速查表
| 脚本路径 | 语言 | 输入 | 输出 | 关键能力 |
|---|---|---|---|---|
scripts/generate_inputs.py | Python | config.yaml,templates/ | jobs/job_001/{topol.tpr, conf.gro, md.mdp} | 支持grid_search: {temp: [298,310], conc: [0.1,0.15]}自动生成 4 个任务 |
scripts/submit_job.sh | Bash | job_config.json | slurm-123456.out,job_001.done | 自动检测sinfo -p gpuq队列空闲数,动态分配--gres=gpu:1 |
scripts/parse_log.sh | Bash | job_001/md.log | job_001/summary.csv(含step, temp, press, pot_en) | 正则匹配Step.*?Temp.*?Press.*?PotEn,跳过WARNING: Listed nonbonded interaction噪声行 |
scripts/plot_summary.py | Python | all_jobs/summary.csv | plots/rmsd_vs_temp.png | 自动按system_name分组,用 seaborn 绘制箱线图 |
注意:所有 Bash 脚本第一行强制
#!/usr/bin/env bash -euxo pipefail,-e遇错退出,-u禁止未定义变量,-x打印执行命令,-o pipefail让a \| b \| c中任意环节失败即整体失败——这是 HPC 脚本可靠性的底线。
3. 快速上手:从零部署到跑通第一个批量任务(以 GROMACS 为例)
3.1 环境准备:三步确认你的集群已就绪
先别急着 clone,用这三条命令验证基础环境是否达标:
# 1. 确认 GROMACS 可用且版本 ≥ 2021.5(因使用 `-gpu_id` 参数) gmx --version | head -n 1 # 2. 确认 packmol 可调用(建模必需) packmol --help | grep "Create molecular" > /dev/null && echo "✓ packmol OK" || echo "✗ packmol missing" # 3. 确认 Slurm 可提交(非 PBS/LSF 用户需修改 scripts/submit_job.sh 中的 sbatch 替换逻辑) sinfo -s | grep "up" > /dev/null && echo "✓ Slurm OK" || echo "✗ Slurm down"若任一失败,请按错误提示安装对应工具(packmol编译需gfortran,GROMACS 推荐用 conda 安装gromacs=2022.5避免 MPI 版本冲突)。
3.2 初始化项目结构:复制模板并修改最小配置
克隆仓库后,进入根目录执行:
# 复制示例配置(含 3 个测试体系:water_box, protein_ligand, membrane) cp -r examples/gromacs_example/* ./ # 修改 config.yaml —— 这是你唯一需要编辑的文件 nano config.yaml打开config.yaml,重点修改三处(其余保持默认即可):
# 1. 指定你的体系列表(支持 glob 模式) systems: - pdb_files: ["examples/pdbs/1abc.pdb"] # 蛋白结构 ligand_smiles: "c1ccccc1" # 苯,将自动用 acpype 生成拓扑 name: "benzene_in_1abc" # 2. 定义参数扫描网格(此处生成 2×2=4 个任务) parameters: temperature: [298, 310] # K nsteps: 10000 # 步数,小规模测试用 dt: 0.002 # ps/step # 3. 集群资源策略(根据你的 HPC 调整) cluster: partition: "cpuq" # 队列名 time: "02:00:00" # 时长 ntasks: 16 # CPU 核数(GROMACS 最佳并行度) mem_per_cpu: "4G" # 内存逻辑说明:
systems下每个元素生成一个独立任务目录(如jobs/job_001/),parameters的笛卡尔积决定该任务下的子任务数(如temperature × nsteps生成 4 个md.mdp)。name字段会成为输出目录名前缀,方便后续grep "benzene_in_1abc" all_jobs/summary.csv快速筛选。
3.3 执行全流程:四条命令走完建模→提交→等待→解析
# 步骤1:生成全部输入文件(耗时取决于体系数,3 个体系约 15 秒) python scripts/generate_inputs.py # 步骤2:提交所有任务(自动检测 slurm 队列负载,每 5 个任务加 1 秒间隔防 flood) bash scripts/submit_all.sh # 步骤3:实时监控进度(显示 running/pending/done 数量,Ctrl+C 退出) bash scripts/monitor_jobs.sh # 步骤4:所有 done 后,一键解析全部结果 python scripts/aggregate_results.py执行完aggregate_results.py,你会得到:
all_jobs/summary.csv:所有任务的step,temp,pot_en,rmsd时间序列;plots/temperature_summary.png:按温度分组的 RMSD 箱线图;logs/run_pipeline.log:完整执行轨迹,含每个任务的启动时间、耗时、错误码。
参数说明:
submit_all.sh默认启用--max-concurrent 10(同时最多提交 10 个任务),避免集群调度器拒绝;monitor_jobs.sh每 30 秒轮询一次squeue -u $USER,比watch -n 30 squeue更省资源;aggregate_results.py会自动跳过job_001/summary.csv缺失的任务,只汇总成功项。
4. 避坑指南:五个真实翻车现场与当场急救方案
4.1 现象:generate_inputs.py报错ModuleNotFoundError: No module named 'jinja2'
原因:脚本依赖jinja2渲染模板,但未隔离 Python 环境。HPC 登录节点常预装旧版 Python(如 3.6),而pip install jinja2可能装到用户目录,/usr/bin/python3无法加载。
解决:
- 不要用系统 Python,用 conda 创建专用环境:
conda create -n md_batch python=3.9 conda activate md_batch pip install jinja2 PyYAML numpy pandas - 修改
generate_inputs.py第一行:#!/usr/bin/env python3→#!/usr/bin/env python3.9(或你的 conda 环境路径/path/to/miniconda3/envs/md_batch/bin/python)
4.2 现象:submit_job.sh提交后squeue显示PD(pending),但 10 分钟不转R(running)
原因:Slurm 资源请求超限。例如config.yaml中设ntasks: 128,但cpuq队列最大只允许 32 核/任务。
解决:
- 查看队列限制:
scontrol show partition cpuq | grep -E "(MaxNodes|MaxCPUsPerNode|State)" - 临时降配测试:在
config.yaml中将ntasks改为16,mem_per_cpu改为"2G"; - 长期方案:在
scripts/submit_job.sh中添加资源探测逻辑:# 获取 cpuq 最大核数 MAX_CORES=$(scontrol show partition cpuq | grep MaxCPUsPerNode | awk '{print $3}') if [ "$NTASKS" -gt "$MAX_CORES" ]; then echo "Warning: requested $NTASKS cores > queue limit $MAX_CORES, capping..." NTASKS=$MAX_CORES fi
4.3 现象:job_001/md.log中出现Fatal error: Too many LINCS warnings,任务在 Step 12 就崩溃
原因:LINCSD 的约束容差(constraints = h-bonds)与初始结构冲突,常见于 packmol 生成的溶剂盒未充分能量最小化。
解决:
- 在
templates/em.mdp(能量最小化模板)中强化约束:; 原配置可能只有 500 步,改为 2000 步并收紧公差 nsteps = 2000 emtol = 1000.0 ; kJ/mol/nm,原值常为 1000.0,此处保持 constraints = h-bonds constraint_algorithm = lincs lincs_iter = 2 ; 增加迭代次数 lincs_order = 4 ; 提高精度阶数 - 强制在
generate_inputs.py中插入 EM 步骤:修改config.yaml的stages字段,确保em在md之前执行。
4.4 现象:parse_log.sh输出的summary.csv中temp列全为0.0
原因:GROMACS 日志格式变更。新版 GROMACS(2022+)默认关闭tcoupl的详细输出,md.log中不再打印Temperature行。
解决:
- 在
templates/md.mdp中显式开启:; 在输出控制部分添加 tcoupl = v-rescale tc-grps = System tau-t = 0.1 ref-t = 298 ; 关键修复:强制输出温度统计 nstcalcenergy = 100 ; 每 100 步计算能量 nstenergy = 100 ; 每 100 步写入 .edr nstlog = 100 ; 每 100 步写入 .log(确保 Temperature 行存在) - 或改用
gmx energy从.edr提取:scripts/parse_log.sh中将grep "Temperature" md.log替换为:gmx energy -f job_001/md.edr -o /tmp/temp.xvg << EOF Temperature EOF # 再用 awk 提取最后一列 tail -n +18 /tmp/temp.xvg | awk '{print $NF}' > job_001/temp.csv
4.5 现象:monitor_jobs.sh显示COMPLETED,但job_001/summary.csv为空
原因:任务虽完成,但gmx mdrun因磁盘满(No space left on device)静默失败,.edr未生成,parse_log.sh因找不到文件直接退出。
解决:
- 在
submit_job.sh结尾添加磁盘检查:# 提交前检查 home 目录剩余空间(GROMACS 临时文件巨大) FREE_SPACE=$(df ~ | tail -n 1 | awk '{print $4}') if [ "$FREE_SPACE" -lt 10000000 ]; then # <10GB echo "ERROR: Less than 10GB free in home, aborting job" exit 1 fi - 在
parse_log.sh中增加.edr存在性校验:if [ ! -f "job_001/md.edr" ]; then echo "ERROR: md.edr missing for job_001, check mdrun log" echo "job_001,MISSING_EDR,0,0,0" >> all_jobs/summary.csv exit 1 fi
5. 进阶技巧:用失败日志反向定位力场缺陷(不止于跑通,更要读懂失败)
5.1 构建「失败模式」数据库:把报错文本映射到物理原因
批量任务的价值不仅在于成功案例,更在于失败样本。我们发现,超过 68% 的gmx mdrun崩溃可归为三类物理异常,只需解析md.log开头 50 行即可预判:
| 日志关键词 | 物理含义 | 应对动作 |
|---|---|---|
Distance between particles is too small | 原子重叠(建模缺陷) | 回退到packmol阶段,增大distance_step参数 |
Potential Energy too large | 力场参数冲突(如电荷过大) | 检查ligand.itp中[ atoms ]的charge值,用acpype -c gas重算电荷 |
LINCS WARNING: relative constraint deviation after LINCS | 键长约束发散(积分步长过大) | 降低dt至0.001,或改用berendsen压控替代Parrinello-Rahman |
实现自动化识别,只需在scripts/analyze_failures.py中加入:
import re import pandas as pd def classify_failure(log_path): with open(log_path) as f: head = f.readlines()[:50] # 只读前 50 行 log_text = "".join(head) if re.search(r"Distance between particles is too small", log_text): return "ATOM_OVERLAP" elif re.search(r"Potential Energy too large", log_text): return "CHARGE_CONFLICT" elif re.search(r"LINCS WARNING", log_text): return "CONSTRAINT_DIVERGE" else: return "UNKNOWN" # 批量分析所有失败任务 failed_jobs = [j for j in os.listdir("jobs") if os.path.exists(f"jobs/{j}/md.log")] results = [] for job in failed_jobs: log_path = f"jobs/{job}/md.log" if "Fatal error" in open(log_path).read(): results.append({"job": job, "category": classify_failure(log_path)}) pd.DataFrame(results).to_csv("failure_taxonomy.csv", index=False)运行后生成failure_taxonomy.csv,你就能一眼看出:本次 200 个任务中,12 个失败源于原子重叠(需优化 packmol 参数),3 个源于电荷冲突(需重跑 acpype),这比盲目重跑全部任务高效十倍。
5.2 用「失败任务」反哺建模参数优化:一个闭环实验设计
某次批量运行中,我们发现ligand_smiles: "CC(=O)O"(乙酸)在 310K 下全部崩溃,但 298K 正常。直觉是高温加剧了羧基质子离解,而 AMBER99SB-ILDN 力场未覆盖此状态。验证思路:
- 从失败任务中提取
job_001/conf.gro(崩溃前最后结构); - 用
pdb2pqr重新质子化:pdb2pqr --ff AMBER --ph 7.4 job_001/conf.gro job_001/protonated.pqr; - 用
acpype为新结构生成拓扑:acpype -i job_001/protonated.pqr -b job_001/protonated; - 将新
protonated.top替换原topol.top,重新提交job_001。
实操细节:
pdb2pqr需提前conda install -c conda-forge pdb2pqr;acpype生成的protonated_GMX.top要重命名为topol.top并替换#include路径;为避免污染,建议在jobs/job_001/下新建revised/子目录存放新文件。
5.3 为长期项目建立「参数快照」机制:避免复现不可追溯
最痛的教训是:三个月后想复现某次成功结果,却发现config.yaml被改过三次,templates/md.mdp也更新了,连gromacs --version都升级了。我们的解决方案是:
- 每次
generate_inputs.py运行时,自动生成jobs/job_001/SNAPSHOT/目录,包含:config_used.yaml(当时读取的完整配置)template_rendered.mdp(实际生成的 mdp,含所有 Jinja2 变量值)env_info.txt(gmx --version,python --version,uname -a)
- 在
aggregate_results.py中,将SNAPSHOT路径写入summary.csv的snapshot_dir列。
这样,未来任何一行数据都能回溯到精确的软硬件环境。从那以后我每次启动批量任务,都强制python scripts/generate_inputs.py --snapshot,哪怕多花 2 秒——因为没有快照的 MD 结果,就像没有 timestamp 的日志,永远无法证伪。
希望帮到你。
本文还有配套的精品资源,点击获取