Skip to content

Commit ee2f862

Browse files
committed
Revert Rayleigh-Ritz rotation frequency reduction
Skipping the Ritz rotation on non-rr_step iterations broke convergence when some bands were locked: the subspace diagonalization then mixes locked and active columns, so the eigenvalue-to-column mapping is wrong and the residual is corrupted, driving bands to the wrong eigenvalues. The rotation is required to keep the mapping correct, so revert to rotating every iteration.
1 parent c2afe2e commit ee2f862

3 files changed

Lines changed: 87 additions & 118 deletions

File tree

source/source_hsolver/diago_ppcg.cpp

Lines changed: 81 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -607,32 +607,6 @@ void DiagoPPCG<T, Device>::lock_epairs(
607607
}
608608
}
609609

610-
// ---------------------------------------------------------------------------
611-
// Compute the residual w_i = H|psi_i> - eps_i * S|psi_i> from the current
612-
// (already updated) hpsi_/spsi_ and lock converged eigenpairs. Used on the
613-
// block-update iterations where a full Rayleigh-Ritz rotation is skipped.
614-
// ---------------------------------------------------------------------------
615-
template <typename T, typename Device>
616-
void DiagoPPCG<T, Device>::compute_residual_and_lock(
617-
Real* eigenvalue,
618-
std::vector<int>& active_cols,
619-
const std::vector<double>& ethr_band)
620-
{
621-
set_zero(w_);
622-
#ifdef _OPENMP
623-
#pragma omp parallel for collapse(2) schedule(static) if (n_dim_ * n_band_ > ppcg_openmp_work_threshold)
624-
#endif
625-
for (int j = 0; j < n_band_; ++j)
626-
{
627-
for (int ig = 0; ig < n_dim_; ++ig)
628-
{
629-
w_[idx(ig, j, ld_psi_)] = hpsi_[idx(ig, j, ld_psi_)]
630-
- spsi_[idx(ig, j, ld_psi_)] * eigenvalue[j];
631-
}
632-
}
633-
lock_epairs(eval_prev_.data(), eigenvalue, ethr_band, active_cols);
634-
}
635-
636610
// ---------------------------------------------------------------------------
637611
// Build K = V^H H V and M = V^H S V where V = [psi, w]
638612
// ---------------------------------------------------------------------------
@@ -969,9 +943,13 @@ template <typename T, typename Device>
969943
void DiagoPPCG<T, Device>::rayleigh_ritz(
970944
T* psi, Real* eigenvalue,
971945
std::vector<int>& active_cols,
972-
const std::vector<double>& ethr_band,
973-
bool rotate)
946+
const std::vector<double>& ethr_band)
974947
{
948+
// Remember the eigenvalues of the previous step; convergence is measured
949+
// as the eigenvalue change between successive Rayleigh-Ritz steps.
950+
eval_prev_.resize(n_band_);
951+
std::copy(eigenvalue, eigenvalue + n_band_, eval_prev_.begin());
952+
975953
gram(psi, hpsi_.data(), n_band_, n_band_, rr_hsub_, n_band_);
976954
gram(psi, spsi_.data(), n_band_, n_band_, rr_ssub_, n_band_);
977955

@@ -999,59 +977,56 @@ void DiagoPPCG<T, Device>::rayleigh_ritz(
999977

1000978
if (sygvd_ok)
1001979
{
1002-
if (rotate)
1003-
{
1004-
const int sz = ld_psi_ * n_band_;
1005-
std::copy(psi, psi + sz, rr_psi_.begin());
1006-
std::copy(spsi_.begin(), spsi_.end(), rr_spsi_.begin());
1007-
std::copy(hpsi_.begin(), hpsi_.end(), rr_hpsi_.begin());
1008-
1009-
std::fill(psi, psi + ld_psi_ * n_band_, T(0));
1010-
set_zero(spsi_);
1011-
set_zero(hpsi_);
1012-
1013-
const T one = T(1);
1014-
const T zero = T(0);
1015-
ModuleBase::gemm_op<T, Device>()('N',
1016-
'N',
1017-
n_dim_,
1018-
n_band_,
1019-
n_band_,
1020-
&one,
1021-
rr_psi_.data(),
1022-
ld_psi_,
1023-
rr_hsub_.data(),
1024-
n_band_,
1025-
&zero,
1026-
psi,
1027-
ld_psi_);
1028-
ModuleBase::gemm_op<T, Device>()('N',
1029-
'N',
1030-
n_dim_,
1031-
n_band_,
1032-
n_band_,
1033-
&one,
1034-
rr_spsi_.data(),
1035-
ld_psi_,
1036-
rr_hsub_.data(),
1037-
n_band_,
1038-
&zero,
1039-
spsi_.data(),
1040-
ld_psi_);
1041-
ModuleBase::gemm_op<T, Device>()('N',
1042-
'N',
1043-
n_dim_,
1044-
n_band_,
1045-
n_band_,
1046-
&one,
1047-
rr_hpsi_.data(),
1048-
ld_psi_,
1049-
rr_hsub_.data(),
1050-
n_band_,
1051-
&zero,
1052-
hpsi_.data(),
1053-
ld_psi_);
1054-
}
980+
const int sz = ld_psi_ * n_band_;
981+
std::copy(psi, psi + sz, rr_psi_.begin());
982+
std::copy(spsi_.begin(), spsi_.end(), rr_spsi_.begin());
983+
std::copy(hpsi_.begin(), hpsi_.end(), rr_hpsi_.begin());
984+
985+
std::fill(psi, psi + ld_psi_ * n_band_, T(0));
986+
set_zero(spsi_);
987+
set_zero(hpsi_);
988+
989+
const T one = T(1);
990+
const T zero = T(0);
991+
ModuleBase::gemm_op<T, Device>()('N',
992+
'N',
993+
n_dim_,
994+
n_band_,
995+
n_band_,
996+
&one,
997+
rr_psi_.data(),
998+
ld_psi_,
999+
rr_hsub_.data(),
1000+
n_band_,
1001+
&zero,
1002+
psi,
1003+
ld_psi_);
1004+
ModuleBase::gemm_op<T, Device>()('N',
1005+
'N',
1006+
n_dim_,
1007+
n_band_,
1008+
n_band_,
1009+
&one,
1010+
rr_spsi_.data(),
1011+
ld_psi_,
1012+
rr_hsub_.data(),
1013+
n_band_,
1014+
&zero,
1015+
spsi_.data(),
1016+
ld_psi_);
1017+
ModuleBase::gemm_op<T, Device>()('N',
1018+
'N',
1019+
n_dim_,
1020+
n_band_,
1021+
n_band_,
1022+
&one,
1023+
rr_hpsi_.data(),
1024+
ld_psi_,
1025+
rr_hsub_.data(),
1026+
n_band_,
1027+
&zero,
1028+
hpsi_.data(),
1029+
ld_psi_);
10551030

10561031
for (int j = 0; j < n_band_; ++j)
10571032
{
@@ -1068,7 +1043,20 @@ void DiagoPPCG<T, Device>::rayleigh_ritz(
10681043
}
10691044

10701045
// Compute residual: w_i = H|psi_i> - eps_i * S|psi_i>
1071-
compute_residual_and_lock(eigenvalue, active_cols, ethr_band);
1046+
set_zero(w_);
1047+
#ifdef _OPENMP
1048+
#pragma omp parallel for collapse(2) schedule(static) if (n_dim_ * n_band_ > ppcg_openmp_work_threshold)
1049+
#endif
1050+
for (int j = 0; j < n_band_; ++j)
1051+
{
1052+
for (int ig = 0; ig < n_dim_; ++ig)
1053+
{
1054+
w_[idx(ig, j, ld_psi_)] = hpsi_[idx(ig, j, ld_psi_)]
1055+
- spsi_[idx(ig, j, ld_psi_)] * eigenvalue[j];
1056+
}
1057+
}
1058+
1059+
lock_epairs(eval_prev_.data(), eigenvalue, ethr_band, active_cols);
10721060
}
10731061

10741062
} // namespace hsolver
@@ -1537,8 +1525,6 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
15371525
rr_hsub_.resize(ncol * ncol);
15381526
rr_ssub_.resize(ncol * ncol);
15391527
rr_eval_.resize(ncol);
1540-
eval_prev_.resize(ncol);
1541-
std::copy(eigenvalue_in, eigenvalue_in + ncol, eval_prev_.begin());
15421528

15431529
std::vector<int> all_cols(ncol);
15441530
std::iota(all_cols.begin(), all_cols.end(), 0);
@@ -1585,9 +1571,7 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
15851571
if (strategy_ == PpcgStrategy::BLOCK_SUBSPACE)
15861572
{
15871573
// Initialize with Rayleigh-Ritz.
1588-
eval_prev_.resize(ncol);
1589-
std::copy(eigenvalue_in, eigenvalue_in + ncol, eval_prev_.begin());
1590-
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band, true);
1574+
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
15911575
// Recompute to keep hpsi/spi consistent with rotated psi.
15921576
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
15931577
apply_s_current(psi_in, spsi_.data(), ncol);
@@ -1608,11 +1592,6 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
16081592
const int nact = static_cast<int>(active_cols.size());
16091593
const int nsb = std::max(1, (nact + sbsize_ - 1) / sbsize_);
16101594

1611-
// Save the previous eigenvalues so that the convergence check can
1612-
// compare the eigenvalue change between successive iterations.
1613-
eval_prev_.resize(ncol);
1614-
std::copy(eigenvalue_in, eigenvalue_in + ncol, eval_prev_.begin());
1615-
16161595
// Precondition the residual.
16171596
divide_by_preconditioner(active_cols, prec, w_);
16181597
copy_cols(w_.data(), active_cols, w_active);
@@ -1652,19 +1631,14 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
16521631
update_one_block(psi_in, cols, l, subspace);
16531632
}
16541633

1655-
// Convergence check. The Ritz values are computed from a full
1656-
// subspace diagonalization every iteration; the Ritz rotation (and
1657-
// the H/S re-application it requires) is only done every rr_step_
1658-
// iterations to keep the basis numerically clean, because the block
1659-
// update already maintains a consistent H|psi>/S|psi>.
1660-
const bool do_rr = (iter % rr_step_) == 0;
1661-
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band, do_rr);
1662-
if (do_rr)
1663-
{
1664-
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1665-
apply_s_current(psi_in, spsi_.data(), ncol);
1666-
}
1667-
record_residual(iter, do_rr ? "rayleigh_ritz" : "block_update");
1634+
// Rayleigh-Ritz after each block update keeps the global subspace
1635+
// synchronized with the updated active vectors. The block update
1636+
// can otherwise drift into an ill-conditioned basis before the next
1637+
// Ritz rotation.
1638+
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
1639+
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
1640+
apply_s_current(psi_in, spsi_.data(), ncol);
1641+
record_residual(iter, "rayleigh_ritz");
16681642

