03_GROMACS分子动力学实战:从 Factor Xa 输入准备到轨迹分析

对接或序列设计给出的通常只是一张静态结构图。但在水溶液中,侧链会转动,主链会摆动,周围的水和离子也在不断运动。分子动力学模拟(molecular dynamics,MD)要做的,就是从这个静态起点出发,计算体系随时间变化的轨迹。

本章不先铺陈太多概念,而是直接用 Factor Xa 的 PDB 结构跑完一套流程:建拓扑、加水和离子、能量最小化、100 ps NVT、100 ps NPT,最后完成 1 ns 生产模拟 并分析轨迹。操作基于 GROMACS 官方 Factor Xa 教程。

整条流程可以先记成一条主线:

text 复制代码
PDB 坐标 → 拓扑 → 水盒与离子 → 能量最小化 → NVT → NPT → 生产模拟 → 
 ↓		  ↓       ↓           ↓
周期边界处理 → RMSD / RMSF / Rg / 温压密度

Factor Xa 水溶液与前章的 MDM2/p53 对接是两个独立体系。本章的目标是把流程真正跑通,并弄清每个文件和参数的含义。1 ns 足够用来练习操作和读图;如果要研究慢构象变化或结合稳定性,还需要根据问题增加采样时间和独立重复。

1. 确认环境和输入,建立本次运行目录

本机使用 GROMACS 2026.4 的 CUDA 构建,GPU 为 RTX 3060。运行时采用一个 MPI 线程和四个 OpenMP 线程。先进入教材数据根目录,确认程序版本:

bash 复制代码
cd /home/tim/project/ImmuneTherapy/Reports/CADD_AIDD_案例教材
export PATH=/opt/gromacs/2026.4/bin:$PATH
gmx --version

版本输出中应能看到 2026.4 和 GPU support: CUDA。如果 GROMACS 安装在其他位置,修改 PATH 中的目录即可。后文所有 gmx 命令都应使用同一个安装版本。另外,程序"支持 CUDA"只说明具备 GPU 能力,具体哪些计算交给了 GPU,还要在运行日志中确认。

输入文件位于 data/md/md-intro-tutorial-main/data/input/:

  • 1fjs.pdb:Factor Xa 的初始坐标;
  • emin-charmm.mdp:能量最小化参数;
  • nvt-charmm.mdp、npt-charmm.mdp:两个平衡阶段的参数;
  • md-charmm.mdp:生产模拟参数。

本例沿用这套输入中的 CHARMM27 力场和 TIP3P 水模型。先在终端记录教材根目录和本次运行目录,后面会反复用到这两个变量:

bash 复制代码
export AIDD_ROOT="$PWD"
export MD_RUN="$AIDD_ROOT/runs/03_factor_xa_1ns"

下面的 Python 脚本仍然在教材根目录运行。它只准备输入,不启动模拟,主要做四件事:检查文件是否齐全、创建独立运行目录、清理 PDB 中的非蛋白记录,以及复制并修改四个阶段的 MDP 文件。

exist_ok=False 是一个简单但实用的保护:同名目录已经存在时,脚本会直接停止,避免新旧结果混在一起。需要重新跑一轮时,请同时修改脚本中的目录名和终端里的 MD_RUN。

python 复制代码
from pathlib import Path
import re

root = Path.cwd()
source = root / 'data/md/md-intro-tutorial-main/data/input'
work = root / 'runs/03_factor_xa_1ns'
required = ['1fjs.pdb', 'emin-charmm.mdp', 'nvt-charmm.mdp',
            'npt-charmm.mdp', 'md-charmm.mdp']
assert all((source / name).is_file() for name in required), '请检查当前工作目录和输入文件'
work.mkdir(parents=True, exist_ok=False)

pdb = (source / '1fjs.pdb').read_text()
protein = [line for line in pdb.splitlines()
           if not line.startswith(('HETATM', 'CONECT'))]
(work / 'protein.pdb').write_text('\n'.join(protein) + '\n')

def set_value(text, key, value):
    pattern = rf'(?mi)^\s*{re.escape(key)}\s*=.*$'
    line = f'{key} = {value}'
    return re.sub(pattern, line, text) if re.search(pattern, text) else text + '\n' + line + '\n'

def value(text, key):
    return re.search(rf'(?mi)^\s*{re.escape(key)}\s*=\s*([^;\n]+)', text).group(1).strip()

for stage, filename in [('em', 'emin-charmm.mdp'), ('nvt', 'nvt-charmm.mdp'),
                        ('npt', 'npt-charmm.mdp'), ('md', 'md-charmm.mdp')]:
    text = (source / filename).read_text()
    if stage != 'em':
        text = set_value(text, 'dt', '0.002')
        text = set_value(text, 'nsteps', 500000 if stage == 'md' else 50000)
    if stage == 'nvt':
        text = set_value(text, 'gen_seed', 20261010)
    if stage == 'md':
        text = set_value(text, 'nstxout-compressed', 2500)
    (work / f'{stage}.mdp').write_text(text)
    if stage != 'em':
        dt = float(value(text, 'dt'))
        steps = int(value(text, 'nsteps'))
        print(f'{stage}: dt={dt:g} ps, nsteps={steps}, duration={dt * steps:g} ps')

