atomppgen.kb 源代码

"""
Kleinman-Bylander 可分离非局域势转换

将半局域势 {V_l(r)} 转换为可分离形式:

    V_NL = Σ_l |β_l⟩ D_l ⟨β_l|

其中投影子 β_l(r) 和耦合系数 D_l 通过以下方式构造:

1. 选择局域道 V_loc(r) = V_l*(r)(通常选择未占据的高角动量通道,如 d)
2. 构造未归一化投影子:χ_l(r) = [V_l(r) - V_loc(r)] · φ_l(r)
3. 归一化:β_l(r) = χ_l(r) / √⟨χ_l|χ_l⟩,使得 ⟨β_l|β_l⟩ = 1
4. 耦合系数:D_l = ⟨χ_l|χ_l⟩ / ⟨φ_l|ΔV|φ_l⟩
   其中 ΔV = V_l - V_loc

技术要点
--------
- 局域道选择:优先选择无占据态的高 l 通道(如 Al 的 d 通道)
- 径向规约波函数:φ_l(r) = u_l(r)/r
- 数值稳定:r→0 时 u_l ~ r^{l+1},φ_l ~ r^l,β_l 在原点有限
- 耦合系数:采用归一化投影子形式,D = W/Z 保证物理等价性

主要功能
--------
kb_transform : 半局域势到 KB 可分离形式的转换
KBResult : 存储 KB 转换结果的数据类

参考文献
--------
Kleinman & Bylander, PRL 48, 1425 (1982)
Giannozzi, Notes on pseudopotential generation (2019)
"""

from dataclasses import dataclass
from typing import Dict
import numpy as np

from atomppgen.invert import InvertResult


__all__ = [
    "KBResult",
    "kb_transform",
]


