Abinit Smearing(占据数展宽)计算:参数与代码实现详解

May 19, 2026
Published in 计算物理

Abstract

本文总结了Abinit当中smearing的参数细节以及代码实现的方法。

Keywords: Abinit, DFT, Smearing, 占据数, Fermi-Dirac, Mermin 自由能

Abinit Smearing(占据数展宽)计算:参数 + 代码实现详解

基于 Abinit 10.4.7 源码分析(src/61_occeig/m_occ.F90 为主)


引言:为什么需要 smearing?

在金属(或带隙极小的半导体)的 DFT 自洽计算中,Fermi 面附近的能带占据数会随 k 点位置发生阶跃式变化(零温台阶函数),导致:

  1. k 点积分收敛极慢:需要极密的 k 网格才能精确积分;
  2. SCF 不稳定:占据数对本征值梯度无穷大,每一步 SCF 占据数可能"跳来跳去";
  3. 结构优化噪声:相邻几何下不同能带跨越 Fermi 能级,能量曲线出现台阶。

Smearing(展宽)方法用一个光滑的 $\tilde{\delta}(x)$ 函数代替零温的台阶,把占据数变成连续可微的函数 $f(\epsilon-\mu)$。这相当于引入一个"假温度"(或真温度),让 Fermi 面附近的态分数占据。

Abinit 把所有"金属型"占据方案统一封装在 src/61_occeig/m_occ.F90 中。


输入参数总览

下表整理了与 smearing 直接相关的所有输入变量,定义位置为 abimkdocs/variables_abinit.py

输入变量类型默认值单位作用
occoptint1占据方案,控制 $\tilde{\delta}(x)$ 的形式(0–9)
tsmearreal0.01Ha(可改 eV/Ry/K)smearing 宽度 / 电子温度
tphyselreal0.0Ha物理电子温度(与 tsmear 配合做"双重 smearing")
spinmagntargetreal−99.99μ_B固定磁矩;非默认时分自旋通道分别确定 Fermi 能级
dosdeltaereal0.0HaDOS 输出的能量步长(option=2)
ivalenceint0occopt=9:价带最高指标
nqfdreal0.0occopt=9:约束的激发电子/空穴数
smdeltaint0DFPT 用,电子寿命计算中的 δ 函数类型(与 occopt 类似但独立)
prtdosint0是否输出 DOS(=1 调用 getnel 的 option=2 分支)

关键依赖关系

  • occopt ∈ {0,1,2}:绝缘体/半导体,不进入本文讨论的 smearing 路径;
  • occopt ∈ {3,4,5,6,7,8,9}:金属型,调用 newoccgetnelinit_occ_ent
  • tphysel > 0tsmear > 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 = 4occopt = 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>0tsmear>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:2294ebands_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(...)

仅当 occopttsmeartphysel 任一变化时才重新生成表(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 步:三次样条系数smdfunoccfunentfun 各调用一次 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=0arg = 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

  1. arg(:) = (enex - eigen(:)) * tsmearinv
  2. splfitsmdfun 上求 DOS 贡献;
  3. 同时对 arg*2arg*0.5 各求一次,输出"半宽 DOS"和"双宽 DOS"做收敛判断;
  4. 写入 unitdosenex 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=500maxDFDarg=200maxBEarg=600maxDBEarg=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.F901–1973全部 smearing 实现
newocc同上453二分法求 Fermi 能级(主入口)
getnel同上124给定 fermie 算 N、occ、S
init_occ_ent同上1007建表(核函数、占据函数、熵函数、样条系数)
occ_fd / occ_dfde同上1643 / 1695解析 FD 占据数及导数
occeig同上1552DFPT 用:能带对占据的雅可比
ebands_has_metal_schemesrc/61_occeig/m_ebands.F902172判断是否走 metallic 路径
entropy(汇总)src/67_common/m_dft_energy.F901130整合 KS/PAW/xc/extfpmd 熵
splfit / splineshared/common/src/28_numeric_noabirule/m_splines.F90三次样条插值
SCF 端调用src/95_drive/m_gstate.F90902 / 2090SCF 起始重设占据 + DOS 输出

附录:完整的"一次 newocc 调用"逐步追踪

设输入 occopt=7, tsmear=0.01 Ha, nelect=8, 已有 100 个本征值 eigen(:)

  1. newocc 入口:检查 occopt ∈ [3,9]nelect > 0nband*maxocc ≥ nelect

  2. 边界fermie_lo = min(eigen) - 0.06 Hafermie_hi = max(eigen) + 0.06 Ha

  3. 左端电子数

    • getnel(fermie_lo, ...)
      • init_occ_ent:因首次调用,构造 12001 点 Gaussian 表(耗时 ~5 ms);
      • arg(i) = (fermie_lo - eigen(i)) / 0.01,由于 fermie_lo 远低于所有 eigen,所有 arg < -6occfun_prev 末端值 = 0;
      • 输出 nelectlo ≈ 0
  4. 右端nelecthi ≈ 2*100 = 200(全占满)。

  5. 二分循环

    iter 1: fermie_mid = (lo+hi)/2, getnel → nelectmid = 150 → 偏高 → fermie_hi = mid
    iter 2: ...                                                  → 95
    ...
    iter ~50: |nelecthi - nelectlo| < 2*8*1e-14 → 退出
  6. 赋值fermie = fermie_mid, entropy = entropye, 输出占据数 occ(:)doccde(:)

  7. 回到 m_gstate:把 fermieentropy_ksocc 存入 results_gs%energies,传给下一次 SCF 迭代。

  8. 下次 SCFinit_occ_entoccopt/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