Skip to content

Commit d6b7000

Browse files
committed
Feature: Add LCAO subspace solver for SCF acceleration in MD/relax
- Add HSolverLCAOSubspace class in hsolver module for subspace diagonalization - Integrate subspace solver into ESolver_KS_LCAO for MD/relax acceleration - Add input parameters: lcao_subspace_persistent, lcao_subspace_clear_thr - First SCF step uses full diagonalization, subsequent steps use subspace solver - Subspace cache updated after each SCF iteration using current wavefunctions - Add cal_PI_sub method to dspin_lcao for projector overlap computation - Add LambdaSolver abstraction with BFGS, ChiGuided, Subspace, FDCG solvers
1 parent 1919027 commit d6b7000

13 files changed

Lines changed: 2527 additions & 17 deletions

File tree

source/source_esolver/esolver_ks_lcao.cpp

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,40 @@ void ESolver_KS_LCAO<TK, TR>::before_scf(UnitCell& ucell, const int istep)
212212
rdmft_solver.update_ion(ucell, *(this->pw_rho), this->locpp.vloc, this->sf.strucFac);
213213
}
214214

215+
// 18) Manage subspace solver cache for MD/relax acceleration
216+
if (PARAM.inp.lcao_subspace_persistent && istep > 0)
217+
{
218+
// Check atomic displacement
219+
double max_disp = 0.0;
220+
for (int iat = 0; iat < ucell.nat; iat++)
221+
{
222+
const auto& tau = ucell.get_tau(iat);
223+
double dx = tau.x - this->last_atom_positions_[iat].x;
224+
double dy = tau.y - this->last_atom_positions_[iat].y;
225+
double dz = tau.z - this->last_atom_positions_[iat].z;
226+
double disp = std::sqrt(dx*dx + dy*dy + dz*dz) * ucell.lat0;
227+
if (disp > max_disp) max_disp = disp;
228+
}
229+
230+
// Clear cache if displacement exceeds threshold or cache is invalid
231+
if (this->subspace_solver_ &&
232+
(PARAM.inp.lcao_subspace_clear_thr > 0.0 &&
233+
max_disp > PARAM.inp.lcao_subspace_clear_thr))
234+
{
235+
this->subspace_solver_->clear_subspace();
236+
GlobalV::ofs_running << " >> Subspace cache cleared: max atomic displacement = "
237+
<< max_disp << " Bohr (threshold: "
238+
<< PARAM.inp.lcao_subspace_clear_thr << " Bohr)" << std::endl;
239+
}
240+
}
241+
242+
// Save current atom positions for next step comparison
243+
this->last_atom_positions_.resize(ucell.nat);
244+
for (int iat = 0; iat < ucell.nat; iat++)
245+
{
246+
this->last_atom_positions_[iat] = ucell.get_tau(iat);
247+
}
248+
215249
ModuleBase::timer::end("ESolver_KS_LCAO", "before_scf");
216250
return;
217251
}
@@ -404,9 +438,31 @@ void ESolver_KS_LCAO<TK, TR>::hamilt2rho_single(UnitCell& ucell, int istep, int
404438
// 3) run Hsolver
405439
if (!skip_solve)
406440
{
407-
hsolver::HSolverLCAO<TK> hsolver_lcao_obj(&(this->pv), PARAM.inp.ks_solver);
408-
hsolver_lcao_obj.solve(static_cast<hamilt::Hamilt<TK>*>(this->p_hamilt), this->psi[0], this->pelec, *this->dmat.dm,
409-
this->chr, PARAM.inp.nspin, skip_charge);
441+
// Try subspace solver first (for SCF acceleration after first step)
442+
bool subspace_used = false;
443+
if constexpr (std::is_same_v<TK, std::complex<double>>)
444+
{
445+
if (this->subspace_solver_ && this->subspace_solver_->has_subspace())
446+
{
447+
this->subspace_solver_->solve(
448+
static_cast<hamilt::Hamilt<TK>*>(this->p_hamilt),
449+
this->psi[0],
450+
this->pelec,
451+
*this->dmat.dm,
452+
this->chr,
453+
PARAM.inp.nspin,
454+
skip_charge);
455+
subspace_used = true;
456+
}
457+
}
458+
459+
// Fall back to standard HSolverLCAO if subspace not available
460+
if (!subspace_used)
461+
{
462+
hsolver::HSolverLCAO<TK> hsolver_lcao_obj(&(this->pv), PARAM.inp.ks_solver);
463+
hsolver_lcao_obj.solve(static_cast<hamilt::Hamilt<TK>*>(this->p_hamilt), this->psi[0], this->pelec, *this->dmat.dm,
464+
this->chr, PARAM.inp.nspin, skip_charge);
465+
}
410466
}
411467

412468
// 4) EXX
@@ -484,6 +540,32 @@ void ESolver_KS_LCAO<TK, TR>::iter_finish(UnitCell& ucell, const int istep, int&
484540
this->pv, this->gd, this->psi, this->chr, this->p_chgmix,
485541
hamilt_lcao, this->orb_, this->deepks,
486542
this->exx_nao, iter, istep, conv_esolver, this->scf_ene_thr);
543+
544+
// Update subspace cache after each SCF iteration (for next iteration acceleration)
545+
// First SCF step (istep=0, iter=1): full diagonalization already done by HSolverLCAO
546+
// -> update_subspace_cache() uses the converged wavefunctions to build initial cache
547+
// Subsequent SCF steps: subspace solver used, then cache updated with new wavefunctions
548+
if constexpr (std::is_same_v<TK, std::complex<double>>)
549+
{
550+
if (PARAM.inp.lcao_subspace_persistent)
551+
{
552+
if (!this->subspace_solver_)
553+
{
554+
this->subspace_solver_.reset(
555+
new hsolver::HSolverLCAOSubspace(&(this->pv), PARAM.inp.ks_solver));
556+
this->subspace_solver_->set_persistent(true);
557+
}
558+
559+
// Update subspace cache using current wavefunctions
560+
// lambda_ref is zero because we don't use DeltaSpin perturbation here
561+
std::vector<ModuleBase::Vector3<double>> lambda_ref(ucell.nat, {0.0, 0.0, 0.0});
562+
this->subspace_solver_->update_subspace_cache(
563+
static_cast<hamilt::Hamilt<TK>*>(this->p_hamilt),
564+
this->psi[0],
565+
this->pelec,
566+
lambda_ref);
567+
}
568+
}
487569
}
488570

