RESEARCH NOTE · DP-GEN
DP-GEN Note
从安装与初始化,到并行训练、模型偏差筛选和最终测试的一份工作流笔记。
1. DP-GEN 原理及安装
1.1 DP-GEN 原理
DP-GEN (Deep Generator) :控制工作流
- * a)init:调用第一性原理计算软件产生初始数据(init_bulk / init_surf)
- * b)run: DP-GEN 主流程,调用 DeePMD-kit、Lammps、VASP,实现同步学习策略,自动迭代扩充数据集并提升模型质量
00.train (training):DeePMD-kit 训练势函数
01.model_devi (exploration):DP-GEN 探索相空间(初始数据集,经典 MD),并标记不准确的数据点
02.fp (labeling):DFT 重新计算这些不准确的数据点(candidate),并使用 dpdata 转化数据格式,添加扩充数据集
循环训练
- * c)test:应用 DP 模型、第一性原理方法及经验力场,计算体系的多种性质
1.2 DP-GEN 安装
- * git 安装
git clone https://gitee.com/deepmodeling/dpgen
cd apgen
pip install setuptools_scm
pip install --user .- * pip安装
pip install dpgen
pip install oss2 # 安装依赖dpgen -h2. DP-GEN 训练流程
Reference: DP-GEN运行 无痛上手指南--LiSi晶体的DP势函数构建 ↗
DP-GEN 运行文件:
- * 输入参数文件: param.json(init & run)
- * 机器配置文件: machine.json
- * VASP计算参数文件:
- INCAR_rlx:init 过程,晶体结构弛豫
- INCAR_md:init 过程,AIMD 模拟
- INCAR_scf:run 过程,labeling 阶段单点计算
- * K 点信息在 INCAR 中设置(KSPACING,KGAMMA)
- * VASP 赝势文件: POTCAR_E1、POTCAR_E2,根据体系元素种类分别设置
- * VASP 结构文件: POSCAR
2.1 init
2.1.1 init 过程主要步骤
- * 根据体系是 bulk / surface,选择使用子命令:init_bulk / init_surf
- * 分 4 个顺序步骤(stages),输出文件夹为 "POSCAR.01x01x01" :
- 生成初始结构(s1):可选择是否优化初始结构,输出文件存储在 "00.place_ele" ,
- 产生MD起始构象(s2):放缩扰动 s1 中的结构,输出文件存储在 "01.scale_pert"
- 执行AIMD(s3):调用 VASP 对 s2 起始构象执行少步数 AIMD,输出文件存储在 **"02.md/scale-*"**
- 整理初始数据(s4):调用 dpdata 提取 s3 中各 snapshot 数据,输出文件存储在 "02.md/deepmd"
2.1.2 dpgen init_bulk 输入文件
INCAR_rlx INCAR_md POSCAR POTCAR_E1 POTCAR_E2 machine.json param.json- * 输入参数文件 param.json
{
"stages": [1, 2, 3, 4], // 顺序执行4个步骤, 可只执行其中部分步骤
"elements": ["Li", "Si"], // 原子类型
"cell_type": "diamond", // s1生成结构的晶型: fcc, hcp, bcc, sc, diamond
"latt": 10.0, // s1生成结构的单胞晶格常数
"super_cell": [1, 1, 1], // s1生成结构的超胞, 对POSCAR也适用
"from_poscar": true, // s1是否采用POSCAR作为初始弛豫结构。true: cell_type, latt失效, false: cell_type, latt须赋值
"from_poscar_path": "./POSCAR_LiSi", // s1 POSCAR文件路径
"potcars": ["./POTCAR_Li", "./POTCAR_Si"], // VASP计算POTCAR文件路径
"relax_incar": "./INCAR_rlx", // VASP弛豫计算的INCAR路径, 含s1时必须赋值, 且此时不受skip_relax赋值的影响
"md_incar" : "./INCAR_md", // VASP AIMD模拟的INCAR路径, 含s3时必须赋值
"skip_relax": false, // true: s2对s1未弛豫的POSCAR做扰动, false: s2对s1弛豫后的CONTCAR做扰动
"scale": [0.99, 1.00, 1.01], // s2缩放超胞盒子的缩放系数
"pert_numb": 10, // 控制每种scale下生成多少个微扰结构(同时扰动晶格矢量和原子位置)
"pert_box": 0.03, // 扰动盒子尺寸矢量的比率, 扰动值/原值, 从[-pert_box, pert_box]中随机采样
"pert_atom": 0.01, // 扰动原子坐标位置的大小(Å), 从[-pert_atom, pert_atom]中随机采样
"md_nstep" : 10, // s3中vasp做AIMD模拟的步数, 以NSW赋值为准
"coll_ndata": 5000, // s4收集AIMD数据的最大数量
"type_map" : ["Li", "Si"], // s4按deepmd文件格式记录元素种类的顺序, 需与'elements', 'potcars'及POSCAR文件顺序一致
"_comment": "that's all"
}2.1.3 提交 dpgen init_bulk 任务
nohup dpgen init_bulk param.json machine.json 1>log 2>err &
# 或
tmux
module load deepmd/2.2.7 && source activate && conda activate deepmd-2.2.7
dpgen init_bulk param.json machine.json 1>log 2>err &2.1.4 dpgen init_bulk 输出文件
POSCAR.01x01x01 # init_bulk 过程特有, 保存输出文件和数据
dpgen.log # 正常结束显示 INFO: Current stage is 4, collect data, 任务中断会停止在某个stage
dpdispatcher.log
log, err # 运行日志,报错信息- * 初始训练数据集 "POSCAR.01x01x01/02.md/deepmd"
# 模拟含 n 帧 (AIMD 离子步),每帧含 N 个原子 (体系原子数) 时间(ps) 长度(Angstrom) 能量(eV) 力(eV/Angstrom) Virial(eV) Pressure(Bar)
# 如果训练非周期系统,应该在系统目录下放一个空的 nopbc 文件,此时 box.raw 不是必需的
.
├── box.raw # nx9 每帧含 3x3 盒子基矢
├── coord.raw # nx(Nx3) 每个原子的 xyz 坐标
├── energy.raw # nx1 每帧含 1 个能量值
├── force.raw # nx(Nx3) 每个原子的受力 xyz 分量
├── set.000 # Numpy 二进制数据集,可用脚本 $deepmd_source_dir/data/raw/raw_to_set.sh + 每个 set 的数量,将原始文件转换为数据集
│ ├── box.npy
│ ├── coord.npy
│ ├── energy.npy
│ ├── force.npy
│ └── virial.npy
├── type_map.raw # typex1 元素类型的顺序
├── type.raw # Nx1 原子种类 (0, 1, 2, ...)
└── virial.raw # nx9 每帧含 3x3 的维里张量 XX XY XZ YX YY YZ ZX ZY ZZ2.2 run
2.2.1 run 过程主要步骤
- * dpgen run 主流程包含一系列迭代 (iteration) ,覆盖势能面(PES)上尽可能多的样本空间
- * 各迭代根据所选系综在给定的温度、压力或体积等条件下进行 DPMD 采样与筛选,实现同步学习
- * 输出文件存储在:__".../iter.00000*/00.train & 01.model_devi & 02.fp"__ 文件夹
- * 每个迭代 __iter.00000\*__ 包含 3 个顺序步骤:
- 00.train (Training):调用 DeePMD-kit
dp train input.json
基于 init 准备的以及之前迭代积累的训练数据,训练多个(默认 4 个)新模型
不同的模型使用不同的随机数种子初始化神经网络
- 01.model_devi (Exploration):调用 LAMMPS
lmp -i input.lammps
使用 00.train 的 1 个模型进行 DeePMD-kit 采样(MD模拟)
引入模型间预测偏差(model-deviation)作为误差判据,并设定上下限
任一 MD 中的 snapshot 构象的模型间预测偏差越大,则当前模型系综对该构象的精度越低
挑选出有希望改进模对 PES 整体预测精度的构象
- 02.fp (Labeling):调用 VASP
mpirun -n 30 vasp_std
对 01.model_devi 挑选的 snapshot 候选构象进行第一性原标理标定(单点计算)
调用 dpdata 收集整理所得数据,加入训练数据集以扩展数据集
- * 每个步骤分为 3 个顺序阶段:
- make:生成相应步骤的输入文件
- run:根据机器配置执行训练任务
- post:收集整理输出文件,分析训练结果
- * DeePMD-kit 的数据单位
| Time | Length | Energy | Force | Pressure |
|:------:|:--------:|:--------:|:-------:|:----------:|
| ps | Å | eV | eV/Å | bar |
$\mathrm{1\ ps = 10^{-12}\ s}$
$\mathrm{1\ eV = 1.602\times10^{-19}\ J\ (N·m)}$
$\mathrm{1\ eV/\AA = 1.602\times10^{-9}\ N}$
$\mathrm{1\ GPa = 10^9\ Pa\ (N/m^2) = 10\ kbar}$
2.2.2 dpgen run 输入文件
注:param.json 参数文件为 run 过程特有,machine.json 无需重新配置
POSCAR.01x01x01(POSCARs, deepmd) INCAR_scf POTCAR_E1 POTCAR_E2 machine.json param.json- * 输入参数文件 param.json
包含用于生成 input.lammps、input.json 等文件的信息
{
"type_map": ["Zr", "Hf", "O"],
"mass_map": [91.22, 178.49, 15.99],
"init_data_prefix": "../init/",
"init_data_sys": [
"./POSCAR.01x01x01/02.md/sys-0032-0064/deepmd" // 初始数据集
],
"sys_configs_prefix": "../init/",
"sys_configs": [
["./POSCAR.01x01x01/02.md/sys-0032-0064/scale-1.000/00000[0-9]/POSCAR"] // 需要训练的结构
],
"_comment": " 00.train ",
"numb_models": 4,
"_model_devi_activation_func": [["tanh","tanh"], ["tanh","gelu"], ["gelu","tanh"], ["gelu","gelu"]],
"default_training_param": {
"model": {
"type_map": ["Zr", "Hf", "O"],
"descriptor": { // 描述符网络
"type": "se_e2_a", // 二阶平滑版本的嵌入原子对称描述符
"sel": "auto", // [100, 100, 100] 每个中心原子周围最大选择数
"rcut_smth": 0.5, // 描述符平滑截止半径(Å),在此距离内原子间作用力平滑过渡到 0
"rcut": 6.0, // 截断半径(Å),超出此距离的原子不计入环境描述。
"neuron": [25, 50, 100], // 描述符神经网络层结构,3 层分别含 25、50、100 个神经元
"resnet_dt": false, // 禁用残差连接(ResNet)的时间步参数
"_type_one_side": true,
"axis_neuron": 12, // 旋转不变性描述符的轴神经元数,影响特征维度
"seed": 1 // 随机数种子
},
"fitting_net": { // 拟合网络
"neuron": [240, 240, 240], // 3 层全连接层,每层 240 个神经元,用于从描述符到原子能量的映射
"resnet_dt": true, // 启用残差连接,缓解深层网络梯度消失问题
"seed": 1 // 初始化种子
}
},
"learning_rate": { // 学习率策略
"type": "exp", // 指数衰减学习率,公式:lr = start_lr * (decay_rate)^(step/decay_steps)
"start_lr": 0.001, // 初始学习率(较高,用于快速收敛)
"_stop_lr": 3.51e-8,
"decay_steps": 4000, // 每 4000 训练步衰减一次 = stop_batch / 200
"_decay_rate": 0.99 // 每次衰减保留原学习率的 99%
},
"loss": { // 损失函数权重
"_type": "ener",
"start_pref_e": 0.02, // 能量项权重,从 0.02 逐步提升到 1
"limit_pref_e": 1,
"start_pref_f": 1000, // 力项权重,初始极高(1000),逐步降到1,强制优先拟合力的准确性
"limit_pref_f": 1,
"start_pref_v": 0.02, // 维里应力项权重,从 0.02 到 1,用于压力相关训练
"limit_pref_v": 1
},
"training": { // 训练过程参数
"_set_prefix": "set", // 训练集/验证集路径前缀,如 set.000
"stop_batch": 800000, // 最大训练步数
"disp_file": "lcurve.out", // 损失曲线输出文件
"disp_freq": 2000, // 每 2000 步输出一次训练信息
"numb_test": 4, // 验证集每次测试用 4 个样本
"save_freq": 2000, // 每 2000 步保存一次模型
"save_ckpt": "model.ckpt", // 模型检查点保存路径
"disp_training": true, // 显示训练集误差
"time_training": true, // 记录各阶段耗时
"profiling": false, // 禁用性能分析,启用会生成 timeline.json
"profiling_file": "timeline.json"
}
},
"_comment": " 01.model_devi ",
"model_devi_dt": 0.001, // LAMMPS timestep 0.001 ps
"model_devi_skip": 0, // 分析时跳过的初始步数,0 表示从第一步开始分析,常用于排除平衡阶段的不稳定数据
"model_devi_f_trust_lo": 0.15, // "0.10" 力预测偏差的信任下限(eV/Å)
"model_devi_f_trust_hi": 0.35, // "0.25" 力预测偏差的信任上限(eV/Å)
"model_devi_clean_traj": true, // 任务完成后自动删除原始轨迹文件
"model_devi_jobs": [
{"sys_idx": [0],"temps": [200, 300, 400, 500], "press": [1.0, 100.0, 1000.0, 10000.0, 50000.0], "trj_freq": 5, "nsteps": 1000, "ensemble": "npt_tri", "_idx": "00"},
{"sys_idx": [0],"temps": [200, 300, 400, 500], "press": [1.0, 100.0, 1000.0, 10000.0, 50000.0], "trj_freq": 5, "nsteps": 1000, "ensemble": "npt_tri", "_idx": "01"},
{"sys_idx": [0],"temps": [200, 300, 400, 500], "press": [1.0, 100.0, 1000.0, 10000.0, 50000.0], "trj_freq": 10, "nsteps": 3000, "ensemble": "npt_tri", "_idx": "02"},
{"sys_idx": [0],"temps": [200, 300, 400, 500], "press": [1.0, 100.0, 1000.0, 10000.0, 50000.0], "trj_freq": 10, "nsteps": 3000, "ensemble": "npt_tri", "_idx": "03"},
{"sys_idx": [0],"temps": [200, 300, 400, 500], "press": [1.0, 100.0, 1000.0, 10000.0, 50000.0], "trj_freq": 20, "nsteps": 5000, "ensemble": "npt_tri", "_idx": "04"},
{"sys_idx": [0],"temps": [200, 300, 400, 500], "press": [1.0, 100.0, 1000.0, 10000.0, 50000.0], "trj_freq": 20, "nsteps": 8000, "ensemble": "npt_tri", "_idx": "05"}
],
"_comment": " 02.fp ",
"fp_style": "vasp",
"shuffle_poscar": false, // 保持筛选出的 POSCAR 文件顺序,不打乱结构的时间序列
"fp_accurate_threshold": 0.999, // 如果精确比例大于这个数值,则不会执行 fp 计算,即 fp_task_max = 0
"fp_accurate_soft_threshold": 0.997, // 如果精确比例在这个数字和 fp_accurate_threshold 之间,fp_task_max 会线性衰减至零
"fp_task_max": 30, // 每个VASP任务最大包含50个结构(避免单个任务过大)
"fp_task_min": 5, // 至少积累10个新结构才启动VASP计算(提高计算效率)
"ratio_failed": 0.05, // 检查未成功终止的任务比例,如果过多 FP 任务未收敛,引发 RuntimeError
"fp_pp_path": "./",
"fp_pp_files": ["POTCAR_Zr", "POTCAR_Hf", "POTCAR_O"],
"fp_incar": "./INCAR_scf"
}2.2.3 提交 dpgen run 任务
nohup dpgen run param.json machine.json > log &
# 或
tmux
module load deepmd/2.2.7 && source activate && conda activate deepmd-2.2.7
dpgen run param.json machine.json 1>log 2>err &2.2.4 dpgen run 输出文件
inter.00000* # 一系列文件夹, 每个按序进行 training -> exploration -> labeling 过程,详情见下
record.dpgen # 记录已执行的任务阶段,DP-GEN 据此恢复 run 主进程
dpgen.log, dpdispatcher.log # 任务执行、分配的详细信息
log # 运行日志- * dpgen.log
记录详细计算流程
注意观察 02.fp 步骤中的 candidate、failed、accurate
- * record.dpgen
记录已执行的任务阶段
| 迭代步骤<br>(iter.00000*) | 迭代阶段<br>(task.0*) |迭代阶段名称<br>(make & run & post)|
|:------------------------------------------:|:--------------------------------------:|:------------------------------:|
| 0 | 0 | make_train |
| 0 | 1 | run_train |
| 0 | 2 | post_train |
|---------------|-----------|-------------------------|
| 0 | 3 | make_model_dev |
| 0 | 4 | run_model_devi |
| 0 | 5 | post_model_devi |
|---------------|-----------|-------------------------|
| 0 | 6 | make_fp |
| 0 | 7 | run_fp |
| 0 | 8 | post_fp |
|---------------|-----------|-------------------------|
| 1 | 0 | make_train |
| 1 | 1 | run_train |
| ... | ... | ... |
- * 势函数最终长训需调整 numb_steps 至数百万步,并调整 decay_steps = numb_steps/200
2.3 test
通过能量与体积的关系曲线(EOS)、弹性常数、表面能、 点缺陷形成能、层错能、声子等性质评估 DP 模型
2.3.1 模型测试
- * 压缩势函数,并链接到 LAMMPS 计算目录
cd run/iter.0000**/00.train/00[0-3]
dp compress -i frozen_model.pb -o graph.pb
cd work_dir
ln -s ~/home/work/hzo_dp/run/iter.000061/00.train/000/graph.pb- * 准备结构文件,运行 LAMMPS
module load deepmd/2.2.7 && source activate && conda activate deepmd-2.2.7
lmp -i in.lammps2.3.2 使用 autotest 模块自动化测试
- * 在当前文件夹准备 graph.pb、in.lammps 或 INCAR、POTCAR_*
- * 创建 "confs" 文件夹,并在其中准备 POSCAR 原胞文件;创建 "work" 文件夹
- * 分别对 relax 和 property 进行:准备 “make” 、运行 “run” 和后处理 “post”
dpgen autotest make relaxation.json && cp ./confs/relaxation/in.lammps . && dpgen autotest run relaxation.json machine.json && dpgen autotest post relaxation.json
# cp confs/relaxation/POTCAR .
dpgen autotest make property.json && dpgen autotest run property.json machine.json && dpgen autotest post property.json- * 机器配置文件 machine.json 可直接使用
- * VASP 自动化测试文件
# 相同部分
{
"structures": ["./confs"],
"interaction": {
"type": "vasp",
"incar": "./INCAR",
"potcar_prefix": "./",
"potcars": {"Zr": "POTCAR_Zr", "Hf": "POTCAR_Hf", "O": "POTCAR_O"}
},
# 结构优化 relaxation.json
"relaxation": {
"cal_type": "relaxation",
"cal_setting": {
"relax_pos": true,
"relax_shape": true,
"relax_vol": true,
"ediff": 1e-6,
"ediffg": -0.01,
"encut": 600,
"kspacing": 0.30,
"kgamma": false}
}
}
# 性质计算 property.json
"properties": [
{
"type": "eos",
"vol_start": 0.9,
"vol_end": 1.1,
"vol_step": 0.01,
"fix_shape": true
},
{
"type": "vacancy",
"supercell": [1, 1, 1]
}
]
}- * LAMMPS 自动化测试文件
# 相同部分
{
"structures": ["./confs"],
"interaction": {
"type": "deepmd",
"model": "./graph.pb",
"deepmd_version": "2.2.7",
"in_lammps": "./in.lammps",
"type_map": {"Zr": 0, "Hf": 1, "O": 2}
},
# 结构优化 relaxation.json
"relaxation": {
"cal_setting":{
"etol": 1e-12,
"ftol": 1e-10,
"maxiter": 10000,
"maximal": 500000,
"relax_shape": true,
"relax_vol": true
}
}
}
# 性质计算 property.json
"properties": [
{
"type": "eos",
"vol_start": 0.9,
"vol_end": 1.1,
"vol_step": 0.01,
"fix_shape": true
},
{
"type": "vacancy",
"supercell": [1, 1, 1]
}
]
}3. dpgen run 输出文件夹
3.1 __"inter.00000*/00.train"__ 文件夹(training)
- * graph.00*.pb:指向 00*/frozen.pb 的软链接文件(DeePMD-kit 的训练模型)
- * data.init:指向 param.json 中 init_data_prefix 的软链接文件
- * "00*" 文件夹(4个模型,000-003):
frozen_model.pb # DeePMD-kit 训练产生的模型
input.json # 当前任务的 DeePMD-kit 训练的输入文件
lcurve.out # 训练过程的误差,判断模型质量
train.log # 训练过程的标准输出3.2 __"inter.00000*/01.model_devi"__ 文件夹(exploration)
- * "conf" 文件夹:存储起始结构 POSCAR 与相应的 LAMMPS 格式起始结构
- * cur_job.json:
model_devi_jobs指定的当前迭代任务的 MD 参数信息
- * "task.00*.00000*" 文件夹:
- 00*:
model_devi_jobs["sys_idx"]中的某个元素(对应 sys_configs)的起始构象探索 - 00000*:若每次迭代只调用第 1 个元素 [0](10个构象),温度和压强各设置 4 和 5 个值,则有 200 个MD任务,对应 "task.000.00000[0-199]" 文件夹
完成后分别将轨迹和构象存储在此文件夹下:
conf.lmp # 当前任务 LAMMPS 格式的起始构象
input.lammps # 当前任务由 DP-GEN 自动生成的 LAMMPS 参数脚本
model_devi.out # 记录 DPMD 采样构象的能量和力的模型偏差- 根据
'trj_freq': 20,每 20 帧保存一帧构象轨迹 - 根据力误差设定值
'model_devi_f_trust_lo'和"model_devi_f_trust_hi"将探索构象筛选为 “候选构象”,发送到 "02.fp" 文件夹 model_devi_jobs指定的迭代进行完毕,会额外进行最后一个迭代:使用初始数据及迭代积累的所有新增数据,只进行 00.train 后主进程执行完成
3.3 __"inter.00000*/02.fp"__ 文件夹(labeling)
- * candidate.shuffle.00*.out:记录 “候选构象” 结构在 01.model_devi 中的任务号和帧数
- * rest_accurate.shuffle.00*.out:记录 max_devi_f 小于
model_devi_f_trust_lo的构象在 01.model_devi 中的任务号和帧数
- * rest_failed.shuffle.00*.out:记录 max_devi_f 大于
model_devi_f_trust_hi的构象在 01.model_devi 中的任务号和帧数
- * POTCAR.00*:相应结构的赝势文件
- * "task.00*.00000*" 文件夹:
- 00*:以某个元素为起始构象的探索
- 00000*:以
fp_task_max指定值进行DFT计算的任务序号 - 01.model_devi 筛选出的 “候选构象” 数量通常远大于 fp_task_max 设定的上限,DP-GEN 随机选择设定值数量的构象进行 DFT 计算
- * "data.00*" 文件夹:
- 存储 “候选构象” 的能量、力、维里等性质的DFT计算完成后,DP-GEN 将其收集并转换为 DeePMD-kit 格式的训练数据
- 新数据与初始数据一起被加入到扩大的数据集,在下一个迭代的 00.train 中开始新的DP势函数训练
- 若最后一个迭代的候选构象数小于
fp_task_min指定值,则该迭代直接拷贝上一迭代的 "00.train" 文件夹
4. DP 训练模型分析
4.1 模型训练分析
4.1.1 每次迭代准确、候选和失败构型的百分比
- * 模型精度达到 99.9% 以上,可认为势函数较好
- * 以 4 个迭代为一轮数据,进一步调参
cd run
python ~/home/document/scripts/iter_task_stats.py 60 60 # 指定开始和结束的迭代4.1.2 损失函数分析 lcurve.out
- * lcurve.out 共 8 列:训练步长、验证损失、训练损失、能量的训练误差、能量的测试误差、力的训练误差、力的测试误差、学习效率
- * 力的训练精度、测试误差比初始小一个数量级,则训练有效
cd run/iter.0000**/00.train/00[0-3]
python ~/home/document/scripts/lcurve_plot.py- * 绘图
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['pdf.fonttype'] = 42
plt.rcParams['mathtext.fontset'] = 'stix'
mpl.rcParams['font.serif'] = ['Times New Roman']
font = {'family':'Times New Roman', 'size':17, 'weight':'normal'}
font_l = {'family':'Times New Roman', 'size':14, 'weight':'normal'}
fig, ax = plt.subplots(1, 1, figsize=(5, 4), dpi=100)
# step rmse_trn rmse_e_trn (eV/atom) rmse_f_trn (eV/Å) rmse_v_trn (eV/atom) lr
data = np.genfromtxt("./data/lcurve_002.out", names=True)
for name in data.dtype.names[1:-1]:
# print(name)
# if name == 'rmse_f_trn':
print(data[name][-2000:].mean())
ax.plot(data['step'], data[name], label=name)
# gen L_t L_1 L_2 L_e_train (eV/atom) L_f_train (eV/Å) L_v_train (eV/atom) L_e_test L_f_test L_v_test
# data = np.genfromtxt("./data/loss.out", names=None)
# ax.plot(data[:, 0], data[:, 4], color="tab:red", label='rmse_e')
# ax.plot(data[:, 0], data[:, 5], color="tab:blue", label='rmse_f')
# ax.plot(data[:, 0], data[:, 6], color="tab:green", label='rmse_v')
ax.set_xlabel('Training Steps', fontdict=font)
ax.set_ylabel('Loss', fontdict=font)
# ax.set_xscale('symlog')
ax.set_yscale('log')
ax.grid(True, linestyle=':', alpha=0.7)
ax.legend(loc=0, prop=font_l)
# ax.set_xlim(-10000, 3000000)
# ax.set_ylim(0, 0.8)
ax.xaxis.get_offset_text().set_fontproperties({'family': 'Times New Roman', 'size': 14})
ax.tick_params(axis='both', which='major', direction='in', length=4, width=1.2, top=True, right=True, labelsize=16)
for tick in ax.get_xticklabels() + ax.get_yticklabels():
tick.set_fontname('Times New Roman')
for spine in ax.spines.values():
spine.set_linewidth(1.2)
plt.show()
# fig.savefig(f'HZO_DP_Loss.png', dpi=1000, bbox_inches='tight', transparent=False)4.2 最大偏差力的分布
- * 新模型对 MD 模拟中采样构象的模型偏差力判据保存在 "iter.00000*/01.model_devi/task*/model_devi.out"
- * 最大偏差力范围设置过高会导致非物理构型成为候选构型,参考:https://doi.org/10.1016/j.cpc.2020.107206
- * 迭代分析参考:https://doi.org/10.1063/5.0041849
4.2.1 迭代分析
- * 基于 max_devi_f_iter.py 脚本获得的 **max_devi_f_\*\*.dat**
cd run
python ~/home/document/scripts/max_devi_f_iter.py --iter 60 --plot 56 60 --xlim 0 0.3 # 不画图则去掉 --plot 参数- * 绘图
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['pdf.fonttype'] = 42
plt.rcParams['mathtext.fontset'] = 'stix'
font = {'family':'Times New Roman', 'size':17, 'weight':'normal'}
font_l = {'family':'Times New Roman', 'size':14, 'weight':'normal'}
fig, ax = plt.subplots(1, 1, figsize=(5, 4), dpi=100)
iter = 56
for i in range(iter-5, iter+1):
data = np.loadtxt(f'./data/max_devi_f_iter/max_devi_f_{i:02}.dat', skiprows=1)
ax.plot(data[:, 0], data[:, 1], linestyle='-', linewidth=1.5, alpha=0.9, label=f'iter {i:02}')
ax.set_xlabel(r'$\sigma^{max}_{f}\ \mathrm{(eV/\AA)}$', fontdict=font)
ax.set_ylabel('Distribution (%)', fontdict=font)
# ax.set_title("Distribution of Maximum Deviation Forces for " + r'$\mathrm{Hf_xZr_{1-x}O_2}$', fontdict=font)
ax.set_xlim(0, 0.3)
ax.legend(loc=0, edgecolor = 'k', fancybox=False,
prop=font_l, frameon=False).get_frame().set_linewidth(1)
ax.grid(True, linestyle='--', linewidth=0.6, alpha=0.7)
# ax.xaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
# ax.yaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
ax.tick_params(axis='both', which='major', direction='in', length=4, width=1, top=True, right=True, labelsize=17)
for tick in ax.get_xticklabels() + ax.get_yticklabels():
tick.set_fontname('Times New Roman')
for spine in ax.spines.values():
spine.set_linewidth(1)
# 插入辅助线及文本
ax.axvline(x=0.15, linestyle='--', dashes=(8, 4), linewidth=1, color='k', alpha=0.9)
ax.axvline(x=0.3, linestyle='--', dashes=(8, 4), linewidth=1, color='k', alpha=0.9)
ax.text(0.147, 2.2, r'$\sigma_{lo}=0.15$', horizontalalignment='right', fontdict=font_l)
ax.text(0.297, 2.2, r'$\sigma_{hi}=0.30$', horizontalalignment='right', fontdict=font_l)
plt.show()
# fig.savefig(f'HZO_DP_MDF_iter_{iter-5}-{iter}.png', dpi=1000, bbox_inches='tight', transparent=False)4.2.2 温度分析
- * 基于 max_devi_f_temp.sy 脚本获得的 **max_devi_f_temp_\*\*_\*\*-\*\*.dat**
cd run
python ~/home/document/scripts/max_devi_f_temp.py --iter 60 --temps 2700,2800,2900,3000,3100,3200 --plot --xlim 0 0.3 # 不画图则去掉 --plot 参数- * 绘图
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['pdf.fonttype'] = 42
plt.rcParams['mathtext.fontset'] = 'stix'
font = {'family':'Times New Roman', 'size':17, 'weight':'normal'}
font_l = {'family':'Times New Roman', 'size':14, 'weight':'normal'}
# 读取数据文件
file_path = './data/max_devi_temp/max_devi_f_50_2200-2600.dat'
with open(file_path, 'r') as f:
first_line = f.readline().strip()
temp_parts = first_line.lstrip('#').split()
temps = [temp_parts[i] + ' ' + temp_parts[i+1] for i in range(0, len(temp_parts), 2)]
print(temps)
data = np.loadtxt(file_path, skiprows=1)
fig, ax = plt.subplots(1, 1, figsize=(5, 4), dpi=100)
for i in range(len(temps)):
hist, bin_edges = np.histogram(data[:, i], range=(0, 0.6), bins=100)
hist_percentage = hist / len(data[:, i]) * 100
ax.plot(bin_edges[:-1], hist_percentage, linestyle='-', linewidth=1.5, alpha=0.9, label=temps[i])
ax.set_xlabel(r'$\sigma^{max}_{f}\ \mathrm{(eV/\AA)}$', fontdict=font)
ax.set_ylabel('Distribution (%)', fontdict=font)
# ax.set_title("Distribution of Maximum Deviation Forces for " + r'$\mathrm{Hf_xZr_{1-x}O_2}$', fontdict=font)
ax.set_xlim(0, 0.2)
ax.legend(loc=0, edgecolor = 'k', fancybox=False,
prop=font_l, frameon=False).get_frame().set_linewidth(1)
ax.grid(True, linestyle='--', linewidth=0.6, alpha=0.7)
# ax.xaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
# ax.yaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
ax.tick_params(axis='both', which='major', direction='in', length=4, width=1, top=True, right=True, labelsize=17)
for tick in ax.get_xticklabels() + ax.get_yticklabels():
tick.set_fontname('Times New Roman')
for spine in ax.spines.values():
spine.set_linewidth(1)
ax.axvline(x=0.15, linestyle='--', dashes=(8, 4), linewidth=1, color='k', alpha=0.9)
ax.axvline(x=0.30, linestyle='--', dashes=(8, 4), linewidth=1, color='k', alpha=0.9)
ax.text(0.147, 15, r'$\sigma_{lo}=0.15$', horizontalalignment='right', fontdict=font_l)
# ax.text(0.297, 15, r'$\sigma_{hi}=0.30$', horizontalalignment='right', fontdict=font_l)
plt.show()
# fig.savefig('HfO2_DP_MDF_temp_50.png', dpi=1000, bbox_inches='tight', transparent=False)4.3 DP 与 DFT 的误差分布对比
4.3.1 增加 DFT 数据
- * 使用 extract_e_dft.py 脚本提取 data.00* 中的 OUTCAR 数据,生成的 test_data 文件夹中包含整理后的 .npy 和 .raw 文件
- * 进入所有迭代(一般前 5 个)的 "iter.0000[20-24]/02.fp" 文件夹提取数据:
cd run
for i in $(seq -f "%06g" 24 29); do cd iter.$i/02.fp && pwd && python ~/home/document/scripts/extract_e_dft.py && cd ../../; done4.3.2 获取数据
- * DP 数据:最后一次迭代 "iter.000030/00.train" 的 00*/frozen_model.pb (4 个模型)
- * DFT 数据:前几次迭代 "iter.0000[24-29]/02.fp" 的 data.00* (candidates)
- * 创建并进入最后一个迭代的 "iter.000030/00.train/00x/dp_dft_data" 文件夹,执行:
cd run
[ ! -d "iter.000030/00.train/000/dp_dft_data" ] && mkdir iter.000030/00.train/000/dp_dft_data; cd iter.000030/00.train/000/dp_dft_data # 创建文件夹
for i in $(seq -f "%06g" 24 29); do dp test -m ../frozen_model.pb -s ../../../../iter.$i/02.fp/test_data -n 0 -d dp_dft_$i.out; done # 获取数据
# 第二次训练
python ~/home/document/scripts/extract_e_dft.py
dp test -m ./frozen_model.pb -s ~/home/work/hzo/run1/iter.000060/02.fp/test_data/ -n 0 -d dp_dft.out- * 模型准确率测试输出
DEEPMD INFO # ---------------output of dp test---------------
DEEPMD INFO # testing system : ../../../iter.000028/02.fp/test_data/Zr4O64Hf28
DEEPMD INFO # number of test data : 14
DEEPMD INFO Energy MAE : 2.409264e-01 eV
DEEPMD INFO Energy RMSE : 2.795323e-01 eV
DEEPMD INFO Energy MAE/Natoms : 2.509650e-03 eV
DEEPMD INFO Energy RMSE/Natoms : 2.911795e-03 eV
DEEPMD INFO Force MAE : 1.189774e-01 eV/A
DEEPMD INFO Force RMSE : 1.522510e-01 eV/A
DEEPMD INFO Virial MAE : 2.797758e+00 eV
DEEPMD INFO Virial RMSE : 4.525023e+00 eV
DEEPMD INFO Virial MAE/Natoms : 2.914332e-02 eV
DEEPMD INFO Virial RMSE/Natoms : 4.713566e-02 eV4.3.3 合并数据
- * 力的误差对比数据足够,不再合并
cd iter.000030/00.train/000/dp_dft_data
cat dp_dft_*.e.out > dp_dft_e.out && cat dp_dft_*.e_peratom.out > dp_dft_e_peratom.out && cat dp_dft_*.v.out > dp_dft_v.out && cat dp_dft_*.v_peratom.out > dp_dft_v_peratom.out4.3.4 绘图
- * 基于 dp_dft.e_peratom.out,__dp_dft_0000**.f.out__
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator, FormatStrFormatter
import matplotlib as mpl
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
import glob
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
import re
from matplotlib import cm
mpl.rcParams['pdf.fonttype'] = 42 # 可编辑pdf,字体:STIXGeneral-Regular
plt.rcParams['mathtext.fontset'] = 'stix' # 公式字体单独设置
font = {'family':'Times New Roman', 'size':18, 'weight':'normal'}
font_in = {'family':'Times New Roman', 'size':10, 'weight':'normal'}
font_le = {'family':'Times New Roman', 'size':12, 'weight':'normal'}
font_le2 = {'family':'Times New Roman', 'size':14, 'weight':'normal'}
font_text = {'family':'Times New Roman', 'size':20, 'weight':'normal'}
# 定义误差分析函数
def calc_err(true_val, pred_val):
r2 = r2_score(true_val, pred_val)
rmse = np.sqrt(mean_squared_error(true_val, pred_val))
return r2, rmse
# 按浓度分类读取能量数据
files = glob.glob('./data/dp_dft/dp_dft_e_peratom.out')
e_data = {}
for f in files:
with open(f, 'r') as file:
lines = file.readlines()
current_group = None
for line in lines:
if line.startswith("#"):
# 使用正则表达式提取Hf后的数字
match = re.search(r'Hf(\d+)', line)
if match:
Hf = float(match.group(1))
gkey = Hf/32
if gkey not in e_data:
e_data[gkey] = {'e_dft': [], 'e_dp': []}
current_group = gkey
else:
data = line.strip().split() # 读取数据行
if len(data) == 2 and current_group is not None:
e_dft = float(data[0])
e_dp = float(data[1])
e_data[current_group]['e_dft'].append(e_dft)
e_data[current_group]['e_dp'].append(e_dp)
# 正常读取能量数据
# e_data = np.vstack([np.loadtxt(f, comments='#') for f in glob.glob('./data/dp_dft/dp_dft_e_peratom.out')])
f_data = np.loadtxt('./data/dp_dft/dp_dft_000024.f.out', comments='#')
# e_dft = e_data[:, 0]
# e_dp = e_data[:, 1]
fx_dft, fy_dft, fz_dft = f_data[:, 0], f_data[:, 1], f_data[:, 2]
fx_dp, fy_dp, fz_dp = f_data[:, 3], f_data[:, 4], f_data[:, 5]
x = np.linspace(-20, 20, 400)
y = x
fig, axes = plt.subplots(1, 2, figsize=(10, 4), dpi=130)
#------------------------------------------Plot Energy Data-----------------------------------------#
sorted_e_data = sorted(e_data.items(), key=lambda x: x[0]) # 分类的能量数据从小到大排序
colors = cm.viridis(np.linspace(0, 1, len(sorted_e_data))) # 使用viridis颜色图
for i, (gkey, data) in enumerate(sorted_e_data):
e_dft = np.array(data['e_dft'])
e_dp = np.array(data['e_dp'])
axes[0].scatter(e_dft, e_dp, s=5, color=colors[i], alpha=0.8, label=f'x = {gkey:.3f}'.rstrip('0').rstrip('.'))
# axes[0].scatter(e_dft, e_dp, s=5, color='#1f78b4', alpha=0.8, label=r'$\mathrm{E}$')
axes[0].plot(x, y, color='grey', linestyle='-', linewidth=1.3, alpha=0.8)
tick_min_1 = -10.5
tick_max_1 = -9.4
axes[0].set_xlim(tick_min_1, tick_max_1)
axes[0].set_ylim(tick_min_1, tick_max_1)
axes[0].set_xticks(np.linspace(tick_min_1, tick_max_1, num=6))
axes[0].set_yticks(np.linspace(tick_min_1, tick_max_1, num=6))
# axes[0].set_title(r'$\mathrm{Hf_xZr_{1\minus x}O_2\ DP\ &\ DFT\ Energy}$', fontdict=font, pad=10)
axes[0].set_xlabel(r'$E_{\mathrm{DFT}}$ (eV/atom)', fontdict=font)
axes[0].set_ylabel(r'$E_{\mathrm{DP}}$ (eV/atom)', fontdict=font)
axes[0].xaxis.set_major_formatter(FormatStrFormatter('%.2f'))
axes[0].yaxis.set_major_formatter(FormatStrFormatter('%.2f'))
# 刻度线、标签及框线设置
axes[0].xaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
axes[0].yaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
axes[0].tick_params(axis='x', which='major', direction='in', length=5, width=1.2, top=True, labelsize=16)
axes[0].tick_params(axis='y', which='major', direction='in', length=5, width=1.2, right=True, labelsize=16)
axes[0].tick_params(axis='x', which='minor', direction='in', length=3, width=1.2, top=True)
axes[0].tick_params(axis='y', which='minor', direction='in', length=3, width=1.2, right=True)
labels = axes[0].get_xticklabels() + axes[0].get_yticklabels()
for label in labels:
label.set_fontname('Times New Roman')
label.set_weight('normal')
for spine in axes[0].spines.values():
spine.set_linewidth(1)
# # 线性拟合线
# coefficients = np.polyfit(e_dft, e_dp, 1)
# polynomial = np.poly1d(coefficients)
# e_dft_fit = np.linspace(min(e_dft), max(e_dft), 100)
# e_dp_fit = polynomial(e_dft_fit)
# axes[0].plot(e_dft_fit, e_dp_fit, color='grey', linestyle='-', linewidth=1.5)
axes[0].legend(loc=0, edgecolor = 'k', fancybox=False, title=r'$\mathrm{Hf_xZr_{1\minus x}O_2}$', title_fontsize=12,
prop=font_le, frameon=False, labelspacing=0.2, handletextpad=0.2).get_frame().set_linewidth(1) # frameon=False不显示边框
#-----------------------------------------Plot Delta_E Data----------------------------------------#
ax1_in = inset_axes(axes[0], width="35%", height="35%", loc='lower right',
bbox_to_anchor=(-0.02, 0.09, 1, 1), bbox_transform=axes[0].transAxes)
e_diff = np.abs(e_dft-e_dp)*1000 # eV -> meV
e_diff_mean = np.mean(e_diff)
counts_1, bins_1, patches_1 = ax1_in.hist(e_diff, bins=10, color='#fcc797', alpha=0.8,
edgecolor='black', linewidth=0.5, rwidth=1)
n_1 = np.sum(counts_1)
p2 = (counts_1/n_1)*100
ax1_in.clear()
width1 = np.diff(bins_1)
ax1_in.bar(bins_1[:-1], p2, width=0.8*width1, color='#fcc797', alpha=0.9,
edgecolor='black', linewidth=0.5, align='edge')
ax1_in.set_title(r'$\left|E_{\mathrm{DFT}} - E_{\mathrm{DP}}\right|$ (meV/atom)', fontdict=font_in)
ax1_in.set_ylabel('Frequency (%)', fontdict=font_in)
xlim_in1 = ax1_in.get_xlim()
# ax1_in.set_xlim(0, xlim_in1[1])
ax1_in.set_xticks(np.linspace(0, 12, num=4))
# ax1_in.set_yticks(np.linspace(0, 60, num=4))
ax1_in.tick_params(axis='both', which='major', labelsize=10, direction='out')
for tick in ax1_in.get_xticklabels() + ax1_in.get_yticklabels():
tick.set_fontname('Times New Roman')
# 计算 MAE, R² 和 RMSE
r2_e, rmse_e = calc_err(e_dft, e_dp)
# print(f"R²_E: {r2_e:.4f}, RMSE_E: {rmse_e:.4f} meV/atom")
axes[0].text(0.85, 0.94, rf'$\mathrm{{RMSE={rmse_e*1000:.3f}\ meV/atom}}$',
transform=axes[0].transAxes, fontsize=11, color='#441d6c', fontname='Times New Roman', ha='right', va='top')
axes[0].text(0.575, 0.84, rf'$\mathrm{{R^2}}={r2_e:.3f}$',
transform=axes[0].transAxes, fontsize=11, color='#441d6c', fontname='Times New Roman', ha='right', va='top')
print(f"MAE_E: {e_diff_mean:.4f} meV/atom")
# axes[0].text(0.86, 0.96, rf'$\mathrm{{\langle\left|E_{{DFT}} \minus E_{{DP}}\right|\rangle = {e_diff_mean:.2f}\ meV/atom}}$',
# transform=axes[0].transAxes, fontsize=10, color='k', fontname='Times New Roman', ha='right', va='top')
#------------------------------------------Plot Force Data------------------------------------------#
axes[1].scatter(fx_dft, fx_dp, s=5, color='#fbe625', alpha=0.8, label=r'$F_\mathrm{x}$')
axes[1].scatter(fy_dft, fy_dp, s=5, color='#31af7c', alpha=0.8, label=r'$F_\mathrm{y}$')
axes[1].scatter(fz_dft, fz_dp, s=5, color='#441d6c', alpha=0.8, label=r'$F_\mathrm{z}$')
axes[1].plot(x, y, color='grey', linestyle='-', linewidth=1.3, alpha=0.8)
tick_min_2 = -15
tick_max_2 = 15
axes[1].set_xlim(tick_min_2 , tick_max_2)
axes[1].set_ylim(tick_min_2 , tick_max_2)
axes[1].set_xticks(np.linspace(tick_min_2 , tick_max_2, num=7))
axes[1].set_yticks(np.linspace(tick_min_2 , tick_max_2, num=7))
# axes[1].set_title(r'$\mathrm{Hf_xZr_{1\minus x}O_2\ DP\ &\ DFT\ Force}$', fontdict=font, pad=10)
axes[1].set_xlabel(r'$F_\mathrm{DFT}$ ($\mathrm{eV/\AA}$)', fontdict=font)
axes[1].set_ylabel(r'$F_\mathrm{DP}$ ($\mathrm{eV/\AA}$)', fontdict=font)
# axes[1].xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
# axes[1].yaxis.set_major_formatter(FormatStrFormatter('%.0f'))
# 刻度线、标签及框线设置
axes[1].xaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
axes[1].yaxis.set_minor_locator(AutoMinorLocator(2)) # 主刻度线间2个次刻度线
axes[1].tick_params(axis='x', which='major', direction='in', length=5, width=1.2, top=True, labelsize=16)
axes[1].tick_params(axis='y', which='major', direction='in', length=5, width=1.2, right=True, labelsize=16)
axes[1].tick_params(axis='x', which='minor', direction='in', length=3, width=1.2, top=True)
axes[1].tick_params(axis='y', which='minor', direction='in', length=3, width=1.2, right=True)
labels = axes[1].get_xticklabels() + axes[1].get_yticklabels()
for label in labels:
label.set_fontname('Times New Roman')
label.set_weight('normal')
for spine in axes[1].spines.values():
spine.set_linewidth(1)
axes[1].legend(loc=0, edgecolor = 'k', fancybox=False, handletextpad=0.2,
prop=font_le2, frameon=False).get_frame().set_linewidth(1) # frameon=False不显示边框
#-----------------------------------------Plot Delta_F Data----------------------------------------#
ax2_in = inset_axes(axes[1], width="35%", height="35%", loc='lower right',
bbox_to_anchor=(-0.02, 0.09, 1, 1), bbox_transform=axes[1].transAxes)
f_diff = np.abs(fx_dft-fx_dp)
f_diff_mean = np.mean(f_diff)
counts_2, bins_2, patches_2 = ax2_in.hist(f_diff, bins=10, color='#fcc797', alpha=0.8,
edgecolor='black', linewidth=0.5, rwidth=1) # 将数据分为 10 个区间
n_2 = np.sum(counts_2)
p2 = (counts_2/n_2)*100
ax2_in.clear()
width2 = np.diff(bins_2)
ax2_in.bar(bins_2[:-1], p2, width=0.8*width2, color='#fcc797', alpha=0.9,
edgecolor='black', linewidth=0.5, align='edge')
ax2_in.set_title(r'$\left|F_\mathrm{DFT} - F_\mathrm{DP}\right|\ (\mathrm{eV/\AA})$', fontdict=font_in)
ax2_in.set_ylabel('Frequency (%)', fontdict=font_in)
xlim_in2 = ax2_in.get_xlim()
# ax2_in.set_xlim(0, xlim_in2[1])
ax2_in.set_xticks(np.linspace(0, 1.2, num=4))
# ax2_in.set_yticks(np.linspace(0, 40, num=3))
ax2_in.tick_params(axis='both', which='major', labelsize=10, direction='out')
for tick in ax2_in.get_xticklabels() + ax2_in.get_yticklabels():
tick.set_fontname('Times New Roman')
# 计算 MAE, R² 和 RMSE
r2_fz, rmse_fz = calc_err(fz_dft, fz_dp)
# print(f"R²_Fz: {r2_fz:.4f}, RMSE_Fz: {rmse_fz:.4f} eV/Å")
axes[1].text(0.76, 0.94, rf'$\mathrm{{RMSE={rmse_fz:.3f}\ eV/\AA}}$',
transform=axes[1].transAxes, fontsize=11, color='#441d6c', fontname='Times New Roman', ha='right', va='top')
axes[1].text(0.58, 0.84, rf'$\mathrm{{R^2}}={r2_fz:.3f}$',
transform=axes[1].transAxes, fontsize=11, color='#441d6c', fontname='Times New Roman', ha='right', va='top')
print(f"MAE_Fz: {f_diff_mean:.4f} eV/Å")
# axes[1].text(0.86, 0.96, rf'$\mathrm{{\langle\left|F_{{DFT}} \minus F_{{DP}}\right|\rangle = {f_diff_mean:.2f}\ eV/\AA}}$',
# transform=axes[1].transAxes, fontsize=10, color='k', fontname='Times New Roman', ha='right', va='top')
# axes[0].text(-0.26, 0.95, '(a)', font=font_text, transform=axes[0].transAxes)
# axes[1].text(-0.21, 0.95, '(b)', font=font_text, transform=axes[1].transAxes)
# 调整子图间的垂直/水平间隔
fig.subplots_adjust(hspace=0.0, wspace=0.3)
plt.show()
# fig.savefig('HZO_DP_DFT_iter_20-24.png', dpi=1000, bbox_inches='tight', transparent=False)5. 机器配置文件 machine.json
- * 控制工作流,涉及计算任务收发、环境设置及提交命令、远程计算节点的机型等,可在同一机器上重复使用
- * 针对 training、exploration、labeling 3 个训练步骤分别独立设置
- * 每步指定 3 个模块:
- "command": 任务运行命令
- "machine": 调用的机器(本地机器、SSH远程、队列系统)、任务路径
- "resources": 调用的资源(CPU/GPU数、加载环境)
- * 注:检查"resouce"下的"source_list"、"module_list",以及fp的"command"
{
"api_version": "1.0",
"deepmd_version": "2.2.11",
"train": [
{
"command": "dp",
"machine": {
"batch_type": "Slurm",
"context_type": "local",
"local_root" : "./",
"remote_root": "./work"
},
"resources": {
"number_node": 2,
"cpu_per_node": 30,
"gpu_per_node": 0,
"queue_name": "huawei",
"_source_list": [""],
"module_list": [""],
"group_size": 4
}
}
],
"model_devi": [
{
"command": "lmp",
"machine": {
"batch_type": "Slurm",
"context_type": "local",
"local_root" : "./",
"remote_root": "./work"
},
"resources": {
"number_node": 2,
"cpu_per_node": 30,
"gpu_per_node": 0,
"queue_name": "huawei",
"_source_list": [""],
"_module_list": [""],
"group_size": 30
}
}
],
"fp": [
{
"command": "mpirun -n 60 vasp_std",
"machine": {
"batch_type": "Slurm",
"context_type": "local",
"local_root" : "./",
"remote_root": "./work"
},
"resources": {
"number_node": 2,
"cpu_per_node": 30,
"gpu_per_node": 0,
"queue_name": "huawei",
"_source_list": [""],
"_module_list": [""],
"group_size": 30
}
}
]
}