21#include <boost/format.hpp>
54 const std::string& name,
55 const Eigen::MatrixXd& mat,
60 for (
Index i = 0; i < mat.rows(); i++) {
62 for (
Index j = 0; j < mat.cols(); j++) {
63 if (j > 0) row_str +=
" ";
64 row_str += (format(
"%1$1.6e") % (mat(i, j) * conversion)).str();
66 mat_prop.
setAttribute(
"row_" + std::to_string(i), row_str);
83 coupling.
setAttribute(
"j", (format(
"%1$1.6e") % J).str());
107 const Eigen::MatrixXd& JAB_dimer,
108 const Eigen::MatrixXd& S_AxB,
109 const Eigen::VectorXd& moEnA_KS,
110 const Eigen::VectorXd& moEnB_KS,
111 const Eigen::VectorXd& moEnA_QP,
112 const Eigen::VectorXd& moEnB_QP,
double min_S_eval) {
114 for (
Index a = start_a; a <= end_a; ++a) {
115 for (
Index b = start_b; b <= end_b; ++b) {
128 bool hasQP_A = (moEnA_QP.size() == moEnA_KS.size());
129 bool hasQP_B = (moEnB_QP.size() == moEnB_KS.size());
132 carrier_summary.
add(
"monomer_energies",
"");
134 (hasQP_A && hasQP_B) ?
"true" :
"false");
137 for (
Index i = 0; i < moEnA_KS.size(); i++) {
139 e.setAttribute(
"level", start_a + i);
140 e.setAttribute(
"eV_KS", (format(
"%1$1.6e") % moEnA_KS(i)).str());
142 e.setAttribute(
"eV_QP", (format(
"%1$1.6e") % moEnA_QP(i)).str());
146 for (
Index i = 0; i < moEnB_KS.size(); i++) {
148 e.setAttribute(
"level", start_b + i);
149 e.setAttribute(
"eV_KS", (format(
"%1$1.6e") % moEnB_KS(i)).str());
151 e.setAttribute(
"eV_QP", (format(
"%1$1.6e") % moEnB_QP(i)).str());
158 Index levA_range = moEnA_KS.size();
159 Index levB_range = moEnB_KS.size();
165 JAB_dimer.topLeftCorner(levA_range, levA_range),
168 S_AxB.topLeftCorner(levA_range, levA_range));
171 JAB_dimer.bottomRightCorner(levB_range, levB_range),
174 S_AxB.bottomRightCorner(levB_range, levB_range));
176 JAB_dimer.topRightCorner(levA_range, levB_range),
179 S_AxB.topRightCorner(levA_range, levB_range));
186 (format(
"%1$1.6e") % min_S_eval).str());
188 (min_S_eval > 0.01) ?
"true" :
"false");
203 WriteCarrier(electron_summary, orbitalsA.
getLumo(),
214 if (std::abs(MOEnergies(orbital.
getHomo()) - MOEnergies(orbital.
getLumo())) <
216 throw std::runtime_error(
217 "Homo Lumo Gap is smaller than degeneracy. "
218 "Either your degeneracy is too large or your Homo and Lumo are "
226 minimal = *std::min_element(deg_min.begin(), deg_min.end());
229 maximal = *std::max_element(deg_max.begin(), deg_max.end());
231 std::pair<Index, Index> result;
232 result.first = minimal;
233 result.second = maximal - minimal + 1;
243 std::vector<Index> list_levelsA =
245 std::vector<Index> list_levelsB =
249 for (
Index iA : list_levelsA) {
251 for (
Index iB : list_levelsB) {
253 double JAB_one_level =
JAB(indexA, indexB);
254 JAB_sq += JAB_one_level * JAB_one_level;
257 return std::sqrt(JAB_sq /
258 double(list_levelsA.size() * list_levelsB.size())) *
284 if ((basisA == 0) || (basisB == 0)) {
285 throw std::runtime_error(
"Basis set size is not stored in monomers");
295 <<
"Levels:Basis A[" << levelsA <<
":" << basisA <<
"]"
296 <<
" B[" << levelsB <<
":" << basisB <<
"]" << flush;
298 if ((levelsA == 0) || (levelsB == 0)) {
299 throw std::runtime_error(
300 "No information about number of occupied/unoccupied levels is stored");
312 Eigen::MatrixXd overlap =
316 <<
"Projecting monomers onto dimer orbitals" << flush;
317 Eigen::MatrixXd A_AB = MOsA.transpose() * overlap.topRows(basisA);
318 Eigen::MatrixXd B_AB = MOsB.transpose() * overlap.bottomRows(basisB);
320 Eigen::VectorXd mag_A = A_AB.rowwise().squaredNorm();
321 if (mag_A.any() < 0.95) {
324 <<
"Projection of orbitals of monomer A on dimer is insufficient,mag="
325 << mag_A.minCoeff() << flush;
327 Eigen::VectorXd mag_B = B_AB.rowwise().squaredNorm();
328 if (mag_B.any() < 0.95) {
331 <<
"Projection of orbitals of monomer B on dimer is insufficient,mag="
332 << mag_B.minCoeff() << flush;
336 Eigen::MatrixXd psi_AxB_dimer_basis(levelsA + levelsB, A_AB.cols());
337 psi_AxB_dimer_basis.topRows(levelsA) = A_AB;
338 psi_AxB_dimer_basis.bottomRows(levelsB) = B_AB;
342 <<
"Projecting the Fock matrix onto the dimer basis" << flush;
343 Eigen::MatrixXd JAB_dimer_full = psi_AxB_dimer_basis *
345 psi_AxB_dimer_basis.transpose();
348 Eigen::MatrixXd S_AxB_full =
349 psi_AxB_dimer_basis * psi_AxB_dimer_basis.transpose();
351 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(S_AxB_full);
352 Eigen::MatrixXd Sm1 = es.operatorInverseSqrt();
354 << es.eigenvalues()(0) << flush;
357 JAB = Sm1 * JAB_dimer_full * Sm1;
379 Index elecsA = levelsA - holesA;
381 Index elecsB = levelsB - holesB;
386 std::vector<Index> hole_idx, elec_idx;
387 for (
Index i = 0; i < holesA; i++) hole_idx.push_back(i);
388 for (
Index i = 0; i < holesB; i++) hole_idx.push_back(levelsA + i);
389 for (
Index i = holesA; i < levelsA; i++) elec_idx.push_back(i);
390 for (
Index i = holesB; i < levelsB; i++) elec_idx.push_back(levelsA + i);
393 Index nh = hole_idx.size();
394 Eigen::MatrixXd JAB_hole(nh, nh), S_hole(nh, nh);
395 for (
Index i = 0; i < nh; i++) {
396 for (
Index j = 0; j < nh; j++) {
397 JAB_hole(i, j) = JAB_dimer_full(hole_idx[i], hole_idx[j]);
398 S_hole(i, j) = S_AxB_full(hole_idx[i], hole_idx[j]);
403 Index ne = elec_idx.size();
404 Eigen::MatrixXd JAB_elec(ne, ne), S_elec(ne, ne);
405 for (
Index i = 0; i < ne; i++) {
406 for (
Index j = 0; j < ne; j++) {
407 JAB_elec(i, j) = JAB_dimer_full(elec_idx[i], elec_idx[j]);
408 S_elec(i, j) = S_AxB_full(elec_idx[i], elec_idx[j]);
419 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_h(S_hole);
421 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_e(S_elec);
444 auto ExtractQPEnergies = [&](
const Orbitals& orb,
Index mo_start,
445 Index n_mos) -> Eigen::VectorXd {
446 if (!orb.
hasQPpert())
return Eigen::VectorXd{};
449 if (mo_start < qpmin || mo_start + n_mos - 1 > qpmax) {
451 <<
TimeStamp() <<
" WARNING: QPpert window [" << qpmin <<
"," << qpmax
452 <<
"] does not fully cover MO range [" << mo_start <<
","
453 << mo_start + n_mos - 1 <<
"] -- QP energies omitted for this range"
455 return Eigen::VectorXd{};
482 ExtractQPEnergies(orbitalsA, orbitalsA.
getLumo(), elecsA);
484 ExtractQPEnergies(orbitalsB, orbitalsB.
getLumo(), elecsB);
488 <<
TimeStamp() <<
" QPpert energies available for fragment A" << flush;
492 <<
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)
Charge transport classes.
Provides a means for comparing floating point numbers.