@@ -789,6 +789,20 @@ void ESolver_KS_LCAO<TK, TR>::iter_finish(UnitCell& ucell, const int istep, int&
789789 dp_escon -= lambda[iat] * gamma_I[iat][alpha];
790790 }
791791 this ->pelec ->f_en .dp_escon = dp_escon;
792+
793+ // Effective electric field: E_eff = -λ_avg × π / a_alpha (a.u.)
794+ // Convert: 1 a.u. = 51.42 V/Å
795+ double a_alpha = ucell.lat0 ;
796+ if (PARAM .inp .deltap_gdir == 1 ) a_alpha *= ucell.a1 .norm ();
797+ else if (PARAM .inp .deltap_gdir == 2 ) a_alpha *= ucell.a2 .norm ();
798+ else a_alpha *= ucell.a3 .norm ();
799+ double lam_avg = 0.0 ;
800+ for (int iat = 0 ; iat < ucell.nat ; ++iat) lam_avg += lambda[iat];
801+ lam_avg /= ucell.nat ;
802+ double E_eff_au = -lam_avg * ModuleBase::PI / a_alpha; // Hartree/(e·Bohr)
803+ double E_eff_V_per_A = E_eff_au * 51.422 ; // V/Å
804+ std::cout << " [E-field] E_eff=" << std::scientific << std::setprecision (3 )
805+ << E_eff_V_per_A << " V/Angstrom (λ_avg=" << lam_avg << " Ry)" << std::endl;
792806 }
793807 else
794808 {
@@ -1069,53 +1083,95 @@ void ESolver_KS_LCAO<TK, TR>::deltap_inner_loop(UnitCell& ucell, const int iter,
10691083 return ;
10701084 }
10711085
1072- std::vector<double > lambda = dp_op->get_lambda ();
1073- std::vector<double > residual (ucell.nat , 0.0 );
1074- const int alpha = PARAM .inp .deltap_gdir - 1 ;
1086+ // Gating: only activate inner loop when charge density is converged.
1087+ // Before this threshold, λ=0 and SCF converges naturally (Phase 1).
1088+ // This is analogous to DeltaSpin's sc_scf_thr gate.
1089+ if (this ->drho > PARAM .inp .deltap_inner_thr )
1090+ {
1091+ return ;
1092+ }
10751093
10761094 // Measure current gamma
10771095 dp->compute_gamma_scf (ucell, psi, this ->pelec );
10781096 const auto & gamma_I = dp->get_results ().gamma_I ;
1079- for (int iat = 0 ; iat < ucell.nat ; ++iat)
1080- residual[iat] = gamma_I[iat][alpha] - deltap_target_[iat];
1097+ const int alpha = PARAM .inp .deltap_gdir - 1 ;
1098+
1099+ // Compute residual: per_atom or constraint_matrix mode
1100+ std::vector<double > residual;
1101+ bool use_constraint_matrix = !deltap_constraint_matrix_.empty ();
1102+
1103+ if (use_constraint_matrix)
1104+ {
1105+ int m = static_cast <int >(deltap_constraint_matrix_.size ());
1106+ residual.resize (m, 0.0 );
1107+ for (int a = 0 ; a < m; ++a)
1108+ {
1109+ double cv = 0.0 ;
1110+ for (int i = 0 ; i < ucell.nat ; ++i)
1111+ cv += deltap_constraint_matrix_[a][i] * gamma_I[i][alpha];
1112+ residual[a] = cv - deltap_constraint_target_[a];
1113+ }
1114+ }
1115+ else
1116+ {
1117+ residual.resize (ucell.nat , 0.0 );
1118+ for (int iat = 0 ; iat < ucell.nat ; ++iat)
1119+ residual[iat] = gamma_I[iat][alpha] - deltap_target_[iat];
1120+ }
1121+
1122+ // BFGS-CG inner loop: optimize lambda with frozen charge density.
1123+ // Each inner iteration re-diagonalizes with trial lambda (no density mixing),
1124+ // which is ~10× faster than a full SCF step.
1125+ int n_inner_atoms = use_constraint_matrix
1126+ ? static_cast <int >(deltap_constraint_matrix_.size ()) : ucell.nat ;
10811127
1082- // BFGS-CG inner loop
10831128 auto & bfgs = dp->bfgs ();
1084- bfgs.start_outer (lambda);
1129+ bfgs.init (n_inner_atoms, 0.5 , PARAM .inp .deltap_conv_thr , 2 , 0.01 , 0.005 );
1130+
1131+ std::vector<double > lambda_inner;
1132+ if (use_constraint_matrix)
1133+ {
1134+ lambda_inner = deltap_constraint_lambda_;
1135+ bfgs.start_outer (lambda_inner);
1136+ }
1137+ else
1138+ {
1139+ lambda_inner = dp_op->get_lambda ();
1140+ bfgs.start_outer (lambda_inner);
1141+ }
1142+
10851143 bool bfgs_converged = false ;
10861144 const int nscf = dp->inner_loop_nscf ();
10871145
10881146 hsolver::HSolverLCAO<TK > hsolver_lcao_obj (&(this ->pv ), PARAM .inp .ks_solver );
10891147
1090- // Diagnostics: print initial state
1091- std::cout << " [DeltaP] inner loop start: nscf=" << nscf;
1092- for (int iat = 0 ; iat < ucell.nat ; ++iat)
1093- std::cout << " l" << iat << " =" << lambda[iat];
1094- std::cout << " rms=" << std::scientific << std::setprecision (4 )
1148+ std::cout << " [DeltaP] inner loop start: nscf=" << nscf
1149+ << " rms=" << std::scientific << std::setprecision (4 )
10951150 << bfgs.get_rms () << std::endl;
10961151
10971152 for (int inner = 0 ; inner < nscf && !bfgs_converged; ++inner)
10981153 {
1099- std::vector<double > lam_trial (ucell. nat ) ;
1154+ std::vector<double > lam_trial = lambda_inner ;
11001155 bfgs.step (residual, inner, lam_trial, bfgs_converged);
1156+ if (bfgs_converged) break ;
11011157
1102- // Diagnostics: print trial lambda and predicted residual
1103- std::cout << " [DeltaP] inner=" << inner
1104- << " rms=" << std::scientific << std::setprecision (4 )
1105- << bfgs.get_rms ();
1106- for (int iat = 0 ; iat < ucell.nat ; ++iat)
1107- std::cout << " l" << iat << " =" << lam_trial[iat];
1108- if (bfgs_converged)
1158+ // Apply trial lambda (convert to effective per-atom lambda)
1159+ std::vector<double > lam_eff (ucell.nat , 0.0 );
1160+ if (use_constraint_matrix)
1161+ {
1162+ int m = static_cast <int >(deltap_constraint_matrix_.size ());
1163+ for (int i = 0 ; i < ucell.nat ; ++i)
1164+ for (int a = 0 ; a < m; ++a)
1165+ lam_eff[i] += lam_trial[a] * deltap_constraint_matrix_[a][i];
1166+ }
1167+ else
11091168 {
1110- std::cout << " converged" << std::endl;
1111- break ;
1169+ lam_eff = lam_trial;
11121170 }
1113- std::cout << std::endl ;
1171+ dp_op-> set_lambda (lam_eff) ;
11141172
1115- // Apply trial lambda and re-diagonalize
1116- dp_op->set_lambda (lam_trial);
11171173 std::unordered_map<int , std::vector<std::complex <double >>> hk_corr;
1118- dp->compute_hk_correction (ucell, psi, lam_trial , hk_corr);
1174+ dp->compute_hk_correction (ucell, psi, lam_eff , hk_corr);
11191175 dp_op->set_hk_correction (hk_corr);
11201176
11211177 // Re-solve with trial lambda (charge density frozen)
@@ -1126,29 +1182,54 @@ void ESolver_KS_LCAO<TK, TR>::deltap_inner_loop(UnitCell& ucell, const int iter,
11261182 // Measure residual at trial point
11271183 dp->compute_gamma_scf (ucell, psi, this ->pelec );
11281184 const auto & gamma_trial = dp->get_results ().gamma_I ;
1129- for (int iat = 0 ; iat < ucell.nat ; ++iat)
1130- residual[iat] = gamma_trial[iat][alpha] - deltap_target_[iat];
1185+ if (use_constraint_matrix)
1186+ {
1187+ int m = static_cast <int >(deltap_constraint_matrix_.size ());
1188+ for (int a = 0 ; a < m; ++a)
1189+ {
1190+ double cv = 0.0 ;
1191+ for (int i = 0 ; i < ucell.nat ; ++i)
1192+ cv += deltap_constraint_matrix_[a][i] * gamma_trial[i][alpha];
1193+ residual[a] = cv - deltap_constraint_target_[a];
1194+ }
1195+ }
1196+ else
1197+ {
1198+ for (int iat = 0 ; iat < ucell.nat ; ++iat)
1199+ residual[iat] = gamma_trial[iat][alpha] - deltap_target_[iat];
1200+ }
11311201
11321202 double alpha_opt = bfgs.accept_trial (residual);
1203+ lambda_inner = lam_trial;
11331204
1134- // Diagnostics: print actual residual after solve
1135- std::cout << " [DeltaP] result:" ;
1136- for (int iat = 0 ; iat < ucell.nat ; ++iat)
1137- std::cout << " g" << iat << " =" << gamma_trial[iat][alpha];
1138- std::cout << " alpha_opt=" << alpha_opt << std::endl;
1205+ std::cout << " [DeltaP] inner=" << inner
1206+ << " rms=" << std::scientific << std::setprecision (4 )
1207+ << bfgs.get_rms () << " alpha_opt=" << alpha_opt << std::endl;
11391208 }
11401209
11411210 // Set final lambda and reconstruct HK correction
1142- bfgs.get_lambda (lambda);
1143- dp_op->set_lambda (lambda);
1211+ std::vector<double > lam_final (ucell.nat , 0.0 );
1212+ if (use_constraint_matrix)
1213+ {
1214+ int m = static_cast <int >(deltap_constraint_matrix_.size ());
1215+ for (int i = 0 ; i < ucell.nat ; ++i)
1216+ for (int a = 0 ; a < m; ++a)
1217+ lam_final[i] += lambda_inner[a] * deltap_constraint_matrix_[a][i];
1218+ deltap_constraint_lambda_ = lambda_inner;
1219+ }
1220+ else
1221+ {
1222+ lam_final = lambda_inner;
1223+ }
1224+ dp_op->set_lambda (lam_final);
11441225 std::unordered_map<int , std::vector<std::complex <double >>> hk_corr_final;
1145- dp->compute_hk_correction (ucell, psi, lambda , hk_corr_final);
1226+ dp->compute_hk_correction (ucell, psi, lam_final , hk_corr_final);
11461227 dp_op->set_hk_correction (hk_corr_final);
11471228 skip_solve = true ; // inner loop already solved
11481229
11491230 std::cout << " [DeltaP] inner loop done: final" ;
11501231 for (int iat = 0 ; iat < ucell.nat ; ++iat)
1151- std::cout << " l" << iat << " =" << lambda [iat];
1232+ std::cout << " l" << iat << " =" << lam_final [iat];
11521233 std::cout << std::endl;
11531234 }
11541235}
0 commit comments