Skip to content

Commit 05cbf8e

Browse files
committed
Fix: refresh v_eff again after updating XC type, to correct H and eigenpairs in the 1st iter of the EXX loop.
1 parent cd270a7 commit 05cbf8e

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

source/source_esolver/esolver_ks_lcaopw.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ namespace ModuleESolver
168168
#ifdef __EXX
169169
if (GlobalC::exx_info.info_global.cal_exx && conv_esolver)
170170
{
171+
const int two_level_step_before = this->two_level_step;
172+
171173
// no separate_loop case
172174
if (!GlobalC::exx_info.info_global.separate_loop)
173175
{
@@ -218,6 +220,20 @@ namespace ModuleESolver
218220
<< std::defaultfloat << " (s)" << std::endl;
219221
conv_esolver = false;
220222
}
223+
224+
// On the 0->1 transition, exx_after_converge switches the XC functional (PBE->hybrid),
225+
// but v_eff was already set for this (converged) iteration under the OLD functional.
226+
// Without this refresh, the 1st iteration of the EXX loop builds H from the stale GGA v_eff
227+
// and then adds Hexx on top of it, double-counting exchange.
228+
// Usually only that one iteration is affected and the loop washes it out;
229+
// but when the 2nd loop converges immediately (density already exact, e.g. a minimal basis fixed by symmetry)
230+
// the polluted H is the final one -- it gets diagonalized and written out by out_mat_hs.
231+
// cal_converged() is used rather than a bare update_from_charge() so that vnew (used
232+
// by force_scc) and descf stay consistent with the refreshed v_eff.
233+
if (!conv_esolver && two_level_step_before == 0 && this->two_level_step == 1)
234+
{
235+
this->pelec->cal_converged();
236+
}
221237
}
222238
#endif
223239
}

source/source_lcao/module_ri/exx_lri_interface.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ void Exx_LRI_Interface<T, Tdata>::exx_iter_finish(const K_Vectors& kv,
326326
}
327327
// mohan update 2025-11-04
328328
this->dm_last_step = dm;
329+
const int two_level_step_before = this->two_level_step;
329330
conv_esolver = this->exx_after_converge(
330331
ucell,
331332
hamilt,
@@ -336,6 +337,20 @@ void Exx_LRI_Interface<T, Tdata>::exx_iter_finish(const K_Vectors& kv,
336337
istep,
337338
elec.f_en.etot,
338339
scf_ene_thr);
340+
341+
// On the 0->1 transition, exx_after_converge switches the XC functional (PBE->hybrid),
342+
// but v_eff was already set for this (converged) iteration under the OLD functional.
343+
// Without this refresh, the 1st iteration of the EXX loop builds H from the stale GGA v_eff
344+
// and then adds Hexx on top of it, double-counting exchange.
345+
// Usually only that one iteration is affected and the loop washes it out;
346+
// but when the 2nd loop converges immediately (density already exact, e.g. a minimal basis fixed by symmetry)
347+
// the polluted H is the final one -- it gets diagonalized and written out by out_mat_hs.
348+
// cal_converged() is used rather than a bare update_from_charge() so that vnew (used
349+
// by force_scc) and descf stay consistent with the refreshed v_eff.
350+
if (!conv_esolver && two_level_step_before == 0 && this->two_level_step == 1)
351+
{
352+
elec.cal_converged();
353+
}
339354
}
340355
//else if ( PARAM.inp.rdmft && two_level_step ) { conv_esolver = true; } // for RDMFT in the future to quit after the first iter of the exx-loop
341356
}

0 commit comments

Comments
 (0)