489571
template <typename TK, typename TR>

source/source_esolver/esolver_ks_lcao.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "source_lcao/setup_exx.h" // for exx, mohan add 20251008
1212
#include "source_lcao/module_rdmft/rdmft.h" // rdmft
1313
#include "source_lcao/setup_dm.h" // mohan add 2025-10-30
14+
#include "source_hsolver/hsolver_lcao_subspace.h" // subspace solver for SCF acceleration
1415

1516
#include <memory>
1617

@@ -92,6 +93,10 @@ class ESolver_KS_LCAO : public ESolver_KS
9293
//! For RDMFT calculations, added by jghan, 2024-03-16
9394
rdmft::RDMFT<TK, TR> rdmft_solver;
9495

96+
//! Subspace solver for accelerating SCF in MD/relax ionic steps
97+
std::unique_ptr<hsolver::HSolverLCAOSubspace> subspace_solver_;
98+
std::vector<ModuleBase::Vector3<double>> last_atom_positions_;
99+
95100
//! For linear-response TDDFT
96101
friend class LR::ESolver_LR<double, double>;
97102
friend class LR::ESolver_LR<std::complex<double>, double>;

source/source_hsolver/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ if(ENABLE_LCAO)
2020
if (ENABLE_MPI)
2121
list(APPEND objects
2222
hsolver_lcao.cpp
23+
hsolver_lcao_subspace.cpp
2324
diago_scalapack.cpp
2425
parallel_k2d.cpp
2526
diago_lapack.cpp
2627
)
2728
else ()
2829
list(APPEND objects
2930
hsolver_lcao.cpp
31+
hsolver_lcao_subspace.cpp
3032
diago_lapack.cpp
3133
)
3234
endif ()

0 commit comments

Comments
 (0)