votca 2026-dev
Loading...
Searching...
No Matches
orbitals.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2023 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// Standard includes
21#include <cstdio>
22#include <fstream>
23#include <iomanip>
24#include <numeric>
25
26// Local VOTCA includes
27#include "votca/tools/version.h"
28#include "votca/xtp/aomatrix.h"
29#include "votca/xtp/orbitals.h"
31#include "votca/xtp/qmstate.h"
32#include "votca/xtp/vc2index.h"
33
34namespace votca {
35namespace xtp {
36
45
46Orbitals::Orbitals() : atoms_("", 0) { ; }
47
56std::vector<Index> Orbitals::CheckDegeneracy(Index level,
57 double energy_difference) const {
58 if (this->hasUnrestrictedOrbitals()) {
59 throw std::runtime_error(
60 "Checking degeneracy not implemented for open-shell systems.");
61 }
62 std::vector<Index> result;
63 if (level > mos_.eigenvalues().size()) {
64 throw std::runtime_error(
65 "Level for degeneracy is higher than maximum level");
66 }
67 double MOEnergyLevel = mos_.eigenvalues()(level);
68
69 for (Index i = 0; i < mos_.eigenvalues().size(); ++i) {
70 if (std::abs(mos_.eigenvalues()(i) - MOEnergyLevel) < energy_difference) {
71 result.push_back(i);
72 }
73 }
74
75 if (result.empty()) {
76 result.push_back(level);
77 }
78 return result;
79}
80
81std::vector<Index> Orbitals::SortEnergies() {
82 if (this->hasUnrestrictedOrbitals()) {
83 throw std::runtime_error(
84 "MO sorting not implemented for open-shell systems.");
85 }
86 std::vector<Index> index = std::vector<Index>(mos_.eigenvalues().size());
87 std::iota(index.begin(), index.end(), 0);
88 std::stable_sort(index.begin(), index.end(), [this](Index i1, Index i2) {
89 return this->MOs().eigenvalues()[i1] < this->MOs().eigenvalues()[i2];
90 });
91 return index;
92}
93
99void Orbitals::SetupDftBasis(std::string basis_name) {
100 if (this->QMAtoms().size() == 0) {
101 throw std::runtime_error("Can't setup AOBasis without atoms");
102 }
103 BasisSet bs;
104 bs.Load(basis_name);
105 dftbasis_.Fill(bs, this->QMAtoms());
106}
107
108void Orbitals::SetupAuxBasis(std::string aux_basis_name) {
109 if (this->QMAtoms().size() == 0) {
110 throw std::runtime_error("Can't setup Aux AOBasis without atoms");
111 }
112 BasisSet bs;
113 bs.Load(aux_basis_name);
114 auxbasis_.Fill(bs, this->QMAtoms());
115}
116
117// Return the density change relative to the ground state. For excited states
118// this corresponds to the response density Delta P, whereas for charged states
119// it returns the projector that must be added to or removed from the ground-
120// state density.
121Eigen::MatrixXd Orbitals::DensityMatrixWithoutGS(const QMState& state) const {
122 if (this->hasUnrestrictedOrbitals() &&
123 state.Type() != QMStateType::ExcitonUKS) {
124 throw std::runtime_error(
125 "DensityMatrixWithoutGS not implemented for open-shell systems "
126 "except ExcitonUKS.");
127 }
128 if (state.Type().isExciton()) {
129 std::array<Eigen::MatrixXd, 2> DMAT = DensityMatrixExcitedState(state);
130 return DMAT[1] - DMAT[0];
131 } else if (state.Type().isKSState() || state.Type().isPQPState()) {
132 return DensityMatrixKSstate(state);
133 } else if (state.Type() == QMStateType::DQPstate) {
134 Eigen::MatrixXd DMATQP = DensityMatrixQuasiParticle(state);
135 if (state.StateIdx() > getHomo()) {
136 return DMATQP;
137 } else {
138 return -DMATQP;
139 }
140 } else {
141 throw std::runtime_error(
142 "DensityMatrixWithoutGS does not yet implement QMStateType:" +
143 state.Type().ToLongString());
144 }
145}
146
147// Return the full AO density associated with the requested state. Depending
148// on the state type this is the ground-state density with the corresponding
149// particle, hole, or excitonic correction added on top.
150Eigen::MatrixXd Orbitals::DensityMatrixFull(const QMState& state) const {
151 if (state.isTransition()) {
152 return this->TransitionDensityMatrix(state);
153 }
154 Eigen::MatrixXd result = this->DensityMatrixGroundState();
155
156 if (getCalculationType() != "NoEmbedding") {
157 result += getInactiveDensity();
158 }
159 if (state.Type().isExciton()) {
160 std::array<Eigen::MatrixXd, 2> DMAT = DensityMatrixExcitedState(state);
161 result = result - DMAT[0] + DMAT[1]; // Ground state + hole_contribution +
162 // electron contribution
163 } else if (state.Type() == QMStateType::DQPstate) {
164 Eigen::MatrixXd DMATQP = DensityMatrixQuasiParticle(state);
165 if (state.StateIdx() > getHomo()) {
166 result += DMATQP;
167 } else {
168 result -= DMATQP;
169 }
170 } else if (state.Type() == QMStateType::KSstate ||
171 state.Type() == QMStateType::PQPstate) {
172 Eigen::MatrixXd DMATKS = DensityMatrixKSstate(state);
173 if (state.StateIdx() <= getHomo()) {
174 result -= DMATKS;
175 } else {
176 result += DMATKS;
177 }
178 } else if (state.Type() == QMStateType::Hole ||
179 state.Type() == QMStateType::Electron) {
180 if (!this->hasUnrestrictedOrbitals()) {
181 throw std::runtime_error("QMStateType:" + state.Type().ToLongString() +
182 " requires an openshell calculation");
183 }
184 } else if (state.Type() != QMStateType::Gstate) {
185 throw std::runtime_error(
186 "DensityMatrixFull does not yet implement QMStateType:" +
187 state.Type().ToLongString());
188 }
189 return result;
190}
191
192// Determine ground state density matrix
193/*Eigen::MatrixXd Orbitals::DensityMatrixGroundState() const {
194 if (!hasMOs()) {
195 throw std::runtime_error("Orbitals file does not contain MO coefficients");
196 }
197 Eigen::MatrixXd occstates = mos_.eigenvectors().leftCols(occupied_levels_);
198 Eigen::MatrixXd dmatGS = occstates * occstates.transpose();
199 if (this->hasUnrestrictedOrbitals()) {
200 Eigen::MatrixXd occstates_beta =
201 mos_beta_.eigenvectors().leftCols(occupied_levels_beta_);
202 dmatGS += occstates_beta * occstates_beta.transpose();
203 } else {
204 dmatGS *= 2.0;
205 }
206 return dmatGS;
207}*/
208
209// Ground-state one-particle density in the AO basis.
210//
211// For unrestricted calculations the total density is assembled as
212//
213// P = P^alpha + P^beta,
214//
215// with P^sigma = C_occ^sigma (C_occ^sigma)^T.
216//
217// For restricted cases the spin-resolved helper reconstructs the same object
218// from the shared spatial orbitals together with the stored alpha/beta
219// occupation pattern.
220Eigen::MatrixXd Orbitals::DensityMatrixGroundState() const {
222 return spin[0] + spin[1];
223}
224
225// Density matrix for a single KS orbital
226// One-orbital projector in the AO basis,
227//
228// P^(k)_(mu,nu) = C_(mu,k) C_(nu,k),
229//
230// used for adding or removing a single KS occupation relative to the ground
231// state.
232Eigen::MatrixXd Orbitals::DensityMatrixKSstate(const QMState& state) const {
233 if (this->hasUnrestrictedOrbitals()) {
234 throw std::runtime_error(
235 "DensityMatrixKSstate not implemented for open-shell systems.");
236 }
237 if (!hasMOs()) {
238 throw std::runtime_error("Orbitals file does not contain MO coefficients");
239 }
240 if (state.Type() != QMStateType::KSstate &&
241 state.Type() != QMStateType::PQPstate) {
242 throw std::runtime_error("State:" + state.ToString() +
243 " is not a Kohn Sham state");
244 }
245 Eigen::VectorXd KSstate = mos_.eigenvectors().col(state.StateIdx());
246 Eigen::MatrixXd dmatKS = KSstate * KSstate.transpose();
247 return dmatKS;
248}
249
251 if (!hasQPdiag()) {
252 throw std::runtime_error("Orbitals file does not contain QP coefficients");
253 }
254 return mos_.eigenvectors().middleCols(qpmin_, qpmax_ - qpmin_ + 1) *
255 QPdiag_.eigenvectors();
256}
257
258// Determine QuasiParticle Density Matrix
259// Quasiparticle projector in the AO basis,
260//
261// P^QP_(mu,nu) = Lambda_(mu,n) Lambda_(nu,n),
262//
263// where Lambda is the AO representation of the quasiparticle eigenvectors in
264// the selected GW subspace.
266 const QMState& state) const {
267 if (this->hasUnrestrictedOrbitals()) {
268 throw std::runtime_error(
269 "DensityMatrixQuasiParticle not implemented for open-shell systems.");
270 }
271 if (state.Type() != QMStateType::DQPstate) {
272 throw std::runtime_error("State:" + state.ToString() +
273 " is not a quasiparticle state");
274 }
275 Eigen::MatrixXd lambda = CalculateQParticleAORepresentation();
276 Eigen::MatrixXd dmatQP = lambda.col(state.StateIdx() - qpmin_) *
277 lambda.col(state.StateIdx() - qpmin_).transpose();
278 return dmatQP;
279}
280
281// Electric dipole moment relative to the molecular origin,
282//
283// mu = sum_A Z_A (R_A - R_0) - Tr[P M],
284//
285// where M contains the AO dipole integrals for x, y, and z and P is the full
286// state density matrix. Transition densities omit the nuclear term and return
287// only the electronic transition dipole.
288Eigen::Vector3d Orbitals::CalcElDipole(const QMState& state) const {
289 Eigen::Vector3d nuclei_dip = Eigen::Vector3d::Zero();
290 if (!state.isTransition()) {
291 for (const QMAtom& atom : atoms_) {
292 nuclei_dip += (atom.getPos() - atoms_.getPos()) * atom.getNuccharge();
293 }
294 }
295 AOBasis basis = getDftBasis();
296 AODipole dipole;
297 dipole.setCenter(atoms_.getPos());
298 dipole.Fill(basis);
299
300 Eigen::MatrixXd dmat = this->DensityMatrixFull(state);
301 Eigen::Vector3d electronic_dip;
302 for (Index i = 0; i < 3; ++i) {
303 electronic_dip(i) = dmat.cwiseProduct(dipole.Matrix()[i]).sum();
304 }
305 return nuclei_dip - electronic_dip;
306}
307
308// AO transition density for a singlet BSE excitation.
309//
310// In TDA form this evaluates
311//
312// gamma_(mu,nu) = sqrt(2) sum_(v,c) A_(v,c) C_(mu,v) C_(nu,c),
313//
314// and for full BSE the resonant and anti-resonant amplitudes are added before
315// the AO transformation.
316Eigen::MatrixXd Orbitals::TransitionDensityMatrix(const QMState& state) const {
317
318 if (state.Type() == QMStateType::ExcitonUKS) {
319 if (!this->hasUnrestrictedOrbitals()) {
320 throw std::runtime_error(
321 "ExcitonUKS transition density requested, but no unrestricted "
322 "orbitals are present.");
323 }
324
325 const Eigen::MatrixXd& BSECoefs = BSE_uks_.eigenvectors();
326 if (BSECoefs.cols() < state.StateIdx() + 1 || BSECoefs.rows() < 2) {
327 throw std::runtime_error(
328 "Orbitals object has no information about state:" + state.ToString());
329 }
330
331 Eigen::VectorXd coeffs = BSECoefs.col(state.StateIdx());
332 if (!useTDA_) {
333 coeffs += BSE_uks_.eigenvectors2().col(state.StateIdx());
334 }
335
336 const Index alpha_vtotal = getHomoAlpha() - bse_vmin_ + 1;
337 const Index alpha_ctotal = bse_cmax_ - getLumoAlpha() + 1;
338 const Index beta_vtotal = getHomoBeta() - bse_vmin_ + 1;
339 const Index beta_ctotal = bse_cmax_ - getLumoBeta() + 1;
340
341 const Index alpha_size = alpha_vtotal * alpha_ctotal;
342 const Index beta_size = beta_vtotal * beta_ctotal;
343
344 if (coeffs.size() != alpha_size + beta_size) {
345 throw std::runtime_error(
346 "ExcitonUKS coefficient vector size does not match expected "
347 "alpha+beta BSE space dimensions.");
348 }
349
350 Eigen::MatrixXd tdm = Eigen::MatrixXd::Zero(mos_.eigenvectors().rows(),
351 mos_.eigenvectors().rows());
352
353 if (alpha_size > 0) {
354 const Eigen::VectorXd coeffs_alpha = coeffs.head(alpha_size);
355 const auto occ_alpha =
356 mos_.eigenvectors().middleCols(bse_vmin_, alpha_vtotal);
357 const auto virt_alpha =
358 mos_.eigenvectors().middleCols(getLumoAlpha(), alpha_ctotal);
359 const Eigen::Map<const Eigen::MatrixXd> mat_alpha(
360 coeffs_alpha.data(), alpha_ctotal, alpha_vtotal);
361
362 tdm += occ_alpha * mat_alpha.transpose() * virt_alpha.transpose();
363 }
364
365 if (beta_size > 0) {
366 const Eigen::VectorXd coeffs_beta = coeffs.tail(beta_size);
367 const auto occ_beta =
368 mos_beta_.eigenvectors().middleCols(bse_vmin_, beta_vtotal);
369 const auto virt_beta =
370 mos_beta_.eigenvectors().middleCols(getLumoBeta(), beta_ctotal);
371 const Eigen::Map<const Eigen::MatrixXd> mat_beta(
372 coeffs_beta.data(), beta_ctotal, beta_vtotal);
373
374 tdm += occ_beta * mat_beta.transpose() * virt_beta.transpose();
375 }
376
377 return tdm;
378 }
379
380 if (this->hasUnrestrictedOrbitals()) {
381 throw std::runtime_error(
382 "TransitionDensityMatrix not implemented for open-shell systems "
383 "except ExcitonUKS.");
384 }
385 if (state.Type() != QMStateType::Singlet) {
386 throw std::runtime_error(
387 "Spin type not known for transition density matrix. Available only for "
388 "singlet");
389 }
390 const Eigen::MatrixXd& BSECoefs = BSE_singlet_.eigenvectors();
391 if (BSECoefs.cols() < state.StateIdx() + 1 || BSECoefs.rows() < 2) {
392 throw std::runtime_error("Orbitals object has no information about state:" +
393 state.ToString());
394 }
395
396 Eigen::VectorXd coeffs = BSECoefs.col(state.StateIdx());
397
398 if (!useTDA_) {
399 coeffs += BSE_singlet_.eigenvectors2().col(state.StateIdx());
400 }
401 coeffs *= std::sqrt(2.0);
402 auto occlevels = mos_.eigenvectors().middleCols(bse_vmin_, bse_vtotal_);
403 auto virtlevels = mos_.eigenvectors().middleCols(bse_cmin_, bse_ctotal_);
404 Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_,
406
407 return occlevels * mat.transpose() * virtlevels.transpose();
408}
409
410// Spin-summed excited-state density correction split into hole and electron
411// blocks. For full BSE the anti-resonant contribution is subtracted from the
412// resonant part, yielding the usual RPA/BSE excited-state density.
413std::array<Eigen::MatrixXd, 2> Orbitals::DensityMatrixExcitedState(
414 const QMState& state) const {
415
416 if (state.Type() == QMStateType::ExcitonUKS) {
417 if (!this->hasUnrestrictedOrbitals()) {
418 throw std::runtime_error(
419 "ExcitonUKS excited-state density requested, but no unrestricted "
420 "orbitals are present.");
421 }
422
423 const Eigen::MatrixXd& X = BSE_uks_.eigenvectors();
424 if (X.cols() < state.StateIdx() + 1 || X.rows() < 2) {
425 throw std::runtime_error(
426 "Orbitals object has no information about state:" + state.ToString());
427 }
428
429 const Index alpha_vtotal = getHomoAlpha() - bse_vmin_ + 1;
430 const Index alpha_ctotal = bse_cmax_ - getLumoAlpha() + 1;
431 const Index beta_vtotal = getHomoBeta() - bse_vmin_ + 1;
432 const Index beta_ctotal = bse_cmax_ - getLumoBeta() + 1;
433
434 const Index alpha_size = alpha_vtotal * alpha_ctotal;
435 const Index beta_size = beta_vtotal * beta_ctotal;
436
437 if (X.rows() != alpha_size + beta_size) {
438 throw std::runtime_error(
439 "ExcitonUKS coefficient vector size does not match expected "
440 "alpha+beta BSE space dimensions.");
441 }
442
443 const Index nao = mos_.eigenvectors().rows();
444 std::array<Eigen::MatrixXd, 2> dmat;
445 dmat[0] = Eigen::MatrixXd::Zero(nao, nao); // hole
446 dmat[1] = Eigen::MatrixXd::Zero(nao, nao); // electron
447
448 auto add_resonant = [&](const Eigen::VectorXd& coeffs, bool alpha_spin) {
449 if (coeffs.size() == 0) {
450 return;
451 }
452
453 const Index vtotal = alpha_spin ? alpha_vtotal : beta_vtotal;
454 const Index ctotal = alpha_spin ? alpha_ctotal : beta_ctotal;
455
456 const auto& mos_spin =
457 alpha_spin ? mos_.eigenvectors() : mos_beta_.eigenvectors();
458 const Index lumo = alpha_spin ? getLumoAlpha() : getLumoBeta();
459
460 const auto occlevels = mos_spin.middleCols(bse_vmin_, vtotal);
461 const auto virtlevels = mos_spin.middleCols(lumo, ctotal);
462
463 const Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), ctotal,
464 vtotal);
465
466 dmat[0] += occlevels * (mat.transpose() * mat) * occlevels.transpose();
467 dmat[1] += virtlevels * (mat * mat.transpose()) * virtlevels.transpose();
468 };
469
470 auto subtract_antiresonant = [&](const Eigen::VectorXd& coeffs,
471 bool alpha_spin) {
472 if (coeffs.size() == 0) {
473 return;
474 }
475
476 const Index vtotal = alpha_spin ? alpha_vtotal : beta_vtotal;
477 const Index ctotal = alpha_spin ? alpha_ctotal : beta_ctotal;
478
479 const auto& mos_spin =
480 alpha_spin ? mos_.eigenvectors() : mos_beta_.eigenvectors();
481 const Index lumo = alpha_spin ? getLumoAlpha() : getLumoBeta();
482
483 const auto occlevels = mos_spin.middleCols(bse_vmin_, vtotal);
484 const auto virtlevels = mos_spin.middleCols(lumo, ctotal);
485
486 const Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), ctotal,
487 vtotal);
488
489 // Full-BSE anti-resonant correction:
490 // hole -= virt * (B B^T) * virt^T
491 // electron -= occ * (B^T B) * occ^T
492 dmat[0] -= virtlevels * (mat * mat.transpose()) * virtlevels.transpose();
493 dmat[1] -= occlevels * (mat.transpose() * mat) * occlevels.transpose();
494 };
495
496 const Eigen::VectorXd Xstate = X.col(state.StateIdx());
497 add_resonant(Xstate.head(alpha_size), true);
498 add_resonant(Xstate.tail(beta_size), false);
499
500 if (!useTDA_) {
501 const Eigen::MatrixXd& Y = BSE_uks_.eigenvectors2();
502 if (Y.cols() < state.StateIdx() + 1 || Y.rows() != X.rows()) {
503 throw std::runtime_error(
504 "Orbitals object has inconsistent anti-resonant ExcitonUKS "
505 "coefficients for state:" +
506 state.ToString());
507 }
508
509 const Eigen::VectorXd Ystate = Y.col(state.StateIdx());
510 subtract_antiresonant(Ystate.head(alpha_size), true);
511 subtract_antiresonant(Ystate.tail(beta_size), false);
512 }
513
514 return dmat;
515 }
516
517 if (this->hasUnrestrictedOrbitals()) {
518 throw std::runtime_error(
519 "DensityMatrixExcitedState not implemented for open-shell systems "
520 "except ExcitonUKS.");
521 }
522
523 std::array<Eigen::MatrixXd, 2> dmat = DensityMatrixExcitedState_R(state);
524 if (!useTDA_) {
525 std::array<Eigen::MatrixXd, 2> dmat_AR =
527 dmat[0] -= dmat_AR[0];
528 dmat[1] -= dmat_AR[1];
529 }
530 return dmat;
531}
532
533// Excited state density matrix
534
535std::array<Eigen::MatrixXd, 2> Orbitals::DensityMatrixExcitedState_R(
536 const QMState& state) const {
537 if (!state.Type().isExciton()) {
538 throw std::runtime_error(
539 "Spin type not known for density matrix. Available are singlet and "
540 "triplet");
541 }
542
543 const Eigen::MatrixXd& BSECoefs = (state.Type() == QMStateType::Singlet)
544 ? BSE_singlet_.eigenvectors()
545 : BSE_triplet_.eigenvectors();
546 if (BSECoefs.cols() < state.StateIdx() + 1 || BSECoefs.rows() < 2) {
547 throw std::runtime_error("Orbitals object has no information about state:" +
548 state.ToString());
549 }
550 /******
551 *
552 * Density matrix for GW-BSE based excitations
553 *
554 * - electron contribution
555 * D_ab = \sum{vc} \sum{c'} A_{vc}A_{vc'} mo_a(c)mo_b(c')
556 *
557 * - hole contribution
558 * D_ab = \sum{vc} \sum{v'} A_{vc}A_{v'c} mo_a(v)mo_b(v')
559 *
560 */
561
562 Eigen::VectorXd coeffs = BSECoefs.col(state.StateIdx());
563
564 std::array<Eigen::MatrixXd, 2> dmatEX;
565 // hole part as matrix products
566 Eigen::MatrixXd occlevels =
567 mos_.eigenvectors().middleCols(bse_vmin_, bse_vtotal_);
568 dmatEX[0] = occlevels * CalcAuxMat_vv(coeffs) * occlevels.transpose();
569
570 // electron part as matrix products
571 Eigen::MatrixXd virtlevels =
572 mos_.eigenvectors().middleCols(bse_cmin_, bse_ctotal_);
573 dmatEX[1] = virtlevels * CalcAuxMat_cc(coeffs) * virtlevels.transpose();
574
575 return dmatEX;
576}
577
578Eigen::MatrixXd Orbitals::CalcAuxMat_vv(const Eigen::VectorXd& coeffs) const {
579 const Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_,
581 return mat.transpose() * mat;
582}
583
584Eigen::MatrixXd Orbitals::CalcAuxMat_cc(const Eigen::VectorXd& coeffs) const {
585 const Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_,
587 return mat * mat.transpose();
588}
589
590std::array<Eigen::MatrixXd, 2> Orbitals::DensityMatrixExcitedState_AR(
591 const QMState& state) const {
592
593 if (!state.Type().isExciton()) {
594 throw std::runtime_error(
595 "Spin type not known for density matrix. Available are singlet and "
596 "triplet");
597 }
598
599 const Eigen::MatrixXd& BSECoefs_AR = (state.Type() == QMStateType::Singlet)
600 ? BSE_singlet_.eigenvectors2()
601 : BSE_triplet_.eigenvectors2();
602 if (BSECoefs_AR.cols() < state.StateIdx() + 1 || BSECoefs_AR.rows() < 2) {
603 throw std::runtime_error("Orbitals object has no information about state:" +
604 state.ToString());
605 }
606 /******
607 *
608 * Density matrix for GW-BSE based excitations
609 *
610 * - electron contribution
611 * D_ab = \sum{vc} \sum{v'} B_{vc}B_{v'c} mo_a(v)mo_b(v')
612 *
613 * - hole contribution
614 * D_ab = \sum{vc} \sum{c'} B_{vc}B_{vc'} mo_a(c)mo_b(c')
615 *
616 *
617 * more efficient:
618 *
619 * - electron contribution
620 * D_ab = \sum{v} \sum{v'} mo_a(v)mo_b(v') [ \sum{c} B_{vc}B_{v'c} ]
621 * = \sum{v} \sum{v'} mo_a(v)mo_b(v') B_{vv'}
622 *
623 * - hole contribution
624 * D_ab = \sum{c} \sum{c'} mo_a(c)mo_b(c') [ \sum{v} B_{vc}B_{vc'} ]
625 * = \sum{c} \sum{c'} mo_a(c)mo_b(c') B_{cc'}
626 *
627 */
628
629 Eigen::VectorXd coeffs = BSECoefs_AR.col(state.StateIdx());
630
631 std::array<Eigen::MatrixXd, 2> dmatAR;
632 Eigen::MatrixXd virtlevels =
633 mos_.eigenvectors().middleCols(bse_cmin_, bse_ctotal_);
634 dmatAR[0] = virtlevels * CalcAuxMat_cc(coeffs) * virtlevels.transpose();
635 // electron part as matrix products
636 Eigen::MatrixXd occlevels =
637 mos_.eigenvectors().middleCols(bse_vmin_, bse_vtotal_);
638 dmatAR[1] = occlevels * CalcAuxMat_vv(coeffs) * occlevels.transpose();
639
640 return dmatAR;
641}
642
646
647Eigen::VectorXd Orbitals::Oscillatorstrengths(const QMStateType& type) const {
648
649 const tools::EigenSystem* es = nullptr;
650
651 if (type == QMStateType::Singlet) {
652 es = &BSE_singlet_;
653 } else if (type == QMStateType::ExcitonUKS) {
654 es = &BSE_uks_;
655 } else {
656 throw std::runtime_error(
657 "Orbitals::Oscillatorstrengths only implemented for "
658 "restricted singlets and combined UKS excitons.");
659 }
660
661 Index size = Index(transition_dipoles_.size());
662 if (size > es->eigenvalues().size()) {
663 size = es->eigenvalues().size();
664 }
665
666 Eigen::VectorXd oscs = Eigen::VectorXd::Zero(size);
667 for (Index i = 0; i < size; ++i) {
668 // f = (2/3) * Omega * |d|^2
669 // Omega must be in Hartree, d in e*bohr
670 oscs(i) =
671 transition_dipoles_[i].squaredNorm() * 2.0 / 3.0 * es->eigenvalues()(i);
672 }
673 return oscs;
674}
675
676double Orbitals::getTotalStateEnergy(const QMState& state) const {
677 double total_energy = getDFTTotalEnergy();
678 if (state.Type() == QMStateType::Gstate) {
679 return total_energy;
680 }
681 total_energy += getExcitedStateEnergy(state);
682 return total_energy;
683}
684
685double Orbitals::getExcitedStateEnergy(const QMState& state) const {
686
687 double omega = 0.0;
688 if (state.isTransition()) {
689 throw std::runtime_error(
690 "Total Energy does not exist for transition state");
691 }
692
693 if (state.Type() == QMStateType::Singlet) {
694 if (BSE_singlet_.eigenvalues().size() < state.StateIdx() + 1) {
695 throw std::runtime_error("Orbitals::getTotalEnergy You want " +
696 state.ToString() +
697 " which has not been calculated");
698 }
699 omega = BSE_singlet_.eigenvalues()[state.StateIdx()];
700 } else if (state.Type() == QMStateType::Triplet) {
701 if (BSE_triplet_.eigenvalues().size() < state.StateIdx() + 1) {
702 throw std::runtime_error("Orbitals::getTotalEnergy You want " +
703 state.ToString() +
704 " which has not been calculated");
705 }
706 omega = BSE_triplet_.eigenvalues()[state.StateIdx()];
707 } else if (state.Type() == QMStateType::DQPstate) {
708 if (QPdiag_.eigenvalues().size() < state.StateIdx() + 1 - getGWAmin()) {
709 throw std::runtime_error("Orbitals::getTotalEnergy You want " +
710 state.ToString() +
711 " which has not been calculated");
712 }
713 return QPdiag_.eigenvalues()[state.StateIdx() - getGWAmin()];
714 } else if (state.Type() == QMStateType::KSstate) {
715 if (mos_.eigenvalues().size() < state.StateIdx() + 1) {
716 throw std::runtime_error("Orbitals::getTotalEnergy You want " +
717 state.ToString() +
718 " which has not been calculated");
719 }
720 return mos_.eigenvalues()[state.StateIdx()];
721 } else if (state.Type() == QMStateType::PQPstate) {
722 if (this->QPpert_energies_.rows() < state.StateIdx() + 1 - getGWAmin()) {
723 throw std::runtime_error("Orbitals::getTotalEnergy You want " +
724 state.ToString() +
725 " which has not been calculated");
726 }
727 return QPpert_energies_(state.StateIdx() - getGWAmin(), 3);
728 } else if (state.Type() == QMStateType::LMOstate) {
729 if (lmos_energies_.size() < state.StateIdx() + 1) {
730 throw std::runtime_error(
731 "Orbitals::getTotalEnergy You want " + state.ToString() +
732 " which is a LMO for virtual orbitals. Not implemented.");
733 }
734 return lmos_energies_(state.StateIdx());
735 } else {
736 throw std::runtime_error(
737 "GetTotalEnergy only knows states:singlet,triplet,KS,DQP,PQP,LMOs");
738 }
739 return omega; // e.g. hartree
740}
741
742std::array<Eigen::MatrixXd, 3> Orbitals::CalcFreeTransition_Dipoles() const {
743 const Eigen::MatrixXd& dft_orbitals = mos_.eigenvectors();
744 AOBasis basis = getDftBasis();
745 // Testing electric dipole AOMatrix
746 AODipole dft_dipole;
747 dft_dipole.Fill(basis);
748
749 // now transition dipole elements for free interlevel transitions
750 std::array<Eigen::MatrixXd, 3> interlevel_dipoles;
751
752 Eigen::MatrixXd empty = dft_orbitals.middleCols(bse_cmin_, bse_ctotal_);
753 Eigen::MatrixXd occ = dft_orbitals.middleCols(bse_vmin_, bse_vtotal_);
754 for (Index i = 0; i < 3; i++) {
755 interlevel_dipoles[i] = empty.transpose() * dft_dipole.Matrix()[i] * occ;
756 }
757 return interlevel_dipoles;
758}
759
763
765
766 transition_dipoles_.clear();
767
768 if (type == QMStateType::Singlet) {
769 std::array<Eigen::MatrixXd, 3> interlevel_dipoles =
771
772 Index numofstates = BSE_singlet_.eigenvalues().size();
773 transition_dipoles_.reserve(numofstates);
774
775 const double sqrt2 = std::sqrt(2.0);
776
777 for (Index i_exc = 0; i_exc < numofstates; ++i_exc) {
778 Eigen::VectorXd coeffs = BSE_singlet_.eigenvectors().col(i_exc);
779 if (!useTDA_) {
780 coeffs += BSE_singlet_.eigenvectors2().col(i_exc);
781 }
782
783 Eigen::Map<Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_, bse_vtotal_);
784
785 Eigen::Vector3d tdipole = Eigen::Vector3d::Zero();
786 for (Index i = 0; i < 3; ++i) {
787 tdipole[i] = mat.cwiseProduct(interlevel_dipoles[i]).sum();
788 }
789
790 // Restricted singlet:
791 // the excitation is the spin-adapted combination of alpha and beta
792 // determinants, so the total transition dipole picks up sqrt(2).
793 transition_dipoles_.push_back(-sqrt2 * tdipole);
794 }
795 return;
796 }
797
798 if (type == QMStateType::ExcitonUKS) {
800 throw std::runtime_error(
801 "Orbitals::CalcCoupledTransition_Dipoles(ExcitonUKS) requires "
802 "unrestricted alpha/beta orbitals.");
803 }
804
805 const Index alpha_homo = getHomoAlpha();
806 const Index beta_homo = getHomoBeta();
807
808 const Index alpha_vtotal = alpha_homo - bse_vmin_ + 1;
809 const Index alpha_ctotal = bse_cmax_ - (alpha_homo + 1) + 1;
810 const Index alpha_size = alpha_vtotal * alpha_ctotal;
811
812 const Index beta_vtotal = beta_homo - bse_vmin_ + 1;
813 const Index beta_ctotal = bse_cmax_ - (beta_homo + 1) + 1;
814 const Index beta_size = beta_vtotal * beta_ctotal;
815
816 if (alpha_vtotal < 0 || alpha_ctotal < 0 || beta_vtotal < 0 ||
817 beta_ctotal < 0) {
818 throw std::runtime_error(
819 "Orbitals::CalcCoupledTransition_Dipoles(ExcitonUKS): "
820 "invalid UKS BSE window.");
821 }
822
823 AOBasis basis = getDftBasis();
824 AODipole dft_dipole;
825 dft_dipole.Fill(basis);
826
827 std::array<Eigen::MatrixXd, 3> interlevel_dipoles_alpha;
828 std::array<Eigen::MatrixXd, 3> interlevel_dipoles_beta;
829
830 const Eigen::MatrixXd virt_alpha =
831 mos_.eigenvectors().middleCols(alpha_homo + 1, alpha_ctotal);
832 const Eigen::MatrixXd occ_alpha =
833 mos_.eigenvectors().middleCols(bse_vmin_, alpha_vtotal);
834
835 const Eigen::MatrixXd virt_beta =
836 mos_beta_.eigenvectors().middleCols(beta_homo + 1, beta_ctotal);
837 const Eigen::MatrixXd occ_beta =
838 mos_beta_.eigenvectors().middleCols(bse_vmin_, beta_vtotal);
839
840 for (Index i = 0; i < 3; ++i) {
841 interlevel_dipoles_alpha[i] =
842 virt_alpha.transpose() * dft_dipole.Matrix()[i] * occ_alpha;
843 interlevel_dipoles_beta[i] =
844 virt_beta.transpose() * dft_dipole.Matrix()[i] * occ_beta;
845 }
846
847 const Index numofstates = BSE_uks_.eigenvalues().size();
848 transition_dipoles_.reserve(numofstates);
849
850 for (Index i_exc = 0; i_exc < numofstates; ++i_exc) {
851 Eigen::VectorXd coeffs_x = BSE_uks_.eigenvectors().col(i_exc);
852 Eigen::VectorXd coeffs = coeffs_x;
853 if (!useTDA_) {
854 coeffs += BSE_uks_.eigenvectors2().col(i_exc);
855 }
856
857 if (coeffs.size() != alpha_size + beta_size) {
858 throw std::runtime_error(
859 "Orbitals::CalcCoupledTransition_Dipoles(ExcitonUKS): "
860 "exciton vector size does not match alpha+beta sector sizes.");
861 }
862
863 Eigen::Map<const Eigen::MatrixXd> mat_alpha(coeffs.data(), alpha_ctotal,
864 alpha_vtotal);
865 Eigen::Map<const Eigen::MatrixXd> mat_beta(coeffs.data() + alpha_size,
866 beta_ctotal, beta_vtotal);
867
868 Eigen::Vector3d tdipole = Eigen::Vector3d::Zero();
869 for (Index i = 0; i < 3; ++i) {
870 tdipole[i] = mat_alpha.cwiseProduct(interlevel_dipoles_alpha[i]).sum() +
871 mat_beta.cwiseProduct(interlevel_dipoles_beta[i]).sum();
872 }
873
874 // Combined UKS exciton:
875 // alpha and beta sectors are already explicit components of the
876 // eigenvector, so there is NO extra sqrt(2) factor here.
877 transition_dipoles_.push_back(-tdipole);
878 }
879 return;
880 }
881
882 throw std::runtime_error(
883 "Orbitals::CalcCoupledTransition_Dipoles only implemented for "
884 "restricted singlets and combined UKS excitons.");
885}
886
888 std::vector<Index> sort_index = SortEnergies();
889 tools::EigenSystem MO_copy = mos_;
890 Index size = mos_.eigenvalues().size();
891 for (Index i = 0; i < size; ++i) {
892 mos_.eigenvalues()(i) = MO_copy.eigenvalues()(sort_index[i]);
893 }
894 for (Index i = 0; i < size; ++i) {
895 mos_.eigenvectors().col(i) = MO_copy.eigenvectors().col(sort_index[i]);
896 }
897}
898
907 const Orbitals& orbitalsB) {
908
909 // This routine constructs a block-diagonal guess only from the restricted
910 // MO coefficient matrices mos_. It therefore only makes sense for
911 // restricted closed-shell inputs.
912 if (orbitalsA.hasUnrestrictedOrbitals() ||
913 orbitalsB.hasUnrestrictedOrbitals()) {
914 throw std::runtime_error(
915 "PrepareDimerGuess currently supports only restricted orbitals.");
916 }
917
918 if (orbitalsA.getSpin() != 1 || orbitalsB.getSpin() != 1) {
919 throw std::runtime_error(
920 "PrepareDimerGuess currently supports only closed-shell singlets.");
921 }
922
923 // constructing the direct product orbA x orbB
924 Index basisA = orbitalsA.getBasisSetSize();
925 Index basisB = orbitalsB.getBasisSetSize();
926
927 // In the restricted closed-shell case, getLumo() is the number of occupied
928 // spatial orbitals, which is exactly what this block-merging logic assumes.
929 Index occA = orbitalsA.getLumo();
930 Index occB = orbitalsB.getLumo();
931
932 mos_.eigenvectors() = Eigen::MatrixXd::Zero(basisA + basisB, basisA + basisB);
933
934 // AxB = | A 0 |
935 // | 0 B |
936 if (orbitalsA.getDFTbasisName() != orbitalsB.getDFTbasisName()) {
937 throw std::runtime_error("Basissets of Orbitals A and B differ " +
938 orbitalsA.getDFTbasisName() + ":" +
939 orbitalsB.getDFTbasisName());
940 }
941 this->SetupDftBasis(orbitalsA.getDFTbasisName());
942
943 if (orbitalsA.getECPName() != orbitalsB.getECPName()) {
944 throw std::runtime_error("ECPs of Orbitals A and B differ " +
945 orbitalsA.getECPName() + ":" +
946 orbitalsB.getECPName());
947 }
948 this->setECPName(orbitalsA.getECPName());
949
950 // Keep occupation metadata fully consistent for a restricted singlet.
951 this->setNumberOfOccupiedLevels(occA + occB);
952 this->setNumberOfOccupiedLevelsBeta(occA + occB);
953 this->setNumberOfAlphaElectrons(occA + occB);
954 this->setNumberOfBetaElectrons(occA + occB);
955 this->setChargeAndSpin(orbitalsA.getCharge() + orbitalsB.getCharge(), 1);
956
957 this->MOs().eigenvectors().block(0, 0, basisA, basisA) =
958 orbitalsA.MOs().eigenvectors();
959 this->MOs().eigenvectors().block(basisA, basisA, basisB, basisB) =
960 orbitalsB.MOs().eigenvectors();
961
962 this->MOs().eigenvalues() = Eigen::VectorXd::Zero(basisA + basisB);
963 this->MOs().eigenvalues().segment(0, basisA) = orbitalsA.MOs().eigenvalues();
964 this->MOs().eigenvalues().segment(basisA, basisB) =
965 orbitalsB.MOs().eigenvalues();
966
967 this->OrderMOsbyEnergy();
968}
969
970void Orbitals::WriteToCpt(const std::string& filename) const {
971 std::lock_guard<std::recursive_mutex> lock(
974 WriteToCpt(cpf);
975}
976
978 CheckpointWriter writer = f.getWriter("/QMdata");
979 WriteToCpt(writer);
980 WriteBasisSetsToCpt(writer);
981}
982
984 CheckpointWriter dftWriter = w.openChild("dft");
985 dftbasis_.WriteToCpt(dftWriter);
986 CheckpointWriter auxWriter = w.openChild("aux");
987 auxbasis_.WriteToCpt(auxWriter);
988}
989
991 w(votca::tools::ToolsVersionStr(), "XTPVersion");
992 w(orbitals_version(), "version");
993 w(occupied_levels_, "occupied_levels");
994 w(occupied_levels_beta_, "occupied_levels_beta");
995 w(number_alpha_electrons_, "number_alpha_electrons");
996 w(number_beta_electrons_, "number_beta_electrons");
997 w(total_charge_, "charge");
998 w(total_spin_, "spin");
999
1000 w(mos_, "mos");
1001 w(mos_beta_, "mos_beta");
1002 w(occupations_, "occupations");
1003 w(active_electrons_, "active_electrons");
1004 w(mos_embedding_, "mos_embedding");
1005 w(lmos_, "LMOs");
1006 w(lmos_energies_, "LMOs_energies");
1007 w(inactivedensity_, "inactivedensity");
1008 w(expandedMOs_, "TruncMOsFullBasis");
1009
1010 CheckpointWriter molgroup = w.openChild("qmmolecule");
1011 atoms_.WriteToCpt(molgroup);
1012
1013 w(qm_energy_, "qm_energy");
1014 w(qm_package_, "qm_package");
1015
1016 w(rpamin_, "rpamin");
1017 w(rpamax_, "rpamax");
1018 w(qpmin_, "qpmin");
1019 w(qpmax_, "qpmax");
1020 w(bse_vmin_, "bse_vmin");
1021 w(bse_cmax_, "bse_cmax");
1022 w(functionalname_, "XCFunctional");
1023 w(grid_quality_, "XC_grid_quality");
1024 w(ScaHFX_, "ScaHFX");
1025
1026 w(useTDA_, "useTDA");
1027 w(ECP_, "ECP");
1028
1029 w(rpa_inputenergies_, "RPA_inputenergies");
1030 w(QPpert_energies_, "QPpert_energies");
1031
1032 w(QPdiag_, "QPdiag");
1033
1034 w(BSE_singlet_, "BSE_singlet");
1035
1036 w(transition_dipoles_, "transition_dipoles");
1037
1038 w(BSE_triplet_, "BSE_triplet");
1039
1040 std::uint8_t tmp = use_Hqp_offdiag_ ? 1u : 0u;
1041 w(tmp, "use_Hqp_offdiag");
1042
1043 w(BSE_singlet_energies_dynamic_, "BSE_singlet_dynamic");
1044
1045 w(BSE_triplet_energies_dynamic_, "BSE_triplet_dynamic");
1046
1047 w(CalcType_, "CalcType");
1048
1049 // spin GW
1050 w(rpa_inputenergies_alpha_, "RPA_inputenergies_alpha");
1051 w(rpa_inputenergies_beta_, "RPA_inputenergies_beta");
1052
1053 w(QPpert_energies_alpha_, "QPpert_energies_alpha");
1054 w(QPpert_energies_beta_, "QPpert_energies_beta");
1055
1056 w(QPdiag_alpha_, "QPdiag_alpha");
1057 w(QPdiag_beta_, "QPdiag_beta");
1058
1059 w(BSE_uks_, "BSE_uks");
1060 w(BSE_uks_energies_dynamic_, "BSE_uks_dynamic");
1061}
1062
1063void Orbitals::ReadFromCpt(const std::string& filename) {
1064 std::lock_guard<std::recursive_mutex> lock(
1067 ReadFromCpt(cpf);
1068}
1069
1071 CheckpointReader reader = f.getReader("/QMdata");
1072 ReadFromCpt(reader);
1073 ReadBasisSetsFromCpt(reader);
1074}
1075
1077 CheckpointReader dftReader = r.openChild("dft");
1078 dftbasis_.ReadFromCpt(dftReader);
1079 CheckpointReader auxReader = r.openChild("aux");
1080 auxbasis_.ReadFromCpt(auxReader);
1081}
1082
1084 r(occupied_levels_, "occupied_levels");
1085 r(number_alpha_electrons_, "number_alpha_electrons");
1086
1087 int version;
1088 r(version, "version");
1089
1090 auto r_optional = [&r](auto& obj, const std::string& key) {
1091 try {
1092 r(obj, key);
1093 } catch (std::runtime_error&) {
1094 // Older checkpoints may not contain this field.
1095 }
1096 };
1097
1098 // Read qmatoms
1099 CheckpointReader molgroup = r.openChild("qmmolecule");
1100 atoms_.ReadFromCpt(molgroup);
1101
1102 r(qm_energy_, "qm_energy");
1103 r(qm_package_, "qm_package");
1104 try {
1105 r(lmos_, "LMOs");
1106 r(lmos_energies_, "LMOs_energies");
1107 } catch (std::runtime_error& e) {
1108 ;
1109 }
1110
1111 r(version, "version");
1112 r(mos_, "mos");
1113 r(mos_embedding_, "mos_embedding");
1114 r(active_electrons_, "active_electrons");
1115 r(inactivedensity_, "inactivedensity");
1116 r(CalcType_, "CalcType");
1117 r(expandedMOs_, "TruncMOsFullBasis");
1118
1119 // spin info available from version 6 or higher
1120 if (version > 5) {
1121 r(occupied_levels_beta_, "occupied_levels_beta");
1122 r(number_beta_electrons_, "number_beta_electrons");
1123 r(total_charge_, "charge");
1124 r(total_spin_, "spin");
1125 r(mos_beta_, "mos_beta");
1126 r(occupations_, "occupations");
1127 }
1128
1129 if (version < 3) {
1130 // clang-format off
1131 std::array<Index, 49> votcaOrder_old = {
1132 0, // s
1133 0, -1, 1, // p
1134 0, -1, 1, -2, 2, // d
1135 0, -1, 1, -2, 2, -3, 3, // f
1136 0, -1, 1, -2, 2, -3, 3, -4, 4, // g
1137 0, -1, 1, -2, 2, -3, 3, -4, 4,-5,5, // h
1138 0, -1, 1, -2, 2, -3, 3, -4, 4,-5,5,-6,6 // i
1139 };
1140 // clang-format on
1141
1142 std::array<Index, 49> multiplier;
1143 multiplier.fill(1);
1144 OrbReorder ord(votcaOrder_old, multiplier);
1145 ord.reorderOrbitals(mos_.eigenvectors(), this->getDftBasis());
1146 }
1147
1148 if (version < 5) { // we need to construct the basissets, NB. can only be
1149 // done after reading the atoms.
1150 std::string dft_basis_name;
1151 std::string aux_basis_name;
1152 r(dft_basis_name, "dftbasis");
1153 r(aux_basis_name, "auxbasis");
1154 this->SetupDftBasis(dft_basis_name);
1155 this->SetupAuxBasis(aux_basis_name);
1156 }
1157
1158 r(rpamin_, "rpamin");
1159 r(rpamax_, "rpamax");
1160 r(qpmin_, "qpmin");
1161 r(qpmax_, "qpmax");
1162 r(bse_vmin_, "bse_vmin");
1163 r(bse_cmax_, "bse_cmax");
1165 try {
1166 r(functionalname_, "XCFunctional");
1167 r(grid_quality_, "XC_grid_quality");
1168 } catch (std::runtime_error& e) {
1169 grid_quality_ = "medium";
1170 }
1171 r(ScaHFX_, "ScaHFX");
1172 r(useTDA_, "useTDA");
1173 r(ECP_, "ECP");
1174
1175 r(rpa_inputenergies_, "RPA_inputenergies");
1176
1177 r(QPpert_energies_, "QPpert_energies");
1178 r(QPdiag_, "QPdiag");
1179
1180 // Optional unrestricted GW data
1181 r_optional(rpa_inputenergies_alpha_, "RPA_inputenergies_alpha");
1182 r_optional(rpa_inputenergies_beta_, "RPA_inputenergies_beta");
1183
1184 r_optional(QPpert_energies_alpha_, "QPpert_energies_alpha");
1185 r_optional(QPpert_energies_beta_, "QPpert_energies_beta");
1186
1187 r_optional(QPdiag_alpha_, "QPdiag_alpha");
1188 r_optional(QPdiag_beta_, "QPdiag_beta");
1189 r_optional(BSE_uks_, "BSE_uks");
1190 r_optional(BSE_uks_energies_dynamic_, "BSE_uks_dynamic");
1191 r(BSE_singlet_, "BSE_singlet");
1192
1193 r(transition_dipoles_, "transition_dipoles");
1194
1195 r(BSE_triplet_, "BSE_triplet");
1196
1197 std::uint8_t tmp = 0;
1198 r(tmp, "use_Hqp_offdiag");
1199 use_Hqp_offdiag_ = (tmp != 0);
1200
1201 if (version > 1) {
1202 r(BSE_singlet_energies_dynamic_, "BSE_singlet_dynamic");
1203
1204 r(BSE_triplet_energies_dynamic_, "BSE_triplet_dynamic");
1205 }
1206 // Backward compatibility for older restricted checkpoints that may not
1207 // contain explicit beta-electron information.
1209 if (number_alpha_electrons_ == 0) {
1211 }
1212 if (number_beta_electrons_ == 0) {
1214 }
1215 }
1216}
1217
1218/*********************************************
1219 * Extensions Spin-DFT
1220 **********************************************/
1221
1222// Spin-resolved ground-state density matrices.
1223//
1224// The returned pair is (P^alpha, P^beta) with
1225//
1226// P^sigma = C_occ^sigma (C_occ^sigma)^T
1227//
1228// for unrestricted calculations. In the restricted branch the same spatial MO
1229// coefficients are reused and distributed over alpha/beta occupations according
1230// to the stored electron counts.
1232 const {
1233 if (!hasMOs()) {
1234 throw std::runtime_error("Orbitals file does not contain MO coefficients");
1235 }
1236
1237 std::array<Eigen::MatrixXd, 2> result;
1238 result[0] = Eigen::MatrixXd::Zero(mos_.eigenvectors().rows(),
1239 mos_.eigenvectors().rows());
1240 result[1] = Eigen::MatrixXd::Zero(mos_.eigenvectors().rows(),
1241 mos_.eigenvectors().rows());
1242
1244 if (occupied_levels_ > 0) {
1245 const Eigen::MatrixXd occ_a =
1246 mos_.eigenvectors().leftCols(occupied_levels_);
1247 result[0] = occ_a * occ_a.transpose();
1248 }
1249 if (occupied_levels_beta_ > 0) {
1250 const Eigen::MatrixXd occ_b =
1251 mos_beta_.eigenvectors().leftCols(occupied_levels_beta_);
1252 result[1] = occ_b * occ_b.transpose();
1253 }
1254 return result;
1255 }
1256
1257 // Restricted branch:
1258 // Prefer explicit alpha/beta electron counts, but fall back to the legacy
1259 // occupied_levels_ convention when those counts were never set.
1262
1263 if (n_alpha == 0 && n_beta == 0 && occupied_levels_ > 0) {
1264 n_alpha = occupied_levels_;
1265 n_beta = occupied_levels_;
1266 }
1267
1268 const Index n_docc = std::min(n_alpha, n_beta);
1269 const Index n_socc_alpha = n_alpha - n_docc;
1270 const Index n_socc_beta = n_beta - n_docc;
1271
1272 if (n_docc > 0) {
1273 const Eigen::MatrixXd docc = mos_.eigenvectors().leftCols(n_docc);
1274 const Eigen::MatrixXd d_docc = docc * docc.transpose();
1275 result[0] += d_docc;
1276 result[1] += d_docc;
1277 }
1278
1279 if (n_socc_alpha > 0) {
1280 const Eigen::MatrixXd socc_a =
1281 mos_.eigenvectors().middleCols(n_docc, n_socc_alpha);
1282 result[0] += socc_a * socc_a.transpose();
1283 }
1284
1285 if (n_socc_beta > 0) {
1286 const Eigen::MatrixXd socc_b =
1287 mos_.eigenvectors().middleCols(n_docc + n_socc_alpha, n_socc_beta);
1288 result[1] += socc_b * socc_b.transpose();
1289 }
1290
1291 return result;
1292}
1293
1294} // namespace xtp
1295} // namespace votca
const Eigen::VectorXd & eigenvalues() const
Definition eigensystem.h:30
const Eigen::MatrixXd & eigenvectors() const
Definition eigensystem.h:33
Container to hold Basisfunctions for all atoms.
Definition aobasis.h:42
void setCenter(const Eigen::Vector3d &r)
Definition aomatrix.h:94
const std::array< Eigen::MatrixXd, 3 > & Matrix() const
Definition aomatrix.h:92
void Fill(const AOBasis &aobasis) final
void Load(const std::string &name)
Definition basisset.cc:149
CheckpointReader getReader()
CheckpointWriter getWriter()
CheckpointReader openChild(const std::string &childName) const
CheckpointWriter openChild(const std::string &childName) const
void reorderOrbitals(Eigen::MatrixXd &moCoefficients, const AOBasis &basis)
Definition orbreorder.cc:74
tools::EigenSystem QPdiag_alpha_
Definition orbitals.h:793
Index number_beta_electrons_
Definition orbitals.h:721
void CalcCoupledTransition_Dipoles()
Definition orbitals.cc:760
bool hasQPdiag() const
Definition orbitals.h:450
Index getCharge() const
Return the stored total charge.
Definition orbitals.h:254
Eigen::MatrixXd occupations_
Definition orbitals.h:729
Eigen::VectorXd rpa_inputenergies_
Definition orbitals.h:769
Index getHomo() const
Return the default HOMO index used by spin-agnostic callers.
Definition orbitals.h:107
Eigen::VectorXd BSE_uks_energies_dynamic_
Definition orbitals.h:797
Eigen::MatrixXd TransitionDensityMatrix(const QMState &state) const
Definition orbitals.cc:316
double getDFTTotalEnergy() const
Return the stored total DFT energy.
Definition orbitals.h:292
Index getSpin() const
Return the stored spin multiplicity.
Definition orbitals.h:252
Eigen::MatrixXd CalculateQParticleAORepresentation() const
Transform quasiparticle amplitudes into the AO representation.
Definition orbitals.cc:250
std::array< Eigen::MatrixXd, 2 > DensityMatrixExcitedState_R(const QMState &state) const
Definition orbitals.cc:535
bool hasUnrestrictedOrbitals() const
Report whether separate beta-spin orbitals are present.
Definition orbitals.h:630
Eigen::VectorXd lmos_energies_
Definition orbitals.h:734
tools::EigenSystem BSE_singlet_
Definition orbitals.h:776
tools::EigenSystem QPdiag_beta_
Definition orbitals.h:794
void PrepareDimerGuess(const Orbitals &orbitalsA, const Orbitals &orbitalsB)
Guess for a dimer based on monomer orbitals.
Definition orbitals.cc:906
tools::EigenSystem mos_beta_
Definition orbitals.h:728
std::string grid_quality_
Definition orbitals.h:766
bool hasMOs() const
Report whether alpha/restricted molecular orbitals are available.
Definition orbitals.h:185
double getTotalStateEnergy(const QMState &state) const
Return the absolute total energy of the requested state in Hartree.
Definition orbitals.cc:676
Eigen::VectorXd BSE_triplet_energies_dynamic_
Definition orbitals.h:782
void ReadBasisSetsFromCpt(CheckpointReader &r)
Deserialize attached AO basis sets from an already-open checkpoint reader.
Definition orbitals.cc:1076
std::array< Eigen::MatrixXd, 2 > DensityMatrixGroundStateSpinResolved() const
Definition orbitals.cc:1231
Eigen::MatrixXd QPpert_energies_beta_
Definition orbitals.h:791
void WriteBasisSetsToCpt(CheckpointWriter &w) const
Definition orbitals.cc:983
std::vector< Index > CheckDegeneracy(Index level, double energy_difference) const
Definition orbitals.cc:56
Eigen::MatrixXd inactivedensity_
Definition orbitals.h:736
std::string getCalculationType() const
Return the stored calculation-type tag.
Definition orbitals.h:242
std::array< Eigen::MatrixXd, 3 > CalcFreeTransition_Dipoles() const
Definition orbitals.cc:742
Eigen::MatrixXd CalcAuxMat_cc(const Eigen::VectorXd &coeffs) const
Definition orbitals.cc:584
Eigen::MatrixXd expandedMOs_
Definition orbitals.h:737
void setNumberOfAlphaElectrons(Index electrons)
Store the total number of alpha electrons.
Definition orbitals.h:139
Eigen::VectorXd Oscillatorstrengths() const
Definition orbitals.cc:643
std::vector< Eigen::Vector3d > transition_dipoles_
Definition orbitals.h:777
static constexpr int orbitals_version()
Definition orbitals.h:804
std::string functionalname_
Definition orbitals.h:765
Eigen::MatrixXd lmos_
Definition orbitals.h:733
Eigen::MatrixXd DensityMatrixWithoutGS(const QMState &state) const
Definition orbitals.cc:121
Eigen::MatrixXd DensityMatrixFull(const QMState &state) const
Definition orbitals.cc:150
Eigen::MatrixXd CalcAuxMat_vv(const Eigen::VectorXd &coeffs) const
Definition orbitals.cc:578
void SetupAuxBasis(std::string aux_basis_name)
Definition orbitals.cc:108
std::string qm_package_
Definition orbitals.h:767
Index getLumoAlpha() const
Return the alpha-spin LUMO index inferred from the stored electron counts.
Definition orbitals.h:75
void setNumberOfBetaElectrons(Index electrons)
Store the total number of beta electrons.
Definition orbitals.h:144
tools::EigenSystem BSE_triplet_
Definition orbitals.h:778
void setBSEindices(Index vmin, Index cmax)
Definition orbitals.h:395
const Eigen::MatrixXd & getInactiveDensity() const
Return the inactive-region density matrix used in embedding workflows.
Definition orbitals.h:620
std::vector< Index > SortEnergies()
Definition orbitals.cc:81
Index getHomoBeta() const
Return the beta-spin HOMO index.
Definition orbitals.h:101
std::array< Eigen::MatrixXd, 2 > DensityMatrixExcitedState_AR(const QMState &state) const
Definition orbitals.cc:590
double getExcitedStateEnergy(const QMState &state) const
Return the excitation energy of the requested state in Hartree.
Definition orbitals.cc:685
void setECPName(const std::string &ECP)
Store the effective core potential label.
Definition orbitals.h:170
void setNumberOfOccupiedLevels(Index occupied_levels)
Definition orbitals.h:122
Index occupied_levels_beta_
Definition orbitals.h:719
Orbitals()
Construct an empty orbital container with default metadata.
Definition orbitals.cc:46
tools::EigenSystem mos_
Definition orbitals.h:727
tools::EigenSystem QPdiag_
Definition orbitals.h:774
Eigen::VectorXd rpa_inputenergies_beta_
Definition orbitals.h:788
Eigen::MatrixXd DensityMatrixKSstate(const QMState &state) const
Build the AO density matrix for a single KS orbital state.
Definition orbitals.cc:232
Eigen::MatrixXd DensityMatrixQuasiParticle(const QMState &state) const
Build the AO density matrix for a quasiparticle state.
Definition orbitals.cc:265
Index getBasisSetSize() const
Return the number of AO basis functions in the DFT basis.
Definition orbitals.h:72
Eigen::MatrixXd QPpert_energies_alpha_
Definition orbitals.h:790
Eigen::VectorXd BSE_singlet_energies_dynamic_
Definition orbitals.h:781
Index getGWAmin() const
Return the lower GW orbital index.
Definition orbitals.h:361
const tools::EigenSystem & MOs() const
Return read-only access to alpha/restricted molecular orbitals.
Definition orbitals.h:192
Eigen::MatrixXd DensityMatrixGroundState() const
Build the ground-state AO density matrix from the stored orbitals.
Definition orbitals.cc:220
std::string CalcType_
Definition orbitals.h:725
Index getLumoBeta() const
Definition orbitals.h:87
const QMMolecule & QMAtoms() const
Return read-only access to the molecular geometry.
Definition orbitals.h:262
tools::EigenSystem mos_embedding_
Definition orbitals.h:731
Eigen::VectorXd rpa_inputenergies_alpha_
Definition orbitals.h:787
void ReadFromCpt(const std::string &filename)
Read the orbital container from a checkpoint file on disk.
Definition orbitals.cc:1063
tools::EigenSystem BSE_uks_
Definition orbitals.h:796
void setNumberOfOccupiedLevelsBeta(Index occupied_levels_beta)
Store the number of occupied beta-spin orbitals.
Definition orbitals.h:134
void setChargeAndSpin(Index charge, Index spin)
Definition orbitals.h:246
Eigen::VectorXd QPpert_energies_
Definition orbitals.h:771
void WriteToCpt(const std::string &filename) const
Write the orbital container to a checkpoint file on disk.
Definition orbitals.cc:970
const std::string & getECPName() const
Return the effective core potential label.
Definition orbitals.h:167
const std::string & getDFTbasisName() const
Return the DFT basis-set name.
Definition orbitals.h:304
void SetupDftBasis(std::string basis_name)
Build and attach the DFT AO basis from the stored molecular geometry.
Definition orbitals.cc:99
std::array< Eigen::MatrixXd, 2 > DensityMatrixExcitedState(const QMState &state) const
Build separate hole and electron AO densities for an excited state.
Definition orbitals.cc:413
std::string ECP_
Definition orbitals.h:722
Index number_alpha_electrons_
Definition orbitals.h:720
Eigen::Vector3d CalcElDipole(const QMState &state) const
Compute the electronic dipole moment associated with a state density.
Definition orbitals.cc:288
Index getHomoAlpha() const
Return the alpha-spin HOMO index.
Definition orbitals.h:83
const AOBasis & getDftBasis() const
Return the DFT AO basis, throwing if it has not been initialized.
Definition orbitals.h:314
QMMolecule atoms_
Definition orbitals.h:739
Index getLumo() const
Return the default LUMO index used by spin-agnostic callers.
Definition orbitals.h:105
container for QM atoms
Definition qmatom.h:37
std::string ToLongString() const
Definition qmstate.cc:72
bool isKSState() const
Definition qmstate.h:92
bool isPQPState() const
Definition qmstate.h:94
bool isExciton() const
Definition qmstate.h:73
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
Definition qmstate.h:135
std::string ToString() const
Definition qmstate.cc:155
Index StateIdx() const
Definition qmstate.h:157
bool isTransition() const
Definition qmstate.h:156
const QMStateType & Type() const
Definition qmstate.h:154
const std::string & ToolsVersionStr()
Definition version.cc:33
std::recursive_mutex & Hdf5Mutex()
Charge transport classes.
Definition ERIs.h:28
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26