GPAW专题

GPAW状态方程与Δ曲线比较

用硅的七点体积扫描演示状态方程拟合、每原子单位与ASE Δ比较。

更新于 2026-11-09约 13 分钟阅读按任务与环境选择适用配置
阅读本篇目录

用硅的七点体积扫描演示状态方程拟合、每原子单位与ASE Δ比较。

1 下载、提交与运行#

下载完整操作包,保存为gpaw-eos-delta.zip。在集群解压后,普通批作业可以执行sbatch job.slurm;下面的直接运行命令用于已经分配好的CPU资源内,不要在登录节点运行生产计算。

python3 -m zipfile -e gpaw-eos-delta.zip .
cd gpaw-eos-delta
bash setup.sh
bash run.sh
bash verify.sh
bash plot.sh

脚本按1个MPI进程、1个线程设置运行环境。先完成电子结构计算,再提取数据并画图;原始结果在results/,两张图片在plots/。verify.sh用于与附件的原样模型对照;改变体系后应自行检查科学收敛,不能继续沿用原模型数值作为目标。

2 先按体积比例建模#

用a=5.43 Å的金刚石Si原胞(2个原子),参考原胞体积40.02575175 ų。体积比例取0.94、0.97、1.00、1.03、1.06、1.09、1.12,晶胞长度乘比例的立方根,原子分数坐标保持不变。这是固定分数坐标的体积扫描,不是每个体积下再做原子优化。

附件实际计算PW300 eV、4×4×4 k点的七个体积点;另附此前同一模型PW250 eV、2×2×2的七个实际数值作对照,不为这七个旧点重复SCF。PBE、12条带、0.01 eV展宽,能量/密度/本征态收敛1E−6/1E−5/1E−8。

体积比例 新曲线原胞总能量/eV
0.94 −10.703029234
0.97 −10.758231357
1.00 −10.787559945
1.03 −10.794664319
1.06 −10.782640670
1.09 −10.754394911
1.12 −10.712344459

3 拟合状态方程#

先把体积与能量都除以原子数,再用EquationOfState(V, E, eos='birchmurnaghan')拟合。平衡体积单位ų/atom,体模量从eV/ų换成GPa;不要把原胞体积和每原子能量混在一个拟合里。

GPAW状态方程与Δ曲线比较:实际结构与计算数据1

4 Δ指标比较的是曲线#

本环境ase.utils.deltacodesdft.delta输入顺序为两套(V₀,B₀,B′),V₀用每原子体积,B₀用eV/ų。返回eV/atom,乘1000得到meV/atom。这里原生ASE算法对每条曲线的最低点作能量对齐,用100个中点求均方根;它不等同于另行扣除区间平均差的连续积分。

曲线比较定义 结果/meV·atom⁻¹
当前原生ASE Δ 1.6368128524
显式均值对齐的连续积分 1.6318533683

两者定义不同,所以分别列出。积分区间19.32953145–21.79713121 ų/atom完全位于两条实际采样曲线内,没有把拟合外推段当作已计算数据。

GPAW状态方程与Δ曲线比较:实际结构与计算数据2

这两套输入同时改变截断能和k网格,因此这个差不能归因于某一个参数,也不构成收敛证明。它只是当前Si模型的两条GPAW曲线比较,没有完成全元素周期表或与Wien2k的标准数据库对比。

旧版GPAW DeltaCodesDFT练习依赖已移除的ASE接口;本例使用当前可用的ASE状态方程和Δ函数。参考:GPAW旧练习的版本说明、当前ASE Δ实现。

完整计算输入#

以下是附件实际执行的run.py,含结构、计算器、提取流程与输出定义。先原样运行,再按上文说明更改模型;inputs.json同时记录示例条件与旧曲线/目标轨道信息。

展开完整Python输入
from pathlib import Path
import os, json, hashlib, numpy as np
from ase.build import bulk
from ase.eos import EquationOfState, birchmurnaghan
from ase.utils.deltacodesdft import delta
from scipy.integrate import quad
from gpaw import GPAW, PW, FermiDirac
R = Path(__file__).resolve().parent / 'results'
R.mkdir()
p = json.loads((R.parent / 'inputs.json').read_text())
checks = []
rows = []