ions = (source / 'emin-charmm.mdp').read_text()
ions = set_value(ions, 'coulombtype', 'Cut-off')
ions = set_value(ions, 'nsteps', 0)
(work / 'ions.mdp').write_text(ions)
print('MD frame interval: 5 ps; expected frames: 201')
print('Prepared: runs/03_factor_xa_1ns')

脚本中的时间设置很直观:dt=0.002 表示每步 0.002 ps,即 2 fs;NVT 和 NPT 各跑 50,000 步,所以都是 100 ps;生产段跑 500,000 步,对应 1000 ps,也就是 1 ns。生产轨迹每 2500 步保存一次,因此相邻坐标帧间隔为 5 ps。

终端输出为:

text 复制代码
nvt: dt=0.002 ps, nsteps=50000, duration=100 ps
npt: dt=0.002 ps, nsteps=50000, duration=100 ps
md: dt=0.002 ps, nsteps=500000, duration=1000 ps
MD frame interval: 5 ps; expected frames: 201
Prepared: runs/03_factor_xa_1ns

这里的 PDB 清理只适用于本案例:删除晶体水、配体等 HETATM 记录以及相应的 CONECT 记录,随后重新加溶剂。如果换成依赖金属、辅因子或配体的体系,必须先判断哪些组分应该保留、如何为它们建立参数,不能直接照搬这段删除逻辑。

接下来进入运行目录。从第 2 节到第 10 节,GROMACS 命令都在这里执行:

bash 复制代码
cd "$MD_RUN"

2. 从蛋白坐标生成拓扑

PDB 文件主要描述"原子在哪里",而模拟还需要知道原子的化学身份、电荷、键连关系以及各种相互作用参数。pdb2gmx 的作用,就是把坐标与所选力场结合起来,生成 GROMACS 后续计算需要的坐标和拓扑。

bash 复制代码
gmx pdb2gmx -f protein.pdb -o processed.gro -p topol.top \
  -ff charmm27 -water tip3p -ignh

几个选项分别控制:

  • -f:输入坐标;-o:处理后的坐标;-p:主拓扑文件;
  • -ff charmm27:使用 CHARMM27 力场;
  • -water tip3p:使用 TIP3P 水模型;
  • -ignh:忽略 PDB 中原有的氢,按力场规则重新生成,减少氢原子命名不一致带来的问题。

运行结束后,应得到 processed.gro、topol.top,以及主拓扑引用的链拓扑和位置限制文件。后续计算会一起读取这些文件,因此移动或备份时不能只带走 topol.top,还要保留其中 #include 引用的全部文件。

终端里的末端、残基和二硫键处理信息值得看一遍。本例沿用官方输入的默认识别方式,但这并不代表 pdb2gmx 已经替我们验证了所有残基在实验条件下的质子化状态。遇到缺失原子、不支持的残基或异常连接,应当在这一步解决。

力场通过键长、键角、扭转、静电和范德华等项计算势能 U U U,再由 F = − ∇ U F=-\nabla U F=−∇U 得到原子受力。动力学积分据此推进坐标和速度,连续保存的坐标帧就形成了轨迹。因此,拓扑或化学身份一旦出错,后面的轨迹再长也没有意义。

本例的完整 MDP 还规定了积分条件:PME 处理长程静电,短程静电与范德华截断均为 1.2 nm,范德华力在 1.0--1.2 nm 范围采用 Force-switch,含氢键长用 h-bonds 约束。第 1 节已经复制了完整文件,后文展示的参数片段只是为了讲解,不能单独保存成一个新 MDP 来使用。

3. 定义模拟盒,加入水和离子

3.1 把蛋白放入周期性水盒

bash 复制代码
gmx editconf -f processed.gro -o box.gro -c -d 1.0 -bt dodecahedron
gmx solvate -cp box.gro -cs spc216.gro -o solv.gro -p topol.top

第一条命令把蛋白居中,并建立十二面体模拟盒。-d 1.0 表示初始蛋白与盒边之间至少留出 1.0 nm。这里的盒子定义的是模拟空间,与分子对接中限定搜索范围的盒子不是一回事。

周期性边界会把盒子在空间中重复,从而用有限体系近似体相环境。它的副作用是,轨迹中的分子有时会显示在盒子两侧,所以第 8 节还要专门处理周期边界。

