关于 molecular-dynamics
molecular-dynamics 是面向分子模拟任务的技能,结合 OpenMM 进行分子动力学模拟,并用 MDAnalysis 处理和分析轨迹。适用于研究蛋白质、配体及其他分子系统的计算工作流。
主要功能
- 01使用 OpenMM 执行分子动力学模拟
- 02借助 MDAnalysis 读取和分析轨迹
- 03计算 RMSD 与 RMSF
- 04分析蛋白质-配体接触
- 05涵盖系统准备与能量最小化
适用场景
- 01评估突变对蛋白质稳定性的影响
- 02模拟配体结合及驻留时间
- 03分析蛋白质构象变化与柔性
- 04量化参考结构相关的结构波动
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ kalarislabs/ research-agent-skills --skill molecular-dynamics安装后在 Claude Code 中输入/molecular-dynamics确认已加载。
附带文件references/mdanalysis_analysis.md
详细介绍
分子动力学
概述
分子动力学(MD)模拟通过积分牛顿运动方程,以计算方式模拟分子系统随时间的演化。本技能涵盖两种互补工具:
- OpenMM(https://openmm.org/):高性能 MD 模拟引擎,支持 GPU,提供 Python API,并灵活支持多种力场
- MDAnalysis(https://mdanalysis.org/):用于读取、写入和分析所有主流模拟软件生成的 MD 轨迹的 Python 库
安装:
conda install -c conda-forge openmm mdanalysis nglview
# or
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"):
"""
Prepare an OpenMM system from a PDB file.
Args:
pdb_file: Path to cleaned PDB file (use PDBFixer for raw PDB files)
forcefield_name: Force field XML file
water_model: Water model XML file
Returns:
pdb, forcefield, system, topology
"""
# Load PDB
pdb = PDBFile(pdb_file)
# Load force field
forcefield = ForceField(forcefield_name, water_model)
# Add hydrogens and solvate
modeller = Modeller(pdb.topology, pdb.positions)
modeller.addHydrogens(forcefield)
# Add solvent box (10 Å padding, 150 mM NaCl)
modeller.addSolvent(
forcefield,
model='tip3p',
padding=10*angstroms,
ionicStrength=0.15*molar
)
print(f"System: {modeller.topology.getNumAtoms()} atoms, "
f"{modeller.topology.getNumResidues()} residues")
# Create system
system = forcefield.createSystem(
modeller.topology,
nonbondedMethod=PME, # Particle Mesh Ewald for long-range electrostatics
nonbondedCutoff=1.0*nanometer,
constraints=HBonds, # Constrain hydrogen bonds (allows 2 fs timestep)
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):
"""
Energy minimize the system to remove steric clashes.
Args:
modeller: Modeller object with topology and positions
system: OpenMM System
output_pdb: Path to save minimized structure
max_iterations: Maximum minimization steps
tolerance: Convergence criterion in kJ/mol/nm
Returns:
simulation object with minimized positions
"""
# Set up integrator (doesn't matter for minimization)
integrator = LangevinMiddleIntegrator(300*kelvin, 1/picosecond, 0.004*picoseconds)
# Create simulation
# Use GPU if available (CUDA or OpenCL), fall back to 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)
# Check initial energy
state = simulation.context.getState(getEnergy=True)
print(f"Initial energy: {state.getPotentialEnergy()}")
# Minimize
simulation.minimizeEnergy(
tolerance=tolerance*kilojoules_per_mole/nanometer,
maxIterations=max_iterations
)
state = simulation.context.getState(getEnergy=True, getPositions=True)
print(f"Minimized energy: {state.getPotentialEnergy()}")
# Save minimized structure
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 equilibration: constant N, V, T.
Equilibrate velocities to target temperature.
Args:
simulation: OpenMM Simulation (after minimization)
n_steps: Number of MD steps (50000 × 2fs = 100 ps)
temperature: Temperature in Kelvin
report_interval: Steps between data reports
output_prefix: File prefix for trajectory and log
"""
# Add position restraints for backbone during NVT
# (Optional: restraint heavy atoms)
# Set temperature
simulation.context.setVelocitiesToTemperature(temperature*kelvin)
# Add reporters
simulation.reporters = []
# Log file
simulation.reporters.append(
StateDataReporter(
f"{output_prefix}_log.txt",
report_interval,
step=True,
potentialEnergy=True,
kineticEnergy=True,
temperature=True,
volume=True,
speed=True
)
)
# DCD trajectory (compact binary format)
simulation.reporters.append(
DCDReporter(f"{output_prefix}_traj.dcd", report_interval)
)
print(f"Running NVT equilibration: {n_steps} steps ({n_steps*2/1000:.1f} ps)")
simulation.step(n_steps)
print("NVT equilibration complete")
return simulation
4. NPT 平衡与生产模拟
def run_npt_production(simulation, n_steps=500000, temperature=300, pressure=1.0,
report_interval=5000, output_prefix="npt"):
"""
NPT production run: constant N, P, T.
Args:
n_steps: Production steps (500000 × 2fs = 1 ns)
temperature: Temperature in Kelvin
pressure: Pressure in bar
report_interval: Steps between reports
"""
# Add Monte Carlo barostat for pressure control
system = simulation.context.getSystem()
system.addForce(MonteCarloBarostat(pressure*bar, temperature*kelvin, 25))
simulation.context.reinitialize(preserveState=True)
# Update reporters
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)
)
# Save checkpoints
simulation.reporters.append(
CheckpointReporter(f"{output_prefix}_checkpoint.chk", 50000)
)
print(f"Running NPT production: {n_steps} steps ({n_steps*2/1000000:.2f} ns)")
simulation.step(n_steps)
print("Production MD complete")
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):
"""
Load an MD trajectory with MDAnalysis.
Args:
topology_file: PDB, PSF, or other topology file
trajectory_file: DCD, XTC, TRR, or other trajectory
"""
u = mda.Universe(topology_file, trajectory_file)
print(f"Universe: {u.atoms.n_atoms} atoms, {u.trajectory.n_frames} frames")
print(f"Time range: 0 to {u.trajectory.totaltime:.0f} ps")
return u
2. RMSD 分析
def compute_rmsd(u, selection="backbone", reference_frame=0):
"""
Compute RMSD of selected atoms relative to reference frame.
Args:
u: MDAnalysis Universe
selection: Atom selection string (MDAnalysis syntax)
reference_frame: Frame index for reference structure
Returns:
numpy array of (time, rmsd) values
"""
# Align trajectory to minimize RMSD
aligner = align.AlignTraj(u, u, select=selection, in_memory=True)
aligner.run()
# Compute RMSD
R = rms.RMSD(u, select=selection, ref_frame=reference_frame)
R.run()
rmsd_data = R.results.rmsd # columns: frame, time, RMSD
return rmsd_data
def plot_rmsd(rmsd_data, title="RMSD over time", output_file="rmsd.png"):
"""Plot RMSD over simulation time."""
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(rmsd_data[:, 1] / 1000, rmsd_data[:, 2], 'b-', linewidth=0.5)
ax.set_xlabel("Time (ns)")
ax.set_ylabel("RMSD (Å)")
ax.set_title(title)
ax.axhline(rmsd_data[:, 2].mean(), color='r', linestyle='--',
label=f'Mean: {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):
"""
Compute per-residue RMSF (flexibility).
Returns:
resids, rmsf_values arrays
"""
# Select atoms
atoms = u.select_atoms(selection)
# Compute RMSF
R = rms.RMSF(atoms)
R.run(start=start_frame)
# Average by residue
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):
"""
Track protein-ligand contacts over trajectory.
Args:
radius: Contact distance cutoff in Angstroms
"""
protein = u.select_atoms(protein_sel)
ligand = u.select_atoms(ligand_sel)
contact_frames = []
for ts in u.trajectory[start_frame:]:
# Find protein atoms within radius of ligand
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):
"""Fix common PDB issues: missing residues, atoms, add H, standardize."""
fixer = PDBFixer(filename=input_pdb)
fixer.findMissingResidues()
fixer.findNonstandardResidues()
fixer.replaceNonstandardResidues()
fixer.removeHeterogens(True) # Remove water/ligands
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 Toolkit)
# For ligand parameterization, use OpenFF toolkit or 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"):
"""Generate GAFF2/OpenFF parameters for a small molecule."""
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 上的模拟速度可提高 10–100 倍(CUDA/OpenCL)
- 采用 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(基于 Web 的系统构建工具):https://charmm-gui.org/
- AmberTools(免费的 Amber 工具):https://ambermd.org/AmberTools.php
- OpenMM 论文:Eastman P 等(2017),PLOS Computational Biology。PMID:28278240
- MDAnalysis 论文:Michaud-Agrawal N 等(2011),J Computational Chemistry。PMID:21500218
Agent 操作流程
- 检查环境。 确认工具包版本和输入格式(SMILES、SDF、PDB、CIF),并在计算前对分子进行规范化处理。
- 明确输入。 根据数据或用户确认格式、标识符和参数。凡是会影响结果的值,都应询问,不要猜测。
- 先运行小规模版本。 先处理少量分子或一个靶标,并检查输出。
- 按照上述说明和参考资料执行完整任务。
- 验证结果。 检查无效结构、单位一致性,以及预测模型的适用域。
- 报告。 说明运行了哪些内容(版本、命令、参数)、检查了哪些内容,以及仍有哪些不确定之处。
| 如果出现这种情况 | 请这样做 |
|---|---|
| 分子无法解析或规范化 | 说明原因并跳过;绝不能悄悄丢弃或更改结构。 |
| 当前安装版本中缺少这些说明里提到的函数、标志或端点 | 查阅已安装版本自身的文档(help()、--help、官方文档),调整做法并告知用户。绝不能虚构 API。 |
| 必需的输入、标识符或参数存在歧义 | 询问用户,或在运行前明确说明假设。 |
诚信准则
- 绝不捏造结果、参数、标识符、引文或统计数据。如果无法运行或验证某项内容,请直言说明。
- 计算预测是待验证的假设,不是实验结果;应明确标注。
- 此处与特定版本相关的细节可能已过时:请对照已安装版本的官方文档进行确认。
- 对于需要付费、占用共享 GPU 或云配额、涉及个人或患者数据,或无法撤销的操作,务必事先征求同意。
相关技能
rowan:Rowan 是一个云原生分子建模和药物化学工作流平台,提供 Python API。
评论
支持 Markdown