55 sigma_->configure(sigma_opt);
62 double QPgap = frequencies(
opt_.homo + 1 -
opt_.qpmin) -
64 return QPgap - DFTgap;
75 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> qpdiag(
getHQP());
86 " ====== Perturbative quasiparticle energies (Hartree) ====== "))
90 << (boost::format(
" DeltaHLGap = %1$+1.6f Hartree") % shift).str()
94 std::string level =
" Level";
97 }
else if ((i +
opt_.qpmin) ==
opt_.homo + 1) {
103 << (boost::format(
" = %1$4d DFT = %2$+1.4f VXC = %3$+1.4f S-X = "
104 "%4$+1.4f S-C = %5$+1.4f GWA = %6$+1.4f") %
116 <<
" ====== QSGW QP state composition (dominant KS contributions) ======"
121 std::string level =
" Level";
122 if (abs_n ==
opt_.homo)
124 else if (abs_n ==
opt_.homo + 1)
129 std::vector<std::pair<double, Index>> contribs;
131 if (weights(m) >= threshold) {
132 contribs.push_back({weights(m), m +
opt_.qpmin});
135 std::sort(contribs.begin(), contribs.end(),
136 [](
const auto& a,
const auto& b) { return a.first > b.first; });
139 for (
const auto& [w, ks] : contribs) {
140 std::string ks_label;
143 else if (ks ==
opt_.homo + 1)
146 line += (boost::format(
" %1$5.1f%% KS_%2$d%3$s") % (100.0 * w) % ks %
151 << level << (boost::format(
" = %1$4d:") % abs_n).str() << line
157 const Eigen::VectorXd& seed_energies,
158 const Eigen::VectorXd& qsgw_energies)
const {
160 <<
TimeStamp() <<
" QSGW quasiparticle energies" << std::flush;
163 " ====== QSGW quasiparticle energies (Hartree) ====== "))
168 std::string level =
" Level";
169 if ((i +
opt_.qpmin) ==
opt_.homo) {
171 }
else if ((i +
opt_.qpmin) ==
opt_.homo + 1) {
176 << (boost::format(
" = %1$4d %2$s = %3$+1.6f QSGW = %4$+1.6f") %
177 (i +
opt_.qpmin) % seed_label % seed_energies(i) % qsgw_energies(i))
186 <<
TimeStamp() <<
" Full quasiparticle Hamiltonian " << std::flush;
189 " ====== Diagonalized quasiparticle energies (Hartree) "
194 std::string level =
" Level";
195 if ((i +
opt_.qpmin) ==
opt_.homo) {
197 }
else if ((i +
opt_.qpmin) ==
opt_.homo + 1) {
202 << (boost::format(
" = %1$4d PQP = %2$+1.6f DQP = %3$+1.6f ") %
203 (i +
opt_.qpmin) % gwa_energies(i) % qp_diag_energies(i))
211 const Eigen::VectorXd& dft_energies)
const {
212 Eigen::VectorXd shifted_energies = dft_energies;
213 shifted_energies.segment(
opt_.homo + 1, dft_energies.size() -
opt_.homo - 1)
214 .array() +=
opt_.shift;
215 return shifted_energies;
222 <<
TimeStamp() <<
" Calculated Hartree exchange contribution"
229 <<
TimeStamp() <<
" Scissor shifting DFT energies by: " <<
opt_.shift
230 <<
" Hrt" << std::flush;
232 rpa_.setRPAInputEnergies(
233 dft_shifted_energies.segment(
opt_.rpamin,
opt_.rpamax -
opt_.rpamin + 1));
234 Eigen::VectorXd frequencies =
240 for (
Index i_gw = 0; i_gw <
opt_.gw_sc_max_iterations; ++i_gw) {
242 if (i_gw %
opt_.reset_3c == 0 && i_gw != 0) {
245 <<
TimeStamp() <<
" Rebuilding 3c integrals" << std::flush;
247 sigma_->PrepareScreening();
249 <<
TimeStamp() <<
" Calculated screening via RPA" << std::flush;
251 <<
TimeStamp() <<
" Solving QP equations " << std::flush;
252 if (
opt_.gw_mixing_order > 0 && i_gw > 0) {
256 frequencies =
SolveQP(frequencies);
258 if (
opt_.gw_sc_max_iterations > 1) {
259 Eigen::VectorXd rpa_energies_old =
rpa_.getRPAInputEnergies();
260 if (
opt_.gw_mixing_order > 0 && i_gw > 0) {
261 if (
opt_.gw_mixing_order == 1) {
263 <<
"GWSC using linear mixing with alpha: " <<
opt_.gw_mixing_alpha
267 <<
"GWSC using Anderson mixing with history "
268 <<
opt_.gw_mixing_order <<
", alpha: " <<
opt_.gw_mixing_alpha
272 Eigen::VectorXd mixed_frequencies = mixing_.
MixHistory();
275 frequencies = mixed_frequencies;
282 for (
int i = 0; i < frequencies.size(); i++) {
284 <<
"... GWSC iter " << i_gw <<
" state " << i <<
" "
285 << std::setprecision(9) << frequencies(i) << std::flush;
289 <<
TimeStamp() <<
" GW_Iteration:" << i_gw
294 << i_gw + 1 <<
" GW iterations." << std::flush;
296 }
else if (i_gw ==
opt_.gw_sc_max_iterations - 1) {
299 <<
" WARNING! GW-self-consistency cycle not converged after "
300 <<
opt_.gw_sc_max_iterations <<
" iterations." << std::flush;
302 <<
TimeStamp() <<
" Run continues. Inspect results carefully!"
323Eigen::VectorXd
GW::SolveQP(
const Eigen::VectorXd& frequencies)
const {
324 sigma_->ResetDiagEvalCounter();
325 Eigen::VectorXd env = Eigen::VectorXd::Zero(
qptotal_);
327 const Eigen::VectorXd intercepts =
331 Eigen::VectorXd frequencies_new = frequencies;
332 Eigen::Array<bool, Eigen::Dynamic, 1> converged =
333 Eigen::Array<bool, Eigen::Dynamic, 1>::Zero(
qptotal_);
341 Index use_threads = 1;
344#pragma omp parallel for schedule(dynamic) num_threads(use_threads)
347 double initial_f = frequencies[gw_level];
348 double intercept = intercepts[gw_level];
349 boost::optional<double> newf;
352 if (
opt_.qp_solver ==
"fixedpoint") {
356 frequencies_new[gw_level] = newf.value();
357 converged[gw_level] =
true;
359 newf =
SolveQP_Grid(intercept, initial_f, gw_level, &local_stats);
361 frequencies_new[gw_level] = newf.value();
362 converged[gw_level] =
true;
367 frequencies_new[gw_level] = newf.value();
374 total_stats.
Add(local_stats);
378 if (!converged.all()) {
379 std::vector<Index> states;
380 for (
Index s = 0; s < converged.size(); s++) {
389 <<
TimeStamp() <<
" Increase the grid search interval" << std::flush;
393 <<
" Sigma diagonal evaluations in SolveQP: "
394 <<
sigma_->GetDiagEvalCounter() << std::flush;
409 return frequencies_new;
416 boost::optional<double> newf = boost::none;
420 double sigma = fqp.
sigma(frequency0, EvalStage::Other);
421 double dsigma_domega = fqp.
deriv(frequency0);
422 double Z = 1.0 - dsigma_domega;
423 if (std::abs(Z) > 1
e-9) {
424 newf = frequency0 + (intercept0 - frequency0 + sigma) / Z;
427 if (stats !=
nullptr) {
434 double intercept0,
double frequency0,
Index gw_level,
double left_limit,
435 double right_limit,
bool allow_rejected_return,
QPStats* stats)
const {
452 std::vector<QPRootCandidate> accepted_roots;
453 std::vector<QPRootCandidate> rejected_roots;
455 bool use_brent =
false;
456 if (
opt_.qp_root_finder ==
"brent") {
462 &wdiag, &accepted_roots, &rejected_roots, use_brent);
467 if (!accepted_roots.empty() || !rejected_roots.empty()) {
469 <<
" Adaptive scan qplevel:" << gw_level <<
" center="
470 << std::max(left_limit, std::min(right_limit, frequency0))
479 if (stats !=
nullptr) {
483 if (!accepted_roots.empty()) {
487 if (!rejected_roots.empty() && !allow_rejected_return) {
492 <<
" Adaptive scan qplevel:" << gw_level
493 <<
" produced only rejected roots in [" << left_limit <<
", "
494 << right_limit <<
"], forcing wider retry" << std::flush;
504 double intercept0,
double frequency0,
Index gw_level,
double left_limit,
505 double right_limit,
bool allow_rejected_return,
QPStats* stats)
const {
520 const bool use_brent = (
opt_.qp_root_finder ==
"brent");
522 std::vector<QPRootCandidate> accepted_roots;
523 std::vector<QPRootCandidate> rejected_roots;
524 std::vector<std::pair<double, double>> roots;
526 if (left_limit < right_limit) {
527 double freq_prev = left_limit;
528 double targ_prev = fqp.
value(freq_prev, EvalStage::Scan);
533 const Index n_steps = std::max<Index>(
534 2,
static_cast<Index>(
535 std::ceil((right_limit - left_limit) /
opt_.qp_dense_spacing)) +
538 for (
Index i_node = 1; i_node < n_steps; ++i_node) {
540 (i_node == n_steps - 1)
542 : std::min(right_limit, left_limit +
static_cast<double>(i_node) *
543 opt_.qp_dense_spacing);
545 const double targ = fqp.
value(freq, EvalStage::Scan);
547 if (targ_prev * targ < 0.0) {
550 frequency0, solver_opt, use_brent);
552 roots.emplace_back(cand->omega, cand->Z);
553 if (cand->accepted) {
554 accepted_roots.push_back(*cand);
556 rejected_roots.push_back(*cand);
569 if (accepted_roots.empty() && rejected_roots.empty()) {
571 <<
" Dense scan qplevel:" << gw_level <<
" found no roots in ["
572 << left_limit <<
", " << right_limit <<
"]" << std::flush;
575 <<
" Dense scan qplevel:" << gw_level <<
" roots (omega:Z)\n\t\t";
576 for (
const auto& root : roots) {
578 << root.second <<
" ";
585 if (stats !=
nullptr) {
589 if (!accepted_roots.empty()) {
590 auto best = std::max_element(
591 accepted_roots.begin(), accepted_roots.end(),
593 return qp_solver::ScoreRoot(a) < qp_solver::ScoreRoot(b);
598 if (!rejected_roots.empty()) {
599 if (!allow_rejected_return) {
604 <<
" Dense scan qplevel:" << gw_level
605 <<
" produced only rejected roots in [" << left_limit <<
", "
606 << right_limit <<
"], forcing wider retry" << std::flush;
612 auto least_bad = std::max_element(
613 rejected_roots.begin(), rejected_roots.end(),
615 return qp_solver::ScoreRoot(a) < qp_solver::ScoreRoot(b);
617 return least_bad->omega;
624 double intercept0,
double frequency0,
Index gw_level,
double left_limit,
625 double right_limit,
bool allow_rejected_return,
QPStats* stats)
const {
627 if (
opt_.qp_grid_search_mode ==
"adaptive") {
629 left_limit, right_limit,
630 allow_rejected_return, stats);
633 if (
opt_.qp_grid_search_mode ==
"dense") {
635 left_limit, right_limit,
636 allow_rejected_return, stats);
639 if (
opt_.qp_grid_search_mode ==
"adaptive_with_dense_fallback") {
642 intercept0, frequency0, gw_level, left_limit, right_limit,
643 allow_rejected_return, &total_stats);
645 if (stats !=
nullptr) {
646 *stats = total_stats;
653 intercept0, frequency0, gw_level, left_limit, right_limit,
654 allow_rejected_return, &dense_stats);
655 total_stats.
Add(dense_stats);
661 <<
" Adaptive QP scan failed for qplevel:" << gw_level
662 <<
", retrying dense grid scan in [" << left_limit <<
", "
663 << right_limit <<
"]" << std::flush;
667 if (stats !=
nullptr) {
668 *stats = total_stats;
673 throw std::runtime_error(
"Unknown gw.qp_grid_search_mode '" +
674 opt_.qp_grid_search_mode +
"'");
681 const double range =
opt_.qp_full_window_half_width;
683 const double full_left_limit = frequency0 - range;
684 const double full_right_limit = frequency0 + range;
686 double restricted_left_limit = full_left_limit;
687 double restricted_right_limit = full_right_limit;
689 bool use_restricted_window =
false;
691 if (
opt_.qp_restrict_search) {
692 const Index mo_level = gw_level +
opt_.qpmin;
693 const bool is_occupied = (mo_level <=
opt_.homo);
696 restricted_right_limit = std::min(full_right_limit, -
opt_.qp_zero_margin);
698 restricted_left_limit =
699 std::max(full_left_limit,
opt_.qp_virtual_min_energy);
702 const double tol = 1
e-12;
703 use_restricted_window =
704 (std::abs(restricted_left_limit - full_left_limit) > tol) ||
705 (std::abs(restricted_right_limit - full_right_limit) > tol);
708 if (use_restricted_window && restricted_left_limit < restricted_right_limit) {
711 restricted_left_limit, restricted_right_limit,
722 <<
" Restricted QP search failed for qplevel:" << gw_level
723 <<
" in window [" << restricted_left_limit <<
", "
724 << restricted_right_limit <<
"], forcing full dense window ["
725 << full_left_limit <<
", " << full_right_limit <<
"]" << std::flush;
733 full_left_limit, full_right_limit,
true,
738 full_left_limit, full_right_limit,
true, stats);
745 boost::optional<double> newf = boost::none;
748 opt_.g_sc_max_iterations,
opt_.g_sc_limit,
opt_.qp_solver_alpha);
749 double freq_new = newton.
FindRoot(f, frequency0);
753 if (stats !=
nullptr) {
760 double epsilon)
const {
762 bool energies_converged =
true;
763 double diff_max = (e1 - e2).cwiseAbs().maxCoeff(&state);
764 if (diff_max > epsilon) {
765 energies_converged =
false;
768 <<
" StateNo:" << state << std::flush;
769 return energies_converged;
773 Eigen::VectorXd diag_backup =
Sigma_c_.diagonal();
800 <<
TimeStamp() <<
" Starting QSGW self-consistency loop " << std::flush;
803 <<
" QSGW start: qptotal_=" <<
qptotal_
804 <<
" vxc rows=" <<
vxc_.rows() <<
" cols=" <<
vxc_.cols()
805 <<
" Sigma_x rows=" <<
Sigma_x_.rows()
819 bool trimmed =
false;
823 if (corr >
opt_.qsgw_max_virt_correction) {
824 qsgw_qpmax =
opt_.qpmin + n - 1;
827 <<
TimeStamp() <<
" QSGW virtual threshold: level "
832 <<
" eV. Trimming QSGW window to [" <<
opt_.qpmin <<
","
833 << qsgw_qpmax <<
"]." << std::flush;
839 <<
TimeStamp() <<
" QSGW virtual threshold: all corrections within "
841 <<
" eV. Using full window [" <<
opt_.qpmin <<
"," << qsgw_qpmax
842 <<
"]." << std::flush;
846 const Index qsgw_qptotal = qsgw_qpmax -
opt_.qpmin + 1;
847 const bool window_trimmed = (qsgw_qpmax <
opt_.qpmax);
850 if (window_trimmed) {
854 sigma_opt.
qpmax = qsgw_qpmax;
861 sigma_->configure(sigma_opt);
862 Sigma_x_ = Eigen::MatrixXd::Zero(qsgw_qptotal, qsgw_qptotal);
863 Sigma_c_ = Eigen::MatrixXd::Zero(qsgw_qptotal, qsgw_qptotal);
866 Eigen::VectorXd e_qp = e_qp_full.head(qsgw_qptotal);
867 qsgw_rotation_ = Eigen::MatrixXd::Identity(qsgw_qptotal, qsgw_qptotal);
874 if (
opt_.ScaHFX > 0.0) {
875 throw std::runtime_error(
876 "GW::CalculateQSGW: QSGW is not compatible with hybrid DFT starting "
877 "points (ScaHFX = " +
878 std::to_string(
opt_.ScaHFX) +
879 "). Use a pure GGA or LDA functional as the DFT starting point.");
882 if (
opt_.sigma_integration ==
"cda") {
883 throw std::runtime_error(
884 "GW::CalculateQSGW: QSGW is not supported with the CDA sigma "
885 "integration method. The Gaussian quadrature grid used by CDA becomes "
886 "ill-conditioned as the QP wavefunctions rotate away from the DFT-MO "
887 "basis, causing numerical divergence. Use sigma_integration=ppm or "
888 "sigma_integration=exact instead.");
894 const Eigen::VectorXd e_dft_minus_vxc =
896 vxc_.diagonal().head(qsgw_qptotal);
898 Eigen::MatrixXd H0 = -
vxc_.topLeftCorner(qsgw_qptotal, qsgw_qptotal);
914 std::vector<Eigen::MatrixXd> Mmn_orig(mtotal);
915 for (
Index m = 0; m < mtotal; m++) {
916 Mmn_orig[m] =
Mmn_[m];
924 <<
TimeStamp() <<
" QSGW mixer: order=" <<
opt_.gw_mixing_order
925 <<
" alpha=" <<
opt_.gw_mixing_alpha << std::flush;
934 double diff_max_prev = std::numeric_limits<double>::max();
936 for (
Index iter = 0; iter <
opt_.qsgw_max_iterations; iter++) {
939 for (
Index m = 0; m < mtotal; m++) {
940 Mmn_[m] = Mmn_orig[m];
947 sigma_->PrepareScreening();
953 Eigen::MatrixXd Sc_row =
sigma_->CalcCorrelationOffDiag(e_qp);
954 Eigen::MatrixXd Sc_col = Sc_row.transpose();
955 Eigen::MatrixXd tilde_Sigma =
Sigma_x_ + 0.5 * (Sc_row + Sc_col);
956 tilde_Sigma.diagonal() +=
sigma_->CalcCorrelationDiag(e_qp);
967 Eigen::VectorXd S_flat = Eigen::Map<const Eigen::VectorXd>(
968 tilde_Sigma.data(), qsgw_qptotal * qsgw_qptotal);
986 Eigen::MatrixXd tilde_Sigma_mixed = Eigen::Map<const Eigen::MatrixXd>(
987 S_flat.data(), qsgw_qptotal, qsgw_qptotal);
988 Eigen::MatrixXd H_qsgw_mixed = H0 + tilde_Sigma_mixed;
989 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_mixed(H_qsgw_mixed);
990 if (es_mixed.info() != Eigen::ComputationInfo::Success) {
991 throw std::runtime_error(
992 "GW::CalculateQSGW: diagonalisation of mixed H_QSGW failed at iter " +
993 std::to_string(iter));
995 Eigen::MatrixXd dU = es_mixed.eigenvectors();
998 Eigen::MatrixXd H_qsgw_new = H0 + tilde_Sigma;
999 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_new(H_qsgw_new);
1000 if (es_new.info() != Eigen::ComputationInfo::Success) {
1001 throw std::runtime_error(
1002 "GW::CalculateQSGW: diagonalisation of H_QSGW failed at iter " +
1003 std::to_string(iter));
1005 Eigen::VectorXd e_new = es_new.eigenvalues();
1007 double diff_max = (e_new - e_qp).cwiseAbs().maxCoeff();
1017 if (iter > 1 && diff_max > 2.0 * diff_max_prev) {
1021 diff_max_prev = diff_max;
1023 if (diff_max <
opt_.qsgw_sc_limit) {
1025 << iter + 1 <<
" iterations." << std::flush;
1034 if (iter ==
opt_.qsgw_max_iterations - 1) {
1036 <<
TimeStamp() <<
" WARNING: QSGW did not converge in "
1037 <<
opt_.qsgw_max_iterations
1038 <<
" iterations. Inspect results carefully." << std::flush;
1049 qsgw_mixer.
UpdateInput(Eigen::Map<const Eigen::VectorXd>(
1050 tilde_Sigma.data(), qsgw_qptotal * qsgw_qptotal));
1077 sigma_->setQSGWRotation(
nullptr, 0, 0);
1078 rpa_.setQSGWRotation(
nullptr, 0, 0);
1084 if (window_trimmed) {
1087 <<
TimeStamp() <<
" QSGW: merging converged energies [" <<
opt_.qpmin
1088 <<
"," << qsgw_qpmax <<
"] with perturbative seed energies for "
1089 << n_excluded <<
" excluded virtual(s) [" << qsgw_qpmax + 1 <<
","
1090 <<
opt_.qpmax <<
"]" << std::flush;
1094 U_full.topLeftCorner(qsgw_qptotal, qsgw_qptotal) =
qsgw_rotation_;
1100 Eigen::VectorXd e_merged = e_qp_full;
1101 e_merged.head(qsgw_qptotal) = e_qp;
1122 sigma_opt_full.
eta =
opt_.eta;
1126 sigma_->configure(sigma_opt_full);
1130 <<
TimeStamp() <<
" QSGW loop complete." << std::flush;
1134 std::string states)
const {
1136 Eigen::VectorXd frequencies =
1139 std::vector<Index> state_inds;
1142 for (
Index gw_level : parsed_states) {
1143 if (gw_level >=
opt_.qpmin && gw_level <=
opt_.qpmax) {
1144 state_inds.push_back(gw_level);
1148 <<
TimeStamp() <<
" PQP(omega) written to '" << filename
1151 const Index num_states = state_inds.size();
1153 const Eigen::VectorXd intercept =
1156 Eigen::MatrixXd mat = Eigen::MatrixXd::Zero(steps, 2 * num_states);
1157#pragma omp parallel for schedule(dynamic)
1158 for (
Index grid_point = 0; grid_point < steps; grid_point++) {
1159 const double offset =
1160 ((double)grid_point - ((double)(steps - 1) / 2.0)) * spacing;
1161 for (
Index i = 0; i < num_states; i++) {
1162 const Index gw_level = state_inds[i];
1163 const double omega = frequencies(gw_level) + offset;
1164 double sigma =
sigma_->CalcCorrelationDiagElement(gw_level, omega);
1165 mat(grid_point, 2 * i) = omega;
1166 mat(grid_point, 2 * i + 1) = sigma + intercept[gw_level];
1172 for (
Index i = 0; i < num_states; i++) {
1173 const Index gw_level = state_inds[i];
1174 out << boost::format(
"#%1$somega_%2$d\tE_QP(omega)_%2$d") %
1175 (i == 0 ?
"" :
"\t") % gw_level;
1178 boost::format numFormat(
"%+1.6f");
1179 Eigen::IOFormat matFormat(Eigen::StreamPrecision, 0,
"\t",
"\n");
1180 out << numFormat % mat.format(matFormat) << std::endl;
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)
double value(double frequency, EvalStage stage=EvalStage::Other) const
double sigma(double frequency, EvalStage stage=EvalStage::Other) const
const QPStats & GetStats() const
double deriv(double frequency) const
boost::optional< double > SolveQP_Grid_Windowed(double intercept0, double frequency0, Index gw_level, double left_limit, double right_limit, bool allow_rejected_return=true, QPStats *stats=nullptr) const
void PlotSigma(std::string filename, Index steps, double spacing, std::string states) const
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonian() const
void PrintGWA_Energies() const
void PrintQSGW_Energies(const std::string &seed_label, const Eigen::VectorXd &seed_energies, const Eigen::VectorXd &qsgw_energies) const
Print a two-column comparison of seed (G0W0 or evGW) vs converged QSGW quasiparticle energies.
void CalculateQSGW()
Run quasiparticle self-consistent GW (QSGW).
bool Converged(const Eigen::VectorXd &e1, const Eigen::VectorXd &e2, double epsilon) const
void configure(const options &opt)
boost::optional< double > SolveQP_Grid_Windowed_Adaptive(double intercept0, double frequency0, Index gw_level, double left_limit, double right_limit, bool allow_rejected_return=true, QPStats *stats=nullptr) const
Eigen::MatrixXd getHQP() const
Eigen::VectorXd SolveQP(const Eigen::VectorXd &frequencies) const
boost::optional< double > SolveQP_Grid_Windowed_Dense(double intercept0, double frequency0, Index gw_level, double left_limit, double right_limit, bool allow_rejected_return=true, QPStats *stats=nullptr) const
qp_solver::RootCandidate QPRootCandidate
void PrintQSGW_Composition(double threshold=0.01) const
Print the dominant DFT-KS orbital contributions to each converged QSGW quasiparticle state.
Eigen::VectorXd qsgw_seed_energies_
const Eigen::VectorXd & dft_energies_
std::unique_ptr< Sigma_base > sigma_
Eigen::VectorXd qsgw_final_energies_
Eigen::VectorXd getGWAResults() const
qp_solver::WindowDiagnostics QPWindowDiagnostics
double CalcHomoLumoShift(Eigen::VectorXd frequencies) const
boost::optional< double > SolveQP_Grid(double intercept0, double frequency0, Index gw_level, QPStats *stats=nullptr) const
const Eigen::MatrixXd & vxc_
void PrintQP_Energies(const Eigen::VectorXd &qp_diag_energies) const
boost::optional< double > SolveQP_FixedPoint(double intercept0, double frequency0, Index gw_level, QPStats *stats=nullptr) const
Eigen::VectorXd ScissorShift_DFTlevel(const Eigen::VectorXd &dft_energies) const
boost::optional< double > SolveQP_Linearisation(double intercept0, double frequency0, Index gw_level, QPStats *stats=nullptr) const
void CalculateGWPerturbation()
Eigen::MatrixXd qsgw_rotation_
std::vector< Index > CreateIndexVector(const std::string &Ids) const
std::string CreateIndexString(const std::vector< Index > &indeces) const
Newton Rapson rootfinder for 1d functions.
double FindRoot(const Func &f, double x0)
Timestamp returns the current time as a string Example: cout << TimeStamp().
#define XTP_LOG(level, log)
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)
void NormalizeGridSearchOptions(Opt &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.
Provides a means for comparing floating point numbers.
static Level current_level
std::string quadrature_scheme
double qp_adaptive_shell_width
Index qp_adaptive_shell_count
double qp_full_window_half_width
Index qp_bisection_max_iter
std::size_t sigma_repeat_calls
std::size_t sigma_scan_calls
std::size_t sigma_derivative_calls
void Add(const Stats &other)
std::size_t TotalSigmaCalls() const
std::size_t sigma_refine_calls
std::size_t sigma_other_calls
std::size_t sigma_unique_frequencies
Index first_interval_shell
Index first_accepted_shell