第二条命令用 spc216.gro 中的水坐标填充盒子,去掉与蛋白重叠的水,并更新 topol.top 中的水分子计数。spc216.gro 在这里只是坐标模板,水分子真正采用什么相互作用参数,仍由前面选择的 TIP3P 拓扑决定。

3.2 中和电荷并设置 0.15 mol/L 盐条件

bash 复制代码
gmx grompp -f ions.mdp -c solv.gro -p topol.top -o ions.tpr
gmx genion -s ions.tpr -o solv_ions.gro -p topol.top \
  -pname NA -nname CL -neutral -conc 0.15 -seed 20261010

grompp 会把参数、坐标和拓扑编译成 TPR 运行输入。这里的 ions.mdp 将步数设为 0,只是为了给加离子步骤准备 TPR,并不真正进行动力学计算。

运行 genion 后,程序会提示选择替换对象,输入组名 SOL,让钠、氯离子替换部分水分子。几个关键选项的含义是:

  • -pname NA、-nname CL:分别指定阳离子和阴离子;
  • -neutral:加入足够的离子来中和体系净电荷;
  • -conc 0.15:设置 0.15 mol/L 的盐浓度条件;
  • -seed 20261010:固定随机种子,便于重复准备过程。

由于还要兼顾电荷中和,而且盒子中的离子只能按整数个加入,最终钠、氯离子数量不必完全相等。本例得到 11,703 个水分子、38 个钠离子和 36 个氯离子,体系共 39,600 个原子。使用同一输入和设置时,可以用这些数字检查是否漏了步骤:原子总数看 solv_ions.gro 第二行,各分子数量看 topol.top 末尾的 [ molecules ]。

4. 能量最小化:先处理不合理的初始接触

bash 复制代码
gmx grompp -f em.mdp -c solv_ions.gro -p topol.top -o em.tpr
gmx mdrun -deffnm em -ntmpi 1 -ntomp 4 -nb gpu -gpu_id 0

刚加完水和离子的体系,可能存在原子距离过近、局部接触不合理等问题。直接开始动力学积分容易产生很大的力,所以先做能量最小化,让坐标落到能量更低、更适合继续计算的位置。

本例 em.mdp 中的几个参数分别是:

  • integrator=steep:使用最速下降法;
  • emtol=1000:最大力小于 1000 kJ·mol⁻¹·nm⁻¹ 时停止;
  • emstep=0.01:优化步长参数;
  • nsteps=-1:沿用输入设置,不预设有限的步数上限。

能量最小化只是在优化坐标,不会生成一段可以按 ps 或 ns 解读的动力学采样。

mdrun 的选项也值得固定下来:-deffnm em 让本阶段文件统一使用 em 前缀;-ntmpi 1 -ntomp 4 指定一个线程 MPI rank 和四个 OpenMP 线程;-nb gpu -gpu_id 0 将短程非键计算交给编号为 0 的 GPU。后文四次 mdrun 都使用这组设置。。GPU 任务分配说明

最小化结束后,先看 em.log 的停止原因。已有运行在 674 步达到停止条件,最大力约为 973.815 kJ·mol⁻¹·nm⁻¹,低于本次设定的 1000。温度、密度以及构象对环境的适应,则交给后续动力学阶段处理。

这一阶段会生成 em.gro、em.edr 和 em.log。其中 em.gro 是 NVT 的初始坐标,后面还会作为 RMSD 分析的参考。日志中出现 LINCS 算法名称并不代表出错;如果出现具体的约束警告,则需要定位相关原子和阶段,检查初始接触、拓扑与时间步。

5. NVT 100 ps:在固定体积下调整温度

NVT 中粒子数、体积和温度受控。本阶段先保持盒子体积不变,让体系在蛋白位置限制的条件下调整到 300 K。前面生成的 nvt.mdp 中,关键设置为:

shell 复制代码
integrator = md
dt = 0.002
nsteps = 50000
define = -DPOSRES
tcoupl = V-rescale
tc-grps = system
tau_t = 1.0
ref_t = 300
pcoupl = no
gen_vel = yes
gen_temp = 300
gen_seed = 20261010

这里只展示与当前阶段最相关的参数,非键计算和约束等其他设置仍以第 1 节生成的完整 MDP 为准。

  • dt=0.002、nsteps=50000:每步 2 fs,共 100 ps;
  • tcoupl=V-rescale、ref_t=300:使用 V-rescale 控温,目标温度 300 K;
  • tau_t=1.0:控温耦合时间常数;
  • pcoupl=no:不做压力耦合,因此盒子体积保持不变;
  • gen_vel=yes、gen_temp=300:按 300 K 对应的速度分布生成初始速度;
  • gen_seed=20261010:固定随机种子,便于复现准备过程。

define=-DPOSRES 会启用拓扑中的蛋白位置限制,让溶剂先在相对受限的蛋白周围调整。位置限制通过势能惩罚偏离参考位置,并不等同于把原子坐标完全冻结。

