votca 2026-dev
Loading...
Searching...
No Matches
bse_uks.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2026 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20#include <chrono>
21#include <iostream>
22
23#include <votca/tools/linalg.h>
24
28#include "votca/xtp/bse_uks.h"
31#include "votca/xtp/rpa_uks.h"
32
33using std::flush;
34
35namespace {
36
37template <class OP>
38Eigen::VectorXd ExpValue(const Eigen::MatrixXd& state1, OP OPxstate2) {
39 return state1.cwiseProduct(OPxstate2.eval()).colwise().sum().transpose();
40}
41
42Eigen::VectorXd ExpValue(const Eigen::MatrixXd& state1,
43 const Eigen::MatrixXd& OPxstate2) {
44 return state1.cwiseProduct(OPxstate2).colwise().sum().transpose();
45}
46
47} // namespace
48
49namespace votca {
50namespace xtp {
51
52namespace {
53double DavidsonToleranceValue(const std::string& tol) {
54 if (tol == "loose") {
55 return 1e-3;
56 } else if (tol == "normal") {
57 return 1e-4;
58 } else if (tol == "strict") {
59 return 1e-5;
60 } else if (tol == "lapack") {
61 return 1e-9;
62 }
63 throw std::runtime_error(tol + " is not a valid Davidson tolerance");
64}
65} // namespace
66
68 const options& opt, Index homo_alpha, Index homo_beta,
69 const Eigen::VectorXd& RPAInputEnergiesAlpha,
70 const Eigen::VectorXd& RPAInputEnergiesBeta,
71 const Eigen::MatrixXd& Hqp_alpha_in, const Eigen::MatrixXd& Hqp_beta_in,
72 const Eigen::VectorXd& epsilon_0_inv,
73 const Eigen::MatrixXd& epsilon_eigenvectors) {
74
75 opt_ = opt;
76 homo_alpha_ = homo_alpha;
77 homo_beta_ = homo_beta;
78
79 alpha_vtotal_ = homo_alpha_ - opt_.vmin + 1;
80 alpha_ctotal_ = opt_.cmax - (homo_alpha_ + 1) + 1;
82
83 beta_vtotal_ = homo_beta_ - opt_.vmin + 1;
84 beta_ctotal_ = opt_.cmax - (homo_beta_ + 1) + 1;
86
87 if (opt_.use_Hqp_offdiag) {
89 AdjustHqpSize(Hqp_alpha_in, RPAInputEnergiesAlpha, homo_alpha_);
90 Hqp_beta_ = AdjustHqpSize(Hqp_beta_in, RPAInputEnergiesBeta, homo_beta_);
91 } else {
92 Hqp_alpha_ = AdjustHqpSize(Hqp_alpha_in, RPAInputEnergiesAlpha, homo_alpha_)
93 .diagonal()
94 .asDiagonal();
95 Hqp_beta_ = AdjustHqpSize(Hqp_beta_in, RPAInputEnergiesBeta, homo_beta_)
96 .diagonal()
97 .asDiagonal();
98 }
99
100 // Store the statically screened interaction in the working copy Mmn_.
101 Mmn_ = Mmn_raw_;
102 Mmn_.alpha.MultiplyRightWithAuxMatrix(epsilon_eigenvectors);
103 Mmn_.beta.MultiplyRightWithAuxMatrix(epsilon_eigenvectors);
104
105 epsilon_0_inv_ = epsilon_0_inv;
106}
107
108Eigen::MatrixXd BSE_UKS::AdjustHqpSize(const Eigen::MatrixXd& Hqp,
109 const Eigen::VectorXd& RPAInputEnergies,
110 Index homo) const {
111 Index bse_vtotal = homo - opt_.vmin + 1;
112 Index bse_ctotal = opt_.cmax - (homo + 1) + 1;
113 Index hqp_size = bse_vtotal + bse_ctotal;
114 Index gwsize = opt_.qpmax - opt_.qpmin + 1;
115 Index RPAoffset = opt_.vmin - opt_.rpamin;
116 Eigen::MatrixXd Hqp_BSE = Eigen::MatrixXd::Zero(hqp_size, hqp_size);
117
118 if (opt_.vmin >= opt_.qpmin) {
119 Index start = opt_.vmin - opt_.qpmin;
120 if (opt_.cmax <= opt_.qpmax) {
121 Hqp_BSE = Hqp.block(start, start, hqp_size, hqp_size);
122 } else {
123 Index virtoffset = gwsize - start;
124 Hqp_BSE.topLeftCorner(virtoffset, virtoffset) =
125 Hqp.block(start, start, virtoffset, virtoffset);
126
127 Index virt_extra = opt_.cmax - opt_.qpmax;
128 Hqp_BSE.diagonal().tail(virt_extra) =
129 RPAInputEnergies.segment(RPAoffset + virtoffset, virt_extra);
130 }
131 }
132
133 if (opt_.vmin < opt_.qpmin) {
134 Index occ_extra = opt_.qpmin - opt_.vmin;
135 Hqp_BSE.diagonal().head(occ_extra) =
136 RPAInputEnergies.segment(RPAoffset, occ_extra);
137
138 Hqp_BSE.block(occ_extra, occ_extra, gwsize, gwsize) = Hqp;
139
140 if (opt_.cmax > opt_.qpmax) {
141 Index virtoffset = occ_extra + gwsize;
142 Index virt_extra = opt_.cmax - opt_.qpmax;
143 Hqp_BSE.diagonal().tail(virt_extra) =
144 RPAInputEnergies.segment(RPAoffset + virtoffset, virt_extra);
145 }
146 }
147
148 return Hqp_BSE;
149}
150
152 const Eigen::VectorXd& RPAInputEnergiesAlpha,
153 const Eigen::VectorXd& RPAInputEnergiesBeta, double energy) {
154
155 RPA_UKS rpa(log_, Mmn_raw_);
156 rpa.configure(homo_alpha_, homo_beta_, opt_.rpamin, opt_.rpamax);
157 rpa.setRPAInputEnergies(RPAInputEnergiesAlpha, RPAInputEnergiesBeta);
158
159 const SymmetricEigenSystem es =
161
162 Mmn_ = Mmn_raw_;
163 Mmn_.alpha.MultiplyRightWithAuxMatrix(es.vectors);
164 Mmn_.beta.MultiplyRightWithAuxMatrix(es.vectors);
165
166 epsilon_0_inv_ = Eigen::VectorXd::Zero(es.values.size());
167 for (Index i = 0; i < es.values.size(); ++i) {
168 if (es.values(i) > 1e-8) {
169 epsilon_0_inv_(i) = 1.0 / es.values(i);
170 }
171 }
172}
173
174template <typename BSE_OPERATOR>
178 opt.homo_beta = homo_beta_;
179 opt.rpamin = opt_.rpamin;
180 opt.qpmin = opt_.qpmin;
181 opt.vmin = opt_.vmin;
182 opt.cmax = opt_.cmax;
183 H.configure(opt);
184}
185
186template <typename BSE_OPERATOR>
188 const Orbitals& orb, const BSE_OPERATOR& H) const {
189
190 const tools::EigenSystem& BSECoefs = orb.BSEUKS();
191
192 ExpectationValues expectation_values;
193
194 const Eigen::MatrixXd temp = H * BSECoefs.eigenvectors();
195
196 expectation_values.direct_term = ExpValue(BSECoefs.eigenvectors(), temp);
197
198 if (!orb.getTDAApprox()) {
199 expectation_values.direct_term +=
200 ExpValue(BSECoefs.eigenvectors2(), H * BSECoefs.eigenvectors2());
201 expectation_values.cross_term =
202 2.0 * ExpValue(BSECoefs.eigenvectors2(), temp);
203 } else {
204 expectation_values.cross_term = Eigen::VectorXd::Zero(0);
205 }
206
207 return expectation_values;
208}
209
210template <typename BSE_OPERATOR>
212 Index state, const Orbitals& orb, const BSE_OPERATOR& H) const {
213
214 const tools::EigenSystem& BSECoefs = orb.BSEUKS();
215
216 ExpectationValues expectation_values;
217
218 const Eigen::MatrixXd Xstate = BSECoefs.eigenvectors().col(state);
219 const Eigen::MatrixXd temp = H * Xstate;
220
221 expectation_values.direct_term = ExpValue(Xstate, temp);
222
223 if (!orb.getTDAApprox()) {
224 const Eigen::MatrixXd Ystate = BSECoefs.eigenvectors2().col(state);
225 expectation_values.direct_term += ExpValue(Ystate, H * Ystate);
226 expectation_values.cross_term = 2.0 * ExpValue(Ystate, temp);
227 } else {
228 expectation_values.cross_term = Eigen::VectorXd::Zero(0);
229 }
230
231 return expectation_values;
232}
233
238 opt.homo_beta = homo_beta_;
239 opt.rpamin = opt_.rpamin;
240 opt.qpmin = opt_.qpmin;
241 opt.vmin = opt_.vmin;
242 opt.cmax = opt_.cmax;
243 H.configure(opt);
244
246 << TimeStamp() << " Setup combined UKS TDA Hamiltonian " << flush;
247 return solve_hermitian(H);
248}
249
250/* tools::EigenSystem BSE_UKS::Solve_excitons_uks_BTDA() const {
251 ExcitonUKSOperator_TDA A(epsilon_0_inv_, Mmn_, Hqp_alpha_, Hqp_beta_);
252 ExcitonUKSOperator_BTDA_B B(epsilon_0_inv_, Mmn_, Hqp_alpha_, Hqp_beta_);
253
254 BSEOperatorUKS_Options opt;
255 opt.homo_alpha = homo_alpha_;
256 opt.homo_beta = homo_beta_;
257 opt.rpamin = opt_.rpamin;
258 opt.qpmin = opt_.qpmin;
259 opt.vmin = opt_.vmin;
260 opt.cmax = opt_.cmax;
261
262 A.configure(opt);
263 B.configure(opt);
264
265 XTP_LOG(Log::error, log_)
266 << TimeStamp() << " Setup combined UKS full BSE exciton hamiltonian "
267 << flush;
268
269 // Cheap production initial guess:
270 // rank excitations by local diagonal full-BSE estimate
271 // omega_i = sqrt(max(0, A_ii^2 - B_ii^2))
272 // but keep the actual start vectors X-only for robustness with the
273 // current HAM Davidson implementation.
274 const Eigen::VectorXd adiag = A.diagonal();
275 const Eigen::VectorXd bdiag = B.diagonal();
276 Eigen::MatrixXd initial_guess =
277 BuildFullBSEXRankedInitialGuess(adiag, bdiag, opt_.nmax);
278
279 HamiltonianOperator<ExcitonUKSOperator_TDA, ExcitonUKSOperator_BTDA_B> Hop(A,
280 B);
281
282 DavidsonSolver DS(log_);
283 DS.set_correction(opt_.davidson_correction);
284 DS.set_tolerance(opt_.davidson_tolerance);
285 DS.set_size_update(opt_.davidson_update);
286 DS.set_iter_max(opt_.davidson_maxiter);
287 DS.set_max_search_space(10 * opt_.nmax);
288 DS.set_matrix_type("HAM");
289 DS.solve(Hop, opt_.nmax, initial_guess);
290
291 tools::EigenSystem result;
292 result.eigenvalues() = DS.eigenvalues();
293
294 Eigen::MatrixXd tmpX = DS.eigenvectors().topRows(A.rows());
295 Eigen::MatrixXd tmpY = DS.eigenvectors().bottomRows(B.rows());
296
297 Eigen::VectorXd normX = tmpX.colwise().squaredNorm();
298 Eigen::VectorXd normY = tmpY.colwise().squaredNorm();
299 Eigen::ArrayXd sqinvnorm = (normX - normY).array().inverse().cwiseSqrt();
300
301 result.eigenvectors() = tmpX * sqinvnorm.matrix().asDiagonal();
302 result.eigenvectors2() = tmpY * sqinvnorm.matrix().asDiagonal();
303
304 return result;
305}*/
306
310
313 opt.homo_beta = homo_beta_;
314 opt.rpamin = opt_.rpamin;
315 opt.qpmin = opt_.qpmin;
316 opt.vmin = opt_.vmin;
317 opt.cmax = opt_.cmax;
318
319 A.configure(opt);
320 B.configure(opt);
321
323 << TimeStamp() << " Setup combined UKS full BSE exciton hamiltonian "
324 << flush;
325
326 // Dense fallback for small systems:
327 // this avoids architecture-sensitive Davidson/HAM behavior in tiny
328 // regression tests while keeping the iterative solver for production sizes.
329 constexpr Index dense_threshold = 128;
330
331 if (A.rows() <= dense_threshold) {
333 << TimeStamp()
334 << " Using dense full UKS-BSE solve for small system (dim=" << A.rows()
335 << ")" << flush;
336 std::chrono::time_point<std::chrono::system_clock> start =
337 std::chrono::system_clock::now();
338
339 const Eigen::MatrixXd Ad = A.dense_matrix();
340 const Eigen::MatrixXd Bd = B.dense_matrix();
341 const Index n = Ad.rows();
342
343 Eigen::MatrixXd Hfull = Eigen::MatrixXd::Zero(2 * n, 2 * n);
344 Hfull.topLeftCorner(n, n) = Ad;
345 Hfull.topRightCorner(n, n) = Bd;
346 Hfull.bottomLeftCorner(n, n) = -Bd;
347 Hfull.bottomRightCorner(n, n) = -Ad;
348
349 Eigen::EigenSolver<Eigen::MatrixXd> es(Hfull, true);
350 if (es.info() != Eigen::Success) {
351 throw std::runtime_error("Dense full UKS-BSE diagonalization failed.");
352 }
353
354 using RootInfo =
355 std::tuple<double, Index, double>; // (eval, col, imag_abs)
356 std::vector<RootInfo> positive_roots;
357 positive_roots.reserve(2 * n);
358
359 for (Index i = 0; i < 2 * n; ++i) {
360 const std::complex<double> ev = es.eigenvalues()(i);
361 if (std::abs(ev.imag()) < 1e-8 && ev.real() > 0.0) {
362 positive_roots.emplace_back(ev.real(), i, std::abs(ev.imag()));
363 }
364 }
365
366 std::sort(positive_roots.begin(), positive_roots.end(),
367 [](const RootInfo& a, const RootInfo& b) {
368 return std::get<0>(a) < std::get<0>(b);
369 });
370
371 if (positive_roots.empty()) {
372 throw std::runtime_error(
373 "Dense full UKS-BSE diagonalization produced no positive real "
374 "roots.");
375 }
376
377 const Index nroots =
378 std::min<Index>(opt_.nmax, static_cast<Index>(positive_roots.size()));
379
380 tools::EigenSystem result;
381 result.eigenvalues() = Eigen::VectorXd::Zero(nroots);
382 result.eigenvectors() = Eigen::MatrixXd::Zero(n, nroots);
383 result.eigenvectors2() = Eigen::MatrixXd::Zero(n, nroots);
384
385 for (Index iroot = 0; iroot < nroots; ++iroot) {
386 const Index col = std::get<1>(positive_roots[iroot]);
387 Eigen::VectorXd vec = es.eigenvectors().col(col).real();
388
389 Eigen::VectorXd X = vec.head(n);
390 Eigen::VectorXd Y = vec.tail(n);
391
392 // Deterministic phase convention: make the largest |X_k| positive.
393 Eigen::Index imax = 0;
394 X.cwiseAbs().maxCoeff(&imax);
395 if (X(imax) < 0.0) {
396 X *= -1.0;
397 Y *= -1.0;
398 }
399
400 const double norm = X.squaredNorm() - Y.squaredNorm();
401 if (std::abs(norm) < 1e-12) {
402 throw std::runtime_error(
403 "Dense full UKS-BSE eigenvector has near-zero (X^2-Y^2) norm.");
404 }
405
406 const double invnorm = 1.0 / std::sqrt(std::abs(norm));
407
408 result.eigenvalues()(iroot) = std::get<0>(positive_roots[iroot]);
409 result.eigenvectors().col(iroot) = X * invnorm;
410 result.eigenvectors2().col(iroot) = Y * invnorm;
411 }
412
413 std::chrono::time_point<std::chrono::system_clock> end =
414 std::chrono::system_clock::now();
415 std::chrono::duration<double> elapsed_time = end - start;
416
418 << TimeStamp() << " Dense full UKS-BSE diagonalization done in "
419 << elapsed_time.count() << " secs" << flush;
420
421 return result;
422 }
423
424 // Hermitian (A-B)(A+B) form for larger systems; the non-Hermitian
425 // Davidson below if A-B or A+B is not positive definite.
426 try {
428 fopt.tolerance = DavidsonToleranceValue(opt_.davidson_tolerance);
429 fopt.max_iterations = opt_.davidson_maxiter;
430 fopt.max_subspace = 10 * opt_.nmax;
431 FullBSEDavidson solver(log_, fopt);
432 return solver.Solve(A, B, opt_.nmax);
433 } catch (const std::runtime_error& error) {
435 << TimeStamp() << " " << error.what()
436 << "; using the non-Hermitian Davidson solver" << flush;
437 }
438
439 const Eigen::VectorXd adiag = A.diagonal();
440 const Eigen::VectorXd bdiag = B.diagonal();
441 Eigen::MatrixXd initial_guess =
442 BuildFullBSEXRankedInitialGuess(adiag, bdiag, opt_.nmax);
443
445 B);
446
448 DS.set_correction(opt_.davidson_correction);
449 DS.set_tolerance(opt_.davidson_tolerance);
450 DS.set_size_update(opt_.davidson_update);
451 DS.set_iter_max(opt_.davidson_maxiter);
452 DS.set_max_search_space(10 * opt_.nmax);
453 DS.set_matrix_type("HAM");
454 DS.solve(Hop, opt_.nmax, initial_guess);
455
456 tools::EigenSystem result;
457 result.eigenvalues() = DS.eigenvalues();
458
459 Eigen::MatrixXd tmpX = DS.eigenvectors().topRows(A.rows());
460 Eigen::MatrixXd tmpY = DS.eigenvectors().bottomRows(B.rows());
461
462 if (const Index bad = NormalizeExcitationVectors(tmpX, tmpY); bad > 0) {
464 << TimeStamp() << " WARNING: " << bad
465 << " BSE root(s) without positive X^2 - Y^2 norm (not converged?)"
466 << flush;
467 }
468 result.eigenvectors() = tmpX;
469 result.eigenvectors2() = tmpY;
470
471 return result;
472}
473
475 orb.setTDAApprox(opt_.useTDA);
476 if (opt_.useTDA) {
478 } else {
480 }
482}
483
484template <typename BSE_OPERATOR>
486 std::chrono::time_point<std::chrono::system_clock> start =
487 std::chrono::system_clock::now();
488
489 tools::EigenSystem result;
490
492 DS.set_correction(opt_.davidson_correction);
493 DS.set_tolerance(opt_.davidson_tolerance);
494 DS.set_size_update(opt_.davidson_update);
495 DS.set_iter_max(opt_.davidson_maxiter);
496 DS.set_max_search_space(10 * opt_.nmax);
497 DS.solve(h, opt_.nmax);
498
499 result.eigenvalues() = DS.eigenvalues();
500 result.eigenvectors() = DS.eigenvectors();
501
502 std::chrono::time_point<std::chrono::system_clock> end =
503 std::chrono::system_clock::now();
504 std::chrono::duration<double> elapsed_time = end - start;
505
506 XTP_LOG(Log::info, log_) << TimeStamp() << " Diagonalization done in "
507 << elapsed_time.count() << " secs" << flush;
508
509 return result;
510}
511
512template <typename BSE_OPERATOR_A, typename BSE_OPERATOR_B>
514 BSE_OPERATOR_A& Aop, BSE_OPERATOR_B& Bop) const {
515 std::chrono::time_point<std::chrono::system_clock> start =
516 std::chrono::system_clock::now();
517
519
521 DS.set_correction(opt_.davidson_correction);
522 DS.set_tolerance(opt_.davidson_tolerance);
523 DS.set_size_update(opt_.davidson_update);
524 DS.set_iter_max(opt_.davidson_maxiter);
525 DS.set_max_search_space(10 * opt_.nmax);
526 DS.set_matrix_type("HAM");
527 DS.solve(Hop, opt_.nmax);
528
529 tools::EigenSystem result;
530 result.eigenvalues() = DS.eigenvalues();
531
532 Eigen::MatrixXd tmpX = DS.eigenvectors().topRows(Aop.rows());
533 Eigen::MatrixXd tmpY = DS.eigenvectors().bottomRows(Bop.rows());
534
535 if (const Index bad = NormalizeExcitationVectors(tmpX, tmpY); bad > 0) {
537 << TimeStamp() << " WARNING: " << bad
538 << " BSE root(s) without positive X^2 - Y^2 norm (not converged?)"
539 << flush;
540 }
541 result.eigenvectors() = tmpX;
542 result.eigenvectors2() = tmpY;
543
544 std::chrono::time_point<std::chrono::system_clock> end =
545 std::chrono::system_clock::now();
546 std::chrono::duration<double> elapsed_time = end - start;
547
548 XTP_LOG(Log::info, log_) << TimeStamp() << " Diagonalization done in "
549 << elapsed_time.count() << " secs" << flush;
550
551 return result;
552}
553
554void BSE_UKS::PrintWeightsUKS(const Eigen::VectorXd& weights) const {
555 struct Contribution {
556 double weight;
557 bool is_alpha;
558 Index v;
559 Index c;
560 };
561
562 constexpr Index max_print = 8;
563
564 double alpha_weight = weights.head(alpha_size_).sum();
565 double beta_weight = weights.tail(beta_size_).sum();
566
568 << boost::format(
569 " alpha-sector: %1$+6.2f%% beta-sector: %2$+6.2f%%") %
570 (100.0 * alpha_weight) % (100.0 * beta_weight)
571 << flush;
572
573 std::vector<Contribution> contributions;
574 contributions.reserve(alpha_size_ + beta_size_);
575
576 for (Index i = 0; i < alpha_size_; ++i) {
577 if (std::abs(weights(i)) > opt_.min_print_weight) {
578 contributions.push_back(
579 {weights(i), true, i / alpha_ctotal_, i % alpha_ctotal_});
580 }
581 }
582
583 for (Index i = 0; i < beta_size_; ++i) {
584 const double w = weights(alpha_size_ + i);
585 if (std::abs(w) > opt_.min_print_weight) {
586 contributions.push_back({w, false, i / beta_ctotal_, i % beta_ctotal_});
587 }
588 }
589
590 std::sort(contributions.begin(), contributions.end(),
591 [](const Contribution& a, const Contribution& b) {
592 return std::abs(a.weight) > std::abs(b.weight);
593 });
594
595 const Index nprint =
596 std::min<Index>(max_print, static_cast<Index>(contributions.size()));
597
598 for (Index i = 0; i < nprint; ++i) {
599 const Contribution& c = contributions[i];
600
601 if (c.is_alpha) {
603 << boost::format(
604 " [alpha] HOMO-%1$-3d -> LUMO+%2$-3d : "
605 "%3$+6.2f%%") %
606 (homo_alpha_ - (opt_.vmin + c.v)) %
607 ((homo_alpha_ + 1 + c.c) - (homo_alpha_ + 1)) %
608 (100.0 * c.weight)
609 << flush;
610 } else {
612 << boost::format(
613 " [beta ] HOMO-%1$-3d -> LUMO+%2$-3d : "
614 "%3$+6.2f%%") %
615 (homo_beta_ - (opt_.vmin + c.v)) %
616 ((homo_beta_ + 1 + c.c) - (homo_beta_ + 1)) %
617 (100.0 * c.weight)
618 << flush;
619 }
620 }
621
622 if (static_cast<Index>(contributions.size()) > nprint) {
624 << boost::format(
625 " ... %1$d more contributions above threshold") %
626 (static_cast<Index>(contributions.size()) - nprint)
627 << flush;
628 }
629}
630
632 std::vector<QMFragment<BSE_Population>> fragments,
633 const Orbitals& orb) const {
634 (void)fragments;
635
636 const tools::EigenSystem& es = orb.BSEUKS();
637 const Eigen::VectorXd oscs =
639
640 if (orb.getTDAApprox()) {
642 << " ====== combined UKS TDA exciton energies (eV) ====== " << flush;
643 } else {
645 << " ====== combined UKS full-BSE exciton energies (eV) ====== "
646 << flush;
647 }
648
649 for (Index i = 0; i < std::min<Index>(opt_.nmax, es.eigenvalues().size());
650 ++i) {
651 XTP_LOG(Log::error, log_) << boost::format(" XU%-4d %+1.6f") % (i + 1) %
653 << flush;
654
655 const Eigen::Vector3d& trdip = orb.TransitionDipoles()[i];
656 const double osc = (i < oscs.size()) ? oscs(i) : 0.0;
657
659 << boost::format(
660 " TrDipole length gauge[e*bohr] dx = %1$+1.4f dy = "
661 "%2$+1.4f dz = %3$+1.4f |d|^2 = %4$+1.4f f = %5$+1.4f") %
662 trdip[0] % trdip[1] % trdip[2] % (trdip.squaredNorm()) % osc
663 << flush;
664
665 Eigen::VectorXd weights = es.eigenvectors().col(i).cwiseAbs2();
666 if (!orb.getTDAApprox()) {
667 weights -= es.eigenvectors2().col(i).cwiseAbs2();
668 }
669
670 PrintWeightsUKS(weights);
671 }
672}
673
675
676 const tools::EigenSystem& BSECoefs = orb.BSEUKS();
677 const Eigen::VectorXd& BSEenergies = BSECoefs.eigenvalues();
678
679 const Eigen::VectorXd& RPAInputEnergiesAlpha = orb.RPAInputEnergiesAlpha();
680 const Eigen::VectorXd& RPAInputEnergiesBeta = orb.RPAInputEnergiesBeta();
681
682 // Static reference contribution of the direct kernel.
683 SetupDirectInteractionOperator(RPAInputEnergiesAlpha, RPAInputEnergiesBeta,
684 0.0);
685
687 configureBSEOperator(Hd_static);
688
689 ExpectationValues expectation_values =
690 ExpectationValue_Operator(orb, Hd_static);
691 Eigen::VectorXd Hd_static_contribution = expectation_values.direct_term;
692
693 if (!orb.getTDAApprox()) {
695 configureBSEOperator(Hd2_static);
696 expectation_values = ExpectationValue_Operator(orb, Hd2_static);
697 Hd_static_contribution += expectation_values.cross_term;
698 }
699
700 Eigen::VectorXd BSEenergies_dynamic = BSEenergies;
701
702 for (Index i_exc = 0; i_exc < BSEenergies.size(); ++i_exc) {
704 << "Dynamical Screening UKS BSE, Excitation " << i_exc << " static "
705 << BSEenergies(i_exc) << flush;
706
707 for (Index iter = 0; iter < opt_.max_dyn_iter; ++iter) {
708 const double old_energy = BSEenergies_dynamic(i_exc);
709
710 SetupDirectInteractionOperator(RPAInputEnergiesAlpha,
711 RPAInputEnergiesBeta, old_energy);
712
714 configureBSEOperator(Hd_dyn);
715
716 expectation_values = ExpectationValue_Operator_State(i_exc, orb, Hd_dyn);
717 Eigen::VectorXd Hd_dynamic_contribution = expectation_values.direct_term;
718
719 if (!orb.getTDAApprox()) {
721 configureBSEOperator(Hd2_dyn);
722 expectation_values =
723 ExpectationValue_Operator_State(i_exc, orb, Hd2_dyn);
724 Hd_dynamic_contribution += expectation_values.cross_term;
725 }
726
727 BSEenergies_dynamic(i_exc) = BSEenergies(i_exc) +
728 Hd_static_contribution(i_exc) -
729 Hd_dynamic_contribution(0);
730
731 XTP_LOG(Log::info, log_) << "Dynamical Screening UKS BSE, excitation "
732 << i_exc << " iteration " << iter << " dynamic "
733 << BSEenergies_dynamic(i_exc) << flush;
734
735 if (std::abs(BSEenergies_dynamic(i_exc) - old_energy) <
736 opt_.dyn_tolerance) {
737 break;
738 }
739 }
740 }
741
742 orb.BSEUKS_dynamic() = BSEenergies_dynamic;
743
744 const double hrt2ev = tools::conv::hrt2ev;
745 const bool has_dipoles = orb.hasTransitionDipoles();
746
747 Eigen::VectorXd oscs = Eigen::VectorXd::Zero(0);
748 if (has_dipoles) {
750 }
751
752 if (orb.getTDAApprox()) {
754 << " ====== combined UKS TDA exciton energies with perturbative "
755 "dynamical screening (eV) ====== "
756 << flush;
757 } else {
759 << " ====== combined UKS full-BSE exciton energies with perturbative "
760 "dynamical screening (eV) ====== "
761 << flush;
762 }
763
764 const Index nprint = std::min<Index>(opt_.nmax, BSEenergies_dynamic.size());
765
766 for (Index i = 0; i < nprint; ++i) {
767 double osc = 0.0;
768 if (has_dipoles && i < oscs.size()) {
769 osc = oscs(i) * BSEenergies_dynamic(i) / BSEenergies(i);
770 }
771
773 << boost::format(
774 " XU(dynamic) = %1$4d Omega = %2$+1.12f eV lambda = %3$+3.2f "
775 "nm"
776 " f = %4$+1.4f") %
777 (i + 1) % (hrt2ev * BSEenergies_dynamic(i)) %
778 (1240.0 / (hrt2ev * BSEenergies_dynamic(i))) % osc
779 << flush;
780 }
781}
782
785
789
795 HdUKSOperator&) const;
797 Hd2UKSOperator&) const;
798
801 const Orbitals&, const ExcitonUKSOperator_TDA&) const;
804 const Orbitals&, const ExcitonUKSOperator_BTDA_B&) const;
806 HdUKSOperator>(const Orbitals&, const HdUKSOperator&) const;
808 Hd2UKSOperator>(const Orbitals&, const Hd2UKSOperator&) const;
809
812 Index, const Orbitals&, const ExcitonUKSOperator_TDA&) const;
815 Index, const Orbitals&, const ExcitonUKSOperator_BTDA_B&) const;
817 HdUKSOperator>(Index, const Orbitals&, const HdUKSOperator&) const;
819 Hd2UKSOperator>(Index, const Orbitals&, const Hd2UKSOperator&) const;
820
821} // namespace xtp
822} // namespace votca
const Eigen::MatrixXd & eigenvectors2() const
Definition eigensystem.h:36
const Eigen::VectorXd & eigenvalues() const
Definition eigensystem.h:30
const Eigen::MatrixXd & eigenvectors() const
Definition eigensystem.h:33
Eigen::VectorXd diagonal() const override
void configure(BSEOperatorUKS_Options opt)
Eigen::MatrixXd dense_matrix() const
const TCMatrix_gwbse_spin & Mmn_raw_
Definition bse_uks.h:80
ExpectationValues ExpectationValue_Operator_State(Index state, const Orbitals &orb, const BSE_OPERATOR &H) const
Definition bse_uks.cc:211
tools::EigenSystem solve_hermitian(BSE_OPERATOR &h) const
Definition bse_uks.cc:485
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
TCMatrix_gwbse_spin Mmn_
Definition bse_uks.h:81
void configureBSEOperator(BSE_OPERATOR &H) const
Definition bse_uks.cc:175
void PrintWeightsUKS(const Eigen::VectorXd &coeffs) const
Definition bse_uks.cc:554
tools::EigenSystem Solve_nonhermitian_Davidson(BSE_OPERATOR_A &Aop, BSE_OPERATOR_B &Bop) const
Definition bse_uks.cc:513
Eigen::MatrixXd Hqp_beta_
Definition bse_uks.h:96
Eigen::VectorXd epsilon_0_inv_
Definition bse_uks.h:94
void Perturbative_DynamicalScreening(Orbitals &orb)
Definition bse_uks.cc:674
ExpectationValues ExpectationValue_Operator(const Orbitals &orb, const BSE_OPERATOR &H) const
Definition bse_uks.cc:187
Eigen::MatrixXd AdjustHqpSize(const Eigen::MatrixXd &Hqp_in, const Eigen::VectorXd &RPAInputEnergies, Index homo) const
Definition bse_uks.cc:108
tools::EigenSystem Solve_excitons_uks_BTDA() const
Definition bse_uks.cc:307
void SetupDirectInteractionOperator(const Eigen::VectorXd &RPAInputEnergiesAlpha, const Eigen::VectorXd &RPAInputEnergiesBeta, double energy)
Definition bse_uks.cc:151
void Solve_excitons_uks(Orbitals &orb) const
Definition bse_uks.cc:474
Eigen::MatrixXd Hqp_alpha_
Definition bse_uks.h:95
void Analyze_excitons_uks(std::vector< QMFragment< BSE_Population > > fragments, const Orbitals &orb) const
Definition bse_uks.cc:631
tools::EigenSystem Solve_excitons_uks_TDA() const
Definition bse_uks.cc:234
Use Davidson algorithm to solve A*V=E*V.
void set_max_search_space(Index N)
Eigen::VectorXd eigenvalues() const
void solve(const MatrixReplacement &A, Index neigen, Index size_initial_guess=0)
void set_matrix_type(std::string mt)
void set_correction(std::string method)
void set_size_update(std::string update_size)
void set_tolerance(std::string tol)
Eigen::MatrixXd eigenvectors() const
Lowest roots of the full (non-TDA) BSE.
tools::EigenSystem Solve(const OpA &A, const OpB &B, Index nroots)
Container for molecular orbitals and derived one-particle data.
Definition orbitals.h:47
const Eigen::VectorXd & BSEUKS_dynamic() const
Definition orbitals.h:761
void CalcCoupledTransition_Dipoles()
Definition orbitals.cc:774
bool getTDAApprox() const
Return whether the Tamm-Dancoff approximation is enabled.
Definition orbitals.h:402
const tools::EigenSystem & BSEUKS() const
Definition orbitals.h:755
Eigen::VectorXd Oscillatorstrengths() const
Definition orbitals.cc:657
bool hasTransitionDipoles() const
Report whether transition dipole moments have been computed.
Definition orbitals.h:535
const std::vector< Eigen::Vector3d > & TransitionDipoles() const
Return the stored transition dipole moments.
Definition orbitals.h:540
const Eigen::VectorXd & RPAInputEnergiesBeta() const
Definition orbitals.h:723
void setTDAApprox(bool usedTDA)
Enable or disable the Tamm-Dancoff approximation flag.
Definition orbitals.h:400
const Eigen::VectorXd & RPAInputEnergiesAlpha() const
Definition orbitals.h:720
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
Timestamp returns the current time as a string Example: cout << TimeStamp().
Definition logger.h:224
#define XTP_LOG(level, log)
Definition logger.h:40
const double hrt2ev
Definition constants.h:53
Charge transport classes.
Definition ERIs.h:28
BSE_OPERATOR_UKS< 0, 1, 0, 1 > ExcitonUKSOperator_BTDA_B
Index NormalizeExcitationVectors(Eigen::MatrixXd &X, Eigen::MatrixXd &Y)
BSE_OPERATOR_UKS< 1, 1, 1, 0 > ExcitonUKSOperator_TDA
Eigen::VectorXd ExpValue(const Eigen::MatrixXd &state1, OP OPxstate2)
Definition bse.cc:708
SymmetricEigenSystem SymmetricEigen(const Eigen::MatrixXd &A, bool vectors=true)
With vectors = false only the eigenvalues are computed.
BSE_OPERATOR_UKS< 0, 0, 0, 1 > Hd2UKSOperator
Eigen::MatrixXd BuildFullBSEXRankedInitialGuess(const Eigen::VectorXd &adiag, const Eigen::VectorXd &bdiag, Index nroots)
Build an initial guess for full-BSE Davidson diagonalization.
BSE_OPERATOR_UKS< 0, 0, 1, 0 > HdUKSOperator
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26