def ck(n, b, **kw):
    checks.append(dict(name=n, passed=bool(b), **kw))
for f, h in p['source_SHA'].items():
    assert hashlib.sha256(Path(f).read_bytes()).hexdigest() == h
for f, h in p['old_sources_SHA'].items():
    assert hashlib.sha256(Path(f).read_bytes()).hexdigest() == h
old = []
for row in p['old_curve']:
    old.append(row)
    ck('old_converged:' + str(row['scale']), row['passed'])
for scale in p['scales']:
    D = R / f'v{scale:.2f}'
    D.mkdir()
    a = bulk('Si', 'diamond', a=5.43)
    a.set_cell(a.cell * scale ** (1 / 3), scale_atoms=True)
    c = GPAW(legacy_gpaw=True, mode=PW(300), xc='PBE', kpts=(4, 4, 4), nbands=12, occupations=FermiDirac(0.01), convergence=dict(energy=1e-06, density=1e-05, eigenstates=1e-08), maxiter=200, txt=str(D / 'native.txt'))
    a.calc = c
    e = float(a.get_potential_energy())
    c.write(str(D / 'state.gpw'), mode='all')
    force = a.get_forces()
    rho = c.get_all_electron_density(gridrefinement=1)
    ne = float(rho.sum() * a.get_volume() / rho.size)
    occs = np.array([c.get_occupation_numbers(kpt=k) for k in range(c.wfs.kd.nibzkpts)])
    ck(str(scale) + ':AE28', abs(ne - 28) < 0.03, value=ne)
    ck(str(scale) + ':valence8', abs(occs.sum() - 8) < 1e-07)
    ck(str(scale) + ':symmetric_force', abs(force).max() < 0.02)
    ck(str(scale) + ':true_volume_not_lattice_scale', abs(a.get_volume() / p['reference_cell_volume_A3'] - scale) < 1e-12)
    ck(str(scale) + ':finite', np.isfinite([e, ne, *force.ravel()]).all() and np.isfinite(rho).all())
    np.savez_compressed(D / 'fields.npz', AE_total_density_e_A3=rho, cell_A=a.cell.array, positions_A=a.positions, forces_eV_A=force, eigenvalues_eV=np.array([c.get_eigenvalues(kpt=k) for k in range(c.wfs.kd.nibzkpts)]), weighted_occupations=occs, IBZ_weights=c.get_k_point_weights())
    row = dict(scale=scale, volume_A3=a.get_volume(), energy_eV=e, atoms=2, AE_electrons=ne, scf_iterations=c.get_number_of_iterations())
    rows.append(row)
    (R / 'PARTIAL-RESULTS.json').write_text(json.dumps(rows, indent=2) + '\n')
    print(scale, e, a.get_volume(), flush=True)
fits = {}
for label, data in [('PW250-k2-reused', old), ('PW300-k4', rows)]:
    v = np.array([r['volume_A3'] / 2 for r in data])
    e = np.array([r['energy_eV'] / 2 for r in data])
    fit = EquationOfState(v, e, eos='birchmurnaghan')
    v0, e0, b0 = fit.fit()
    e0, b0, bp, v0 = map(float, fit.eos_parameters)
    pred = birchmurnaghan(v, *fit.eos_parameters)
    rmse = float(np.sqrt(np.mean((pred - e) ** 2)))
    ck(label + ':minimum_in_sampled_volume_range', min(v) < v0 < max(v))
    ck(label + ':positive_bulk_modulus', b0 > 0)
    ck(label + ':fit_RMSE', rmse < 0.005, value=rmse)
    fits[label] = dict(v0_A3_atom=v0, e0_eV_atom=e0, B0_eV_A3=b0, Bprime=bp, fit_RMSE_eV_atom=rmse, volumes_A3_atom=v.tolist(), energies_eV_atom=e.tolist())