bash 复制代码
gmx grompp -f nvt.mdp -c em.gro -r em.gro -p topol.top -o nvt.tpr
gmx mdrun -deffnm nvt -ntmpi 1 -ntomp 4 -nb gpu -gpu_id 0
gmx energy -f nvt.edr -o nvt_temperature.xvg

grompp 中,-c em.gro 提供初始坐标,-r em.gro 提供位置限制的参考坐标。两者在这里使用同一个文件,但作用不同。

运行 gmx energy 后选择 Temperature,再输入 0 结束选择。重点看 0--100 ps 的温度曲线是否在目标值附近波动,有没有持续异常漂移;只看一个接近 300 K 的平均值并不够。

完成后,nvt.gro 保存末帧坐标,nvt.cpt 保存继续模拟所需的状态。下一阶段会同时接上这两个文件,而不是只拿一张静态坐标重新生成速度。

6. NPT 100 ps:让盒子体积和密度调整

NPT 在控温的基础上加入压力耦合,让盒子体积和密度有机会继续调整。本例仍使用 2 fs 时间步和 50,000 步,保留蛋白位置限制与 300 K 控温。相对 NVT,主要变化是:

shell 复制代码
continuation = yes
pcoupl = C-rescale
pcoupltype = isotropic
tau_p = 5.0
ref_p = 1.0
compressibility = 4.5e-5
refcoord_scaling = com

这些参数分别说明"怎样接着跑"和"怎样控压":

  • continuation=yes:承接前一阶段的状态,避免重新处理约束初始值;
  • pcoupl=C-rescale:使用 C-rescale 压控;
  • ref_p=1.0:目标压力为 1 bar;
  • tau_p=5.0:压力耦合时间常数,单位 ps;
  • compressibility=4.5e-5:压缩率,单位 bar⁻¹;
  • pcoupltype=isotropic:各方向按相同比例缩放盒子;
  • refcoord_scaling=com:指定压力耦合缩放时,位置限制参考坐标的处理方式。
bash 复制代码
gmx grompp -f npt.mdp -c nvt.gro -r nvt.gro -t nvt.cpt \
  -p topol.top -o npt.tpr
gmx mdrun -deffnm npt -ntmpi 1 -ntomp 4 -nb gpu -gpu_id 0
gmx energy -f npt.edr -o npt_thermodynamics.xvg

这里 -c nvt.gro 是 NVT 的末帧坐标,-r nvt.gro 是位置限制参考,-t nvt.cpt 则把前一阶段的速度和可衔接状态传进来。因此,本阶段不再设置 gen_vel=yes。

提取能量时依次选择 Temperature、Pressure、Density,最后输入 0。读图时要把三条曲线结合起来看:温度是否稳定,密度或体积是否还在持续漂移。小体系的瞬时压力涨落很大,不可能每个时间点都贴着 1 bar。如果密度仍有明显趋势,应先查明原因,必要时延长平衡,而不是因为 100 ps 已经跑完就直接判定平衡充分。

7. 生产模拟 1 ns:开始保存要分析的轨迹

完成温度和压力调整后,进入生产模拟。本段仍采用 2 fs 时间步、300 K V-rescale 控温和 1 bar C-rescale 压控,但总步数增加到 500,000,并移除 define=-DPOSRES,让蛋白不再受位置限制。

第 1 节生成的 md.mdp 应包含以下输出控制:

shell 复制代码
dt = 0.002
nsteps = 500000
continuation = yes
nstxout = 0
nstvout = 0
nstfout = 0
nstlog = 1000
nstenergy = 500
nstxout-compressed = 2500

这里的"计算步长"和"保存间隔"是两回事:GROMACS 每 2 fs 根据原子受力更新一次速度和位置,但不会把每一步都保存下来,只按设定的间隔写入部分结果。

  • nstxout=0、nstvout=0、nstfout=0:不额外输出全精度坐标、速度和力轨迹,避免生成不需要的大文件;不输出并不代表没有计算;
  • nstxout-compressed=2500:每 2500 步写入一次压缩坐标,即每 5 ps 保存一帧;0--1000 ps 加上初始帧,共 201 帧;
  • nstenergy=500:每 500 步保存一次能量,对应 1 ps;
  • nstlog=1000:每 1000 步写一次日志能量信息,对应 2 ps。

因此,坐标和能量的时间网格不同,后续不能按行号把它们强行拼在一起。

bash 复制代码
gmx grompp -f md.mdp -c npt.gro -t npt.cpt -p topol.top -o md.tpr
gmx mdrun -deffnm md -ntmpi 1 -ntomp 4 -nb gpu -gpu_id 0
gmx check -f md.xtc

生产阶段结束后,可以先按用途认一下这些文件:

  • md.tpr:编译后的运行输入;
  • md.xtc:压缩坐标轨迹,是结构分析的主要输入;
  • md.edr:温度、压力、密度等能量记录;
  • md.gro:末帧坐标;
  • md.cpt:续跑所需的检查点;
  • md.log:实际运行与性能日志。

