|
6 | 6 | #include "source_cell/klist.h" |
7 | 7 | #include "source_cell/unitcell.h" |
8 | 8 | #include "source_pw/module_pwdft/onsite_proj.h" |
| 9 | +#include "source_io/module_unk/unk_overlap_pw.h" |
9 | 10 | #include "source_base/constants.h" |
| 11 | +#include "source_base/module_external/lapack_connector.h" |
10 | 12 | #include <iomanip> |
11 | 13 | #include <iostream> |
12 | 14 |
|
@@ -149,9 +151,9 @@ void deltap_iter_finish( |
149 | 151 | if (gamma_total == 0.0) |
150 | 152 | return; |
151 | 153 |
|
152 | | - // Compute per-atom gamma at lambda=0 (baseline) |
| 154 | + // Compute per-atom gamma via Wilson loop decomposition |
153 | 155 | std::vector<double> gamma_baseline(nat, 0.0); |
154 | | - compute_per_atom_gamma_from_becp(ucell, nocc, gamma_total, gamma_baseline); |
| 156 | + compute_per_atom_gamma_wilson(ucell, nocc, psi_cpu, wfcpw, rhopw, gdir, gamma_baseline); |
155 | 157 |
|
156 | 158 | double step = inp.deltap_lambda_step; |
157 | 159 | double mixing = inp.deltap_lambda_mixing; |
@@ -182,7 +184,7 @@ void deltap_iter_finish( |
182 | 184 | for (int inner = 0; inner < inner_nmax; inner++) |
183 | 185 | { |
184 | 186 | std::vector<double> gamma_trial(nat, 0.0); |
185 | | - compute_per_atom_gamma_from_becp(ucell, nocc, gamma_total, gamma_trial); |
| 187 | + compute_per_atom_gamma_wilson(ucell, nocc, psi_cpu, wfcpw, rhopw, gdir, gamma_trial); |
186 | 188 |
|
187 | 189 | double max_res_inner = 0.0; |
188 | 190 | for (int iat = 0; iat < nat; iat++) |
@@ -219,7 +221,7 @@ void deltap_iter_finish( |
219 | 221 |
|
220 | 222 | // Compute final max_res and per-atom gamma for output |
221 | 223 | std::vector<double> gamma_final(nat, 0.0); |
222 | | - compute_per_atom_gamma_from_becp(ucell, nocc, gamma_total, gamma_final); |
| 224 | + compute_per_atom_gamma_wilson(ucell, nocc, psi_cpu, wfcpw, rhopw, gdir, gamma_final); |
223 | 225 | double max_res = 0.0; |
224 | 226 | for (int iat = 0; iat < nat; iat++) |
225 | 227 | { |
@@ -307,4 +309,107 @@ void compute_per_atom_gamma_from_becp( |
307 | 309 | gamma_per_atom[iat] = gamma_total * w[iat] / w_total; |
308 | 310 | } |
309 | 311 |
|
| 312 | +void compute_per_atom_gamma_wilson( |
| 313 | + const UnitCell& ucell, |
| 314 | + int nocc, |
| 315 | + const psi::Psi<std::complex<double>>* psi_cpu, |
| 316 | + const ModulePW::PW_Basis_K* wfcpw, |
| 317 | + const ModulePW::PW_Basis* rhopw, |
| 318 | + int gdir, |
| 319 | + std::vector<double>& gamma_per_atom) |
| 320 | +{ |
| 321 | + int nat = ucell.nat; |
| 322 | + gamma_per_atom.assign(nat, 0.0); |
| 323 | + if (nocc < 1 || psi_cpu == nullptr || wfcpw == nullptr || rhopw == nullptr) return; |
| 324 | + |
| 325 | + int nk = psi_cpu->get_nk(); |
| 326 | + int npol = psi_cpu->get_npol(); |
| 327 | + |
| 328 | + // Build Wilson loop matrix M_{nm} = <u_n(k0)|e^{iG·r}|u_m(k0)> |
| 329 | + // For Gamma-only: single k-point, G-phase overlap gives the Berry phase matrix |
| 330 | + int m_dim = nocc; |
| 331 | + std::vector<std::complex<double>> M(m_dim * m_dim); |
| 332 | + unkOverlap_pw uw; |
| 333 | + |
| 334 | + ModuleBase::Vector3<double> G(0.0, 0.0, 0.0); |
| 335 | + if (gdir == 1) G = ModuleBase::Vector3<double>(1.0, 0.0, 0.0); |
| 336 | + else if (gdir == 2) G = ModuleBase::Vector3<double>(0.0, 1.0, 0.0); |
| 337 | + else G = ModuleBase::Vector3<double>(0.0, 0.0, 1.0); |
| 338 | + |
| 339 | + for (int nb = 0; nb < m_dim; nb++) |
| 340 | + for (int mb = 0; mb < m_dim; mb++) |
| 341 | + M[nb * m_dim + mb] = uw.unkdotp_G0(rhopw, wfcpw, 0, 0, nb, mb, psi_cpu, G); |
| 342 | + |
| 343 | + // Diagonalize: M = V · diag(lambda) · V^{-1} |
| 344 | + // LAPACK zgeev: column-major layout (Fortran order) |
| 345 | + // M is n×n column-major: M(nb, mb) = M[nb * n + mb] |
| 346 | + // For right eigenvectors: M * V = V * lambda |
| 347 | + int info = 0; |
| 348 | + std::vector<std::complex<double>> eigenvalues(m_dim); |
| 349 | + std::vector<std::complex<double>> VR(m_dim * m_dim); |
| 350 | + std::vector<std::complex<double>> work(4 * m_dim); |
| 351 | + std::vector<double> rwork(2 * m_dim); |
| 352 | + |
| 353 | + char jobvl = 'N'; |
| 354 | + char jobvr = 'V'; |
| 355 | + int lwork = 4 * m_dim; |
| 356 | + zgeev_(&jobvl, &jobvr, &m_dim, M.data(), &m_dim, eigenvalues.data(), |
| 357 | + nullptr, &m_dim, VR.data(), &m_dim, |
| 358 | + work.data(), &lwork, rwork.data(), &info); |
| 359 | + |
| 360 | + if (info != 0) return; |
| 361 | + |
| 362 | + std::vector<double> theta(m_dim); |
| 363 | + for (int n = 0; n < m_dim; n++) |
| 364 | + theta[n] = atan2(eigenvalues[n].imag(), eigenvalues[n].real()); |
| 365 | + |
| 366 | + // Get becp at k0 from OnsiteProjector |
| 367 | + auto* onsite_p = projectors::OnsiteProjector<double, base_device::DEVICE_CPU>::get_instance(); |
| 368 | + if (onsite_p == nullptr) return; |
| 369 | + int tot_nproj = onsite_p->get_tot_nproj(); |
| 370 | + if (tot_nproj == 0) return; |
| 371 | + const std::complex<double>* becp = onsite_p->get_becp(); |
| 372 | + if (becp == nullptr) return; |
| 373 | + |
| 374 | + // Project becp onto Wilson loop eigenvectors: proj[α, n] = Σ_m VR[m,n] × becp[α,m] |
| 375 | + // VR is column-major: VR(m, n) = VR[n * m_dim + m] |
| 376 | + std::vector<std::vector<double>> w_atom_band(nat, std::vector<double>(m_dim, 0.0)); |
| 377 | + |
| 378 | + int iproj = 0; |
| 379 | + for (int iat = 0; iat < nat; iat++) |
| 380 | + { |
| 381 | + int nh = onsite_p->get_nh(iat); |
| 382 | + for (int ip = 0; ip < nh; ip++) |
| 383 | + { |
| 384 | + for (int n = 0; n < m_dim; n++) |
| 385 | + { |
| 386 | + std::complex<double> proj_val(0.0, 0.0); |
| 387 | + for (int m = 0; m < m_dim; m++) |
| 388 | + { |
| 389 | + std::complex<double> vr_mn = VR[n * m_dim + m]; |
| 390 | + std::complex<double> becp_val = becp[m * tot_nproj + iproj]; |
| 391 | + proj_val += vr_mn * std::conj(becp_val); |
| 392 | + } |
| 393 | + double weight = proj_val.real() * proj_val.real() + proj_val.imag() * proj_val.imag(); |
| 394 | + w_atom_band[iat][n] += weight; |
| 395 | + } |
| 396 | + iproj++; |
| 397 | + } |
| 398 | + } |
| 399 | + |
| 400 | + // Normalize per-band weights and compute per-atom gamma |
| 401 | + double gamma_sum_check = 0.0; |
| 402 | + for (int n = 0; n < m_dim; n++) |
| 403 | + { |
| 404 | + double w_tot = 0.0; |
| 405 | + for (int iat = 0; iat < nat; iat++) w_tot += w_atom_band[iat][n]; |
| 406 | + if (w_tot < 1e-30) continue; |
| 407 | + for (int iat = 0; iat < nat; iat++) |
| 408 | + gamma_per_atom[iat] += w_atom_band[iat][n] * theta[n] / w_tot; |
| 409 | + } |
| 410 | + for (int iat = 0; iat < nat; iat++) gamma_sum_check += gamma_per_atom[iat]; |
| 411 | + double th_sum = 0; for (int n=0; n<m_dim; n++) th_sum += theta[n]; |
| 412 | + std::cout << " [Wilson] Σγ=" << gamma_sum_check << " Σθ=" << th_sum << std::endl; |
| 413 | +} |
| 414 | + |
310 | 415 | } // namespace pw_deltap |
0 commit comments