votca 2026-dev
Loading...
Searching...
No Matches
gwbse.cc
Go to the documentation of this file.
1
2
3/*
4 * Copyright 2009-2023 The VOTCA Development Team
5 * (http://www.votca.org)
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License")
8 *
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */
21
22// Third party includes
23#include <boost/algorithm/string.hpp>
24#include <boost/format.hpp>
25
26// VOTCA includes
27#include <stdexcept>
29
30// Local VOTCA includes
32#include "votca/xtp/basisset.h"
33#include "votca/xtp/bse.h"
35#include "votca/xtp/gwbse.h"
36#include "votca/xtp/logger.h"
38#include "votca/xtp/orbitals.h"
39#include "votca/xtp/rpa_uks.h"
42#include "votca/xtp/vxc_grid.h"
44
45using std::flush;
46namespace votca {
47namespace xtp {
48
50 Index ignored_corelevels = 0;
51 if (!orbitals_.hasECPName()) {
52 ECPBasisSet basis;
53 basis.Load("corelevels");
54 Index coreElectrons = 0;
55 for (const auto& atom : orbitals_.QMAtoms()) {
56 coreElectrons += basis.getElement(atom.getElement()).getNcore();
57 }
58 ignored_corelevels = coreElectrons / 2;
59 }
60 return ignored_corelevels;
61}
62
64
65 // getting level ranges
66 Index rpamax = 0;
67 Index rpamin = 0; // never changes
68 Index qpmin = 0;
69 Index qpmax = 0;
70 Index bse_vmin = 0;
71 Index bse_cmax = 0;
72
73 const bool is_uks = orbitals_.hasUnrestrictedOrbitals();
74 Index homo = orbitals_.getHomo(); // indexed from 0
75 if (is_uks) {
76 homo = std::max(orbitals_.getHomoAlpha(), orbitals_.getHomoBeta());
77 }
78 Index num_of_levels = orbitals_.getBasisSetSize();
79 Index num_of_occlevels = orbitals_.getNumberOfAlphaElectrons();
80 if (is_uks) {
81 num_of_occlevels = std::max(orbitals_.getNumberOfAlphaElectrons(),
82 orbitals_.getNumberOfBetaElectrons());
83 }
84
85 std::string ranges = options.get(".ranges").as<std::string>();
86
87 // now check validity, and get rpa, qp, and bse level ranges accordingly
88
89 if (ranges == "factor") {
90
91 double rpamaxfactor = options.get(".rpamax").as<double>();
92 rpamax = Index(rpamaxfactor * double(num_of_levels)) -
93 1; // total number of levels
94
95 double qpminfactor = options.get(".qpmin").as<double>();
96 qpmin =
97 num_of_occlevels - Index(qpminfactor * double(num_of_occlevels)) - 1;
98
99 double qpmaxfactor = options.get(".qpmax").as<double>();
100 qpmax =
101 num_of_occlevels + Index(qpmaxfactor * double(num_of_occlevels)) - 1;
102
103 double bseminfactor = options.get(".bsemin").as<double>();
104 bse_vmin =
105 num_of_occlevels - Index(bseminfactor * double(num_of_occlevels)) - 1;
106
107 double bsemaxfactor = options.get(".bsemax").as<double>();
108 bse_cmax =
109 num_of_occlevels + Index(bsemaxfactor * double(num_of_occlevels)) - 1;
110
111 } else if (ranges == "explicit") {
112 // get explicit numbers
113 rpamax = options.get(".rpamax").as<Index>();
114 qpmin = options.get(".qpmin").as<Index>();
115 qpmax = options.get(".qpmax").as<Index>();
116 bse_vmin = options.get(".bsemin").as<Index>();
117 bse_cmax = options.get(".bsemax").as<Index>();
118 } else if (ranges == "default") {
119 rpamax = num_of_levels - 1;
120 qpmin = 0;
121 qpmax = 3 * homo + 1;
122 bse_vmin = 0;
123 bse_cmax = 3 * homo + 1;
124 } else if (ranges == "full") {
125 rpamax = num_of_levels - 1;
126 qpmin = 0;
127 qpmax = num_of_levels - 1;
128 bse_vmin = 0;
129 bse_cmax = num_of_levels - 1;
130 }
131 std::string ignore_corelevels =
132 options.get(".ignore_corelevels").as<std::string>();
133
134 if (ignore_corelevels == "RPA" || ignore_corelevels == "GW" ||
135 ignore_corelevels == "BSE") {
136 Index ignored_corelevels = CountCoreLevels();
137 if (ignore_corelevels == "RPA") {
138 rpamin = ignored_corelevels;
139 }
140 if (ignore_corelevels == "GW" || ignore_corelevels == "RPA") {
141 if (qpmin < ignored_corelevels) {
142 qpmin = ignored_corelevels;
143 }
144 }
145 if (ignore_corelevels == "GW" || ignore_corelevels == "RPA" ||
146 ignore_corelevels == "BSE") {
147 if (bse_vmin < ignored_corelevels) {
148 bse_vmin = ignored_corelevels;
149 }
150 }
151
153 << TimeStamp() << " Ignoring " << ignored_corelevels
154 << " core levels for " << ignore_corelevels << " and beyond." << flush;
155 }
156
157 // check maximum and minimum sizes
158 if (rpamax >= num_of_levels) {
159 rpamax = num_of_levels - 1;
160 }
161 if (qpmax >= num_of_levels) {
162 qpmax = num_of_levels - 1;
163 }
164 if (bse_cmax >= num_of_levels) {
165 bse_cmax = num_of_levels - 1;
166 }
167 if (bse_vmin < 0) {
168 bse_vmin = 0;
169 }
170 if (qpmin < 0) {
171 qpmin = 0;
172 }
173
174 Index bse_vmax = homo;
175 Index bse_cmin = homo + 1;
176
177 // Energy caps on the GW and BSE windows (eV above the DFT LUMO / below the
178 // DFT HOMO; <= 0: off). They only shrink the windows set above, and never
179 // cut a (near-)degenerate set of levels.
180 {
181 const double qp_emax =
182 options.ifExistsReturnElseReturnDefault<double>(".qp_emax", 0.0);
183 const double qp_emin =
184 options.ifExistsReturnElseReturnDefault<double>(".qp_emin", 0.0);
185 const double bse_emax =
186 options.ifExistsReturnElseReturnDefault<double>(".bse_emax", 0.0);
187 const double bse_emin =
188 options.ifExistsReturnElseReturnDefault<double>(".bse_emin", 0.0);
189 std::vector<const Eigen::VectorXd*> spins{&orbitals_.MOs().eigenvalues()};
190 std::vector<Index> homos{is_uks ? orbitals_.getHomoAlpha() : homo};
191 if (is_uks) {
192 spins.push_back(&orbitals_.MOs_beta().eigenvalues());
193 homos.push_back(orbitals_.getHomoBeta());
194 }
195 // levels closer than this count as degenerate and stay together
196 constexpr double degenerate = 5e-4; // Ha
197 // highest level (over the spins) within cap_ev above the LUMO
198 auto upper = [&](double cap_ev) {
199 Index last = homo + 1;
200 for (std::size_t s = 0; s < spins.size(); ++s) {
201 const Eigen::VectorXd& e = *spins[s];
202 const double limit = e(homos[s] + 1) + cap_ev * tools::conv::ev2hrt;
203 Index i = homos[s] + 1;
204 while (i + 1 < e.size() && e(i + 1) <= limit) {
205 ++i;
206 }
207 const double edge = e(i); // keep the set degenerate with it
208 while (i + 1 < e.size() && e(i + 1) - edge < degenerate) {
209 ++i;
210 }
211 last = std::max(last, i);
212 }
213 return last;
214 };
215 // lowest level (over the spins) within cap_ev below the HOMO
216 auto lower = [&](double cap_ev) {
217 Index first = homo;
218 for (std::size_t s = 0; s < spins.size(); ++s) {
219 const Eigen::VectorXd& e = *spins[s];
220 const double limit = e(homos[s]) - cap_ev * tools::conv::ev2hrt;
221 Index i = homos[s];
222 while (i - 1 >= 0 && e(i - 1) >= limit) {
223 --i;
224 }
225 const double edge = e(i);
226 while (i - 1 >= 0 && edge - e(i - 1) < degenerate) {
227 --i;
228 }
229 first = std::min(first, i);
230 }
231 return first;
232 };
233 auto report = [&](const std::string& what, const std::string& option,
234 double cap, Index before, Index after) {
236 << TimeStamp() << " " << what << " capped by " << option << " = "
237 << cap << " eV: " << before << " -> " << after << " ("
238 << std::abs(before - after) << " levels removed)" << flush;
239 };
240 if (qp_emax > 0.0) {
241 const Index cap = std::min(qpmax, upper(qp_emax));
242 report("GW window top", "qp_emax", qp_emax, qpmax, cap);
243 qpmax = cap;
244 }
245 if (qp_emin > 0.0) {
246 const Index cap = std::max(qpmin, lower(qp_emin));
247 report("GW window bottom", "qp_emin", qp_emin, qpmin, cap);
248 qpmin = cap;
249 }
250 if (bse_emax > 0.0) {
251 const Index cap = std::min(bse_cmax, upper(bse_emax));
252 report("BSE window top", "bse_emax", bse_emax, bse_cmax, cap);
253 bse_cmax = cap;
254 }
255 if (bse_emin > 0.0) {
256 const Index cap = std::max(bse_vmin, lower(bse_emin));
257 report("BSE window bottom", "bse_emin", bse_emin, bse_vmin, cap);
258 bse_vmin = cap;
259 }
260 }
261
262 if (rpamin < 0 || rpamax < 0 || rpamin > rpamax) {
263 throw std::runtime_error("Invalid RPA level range after setup/clamping.");
264 }
265 if (qpmin < 0 || qpmax < 0 || qpmin > qpmax) {
266 throw std::runtime_error("Invalid GW level range after setup/clamping.");
267 }
268 if (bse_vmin < 0 || bse_vmax < 0 || bse_vmin > bse_vmax) {
269 throw std::runtime_error(
270 "Invalid BSE occupied level range after setup/clamping.");
271 }
272 if (bse_cmin < 0 || bse_cmax < 0 || bse_cmin > bse_cmax) {
273 throw std::runtime_error(
274 "Invalid BSE virtual level range after setup/clamping.");
275 }
276 if (bse_vmax >= num_of_levels || bse_cmax >= num_of_levels) {
277 throw std::runtime_error(
278 "BSE level range exceeds available orbital indices.");
279 }
280 if (bse_vmax >= bse_cmin) {
281 throw std::runtime_error("BSE occupied and virtual level ranges overlap.");
282 }
283
284 gwopt_.homo = homo;
285 gwopt_.qpmin = qpmin;
286 gwopt_.qpmax = qpmax;
287 gwopt_.rpamin = rpamin;
288 gwopt_.rpamax = rpamax;
289
290 bseopt_.vmin = bse_vmin;
291 bseopt_.cmax = bse_cmax;
292 bseopt_.homo = homo;
293 bseopt_.qpmin = qpmin;
294 bseopt_.qpmax = qpmax;
295 bseopt_.rpamin = rpamin;
296 bseopt_.rpamax = rpamax;
297
298 orbitals_.setRPAindices(rpamin, rpamax);
299 orbitals_.setGWindices(qpmin, qpmax);
300 orbitals_.setBSEindices(bse_vmin, bse_cmax);
301
302 // Index bse_vmax = homo;
303 // Index bse_cmin = homo + 1;
304 Index bse_vtotal = bse_vmax - bse_vmin + 1;
305 Index bse_ctotal = bse_cmax - bse_cmin + 1;
306 Index bse_size = bse_vtotal * bse_ctotal;
307
308 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " RPA level range [" << rpamin
309 << ":" << rpamax << "]" << flush;
310 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " GW level range [" << qpmin
311 << ":" << qpmax << "]" << flush;
313 << TimeStamp() << " BSE level range occ[" << bse_vmin << ":" << bse_vmax
314 << "] virt[" << bse_cmin << ":" << bse_cmax << "]" << flush;
315
316 gwopt_.reset_3c = options.get(".gw.rebuild_3c_freq").as<Index>();
317
318 bseopt_.nmax = options.get(".bse.exctotal").as<Index>();
319 if (bseopt_.nmax > bse_size || bseopt_.nmax < 0) {
320 bseopt_.nmax = bse_size;
321 }
322
323 bseopt_.davidson_correction =
324 options.get("bse.davidson.correction").as<std::string>();
325
326 bseopt_.davidson_tolerance =
327 options.get("bse.davidson.tolerance").as<std::string>();
328
329 bseopt_.davidson_update =
330 options.get("bse.davidson.update").as<std::string>();
331
332 bseopt_.davidson_maxiter = options.get("bse.davidson.maxiter").as<Index>();
333
334 bseopt_.useTDA = options.get("bse.useTDA").as<bool>();
335 orbitals_.setTDAApprox(bseopt_.useTDA);
336 if (!bseopt_.useTDA) {
337 XTP_LOG(Log::error, *pLog_) << " BSE type: full" << flush;
338 } else {
339 XTP_LOG(Log::error, *pLog_) << " BSE type: TDA" << flush;
340 }
341
342 Index full_bse_size = (bseopt_.useTDA) ? bse_size : 2 * bse_size;
343 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " BSE Hamiltonian has size "
344 << full_bse_size << "x" << full_bse_size << flush;
345
346 bseopt_.use_Hqp_offdiag = options.get("bse.use_Hqp_offdiag").as<bool>();
347 orbitals_.SetFlagUseHqpOffdiag(bseopt_.use_Hqp_offdiag);
348
349 if (!bseopt_.use_Hqp_offdiag) {
351 << " BSE without Hqp offdiagonal elements" << flush;
352 } else {
354 << " BSE with Hqp offdiagonal elements" << flush;
355 }
356
357 bseopt_.max_dyn_iter = options.get("bse.dyn_screen_max_iter").as<Index>();
358 bseopt_.dyn_tolerance = options.get("bse.dyn_screen_tol").as<double>();
359 if (bseopt_.max_dyn_iter > 0) {
361 }
362
363 functional_ = orbitals_.getXCFunctionalName();
364 grid_ = orbitals_.getXCGrid();
365
366 dftbasis_name_ = orbitals_.getDFTbasisName();
367 if (orbitals_.hasAuxbasisName()) {
368 auxbasis_name_ = orbitals_.getAuxbasisName();
369 } else if (options.exists(".auxbasisset")) {
370 auxbasis_name_ = options.get(".auxbasisset").as<std::string>();
371 } else {
373 try {
374 BasisSet b;
376 } catch (std::runtime_error&) {
377 throw std::runtime_error(
378 "There is no auxbasis from the dftcalculation nor did you specify an "
379 "auxbasisset for the gwbse calculation. Also no auxiliary basisset "
380 "for basisset " +
381 dftbasis_name_ + " could be found!");
382 }
384 << " No auxbasisset given, using " << auxbasis_name_ << flush;
385 }
386
387 std::string mode = options.get("gw.mode").as<std::string>();
388 if (mode == "G0W0") {
389 gwopt_.gw_sc_max_iterations = 1;
390 }
391
392 XTP_LOG(Log::error, *pLog_) << " Running GW as: " << mode << flush;
393 gwopt_.ScaHFX = orbitals_.getScaHFX();
394
395 gwopt_.shift = options.get("gw.scissor_shift").as<double>();
396 gwopt_.g_sc_limit =
397 options.get("gw.qp_sc_limit").as<double>(); // convergence criteria
398 // for qp iteration
399 // [Hartree]]
400 gwopt_.g_sc_max_iterations =
401 options.get("gw.qp_sc_max_iter").as<Index>(); // convergence
402 // criteria for qp
403 // iteration
404 // [Hartree]]
405
406 if (mode == "evGW") {
407 gwopt_.gw_sc_max_iterations = options.get("gw.sc_max_iter").as<Index>();
408 }
409
410 gwopt_.gw_sc_limit =
411 options.get("gw.sc_limit").as<double>(); // convergence criteria
412 // for shift it
414 << " qp_sc_limit [Hartree]: " << gwopt_.g_sc_limit << flush;
415 if (gwopt_.gw_sc_max_iterations > 1) {
417 << " gw_sc_limit [Hartree]: " << gwopt_.gw_sc_limit << flush;
418 }
419 bseopt_.min_print_weight = options.get("bse.print_weight").as<double>();
420 // print exciton WF composition weight larger than minimum
421
422 // possible tasks
423 std::string tasks_string = options.get(".tasks").as<std::string>();
424 boost::algorithm::to_lower(tasks_string);
425
426 if (tasks_string.find("gw") != std::string::npos) {
427 do_gw_ = true;
428 }
429
430 if (is_uks) {
431 if (tasks_string.find("excitons") != std::string::npos ||
432 tasks_string.find("exciton_uks") != std::string::npos) {
433 do_gw_ = true;
434 do_bse_exciton_uks_ = true;
435 }
436
437 if (tasks_string.find("singlets") != std::string::npos ||
438 tasks_string.find("triplets") != std::string::npos) {
439 throw std::runtime_error(
440 "Invalid gwbse task for UKS reference: 'singlets' and 'triplets' "
441 "are not defined for open-shell systems.\n"
442 "Use 'exciton_uks' (or 'excitons') instead.");
443 }
444 } else {
445 if (tasks_string.find("all") != std::string::npos) {
446 if (is_uks) {
447 do_gw_ = true;
448 do_bse_exciton_uks_ = true;
449 } else {
450 do_gw_ = true;
451 do_bse_singlets_ = true;
452 do_bse_triplets_ = true;
453 }
454 }
455 if (tasks_string.find("singlets") != std::string::npos) {
456 do_bse_singlets_ = true;
457 }
458 if (tasks_string.find("triplets") != std::string::npos) {
459 do_bse_triplets_ = true;
460 }
461 }
462
463 XTP_LOG(Log::error, *pLog_) << " Tasks: " << flush;
464 if (do_gw_) {
465 XTP_LOG(Log::error, *pLog_) << " GW " << flush;
466 }
467 if (do_bse_singlets_) {
468 XTP_LOG(Log::error, *pLog_) << " singlets " << flush;
469 }
470 if (do_bse_triplets_) {
471 XTP_LOG(Log::error, *pLog_) << " triplets " << flush;
472 }
474 XTP_LOG(Log::error, *pLog_) << " exciton_uks " << flush;
475 }
476
477 if (options.exists("bse.fragments")) {
478 std::vector<tools::Property*> prop_region =
479 options.Select("bse.fragments.fragment");
480 Index index = 0;
481 for (tools::Property* prop : prop_region) {
482 std::string indices = prop->get("indices").as<std::string>();
483 fragments_.push_back(QMFragment<BSE_Population>(index, indices));
484 index++;
485 }
486 }
487
488 gwopt_.sigma_integration =
489 options.get("gw.sigma_integrator").as<std::string>();
491 << " Sigma integration: " << gwopt_.sigma_integration << flush;
492 gwopt_.eta = options.get("gw.eta").as<double>();
493 XTP_LOG(Log::error, *pLog_) << " eta: " << gwopt_.eta << flush;
494 if (gwopt_.sigma_integration == "exact") {
496 << " RPA Hamiltonian size: " << (homo + 1 - rpamin) * (rpamax - homo)
497 << flush;
498 }
499 if (gwopt_.sigma_integration == "cda") {
500 gwopt_.order = options.get("gw.quadrature_order").as<Index>();
502 << " Quadrature integration order : " << gwopt_.order << flush;
503 gwopt_.quadrature_scheme =
504 options.get("gw.quadrature_scheme").as<std::string>();
506 << " Quadrature integration scheme : " << gwopt_.quadrature_scheme
507 << flush;
508 gwopt_.alpha = options.get("gw.alpha").as<double>();
510 << " Alpha smoothing parameter : " << gwopt_.alpha << flush;
511 }
512 gwopt_.qp_solver = options.get("gw.qp_solver").as<std::string>();
513
514 XTP_LOG(Log::error, *pLog_) << " QP solver: " << gwopt_.qp_solver << flush;
515 if (gwopt_.qp_solver == "grid") {
516 // Grid-based QP solving now exposes the full search extent, dense scan,
517 // and adaptive shell scan as separate controls. This avoids the old
518 // overloading of qp_grid_steps / qp_grid_spacing, where one pair of values
519 // simultaneously determined the window width and both scan resolutions.
520 //
521 // New-style options are read first. Deprecated legacy aliases are then
522 // translated only for values that are still unset.
523 // New decoupled QP search options
524 if (options.exists("gw.qp_full_window_half_width")) {
525 gwopt_.qp_full_window_half_width =
526 options.get("gw.qp_full_window_half_width").as<double>();
527 }
528 if (options.exists("gw.qp_dense_spacing")) {
529 gwopt_.qp_dense_spacing = options.get("gw.qp_dense_spacing").as<double>();
530 }
531 if (options.exists("gw.qp_adaptive_shell_width")) {
532 gwopt_.qp_adaptive_shell_width =
533 options.get("gw.qp_adaptive_shell_width").as<double>();
534 }
535 if (options.exists("gw.qp_adaptive_shell_count")) {
536 gwopt_.qp_adaptive_shell_count =
537 options.get("gw.qp_adaptive_shell_count").as<Index>();
538 }
539
540 // Deprecated legacy aliases. They are accepted for backward
541 // compatibility and mapped onto the canonical controls below.
542 const bool has_legacy_steps = options.exists("gw.qp_grid_steps");
543 const bool has_legacy_spacing = options.exists("gw.qp_grid_spacing");
544
545 if (has_legacy_steps != has_legacy_spacing) {
546 throw std::runtime_error(
547 "Deprecated gw.qp_grid_steps and gw.qp_grid_spacing must be given "
548 "together if either is used.");
549 }
550
551 if (has_legacy_steps && has_legacy_spacing) {
552 gwopt_.qp_grid_steps = options.get("gw.qp_grid_steps").as<Index>();
553 gwopt_.qp_grid_spacing = options.get("gw.qp_grid_spacing").as<double>();
554
555 const double legacy_half_width =
557 const double legacy_shell_width =
559
560 if (gwopt_.qp_full_window_half_width <= 0.0) {
561 gwopt_.qp_full_window_half_width = legacy_half_width;
562 }
563 if (gwopt_.qp_dense_spacing <= 0.0) {
564 gwopt_.qp_dense_spacing = gwopt_.qp_grid_spacing;
565 }
566 if (gwopt_.qp_adaptive_shell_count <= 0 &&
567 gwopt_.qp_adaptive_shell_width <= 0.0) {
568 gwopt_.qp_adaptive_shell_width = legacy_shell_width;
569 }
570
572 << " Deprecated GW QP options detected: " << "qp_grid_steps="
573 << gwopt_.qp_grid_steps
574 << " qp_grid_spacing=" << gwopt_.qp_grid_spacing
575 << " -> mapped to qp_full_window_half_width="
576 << gwopt_.qp_full_window_half_width
577 << " qp_dense_spacing=" << gwopt_.qp_dense_spacing
578 << " qp_adaptive_shell_width=" << gwopt_.qp_adaptive_shell_width
579 << flush;
580 }
581
582 // Final normalization produces one canonical option set used by both
583 // GW and GW_UKS implementations.
585
587 << " QP full window half-width: " << gwopt_.qp_full_window_half_width
588 << flush;
590 << " QP dense spacing: " << gwopt_.qp_dense_spacing << flush;
592 << " QP adaptive shell width: " << gwopt_.qp_adaptive_shell_width
593 << flush;
595 << " QP adaptive shell count: " << gwopt_.qp_adaptive_shell_count
596 << flush;
597 }
598 gwopt_.qp_root_finder = options.get("gw.qp_root_finder").as<std::string>();
600 << " QP root finder: " << gwopt_.qp_root_finder << flush;
601
602 gwopt_.gw_mixing_order =
603 options.get("gw.mixing_order").as<Index>(); // max history in
604 // mixing (0: plain,
605 // 1: linear, >1
606 // Anderson)
607
608 gwopt_.gw_mixing_alpha = options.get("gw.mixing_alpha").as<double>();
609 gwopt_.qp_grid_search_mode =
610 options.get("gw.qp_grid_search_mode").as<std::string>();
611 gwopt_.qp_restrict_search = options.get("gw.qp_restrict_search").as<bool>();
612 gwopt_.qp_root_continuity = options.ifExistsReturnElseReturnDefault<bool>(
613 "gw.qp_root_continuity", true);
614 gwopt_.qp_root_tracking = options.ifExistsReturnElseReturnDefault<bool>(
615 "gw.qp_root_tracking", false);
617 options.ifExistsReturnElseReturnDefault<bool>("gw.core_exchange", true);
618 gwopt_.screening_update =
619 options.ifExistsReturnElseReturnDefault<std::string>(
620 "gw.screening_update", "adaptive");
621 ScreeningUpdate::Parse(gwopt_.screening_update); // validates
622 gwopt_.screening_update_ratio =
623 options.ifExistsReturnElseReturnDefault<double>(
624 "gw.screening_update_ratio", 0.1);
625 gwopt_.screening_update_max_inner =
627 "gw.screening_update_max_inner", 10);
628 gwopt_.qp_zero_margin = options.get("gw.qp_zero_margin").as<double>();
629 gwopt_.qp_virtual_min_energy =
630 options.get("gw.qp_virtual_min_energy").as<double>();
631
632 // QSGW options
633 gwopt_.do_qsgw =
634 options.ifExistsReturnElseReturnDefault<bool>("gw.do_qsgw", false);
635 gwopt_.qsgw_max_iterations = options.ifExistsReturnElseReturnDefault<Index>(
636 "gw.qsgw_max_iterations", 50);
637 gwopt_.qsgw_sc_limit =
638 options.ifExistsReturnElseReturnDefault<double>("gw.qsgw_sc_limit", 1e-5);
639 gwopt_.qsgw_max_virt_correction =
640 options.ifExistsReturnElseReturnDefault<double>(
641 "gw.qsgw_max_virt_correction", 0.5);
642 gwopt_.out_of_window_shift.mode = OutOfWindowShift::Parse(
643 options.ifExistsReturnElseReturnDefault<std::string>(
644 "gw.out_of_window_shift", "max"));
645 gwopt_.out_of_window_shift.fit_fraction =
646 options.ifExistsReturnElseReturnDefault<double>(
647 "gw.out_of_window_fit_fraction", 0.3);
648 if (gwopt_.out_of_window_shift.mode != OutOfWindowShift::Mode::Max) {
650 << " Levels outside the GW window shifted by: "
651 << OutOfWindowShift::Name(gwopt_.out_of_window_shift.mode)
652 << ((gwopt_.out_of_window_shift.mode == OutOfWindowShift::Mode::Linear)
653 ? " (fit fraction " +
654 std::to_string(gwopt_.out_of_window_shift.fit_fraction) +
655 ")"
656 : std::string())
657 << flush;
658 }
659 if (gwopt_.do_qsgw) {
661 << " QSGW enabled: max_iter=" << gwopt_.qsgw_max_iterations
662 << " sc_limit=" << gwopt_.qsgw_sc_limit << " Ha"
663 << " max_virt_correction=" << gwopt_.qsgw_max_virt_correction << " Ha ("
664 << gwopt_.qsgw_max_virt_correction * 27.2114 << " eV)" << std::flush;
665 }
666
667 if (mode == "evGW") {
668 if (gwopt_.gw_mixing_order == 0) {
669 XTP_LOG(Log::error, *pLog_) << " evGW with plain update " << std::flush;
670 } else if (gwopt_.gw_mixing_order == 1) {
671 XTP_LOG(Log::error, *pLog_) << " evGW with linear update using alpha "
672 << gwopt_.gw_mixing_alpha << std::flush;
673 } else {
674 XTP_LOG(Log::error, *pLog_) << " evGW with Anderson update with history "
675 << gwopt_.gw_mixing_order << " using alpha "
676 << gwopt_.gw_mixing_alpha << std::flush;
677 }
678 if (gwopt_.screening_update == "adaptive") {
680 << " evGW screening update: adaptive (ratio "
681 << gwopt_.screening_update_ratio << ", at most "
682 << gwopt_.screening_update_max_inner << " iterations per W)"
683 << std::flush;
684 } else {
686 << " evGW screening update: every iteration" << std::flush;
687 }
688 }
689
690 if (options.exists("gw.sigma_plot")) {
691 sigma_plot_states_ = options.get("gw.sigma_plot.states").as<std::string>();
692 sigma_plot_steps_ = options.get("gw.sigma_plot.steps").as<Index>();
693 sigma_plot_spacing_ = options.get("gw.sigma_plot.spacing").as<double>();
695 options.get("gw.sigma_plot.filename").as<std::string>();
696
698 << " Sigma plot states: " << sigma_plot_states_ << flush;
700 << " Sigma plot steps: " << sigma_plot_steps_ << flush;
702 << " Sigma plot spacing: " << sigma_plot_spacing_ << flush;
704 << " Sigma plot filename: " << sigma_plot_filename_ << flush;
705 }
706}
707
709
710 const double hrt2ev = tools::conv::hrt2ev;
711 tools::Property& gwbse_summary = summary.add("GWBSE", "");
712 if (do_gw_) {
713 gwbse_summary.setAttribute("units", "eV");
714 gwbse_summary.setAttribute(
715 "DFTEnergy",
716 (boost::format("%1$+1.6f ") % (orbitals_.getDFTTotalEnergy() * hrt2ev))
717 .str());
718
719 if (orbitals_.hasUnrestrictedOrbitals()) {
720 gwbse_summary.add("dft_alpha", "");
721 gwbse_summary.add("dft_beta", "");
722
723 tools::Property& alpha_summary = gwbse_summary.get("dft_alpha");
724 tools::Property& beta_summary = gwbse_summary.get("dft_beta");
725
726 alpha_summary.setAttribute("HOMO", orbitals_.getHomoAlpha());
727 alpha_summary.setAttribute("LUMO", orbitals_.getLumoAlpha());
728 beta_summary.setAttribute("HOMO", orbitals_.getHomoBeta());
729 beta_summary.setAttribute("LUMO", orbitals_.getLumoBeta());
730 for (Index state = 0; state < gwopt_.qpmax + 1 - gwopt_.qpmin; state++) {
731 tools::Property& level_alpha = alpha_summary.add("level", "");
732 level_alpha.setAttribute("number", state + gwopt_.qpmin);
733 level_alpha.add(
734 "dft_energy",
735 (boost::format("%1$+1.6f ") %
736 (orbitals_.MOs().eigenvalues()(state + gwopt_.qpmin) * hrt2ev))
737 .str());
738 level_alpha.add("gw_energy",
739 (boost::format("%1$+1.6f ") %
740 (orbitals_.QPpertEnergiesAlpha()(state) * hrt2ev))
741 .str());
742 level_alpha.add(
743 "qp_energy",
744 (boost::format("%1$+1.6f ") %
745 (orbitals_.QPdiagAlpha().eigenvalues()(state) * hrt2ev))
746 .str());
747
748 tools::Property& level_beta = beta_summary.add("level", "");
749 level_beta.setAttribute("number", state + gwopt_.qpmin);
750 level_beta.add(
751 "dft_energy",
752 (boost::format("%1$+1.6f ") %
753 (orbitals_.MOs_beta().eigenvalues()(state + gwopt_.qpmin) *
754 hrt2ev))
755 .str());
756 level_beta.add("gw_energy",
757 (boost::format("%1$+1.6f ") %
758 (orbitals_.QPpertEnergiesBeta()(state) * hrt2ev))
759 .str());
760 level_beta.add("qp_energy",
761 (boost::format("%1$+1.6f ") %
762 (orbitals_.QPdiagBeta().eigenvalues()(state) * hrt2ev))
763 .str());
764 }
765 } else {
766 tools::Property& dft_summary = gwbse_summary.add("dft", "");
767 dft_summary.setAttribute("HOMO", gwopt_.homo);
768 dft_summary.setAttribute("LUMO", gwopt_.homo + 1);
769
770 for (Index state = 0; state < gwopt_.qpmax + 1 - gwopt_.qpmin; state++) {
771 tools::Property& level_summary = dft_summary.add("level", "");
772 level_summary.setAttribute("number", state + gwopt_.qpmin);
773 level_summary.add(
774 "dft_energy",
775 (boost::format("%1$+1.6f ") %
776 (orbitals_.MOs().eigenvalues()(state + gwopt_.qpmin) * hrt2ev))
777 .str());
778 level_summary.add("gw_energy",
779 (boost::format("%1$+1.6f ") %
780 (orbitals_.QPpertEnergies()(state) * hrt2ev))
781 .str());
782 level_summary.add("qp_energy",
783 (boost::format("%1$+1.6f ") %
784 (orbitals_.QPdiag().eigenvalues()(state) * hrt2ev))
785 .str());
786 }
787 }
788 }
789 if (do_bse_singlets_) {
790 tools::Property& singlet_summary = gwbse_summary.add("singlets", "");
791 for (Index state = 0; state < bseopt_.nmax; ++state) {
792 tools::Property& level_summary = singlet_summary.add("level", "");
793 level_summary.setAttribute("number", state + 1);
794 level_summary.add(
795 "omega", (boost::format("%1$+1.6f ") %
796 (orbitals_.BSESinglets().eigenvalues()(state) * hrt2ev))
797 .str());
798 if (orbitals_.hasTransitionDipoles()) {
799
800 const Eigen::Vector3d& dipoles = (orbitals_.TransitionDipoles())[state];
801 double f = 2 * dipoles.squaredNorm() *
802 orbitals_.BSESinglets().eigenvalues()(state) / 3.0;
803
804 level_summary.add("f", (boost::format("%1$+1.6f ") % f).str());
805 tools::Property& dipol_summary = level_summary.add(
806 "Trdipole", (boost::format("%1$+1.4f %2$+1.4f %3$+1.4f") %
807 dipoles.x() % dipoles.y() % dipoles.z())
808 .str());
809 dipol_summary.setAttribute("unit", "e*bohr");
810 dipol_summary.setAttribute("gauge", "length");
811 }
812 }
813 }
814 if (do_bse_triplets_) {
815 tools::Property& triplet_summary = gwbse_summary.add("triplets", "");
816 for (Index state = 0; state < bseopt_.nmax; ++state) {
817 tools::Property& level_summary = triplet_summary.add("level", "");
818 level_summary.setAttribute("number", state + 1);
819 level_summary.add(
820 "omega", (boost::format("%1$+1.6f ") %
821 (orbitals_.BSETriplets().eigenvalues()(state) * hrt2ev))
822 .str());
823 }
824 }
826 tools::Property& exciton_uks_summary = gwbse_summary.add("exciton_uks", "");
827
828 const bool has_dipoles = orbitals_.hasTransitionDipoles();
829 Eigen::VectorXd oscs = Eigen::VectorXd::Zero(0);
830 if (has_dipoles) {
831 oscs =
832 orbitals_.Oscillatorstrengths(QMStateType(QMStateType::ExcitonUKS));
833 }
834
835 for (Index state = 0;
836 state <
837 std::min<Index>(bseopt_.nmax, orbitals_.BSEUKS().eigenvalues().size());
838 ++state) {
839 tools::Property& level_summary = exciton_uks_summary.add("level", "");
840 level_summary.setAttribute("number", state + 1);
841 level_summary.add("omega",
842 (boost::format("%1$+1.6f ") %
843 (orbitals_.BSEUKS().eigenvalues()(state) * hrt2ev))
844 .str());
845 const Index ndip =
846 static_cast<Index>(orbitals_.TransitionDipoles().size());
847 const Index nosc = static_cast<Index>(oscs.size());
848
849 if (has_dipoles && state < ndip && state < nosc) {
850 const Eigen::Vector3d& dipoles = orbitals_.TransitionDipoles()[state];
851
852 level_summary.add("f",
853 (boost::format("%1$+1.6f ") % oscs(state)).str());
854
855 tools::Property& dipol_summary = level_summary.add(
856 "Trdipole", (boost::format("%1$+1.4f %2$+1.4f %3$+1.4f") %
857 dipoles.x() % dipoles.y() % dipoles.z())
858 .str());
859 dipol_summary.setAttribute("unit", "e*bohr");
860 dipol_summary.setAttribute("gauge", "length");
861 }
862 }
863 }
864
865 return;
866}
867
868/*
869 * Many-body Green's fuctions theory implementation
870 *
871 * data required from orbitals file
872 * - atomic coordinates
873 * - DFT molecular orbitals (energies and coeffcients)
874 * - DFT exchange-correlation potential matrix in atomic orbitals
875 * - number of electrons, number of levels
876 */
877
878Eigen::MatrixXd GWBSE::CalculateVXC(const AOBasis& dftbasis) {
879 if (orbitals_.getXCFunctionalName().empty()) {
880 orbitals_.setXCFunctionalName(functional_);
881 } else {
882 if (!(functional_ == orbitals_.getXCFunctionalName())) {
883 throw std::runtime_error("Functionals from DFT " +
884 orbitals_.getXCFunctionalName() + " GWBSE " +
885 functional_ + " differ!");
886 }
887 }
888
889 Vxc_Grid grid;
890 grid.GridSetup(grid_, orbitals_.QMAtoms(), dftbasis);
892 << TimeStamp() << " Setup grid for integration with gridsize: " << grid_
893 << " with " << grid.getGridSize() << " points, divided into "
894 << grid.getBoxesSize() << " boxes" << flush;
895 Vxc_Potential<Vxc_Grid> vxcpotential(grid);
896 vxcpotential.setXCfunctional(functional_);
898 << TimeStamp() << " Integrating Vxc with functional " << functional_
899 << flush;
900 Eigen::MatrixXd DMAT = orbitals_.DensityMatrixGroundState();
901 Mat_p_Energy e_vxc_ao = vxcpotential.IntegrateVXC(DMAT);
902 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " Calculated Vxc" << flush;
904 << TimeStamp() << " Set hybrid exchange factor: " << orbitals_.getScaHFX()
905 << flush;
906 Index qptotal = gwopt_.qpmax - gwopt_.qpmin + 1;
907 Eigen::MatrixXd mos =
908 orbitals_.MOs().eigenvectors().middleCols(gwopt_.qpmin, qptotal);
909
910 Eigen::MatrixXd vxc = mos.transpose() * e_vxc_ao.matrix() * mos;
912 << TimeStamp() << " Calculated exchange-correlation expectation values "
913 << flush;
914
915 return vxc;
916}
917
918std::pair<Eigen::MatrixXd, Eigen::MatrixXd> GWBSE::CalculateVXCSpinResolved(
919 const AOBasis& dftbasis) {
920 if (orbitals_.getXCFunctionalName().empty()) {
921 orbitals_.setXCFunctionalName(functional_);
922 } else if (!(functional_ == orbitals_.getXCFunctionalName())) {
923 throw std::runtime_error("Functionals from DFT " +
924 orbitals_.getXCFunctionalName() + " GWBSE " +
925 functional_ + " differ!");
926 }
927
928 Vxc_Grid grid;
929 grid.GridSetup(grid_, orbitals_.QMAtoms(), dftbasis);
930 Vxc_Potential<Vxc_Grid> vxcpotential(grid);
931 vxcpotential.setXCfunctional(functional_);
932
933 auto dmats = orbitals_.DensityMatrixGroundStateSpinResolved();
934 auto e_vxc_ao = vxcpotential.IntegrateVXCSpin(dmats[0], dmats[1]);
935
936 Index qptotal = gwopt_.qpmax - gwopt_.qpmin + 1;
937 Eigen::MatrixXd mos_alpha =
938 orbitals_.MOs().eigenvectors().middleCols(gwopt_.qpmin, qptotal);
939 Eigen::MatrixXd mos_beta =
940 orbitals_.MOs_beta().eigenvectors().middleCols(gwopt_.qpmin, qptotal);
941
942 Eigen::MatrixXd vxc_alpha =
943 mos_alpha.transpose() * e_vxc_ao.vxc_alpha * mos_alpha;
944 Eigen::MatrixXd vxc_beta =
945 mos_beta.transpose() * e_vxc_ao.vxc_beta * mos_beta;
946
947 return std::make_pair(vxc_alpha, vxc_beta);
948}
949
951
952 // set the parallelization
953 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Using "
954 << OPENMP::getMaxThreads() << " threads" << flush;
955
957 << TimeStamp() << " Using " << EigenBackendDescription() << flush;
960 << " Screening eigensolvers and inverses call "
961 "Accelerate LAPACK directly"
962 << flush;
963 }
965 << TimeStamp() << " Molecule Coordinates [A] " << flush;
966 for (QMAtom& atom : orbitals_.QMAtoms()) {
967 std::string output = (boost::format("%5d"
968 "%5s"
969 " %1.4f %1.4f %1.4f") %
970 atom.getId() % atom.getElement() %
971 (atom.getPos().x() * tools::conv::bohr2ang) %
972 (atom.getPos().y() * tools::conv::bohr2ang) %
973 (atom.getPos().z() * tools::conv::bohr2ang))
974 .str();
975
976 XTP_LOG(Log::error, *pLog_) << output << flush;
977 }
978
979 std::string dft_package = orbitals_.getQMpackage();
981 << TimeStamp() << " DFT data was created by " << dft_package << flush;
982
983 BasisSet dftbs;
984 dftbs.Load(dftbasis_name_);
985
987 << TimeStamp() << " Loaded DFT Basis Set " << dftbasis_name_ << flush;
988
989 AOBasis dftbasis = orbitals_.getDftBasis();
990 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Filled DFT Basis of size "
991 << dftbasis.AOBasisSize() << flush;
993 << TimeStamp() << " Loaded Auxbasis Set " << auxbasis_name_ << flush;
994
995 orbitals_.SetupAuxBasis(auxbasis_name_);
996 AOBasis auxbasis = orbitals_.getAuxBasis();
997 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Filled Auxbasis of size "
998 << auxbasis.AOBasisSize() << flush;
999
1001 fragments_.size() > 0) {
1002 for (const auto& frag : fragments_) {
1003 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Fragment " << frag.getId()
1004 << " size:" << frag.size() << flush;
1005 }
1006 }
1007
1008 if (!do_gw_) {
1009 if ((!orbitals_.hasUnrestrictedOrbitals() && !orbitals_.hasQPdiag()) ||
1010 (orbitals_.hasUnrestrictedOrbitals() &&
1011 (!orbitals_.hasQPdiagAlpha() || !orbitals_.hasQPdiagBeta()))) {
1012 throw std::runtime_error(
1013 "You want no GW calculation but the orb file has no matching QP "
1014 "coefficients.");
1015 }
1016 }
1017 const bool is_uks = orbitals_.hasUnrestrictedOrbitals();
1018 TCMatrix_gwbse Mmn;
1019 TCMatrix_gwbse_spin Mmn_spin;
1020 // rpamin here, because RPA needs till rpamin
1021 Index max_3c = std::max(bseopt_.cmax, gwopt_.qpmax);
1022 if (is_uks) {
1023 Mmn_spin.alpha.Initialize(auxbasis.AOBasisSize(), gwopt_.rpamin, max_3c,
1024 gwopt_.rpamin, gwopt_.rpamax);
1025 Mmn_spin.beta.Initialize(auxbasis.AOBasisSize(), gwopt_.rpamin, max_3c,
1026 gwopt_.rpamin, gwopt_.rpamax);
1028 << TimeStamp() << " Calculating alpha spin Mmn " << flush;
1029 Mmn_spin.alpha.Fill(auxbasis, dftbasis, orbitals_.MOs().eigenvectors());
1031 << TimeStamp() << " Calculating beta spin Mmn " << flush;
1032 Mmn_spin.beta.Fill(auxbasis, dftbasis, orbitals_.MOs_beta().eigenvectors());
1033 } else {
1034 Mmn.Initialize(auxbasis.AOBasisSize(), gwopt_.rpamin, max_3c, gwopt_.rpamin,
1035 gwopt_.rpamax);
1037 << TimeStamp() << " Calculating Mmn (3-center-repulsion x orbitals) "
1038 << flush;
1039 Mmn.Fill(auxbasis, dftbasis, orbitals_.MOs().eigenvectors());
1041 << TimeStamp() << " Removed " << Mmn.Removedfunctions()
1042 << " functions from Aux Coulomb matrix to avoid near linear "
1043 "dependencies"
1044 << flush;
1046 << TimeStamp() << " Calculated Mmn (3-center-repulsion x orbitals) "
1047 << flush;
1048 }
1049 if (const std::string usage = MemoryBudget::Usage(); !usage.empty()) {
1051 << TimeStamp() << " After Mmn: " << usage << flush;
1052 }
1053
1054 // The environment's reaction field in the metric of Mmn. Needs only the
1055 // auxiliary basis and its metric, so it stays valid however Mmn is later
1056 // rotated, dressed or refilled (QSGW) -- all of it on this basis.
1057 Eigen::MatrixXd reaction_field;
1058 if (!environment_.empty()) {
1059 if (is_uks) {
1060 throw std::runtime_error(
1061 "GWBSE: environment screening is implemented for closed-shell "
1062 "systems only.");
1063 }
1064 std::chrono::time_point<std::chrono::system_clock> start =
1065 std::chrono::system_clock::now();
1066 Index n_explicit = 0;
1067 for (const PolarSegment& seg : environment_.explicit_segments) {
1068 n_explicit += seg.size();
1069 }
1070 Index n_shell = 0;
1071 for (const PolarSegment& seg : environment_.shell_segments) {
1072 n_shell += seg.size();
1073 }
1075 << TimeStamp() << " Environment screening: " << n_explicit
1076 << " explicit Thole sites (exp_damp " << environment_.exp_damp << "), "
1077 << n_shell << " shell sites (epsilon " << environment_.shell_dielectric
1078 << ")" << flush;
1079 const Eigen::MatrixXd B =
1081 reaction_field =
1083 std::chrono::duration<double> elapsed_time =
1084 std::chrono::system_clock::now() - start;
1086 << TimeStamp() << " Built environment reaction field in "
1087 << elapsed_time.count() << " seconds." << flush;
1088 }
1089
1090 Eigen::MatrixXd Hqp;
1091 Eigen::MatrixXd Hqp_alpha;
1092 Eigen::MatrixXd Hqp_beta;
1093 if (do_gw_) {
1094
1095 std::chrono::time_point<std::chrono::system_clock> start =
1096 std::chrono::system_clock::now();
1097 if (is_uks) {
1098 if (gwopt_.do_qsgw) {
1099 throw std::runtime_error(
1100 "QSGW (gw.do_qsgw) is not implemented for open-shell (UKS) "
1101 "references.");
1102 }
1103 auto vxc = CalculateVXCSpinResolved(dftbasis);
1104 GW_UKS::options gwopt_uks;
1105 gwopt_uks.homo_alpha = orbitals_.getHomoAlpha();
1106 gwopt_uks.homo_beta = orbitals_.getHomoBeta();
1107 gwopt_uks.qpmin = gwopt_.qpmin;
1108 gwopt_uks.qpmax = gwopt_.qpmax;
1109 gwopt_uks.rpamin = gwopt_.rpamin;
1110 gwopt_uks.rpamax = gwopt_.rpamax;
1111 gwopt_uks.eta = gwopt_.eta;
1112 gwopt_uks.g_sc_limit = gwopt_.g_sc_limit;
1113 gwopt_uks.g_sc_max_iterations = gwopt_.g_sc_max_iterations;
1114 gwopt_uks.gw_sc_limit = gwopt_.gw_sc_limit;
1115 gwopt_uks.gw_sc_max_iterations = gwopt_.gw_sc_max_iterations;
1116 gwopt_uks.shift = gwopt_.shift;
1117 gwopt_uks.ScaHFX = gwopt_.ScaHFX;
1118 gwopt_uks.sigma_integration = gwopt_.sigma_integration;
1119 gwopt_uks.reset_3c = gwopt_.reset_3c;
1120 gwopt_uks.qp_solver = gwopt_.qp_solver;
1121 gwopt_uks.qp_solver_alpha = gwopt_.qp_solver_alpha;
1122 gwopt_uks.qp_grid_steps = gwopt_.qp_grid_steps;
1123 gwopt_uks.qp_grid_spacing = gwopt_.qp_grid_spacing;
1124
1125 // Forward the already-normalized canonical grid-search controls so that
1126 // RKS and UKS use the same search design and only differ in the
1127 // spin-dependent sigma evaluation.
1128 gwopt_uks.qp_full_window_half_width = gwopt_.qp_full_window_half_width;
1129 gwopt_uks.qp_dense_spacing = gwopt_.qp_dense_spacing;
1130 gwopt_uks.qp_adaptive_shell_width = gwopt_.qp_adaptive_shell_width;
1131 gwopt_uks.qp_adaptive_shell_count = gwopt_.qp_adaptive_shell_count;
1132 gwopt_uks.gw_mixing_order = gwopt_.gw_mixing_order;
1133 gwopt_uks.gw_mixing_alpha = gwopt_.gw_mixing_alpha;
1134 gwopt_uks.quadrature_scheme = gwopt_.quadrature_scheme;
1135 gwopt_uks.order = gwopt_.order;
1136 gwopt_uks.alpha = gwopt_.alpha;
1137 gwopt_uks.out_of_window_shift = gwopt_.out_of_window_shift;
1138 gwopt_uks.qp_restrict_search = gwopt_.qp_restrict_search;
1139 gwopt_uks.qp_root_continuity = gwopt_.qp_root_continuity;
1140 gwopt_uks.screening_update = gwopt_.screening_update;
1141 gwopt_uks.qp_root_tracking = gwopt_.qp_root_tracking;
1142 gwopt_uks.screening_update_ratio = gwopt_.screening_update_ratio;
1143 gwopt_uks.screening_update_max_inner = gwopt_.screening_update_max_inner;
1144 gwopt_uks.qp_zero_margin = gwopt_.qp_zero_margin;
1145 gwopt_uks.qp_virtual_min_energy = gwopt_.qp_virtual_min_energy;
1146 gwopt_uks.qp_root_finder = gwopt_.qp_root_finder;
1147 gwopt_uks.qp_grid_search_mode = gwopt_.qp_grid_search_mode;
1148
1149 GW_UKS gw = GW_UKS(*pLog_, Mmn_spin, vxc.first, vxc.second,
1150 orbitals_.MOs().eigenvalues(),
1151 orbitals_.MOs_beta().eigenvalues());
1152 gw.configure(gwopt_uks);
1153 if (gwopt_.rpamin > 0 && core_exchange_) {
1154 gw.setCoreExchange(
1156 auxbasis, dftbasis, orbitals_.MOs().eigenvectors(),
1157 gwopt_.rpamin, gwopt_.qpmin, gwopt_.qpmax),
1159 auxbasis, dftbasis, orbitals_.MOs_beta().eigenvectors(),
1160 gwopt_.rpamin, gwopt_.qpmin, gwopt_.qpmax));
1162 << TimeStamp() << " Sigma_x includes the exchange with the "
1163 << gwopt_.rpamin << " frozen core levels" << flush;
1164 }
1166 orbitals_.QPpertEnergiesAlpha() = gw.getGWAResultsAlpha();
1167 orbitals_.QPpertEnergiesBeta() = gw.getGWAResultsBeta();
1168 orbitals_.RPAInputEnergiesAlpha() = gw.RPAInputEnergiesAlpha();
1169 orbitals_.RPAInputEnergiesBeta() = gw.RPAInputEnergiesBeta();
1170 gw.CalculateHQP();
1171 Hqp_alpha = gw.getHQPAlpha();
1172 Hqp_beta = gw.getHQPBeta();
1173 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_alpha =
1175 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_beta =
1177 orbitals_.QPdiagAlpha().eigenvectors() = es_alpha.eigenvectors();
1178 orbitals_.QPdiagAlpha().eigenvalues() = es_alpha.eigenvalues();
1179 orbitals_.QPdiagBeta().eigenvectors() = es_beta.eigenvectors();
1180 orbitals_.QPdiagBeta().eigenvalues() = es_beta.eigenvalues();
1181 std::chrono::duration<double> elapsed_time =
1182 std::chrono::system_clock::now() - start;
1184 << TimeStamp() << " UKS GW calculation took " << elapsed_time.count()
1185 << " seconds." << flush;
1186 } else {
1187 Eigen::MatrixXd vxc = CalculateVXC(dftbasis);
1188 GW gw = GW(*pLog_, Mmn, vxc, orbitals_.MOs().eigenvalues());
1189 gw.configure(gwopt_);
1190 gw.setReactionField(reaction_field);
1191 if (gwopt_.rpamin > 0 && core_exchange_) {
1193 auxbasis, dftbasis, orbitals_.MOs().eigenvectors(), gwopt_.rpamin,
1194 gwopt_.qpmin, gwopt_.qpmax));
1196 << TimeStamp() << " Sigma_x includes the exchange with the "
1197 << gwopt_.rpamin << " frozen core levels" << flush;
1198 }
1199
1201
1202 if (!sigma_plot_states_.empty()) {
1205 }
1206
1207 orbitals_.QPpertEnergies() = gw.getGWAResults();
1208 orbitals_.RPAInputEnergies() = gw.RPAInputEnergies();
1209
1211 << TimeStamp() << " Calculating offdiagonal part of Sigma " << flush;
1212 if (gwopt_.do_qsgw) {
1213 // QSGW: iterate rotation of Mmn until QP energies converge.
1214 gw.CalculateQSGW();
1215
1216 // Print seed vs QSGW comparison. Label the seed column by what
1217 // CalculateGWPerturbation ran: G0W0 if one iteration, evGW otherwise.
1218 const std::string seed_label =
1219 (gwopt_.gw_sc_max_iterations == 1) ? "G0W0" : "evGW";
1220 Eigen::VectorXd qsgw_energies = gw.getGWAResults();
1221 gw.PrintQSGW_Energies(seed_label, gw.getQSGWSeedEnergies(),
1222 qsgw_energies);
1224
1225 // The QSGW orbitals and energies: QPdiag holds U (DFT MOs -> QSGW
1226 // orbitals, columns) and the energies, so that BSE-only runs from the
1227 // .orb file and everything built from the BSE vectors (transition
1228 // dipoles, densities) use C_qp = C_dft U (Orbitals::BSEOrbitals).
1229 // QPpertEnergies() keeps the G0W0/evGW seed. The RPA energies are
1230 // the QSGW ones in the window.
1231 orbitals_.QPdiag().eigenvalues() = qsgw_energies;
1232 orbitals_.QPdiag().eigenvectors() = gw.getQSGWRotation();
1233 orbitals_.setQSGW(true);
1234 orbitals_.RPAInputEnergies() = gw.RPAInputEnergies();
1235
1236 // The BSE in the QSGW orbitals: integrals refilled with C_qp (levels
1237 // outside the GW window stay DFT MOs, scissor-shifted in the RPA
1238 // energies and in AdjustHqpSize), and H_qp is diagonal.
1240 << TimeStamp() << " Rebuilding Mmn in QSGW QP wavefunction basis"
1241 << flush;
1242 Mmn.Fill(auxbasis, dftbasis, orbitals_.BSEOrbitals());
1244 << TimeStamp() << " Rebuilt Mmn (3-center-repulsion x QP orbitals)"
1245 << flush;
1246 Hqp = qsgw_energies.asDiagonal();
1247 } else {
1248 orbitals_.setQSGW(false);
1249 gw.CalculateHQP();
1251 << TimeStamp() << " Calculated offdiagonal part of Sigma "
1252 << flush;
1253
1254 Hqp = gw.getHQP();
1255
1256 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es =
1258 if (es.info() == Eigen::ComputationInfo::Success) {
1260 << TimeStamp() << " Diagonalized QP Hamiltonian " << flush;
1261 }
1262
1263 orbitals_.QPdiag().eigenvectors() = es.eigenvectors();
1264 orbitals_.QPdiag().eigenvalues() = es.eigenvalues();
1265 }
1266 std::chrono::duration<double> elapsed_time =
1267 std::chrono::system_clock::now() - start;
1269 << TimeStamp() << " GW calculation took " << elapsed_time.count()
1270 << " seconds." << flush;
1271 }
1272
1273 } else {
1274 if (orbitals_.getGWAmax() != gwopt_.qpmax ||
1275 orbitals_.getGWAmin() != gwopt_.qpmin ||
1276 orbitals_.getRPAmax() != gwopt_.rpamax ||
1277 orbitals_.getRPAmin() != gwopt_.rpamin) {
1278 throw std::runtime_error(
1279 "The ranges for GW and RPA do not agree with the ranges from the "
1280 ".orb file, rerun your GW calculation");
1281 }
1282
1283 if (is_uks) {
1284 const Eigen::MatrixXd& qpcoeff_alpha =
1285 orbitals_.QPdiagAlpha().eigenvectors();
1286 const Eigen::MatrixXd& qpcoeff_beta =
1287 orbitals_.QPdiagBeta().eigenvectors();
1288
1289 Hqp_alpha = qpcoeff_alpha *
1290 orbitals_.QPdiagAlpha().eigenvalues().asDiagonal() *
1291 qpcoeff_alpha.transpose();
1292 Hqp_beta = qpcoeff_beta *
1293 orbitals_.QPdiagBeta().eigenvalues().asDiagonal() *
1294 qpcoeff_beta.transpose();
1295 } else if (orbitals_.isQSGW()) {
1296 // QSGW BSE-only run: rebuild Mmn_ in the QSGW orbitals stored in
1297 // QPdiag. Hqp is diagonal in that basis.
1299 << TimeStamp()
1300 << " Rebuilding Mmn in QSGW QP wavefunction basis (BSE-only)"
1301 << flush;
1302 Mmn.Fill(auxbasis, dftbasis, orbitals_.BSEOrbitals());
1304 << TimeStamp() << " Rebuilt Mmn (3-center-repulsion x QP orbitals)"
1305 << flush;
1306 Hqp = orbitals_.QPdiag().eigenvalues().asDiagonal();
1307 } else {
1308 const Eigen::MatrixXd& qpcoeff = orbitals_.QPdiag().eigenvectors();
1309
1310 Hqp = qpcoeff * orbitals_.QPdiag().eigenvalues().asDiagonal() *
1311 qpcoeff.transpose();
1312 }
1313 }
1314
1315 // proceed only if BSE requested
1317
1318 std::chrono::time_point<std::chrono::system_clock> start =
1319 std::chrono::system_clock::now();
1320
1321 if (is_uks) {
1322
1323 // Build a single spin-summed dielectric screening from the unrestricted
1324 // RPA response and reuse it for both exciton spin channels.
1325 RPA_UKS rpa_uks_bse(*pLog_, Mmn_spin);
1326 rpa_uks_bse.configure(orbitals_.getHomoAlpha(), orbitals_.getHomoBeta(),
1327 bseopt_.rpamin, bseopt_.rpamax);
1328 rpa_uks_bse.setRPAInputEnergies(orbitals_.RPAInputEnergiesAlpha(),
1329 orbitals_.RPAInputEnergiesBeta());
1330
1331 const SymmetricEigenSystem es_bse =
1332 SymmetricEigen(rpa_uks_bse.calculate_epsilon_r(0.0));
1333
1334 Eigen::VectorXd epsilon_0_inv_bse =
1335 Eigen::VectorXd::Zero(es_bse.values.size());
1336 for (Index i = 0; i < es_bse.values.size(); ++i) {
1337 if (es_bse.values(i) > 1e-8) {
1338 epsilon_0_inv_bse(i) = 1.0 / es_bse.values(i);
1339 }
1340 }
1341
1342 if (do_bse_exciton_uks_) {
1343 BSE_UKS::options bseopt_uks;
1344 bseopt_uks.useTDA = bseopt_.useTDA;
1345 bseopt_uks.rpamin = bseopt_.rpamin;
1346 bseopt_uks.rpamax = bseopt_.rpamax;
1347 bseopt_uks.qpmin = bseopt_.qpmin;
1348 bseopt_uks.qpmax = bseopt_.qpmax;
1349 bseopt_uks.vmin = bseopt_.vmin;
1350 bseopt_uks.cmax = bseopt_.cmax;
1351 bseopt_uks.nmax = bseopt_.nmax;
1352 bseopt_uks.davidson_correction = bseopt_.davidson_correction;
1353 bseopt_uks.davidson_tolerance = bseopt_.davidson_tolerance;
1354 bseopt_uks.davidson_update = bseopt_.davidson_update;
1355 bseopt_uks.davidson_maxiter = bseopt_.davidson_maxiter;
1356 bseopt_uks.min_print_weight = bseopt_.min_print_weight;
1357 bseopt_uks.use_Hqp_offdiag = bseopt_.use_Hqp_offdiag;
1358 bseopt_uks.max_dyn_iter = bseopt_.max_dyn_iter;
1359 bseopt_uks.dyn_tolerance = bseopt_.dyn_tolerance;
1360
1361 BSE_UKS bse_uks(*pLog_, Mmn_spin);
1363 bseopt_uks, orbitals_.getHomoAlpha(), orbitals_.getHomoBeta(),
1364 orbitals_.RPAInputEnergiesAlpha(), orbitals_.RPAInputEnergiesBeta(),
1365 Hqp_alpha, Hqp_beta, epsilon_0_inv_bse, es_bse.vectors);
1366
1369 << TimeStamp() << " Solved combined UKS BSE exciton problem "
1370 << flush;
1372
1375 }
1376 }
1377 } else {
1378 BSE bse = BSE(*pLog_, Mmn);
1379 bse.setReactionField(reaction_field, environment_.include_kreac);
1380 bse.configure(bseopt_, orbitals_.RPAInputEnergies(), Hqp);
1381
1382 if (do_bse_triplets_) {
1385 << TimeStamp() << " Solved BSE for triplets " << flush;
1387 }
1388
1389 if (do_bse_singlets_) {
1392 << TimeStamp() << " Solved BSE for singlets " << flush;
1394 }
1395
1397 if (do_bse_triplets_) {
1399 orbitals_);
1400 }
1401
1402 if (do_bse_singlets_) {
1404 orbitals_);
1405 }
1406 }
1407 }
1408
1409 std::chrono::duration<double> elapsed_time =
1410 std::chrono::system_clock::now() - start;
1411 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " BSE calculation took "
1412 << elapsed_time.count() << " seconds." << flush;
1413 }
1415 << TimeStamp() << " GWBSE calculation finished " << flush;
1416 return true;
1417}
1418
1419} // namespace xtp
1420} // namespace votca
class to manage program options with xml serialization functionality
Definition property.h:55
Property & add(const std::string &key, const std::string &value)
add a new property to structure
Definition property.cc:108
Property & get(const std::string &key)
get existing property
Definition property.cc:79
bool exists(const std::string &key) const
check whether property exists
Definition property.cc:122
T as() const
return value as type
Definition property.h:283
T ifExistsReturnElseReturnDefault(const std::string &key, T defaultvalue) const
Definition property.h:332
std::vector< Property * > Select(const std::string &filter)
select property based on a filter
Definition property.cc:185
void setAttribute(const std::string &attribute, const T &value)
set an attribute
Definition property.h:325
Container to hold Basisfunctions for all atoms.
Definition aobasis.h:42
Index AOBasisSize() const
Definition aobasis.h:46
void configure_with_precomputed_screening(const options &opt, Index homo_alpha, Index homo_beta, const Eigen::VectorXd &RPAInputEnergiesAlpha, const Eigen::VectorXd &RPAInputEnergiesBeta, const Eigen::MatrixXd &Hqp_alpha_in, const Eigen::MatrixXd &Hqp_beta_in, const Eigen::VectorXd &epsilon_0_inv, const Eigen::MatrixXd &epsilon_eigenvectors)
Definition bse_uks.cc:67
void Perturbative_DynamicalScreening(Orbitals &orb)
Definition bse_uks.cc:674
void Solve_excitons_uks(Orbitals &orb) const
Definition bse_uks.cc:474
void Analyze_excitons_uks(std::vector< QMFragment< BSE_Population > > fragments, const Orbitals &orb) const
Definition bse_uks.cc:631
void setReactionField(const Eigen::MatrixXd &R, bool include_kreac)
Screen by a static polarizable environment (embedded BSE).
Definition bse.cc:209
void Solve_singlets(Orbitals &orb) const
Definition bse.cc:368
void Analyze_singlets(std::vector< QMFragment< BSE_Population > > fragments, const Orbitals &orb) const
Definition bse.cc:606
void Perturbative_DynamicalScreening(const QMStateType &type, Orbitals &orb)
Definition bse.cc:934
void Analyze_triplets(std::vector< QMFragment< BSE_Population > > fragments, const Orbitals &orb) const
Definition bse.cc:665
void configure(const options &opt, const Eigen::VectorXd &RPAEnergies, const Eigen::MatrixXd &Hqp_in)
Definition bse.cc:63
void Solve_triplets(Orbitals &orb) const
Definition bse.cc:426
void Load(const std::string &name)
Definition basisset.cc:149
void Load(const std::string &name)
const ECPElement & getElement(std::string element_type) const
Index getNcore() const
Definition ecpbasisset.h:90
static Eigen::MatrixXd Kernel(const AOBasis &auxbasis, const ScreeningEnvironment &env)
B for a whole environment: ReactionFieldKernel of the explicit segments plus ShellKernel of the shell...
static Eigen::MatrixXd SymmetrizedReactionField(const Eigen::MatrixXd &B, const Eigen::MatrixXd &T)
R = T^T B T, the reaction field in the metric of the stored three-centre integrals,...
bool Evaluate()
Definition gwbse.cc:950
bool do_bse_exciton_uks_
Definition gwbse.h:92
std::pair< Eigen::MatrixXd, Eigen::MatrixXd > CalculateVXCSpinResolved(const AOBasis &dftbasis)
Definition gwbse.cc:918
std::vector< QMFragment< BSE_Population > > fragments_
Definition gwbse.h:113
std::string sigma_plot_states_
Definition gwbse.h:101
ScreeningEnvironment environment_
Definition gwbse.h:115
BSE::options bseopt_
Definition gwbse.h:99
void addoutput(tools::Property &summary)
Definition gwbse.cc:708
bool do_bse_triplets_
Definition gwbse.h:90
std::string grid_
Definition gwbse.h:96
std::string auxbasis_name_
Definition gwbse.h:110
bool do_bse_singlets_
Definition gwbse.h:89
bool core_exchange_
Definition gwbse.h:104
Orbitals & orbitals_
Definition gwbse.h:85
Eigen::MatrixXd CalculateVXC(const AOBasis &dftbasis)
Definition gwbse.cc:878
Index sigma_plot_steps_
Definition gwbse.h:105
std::string functional_
Definition gwbse.h:95
std::string dftbasis_name_
Definition gwbse.h:111
void Initialize(tools::Property &options)
Definition gwbse.cc:63
Index CountCoreLevels()
Definition gwbse.cc:49
GW::options gwopt_
Definition gwbse.h:98
double sigma_plot_spacing_
Definition gwbse.h:106
Logger * pLog_
Definition gwbse.h:84
std::string sigma_plot_filename_
Definition gwbse.h:107
bool do_dynamical_screening_bse_
Definition gwbse.h:91
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonianAlpha() const
Definition gw_uks.cc:1028
Eigen::VectorXd getGWAResultsAlpha() const
Definition gw_uks.cc:461
const Eigen::VectorXd & RPAInputEnergiesAlpha() const
Definition gw_uks.cc:471
const Eigen::VectorXd & RPAInputEnergiesBeta() const
Definition gw_uks.cc:474
Eigen::MatrixXd getHQPBeta() const
Definition gw_uks.cc:1021
Eigen::VectorXd getGWAResultsBeta() const
Definition gw_uks.cc:466
Eigen::MatrixXd getHQPAlpha() const
Definition gw_uks.cc:1016
void setCoreExchange(const Eigen::MatrixXd &alpha, const Eigen::MatrixXd &beta)
Frozen-core exchange per spin, see GW::setCoreExchange.
Definition gw_uks.h:103
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonianBeta() const
Definition gw_uks.cc:1035
void configure(const options &opt)
Definition gw_uks.cc:41
void CalculateGWPerturbation()
Definition gw_uks.cc:192
void PlotSigma(std::string filename, Index steps, double spacing, std::string states) const
Definition gw.cc:1482
const Eigen::VectorXd & getQSGWSeedEnergies() const
Return the seed (G0W0/evGW) energies that QSGW started from.
Definition gw.h:209
void setReactionField(const Eigen::MatrixXd &R)
Screen by a static polarizable environment (embedded GW).
Definition gw.cc:68
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > DiagonalizeQPHamiltonian() const
Definition gw.cc:131
void setCoreExchange(const Eigen::MatrixXd &X)
Definition gw.h:177
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.
Definition gw.cc:221
void CalculateQSGW()
Run quasiparticle self-consistent GW (QSGW).
Definition gw.cc:1178
void configure(const options &opt)
Definition gw.cc:39
Eigen::MatrixXd getHQP() const
Definition gw.cc:125
void PrintQSGW_Composition(double threshold=0.01) const
Print the dominant DFT-KS orbital contributions to each converged QSGW quasiparticle state.
Definition gw.cc:178
void CalculateHQP()
Definition gw.cc:1138
Eigen::VectorXd getGWAResults() const
Definition gw.cc:545
Eigen::VectorXd RPAInputEnergies() const
Definition gw.h:245
const Eigen::MatrixXd & getQSGWRotation() const
Definition gw.h:206
void CalculateGWPerturbation()
Definition gw.cc:283
Eigen::MatrixXd & matrix()
Definition eigen.h:97
static std::string Usage()
container for QM atoms
Definition qmatom.h:37
Unrestricted RPA helper for spin-resolved GW screening.
Definition rpa_uks.h:70
void setRPAInputEnergies(const Eigen::VectorXd &rpaenergies_alpha, const Eigen::VectorXd &rpaenergies_beta)
Set alpha and beta RPA input energies directly.
Definition rpa_uks.h:168
void configure(Index homo_alpha, Index homo_beta, Index rpamin, Index rpamax)
Definition rpa_uks.h:98
Eigen::MatrixXd calculate_epsilon_r(double frequency) const
Dielectric matrix on the real frequency axis.
Definition rpa_uks.h:131
static Mode Parse(const std::string &name)
void Fill(const AOBasis &auxbasis, const AOBasis &dftbasis, const Eigen::MatrixXd &dft_orbitals)
void Initialize(Index basissize, Index mmin, Index mmax, Index nmin, Index nmax)
static Eigen::MatrixXd CoreExchange(const AOBasis &auxbasis, const AOBasis &dftbasis, const Eigen::MatrixXd &mos, Index ncore, Index qpmin, Index qpmax)
const Eigen::MatrixXd & InvSqrt() const
Index Removedfunctions() const
Definition threecenter.h:52
Timestamp returns the current time as a string Example: cout << TimeStamp().
Definition logger.h:224
Index getGridSize() const
Definition vxc_grid.h:41
void GridSetup(const std::string &type, const QMMolecule &atoms, const AOBasis &basis)
Definition vxc_grid.cc:230
Index getBoxesSize() const
Definition vxc_grid.h:42
Mat_p_Energy IntegrateVXC(const Eigen::MatrixXd &density_matrix) const
void setXCfunctional(const std::string &functional)
SpinResult IntegrateVXCSpin(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta) const
#define XTP_LOG(level, log)
Definition logger.h:40
const double ev2hrt
Definition constants.h:54
const double hrt2ev
Definition constants.h:53
const double bohr2ang
Definition constants.h:49
Index getMaxThreads()
Definition eigen.h:145
bool LapackAvailable()
Whether the routines below can run here (Accelerate build, macOS 13.3+).
double LegacyAdaptiveShellWidth(const Opt &opt)
void NormalizeGridSearchOptions(Opt &opt)
double LegacyFullWindowHalfWidth(const Opt &opt)
Charge transport classes.
Definition ERIs.h:28
SymmetricEigenSystem SymmetricEigen(const Eigen::MatrixXd &A, bool vectors=true)
With vectors = false only the eigenvalues are computed.
ClassicalSegment< PolarSite > PolarSegment
std::string EigenBackendDescription()
Definition eigen.h:63
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
std::string davidson_update
Definition bse_uks.h:47
std::string davidson_tolerance
Definition bse_uks.h:46
std::string davidson_correction
Definition bse_uks.h:45
std::string screening_update
Definition gw_uks.h:82
std::string quadrature_scheme
Definition gw_uks.h:73
OutOfWindowShift out_of_window_shift
Definition gw_uks.h:35
std::string qp_grid_search_mode
Definition gw_uks.h:92
std::string sigma_integration
Definition gw_uks.h:49
std::string qp_root_finder
Definition gw_uks.h:91
static Mode Parse(const std::string &name)
static std::string Name(Mode mode)