16691643
++iter;
16701644
}
@@ -1680,7 +1654,7 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
16801654
// Diagonal Rayleigh quotients are poor approximations for random
16811655
// initial guesses; starting the CG loop with them produces wrong
16821656
// gradients that drive the search toward high-energy bands.
1683-
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band, true);
1657+
rayleigh_ritz(psi_in, eigenvalue_in, active_cols, ethr_band);
16841658
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);
16851659
apply_s_current(psi_in, spsi_.data(), ncol);
16861660
record_residual(0, "initial_rr");
@@ -1725,7 +1699,7 @@ double DiagoPPCG<T, Device>::diag(const HPsiFunc& hpsi_func,
17251699
apply_s_current(psi_in, spsi_.data(), ncol);
17261700

17271701
std::vector<int> dummy_active;
1728-
rayleigh_ritz(psi_in, eigenvalue_in, dummy_active, ethr_band, true);
1702+
rayleigh_ritz(psi_in, eigenvalue_in, dummy_active, ethr_band);
17291703

17301704
// Sync hpsi/spi to the rotated wavefunctions.
17311705
apply_h(hpsi_func, psi_in, hpsi_.data(), ncol);

source/source_hsolver/diago_ppcg.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,6 @@ class DiagoPPCG
178178
const std::vector<double>& ethr_band,
179179
std::vector<int>& active_cols) const;
180180

