37Eigen::VectorXd
ExpValue(
const Eigen::MatrixXd& state1, OP OPxstate2) {
38 return state1.cwiseProduct(OPxstate2.eval()).colwise().sum().transpose();
41Eigen::VectorXd
ExpValue(
const Eigen::MatrixXd& state1,
42 const Eigen::MatrixXd& OPxstate2) {
43 return state1.cwiseProduct(OPxstate2).colwise().sum().transpose();
53 const Eigen::VectorXd& RPAInputEnergiesAlpha,
54 const Eigen::VectorXd& RPAInputEnergiesBeta,
55 const Eigen::MatrixXd& Hqp_alpha_in,
const Eigen::MatrixXd& Hqp_beta_in,
56 const Eigen::VectorXd& epsilon_0_inv,
57 const Eigen::MatrixXd& epsilon_eigenvectors) {
71 if (
opt_.use_Hqp_offdiag) {
86 Mmn_.alpha.MultiplyRightWithAuxMatrix(epsilon_eigenvectors);
87 Mmn_.beta.MultiplyRightWithAuxMatrix(epsilon_eigenvectors);
93 const Eigen::VectorXd& RPAInputEnergies,
96 Index bse_ctotal =
opt_.cmax - (homo + 1) + 1;
97 Index hqp_size = bse_vtotal + bse_ctotal;
100 Eigen::MatrixXd Hqp_BSE = Eigen::MatrixXd::Zero(hqp_size, hqp_size);
105 Hqp_BSE = Hqp.block(start, start, hqp_size, hqp_size);
107 Index virtoffset = gwsize - start;
108 Hqp_BSE.topLeftCorner(virtoffset, virtoffset) =
109 Hqp.block(start, start, virtoffset, virtoffset);
112 Hqp_BSE.diagonal().tail(virt_extra) =
113 RPAInputEnergies.segment(RPAoffset + virtoffset, virt_extra);
119 Hqp_BSE.diagonal().head(occ_extra) =
120 RPAInputEnergies.segment(RPAoffset, occ_extra);
122 Hqp_BSE.block(occ_extra, occ_extra, gwsize, gwsize) = Hqp;
125 Index virtoffset = occ_extra + gwsize;
127 Hqp_BSE.diagonal().tail(virt_extra) =
128 RPAInputEnergies.segment(RPAoffset + virtoffset, virt_extra);
136 const Eigen::VectorXd& RPAInputEnergiesAlpha,
137 const Eigen::VectorXd& RPAInputEnergiesBeta,
double energy) {
143 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(
147 Mmn_.alpha.MultiplyRightWithAuxMatrix(es.eigenvectors());
148 Mmn_.beta.MultiplyRightWithAuxMatrix(es.eigenvectors());
151 for (
Index i = 0; i < es.eigenvalues().size(); ++i) {
152 if (es.eigenvalues()(i) > 1
e-8) {
158template <
typename BSE_OPERATOR>
170template <
typename BSE_OPERATOR>
188 expectation_values.
cross_term = Eigen::VectorXd::Zero(0);
191 return expectation_values;
194template <
typename BSE_OPERATOR>
202 const Eigen::MatrixXd Xstate = BSECoefs.
eigenvectors().col(state);
203 const Eigen::MatrixXd temp =
H * Xstate;
208 const Eigen::MatrixXd Ystate = BSECoefs.
eigenvectors2().col(state);
212 expectation_values.
cross_term = Eigen::VectorXd::Zero(0);
215 return expectation_values;
230 <<
TimeStamp() <<
" Setup combined UKS TDA Hamiltonian " << flush;
307 <<
TimeStamp() <<
" Setup combined UKS full BSE exciton hamiltonian "
313 constexpr Index dense_threshold = 128;
315 if (A.
rows() <= dense_threshold) {
318 <<
" Using dense full UKS-BSE solve for small system (dim=" << A.
rows()
320 std::chrono::time_point<std::chrono::system_clock> start =
321 std::chrono::system_clock::now();
325 const Index n = Ad.rows();
327 Eigen::MatrixXd Hfull = Eigen::MatrixXd::Zero(2 * n, 2 * n);
328 Hfull.topLeftCorner(n, n) = Ad;
329 Hfull.topRightCorner(n, n) = Bd;
330 Hfull.bottomLeftCorner(n, n) = -Bd;
331 Hfull.bottomRightCorner(n, n) = -Ad;
333 Eigen::EigenSolver<Eigen::MatrixXd> es(Hfull,
true);
334 if (es.info() != Eigen::Success) {
335 throw std::runtime_error(
"Dense full UKS-BSE diagonalization failed.");
339 std::tuple<double, Index, double>;
340 std::vector<RootInfo> positive_roots;
341 positive_roots.reserve(2 * n);
343 for (
Index i = 0; i < 2 * n; ++i) {
344 const std::complex<double> ev = es.eigenvalues()(i);
345 if (std::abs(ev.imag()) < 1
e-8 && ev.real() > 0.0) {
346 positive_roots.emplace_back(ev.real(), i, std::abs(ev.imag()));
350 std::sort(positive_roots.begin(), positive_roots.end(),
351 [](
const RootInfo& a,
const RootInfo& b) {
352 return std::get<0>(a) < std::get<0>(b);
355 if (positive_roots.empty()) {
356 throw std::runtime_error(
357 "Dense full UKS-BSE diagonalization produced no positive real "
362 std::min<Index>(
opt_.nmax,
static_cast<Index>(positive_roots.size()));
365 result.
eigenvalues() = Eigen::VectorXd::Zero(nroots);
366 result.
eigenvectors() = Eigen::MatrixXd::Zero(n, nroots);
369 for (
Index iroot = 0; iroot < nroots; ++iroot) {
370 const Index col = std::get<1>(positive_roots[iroot]);
371 Eigen::VectorXd vec = es.eigenvectors().col(col).real();
373 Eigen::VectorXd X = vec.head(n);
374 Eigen::VectorXd Y = vec.tail(n);
377 Eigen::Index imax = 0;
378 X.cwiseAbs().maxCoeff(&imax);
384 const double norm = X.squaredNorm() - Y.squaredNorm();
385 if (std::abs(norm) < 1
e-12) {
386 throw std::runtime_error(
387 "Dense full UKS-BSE eigenvector has near-zero (X^2-Y^2) norm.");
390 const double invnorm = 1.0 / std::sqrt(std::abs(norm));
392 result.
eigenvalues()(iroot) = std::get<0>(positive_roots[iroot]);
397 std::chrono::time_point<std::chrono::system_clock> end =
398 std::chrono::system_clock::now();
399 std::chrono::duration<double> elapsed_time = end - start;
402 <<
TimeStamp() <<
" Dense full UKS-BSE diagonalization done in "
403 << elapsed_time.count() <<
" secs" << flush;
409 const Eigen::VectorXd adiag = A.
diagonal();
410 const Eigen::VectorXd bdiag = B.
diagonal();
411 Eigen::MatrixXd initial_guess =
432 Eigen::VectorXd normX = tmpX.colwise().squaredNorm();
433 Eigen::VectorXd normY = tmpY.colwise().squaredNorm();
434 Eigen::ArrayXd sqinvnorm = (normX - normY).array().inverse().cwiseSqrt();
436 result.
eigenvectors() = tmpX * sqinvnorm.matrix().asDiagonal();
437 result.
eigenvectors2() = tmpY * sqinvnorm.matrix().asDiagonal();
452template <
typename BSE_OPERATOR>
454 std::chrono::time_point<std::chrono::system_clock> start =
455 std::chrono::system_clock::now();
470 std::chrono::time_point<std::chrono::system_clock> end =
471 std::chrono::system_clock::now();
472 std::chrono::duration<double> elapsed_time = end - start;
475 << elapsed_time.count() <<
" secs" << flush;
480template <
typename BSE_OPERATOR_A,
typename BSE_OPERATOR_B>
482 BSE_OPERATOR_A& Aop, BSE_OPERATOR_B& Bop)
const {
483 std::chrono::time_point<std::chrono::system_clock> start =
484 std::chrono::system_clock::now();
500 Eigen::MatrixXd tmpX = DS.
eigenvectors().topRows(Aop.rows());
501 Eigen::MatrixXd tmpY = DS.
eigenvectors().bottomRows(Bop.rows());
503 Eigen::VectorXd normX = tmpX.colwise().squaredNorm();
504 Eigen::VectorXd normY = tmpY.colwise().squaredNorm();
505 Eigen::ArrayXd sqinvnorm = (normX - normY).array().inverse().cwiseSqrt();
507 result.
eigenvectors() = tmpX * sqinvnorm.matrix().asDiagonal();
508 result.
eigenvectors2() = tmpY * sqinvnorm.matrix().asDiagonal();
510 std::chrono::time_point<std::chrono::system_clock> end =
511 std::chrono::system_clock::now();
512 std::chrono::duration<double> elapsed_time = end - start;
515 << elapsed_time.count() <<
" secs" << flush;
521 struct Contribution {
528 constexpr Index max_print = 8;
530 double alpha_weight = weights.head(
alpha_size_).sum();
531 double beta_weight = weights.tail(
beta_size_).sum();
535 " alpha-sector: %1$+6.2f%% beta-sector: %2$+6.2f%%") %
536 (100.0 * alpha_weight) % (100.0 * beta_weight)
539 std::vector<Contribution> contributions;
543 if (std::abs(weights(i)) >
opt_.min_print_weight) {
544 contributions.push_back(
551 if (std::abs(w) >
opt_.min_print_weight) {
556 std::sort(contributions.begin(), contributions.end(),
557 [](
const Contribution& a,
const Contribution& b) {
558 return std::abs(a.weight) > std::abs(b.weight);
562 std::min<Index>(max_print,
static_cast<Index>(contributions.size()));
564 for (
Index i = 0; i < nprint; ++i) {
565 const Contribution& c = contributions[i];
570 " [alpha] HOMO-%1$-3d -> LUMO+%2$-3d : "
579 " [beta ] HOMO-%1$-3d -> LUMO+%2$-3d : "
588 if (
static_cast<Index>(contributions.size()) > nprint) {
591 " ... %1$d more contributions above threshold") %
592 (
static_cast<Index>(contributions.size()) - nprint)
603 const Eigen::VectorXd oscs =
608 <<
" ====== combined UKS TDA exciton energies (eV) ====== " << flush;
611 <<
" ====== combined UKS full-BSE exciton energies (eV) ====== "
622 const double osc = (i < oscs.size()) ? oscs(i) : 0.0;
626 " TrDipole length gauge[e*bohr] dx = %1$+1.4f dy = "
627 "%2$+1.4f dz = %3$+1.4f |d|^2 = %4$+1.4f f = %5$+1.4f") %
628 trdip[0] % trdip[1] % trdip[2] % (trdip.squaredNorm()) % osc
631 Eigen::VectorXd weights = es.
eigenvectors().col(i).cwiseAbs2();
643 const Eigen::VectorXd& BSEenergies = BSECoefs.
eigenvalues();
657 Eigen::VectorXd Hd_static_contribution = expectation_values.
direct_term;
663 Hd_static_contribution += expectation_values.
cross_term;
666 Eigen::VectorXd BSEenergies_dynamic = BSEenergies;
668 for (
Index i_exc = 0; i_exc < BSEenergies.size(); ++i_exc) {
670 <<
"Dynamical Screening UKS BSE, Excitation " << i_exc <<
" static "
671 << BSEenergies(i_exc) << flush;
673 for (
Index iter = 0; iter <
opt_.max_dyn_iter; ++iter) {
674 const double old_energy = BSEenergies_dynamic(i_exc);
677 RPAInputEnergiesBeta, old_energy);
683 Eigen::VectorXd Hd_dynamic_contribution = expectation_values.
direct_term;
690 Hd_dynamic_contribution += expectation_values.
cross_term;
693 BSEenergies_dynamic(i_exc) = BSEenergies(i_exc) +
694 Hd_static_contribution(i_exc) -
695 Hd_dynamic_contribution(0);
698 << i_exc <<
" iteration " << iter <<
" dynamic "
699 << BSEenergies_dynamic(i_exc) << flush;
701 if (std::abs(BSEenergies_dynamic(i_exc) - old_energy) <
702 opt_.dyn_tolerance) {
713 Eigen::VectorXd oscs = Eigen::VectorXd::Zero(0);
720 <<
" ====== combined UKS TDA exciton energies with perturbative "
721 "dynamical screening (eV) ====== "
725 <<
" ====== combined UKS full-BSE exciton energies with perturbative "
726 "dynamical screening (eV) ====== "
730 const Index nprint = std::min<Index>(
opt_.nmax, BSEenergies_dynamic.size());
732 for (
Index i = 0; i < nprint; ++i) {
734 if (has_dipoles && i < oscs.size()) {
735 osc = oscs(i) * BSEenergies_dynamic(i) / BSEenergies(i);
740 " XU(dynamic) = %1$4d Omega = %2$+1.12f eV lambda = %3$+3.2f "
743 (i + 1) % (hrt2ev * BSEenergies_dynamic(i)) %
744 (1240.0 / (hrt2ev * BSEenergies_dynamic(i))) % osc
Eigen::VectorXd diagonal() const override
void configure(BSEOperatorUKS_Options opt)
Eigen::MatrixXd dense_matrix() const
const TCMatrix_gwbse_spin & Mmn_raw_
ExpectationValues ExpectationValue_Operator_State(Index state, const Orbitals &orb, const BSE_OPERATOR &H) const
tools::EigenSystem solve_hermitian(BSE_OPERATOR &h) const
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)
void configureBSEOperator(BSE_OPERATOR &H) const
void PrintWeightsUKS(const Eigen::VectorXd &coeffs) const
tools::EigenSystem Solve_nonhermitian_Davidson(BSE_OPERATOR_A &Aop, BSE_OPERATOR_B &Bop) const
Eigen::MatrixXd Hqp_beta_
Eigen::VectorXd epsilon_0_inv_
void Perturbative_DynamicalScreening(Orbitals &orb)
ExpectationValues ExpectationValue_Operator(const Orbitals &orb, const BSE_OPERATOR &H) const
Eigen::MatrixXd AdjustHqpSize(const Eigen::MatrixXd &Hqp_in, const Eigen::VectorXd &RPAInputEnergies, Index homo) const
tools::EigenSystem Solve_excitons_uks_BTDA() const
void SetupDirectInteractionOperator(const Eigen::VectorXd &RPAInputEnergiesAlpha, const Eigen::VectorXd &RPAInputEnergiesBeta, double energy)
void Solve_excitons_uks(Orbitals &orb) const
Eigen::MatrixXd Hqp_alpha_
void Analyze_excitons_uks(std::vector< QMFragment< BSE_Population > > fragments, const Orbitals &orb) const
tools::EigenSystem Solve_excitons_uks_TDA() const
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_iter_max(Index N)
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
Container for molecular orbitals and derived one-particle data.
const Eigen::VectorXd & BSEUKS_dynamic() const
void CalcCoupledTransition_Dipoles()
bool getTDAApprox() const
Return whether the Tamm-Dancoff approximation is enabled.
const tools::EigenSystem & BSEUKS() const
Eigen::VectorXd Oscillatorstrengths() const
bool hasTransitionDipoles() const
Report whether transition dipole moments have been computed.
const std::vector< Eigen::Vector3d > & TransitionDipoles() const
Return the stored transition dipole moments.
const Eigen::VectorXd & RPAInputEnergiesBeta() const
void setTDAApprox(bool usedTDA)
Enable or disable the Tamm-Dancoff approximation flag.
const Eigen::VectorXd & RPAInputEnergiesAlpha() const
Unrestricted RPA helper for spin-resolved GW screening.
void setRPAInputEnergies(const Eigen::VectorXd &rpaenergies_alpha, const Eigen::VectorXd &rpaenergies_beta)
Set alpha and beta RPA input energies directly.
void configure(Index homo_alpha, Index homo_beta, Index rpamin, Index rpamax)
Configure orbital window and spin-resolved HOMO indices.
Eigen::MatrixXd calculate_epsilon_r(double frequency) const
Dielectric matrix on the real frequency axis.
Timestamp returns the current time as a string Example: cout << TimeStamp()
#define XTP_LOG(level, log)
Charge transport classes.
BSE_OPERATOR_UKS< 0, 1, 0, 1 > ExcitonUKSOperator_BTDA_B
BSE_OPERATOR_UKS< 1, 1, 1, 0 > ExcitonUKSOperator_TDA
Eigen::VectorXd ExpValue(const Eigen::MatrixXd &state1, OP OPxstate2)
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.
Eigen::VectorXd cross_term
Eigen::VectorXd direct_term