用 tleap 给体系加盐时,经常会看到这样一个公式:离子数 = 0.0187 × 浓度 × 水分子数。这个 0.0187 看起来像个 magic number,其实它有很清楚的物理来源。这篇短文把它的来历、用法和注意事项整理一下。
N_ion = 0.0187 × C × N_water
| 符号 | 含义 |
|---|---|
C |
目标盐浓度,单位 mol/L(如 0.15) |
N_water |
体系中的水分子数 |
N_ion |
每种离子(如 Na⁺、Cl⁻)各需加入的个数 |
0.0187 的含义是每个水分子平均占据的体积,换算成 L/mol,也就是 1/[H₂O]。
它最早来自 VMD 的 autoionize 插件。插件作者采用的是早年 NAMD2 + CHARMM22(TIP3P 水)模拟得到的平衡水密度,约每个水分子 31.05 ų:
31.05 ų × 6.022×10²³ mol⁻¹ = 1.870×10²⁵ ų/mol = 0.01870 L/mol
对比几种取法:
| 来源 | 系数 | 对应水浓度 |
|---|---|---|
| 真实纯水(摩尔质量 / 密度) | 0.01807 | ≈ 55.3 M |
| TIP3P 水密度 | ≈ 0.0183 | ≈ 54.6 M |
| autoionize 采用值 | 0.0187 | ≈ 53.5 M |
差别只有 2%~3%。换算到离子个数上通常只差零点几个,取整后基本没有影响,所以换成 OPC、TIP4P-Ew 等水模型时也没必要改这个系数。
如果直接用”盒子体积 × 浓度”,溶质(蛋白)占据的体积也被算进去了,结果离子偏多。用水分子数相当于只统计溶剂体积,更接近”溶液里的盐浓度”这个定义。
以 Amber 官方 tutorial 的一个体系为例(截角八面体盒子 208141.8 ų,加入 5569 个水,目标 0.15 M NaCl):
| 算法 | 计算 | 每种离子个数 |
|---|---|---|
| 盒子体积 | 208141.8 × 0.15 × 6.022×10⁻⁴ | ≈ 19 |
| 水分子数 | 0.0187 × 0.15 × 5569 | ≈ 16 |
小盒子、大蛋白的体系里,两种算法差别会更明显。这也是不同教程算出来的数字经常对不上的原因。
盐离子是在中和电荷之外额外加的。典型流程:先中和,再按公式加盐。
source leaprc.protein.ff19SB
source leaprc.water.opc
mol = loadpdb protein.pdb
solvateOct mol OPCBOX 10.0
# 第一步:中和(0 表示让 tleap 自动算个数)
addIons mol Na+ 0
addIons mol Cl- 0
# 第二步:按 0.0187 × C × N_water 算出 N,再成对加入
# 例如 N_water = 5569, C = 0.15 → N ≈ 16
addIonsRand mol Na+ 16 Cl- 16
saveamberparm mol system.prmtop system.inpcrd
quit
N_water 可以直接从 solvateOct 输出的 Added XXXX residues 读到。加离子时会替换掉少量水,这点误差可以忽略。
对净电荷很大的体系(核酸、高电荷蛋白),”先中和、再按体积加盐”会让体相盐浓度偏离目标值:中和用的反离子大多聚集在溶质周围,并不贡献体相浓度。
这种情况可以考虑 SLTCAP 方法(Schmit et al., J. Chem. Theory Comput. 2018),它根据溶质净电荷和目标体相浓度,同时给出阳离子和阴离子的个数。Amber 官方 tutorial 也提到了 Matias Machado 在邮件列表中给出的类似改进算法。
完