votca 2026-dev
Loading...
Searching...
No Matches
rpa_uks.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2021 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20#include "votca/xtp/rpa_uks.h"
21
22#include <algorithm>
23#include <cmath>
24#include <iomanip>
25#include <sstream>
26
27#include "votca/xtp/aomatrix.h"
30#include "votca/xtp/vc2index.h"
31
32namespace {
33
34constexpr double kUKSApBPrefactor = 2.0;
35
36} // namespace
37
38namespace votca {
39namespace xtp {
40
41void RPA_UKS::UpdateRPAInputEnergies(const Eigen::VectorXd& dftenergies_alpha,
42 const Eigen::VectorXd& dftenergies_beta,
43 const Eigen::VectorXd& gwaenergies_alpha,
44 const Eigen::VectorXd& gwaenergies_beta,
45 Index qpmin) {
46 // Total number of orbitals retained in the RPA window.
47 const Index rpatotal = rpamax_ - rpamin_ + 1;
48
49 // Start from the DFT energies in the configured RPA window for each spin.
50 energies_alpha_ = dftenergies_alpha.segment(rpamin_, rpatotal);
51 energies_beta_ = dftenergies_beta.segment(rpamin_, rpatotal);
52
53 // Number of GW-corrected states available in the qp window for each spin.
54 const Index gwsize_alpha = Index(gwaenergies_alpha.size());
55 const Index gwsize_beta = Index(gwaenergies_beta.size());
56
57 // Replace DFT energies by GW energies where available.
58 energies_alpha_.segment(qpmin - rpamin_, gwsize_alpha) = gwaenergies_alpha;
59 energies_beta_.segment(qpmin - rpamin_, gwsize_beta) = gwaenergies_beta;
60
61 // Outside the explicitly corrected qp window, shift remaining occupied and
62 // virtual states by the largest observed correction in the corresponding
63 // sector. This follows the same idea as the restricted implementation, but
64 // is applied independently to alpha and beta channels.
66 qpmin, gwsize_alpha);
67 ShiftUncorrectedEnergies(energies_beta_, dftenergies_beta, homo_beta_, qpmin,
68 gwsize_beta);
69
71}
72
81
83 const Eigen::VectorXd*& omegas,
84 const std::vector<Eigen::VectorXd>*& modes) const {
85 if (!screening_cached_) {
87 }
88 omegas = &screening_omegas_;
89 modes = &screening_modes_;
90}
91
93 const rpa_eigensolution& rpa_solution = Diagonalize_H2p();
94 const Eigen::MatrixXd& XpY = rpa_solution.XpY;
95 const Eigen::VectorXd& omegas = rpa_solution.omega;
96
97 const Index lumo_alpha = homo_alpha_ + 1;
98 const Index lumo_beta = homo_beta_ + 1;
99
100 const Index n_occ_alpha = lumo_alpha - rpamin_;
101 const Index n_occ_beta = lumo_beta - rpamin_;
102 const Index n_unocc_alpha = rpamax_ - homo_alpha_;
103 const Index n_unocc_beta = rpamax_ - homo_beta_;
104
105 const Index size_alpha = n_occ_alpha * n_unocc_alpha;
106 const Index auxsize = Mmn_.alpha.auxsize();
107 const Index nmodes = Index(omegas.size());
108
109 std::vector<Eigen::VectorXd> all_modes;
110 all_modes.reserve(nmodes);
111 std::vector<double> all_norms;
112 all_norms.reserve(nmodes);
113
114 vc2index vc_alpha(0, 0, n_unocc_alpha);
115 vc2index vc_beta(0, 0, n_unocc_beta);
116
117 double max_norm = 0.0;
118 for (Index s = 0; s < nmodes; s++) {
119 Eigen::VectorXd mode = Eigen::VectorXd::Zero(auxsize);
120
121 for (Index v = 0; v < n_occ_alpha; v++) {
122 const auto Mmn_v = Mmn_.alpha[v].middleRows(n_occ_alpha, n_unocc_alpha);
123 const auto XpY_v = XpY.col(s).segment(vc_alpha.I(v, 0), n_unocc_alpha);
124 mode.noalias() += Mmn_v.transpose() * XpY_v;
125 }
126
127 for (Index v = 0; v < n_occ_beta; v++) {
128 const auto Mmn_v = Mmn_.beta[v].middleRows(n_occ_beta, n_unocc_beta);
129 const auto XpY_v =
130 XpY.col(s).segment(size_alpha + vc_beta.I(v, 0), n_unocc_beta);
131 mode.noalias() += Mmn_v.transpose() * XpY_v;
132 }
133
134 const double norm = mode.norm();
135 max_norm = std::max(max_norm, norm);
136 all_modes.push_back(std::move(mode));
137 all_norms.push_back(norm);
138 }
139
140 const double tol = 1e-10 * std::max(1.0, max_norm);
141
142 std::vector<Eigen::VectorXd> active_modes;
143 std::vector<double> active_omegas;
144 active_modes.reserve(nmodes);
145 active_omegas.reserve(nmodes);
146
147 for (Index s = 0; s < nmodes; s++) {
148 if (all_norms[s] > tol) {
149 active_modes.push_back(std::move(all_modes[s]));
150 active_omegas.push_back(omegas(s));
151 }
152 }
153
154 screening_omegas_ = Eigen::VectorXd::Zero(Index(active_omegas.size()));
155 for (Index s = 0; s < screening_omegas_.size(); s++) {
156 screening_omegas_(s) = active_omegas[std::size_t(s)];
157 }
158
159 screening_modes_ = std::move(active_modes);
160 screening_cached_ = true;
161}
162
163void RPA_UKS::ShiftUncorrectedEnergies(Eigen::VectorXd& energies,
164 const Eigen::VectorXd& dftenergies,
165 Index homo, Index qpmin, Index gwsize) {
166 const Index lumo = homo + 1;
167 const Index qpmax = qpmin + gwsize - 1;
168
169 // Largest absolute correction seen for occupied states inside the explicitly
170 // corrected GW window.
171 const double max_correction_occ =
172 getMaxCorrection(energies, dftenergies, qpmin, homo);
173
174 // Largest absolute correction seen for virtual states inside the explicitly
175 // corrected GW window.
176 const double max_correction_virt =
177 getMaxCorrection(energies, dftenergies, lumo, qpmax);
178
179 // The local "energies" vector is indexed relative to rpamin_, not relative to
180 // the absolute orbital numbering. Therefore qpmin and qpmax must first be
181 // shifted by rpamin_ when selecting head/tail segments.
182 energies.head(qpmin - rpamin_).array() -= max_correction_occ;
183 energies.tail(rpamax_ - qpmax).array() += max_correction_virt;
184}
185
186double RPA_UKS::getMaxCorrection(const Eigen::VectorXd& energies,
187 const Eigen::VectorXd& dftenergies, Index min,
188 Index max) const {
189 if (max < min) {
190 return 0.0;
191 }
192
193 const Index range = max - min + 1;
194
195 // Compare the current working energies (possibly partly GW-corrected) to the
196 // original DFT energies over the requested orbital range and return the
197 // largest absolute deviation.
198 const Eigen::VectorXd corrections =
199 energies.segment(min - rpamin_, range) - dftenergies.segment(min, range);
200 return corrections.cwiseAbs().maxCoeff();
201}
202
203template <bool imag>
204Eigen::MatrixXd RPA_UKS::calculate_epsilon(double frequency) const {
205 // The dielectric matrix lives in the auxiliary basis.
206 const Index size = Mmn_.alpha.auxsize();
207 Eigen::MatrixXd result = Eigen::MatrixXd::Zero(size, size);
208
209 // Accumulate the independent-particle polarizability contribution for one
210 // spin channel. The final dielectric matrix is the sum of alpha and beta
211 // contributions.
212 auto accumulate_spin = [&](const TCMatrix_gwbse& Mmn,
213 const Eigen::VectorXd& energies, Index homo) {
214 const Index lumo = homo + 1;
215
216 // Number of occupied and unoccupied orbitals in the selected RPA window for
217 // this spin channel.
218 const Index n_occ = lumo - rpamin_;
219 const Index n_unocc = rpamax_ - lumo + 1;
220
221 if (n_occ <= 0 || n_unocc <= 0) {
222 return;
223 }
224
225 const double freq2 = frequency * frequency;
226 const double eta2 = eta_ * eta_;
227
228 OpenMP_CUDA transform;
229 transform.createTemporaries(n_unocc, size);
230
231#pragma omp parallel
232 {
233 const Index threadid = OPENMP::getThreadId();
234
235#pragma omp for schedule(dynamic)
236 for (Index m_level = 0; m_level < n_occ; m_level++) {
237 // m_level labels an occupied state within the local [rpamin_, rpamax_]
238 // window. The associated virtual states are taken from the bottom part
239 // of the same local energy ladder.
240 const double qp_energy_m = energies(m_level);
241
242 // Mmn[m_level] stores the three-center objects for a fixed occupied
243 // state m against all n in the selected orbital window. bottomRows(...)
244 // extracts the virtual block n = lumo ... rpamax for this spin.
245 Eigen::MatrixXd Mmn_RPA = Mmn[m_level].bottomRows(n_unocc);
246 transform.PushMatrix(Mmn_RPA, threadid);
247
248 // Bare particle-hole energy differences Delta_e = e_a - e_i for this
249 // fixed occupied state i = m_level and all virtual states a.
250 const Eigen::ArrayXd deltaE =
251 energies.tail(n_unocc).array() - qp_energy_m;
252
253 Eigen::VectorXd denom;
254
255 if (imag) {
256 // Imaginary-frequency expression:
257 //
258 // contribution ~ 2 * Delta_e / (Delta_e^2 + w^2)
259 //
260 // This matches the algebraic structure used in the restricted code.
261 // The crucial difference is that here alpha and beta channels are
262 // added explicitly instead of being folded into a closed-shell
263 // degeneracy factor.
264 denom = 2.0 * deltaE / (deltaE.square() + freq2);
265 } else {
266 // Real-frequency expression with finite broadening eta:
267 //
268 // 2 * [ (Delta_e - w)/((Delta_e - w)^2 + eta^2)
269 // + (Delta_e + w)/((Delta_e + w)^2 + eta^2) ]
270 //
271 // Again, the explicit spin resolution is handled by summing over the
272 // two channels separately.
273 Eigen::ArrayXd deltEf = deltaE - frequency;
274 Eigen::ArrayXd sum = deltEf / (deltEf.square() + eta2);
275 deltEf = deltaE + frequency;
276 sum += deltEf / (deltEf.square() + eta2);
277 denom = sum;
278 }
279
280 // Contract the virtual-index weights back to the auxiliary basis. This
281 // is the same optimized RI reduction pattern as in the restricted code.
282 transform.A_TDA(denom, threadid);
283 }
284 }
285
286 // Add this spin-channel contribution to the final dielectric matrix.
287 result += transform.getReductionVar();
288 };
289
290 // Total independent-particle response is the sum of alpha and beta channels.
291 accumulate_spin(Mmn_.alpha, energies_alpha_, homo_alpha_);
292 accumulate_spin(Mmn_.beta, energies_beta_, homo_beta_);
293
294 // epsilon = 1 - v chi0 in the present RI convention ultimately appears here
295 // as an identity contribution on the auxiliary-space diagonal added to the
296 // accumulated response matrix, consistent with the restricted implementation.
297 result.diagonal().array() += 1.0;
298 return result;
299}
300
301template Eigen::MatrixXd RPA_UKS::calculate_epsilon<true>(
302 double frequency) const;
303template Eigen::MatrixXd RPA_UKS::calculate_epsilon<false>(
304 double frequency) const;
305
307 std::complex<double> frequency) const {
308 const Index size = Mmn_.alpha.auxsize();
309 Eigen::MatrixXd result = Eigen::MatrixXd::Zero(size, size);
310
311 // Same structure as above, but for the real part evaluated at complex
312 // frequency z = w + i*gamma.
313 auto accumulate_spin = [&](const TCMatrix_gwbse& Mmn,
314 const Eigen::VectorXd& energies, Index homo) {
315 const Index lumo = homo + 1;
316 const Index n_occ = lumo - rpamin_;
317 const Index n_unocc = rpamax_ - lumo + 1;
318
319 if (n_occ <= 0 || n_unocc <= 0) {
320 return;
321 }
322
323 OpenMP_CUDA transform;
324 transform.createTemporaries(n_unocc, size);
325
326#pragma omp parallel
327 {
328 const Index threadid = OPENMP::getThreadId();
329
330#pragma omp for schedule(dynamic)
331 for (Index m_level = 0; m_level < n_occ; m_level++) {
332 const double qp_energy_m = energies(m_level);
333
334 Eigen::MatrixXd Mmn_RPA = Mmn[m_level].bottomRows(n_unocc);
335 transform.PushMatrix(Mmn_RPA, threadid);
336
337 const Eigen::ArrayXd deltaE =
338 energies.tail(n_unocc).array() - qp_energy_m;
339
340 // Terms corresponding to Re[ 1 / (w + i*gamma - Delta_e) ] and its
341 // partner at -Delta_e, written in the same real-valued form as in the
342 // restricted implementation.
343 const Eigen::ArrayXd deltaEm = frequency.real() - deltaE;
344 const Eigen::ArrayXd deltaEp = frequency.real() + deltaE;
345
346 const double sigma_1 = std::pow(frequency.imag() + eta_, 2);
347 const double sigma_2 = std::pow(frequency.imag() - eta_, 2);
348
349 const Eigen::VectorXd chi =
350 deltaEm * (deltaEm.cwiseAbs2() + sigma_1).cwiseInverse() -
351 deltaEp * (deltaEp.cwiseAbs2() + sigma_2).cwiseInverse();
352
353 transform.A_TDA(chi, threadid);
354 }
355 }
356
357 // The overall prefactor follows the existing restricted convention. The
358 // spin resolution still enters explicitly through separate alpha/beta sums.
359 result -= transform.getReductionVar();
360 };
361
362 accumulate_spin(Mmn_.alpha, energies_alpha_, homo_alpha_);
363 accumulate_spin(Mmn_.beta, energies_beta_, homo_beta_);
364
365 result.diagonal().array() += 1.0;
366 return result;
367}
368
370 if (h2p_cached_) {
371 return h2p_solution_cache_;
372 }
373 // AmB contains the bare particle-hole energy differences and is diagonal in
374 // the particle-hole basis.
375 Eigen::VectorXd AmB = Calculate_H2p_AmB();
376
377 // ApB adds the Coulomb coupling between particle-hole states, including
378 // alpha-alpha, beta-beta, and mixed alpha-beta / beta-alpha blocks.
379 Eigen::MatrixXd ApB = Calculate_H2p_ApB();
380
382 sol.ERPA_correlation = -0.25 * (ApB.trace() + AmB.sum());
383
384 // Build the symmetrized Hermitian matrix
385 //
386 // C = sqrt(AmB) (ApB) sqrt(AmB)
387 //
388 // exactly as done in the restricted code path.
389 Eigen::MatrixXd& C = ApB;
390 C.applyOnTheLeft(AmB.cwiseSqrt().asDiagonal());
391 C.applyOnTheRight(AmB.cwiseSqrt().asDiagonal());
392
393 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es = Diagonalize_H2p_C(C);
394
395 sol.omega = Eigen::VectorXd::Zero(es.eigenvalues().size());
396 sol.omega = es.eigenvalues().cwiseSqrt();
397 sol.ERPA_correlation += 0.5 * sol.omega.sum();
398
400 << " Lowest neutral excitation energy (eV): "
401 << tools::conv::hrt2ev * sol.omega.minCoeff()
402 << std::flush;
403
405 << TimeStamp()
406 << " RPA correlation energy (Hartree): " << sol.ERPA_correlation
407 << std::flush;
408
409 const Index rpasize = Index(AmB.size());
410 sol.XpY = Eigen::MatrixXd(rpasize, rpasize);
411
412 // Reconstruct X+Y eigenvectors from the symmetrized eigenvectors.
413 const Eigen::VectorXd AmB_sqrt = AmB.cwiseSqrt();
414 const Eigen::VectorXd Omega_sqrt_inv = sol.omega.cwiseSqrt().cwiseInverse();
415 for (Index s = 0; s < rpasize; s++) {
416 sol.XpY.col(s) =
417 Omega_sqrt_inv(s) * AmB_sqrt.cwiseProduct(es.eigenvectors().col(s));
418 }
419
420 h2p_solution_cache_ = std::move(sol);
421 h2p_cached_ = true;
422 return h2p_solution_cache_;
423}
424
425Eigen::VectorXd RPA_UKS::Calculate_H2p_AmB() const {
426 const Index lumo_alpha = homo_alpha_ + 1;
427 const Index lumo_beta = homo_beta_ + 1;
428
429 const Index n_occ_alpha = lumo_alpha - rpamin_;
430 const Index n_occ_beta = lumo_beta - rpamin_;
431 const Index n_unocc_alpha = rpamax_ - lumo_alpha + 1;
432 const Index n_unocc_beta = rpamax_ - lumo_beta + 1;
433
434 // Total number of alpha and beta particle-hole excitations in the selected
435 // window.
436 const Index size_alpha = n_occ_alpha * n_unocc_alpha;
437 const Index size_beta = n_occ_beta * n_unocc_beta;
438
439 Eigen::VectorXd AmB = Eigen::VectorXd::Zero(size_alpha + size_beta);
440
441 // alpha block:
442 // excitation basis index enumerates all alpha (v -> c) combinations
443 vc2index vc_alpha(0, 0, n_unocc_alpha);
444 for (Index v = 0; v < n_occ_alpha; v++) {
445 const Index i = vc_alpha.I(v, 0);
446 AmB.segment(i, n_unocc_alpha) =
447 energies_alpha_.segment(n_occ_alpha, n_unocc_alpha).array() -
449 }
450
451 // beta block:
452 // appended after all alpha excitations
453 vc2index vc_beta(0, 0, n_unocc_beta);
454 for (Index v = 0; v < n_occ_beta; v++) {
455 const Index i = size_alpha + vc_beta.I(v, 0);
456 AmB.segment(i, n_unocc_beta) =
457 energies_beta_.segment(n_occ_beta, n_unocc_beta).array() -
459 }
460
461 return AmB;
462}
463
464Eigen::MatrixXd RPA_UKS::Calculate_H2p_ApB() const {
465 const Index lumo_alpha = homo_alpha_ + 1;
466 const Index lumo_beta = homo_beta_ + 1;
467
468 const Index n_occ_alpha = lumo_alpha - rpamin_;
469 const Index n_occ_beta = lumo_beta - rpamin_;
470 const Index n_unocc_alpha = rpamax_ - lumo_alpha + 1;
471 const Index n_unocc_beta = rpamax_ - lumo_beta + 1;
472
473 const Index size_alpha = n_occ_alpha * n_unocc_alpha;
474 const Index size_beta = n_occ_beta * n_unocc_beta;
475
476 Eigen::MatrixXd ApB =
477 Eigen::MatrixXd::Zero(size_alpha + size_beta, size_alpha + size_beta);
478
479 vc2index vc_alpha(0, 0, n_unocc_alpha);
480 vc2index vc_beta(0, 0, n_unocc_beta);
481
482 // alpha-alpha block:
483 // Coulomb coupling between alpha particle-hole excitations.
484#pragma omp parallel for schedule(guided)
485 for (Index v2 = 0; v2 < n_occ_alpha; v2++) {
486 const Index i2 = vc_alpha.I(v2, 0);
487 const Eigen::MatrixXd Mmn_v2T =
488 Mmn_.alpha[v2].middleRows(n_occ_alpha, n_unocc_alpha).transpose();
489
490 for (Index v1 = v2; v1 < n_occ_alpha; v1++) {
491 const Index i1 = vc_alpha.I(v1, 0);
492
493 // Only a single factor 2.0 is kept here from the algebraic A+B
494 // structure. The closed-shell spin degeneracy factor of the restricted
495 // implementation is not present anymore; spin is represented explicitly
496 // by separate alpha and beta blocks.
497 ApB.block(i1, i2, n_unocc_alpha, n_unocc_alpha) =
498 kUKSApBPrefactor *
499 Mmn_.alpha[v1].middleRows(n_occ_alpha, n_unocc_alpha) * Mmn_v2T;
500 }
501 }
502
503 // beta-beta block:
504#pragma omp parallel for schedule(guided)
505 for (Index v2 = 0; v2 < n_occ_beta; v2++) {
506 const Index i2 = size_alpha + vc_beta.I(v2, 0);
507 const Eigen::MatrixXd Mmn_v2T =
508 Mmn_.beta[v2].middleRows(n_occ_beta, n_unocc_beta).transpose();
509
510 for (Index v1 = v2; v1 < n_occ_beta; v1++) {
511 const Index i1 = size_alpha + vc_beta.I(v1, 0);
512 ApB.block(i1, i2, n_unocc_beta, n_unocc_beta) =
513 kUKSApBPrefactor *
514 Mmn_.beta[v1].middleRows(n_occ_beta, n_unocc_beta) * Mmn_v2T;
515 }
516 }
517
518 // alpha-beta block:
519 // In RPA, Coulomb coupling acts on total density fluctuations and therefore
520 // couples alpha and beta particle-hole sectors as well.
521#pragma omp parallel for schedule(guided)
522 for (Index v_beta = 0; v_beta < n_occ_beta; v_beta++) {
523 const Index i_beta = size_alpha + vc_beta.I(v_beta, 0);
524 const Eigen::MatrixXd Mmn_beta_T =
525 Mmn_.beta[v_beta].middleRows(n_occ_beta, n_unocc_beta).transpose();
526
527 for (Index v_alpha = 0; v_alpha < n_occ_alpha; v_alpha++) {
528 const Index i_alpha = vc_alpha.I(v_alpha, 0);
529 ApB.block(i_alpha, i_beta, n_unocc_alpha, n_unocc_beta) =
530 kUKSApBPrefactor *
531 Mmn_.alpha[v_alpha].middleRows(n_occ_alpha, n_unocc_alpha) *
532 Mmn_beta_T;
533 }
534 }
535
536 // Symmetrize alpha-alpha block from its computed lower triangle.
537 ApB.block(0, 0, size_alpha, size_alpha)
538 .template triangularView<Eigen::StrictlyUpper>() =
539 ApB.block(0, 0, size_alpha, size_alpha)
540 .transpose()
541 .template triangularView<Eigen::StrictlyUpper>();
542
543 // Symmetrize beta-beta block from its computed lower triangle.
544 ApB.block(size_alpha, size_alpha, size_beta, size_beta)
545 .template triangularView<Eigen::StrictlyUpper>() =
546 ApB.block(size_alpha, size_alpha, size_beta, size_beta)
547 .transpose()
548 .template triangularView<Eigen::StrictlyUpper>();
549
550 // Copy the mixed alpha-beta block into the beta-alpha block.
551 ApB.block(size_alpha, 0, size_beta, size_alpha) =
552 ApB.block(0, size_alpha, size_alpha, size_beta).transpose();
553
554 // Add the diagonal bare transition energies.
555 ApB.diagonal() += Calculate_H2p_AmB();
556
557 return ApB;
558}
559
560Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> RPA_UKS::Diagonalize_H2p_C(
561 const Eigen::MatrixXd& C) const {
563 << TimeStamp() << " Diagonalizing two-particle Hamiltonian "
564 << std::flush;
565
566 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(C);
567
569 << TimeStamp() << " Diagonalization done " << std::flush;
570
571 const double minCoeff = es.eigenvalues().minCoeff();
572 if (minCoeff <= 0.0) {
574 << TimeStamp() << " Detected non-positive eigenvalue: " << minCoeff
575 << std::flush;
576 throw std::runtime_error("Detected non-positive eigenvalue.");
577 }
578
579 return es;
580}
581
582} // namespace xtp
583} // namespace votca
void A_TDA(const Eigen::VectorXd &vec, Index OpenmpThread)
Eigen::MatrixXd getReductionVar()
void createTemporaries(Index rows, Index cols)
void PushMatrix(const Eigen::MatrixXd &mat, Index OpenmpThread)
void BuildCachedScreeningModes() const
Definition rpa_uks.cc:92
Eigen::VectorXd screening_omegas_
Definition rpa_uks.h:361
Eigen::VectorXd energies_beta_
Definition rpa_uks.h:348
const TCMatrix_gwbse_spin & Mmn_
Definition rpa_uks.h:354
Eigen::MatrixXd calculate_epsilon(double frequency) const
Internal implementation of epsilon(w).
Definition rpa_uks.cc:204
const double eta_
Definition rpa_uks.h:344
rpa_eigensolution h2p_solution_cache_
Definition rpa_uks.h:357
void InvalidateH2pCache()
Definition rpa_uks.cc:73
double getMaxCorrection(const Eigen::VectorXd &energies, const Eigen::VectorXd &dftenergies, Index min, Index max) const
Return the largest absolute GW correction in a given index range.
Definition rpa_uks.cc:186
std::vector< Eigen::VectorXd > screening_modes_
Definition rpa_uks.h:362
void GetCachedScreeningModes(const Eigen::VectorXd *&omegas, const std::vector< Eigen::VectorXd > *&modes) const
Definition rpa_uks.cc:82
Eigen::VectorXd Calculate_H2p_AmB() const
Construct the diagonal AmB block of the two-particle Hamiltonian.
Definition rpa_uks.cc:425
Eigen::VectorXd energies_alpha_
Definition rpa_uks.h:347
void ShiftUncorrectedEnergies(Eigen::VectorXd &energies, const Eigen::VectorXd &dftenergies, Index homo, Index qpmin, Index gwsize)
Shift uncorrected states outside the GW window.
Definition rpa_uks.cc:163
Eigen::MatrixXd Calculate_H2p_ApB() const
Construct the ApB block of the unrestricted two-particle Hamiltonian.
Definition rpa_uks.cc:464
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > Diagonalize_H2p_C(const Eigen::MatrixXd &C) const
Diagonalize the symmetrized two-particle matrix C.
Definition rpa_uks.cc:560
const rpa_eigensolution & Diagonalize_H2p() const
Diagonalize the unrestricted two-particle Hamiltonian.
Definition rpa_uks.cc:369
Eigen::MatrixXd calculate_epsilon_r(double frequency) const
Dielectric matrix on the real frequency axis.
Definition rpa_uks.h:121
void UpdateRPAInputEnergies(const Eigen::VectorXd &dftenergies_alpha, const Eigen::VectorXd &dftenergies_beta, const Eigen::VectorXd &gwaenergies_alpha, const Eigen::VectorXd &gwaenergies_beta, Index qpmin)
Update alpha and beta RPA input energies from DFT and GW energies.
Definition rpa_uks.cc:41
Timestamp returns the current time as a string Example: cout << TimeStamp()
Definition logger.h:224
This class transforms a pair of indices v,c into a compound index I, via I=ctotal*v+c the fast dimens...
Definition vc2index.h:36
Index I(Index v, Index c) const
Definition vc2index.h:42
#define XTP_LOG(level, log)
Definition logger.h:40
const double hrt2ev
Definition constants.h:53
Index getThreadId()
Definition eigen.h:143
Charge transport classes.
Definition ERIs.h:28
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
Solution of the two-particle Hamiltonian in the unrestricted basis.
Definition rpa_uks.h:242