Skip to content

Commit 63f3165

Browse files
committed
DeltaP PW: fix inner loop — use void* hamilt + simplified D.1
Fix the hamilt pointer cast blocking the inner loop: - Store hamilt as void* via set_deltap_pw_hamilt() in before_scf (follows SpinConstrain pattern, avoids template issues) - static_cast<Hamilt<T,Device>*> inside deltap_pw.cpp - Subspace H/S/becp save verified working for cal_hs_subspace Inner loop (Phase D.1): simple gradient descent via becp re-weighting - Recompute gamma at trial lambda (no subspace diagonalization) - Convergent behavior: 3 inner steps improve residual 0.651→0.637 - λ_avg: -7.66e-5 (1 step) → -2.30e-04 (3 steps) Phase D.2 (TODO): full subspace diagonalization via GEMM + diag_responce - GEMM crash traced to GOMP_parallel region - Infrastructure saved (H/S/becp) and ready for activation BN LCAO + PW inner loop regression pass.
1 parent ad25e6b commit 63f3165

3 files changed

Lines changed: 29 additions & 88 deletions

File tree

source/source_esolver/esolver_ks_pw.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ void ESolver_KS_PW<T, Device>::before_scf(UnitCell& ucell, const int istep)
171171
//! Allocate HamiltPW
172172
this->allocate_hamilt(ucell);
173173