gmx check 应报告轨迹覆盖 0--1000 ps,共 201 帧,相邻帧间隔 5 ps。如果只到中间某个时间点,应先确认计算是否真正结束,再做全时长统计。

运行完成了上述全部阶段,流程耗时约 471 秒。该次生产日志显示,短程非键、PME、坐标更新和约束均使用 GPU。不同机器的运行时间和数值末位可能不同,核对应以阶段时长、原子数、日志和产物是否对应同一套设置为主。

如果生产段意外中断,并且已有可用检查点,可以在同一目录续跑:

bash 复制代码
gmx mdrun -deffnm md -cpi md.cpt -append \
  -ntmpi 1 -ntomp 4 -nb gpu -gpu_id 0

-cpi md.cpt 读取检查点,-append 将新结果追加到已有文件。续跑会沿用原 TPR 的结束步数,适合完成这次被中断的 1 ns 计算;如果想延长总时长,则需要另外更新运行输入。

8. 处理周期边界,准备一致的参考

生产完成后,先别急着算 RMSD。周期边界可能让同一个蛋白在显示时拆到盒子两侧,直接分析或可视化容易得到奇怪结果。下面先把参考结构和轨迹处理成完整、可比较的蛋白坐标:

bash 复制代码
gmx trjconv -s md.tpr -f em.gro -o reference_protein.pdb -pbc cluster
gmx trjconv -s md.tpr -f md.xtc -o whole.xtc -pbc whole
gmx trjconv -s md.tpr -f whole.xtc -o nojump.xtc -pbc nojump
gmx trjconv -s md.tpr -f nojump.xtc -o protein_centered.xtc -center -pbc mol

四条命令依次完成:

  1. 用 -pbc cluster 聚拢 EM 参考结构中的蛋白,生成 reference_protein.pdb;
  2. 用 -pbc whole 恢复轨迹中完整的分子;
  3. 用 -pbc nojump 消除分子跨周期边界造成的帧间跳跃;
  4. 将蛋白居中,并用 -pbc mol 按分子重新放置,得到 protein_centered.xtc。

第一条命令的聚类组和输出组都选择 Protein ;第二、三条的输出组也选择 Protein ;最后一条的居中组与输出组同样选择 Protein 。原始 md.xtc 保留完整体系,处理后的轨迹只保留蛋白,方便后续分析。选择组名比死记菜单编号更可靠,因为不同拓扑中的编号可能变化。

这套分步处理适用于当前体系。换成其他体系时,仍要检查多条链是否保持预期的相对关系;运行过命令不等于坐标一定处理正确。多种变换通常需要分开执行,具体可参阅 trjconv 处理说明。

还要记住参考结构的来源:本章 RMSD 比较的是轨迹与 EM 结构 的差异。md.tpr 中的生产初始坐标来自 NPT 末帧,与 EM 坐标并不相同;如果随手替换 RMSD 命令中的参考,指标的含义也会跟着改变。

9. 从轨迹提取 RMSD、RMSF、Rg 和热力学指标

9.1 RMSD:整体结构相对参考偏移了多少

bash 复制代码
gmx rms -s reference_protein.pdb -f protein_centered.xtc \
  -o rmsd_ca.xvg -tu ps

程序会先询问拟合组,再询问测量组,两次都选择 C-alpha。计算时先消除 Cα 集合的整体平移和转动,再比较对应原子的位置差异。输出第一列是时间(ps),第二列是 RMSD(nm)。

所以,这条曲线回答的是一个很具体的问题:蛋白 Cα 相对最小化后的参考结构偏移了多少。 如果某个时间窗口出现平台,只能说明 RMSD 在这段时间没有持续明显漂移;是否充分收敛,还要结合采样长度、局部重排和其他指标判断。

9.2 RMSF 和 Rg:哪些区域更灵活,整体是否仍然紧凑

bash 复制代码
gmx rmsf -s md.tpr -f protein_centered.xtc -o rmsf_ca.xvg -res
gmx gyrate -s md.tpr -f protein_centered.xtc -o rg.xvg

运行 rmsf 时选择 C-alpha ,-res 表示按残基输出。RMSF 描述拟合后各原子围绕平均位置的波动,适合用来寻找环区、末端等较活跃的区域。它关注的是局部波动,与相对初始结构的 RMSD 不是同一个指标。

多链结构中可能出现重复的残基编号,解释峰值时还要对照链身份。第 10 节使用散点图,就是为了避免把不同链的末端和起点连成一条并不存在的连续曲线。

