21#include <boost/format.hpp>
55 const std::string& name,
56 const Eigen::MatrixXd& mat,
61 for (
Index i = 0; i < mat.rows(); i++) {
63 for (
Index j = 0; j < mat.cols(); j++) {
64 if (j > 0) row_str +=
" ";
65 row_str += (format(
"%1$1.6e") % (mat(i, j) * conversion)).str();
67 mat_prop.
setAttribute(
"row_" + std::to_string(i), row_str);
84 coupling.
setAttribute(
"j", (format(
"%1$1.6e") % J).str());
108 const Eigen::MatrixXd& JAB_dimer,
109 const Eigen::MatrixXd& S_AxB,
110 const Eigen::VectorXd& moEnA_KS,
111 const Eigen::VectorXd& moEnB_KS,
112 const Eigen::VectorXd& moEnA_QP,
113 const Eigen::VectorXd& moEnB_QP,
double min_S_eval) {
115 for (
Index a = start_a; a <= end_a; ++a) {
116 for (
Index b = start_b; b <= end_b; ++b) {
129 bool hasQP_A = (moEnA_QP.size() == moEnA_KS.size());
130 bool hasQP_B = (moEnB_QP.size() == moEnB_KS.size());
133 carrier_summary.
add(
"monomer_energies",
"");
135 (hasQP_A && hasQP_B) ?
"true" :
"false");
138 for (
Index i = 0; i < moEnA_KS.size(); i++) {
140 e.setAttribute(
"level", start_a + i);
141 e.setAttribute(
"eV_KS", (format(
"%1$1.6e") % moEnA_KS(i)).str());
143 e.setAttribute(
"eV_QP", (format(
"%1$1.6e") % moEnA_QP(i)).str());
147 for (
Index i = 0; i < moEnB_KS.size(); i++) {
149 e.setAttribute(
"level", start_b + i);
150 e.setAttribute(
"eV_KS", (format(
"%1$1.6e") % moEnB_KS(i)).str());
152 e.setAttribute(
"eV_QP", (format(
"%1$1.6e") % moEnB_QP(i)).str());
159 Index levA_range = moEnA_KS.size();
160 Index levB_range = moEnB_KS.size();
166 JAB_dimer.topLeftCorner(levA_range, levA_range),
169 S_AxB.topLeftCorner(levA_range, levA_range));
172 JAB_dimer.bottomRightCorner(levB_range, levB_range),
175 S_AxB.bottomRightCorner(levB_range, levB_range));
177 JAB_dimer.topRightCorner(levA_range, levB_range),
180 S_AxB.topRightCorner(levA_range, levB_range));
187 (format(
"%1$1.6e") % min_S_eval).str());
189 (min_S_eval > 0.01) ?
"true" :
"false");
204 WriteCarrier(electron_summary, orbitalsA.
getLumo(),
215 if (std::abs(MOEnergies(orbital.
getHomo()) - MOEnergies(orbital.
getLumo())) <
217 throw std::runtime_error(
218 "Homo Lumo Gap is smaller than degeneracy. "
219 "Either your degeneracy is too large or your Homo and Lumo are "
227 minimal = *std::min_element(deg_min.begin(), deg_min.end());
230 maximal = *std::max_element(deg_max.begin(), deg_max.end());
232 std::pair<Index, Index> result;
233 result.first = minimal;
234 result.second = maximal - minimal + 1;
244 std::vector<Index> list_levelsA =
246 std::vector<Index> list_levelsB =
250 for (
Index iA : list_levelsA) {
252 for (
Index iB : list_levelsB) {
254 double JAB_one_level =
JAB(indexA, indexB);
255 JAB_sq += JAB_one_level * JAB_one_level;
258 return std::sqrt(JAB_sq /
259 double(list_levelsA.size() * list_levelsB.size())) *
285 if ((basisA == 0) || (basisB == 0)) {
286 throw std::runtime_error(
"Basis set size is not stored in monomers");
296 <<
"Levels:Basis A[" << levelsA <<
":" << basisA <<
"]"
297 <<
" B[" << levelsB <<
":" << basisB <<
"]" << flush;
299 if ((levelsA == 0) || (levelsB == 0)) {
300 throw std::runtime_error(
301 "No information about number of occupied/unoccupied levels is stored");
313 Eigen::MatrixXd overlap =
317 <<
"Projecting monomers onto dimer orbitals" << flush;
318 Eigen::MatrixXd A_AB = MOsA.transpose() * overlap.topRows(basisA);
319 Eigen::MatrixXd B_AB = MOsB.transpose() * overlap.bottomRows(basisB);
321 Eigen::VectorXd mag_A = A_AB.rowwise().squaredNorm();
322 if (mag_A.any() < 0.95) {
325 <<
"Projection of orbitals of monomer A on dimer is insufficient,mag="
326 << mag_A.minCoeff() << flush;
328 Eigen::VectorXd mag_B = B_AB.rowwise().squaredNorm();
329 if (mag_B.any() < 0.95) {
332 <<
"Projection of orbitals of monomer B on dimer is insufficient,mag="
333 << mag_B.minCoeff() << flush;
337 Eigen::MatrixXd psi_AxB_dimer_basis(levelsA + levelsB, A_AB.cols());
338 psi_AxB_dimer_basis.topRows(levelsA) = A_AB;
339 psi_AxB_dimer_basis.bottomRows(levelsB) = B_AB;
343 <<
"Projecting the Fock matrix onto the dimer basis" << flush;
344 Eigen::MatrixXd JAB_dimer_full = psi_AxB_dimer_basis *
346 psi_AxB_dimer_basis.transpose();
349 Eigen::MatrixXd S_AxB_full =
350 psi_AxB_dimer_basis * psi_AxB_dimer_basis.transpose();
352 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(S_AxB_full);
353 Eigen::MatrixXd Sm1 = es.operatorInverseSqrt();
355 << es.eigenvalues()(0) << flush;
358 JAB = Sm1 * JAB_dimer_full * Sm1;
380 Index elecsA = levelsA - holesA;
382 Index elecsB = levelsB - holesB;
387 std::vector<Index> hole_idx, elec_idx;
388 for (
Index i = 0; i < holesA; i++) hole_idx.push_back(i);
389 for (
Index i = 0; i < holesB; i++) hole_idx.push_back(levelsA + i);
390 for (
Index i = holesA; i < levelsA; i++) elec_idx.push_back(i);
391 for (
Index i = holesB; i < levelsB; i++) elec_idx.push_back(levelsA + i);
394 Index nh = hole_idx.size();
395 Eigen::MatrixXd JAB_hole(nh, nh), S_hole(nh, nh);
396 for (
Index i = 0; i < nh; i++) {
397 for (
Index j = 0; j < nh; j++) {
398 JAB_hole(i, j) = JAB_dimer_full(hole_idx[i], hole_idx[j]);
399 S_hole(i, j) = S_AxB_full(hole_idx[i], hole_idx[j]);
404 Index ne = elec_idx.size();
405 Eigen::MatrixXd JAB_elec(ne, ne), S_elec(ne, ne);
406 for (
Index i = 0; i < ne; i++) {
407 for (
Index j = 0; j < ne; j++) {
408 JAB_elec(i, j) = JAB_dimer_full(elec_idx[i], elec_idx[j]);
409 S_elec(i, j) = S_AxB_full(elec_idx[i], elec_idx[j]);
420 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_h(S_hole);
422 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_e(S_elec);
445 auto ExtractQPEnergies = [&](
const Orbitals& orb,
Index mo_start,
446 Index n_mos) -> Eigen::VectorXd {
447 if (!orb.
hasQPpert())
return Eigen::VectorXd{};
450 if (mo_start < qpmin || mo_start + n_mos - 1 > qpmax) {
452 <<
TimeStamp() <<
" WARNING: QPpert window [" << qpmin <<
"," << qpmax
453 <<
"] does not fully cover MO range [" << mo_start <<
","
454 << mo_start + n_mos - 1 <<
"] -- QP energies omitted for this range"
456 return Eigen::VectorXd{};
483 ExtractQPEnergies(orbitalsA, orbitalsA.
getLumo(), elecsA);
485 ExtractQPEnergies(orbitalsB, orbitalsB.
getLumo(), elecsB);
489 <<
TimeStamp() <<
" QPpert energies available for fragment A" << flush;
493 <<
TimeStamp() <<
" QPpert energies available for fragment B" << flush;
Eigen::MatrixXd CalculateOverlapMatrix(const Orbitals &orbitalsAB) const
void CheckAtomCoordinates(const Orbitals &orbitalsA, const Orbitals &orbitalsB, const Orbitals &orbitalsAB) const
void WriteToProperty(tools::Property &type_summary, const Orbitals &orbitalsA, const Orbitals &orbitalsB, Index a, Index b) const
Eigen::MatrixXd JAB_dimer_elec_
double min_S_eigenvalue_elec_
Eigen::VectorXd moEnergiesA_hole_QP_
double min_S_eigenvalue_hole_
Eigen::MatrixXd JAB_dimer_hole_
std::pair< Index, Index > Range_orbA
std::pair< Index, Index > DetermineRangeOfStates(const Orbitals &orbital, Index numberofstates) const
void Initialize(tools::Property &) override
Eigen::VectorXd moEnergiesB_elec_KS_
double getCouplingElement(Index levelA, Index levelB, const Orbitals &orbitalsA, const Orbitals &orbitalsB) const
Eigen::MatrixXd S_AxB_hole_
Eigen::VectorXd moEnergiesB_elec_QP_
void Addoutput(tools::Property &type_summary, const Orbitals &orbitalsA, const Orbitals &orbitalsB) const override
static void WriteMatrixToProperty(tools::Property &prop, const std::string &name, const Eigen::MatrixXd &mat, double conversion=1.0)
Write an Eigen matrix as rows of space-separated values into an XML property node....
Eigen::VectorXd moEnergiesB_hole_KS_
Eigen::VectorXd moEnergiesA_elec_KS_
Eigen::VectorXd moEnergiesA_hole_KS_
Eigen::VectorXd moEnergiesA_elec_QP_
std::pair< Index, Index > Range_orbB
void CalculateCouplings(const Orbitals &orbitalsA, const Orbitals &orbitalsB, const Orbitals &orbitalsAB) override
Eigen::VectorXd moEnergiesB_hole_QP_
Eigen::MatrixXd S_AxB_elec_
std::string Identify() const
Container for molecular orbitals and derived one-particle data.
Index getHomo() const
Return the default HOMO index used by spin-agnostic callers.
const Eigen::VectorXd & QPpertEnergies() const
Return read-only access to perturbative quasiparticle energies.
std::vector< Index > CheckDegeneracy(Index level, double energy_difference) const
bool hasQPpert() const
Report whether perturbative quasiparticle energies are available.
Index getBasisSetSize() const
Return the number of AO basis functions in the DFT basis.
Index getGWAmin() const
Return the lower GW orbital index.
const tools::EigenSystem & MOs() const
Return read-only access to alpha/restricted molecular orbitals.
Index getGWAmax() const
Return the upper GW orbital index.
const std::string & getDFTbasisName() const
Return the DFT basis-set name.
Index getLumo() const
Return the default LUMO index used by spin-agnostic callers.
Timestamp returns the current time as a string Example: cout << TimeStamp().
#define XTP_LOG(level, log)
void Cite(const std::string &key)
marks a reference as used (thread safe; unknown keys are printed as such)
Charge transport classes.
Provides a means for comparing floating point numbers.