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
276280template <typename T, typename Device>
277281void 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
0 commit comments