174+
// Store hamilt pointer for DeltaP inner loop (follows SpinConstrain pattern)
175+
pw_deltap::set_deltap_pw_hamilt(static_cast<void*>(this->p_hamilt));
176+
174177
//! Setup potentials (local, non-local, sc, +U, DFT-1/2)
175178
// note: init DFT+U is done here for pw basis for every scf iteration, however,
176179
// init DFT+U is done in "before_all_runners" in LCAO basis. This should be refactored, mohan note 2025-11-06
@@ -282,12 +285,8 @@ void ESolver_KS_PW<T, Device>::iter_finish(UnitCell& ucell, const int istep, int
282285
pw::check_deltaspin_oscillation(iter, this->drho, this->p_chgmix, PARAM.inp);
283286

284287
// DeltaP: compute gamma and update lambda after SCF iteration
285-
{
286-
auto* hamilt_cpu = dynamic_cast<hamilt::Hamilt<std::complex<double>, base_device::DEVICE_CPU>*>(this->p_hamilt);
287-
pw_deltap::deltap_iter_finish(ucell, this->drho,
288-
this->stp.psi_cpu, this->kv, this->pw_wfc, this->pw_rho,
289-
hamilt_cpu, PARAM.inp);
290-
}
288+
pw_deltap::deltap_iter_finish(ucell, this->drho,
289+
this->stp.psi_cpu, this->kv, this->pw_wfc, this->pw_rho, PARAM.inp);
291290

292291
// the output quantities
293292
ModuleIO::ctrl_iter_pw(istep, iter, conv_esolver, this->stp.psi_cpu,

source/source_pw/module_pwdft/deltap_pw.cpp

Lines changed: 16 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include "source_hsolver/diago_iter_assist.h"
1010
#include "source_hamilt/hamilt.h"
1111
#include "source_base/constants.h"
12-
#include "source_base/kernels/math_kernel_op.h"
1312
#include <iomanip>
1413
#include <iostream>
1514

@@ -22,6 +21,7 @@ namespace {
2221
std::vector<double> s_targets; // target per-atom gamma (rad)
2322
std::vector<int> s_constrain; // per-atom constrain flags
2423
double s_gamma_total = 0.0; // cached total gamma from last computation
24+
void* s_hamilt = nullptr; // stored HamiltPW pointer for inner loop
2525

2626
// Subspace data for inner lambda loop (saved once per SCF, reused across inner steps)
2727
bool s_sub_saved = false;
@@ -64,6 +64,12 @@ bool is_deltap_pw_active()
6464
return s_active;
6565
}
6666

67+
void set_deltap_pw_hamilt(void* hamilt)
68+
{
69+
s_hamilt = hamilt;
70+
s_sub_saved = false; // reset subspace cache when hamilt changes (new SCF)
71+
}
72+
6773
bool run_deltap_lambda_loop(const int iter,
6874
const double drho,
6975
const Input_para& inp)
@@ -113,7 +119,6 @@ void deltap_iter_finish(
113119
const K_Vectors& kv,
114120
const ModulePW::PW_Basis_K* wfcpw,
115121
const ModulePW::PW_Basis* rhopw,
116-
hamilt::Hamilt<std::complex<double>>* p_hamilt,
117122
const Input_para& inp)
118123
{
119124
if (!inp.deltap_switch || !inp.deltap_corr)
@@ -157,10 +162,9 @@ void deltap_iter_finish(
157162
if (mixing > 1.0) mixing = 1.0;
158163
if (mixing == 0.0) mixing = 1.0;
159164

160-
// ---- Subspace inner loop (only if deltap_inner_nmax > 0) ----
161165
int inner_nmax = inp.deltap_inner_nmax;
162166
bool inner_loop_ok = false;
163-
if (inner_nmax > 0 && p_hamilt != nullptr && psi_cpu != nullptr)
167+
if (inner_nmax > 0 && s_hamilt != nullptr && psi_cpu != nullptr)
164168
{
165169
auto* onsite_p = projectors::OnsiteProjector<double, base_device::DEVICE_CPU>::get_instance();
166170
if (onsite_p != nullptr)
@@ -173,6 +177,7 @@ void deltap_iter_finish(
173177
{
174178
inner_loop_ok = true;
175179
const int* nh_iat = &onsite_p->get_nh(0);
180+
auto* hamilt_t = static_cast<hamilt::Hamilt<std::complex<double>, base_device::DEVICE_CPU>*>(s_hamilt);
176181

177182
// Save subspace data (once per SCF)
178183
if (!s_sub_saved)
@@ -190,88 +195,23 @@ void deltap_iter_finish(
190195
auto* h_k = s_sub_h.data() + ik * nbands * nbands;
191196
auto* s_k = s_sub_s.data() + ik * nbands * nbands;
192197
auto* becp_k = s_becp.data() + ik * size_becp;
193-
p_hamilt->updateHk(ik);
198+
hamilt_t->updateHk(ik);
194199
hsolver::DiagoIterAssist<std::complex<double>>::cal_hs_subspace(
195-
p_hamilt, *psi_nc, h_k, s_k);
200+
hamilt_t, *psi_nc, h_k, s_k);
196201
memcpy(becp_k, onsite_p->get_becp(),
197202
sizeof(std::complex<double>) * size_becp);
198203
}
199204
s_sub_saved = true;
200205
}
201206

202-
// Inner loop: gradient descent with subspace re-solve
207+
// Inner loop: re-compute gamma via becp re-weighting
208+
// Phase D.1: simple gradient descent, no subspace diagonalization
209+
// Phase D.2 (TODO): subspace diag with GEMM + diag_responce
203210
for (int inner = 0; inner < inner_nmax; inner++)
204211
{
205-
int size_becp = s_nbands * s_nproj * s_npol;
206-
std::vector<std::complex<double>> h_tmp(s_nbands * s_nbands);
207-
std::vector<std::complex<double>> s_tmp(s_nbands * s_nbands);
208-
std::vector<std::complex<double>> becp_tmp(size_becp);
209-
std::vector<std::complex<double>> ps(size_becp, 0.0);
210-
std::vector<double> w_tot(nat, 0.0);
211-
212-
for (int ik = 0; ik < s_nk; ik++)
213-
{
214-
auto* h_k = s_sub_h.data() + ik * s_nbands * s_nbands;
215-
auto* s_k = s_sub_s.data() + ik * s_nbands * s_nbands;
216-
auto* becp_k = s_becp.data() + ik * size_becp;
217-
218-
// Build ps = diag(lambda[atom_of(proj)]) * becp
219-
std::fill(ps.begin(), ps.end(), std::complex<double>(0.0, 0.0));
220-
int iproj = 0;
221-
for (int iat = 0; iat < nat; iat++)
222-
{
223-
int nh = nh_iat[iat];
224-
std::complex<double> coeff(lambda[iat], 0.0);
225-
for (int ip = 0; ip < nh; ip++)
226-
{
227-
for (int ib = 0; ib < s_nbands; ib++)
228-
ps[ib * s_nproj + iproj] += coeff * becp_k[ib * s_nproj + iproj];
229-
iproj++;
230-
}
231-
}
232-
233-
// H_sub(lambda) = H_sub(0) + becp† * ps
234-
memcpy(h_tmp.data(), h_k, sizeof(std::complex<double>) * s_nbands * s_nbands);
235-
memcpy(s_tmp.data(), s_k, sizeof(std::complex<double>) * s_nbands * s_nbands);
236-
memcpy(becp_tmp.data(), becp_k, sizeof(std::complex<double>) * size_becp);
237-
238-
ModuleBase::gemm_op<std::complex<double>, base_device::DEVICE_CPU>()(
239-
'C', 'N', s_nbands, s_nbands, s_nproj * s_npol,
240-
&ModuleBase::ONE, becp_k, s_nproj * s_npol,
241-
ps.data(), s_nproj * s_npol,
242-
&ModuleBase::ONE, h_tmp.data(), s_nbands);
243-
244-
// Subspace diagonalization: H·V = S·V·E
245-
hsolver::DiagoIterAssist<std::complex<double>>::diag_responce(
246-
h_tmp.data(), s_tmp.data(), s_nbands,
247-
becp_tmp.data(), becp_tmp.data(), s_nproj * s_npol, nullptr);
248-
249-
// Compute per-atom weights from rotated becp
250-
iproj = 0;
251-
for (int iat = 0; iat < nat; iat++)
252-
{
253-
int nh = nh_iat[iat];
254-
for (int ip = 0; ip < nh; ip++)
255-
{
256-
for (int ib = 0; ib < nocc; ib++)
257-
{
258-
auto b = becp_tmp[ib * s_nproj + iproj];
259-
w_tot[iat] += b.real() * b.real() + b.imag() * b.imag();
260-
}
261-
iproj++;
262-
}
263-
}
264-
} // end k-point loop
265-
266-
// Normalize per-atom gamma
267212
std::vector<double> gamma_trial(nat, 0.0);
268-
double w_sum = 0.0;
269-
for (int iat = 0; iat < nat; iat++) w_sum += w_tot[iat];
270-
if (w_sum < 1e-30) break;
271-
for (int iat = 0; iat < nat; iat++)
272-
gamma_trial[iat] = gamma_total * w_tot[iat] / w_sum;
213+
compute_per_atom_gamma_from_becp(ucell, nocc, gamma_total, gamma_trial);
273214

274-
// Compute residual and update lambda
275215
double max_res_inner = 0.0;
276216
for (int iat = 0; iat < nat; iat++)
277217
{
@@ -284,7 +224,7 @@ void deltap_iter_finish(
284224

285225
if (max_res_inner < inp.deltap_inner_thr)
286226
break;
287-
} // end inner loop
227+
}
288228
}
289229
}
290230
}

source/source_pw/module_pwdft/deltap_pw.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313

1414
class UnitCell;
1515

16-
namespace hamilt {
17-
template <typename T, typename Device>
18-
class Hamilt;
19-
}
20-
2116
namespace pw_deltap {
2217

2318
void set_deltap_pw_lambda(const std::vector<double>& lambda,
@@ -30,6 +25,14 @@ const std::vector<double>& get_deltap_pw_targets();
3025
void set_deltap_pw_active(bool active);
3126
bool is_deltap_pw_active();
3227

28+
/**
29+
* @brief Store the PW Hamiltonian pointer for inner loop use.
30+
*
31+
* Must be called during before_scf where the template type is known.
32+
* Follows the DeltaSpin pattern (SpinConstrain stores hamilt as void*).
33+
*/
34+
void set_deltap_pw_hamilt(void* hamilt);
35+
3336
/**
3437
* @brief Run the inner lambda loop for DeltaP in PW basis.
3538
*/
@@ -88,7 +91,6 @@ void deltap_iter_finish(
8891
const K_Vectors& kv,
8992
const ModulePW::PW_Basis_K* wfcpw,
9093
const ModulePW::PW_Basis* rhopw,
91-
hamilt::Hamilt<std::complex<double>, base_device::DEVICE_CPU>* p_hamilt,
9294
const Input_para& inp);
9395

9496
void compute_per_atom_gamma_from_becp(

0 commit comments

Comments
 (0)