a, b = fits.values()
args = [a['v0_A3_atom'], a['B0_eV_A3'], a['Bprime'], b['v0_A3_atom'], b['B0_eV_A3'], b['Bprime']]
native = float(delta(*args))
vref = (args[0] + args[3]) / 2
lo = 0.94 * vref
hi = 1.06 * vref
v = lo + (np.arange(100) + 0.5) * (hi - lo) / 100
ea = birchmurnaghan(v, 0, args[1], args[2], args[0])
eb = birchmurnaghan(v, 0, args[4], args[5], args[3])
independent = float(np.sqrt(np.mean((ea - eb) ** 2)))
ck('native_ASE100point_RMS_independent', abs(native - independent) < 1e-12)
ck('native_symmetric_exchange', abs(native - float(delta(*args[3:], *args[:3]))) < 1e-12)
ck('native_self_delta_zero', abs(float(delta(*args[:3], *args[:3]))) < 1e-14)

def diff(v):
    return float(birchmurnaghan(v, 0, args[1], args[2], args[0]) - birchmurnaghan(v, 0, args[4], args[5], args[3]))
mean = quad(diff, lo, hi, epsabs=1e-13)[0] / (hi - lo)
centered = np.sqrt(quad(lambda x: (diff(x) - mean) ** 2, lo, hi, epsabs=1e-14)[0] / (hi - lo))
dense = np.linspace(lo, hi, 10001)
de = birchmurnaghan(dense, 0, args[1], args[2], args[0]) - birchmurnaghan(dense, 0, args[4], args[5], args[3])
dm = np.trapezoid(de, dense) / (hi - lo)
dc = np.sqrt(np.trapezoid((de - dm) ** 2, dense) / (hi - lo))
shift = 4.7
shiftmean = mean + shift
shifted = np.sqrt(quad(lambda x: (diff(x) + shift - shiftmean) ** 2, lo, hi, epsabs=1e-14)[0] / (hi - lo))
ck('explicit_mean_centered_continuum_dense_quadrature', abs(centered - dc) < 1e-07)
ck('explicit_mean_centered_energy_offset_invariance', abs(centered - shifted) < 1e-12)
for label, data in [('coarse', old), ('fine', rows)]:
    sampled = [r['volume_A3'] / 2 for r in data]
    ck(label + ':whole_integral_window_inside_actual_samples', min(sampled) <= lo and max(sampled) >= hi, sampled_range=[min(sampled), max(sampled)], integral_range=[lo, hi])
for f, h in p['old_sources_SHA'].items():
    ck('original_EOS_source_immutable:' + f, hashlib.sha256(Path(f).read_bytes()).hexdigest() == h)
for f, h in p['source_SHA'].items():
    ck('library_immutable:' + f, hashlib.sha256(Path(f).read_bytes()).hexdigest() == h)
out = dict(passed=all((x['passed'] for x in checks)), checks=len(checks), items=checks, old_curve=old, new_curve=rows, fits=fits, summary=dict(native_ASE_delta_eV_atom=native, native_ASE_delta_meV_atom=1000 * native, explicit_mean_centered_continuum_eV_atom=float(centered), explicit_mean_centered_continuum_meV_atom=float(centered * 1000), integration_range_A3_atom=[lo, hi]), whole_group_complete=False, ASE_native_minima_aligned_100point_RMS_and_mean_centered_continuum_distinguished=True, no_all_element_DeltaCodesDFT_or_convergence_claim=True)
(R / 'VALIDATION.json').write_text(json.dumps(out, indent=2) + '\n')
print('418', out['passed'], out['checks'], out['summary'], flush=True)
assert out['passed']

终端查看结果#

计算后先查看逐项结果,再打开图。下面只读取输出,不会启动新的自洽:

python3 -c "import json; v=json.load(open('results/VALIDATION.json')); print(v.get('results')); print(v.get('summary', {}))"
source env.sh
python3 -c "import numpy as np; d=np.load('results/v0.94/fields.npz'); print([(k, d[k].shape) for k in d.files])"
ls -lh plots/*.png

环境:GPAW26.7.0、GCC13.3、OpenMPI5.0.10rc2、MKL2023.2、Libxc7.0、Python3.11.16。模块为gpaw/26.7.0-8173m;实际输入由Python/ASE组织,使用附件的env.sh加载一致环境。附件不打包PAW数据集或二进制检查点,运行时使用集群已安装数据集。

需要根据你的体系选择资源?联系讨论计算需求。实际可用软件与权限,以当前账户和运行环境为准。

查看配图

放大后可滚动查看细节,按 Esc 关闭。

100%查看原图 ↗