运行 gyrate 时选择 Protein。默认计算质量加权的回旋半径 Rg,用来描述蛋白原子相对质心的分散程度。输出通常包含总体 Rg 和各轴分量,下面代码读取第二列,也就是总体 Rg。蛋白可以在整体 Rg 基本不变时发生明显的局部重排,因此 Rg 和 RMSD 最好一起看。

9.3 温度、压力和密度:检查生产段的运行状态

bash 复制代码
gmx energy -f md.edr -o thermodynamics.xvg

依次选择 Temperature、Pressure、Density ,最后输入 0。随后查看 XVG 图例,确认三列数值的实际顺序。温度单位为 K,压力为 bar,密度为 kg/m³,时间为 ps。

这里的 md.edr 只包含生产段数据,NVT 和 NPT 的状态已在各自阶段检查。另一个常见误读是把势能下降当成亲和力提高:势能只是当前构象在所用模型下的能量,结合自由能还涉及结合态与游离态的构象、溶剂和熵等贡献,二者不能直接画等号。

10. 读取 XVG,生成 CSV 和分析图

XVG 文件中,# 开头的是注释,@ 开头的是绘图信息,剩下的才是数值。下面的 Python 代码仍在 runs/03_factor_xa_1ns 中运行,需要 NumPy、pandas 和 Matplotlib。

脚本先定义 read_xvg() 统一读取数值,再分别整理结构指标、热力学指标和 RMSF。由于坐标每 5 ps 保存一次、能量每 1 ps 保存一次,代码保留两条时间轴并写成不同 CSV;RMSF 的横轴是残基编号,也单独保存。最后生成一张 2×3 汇总图。

python 复制代码
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

work = Path.cwd()

def read_xvg(name):
    lines = [line.split() for line in (work / name).read_text().splitlines()
             if line.strip() and not line.lstrip().startswith(('#', '@'))]
    a = np.asarray(lines, dtype=float)
    assert a.ndim == 2 and np.isfinite(a).all(), name
    return a

rmsd = read_xvg('rmsd_ca.xvg')
rg = read_xvg('rg.xvg')
rmsf = read_xvg('rmsf_ca.xvg')
therm = read_xvg('thermodynamics.xvg')
assert len(rmsd) == len(rg) and np.allclose(rmsd[:, 0], rg[:, 0])
assert therm.shape[1] == 4, '检查是否仅选择了 Temperature、Pressure、Density'
structural = pd.DataFrame({
    'time_ps': rmsd[:, 0],
    'time_ns': rmsd[:, 0] / 1000,
    'ca_rmsd_to_em_nm': rmsd[:, 1],
    'protein_mass_weighted_rg_nm': rg[:, 1],
})
thermal = pd.DataFrame(therm, columns=[
    'time_ps', 'temperature_K', 'pressure_bar', 'density_kg_m3'])
fluctuation = pd.DataFrame(rmsf[:, :2], columns=['residue_number', 'ca_rmsf_nm'])
structural.to_csv(work / 'structural_metrics.csv', index=False)
thermal.to_csv(work / 'thermodynamics.csv', index=False)
fluctuation.to_csv(work / 'rmsf_ca.csv', index=False)

fig, axes = plt.subplots(2, 3, figsize=(13, 7), constrained_layout=True)
axes[0, 0].plot(structural.time_ns, structural.ca_rmsd_to_em_nm)
axes[0, 0].set(xlabel='Time (ns)', ylabel='CA RMSD to EM (nm)')
axes[0, 1].plot(structural.time_ns, structural.protein_mass_weighted_rg_nm)
axes[0, 1].set(xlabel='Time (ns)', ylabel='Mass-weighted Rg (nm)')
axes[0, 2].scatter(fluctuation.residue_number, fluctuation.ca_rmsf_nm, s=8, alpha=0.7)
axes[0, 2].set(xlabel='Residue number', ylabel='CA RMSF (nm)')
for ax, column, label in zip(axes[1],
                           ['temperature_K', 'pressure_bar', 'density_kg_m3'],
                           ['Temperature (K)', 'Pressure (bar)', 'Density (kg/m3)']):
    ax.plot(thermal.time_ps / 1000, thermal[column])
    ax.set(xlabel='Time (ns)', ylabel=label)
fig.savefig(work / 'trajectory_analysis.png', dpi=180)
plt.close(fig)
print('帧数:', len(structural))
print('时间范围 ps:', structural.time_ps.iloc[0], structural.time_ps.iloc[-1])
print('平均 CA RMSD nm:', round(structural.ca_rmsd_to_em_nm.mean(), 6))
print('平均 Rg nm:', round(structural.protein_mass_weighted_rg_nm.mean(), 6))
print('平均温度 K:', round(thermal.temperature_K.mean(), 3))
print('平均压力 bar:', round(thermal.pressure_bar.mean(), 3))
print('平均密度 kg/m3:', round(thermal.density_kg_m3.mean(), 3))
print('已保存三份 CSV 和 trajectory_analysis.png')