181-
void compute_residual_and_lock(Real* eigenvalue,
182-
std::vector<int>& active_cols,
183-
const std::vector<double>& ethr_band);
184-
185181
void build_small_subspace(const T* psi,
186182
const std::vector<int>& cols,
187183
SmallSubspace& subspace) const;
@@ -199,8 +195,7 @@ class DiagoPPCG
199195

200196
void rayleigh_ritz(T* psi, Real* eigenvalue,
201197
std::vector<int>& active_cols,
202-
const std::vector<double>& ethr_band,
203-
bool rotate);
198+
const std::vector<double>& ethr_band);
204199

205200
// -------------------------------------------------------------------------
206201
// Conjugate-gradient strategy helpers (File 2 style)

tests/01_PW/817_PW_PPCG/result.ref

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
etotref -4862.3309719168019001
2-
etotperatomref -2431.1654859584
3-
totalforceref 9.121064
4-
totalstressref 37222.764239
1+
etotref -4862.3309719757144194
2+
etotperatomref -2431.1654859879
3+
totalforceref 9.131552
4+
totalstressref 37222.701329
55
pointgroupref C_1
66
spacegroupref C_1
77
nksibzref 2
8-
totaltimeref 2.63
8+
totaltimeref 3.99

0 commit comments

Comments
 (0)