File size: 3,770 Bytes
a244197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from ase.io import read, write
from ase import units
from ase.md.langevin import Langevin
from ase.md.velocitydistribution import Stationary, ZeroRotation, MaxwellBoltzmannDistribution
import random
import os
import time
import numpy as np
import matplotlib.pyplot as plt
import argparse
from onescience.utils.mace.calculators import MACECalculator


def simpleMD(init_conf, temp, calc, fname, s, T, timestep_fs, friction, plot_file="md_plot.png"):
    """
    在服务器上运行的 MD 模拟,不依赖 Jupyter,结束后保存图片。
    """
    init_conf.set_calculator(calc)

    # 初始化温度
    random.seed(701)
    MaxwellBoltzmannDistribution(init_conf, temperature_K=300)
    Stationary(init_conf)
    ZeroRotation(init_conf)

    dyn = Langevin(
        init_conf,
        timestep_fs * units.fs,
        temperature_K=temp,
        friction=friction
    )

    time_fs = []
    temperature = []
    energies = []

    # 删除之前存储的同名轨迹
    if os.path.exists(fname):
        os.system('rm -rfv ' + fname)

    # 记录函数:仅存储数据,不实时绘图
    def write_frame():
        dyn.atoms.write(fname, append=True)
        time_fs.append(dyn.get_time() / units.fs)
        temperature.append(dyn.atoms.get_temperature())
        energies.append(dyn.atoms.get_potential_energy() / len(dyn.atoms))

        # 可选:每隔一段时间打印进度
        if len(time_fs) % 10 == 0:   # 每10步打印一次
            print(f"Step {len(time_fs)}, Time: {time_fs[-1]:.1f} fs, Temp: {temperature[-1]:.1f} K, E: {energies[-1]:.4f} eV/atom")

    dyn.attach(write_frame, interval=s)

    t0 = time.time()
    dyn.run(T)
    t1 = time.time()
    print("MD 完成于 {0:.2f} 分钟!".format((t1 - t0) / 60))

    # 模拟结束后绘制并保存图片
    if time_fs and energies and temperature:
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 6), sharex='all')
        ax1.plot(time_fs, energies, 'b-')
        ax1.set_ylabel('E (eV/atom)')
        ax2.plot(time_fs, temperature, 'r-')
        ax2.set_ylabel('T (K)')
        ax2.set_xlabel('Time (fs)')
        plt.tight_layout()
        plt.savefig(plot_file, dpi=150)
        print(f"Plot saved to {plot_file}")
        plt.close()   # 避免在无显示器的服务器上弹出窗口
    else:
        print("No data to plot.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--init_conf", type=str, required=True)
    parser.add_argument("--model", type=str, required=True)
    parser.add_argument("--output_xyz", type=str, required=True)
    parser.add_argument("--plot_file", type=str, required=True)
    parser.add_argument("--device", type=str, default="cuda")
    parser.add_argument("--default_dtype", type=str, default="float64")
    parser.add_argument("--temperature_k", type=float, required=True)
    parser.add_argument("--time_step_fs", type=float, default=1.0)
    parser.add_argument("--friction", type=float, default=0.1)
    parser.add_argument("--save_interval", type=int, default=10)
    parser.add_argument("--steps", type=int, required=True)

    args = parser.parse_args()

    # 读取初始构型(使用传进来的绝对路径,不再硬编码相对路径)
    init_conf = read(args.init_conf, ':')[0].copy()

    # 加载MACE模型
    mace_calc = MACECalculator(
        model_paths=[args.model],
        device=args.device,
        default_dtype=args.default_dtype
    )

    # 启动MD
    simpleMD(
        init_conf=init_conf,
        temp=args.temperature_k,
        calc=mace_calc,
        fname=args.output_xyz,
        s=args.save_interval,
        T=args.steps,
        timestep_fs=args.time_step_fs,
        friction=args.friction,
        plot_file=args.plot_file
    )