1 /*
2  *            Copyright 2009-2020 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/xtp/aomatrix.h"
28 #include "votca/xtp/orbitals.h"
29 #include "votca/xtp/orbreorder.h"
30 #include "votca/xtp/qmstate.h"
31 #include "votca/xtp/vc2index.h"
32 #include "votca/xtp/version.h"
33 
34 namespace votca {
35 namespace xtp {
36 
Orbitals()37 Orbitals::Orbitals() : atoms_("", 0) { ; }
38 
39 /**
40  *
41  * @param  energy_difference_ [ev] Two levels are degenerate if their energy is
42  * smaller than this value
43  * @return vector with indices off all orbitals degenerate to this including
44  * itself
45  */
CheckDegeneracy(Index level,double energy_difference) const46 std::vector<Index> Orbitals::CheckDegeneracy(Index level,
47                                              double energy_difference) const {
48 
49   std::vector<Index> result;
50   if (level > mos_.eigenvalues().size()) {
51     throw std::runtime_error(
52         "Level for degeneracy is higher than maximum level");
53   }
54   double MOEnergyLevel = mos_.eigenvalues()(level);
55 
56   for (Index i = 0; i < mos_.eigenvalues().size(); ++i) {
57     if (std::abs(mos_.eigenvalues()(i) - MOEnergyLevel) < energy_difference) {
58       result.push_back(i);
59     }
60   }
61 
62   if (result.empty()) {
63     result.push_back(level);
64   }
65   return result;
66 }
67 
SortEnergies()68 std::vector<Index> Orbitals::SortEnergies() {
69   std::vector<Index> index = std::vector<Index>(mos_.eigenvalues().size());
70   std::iota(index.begin(), index.end(), 0);
71   std::stable_sort(index.begin(), index.end(), [this](Index i1, Index i2) {
72     return this->MOs().eigenvalues()[i1] < this->MOs().eigenvalues()[i2];
73   });
74   return index;
75 }
76 
77 /*
78  * Returns the density matrix relative to the ground state, for the full density
79  * use DensityMatrixFull
80  */
DensityMatrixWithoutGS(const QMState & state) const81 Eigen::MatrixXd Orbitals::DensityMatrixWithoutGS(const QMState& state) const {
82   if (state.Type().isExciton()) {
83     std::array<Eigen::MatrixXd, 2> DMAT = DensityMatrixExcitedState(state);
84     return DMAT[1] - DMAT[0];
85   } else if (state.Type().isKSState() || state.Type().isPQPState()) {
86     return DensityMatrixKSstate(state);
87   } else if (state.Type() == QMStateType::DQPstate) {
88     Eigen::MatrixXd DMATQP = DensityMatrixQuasiParticle(state);
89     if (state.StateIdx() > getHomo()) {
90       return DMATQP;
91     } else {
92       return -DMATQP;
93     }
94   } else {
95     throw std::runtime_error(
96         "DensityMatrixWithoutGS does not yet implement QMStateType:" +
97         state.Type().ToLongString());
98   }
99 }
100 
101 /*
102  * Returns the density matrix with the ground state density, for the partial
103  * density relative to the ground state use DensityMatrixWithoutGS
104  */
DensityMatrixFull(const QMState & state) const105 Eigen::MatrixXd Orbitals::DensityMatrixFull(const QMState& state) const {
106   if (state.isTransition()) {
107     return this->TransitionDensityMatrix(state);
108   }
109   Eigen::MatrixXd result = this->DensityMatrixGroundState();
110   if (state.Type().isExciton()) {
111     std::array<Eigen::MatrixXd, 2> DMAT = DensityMatrixExcitedState(state);
112     result = result - DMAT[0] + DMAT[1];  // Ground state + hole_contribution +
113                                           // electron contribution
114   } else if (state.Type() == QMStateType::DQPstate) {
115     Eigen::MatrixXd DMATQP = DensityMatrixQuasiParticle(state);
116     if (state.StateIdx() > getHomo()) {
117       result += DMATQP;
118     } else {
119       result -= DMATQP;
120     }
121   } else if (state.Type() != QMStateType::Gstate) {
122     throw std::runtime_error(
123         "DensityMatrixFull does not yet implement QMStateType:" +
124         state.Type().ToLongString());
125   }
126   return result;
127 }
128 
129 // Determine ground state density matrix
DensityMatrixGroundState() const130 Eigen::MatrixXd Orbitals::DensityMatrixGroundState() const {
131   if (!hasMOs()) {
132     throw std::runtime_error("Orbitals file does not contain MO coefficients");
133   }
134   Eigen::MatrixXd occstates = mos_.eigenvectors().leftCols(occupied_levels_);
135   Eigen::MatrixXd dmatGS = 2.0 * occstates * occstates.transpose();
136   return dmatGS;
137 }
138 
139 // Density matrix for a single KS orbital
DensityMatrixKSstate(const QMState & state) const140 Eigen::MatrixXd Orbitals::DensityMatrixKSstate(const QMState& state) const {
141   if (!hasMOs()) {
142     throw std::runtime_error("Orbitals file does not contain MO coefficients");
143   }
144   if (state.Type() != QMStateType::KSstate &&
145       state.Type() != QMStateType::PQPstate) {
146     throw std::runtime_error("State:" + state.ToString() +
147                              " is not a Kohn Sham state");
148   }
149   Eigen::VectorXd KSstate = mos_.eigenvectors().col(state.StateIdx());
150   Eigen::MatrixXd dmatKS = KSstate * KSstate.transpose();
151   return dmatKS;
152 }
153 
CalculateQParticleAORepresentation() const154 Eigen::MatrixXd Orbitals::CalculateQParticleAORepresentation() const {
155   if (!hasQPdiag()) {
156     throw std::runtime_error("Orbitals file does not contain QP coefficients");
157   }
158   return mos_.eigenvectors().middleCols(qpmin_, qpmax_ - qpmin_ + 1) *
159          QPdiag_.eigenvectors();
160 }
161 
162 // Determine QuasiParticle Density Matrix
DensityMatrixQuasiParticle(const QMState & state) const163 Eigen::MatrixXd Orbitals::DensityMatrixQuasiParticle(
164     const QMState& state) const {
165   if (state.Type() != QMStateType::DQPstate) {
166     throw std::runtime_error("State:" + state.ToString() +
167                              " is not a quasiparticle state");
168   }
169   Eigen::MatrixXd lambda = CalculateQParticleAORepresentation();
170   Eigen::MatrixXd dmatQP = lambda.col(state.StateIdx() - qpmin_) *
171                            lambda.col(state.StateIdx() - qpmin_).transpose();
172   return dmatQP;
173 }
174 
CalcElDipole(const QMState & state) const175 Eigen::Vector3d Orbitals::CalcElDipole(const QMState& state) const {
176   Eigen::Vector3d nuclei_dip = Eigen::Vector3d::Zero();
177   if (!state.isTransition()) {
178     for (const QMAtom& atom : atoms_) {
179       nuclei_dip += (atom.getPos() - atoms_.getPos()) * atom.getNuccharge();
180     }
181   }
182   AOBasis basis = SetupDftBasis();
183   AODipole dipole;
184   dipole.setCenter(atoms_.getPos());
185   dipole.Fill(basis);
186 
187   Eigen::MatrixXd dmat = this->DensityMatrixFull(state);
188   Eigen::Vector3d electronic_dip;
189   for (Index i = 0; i < 3; ++i) {
190     electronic_dip(i) = dmat.cwiseProduct(dipole.Matrix()[i]).sum();
191   }
192   return nuclei_dip - electronic_dip;
193 }
194 
TransitionDensityMatrix(const QMState & state) const195 Eigen::MatrixXd Orbitals::TransitionDensityMatrix(const QMState& state) const {
196   if (state.Type() != QMStateType::Singlet) {
197     throw std::runtime_error(
198         "Spin type not known for transition density matrix. Available only for "
199         "singlet");
200   }
201   const Eigen::MatrixXd& BSECoefs = BSE_singlet_.eigenvectors();
202   if (BSECoefs.cols() < state.StateIdx() + 1 || BSECoefs.rows() < 2) {
203     throw std::runtime_error("Orbitals object has no information about state:" +
204                              state.ToString());
205   }
206 
207   // The Transition dipole is sqrt2 bigger because of the spin, the excited
208   // state is a linear combination of 2 slater determinants, where either alpha
209   // or beta spin electron is excited
210 
211   /*Trying to implement D_{alpha,beta}=
212    * sqrt2*sum_{i}^{occ}sum_{j}^{virt}{BSEcoef(i,j)*MOcoef(alpha,i)*MOcoef(beta,j)}
213    */
214   // c stands for conduction band and thus virtual orbitals
215   // v stand for valence band and thus occupied orbitals
216 
217   Eigen::VectorXd coeffs = BSECoefs.col(state.StateIdx());
218 
219   if (!useTDA_) {
220     coeffs += BSE_singlet_.eigenvectors2().col(state.StateIdx());
221   }
222   coeffs *= std::sqrt(2.0);
223   auto occlevels = mos_.eigenvectors().middleCols(bse_vmin_, bse_vtotal_);
224   auto virtlevels = mos_.eigenvectors().middleCols(bse_cmin_, bse_ctotal_);
225   Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_,
226                                         bse_vtotal_);
227 
228   return occlevels * mat.transpose() * virtlevels.transpose();
229 }
230 
DensityMatrixExcitedState(const QMState & state) const231 std::array<Eigen::MatrixXd, 2> Orbitals::DensityMatrixExcitedState(
232     const QMState& state) const {
233   std::array<Eigen::MatrixXd, 2> dmat = DensityMatrixExcitedState_R(state);
234   if (!useTDA_) {
235     std::array<Eigen::MatrixXd, 2> dmat_AR =
236         DensityMatrixExcitedState_AR(state);
237     dmat[0] -= dmat_AR[0];
238     dmat[1] -= dmat_AR[1];
239   }
240   return dmat;
241 }
242 
243 // Excited state density matrix
244 
DensityMatrixExcitedState_R(const QMState & state) const245 std::array<Eigen::MatrixXd, 2> Orbitals::DensityMatrixExcitedState_R(
246     const QMState& state) const {
247   if (!state.Type().isExciton()) {
248     throw std::runtime_error(
249         "Spin type not known for density matrix. Available are singlet and "
250         "triplet");
251   }
252 
253   const Eigen::MatrixXd& BSECoefs = (state.Type() == QMStateType::Singlet)
254                                         ? BSE_singlet_.eigenvectors()
255                                         : BSE_triplet_.eigenvectors();
256   if (BSECoefs.cols() < state.StateIdx() + 1 || BSECoefs.rows() < 2) {
257     throw std::runtime_error("Orbitals object has no information about state:" +
258                              state.ToString());
259   }
260   /******
261    *
262    *    Density matrix for GW-BSE based excitations
263    *
264    *    - electron contribution
265    *      D_ab = \sum{vc} \sum{c'} A_{vc}A_{vc'} mo_a(c)mo_b(c')
266    *
267    *    - hole contribution
268    *      D_ab = \sum{vc} \sum{v'} A_{vc}A_{v'c} mo_a(v)mo_b(v')
269    *
270    */
271 
272   Eigen::VectorXd coeffs = BSECoefs.col(state.StateIdx());
273 
274   std::array<Eigen::MatrixXd, 2> dmatEX;
275   // hole part as matrix products
276   Eigen::MatrixXd occlevels =
277       mos_.eigenvectors().middleCols(bse_vmin_, bse_vtotal_);
278   dmatEX[0] = occlevels * CalcAuxMat_vv(coeffs) * occlevels.transpose();
279 
280   // electron part as matrix products
281   Eigen::MatrixXd virtlevels =
282       mos_.eigenvectors().middleCols(bse_cmin_, bse_ctotal_);
283   dmatEX[1] = virtlevels * CalcAuxMat_cc(coeffs) * virtlevels.transpose();
284 
285   return dmatEX;
286 }
287 
CalcAuxMat_vv(const Eigen::VectorXd & coeffs) const288 Eigen::MatrixXd Orbitals::CalcAuxMat_vv(const Eigen::VectorXd& coeffs) const {
289   const Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_,
290                                               bse_vtotal_);
291   return mat.transpose() * mat;
292 }
293 
CalcAuxMat_cc(const Eigen::VectorXd & coeffs) const294 Eigen::MatrixXd Orbitals::CalcAuxMat_cc(const Eigen::VectorXd& coeffs) const {
295   const Eigen::Map<const Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_,
296                                               bse_vtotal_);
297   return mat * mat.transpose();
298 }
299 
DensityMatrixExcitedState_AR(const QMState & state) const300 std::array<Eigen::MatrixXd, 2> Orbitals::DensityMatrixExcitedState_AR(
301     const QMState& state) const {
302 
303   if (!state.Type().isExciton()) {
304     throw std::runtime_error(
305         "Spin type not known for density matrix. Available are singlet and "
306         "triplet");
307   }
308 
309   const Eigen::MatrixXd& BSECoefs_AR = (state.Type() == QMStateType::Singlet)
310                                            ? BSE_singlet_.eigenvectors2()
311                                            : BSE_triplet_.eigenvectors2();
312   if (BSECoefs_AR.cols() < state.StateIdx() + 1 || BSECoefs_AR.rows() < 2) {
313     throw std::runtime_error("Orbitals object has no information about state:" +
314                              state.ToString());
315   }
316   /******
317    *
318    *    Density matrix for GW-BSE based excitations
319    *
320    *    - electron contribution
321    *      D_ab = \sum{vc} \sum{v'} B_{vc}B_{v'c} mo_a(v)mo_b(v')
322    *
323    *    - hole contribution
324    *      D_ab = \sum{vc} \sum{c'} B_{vc}B_{vc'} mo_a(c)mo_b(c')
325    *
326    *
327    *   more efficient:
328    *
329    *   - electron contribution
330    *      D_ab = \sum{v} \sum{v'} mo_a(v)mo_b(v') [ \sum{c} B_{vc}B_{v'c} ]
331    *           = \sum{v} \sum{v'} mo_a(v)mo_b(v') B_{vv'}
332    *
333    *   - hole contribution
334    *      D_ab = \sum{c} \sum{c'} mo_a(c)mo_b(c') [ \sum{v} B_{vc}B_{vc'} ]
335    *           = \sum{c} \sum{c'} mo_a(c)mo_b(c') B_{cc'}
336    *
337    */
338 
339   Eigen::VectorXd coeffs = BSECoefs_AR.col(state.StateIdx());
340 
341   std::array<Eigen::MatrixXd, 2> dmatAR;
342   Eigen::MatrixXd virtlevels =
343       mos_.eigenvectors().middleCols(bse_cmin_, bse_ctotal_);
344   dmatAR[0] = virtlevels * CalcAuxMat_cc(coeffs) * virtlevels.transpose();
345   // electron part as matrix products
346   Eigen::MatrixXd occlevels =
347       mos_.eigenvectors().middleCols(bse_vmin_, bse_vtotal_);
348   dmatAR[1] = occlevels * CalcAuxMat_vv(coeffs) * occlevels.transpose();
349 
350   return dmatAR;
351 }
352 
Oscillatorstrengths() const353 Eigen::VectorXd Orbitals::Oscillatorstrengths() const {
354 
355   Index size = Index(transition_dipoles_.size());
356   if (size > BSE_singlet_.eigenvalues().size()) {
357     size = BSE_singlet_.eigenvalues().size();
358   }
359   Eigen::VectorXd oscs = Eigen::VectorXd::Zero(size);
360   for (Index i = 0; i < size; ++i) {
361     oscs(i) = transition_dipoles_[i].squaredNorm() * 2.0 / 3.0 *
362               (BSE_singlet_.eigenvalues()(i));
363   }
364   return oscs;
365 }
366 
getTotalStateEnergy(const QMState & state) const367 double Orbitals::getTotalStateEnergy(const QMState& state) const {
368   double total_energy = getDFTTotalEnergy();
369   if (state.Type() == QMStateType::Gstate) {
370     return total_energy;
371   }
372   total_energy += getExcitedStateEnergy(state);
373   return total_energy;
374 }
375 
getExcitedStateEnergy(const QMState & state) const376 double Orbitals::getExcitedStateEnergy(const QMState& state) const {
377 
378   double omega = 0.0;
379   if (state.isTransition()) {
380     throw std::runtime_error(
381         "Total Energy does not exist for transition state");
382   }
383 
384   if (state.Type() == QMStateType::Singlet) {
385     if (BSE_singlet_.eigenvalues().size() < state.StateIdx() + 1) {
386       throw std::runtime_error("Orbitals::getTotalEnergy You want " +
387                                state.ToString() +
388                                " which has not been calculated");
389     }
390     omega = BSE_singlet_.eigenvalues()[state.StateIdx()];
391   } else if (state.Type() == QMStateType::Triplet) {
392     if (BSE_triplet_.eigenvalues().size() < state.StateIdx() + 1) {
393       throw std::runtime_error("Orbitals::getTotalEnergy You want " +
394                                state.ToString() +
395                                " which has not been calculated");
396     }
397     omega = BSE_triplet_.eigenvalues()[state.StateIdx()];
398   } else if (state.Type() == QMStateType::DQPstate) {
399     if (QPdiag_.eigenvalues().size() < state.StateIdx() + 1 - getGWAmin()) {
400       throw std::runtime_error("Orbitals::getTotalEnergy You want " +
401                                state.ToString() +
402                                " which has not been calculated");
403     }
404     return QPdiag_.eigenvalues()[state.StateIdx() - getGWAmin()];
405   } else if (state.Type() == QMStateType::KSstate) {
406     if (mos_.eigenvalues().size() < state.StateIdx() + 1) {
407       throw std::runtime_error("Orbitals::getTotalEnergy You want " +
408                                state.ToString() +
409                                " which has not been calculated");
410     }
411     return mos_.eigenvalues()[state.StateIdx()];
412   } else if (state.Type() == QMStateType::PQPstate) {
413     if (this->QPpert_energies_.rows() < state.StateIdx() + 1 - getGWAmin()) {
414       throw std::runtime_error("Orbitals::getTotalEnergy You want " +
415                                state.ToString() +
416                                " which has not been calculated");
417     }
418     return QPpert_energies_(state.StateIdx() - getGWAmin(), 3);
419   } else {
420     throw std::runtime_error(
421         "GetTotalEnergy only knows states:singlet,triplet,KS,DQP,PQP");
422   }
423   return omega;  //  e.g. hartree
424 }
425 
CalcFreeTransition_Dipoles() const426 std::array<Eigen::MatrixXd, 3> Orbitals::CalcFreeTransition_Dipoles() const {
427   const Eigen::MatrixXd& dft_orbitals = mos_.eigenvectors();
428   AOBasis basis = SetupDftBasis();
429   // Testing electric dipole AOMatrix
430   AODipole dft_dipole;
431   dft_dipole.Fill(basis);
432 
433   // now transition dipole elements for free interlevel transitions
434   std::array<Eigen::MatrixXd, 3> interlevel_dipoles;
435 
436   Eigen::MatrixXd empty = dft_orbitals.middleCols(bse_cmin_, bse_ctotal_);
437   Eigen::MatrixXd occ = dft_orbitals.middleCols(bse_vmin_, bse_vtotal_);
438   for (Index i = 0; i < 3; i++) {
439     interlevel_dipoles[i] = empty.transpose() * dft_dipole.Matrix()[i] * occ;
440   }
441   return interlevel_dipoles;
442 }
443 
CalcCoupledTransition_Dipoles()444 void Orbitals::CalcCoupledTransition_Dipoles() {
445   std::array<Eigen::MatrixXd, 3> interlevel_dipoles =
446       CalcFreeTransition_Dipoles();
447   Index numofstates = BSE_singlet_.eigenvalues().size();
448   transition_dipoles_.resize(0);
449   transition_dipoles_.reserve(numofstates);
450   const double sqrt2 = std::sqrt(2.0);
451   for (Index i_exc = 0; i_exc < numofstates; i_exc++) {
452 
453     Eigen::VectorXd coeffs = BSE_singlet_.eigenvectors().col(i_exc);
454     if (!useTDA_) {
455       coeffs += BSE_singlet_.eigenvectors2().col(i_exc);
456     }
457     Eigen::Map<Eigen::MatrixXd> mat(coeffs.data(), bse_ctotal_, bse_vtotal_);
458     Eigen::Vector3d tdipole = Eigen::Vector3d::Zero();
459     for (Index i = 0; i < 3; i++) {
460       tdipole[i] = mat.cwiseProduct(interlevel_dipoles[i]).sum();
461     }
462     // The Transition dipole is sqrt2 bigger because of the spin, the
463     // excited state is a linear combination of 2 slater determinants,
464     // where either alpha or beta spin electron is excited
465     transition_dipoles_.push_back(-sqrt2 * tdipole);  //- because electrons are
466                                                       // negatively charged
467   }
468 }
469 
OrderMOsbyEnergy()470 void Orbitals::OrderMOsbyEnergy() {
471   std::vector<Index> sort_index = SortEnergies();
472   tools::EigenSystem MO_copy = mos_;
473   Index size = mos_.eigenvalues().size();
474   for (Index i = 0; i < size; ++i) {
475     mos_.eigenvalues()(i) = MO_copy.eigenvalues()(sort_index[i]);
476   }
477   for (Index i = 0; i < size; ++i) {
478     mos_.eigenvectors().col(i) = MO_copy.eigenvectors().col(sort_index[i]);
479   }
480 }
481 
482 /**
483  * \brief Guess for a dimer based on monomer orbitals
484  *
485  * Given two monomer orbitals (A and B) constructs a guess for dimer
486  * orbitals: | A 0 | and energies: [EA, EB]
487  *           | 0 B |
488  */
PrepareDimerGuess(const Orbitals & orbitalsA,const Orbitals & orbitalsB)489 void Orbitals::PrepareDimerGuess(const Orbitals& orbitalsA,
490                                  const Orbitals& orbitalsB) {
491 
492   // constructing the direct product orbA x orbB
493   Index basisA = orbitalsA.getBasisSetSize();
494   Index basisB = orbitalsB.getBasisSetSize();
495 
496   Index electronsA = orbitalsA.getNumberOfAlphaElectrons();
497   Index electronsB = orbitalsB.getNumberOfAlphaElectrons();
498 
499   mos_.eigenvectors() = Eigen::MatrixXd::Zero(basisA + basisB, basisA + basisB);
500 
501   // AxB = | A 0 |  //   A = [EA, EB]  //
502   //       | 0 B |  //                 //
503   if (orbitalsA.getDFTbasisName() != orbitalsB.getDFTbasisName()) {
504     throw std::runtime_error("Basissets of Orbitals A and B differ " +
505                              orbitalsA.getDFTbasisName() + ":" +
506                              orbitalsB.getDFTbasisName());
507   }
508   this->setDFTbasisName(orbitalsA.getDFTbasisName());
509   if (orbitalsA.getECPName() != orbitalsB.getECPName()) {
510     throw std::runtime_error("ECPs of Orbitals A and B differ " +
511                              orbitalsA.getECPName() + ":" +
512                              orbitalsB.getECPName());
513   }
514   this->setECPName(orbitalsA.getECPName());
515   this->setBasisSetSize(basisA + basisB);
516   this->setNumberOfOccupiedLevels(electronsA + electronsB);
517   this->setNumberOfAlphaElectrons(electronsA + electronsB);
518 
519   mos_.eigenvectors().topLeftCorner(basisA, basisA) =
520       orbitalsA.MOs().eigenvectors();
521   mos_.eigenvectors().bottomRightCorner(basisB, basisB) =
522       orbitalsB.MOs().eigenvectors();
523 
524   mos_.eigenvalues().resize(basisA + basisB);
525 
526   mos_.eigenvalues().head(basisA) = orbitalsA.MOs().eigenvalues();
527   mos_.eigenvalues().tail(basisB) = orbitalsB.MOs().eigenvalues();
528 
529   OrderMOsbyEnergy();
530 
531   return;
532 }
533 
WriteToCpt(const std::string & filename) const534 void Orbitals::WriteToCpt(const std::string& filename) const {
535   CheckpointFile cpf(filename, CheckpointAccessLevel::CREATE);
536   WriteToCpt(cpf);
537 }
538 
WriteToCpt(CheckpointFile f) const539 void Orbitals::WriteToCpt(CheckpointFile f) const {
540   WriteToCpt(f.getWriter("/QMdata"));
541 }
542 
WriteToCpt(CheckpointWriter w) const543 void Orbitals::WriteToCpt(CheckpointWriter w) const {
544   w(XtpVersionStr(), "XTPVersion");
545   w(orbitals_version(), "version");
546   w(basis_set_size_, "basis_set_size");
547   w(occupied_levels_, "occupied_levels");
548   w(number_alpha_electrons_, "number_alpha_electrons");
549 
550   w(mos_, "mos");
551 
552   CheckpointWriter molgroup = w.openChild("qmmolecule");
553   atoms_.WriteToCpt(molgroup);
554 
555   w(qm_energy_, "qm_energy");
556   w(qm_package_, "qm_package");
557 
558   w(dftbasis_, "dftbasis");
559   w(auxbasis_, "auxbasis");
560 
561   w(rpamin_, "rpamin");
562   w(rpamax_, "rpamax");
563   w(qpmin_, "qpmin");
564   w(qpmax_, "qpmax");
565   w(bse_vmin_, "bse_vmin");
566   w(bse_cmax_, "bse_cmax");
567   w(functionalname_, "XCFunctional");
568   w(grid_quality_, "XC_grid_quality");
569   w(ScaHFX_, "ScaHFX");
570 
571   w(useTDA_, "useTDA");
572   w(ECP_, "ECP");
573 
574   w(rpa_inputenergies_, "RPA_inputenergies");
575   w(QPpert_energies_, "QPpert_energies");
576 
577   w(QPdiag_, "QPdiag");
578 
579   w(BSE_singlet_, "BSE_singlet");
580 
581   w(transition_dipoles_, "transition_dipoles");
582 
583   w(BSE_triplet_, "BSE_triplet");
584 
585   w(use_Hqp_offdiag_, "use_Hqp_offdiag");
586 
587   w(BSE_singlet_energies_dynamic_, "BSE_singlet_dynamic");
588 
589   w(BSE_triplet_energies_dynamic_, "BSE_triplet_dynamic");
590 }
591 
ReadFromCpt(const std::string & filename)592 void Orbitals::ReadFromCpt(const std::string& filename) {
593   CheckpointFile cpf(filename, CheckpointAccessLevel::READ);
594   ReadFromCpt(cpf);
595 }
596 
ReadFromCpt(CheckpointFile f)597 void Orbitals::ReadFromCpt(CheckpointFile f) {
598   ReadFromCpt(f.getReader("/QMdata"));
599 }
600 
ReadFromCpt(CheckpointReader r)601 void Orbitals::ReadFromCpt(CheckpointReader r) {
602   r(basis_set_size_, "basis_set_size");
603   r(occupied_levels_, "occupied_levels");
604   r(number_alpha_electrons_, "number_alpha_electrons");
605   int version;
606   r(version, "version");
607   // Read qmatoms
608   CheckpointReader molgroup = r.openChild("qmmolecule");
609   atoms_.ReadFromCpt(molgroup);
610 
611   r(qm_energy_, "qm_energy");
612   r(qm_package_, "qm_package");
613 
614   r(dftbasis_, "dftbasis");
615   r(auxbasis_, "auxbasis");
616 
617   r(version, "version");
618   r(mos_, "mos");
619   if (version < 3) {
620     // clang-format off
621     std::array<Index, 49> votcaOrder_old = {
622         0,                             // s
623         0, -1, 1,                      // p
624         0, -1, 1, -2, 2,               // d
625         0, -1, 1, -2, 2, -3, 3,        // f
626         0, -1, 1, -2, 2, -3, 3, -4, 4,  // g
627         0, -1, 1, -2, 2, -3, 3, -4, 4,-5,5,  // h
628         0, -1, 1, -2, 2, -3, 3, -4, 4,-5,5,-6,6  // i
629     };
630     // clang-format on
631 
632     std::array<Index, 49> multiplier;
633     multiplier.fill(1);
634     OrbReorder ord(votcaOrder_old, multiplier);
635     ord.reorderOrbitals(mos_.eigenvectors(), this->SetupDftBasis());
636   }
637 
638   r(rpamin_, "rpamin");
639   r(rpamax_, "rpamax");
640   r(qpmin_, "qpmin");
641   r(qpmax_, "qpmax");
642   r(bse_vmin_, "bse_vmin");
643   r(bse_cmax_, "bse_cmax");
644   setBSEindices(bse_vmin_, bse_cmax_);
645   try {
646     r(functionalname_, "XCFunctional");
647     r(grid_quality_, "XC_grid_quality");
648   } catch (std::runtime_error& e) {
649     grid_quality_ = "medium";
650   }
651   r(ScaHFX_, "ScaHFX");
652   r(useTDA_, "useTDA");
653   r(ECP_, "ECP");
654 
655   r(rpa_inputenergies_, "RPA_inputenergies");
656   r(QPpert_energies_, "QPpert_energies");
657   r(QPdiag_, "QPdiag");
658 
659   r(BSE_singlet_, "BSE_singlet");
660 
661   r(transition_dipoles_, "transition_dipoles");
662 
663   r(BSE_triplet_, "BSE_triplet");
664 
665   r(use_Hqp_offdiag_, "use_Hqp_offdiag");
666 
667   if (version > 1) {
668     r(BSE_singlet_energies_dynamic_, "BSE_singlet_dynamic");
669 
670     r(BSE_triplet_energies_dynamic_, "BSE_triplet_dynamic");
671   }
672 }
673 }  // namespace xtp
674 }  // namespace votca
675