将上述命令用于本章保存的 Debian 轨迹,核对输出如下:

text 复制代码
帧数: 201
时间范围 ps: 0.0 1000.0
平均 CA RMSD nm: 0.103076
平均 Rg nm: 1.889764
平均温度 K: 299.996
平均压力 bar: -0.169
平均密度 kg/m3: 1021.998
已保存三份 CSV 和 trajectory_analysis.png

这些是已有轨迹的输出示例,不要求每次重跑都逐点复现。自己计算时,先核对时长、帧数、单位、选择组和参考结构,再讨论数值差异。本章的 Rg 使用 GROMACS 从 TPR 读取的原子质量计算;与其他软件比较时,也要确认原子质量和选择组一致。

11. 这张图怎么看,结果正常吗?

简单说:**这张图的结果整体正常。作为一条 1 ns 的教学模拟,没有看到明显失稳,但还不能据此宣布"已经充分收敛"。**​

  • RMSD :起初快速上升,随后主要在 0.09--0.115 nm 之间波动,平均约 **0.103 nm(1.03 Å)**。说明蛋白偏离了能量最小化结构,但没有持续发生大幅整体变形。
  • Rg :基本稳定在 1.89 nm 附近,没有持续增大或减小,说明蛋白没有明显逐渐展开或塌缩。
  • RMSF :多数残基约为 0.04--0.10 nm ,少数位置较高,100 位附近最高接近 0.28 nm。多数区域较稳定、局部区域较灵活是常见现象;高峰可能来自环区或链端,需要映射回具体链和结构位置,不能直接视为异常。
  • 温度 :在 296--305 K 之间波动,平均 299.996 K,控温正常。
  • 压力 :正负几百 bar 的瞬时跳动看起来夸张,但对这种规模的原子模拟属于正常现象。平均压力为 −0.169 bar,也没有持续漂移。
  • 密度 :稳定在约 1022 kg/m³,量级合理,没有持续收缩或膨胀。

读这类图时,先看有没有持续上升或下降的趋势,再看围绕什么水平波动,最后才看全区间均值。下面逐图来看。

11.1 上排:蛋白整体结构和局部柔性

左上,Cα RMSD:蛋白相对参考结构变了多少

横轴是时间,纵轴是当前帧的 Cα 与 EM 参考结构之间的 RMSD。数值越大,说明整体 Cα 位置偏离参考越多。

这条曲线在开始时迅速升到约 0.09--0.10 nm,随后主要在约 0.09--0.115 nm 之间波动,全区间均值为 0.103076 nm,也就是约 1.03 Å。初始上升表示蛋白离开能量最小化后的局部构象,在动力学过程中调整到更偏好的状态;后面没有持续向上冲,而是围绕一定水平上下波动。

因此,这张 RMSD 图没有显示蛋白在 1 ns 内持续发生大幅整体变形,属于这条短轨迹中可以接受的表现。

真正需要警惕的是 RMSD 长时间单向上升、突然出现很大的台阶,或者多次跳变后不再恢复。遇到这些情况,应先检查周期边界、参考结构和原子映射,再结合轨迹动画判断是否存在真实的构象变化。反过来,曲线出现平台也不能单独证明体系已经收敛,因为我们只观察了 1 ns,而且只看了一个整体指标。

中上,回旋半径 Rg:蛋白有没有逐渐展开或收缩

Rg 反映蛋白原子相对质心的分散程度。对同一个蛋白来说,Rg 持续增大可能提示整体变得更松散,持续减小则可能提示收缩。

图中 Rg 在最初的短暂调整后,主要围绕 1.89 nm 波动,平均为 1.889764 nm,没有明显的长期上升或下降趋势。这与 RMSD 的结果相互印证:蛋白会运动和局部调整,但没有看到整体持续展开或塌缩。 对这条 1 ns 轨迹来说,这是正常且令人放心的表现。

不过,Rg 稳定不代表内部构象完全不变。蛋白可以在整体大小基本不变的情况下发生局部重排,所以还要看 RMSD、RMSF 和具体结构。

右上,Cα RMSF:哪些残基更灵活

RMSF 的横轴是残基编号,纵轴表示该残基的 Cα 在拟合后围绕平均位置波动的大小。点越高,说明这个残基越灵活;它不再是一条时间曲线。

图中多数残基的 RMSF 大致在 0.04--0.10 nm,少数区域达到 0.10--0.20 nm,100 位附近还有一个接近 0.28 nm 的突出峰。这种分布并不反常:折叠较好的核心区域通常波动较小,环区、末端和部分表面区域通常更灵活。多数残基较低、少数局部区域较高,正好符合这种预期。

