Skip to content

Commit 6102479

Browse files
committed
Feat(deltaspin): implement DeltaQS unified charge-spin constraint framework
Extend DeltaSpin to DeltaQS, unifying charge (DeltaQ) and spin (DeltaSpin) constraints as orthogonal projections of a single Lagrange framework: E = E_KS + sum_I mu_I(N_I - N_target) + sum_I lambda_I(M_I - M_target) S1: Core SCF module - SpinConstrain class extended with mu/Ni/target_charge, Operator contributeHR() supports (mu+lambda, mu-lambda) potential, joint lambda loop (run_qs_lambda_loop), charge projection cal_ni_lcao() S2: Gradient extraction - write_gradient_file(), print_Ni/Charge_Force, cal_charge_escon() for charge constraint energy correction S3: Grid scan - run_qs_grid_scan() for 2D E(N,M) potential surface mapping S4: Gradient descent optimizer - run_qs_gradient_descent() with line search S5: L-BFGS optimizer - run_qs_lbfgs() with history window for high-dim S6: Attribution analysis - run_qs_attribution() for A/B/C classification New INPUT: sc_charge_switch, sc_qs_mode, sc_charge_thr, sc_charge_alpha, sc_ground_state_search, sc_outer_thr, sc_gradient_output New STRU keywords: tc (target charge), cq (charge constraint flag), mu Backward compatible: sc_charge_switch=false preserves original DeltaSpin
1 parent 21c1ba0 commit 6102479

15 files changed

Lines changed: 1481 additions & 13 deletions

File tree

