thermoelasticsim.md.md_simulator 源代码

# 文件名: md_simulator.py
# 作者: Gilbert Young
# 修改日期: 2025-03-27
# 文件描述: 实现分子动力学模拟器 MDSimulator 类,用于执行分子动力学模拟。

import logging

import matplotlib.pyplot as plt

from .thermostats import (
    AndersenThermostat,
    BerendsenThermostat,
    NoseHooverChainThermostat,
    NoseHooverThermostat,
)

logger = logging.getLogger(__name__)


[文档] class MDSimulator: """ 分子动力学模拟器,支持NVE、NVT和NPT系综 Parameters ---------- cell : Cell 包含原子的晶胞对象 potential : Potential 势能对象 integrator : Integrator 积分器对象 ensemble : str 系综类型: 'NVE', 'NVT', 或 'NPT' thermostat : Optional[Dict] or Thermostat 恒温器配置或对象 barostat : Optional[Barostat] 恒压器对象 """
[文档] def __init__( self, cell, potential, integrator, ensemble="NVE", thermostat=None, barostat=None, ): self.cell = cell self.potential = potential self.integrator = integrator self.ensemble = ensemble.upper() self.dt = None # 验证系综配置 if self.ensemble not in ["NVE", "NVT", "NPT"]: raise ValueError(f"Unknown ensemble: {self.ensemble}") if self.ensemble == "NVE" and (thermostat or barostat): raise ValueError("NVE ensemble should not have thermostat or barostat") if self.ensemble == "NVT" and barostat: raise ValueError("NVT ensemble should not have barostat") if self.ensemble == "NPT" and not (thermostat and barostat): raise ValueError("NPT ensemble requires both thermostat and barostat") # 配置恒温器 if isinstance(thermostat, dict): self.thermostat = self._setup_thermostat(thermostat) else: self.thermostat = thermostat self.barostat = barostat # 轨迹数据 self.time = [] self.temperature = [] self.energy = [] self.pressure = [] self.volume = []
def _setup_thermostat(self, config): """根据配置字典设置恒温器""" thermostat_type = config.get("type") params = config.get("params", {}) if thermostat_type == "Berendsen": return BerendsenThermostat( target_temperature=params.get("target_temperature", 300.0), tau=params["tau"], ) elif thermostat_type == "Andersen": return AndersenThermostat( target_temperature=params.get("target_temperature", 300.0), collision_frequency=params["collision_frequency"], ) elif thermostat_type == "NoseHoover": return NoseHooverThermostat( target_temperature=params.get("target_temperature", 300.0), time_constant=params["time_constant"], Q=params.get("Q", None), ) elif thermostat_type == "NoseHooverChain": return NoseHooverChainThermostat( target_temperature=params.get("target_temperature", 300.0), time_constant=params["time_constant"], chain_length=params.get("chain_length", 3), Q=params.get("Q", None), ) else: raise ValueError(f"Unsupported thermostat type: {thermostat_type}")
[文档] def run_nve(self, steps, dt): """运行NVE系综模拟""" current_time = 0.0 self.dt = dt # 初始化力 self.potential.calculate_forces(self.cell) for step in range(steps): # 积分更新位置和速度 self.integrator.integrate(self.cell, self.potential, dt) current_time += dt # 记录数据 self._record_state(current_time)
[文档] def run_nvt(self, steps, dt): """运行NVT系综模拟""" current_time = 0.0 self.dt = dt # 初始化力 self.potential.calculate_forces(self.cell) for step in range(steps): # 积分更新位置和速度 self.integrator.integrate(self.cell, self.potential, dt) # 应用恒温器 (修改处:将原来的 apply(self.cell.atoms, dt) 改为 apply(self.cell, dt, self.potential)) self.thermostat.apply(self.cell, dt, self.potential) current_time += dt # 记录数据 self._record_state(current_time)
[文档] def run_npt(self, steps, dt): """运行NPT系综模拟""" current_time = 0.0 self.dt = dt # 初始化力 self.potential.calculate_forces(self.cell) for step in range(steps): # 更新位置和速度(1/4步) self.integrator.integrate(self.cell, self.potential, dt / 4) # 应用恒压器(1/2步) self.barostat.apply(self.cell, dt / 2, self.potential) # 应用恒温器(1/2步) self.thermostat.apply(self.cell, dt / 2, self.potential) # 更新位置和速度(1/2步) self.integrator.integrate(self.cell, self.potential, dt / 2) # 应用恒温器(1/2步) self.thermostat.apply(self.cell, dt / 2, self.potential) # 应用恒压器(1/2步) self.barostat.apply(self.cell, dt / 2, self.potential) # 更新位置和速度(1/4步) self.integrator.integrate(self.cell, self.potential, dt / 4) current_time += dt # 记录数据 self._record_state(current_time)
[文档] def run(self, steps: int, dt: float): """ 运行分子动力学模拟 Parameters ---------- steps : int 模拟步数 dt : float 时间步长 """ if self.ensemble == "NVE": self.run_nve(steps, dt) elif self.ensemble == "NVT": self.run_nvt(steps, dt) elif self.ensemble == "NPT": self.run_npt(steps, dt) # 完成后绘制温度演化图 self.plot_temperature() if self.ensemble == "NPT": self.plot_pressure()
def _record_state(self, current_time): """记录系统状态""" temp = self.cell.calculate_temperature() total_energy = self.calculate_total_energy() volume = self.cell.volume self.time.append(current_time) self.temperature.append(temp) self.energy.append(total_energy) self.volume.append(volume) if self.ensemble == "NPT": pressure = self.barostat.calculate_internal_pressure( self.cell.calculate_stress_tensor(self.potential) ) self.pressure.append(pressure)
[文档] def calculate_total_energy(self) -> float: """计算系统总能量""" kinetic = self.integrator.calculate_kinetic_energy(self.cell.atoms) potential = self.potential.calculate_energy(self.cell) return kinetic + potential
[文档] def plot_temperature(self): """绘制温度演化图""" plt.figure(figsize=(10, 6)) plt.plot(self.time, self.temperature, label="Temperature (K)") if self.thermostat: plt.axhline( y=self.thermostat.target_temperature, color="r", linestyle="--", label="Target Temperature", ) plt.xlabel("Time (fs)") plt.ylabel("Temperature (K)") plt.title("Temperature Evolution") plt.legend() plt.grid(True) plt.show()
[文档] def plot_pressure(self): """绘制压力演化图""" plt.figure(figsize=(10, 6)) plt.plot(self.time, self.pressure, label="Pressure (GPa)") plt.xlabel("Time (fs)") plt.ylabel("Pressure (GPa)") plt.title("Pressure Evolution") plt.legend() plt.grid(True) plt.show()