所以,高峰本身不是报错,也不能直接解释为"这个残基不稳定"或"这里一定很重要"。 下一步应把高峰残基映射回具体链和结构位置,看看它是否位于环区、链端或界面,并用轨迹可视化确认。由于多链结构中的残基编号可能重复,只凭这张图上的"100"还不能确定它究竟是哪条链的哪个残基。如果整个蛋白大量残基都出现很高的 RMSF,或者高峰伴随 Rg 持续增长,才更需要怀疑整体失稳或分析处理有问题。

11.2 下排:模拟环境是否稳定

左下,温度:是否围绕 300 K 波动

温度基本在 296--305 K 之间快速涨落,平均为 299.996 K,没有持续升高或降低。这就是控温正常工作时常见的图形。 瞬时温度本来就由原子动能计算,不可能每一帧都精确等于 300 K;这里应关注平均水平和长期趋势,而不是每一个尖峰。

中下,压力:几百 bar 的上下跳动是不是出问题了

这张图里最吓人的可能是压力:瞬时读数在正负几百 bar 之间大幅波动,而目标压力只有 1 bar。对小规模原子模拟来说,这种瞬时压力涨落是正常现象。压力来自体系维里和动能等贡献,对短时间的原子运动非常敏感,不应要求每一帧都贴近 1 bar。

本段平均压力为 −0.169 bar,曲线也没有持续向某个方向漂移。结合稳定的密度来看,没有明显异常。真正值得关注的是长时间窗口的平均压力是否偏离目标,或压力与体积、密度是否出现同步的持续异常,而不是单个尖峰有多大。

右下,密度:体系是否还在明显收缩或膨胀

密度主要围绕 1022 kg/m³ 波动,全区间平均为 1021.998 kg/m³,后期没有持续上升或下降。对这个蛋白水溶液体系而言,这是合理的量级,也说明经过 NPT 后,生产段的盒子体积和密度没有表现出明显失控。

11.3 综合起来,可以得出什么结论

把六张图放在一起,比较稳妥的表述是:

在 1 ns 生产模拟中,Factor Xa 的 Cα 相对 EM 参考保持约 0.10 nm 的整体偏移,Rg 基本稳定,多数残基波动较低,局部区域较为灵活;温度、压力和密度均围绕稳定水平涨落。轨迹未显示明显的整体失稳或运行异常。

这就是目前证据能够支持的范围。它说明这条短轨迹在结构和运行状态上没有明显问题,不能进一步推出"蛋白永远稳定""模拟已经收敛"或"结果与实验一致"。

这里的均值覆盖 0--1000 ps,没有根据收敛检验选择截断区间。201 帧来自同一条连续轨迹,彼此存在时间相关性,也不能当作 201 次独立实验。如果要研究更慢的构象变化,需要延长模拟、增加独立重复,并比较不同时间段和不同重复之间的结果。

出现异常 RMSD 时,仍应优先检查参考结构、原子映射和周期边界。本章曾用 GROMACS 与 MDTraj 对同一完整参考独立复算,最大差约为 4.93×10⁻⁶ nm,说明两种实现在这项计算上相互吻合;这只是对计算实现的交叉检查,不是对力场准确性或采样充分性的验证。

迁移到蛋白--肽体系时,拟合对象尤其重要。如果只把肽拟合到参考肽,即使整条肽已经离开受体,内部形状仍可能非常相似,RMSD 也可能很小。研究结合姿态时,通常应先拟合受体,再观察肽相对受体的位置、界面接触和波动。短时间内没有解离,也不能直接推出结合很强,更不能据此得到 Kd。

相关推荐
gz927cool4 小时前
Python 全栈应用最佳实践完整指南
开发语言·python·ai
日日大王学编程5 小时前
阿里巴巴开源 Data Agent(Text2SQL)项目全维度详解
java·ai·开源
七夜zippoe6 小时前
Agent 测试实战:单元测试、集成测试与 Mock LLM 策略
ai·单元测试·集成测试·mock·agent
Solange__6 小时前
物流园区怎么用AI生成3D调度预演?从道路底图、车辆转弯到网页评审
图像处理·人工智能·游戏·3d·ai·自然语言处理·关卡设计
最强小杰6 小时前
gpt-image-2.5-sunburst + OpenAI SDK + Cline 怎么接?完整图生图与局部重绘教程
gpt·ios·ai·iphone
骑着蜗牛撵大象3277 小时前
闲鱼捡漏自动化:关键词加价格上限先粗筛,AI 大模型再做第一轮判定
大数据·人工智能·ai·自动化·二手交易·闲鱼·关键词监控
小贺儿开发17 小时前
《流光绘忆》Unity 沉浸式光影互动
科技·unity·ai·程序员·工具·互动·光影
奇牙coding17 小时前
ofox.io 第三方模型 ID `wan-3.0-prime` 一直报 401 怎么办?从鉴权头、签名、区域到请求时间逐项排查
ai
数智园区合作社18 小时前
问数平台怎么把同一个指标锁成一个数
大数据·数据库·人工智能·ai