docs/deltaqs_implementation.md

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
# DeltaQS 统一框架实现文档
2+
3+
## 概述
4+
5+
本文档记录了将 DeltaSpin(自旋约束 DFT)扩展为 DeltaQS(电荷-自旋联合约束 DFT)的完整实现。DeltaQS 将 DeltaSpin 和 DeltaQ 统一为同一 Lagrange 框架在两个正交子空间上的投影,实现了电荷和磁矩的联合约束与基态优化。
6+
7+
## 理论基础
8+
9+
### 统一 Lagrange 泛函
10+
11+
$$E_{\text{total}} = E_{KS} + \sum_I \mu_I (N_I - N_I^{\text{target}}) + \sum_I \lambda_I (M_I - M_I^{\text{target}})$$
12+
13+
有效势:
14+
$$v_{\text{eff}}^\alpha = v_{KS}^\alpha + \sum_I (\mu_I + \lambda_I) w_I$$
15+
$$v_{\text{eff}}^\beta = v_{KS}^\beta + \sum_I (\mu_I - \lambda_I) w_I$$
16+
17+
其中 μ_I 是电荷 Lagrange 乘子,λ_I 是自旋 Lagrange 乘子。
18+
19+
### 三个模式的关系
20+
21+
| 模式 | μ | λ | 有效势修正 |
22+
|------|---|---|-----------|
23+
| DeltaSpin | 0 | λ | α: +λw, β: -λw |
24+
| DeltaQ | μ | 0 | α: +μw, β: +μw |
25+
| DeltaQS | μ | λ | α: (μ+λ)w, β: (μ-λ)w |
26+
27+
### 梯度信息(包络定理)
28+
29+
$$\frac{\partial E}{\partial N_I^{\text{target}}} = -\mu_I, \qquad \frac{\partial E}{\partial M_I^{\text{target}}} = -\lambda_I$$
30+
31+
一次 SCF 计算同时给出 E 和梯度 (−μ, −λ),不需要有限差分。
32+
33+
## 文件变更清单
34+
35+
### 新增文件
36+
37+
| 文件 | 说明 |
38+
|------|------|
39+
| `source/source_lcao/module_deltaspin/deltaqs.cpp` | DeltaQS 全部 S1-S6 功能实现 |
40+
41+
### 修改文件
42+
43+
| 文件 | 变更说明 |
44+
|------|---------|
45+
| `source/source_io/module_parameter/input_parameter.h` | 新增 9 个 DeltaQS INPUT 参数 |
46+
| `source/source_io/module_parameter/read_input_item_other.cpp` | 注册新参数的解析、验证和文档 |
47+
| `source/source_cell/atom_spec.h` | Atom 结构体新增 `target_charge`, `mu`, `constrain_charge` 字段 |
48+
| `source/source_cell/read_atoms_helper.cpp` | 分配新字段 + STRU 解析 `tc`/`cq`/`mu` 关键词 |
49+
| `source/source_cell/unitcell.h` | 新增 3 个 getter 方法声明 |
50+
| `source/source_cell/unitcell.cpp` | 实现 `get_target_charge()`, `get_mu()`, `get_constrain_charge()` |
51+
| `source/source_lcao/module_deltaspin/spin_constrain.h` | SpinConstrain 类扩展:数据成员、方法声明 |
52+
| `source/source_lcao/module_deltaspin/spin_constrain.cpp` | `cal_escon()` 增加电荷约束能量项 |
53+
| `source/source_lcao/module_deltaspin/deltaspin_lcao.cpp` | Facade 层集成 DeltaQS 初始化和联合循环 |
54+
| `source/source_lcao/module_deltaspin/template_helpers.cpp` | `double` 特化的所有新方法 no-op stubs |
55+
| `source/source_lcao/module_deltaspin/CMakeLists.txt` | 添加 `deltaqs.cpp` |
56+
| `source/source_lcao/module_operator_lcao/dspin_lcao.h` | 新增 `mu_save` 成员 |
57+
| `source/source_lcao/module_operator_lcao/dspin_lcao.cpp` | `contributeHR()` 支持 (μ+λ, μ-λ) 势 |
58+
59+
## 分阶段实现详情
60+
61+
### S1: DeltaQS SCF 核心模块
62+
63+
#### S1.1 SpinConstrain 类扩展
64+
65+
新增数据成员(`spin_constrain.h`):
66+
67+
```cpp
68+
// 电荷约束数据(每原子)
69+
std::vector<double> mu_; // 电荷 Lagrange 乘子 (Ry/e)
70+
std::vector<double> target_charge_; // 目标投影电荷 (electrons)
71+
std::vector<double> Ni_; // 当前计算投影电荷
72+
std::vector<int> constrain_charge_; // 电荷约束标志 (0=free, 1=constrained)
73+
74+
// 配置参数
75+
bool charge_constraint_enabled_;
76+
std::string qs_mode_; // "deltaspin", "deltaq", "deltaqs", "auto"
77+
double sc_charge_thr_; // 电荷收敛阈值
78+
double charge_alpha_trial_; // μ 更新步长 (Ry/e²)
79+
double charge_restrict_current_; // μ 最大步长 (Ry/e)
80+
bool ground_state_search_;
81+
int outer_max_iter_;
82+
double outer_thr_;
83+
bool gradient_output_;
84+
```
85+
86+
#### S1.2 Operator 修改(dspin_lcao.cpp)
87+
88+
核心变更:`contributeHR()` 中的增量势计算。
89+
90+
**原 DeltaSpin (nspin=2):**
91+
```
92+
coeff_up = +delta_lambda_z
93+
coeff_down = -delta_lambda_z
94+
```
95+
96+
**DeltaQS (nspin=2):**
97+
```
98+
coeff_up = delta_mu + delta_lambda_z
99+
coeff_down = delta_mu - delta_lambda_z
100+
```
101+
102+
新增 `cal_coeff_lambda_qs()` 函数,同时对 nspin=2 (double) 和 nspin=4 (complex) 提供重载。`contributeHR()` 中通过 `sc.is_charge_constraint_enabled()` 判断是否使用 QS 系数。
103+
104+
`mu_save` 向量用于增量更新:`delta_mu = mu_current - mu_save`,与 `lambda_save` 平行管理。
105+
106+
#### S1.3 联合 Lambda 循环
107+
108+
`run_qs_lambda_loop()` 实现交替优化策略:
109+
110+
1. 先运行标准 `run_lambda_loop()` 收敛自旋约束 (λ)
111+
2. 计算电荷投影 `cal_ni_lcao()`
112+
3. 梯度下降更新 μ:`μ_I += κ_μ · (N_I - N_I^target)`
113+
4. 重新对角化,检查电荷 RMS
114+
5. 迭代直到电荷 RMS < sc_charge_thr
115+
116+
#### S1.4 电荷投影计算
117+
118+
`cal_ni_lcao()` 复用 DeltaSpin 的 `pre_hr` 投影框架:
119+
120+
- nspin=2: 使用总密度矩阵 `switch_dmr(0)` + `cal_moment()`
121+
- nspin=4: 使用旋量密度矩阵,求迹得到总电荷
122+
123+
#### S1.5 约束能量修正
124+
125+
`cal_escon()` 扩展:
126+
127+
```
128+
E_scon = -Σ_I (λ_I · Mi_I) - Σ_I (μ_I · Ni_I)
129+
```
130+
131+
#### S1.6 INPUT 参数
132+
133+
| 参数 | 类型 | 默认值 | 说明 |
134+
|------|------|--------|------|
135+
| `sc_charge_switch` | bool | false | 启用电荷约束 |
136+
| `sc_qs_mode` | string | "auto" | 模式选择 |
137+
| `sc_charge_thr` | double | 1e-4 | 电荷收敛阈值 (e) |
138+
| `sc_charge_alpha` | double | 0.01 | μ 步长 (eV/e²) |
139+
| `sc_charge_sccut` | double | 3.0 | μ 最大步长 (eV/e) |
140+
| `sc_ground_state_search` | bool | false | 外层优化开关 |
141+
| `sc_outer_max_iter` | int | 50 | 外层最大迭代 |
142+
| `sc_outer_thr` | double | 1e-4 | 外层收敛阈值 (eV) |
143+
| `sc_gradient_output` | bool | false | 梯度输出开关 |
144+
145+
#### S1.7 STRU 格式扩展
146+
147+
```
148+
ATOMIC_POSITIONS
149+
Direct
150+
Fe
151+
0.0
152+
2
153+
0.00 0.00 0.00 mag 2.0 sc 1 1 1 tc 6.5 cq 1 mu 0.0
154+
```
155+
156+
新增关键词:
157+
- `tc <value>`: 目标电荷 (electrons)
158+
- `cq <flag>`: 电荷约束标志 (0=free, 1=constrained)
159+
- `mu <value>`: 初始电荷乘子 (eV/e,内部转为 Ry/e)
160+
161+
#### S1.8 SCF 集成
162+
163+
Facade 函数 `init_deltaspin_lcao()``init_sc()` 后调用 `init_deltaqs()` 初始化电荷约束数据。`run_deltaspin_lambda_loop_lcao()` 根据 `is_charge_constraint_enabled()` 选择调用 `run_lambda_loop()``run_qs_lambda_loop()`
164+
165+
### S2: 梯度提取与输出
166+
167+
#### 梯度文件输出
168+
169+
`write_gradient_file()` 输出 `deltaqs_gradient_<step>.dat`
170+
171+
```
172+
# Atom Ni Mi_z target_N target_M mu(Ry) lambda_z(Ry) mu(eV) lambda_z(eV)
173+
Fe_0 6.52 2.01 6.50 2.00 0.012 0.003 0.163 0.041
174+
```
175+
176+
#### 打印函数
177+
178+
- `print_Ni()`: 输出各原子的 Ni、目标值、偏差
179+
- `print_Charge_Force()`: 输出各原子的 μ 乘子
180+
- `cal_charge_escon()`: 电荷约束能量修正
181+
182+
### S3: 网格扫描框架
183+
184+
`run_qs_grid_scan()` 实现 2D E(N, M) 势能面映射:
185+
186+
```
187+
输入: scan_atom, N_min, N_max, N_step, M_min, M_max, M_step
188+
输出: deltaqs_grid_scan.dat
189+
```
190+
191+
对每个 (N, M) 网格点:
192+
1. 设置 target_charge 和 target_mag
193+
2. 运行 `run_qs_lambda_loop()` 收敛
194+
3. 记录 E, μ, λ, Ni, Mi
195+
196+
输出文件包含完整势能面数据,可直接用于 CP-3 验证。
197+
198+
### S4: 梯度下降优化器
199+
200+
`run_qs_gradient_descent()` 实现 2D 势能面上的梯度下降:
201+
202+
```
203+
输入: max_steps, step_size, conv_thr
204+
输出: deltaqs_gradient_descent.dat
205+
```
206+
207+
每步:
208+
1. 运行 DeltaQS SCF → 得到 E, μ, λ
209+
2. 构造梯度: g_N = -μ_I + μ_ref, g_M = -λ_I
210+
3. 检查收敛: max|grad| < conv_thr
211+
4. 更新目标: N_target += step_size · μ, M_target += step_size · λ
212+
213+
### S5: L-BFGS 优化器
214+
215+
`run_qs_lbfgs()` 实现高维 L-BFGS 优化:
216+
217+
```
218+
输入: max_steps, conv_thr, history_size=5
219+
输出: deltaqs_lbfgs.dat
220+
```
221+
222+
优化变量: x = (N_1, ..., N_{k-1}, M_1, ..., M_k),其中原子 k 为电荷缓冲池。
223+
224+
算法流程:
225+
1. 运行 DeltaQS SCF → 得到 E, μ, λ
226+
2. 构造梯度向量(维度 = active_charge - 1 + active_spin)
227+
3. L-BFGS two-loop recursion 计算搜索方向
228+
4. 线搜索步长 α = 0.1(可配置)
229+
5. 更新目标值,维护历史 {s_k, y_k} 对
230+
231+
支持配置历史窗口大小(默认 m=5)。
232+
233+
### S6: 差异归因工具
234+
235+
`run_qs_attribution()` 实现 CP-6 差异归因分析:
236+
237+
输出 `deltaqs_attribution.dat`,包含:
238+
- 各原子的 Ni, Mi, μ, λ 详细数据
239+
- 总磁矩 S* = Σ Mi
240+
- 归因分类判断:
241+
- **A**: 同一 M,不同磁构型(FM vs AFM)
242+
- **B**: M 不在扫描范围内
243+
- **C**: 多体效应(分数自旋态)
244+
245+
## 使用指南
246+
247+
### 基本 DeltaQS 计算
248+
249+
INPUT 文件:
250+
```
251+
sc_mag_switch 1
252+
sc_charge_switch 1
253+
sc_qs_mode auto
254+
sc_charge_thr 1e-4
255+
sc_charge_alpha 0.01
256+
sc_scf_thr_mode immediate
257+
```
258+
259+
STRU 文件:
260+
```
261+
V
262+
0.0
263+
2
264+
0.00 0.00 0.00 mag 1.0 sc 1 tc 3.5 cq 1
265+
0.00 0.00 3.00 mag 1.0 sc 1 tc 3.5 cq 1
266+
```
267+
268+
### 基态搜索
269+
270+
```
271+
sc_ground_state_search true
272+
sc_outer_max_iter 50
273+
sc_outer_thr 1e-4
274+
sc_gradient_output true
275+
```
276+
277+
### 网格扫描(CP-3 验证)
278+
279+
通过代码调用:
280+
```cpp
281+
sc.run_qs_grid_scan(scan_atom, N_min, N_max, N_step, M_min, M_max, M_step);
282+
```
283+
284+
## 验证检查点
285+
286+
| 检查点 | 验证内容 | 判据 |
287+
|--------|---------|------|
288+
| CP-0 | DeltaQS 与标准 SCF 等价性 | |E_QS - E_ref| < 1e-5 Ha |
289+
| CP-1 | 电荷梯度 μ 验证 | |g_FD - (-μ)| / (|g_FD| + η) < 0.01 |
290+
| CP-2 | 自旋梯度 λ 验证 | |g_FD^M - (-λ)| / (|g_FD^M| + η) < 0.01 |
291+
| CP-3 | 2D E(N,M) 势能面 | 无断崖,最小值处 μ₁≈μ₂, λ≈0 |
292+
| CP-4 | 梯度下降轨迹 | ≥90% 轨迹收敛到全局最小 |
293+
| CP-5 | 高维 L-BFGS | E_global ≤ E_M-scan^min |
294+
| CP-6 | 差异归因 | 每个 ΔE<0 案例可归入 A/B/C |
295+
296+
## 向后兼容性
297+
298+
-`sc_charge_switch = false` 时,所有 DeltaQS 代码路径不执行,行为与原 DeltaSpin 完全一致
299+
- `sc_qs_mode = "auto"` 自动根据开关推断模式
300+
- 现有测试用例不受影响
301+
302+
## 单位系统
303+
304+
|| 内部单位 | INPUT 单位 | 转换 |
305+
|----|---------|-----------|------|
306+
| μ | Ry/e | eV/e | × Ry_to_eV |
307+
| λ | Ry/μB | eV/μB | × Ry_to_eV |
308+
| Ni | electrons | electrons | - |
309+
| Mi | μB | μB | - |
310+
| E | Ry | Ry | - |
311+
312+
## 后续开发方向
313+
314+
1. **PW 基组支持**: 当前 `cal_ni_lcao()` 仅支持 LCAO,需添加 `cal_ni_pw()` 路径
315+
2. **nspin=4 联合优化**: `run_qs_lambda_loop()` 中的 nspin=4 分支需要完善
316+
3. **子空间加速集成**: DeltaQS 的 μ 更新与 LCAO subspace acceleration 的兼容
317+
4. **自动步长选择**: L-BFGS 中的 Armijo/Wolfe 线搜索替代固定步长
318+
5. **MPI 并行**: 网格扫描和 L-BFGS 多起点的 MPI 任务分配

