Skip to content

Commit 9b1bd12

Browse files
author
dyzheng
committed
Feat(psi): integrate k-point paging into solver and force/stress paths
- HSolverPW: load_k_to_gpu before each k-point in dav_subspace loop - psi_prepare: PAGED_GPU mode support, skip GPU prep when paging - setup_psi_pw: log active GPU memory mode - diago_dav_subspace: host buffer optimization (hcc_h/scc_h), d_eigenvalue sync fix, diag_zhegvx CPU fallback for GPU - forces/stress: load_k_to_gpu/ensure_k_on_gpu guards before k-point loops - onsite_proj: ensure_k_on_gpu in cal_occupations - kernel force/stress_op: add load_k_to_gpu before GPU kernel launch
1 parent cfe3238 commit 9b1bd12

11 files changed

Lines changed: 222 additions & 47 deletions

File tree

source/source_hsolver/diago_dav_subspace.cpp

Lines changed: 120 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "diago_dav_subspace.h"
22

3+
#include <type_traits>
4+
35
#include "diago_iter_assist.h"
46

57
#include "source_base/module_device/device.h"
@@ -103,6 +105,8 @@ Diago_DavSubspace<T, Device>::~Diago_DavSubspace()
103105
delmem_real_op()(this->d_precondition);
104106
delmem_complex_op()(this->d_scc);
105107
delmem_real_op()(this->d_eigenvalue);
108+
delmem_complex_h_op()(this->hcc_h);
109+
delmem_complex_h_op()(this->scc_h);
106110
}
107111
#endif
108112
}
@@ -275,17 +279,21 @@ int Diago_DavSubspace<T, Device>::diag_once(const HPsiFunc& hpsi_func,
275279

276280
template <typename T, typename Device>
277281
void Diago_DavSubspace<T, Device>::cal_grad(const HPsiFunc& hpsi_func,
278-
const HPsiFunc& spsi_func,
279-
const int& dim,
280-
const int& nbase,
281-
const int& notconv,
282-
T* psi_iter,
283-
T* hpsi,
284-
T* spsi,
285-
T* vcc,
286-
const int* unconv,
287-
std::vector<Real>* eigenvalue_iter)
282+
const HPsiFunc& spsi_func,
283+
const int& dim,
284+
const int& nbase,
285+
const int& notconv_ref,
286+
T* psi_iter,
287+
T* hpsi,
288+
T* spsi,
289+
T* vcc,
290+
const int* unconv,
291+
std::vector<Real>* eigenvalue_iter)
288292
{
293+
// Local copy: notconv_ref is a reference to this->notconv which may be
294+
// modified to 0 inside this function (all_zero path). We need the original
295+
// value for the remainder of the function.
296+
const int notconv = notconv_ref;
289297
ModuleBase::timer::start("Diago_DavSubspace", "cal_grad");
290298

291299
for (size_t i = 0; i < notconv; i++)
@@ -436,13 +444,23 @@ void Diago_DavSubspace<T, Device>::cal_grad(const HPsiFunc& hpsi_func,
436444
{
437445
if (psi_norm_host[i] <= 1.0e-12)
438446
{
439-
std::cout << "Diago_DavSubspace::cal_grad: psi_norm <= 0 for band " << i << std::endl;
440-
std::cout << "This may be due to npwx < nbands: the number of plane waves is less than" << std::endl;
441-
std::cout << "the number of bands, leading to a rank-deficient problem." << std::endl;
442-
std::cout << "Please increase ecutwfc or reduce nbands." << std::endl;
443-
delmem_real_h_op()(psi_norm_host);
444-
delmem_real_op()(psi_norm);
445-
ModuleBase::WARNING_QUIT("cal_grad", "psi_norm <= 0");
447+
std::cout << "Diago_DavSubspace::cal_grad: norm of new direction for band " << i
448+
<< " is too small, treating as converged." << std::endl;
449+
}
450+
}
451+
{
452+
bool all_zero = true;
453+
for (int i = 0; i < notconv; i++)
454+
{
455+
if (psi_norm_host[i] > 1.0e-12)
456+
{
457+
all_zero = false;
458+
break;
459+
}
460+
}
461+
if (all_zero && notconv > 0)
462+
{
463+
this->notconv = 0;
446464
}
447465
}
448466
delmem_real_h_op()(psi_norm_host);
@@ -466,12 +484,23 @@ void Diago_DavSubspace<T, Device>::cal_grad(const HPsiFunc& hpsi_func,
466484
{
467485
if (psi_norm[i] <= 1.0e-12)
468486
{
469-
std::cout << "Diago_DavSubspace::cal_grad: psi_norm <= 0 for band " << i << std::endl;
470-
std::cout << "This may be due to npwx < nbands: the number of plane waves is less than" << std::endl;
471-
std::cout << "the number of bands, leading to a rank-deficient problem." << std::endl;
472-
std::cout << "Please increase ecutwfc or reduce nbands." << std::endl;
473-
delmem_real_h_op()(psi_norm);
474-
ModuleBase::WARNING_QUIT("cal_grad", "psi_norm <= 0");
487+
std::cout << "Diago_DavSubspace::cal_grad: norm of new direction for band " << i
488+
<< " is too small, treating as converged." << std::endl;
489+
}
490+
}
491+
{
492+
bool all_zero = true;
493+
for (int i = 0; i < notconv; i++)
494+
{
495+
if (psi_norm[i] > 1.0e-12)
496+
{
497+
all_zero = false;
498+
break;
499+
}
500+
}
501+
if (all_zero && notconv > 0)
502+
{
503+
this->notconv = 0;
475504
}
476505
}
477506
delmem_real_h_op()(psi_norm);
@@ -585,8 +614,30 @@ void Diago_DavSubspace<T, Device>::cal_elem(const int& dim,
585614
mtfunc::dsp_dav_subspace_reduce(hcc, scc, nbase, this->nbase_x, this->notconv, this->diag_comm.comm);
586615
#else
587616
assert(this->diag_comm.comm == POOL_WORLD);
588-
Parallel_Reduce::reduce_pool(hcc + nbase * this->nbase_x, notconv * this->nbase_x);
589-
Parallel_Reduce::reduce_pool(scc + nbase * this->nbase_x, notconv * this->nbase_x);
617+
// For GPU, need to copy to CPU before MPI reduction
618+
if constexpr (std::is_same<Device, base_device::DEVICE_GPU>::value)
619+
{
620+
// Allocate CPU buffers if needed
621+
if (this->hcc_h == nullptr)
622+
{
623+
resmem_complex_h_op()(this->hcc_h, this->nbase_x * this->nbase_x, "DAV::hcc_h");
624+
resmem_complex_h_op()(this->scc_h, this->nbase_x * this->nbase_x, "DAV::scc_h");
625+
}
626+
// Copy from GPU to CPU
627+
syncmem_d2h_op()(this->hcc_h, hcc + nbase * this->nbase_x, notconv * this->nbase_x);
628+
syncmem_d2h_op()(this->scc_h, scc + nbase * this->nbase_x, notconv * this->nbase_x);
629+
// Reduce on CPU
630+
Parallel_Reduce::reduce_pool(this->hcc_h, notconv * this->nbase_x);
631+
Parallel_Reduce::reduce_pool(this->scc_h, notconv * this->nbase_x);
632+
// Copy back to GPU
633+
syncmem_h2d_op()(hcc + nbase * this->nbase_x, this->hcc_h, notconv * this->nbase_x);
634+
syncmem_h2d_op()(scc + nbase * this->nbase_x, this->scc_h, notconv * this->nbase_x);
635+
}
636+
else
637+
{
638+
Parallel_Reduce::reduce_pool(hcc + nbase * this->nbase_x, notconv * this->nbase_x);
639+
Parallel_Reduce::reduce_pool(scc + nbase * this->nbase_x, notconv * this->nbase_x);
640+
}
590641
#endif
591642
}
592643
#endif
@@ -615,9 +666,24 @@ void Diago_DavSubspace<T, Device>::diag_zhegvx(const int& nbase,
615666
#if defined(__CUDA) || defined(__ROCM)
616667
if (this->diag_comm.rank == 0)
617668
{
618-
syncmem_complex_op()(this->d_scc, scc, nbase * this->nbase_x);
619-
ct::kernels::lapack_hegvd<T, ct_Device>()(nbase, this->nbase_x, this->hcc, this->d_scc, this->d_eigenvalue, this->vcc);
620-
syncmem_var_d2h_op()((*eigenvalue_iter).data(), this->d_eigenvalue, this->nbase_x);
669+
// Copy hcc and scc from GPU to CPU for robust eigensolving
670+
std::vector<T> hcc_h(nbase * this->nbase_x, *this->zero);
671+
std::vector<T> scc_h(nbase * this->nbase_x, *this->zero);
672+
syncmem_d2h_op()(hcc_h.data(), this->hcc, nbase * this->nbase_x);
673+
syncmem_d2h_op()(scc_h.data(), scc, nbase * this->nbase_x);
674+
675+
// Solve on CPU using hegvx (more robust than hegvd for ill-conditioned overlap)
676+
std::vector<T> vcc_h(nbase * this->nbase_x, *this->zero);
677+
hegvx_op<T, base_device::DEVICE_CPU>()(this->cpu_ctx,
678+
nbase,
679+
this->nbase_x,
680+
hcc_h.data(),
681+
scc_h.data(),
682+
nband,
683+
(*eigenvalue_iter).data(),
684+
vcc_h.data());
685+
// Copy eigenvectors back to GPU
686+
syncmem_h2d_op()(this->vcc, vcc_h.data(), nbase * this->nbase_x);
621687
}
622688
#endif
623689
}
@@ -715,9 +781,25 @@ void Diago_DavSubspace<T, Device>::diag_zhegvx(const int& nbase,
715781
if (this->diag_comm.nproc > 1)
716782
{
717783
// vcc: nbase * nband
718-
for (int i = 0; i < nband; i++)
784+
if constexpr (std::is_same<Device, base_device::DEVICE_GPU>::value)
719785
{
720-
MPI_Bcast(&vcc[i * this->nbase_x], nbase, MPI_DOUBLE_COMPLEX, 0, this->diag_comm.comm);
786+
// Copy vcc from GPU to CPU for MPI broadcast
787+
T* vcc_h = nullptr;
788+
resmem_complex_h_op()(vcc_h, nband * this->nbase_x, "DAV::vcc_h");
789+
syncmem_d2h_op()(vcc_h, vcc, nband * this->nbase_x);
790+
for (int i = 0; i < nband; i++)
791+
{
792+
MPI_Bcast(&vcc_h[i * this->nbase_x], nbase, MPI_DOUBLE_COMPLEX, 0, this->diag_comm.comm);
793+
}
794+
syncmem_h2d_op()(vcc, vcc_h, nband * this->nbase_x);
795+
delmem_complex_h_op()(vcc_h);
796+
}
797+
else
798+
{
799+
for (int i = 0; i < nband; i++)
800+
{
801+
MPI_Bcast(&vcc[i * this->nbase_x], nbase, MPI_DOUBLE_COMPLEX, 0, this->diag_comm.comm);
802+
}
721803
}
722804
MPI_Bcast((*eigenvalue_iter).data(), nband, MPI_DOUBLE, 0, this->diag_comm.comm);
723805
}
@@ -795,6 +877,14 @@ void Diago_DavSubspace<T, Device>::refresh(const int& dim,
795877

796878
if (this->device == base_device::GpuDevice)
797879
{
880+
#if defined(__CUDA) || defined(__ROCM)
881+
// this->d_eigenvalue was last synced inside cal_grad, i.e. BEFORE the
882+
// latest diag_zhegvx. eigenvalue_in_hsolver holds the up-to-date
883+
// eigenvalues, so refresh the device copy here; otherwise the restarted
884+
// subspace Hamiltonian gets a stale diagonal and the Davidson
885+
// iteration diverges on GPU.
886+
syncmem_var_h2d_op()(this->d_eigenvalue, eigenvalue_in_hsolver, nband);
887+
#endif
798888
refresh_hcc_scc_vcc_op<T, Device>()(nbase, hcc, scc, vcc, this->nbase_x, this->d_eigenvalue, this->one_);
799889
}
800890
else

source/source_hsolver/diago_dav_subspace.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,18 @@ class Diago_DavSubspace
190190
using syncmem_h2d_op = base_device::memory::synchronize_memory_op<T, Device, base_device::DEVICE_CPU>;
191191
using syncmem_d2h_op = base_device::memory::synchronize_memory_op<T, base_device::DEVICE_CPU, Device>;
192192

193+
using resmem_complex_h_op = base_device::memory::resize_memory_op<T, base_device::DEVICE_CPU>;
194+
using delmem_complex_h_op = base_device::memory::delete_memory_op<T, base_device::DEVICE_CPU>;
195+
193196
// Note that ct_Device is different from base_device!
194197
using ct_Device = typename ct::PsiToContainer<Device>::type;
195198
// using hegvd_op = container::kernels::lapack_hegvd<T, ct_Device>;
196199

197200
const T *one = nullptr, *zero = nullptr, *neg_one = nullptr;
198201
const T one_ = static_cast<T>(1.0), zero_ = static_cast<T>(0.0), neg_one_ = static_cast<T>(-1.0);
202+
203+
T* hcc_h = nullptr;
204+
T* scc_h = nullptr;
199205
};
200206

201207
} // namespace hsolver

source/source_hsolver/hsolver_pw.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "source_hsolver/diago_dav_subspace.h"
1313
#include "source_hsolver/diago_david.h"
1414
#include "source_hsolver/diago_iter_assist.h"
15+
#include "source_io/module_parameter/parameter.h"
1516
#include "source_psi/psi.h"
1617
#include "source_estate/elecstate_tools.h"
1718

@@ -109,21 +110,23 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
109110
// update H(k) for each k point
110111
pHamilt->updateHk(ik);
111112

112-
113+
psi.load_k_to_gpu(ik);
113114

114115
// update psi pointer for each k point
115116
psi.fix_k(ik);
116117

117118
// If using k-point continuity and not first k-point, propagate from parent
118-
if (ik > 0 && count == 0 && k_parent.find(ik) != k_parent.end()) {
119+
// Skip in PAGED_GPU mode: only one k-point resides on GPU at a time
120+
if (ik > 0 && count == 0 && k_parent.find(ik) != k_parent.end()
121+
&& psi.get_storage_mode() != psi::PsiStorageMode::PAGED_GPU) {
119122
propagate_psi(psi, k_parent[ik], ik);
120123
}
121124

122125
// template add precondition calculating here
123126
update_precondition(precondition, ik, this->wfc_basis->npwk[ik], Real(pes->pot->get_vl_of_0()));
124127

125128
// use smooth threshold for all iter methods
126-
if (this->diago_smooth_ethr == true)
129+
if (PARAM.inp.diago_smooth_ethr == true)
127130
{
128131
this->cal_smooth_ethr(pes->klist->wk[ik],
129132
&pes->wg(ik, 0),
@@ -136,6 +139,8 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
136139
// solve eigenvector and eigenvalue for H(k)
137140
this->hamiltSolvePsiK(pHamilt, psi, precondition, eigenvalues.data() + ik * psi.get_nbands(), this->wfc_basis->nks);
138141

142+
psi.store_k_from_gpu(ik);
143+
139144
if (skip_charge)
140145
{
141146
GlobalV::ofs_running << " Average iterative diagonalization steps for k-points " << ik
@@ -152,7 +157,7 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
152157
// update H(k) for each k point
153158
pHamilt->updateHk(ik);
154159

155-
160+
psi.load_k_to_gpu(ik);
156161

157162
// update psi pointer for each k point
158163
psi.fix_k(ik);
@@ -161,7 +166,7 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
161166
update_precondition(precondition, ik, this->wfc_basis->npwk[ik], Real(pes->pot->get_vl_of_0()));
162167

163168
// use smooth threshold for all iter methods
164-
if (this->diago_smooth_ethr == true)
169+
if (PARAM.inp.diago_smooth_ethr == true)
165170
{
166171
this->cal_smooth_ethr(pes->klist->wk[ik],
167172
&pes->wg(ik, 0),
@@ -174,6 +179,8 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
174179
// solve eigenvector and eigenvalue for H(k)
175180
this->hamiltSolvePsiK(pHamilt, psi, precondition, eigenvalues.data() + ik * psi.get_nbands(), this->wfc_basis->nks);
176181

182+
psi.store_k_from_gpu(ik);
183+
177184
// output iteration information and reset avg_iter
178185
if (skip_charge)
179186
{
@@ -211,7 +218,7 @@ void HSolverPW<T, Device>::solve(hamilt::Hamilt<T, Device>* pHamilt,
211218
elecstate::calEBand(_pes_pw->ekb,_pes_pw->wg,_pes_pw->f_en);
212219
if (skip_charge)
213220
{
214-
if (this->use_uspp)
221+
if (PARAM.globalv.use_uspp)
215222
{
216223
reinterpret_cast<elecstate::ElecStatePW<T, Device>*>(pes)->cal_becsum(psi);
217224
}
@@ -319,7 +326,7 @@ void HSolverPW<T, Device>::hamiltSolvePsiK(hamilt::Hamilt<T, Device>* hm,
319326
const int nbasis = psi.get_nbasis();
320327
const int ndim = psi.get_current_ngk();
321328
DiagoBPCG<T, Device> bpcg(pre_condition.data());
322-
bpcg.init_iter(this->nbands, nband_l, nbasis, ndim);
329+
bpcg.init_iter(PARAM.inp.nbands, nband_l, nbasis, ndim);
323330
bpcg.diag(hpsi_func, psi.get_pointer(), eigenvalue, this->ethr_band);
324331
}
325332
else if (this->method == "dav_subspace")
@@ -330,12 +337,12 @@ void HSolverPW<T, Device>::hamiltSolvePsiK(hamilt::Hamilt<T, Device>* hm,
330337
psi.get_nbands(),
331338
psi.get_k_first() ? psi.get_current_ngk()
332339
: psi.get_nk() * psi.get_nbasis(),
333-
this->pw_diag_ndim,
340+
PARAM.inp.pw_diag_ndim,
334341
this->diag_thr,
335342
this->diag_iter_max,
336343
comm_info,
337-
this->diag_subspace,
338-
this->nb2d);
344+
PARAM.inp.diag_subspace,
345+
PARAM.inp.nb2d);
339346

340347
DiagoIterAssist<T, Device>::avg_iter += static_cast<double>(
341348
dav_subspace.diag(hpsi_func,
@@ -365,7 +372,7 @@ void HSolverPW<T, Device>::hamiltSolvePsiK(hamilt::Hamilt<T, Device>* hm,
365372
const int nband = psi.get_nbands(); /// number of eigenpairs sought
366373
const int ld_psi = psi.get_nbasis(); /// leading dimension of psi
367374

368-
DiagoDavid<T, Device> david(pre_condition.data(), nband, dim, this->pw_diag_ndim, comm_info);
375+
DiagoDavid<T, Device> david(pre_condition.data(), nband, dim, PARAM.inp.pw_diag_ndim, comm_info);
369376
// do diag and add davidson iteration counts up to avg_iter
370377
DiagoIterAssist<T, Device>::avg_iter += static_cast<double>(
371378
david.diag(hpsi_func,

source/source_psi/psi_prepare.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,16 @@ void PSIPrepare<T, Device>::initialize_psi(Psi<std::complex<double>>* psi,
176176
this->psi_initer->init_psig(psi_cpu->get_pointer(), ik);
177177
if (psi_device->get_pointer() != psi_cpu->get_pointer())
178178
{
179-
syncmem_h2d_op()(psi_device->get_pointer(), psi_cpu->get_pointer(), nbands_start * nbasis);
179+
if (kspw_psi->get_storage_mode() == psi::PsiStorageMode::PAGED_GPU)
180+
{
181+
T* dst_cpu = kspw_psi->get_cpu_pointer(ik);
182+
std::memcpy(dst_cpu, psi_cpu->get_pointer(), sizeof(T) * nbands_l * nbasis);
183+
kspw_psi->load_k_to_gpu(ik);
184+
}
185+
else
186+
{
187+
syncmem_h2d_op()(psi_device->get_pointer(), psi_cpu->get_pointer(), nbands_start * nbasis);
188+
}
180189
}
181190

182191

@@ -208,7 +217,13 @@ void PSIPrepare<T, Device>::initialize_psi(Psi<std::complex<double>>* psi,
208217
{
209218
if (psi_device->get_pointer() != kspw_psi->get_pointer())
210219
{
211-
syncmem_complex_op()(kspw_psi->get_pointer(), psi_device->get_pointer(), nbands_l * nbasis);
220+
if (kspw_psi->get_storage_mode() == psi::PsiStorageMode::PAGED_GPU)
221+
{
222+
}
223+
else
224+
{
225+
syncmem_complex_op()(kspw_psi->get_pointer(), psi_device->get_pointer(), nbands_l * nbasis);
226+
}
212227
}
213228
}
214229
}

0 commit comments

Comments
 (0)