BaneTask:把量子化学项目从任务生成一路管到结果分析
前言
笔者初学计算时常常觉得,量子化学计算的步骤实在太多:前一个任务跑完,要取结构、改输入、交下一个任务,最后还得挨个翻日志、做后处理。有没有办法写一个脚本,把整套流程一次做完?后来,笔者借助 AI 完成了一个简单的工作流脚本,用来自动做结构优化、激发态优化和高精度单点,第一次体会到了自动化的甜头,从此便在这条路上一去不返。
不过,工作流脚本很快遇到了瓶颈。任务只要稍微一变,脚本就得大改;脚本一多,还得记住哪个脚本对应哪套流程。笔者没什么代码基础,当时看 bash 变量都头晕,换个泛函要手动一个个改。于是笔者开始想:bash 脚本里那一大堆 shell 命令其实与具体任务无关,真正需要经常改的只有方法、基组、结构来源和任务顺序。那能不能把笔者看不懂的 shell 全部藏起来,只留下任务本身?
说干就干,笔者让 AI 按照个人习惯搓出了一个 Python 项目 tasker,这也是 BaneTask 的前身。tasker 服役了很久,但不是因为它架构多好、功能多强。相反,它只是一个很不成熟的 demo,不仅经常出问题,还被早期思路束缚,新需求一来就容易卡住。可尝过自动化的甜头后,笔者已经没有办法再回去手动提交任务了。每当 tasker 满足不了需求,笔者就往里面硬塞功能,缝缝补补用了半年,最后成了究极矢山,稍微动一块就可能崩全盘。
当时笔者能使用的服务器还是 CentOS 7,系统 Python 是 2.7,conda 启动要卡十来秒(那时不懂事,啥垃圾敢都往环境里塞)。把 tasker 丢给 AI 后,AI 根据笔者大量关于启动速度的抱怨,建议用 C++ 重构。现在想想属于是被坑了:这个项目又不做重计算,继续用 Python 本来能省下日后自己从头写一套类 cclib 结果库的麻烦。但总之,BaneTask 1 就这样诞生了。
BaneTask 1 并没有立刻接班。旧 tasker 虽然是矢山,好歹已经跑得比较稳定;新项目看起来有光明的未来,用起来却得频繁救火。真正的转折出现在笔者开始图谋自动提取结果。tasker作为python项目,自然使用了cclib,但是在那老旧的服务器上加载环境并提取一个波长居然能用1分钟,使用体验极其糟糕。后来笔者让 AI 对着g16日志写了个简单的 C++ 提取器(banelib的前身),才发现BaneTask是真的快!这个体验最终促使笔者放弃 tasker,全面转向 BaneTask。
BaneTask 1 依然背着不少早期包袱。直到 Claude 4 发布后,笔者把整个项目交给它重组,删掉大量过时逻辑,重新整理 .bt 文件结构,换了大版本号,BaneTask 2 就此诞生。
BaneTask2
BaneTask 面向的不是某一个输入文件,而是一串彼此依赖的计算步骤。
一个常见项目可能要先优化结构、做频率检查,再换程序或方法做高精度单点,接着完成波函数分析、结果汇总和绘图。手写脚本当然也能完成这些事,但随着分支、补跑和批量 case 增加,脚本很容易被目录名、判断语句和提交命令淹没。
BaneTask 把这些规则集中写进 .bt 或 .projbt:
1
2
3
4
5
结构文件 + .bt / .projbt
-> banetask 展开任务并生成输入、命令和 .bwrk
-> btrun 在本地或调度系统上执行 workflow
-> btproc 提取规范结果
-> btdb / @derive / @plot 查询、计算、导表和出图
其中 .bt 通常描述一个 case,.projbt 用于组织一批共享同一套流程的 case。Gaussian、ORCA 等量子化学程序仍然负责真正的电子结构计算;而 BaneTask 则负责把结构、输入、依赖、资源、执行和结果接成一条可以复用的链。
写的是任务,不是提交脚本
下面这份文件先用 Gaussian 做优化和频率计算,再用 ORCA 基于优化后的结构做单点。只有上游正常完成且没有虚频时,sp 才会生成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
autorun: false
project: demo
define:
method: B3LYP
basis: 6-31G(d)
$opt
%source origin
%program gaussian
%control
totcore 16
totmem 64000
%keywords "opt freq [method]/[basis]"
%plot convergence conv
x step.ordinal label "Step"
y step.energy label "Energy" unit hartree
y step.convergence(max_force) label "Max force"
y step.convergence(rms_force) label "RMS force"
$sp
%source opt
%program orca
%control
totcore 32
totmem 96000
%keywords "wB97M-V def2-TZVP def2/J RIJCOSX TightSCF"
%when
= 0
@derive summary
let E = energy(sp)
emit final_energy = E
@end
任务之间的关系直接写在 %source 和 %when 里,不需要自己拼路径,也不需要在 shell 中轮询日志。运行时只需要:
1
2
banetask demo.bt
btrun kick --backend local --path .
计算结束后再次运行 banetask demo.bt(在autorun任务中通常是自动进行的),已经满足依赖的 @derive 和 %plot 会读取现有结果,生成派生结果和收敛图。任务没有变化时,已有 artifact 会按 fingerprint 判断是否需要重算或重画。
这就是笔者最初想要的效果:日常修改集中在方法、基组、资源、来源和条件上,而不是散落在几十行提交脚本里。
批量任务
如果要比较多种方法、基组或溶剂,最笨的办法是复制出一堆任务块,然后祈祷自己没有漏改某一行。BaneTask 的 matrix: 会直接做组合展开:
1
2
3
matrix:
method: B3LYP, CAM-B3LYP, wB97XD
basis: def2-SVP, def2-TZVP
展开后,每个变体都有独立任务名和元数据;%source、%when、guess 来源和绘图引用会跟着改写。需要一一配对的方法和基组,也可以用 map:来使数组按照索引匹配,给每个泛函用最合适的DFT-D校正。
更大的流程可以继续拆分。&INCLUDE 能并入任务文件或局部片段,@foreach / @foreach? 可以根据 YAML 集合或提取出的结构帧批量生成任务。模板不必复制到每个项目里,公共方法和后处理习惯也能集中维护。
项目初始化则可以交给 btc。给它一批结构文件,它可以生成单 case 的 .bt,也可以直接铺出带 .projbt 的多 case 项目目录。
条件分支
BaneTask 的条件不只会判断文件在不在。%when 可以检查任务是否完成,也可以读取上游 .bane.result.kv 中的结果:
1
2
3
4
5
6
7
8
$ts_refine
%source ts_guess
%program gaussian
%when
completed ts_guess
= 1
< -500
%keywords "opt=(ts,calcfc) B3LYP/6-31G(d)"
这类写法很适合过渡态筛选、频率检查、激发态分流和批量补跑。上游日志更新后,快照过旧或缺失时,BaneTask 会尝试重新提取结果,再判断下游是否应该进入当前计划。
任务因此不再只是按固定顺序向下执行。计算结果本身也能决定后面走哪条路。
从本地工作站到计算集群
生成输入文件只是前半段。每个任务还会生成 .bwrk workflow,保存资源请求、执行命令、工作目录和前后处理步骤,再由 btrun 负责提交。
btrun 当前支持本地文件队列、Slurm、SGE 和 PBS。服务器较多时,可以用 profile、queue 和 route 描述不同执行目标,让它根据核数、内存和 GPU 请求选择合适的队列;远程 profile 还可以通过 SSH/rsync 提交并取回结果。
常用入口很少:
1
2
3
4
btrun kick ./project
btrun submit --path ./project
btrun render --path ./project --explain
btrun queue list
render 只渲染原生提交脚本,不真正提交,适合先检查资源和调度命令。btrun task 也能绕过 .bt,直接按照环境配置提交已有的 Gaussian、ORCA 等输入文件。
程序环境并没有被硬编码进 BaneTask。每类程序通过 .conf 描述输入后缀、输出后缀、环境加载方式和实际运行命令。换服务器时,通常改环境配置文件即可。
结果处理
早期版本最关心的是如何把任务交出去;现在的 BaneTask 同样关心任务跑完以后怎么用结果。
btproc result 会从量化输出中提取规范结果,保留属性名、单位、序列轴和来源信息。每个任务既有轻量快照供 %when 使用,也会留下标准记录和数据库同步标记。项目目录中常见的结果结构大致如下:
1
2
3
4
5
6
7
8
9
Results/
DB/
snapshots/
records/
queue/
project.db
Derived/
Plots/
Artifacts/
btdb 负责把记录同步进 SQLite。想临时看一眼,可以直接查询:
1
2
btdb case query --path ./caseA \
--sql "SELECT task_name, energy, nimag FROM task_flat_latest"
一批 case 的同名任务也可以横向比较:
1
btdb compare opt sp --metric energy --path ./demo_project
查询结果能输出为表格、CSV、TSV、JSON 或 Markdown。内置报告模板可以生成常用汇总,obsidian export 还能把项目整理为带任务页、结构页和数据附件的笔记目录。
派生计算和绘图
很多后处理并不是“从日志里取一个数”这么简单。反应能需要组合多个任务,活化自由能要叠加单点能和热修正,构象排序还要统一单位、计算相对能量和 Boltzmann 布居。
这些纯结果计算可以写进 @derive:
1
2
3
4
5
6
7
8
9
@derive activation
let G_r = single_point(sp_R) + gibbs_corr(freq_R)
let G_ts = single_point(sp_TS) + gibbs_corr(freq_TS)
require nimag(freq_R) == 0
require nimag(freq_TS) == 1
emit delta_g = convert(G_ts - G_r, "kcal/mol")
@end
Derive DSL 除了标量运算,还能从一组任务生成表,筛选、排序、分组、聚合,并导出 CSV、TSV、JSONL、KV、XYZ、extxyz 等 artifact。当前版本还支持规范能量选择、CBS 外推、谐振零点能和 RRHO 热焓等计算。
需要出图时,可以继续写 @plot 或任务内 %plot。目前已有能垒图、优化收敛图、普通曲线、IR/UV-Vis 光谱、相关图、热图、直方图、柱图、误差棒、前线轨道能级图和激发态能级图。BaneTask 会同时归档实际数据、gnuplot 脚本、Plot DSL 原文和 manifest,图不是一次性的黑箱输出,后面仍然可以追溯和重画。
到这里,一套流程已经不再止于“把计算跑完”。输入、执行、结果表和图片都能由同一份任务文件描述。
当前版本支持的程序和工具
截至BaneTask 2.27,正式支持的输入程序包括:
- Gaussian
- ORCA
- GAMESS
- AMESP
- BDF
- MRCC
- MAPLE
- xTB
script:生成并执行自定义脚本other:通过模板接入其他程序
不同程序的结果解析覆盖范围并不完全相同;有些后端目前以输入生成和 workflow 为主。实际可查询的属性取决于构建时启用的 BaneLib 结果后端。
项目附带的命令行工具也已经分工明确:
banetask:解析 DSL、展开任务、生成输入和 artifactbtrun:执行与调度btdb:数据库同步、查询、报告和比较btool:项目维护、格式化、迁移、展开、解释和静态检查btc:从模板初始化单 case 或多 case 项目btproc:结果、方法、结构轨迹和其他运行期处理
BaneTask 适合谁
如果你偶尔只跑一个输入文件,手动提交可能更省事(btrun可以帮你提交到调度系统哦)。BaneTask 更适合下面这些情况:
- 同一套流程要在很多分子上重复;
- 任务之间有连续的结构和结果依赖;
- 需要根据虚频、能量或收敛状态决定后续分支;
- 经常在 Gaussian、ORCA、xTB 等程序之间传递结构;
- 不想让每个项目都带着一份越改越乱的 shell 脚本;
- 希望计算结束后能直接查询、比较、导表和出图;
- 需要把任务来源、参数和结果长期保留下来,而不是只留一堆日志。
它不是一个开箱即用的在线计算平台。第一次使用仍然需要配置量化程序环境和 btrun 的执行目标;从源码构建还需要 CMake、C++17 编译器和 BaneLib。但这些配置完成后,同一份 .bt 可以在不同机器和项目间反复使用。
快速开始
准备一个结构文件和任务文件:
1
2
3
methane/
methane.xyz
methane.bt
生成并运行:
1
2
banetask methane/methane.bt
btrun kick --backend local --path ./methane
计算完成后同步和查询结果:
1
2
3
btdb case sync --path ./methane
btdb case query --path ./methane \
--sql "SELECT task_name, energy, nimag FROM task_flat_latest"
完整 DSL、运行环境、Derive、Plot 和数据库用法请查看项目手册。
项目地址
- Website: https://bane-dysta.top/software/banetask
- Gitee / Issues: https://gitee.com/bane-dysta/banetask2