分子动力学模拟 是做什么的?
概述
分子动力学(MD)模拟通过积分牛顿运动方程,以计算方式模拟分子系统随时间演化的过程。本技能涵盖两个互补的工具:
- OpenMM (https://openmm.org/):高性能 MD 模拟引擎,支持 GPU、Python API 和灵活的力场支持
- MDAnalysis (https://mdanalysis.org/):用于读取、写入和分析来自所有主要模拟包的 MD 轨迹的 Python 库
安装:
conda install -c conda-forge openmm mdanalysis nglview
# 或
uv pip install openmm mdanalysis
何时使用此技能
在以下情况下使用分子动力学:
- 蛋白质稳定性分析:突变如何影响蛋白质动力学?
- 药物结合模拟:表征配体的结合模式和停留时间
- 构象采样:探索蛋白质的柔性和构象变化
- 蛋白质-蛋白质相互作用:模拟界面动力学和结合能量学
- RMSD/RMSF 分析:量化相对于参考结构的结构波动
- 自由能估算:计算结合自由能或构象自由能
- 膜模拟:模拟脂质双分子层中的蛋白质
- 固有无序蛋白质:研究 IDR 构象系综
核心工作流程:OpenMM 模拟
1. 系统准备
from openmm.app import *
from openmm import *
from openmm.unit import *
import sys
def prepare_system_from_pdb(pdb_file, forcefield_name="amber14-all.xml",
water_model="amber14/tip3pfb.xml"):
"""
从 PDB 文件准备 OpenMM 系统。
参数:
pdb_file: 清理后的 PDB 文件路径(原始 PDB 文件请使用 PDBFixer)
forcefield_name: 力场 XML 文件
water_model: 水模型 XML 文件
返回:
pdb, forcefield, system, topology
"""
# 加载 PDB
pdb = PDBFile(pdb_file)
# 加载力场
forcefield = ForceField(forcefield_name, water_model)
# 添加氢原子和溶剂
modeller = Modeller(pdb.topology, pdb.positions)
modeller.addHydrogens(forcefield)
# 添加溶剂盒(10 Å 填充,150 mM NaCl)
modeller.addSolvent(
forcefield,
model='tip3p',
padding=10*angstroms,
ionicStrength=0.15*molar
)
print(f"系统:{modeller.topology.getNumAtoms()} 个原子,"
f"{modeller.topology.getNumResidues()} 个残基")
# 创建系统
system = forcefield.createSystem(
modeller.topology,
nonbondedMethod=PME, # 粒子网格埃瓦尔德方法用于长程静电
nonbondedCutoff=1.0*nanometer,
constraints=HBonds, # 约束氢键(允许 2 fs 时间步长)
rigidWater=True,
ewaldErrorTolerance=0.0005
)
return modeller, system
2. 能量最小化
from openmm.app import *
from openmm import *
from openmm.unit import *
def minimize_energy(modeller, system, output_pdb="minimized.pdb",
max_iterations=1000, tolerance=10.0):
"""
对系统进行能量最小化以消除空间位阻。
参数:
modeller: 包含拓扑和位置的 Modeller 对象
system: OpenMM System
output_pdb: 保存最小化结构的路径
max_iterations: 最大最小化步数
tolerance: kJ/mol/nm 单位的收敛标准
返回:
包含最小化位置的 simulation 对象
"""
# 设置积分器(最小化无关)
integrator = LangevinMiddleIntegrator(300*kelvin, 1/picosecond, 0.004*picoseconds)
# 创建模拟
# 如果可用,使用 GPU(CUDA 或 OpenCL),否则回退到 CPU
try:
platform = Platform.getPlatformByName('CUDA')
properties = {'DeviceIndex': '0', 'Precision': 'mixed'}
except Exception:
try:
platform = Platform.getPlatformByName('OpenCL')
properties = {}
except Exception:
platform = Platform.getPlatformByName('CPU')
properties = {}
simulation = Simulation(
modeller.topology, system, integrator,
platform, properties
)
simulation.context.setPositions(modeller.positions)
# 检查初始能量
state = simulation.context.getState(getEnergy=True)
print(f"初始能量:{state.getPotentialEnergy()}")
# 最小化
simulation.minimizeEnergy(
tolerance=tolerance*kilojoules_per_mole/nanometer,
maxIterations=max_iterations
)
state = simulation.context.getState(getEnergy=True, getPositions=True)
print(f"最小化后能量:{state.getPotentialEnergy()}")
# 保存最小化结构
with open(output_pdb, 'w') as f:
PDBFile.writeFile(simulation.topology, state.getPositions(), f)
return simulation
3. NVT 平衡
from openmm.app import *
from openmm import *
from openmm.unit import *
def run_nvt_equilibration(simulation, n_steps=50000, temperature=300,
report_interval=1000, output_prefix="nvt"):
"""
NVT 平衡:恒定 N、V、T。
将速度平衡至目标温度。
参数:
simulation: OpenMM Simulation(最小化后)
n_steps: MD 步数(50000 × 2fs = 100 ps)
temperature: 开尔文温度
report_interval: 数据报告之间的步数
output_prefix: 轨迹和日志文件的文件名前缀
"""
# 在 NVT 期间为主链添加位置约束
# (可选:约束重原子)
# 设置温度
simulation.context.setVelocitiesToTemperature(temperature*kelvin)
# 添加报告器
simulation.reporters = []
# 日志文件
simulation.reporters.append(
StateDataReporter(
f"{output_prefix}_log.txt",
report_interval,
step=True,
potentialEnergy=True,
kineticEnergy=True,
temperature=True,
volume=True,
speed=True
)
)
# DCD 轨迹(紧凑的二进制格式)
simulation.reporters.append(
DCDReporter(f"{output_prefix}_traj.dcd", report_interval)
)
print(f"运行 NVT 平衡:{n_steps} 步({n_steps*2/1000:.1f} ps)")
simulation.step(n_steps)
print("NVT 平衡完成")
return simulation
4. NPT 平衡与生产运行
def run_npt_production(simulation, n_steps=500000, temperature=300, pressure=1.0,
report_interval=5000, output_prefix="npt"):
"""
NPT 生产运行:恒定 N、P、T。
参数:
n_steps: 生产步数(500000 × 2fs = 1 ns)
temperature: 开尔文温度
pressure: 巴压力
report_interval: 报告之间的步数
"""
# 添加蒙特卡罗压力控制器以控制压力
system = simulation.context.getSystem()
system.addForce(MonteCarloBarostat(pressure*bar, temperature*kelvin, 25))
simulation.context.reinitialize(preserveState=True)
# 更新报告器
simulation.reporters = []
simulation.reporters.append(
StateDataReporter(
f"{output_prefix}_log.txt",
report_interval,
step=True,
potentialEnergy=True,
temperature=True,
density=True,
speed=True
)
)
simulation.reporters.append(
DCDReporter(f"{output_prefix}_traj.dcd", report_interval)
)
# 保存检查点
simulation.reporters.append(
CheckpointReporter(f"{output_prefix}_checkpoint.chk", 50000)
)
print(f"运行 NPT 生产:{n_steps} 步({n_steps*2/1000000:.2f} ns)")
simulation.step(n_steps)
print("生产 MD 完成")
return simulation
使用 MDAnalysis 进行轨迹分析
1. 加载轨迹
import MDAnalysis as mda
from MDAnalysis.analysis import rms, align, contacts
import numpy as np
import matplotlib.pyplot as plt
def load_trajectory(topology_file, trajectory_file):
"""
使用 MDAnalysis 加载 MD 轨迹。
参数:
topology_file: PDB、PSF 或其他拓扑文件
trajectory_file: DCD、XTC、TRR 或其他轨迹文件
"""
u = mda.Universe(topology_file, trajectory_file)
print(f"Universe:{u.atoms.n_atoms} 个原子,{u.trajectory.n_frames} 帧")
print(f"时间范围:0 到 {u.trajectory.totaltime:.0f} ps")
return u
2. RMSD 分析
def compute_rmsd(u, selection="backbone", reference_frame=0):
"""
计算选定原子相对于参考帧的 RMSD。
参数:
u: MDAnalysis Universe
selection: 原子选择字符串(MDAnalysis 语法)
reference_frame: 参考结构的帧索引
返回:
(time, rmsd) 值的 numpy 数组
"""
# 对齐轨迹以最小化 RMSD
aligner = align.AlignTraj(u, u, select=selection, in_memory=True)
aligner.run()
# 计算 RMSD
R = rms.RMSD(u, select=selection, ref_frame=reference_frame)
R.run()
rmsd_data = R.results.rmsd # 列:frame, time, RMSD
return rmsd_data
def plot_rmsd(rmsd_data, title="RMSD 随时间变化", output_file="rmsd.png"):
"""绘制模拟时间内的 RMSD。"""
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(rmsd_data[:, 1] / 1000, rmsd_data[:, 2], 'b-', linewidth=0.5)
ax.set_xlabel("时间 (ns)")
ax.set_ylabel("RMSD (Å)")
ax.set_title(title)
ax.axhline(rmsd_data[:, 2].mean(), color='r', linestyle='--',
label=f'平均值:{rmsd_data[:, 2].mean():.2f} Å')
ax.legend()
plt.tight_layout()
plt.savefig(output_file, dpi=150)
return fig
3. RMSF 分析(每个残基的柔性)
def compute_rmsf(u, selection="backbone", start_frame=0):
"""
计算每个残基的 RMSF(柔性)。
返回:
resids, rmsf_values 数组
"""
# 选择原子
atoms = u.select_atoms(selection)
# 计算 RMSF
R = rms.RMSF(atoms)
R.run(start=start_frame)
# 按残基取平均值
resids = []
rmsf_per_res = []
for res in u.select_atoms(selection).residues:
res_atoms = res.atoms.intersection(atoms)
if len(res_atoms) > 0:
resids.append(res.resid)
rmsf_per_res.append(R.results.rmsf[res_atoms.indices].mean())
return np.array(resids), np.array(rmsf_per_res)
4. 蛋白质-配体接触
def analyze_contacts(u, protein_sel="protein", ligand_sel="resname LIG",
radius=4.5, start_frame=0):
"""
追踪轨迹中的蛋白质-配体接触。
参数:
radius: 接触距离截止值,单位为埃
"""
protein = u.select_atoms(protein_sel)
ligand = u.select_atoms(ligand_sel)
contact_frames = []
for ts in u.trajectory[start_frame:]:
# 查找距离配体半径范围内的蛋白质原子
distances = contacts.contact_matrix(
protein.positions, ligand.positions, radius
)
contact_residues = set()
for i in range(distances.shape[0]):
if distances[i].any():
contact_residues.add(protein.atoms[i].resid)
contact_frames.append(contact_residues)
return contact_frames
力场选择指南
| 系统 | 推荐力场 | 水模型 |
|---|---|---|
| 标准蛋白质 | AMBER14 (amber14-all.xml) | TIP3P-FB |
| 蛋白质 + 小分子 | AMBER14 + GAFF2 | TIP3P-FB |
| 膜蛋白质 | CHARMM36m | TIP3P |
| 核酸 | AMBER99-bsc1 或 AMBER14 | TIP3P |
| 无序蛋白质 | ff19SB 或 CHARMM36m | TIP3P |
系统准备工具
PDBFixer(用于原始 PDB 文件)
from pdbfixer import PDBFixer
from openmm.app import PDBFile
def fix_pdb(input_pdb, output_pdb, ph=7.0):
"""修复常见 PDB 问题:缺失残基、原子、添加 H、标准化。"""
fixer = PDBFixer(filename=input_pdb)
fixer.findMissingResidues()
fixer.findNonstandardResidues()
fixer.replaceNonstandardResidues()
fixer.removeHeterogens(True) # 移除水/配体
fixer.findMissingAtoms()
fixer.addMissingAtoms()
fixer.addMissingHydrogens(ph)
with open(output_pdb, 'w') as f:
PDBFile.writeFile(fixer.topology, fixer.positions, f)
return output_pdb
用于小分子的 GAFF2(通过 OpenFF 工具包)
# 对于配体参数化,请使用 OpenFF 工具包或 ACPYPE
# uv pip install openff-toolkit
from openff.toolkit import Molecule, ForceField as OFFForceField
from openff.interchange import Interchange
def parameterize_ligand(smiles, ff_name="openff-2.0.0.offxml"):
"""为小分子生成 GAFF2/OpenFF 参数。"""
mol = Molecule.from_smiles(smiles)
mol.generate_conformers(n_conformers=1)
off_ff = OFFForceField(ff_name)
interchange = off_ff.create_interchange(mol.to_topology())
return interchange
最佳实践
- 始终在 MD 前进行最小化:原始 PDB 结构存在空间位阻
- 在生产运行前进行平衡:NVT(50–100 ps)→ NPT(100–500 ps)→ 生产运行
- 使用 GPU:在 GPU(CUDA/OpenCL)上模拟速度提升 10–100 倍
- 2 fs 时间步长并配合 HBonds 约束:标准;使用 HMR(氢质量再分配)时可用 4 fs
- 仅分析平衡后的轨迹:丢弃前 20–50% 作为平衡阶段
- 保存检查点:MD 运行可能失败;检查点允许重启
- 周期性边界条件:对于溶剂化系统是必需的
- 用于静电的 PME:对于带电系统比截断方法更准确
其他资源
- OpenMM 文档:https://openmm.org/documentation.html
- MDAnalysis 用户指南:https://docs.mdanalysis.org/
- GROMACS(替代 MD 引擎):https://manual.gromacs.org/
- NAMD(替代):https://www.ks.uiuc.edu/Research/namd/
- CHARMM-GUI(系统构建器):https://www.charmm-gui.org/