Abinit Smearing(占据数展宽)计算:参数 + 代码实现详解
基于 Abinit 10.4.7 源码分析(src/61_occeig/m_occ.F90 为主)
引言:为什么需要 smearing?
在金属(或带隙极小的半导体)的 DFT 自洽计算中,Fermi 面附近的能带占据数会随 k 点位置发生阶跃式变化(零温台阶函数),导致:
- k 点积分收敛极慢:需要极密的 k 网格才能精确积分;
- SCF 不稳定:占据数对本征值梯度无穷大,每一步 SCF 占据数可能"跳来跳去";
- 结构优化噪声:相邻几何下不同能带跨越 Fermi 能级,能量曲线出现台阶。
Smearing(展宽)方法用一个光滑的 $\tilde{\delta}(x)$ 函数代替零温的台阶,把占据数变成连续可微的函数 $f(\epsilon-\mu)$。这相当于引入一个"假温度"(或真温度),让 Fermi 面附近的态分数占据。
Abinit 把所有"金属型"占据方案统一封装在 src/61_occeig/m_occ.F90 中。
输入参数总览
下表整理了与 smearing 直接相关的所有输入变量,定义位置为 abimkdocs/variables_abinit.py。
| 输入变量 | 类型 | 默认值 | 单位 | 作用 |
|---|---|---|---|---|
occopt | int | 1 | — | 占据方案,控制 $\tilde{\delta}(x)$ 的形式(0–9) |
tsmear | real | 0.01 | Ha(可改 eV/Ry/K) | smearing 宽度 / 电子温度 |
tphysel | real | 0.0 | Ha | 物理电子温度(与 tsmear 配合做"双重 smearing") |
spinmagntarget | real | −99.99 | μ_B | 固定磁矩;非默认时分自旋通道分别确定 Fermi 能级 |
dosdeltae | real | 0.0 | Ha | DOS 输出的能量步长(option=2) |
ivalence | int | 0 | — | 仅 occopt=9:价带最高指标 |
nqfd | real | 0.0 | — | 仅 occopt=9:约束的激发电子/空穴数 |
smdelta | int | 0 | — | DFPT 用,电子寿命计算中的 δ 函数类型(与 occopt 类似但独立) |
prtdos | int | 0 | — | 是否输出 DOS(=1 调用 getnel 的 option=2 分支) |
关键依赖关系:
occopt ∈ {0,1,2}:绝缘体/半导体,不进入本文讨论的 smearing 路径;occopt ∈ {3,4,5,6,7,8,9}:金属型,调用newocc→getnel→init_occ_ent;- 当
tphysel > 0且tsmear > 0时启用"双重 smearing"(dblsmr=1),原始 FD 分布与occopt指定的核函数做卷积; tsmear是单位 Hartree 的能量,0.001 Ha ≈ 27.2 meV ≈ 315.8 K。
八种 smearing 函数公式(occopt = 3..8)
下面所有公式中 $x \equiv (\mu - \epsilon)/k_BT$(注意 Abinit 用 arg = (fermie - eigen)*tsmearinv,与常见教科书定义符号相反)。$\tilde{\delta}(x)$ 是光滑的"展宽 δ 函数"(DOS 核),其积分 $f(x) = \int_{-\infty}^x \tilde{\delta}(x')dx'$ 就是占据数函数。
occopt = 3:Fermi–Dirac
$$\tilde{\delta}(x) = \frac{1}{(e^{x/2}+e^{-x/2})^2} = \frac{1}{4\cosh^2(x/2)}$$
$$f(x) = \frac{1}{1 + e^{-x}}$$
物理含义:真正的有限温热力学占据;tsmear 此时是真实的电子温度。代码位置:m_occ.F90:1116。
occopt = 4 与 occopt = 5:Marzari 冷展宽
$$\tilde{\delta}(x) = \frac{1}{\sqrt{\pi}} e^{-x^2}\bigl[,1.5 - 1.5,a,x - x^2 + a,x^3,\bigr]$$
occopt = 4:$a = -0.5634$(最小化非物理"凸起")occopt = 5:$a = -0.8165$(保证占据函数单调)
特点:高阶矩为零 → 能量对 tsmear 仅 $O(tsmear^3)$ 误差。代码位置:m_occ.F90:1120-1133。
occopt = 6:Methfessel–Paxton(2 阶 Hermite)
$$\tilde{\delta}(x) = \frac{1}{\sqrt{\pi}}(1.5 - x^2)e^{-x^2}$$
等价于 Marzari 冷展宽中 $a=0$ 的情形。代码位置:m_occ.F90:1135-1143。
occopt = 7:Gaussian
$$\tilde{\delta}(x) = \frac{1}{\sqrt{\pi}}e^{-x^2}$$
$$f(x) = \frac{1}{2}[1 + \mathrm{erf}(x)]$$
MP 的 0 阶。代码位置:m_occ.F90:1145-1153。最稳定、推荐默认值。
occopt = 8:均匀展宽(仅测试用)
$$\tilde{\delta}(x) = \begin{cases} 1 & |x| < 1/2 \ 1/2 & |x| = 1/2 \ 0 & |x| > 1/2 \end{cases}$$
代码位置:m_occ.F90:1155-1168。
occopt = 9:双 quasi-Fermi 能级(激发态)
强制在价带($\le$ ivalence)留 nqfd 个空穴,导带留 nqfd 个电子,分别求解两个独立的 Fermi 能级 $\mu_e$ 和 $\mu_h$,均使用 Fermi–Dirac 形式。详见 Paillard et al. 2019。
双重 smearing(tphysel>0 且 tsmear>0)
dblsmr=1 时把 FD 函数与上述某个核 $\tilde{\delta}_{\text{occopt}}$ 做卷积:
$$\tilde{\delta}{\text{dbl}}(x) = \int dy, \tilde{\delta}{\text{FD}}(y) \cdot \tilde{\delta}_{\text{occopt}}!\bigl(\tfrac{x-y}{r}\bigr) \cdot \tfrac{1}{r}$$
其中 $r = \tt{tsmear/tphysel}$。这样 tphysel 是物理温度,tsmear 是 k 点收敛性附加展宽。代码位置:m_occ.F90:1174-1395。
能量修正(encorr)
对 occopt=4..7,自由能 $E_{\text{free}}$ 与零温能 $E_{\text{phys}}$ 的关系为:
$$E_{\text{phys}} = E_{\text{free}} - \alpha,(E_{\text{internal}} - E_{\text{free}}) + O(\tt{tsmear}^3)$$
代码用二阶矩比 encorr = smom2 * tratio² / secmom 估算 $\alpha$(m_occ.F90:1390)。
核心调用链:从 SCF 到占据数
m_gstate.F90 (SCF 驱动器)
│
├─ vtorho (求解 H ψ = ε ψ,得到 eigen)
│
└─ newocc (m_occ.F90:453) ← 主入口,二分法求 fermie
│
└─ getnel (m_occ.F90:124) ← 给定 fermie 计算 nelect、occ、entropy
│
├─ init_occ_ent (m_occ.F90:1007)
│ 建表:xgrid, smdfun, occfun, entfun,仅在 occopt/tsmear/tphysel
│ 变化时重新计算(带 SAVE 缓存)
│
└─ splfit (m_splines.F90)
对每条本征值做三次样条插值,从表中读出 occ 和 ent
上层入口:m_ebands.F90:2294 的 ebands_has_metal_scheme 决定是否走 metallic 分支:
ans = (any(ebands%occopt == [3, 4, 5, 6, 7, 8, 9]))
init_occ_ent:建立查找表(一次性工作)
位置:m_occ.F90:1007-1512,约 500 行。
关键常量
integer,parameter :: nptsdiv2_def = 6000 ! 网格半宽点数
real(dp),parameter :: limit_occ = 6.0_dp ! 一般情况积分截断
= 30.0_dp ! occopt=3 或 9 (FD 尾长)
= 30 + 6*tratio ! 双重 smearing
real(dp),parameter :: maxFDarg = 500.0_dp ! FD 数值上溢保护缓存机制(避免每次重算)
init_occ_ent 用 Fortran SAVE 属性保存上次调用的网格:
integer,save :: occopt_prev = -9999
real(dp),save :: tphysel_prev = -9999, tsmear_prev = -9999
real(dp),save :: smdfun_prev(...), occfun_prev(...), entfun_prev(...), xgrid_prev(...)
仅当 occopt、tsmear 或 tphysel 任一变化时才重新生成表(m_occ.F90:1046)。这对 EPH 等需要在多温度下扫描的代码非常关键。
表的构建步骤
第 1 步:建立无量纲网格
increm = limit_occ / nptsdiv2_def ! 步长 ≈ 0.001 (或 0.005 对 FD)
do ii = -nptsdiv2_def, nptsdiv2_def
xgrid_prev(ii) = ii*increm
end do
第 2 步:在每个 xgrid 点求 $\tilde{\delta}(x)$ 值(按 occopt 走不同分支,公式见上一章)。
第 3 步:积分得到占据函数 $f(x)$ 与熵函数 $s(x)$
使用 4 阶 Simpson 改进算法(Numerical Recipes 4.1.14, $O(1/N^4)$ 收敛):
occfun_prev(ii,1) = occfun_prev(ii-1,1) + increm * ( &
17*smdfun_prev(ii,1) + 42*smdfun_prev(ii-1,1) &
- 16*smdfun_prev(ii-2,1) + 6*smdfun_prev(ii-3,1) &
- smdfun_prev(ii-4,1) ) / 48.0_dp
熵函数:$s(x) = -\int x',\tilde{\delta}(x'),dx'$(注意 Abinit 把 $kT$ 提到外面作为前因子)。
第 4 步:归一化
factor = 1.0_dp / occfun_prev(nptsdiv2_def,1)
smdfun_prev(:,1) = smdfun_prev(:,1) * factor
occfun_prev(:,1) = occfun_prev(:,1) * factor
entfun_prev(:,1) = entfun_prev(:,1) * factor
第 5 步:三次样条系数
对 smdfun、occfun、entfun 各调用一次 spline 算二阶导数,存到 (:,2)。后续插值用 splfit 即可在 O(1) 时间内对任意 $x$ 求值。
第 6 步:返回 tsmearinv
if (|tphysel| < tol12) tsmearinv = 1/tsmear
else tsmearinv = 1/tphysel
getnel:给定 Fermi 能级求 N、占据数、熵
位置:m_occ.F90:124-409,含两条路径(option=1 求 N/occ;option=2 输出 DOS)。
输入
eigen(mband*nkpt*nsppol):本征能量(Hartree)fermie:试探的 Fermi 能级tsmear,tphysel,occopt,wtk(nkpt),maxocc=2/(nsppol*nspinor)
算法步骤(option=1)
步骤 1:构造无量纲变量
do isppol = 1, nsppol
do ikpt = 1, nkpt
do iband = 1, nband(...)
arg(index) = (fermie - eigen(index_tot+iband)) * tsmearinv
end do
end do
end do
若 tsmear=0:arg = sign(huge_tsmearinv, fermie-eigen),即返回严格 Heaviside。
步骤 2:样条插值查表
call splfit(xgrid, doccde_tmp, occfun, 1, arg, occ_tmp, 2*nptsdiv2+1, number_of_bands)
call splfit(xgrid, derfun, entfun, 0, arg, ent, 2*nptsdiv2+1, number_of_bands)
occfun 的导数(占据数对能量的导数)也同时返回到 doccde_tmp。
步骤 3:加权求和
nelect = 0; entropy = 0
do isppol, ikpt, iband
occ(index_tot+iband) = occ_tmp(index) * maxocc
doccde(...) = -doccde_tmp(index) * maxocc * tsmearinv
nelect = nelect + wtk(ikpt) * occ(...)
entropy = entropy + wtk(ikpt) * ent(index) * maxocc
end do
关键归一化:
maxocc = 2/(nsppol*nspinor):自旋通道每个 k 点的最大占据;- 负号
-doccde_tmp:因为arg ∝ -ε,链式求导多一个负号; - 因子
tsmearinv:把无量纲导数转换为对能量的导数。
算法步骤(option=2,输出 DOS)
对能量网格 enex = enemin + i*deltaene:
arg(:) = (enex - eigen(:)) * tsmearinv;splfit在smdfun上求 DOS 贡献;- 同时对
arg*2、arg*0.5各求一次,输出"半宽 DOS"和"双宽 DOS"做收敛判断; - 写入
unitdos:enex dostot intdostot doshalftot dosdbletot。
newocc:二分法找 Fermi 能级
位置:m_occ.F90:453-991,约 540 行。这是 SCF 真正调用的入口(除了首次读 WFK 文件外)。
算法(标准情形,occopt=3..8)
初始化边界:
fermie_lo = min(eigen) - 6.001*tsmear ! Gaussian 情形
if (occopt==3 .or. ==9) fermie_lo -= 24*tsmear ! FD 尾长加大
fermie_hi = max(eigen) + 6.001*tsmear (+24*tsmear)
计算两端的电子数:
call getnel(... fermie_lo, ..., nelectlo, ...)
call getnel(... fermie_hi, ..., nelecthi, ...)
应保证 nelectlo < nelect_target < nelecthi。
二分迭代(最多 niter_max = 120 次):
do ii = 1, niter_max
fermie_mid = (fermie_hi + fermie_lo) * 0.5
call getnel(... fermie_mid, ..., nelectmid, ...)
if (nelectmid > nelect*(1-tol14)) then
fermie_hi = fermie_mid; nelecthi = nelectmid
end if
if (nelectmid < nelect*(1+tol14)) then
fermie_lo = fermie_mid; nelectlo = nelectmid
end if
if (|nelecthi-nelectlo| <= 2*nelect*tol14) exit ! 收敛
if (|fermie_hi-fermie_lo| <= tol14*|sum|) exit
end do
为何选二分法而不是 Newton:
- 鲁棒:占据函数即使非单调(
occopt=4,6)也能收敛到一个解; - 精度
tol14(约 1e-14)几乎机器精度; - 收敛代价 ~50 次迭代(120 步上限保险),每步一次
getnel,开销远低于一次 SCF 迭代。
固定磁矩分支(spinmagntarget ≠ -99.99)
对每个自旋通道 is=1,2 独立做二分:
nelectt(1) = (nelect + spinmagntarget) / 2 ! spin-up 目标
nelectt(2) = (nelect - spinmagntarget) / 2 ! spin-down 目标
两个通道得到不同的 Fermi 能级 $\mu_\uparrow$、$\mu_\downarrow$,但全局只输出一个 fermie(最后一次循环值)。
occopt=9 分支(双 quasi-Fermi)
并行做两次二分:
- 高能侧:约束
ne_qFD个激发电子 → $\mu_e$ - 低能侧:约束
nelect - nh_qFD个剩余电子(即留nh_qFD个空穴)→ $\mu_h$
熵相加:entropy = entropye + entropyh。
熵如何进入总能(Mermin 自由能)
位置:src/67_common/m_dft_energy.F90:1148-1156。
energies%entropy = energies%entropy_ks ! 来自 newocc/getnel
+ entropy_paw + entropy_xc + entropy_extfpmd
if (occopt >= 3 .and. occopt <= 8) then
if (|tphysel| < tol10) then
energies%e_entropy = - dtset%tsmear * energies%entropy
else
energies%e_entropy = - dtset%tphysel * energies%entropy
end if
else
energies%e_entropy = zero
end if
总能(src/44_abitypes_defs/m_results_gs.F90:113):
$$E_{\text{tot}} = E_k + E_{\text{Hart}} + E_{\text{xc}} + E_{ei} + E_{\text{Ewald}} + E_{ii} + E_{nl} - T\cdot S ,[+ E_{\text{PAW}}]$$
被最小化的是 Mermin 自由能 $F = E - TS$,而非内能;这正是变分原理在有限温下的推广。
辅助函数:解析的 FD/BE
m_occ.F90 还提供独立调用的函数(用于响应函数、EPH 等):
| 函数 | 位置 | 公式 |
|---|---|---|
occ_fd(ε, kT, μ) | 1643 | $1/[\exp((\epsilon-\mu)/k_BT)+1]$ |
occ_dfde(ε, kT, μ) | 1695 | $-\exp(x)/[k_BT(e^x+1)^2]$ |
occ_be(ε, kT, μ) | 1739 | $1/[\exp((\epsilon-\mu)/k_BT)-1]$(Bose) |
occ_dbe(ε, kT, μ) | 1783 | $\exp(x)/[k_BT(e^x-1)^2]$ |
都做了上下溢出保护:maxFDarg=500,maxDFDarg=200,maxBEarg=600,maxDBEarg=200。
参数选择建议
| 场景 | 推荐设置 |
|---|---|
| 一般金属(如 Al) | occopt 7, tsmear 0.01 Ha |
| d 带金属(如 Cu, Fe) | occopt 7, tsmear 0.001~0.005 Ha;务必做 k 点收敛 |
| 半导体/分子 | occopt 1(无 smearing) |
| 真实有限温(Born–Oppenheimer MD 中的电子温度) | occopt 3 配合 tsmear = k_BT |
| 仅为收敛性引入展宽,但想保留物理温度 | occopt 4..7 + tphysel(双重 smearing) |
| 高精度结构优化 | occopt 7(避免 4/5/6 的非单调性导致能量跳变) |
| 激发态约束计算(ΔSCF) | occopt 9 + ivalence + nqfd |
收敛检查的不二法门:固定 k 网格扫 tsmear,再固定 tsmear 加密 k 网格,直到能量、力相对变化 < 收敛标准。
关键代码地图
| 模块 / 子程序 | 文件 | 行号 | 作用 |
|---|---|---|---|
m_occ(模块) | src/61_occeig/m_occ.F90 | 1–1973 | 全部 smearing 实现 |
newocc | 同上 | 453 | 二分法求 Fermi 能级(主入口) |
getnel | 同上 | 124 | 给定 fermie 算 N、occ、S |
init_occ_ent | 同上 | 1007 | 建表(核函数、占据函数、熵函数、样条系数) |
occ_fd / occ_dfde | 同上 | 1643 / 1695 | 解析 FD 占据数及导数 |
occeig | 同上 | 1552 | DFPT 用:能带对占据的雅可比 |
ebands_has_metal_scheme | src/61_occeig/m_ebands.F90 | 2172 | 判断是否走 metallic 路径 |
entropy(汇总) | src/67_common/m_dft_energy.F90 | 1130 | 整合 KS/PAW/xc/extfpmd 熵 |
splfit / spline | shared/common/src/28_numeric_noabirule/m_splines.F90 | — | 三次样条插值 |
| SCF 端调用 | src/95_drive/m_gstate.F90 | 902 / 2090 | SCF 起始重设占据 + DOS 输出 |
附录:完整的"一次 newocc 调用"逐步追踪
设输入 occopt=7, tsmear=0.01 Ha, nelect=8, 已有 100 个本征值 eigen(:)。
-
newocc入口:检查occopt ∈ [3,9]、nelect > 0、nband*maxocc ≥ nelect。 -
边界:
fermie_lo = min(eigen) - 0.06 Ha,fermie_hi = max(eigen) + 0.06 Ha。 -
左端电子数:
- 调
getnel(fermie_lo, ...):init_occ_ent:因首次调用,构造 12001 点 Gaussian 表(耗时 ~5 ms);- 算
arg(i) = (fermie_lo - eigen(i)) / 0.01,由于fermie_lo远低于所有 eigen,所有arg < -6→occfun_prev末端值 = 0; - 输出
nelectlo ≈ 0。
- 调
-
右端:
nelecthi ≈ 2*100 = 200(全占满)。 -
二分循环:
iter 1: fermie_mid = (lo+hi)/2, getnel → nelectmid = 150 → 偏高 → fermie_hi = mid iter 2: ... → 95 ... iter ~50: |nelecthi - nelectlo| < 2*8*1e-14 → 退出 -
赋值:
fermie = fermie_mid,entropy = entropye, 输出占据数occ(:)和doccde(:)。 -
回到
m_gstate:把fermie、entropy_ks、occ存入results_gs%energies,传给下一次 SCF 迭代。 -
下次 SCF:
init_occ_ent因occopt/tsmear/tphysel未变,立即返回缓存表(耗时 ~微秒),整个二分过程仅 ~50 × splfit 调用 ≈ 数毫秒。
报告生成于 2026-05-19,基于 Abinit 10.4.7。
References
Gonze, Xavier, et al. "The Abinit Project: Impact, Environment and Recent Developments." Computer Physics Communications, vol. 248, 2020, p. 107042. link
Marzari, Nicola, and David Vanderbilt. "Maximally Localized Generalized Wannier Functions for Composite Energy Bands." Physical Review B, vol. 56, no. 20, 1997, pp. 12847–12865. link
Mermin, N. David. "Thermal Properties of the Inhomogeneous Electron Gas." Physical Review, vol. 137, no. 5A, 1965, pp. A1441–A1443. link
Methfessel, M., and A. T. Paxton. "High-Precision Sampling for Brillouin-Zone Integration in Metals." Physical Review B, vol. 40, no. 6, 1989, pp. 3616–3621. link
Paillard, Charles, et al. "Noncollinear Magnetism from First Principles." Physical Review B, vol. 100, no. 2, 2019, p. 024426. link
The Abinit Code. Version 10.4.7, 2024. link