@@ -84,16 +84,26 @@ double ParabolicCorrection::apply_correction(const UnitCell& cell,
8484 // 2. 计算电荷与偶极
8585 double net_charge = -PARAM .inp .nelec_delta ;
8686 double total_dipole = calc_total_dipole (cell, rho_basis, rho_elec, nspin, dir, slab_center);
87+
88+ double total_quadrupole = calc_total_quadrupole (cell, rho_basis, rho_elec, nspin, dir, slab_center);
89+ double factor_common = (ModuleBase::FOUR_PI / omega) * ModuleBase::e2 ; // 4pi/Omega * e2
90+ double factor_quad = factor_common * 0.5 ;
91+ double madelung_term = - (ModuleBase::PI / 3.0 ) * net_charge / lat_vec * ModuleBase::e2 ;
92+ double quadrupole_term = - factor_quad * total_quadrupole;
93+
94+ double v_const = madelung_term + quadrupole_term;
8795
8896 // 缓存偶极矩供后续力修正使用
8997 this ->last_total_dipole_ = total_dipole;
9098
9199 // 3. 计算离子修正能 (这是你想要加入的!)
92- double e_ion_corr = calc_energy_correction (cell, dir, net_charge, total_dipole, vacuum_center);
100+ double e_ion_corr = calc_energy_correction (cell, dir, net_charge, total_dipole, vacuum_center,v_const);
101+ double e_elec_corr = 0.0 ;
93102
94103 // 4. 构造 1D 修正势并叠加到 v_hartree
95- double factor = (ModuleBase::FOUR_PI / omega) * ModuleBase::e2 ;
104+ // double factor = (ModuleBase::FOUR_PI / omega) * ModuleBase::e2;
96105 int nrxx = rho_basis->nrxx ;
106+ int nspin_eff = (nspin == 2 ) ? 2 : 1 ;
97107
98108 #ifdef _OPENMP
99109 #pragma omp parallel for
@@ -117,23 +127,38 @@ double ParabolicCorrection::apply_correction(const UnitCell& cell,
117127 double dist_bohr = dist_frac * lat_vec;
118128
119129 // 核心抛物线修正公式
120- double v_corr = factor * ( -0.5 * net_charge * dist_bohr * dist_bohr
130+ double v_corr = factor_common * ( -0.5 * net_charge * dist_bohr * dist_bohr
121131 + total_dipole * dist_bohr );
122132
123133 v_hartree[ir] += v_corr;
134+ double rho_val = 0.0 ;
135+ for (int is=0 ; is<nspin_eff; ++is) rho_val += rho_elec[is][ir];
136+ e_elec_corr += rho_val * v_corr;
137+ }
138+ Parallel_Reduce::reduce_pool (e_elec_corr);
139+ e_elec_corr *= (cell.omega / rho_basis->nxyz );
140+ double total_correction_energy = e_ion_corr - e_elec_corr;
141+
142+ if (GlobalV::RANK_IN_POOL == 0 ) {
143+ std::cout << " DEBUG PARABOLIC:" << std::endl;
144+ std::cout << " nelec_delta (Input): " << PARAM .inp .nelec_delta << std::endl;
145+ std::cout << " net_charge (Used): " << net_charge << std::endl;
146+ std::cout << " Total Dipole: " << total_dipole << std::endl;
147+ std::cout << " Total Quadrupole: " << total_quadrupole << std::endl; // 建议打印四极矩
148+ std::cout << " Madelung Term: " << madelung_term << std::endl; // 建议打印各项贡献
149+ std::cout << " Quadrupole Term: " << quadrupole_term << std::endl;
150+ std::cout << " Constant Shift: " << v_const << std::endl;
151+
152+ std::cout << " ----------------------------------------" << std::endl;
153+ std::cout << " Electronic Energy Corr: " << e_elec_corr << " Ry" << std::endl;
154+ std::cout << " Ionic Energy Corr: " << e_ion_corr << " Ry" << std::endl;
155+ std::cout << " TOTAL Parabolic Corr: " << total_correction_energy << " Ry" << std::endl;
156+ std::cout << " ----------------------------------------" << std::endl;
124157 }
125- if (GlobalV::RANK_IN_POOL == 0 ) {
126- std::cout << " DEBUG PARABOLIC:" << std::endl;
127- std::cout << " nelec_delta (Input): " << PARAM .inp .nelec_delta << std::endl;
128- std::cout << " net_charge (Used): " << net_charge << std::endl;
129- // std::cout << " Calculated Q (Ion-Elec): " << calc_net_charge(cell, GlobalV::nelec) << std::endl;
130- std::cout << " Total Dipole: " << total_dipole << std::endl;
131- std::cout << " Slab Center: " << slab_center << std::endl;
132- std::cout << " Factor: " << factor << std::endl;
133- }
134158
135159 // 返回离子修正能,方便外部加到 Total Energy
136- return e_ion_corr;
160+ // double total_ion_charge = net_charge - (-PARAM.inp.nelec_delta);
161+ return total_correction_energy;
137162}
138163
139164// ---------------------------------------------------------
@@ -228,7 +253,8 @@ double ParabolicCorrection::calc_energy_correction(const UnitCell& cell,
228253 int dir,
229254 double net_charge,
230255 double total_dipole,
231- double vacuum_center)
256+ double vacuum_center,
257+ double v_const)
232258{
233259 double lat_vec = 0.0 ;
234260 if (dir == 0 ) lat_vec = cell.a1 .norm () * cell.lat0 ;
@@ -255,7 +281,7 @@ double ParabolicCorrection::calc_energy_correction(const UnitCell& cell,
255281
256282 // V_corr at atom position
257283 double v_at_atom = factor * ( -0.5 * net_charge * dist_bohr * dist_bohr
258- + total_dipole * dist_bohr );
284+ + total_dipole * dist_bohr ) + v_const ;
259285
260286 e_corr += Z * v_at_atom;
261287 }
@@ -311,4 +337,114 @@ void ParabolicCorrection::calc_force_correction(const UnitCell& cell,
311337 iat++;
312338 }
313339 }
340+ }
341+
342+ double ParabolicCorrection::calc_total_quadrupole (const UnitCell& cell,
343+ const ModulePW::PW_Basis* rho_basis,
344+ const double * const * rho_elec,
345+ int nspin,
346+ int dir,
347+ double center)
348+ {
349+ double lat_vec = 0.0 ;
350+ if (dir==0 ) lat_vec = cell.a1 .norm () * cell.lat0 ;
351+ else if (dir==1 ) lat_vec = cell.a2 .norm () * cell.lat0 ;
352+ else lat_vec = cell.a3 .norm () * cell.lat0 ;
353+
354+ // Q_ion
355+ double q_ion = calc_ion_quadrupole (cell, dir, center);
356+ // 注意:离子位置已经是物理长度(Bohr),所以算出来直接是 Bohr^2
357+ // 如果 calc_ion_dipole 用的是分数坐标差,这里要注意单位转换。
358+ // 看原代码 calc_ion_dipole 中 dist 是分数坐标差,最后乘了 lat_vec。
359+ // 这里我们需要 (dist_frac * lat_vec)^2
360+
361+ // Q_elec
362+ double q_elec_frac = calc_elec_quadrupole (rho_basis, rho_elec, nspin, dir, center, cell.omega );
363+ // 同样需要乘以 lat_vec^2
364+ double q_elec = q_elec_frac * (lat_vec * lat_vec);
365+ // 假设辅助函数内部处理单位,或者在这里统一处理
366+ // 让我们看辅助函数实现细节:
367+ return q_ion - q_elec;
368+ }
369+
370+ double ParabolicCorrection::calc_ion_quadrupole (const UnitCell& cell, int dir, double center)
371+ {
372+ double q = 0.0 ;
373+
374+ double lat_vec = 0.0 ;
375+ if (dir==0 ) lat_vec = cell.a1 .norm () * cell.lat0 ;
376+ else if (dir==1 ) lat_vec = cell.a2 .norm () * cell.lat0 ;
377+ else lat_vec = cell.a3 .norm () * cell.lat0 ;
378+
379+ for (int it=0 ; it<cell.ntype ; ++it) {
380+ for (int ia=0 ; ia<cell.atoms [it].na ; ++ia) {
381+ double pos = cell.atoms [it].taud [ia][dir];
382+ double dist_frac = pos - center;
383+
384+ // 最小镜像处理
385+ if (dist_frac > 0.5 ) dist_frac -= 1.0 ;
386+ if (dist_frac < -0.5 ) dist_frac += 1.0 ;
387+
388+ double dist_bohr = dist_frac * lat_vec;
389+
390+ // sum Z * r^2
391+ q += cell.atoms [it].ncpp .zv * (dist_bohr * dist_bohr);
392+ }
393+ }
394+ return q;
395+ }
396+
397+ double ParabolicCorrection::calc_elec_quadrupole (const ModulePW::PW_Basis* rho_basis,
398+ const double * const * rho_elec,
399+ int nspin,
400+ int dir,
401+ double center,
402+ double omega)
403+ {
404+ double q = 0.0 ;
405+ int nrxx = rho_basis->nrxx ;
406+ int n_components = (nspin == 2 ) ? 2 : 1 ;
407+
408+ // 获取晶格长度
409+ // 这里的 omega 参数虽然传进来了,但我们需要长度来转换坐标
410+ // 简单起见,我们在外部计算好 lat_vec 比较麻烦,
411+ // 不如在这里根据 dir 重新算一下或者假设输入保证一致。
412+ // 为了严谨,建议像 calc_elec_dipole 一样,只算分数坐标部分的积分,最后在外面乘长度平方。
413+ // 但 calc_elec_dipole 是最后乘了 lat_vec。
414+ // 我们这里直接在循环里乘好吧,或者模仿原结构。
415+
416+ // 为了复用代码结构,我们计算 sum [ rho * (dist_frac)^2 ]
417+ // 最后再乘以 (lat_vec^2) * (omega / nxyz)
418+
419+ for (int ir = 0 ; ir < nrxx; ++ir)
420+ {
421+ int i = ir / (rho_basis->ny * rho_basis->nplane );
422+ int j = ir / rho_basis->nplane - i * rho_basis->ny ;
423+ int k = ir % rho_basis->nplane + rho_basis->startz_current ;
424+
425+ double pos = 0.0 ;
426+ if (dir==0 ) pos = (double )i / rho_basis->nx ;
427+ else if (dir==1 ) pos = (double )j / rho_basis->ny ;
428+ else pos = (double )k / rho_basis->nz ;
429+
430+ double dist = pos - center;
431+ if (dist > 0.5 ) dist -= 1.0 ;
432+ if (dist < -0.5 ) dist += 1.0 ;
433+
434+ double rho_val = 0.0 ;
435+ for (int is=0 ; is<n_components; ++is) {
436+ rho_val += rho_elec[is][ir];
437+ }
438+
439+ q += rho_val * (dist * dist);
440+ }
441+
442+ Parallel_Reduce::reduce_pool (q);
443+
444+ // 积分体积元 dV = omega / nxyz
445+ q *= (omega / rho_basis->nxyz );
446+
447+ // 此时 q 是 sum rho * (dist_frac)^2 * dV
448+ // 我们需要在外部乘以 lat_vec^2 才能变成 Bohr^2 单位
449+ return q;
314450}
0 commit comments