Post

BaneTask:把量子化学项目从任务生成一路管到结果分析

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、展开任务、生成输入和 artifact
  • btrun:执行与调度
  • 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
This post is licensed under CC BY 4.0 by the author.
Total hits!