votca 2026-dev
Loading...
Searching...
No Matches
dftengine.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2026 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// Third party includes
21#include <algorithm>
22#include <boost/filesystem.hpp>
23#include <boost/format.hpp>
24
25// VOTCA includes
28
29// Local VOTCA includes
33#include "votca/xtp/aomatrix.h"
36#include "votca/xtp/dftengine.h"
38#include "votca/xtp/logger.h"
39#include "votca/xtp/mmregion.h"
40#include "votca/xtp/orbitals.h"
43
44namespace votca {
45namespace xtp {
46
59
61
62 const std::string key_xtpdft = "xtpdft";
63 dftbasis_name_ = options.get(".basisset").as<std::string>();
64
65 if (options.exists(".auxbasisset")) {
66 auxbasis_name_ = options.get(".auxbasisset").as<std::string>();
67 }
68
69 if (!auxbasis_name_.empty()) {
70 screening_eps_ = options.get(key_xtpdft + ".screening_eps").as<double>();
72 options.get(key_xtpdft + ".fock_matrix_reset").as<Index>();
73 }
74 if (options.exists(".ecp")) {
75 ecp_name_ = options.get(".ecp").as<std::string>();
76 }
77
78 initial_guess_ = options.get(".initial_guess").as<std::string>();
79
80 grid_name_ = options.get(key_xtpdft + ".integration_grid").as<std::string>();
81 xc_functional_name_ = options.get(".functional").as<std::string>();
82
83 if (options.exists(key_xtpdft + ".externaldensity")) {
86 options.get(key_xtpdft + ".externaldensity.orbfile").as<std::string>();
87 gridquality_ = options.get(key_xtpdft + ".externaldensity.gridquality")
88 .as<std::string>();
89 state_ =
90 options.get(key_xtpdft + ".externaldensity.state").as<std::string>();
91 }
92
93 if (options.exists(".externalfield")) {
95 extfield_ = options.get(".externalfield").as<Eigen::Vector3d>();
96 }
97
98 conv_opt_.Econverged =
99 options.get(key_xtpdft + ".convergence.energy").as<double>();
100 conv_opt_.error_converged =
101 options.get(key_xtpdft + ".convergence.error").as<double>();
102 max_iter_ =
103 options.get(key_xtpdft + ".convergence.max_iterations").as<Index>();
104
105 std::string method =
106 options.get(key_xtpdft + ".convergence.method").as<std::string>();
107 if (method == "DIIS") {
108 conv_opt_.usediis = true;
109 } else if (method == "mixing") {
110 conv_opt_.usediis = false;
111 }
112 if (!conv_opt_.usediis) {
113 conv_opt_.histlength = 1;
114 conv_opt_.maxout = false;
115 }
116 conv_opt_.mixingparameter =
117 options.get(key_xtpdft + ".convergence.mixing").as<double>();
118 conv_opt_.levelshift =
119 options.get(key_xtpdft + ".convergence.levelshift").as<double>();
120 conv_opt_.levelshiftend =
121 options.get(key_xtpdft + ".convergence.levelshift_end").as<double>();
122 conv_opt_.maxout =
123 options.get(key_xtpdft + ".convergence.DIIS_maxout").as<bool>();
124 conv_opt_.histlength =
125 options.get(key_xtpdft + ".convergence.DIIS_length").as<Index>();
126 conv_opt_.diis_start =
127 options.get(key_xtpdft + ".convergence.DIIS_start").as<double>();
128 conv_opt_.adiis_start =
129 options.get(key_xtpdft + ".convergence.ADIIS_start").as<double>();
130
131 if (options.exists(key_xtpdft + ".dft_in_dft.activeatoms")) {
133 options.get(key_xtpdft + ".dft_in_dft.activeatoms").as<std::string>();
135 options.get(key_xtpdft + ".dft_in_dft.threshold").as<double>();
137 options.get(key_xtpdft + ".dft_in_dft.levelshift").as<double>();
138 truncate_ =
139 options.get(key_xtpdft + ".dft_in_dft.truncate_basis").as<bool>();
140 if (truncate_) {
142 options.get(key_xtpdft + ".dft_in_dft.truncation_threshold")
143 .as<double>();
144 }
145 }
146}
147
148void DFTEngine::PrintMOs(const Eigen::VectorXd& MOEnergies, Log::Level level) {
149 XTP_LOG(level, *pLog_) << " Orbital energies: " << std::flush;
150 XTP_LOG(level, *pLog_) << " index occupation energy(Hartree) " << std::flush;
151
152 for (Index i = 0; i < MOEnergies.size(); ++i) {
153 Index occupancy = 0;
154 if (i < num_docc_) {
155 occupancy = 2;
156 } else if (i < num_docc_ + num_socc_alpha_) {
157 occupancy = 1;
158 }
159
160 XTP_LOG(level, *pLog_) << (boost::format(" %1$5d %2$1d %3$+1.10f") %
161 i % occupancy % MOEnergies(i))
162 .str()
163 << std::flush;
164 }
165 return;
166}
167
168void DFTEngine::PrintMOsUKS(const Eigen::VectorXd& alpha_energies,
169 const Eigen::VectorXd& beta_energies,
170 Log::Level level) const {
171 XTP_LOG(level, *pLog_) << " UKS orbital energies:" << std::flush;
172 XTP_LOG(level, *pLog_) << " index occ eps_a(Ha) eps_b(Ha)"
173 << std::flush;
174
175 const Index nrows =
176 std::max<Index>(alpha_energies.size(), beta_energies.size());
177
178 for (Index i = 0; i < nrows; ++i) {
179 const bool occ_a = (i < num_alpha_electrons_);
180 const bool occ_b = (i < num_beta_electrons_);
181
182 std::string occ = "0";
183 if (occ_a && occ_b) {
184 occ = "2";
185 } else if (occ_a) {
186 occ = "a";
187 } else if (occ_b) {
188 occ = "b";
189 }
190
191 std::string eps_a = " -";
192 std::string eps_b = " -";
193
194 if (i < alpha_energies.size()) {
195 eps_a = (boost::format("%+1.10f") % alpha_energies(i)).str();
196 }
197 if (i < beta_energies.size()) {
198 eps_b = (boost::format("%+1.10f") % beta_energies(i)).str();
199 }
200
201 XTP_LOG(level, *pLog_) << (boost::format(
202 " %1$5d %2$1s %3$15s %4$15s") %
203 i % occ % eps_a % eps_b)
204 .str()
205 << std::flush;
206 }
207
208 if (num_alpha_electrons_ > 0 &&
209 num_alpha_electrons_ < alpha_energies.size()) {
210 XTP_LOG(level, *pLog_) << (boost::format(
211 " alpha HOMO-LUMO gap: %+1.10f Ha") %
212 (alpha_energies(num_alpha_electrons_) -
213 alpha_energies(num_alpha_electrons_ - 1)))
214 .str()
215 << std::flush;
216 }
217
218 if (num_beta_electrons_ > 0 && num_beta_electrons_ < beta_energies.size()) {
219 XTP_LOG(level, *pLog_) << (boost::format(
220 " beta HOMO-LUMO gap: %+1.10f Ha") %
221 (beta_energies(num_beta_electrons_) -
222 beta_energies(num_beta_electrons_ - 1)))
223 .str()
224 << std::flush;
225 }
226}
227
228void DFTEngine::CalcElDipole(const Orbitals& orb) const {
229 QMState state = QMState("n");
230 Eigen::Vector3d result = orb.CalcElDipole(state);
232 << TimeStamp() << " Electric Dipole is[e*bohr]:\n\t\t dx=" << result[0]
233 << "\n\t\t dy=" << result[1] << "\n\t\t dz=" << result[2] << std::flush;
234 return;
235}
236
237// Build the Coulomb and exact-exchange contributions generated by the current
238// density matrix. The returned pair is conventionally interpreted as
239//
240// (J[P], -K[P]),
241//
242// so that the hybrid Fock update becomes F = H0 + J[P] + a_x (-K[P]) + V_xc.
243// For RI/3c builds the occupied MO block is supplied when available to avoid an
244// unnecessary reconstruction of exchange intermediates.
245std::array<Eigen::MatrixXd, 2> DFTEngine::CalcERIs_EXX(
246 const Eigen::MatrixXd& MOCoeff, const Eigen::MatrixXd& Dmat,
247 double error) const {
248 if (!auxbasis_name_.empty()) {
249 if (conv_accelerator_.getUseMixing() || MOCoeff.rows() == 0) {
250 return ERIs_.CalculateERIs_EXX_3c(Eigen::MatrixXd::Zero(0, 0), Dmat);
251 } else {
252 Eigen::MatrixXd occblock = MOCoeff.leftCols(num_docc_ + num_socc_alpha_);
253 return ERIs_.CalculateERIs_EXX_3c(occblock, Dmat);
254 }
255 } else {
256 return ERIs_.CalculateERIs_EXX_4c(Dmat, error);
257 }
258}
259
260// Pure Coulomb contribution J[P] from the current AO density matrix. The code
261// dispatches to either RI/3c or conventional 4-center integral evaluation.
262Eigen::MatrixXd DFTEngine::CalcERIs(const Eigen::MatrixXd& Dmat,
263 double error) const {
264 if (!auxbasis_name_.empty()) {
265 return ERIs_.CalculateERIs_3c(Dmat);
266 } else {
267 return ERIs_.CalculateERIs_4c(Dmat, error);
268 }
269}
270
272 const Mat_p_Energy& H0) const {
273 return conv_accelerator_.SolveFockmatrix(H0.matrix());
274}
275
276// Construct a self-consistent model-potential guess by starting from an
277// atomic density P^(0), evaluating
278//
279// F[P^(0)] = H0 + J[P^(0)] + a_x (-K[P^(0)]) + V_xc[P^(0)],
280//
281// and diagonalizing the resulting Fock matrix once.
283 const Mat_p_Energy& H0, const QMMolecule& mol,
284 const Vxc_Potential<Vxc_Grid>& vxcpotential) const {
285 Eigen::MatrixXd Dmat = AtomicGuess(mol);
286 Mat_p_Energy e_vxc = vxcpotential.IntegrateVXC(Dmat);
288 << TimeStamp() << " Filled DFT Vxc matrix " << std::flush;
289
290 Eigen::MatrixXd H = H0.matrix() + e_vxc.matrix();
291
292 if (ScaHFX_ > 0) {
293 std::array<Eigen::MatrixXd, 2> both =
294 CalcERIs_EXX(Eigen::MatrixXd::Zero(0, 0), Dmat, 1e-12);
295 H += both[0];
296 H += ScaHFX_ * both[1];
297 } else {
298 H += CalcERIs(Dmat, 1e-12);
299 }
300 return conv_accelerator_.SolveFockmatrix(H);
301}
302
304 Prepare(orb);
305 Mat_p_Energy H0 = SetupH0(orb.QMAtoms());
306 Vxc_Potential<Vxc_Grid> vxcpotential = SetupVxc(orb.QMAtoms());
307 ConfigOrbfile(orb);
308
310 return EvaluateUKS(orb, H0, vxcpotential);
311 }
312 return EvaluateClosedShell(orb, H0, vxcpotential);
313}
314
315// Restricted SCF loop. The total energy is assembled as
316//
317// E = Tr[P H0] + E_nuc + E_coul + E_xc + E_exx,
318//
319// with P = 2 C_occ C_occ^T. DIIS or mixing updates the density until both
320// the energy change and the commutator error are converged.
322 Orbitals& orb, const Mat_p_Energy& H0,
323 const Vxc_Potential<Vxc_Grid>& vxcpotential) {
324
326 MOs.eigenvalues() = Eigen::VectorXd::Zero(H0.cols());
327 MOs.eigenvectors() = Eigen::MatrixXd::Zero(H0.rows(), H0.cols());
328
329 if (initial_guess_ == "orbfile") {
331 << TimeStamp() << " Reading guess from orbitals object/file"
332 << std::flush;
333 MOs = orb.MOs();
335 } else {
337 << TimeStamp() << " Setup Initial Guess using: " << initial_guess_
338 << std::flush;
339 if (initial_guess_ == "independent") {
340 MOs = IndependentElectronGuess(H0);
341 } else if (initial_guess_ == "atom") {
342 MOs = ModelPotentialGuess(H0, orb.QMAtoms(), vxcpotential);
343 } else if (initial_guess_ == "huckel") {
344 MOs = ExtendedHuckelGuess(orb.QMAtoms());
345 } else if (initial_guess_ == "huckel_dft") {
346 MOs = ExtendedHuckelDFTGuess(H0, orb.QMAtoms(), vxcpotential);
347 } else {
348 throw std::runtime_error("Initial guess method not known/implemented");
349 }
350 }
351
353 conv_accelerator_.DensityMatrixSpinResolved(MOs);
354 Eigen::MatrixXd Dmat = spin_dmat.total();
355
357 << TimeStamp() << " Guess Matrix gives N=" << std::setprecision(9)
358 << Dmat.cwiseProduct(dftAOoverlap_.Matrix()).sum() << " electrons."
359 << std::flush;
360
362 << TimeStamp() << " STARTING SCF cycle" << std::flush;
364 << " ----------------------------------------------"
365 "----------------------------"
366 << std::flush;
367
368 Eigen::MatrixXd J = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
369 Eigen::MatrixXd K;
370 if (ScaHFX_ > 0) {
371 K = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
372 }
373
374 double start_incremental_F_threshold = 1e-4;
375 if (!auxbasis_name_.empty()) {
376 start_incremental_F_threshold = 0.0; // Disable if RI is used
377 }
378 IncrementalFockBuilder incremental_fock(*pLog_, start_incremental_F_threshold,
380 incremental_fock.Configure(Dmat);
381
382 for (Index this_iter = 0; this_iter < max_iter_; this_iter++) {
383 XTP_LOG(Log::error, *pLog_) << std::flush;
384 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Iteration " << this_iter + 1
385 << " of " << max_iter_ << std::flush;
386
387 Mat_p_Energy e_vxc = vxcpotential.IntegrateVXC(Dmat);
389 << TimeStamp() << " Filled DFT Vxc matrix " << std::flush;
390
391 Eigen::MatrixXd H = H0.matrix() + e_vxc.matrix();
392 double Eone = Dmat.cwiseProduct(H0.matrix()).sum();
393 double Etwo = e_vxc.energy();
394 double exx = 0.0;
395
396 incremental_fock.Start(this_iter, conv_accelerator_.getDIIsError());
397 incremental_fock.resetMatrices(J, K, Dmat);
398 incremental_fock.UpdateCriteria(conv_accelerator_.getDIIsError(),
399 this_iter);
400
401 double integral_error =
402 std::min(conv_accelerator_.getDIIsError() * 1e-5, 1e-5);
403
404 if (ScaHFX_ > 0) {
405 std::array<Eigen::MatrixXd, 2> both = CalcERIs_EXX(
406 MOs.eigenvectors(), incremental_fock.getDmat_diff(), integral_error);
407 J += both[0];
408 H += J;
409 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
410 K += both[1];
411 H += 0.5 * ScaHFX_ * K;
412 exx = 0.25 * ScaHFX_ * Dmat.cwiseProduct(K).sum();
414 << TimeStamp() << " Filled F+K matrix " << std::flush;
415 } else {
416 J += CalcERIs(incremental_fock.getDmat_diff(), integral_error);
418 << TimeStamp() << " Filled F matrix " << std::flush;
419 H += J;
420 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
421 }
422
423 Etwo += exx;
424 double totenergy = Eone + H0.energy() + Etwo;
425
426 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " Single particle energy "
427 << std::setprecision(12) << Eone << std::flush;
428 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " Two particle energy "
429 << std::setprecision(12) << Etwo << std::flush;
431 << TimeStamp() << std::setprecision(12) << " Local Exc contribution "
432 << e_vxc.energy() << std::flush;
433 if (ScaHFX_ > 0) {
435 << TimeStamp() << std::setprecision(12)
436 << " Non local Ex contribution " << exx << std::flush;
437 }
439 << TimeStamp() << " Total Energy " << std::setprecision(12) << totenergy
440 << std::flush;
441
442 Dmat = conv_accelerator_.Iterate(Dmat, H, MOs, totenergy);
443 incremental_fock.UpdateDmats(Dmat, conv_accelerator_.getDIIsError(),
444 this_iter);
445
447
448 if (num_docc_ + num_socc_alpha_ > 0 &&
449 num_docc_ + num_socc_alpha_ < MOs.eigenvalues().size()) {
451 << "\t\tGAP "
454 << std::flush;
455 }
456
457 if (conv_accelerator_.isConverged()) {
459 << TimeStamp() << " Total Energy has converged to "
460 << std::setprecision(9) << conv_accelerator_.getDeltaE()
461 << "[Ha] after " << this_iter + 1
462 << " iterations. DIIS error is converged up to "
463 << conv_accelerator_.getDIIsError() << std::flush;
465 << TimeStamp() << " Final Single Point Energy "
466 << std::setprecision(12) << totenergy << " Ha" << std::flush;
467 XTP_LOG(Log::error, *pLog_) << TimeStamp() << std::setprecision(12)
468 << " Final Local Exc contribution "
469 << e_vxc.energy() << " Ha" << std::flush;
470 if (ScaHFX_ > 0) {
471 XTP_LOG(Log::error, *pLog_) << TimeStamp() << std::setprecision(12)
472 << " Final Non Local Ex contribution "
473 << exx << " Ha" << std::flush;
474 }
475
477
478 Index nuclear_charge = 0;
479 for (const QMAtom& atom : orb.QMAtoms()) {
480 nuclear_charge += atom.getNuccharge();
481 }
482
483 orb.setQMEnergy(totenergy);
484 orb.MOs() = MOs;
489 nuclear_charge - numofelectrons_,
491
492 CalcElDipole(orb);
493 return true;
494 } else if (this_iter == max_iter_ - 1) {
496 << TimeStamp() << " DFT calculation has not converged after "
497 << max_iter_
498 << " iterations. Use more iterations or another convergence "
499 "acceleration scheme."
500 << std::flush;
501 return false;
502 }
503 }
504
505 return true;
506}
507
508// Unrestricted SCF loop. The alpha and beta channels are iterated through
509// separate Fock matrices
510//
511// F^alpha = H0 + J[P^alpha + P^beta] + V_xc^alpha + K^alpha
512// F^beta = H0 + J[P^alpha + P^beta] + V_xc^beta + K^beta,
513//
514// while the total energy uses the spin-summed one-electron and Coulomb terms
515// together with spin-resolved XC and exact-exchange contributions.
517 const Vxc_Potential<Vxc_Grid>& vxcpotential) {
518 tools::EigenSystem MOs_alpha;
519 tools::EigenSystem MOs_beta;
520
521 MOs_alpha.eigenvalues() = Eigen::VectorXd::Zero(H0.cols());
522 MOs_alpha.eigenvectors() = Eigen::MatrixXd::Zero(H0.rows(), H0.cols());
523 MOs_beta.eigenvalues() = Eigen::VectorXd::Zero(H0.cols());
524 MOs_beta.eigenvectors() = Eigen::MatrixXd::Zero(H0.rows(), H0.cols());
525
526 UKSConvergenceAcc conv_uks;
527
531
535
536 conv_uks.Configure(opt_alpha, opt_beta);
537 conv_uks.setLogger(pLog_);
538 conv_uks.setOverlap(dftAOoverlap_, 1e-8);
539
540 if (initial_guess_ == "orbfile") {
542 << TimeStamp() << " Reading UKS guess from orbitals object/file"
543 << std::flush;
544
545 MOs_alpha = orb.MOs();
546 MOs_alpha.eigenvectors() = OrthogonalizeGuess(MOs_alpha.eigenvectors());
547
548 if (orb.hasBetaMOs()) {
549 MOs_beta = orb.MOs_beta();
550 MOs_beta.eigenvectors() = OrthogonalizeGuess(MOs_beta.eigenvectors());
551 } else {
553 << TimeStamp()
554 << " Orbital file has no beta MOs, using alpha guess for beta."
555 << std::flush;
556 MOs_beta = MOs_alpha;
557 }
558 } else {
560 << TimeStamp() << " Setup UKS Initial Guess using: " << initial_guess_
561 << std::flush;
562
563 tools::EigenSystem guess;
564 if (initial_guess_ == "independent") {
565 guess = IndependentElectronGuess(H0);
566 } else if (initial_guess_ == "atom") {
567 guess = ModelPotentialGuess(H0, orb.QMAtoms(), vxcpotential);
568 } else if (initial_guess_ == "huckel") {
569 guess = ExtendedHuckelGuess(orb.QMAtoms());
570 } else if (initial_guess_ == "huckel_dft") {
571 guess = ExtendedHuckelDFTGuess(H0, orb.QMAtoms(), vxcpotential);
572 } else {
573 throw std::runtime_error("Initial guess method not known/implemented");
574 }
575
576 MOs_alpha = guess;
577 MOs_beta = guess;
578 }
579
580 // Build the initial spin densities P^alpha and P^beta from the chosen
581 // starting orbitals before entering the coupled UKS iterations.
583 conv_uks.DensityMatrix(MOs_alpha, MOs_beta);
584
586 << TimeStamp() << " UKS guess gives Nalpha="
587 << Dspin.alpha.cwiseProduct(dftAOoverlap_.Matrix()).sum()
588 << " Nbeta=" << Dspin.beta.cwiseProduct(dftAOoverlap_.Matrix()).sum()
589 << " Ntot=" << Dspin.total().cwiseProduct(dftAOoverlap_.Matrix()).sum()
590 << std::flush;
591
593 << TimeStamp() << " STARTING UKS SCF cycle" << std::flush;
595 << " ------------------------------------------------------------"
596 << std::flush;
597
598 for (Index this_iter = 0; this_iter < max_iter_; ++this_iter) {
599 XTP_LOG(Log::error, *pLog_) << std::flush;
600 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Iteration " << this_iter + 1
601 << " of " << max_iter_ << std::flush;
602
603 Eigen::MatrixXd H_alpha = H0.matrix();
604 Eigen::MatrixXd H_beta = H0.matrix();
605
606 // The Coulomb contribution depends only on the total density
607 // P = P^alpha + P^beta, while exchange and XC remain spin resolved.
608 const Eigen::MatrixXd D_total = Dspin.total();
609
610 double E_one = Dspin.alpha.cwiseProduct(H0.matrix()).sum() +
611 Dspin.beta.cwiseProduct(H0.matrix()).sum();
612
613 double E_coul = 0.0;
614 double E_xc = 0.0;
615 double E_exx = 0.0;
616
617 double integral_error = std::min(conv_uks.getDIIsError() * 1e-5, 1e-5);
618
619 if (ScaHFX_ > 0) {
620 std::array<Eigen::MatrixXd, 2> both_alpha = CalcERIs_EXX(
621 Eigen::MatrixXd::Zero(0, 0), Dspin.alpha, integral_error);
622 std::array<Eigen::MatrixXd, 2> both_beta =
623 CalcERIs_EXX(Eigen::MatrixXd::Zero(0, 0), Dspin.beta, integral_error);
624
625 Eigen::MatrixXd J = both_alpha[0] + both_beta[0];
626 Eigen::MatrixXd K_alpha = both_alpha[1];
627 Eigen::MatrixXd K_beta = both_beta[1];
628
629 H_alpha += J + ScaHFX_ * K_alpha;
630 H_beta += J + ScaHFX_ * K_beta;
631
632 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
633 E_exx = 0.5 * ScaHFX_ *
634 (Dspin.alpha.cwiseProduct(K_alpha).sum() +
635 Dspin.beta.cwiseProduct(K_beta).sum());
636 } else {
637 Eigen::MatrixXd J = CalcERIs(D_total, integral_error);
638 H_alpha += J;
639 H_beta += J;
640 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
641 }
642
643 auto vxc = vxcpotential.IntegrateVXCSpin(Dspin.alpha, Dspin.beta);
644 H_alpha += vxc.vxc_alpha;
645 H_beta += vxc.vxc_beta;
646 E_xc = vxc.energy;
647
648 double totenergy = H0.energy() + E_one + E_coul + E_xc + E_exx;
649
650 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " One particle energy "
651 << std::setprecision(12) << E_one << std::flush;
652 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " Coulomb contribution "
653 << std::setprecision(12) << E_coul << std::flush;
654 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " XC contribution "
655 << std::setprecision(12) << E_xc << std::flush;
656 if (ScaHFX_ > 0) {
658 << TimeStamp() << " EXX contribution " << std::setprecision(12)
659 << E_exx << std::flush;
660 }
662 << TimeStamp() << " Total Energy " << std::setprecision(12) << totenergy
663 << std::flush;
664
665 UKSConvergenceAcc::SpinFock Hspin{H_alpha, H_beta};
666 Dspin = conv_uks.Iterate(Dspin, Hspin, MOs_alpha, MOs_beta, totenergy);
667
669 << TimeStamp()
670 << " Nalpha=" << Dspin.alpha.cwiseProduct(dftAOoverlap_.Matrix()).sum()
671 << " Nbeta=" << Dspin.beta.cwiseProduct(dftAOoverlap_.Matrix()).sum()
672 << std::flush;
673
675 << TimeStamp() << " <Sz> = "
677 << std::flush;
678
679 PrintMOsUKS(MOs_alpha.eigenvalues(), MOs_beta.eigenvalues(), Log::info);
680
681 if (conv_uks.isConverged()) {
682 Index nuclear_charge = 0;
683 for (const QMAtom& atom : orb.QMAtoms()) {
684 nuclear_charge += atom.getNuccharge();
685 }
686
687 orb.setQMEnergy(totenergy);
688 orb.MOs() = MOs_alpha;
689 orb.MOs_beta() = MOs_beta;
695 nuclear_charge - numofelectrons_,
697
699 << TimeStamp() << " UKS converged after " << this_iter + 1
700 << " iterations. Delta E=" << conv_uks.getDeltaE()
701 << " DIIS error=" << conv_uks.getDIIsError() << std::flush;
702
704 << TimeStamp() << " Final Single Point Energy "
705 << std::setprecision(12) << totenergy << " Ha" << std::flush;
707 << TimeStamp() << std::setprecision(12) << " Final XC contribution "
708 << E_xc << " Ha" << std::flush;
709 if (ScaHFX_ > 0) {
711 << TimeStamp() << std::setprecision(12)
712 << " Final EXX contribution " << E_exx << " Ha" << std::flush;
713 }
714
716 << TimeStamp() << " <Sz> = "
718 << std::flush;
719
720 PrintMOsUKS(MOs_alpha.eigenvalues(), MOs_beta.eigenvalues(), Log::error);
721
722 CalcElDipole(orb);
723 return true;
724 }
725
726 if (this_iter == max_iter_ - 1) {
728 << TimeStamp() << " UKS calculation has not converged after "
729 << max_iter_ << " iterations." << std::flush;
730 return false;
731 }
732 }
733
734 return false;
735}
736
737// One-electron core Hamiltonian and its constant energy offset.
738//
739// The matrix part is
740//
741// H0 = T + V_nuc + V_ECP + V_ext,
742//
743// while the scalar energy collects all nucleus-nucleus and nucleus-external
744// interaction terms that do not depend on the electronic density.
746
747 AOKinetic dftAOkinetic;
748
749 dftAOkinetic.Fill(dftbasis_);
751 << TimeStamp() << " Filled DFT Kinetic energy matrix ." << std::flush;
752
753 AOMultipole dftAOESP;
754 dftAOESP.FillPotential(dftbasis_, mol);
756 << TimeStamp() << " Filled DFT nuclear potential matrix." << std::flush;
757
758 Eigen::MatrixXd H0 = dftAOkinetic.Matrix() + dftAOESP.Matrix();
760 << TimeStamp() << " Constructed independent particle hamiltonian "
761 << std::flush;
762 double E0 = NuclearRepulsion(mol);
763 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Nuclear Repulsion Energy is "
764 << std::setprecision(9) << E0 << std::flush;
765
766 if (!ecp_name_.empty()) {
767 AOECP dftAOECP;
768 dftAOECP.FillPotential(dftbasis_, ecp_);
769 H0 += dftAOECP.Matrix();
771 << TimeStamp() << " Filled DFT ECP matrix" << std::flush;
772 }
773
774 if (externalsites_ != nullptr) {
775 XTP_LOG(Log::error, *pLog_) << TimeStamp() << " " << externalsites_->size()
776 << " External sites" << std::flush;
777 bool has_quadrupoles = std::any_of(
778 externalsites_->begin(), externalsites_->end(),
779 [](const std::unique_ptr<StaticSite>& s) { return s->getRank() == 2; });
780 std::string header =
781 " Name Coordinates[a0] charge[e] dipole[e*a0] ";
782 if (has_quadrupoles) {
783 header += " quadrupole[e*a0^2]";
784 }
785 XTP_LOG(Log::error, *pLog_) << header << std::flush;
786 Index limit = 50;
787 Index counter = 0;
788 for (const std::unique_ptr<StaticSite>& site : *externalsites_) {
789 if (counter == limit) {
790 break;
791 }
792 std::string output =
793 (boost::format(" %1$s"
794 " %2$+1.4f %3$+1.4f %4$+1.4f"
795 " %5$+1.4f") %
796 site->getElement() % site->getPos()[0] % site->getPos()[1] %
797 site->getPos()[2] % site->getCharge())
798 .str();
799 const Eigen::Vector3d& dipole = site->getDipole();
800 output += (boost::format(" %1$+1.4f %2$+1.4f %3$+1.4f") % dipole[0] %
801 dipole[1] % dipole[2])
802 .str();
803 if (site->getRank() > 1) {
804 Eigen::VectorXd quadrupole = site->Q().tail<5>();
805 output +=
806 (boost::format(" %1$+1.4f %2$+1.4f %3$+1.4f %4$+1.4f %5$+1.4f") %
807 quadrupole[0] % quadrupole[1] % quadrupole[2] % quadrupole[3] %
808 quadrupole[4])
809 .str();
810 }
811 XTP_LOG(Log::error, *pLog_) << output << std::flush;
812 counter++;
813 }
814 if (counter == limit) {
816 << " ... (" << externalsites_->size() - limit
817 << " sites not displayed)\n"
818 << std::flush;
819 }
820
821 Mat_p_Energy ext_multipoles =
824 << TimeStamp() << " Nuclei-external site interaction energy "
825 << std::setprecision(9) << ext_multipoles.energy() << std::flush;
826 E0 += ext_multipoles.energy();
827 H0 += ext_multipoles.matrix();
828 }
829
831 Orbitals extdensity;
832 extdensity.ReadFromCpt(orbfilename_);
833 Mat_p_Energy extdensity_result = IntegrateExternalDensity(mol, extdensity);
834 E0 += extdensity_result.energy();
836 << TimeStamp() << " Nuclei-external density interaction energy "
837 << std::setprecision(9) << extdensity_result.energy() << std::flush;
838 H0 += extdensity_result.matrix();
839 }
840
842
844 << TimeStamp() << " Integrating external electric field with F[Hrt]="
845 << extfield_.transpose() << std::flush;
846 H0 += IntegrateExternalField(mol);
847 }
848
849 return Mat_p_Energy(E0, H0);
850}
851
852// Precompute SCF-invariant matrices: overlap for the generalized eigenvalue
853// problem and the RI/4c electron-repulsion backend that later yields J[P] and
854// K[P].
858 << TimeStamp() << " Filled DFT Overlap matrix." << std::flush;
859
860 conv_opt_.numberofelectrons = numofelectrons_;
861 conv_opt_.number_alpha_electrons = num_alpha_electrons_;
862 conv_opt_.number_beta_electrons = num_beta_electrons_;
866 conv_accelerator_.Configure(conv_opt_);
867 conv_accelerator_.setLogger(pLog_);
868 conv_accelerator_.setOverlap(dftAOoverlap_, 1e-8);
869 conv_accelerator_.PrintConfigOptions();
870
871 if (!auxbasis_name_.empty()) {
872 // prepare invariant part of electron repulsion integrals
873 ERIs_.Initialize(dftbasis_, auxbasis_);
875 << TimeStamp() << " Inverted AUX Coulomb matrix, removed "
876 << ERIs_.Removedfunctions() << " functions from aux basis"
877 << std::flush;
879 << TimeStamp()
880 << " Setup invariant parts of Electron Repulsion integrals "
881 << std::flush;
882 } else {
884 << TimeStamp() << " Calculating 4c diagonals. " << std::flush;
885 ERIs_.Initialize_4c(dftbasis_);
887 << TimeStamp() << " Calculated 4c diagonals. " << std::flush;
888 }
889
890 return;
891}
892
894 const QMAtom& uniqueAtom) const {
895 bool with_ecp = !ecp_name_.empty();
896 if (uniqueAtom.getElement() == "H" || uniqueAtom.getElement() == "He") {
897 with_ecp = false;
898 }
899
900 QMMolecule atom = QMMolecule("individual_atom", 0);
901 atom.push_back(uniqueAtom);
902
903 BasisSet basisset;
904 basisset.Load(dftbasis_name_);
905 AOBasis dftbasis;
906 dftbasis.Fill(basisset, atom);
907 Vxc_Grid grid;
908 grid.GridSetup(grid_name_, atom, dftbasis);
909 Vxc_Potential<Vxc_Grid> gridIntegration(grid);
910 gridIntegration.setXCfunctional(xc_functional_name_);
911
912 ECPAOBasis ecp;
913 if (with_ecp) {
914 ECPBasisSet ecps;
915 ecps.Load(ecp_name_);
916 ecp.Fill(ecps, atom);
917 }
918
919 Index numofelectrons = uniqueAtom.getNuccharge();
920 Index alpha_e = 0;
921 Index beta_e = 0;
922
923 if ((numofelectrons % 2) != 0) {
924 alpha_e = numofelectrons / 2 + numofelectrons % 2;
925 beta_e = numofelectrons / 2;
926 } else {
927 alpha_e = numofelectrons / 2;
928 beta_e = alpha_e;
929 }
930
931 AOOverlap dftAOoverlap;
932 AOKinetic dftAOkinetic;
933 AOMultipole dftAOESP;
934 AOECP dftAOECP;
935 ERIs ERIs_atom;
936
937 dftAOoverlap.Fill(dftbasis);
938 dftAOkinetic.Fill(dftbasis);
939
940 dftAOESP.FillPotential(dftbasis, atom);
941 ERIs_atom.Initialize_4c(dftbasis);
942
943 ConvergenceAcc Convergence_alpha;
944 ConvergenceAcc Convergence_beta;
947 opt_alpha.histlength = 20;
948 opt_alpha.levelshift = 0.1;
949 opt_alpha.levelshiftend = 0.0;
950 opt_alpha.usediis = true;
951 opt_alpha.adiis_start = 0.0;
952 opt_alpha.diis_start = 0.0;
953 opt_alpha.numberofelectrons = alpha_e;
954
955 ConvergenceAcc::options opt_beta = opt_alpha;
956 opt_beta.numberofelectrons = beta_e;
957
958 Logger log;
959 Convergence_alpha.Configure(opt_alpha);
960 Convergence_alpha.setLogger(&log);
961 Convergence_alpha.setOverlap(dftAOoverlap, 1e-8);
962 Convergence_beta.Configure(opt_beta);
963 Convergence_beta.setLogger(&log);
964 Convergence_beta.setOverlap(dftAOoverlap, 1e-8);
965
966 Eigen::MatrixXd H0 = dftAOkinetic.Matrix() + dftAOESP.Matrix();
967 if (with_ecp) {
968 dftAOECP.FillPotential(dftbasis, ecp);
969 H0 += dftAOECP.Matrix();
970 }
971
972 tools::EigenSystem MOs_alpha = Convergence_alpha.SolveFockmatrix(H0);
973 Eigen::MatrixXd dftAOdmat_alpha = Convergence_alpha.DensityMatrix(MOs_alpha);
974
975 if (uniqueAtom.getElement() == "H") {
976 return dftAOdmat_alpha;
977 }
978
979 tools::EigenSystem MOs_beta = Convergence_beta.SolveFockmatrix(H0);
980 Eigen::MatrixXd dftAOdmat_beta = Convergence_beta.DensityMatrix(MOs_beta);
981
982 Index maxiter = 80;
983 for (Index this_iter = 0; this_iter < maxiter; this_iter++) {
984 Eigen::MatrixXd H_alpha = H0;
985 Eigen::MatrixXd H_beta = H0;
986
987 double E_coul = 0.0;
988 double E_exx = 0.0;
989 double E_xc = 0.0;
990
991 double integral_error = std::min(1e-5 * 0.5 *
992 (Convergence_alpha.getDIIsError() +
993 Convergence_beta.getDIIsError()),
994 1e-5);
995
996 if (ScaHFX_ > 0) {
997 std::array<Eigen::MatrixXd, 2> both_alpha =
998 ERIs_atom.CalculateERIs_EXX_4c(dftAOdmat_alpha, integral_error);
999 std::array<Eigen::MatrixXd, 2> both_beta =
1000 ERIs_atom.CalculateERIs_EXX_4c(dftAOdmat_beta, integral_error);
1001
1002 Eigen::MatrixXd Hartree = both_alpha[0] + both_beta[0];
1003 H_alpha += Hartree + ScaHFX_ * both_alpha[1];
1004 H_beta += Hartree + ScaHFX_ * both_beta[1];
1005
1006 E_coul =
1007 0.5 * (dftAOdmat_alpha + dftAOdmat_beta).cwiseProduct(Hartree).sum();
1008 E_exx = 0.5 * ScaHFX_ *
1009 (both_alpha[1].cwiseProduct(dftAOdmat_alpha).sum() +
1010 both_beta[1].cwiseProduct(dftAOdmat_beta).sum());
1011 } else {
1012 Eigen::MatrixXd Hartree = ERIs_atom.CalculateERIs_4c(
1013 dftAOdmat_alpha + dftAOdmat_beta, integral_error);
1014 H_alpha += Hartree;
1015 H_beta += Hartree;
1016 E_coul =
1017 0.5 * (dftAOdmat_alpha + dftAOdmat_beta).cwiseProduct(Hartree).sum();
1018 }
1019
1020 auto vxc =
1021 gridIntegration.IntegrateVXCSpin(dftAOdmat_alpha, dftAOdmat_beta);
1022 H_alpha += vxc.vxc_alpha;
1023 H_beta += vxc.vxc_beta;
1024 E_xc = vxc.energy;
1025
1026 double E_one_alpha = dftAOdmat_alpha.cwiseProduct(H0).sum();
1027 double E_one_beta = dftAOdmat_beta.cwiseProduct(H0).sum();
1028 double totenergy = E_one_alpha + E_one_beta + E_coul + E_exx + E_xc;
1029
1030 dftAOdmat_alpha = Convergence_alpha.Iterate(dftAOdmat_alpha, H_alpha,
1031 MOs_alpha, totenergy);
1032 dftAOdmat_beta =
1033 Convergence_beta.Iterate(dftAOdmat_beta, H_beta, MOs_beta, totenergy);
1034
1036 << TimeStamp() << " Iter " << this_iter << " of " << maxiter << " Etot "
1037 << totenergy << " diise_a " << Convergence_alpha.getDIIsError()
1038 << " diise_b " << Convergence_beta.getDIIsError() << "\n\t\t a_gap "
1039 << MOs_alpha.eigenvalues()(alpha_e) -
1040 MOs_alpha.eigenvalues()(alpha_e - 1)
1041 << " b_gap "
1042 << MOs_beta.eigenvalues()(beta_e) - MOs_beta.eigenvalues()(beta_e - 1)
1043 << " Nalpha="
1044 << dftAOoverlap.Matrix().cwiseProduct(dftAOdmat_alpha).sum()
1045 << " Nbeta=" << dftAOoverlap.Matrix().cwiseProduct(dftAOdmat_beta).sum()
1046 << std::flush;
1047
1048 bool converged =
1049 Convergence_alpha.isConverged() && Convergence_beta.isConverged();
1050 if (converged || this_iter == maxiter - 1) {
1051 if (converged) {
1053 << TimeStamp() << " Converged after " << this_iter + 1
1054 << " iterations" << std::flush;
1055 } else {
1057 << TimeStamp() << " Not converged after " << this_iter + 1
1058 << " iterations. Unconverged density.\n\t\t\t"
1059 << " DIIsError_alpha=" << Convergence_alpha.getDIIsError()
1060 << " DIIsError_beta=" << Convergence_beta.getDIIsError()
1061 << std::flush;
1062 }
1063 break;
1064 }
1065 }
1066
1067 Eigen::MatrixXd avgmatrix =
1068 SphericalAverageShells(dftAOdmat_alpha + dftAOdmat_beta, dftbasis);
1070 << TimeStamp() << " Atomic density Matrix for " << uniqueAtom.getElement()
1071 << " gives N=" << std::setprecision(9)
1072 << avgmatrix.cwiseProduct(dftAOoverlap.Matrix()).sum() << " electrons."
1073 << std::flush;
1074 return avgmatrix;
1075}
1076
1077Eigen::MatrixXd DFTEngine::AtomicGuess(const QMMolecule& mol) const {
1078
1079 std::vector<std::string> elements = mol.FindUniqueElements();
1081 << TimeStamp() << " Scanning molecule of size " << mol.size()
1082 << " for unique elements" << std::flush;
1083 QMMolecule uniqueelements = QMMolecule("uniqueelements", 0);
1084 for (auto element : elements) {
1085 uniqueelements.push_back(QMAtom(0, element, Eigen::Vector3d::Zero()));
1086 }
1087
1088 XTP_LOG(Log::info, *pLog_) << TimeStamp() << " " << uniqueelements.size()
1089 << " unique elements found" << std::flush;
1090 std::vector<Eigen::MatrixXd> uniqueatom_guesses;
1091 for (QMAtom& unique_atom : uniqueelements) {
1093 << TimeStamp() << " Calculating atom density for "
1094 << unique_atom.getElement() << std::flush;
1095 Eigen::MatrixXd dmat_unrestricted = RunAtomicDFT_unrestricted(unique_atom);
1096 uniqueatom_guesses.push_back(dmat_unrestricted);
1097 }
1098
1099 Eigen::MatrixXd guess =
1100 Eigen::MatrixXd::Zero(dftbasis_.AOBasisSize(), dftbasis_.AOBasisSize());
1101 Index start = 0;
1102 for (const QMAtom& atom : mol) {
1103 Index index = 0;
1104 for (; index < uniqueelements.size(); index++) {
1105 if (atom.getElement() == uniqueelements[index].getElement()) {
1106 break;
1107 }
1108 }
1109 Eigen::MatrixXd& dmat_unrestricted = uniqueatom_guesses[index];
1110 guess.block(start, start, dmat_unrestricted.rows(),
1111 dmat_unrestricted.cols()) = dmat_unrestricted;
1112 start += dmat_unrestricted.rows();
1113 }
1114
1115 return guess;
1116}
1117
1119 if (initial_guess_ == "orbfile") {
1120
1121 if (orb.hasDFTbasisName()) {
1122 if (orb.getDFTbasisName() != dftbasis_name_) {
1123 throw std::runtime_error(
1124 (boost::format("Basisset Name in guess orb file "
1125 "and in dftengine option file differ %1% vs %2%") %
1127 .str());
1128 }
1129 } else {
1131 << TimeStamp()
1132 << " WARNING: "
1133 "Orbital file has no basisset information,"
1134 "using it as a guess might work or not for calculation with "
1135 << dftbasis_name_ << std::flush;
1136 }
1137 }
1138
1139 const Index target_charge = orb.getCharge();
1140 const Index multiplicity = orb.getSpin();
1141
1142 orb.setChargeAndSpin(target_charge, multiplicity);
1145
1147 orb.setXCGrid(grid_name_);
1148 orb.setScaHFX(ScaHFX_);
1149 if (!ecp_name_.empty()) {
1150 orb.setECPName(ecp_name_);
1151 }
1152 if (!auxbasis_name_.empty()) {
1154 }
1155
1156 if (initial_guess_ == "orbfile") {
1157 if (orb.hasECPName() || !ecp_name_.empty()) {
1158 if (orb.getECPName() != ecp_name_) {
1159 throw std::runtime_error(
1160 (boost::format("ECPs in orb file: %1% and options %2% differ") %
1161 orb.getECPName() % ecp_name_)
1162 .str());
1163 }
1164 }
1167 throw std::runtime_error(
1168 (boost::format("Number of electrons in guess orb file "
1169 "and in dftengine differ: "
1170 "alpha %1% vs %2%, beta %3% vs %4%.") %
1173 .str());
1174 }
1175 if (orb.getBasisSetSize() != dftbasis_.AOBasisSize()) {
1176 throw std::runtime_error(
1177 (boost::format("Number of levels in guess orb file: "
1178 "%1% and in dftengine: %2% differ.") %
1179 orb.getBasisSetSize() % dftbasis_.AOBasisSize())
1180 .str());
1181 }
1182 } else {
1185 }
1186 return;
1187}
1188
1189void DFTEngine::Prepare(Orbitals& orb, Index numofelectrons) {
1190 QMMolecule& mol = orb.QMAtoms();
1191
1193 << TimeStamp() << " Using " << OPENMP::getMaxThreads() << " threads"
1194 << std::flush;
1195
1196 if (XTP_HAS_MKL_OVERLOAD()) {
1198 << TimeStamp() << " Using MKL overload for Eigen " << std::flush;
1199 } else {
1201 << TimeStamp()
1202 << " Using native Eigen implementation, no BLAS overload "
1203 << std::flush;
1204 }
1205
1206 XTP_LOG(Log::error, *pLog_) << " Molecule Coordinates [A] " << std::flush;
1207 for (const QMAtom& atom : mol) {
1208 const Eigen::Vector3d pos = atom.getPos() * tools::conv::bohr2ang;
1209 std::string output = (boost::format(" %1$s"
1210 " %2$+1.4f %3$+1.4f %4$+1.4f") %
1211 atom.getElement() % pos[0] % pos[1] % pos[2])
1212 .str();
1213
1214 XTP_LOG(Log::error, *pLog_) << output << std::flush;
1215 }
1216
1218 dftbasis_ = orb.getDftBasis();
1219
1221 << TimeStamp() << " Loaded DFT Basis Set " << dftbasis_name_ << " with "
1222 << dftbasis_.AOBasisSize() << " functions" << std::flush;
1223
1224 if (!auxbasis_name_.empty()) {
1225 BasisSet auxbasisset;
1226 auxbasisset.Load(auxbasis_name_);
1227 auxbasis_.Fill(auxbasisset, mol);
1229 << TimeStamp() << " Loaded AUX Basis Set " << auxbasis_name_ << " with "
1230 << auxbasis_.AOBasisSize() << " functions" << std::flush;
1231 }
1232 if (!ecp_name_.empty()) {
1233 ECPBasisSet ecpbasisset;
1234 ecpbasisset.Load(ecp_name_);
1236 << TimeStamp() << " Loaded ECP library " << ecp_name_ << std::flush;
1237
1238 std::vector<std::string> results = ecp_.Fill(ecpbasisset, mol);
1240 << TimeStamp() << " Filled ECP Basis" << std::flush;
1241 if (results.size() > 0) {
1242 std::string message = "";
1243 for (const std::string& element : results) {
1244 message += " " + element;
1245 }
1247 << TimeStamp() << " Found no ECPs for elements" << message
1248 << std::flush;
1249 }
1250 }
1251
1252 numofelectrons_ = 0;
1255 num_docc_ = 0;
1256 num_socc_alpha_ = 0;
1257
1258 Index nuclear_charge = 0;
1259 for (const QMAtom& atom : mol) {
1260 nuclear_charge += atom.getNuccharge();
1261 }
1262
1263 Index target_charge = orb.getCharge();
1264 Index multiplicity = orb.getSpin();
1265
1266 if (multiplicity < 1) {
1267 throw std::runtime_error("Spin multiplicity must be >= 1.");
1268 }
1269
1270 if (numofelectrons >= 0) {
1271 numofelectrons_ = numofelectrons;
1272 } else {
1273 numofelectrons_ = nuclear_charge - target_charge;
1274 }
1275
1276 Index spin_excess = multiplicity - 1;
1277
1278 if (numofelectrons_ < 0) {
1279 throw std::runtime_error("Computed a negative number of electrons.");
1280 }
1281
1282 if (spin_excess > numofelectrons_) {
1283 throw std::runtime_error(
1284 "Spin multiplicity incompatible with total number of electrons.");
1285 }
1286
1287 if (((numofelectrons_ + spin_excess) % 2) != 0) {
1288 throw std::runtime_error(
1289 "Charge and spin multiplicity imply non-integer alpha/beta "
1290 "occupations.");
1291 }
1292
1293 num_alpha_electrons_ = (numofelectrons_ + spin_excess) / 2;
1294 num_beta_electrons_ = (numofelectrons_ - spin_excess) / 2;
1295
1298
1300 << TimeStamp() << " Total number of electrons: " << numofelectrons_
1301 << " (charge=" << target_charge << ", multiplicity=" << multiplicity
1302 << ", alpha=" << num_alpha_electrons_ << ", beta=" << num_beta_electrons_
1303 << ", docc=" << num_docc_ << ", socc=" << num_socc_alpha_ << ")"
1304 << std::flush;
1305
1307 return;
1308}
1309
1312 if (ScaHFX_ > 0) {
1314 << TimeStamp() << " Using hybrid functional with alpha=" << ScaHFX_
1315 << std::flush;
1316 }
1317 Vxc_Grid grid;
1318 grid.GridSetup(grid_name_, mol, dftbasis_);
1319 Vxc_Potential<Vxc_Grid> vxc(grid);
1322 << TimeStamp() << " Setup numerical integration grid " << grid_name_
1323 << " for vxc functional " << xc_functional_name_ << std::flush;
1325 << "\t\t "
1326 << " with " << grid.getGridSize() << " points"
1327 << " divided into " << grid.getBoxesSize() << " boxes" << std::flush;
1328 return vxc;
1329}
1330
1331double DFTEngine::NuclearRepulsion(const QMMolecule& mol) const {
1332 double E_nucnuc = 0.0;
1333
1334 for (Index i = 0; i < mol.size(); i++) {
1335 const Eigen::Vector3d& r1 = mol[i].getPos();
1336 double charge1 = double(mol[i].getNuccharge());
1337 for (Index j = 0; j < i; j++) {
1338 const Eigen::Vector3d& r2 = mol[j].getPos();
1339 double charge2 = double(mol[j].getNuccharge());
1340 E_nucnuc += charge1 * charge2 / (r1 - r2).norm();
1341 }
1342 }
1343 return E_nucnuc;
1344}
1345
1346// spherically average the density matrix belonging to two shells
1348 const Eigen::MatrixXd& dmat, const AOBasis& dftbasis) const {
1349 Eigen::MatrixXd avdmat = Eigen::MatrixXd::Zero(dmat.rows(), dmat.cols());
1350 for (const AOShell& shellrow : dftbasis) {
1351 Index size_row = shellrow.getNumFunc();
1352 Index start_row = shellrow.getStartIndex();
1353 for (const AOShell& shellcol : dftbasis) {
1354 Index size_col = shellcol.getNumFunc();
1355 Index start_col = shellcol.getStartIndex();
1356 Eigen::MatrixXd shelldmat =
1357 dmat.block(start_row, start_col, size_row, size_col);
1358 if (shellrow.getL() == shellcol.getL()) {
1359 double diagavg = shelldmat.diagonal().sum() / double(shelldmat.rows());
1360 Index offdiagelements =
1361 shelldmat.rows() * shelldmat.cols() - shelldmat.cols();
1362 double offdiagavg = (shelldmat.sum() - shelldmat.diagonal().sum()) /
1363 double(offdiagelements);
1364 avdmat.block(start_row, start_col, size_row, size_col).array() =
1365 offdiagavg;
1366 avdmat.block(start_row, start_col, size_row, size_col)
1367 .diagonal()
1368 .array() = diagavg;
1369 } else {
1370 double avg = shelldmat.sum() / double(shelldmat.size());
1371 avdmat.block(start_row, start_col, size_row, size_col).array() = avg;
1372 }
1373 }
1374 }
1375 return avdmat;
1376}
1377
1379 const QMMolecule& mol,
1380 const std::vector<std::unique_ptr<StaticSite> >& multipoles) const {
1381
1382 if (multipoles.size() == 0) {
1383 return 0;
1384 }
1385
1386 double E_ext = 0;
1387 eeInteractor interactor;
1388 for (const QMAtom& atom : mol) {
1389 StaticSite nucleus = StaticSite(atom, double(atom.getNuccharge()));
1390 for (const std::unique_ptr<StaticSite>& site : *externalsites_) {
1391 if ((site->getPos() - nucleus.getPos()).norm() < 1e-7) {
1393 << " External site sits on nucleus, "
1394 "interaction between them is ignored."
1395 << std::flush;
1396 continue;
1397 }
1398 E_ext += interactor.CalcStaticEnergy_site(*site, nucleus);
1399 }
1400 }
1401 return E_ext;
1402}
1403
1404Eigen::MatrixXd DFTEngine::IntegrateExternalField(const QMMolecule& mol) const {
1405
1406 AODipole dipole;
1407 dipole.setCenter(mol.getPos());
1408 dipole.Fill(dftbasis_);
1409 Eigen::MatrixXd result =
1410 Eigen::MatrixXd::Zero(dipole.Dimension(), dipole.Dimension());
1411 for (Index i = 0; i < 3; i++) {
1412 result -= dipole.Matrix()[i] * extfield_[i];
1413 }
1414 return result;
1415}
1416
1418 const QMMolecule& mol,
1419 const std::vector<std::unique_ptr<StaticSite> >& multipoles) const {
1420
1421 Mat_p_Energy result(dftbasis_.AOBasisSize(), dftbasis_.AOBasisSize());
1422 AOMultipole dftAOESP;
1423
1424 dftAOESP.FillPotential(dftbasis_, multipoles);
1426 << TimeStamp() << " Filled DFT external multipole potential matrix"
1427 << std::flush;
1428 result.matrix() = dftAOESP.Matrix();
1429 result.energy() = ExternalRepulsion(mol, multipoles);
1430
1431 return result;
1432}
1433
1435 const QMMolecule& mol, const Orbitals& extdensity) const {
1436 BasisSet basis;
1437 basis.Load(extdensity.getDFTbasisName());
1438 AOBasis aobasis;
1439 aobasis.Fill(basis, extdensity.QMAtoms());
1440 Vxc_Grid grid;
1441 grid.GridSetup(gridquality_, extdensity.QMAtoms(), aobasis);
1442 DensityIntegration<Vxc_Grid> numint(grid);
1443 Eigen::MatrixXd dmat = extdensity.DensityMatrixFull(state_);
1444
1445 numint.IntegrateDensity(dmat);
1447 << TimeStamp() << " Calculated external density" << std::flush;
1448 Eigen::MatrixXd e_contrib = numint.IntegratePotential(dftbasis_);
1450 << TimeStamp() << " Calculated potential from electron density"
1451 << std::flush;
1452 AOMultipole esp;
1453 esp.FillPotential(dftbasis_, extdensity.QMAtoms());
1454
1455 double nuc_energy = 0.0;
1456 for (const QMAtom& atom : mol) {
1457 nuc_energy +=
1458 numint.IntegratePotential(atom.getPos()) * double(atom.getNuccharge());
1459 for (const QMAtom& extatom : extdensity.QMAtoms()) {
1460 const double dist = (atom.getPos() - extatom.getPos()).norm();
1461 nuc_energy +=
1462 double(atom.getNuccharge()) * double(extatom.getNuccharge()) / dist;
1463 }
1464 }
1466 << TimeStamp() << " Calculated potential from nuclei" << std::flush;
1468 << TimeStamp() << " Electrostatic: " << nuc_energy << std::flush;
1469 return Mat_p_Energy(nuc_energy, e_contrib + esp.Matrix());
1470}
1471
1473 const Eigen::MatrixXd& GuessMOs) const {
1474 Eigen::MatrixXd nonortho =
1475 GuessMOs.transpose() * dftAOoverlap_.Matrix() * GuessMOs;
1476 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(nonortho);
1477 Eigen::MatrixXd result = GuessMOs * es.operatorInverseSqrt();
1478 return result;
1479}
1480
1481/*************************************************************
1482 * Extended Hueckel Theory
1483 ************************************************************/
1485 const QMMolecule& mol) const {
1486
1488
1489 const Index nao = dftbasis_.AOBasisSize();
1490 Eigen::VectorXd eps = Eigen::VectorXd::Zero(nao);
1491
1492 for (const AOShell& shell : dftbasis_) {
1493
1494 int l = static_cast<int>(shell.getL());
1495 Index start = shell.getStartIndex();
1496 Index nfunc = shell.getNumFunc();
1497
1498 const QMAtom& atom = mol[shell.getAtomIndex()];
1499 const std::string& element = atom.getElement();
1500
1501 int used_l = l;
1502 double e = params.GetWithFallback(element, l, &used_l);
1503
1504 for (Index i = 0; i < nfunc; ++i) {
1505 eps(start + i) = e;
1506 }
1507 }
1508
1509 return eps;
1510}
1511
1512Eigen::MatrixXd DFTEngine::BuildEHTHamiltonian(const QMMolecule& mol) const {
1513
1514 const Eigen::MatrixXd& S = dftAOoverlap_.Matrix();
1515 const Index nao = S.rows();
1516 Eigen::VectorXd eps = BuildEHTOrbitalEnergies(mol);
1517 Eigen::MatrixXd H = Eigen::MatrixXd::Zero(nao, nao);
1518 constexpr double K = 1.75;
1519
1520 for (Index mu = 0; mu < nao; ++mu) {
1521 H(mu, mu) = eps(mu);
1522 for (Index nu = 0; nu < mu; ++nu) {
1523 double hij = K * S(mu, nu) * 0.5 * (eps(mu) + eps(nu));
1524 H(mu, nu) = hij;
1525 H(nu, mu) = hij;
1526 }
1527 }
1528
1529 return H;
1530}
1531
1533
1535 << TimeStamp() << " Building Extended Huckel guess" << std::flush;
1536
1537 Eigen::MatrixXd H = BuildEHTHamiltonian(mol);
1538
1540 << TimeStamp() << " Solving EHT generalized eigenproblem" << std::flush;
1541
1542 return conv_accelerator_.SolveFockmatrix(H);
1543}
1544
1546 const Mat_p_Energy& H0, const QMMolecule& mol,
1547 const Vxc_Potential<Vxc_Grid>& vxcpotential) const {
1548
1550
1551 Eigen::MatrixXd Dmat = conv_accelerator_.DensityMatrix(eht);
1552
1553 Mat_p_Energy e_vxc = vxcpotential.IntegrateVXC(Dmat);
1554
1555 Eigen::MatrixXd H = H0.matrix() + e_vxc.matrix();
1556
1557 if (ScaHFX_ > 0) {
1558 std::array<Eigen::MatrixXd, 2> both =
1559 CalcERIs_EXX(Eigen::MatrixXd::Zero(0, 0), Dmat, 1e-12);
1560 H += both[0];
1561 H += ScaHFX_ * both[1];
1562 } else {
1563 H += CalcERIs(Dmat, 1e-12);
1564 }
1565
1566 return conv_accelerator_.SolveFockmatrix(H);
1567}
1568
1569} // namespace xtp
1570} // namespace votca
const Eigen::VectorXd & eigenvalues() const
Definition eigensystem.h:30
const Eigen::MatrixXd & eigenvectors() const
Definition eigensystem.h:33
class to manage program options with xml serialization functionality
Definition property.h:55
Property & get(const std::string &key)
get existing property
Definition property.cc:79
bool exists(const std::string &key) const
check whether property exists
Definition property.cc:122
T as() const
return value as type
Definition property.h:283
Container to hold Basisfunctions for all atoms.
Definition aobasis.h:42
void Fill(const BasisSet &bs, const QMMolecule &atoms)
Definition aobasis.cc:85
Index Dimension() final
Definition aomatrix.h:91
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 FillPotential(const AOBasis &aobasis, const ECPAOBasis &ecp)
Definition aoecp.cc:59
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
Definition aomatrix.h:41
void FillPotential(const AOBasis &aobasis, const QMMolecule &atoms)
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
Definition aomatrix.h:52
const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > & Matrix() const
Definition aopotential.h:39
void push_back(const T &atom)
std::vector< std::string > FindUniqueElements() const
const Eigen::Vector3d & getPos() const
void Load(const std::string &name)
Definition basisset.cc:149
Eigen::MatrixXd DensityMatrix(const tools::EigenSystem &MOs) const
void Configure(const ConvergenceAcc::options &opt)
void setLogger(Logger *log)
Attach the logger used for convergence diagnostics.
Eigen::MatrixXd Iterate(const Eigen::MatrixXd &dmat, Eigen::MatrixXd &H, tools::EigenSystem &MOs, double totE)
double getDIIsError() const
Return the DIIS commutator norm from the latest iteration.
tools::EigenSystem SolveFockmatrix(const Eigen::MatrixXd &H) const
Solve the generalized eigenvalue problem for the current Fock matrix.
void setOverlap(AOOverlap &S, double etol)
Precompute overlap-dependent quantities used when solving the Fock matrix.
Eigen::MatrixXd CalcERIs(const Eigen::MatrixXd &Dmat, double error) const
Build the Coulomb matrix contribution from the current density matrix.
Definition dftengine.cc:262
std::string auxbasis_name_
Definition dftengine.h:242
std::string gridquality_
Definition dftengine.h:280
tools::EigenSystem ModelPotentialGuess(const Mat_p_Energy &H0, const QMMolecule &mol, const Vxc_Potential< Vxc_Grid > &vxcpotential) const
Definition dftengine.cc:282
Mat_p_Energy IntegrateExternalMultipoles(const QMMolecule &mol, const std::vector< std::unique_ptr< StaticSite > > &multipoles) const
tools::EigenSystem IndependentElectronGuess(const Mat_p_Energy &H0) const
Generate an initial guess by diagonalizing the core Hamiltonian only.
Definition dftengine.cc:271
bool EvaluateClosedShell(Orbitals &orb, const Mat_p_Energy &H0, const Vxc_Potential< Vxc_Grid > &vxcpotential)
Run the restricted closed-shell SCF loop and store the converged result.
Definition dftengine.cc:321
void PrintMOsUKS(const Eigen::VectorXd &alpha_energies, const Eigen::VectorXd &beta_energies, Log::Level level) const
Print separate alpha and beta orbital energies for a UKS calculation.
Definition dftengine.cc:168
std::string dftbasis_name_
Definition dftengine.h:243
bool EvaluateUKS(Orbitals &orb, const Mat_p_Energy &H0, const Vxc_Potential< Vxc_Grid > &vxcpotential)
Definition dftengine.cc:516
Mat_p_Energy IntegrateExternalDensity(const QMMolecule &mol, const Orbitals &extdensity) const
std::string ecp_name_
Definition dftengine.h:244
std::string grid_name_
Definition dftengine.h:254
Eigen::MatrixXd RunAtomicDFT_unrestricted(const QMAtom &uniqueAtom) const
Definition dftengine.cc:893
void Prepare(Orbitals &orb, Index numofelectrons=-1)
std::string initial_guess_
Definition dftengine.h:259
std::string orbfilename_
Definition dftengine.h:279
Eigen::MatrixXd AtomicGuess(const QMMolecule &mol) const
Build an atomic-density based initial guess in the AO basis.
Eigen::MatrixXd BuildEHTHamiltonian(const QMMolecule &mol) const
Build the extended-Hückel Hamiltonian for the current molecule.
Eigen::MatrixXd IntegrateExternalField(const QMMolecule &mol) const
Integrate a homogeneous external electric field into the AO basis.
Eigen::Vector3d extfield_
Definition dftengine.h:286
std::array< Eigen::MatrixXd, 2 > CalcERIs_EXX(const Eigen::MatrixXd &MOCoeff, const Eigen::MatrixXd &Dmat, double error) const
Definition dftengine.cc:245
void Initialize(tools::Property &options)
Read DFT, grid, and SCF settings from the user options tree.
Definition dftengine.cc:60
std::string xc_functional_name_
Definition dftengine.h:275
void CalcElDipole(const Orbitals &orb) const
Evaluate and print the electronic dipole moment from the final density.
Definition dftengine.cc:228
double ExternalRepulsion(const QMMolecule &mol, const std::vector< std::unique_ptr< StaticSite > > &multipoles) const
ConvergenceAcc::options conv_opt_
Definition dftengine.h:264
void SetupInvariantMatrices()
Precompute AO matrices that remain unchanged throughout the SCF procedure.
Definition dftengine.cc:855
tools::EigenSystem ExtendedHuckelDFTGuess(const Mat_p_Energy &H0, const QMMolecule &mol, const Vxc_Potential< Vxc_Grid > &vxcpotential) const
std::string active_atoms_as_string_
Definition dftengine.h:289
void ConfigOrbfile(Orbitals &orb)
Propagate basis-set, XC, and metadata settings into the orbital container.
Eigen::VectorXd BuildEHTOrbitalEnergies(const QMMolecule &mol) const
Build orbital energies used in the extended-Hückel starting guess.
void PrintMOs(const Eigen::VectorXd &MOEnergies, Log::Level level)
Print a one-spin list of orbital energies and occupations to the logger.
Definition dftengine.cc:148
AOOverlap dftAOoverlap_
Definition dftengine.h:257
Mat_p_Energy SetupH0(const QMMolecule &mol) const
Assemble the one-electron core Hamiltonian for the current molecule.
Definition dftengine.cc:745
tools::EigenSystem ExtendedHuckelGuess(const QMMolecule &mol) const
Eigen::MatrixXd OrthogonalizeGuess(const Eigen::MatrixXd &GuessMOs) const
Orthonormalize an initial MO guess with respect to the AO overlap matrix.
Eigen::MatrixXd SphericalAverageShells(const Eigen::MatrixXd &dmat, const AOBasis &dftbasis) const
bool Evaluate(Orbitals &orb)
Definition dftengine.cc:303
std::vector< std::unique_ptr< StaticSite > > * externalsites_
Definition dftengine.h:271
Vxc_Potential< Vxc_Grid > SetupVxc(const QMMolecule &mol)
double NuclearRepulsion(const QMMolecule &mol) const
Compute the classical nucleus-nucleus repulsion energy.
ConvergenceAcc conv_accelerator_
Definition dftengine.h:266
double IntegratePotential(const Eigen::Vector3d &rvector) const
double IntegrateDensity(const Eigen::MatrixXd &density_matrix)
Container to hold ECPs for all atoms.
Definition ecpaobasis.h:43
std::vector< std::string > Fill(const ECPBasisSet &bs, QMMolecule &atoms)
Definition ecpaobasis.cc:69
void Load(const std::string &name)
Takes a density matrix and and an auxiliary basis set and calculates the electron repulsion integrals...
Definition ERIs.h:35
std::array< Eigen::MatrixXd, 2 > CalculateERIs_EXX_4c(const Eigen::MatrixXd &DMAT, double error) const
Definition ERIs.h:61
void Initialize_4c(const AOBasis &dftbasis)
Definition ERIs.cc:32
Eigen::MatrixXd CalculateERIs_4c(const Eigen::MatrixXd &DMAT, double error) const
Definition ERIs.h:56
double GetWithFallback(const std::string &element, int l, int *used_l=nullptr) const
void UpdateDmats(const Eigen::MatrixXd &dmat, double DiisError, Index Iteration)
void Configure(const Eigen::MatrixXd &dmat)
void resetMatrices(Eigen::MatrixXd &J, Eigen::MatrixXd &K, const Eigen::MatrixXd &dmat)
void Start(Index iteration, double DiisError)
void UpdateCriteria(double DiisError, Index Iteration)
const Eigen::MatrixXd & getDmat_diff() const
Logger is used for thread-safe output of messages.
Definition logger.h:164
Eigen::MatrixXd & matrix()
Definition eigen.h:80
double & energy()
Definition eigen.h:81
Index cols() const
Definition eigen.h:79
Index rows() const
Definition eigen.h:78
Container for molecular orbitals and derived one-particle data.
Definition orbitals.h:47
void setScaHFX(double ScaHFX)
Store the fraction of exact exchange associated with the functional.
Definition orbitals.h:419
Index getCharge() const
Return the stored total charge.
Definition orbitals.h:251
Index getSpin() const
Return the stored spin multiplicity.
Definition orbitals.h:249
const tools::EigenSystem & MOs_beta() const
Return read-only access to beta-spin molecular orbitals.
Definition orbitals.h:202
Index getNumberOfAlphaElectrons() const
Return the stored number of alpha electrons.
Definition orbitals.h:159
void setNumberOfAlphaElectrons(Index electrons)
Store the total number of alpha electrons.
Definition orbitals.h:139
Eigen::MatrixXd DensityMatrixFull(const QMState &state) const
Definition orbitals.cc:148
void SetupAuxBasis(std::string aux_basis_name)
Definition orbitals.cc:108
void setNumberOfBetaElectrons(Index electrons)
Store the total number of beta electrons.
Definition orbitals.h:144
void setECPName(const std::string &ECP)
Store the effective core potential label.
Definition orbitals.h:170
void setXCGrid(std::string grid)
Store the numerical XC grid quality label.
Definition orbitals.h:280
void setNumberOfOccupiedLevels(Index occupied_levels)
Definition orbitals.h:122
Index getBasisSetSize() const
Return the number of AO basis functions in the DFT basis.
Definition orbitals.h:72
void setQMEnergy(double qmenergy)
Store the total DFT energy.
Definition orbitals.h:292
bool hasECPName() const
Report whether an effective core potential label has been stored.
Definition orbitals.h:164
const tools::EigenSystem & MOs() const
Return read-only access to alpha/restricted molecular orbitals.
Definition orbitals.h:192
const QMMolecule & QMAtoms() const
Return read-only access to the molecular geometry.
Definition orbitals.h:259
void ReadFromCpt(const std::string &filename)
Read the orbital container from a checkpoint file on disk.
Definition orbitals.cc:770
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:243
bool hasDFTbasisName() const
Report whether a DFT basis-set name has been stored.
Definition orbitals.h:296
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:301
bool hasBetaMOs() const
Report whether beta-spin molecular orbitals are available.
Definition orbitals.h:187
void SetupDftBasis(std::string basis_name)
Build and attach the DFT AO basis from the stored molecular geometry.
Definition orbitals.cc:99
Eigen::Vector3d CalcElDipole(const QMState &state) const
Compute the electronic dipole moment associated with a state density.
Definition orbitals.cc:286
Index getNumberOfBetaElectrons() const
Return the stored number of beta electrons.
Definition orbitals.h:161
const AOBasis & getDftBasis() const
Return the DFT AO basis, throwing if it has not been initialized.
Definition orbitals.h:311
void setXCFunctionalName(std::string functionalname)
Definition orbitals.h:273
container for QM atoms
Definition qmatom.h:37
const std::string & getElement() const
Definition qmatom.h:63
Index getNuccharge() const
Definition qmatom.h:69
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
Definition qmstate.h:133
Class to represent Atom/Site in electrostatic.
Definition staticsite.h:37
const Eigen::Vector3d & getPos() const
Definition staticsite.h:80
Timestamp returns the current time as a string Example: cout << TimeStamp()
Definition logger.h:224
SpinDensity DensityMatrix(const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta) const
void setOverlap(AOOverlap &S, double etol)
void Configure(const options &opt_alpha, const options &opt_beta)
SpinDensity Iterate(const SpinDensity &dmat, SpinFock &H, tools::EigenSystem &MOs_alpha, tools::EigenSystem &MOs_beta, double totE)
Index getGridSize() const
Definition vxc_grid.h:41
void GridSetup(const std::string &type, const QMMolecule &atoms, const AOBasis &basis)
Definition vxc_grid.cc:229
Index getBoxesSize() const
Definition vxc_grid.h:42
Mat_p_Energy IntegrateVXC(const Eigen::MatrixXd &density_matrix) const
void setXCfunctional(const std::string &functional)
static double getExactExchange(const std::string &functional)
SpinResult IntegrateVXCSpin(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta) const
Mediates interaction between polar and static sites.
double CalcStaticEnergy_site(const StaticSite &site1, const StaticSite &site2) const
#define XTP_LOG(level, log)
Definition logger.h:40
const double bohr2ang
Definition constants.h:49
Index getMaxThreads()
Definition eigen.h:128
Charge transport classes.
Definition ERIs.h:28
bool XTP_HAS_MKL_OVERLOAD()
Definition eigen.h:41
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
Level
be loud and noisy
Definition globals.h:28
Spin-resolved density matrices returned for open-shell SCF updates.
Eigen::MatrixXd total() const
Return the total density P = P^alpha + P^beta.