52 rpa_.setOutOfWindowShift(
opt_.out_of_window_shift);
66 sigma_->configure(sigma_opt);
83 <<
" Dressed 3c integrals with (1+R)^1/2 for environment screening"
92 <<
" Environment reaction field, static COH+SEX, diagonal [eV]:"
95 std::string level =
" Level";
98 }
else if (i == homo_rel + 1) {
103 << (boost::format(
" = %1$4d Sigma_reac = %2$+1.6f") % (i +
opt_.qpmin) %
108 if (homo_rel >= 0 && homo_rel + 1 <
qptotal_) {
110 << (boost::format(
" HOMO %1$+1.4f eV, LUMO %2$+1.4f eV, gap %3$+1.4f "
124 double QPgap = frequencies(
opt_.homo + 1 -
opt_.qpmin) -
125 frequencies(
opt_.homo -
opt_.qpmin);
126 return QPgap - DFTgap;
137 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> qpdiag(
getHQP());
148 " ====== Perturbative quasiparticle energies (Hartree) ====== "))
152 << (boost::format(
" DeltaHLGap = %1$+1.6f Hartree") % shift).str()
156 std::string level =
" Level";
157 if ((i +
opt_.qpmin) ==
opt_.homo) {
159 }
else if ((i +
opt_.qpmin) ==
opt_.homo + 1) {
165 const std::string env =
167 ? (boost::format(
" S-R = %1$+1.4f") %
Sigma_reac_(i, i)).str()
171 << (boost::format(
" = %1$4d DFT = %2$+1.4f VXC = %3$+1.4f S-X = "
172 "%4$+1.4f S-C = %5$+1.4f") %
176 << env << (boost::format(
" GWA = %1$+1.4f") % gwa_energies(i)).str()
185 <<
" ====== QSGW QP state composition (dominant KS contributions) ======"
190 std::string level =
" Level";
191 if (abs_n ==
opt_.homo)
193 else if (abs_n ==
opt_.homo + 1)
198 std::vector<std::pair<double, Index>> contribs;
200 if (weights(m) >= threshold) {
201 contribs.push_back({weights(m), m +
opt_.qpmin});
204 std::sort(contribs.begin(), contribs.end(),
205 [](
const auto& a,
const auto& b) { return a.first > b.first; });
208 for (
const auto& [w, ks] : contribs) {
209 std::string ks_label;
212 else if (ks ==
opt_.homo + 1)
215 line += (boost::format(
" %1$5.1f%% KS_%2$d%3$s") % (100.0 * w) % ks %
220 << level << (boost::format(
" = %1$4d:") % abs_n).str() << line
226 const Eigen::VectorXd& seed_energies,
227 const Eigen::VectorXd& qsgw_energies)
const {
229 <<
TimeStamp() <<
" QSGW quasiparticle energies" << std::flush;
232 " ====== QSGW quasiparticle energies (Hartree) ====== "))
237 std::string level =
" Level";
238 if ((i +
opt_.qpmin) ==
opt_.homo) {
240 }
else if ((i +
opt_.qpmin) ==
opt_.homo + 1) {
245 << (boost::format(
" = %1$4d %2$s = %3$+1.6f QSGW = %4$+1.6f") %
246 (i +
opt_.qpmin) % seed_label % seed_energies(i) % qsgw_energies(i))
255 <<
TimeStamp() <<
" Full quasiparticle Hamiltonian " << std::flush;
258 " ====== Diagonalized quasiparticle energies (Hartree) "
263 std::string level =
" Level";
264 if ((i +
opt_.qpmin) ==
opt_.homo) {
266 }
else if ((i +
opt_.qpmin) ==
opt_.homo + 1) {
271 << (boost::format(
" = %1$4d PQP = %2$+1.6f DQP = %3$+1.6f ") %
272 (i +
opt_.qpmin) % gwa_energies(i) % qp_diag_energies(i))
280 const Eigen::VectorXd& dft_energies)
const {
281 Eigen::VectorXd shifted_energies = dft_energies;
282 shifted_energies.segment(
opt_.homo + 1, dft_energies.size() -
opt_.homo - 1)
283 .array() +=
opt_.shift;
284 return shifted_energies;
291 auto t =
timings_.Measure(
"exchange Sigma_x");
298 <<
TimeStamp() <<
" Calculated Hartree exchange contribution"
305 auto t =
timings_.Measure(
"reaction field Sigma_reac");
311 auto t =
timings_.Measure(
"dress Mmn for the environment");
319 <<
TimeStamp() <<
" Scissor shifting DFT energies by: " <<
opt_.shift
320 <<
" Hrt" << std::flush;
322 rpa_.setRPAInputEnergies(
323 dft_shifted_energies.segment(
opt_.rpamin,
opt_.rpamax -
opt_.rpamin + 1));
324 Eigen::VectorXd frequencies =
332 Eigen::VectorXd qp_solution = frequencies;
333 Eigen::VectorXd rpa_energies_solved =
rpa_.getRPAInputEnergies();
337 opt_.screening_update_ratio,
338 opt_.screening_update_max_inner);
339 bool rebuild_screening =
true;
342 Index mixing_iteration = 0;
343 Index n_iterations = 0;
350 bool tracking_verified =
false;
351 bool verifying =
false;
353 for (
Index i_gw = 0; i_gw <
opt_.gw_sc_max_iterations; ++i_gw) {
357 if (
opt_.reset_3c > 0 && i_gw %
opt_.reset_3c == 0 && i_gw != 0) {
358 auto t =
timings_.Measure(
"rebuild Mmn");
361 <<
TimeStamp() <<
" Rebuilding 3c integrals" << std::flush;
363 rebuild_screening =
true;
365 if (rebuild_screening) {
371 auto t =
timings_.Measure(
"screening: other");
372 sigma_->PrepareScreening();
374 screening.
Built(
rpa_.getRPAInputEnergies());
379 mixing_iteration = 0;
381 if (
const std::string summary =
sigma_->ScreeningSummary();
392 <<
TimeStamp() <<
" Calculated screening via RPA (build "
393 << screening.
builds() <<
")" << std::flush;
396 <<
TimeStamp() <<
" Solving QP equations " << std::flush;
397 const bool mix =
opt_.gw_mixing_order > 0 && mixing_iteration > 0;
402 const Eigen::VectorXd qp_input = frequencies;
404 auto t =
timings_.Measure(
"QP equations (Sigma_c diagonal)");
405 frequencies =
SolveQP(frequencies);
407 qp_solution = frequencies;
408 rpa_energies_solved =
rpa_.getRPAInputEnergies();
412 if (
opt_.gw_sc_max_iterations > 1) {
413 Eigen::VectorXd rpa_energies_old =
rpa_.getRPAInputEnergies();
415 if (
opt_.gw_mixing_order == 1) {
417 <<
"GWSC using linear mixing with alpha: " <<
opt_.gw_mixing_alpha
421 <<
"GWSC using Anderson mixing with history "
422 <<
opt_.gw_mixing_order <<
", alpha: " <<
opt_.gw_mixing_alpha
426 Eigen::VectorXd mixed_frequencies = mixing_.
MixHistory();
429 frequencies = mixed_frequencies;
436 for (
int i = 0; i < frequencies.size(); i++) {
438 <<
"... GWSC iter " << i_gw <<
" state " << i <<
" "
439 << std::setprecision(9) << frequencies(i) << std::flush;
443 <<
TimeStamp() <<
" GW_Iteration:" << i_gw
448 const double residual = (qp_solution - qp_input).cwiseAbs().maxCoeff();
450 <<
"input) max=" << residual << std::flush;
452 <<
TimeStamp() <<
" Largest QP residuals (level: input -> solution): "
457 residual,
rpa_.getRPAInputEnergies(),
opt_.gw_sc_limit, &drift);
460 <<
TimeStamp() <<
" Change since the screening was built: " << drift
461 <<
" Hrt after " << screening.
inner() <<
" iteration(s)"
466 if (verifying && roots_before.size() ==
qptotal_) {
469 std::vector<Index> moved;
472 10.0 *
opt_.gw_sc_limit) {
473 moved.push_back(l +
opt_.qpmin);
479 <<
TimeStamp() <<
" QP root tracking: the full search chose "
481 <<
"; continuing with the full search" << std::flush;
486 !tracking_verified && i_gw > 0) {
490 tracking_verified =
true;
492 rebuild_screening =
false;
495 <<
" QP root tracking converged; verifying with a full search"
497 if (i_gw <
opt_.gw_sc_max_iterations - 1) {
503 << i_gw + 1 <<
" GW iterations." << std::flush;
505 }
else if (i_gw ==
opt_.gw_sc_max_iterations - 1) {
508 <<
" WARNING! GW-self-consistency cycle not converged after "
509 <<
opt_.gw_sc_max_iterations <<
" iterations." << std::flush;
511 <<
TimeStamp() <<
" Run continues. Inspect results carefully!"
521 if (
opt_.gw_sc_max_iterations > 1) {
523 <<
TimeStamp() <<
" evGW: " << n_iterations <<
" QP iterations, "
524 << screening.
builds() <<
" screening builds ("
526 << (
opt_.qp_root_tracking
527 ?
"; root tracking solved " +
529 std::to_string(n_iterations *
qptotal_) +
530 " level solves locally"
539 auto t =
timings_.Measure(
"QP equations (Sigma_c diagonal)");
540 const Eigen::VectorXd rpa_energies_next =
rpa_.getRPAInputEnergies();
541 rpa_.setRPAInputEnergies(rpa_energies_solved);
543 rpa_.setRPAInputEnergies(rpa_energies_next);
565 n += c.competing ? 1 : 0;
571 <<
" QP level(s) with two roots of comparable weight "
572 "(second Z at least half the largest):"
578 << (boost::format(
" Level = %1$4d chosen %2$+1.6f (Z = %3$.3f), "
579 "other %4$+1.6f (Z = %5$.3f)") %
580 (i +
opt_.qpmin) % c.omega % c.Z % c.omega_alt % c.Z_alt)
587Eigen::VectorXd
GW::SolveQP(
const Eigen::VectorXd& frequencies)
const {
588 sigma_->ResetDiagEvalCounter();
590 Eigen::VectorXd env = Eigen::VectorXd::Zero(
qptotal_);
592 const Eigen::VectorXd intercepts =
596 Eigen::VectorXd frequencies_new = frequencies;
597 Eigen::Array<bool, Eigen::Dynamic, 1> converged =
598 Eigen::Array<bool, Eigen::Dynamic, 1>::Zero(
qptotal_);
601 const bool track =
opt_.qp_root_tracking;
602 const bool use_tracked =
604 Eigen::VectorXd roots_new = frequencies;
605 Eigen::VectorXd Z_new = Eigen::VectorXd::Zero(
qptotal_);
612 const double e_homo =
614 const double e_lumo = (h + 1 >= 0 && h + 1 <
qptotal_)
624 Index use_threads = 1;
627#pragma omp parallel for schedule(dynamic) num_threads(use_threads)
630 double initial_f = frequencies[gw_level];
631 double intercept = intercepts[gw_level];
632 boost::optional<double> newf;
637 auto cand =
SolveQP_Tracked(intercept, initial_f, gw_level, &local_stats);
645 if (!newf &&
opt_.qp_solver ==
"fixedpoint") {
649 frequencies_new[gw_level] = newf.value();
650 converged[gw_level] =
true;
653 newf =
SolveQP_Grid(intercept, initial_f, gw_level, &grid_stats);
654 local_stats.
Add(grid_stats);
656 frequencies_new[gw_level] = newf.value();
657 converged[gw_level] =
true;
662 local_stats.
Add(lin_stats);
664 frequencies_new[gw_level] = newf.value();
669 roots_new[gw_level] = newf.value();
673 const double d = fqp.
deriv(newf.value());
674 Z_root = (std::abs(d) > 1
e-14) ? -1.0 / d : 0.0;
676 Z_new[gw_level] = Z_root;
681 total_stats.
Add(local_stats);
696 <<
TimeStamp() <<
" QP root tracking: " << n_local <<
" of "
697 <<
qptotal_ <<
" levels solved near their previous root"
702 if (!converged.all()) {
703 std::vector<Index> states;
704 for (
Index s = 0; s < converged.size(); s++) {
713 <<
TimeStamp() <<
" Increase the grid search interval" << std::flush;
717 <<
" Sigma diagonal evaluations in SolveQP: "
718 <<
sigma_->GetDiagEvalCounter() << std::flush;
733 return frequencies_new;
737 double intercept0,
double frequency0,
Index gw_level,
743 const double halfwidth = std::clamp(4.0 * moved, 0.01, 0.1);
751 opt_.qp_root_finder ==
"brent");
752 if (stats !=
nullptr) {
762 boost::optional<double> newf = boost::none;
768 fqp.
value(frequency0, EvalStage::Other),
769 fqp.
deriv(frequency0));
771 if (stats !=
nullptr) {
778 double intercept0,
double frequency0,
Index gw_level,
double left_limit,
779 double right_limit,
bool allow_rejected_return,
QPStats* stats)
const {
798 std::vector<QPRootCandidate> accepted_roots;
799 std::vector<QPRootCandidate> rejected_roots;
801 bool use_brent =
false;
802 if (
opt_.qp_root_finder ==
"brent") {
808 &wdiag, &accepted_roots, &rejected_roots, use_brent);
813 if (!accepted_roots.empty() || !rejected_roots.empty()) {
815 <<
" Adaptive scan qplevel:" << gw_level <<
" center="
816 << std::max(left_limit, std::min(right_limit, frequency0))
825 if (stats !=
nullptr) {
829 if (!accepted_roots.empty()) {
836 if (!rejected_roots.empty() && !allow_rejected_return) {
841 <<
" Adaptive scan qplevel:" << gw_level
842 <<
" produced only rejected roots in [" << left_limit <<
", "
843 << right_limit <<
"], forcing wider retry" << std::flush;
853 double intercept0,
double frequency0,
Index gw_level,
double left_limit,
854 double right_limit,
bool allow_rejected_return,
QPStats* stats)
const {
871 const bool use_brent = (
opt_.qp_root_finder ==
"brent");
873 std::vector<QPRootCandidate> accepted_roots;
874 std::vector<QPRootCandidate> rejected_roots;
875 std::vector<std::pair<double, double>> roots;
877 if (left_limit < right_limit) {
878 double freq_prev = left_limit;
879 double targ_prev = fqp.
value(freq_prev, EvalStage::Scan);
884 const Index n_steps = std::max<Index>(
885 2,
static_cast<Index>(
886 std::ceil((right_limit - left_limit) /
opt_.qp_dense_spacing)) +
889 auto node = [&](
Index i_node) {
890 return (i_node == n_steps - 1)
892 : std::min(right_limit,
893 left_limit +
static_cast<double>(i_node) *
894 opt_.qp_dense_spacing);
898 std::vector<double> nodes;
899 for (
Index i_node = 1; i_node < n_steps; ++i_node) {
900 nodes.push_back(node(i_node));
905 for (
Index i_node = 1; i_node < n_steps; ++i_node) {
906 const double freq = node(i_node);
908 const double targ = fqp.
value(freq, EvalStage::Scan);
910 if (targ_prev * targ < 0.0) {
913 frequency0, solver_opt, use_brent);
915 roots.emplace_back(cand->omega, cand->Z);
916 if (cand->accepted) {
917 accepted_roots.push_back(*cand);
919 rejected_roots.push_back(*cand);
932 if (accepted_roots.empty() && rejected_roots.empty()) {
934 <<
" Dense scan qplevel:" << gw_level <<
" found no roots in ["
935 << left_limit <<
", " << right_limit <<
"]" << std::flush;
938 <<
" Dense scan qplevel:" << gw_level <<
" roots (omega:Z)\n\t\t";
939 for (
const auto& root : roots) {
941 << root.second <<
" ";
948 if (stats !=
nullptr) {
952 if (!accepted_roots.empty()) {
958 if (!rejected_roots.empty()) {
959 if (!allow_rejected_return) {
964 <<
" Dense scan qplevel:" << gw_level
965 <<
" produced only rejected roots in [" << left_limit <<
", "
966 << right_limit <<
"], forcing wider retry" << std::flush;
972 auto least_bad = std::max_element(
973 rejected_roots.begin(), rejected_roots.end(),
975 return qp_solver::ScoreRoot(a) < qp_solver::ScoreRoot(b);
977 return least_bad->omega;
984 double intercept0,
double frequency0,
Index gw_level,
double left_limit,
985 double right_limit,
bool allow_rejected_return,
QPStats* stats)
const {
987 if (
opt_.qp_grid_search_mode ==
"adaptive") {
989 left_limit, right_limit,
990 allow_rejected_return, stats);
993 if (
opt_.qp_grid_search_mode ==
"dense") {
995 left_limit, right_limit,
996 allow_rejected_return, stats);
999 if (
opt_.qp_grid_search_mode ==
"adaptive_with_dense_fallback") {
1002 intercept0, frequency0, gw_level, left_limit, right_limit,
1003 allow_rejected_return, &total_stats);
1005 if (stats !=
nullptr) {
1006 *stats = total_stats;
1013 intercept0, frequency0, gw_level, left_limit, right_limit,
1014 allow_rejected_return, &dense_stats);
1015 total_stats.
Add(dense_stats);
1021 <<
" Adaptive QP scan failed for qplevel:" << gw_level
1022 <<
", retrying dense grid scan in [" << left_limit <<
", "
1023 << right_limit <<
"]" << std::flush;
1027 if (stats !=
nullptr) {
1028 *stats = total_stats;
1033 throw std::runtime_error(
"Unknown gw.qp_grid_search_mode '" +
1034 opt_.qp_grid_search_mode +
"'");
1041 const double range =
opt_.qp_full_window_half_width;
1043 const double full_left_limit = frequency0 - range;
1044 const double full_right_limit = frequency0 + range;
1046 double restricted_left_limit = full_left_limit;
1047 double restricted_right_limit = full_right_limit;
1049 bool use_restricted_window =
false;
1051 if (
opt_.qp_restrict_search) {
1052 const Index mo_level = gw_level +
opt_.qpmin;
1053 const bool is_occupied = (mo_level <=
opt_.homo);
1063 const double bound = (frequency0 < -
opt_.qp_zero_margin)
1064 ? -
opt_.qp_zero_margin
1066 restricted_right_limit = std::min(full_right_limit, bound);
1068 const double bound = (frequency0 >
opt_.qp_virtual_min_energy)
1069 ?
opt_.qp_virtual_min_energy
1071 restricted_left_limit = std::max(full_left_limit, bound);
1074 const double tol = 1
e-12;
1075 use_restricted_window =
1076 (std::abs(restricted_left_limit - full_left_limit) > tol) ||
1077 (std::abs(restricted_right_limit - full_right_limit) > tol);
1080 if (use_restricted_window && restricted_left_limit < restricted_right_limit) {
1083 restricted_left_limit, restricted_right_limit,
1094 <<
" Restricted QP search failed for qplevel:" << gw_level
1095 <<
" in window [" << restricted_left_limit <<
", "
1096 << restricted_right_limit <<
"], forcing full dense window ["
1097 << full_left_limit <<
", " << full_right_limit <<
"]" << std::flush;
1105 full_left_limit, full_right_limit,
true,
1110 full_left_limit, full_right_limit,
true, stats);
1117 boost::optional<double> newf = boost::none;
1120 opt_.g_sc_max_iterations,
opt_.g_sc_limit,
opt_.qp_solver_alpha);
1121 double freq_new = newton.
FindRoot(f, frequency0);
1125 if (stats !=
nullptr) {
1132 double epsilon)
const {
1134 bool energies_converged =
true;
1135 double diff_max = (e1 - e2).cwiseAbs().maxCoeff(&state);
1136 if (diff_max > epsilon) {
1137 energies_converged =
false;
1140 <<
" StateNo:" << state << std::flush;
1141 return energies_converged;
1145 if (!
sigma_->HasOffDiagonal()) {
1147 <<
TimeStamp() <<
" WARNING: no off-diagonal Sigma_c for sigma "
1148 <<
"integration " <<
opt_.sigma_integration
1149 <<
"; the DQP Hamiltonian has no off-diagonal correlation"
1152 const auto start = std::chrono::steady_clock::now();
1153 Eigen::VectorXd diag_backup =
Sigma_c_.diagonal();
1157 <<
TimeStamp() <<
" Sigma_c off-diagonal ("
1159 << std::chrono::duration<double>(std::chrono::steady_clock::now() - start)
1161 <<
" s" << std::flush;
1186 <<
TimeStamp() <<
" Starting QSGW self-consistency loop " << std::flush;
1190 <<
" vxc rows=" <<
vxc_.rows() <<
" cols=" <<
vxc_.cols()
1191 <<
" Sigma_x rows=" <<
Sigma_x_.rows() << std::flush;
1204 bool trimmed =
false;
1208 if (corr >
opt_.qsgw_max_virt_correction) {
1209 qsgw_qpmax =
opt_.qpmin + n - 1;
1212 <<
TimeStamp() <<
" QSGW virtual threshold: level "
1217 <<
" eV. Trimming QSGW window to [" <<
opt_.qpmin <<
","
1218 << qsgw_qpmax <<
"]." << std::flush;
1224 <<
TimeStamp() <<
" QSGW virtual threshold: all corrections within "
1226 <<
" eV. Using full window [" <<
opt_.qpmin <<
"," << qsgw_qpmax
1227 <<
"]." << std::flush;
1231 const Index qsgw_qptotal = qsgw_qpmax -
opt_.qpmin + 1;
1232 const bool window_trimmed = (qsgw_qpmax <
opt_.qpmax);
1235 if (window_trimmed) {
1239 sigma_opt.
qpmax = qsgw_qpmax;
1246 sigma_->configure(sigma_opt);
1247 Sigma_x_ = Eigen::MatrixXd::Zero(qsgw_qptotal, qsgw_qptotal);
1248 Sigma_c_ = Eigen::MatrixXd::Zero(qsgw_qptotal, qsgw_qptotal);
1251 Eigen::VectorXd e_qp = e_qp_full.head(qsgw_qptotal);
1252 qsgw_rotation_ = Eigen::MatrixXd::Identity(qsgw_qptotal, qsgw_qptotal);
1254 if (
opt_.sigma_integration ==
"cda") {
1255 throw std::runtime_error(
1256 "GW::CalculateQSGW: QSGW is not supported with the CDA sigma "
1257 "integration method. The Gaussian quadrature grid used by CDA becomes "
1258 "ill-conditioned as the QP wavefunctions rotate away from the DFT-MO "
1259 "basis, causing numerical divergence. Use sigma_integration=ppm or "
1260 "sigma_integration=exact instead.");
1262 if (
opt_.qsgw_regularization ==
"srg") {
1266 <<
" QSGW static self-energy: SRG regularization (Marie and Loos), "
1268 <<
opt_.qsgw_srg_s <<
" Ha^-2 (eta-equivalent "
1269 << std::sqrt(0.5 /
opt_.qsgw_srg_s) <<
" Ha)" << std::flush;
1274 Eigen::MatrixXd H0 = -
vxc_.topLeftCorner(qsgw_qptotal, qsgw_qptotal);
1294 std::vector<Eigen::MatrixXd> Mmn_orig(mtotal);
1295 for (
Index m = 0; m < mtotal; m++) {
1296 Mmn_orig[m] =
Mmn_[m];
1301 const Eigen::MatrixXd aux_frame_orig =
Mmn_.AuxFrame();
1311 if (
opt_.ScaHFX > 0.0) {
1312 Eigen::MatrixXd K0 =
sigma_->CalcExchangeMatrix();
1316 H0 -=
opt_.ScaHFX * K0;
1318 <<
TimeStamp() <<
" QSGW from a hybrid start: removed " <<
opt_.ScaHFX
1319 <<
" x exchange of the DFT orbitals from H0" << std::flush;
1327 <<
" WARNING: QSGW from a hybrid start with a frozen core and "
1328 "core_exchange=false: the exchange with the core stays at "
1330 <<
" x its full value, so the result depends on the starting "
1331 "functional. Use core_exchange=true."
1348 const bool hartree_screened =
opt_.qsgw_hartree_update &&
1349 opt_.qsgw_hartree_screened &&
1352 Eigen::MatrixXd J0_bare;
1353 if (
opt_.qsgw_hartree_update) {
1354 J0_bare =
sigma_->CalcHartreeMatrix();
1359 <<
TimeStamp() <<
" QSGW Hartree update with "
1360 << (hartree_screened ?
"u = v + v_reac (screened)"
1361 :
"the bare v (environment does not follow)")
1367 const Index nocc_w = homo_w + 1;
1368 const Index nvirt_w = qsgw_qptotal - nocc_w;
1369 Eigen::VectorXd sigma_reac_diag0;
1381 <<
TimeStamp() <<
" QSGW mixer: order=" <<
opt_.gw_mixing_order
1382 <<
" alpha=" <<
opt_.gw_mixing_alpha << std::flush;
1388 const Index nflat = qsgw_qptotal * qsgw_qptotal;
1389 auto diagonalise = [&](
const Eigen::VectorXd&
S,
Index iter) {
1390 const Eigen::MatrixXd
H = H0 + Eigen::Map<const Eigen::MatrixXd>(
1391 S.data(), qsgw_qptotal, qsgw_qptotal);
1392 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(
H);
1393 if (es.info() != Eigen::ComputationInfo::Success) {
1394 throw std::runtime_error(
1395 "GW::CalculateQSGW: diagonalisation of H_QSGW failed at iter " +
1396 std::to_string(iter));
1401 Eigen::VectorXd S_in;
1402 bool converged =
false;
1403 for (
Index iter = 0; iter <
opt_.qsgw_max_iterations; iter++) {
1406 for (
Index m = 0; m < mtotal; m++) {
1407 Mmn_[m] = Mmn_orig[m];
1409 Mmn_.RestoreAuxFrame(aux_frame_orig);
1417 Eigen::MatrixXd sigma_reac;
1423 sigma_->PrepareScreening();
1429 const Eigen::MatrixXd Xw =
1436 Eigen::MatrixXd tilde_Sigma =
Sigma_x_;
1437 if (
opt_.qsgw_regularization ==
"srg") {
1438 tilde_Sigma +=
sigma_->CalcCorrelationSRG(e_qp,
opt_.qsgw_srg_s);
1440 tilde_Sigma +=
sigma_->CalcCorrelationOffDiag(e_qp);
1441 tilde_Sigma.diagonal() +=
sigma_->CalcCorrelationDiag(e_qp);
1443 if (sigma_reac.size() > 0) {
1444 tilde_Sigma += sigma_reac;
1447 double dj_max = 0.0;
1448 double dj_reac_max = 0.0;
1449 if (
opt_.qsgw_hartree_update) {
1452 sigma_->CalcHartreeMatrix() *
1455 dj_max = dJ_bare.cwiseAbs().maxCoeff();
1456 if (hartree_screened) {
1457 const Eigen::MatrixXd dJ =
1461 dj_reac_max = (dJ - dJ_bare).cwiseAbs().maxCoeff();
1464 tilde_Sigma += dJ_bare;
1467 if (
opt_.qsgw_hartree_update || sigma_reac.size() > 0) {
1472 if (nocc_w > 0 && nvirt_w > 0) {
1473 const Eigen::MatrixXd Uvo =
1476 Eigen::JacobiSVD<Eigen::MatrixXd>(Uvo).singularValues().maxCoeff();
1479 std::string reac_text;
1480 if (sigma_reac.size() > 0) {
1482 sigma_reac_diag0 = sigma_reac.diagonal();
1484 const Eigen::VectorXd dsr = sigma_reac.diagonal() - sigma_reac_diag0;
1485 reac_text = (boost::format(
" dSigma_reac HOMO %1$+.4f LUMO %2$+.4f "
1494 << (boost::format(
" max|dJ| %1$.4f max|dJ_reac| %2$.4f") %
1499 << (boost::format(
" eV occ-virt rotation %1$.3f deg") % angle).str()
1502 const Eigen::VectorXd S_out =
1503 Eigen::Map<const Eigen::VectorXd>(tilde_Sigma.data(), nflat);
1506 Eigen::VectorXd S_next;
1507 double residual = std::numeric_limits<double>::infinity();
1512 residual = (S_out - S_in).cwiseAbs().maxCoeff();
1519 const Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es =
1520 diagonalise(S_next, iter);
1521 const double diff_max = (es.eigenvalues() - e_qp).cwiseAbs().maxCoeff();
1525 <<
" eV max|Sigma_out - Sigma_in| = "
1528 if (residual <
opt_.qsgw_sc_limit && diff_max <
opt_.qsgw_sc_limit) {
1530 << iter + 1 <<
" iterations." << std::flush;
1534 const Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_out =
1535 diagonalise(S_out, iter);
1536 e_qp = es_out.eigenvalues();
1543 e_qp = es.eigenvalues();
1549 <<
TimeStamp() <<
" WARNING: QSGW did not converge in "
1550 <<
opt_.qsgw_max_iterations <<
" iterations. Inspect results carefully."
1563 if (window_trimmed) {
1566 <<
TimeStamp() <<
" QSGW: merging converged energies [" <<
opt_.qpmin
1567 <<
"," << qsgw_qpmax <<
"] with perturbative seed energies for "
1568 << n_excluded <<
" excluded virtual(s) [" << qsgw_qpmax + 1 <<
","
1569 <<
opt_.qpmax <<
"]" << std::flush;
1573 U_full.topLeftCorner(qsgw_qptotal, qsgw_qptotal) =
qsgw_rotation_;
1579 Eigen::VectorXd e_merged = e_qp_full;
1580 e_merged.head(qsgw_qptotal) = e_qp;
1600 sigma_opt_full.
eta =
opt_.eta;
1604 sigma_->configure(sigma_opt_full);
1608 <<
TimeStamp() <<
" QSGW loop complete." << std::flush;
1613class RPAEnergyGuard {
1615 RPAEnergyGuard(
RPA& rpa,
const Eigen::VectorXd& energies)
1616 : rpa_(rpa), saved_(rpa.getRPAInputEnergies()) {
1617 if (energies.size() == saved_.size()) {
1618 rpa_.setRPAInputEnergies(energies);
1621 ~RPAEnergyGuard() { rpa_.setRPAInputEnergies(saved_); }
1622 RPAEnergyGuard(
const RPAEnergyGuard&) =
delete;
1623 RPAEnergyGuard& operator=(
const RPAEnergyGuard&) =
delete;
1627 Eigen::VectorXd saved_;
1632 double spacing)
const {
1636 Eigen::MatrixXd curves = Eigen::MatrixXd::Zero(nlev, steps);
1643 const Index chunk = std::min<Index>(std::max<Index>(steps, 1), 96);
1644 const Index nchunks = (steps + chunk - 1) / chunk;
1645#pragma omp parallel for schedule(dynamic)
1646 for (
Index task = 0; task < nlev * nchunks; task++) {
1647 const Index i = task / nchunks;
1648 const Index first = (task % nchunks) * chunk;
1649 const Index n = std::min(chunk, steps - first);
1650 const Index gw_level = levels[std::size_t(i)];
1652 Eigen::VectorXd omega(n);
1653 for (
Index k = 0; k < n; ++k) {
1655 center + (double(first + k) - (double(steps - 1) / 2.0)) * spacing;
1657 curves.block(i, first, 1, n) =
1658 sigma_->CalcCorrelationDiagElements(gw_level, omega).transpose();
1664 std::string states) {
1671 std::vector<Index> state_inds;
1674 for (
Index gw_level : parsed_states) {
1675 if (gw_level >=
opt_.qpmin && gw_level <=
opt_.qpmax) {
1676 state_inds.push_back(gw_level);
1679 std::vector<Index> levels;
1680 for (
Index s : state_inds) {
1681 levels.push_back(s -
opt_.qpmin);
1683 const Index num_states =
Index(state_inds.size());
1684 const Eigen::VectorXd intercept =
1688 auto t0 = std::chrono::steady_clock::now();
1689 const Eigen::MatrixXd curves =
SigmaCurves(levels, steps, spacing);
1691 std::chrono::duration<double>(std::chrono::steady_clock::now() - t0)
1694 <<
TimeStamp() <<
" PQP(omega) written to '" << filename
1696 << num_states * steps <<
" points, " <<
seconds <<
" s)" << std::flush;
1698 Eigen::MatrixXd mat = Eigen::MatrixXd::Zero(steps, 2 * num_states);
1699 for (
Index i = 0; i < num_states; i++) {
1700 const Index n = levels[std::size_t(i)];
1702 for (
Index k = 0; k < steps; ++k) {
1704 center + (double(k) - (double(steps - 1) / 2.0)) * spacing;
1706 mat.col(2 * i + 1) = curves.row(i).transpose().array() + intercept(n);
1711 for (
Index i = 0; i < num_states; i++) {
1712 const Index gw_level = state_inds[std::size_t(i)];
1713 out << boost::format(
"#%1$somega_%2$d\tE_QP(omega)_%2$d") %
1714 (i == 0 ?
"" :
"\t") % gw_level;
1717 boost::format numFormat(
"%+1.6f");
1718 Eigen::IOFormat matFormat(Eigen::StreamPrecision, 0,
"\t",
"\n");
1719 out << numFormat % mat.format(matFormat) << std::endl;
1724 const std::string& version) {
1725 if (!
sigma_->HasPoleForm()) {
1726 throw std::runtime_error(
1727 "gw.sigma_dump needs sigma_integrator ppm or exact");
1730 throw std::runtime_error(
1731 "GW::DumpSigma called before CalculateGWPerturbation");
1733 auto t0 = std::chrono::steady_clock::now();
1735 << dopt.
filename <<
"'" << std::flush;
1745 const double eta2 =
opt_.eta *
opt_.eta;
1760 Eigen::VectorXd nu = Eigen::VectorXd::Zero(nimag);
1761 Eigen::VectorXd nu_weights = Eigen::VectorXd::Zero(nimag);
1763 Eigen::MatrixXd J = Eigen::MatrixXd::Zero(nimag, nimag);
1764 for (
Index k = 1; k < nimag; k++) {
1765 const double kd = double(k);
1766 J(k, k - 1) = J(k - 1, k) = kd / std::sqrt(4.0 * kd * kd - 1.0);
1768 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(J);
1769 for (
Index k = 0; k < nimag; k++) {
1770 const double x = es.eigenvalues()(k);
1771 const double w = 2.0 * std::pow(es.eigenvectors()(0, k), 2);
1772 nu(k) = dopt.
imag_scale * (1.0 + x) / (1.0 - x);
1773 nu_weights(k) = w * dopt.
imag_scale * 2.0 / ((1.0 - x) * (1.0 - x));
1777 const Index nbins = std::max<Index>(
1780 Eigen::VectorXd bin_edges(nbins + 1);
1781 for (
Index b = 0; b <= nbins; b++) {
1785 Eigen::VectorXd sigma_check = Eigen::VectorXd::Zero(
qptotal_);
1786 Eigen::VectorXd dsigma = Eigen::VectorXd::Zero(
qptotal_);
1787 Eigen::VectorXd d2sigma = Eigen::VectorXd::Zero(
qptotal_);
1788 Eigen::VectorXd sigma_dft = Eigen::VectorXd::Zero(
qptotal_);
1789 Eigen::MatrixXf spectral = Eigen::MatrixXf::Zero(nspec, nbins);
1790 Eigen::VectorXd weight_below = Eigen::VectorXd::Zero(nspec);
1791 Eigen::VectorXd weight_above = Eigen::VectorXd::Zero(nspec);
1792 Eigen::VectorXd weight_total = Eigen::VectorXd::Zero(nspec);
1793 Eigen::VectorXd first_moment = Eigen::VectorXd::Zero(nspec);
1794 Eigen::MatrixXd imag_re = Eigen::MatrixXd::Zero(nspec, nimag);
1795 Eigen::MatrixXd imag_im = Eigen::MatrixXd::Zero(nspec, nimag);
1797#pragma omp parallel for schedule(dynamic)
1803 const bool spec = (n < nspec);
1804 double s_qp = 0.0, d2 = 0.0, wsum = 0.0, m1 = 0.0, below = 0.0, above = 0.0;
1805 Eigen::VectorXd
hist = Eigen::VectorXd::Zero(spec ? nbins : 0);
1806 Eigen::ArrayXd re = Eigen::ArrayXd::Zero(spec ? nimag : 0);
1807 Eigen::ArrayXd im = Eigen::ArrayXd::Zero(spec ? nimag : 0);
1811 Eigen::ArrayXd z(B), a(B), x(B), x2(B), den(B), xm(B), xm2(B), ad(B);
1818 const double omega = poles.
omegas(s);
1819 const double* r = pf.
residues + B * s;
1820 for (
Index m = 0; m < B; ++m) {
1821 z(m) = poles.
energies(m) + ((m < poles.
n_occ) ? -omega : omega);
1822 a(m) = c * r[m] * r[m];
1828 s_qp += (a * x / den).sum();
1829 d2 += (a * 2.0 * x * (x2 - 3.0 * eta2) / (den.square() * den)).sum();
1834 m1 += (a * z).sum();
1835 for (
Index m = 0; m < B; ++m) {
1840 }
else if (b >=
double(nbins)) {
1849 for (
Index k = 0; k < nimag; k++) {
1850 ad = a / (xm2 + nu(k) * nu(k));
1851 re(k) += (ad * xm).sum();
1852 im(k) -= nu(k) * ad.sum();
1855 sigma_check(n) = s_qp;
1857 dsigma(n) =
sigma_->CalcCorrelationDiagElementDerivative(n, eqp);
1861 spectral.row(n) =
hist.transpose().cast<
float>();
1862 weight_below(n) = below;
1863 weight_above(n) = above;
1864 weight_total(n) = wsum;
1865 first_moment(n) = m1;
1866 imag_re.row(n) = re.matrix().transpose();
1867 imag_im.row(n) = im.matrix().transpose();
1871 const Eigen::VectorXd sigma_c =
Sigma_c_.diagonal();
1872 const double max_dev = (sigma_check - sigma_c).cwiseAbs().maxCoeff();
1875 <<
" Sigma dump: max |Sigma_c(pole sum) - Sigma_c| at the QP solution = "
1876 << max_dev <<
" Ha" << std::flush;
1877 const Eigen::VectorXd Z = (1.0 - dsigma.array()).inverse().matrix();
1882 Eigen::MatrixXf curves;
1884 std::vector<Index> all(
static_cast<std::size_t
>(
qptotal_));
1886 all[std::size_t(n)] = n;
1891 Eigen::Matrix<Index, Eigen::Dynamic, 1> levels(
qptotal_);
1892 Eigen::Matrix<Index, Eigen::Dynamic, 1> competing(
qptotal_);
1893 Eigen::VectorXd omega_alt = Eigen::VectorXd::Zero(
qptotal_);
1894 Eigen::VectorXd Z_alt = Eigen::VectorXd::Zero(
qptotal_);
1896 levels(n) = n +
opt_.qpmin;
1900 competing(n) = c.competing ? 1 : 0;
1901 omega_alt(n) = c.omega_alt;
1910 "Sigma_c,nn(w) = sum_p a_p Re[1/(w - z_p +- i eta)]; Hartree; "
1911 "levels = absolute MO indices qpmin..qpmax; per-level spectra "
1912 "and imaginary axis (z = mu + i nu) and the matrices vxc, sigma_x, "
1913 "Hqp for the first n_spec levels; sigma_c_curves(n, k) = "
1914 "Sigma_c(e_dft(n) + (k - (curve_steps-1)/2) curve_spacing) for all "
1917 w(std::string(
"2"),
"format_version");
1918 w(version,
"xtp_version");
1919 w(
opt_.sigma_integration,
"sigma_integrator");
1920 w(gw_mode,
"gw_mode");
1922 w(
opt_.qpmin,
"qpmin");
1923 w(
opt_.qpmax,
"qpmax");
1924 w(
opt_.rpamin,
"rpamin");
1925 w(
opt_.rpamax,
"rpamax");
1927 w(
opt_.ScaHFX,
"ScaHFX");
1935 w(max_dev,
"sigma_c_check_max_dev");
1937 w(levels,
"levels");
1942 w(Eigen::VectorXd(
vxc_.diagonal()),
"vxc_diag");
1943 w(Eigen::VectorXd(
Sigma_x_.diagonal()),
"sigma_x_diag");
1944 w(Eigen::VectorXd(
Sigma_reac_.diagonal()),
"sigma_reac");
1945 w(Eigen::MatrixXd(
vxc_.topLeftCorner(nspec, nspec)),
"vxc");
1946 w(Eigen::MatrixXd(
Sigma_x_.topLeftCorner(nspec, nspec)),
"sigma_x");
1947 w(Eigen::MatrixXd(
getHQP().topLeftCorner(nspec, nspec)),
"Hqp");
1950 w(sigma_c,
"sigma_c");
1951 w(dsigma,
"dsigma_c");
1952 w(d2sigma,
"d2sigma_c");
1954 w(sigma_dft,
"sigma_c_at_e_dft");
1955 w(competing,
"competing_roots");
1956 w(omega_alt,
"omega_alt");
1962 w(nu_weights,
"imag_weights");
1963 w(imag_re,
"sigma_c_imag_re");
1964 w(imag_im,
"sigma_c_imag_im");
1965 w(bin_edges,
"spectral_bin_edges");
1966 w(spectral,
"spectral_weights");
1967 w(weight_below,
"spectral_weight_below");
1968 w(weight_above,
"spectral_weight_above");
1969 w(weight_total,
"pole_weight_total");
1970 w(first_moment,
"pole_first_moment");
1974 w(curves,
"sigma_c_curves");
1980 "W_c = W - v in modes s (omegas) and in the plasmon-pole frame i "
1981 "(eigenvectors of eps^-1(0)): eps^-1_ii(i nu) - 1 = -sum_s a_is "
1982 "Omega_s/(nu^2 + Omega_s^2); frame_moments(i,k) = sum_s a_is "
1983 "Omega_s^(k-1); frame_weights = -(eps^-1(0) - 1)_ii"),
1986 w(Eigen::VectorXf(scr.
strength.cast<
float>()),
"strength");
1990 "frame_eps_inv_imag");
1994 std::chrono::duration<double>(std::chrono::steady_clock::now() - t0)
1998 <<
" levels with spectra, " << scr.
omegas.size() <<
" screening modes, "
1999 <<
seconds <<
" s" << std::flush;
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)
CheckpointWriter getWriter()
static Eigen::MatrixXd DressingMatrix(const Eigen::MatrixXd &R)
S = (1 + R)^(1/2), the dressing of the auxiliary index that turns the bare interaction into u = v + v...
double value(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
Eigen::MatrixXd reaction_field_
void setReactionField(const Eigen::MatrixXd &R)
Screen by a static polarizable environment (embedded GW).
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonian() const
void PrintGWA_Energies() const
Eigen::VectorXd tracked_change_
void DumpSigma(const sigma_dump_options &dopt, const std::string &gw_mode, const std::string &version)
Write the self-energy and screening data of the finished GW (G0W0 or evGW, ppm or exact) to a separat...
boost::optional< qp_solver::RootCandidate > SolveQP_Tracked(double intercept0, double frequency0, Index gw_level, QPStats *stats) 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 PrintCompetingRoots(Log::Level level) const
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 SigmaCurves(const std::vector< Index > &levels, Index steps, double spacing) const
Eigen::VectorXd qp_solution_
Eigen::MatrixXd getHQP() const
Index tracked_local_total_
Eigen::VectorXd SolveQP(const Eigen::VectorXd &frequencies) const
void PrintReactionFieldShifts() const
Eigen::VectorXd rpa_energies_solved_
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::MatrixXd Sigma_reac_
Eigen::VectorXd qsgw_seed_energies_
const Eigen::VectorXd & dft_energies_
std::unique_ptr< Sigma_base > sigma_
std::vector< qp_solver::CompetingRoots > competing_roots_
Eigen::VectorXd qsgw_final_energies_
Eigen::VectorXd getGWAResults() const
Eigen::VectorXd tracked_Z_
void DressForEnvironment()
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 RecordCompetingRoots(Index gw_level, const std::vector< QPRootCandidate > &roots, double chosen) const
void PrintQP_Energies(const Eigen::VectorXd &qp_diag_energies) const
void PlotSigma(std::string filename, Index steps, double spacing, std::string states)
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_
Eigen::MatrixXd dressing_
Eigen::MatrixXd core_exchange_
Eigen::VectorXd tracked_root_
std::vector< Index > CreateIndexVector(const std::string &Ids) const
std::string CreateIndexString(const std::vector< Index > &indeces) const
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).
Timestamp returns the current time as a string Example: cout << TimeStamp().
#define XTP_LOG(level, log)
void Cite(const std::string &key)
marks a reference as used (thread safe; unknown keys are printed as such)
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)
auto PrefetchIfAvailable(const QPFunc &fqp, const std::vector< double > &nodes, int) -> decltype(fqp.Prefetch(nodes), void())
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.
Provides a means for comparing floating point numbers.
static Level current_level
Pole sums of the exact (RPA spectral) correlation self-energy.
const Eigen::VectorXd & energies
const Eigen::VectorXd & omegas
Options of DumpSigma (energies in Hartree unless noted).
Eigen::MatrixXd frame_eps_inv_imag
Eigen::VectorXd frame_weights
Eigen::MatrixXd frame_moments
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