Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions source/source_esolver/esolver_ks_lcaopw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ namespace ModuleESolver
#ifdef __EXX
if (GlobalC::exx_info.info_global.cal_exx && conv_esolver)
{
const int two_level_step_before = this->two_level_step;

// no separate_loop case
if (!GlobalC::exx_info.info_global.separate_loop)
{
Expand Down Expand Up @@ -218,6 +220,20 @@ namespace ModuleESolver
<< std::defaultfloat << " (s)" << std::endl;
conv_esolver = false;
}

// On the 0->1 transition, exx_after_converge switches the XC functional (PBE->hybrid),
// but v_eff was already set for this (converged) iteration under the OLD functional.
// Without this refresh, the 1st iteration of the EXX loop builds H from the stale GGA v_eff
// and then adds Hexx on top of it, double-counting exchange.
// Usually only that one iteration is affected and the loop washes it out;
// but when the 2nd loop converges immediately (density already exact, e.g. a minimal basis fixed by symmetry)
// the polluted H is the final one -- it gets diagonalized and written out by out_mat_hs.
// cal_converged() is used rather than a bare update_from_charge() so that vnew (used
// by force_scc) and descf stay consistent with the refreshed v_eff.
Comment thread
maki49 marked this conversation as resolved.
if (!conv_esolver && two_level_step_before == 0 && this->two_level_step == 1)
{
this->pelec->cal_converged();
}
}
#endif
}
Expand Down
15 changes: 15 additions & 0 deletions source/source_lcao/module_ri/exx_lri_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ void Exx_LRI_Interface<T, Tdata>::exx_iter_finish(const K_Vectors& kv,
}
// mohan update 2025-11-04
this->dm_last_step = dm;
const int two_level_step_before = this->two_level_step;
conv_esolver = this->exx_after_converge(
ucell,
hamilt,
Expand All @@ -336,6 +337,20 @@ void Exx_LRI_Interface<T, Tdata>::exx_iter_finish(const K_Vectors& kv,
istep,
elec.f_en.etot,
scf_ene_thr);

// On the 0->1 transition, exx_after_converge switches the XC functional (PBE->hybrid),
// but v_eff was already set for this (converged) iteration under the OLD functional.
// Without this refresh, the 1st iteration of the EXX loop builds H from the stale GGA v_eff
// and then adds Hexx on top of it, double-counting exchange.
// Usually only that one iteration is affected and the loop washes it out;
// but when the 2nd loop converges immediately (density already exact, e.g. a minimal basis fixed by symmetry)
// the polluted H is the final one -- it gets diagonalized and written out by out_mat_hs.
// cal_converged() is used rather than a bare update_from_charge() so that vnew (used
// by force_scc) and descf stay consistent with the refreshed v_eff.
Comment thread
maki49 marked this conversation as resolved.
if (!conv_esolver && two_level_step_before == 0 && this->two_level_step == 1)
{
elec.cal_converged();
}
}
//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
}
Expand Down
Loading