四、输入文件结构:分子拓扑、力场参数、模拟参数

说实话,FEP 计算真正“跑起来”之前,最容易被忽略的恰恰是那几份输入文件。你想想看,拓扑、力场、模拟参数,这三样东西就像三根柱子,少一根整个计算就塌了。我在项目中接手过不少“跑不出来”的案例,十有八九是输入文件版本出了乱子。所以这章咱们好好捋一捋,到底每份文件里装着什么,版本控制又该盯住哪些细节。

4.1 分子拓扑文件

分子拓扑文件,说白了就是告诉程序“这个分子长什么样”。它定义原子编号、键的连接、角度、二面角、等价原子、电荷等。常见的文件后缀是 .top 或 .itp。GROMACS 里通常用 .itp 作为包含片段,用 .top 作为主拓扑。

我个人习惯把每个配体或蛋白的拓扑单独写成一个 .itp,再用 #include 合进主拓扑。这样版本控制里每个配体的变更记录就清晰很多——你改了一个基团的电荷,只需要看那个 .itp 的 diff 就够了。

来看一个简单的配体拓扑片段:

[ moleculetype ]
; name            nrexcl
MOL                 3

[ atoms ]
;   nr  type  resnr residue  atom   cgnr     charge    mass
     1  opls_135   1   MOL      C1      1      0.000   12.011
     2  opls_140   1   MOL      H1      2      0.060    1.008
     3  opls_140   1   MOL      H2      3      0.060    1.008
     4  opls_140   1   MOL      H3      4      0.060    1.008

[ bonds ]
;  ai   aj funct   r           k
    1    2    1   0.1090   284512.0
    1    3    1   0.1090   284512.0
    1    4    1   0.1090   284512.0

这里每个原子的类型(比如 opls_135)直接对应着力场参数文件里的定义。一旦改了原子类型,力场参数也必须跟着换。嗯,这就是版本控制里最容易踩坑的地方——改拓扑忘了改力场。

曾经踩过的坑:有一次我改了一个配体的二面角类型,顺手在 .itp 里把原子类型从 opls_135 换成了 opls_136。结果跑能量极小化时报错,说是力场里找不到参数。查了半小时才意识到力场文件还是旧版。从那以后我每次修改拓扑都会同步检查力场参数的版本。

4.2 力场参数文件

力场参数文件通常是一堆 .itp 或 .dat 的集合,定义了原子间的非键相互作用参数(σ、ε)、键长平衡值、键角平衡值、二面角势垒等。不同的力场(AMBER、CHARMM、OPLS)文件结构差异很大。

举个例子,你可能会看到一个 OPLS 力场目录是这样的:

ff_opls/
  ffoplsaa.itp        # 主力场文件
  ffoplsaanb.itp      # 非键参数
  ffoplsaabon.itp     # 键参数
  ffoplsaa.atp        # 原子类型定义
  ...

这里最关键的其实是各个文件的 [ defaults ] 段和 [ atomtypes ] 段。一旦组合规则改成了 1-4 作用方式,或者把 fudgeLJ 从 0.5 改成了 0.0,计算结果会产生明显差异。我在项目中就遇到过两次 FEP 结果因为力场版本不一致而无法复现的情况——一次是用了不同的 fudgeLJ 值,另一次是二面角参数被误改成 CHARMM 风格。

力场类型 常见文件后缀 版本控制要点
AMBER .prm, .frcmod 注意补充参数文件是否对应同一力场版本
CHARMM .prm, .rtf 键参数使用多个段,检查原子类型名称一致性
OPLS .itp, .atp 组合规则和1-4作用矩阵极易被修改

小技巧:我习惯在仓库里放一个 ff_version.txt 文件,专门记录每个力场文件的来源和版本号(比如“OPLS-AA 2023-05-31”)。这样即使文件内容没变,也能知道它来自哪个 release。

4.3 模拟参数文件

模拟参数文件通常以 .mdp 结尾(GROMACS 常用),它控制模拟的“软条件”:温度、压力、步长、约束算法、周期性、能量组等。你会发现同一个分子体系,换一个 .mdp 能跑出截然不同的结果。版本控制里,.mdp 文件的每一次变更都应该被严格记录。

来看看一个典型的平衡阶段 .mdp:

define                   = -DPOSRES
integrator               = md
nsteps                   = 50000        ; 100 ps at 2 fs
dt                       = 0.002
nstxout                  = 500          ; output every 1 ps
nstenergy                = 500
nstlist                  = 10
cutoff-scheme            = Verlet
rcoulomb                 = 1.0
rvdw                     = 1.0
tcoupl                   = v-rescale
tc-grps                  = System
tau-t                    = 0.1
ref-t                    = 298.15
Pcoupl                   = berendsen
pcoupltype               = isotropic
tau-p                    = 1.0
ref-p                    = 1.0
compressibility          = 4.5e-5
gen-vel                  = yes
gen-temp                 = 298.15

注意这里的 ref-t、tau-t、dt 和 nsteps。你改一个数字,就需要重新平衡或生产。我见过有人把 dt 从 0.002 改成 0.004,结果模拟直接炸掉——因为约束算法来不及处理氢原子振动。版本控制里如果只改了这一个数字,提交信息要写清楚为什么改。

4.4 版本控制中的结构化组织

把上面三份文件组合进同一个仓库时,目录结构的设计直接影响可追踪性。我个人推荐这样的树形组织:

fep_project/
  topology/
    protein.itp
    ligand_a.itp
    ligand_b.itp
    topol.top
  ff/
    ff_opls/
      ffoplsaa.itp
      ffoplsaanb.itp
    ff_charmm36/
      ...
  mdp/
    em.mdp
    npt_eq.mdp
    prod.mdp
  scripts/
    prep_fep.py
  analysis/
    ...

你看,拓扑、力场、参数三个目录清晰分离。每个目录下的文件都可以独立做版本控制。比如你为了测试新力场,可以新开一个 ff_new 目录,然后在 topol.top 里用 #include 切换。切换前先 commit 一次,切换后再 commit,这样回溯时你就能看到所有的变更路径。

核心原则:任何一个文件内容的变动,都应该对应一个单独的 commit。不要攒一堆改动再一次性提交——那样你根本不知道是哪个参数导致的结果变化。

最后,补上一张这章提到的输入文件结构关系图。它帮你快速理解三份文件如何构成完整的模拟输入,以及版本控制怎么把它们串起来。

输入文件结构关系与版本控制 分子拓扑文件 .top / .itp 力场参数文件 ff*.itp / .prm 模拟参数文件 .mdp 完整的模拟输入 拓扑 + 力场 + 参数 版本控制仓库 git 历史 / 标签 计算运行 FEP 结果 虚线表示版本控制管理关系,实线表示数据流

好了,这三类输入文件的结构和版本控制要点就是这样。你下次遇到“模拟跑不完”或者“结果不对劲”的时候,不妨先回头查查这些文件在仓库里的 commit 历史——多半能找到线索。