votca 2026-dev
Loading...
Searching...
No Matches
gw_uks.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2026 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 */
7
8#include <algorithm>
9#include <fstream>
10#include <iostream>
11#include <limits>
12#include <vector>
13
16#include "votca/xtp/gw_uks.h"
19#include "votca/xtp/rpa_uks.h"
22
24
25namespace votca {
26namespace xtp {
27
29 const Eigen::MatrixXd& vxc_alpha,
30 const Eigen::MatrixXd& vxc_beta,
31 const Eigen::VectorXd& dft_energies_alpha,
32 const Eigen::VectorXd& dft_energies_beta)
33 : log_(log),
34 Mmn_(Mmn),
35 vxc_alpha_(vxc_alpha),
36 vxc_beta_(vxc_beta),
37 dft_energies_alpha_(dft_energies_alpha),
38 dft_energies_beta_(dft_energies_beta),
39 rpa_(log, Mmn) {}
40
41void GW_UKS::configure(const options& opt) {
42 opt_ = opt;
43
44 // Normalize legacy and new grid-search settings once at configuration time.
45 // This keeps the UKS path numerically aligned with the RKS path.
47 qptotal_ = opt_.qpmax - opt_.qpmin + 1;
48 rpa_.configure(opt_.homo_alpha, opt_.homo_beta, opt_.rpamin, opt_.rpamax);
49 rpa_.setOutOfWindowShift(opt_.out_of_window_shift);
50
51 sigma_alpha_ = SigmaFactory_UKS().Create(opt_.sigma_integration, Mmn_, rpa_,
53 sigma_beta_ = SigmaFactory_UKS().Create(opt_.sigma_integration, Mmn_, rpa_,
55
56 if (!sigma_alpha_ || !sigma_beta_) {
57 throw std::runtime_error(
58 "GW_UKS currently supports only implemented unrestricted sigma "
59 "evaluators. At present this is 'ppm', 'cda', and 'exact'.");
60 }
61
63 sigma_opt.homo = opt_.homo_alpha;
64 sigma_opt.qpmax = opt_.qpmax;
65 sigma_opt.qpmin = opt_.qpmin;
66 sigma_opt.rpamin = opt_.rpamin;
67 sigma_opt.rpamax = opt_.rpamax;
68 sigma_opt.eta = opt_.eta;
69 sigma_opt.alpha = opt_.alpha;
70 sigma_opt.quadrature_scheme = opt_.quadrature_scheme;
71 sigma_opt.order = opt_.order;
72 sigma_alpha_->configure(sigma_opt);
73 sigma_opt.homo = opt_.homo_beta;
74 sigma_beta_->configure(sigma_opt);
75
76 Sigma_x_alpha_ = Eigen::MatrixXd::Zero(qptotal_, qptotal_);
77 Sigma_x_beta_ = Eigen::MatrixXd::Zero(qptotal_, qptotal_);
78 Sigma_c_alpha_ = Eigen::MatrixXd::Zero(qptotal_, qptotal_);
79 Sigma_c_beta_ = Eigen::MatrixXd::Zero(qptotal_, qptotal_);
80
81 auto print_spin_ranges = [&](Spin spin, Index homo) {
82 const Index lumo = homo + 1;
83
84 const Index n_occ_rpa =
85 (opt_.rpamin <= homo) ? (homo - opt_.rpamin + 1) : 0;
86 const Index n_virt_rpa =
87 (lumo <= opt_.rpamax) ? (opt_.rpamax - lumo + 1) : 0;
88
89 const Index qp_occ_min = opt_.qpmin;
90 const Index qp_occ_max = std::min(opt_.qpmax, homo);
91 const Index n_occ_qp =
92 (qp_occ_min <= qp_occ_max) ? (qp_occ_max - qp_occ_min + 1) : 0;
93
94 const Index qp_virt_min = std::max(opt_.qpmin, lumo);
95 const Index qp_virt_max = opt_.qpmax;
96 const Index n_virt_qp =
97 (qp_virt_min <= qp_virt_max) ? (qp_virt_max - qp_virt_min + 1) : 0;
98
100 << TimeStamp() << " UKS " << SpinName(spin)
101 << " effective ranges: HOMO=" << homo << " LUMO=" << lumo
102 << " | RPA occ[" << opt_.rpamin << ":" << homo << "]"
103 << " virt[" << lumo << ":" << opt_.rpamax << "]"
104 << " (#occ=" << n_occ_rpa << ", #virt=" << n_virt_rpa << ")"
105 << " | GW occ[" << qp_occ_min << ":" << qp_occ_max << "]"
106 << " virt[" << qp_virt_min << ":" << qp_virt_max << "]"
107 << " (#occ=" << n_occ_qp << ", #virt=" << n_virt_qp << ")"
108 << std::flush;
109 };
110
111 print_spin_ranges(Spin::Alpha, opt_.homo_alpha);
112 print_spin_ranges(Spin::Beta, opt_.homo_beta);
113
114 if (opt_.sigma_integration == "ppm") {
115 auto* ppm_a = dynamic_cast<Sigma_PPM_UKS*>(sigma_alpha_.get());
116 auto* ppm_b = dynamic_cast<Sigma_PPM_UKS*>(sigma_beta_.get());
117
118 if (ppm_a == nullptr || ppm_b == nullptr) {
119 throw std::runtime_error(
120 "GW_UKS: expected Sigma_PPM_UKS evaluators for "
121 "sigma_integration=ppm");
122 }
123
124 ppm_a->SetSharedPPM(ppm_);
125 ppm_b->SetSharedPPM(ppm_);
126 }
127}
128
129const Eigen::VectorXd& GW_UKS::DftEnergies(Spin spin) const {
131}
132const Eigen::MatrixXd& GW_UKS::Vxc(Spin spin) const {
133 return (spin == Spin::Alpha) ? vxc_alpha_ : vxc_beta_;
134}
135Eigen::MatrixXd& GW_UKS::SigmaX(Spin spin) {
136 return (spin == Spin::Alpha) ? Sigma_x_alpha_ : Sigma_x_beta_;
137}
138Eigen::MatrixXd& GW_UKS::SigmaC(Spin spin) {
139 return (spin == Spin::Alpha) ? Sigma_c_alpha_ : Sigma_c_beta_;
140}
141const Eigen::MatrixXd& GW_UKS::SigmaX(Spin spin) const {
142 return (spin == Spin::Alpha) ? Sigma_x_alpha_ : Sigma_x_beta_;
143}
144const Eigen::MatrixXd& GW_UKS::SigmaC(Spin spin) const {
145 return (spin == Spin::Alpha) ? Sigma_c_alpha_ : Sigma_c_beta_;
146}
151 return (spin == Spin::Alpha) ? *sigma_alpha_ : *sigma_beta_;
152}
154 return (spin == Spin::Alpha) ? opt_.homo_alpha : opt_.homo_beta;
155}
156const char* GW_UKS::SpinName(Spin spin) const {
157 return (spin == Spin::Alpha) ? "alpha" : "beta";
158}
159
160std::string GW_UKS::LevelLabel(Spin spin, Index level) const {
161 if (level == Homo(spin)) {
162 return " HOMO ";
163 } else if (level == Homo(spin) + 1) {
164 return " LUMO ";
165 } else {
166 return " Level";
167 }
168}
169
170const char* GW_UKS::OccupationTag(Spin spin, Index level) const {
171 return (level <= Homo(spin)) ? "occ" : "virt";
172}
173
175 const Eigen::VectorXd& dft_energies, Index homo) const {
176 Eigen::VectorXd shifted_energies = dft_energies;
177 shifted_energies.segment(homo + 1, dft_energies.size() - homo - 1).array() +=
178 opt_.shift;
179 return shifted_energies;
180}
181
182double GW_UKS::CalcSpinHomoLumoShift(const Eigen::VectorXd& frequencies,
183 Spin spin) const {
184 const Index homo = Homo(spin);
185 const Eigen::VectorXd& dft = DftEnergies(spin);
186 const double DFTgap = dft(homo + 1) - dft(homo);
187 const double QPgap =
188 frequencies(homo + 1 - opt_.qpmin) - frequencies(homo - opt_.qpmin);
189 return QPgap - DFTgap;
190}
191
193 Sigma_x_alpha_ = (1 - opt_.ScaHFX) * sigma_alpha_->CalcExchangeMatrix();
194 Sigma_x_beta_ = (1 - opt_.ScaHFX) * sigma_beta_->CalcExchangeMatrix();
195 if (core_exchange_alpha_.size() > 0) {
196 Sigma_x_alpha_ += (1 - opt_.ScaHFX) * core_exchange_alpha_;
197 Sigma_x_beta_ += (1 - opt_.ScaHFX) * core_exchange_beta_;
198 }
200 << TimeStamp()
201 << " Calculated spin-resolved Hartree exchange contribution"
202 << std::flush;
203
204 Eigen::VectorXd dft_shifted_alpha =
206 Eigen::VectorXd dft_shifted_beta =
208
210 << TimeStamp()
211 << " Scissor shifting alpha/beta DFT energies by: " << opt_.shift
212 << " Hrt" << std::flush;
213
214 rpa_.setRPAInputEnergies(
215 dft_shifted_alpha.segment(opt_.rpamin, opt_.rpamax - opt_.rpamin + 1),
216 dft_shifted_beta.segment(opt_.rpamin, opt_.rpamax - opt_.rpamin + 1));
217
218 Eigen::VectorXd frequencies_alpha =
219 dft_shifted_alpha.segment(opt_.qpmin, qptotal_);
220 Eigen::VectorXd frequencies_beta =
221 dft_shifted_beta.segment(opt_.qpmin, qptotal_);
222
223 Anderson mixing_alpha;
224 Anderson mixing_beta;
225 mixing_alpha.Configure(opt_.gw_mixing_order, opt_.gw_mixing_alpha);
226 mixing_beta.Configure(opt_.gw_mixing_order, opt_.gw_mixing_alpha);
227 // The QP energies of the last QP solve and the RPA energies it saw; see
228 // GW::CalculateGWPerturbation.
229 Eigen::VectorXd qp_solution_alpha = frequencies_alpha;
230 Eigen::VectorXd qp_solution_beta = frequencies_beta;
231 Eigen::VectorXd rpa_solved_alpha = rpa_.getRPAInputEnergiesAlpha();
232 Eigen::VectorXd rpa_solved_beta = rpa_.getRPAInputEnergiesBeta();
233
234 // alpha and beta RPA energies as one vector, for the screening update
235 auto rpa_both = [this]() {
236 const Eigen::VectorXd& a = rpa_.getRPAInputEnergiesAlpha();
237 const Eigen::VectorXd& b = rpa_.getRPAInputEnergiesBeta();
238 Eigen::VectorXd both(a.size() + b.size());
239 both << a, b;
240 return both;
241 };
242 ScreeningUpdate screening;
243 screening.Configure(ScreeningUpdate::Parse(opt_.screening_update),
244 opt_.screening_update_ratio,
245 opt_.screening_update_max_inner);
246 bool rebuild_screening = true;
247 Index mixing_iteration = 0; // see GW::CalculateGWPerturbation
248 Index n_iterations = 0;
249 // root tracking per spin, as in GW::CalculateGWPerturbation
250 tracked_root_[0].resize(0);
251 tracked_root_[1].resize(0);
252 tracking_active_ = false;
254 bool tracking_verified = false;
255 bool verifying = false; // this iteration is the verifying full search
256
257 for (Index i_gw = 0; i_gw < opt_.gw_sc_max_iterations; ++i_gw) {
258 gw_sc_iteration_ = i_gw;
259 tracking_active_ = opt_.qp_root_tracking && i_gw > 0 && !tracking_verified;
260 const Eigen::VectorXd roots_before_alpha = tracked_root_[0];
261 const Eigen::VectorXd roots_before_beta = tracked_root_[1];
262 if (opt_.reset_3c > 0 && i_gw % opt_.reset_3c == 0 && i_gw != 0) {
263 Mmn_.alpha.Rebuild();
264 Mmn_.beta.Rebuild();
266 << TimeStamp() << " Rebuilding alpha/beta 3c integrals" << std::flush;
267 rebuild_screening = true;
268 }
269
270 if (rebuild_screening) {
271 if (i_gw == 0) {
272 XTP_LOG(Log::error, log_) << TimeStamp() << " Building the screening; "
273 << MemoryBudget::Usage() << std::flush;
274 }
275 if (opt_.sigma_integration == "ppm") {
276 ppm_.PPM_construct_parameters(rpa_);
277 if (ppm_.InvalidModes() > 0) {
279 << TimeStamp() << " WARNING: " << ppm_.InvalidModes()
280 << " PPM modes with negative squared frequency (|.| used)"
281 << std::flush;
282 }
283 }
284 sigma_alpha_->PrepareScreening();
285 sigma_beta_->PrepareScreening();
286 screening.Built(rpa_both());
287 if (screening.mode() == ScreeningUpdate::Mode::Adaptive &&
288 screening.builds() > 1) {
289 mixing_alpha = Anderson();
290 mixing_beta = Anderson();
291 mixing_alpha.Configure(opt_.gw_mixing_order, opt_.gw_mixing_alpha);
292 mixing_beta.Configure(opt_.gw_mixing_order, opt_.gw_mixing_alpha);
293 mixing_iteration = 0;
294 }
295
296 const Log::Level screening_level =
297 (i_gw == 0) ? Log::Level::error : Log::Level::info;
298 XTP_LOG(screening_level, log_)
299 << TimeStamp() << " Calculated unrestricted screening via RPA; "
300 << MemoryBudget::Usage() << std::flush;
301 }
302
303 const bool mix = opt_.gw_mixing_order > 0 && mixing_iteration > 0;
304 if (mix) {
305 mixing_alpha.UpdateInput(frequencies_alpha);
306 mixing_beta.UpdateInput(frequencies_beta);
307 }
308
309 const Eigen::VectorXd qp_input_alpha = frequencies_alpha;
310 const Eigen::VectorXd qp_input_beta = frequencies_beta;
311 frequencies_alpha = SolveQP(Spin::Alpha, frequencies_alpha);
312 frequencies_beta = SolveQP(Spin::Beta, frequencies_beta);
313 qp_solution_alpha = frequencies_alpha;
314 qp_solution_beta = frequencies_beta;
315 rpa_solved_alpha = rpa_.getRPAInputEnergiesAlpha();
316 rpa_solved_beta = rpa_.getRPAInputEnergiesBeta();
317 ++mixing_iteration;
318 ++n_iterations;
319
320 if (opt_.gw_sc_max_iterations > 1) {
321 Eigen::VectorXd rpa_alpha_old = rpa_.getRPAInputEnergiesAlpha();
322 Eigen::VectorXd rpa_beta_old = rpa_.getRPAInputEnergiesBeta();
323
324 if (mix) {
325 mixing_alpha.UpdateOutput(frequencies_alpha);
326 mixing_beta.UpdateOutput(frequencies_beta);
327 frequencies_alpha = mixing_alpha.MixHistory();
328 frequencies_beta = mixing_beta.MixHistory();
329 }
330
331 rpa_.UpdateRPAInputEnergies(dft_energies_alpha_, dft_energies_beta_,
332 frequencies_alpha, frequencies_beta,
333 opt_.qpmin);
334
336 << TimeStamp() << " GW_Iteration:" << i_gw << " Shift_alpha[Hrt]:"
337 << CalcSpinHomoLumoShift(frequencies_alpha, Spin::Alpha)
338 << " Shift_beta[Hrt]:"
339 << CalcSpinHomoLumoShift(frequencies_beta, Spin::Beta) << std::flush;
340
341 // log the largest changes per spin; the decision is ScreeningUpdate's
342 Converged(rpa_.getRPAInputEnergiesAlpha(), rpa_alpha_old,
343 opt_.gw_sc_limit);
344 Converged(rpa_.getRPAInputEnergiesBeta(), rpa_beta_old, opt_.gw_sc_limit);
345 const double residual =
346 std::max((qp_solution_alpha - qp_input_alpha).cwiseAbs().maxCoeff(),
347 (qp_solution_beta - qp_input_beta).cwiseAbs().maxCoeff());
348 XTP_LOG(Log::info, log_) << TimeStamp() << " QP residual (solution - "
349 << "input) max=" << residual << std::flush;
351 << TimeStamp() << " Largest QP residuals alpha (level: input -> "
352 << "solution): "
353 << qp_solver::LargestResiduals(qp_solution_alpha, qp_input_alpha,
354 opt_.qpmin)
355 << std::flush;
357 << TimeStamp() << " Largest QP residuals beta: "
358 << qp_solver::LargestResiduals(qp_solution_beta, qp_input_beta,
359 opt_.qpmin)
360 << std::flush;
361 double drift = 0.0;
362 const ScreeningUpdate::Next next =
363 screening.Decide(residual, rpa_both(), opt_.gw_sc_limit, &drift);
364 if (screening.mode() == ScreeningUpdate::Mode::Adaptive) {
366 << TimeStamp() << " Change since the screening was built: " << drift
367 << " Hrt after " << screening.inner() << " iteration(s)"
368 << ((next == ScreeningUpdate::Next::Rebuild) ? "; rebuilding W"
369 : "")
370 << std::flush;
371 }
372 if (verifying && roots_before_alpha.size() == qptotal_ &&
373 roots_before_beta.size() == qptotal_) {
374 // levels the verifying full search put on another root
375 std::vector<Index> moved_alpha;
376 std::vector<Index> moved_beta;
377 for (Index l = 0; l < qptotal_; ++l) {
378 if (std::abs(tracked_root_[0](l) - roots_before_alpha(l)) >
379 10.0 * opt_.gw_sc_limit) {
380 moved_alpha.push_back(l + opt_.qpmin);
381 }
382 if (std::abs(tracked_root_[1](l) - roots_before_beta(l)) >
383 10.0 * opt_.gw_sc_limit) {
384 moved_beta.push_back(l + opt_.qpmin);
385 }
386 }
387 if ((!moved_alpha.empty() || !moved_beta.empty()) &&
389 IndexParser rp;
391 << TimeStamp() << " QP root tracking: the full search chose "
392 << "other roots for levels alpha ["
393 << rp.CreateIndexString(moved_alpha) << "] beta ["
394 << rp.CreateIndexString(moved_beta)
395 << "]; continuing with the full search" << std::flush;
396 }
397 verifying = false;
398 }
399 if (next == ScreeningUpdate::Next::Converged && opt_.qp_root_tracking &&
400 !tracking_verified && i_gw > 0) {
401 // converged on the tracked roots: one QP solve with the full search
402 // (same G and W) checks them; see GW::CalculateGWPerturbation
403 tracking_verified = true;
404 verifying = true;
405 rebuild_screening = false;
407 << TimeStamp()
408 << " QP root tracking converged; verifying with a full search"
409 << std::flush;
410 if (i_gw < opt_.gw_sc_max_iterations - 1) {
411 continue;
412 }
413 }
416 << TimeStamp() << " Converged after " << i_gw + 1
417 << " unrestricted GW iterations." << std::flush;
418 break;
419 } else if (i_gw == opt_.gw_sc_max_iterations - 1) {
421 << TimeStamp()
422 << " WARNING! UKS GW-self-consistency cycle not converged after "
423 << opt_.gw_sc_max_iterations << " iterations." << std::flush;
424 break;
425 }
426 rebuild_screening = (next == ScreeningUpdate::Next::Rebuild);
427 }
428 }
429 screening_builds_ = screening.builds();
430 qp_iterations_ = n_iterations;
431 tracking_active_ = false;
432 if (opt_.gw_sc_max_iterations > 1) {
434 << TimeStamp() << " evGW (UKS): " << n_iterations << " QP iterations, "
435 << screening.builds() << " screening builds ("
436 << ScreeningUpdate::Name(screening.mode()) << ")"
437 << (opt_.qp_root_tracking
438 ? "; root tracking solved " +
439 std::to_string(tracked_local_total_) + " of " +
440 std::to_string(2 * n_iterations * qptotal_) +
441 " level solves locally"
442 : "")
443 << std::flush;
444 }
445
446 // Sigma_c of the last QP solve, at its solution and with the energies in
447 // G it saw; the RPA energies updated after the solve stay.
450 const Eigen::VectorXd rpa_next_alpha = rpa_.getRPAInputEnergiesAlpha();
451 const Eigen::VectorXd rpa_next_beta = rpa_.getRPAInputEnergiesBeta();
452 rpa_.setRPAInputEnergies(rpa_solved_alpha, rpa_solved_beta);
453 Sigma_c_alpha_.diagonal() =
454 sigma_alpha_->CalcCorrelationDiag(qp_solution_alpha);
455 Sigma_c_beta_.diagonal() = sigma_beta_->CalcCorrelationDiag(qp_solution_beta);
456 rpa_.setRPAInputEnergies(rpa_next_alpha, rpa_next_beta);
459}
460
461Eigen::VectorXd GW_UKS::getGWAResultsAlpha() const {
462 return Sigma_x_alpha_.diagonal() + Sigma_c_alpha_.diagonal() -
463 vxc_alpha_.diagonal() +
464 dft_energies_alpha_.segment(opt_.qpmin, qptotal_);
465}
466Eigen::VectorXd GW_UKS::getGWAResultsBeta() const {
467 return Sigma_x_beta_.diagonal() + Sigma_c_beta_.diagonal() -
468 vxc_beta_.diagonal() +
469 dft_energies_beta_.segment(opt_.qpmin, qptotal_);
470}
471const Eigen::VectorXd& GW_UKS::RPAInputEnergiesAlpha() const {
472 return rpa_.getRPAInputEnergiesAlpha();
473}
474const Eigen::VectorXd& GW_UKS::RPAInputEnergiesBeta() const {
475 return rpa_.getRPAInputEnergiesBeta();
476}
477
479 const auto& list = competing_roots_[spin == Spin::Alpha ? 0 : 1];
480 Index n = 0;
481 for (const auto& c : list) {
482 n += c.competing ? 1 : 0;
483 }
484 if (n == 0) {
485 return;
486 }
487 XTP_LOG(level, log_) << TimeStamp() << " " << SpinName(spin) << ": " << n
488 << " QP level(s) with two roots of comparable weight "
489 "(second Z at least half the largest):"
490 << std::flush;
491 for (Index i = 0; i < Index(list.size()); ++i) {
492 const auto& c = list[std::size_t(i)];
493 if (c.competing) {
494 XTP_LOG(level, log_)
495 << (boost::format(" Level = %1$4d chosen %2$+1.6f (Z = %3$.3f), "
496 "other %4$+1.6f (Z = %5$.3f)") %
497 (i + opt_.qpmin) % c.omega % c.Z % c.omega_alt % c.Z_alt)
498 .str()
499 << std::flush;
500 }
501 }
502}
503
504Eigen::VectorXd GW_UKS::SolveQP(Spin spin,
505 const Eigen::VectorXd& frequencies) const {
506 competing_roots_[spin == Spin::Alpha ? 0 : 1].assign(
507 std::size_t(qptotal_), qp_solver::CompetingRoots{});
508 const Eigen::VectorXd intercepts =
509 DftEnergies(spin).segment(opt_.qpmin, qptotal_) +
510 SigmaX(spin).diagonal() - Vxc(spin).diagonal();
511
512 Eigen::VectorXd frequencies_new = frequencies;
513
514 Index use_threads = qptotal_;
515#ifdef _OPENMP
516 use_threads =
518#endif
519
520 const std::size_t si = (spin == Spin::Alpha) ? 0 : 1;
521 const bool track = opt_.qp_root_tracking;
522 const bool use_tracked =
523 track && tracking_active_ && tracked_root_[si].size() == qptotal_;
524 Eigen::VectorXd roots_new = frequencies;
525 Eigen::VectorXd Z_new = Eigen::VectorXd::Zero(qptotal_);
526 Index n_local = 0;
527
528#pragma omp parallel for schedule(dynamic) num_threads(use_threads)
529 for (Index gw_level = 0; gw_level < qptotal_; ++gw_level) {
530 const double initial_f = frequencies[gw_level];
531 const double intercept = intercepts[gw_level];
532
533 boost::optional<double> newf;
534 QPStats fixed_stats;
535 QPStats grid_stats;
536 QPStats lin_stats;
537 double Z_root = 0.0;
538
539 if (use_tracked) {
540 const double center = tracked_root_[si](gw_level);
541 const double moved =
542 std::max(tracked_change_[si](gw_level), std::abs(initial_f - center));
543 const double halfwidth = std::clamp(4.0 * moved, 0.01, 0.1);
544 QPFunc fqp(gw_level, SigmaEvaluator(spin), intercept);
545 qp_solver::SolverOptions solver_opt;
546 solver_opt.g_sc_limit = opt_.g_sc_limit;
547 solver_opt.qp_bisection_max_iter = opt_.g_sc_max_iterations;
548 solver_opt.qp_dense_spacing = opt_.qp_dense_spacing;
549 auto cand = qp_solver::SolveQP_Tracked(
550 fqp, center, halfwidth, tracked_Z_[si](gw_level), 0.5, solver_opt,
551 opt_.qp_root_finder == "brent");
552 fixed_stats = fqp.GetStats();
553 if (cand) {
554 newf = cand->omega;
555 Z_root = cand->Z;
556#pragma omp atomic
557 ++n_local;
558 }
559 }
560
561 if (!newf && opt_.qp_solver == "fixedpoint") {
562 newf = SolveQP_FixedPoint(spin, intercept, initial_f, gw_level,
563 &fixed_stats);
564 }
565
566 if (newf) {
567 frequencies_new[gw_level] = newf.value();
568 } else {
569 newf = SolveQP_Grid(spin, intercept, initial_f, gw_level, &grid_stats);
570 if (newf) {
571 frequencies_new[gw_level] = newf.value();
572 } else {
573 newf = SolveQP_Linearisation(spin, intercept, initial_f, gw_level,
574 &lin_stats);
575 if (newf) {
576 frequencies_new[gw_level] = newf.value();
577 }
578 }
579 }
580 if (track && newf) {
581 roots_new[gw_level] = newf.value();
582 if (Z_root <= 0.0) {
583 QPFunc fqp(gw_level, SigmaEvaluator(spin), intercept);
584 const double d = fqp.deriv(newf.value());
585 Z_root = (std::abs(d) > 1e-14) ? -1.0 / d : 0.0;
586 }
587 Z_new[gw_level] = Z_root;
588 }
589
591#pragma omp critical
592 {
593 QPStats total_stats;
594 total_stats.Add(fixed_stats);
595 total_stats.Add(grid_stats);
596 total_stats.Add(lin_stats);
597
598 const Index mo_level = opt_.qpmin + gw_level;
599
601 << " QP stats " << LevelLabel(spin, mo_level) << " mo=" << mo_level
602 << " spin=" << SpinName(spin)
603 << " sigma_calls=" << total_stats.TotalSigmaCalls()
604 << " scan=" << total_stats.sigma_scan_calls
605 << " refine=" << total_stats.sigma_refine_calls
606 << " other=" << total_stats.sigma_other_calls
607 << " deriv_calls=" << total_stats.deriv_calls
608 << " unique_omega=" << total_stats.sigma_unique_frequencies
609 << " repeat_omega=" << total_stats.sigma_repeat_calls << std::flush;
610 }
611 }
612 }
613
614 if (track) {
615 tracked_change_[si] =
616 (tracked_root_[si].size() == qptotal_)
617 ? Eigen::VectorXd((roots_new - tracked_root_[si]).cwiseAbs())
618 : Eigen::VectorXd::Zero(qptotal_);
619 tracked_root_[si] = roots_new;
620 tracked_Z_[si] = Z_new;
621 tracked_local_total_ += n_local;
622 }
623 return frequencies_new;
624}
625
626boost::optional<double> GW_UKS::SolveQP_Linearisation(Spin spin,
627 double intercept0,
628 double frequency0,
629 Index gw_level,
630 QPStats* stats) const {
631 boost::optional<double> newf = boost::none;
632
633 QPFunc fqp(gw_level, SigmaEvaluator(spin), intercept0);
634
635 // fqp.deriv is f'(w) = dSigma_c/dw - 1
636 newf = qp_solver::LinearisedRoot(frequency0,
637 fqp.value(frequency0, EvalStage::Other),
638 fqp.deriv(frequency0));
639
640 if (stats != nullptr) {
641 *stats = fqp.GetStats();
642 }
643
644 return newf;
645}
646
647boost::optional<double> GW_UKS::SolveQP_Grid(Spin spin, double intercept0,
648 double frequency0, Index gw_level,
649 QPStats* stats) const {
650 // The full QP search window is now controlled explicitly and no longer
651 // derived from the dense-grid spacing.
652 const double range = opt_.qp_full_window_half_width;
653
654 const double full_left_limit = frequency0 - range;
655 const double full_right_limit = frequency0 + range;
656
657 double restricted_left_limit = full_left_limit;
658 double restricted_right_limit = full_right_limit;
659
660 bool use_restricted_window = false;
661
662 if (opt_.qp_restrict_search) {
663 const Index mo_level = gw_level + opt_.qpmin;
664 const bool is_occupied = (mo_level <= Homo(spin));
665
666 if (is_occupied) {
667 restricted_right_limit = std::min(full_right_limit, -opt_.qp_zero_margin);
668 } else {
669 restricted_left_limit =
670 std::max(full_left_limit, opt_.qp_virtual_min_energy);
671 }
672
673 const double tol = 1e-12;
674 use_restricted_window =
675 (std::abs(restricted_left_limit - full_left_limit) > tol) ||
676 (std::abs(restricted_right_limit - full_right_limit) > tol);
677 }
678
679 if (use_restricted_window && restricted_left_limit < restricted_right_limit) {
680 auto restricted =
681 SolveQP_Grid_Windowed(spin, intercept0, frequency0, gw_level,
682 restricted_left_limit, restricted_right_limit,
683 false, // accepted roots only
684 stats);
685
686 if (restricted) {
687 return restricted;
688 }
689
691#pragma omp critical
692 {
694 << " Restricted QP search failed for "
695 << LevelLabel(spin, opt_.qpmin + gw_level) << " in window ["
696 << restricted_left_limit << ", " << restricted_right_limit
697 << "], forcing full dense window [" << full_left_limit << ", "
698 << full_right_limit << "]" << std::flush;
699 }
700 }
701
702 return SolveQP_Grid_Windowed_Dense(spin, intercept0, frequency0, gw_level,
703 full_left_limit, full_right_limit, true,
704 stats);
705 }
706
707 return SolveQP_Grid_Windowed(spin, intercept0, frequency0, gw_level,
708 full_left_limit, full_right_limit, true, stats);
709}
710
712 Spin spin, double intercept0, double frequency0, Index gw_level,
713 double left_limit, double right_limit, bool allow_rejected_return,
714 QPStats* stats) const {
715
716 QPFunc fqp(gw_level, SigmaEvaluator(spin), intercept0);
717
718 qp_solver::SolverOptions solver_opt;
719
720 // Pass only canonical search controls into the shared grid-search utility.
721 // UKS uses the same adaptive/dense logic as RKS; only the spin-resolved
722 // sigma evaluation changes.
723 solver_opt.g_sc_limit = opt_.g_sc_limit;
724 solver_opt.qp_bisection_max_iter = opt_.g_sc_max_iterations;
725 solver_opt.qp_full_window_half_width = opt_.qp_full_window_half_width;
726 solver_opt.qp_dense_spacing = opt_.qp_dense_spacing;
727 solver_opt.qp_adaptive_shell_width = opt_.qp_adaptive_shell_width;
728 solver_opt.qp_adaptive_shell_count = opt_.qp_adaptive_shell_count;
729 solver_opt.prefer_nearest_root =
730 opt_.qp_root_continuity && gw_sc_iteration_ > 0;
732 std::vector<QPRootCandidate> accepted_roots;
733 std::vector<QPRootCandidate> rejected_roots;
734
735 bool use_brent = false;
736 if (opt_.qp_root_finder == "brent") {
737 use_brent = true;
738 }
739
741 fqp, frequency0, left_limit, right_limit, gw_sc_iteration_, solver_opt,
742 &wdiag, &accepted_roots, &rejected_roots, use_brent);
743
745#pragma omp critical
746 {
747 if (accepted_roots.empty() && rejected_roots.empty()) {
749 << " No roots found for " << LevelLabel(spin, opt_.qpmin + gw_level)
750 << " (center="
751 << std::max(left_limit, std::min(right_limit, frequency0))
752 << ", shells=" << wdiag.shells_explored << ")" << std::flush;
753 } else {
755 << " Adaptive scan " << LevelLabel(spin, opt_.qpmin + gw_level)
756 << " shells=" << wdiag.shells_explored
757 << " first_interval_shell=" << wdiag.first_interval_shell
758 << " first_accepted_shell=" << wdiag.first_accepted_shell
759 << " intervals=" << wdiag.intervals_found << std::flush;
760 }
761 }
762 }
763
764 if (stats != nullptr) {
765 *stats = fqp.GetStats();
766 }
767
768 if (!accepted_roots.empty()) {
769 if (result) {
770 RecordCompetingRoots(spin, gw_level, accepted_roots, result.value());
771 }
772 return result;
773 }
774
775 if (!rejected_roots.empty() && !allow_rejected_return) {
777#pragma omp critical
778 {
780 << " Adaptive scan " << LevelLabel(spin, opt_.qpmin + gw_level)
781 << " produced only rejected roots in [" << left_limit << ", "
782 << right_limit << "], forcing wider retry" << std::flush;
783 }
784 }
785 return boost::none;
786 }
787
788 return result;
789}
790
792 Spin spin, double intercept0, double frequency0, Index gw_level,
793 double left_limit, double right_limit, bool allow_rejected_return,
794 QPStats* stats) const {
795
796 QPFunc fqp(gw_level, SigmaEvaluator(spin), intercept0);
797
798 qp_solver::SolverOptions solver_opt;
799
800 // The dense path performs a uniform sign-change scan over the requested
801 // interval. Its mesh is controlled by qp_dense_spacing and is independent
802 // from the adaptive shell spacing.
803 solver_opt.g_sc_limit = opt_.g_sc_limit;
804 solver_opt.qp_bisection_max_iter = opt_.g_sc_max_iterations;
805 solver_opt.qp_full_window_half_width = opt_.qp_full_window_half_width;
806 solver_opt.qp_dense_spacing = opt_.qp_dense_spacing;
807 solver_opt.qp_adaptive_shell_width = opt_.qp_adaptive_shell_width;
808 solver_opt.qp_adaptive_shell_count = opt_.qp_adaptive_shell_count;
809 solver_opt.prefer_nearest_root =
810 opt_.qp_root_continuity && gw_sc_iteration_ > 0;
811
812 const bool use_brent = (opt_.qp_root_finder == "brent");
813
814 std::vector<QPRootCandidate> accepted_roots;
815 std::vector<QPRootCandidate> rejected_roots;
816 std::vector<std::pair<double, double>> roots; // omega : Z
817
818 if (left_limit < right_limit) {
819 double freq_prev = left_limit;
820 double targ_prev = fqp.value(freq_prev, EvalStage::Scan);
821
822 // Dense scanning is the robustness path: it uniformly samples the entire
823 // interval to avoid missing sign changes that a shell-based scan may not
824 // hit directly.
825 const Index n_steps = std::max<Index>(
826 2, static_cast<Index>(
827 std::ceil((right_limit - left_limit) / opt_.qp_dense_spacing)) +
828 1);
829
830 for (Index i_node = 1; i_node < n_steps; ++i_node) {
831 const double freq =
832 (i_node == n_steps - 1)
833 ? right_limit
834 : std::min(right_limit, left_limit + static_cast<double>(i_node) *
835 opt_.qp_dense_spacing);
836
837 const double targ = fqp.value(freq, EvalStage::Scan);
838
839 if (targ_prev * targ < 0.0) {
840 auto cand =
841 qp_solver::RefineQPInterval(freq_prev, targ_prev, freq, targ, fqp,
842 frequency0, solver_opt, use_brent);
843 if (cand) {
844 roots.emplace_back(cand->omega, cand->Z);
845 if (cand->accepted) {
846 accepted_roots.push_back(*cand);
847 } else {
848 rejected_roots.push_back(*cand);
849 }
850 }
851 }
852
853 freq_prev = freq;
854 targ_prev = targ;
855 }
856 }
857
859#pragma omp critical
860 {
861 if (accepted_roots.empty() && rejected_roots.empty()) {
863 << " Dense scan " << LevelLabel(spin, opt_.qpmin + gw_level)
864 << " found no roots in [" << left_limit << ", " << right_limit
865 << "]" << std::flush;
866 } else {
868 << " Dense scan " << LevelLabel(spin, opt_.qpmin + gw_level)
869 << " roots (omega:Z)\n\t\t";
870 for (const auto& root : roots) {
871 XTP_LOG(Log::info, log_) << std::setprecision(5) << root.first << ":"
872 << root.second << " ";
873 }
874 XTP_LOG(Log::info, log_) << std::flush;
875 }
876 }
877 }
878
879 if (stats != nullptr) {
880 *stats = fqp.GetStats();
881 }
882
883 if (!accepted_roots.empty()) {
884 const double best = qp_solver::SelectRoot(accepted_roots, solver_opt).omega;
885 RecordCompetingRoots(spin, gw_level, accepted_roots, best);
886 return best;
887 }
888
889 if (!rejected_roots.empty()) {
890 if (!allow_rejected_return) {
892#pragma omp critical
893 {
895 << " Dense scan " << LevelLabel(spin, opt_.qpmin + gw_level)
896 << " produced only rejected roots in [" << left_limit << ", "
897 << right_limit << "], forcing wider retry" << std::flush;
898 }
899 }
900 return boost::none;
901 }
902 auto least_bad = std::max_element(
903 rejected_roots.begin(), rejected_roots.end(),
904 [](const QPRootCandidate& a, const QPRootCandidate& b) {
905 return qp_solver::ScoreRoot(a) < qp_solver::ScoreRoot(b);
906 });
907 return least_bad->omega;
908 }
909
910 return boost::none;
911}
912
913boost::optional<double> GW_UKS::SolveQP_Grid_Windowed(
914 Spin spin, double intercept0, double frequency0, Index gw_level,
915 double left_limit, double right_limit, bool allow_rejected_return,
916 QPStats* stats) const {
917
918 if (opt_.qp_grid_search_mode == "adaptive") {
919 return SolveQP_Grid_Windowed_Adaptive(spin, intercept0, frequency0,
920 gw_level, left_limit, right_limit,
921 allow_rejected_return, stats);
922 }
923
924 if (opt_.qp_grid_search_mode == "dense") {
925 return SolveQP_Grid_Windowed_Dense(spin, intercept0, frequency0, gw_level,
926 left_limit, right_limit,
927 allow_rejected_return, stats);
928 }
929
930 if (opt_.qp_grid_search_mode == "adaptive_with_dense_fallback") {
931 QPStats total_stats;
932 auto adaptive = SolveQP_Grid_Windowed_Adaptive(
933 spin, intercept0, frequency0, gw_level, left_limit, right_limit,
934 allow_rejected_return, &total_stats);
935 if (adaptive) {
936 if (stats != nullptr) {
937 *stats = total_stats;
938 }
939 return adaptive;
940 }
941
942 QPStats dense_stats;
943 auto dense = SolveQP_Grid_Windowed_Dense(
944 spin, intercept0, frequency0, gw_level, left_limit, right_limit,
945 allow_rejected_return, &dense_stats);
946 total_stats.Add(dense_stats);
947
949#pragma omp critical
950 {
952 << " Adaptive QP scan failed for "
953 << LevelLabel(spin, opt_.qpmin + gw_level)
954 << ", retrying dense grid scan in [" << left_limit << ", "
955 << right_limit << "]" << std::flush;
956 }
957 }
958
959 if (stats != nullptr) {
960 *stats = total_stats;
961 }
962 return dense;
963 }
964
965 throw std::runtime_error("Unknown gw.qp_grid_search_mode '" +
966 opt_.qp_grid_search_mode + "'");
967}
968
969boost::optional<double> GW_UKS::SolveQP_FixedPoint(Spin spin, double intercept0,
970 double frequency0,
971 Index gw_level,
972 QPStats* stats) const {
973 boost::optional<double> newf = boost::none;
974
975 QPFunc f(gw_level, SigmaEvaluator(spin), intercept0);
977 opt_.g_sc_max_iterations, opt_.g_sc_limit, opt_.qp_solver_alpha);
978
979 const double freq_new = newton.FindRoot(f, frequency0);
980 if (newton.getInfo() == NewtonRapson<QPFunc>::success) {
981 newf = freq_new;
982 }
983
984 if (stats != nullptr) {
985 *stats = f.GetStats();
986 }
987
988 return newf;
989}
990
991bool GW_UKS::Converged(const Eigen::VectorXd& e1, const Eigen::VectorXd& e2,
992 double epsilon) const {
993 Index state = 0;
994 const double diff_max = (e1 - e2).cwiseAbs().maxCoeff(&state);
995 XTP_LOG(Log::info, log_) << TimeStamp() << " E_diff max=" << diff_max
996 << " StateNo:" << state << std::flush;
997 return diff_max <= epsilon;
998}
999
1001 if (!sigma_alpha_->HasOffDiagonal()) {
1003 << TimeStamp() << " WARNING: no off-diagonal Sigma_c for sigma "
1004 << "integration " << opt_.sigma_integration
1005 << "; the DQP Hamiltonian has no off-diagonal correlation"
1006 << std::flush;
1007 }
1008 Eigen::VectorXd diag_backup_alpha = Sigma_c_alpha_.diagonal();
1009 Eigen::VectorXd diag_backup_beta = Sigma_c_beta_.diagonal();
1010 Sigma_c_alpha_ = sigma_alpha_->CalcCorrelationOffDiag(getGWAResultsAlpha());
1011 Sigma_c_beta_ = sigma_beta_->CalcCorrelationOffDiag(getGWAResultsBeta());
1012 Sigma_c_alpha_.diagonal() = diag_backup_alpha;
1013 Sigma_c_beta_.diagonal() = diag_backup_beta;
1014}
1015
1016Eigen::MatrixXd GW_UKS::getHQPAlpha() const {
1018 Eigen::MatrixXd(
1019 dft_energies_alpha_.segment(opt_.qpmin, qptotal_).asDiagonal());
1020}
1021Eigen::MatrixXd GW_UKS::getHQPBeta() const {
1023 Eigen::MatrixXd(
1024 dft_energies_beta_.segment(opt_.qpmin, qptotal_).asDiagonal());
1025}
1026
1027Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd>
1029 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> qpdiag(getHQPAlpha());
1030 PrintQP_Energies(Spin::Alpha, qpdiag.eigenvalues());
1031 return qpdiag;
1032}
1033
1034Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd>
1036 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> qpdiag(getHQPBeta());
1037 PrintQP_Energies(Spin::Beta, qpdiag.eigenvalues());
1038 return qpdiag;
1039}
1040
1042 const Eigen::VectorXd gwa =
1044
1045 const Index homo = Homo(spin);
1046 const Eigen::VectorXd& dft = DftEnergies(spin);
1047
1049 << (boost::format(" ====== Perturbative %1% quasiparticle energies "
1050 "(Hartree) ======") %
1051 SpinName(spin))
1052 .str()
1053 << std::flush;
1054
1055 if (opt_.qpmin <= homo && homo + 1 <= opt_.qpmax) {
1056 const double dft_gap = dft(homo + 1) - dft(homo);
1057 const double qp_gap = gwa(homo + 1 - opt_.qpmin) - gwa(homo - opt_.qpmin);
1058
1060 << (boost::format(" %1% DFT gap = %2$+1.6f Hartree %1% GWA gap = "
1061 "%3$+1.6f Hartree Delta = %4$+1.6f Hartree") %
1062 SpinName(spin) % dft_gap % qp_gap % (qp_gap - dft_gap))
1063 .str()
1064 << std::flush;
1065 }
1066
1067 for (Index i = 0; i < qptotal_; i++) {
1068 const Index level = i + opt_.qpmin;
1070 << LevelLabel(spin, level)
1071 << (boost::format(" = %1$4d (%2%) %3% DFT = %4$+1.4f VXC = %5$+1.4f "
1072 "S-X = %6$+1.4f S-C = %7$+1.4f GWA = %8$+1.4f") %
1073 level % OccupationTag(spin, level) % SpinName(spin) %
1074 DftEnergies(spin)(level) % Vxc(spin)(i, i) % SigmaX(spin)(i, i) %
1075 SigmaC(spin)(i, i) % gwa(i))
1076 .str()
1077 << std::flush;
1078 }
1079}
1080
1082 const Eigen::VectorXd& qp_diag_energies) const {
1083 const Eigen::VectorXd gwa =
1085
1086 const Index homo = Homo(spin);
1087 const Eigen::VectorXd& dft = DftEnergies(spin);
1088
1089 XTP_LOG(Log::error, log_) << TimeStamp() << " Full " << SpinName(spin)
1090 << " quasiparticle Hamiltonian" << std::flush;
1091
1092 if (opt_.qpmin <= homo && homo + 1 <= opt_.qpmax) {
1093 const double dft_gap = dft(homo + 1) - dft(homo);
1094 const double pqp_gap = gwa(homo + 1 - opt_.qpmin) - gwa(homo - opt_.qpmin);
1095 const double dqp_gap = qp_diag_energies(homo + 1 - opt_.qpmin) -
1096 qp_diag_energies(homo - opt_.qpmin);
1097
1099 << (boost::format(" %1% DFT gap = %2$+1.6f Hartree %1% PQP gap = "
1100 "%3$+1.6f Hartree %1% DQP gap = %4$+1.6f Hartree") %
1101 SpinName(spin) % dft_gap % pqp_gap % dqp_gap)
1102 .str()
1103 << std::flush;
1104 }
1105
1106 for (Index i = 0; i < qptotal_; i++) {
1107 const Index level = i + opt_.qpmin;
1109 << LevelLabel(spin, level)
1110 << (boost::format(" = %1$4d (%2%) %3% PQP = %4$+1.6f DQP = %5$+1.6f") %
1111 level % OccupationTag(spin, level) % SpinName(spin) % gwa(i) %
1112 qp_diag_energies(i))
1113 .str()
1114 << std::flush;
1115 }
1116}
1117
1118} // namespace xtp
1119} // namespace votca
virtual std::unique_ptr< T > Create(const key_t &key, args_t &&...arguments)
Anderson mixing as convergence acceleration in SCF/fixed point problems.
void UpdateInput(const Eigen::VectorXd &newInput)
const Eigen::VectorXd MixHistory()
void UpdateOutput(const Eigen::VectorXd &newOutput)
void Configure(const Index order, const double alpha)
const QPStats & GetStats() const
Definition gw_uks.h:191
double deriv(double frequency) const
Definition gw_uks.h:184
double value(double frequency, EvalStage stage=EvalStage::Other) const
Definition gw_uks.h:180
boost::optional< double > SolveQP_Grid_Windowed(Spin spin, double intercept0, double frequency0, Index gw_level, double left_limit, double right_limit, bool allow_rejected_return=true, QPStats *stats=nullptr) const
Definition gw_uks.cc:913
void PrintCompetingRoots(Spin spin, Log::Level level) const
Definition gw_uks.cc:478
TCMatrix_gwbse_spin & Mmn_
Definition gw_uks.h:315
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonianAlpha() const
Definition gw_uks.cc:1028
bool Converged(const Eigen::VectorXd &e1, const Eigen::VectorXd &e2, double epsilon) const
Definition gw_uks.cc:991
Eigen::MatrixXd core_exchange_alpha_
Definition gw_uks.h:258
void PrintQP_Energies(Spin spin, const Eigen::VectorXd &qp_diag_energies) const
Definition gw_uks.cc:1081
Eigen::MatrixXd & SigmaC(Spin spin)
Definition gw_uks.cc:138
const Eigen::VectorXd & DftEnergies(Spin spin) const
Definition gw_uks.cc:129
std::unique_ptr< Sigma_base_UKS > sigma_alpha_
Definition gw_uks.h:321
boost::optional< double > SolveQP_Linearisation(Spin spin, double intercept0, double frequency0, Index gw_level, QPStats *stats=nullptr) const
Definition gw_uks.cc:626
std::string LevelLabel(Spin spin, Index level) const
Definition gw_uks.cc:160
Eigen::VectorXd tracked_Z_[2]
Definition gw_uks.h:254
Index qp_iterations_
Definition gw_uks.h:261
Eigen::MatrixXd Sigma_x_alpha_
Definition gw_uks.h:323
std::unique_ptr< Sigma_base_UKS > sigma_beta_
Definition gw_uks.h:322
boost::optional< double > SolveQP_Grid_Windowed_Adaptive(Spin spin, double intercept0, double frequency0, Index gw_level, double left_limit, double right_limit, bool allow_rejected_return=true, QPStats *stats=nullptr) const
Definition gw_uks.cc:711
boost::optional< double > SolveQP_Grid(Spin spin, double intercept0, double frequency0, Index gw_level, QPStats *stats=nullptr) const
Definition gw_uks.cc:647
Index screening_builds_
Definition gw_uks.h:260
Eigen::MatrixXd Sigma_c_beta_
Definition gw_uks.h:326
Eigen::MatrixXd & SigmaX(Spin spin)
Definition gw_uks.cc:135
Eigen::VectorXd getGWAResultsAlpha() const
Definition gw_uks.cc:461
const Eigen::MatrixXd & vxc_alpha_
Definition gw_uks.h:316
Eigen::VectorXd ScissorShift_DFTlevel(const Eigen::VectorXd &dft_energies, Index homo) const
Definition gw_uks.cc:174
const Eigen::VectorXd & RPAInputEnergiesAlpha() const
Definition gw_uks.cc:471
Index tracked_local_total_
Definition gw_uks.h:257
qp_solver::WindowDiagnostics QPWindowDiagnostics
Definition gw_uks.h:30
const char * OccupationTag(Spin spin, Index level) const
Definition gw_uks.cc:170
qp_solver::Stats QPStats
Definition gw_uks.h:28
Eigen::MatrixXd core_exchange_beta_
Definition gw_uks.h:259
Sigma_base_UKS & SigmaEvaluator(Spin spin)
Definition gw_uks.cc:147
Eigen::VectorXd SolveQP(Spin spin, const Eigen::VectorXd &frequencies) const
Definition gw_uks.cc:504
const Eigen::VectorXd & RPAInputEnergiesBeta() const
Definition gw_uks.cc:474
Eigen::MatrixXd getHQPBeta() const
Definition gw_uks.cc:1021
bool tracking_active_
Definition gw_uks.h:256
void PrintGWA_Energies(Spin spin) const
Definition gw_uks.cc:1041
Eigen::VectorXd tracked_change_[2]
Definition gw_uks.h:255
Eigen::VectorXd getGWAResultsBeta() const
Definition gw_uks.cc:466
Eigen::MatrixXd getHQPAlpha() const
Definition gw_uks.cc:1016
Eigen::MatrixXd Sigma_x_beta_
Definition gw_uks.h:324
Index gw_sc_iteration_
Definition gw_uks.h:314
const char * SpinName(Spin spin) const
Definition gw_uks.cc:156
GW_UKS(Logger &log, TCMatrix_gwbse_spin &Mmn, const Eigen::MatrixXd &vxc_alpha, const Eigen::MatrixXd &vxc_beta, const Eigen::VectorXd &dft_energies_alpha, const Eigen::VectorXd &dft_energies_beta)
Definition gw_uks.cc:28
const Eigen::VectorXd & dft_energies_alpha_
Definition gw_uks.h:318
double CalcSpinHomoLumoShift(const Eigen::VectorXd &frequencies, Spin spin) const
Definition gw_uks.cc:182
boost::optional< double > SolveQP_Grid_Windowed_Dense(Spin spin, double intercept0, double frequency0, Index gw_level, double left_limit, double right_limit, bool allow_rejected_return=true, QPStats *stats=nullptr) const
Definition gw_uks.cc:791
const Eigen::MatrixXd & vxc_beta_
Definition gw_uks.h:317
const Eigen::VectorXd & dft_energies_beta_
Definition gw_uks.h:319
Logger & log_
Definition gw_uks.h:313
qp_solver::RootCandidate QPRootCandidate
Definition gw_uks.h:29
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonianBeta() const
Definition gw_uks.cc:1035
Eigen::MatrixXd Sigma_c_alpha_
Definition gw_uks.h:325
const Eigen::MatrixXd & Vxc(Spin spin) const
Definition gw_uks.cc:132
std::vector< qp_solver::CompetingRoots > competing_roots_[2]
Definition gw_uks.h:251
void configure(const options &opt)
Definition gw_uks.cc:41
void RecordCompetingRoots(Spin spin, Index gw_level, const std::vector< QPRootCandidate > &roots, double chosen) const
Definition gw_uks.h:262
void CalculateGWPerturbation()
Definition gw_uks.cc:192
boost::optional< double > SolveQP_FixedPoint(Spin spin, double intercept0, double frequency0, Index gw_level, QPStats *stats=nullptr) const
Definition gw_uks.cc:969
Index Homo(Spin spin) const
Definition gw_uks.cc:153
Eigen::VectorXd tracked_root_[2]
Definition gw_uks.h:253
std::string CreateIndexString(const std::vector< Index > &indeces) const
Logger is used for thread-safe output of messages.
Definition logger.h:164
static std::string Usage()
Newton Rapson rootfinder for 1d functions.
double FindRoot(const Func &f, double x0)
When evGW rebuilds the screening W.
void Built(const Eigen::VectorXd &energies)
W has just been built from these energies.
void Configure(Mode mode, double ratio, Index max_inner)
static std::string Name(Mode mode)
Next Decide(double residual, const Eigen::VectorXd &new_energies, double limit, double *drift_out=nullptr)
static Mode Parse(const std::string &name)
Index inner() const
QP iterations since W was last built (before the current one).
void SetSharedPPM(const PPM &ppm)
Timestamp returns the current time as a string Example: cout << TimeStamp().
Definition logger.h:224
#define XTP_LOG(level, log)
Definition logger.h:40
Index getMaxThreads()
Definition eigen.h:145
boost::optional< RootCandidate > SolveQP_Tracked(QPFunc &fqp, double center, double halfwidth, double Z_previous, double min_Z_fraction, const SolverOptions &opt, bool use_brent=false)
boost::optional< double > SolveQP_Grid_Windowed(QPFunc &fqp, double frequency0, double left_limit, double right_limit, Index gw_sc_iteration, const SolverOptions &opt, WindowDiagnostics *wdiag=nullptr, std::vector< RootCandidate > *accepted_roots_out=nullptr, std::vector< RootCandidate > *rejected_roots_out=nullptr, bool use_brent=false)
std::string LargestResiduals(const Eigen::VectorXd &solution, const Eigen::VectorXd &input, Index first_level, Index n=5)
void NormalizeGridSearchOptions(Opt &opt)
boost::optional< double > LinearisedRoot(double w0, double f0, double fprime)
const RootCandidate & SelectRoot(const std::vector< RootCandidate > &roots, const SolverOptions &opt)
boost::optional< RootCandidate > RefineQPInterval(double lowerbound, double f_lowerbound, double upperbound, double f_upperbound, const QPFunc &f, double reference, const SolverOptions &opt, bool use_brent)
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
static Level current_level
Definition globals.h:30
Level
be loud and noisy
Definition globals.h:28
void Add(const Stats &other)
std::size_t TotalSigmaCalls() const