[文档] @dataclass class KBResult: """ Kleinman-Bylander 转换结果 Attributes ---------- V_loc : np.ndarray 局域势 V_loc(r)(Hartree) beta_l : Dict[int, np.ndarray] 投影子 {l: β_l(r)}(Bohr^{-3/2},已归一化) D_l : Dict[int, float] 耦合系数 {l: D_l}(Hartree) loc_channel : int 局域通道角动量量子数 l* r : np.ndarray 径向网格(Bohr) diagnostics : Dict 诊断信息: - n_channels : 通道数 - projector_norms : 归一化前的投影子模 {l: ⟨β_l|β_l⟩} - coupling_strengths : 耦合强度 {l: D_l} - loc_potential_max : 局域势最大值 - loc_potential_min : 局域势最小值 """ V_loc: np.ndarray beta_l: Dict[int, np.ndarray] D_l: Dict[int, float] loc_channel: int r: np.ndarray diagnostics: Dict
[文档] def kb_transform( invert_results: Dict[int, InvertResult], u_by_l: Dict[int, np.ndarray], r: np.ndarray, w: np.ndarray, loc_channel: int = 2, ) -> KBResult: """ 将半局域势转换为 Kleinman-Bylander 可分离形式 对于每个非局域通道 l ≠ l*,构造投影子: β_l(r) = [V_l(r) - V_loc(r)] · φ_l(r) 其中 φ_l(r) = u_l(r)/r 是径向规约波函数。 归一化条件:⟨β_l|β_l⟩ = 1,耦合系数 D_l = ⟨β_l|[V_l - V_loc]|β_l⟩。 在归一化投影子的情况下,D_l = 1/⟨β_l(原始)|β_l(原始)⟩。 Parameters ---------- invert_results : Dict[int, InvertResult] 半局域势反演结果 {l: InvertResult} u_by_l : Dict[int, np.ndarray] 径向波函数 {l: u_l(r)} r : np.ndarray 径向网格(Bohr) w : np.ndarray 积分权重 loc_channel : int, default=2 局域通道 l*,默认为 d 通道(l=2) Returns ------- KBResult 包含局域势、投影子、耦合系数和诊断信息 Raises ------ ValueError 如果局域通道不在 invert_results 中 如果网格长度不匹配 Notes ----- 1. **局域道选择**:优先选择无占据态的高角动量通道(如 Al 的 d)。 占据通道作为局域道会引入额外的数值问题。 2. **径向规约波函数**:φ_l(r) = u_l(r)/r,满足径向薛定谔方程。 在 r→0 时,u_l ~ r^{l+1},因此 φ_l ~ r^l 有限。 3. **耦合系数公式**: 标准 KB 形式:V_NL = |χ⟩ D ⟨χ|,其中 D = 1/⟨φ|ΔV|φ⟩ 本实现使用归一化投影子 β = χ/√⟨χ|χ⟩,因此: D_l = ⟨χ|χ⟩ / ⟨φ|ΔV|φ⟩ = W / Z 其中 W = ∫ |χ|² w dr,Z = ∫ ΔV · φ² w dr 4. **数值稳定**:原点附近 r→0 时使用 np.maximum(r, r_min) 保护除法。 Examples -------- >>> from atomppgen import solve_ae_atom, tm_pseudize, invert_semilocal_potential >>> ae = solve_ae_atom(Z=13, spin_mode='LDA', lmax=2) >>> invert_results = {} >>> for l in [0, 1, 2]: ... tm = tm_pseudize(ae.r, ae.w, ae.u_by_l[l][-1], ae.eps_by_l[l][-1], l, rc=2.0+0.1*l) ... invert_results[l] = invert_semilocal_potential(tm, ae.r) >>> kb = kb_transform(invert_results, {l: ae.u_by_l[l][-1] for l in [0,1,2]}, ... ae.r, ae.w, loc_channel=2) >>> print(kb.diagnostics['n_channels']) # 3 (s, p, d) """ # 参数验证 if loc_channel not in invert_results: raise ValueError(f"局域通道 l={loc_channel} 不在 invert_results 中") for l, inv in invert_results.items(): if len(inv.r) != len(r): raise ValueError(f"通道 l={l} 的网格长度 {len(inv.r)} 与输入网格 {len(r)} 不匹配") if l not in u_by_l: raise ValueError(f"通道 l={l} 缺少径向波函数 u_l") if len(w) != len(r): raise ValueError(f"积分权重长度 {len(w)} 与网格长度 {len(r)} 不匹配") # 提取局域势 V_loc = invert_results[loc_channel].V_l.copy() # 构造投影子和耦合系数 beta_l = {} D_l = {} projector_norms = {} for l, inv in invert_results.items(): if l == loc_channel: # 局域通道不需要投影子 continue # 计算径向规约波函数 φ_l = u_l/r # 保护除法:r→0 时使用最小半径 r_safe = np.maximum(r, 1e-10) phi_l = u_by_l[l] / r_safe # 构造投影子 β_l = [V_l - V_loc] · φ_l delta_V = inv.V_l - V_loc beta_raw = delta_V * phi_l # 计算归一化前的模 W = ⟨χ|χ⟩ = ∫ |χ|² w dr # 其中 χ = ΔV · φ_l(未归一化投影子) W = np.sum(beta_raw**2 * w) if W <= 0: raise ValueError(f"通道 l={l} 的投影子模平方 ⟨χ|χ⟩={W:.3e} ≤ 0,无法归一化") # 归一化投影子 β = χ / √W beta_normalized = beta_raw / np.sqrt(W) # 耦合系数 D_l = W / Z # 其中 Z = ⟨φ|ΔV|φ⟩ = ∫ ΔV · φ² w dr Z = np.sum(delta_V * (phi_l**2) * w) if abs(Z) < 1e-12: raise ValueError(f"通道 l={l} 的分母 ⟨φ|ΔV|φ⟩={Z:.3e} 接近零,无法计算耦合系数") D_l_val = W / Z beta_l[l] = beta_normalized D_l[l] = D_l_val projector_norms[l] = W # 诊断信息 diagnostics = { 'n_channels': len(invert_results), 'projector_norms': projector_norms, 'coupling_strengths': D_l.copy(), 'loc_potential_max': float(np.max(V_loc)), 'loc_potential_min': float(np.min(V_loc)), } return KBResult( V_loc=V_loc, beta_l=beta_l, D_l=D_l, loc_channel=loc_channel, r=r, diagnostics=diagnostics, )