source/source_cell/atom_spec.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class Atom
4040
std::vector<ModuleBase::Vector3<double>> force; // force acting on each atom in this type.
4141
std::vector<ModuleBase::Vector3<double>> lambda; // Lagrange multiplier for each atom in this type. used in deltaspin
4242
std::vector<ModuleBase::Vector3<int>> constrain; // constrain for each atom in this type. used in deltaspin
43+
std::vector<double> target_charge; // target charge for each atom (electrons), used in deltaqs
44+
std::vector<double> mu; // charge Lagrange multiplier mu for each atom (Ry/e), used in deltaqs
45+
std::vector<int> constrain_charge; // charge constraint flag for each atom: 0=free, 1=constrained
4346
std::string label_orb = "\0"; // atomic Element symbol in the orbital file of lcao
4447

4548
std::vector<double> mag;

source/source_cell/read_atoms_helper.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ void allocate_atom_properties(Atom& atom, int na, double mass)
6262
atom.m_loc_.resize(na, ModuleBase::Vector3<double>(0,0,0));
6363
atom.lambda.resize(na, ModuleBase::Vector3<double>(0,0,0));
6464
atom.constrain.resize(na, ModuleBase::Vector3<int>(0,0,0));
65+
atom.target_charge.resize(na, 0.0);
66+
atom.mu.resize(na, 0.0);
67+
atom.constrain_charge.resize(na, 0);
6568
atom.mass = mass;
6669
}
6770

@@ -478,6 +481,22 @@ bool parse_atom_properties(std::ifstream& ifpos,
478481
atom.constrain[ia].z=tmplam;
479482
}
480483
}
484+
else if ( tmpid == "tc")
485+
{
486+
ifpos >> atom.target_charge[ia];
487+
}
488+
else if ( tmpid == "cq")
489+
{
490+
int cq_val = 0;
491+
ifpos >> cq_val;
492+
atom.constrain_charge[ia] = cq_val;
493+
}
494+
else if ( tmpid == "mu")
495+
{
496+
double mu_val = 0.0;
497+
ifpos >> mu_val;
498+
atom.mu[ia] = mu_val / ModuleBase::Ry_to_eV;
499+
}
481500
}
482501
// move to next line
483502
while ( (tmpid != "\n") && (ifpos.good()) )

0 commit comments

Comments
 (0)