22#include <boost/format.hpp>
58void CanonicalizeOrbitalPhases(Eigen::MatrixXd& coeffs) {
59 constexpr double tol = 1
e-14;
61 for (
Index col = 0; col < coeffs.cols(); ++col) {
62 Eigen::Index pivot = 0;
63 const double maxabs = coeffs.col(col).cwiseAbs().maxCoeff(&pivot);
69 if (coeffs(pivot, col) < 0.0) {
70 coeffs.col(col) *= -1.0;
75void CanonicalizeOrbitalPhases(tools::EigenSystem& mos) {
76 CanonicalizeOrbitalPhases(mos.eigenvectors());
106 const std::string key_xtpdft =
"xtpdft";
109 if (options.
exists(
".auxbasisset")) {
116 options.
get(key_xtpdft +
".fock_matrix_reset").
as<
Index>();
118 if (options.
exists(
".ecp")) {
122 if (options.
exists(key_xtpdft +
".force_uks_path")) {
126 if (options.
exists(key_xtpdft +
".polarizability")) {
128 options.
get(key_xtpdft +
".polarizability").
as<
bool>();
131 if (options.
exists(key_xtpdft +
".compute_forces")) {
146 throw std::runtime_error(
147 "compute_forces=true was requested, but the libint2 this was "
148 "built against does not support derivative integrals for one "
149 "or more operator categories it needs (one-body, the two-center "
150 "Coulomb metric, or three-center RI -- see "
151 "libint2_derivative_calls.cc's own compile guards for exactly "
152 "which). Many pre-packaged libint2 builds (Homebrew, Ubuntu "
153 "apt, etc.) do not enable derivative-integral support for all "
154 "of these by default; rebuild libint2 with "
155 "--enable-1body/--enable-eri2/--enable-eri3 to use this "
171 throw std::runtime_error(
172 "compute_forces=true was requested together with an ECP ('" +
174 "'), but analytic nuclear forces do not yet include the ECP "
175 "contribution to the force (d(V_ECP)/dR) -- computing forces "
176 "in this configuration would silently omit that term rather "
177 "than fail visibly. Either drop the ECP or do not request "
178 "compute_forces until this is implemented.");
181 if (options.
exists(key_xtpdft +
".cdft.enabled")) {
194 std::string indices_str =
195 options.
get(key_xtpdft +
".cdft.indices").
as<std::string>();
196 if (indices_str.empty()) {
197 throw std::runtime_error(
198 "cdft.enabled=true was requested, but cdft.indices is empty -- "
199 "specify which atoms (0-based, e.g. '1 3 13:17', same syntax "
200 "already used for diabatization.xml's own fragment indices) "
201 "make up the constrained fragment.");
206 options.
get(key_xtpdft +
".cdft.charge").
as<
double>();
208 options.
get(key_xtpdft +
".cdft.initial_lambda").
as<
double>();
210 options.
get(key_xtpdft +
".cdft.max_iterations").
as<
Index>();
212 options.
get(key_xtpdft +
".cdft.population_tolerance").
as<
double>();
214 options.
get(key_xtpdft +
".cdft.guess_strategy").
as<std::string>();
231 throw std::runtime_error(
232 "initial_guess=dimer_guess requires both dimer_guess_orbA and "
233 "dimer_guess_orbB to be set to real monomer .orb file paths.");
237 grid_name_ = options.
get(key_xtpdft +
".integration_grid").
as<std::string>();
240 if (options.
exists(key_xtpdft +
".externaldensity")) {
243 options.
get(key_xtpdft +
".externaldensity.orbfile").
as<std::string>();
244 gridquality_ = options.
get(key_xtpdft +
".externaldensity.gridquality")
247 options.
get(key_xtpdft +
".externaldensity.state").
as<std::string>();
250 if (options.
exists(
".externalfield")) {
256 options.
get(key_xtpdft +
".convergence.energy").
as<
double>();
258 options.
get(key_xtpdft +
".convergence.error").
as<
double>();
260 options.
get(key_xtpdft +
".convergence.max_iterations").
as<
Index>();
263 options.
get(key_xtpdft +
".convergence.method").
as<std::string>();
264 if (method ==
"DIIS") {
266 }
else if (method ==
"mixing") {
274 options.
get(key_xtpdft +
".convergence.mixing").
as<
double>();
278 options.
get(key_xtpdft +
".convergence.mixing_max").
as<
double>();
280 options.
get(key_xtpdft +
".convergence.levelshift").
as<
double>();
282 options.
get(key_xtpdft +
".convergence.levelshift_end").
as<
double>();
287 options.
get(key_xtpdft +
".convergence.mixing_end").
as<
double>();
289 options.
get(key_xtpdft +
".convergence.DIIS_maxout").
as<
bool>();
291 options.
get(key_xtpdft +
".convergence.DIIS_length").
as<
Index>();
293 options.
get(key_xtpdft +
".convergence.DIIS_start").
as<
double>();
295 options.
get(key_xtpdft +
".convergence.ADIIS_start").
as<
double>();
297 options.
get(key_xtpdft +
".convergence.davidson_max_iter").
as<
Index>();
299 key_xtpdft +
".convergence.energy_reset", 1.0);
301 key_xtpdft +
".convergence.second_order",
"auto");
304 throw std::runtime_error(
305 "xtpdft.convergence.second_order must be auto, always or off, not " +
309 key_xtpdft +
".overlap_tolerance", 1
e-8);
311 key_xtpdft +
".ri_pair_threshold", 1
e-10);
313 if (options.
exists(key_xtpdft +
".dft_in_dft.activeatoms")) {
315 options.
get(key_xtpdft +
".dft_in_dft.activeatoms").
as<std::string>();
317 options.
get(key_xtpdft +
".dft_in_dft.threshold").
as<
double>();
319 options.
get(key_xtpdft +
".dft_in_dft.levelshift").
as<
double>();
321 options.
get(key_xtpdft +
".dft_in_dft.truncate_basis").
as<
bool>();
324 options.
get(key_xtpdft +
".dft_in_dft.truncation_threshold")
331 XTP_LOG(level, *
pLog_) <<
" Orbital energies: " << std::flush;
332 XTP_LOG(level, *
pLog_) <<
" index occupation energy(Hartree) " << std::flush;
334 for (
Index i = 0; i < MOEnergies.size(); ++i) {
342 XTP_LOG(level, *
pLog_) << (boost::format(
" %1$5d %2$1d %3$+1.10f") %
343 i % occupancy % MOEnergies(i))
351 const Eigen::VectorXd& beta_energies,
353 XTP_LOG(level, *
pLog_) <<
" UKS orbital energies:" << std::flush;
354 XTP_LOG(level, *
pLog_) <<
" index occ eps_a(Ha) eps_b(Ha)"
358 std::max<Index>(alpha_energies.size(), beta_energies.size());
360 for (
Index i = 0; i < nrows; ++i) {
364 std::string occ =
"0";
365 if (occ_a && occ_b) {
373 std::string eps_a =
" -";
374 std::string eps_b =
" -";
376 if (i < alpha_energies.size()) {
377 eps_a = (boost::format(
"%+1.10f") % alpha_energies(i)).str();
379 if (i < beta_energies.size()) {
380 eps_b = (boost::format(
"%+1.10f") % beta_energies(i)).str();
384 " %1$5d %2$1s %3$15s %4$15s") %
385 i % occ % eps_a % eps_b)
393 " alpha HOMO-LUMO gap: %+1.10f Ha") %
402 " beta HOMO-LUMO gap: %+1.10f Ha") %
414 <<
TimeStamp() <<
" Electric Dipole is[e*bohr]:\n\t\t dx=" << result[0]
415 <<
"\n\t\t dy=" << result[1] <<
"\n\t\t dz=" << result[2] << std::flush;
449 Orbitals& orb,
const Eigen::MatrixXd& Dmat,
454 <<
" Skipping force calculation: RI-J gradient (DFTGradient::"
455 "RIJGradient) only implements the RI path, but this SCF ran "
456 "without an auxiliary basis (conventional 4-center ERIs)."
464 <<
" Skipping force calculation: the libint2 this was built "
465 "against does not support derivative integrals for one or "
466 "more operator categories it needs. Many pre-packaged "
467 "libint2 builds (Homebrew, Ubuntu apt, etc.) do not enable "
468 "this by default -- rebuild libint2 with "
469 "--enable-1body/--enable-eri2/--enable-eri3 to use analytic "
490 <<
"') was used for this SCF, but analytic nuclear forces do "
491 "not yet include the ECP contribution to the force "
492 "(d(V_ECP)/dR) -- computing forces in this configuration "
493 "would silently omit that term rather than fail visibly."
502 << natoms <<
" atoms)" << std::flush;
516 <<
" Computing one-electron (kinetic + nuclear "
517 "attraction) derivatives"
520 std::vector<AOMatrixDerivative> dVne =
522 Eigen::MatrixXd eone_grad = Eigen::MatrixXd::Zero(natoms, 3);
523 for (
Index a = 0; a < natoms; ++a) {
524 for (
Index xyz = 0; xyz < 3; ++xyz) {
525 eone_grad(a, xyz) = Dmat.cwiseProduct(dT[a][xyz] + dVne[a][xyz]).sum();
529 <<
TimeStamp() <<
" One-electron derivatives done" << std::flush;
560 Eigen::MatrixXd W = 2.0 * C_occ * eps_occ.asDiagonal() * C_occ.transpose();
563 <<
TimeStamp() <<
" Computing overlap (Pulay) derivatives"
566 Eigen::MatrixXd overlap_pulay_grad = Eigen::MatrixXd::Zero(natoms, 3);
567 for (
Index a = 0; a < natoms; ++a) {
568 for (
Index xyz = 0; xyz < 3; ++xyz) {
569 overlap_pulay_grad(a, xyz) = -W.cwiseProduct(dS[a][xyz]).sum();
573 <<
TimeStamp() <<
" Overlap derivatives done" << std::flush;
576 <<
TimeStamp() <<
" Computing RI-J (Coulomb) gradient" << std::flush;
577 Eigen::MatrixXd rij_term =
580 <<
TimeStamp() <<
" RI-J gradient done" << std::flush;
583 <<
TimeStamp() <<
" Computing XC grid (Pulay + weight) gradient terms"
589 <<
TimeStamp() <<
" XC grid gradient terms done" << std::flush;
591 Eigen::MatrixXd grad = nucrep_term + eone_grad + overlap_pulay_grad +
592 rij_term + pulay_term + weight_term;
615 <<
TimeStamp() <<
" Computing RI-K (exact exchange) gradient"
619 <<
TimeStamp() <<
" RI-K gradient done" << std::flush;
627 Eigen::Vector3d sum = grad.colwise().sum();
628 if (sum.cwiseAbs().maxCoeff() > 1
e-4) {
631 <<
" WARNING: computed forces do not sum to zero across atoms "
632 "(translational invariance check failed, max component="
633 << sum.cwiseAbs().maxCoeff()
634 <<
") -- treat these forces with "
646 Eigen::MatrixXd force = -grad;
650 <<
TimeStamp() <<
" Computed and stored ground-state nuclear forces."
659 for (
Index a = 0; a < natoms; ++a) {
661 (boost::format(
" %1$s"
662 " %2$+1.6f %3$+1.6f %4$+1.6f") %
663 mol[a].getElement() % force(a, 0) % force(a, 1) % force(a, 2))
698 Eigen::MatrixXd C_alpha_occ = MOs_alpha.
eigenvectors().leftCols(n_occ_alpha);
699 Eigen::MatrixXd C_beta_occ = MOs_beta.
eigenvectors().leftCols(n_occ_beta);
700 Eigen::VectorXd eps_alpha_occ = MOs_alpha.
eigenvalues().head(n_occ_alpha);
701 Eigen::VectorXd eps_beta_occ = MOs_beta.
eigenvalues().head(n_occ_beta);
703 C_alpha_occ * eps_alpha_occ.asDiagonal() * C_alpha_occ.transpose() +
704 C_beta_occ * eps_beta_occ.asDiagonal() * C_beta_occ.transpose();
708 Eigen::MatrixXd overlap_pulay_grad = Eigen::MatrixXd::Zero(natoms, 3);
709 for (
Index a = 0; a < natoms; ++a) {
710 for (
Index xyz = 0; xyz < 3; ++xyz) {
711 overlap_pulay_grad(a, xyz) = -W.cwiseProduct(dS[a][xyz]).sum();
714 return overlap_pulay_grad;
722 const Eigen::MatrixXd D_total = Dspin.
total();
731 std::vector<AOMatrixDerivative> dVne =
733 Eigen::MatrixXd eone_grad = Eigen::MatrixXd::Zero(natoms, 3);
734 for (
Index a = 0; a < natoms; ++a) {
735 for (
Index xyz = 0; xyz < 3; ++xyz) {
736 eone_grad(a, xyz) = D_total.cwiseProduct(dT[a][xyz] + dVne[a][xyz]).sum();
740 Eigen::MatrixXd overlap_pulay_grad =
743 Eigen::MatrixXd grad =
759 Eigen::MatrixXd C_alpha_occ =
761 Eigen::MatrixXd C_beta_occ =
777 <<
" Skipping UKS force calculation: RI-J gradient only "
778 "implements the RI path, but this SCF ran without an "
787 <<
" Skipping UKS force calculation: the libint2 this was "
788 "built against does not support derivative integrals for "
789 "one or more operator categories it needs. Many "
790 "pre-packaged libint2 builds (Homebrew, Ubuntu apt, etc.) "
791 "do not enable this by default -- rebuild libint2 with "
792 "--enable-1body/--enable-eri2/--enable-eri3 to use "
804 <<
TimeStamp() <<
" Skipping UKS force calculation: an ECP ('"
806 <<
"') was used for this SCF, but analytic nuclear forces do "
807 "not yet include the ECP contribution to the force "
808 "(d(V_ECP)/dR) -- computing forces in this configuration "
809 "would silently omit that term rather than fail visibly."
814 Eigen::MatrixXd grad =
830 Eigen::Vector3d sum = grad.colwise().sum();
831 if (sum.cwiseAbs().maxCoeff() > 1
e-4) {
834 <<
" WARNING: computed UKS forces do not sum to zero across "
835 "atoms (translational invariance check failed, max "
837 << sum.cwiseAbs().maxCoeff()
838 <<
") -- treat these forces with "
847 Eigen::MatrixXd force = -grad;
851 <<
TimeStamp() <<
" Computed and stored ground-state UKS nuclear forces."
857 for (
Index a = 0; a < force.rows(); ++a) {
858 std::string output = (boost::format(
" %1$s"
859 " %2$+1.6f %3$+1.6f %4$+1.6f") %
860 mol_for_print[a].getElement() % force(a, 0) %
861 force(a, 1) % force(a, 2))
876 const Eigen::MatrixXd& MOCoeff,
const Eigen::MatrixXd& Dmat,
877 double error)
const {
879 std::array<Eigen::MatrixXd, 2> result;
881 auto t =
timings_.Measure(
"J (RI)");
882 result[0] =
ERIs_.CalculateERIs_3c(Dmat);
885 auto t =
timings_.Measure(
"K (RI, from density matrix)");
886 result[1] =
ERIs_.CalculateEXX_3c(Eigen::MatrixXd::Zero(0, 0), Dmat);
888 auto t =
timings_.Measure(
"K (RI, from occupied MOs)");
890 result[1] =
ERIs_.CalculateEXX_3c(occblock, Dmat);
894 auto t =
timings_.Measure(
"J+K (4c)");
895 return ERIs_.CalculateERIs_EXX_4c(Dmat, error);
902 double error)
const {
904 auto t =
timings_.Measure(
"J (RI)");
905 return ERIs_.CalculateERIs_3c(Dmat);
907 auto t =
timings_.Measure(
"J (4c)");
908 return ERIs_.CalculateERIs_4c(Dmat, error);
913 const double gb = 1024.0 * 1024.0 * 1024.0;
914 const double n = double(
dftbasis_.AOBasisSize());
921 const double naux = double(
ERIs_.AuxSize());
923 const double tensor =
924 naux * double(
ERIs_.StoredPairs()) *
sizeof(double) / gb;
925 const double kept = double(
ERIs_.StoredPairs()) /
926 double(std::max<Index>(1,
ERIs_.AllPairs()));
929 const double scratch = threads * 4 * n * n *
sizeof(double) / gb;
931 <<
TimeStamp() <<
" RI: " <<
ERIs_.AuxSize() <<
" aux functions ("
932 <<
ERIs_.Removedfunctions()
933 <<
" removed from the metric); stored 3c tensor "
934 << std::setprecision(3) << tensor <<
" GB (" << 100.0 * kept
936 <<
"); J/K scratch up to about " << scratch <<
" GB" << std::flush;
942 <<
" Resident memory after setup: " << std::setprecision(3) << rss
943 <<
" GB" << std::flush;
961 Eigen::MatrixXd Dmat = [&]() {
962 auto t =
timings_.Measure(
"guess: atomic densities");
970 <<
TimeStamp() <<
" Filled DFT Vxc matrix " << std::flush;
975 std::array<Eigen::MatrixXd, 2> both =
997 <<
TimeStamp() <<
" Peak resident memory of this process so far: "
998 << std::setprecision(3) << peak <<
" GB" << std::flush;
1005 throw std::runtime_error(
1006 "xtpdft polarizability: closed-shell ground states only");
1010 <<
" Static polarizability (coupled-perturbed KS, 3 uniform fields)"
1015 const std::vector<Eigen::MatrixXd> h(dipole.
Matrix().begin(),
1018 Eigen::Matrix3d alpha;
1019 for (
Index a = 0; a < 3; ++a) {
1020 for (
Index c = 0; c < 3; ++c) {
1021 alpha(a, c) = -dipole.
Matrix()[std::size_t(a)]
1022 .cwiseProduct(dP[std::size_t(c)])
1026 alpha = 0.5 * (alpha + alpha.transpose()).eval();
1030 <<
TimeStamp() <<
" Polarizability [a.u.]:\n"
1031 << alpha <<
"\n isotropic " << alpha.trace() / 3
1032 <<
" a.u. = " << alpha.trace() / 3 * A3 <<
" A^3" << std::flush;
1059 bool converged =
RunCDFT(orb, constraint);
1062 if (converged && original_compute_forces) {
1078 <<
" CDFT converged -- computing the ordinary DFT force once, "
1079 "for the final, converged density only"
1110 std::map<std::string, Eigen::MatrixXd> reference_densities =
1120 std::vector<HirshfeldPartition::AtomicReference> atoms =
1124 std::array<Eigen::MatrixXd, 2> Dspin =
1130 Eigen::MatrixXd density_total = Dspin[0] + Dspin[1];
1132 Eigen::MatrixXd cdft_gradient_correction =
1135 cdft_gradient_correction +=
1137 atoms, atom_index, density_total, orb.
QMAtoms(), full_dftbasis,
1147 constraint.
lambda * cdft_gradient_correction);
1153 const std::string previous_basis =
1167 <<
" Starting from the orbitals of the previous QM/MM iteration"
1171 <<
TimeStamp() <<
" Previous orbitals not usable as guess (" << reason
1178 auto t =
timings_.Measure(
"setup: XC grid");
1183 bool success =
false;
1188 <<
" Forcing closed-shell singlet through UKS development path."
1201 const std::string& previous_basis,
1202 std::string& reason)
const {
1210 reason =
"basis size differs";
1213 if (!previous_basis.empty() && previous_basis != orb.
getDFTbasisName()) {
1214 reason =
"basis set differs";
1219 reason =
"electron count differs";
1252 double lambda_lo = constraint.
lambda - 0.1;
1253 double lambda_hi = constraint.
lambda + 0.1;
1255 auto EvaluateMismatch = [&](
double lambda) ->
double {
1258 <<
TimeStamp() <<
" CDFT: starting inner SCF at lambda=" << lambda
1260 bool scf_converged =
EvaluateUKS(orb, H0, vxcpotential);
1261 if (!scf_converged) {
1262 throw std::runtime_error(
1263 "RunCDFT: inner SCF did not converge at lambda=" +
1264 std::to_string(lambda));
1277 std::array<Eigen::MatrixXd, 2> Dspin =
1288 double mismatch_lo = EvaluateMismatch(lambda_lo);
1289 double mismatch_hi = EvaluateMismatch(lambda_hi);
1291 Index bracket_attempts = 0;
1292 constexpr Index kMaxBracketAttempts = 10;
1293 while (mismatch_lo * mismatch_hi > 0.0 &&
1294 bracket_attempts < kMaxBracketAttempts) {
1295 double width = lambda_hi - lambda_lo;
1296 lambda_lo -= 0.5 * width;
1297 lambda_hi += 0.5 * width;
1298 mismatch_lo = EvaluateMismatch(lambda_lo);
1299 mismatch_hi = EvaluateMismatch(lambda_hi);
1302 if (mismatch_lo * mismatch_hi > 0.0) {
1305 <<
" RunCDFT: could not bracket a root for the population "
1307 << kMaxBracketAttempts
1308 <<
" bracket-expansion attempts -- the target population may "
1309 "be unreachable for this system, or the initial "
1310 "lambda guess may be far from the actual root."
1319 double lambda_mid = 0.5 * (lambda_lo + lambda_hi);
1320 double mismatch_mid = EvaluateMismatch(lambda_mid);
1323 <<
TimeStamp() <<
" CDFT outer iteration " << outer_iter + 1 <<
" of "
1325 <<
" population mismatch=" << mismatch_mid << std::flush;
1328 constraint.
lambda = lambda_mid;
1331 <<
TimeStamp() <<
" CDFT converged after " << outer_iter + 1
1332 <<
" outer iterations, lambda=" << lambda_mid << std::flush;
1336 if (mismatch_mid * mismatch_lo < 0.0) {
1337 lambda_hi = lambda_mid;
1338 mismatch_hi = mismatch_mid;
1340 lambda_lo = lambda_mid;
1341 mismatch_lo = mismatch_mid;
1344 }
catch (
const std::runtime_error&) {
1352 <<
" RunCDFT: outer bisection loop did not converge "
1355 constraint.
lambda = 0.5 * (lambda_lo + lambda_hi);
1376 <<
TimeStamp() <<
" Reading guess from orbitals object/file"
1401 throw std::runtime_error(
1402 "initial_guess=dimer_guess: this is a restricted (closed-shell) "
1403 "calculation, but at least one monomer .orb file is open-shell "
1404 "or unrestricted. Use force_uks_path to run the dimer "
1405 "unrestricted with this guess.");
1408 throw std::runtime_error(
1409 "initial_guess=dimer_guess: the monomers have " +
1411 " electrons in total, but this calculation has " +
1413 ". Check the monomer charges against the dimer charge.");
1415 MOs = dimer_guess_orb.
MOs();
1418 throw std::runtime_error(
"Initial guess method not known/implemented");
1424 Eigen::MatrixXd Dmat = spin_dmat.
total();
1427 <<
TimeStamp() <<
" Guess Matrix gives N=" << std::setprecision(9)
1428 << Dmat.cwiseProduct(
dftAOoverlap_.Matrix()).sum() <<
" electrons."
1438 const Eigen::MatrixXd&
F,
1439 const Eigen::MatrixXd& X) {
1445 <<
TimeStamp() <<
" STARTING SCF cycle" << std::flush;
1447 <<
" ----------------------------------------------"
1448 "----------------------------"
1451 Eigen::MatrixXd J = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
1454 K = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
1457 double start_incremental_F_threshold = 1
e-4;
1459 start_incremental_F_threshold = 0.0;
1476 <<
TimeStamp() <<
" Filled DFT Vxc matrix " << std::flush;
1479 double Eone = Dmat.cwiseProduct(H0.
matrix()).sum();
1480 double Etwo = e_vxc.
energy();
1488 double integral_error =
1496 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
1499 exx = 0.25 *
ScaHFX_ * Dmat.cwiseProduct(K).sum();
1501 <<
TimeStamp() <<
" Filled F+K matrix " << std::flush;
1505 <<
TimeStamp() <<
" Filled F matrix " << std::flush;
1507 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
1511 double totenergy = Eone + H0.
energy() + Etwo;
1514 << std::setprecision(12) << Eone << std::flush;
1516 << std::setprecision(12) << Etwo << std::flush;
1518 <<
TimeStamp() << std::setprecision(12) <<
" Local Exc contribution "
1519 << e_vxc.
energy() << std::flush;
1523 <<
" Non local Ex contribution " << exx << std::flush;
1526 <<
TimeStamp() <<
" Total Energy " << std::setprecision(12) << totenergy
1530 auto t =
timings_.Measure(
"DIIS/ADIIS + diagonalisation");
1549 <<
TimeStamp() <<
" Total Energy has converged to "
1551 <<
"[Ha] after " << this_iter + 1
1552 <<
" iterations. DIIS error is converged up to "
1555 <<
TimeStamp() <<
" Final Single Point Energy "
1556 << std::setprecision(12) << totenergy <<
" Ha" << std::flush;
1558 <<
" Final Local Exc contribution "
1559 << e_vxc.
energy() <<
" Ha" << std::flush;
1562 <<
" Final Non Local Ex contribution "
1563 << exx <<
" Ha" << std::flush;
1568 Index nuclear_charge = 0;
1570 nuclear_charge += atom.getNuccharge();
1583 auto t =
timings_.Measure(
"forces");
1589 }
else if (this_iter ==
max_iter_ - 1) {
1591 <<
TimeStamp() <<
" DFT calculation has not converged after "
1593 <<
" iterations. Use more iterations or another convergence "
1594 "acceleration scheme."
1631 conv_uks.
Configure(opt_alpha, opt_beta);
1637 <<
TimeStamp() <<
" Reading UKS guess from orbitals object/file"
1640 MOs_alpha = orb.
MOs();
1649 <<
" Orbital file has no beta MOs, using alpha guess for beta."
1651 MOs_beta = MOs_alpha;
1656 <<
" Building UKS guess from two monomer .orb files (dimer_guess)"
1659 MOs_alpha = dimer_guess_orb.
MOs();
1661 MOs_beta = dimer_guess_orb.
MOs_beta();
1678 throw std::runtime_error(
"Initial guess method not known/implemented");
1691 <<
TimeStamp() <<
" UKS guess gives Nalpha="
1698 <<
TimeStamp() <<
" STARTING UKS SCF cycle" << std::flush;
1700 <<
" ------------------------------------------------------------"
1708 Eigen::MatrixXd H_alpha = H0.
matrix();
1709 Eigen::MatrixXd H_beta = H0.
matrix();
1713 const Eigen::MatrixXd D_total = Dspin.
total();
1715 double E_one = Dspin.
alpha.cwiseProduct(H0.
matrix()).sum() +
1718 double E_coul = 0.0;
1722 double integral_error = std::min(conv_uks.
getDIIsError() * 1
e-5, 1
e-5);
1725 std::array<Eigen::MatrixXd, 2> both_alpha =
CalcERIs_EXX(
1726 Eigen::MatrixXd::Zero(0, 0), Dspin.
alpha, integral_error);
1727 std::array<Eigen::MatrixXd, 2> both_beta =
1730 Eigen::MatrixXd J = both_alpha[0] + both_beta[0];
1731 Eigen::MatrixXd K_alpha = both_alpha[1];
1732 Eigen::MatrixXd K_beta = both_beta[1];
1734 H_alpha += J +
ScaHFX_ * K_alpha;
1735 H_beta += J +
ScaHFX_ * K_beta;
1737 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
1739 (Dspin.
alpha.cwiseProduct(K_alpha).sum() +
1740 Dspin.
beta.cwiseProduct(K_beta).sum());
1742 Eigen::MatrixXd J =
CalcERIs(D_total, integral_error);
1745 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
1752 H_alpha += vxc.vxc_alpha;
1753 H_beta += vxc.vxc_beta;
1756 double totenergy = H0.
energy() + E_one + E_coul + E_xc + E_exx;
1775 H_alpha += (c.lambda * c.spin_alpha_coefficient) * c.weight_matrix;
1776 H_beta += (c.lambda * c.spin_beta_coefficient) * c.weight_matrix;
1778 c.spin_alpha_coefficient *
1779 Dspin.
alpha.cwiseProduct(c.weight_matrix).sum() +
1780 c.spin_beta_coefficient *
1781 Dspin.
beta.cwiseProduct(c.weight_matrix).sum();
1782 totenergy += c.lambda * (population - c.target_population);
1787 << std::setprecision(12) << E_one << std::flush;
1789 << std::setprecision(12) << E_coul << std::flush;
1791 << std::setprecision(12) << E_xc << std::flush;
1794 <<
TimeStamp() <<
" EXX contribution " << std::setprecision(12)
1795 << E_exx << std::flush;
1798 <<
TimeStamp() <<
" Total Energy " << std::setprecision(12) << totenergy
1813 [
this, &H0, &vxcpotential](
const Eigen::MatrixXd& alpha_new,
1814 const Eigen::MatrixXd& beta_new) {
1821 [
this, &vxcpotential](
1824 const Eigen::MatrixXd& F_beta,
const Eigen::MatrixXd& X) {
1830 auto t =
timings_.Measure(
"DIIS/ADIIS + diagonalisation");
1831 Dspin = conv_uks.
Iterate(Dspin, Hspin, MOs_alpha, MOs_beta, totenergy);
1834 MOs_beta = MOs_alpha;
1852 Index nuclear_charge = 0;
1854 nuclear_charge += atom.getNuccharge();
1857 CanonicalizeOrbitalPhases(MOs_alpha);
1858 CanonicalizeOrbitalPhases(MOs_beta);
1861 orb.
MOs() = MOs_alpha;
1872 <<
TimeStamp() <<
" UKS converged after " << this_iter + 1
1873 <<
" iterations. Delta E=" << conv_uks.
getDeltaE()
1874 <<
" DIIS error=" << conv_uks.
getDIIsError() << std::flush;
1877 <<
TimeStamp() <<
" Final Single Point Energy "
1878 << std::setprecision(12) << totenergy <<
" Ha" << std::flush;
1880 <<
TimeStamp() << std::setprecision(12) <<
" Final XC contribution "
1881 << E_xc <<
" Ha" << std::flush;
1885 <<
" Final EXX contribution " << E_exx <<
" Ha" << std::flush;
1896 auto t =
timings_.Measure(
"forces");
1906 <<
TimeStamp() <<
" UKS calculation has not converged after "
1907 <<
max_iter_ <<
" iterations." << std::flush;
1925 std::make_unique<DFTTimings::Scope>(
timings_,
"setup: one-electron H0");
1931 <<
TimeStamp() <<
" Filled DFT Kinetic energy matrix ." << std::flush;
1936 <<
TimeStamp() <<
" Filled DFT nuclear potential matrix." << std::flush;
1938 Eigen::MatrixXd H0 = dftAOkinetic.
Matrix() + dftAOESP.
Matrix();
1940 <<
TimeStamp() <<
" Constructed independent particle hamiltonian "
1944 << std::setprecision(9) << E0 << std::flush;
1951 <<
TimeStamp() <<
" Filled DFT ECP matrix" << std::flush;
1957 <<
" External sites" << std::flush;
1958 bool has_quadrupoles = std::any_of(
1960 [](
const std::unique_ptr<StaticSite>& s) { return s->getRank() == 2; });
1961 std::string header =
1962 " Name Coordinates[a0] charge[e] dipole[e*a0] ";
1963 if (has_quadrupoles) {
1964 header +=
" quadrupole[e*a0^2]";
1969 for (
const std::unique_ptr<StaticSite>& site : *
externalsites_) {
1970 if (counter == limit) {
1973 std::string output =
1974 (boost::format(
" %1$s"
1975 " %2$+1.4f %3$+1.4f %4$+1.4f"
1977 site->getElement() % site->getPos()[0] % site->getPos()[1] %
1978 site->getPos()[2] % site->getCharge())
1980 const Eigen::Vector3d& dipole = site->getDipole();
1981 output += (boost::format(
" %1$+1.4f %2$+1.4f %3$+1.4f") % dipole[0] %
1982 dipole[1] % dipole[2])
1984 if (site->getRank() > 1) {
1985 Eigen::VectorXd quadrupole = site->Q().tail<5>();
1987 (boost::format(
" %1$+1.4f %2$+1.4f %3$+1.4f %4$+1.4f %5$+1.4f") %
1988 quadrupole[0] % quadrupole[1] % quadrupole[2] % quadrupole[3] %
1995 if (counter == limit) {
1998 <<
" sites not displayed)\n"
2002 auto t =
timings_.Measure(
"setup: external multipoles");
2006 <<
TimeStamp() <<
" Nuclei-external site interaction energy "
2007 << std::setprecision(9) << ext_multipoles.
energy() << std::flush;
2008 E0 += ext_multipoles.
energy();
2009 H0 += ext_multipoles.
matrix();
2016 E0 += extdensity_result.
energy();
2018 <<
TimeStamp() <<
" Nuclei-external density interaction energy "
2019 << std::setprecision(9) << extdensity_result.
energy() << std::flush;
2020 H0 += extdensity_result.
matrix();
2026 throw std::runtime_error(
2027 "DFTEngine: the external potential matrix does not match the DFT "
2036 <<
TimeStamp() <<
" Integrating external electric field with F[Hrt]="
2043 <<
TimeStamp() <<
" Integrating external Ewald Potential" << std::flush;
2044 auto t =
timings_.Measure(
"setup: Ewald potential on grid");
2060 throw std::runtime_error(
2061 "DFTEngine: the external Ewald potential grid has " +
2063 " boxes but this molecule's own grid has " +
2065 ". The potential was evaluated on a different grid than the one "
2066 "being integrated over.");
2072 const std::vector<double>& source =
2074 if (
Index(source.size()) != box.
size()) {
2075 throw std::runtime_error(
"DFTEngine: box " + std::to_string(i) +
2076 " of the external Ewald "
2077 "potential grid holds " +
2078 std::to_string(source.size()) +
2079 " values for " + std::to_string(box.
size()) +
2087 std::string ewald_key;
2093 for (
double v : ewaldgrid[i].getPotentialValues()) {
2099 std::ostringstream key;
2101 << points <<
"|" << sum <<
"|" << sum2;
2102 ewald_key = key.str();
2109 <<
" Reusing the Ewald potential matrix of the previous run"
2113 const Eigen::MatrixXd ewald_matrix =
2126 <<
TimeStamp() <<
" Nuclei-external Ewald potential energy "
2135 std::ostringstream key;
2140 for (
const AOShell& shell : *basis) {
2141 key << static_cast<int>(shell.getL()) <<
"," << shell.getSize() <<
","
2142 << shell.getPos().x() <<
"," << shell.getPos().y() <<
","
2143 << shell.getPos().z() <<
";";
2156 <<
TimeStamp() <<
" SCF thresholds for this run: Delta E "
2158 <<
" (set by the caller)" << std::flush;
2163 ERIs_.AuxSize() == 0) {
2175 auto overlap_timer =
2176 std::make_unique<DFTTimings::Scope>(
timings_,
"setup: overlap, S^-1/2");
2179 <<
TimeStamp() <<
" Filled DFT Overlap matrix." << std::flush;
2191 overlap_timer.reset();
2201 <<
" Reusing the RI integrals of the previous run (same basis sets "
2206 auto ri_start = DFTTimings::Clock::now();
2214 std::chrono::duration<double>(DFTTimings::Clock::now() - ri_start)
2216 timings_.Add(
"setup: RI metric V^-1/2",
ERIs_.MetricSeconds());
2217 timings_.Add(
"setup: RI 3c integrals", ri_seconds -
ERIs_.MetricSeconds());
2219 <<
TimeStamp() <<
" Inverted AUX Coulomb matrix, removed "
2220 <<
ERIs_.Removedfunctions() <<
" functions from aux basis"
2224 <<
" Setup invariant parts of Electron Repulsion integrals "
2228 <<
TimeStamp() <<
" Calculating 4c diagonals. " << std::flush;
2229 auto t =
timings_.Measure(
"setup: 4c Schwarz screening");
2232 <<
TimeStamp() <<
" Calculated 4c diagonals. " << std::flush;
2252class SphericalAtomSCF {
2254 explicit SphericalAtomSCF(
const AOBasis& basis) : n_(basis.AOBasisSize()) {
2255 for (
const AOShell& shell : basis) {
2256 const Index l =
static_cast<Index>(shell.getL());
2258 while (g <
Index(l_.size()) && l_[g] != l) {
2261 if (g ==
Index(l_.size())) {
2263 starts_.emplace_back();
2265 starts_[g].push_back(shell.getStartIndex());
2269 Index Groups()
const {
return Index(l_.size()); }
2272 Eigen::MatrixXd Reduce(
const Eigen::MatrixXd& full,
Index g)
const {
2273 const std::vector<Index>& st = starts_[g];
2274 const Index size = 2 * l_[g] + 1;
2275 Eigen::MatrixXd red(st.size(), st.size());
2276 for (
Index i = 0; i <
Index(st.size()); ++i) {
2277 for (
Index j = 0; j <
Index(st.size()); ++j) {
2278 red(i, j) = full.block(st[i], st[j], size, size).trace() / double(size);
2286 Eigen::MatrixXd Expand(
const std::vector<Eigen::MatrixXd>& red)
const {
2287 Eigen::MatrixXd full = Eigen::MatrixXd::Zero(n_, n_);
2288 for (
Index g = 0; g < Groups(); ++g) {
2289 const std::vector<Index>& st = starts_[g];
2290 const Index size = 2 * l_[g] + 1;
2291 for (
Index i = 0; i <
Index(st.size()); ++i) {
2292 for (
Index j = 0; j <
Index(st.size()); ++j) {
2293 full.block(st[i], st[j], size, size)
2295 .setConstant(red[g](i, j));
2304 std::vector<Eigen::MatrixXd> Density(
2305 const std::vector<Eigen::MatrixXd>& fock,
2306 const std::vector<Eigen::MatrixXd>& overlap,
2307 const std::vector<Eigen::VectorXd>& occupation)
const {
2308 std::vector<Eigen::MatrixXd> dens(Groups());
2309 for (
Index g = 0; g < Groups(); ++g) {
2310 Eigen::GeneralizedSelfAdjointEigenSolver<Eigen::MatrixXd> es(fock[g],
2312 const Index nocc = occupation[g].size();
2313 const Eigen::MatrixXd c = es.eigenvectors().leftCols(nocc);
2314 dens[g] = c * (occupation[g] / double(2 * l_[g] + 1)).asDiagonal() *
2322 std::vector<Eigen::VectorXd> AufbauOccupation(
2323 const std::vector<Eigen::MatrixXd>& fock,
2324 const std::vector<Eigen::MatrixXd>& overlap,
double nelectrons)
const {
2330 std::vector<Level> levels;
2331 std::vector<Eigen::VectorXd> occ(Groups());
2332 for (
Index g = 0; g < Groups(); ++g) {
2333 Eigen::GeneralizedSelfAdjointEigenSolver<Eigen::MatrixXd> es(
2334 fock[g], overlap[g], Eigen::EigenvaluesOnly);
2335 occ[g] = Eigen::VectorXd::Zero(es.eigenvalues().size());
2336 for (
Index k = 0; k < es.eigenvalues().size(); ++k) {
2337 levels.push_back({es.eigenvalues()(k), g, k});
2341 levels.begin(), levels.end(),
2342 [](
const Level& a,
const Level& b) { return a.energy < b.energy; });
2343 double left = nelectrons;
2344 for (
const Level& lev : levels) {
2345 const double take = std::clamp(left, 0.0,
double(2 * l_[lev.group] + 1));
2346 occ[lev.group](lev.index) = take;
2355 bool OccupationFromSubshells(
2356 const std::vector<std::array<double, 3>>& subshells,
2357 const std::vector<Eigen::MatrixXd>& overlap,
2358 std::vector<Eigen::VectorXd>& occ)
const {
2359 occ.assign(Groups(), Eigen::VectorXd());
2360 for (
Index g = 0; g < Groups(); ++g) {
2361 occ[g] = Eigen::VectorXd::Zero(overlap[g].rows());
2363 for (
const auto& sub : subshells) {
2367 while (g < Groups() && l_[g] != l) {
2370 if (g == Groups() || k >= occ[g].size()) {
2373 occ[g](k) += sub[2];
2380 std::vector<Index> l_;
2381 std::vector<std::vector<Index>> starts_;
2387 explicit SimpleDIIS(
Index maxhist) : maxhist_(maxhist) {}
2389 Eigen::VectorXd Extrapolate(
const Eigen::VectorXd& fock,
2390 const Eigen::VectorXd& error) {
2391 focks_.push_back(fock);
2392 errors_.push_back(error);
2393 if (
Index(focks_.size()) > maxhist_) {
2394 focks_.erase(focks_.begin());
2395 errors_.erase(errors_.begin());
2401 Eigen::MatrixXd b = Eigen::MatrixXd::Zero(m + 1, m + 1);
2402 for (
Index i = 0; i < m; ++i) {
2403 for (
Index j = 0; j <= i; ++j) {
2404 b(i, j) = b(j, i) = errors_[i].dot(errors_[j]);
2406 b(i, m) = b(m, i) = -1.0;
2412 const double scale = b.topLeftCorner(m, m).diagonal().maxCoeff();
2414 b.topLeftCorner(m, m) /= scale;
2416 Eigen::VectorXd rhs = Eigen::VectorXd::Zero(m + 1);
2418 const Eigen::VectorXd
x = b.colPivHouseholderQr().solve(rhs);
2419 if (!
x.allFinite()) {
2422 Eigen::VectorXd result = Eigen::VectorXd::Zero(fock.size());
2423 for (
Index i = 0; i < m; ++i) {
2424 result +=
x(i) * focks_[i];
2431 std::vector<Eigen::VectorXd> focks_;
2432 std::vector<Eigen::VectorXd> errors_;
2444std::vector<std::array<double, 4>> ValenceConfiguration(
Index z,
Index ncore,
2452 std::vector<Sub> subs;
2454 for (
Index sum = 1; left > 0; ++sum) {
2455 for (
Index l = (sum - 1) / 2; l >= 0 && left > 0; --l) {
2456 const Index take = std::min(left, 2 * (2 * l + 1));
2457 subs.push_back({sum - l, l, double(take)});
2461 std::stable_sort(subs.begin(), subs.end(), [](
const Sub& a,
const Sub& b) {
2462 return a.n < b.n || (a.n == b.n && a.l < b.l);
2465 std::size_t first = 0;
2466 while (first < subs.size() && removed < ncore) {
2467 removed +=
Index(subs[first].electrons);
2470 if (removed != ncore) {
2473 std::vector<std::array<double, 4>> result;
2474 for (std::size_t i = first; i < subs.size(); ++i) {
2476 for (std::size_t j = first; j < i; ++j) {
2477 k += (subs[j].l == subs[i].l) ? 1 : 0;
2479 result.push_back({double(subs[i].l), double(k), 0.5 * subs[i].electrons,
2480 0.5 * subs[i].electrons});
2483 double excess = 0.5 * double(nalpha - nbeta);
2484 for (
auto it = result.rbegin(); it != result.rend() && excess > 0.0; ++it) {
2485 const double capacity = double(2 *
Index((*it)[0]) + 1);
2486 const double move = std::min(excess, capacity - (*it)[2]);
2487 if (move > 0.0 && (*it)[3] >= move) {
2493 if (excess > 1
e-12) {
2526std::optional<std::pair<Index, Index>> HundsRuleAlphaBetaElectrons(
2527 Index nuclear_charge) {
2528 switch (nuclear_charge) {
2530 return std::make_pair(1, 0);
2532 return std::make_pair(1, 1);
2534 return std::make_pair(2, 1);
2536 return std::make_pair(2, 2);
2538 return std::make_pair(3, 2);
2540 return std::make_pair(4, 2);
2542 return std::make_pair(5, 2);
2544 return std::make_pair(5, 3);
2546 return std::make_pair(5, 4);
2548 return std::make_pair(5, 5);
2550 return std::make_pair(6, 5);
2552 return std::make_pair(6, 6);
2554 return std::make_pair(7, 6);
2556 return std::make_pair(8, 6);
2558 return std::make_pair(9, 6);
2560 return std::make_pair(9, 7);
2562 return std::make_pair(9, 8);
2564 return std::make_pair(9, 9);
2566 return std::make_pair(10, 9);
2568 return std::make_pair(10, 10);
2571 return std::make_pair(16, 15);
2573 return std::make_pair(17, 15);
2575 return std::make_pair(18, 15);
2577 return std::make_pair(18, 16);
2579 return std::make_pair(18, 17);
2581 return std::make_pair(18, 18);
2584 return std::make_pair(25, 24);
2586 return std::make_pair(26, 24);
2588 return std::make_pair(27, 24);
2590 return std::make_pair(27, 25);
2592 return std::make_pair(27, 26);
2594 return std::make_pair(27, 27);
2596 return std::nullopt;
2602 const QMAtom& uniqueAtom,
bool use_hunds_rule_occupation)
const {
2614 dftbasis.
Fill(basisset, atom);
2624 ecp.
Fill(ecps, atom);
2631 const Index ncore = z - atom[0].getNuccharge();
2632 const Index numofelectrons = atom[0].getNuccharge();
2640 if (use_hunds_rule_occupation) {
2641 auto hunds_rule = HundsRuleAlphaBetaElectrons(z);
2642 if (hunds_rule.has_value()) {
2644 alpha_e = hunds_rule->first - ncore / 2;
2645 beta_e = hunds_rule->second - ncore / 2;
2649 <<
" No Hund's-rule ground-state occupation table "
2650 "entry for nuclear charge "
2652 <<
" (d/f-block elements are not covered -- see "
2653 "HundsRuleAlphaBetaElectrons's own comment for why) -- "
2654 "falling back to the simpler, parity-based alpha/beta split."
2656 use_hunds_rule_occupation =
false;
2659 if (!use_hunds_rule_occupation) {
2660 if ((numofelectrons % 2) != 0) {
2661 alpha_e = numofelectrons / 2 + numofelectrons % 2;
2662 beta_e = numofelectrons / 2;
2664 alpha_e = numofelectrons / 2;
2675 dftAOoverlap.
Fill(dftbasis);
2676 dftAOkinetic.
Fill(dftbasis);
2681 Eigen::MatrixXd H0 = dftAOkinetic.
Matrix() + dftAOESP.
Matrix();
2689 Eigen::GeneralizedSelfAdjointEigenSolver<Eigen::MatrixXd> es(
2690 H0, dftAOoverlap.
Matrix());
2691 const Eigen::VectorXd c = es.eigenvectors().col(0);
2692 return c * c.transpose();
2697 const SphericalAtomSCF sph(dftbasis);
2698 const Index ngroups = sph.Groups();
2699 using Radial = std::vector<Eigen::MatrixXd>;
2700 Radial S_red(ngroups);
2701 Radial H0_red(ngroups);
2703 Radial X_red(ngroups);
2704 for (
Index g = 0; g < ngroups; ++g) {
2705 S_red[g] = sph.Reduce(dftAOoverlap.
Matrix(), g);
2706 H0_red[g] = sph.Reduce(H0, g);
2707 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(S_red[g]);
2708 X_red[g] = es.eigenvectors() *
2709 es.eigenvalues().cwiseInverse().cwiseSqrt().asDiagonal() *
2710 es.eigenvectors().transpose();
2715 Radial Da, Db, Fa, Fb;
2716 double energy = 0.0;
2720 const double scahfx =
2722 auto Build = [&](
const Radial& Da,
const Radial& Db) {
2723 const Eigen::MatrixXd D_alpha = sph.Expand(Da);
2724 const Eigen::MatrixXd D_beta = sph.Expand(Db);
2725 const Eigen::MatrixXd D_total = D_alpha + D_beta;
2726 Eigen::MatrixXd H_alpha = H0;
2727 Eigen::MatrixXd H_beta = H0;
2730 std::array<Eigen::MatrixXd, 2> both_alpha =
2732 std::array<Eigen::MatrixXd, 2> both_beta =
2734 const Eigen::MatrixXd hartree = both_alpha[0] + both_beta[0];
2735 H_alpha += hartree + scahfx * both_alpha[1];
2736 H_beta += hartree + scahfx * both_beta[1];
2737 e_two = 0.5 * D_total.cwiseProduct(hartree).sum() +
2739 (both_alpha[1].cwiseProduct(D_alpha).sum() +
2740 both_beta[1].cwiseProduct(D_beta).sum());
2742 const Eigen::MatrixXd hartree =
2746 e_two = 0.5 * D_total.cwiseProduct(hartree).sum();
2750 H_beta += vxc.vxc_beta;
2754 st.energy = D_total.cwiseProduct(H0).sum() + e_two + vxc.energy;
2755 st.Fa.resize(ngroups);
2756 st.Fb.resize(ngroups);
2757 for (
Index g = 0; g < ngroups; ++g) {
2758 st.Fa[g] = sph.Reduce(H_alpha, g);
2759 st.Fb[g] = sph.Reduce(H_beta, g);
2766 std::vector<Eigen::VectorXd> occ_alpha;
2767 std::vector<Eigen::VectorXd> occ_beta;
2768 bool fixed_occupation =
false;
2770 const std::vector<std::array<double, 4>> config =
2771 ValenceConfiguration(z, ncore, alpha_e, beta_e);
2772 std::vector<std::array<double, 3>> alpha_subshells;
2773 std::vector<std::array<double, 3>> beta_subshells;
2774 for (
const auto& sub : config) {
2775 alpha_subshells.push_back({sub[0], sub[1], sub[2]});
2776 beta_subshells.push_back({sub[0], sub[1], sub[3]});
2780 sph.OccupationFromSubshells(alpha_subshells, S_red, occ_alpha) &&
2781 sph.OccupationFromSubshells(beta_subshells, S_red, occ_beta);
2782 if (!fixed_occupation) {
2784 <<
TimeStamp() <<
" No subshell configuration for "
2786 <<
" in this basis/ECP; atomic SCF uses aufbau occupation"
2790 auto Occupy = [&](
const Radial&
F,
bool alpha) {
2791 if (fixed_occupation) {
2792 return sph.Density(
F, S_red, alpha ? occ_alpha : occ_beta);
2796 sph.AufbauOccupation(
F, S_red,
double(alpha ? alpha_e : beta_e)));
2799 auto Flatten = [&](
const State& st, Eigen::VectorXd& fock,
2800 Eigen::VectorXd& error) {
2802 for (
Index g = 0; g < ngroups; ++g) {
2803 total += 2 * st.Fa[g].size();
2806 error.resize(total);
2808 for (
Index spin = 0; spin < 2; ++spin) {
2809 const Radial&
F = (spin == 0) ? st.Fa : st.Fb;
2810 const Radial&
D = (spin == 0) ? st.Da : st.Db;
2811 for (
Index g = 0; g < ngroups; ++g) {
2812 const Eigen::MatrixXd fds =
F[g] *
D[g] * S_red[g];
2813 const Eigen::MatrixXd err =
2814 X_red[g] * (fds - fds.transpose()) * X_red[g];
2815 const Index size =
F[g].size();
2816 fock.segment(pos, size) =
2817 Eigen::Map<const Eigen::VectorXd>(
F[g].data(), size);
2818 error.segment(pos, size) =
2819 Eigen::Map<const Eigen::VectorXd>(err.data(), size);
2824 auto Unflatten = [&](
const Eigen::VectorXd& fock, Radial& Fa, Radial& Fb) {
2826 for (
Index spin = 0; spin < 2; ++spin) {
2827 Radial&
F = (spin == 0) ? Fa : Fb;
2828 for (
Index g = 0; g < ngroups; ++g) {
2829 const Index rows = S_red[g].rows();
2830 F[g] = Eigen::Map<const Eigen::MatrixXd>(fock.data() + pos, rows, rows);
2840 State cur = Build(Occupy(H0_red,
true), Occupy(H0_red,
false));
2842 const Index maxiter = 100;
2846 const double error_tolerance = std::max(
conv_opt_.error_converged, 1
e-6);
2847 const double energy_tolerance = std::max(
conv_opt_.Econverged, 1
e-8);
2848 double energy_old = cur.energy;
2849 bool converged =
false;
2850 Index this_iter = 0;
2851 for (; this_iter < maxiter; this_iter++) {
2852 Eigen::VectorXd fock;
2853 Eigen::VectorXd error;
2854 Flatten(cur, fock, error);
2855 const double max_error = error.cwiseAbs().maxCoeff();
2857 <<
TimeStamp() <<
" Iter " << this_iter <<
" of " << maxiter <<
" Etot "
2858 << std::setprecision(12) << cur.energy <<
" error " << max_error
2860 if (this_iter > 0 && max_error < error_tolerance &&
2861 std::abs(cur.energy - energy_old) < energy_tolerance) {
2865 energy_old = cur.energy;
2868 Unflatten(diis.Extrapolate(fock, error), Fa, Fb);
2869 cur = Build(Occupy(Fa,
true), Occupy(Fb,
false));
2873 <<
TimeStamp() <<
" Converged after " << this_iter + 1
2874 <<
" iterations, Etot=" << std::setprecision(12) << cur.energy
2878 <<
TimeStamp() <<
" Not converged after " << maxiter
2879 <<
" iterations. Unconverged density." << std::flush;
2881 const Radial& Da = cur.Da;
2882 const Radial& Db = cur.Db;
2884 const Eigen::MatrixXd density = sph.Expand(Da) + sph.Expand(Db);
2887 <<
" gives N=" << std::setprecision(9)
2888 << density.cwiseProduct(dftAOoverlap.
Matrix()).sum() <<
" electrons."
2897 <<
TimeStamp() <<
" Scanning molecule of size " << mol.
size()
2898 <<
" for unique elements" << std::flush;
2900 for (
auto element : elements) {
2901 uniqueelements.
push_back(
QMAtom(0, element, Eigen::Vector3d::Zero()));
2905 <<
" unique elements found" << std::flush;
2906 std::vector<Eigen::MatrixXd> uniqueatom_guesses;
2907 for (
QMAtom& unique_atom : uniqueelements) {
2909 <<
TimeStamp() <<
" Calculating atom density for "
2910 << unique_atom.getElement() << std::flush;
2912 uniqueatom_guesses.push_back(dmat_unrestricted);
2915 Eigen::MatrixXd guess =
2918 for (
const QMAtom& atom : mol) {
2920 for (; index < uniqueelements.
size(); index++) {
2921 if (atom.getElement() == uniqueelements[index].getElement()) {
2925 Eigen::MatrixXd& dmat_unrestricted = uniqueatom_guesses[index];
2926 guess.block(start, start, dmat_unrestricted.rows(),
2927 dmat_unrestricted.cols()) = dmat_unrestricted;
2928 start += dmat_unrestricted.rows();
2934std::map<std::string, Eigen::MatrixXd>
2938 <<
TimeStamp() <<
" Scanning molecule of size " << mol.
size()
2939 <<
" for unique elements (Hirshfeld reference densities)" << std::flush;
2941 std::map<std::string, Eigen::MatrixXd> reference_densities;
2942 for (
const std::string& element : elements) {
2943 QMAtom unique_atom(0, element, Eigen::Vector3d::Zero());
2945 <<
TimeStamp() <<
" Calculating Hirshfeld reference density for "
2946 << element << std::flush;
2953 return reference_densities;
2961 const Eigen::MatrixXd& alpha_new,
const Eigen::MatrixXd& beta_new) {
2965 constexpr double kIntegralError = 1
e-8;
2967 std::array<Eigen::MatrixXd, 2> both_alpha_new =
2968 CalcERIs_EXX(Eigen::MatrixXd::Zero(0, 0), alpha_new, kIntegralError);
2969 std::array<Eigen::MatrixXd, 2> both_beta_new =
2970 CalcERIs_EXX(Eigen::MatrixXd::Zero(0, 0), beta_new, kIntegralError);
2971 Eigen::MatrixXd J_new = both_alpha_new[0] + both_beta_new[0];
2975 Eigen::MatrixXd J_new =
CalcERIs(alpha_new + beta_new, kIntegralError);
2976 H_new.
alpha += J_new;
2977 H_new.
beta += J_new;
2979 auto vxc_new = [&]() {
2983 H_new.
alpha += vxc_new.vxc_alpha;
2984 H_new.
beta += vxc_new.vxc_beta;
2986 H_new.
alpha += (c.lambda * c.spin_alpha_coefficient) * c.weight_matrix;
2987 H_new.
beta += (c.lambda * c.spin_beta_coefficient) * c.weight_matrix;
2999 const Eigen::MatrixXd&
F,
3000 const Eigen::MatrixXd& X) {
3003 const Index nvirt = C.cols() - nocc;
3004 const Eigen::MatrixXd C_occ = C.leftCols(nocc);
3005 const Eigen::MatrixXd C_virt = C.rightCols(nvirt);
3006 const Eigen::MatrixXd F_oo = C_occ.transpose() *
F * C_occ;
3007 const Eigen::MatrixXd F_vv = C_virt.transpose() *
F * C_virt;
3008 const Eigen::MatrixXd P0 = 2.0 * C_occ * C_occ.transpose();
3010 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
3011 std::vector<Eigen::MatrixXd> U;
3012 for (
Index k = 0; k < X.cols(); ++k) {
3013 const Eigen::VectorXd x = X.col(k);
3014 U.push_back(Eigen::Map<const RowMajor>(x.data(), nocc, nvirt).transpose());
3016 const std::vector<Eigen::MatrixXd>
G =
3018 Eigen::MatrixXd HX(X.rows(), X.cols());
3019 for (
Index k = 0; k < X.cols(); ++k) {
3020 const RowMajor R = 4.0 * (F_vv * U[std::size_t(k)] -
3021 U[std::size_t(k)] * F_oo +
G[std::size_t(k)])
3023 HX.col(k) = Eigen::Map<const Eigen::VectorXd>(R.data(), R.size());
3038 const Eigen::MatrixXd& F_beta,
const Eigen::MatrixXd& X) {
3040 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
3044 Eigen::MatrixXd C_occ;
3045 Eigen::MatrixXd C_virt;
3046 Eigen::MatrixXd F_oo;
3047 Eigen::MatrixXd F_vv;
3055 c.nvirt = C.cols() - nocc;
3056 c.C_occ = C.leftCols(nocc);
3057 c.C_virt = C.rightCols(c.nvirt);
3058 c.F_oo = c.C_occ.transpose() *
F * c.C_occ;
3059 c.F_vv = c.C_virt.transpose() *
F * c.C_virt;
3060 c.P = c.C_occ * c.C_occ.transpose();
3063 const std::array<Channel, 2> ch = {
3066 const std::array<Index, 2> offset = {0, ch[0].nocc * ch[0].nvirt};
3068 const Index ncols = X.cols();
3069 std::vector<std::array<Eigen::MatrixXd, 2>> U(
3070 static_cast<std::size_t
>(ncols));
3071 std::vector<std::array<Eigen::MatrixXd, 2>> dP(
3072 static_cast<std::size_t
>(ncols));
3073 for (
Index k = 0; k < ncols; ++k) {
3074 for (std::size_t s = 0; s < 2; ++s) {
3075 const Channel& c = ch[s];
3076 const Eigen::VectorXd x = X.col(k).segment(offset[s], c.nocc * c.nvirt);
3077 U[std::size_t(k)][s] =
3078 Eigen::Map<const RowMajor>(x.data(), c.nocc, c.nvirt).transpose();
3079 const Eigen::MatrixXd half =
3080 c.C_virt * U[std::size_t(k)][s] * c.C_occ.transpose();
3081 dP[std::size_t(k)][s] = half + half.transpose();
3084 std::vector<std::array<Eigen::MatrixXd, 2>>
G;
3086 auto t =
timings_.Measure(
"second order: f_xc (spin)");
3089 Eigen::MatrixXd HX(X.rows(), ncols);
3090 for (
Index k = 0; k < ncols; ++k) {
3091 std::array<Eigen::MatrixXd, 2>& g =
G[std::size_t(k)];
3092 const std::array<Eigen::MatrixXd, 2>& d = dP[std::size_t(k)];
3095 auto t =
timings_.Measure(
"second order: J (RI)");
3096 const Eigen::MatrixXd J =
ERIs_.CalculateERIs_3c(d[0] + d[1]);
3101 auto t =
timings_.Measure(
"second order: K (RI)");
3102 for (std::size_t s = 0; s < 2; ++s) {
3104 const Eigen::MatrixXd A = ch[s].C_virt * U[std::size_t(k)][s];
3106 ERIs_.CalculateEXX_factors(A + ch[s].C_occ, A - ch[s].C_occ);
3110 auto t =
timings_.Measure(
"second order: J+K (4c)");
3111 const std::array<Eigen::MatrixXd, 2> jk_a =
3112 ERIs_.CalculateERIs_EXX_4c(d[0], 1
e-12);
3113 const std::array<Eigen::MatrixXd, 2> jk_b =
3114 ERIs_.CalculateERIs_EXX_4c(d[1], 1
e-12);
3115 g[0] += jk_a[0] + jk_b[0];
3116 g[1] += jk_a[0] + jk_b[0];
3122 for (std::size_t s = 0; s < 2; ++s) {
3123 const Channel& c = ch[s];
3124 const Eigen::MatrixXd& u = U[std::size_t(k)][s];
3125 const RowMajor R = 2.0 * (c.F_vv * u - u * c.F_oo +
3126 c.C_virt.transpose() * g[s] * c.C_occ)
3128 HX.col(k).segment(offset[s], c.nocc * c.nvirt) =
3129 Eigen::Map<const Eigen::VectorXd>(R.data(), R.size());
3137 const Eigen::MatrixXd& C_occ,
const Eigen::MatrixXd& C_virt,
3138 const std::vector<Eigen::MatrixXd>& U) {
3139 auto density = [&](
const Eigen::MatrixXd& u) {
3140 const Eigen::MatrixXd half = (C_virt * u) * C_occ.transpose();
3141 return Eigen::MatrixXd(2.0 * (half + half.transpose()));
3143 std::vector<Eigen::MatrixXd> dP;
3144 for (
const Eigen::MatrixXd& u : U) {
3145 dP.push_back(density(u));
3147 std::vector<Eigen::MatrixXd>
G;
3149 auto t =
timings_.Measure(
"response: f_xc");
3152 std::vector<Eigen::MatrixXd> result;
3153 for (std::size_t k = 0; k < U.size(); ++k) {
3156 auto t =
timings_.Measure(
"response: J (RI)");
3157 G[k] +=
ERIs_.CalculateERIs_3c(dP[k]);
3160 auto t =
timings_.Measure(
"response: K (RI)");
3161 const Eigen::MatrixXd A = C_virt * U[k];
3163 0.5 *
ScaHFX_ *
ERIs_.CalculateEXX_factors(A + C_occ, A - C_occ);
3166 auto t =
timings_.Measure(
"response: J+K (4c)");
3167 const std::array<Eigen::MatrixXd, 2> jk =
3168 ERIs_.CalculateERIs_EXX_4c(dP[k], 1
e-12);
3174 result.push_back(C_virt.transpose() *
G[k] * C_occ);
3180 const Orbitals& orb,
const std::vector<Eigen::MatrixXd>& perturbations,
3181 double tolerance,
Index max_iterations) {
3186 throw std::runtime_error(
3187 "DFTEngine::StaticResponse: closed-shell ground states only");
3191 throw std::runtime_error(
3192 "DFTEngine::StaticResponse: the orbitals do not belong to this basis");
3198 const Index n_virt = n_mo - n_occ;
3199 const Eigen::MatrixXd C_occ = orb.
MOs().
eigenvectors().leftCols(n_occ);
3200 const Eigen::MatrixXd C_virt = orb.
MOs().
eigenvectors().rightCols(n_virt);
3203 Eigen::MatrixXd diag(n_virt, n_occ);
3204 for (
Index i = 0; i < n_occ; ++i) {
3205 diag.col(i) =
e.tail(n_virt).array() -
e(i);
3207 if (diag.minCoeff() <= 0.0) {
3208 throw std::runtime_error(
3209 "DFTEngine::StaticResponse: no gap between occupied and virtual "
3212 const Eigen::MatrixXd P0 = 2.0 * C_occ * C_occ.transpose();
3214 const Index n_rhs =
Index(perturbations.size());
3215 std::vector<Eigen::MatrixXd> b(
static_cast<std::size_t
>(n_rhs));
3216 for (
Index k = 0; k < n_rhs; ++k) {
3217 const Eigen::MatrixXd& h = perturbations[std::size_t(k)];
3218 if (h.rows() != n_ao || h.cols() != n_ao) {
3219 throw std::runtime_error(
3220 "DFTEngine::StaticResponse: perturbation does not match the basis");
3222 b[std::size_t(k)] = -(C_virt.transpose() * h * C_occ);
3225 auto density = [&](
const Eigen::MatrixXd& U) {
3226 const Eigen::MatrixXd half = (C_virt * U) * C_occ.transpose();
3227 return Eigen::MatrixXd(2.0 * (half + half.transpose()));
3230 auto apply = [&](
const std::vector<Eigen::MatrixXd>& U) {
3231 std::vector<Eigen::MatrixXd> result =
3233 for (std::size_t k = 0; k < U.size(); ++k) {
3234 result[k] += diag.cwiseProduct(U[k]);
3243 const double kDropNorm = 1
e-10;
3244 std::vector<Eigen::MatrixXd>
V;
3245 std::vector<Eigen::MatrixXd> AV;
3246 std::vector<Eigen::MatrixXd> x(
static_cast<std::size_t
>(n_rhs));
3247 std::vector<Eigen::MatrixXd> r(
static_cast<std::size_t
>(n_rhs));
3248 std::vector<double> bnorm(
static_cast<std::size_t
>(n_rhs), 0.0);
3249 std::vector<Index> active;
3250 for (
Index k = 0; k < n_rhs; ++k) {
3251 const std::size_t kk = std::size_t(k);
3252 bnorm[kk] = b[kk].norm();
3253 x[kk] = Eigen::MatrixXd::Zero(n_virt, n_occ);
3255 if (bnorm[kk] > 0.0) {
3256 active.push_back(k);
3259 Index iteration = 0;
3260 while (!active.empty()) {
3261 if (iteration == max_iterations) {
3262 throw std::runtime_error(
3263 "DFTEngine::StaticResponse: CPKS did not converge in " +
3264 std::to_string(max_iterations) +
" iterations");
3269 std::vector<Eigen::MatrixXd> fresh;
3270 for (
Index k : active) {
3271 Eigen::MatrixXd v = r[std::size_t(k)].cwiseQuotient(diag);
3272 const double norm0 = v.norm();
3273 for (
Index pass = 0; pass < 2; ++pass) {
3274 for (
const Eigen::MatrixXd& u :
V) {
3275 v -= u.cwiseProduct(v).sum() * u;
3277 for (
const Eigen::MatrixXd& u : fresh) {
3278 v -= u.cwiseProduct(v).sum() * u;
3281 const double norm = v.norm();
3282 if (norm > kDropNorm * norm0) {
3283 fresh.push_back(v / norm);
3286 if (fresh.empty()) {
3287 throw std::runtime_error(
3288 "DFTEngine::StaticResponse: CPKS subspace stopped growing");
3290 const std::vector<Eigen::MatrixXd> Afresh = apply(fresh);
3291 V.insert(
V.end(), fresh.begin(), fresh.end());
3292 AV.insert(AV.end(), Afresh.begin(), Afresh.end());
3294 Eigen::MatrixXd M(m, m);
3295 for (
Index i = 0; i < m; ++i) {
3296 for (
Index j = 0; j <= i; ++j) {
3298 0.5 * (
V[std::size_t(i)].cwiseProduct(AV[std::size_t(j)]).sum() +
3299 V[std::size_t(j)].cwiseProduct(AV[std::size_t(i)]).sum());
3303 const Eigen::LDLT<Eigen::MatrixXd> solver(M);
3304 std::vector<Index> still;
3306 for (
Index k : active) {
3307 const std::size_t kk = std::size_t(k);
3308 Eigen::VectorXd proj(m);
3309 for (
Index i = 0; i < m; ++i) {
3310 proj(i) =
V[std::size_t(i)].cwiseProduct(b[kk]).sum();
3312 const Eigen::VectorXd y = solver.solve(proj);
3315 for (
Index i = 0; i < m; ++i) {
3316 x[kk] += y(i) *
V[std::size_t(i)];
3317 r[kk] -= y(i) * AV[std::size_t(i)];
3319 const double rel = r[kk].norm() / bnorm[kk];
3320 worst = std::max(worst, rel);
3321 if (rel >= tolerance) {
3326 <<
TimeStamp() <<
" CPKS iteration " << iteration <<
": "
3327 << fresh.size() <<
" new directions, subspace " << m
3328 <<
", largest relative residual " << worst << std::flush;
3332 std::vector<Eigen::MatrixXd> dP;
3333 for (
const Eigen::MatrixXd& u : x) {
3334 dP.push_back(density(u));
3348 std::map<std::string, Eigen::MatrixXd> reference_densities =
3355 full_dftbasis.
Fill(basisset, mol);
3361 std::vector<HirshfeldPartition::AtomicReference> atoms =
3363 reference_densities);
3368 double neutral_reference_population = 0.0;
3370 if (atom_index < 0 || atom_index >=
static_cast<Index>(mol.
size())) {
3371 throw std::runtime_error(
3372 "BuildCDFTConstraint: cdft.indices contains atom index " +
3373 std::to_string(atom_index) +
", but this molecule only has " +
3374 std::to_string(mol.
size()) +
3375 " atoms (0-based indexing -- valid range is 0.." +
3376 std::to_string(mol.
size() - 1) +
").");
3386 atoms, atom_index, full_dftbasis, grid);
3387 neutral_reference_population +=
3388 static_cast<double>(mol[atom_index].getNuccharge());
3399 <<
" atom(s), neutral reference population="
3400 << neutral_reference_population
3415 throw std::runtime_error(
3416 (boost::format(
"Basisset Name in guess orb file "
3417 "and in dftengine option file differ %1% vs %2%") %
3425 "Orbital file has no basisset information,"
3426 "using it as a guess might work or not for calculation with "
3451 throw std::runtime_error(
3452 (boost::format(
"ECPs in orb file: %1% and options %2% differ") %
3459 throw std::runtime_error(
3460 (boost::format(
"Number of electrons in guess orb file "
3461 "and in dftengine differ: "
3462 "alpha %1% vs %2%, beta %3% vs %4%.") %
3468 throw std::runtime_error(
3469 (boost::format(
"Number of levels in guess orb file: "
3470 "%1% and in dftengine: %2% differ.") %
3492 for (
const QMAtom& atom : mol) {
3494 std::string output = (boost::format(
" %1$s"
3495 " %2$+1.4f %3$+1.4f %4$+1.4f") %
3496 atom.getElement() % pos[0] % pos[1] % pos[2])
3507 <<
dftbasis_.AOBasisSize() <<
" functions" << std::flush;
3515 <<
auxbasis_.AOBasisSize() <<
" functions" << std::flush;
3523 std::vector<std::string> results =
ecp_.Fill(ecpbasisset, mol);
3525 <<
TimeStamp() <<
" Filled ECP Basis" << std::flush;
3526 if (results.size() > 0) {
3527 std::string message =
"";
3528 for (
const std::string& element : results) {
3529 message +=
" " + element;
3532 <<
TimeStamp() <<
" Found no ECPs for elements" << message
3543 Index nuclear_charge = 0;
3544 for (
const QMAtom& atom : mol) {
3545 nuclear_charge += atom.getNuccharge();
3551 if (multiplicity < 1) {
3552 throw std::runtime_error(
"Spin multiplicity must be >= 1.");
3555 if (numofelectrons >= 0) {
3561 Index spin_excess = multiplicity - 1;
3564 throw std::runtime_error(
"Computed a negative number of electrons.");
3568 throw std::runtime_error(
3569 "Spin multiplicity incompatible with total number of electrons.");
3573 throw std::runtime_error(
3574 "Charge and spin multiplicity imply non-integer alpha/beta "
3586 <<
" (charge=" << target_charge <<
", multiplicity=" << multiplicity
3610 <<
"\t\t " <<
" with " << grid.
getGridSize() <<
" points"
3611 <<
" divided into " << grid.
getBoxesSize() <<
" boxes" << std::flush;
3616 double E_nucnuc = 0.0;
3618 for (
Index i = 0; i < mol.
size(); i++) {
3619 const Eigen::Vector3d& r1 = mol[i].
getPos();
3620 double charge1 = double(mol[i].getNuccharge());
3621 for (
Index j = 0; j < i; j++) {
3622 const Eigen::Vector3d& r2 = mol[j].
getPos();
3623 double charge2 = double(mol[j].getNuccharge());
3624 E_nucnuc += charge1 * charge2 / (r1 - r2).norm();
3641 const Eigen::MatrixXd& dmat,
const AOBasis& dftbasis)
const {
3642 Eigen::MatrixXd avdmat = Eigen::MatrixXd::Zero(dmat.rows(), dmat.cols());
3643 for (
const AOShell& shellrow : dftbasis) {
3644 for (
const AOShell& shellcol : dftbasis) {
3645 if (shellrow.getL() != shellcol.getL()) {
3648 const Index size = shellrow.getNumFunc();
3649 const double diagavg = dmat.block(shellrow.getStartIndex(),
3650 shellcol.getStartIndex(), size, size)
3654 .block(shellrow.getStartIndex(), shellcol.getStartIndex(), size, size)
3656 .setConstant(diagavg);
3673 const std::vector<std::unique_ptr<StaticSite>>& multipoles)
const {
3675 if (multipoles.size() == 0) {
3681 for (
const QMAtom& atom : mol) {
3683 for (
const std::unique_ptr<StaticSite>& site : multipoles) {
3684 if ((site->getPos() - nucleus.
getPos()).norm() < 1
e-7) {
3686 <<
" External site sits on nucleus, "
3687 "interaction between them is ignored."
3722 if (
const PolarSite* p = AsChargeResponseSite(*site)) {
3725 const Eigen::Vector3d r = nucleus.
getPos() - p->getPos();
3727 E_ext += nucleus.
getCharge() * (p->Induced_Charge() * k.
B0 +
3728 k.
B1 * p->getInducedDipole().dot(r));
3730 Q.segment<3>(1) += site->getInducedDipole();
3732 Index rank = site->getRank();
3733 if (rank < 1 && Q.segment<3>(1).norm() > 1
e-12) {
3736 StaticSite effective(site->getId(), site->getElement(), site->getPos());
3750 Eigen::MatrixXd result =
3752 for (
Index i = 0; i < 3; i++) {
3760 const std::vector<std::unique_ptr<StaticSite>>& multipoles)
const {
3767 std::vector<AOGaussianSites::Source> gaussian;
3768 for (
const std::unique_ptr<StaticSite>& site : multipoles) {
3769 if (
const PolarSite* p = AsChargeResponseSite(*site)) {
3770 gaussian.push_back({p->getPos(), p->CRWidth(), p->Induced_Charge(),
3771 p->getInducedDipole()});
3774 if (!gaussian.empty()) {
3776 std::vector<std::unique_ptr<StaticSite>> point_sites;
3777 for (
const std::unique_ptr<StaticSite>& site : multipoles) {
3778 if (AsChargeResponseSite(*site) !=
nullptr) {
3783 point_sites.push_back(std::make_unique<PolarSite>(*p));
3785 point_sites.push_back(std::make_unique<StaticSite>(*site));
3797 <<
TimeStamp() <<
" Filled DFT external multipole potential matrix ("
3798 << gaussian.size() <<
" charge-response sites as Gaussians)"
3807 <<
TimeStamp() <<
" Filled DFT external multipole potential matrix"
3817 Eigen::MatrixXd sites(
Index(multipoles.size()), 13);
3818 for (
Index i = 0; i <
Index(multipoles.size()); ++i) {
3820 sites.block<1, 3>(i, 0) = site.
getPos().transpose();
3821 sites(i, 3) = double(site.
getRank());
3822 sites.block<1, 9>(i, 4) = site.
Q().transpose();
3827 setup_cache_->multipole_sites.rows() == sites.rows() &&
3832 <<
" Reusing the permanent multipole potential matrix of the previous "
3844 <<
TimeStamp() <<
" Filled DFT permanent multipole potential matrix"
3848 const bool has_induced =
3849 std::any_of(multipoles.begin(), multipoles.end(),
3850 [](
const std::unique_ptr<StaticSite>& site) {
3851 return site->getInducedDipole().norm() > 1e-12;
3858 <<
TimeStamp() <<
" Filled DFT induced dipole potential matrix"
3878 <<
TimeStamp() <<
" Calculated external density" << std::flush;
3881 <<
TimeStamp() <<
" Calculated potential from electron density"
3886 double nuc_energy = 0.0;
3887 for (
const QMAtom& atom : mol) {
3891 const double dist = (atom.getPos() - extatom.getPos()).norm();
3893 double(atom.getNuccharge()) * double(extatom.getNuccharge()) / dist;
3897 <<
TimeStamp() <<
" Calculated potential from nuclei" << std::flush;
3899 <<
TimeStamp() <<
" Electrostatic: " << nuc_energy << std::flush;
3904 const Eigen::MatrixXd& GuessMOs)
const {
3905 Eigen::MatrixXd nonortho =
3907 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(nonortho);
3908 Eigen::MatrixXd result = GuessMOs * es.operatorInverseSqrt();
3921 Eigen::VectorXd eps = Eigen::VectorXd::Zero(nao);
3925 int l =
static_cast<int>(shell.getL());
3926 Index start = shell.getStartIndex();
3927 Index nfunc = shell.getNumFunc();
3929 const QMAtom& atom = mol[shell.getAtomIndex()];
3930 const std::string& element = atom.
getElement();
3935 for (
Index i = 0; i < nfunc; ++i) {
3946 const Index nao =
S.rows();
3948 Eigen::MatrixXd
H = Eigen::MatrixXd::Zero(nao, nao);
3949 constexpr double K = 1.75;
3951 for (
Index mu = 0; mu < nao; ++mu) {
3952 H(mu, mu) = eps(mu);
3953 for (
Index nu = 0; nu < mu; ++nu) {
3954 double hij = K *
S(mu, nu) * 0.5 * (eps(mu) + eps(nu));
3966 <<
TimeStamp() <<
" Building Extended Huckel guess" << std::flush;
3971 <<
TimeStamp() <<
" Solving EHT generalized eigenproblem" << std::flush;
3989 std::array<Eigen::MatrixXd, 2> both =
4017 if (nA + nB != dimer_mol.
size()) {
4018 throw std::runtime_error(
4019 "BuildDimerGuessFromMonomerFiles: monomer A (" + std::to_string(nA) +
4020 " atoms) + monomer B (" + std::to_string(nB) +
4021 " atoms) does not equal this calculation's own molecule (" +
4022 std::to_string(dimer_mol.
size()) +
4023 " atoms) -- wrong monomer file(s), or this calculation's molecule "
4024 "is not simply the concatenation of these two monomers.");
4026 for (
Index i = 0; i < nA; ++i) {
4027 if (atomsA[i].getElement() != dimer_mol[i].getElement()) {
4028 throw std::runtime_error(
4029 "BuildDimerGuessFromMonomerFiles: monomer A's own atom " +
4030 std::to_string(i) +
" (" + atomsA[i].getElement() +
4031 ") does not match this calculation's own atom " + std::to_string(i) +
4032 " (" + dimer_mol[i].getElement() +
4033 ") -- dimer_guess assumes monomer A occupies exactly the first "
4034 "N_A atoms of this calculation's molecule, in the same order.");
4037 for (
Index i = 0; i < nB; ++i) {
4038 if (atomsB[i].getElement() != dimer_mol[nA + i].getElement()) {
4039 throw std::runtime_error(
4040 "BuildDimerGuessFromMonomerFiles: monomer B's own atom " +
4041 std::to_string(i) +
" (" + atomsB[i].getElement() +
4042 ") does not match this calculation's own atom " +
4043 std::to_string(nA + i) +
" (" + dimer_mol[nA + i].getElement() +
4044 ") -- dimer_guess assumes monomer B occupies exactly the "
4045 "remaining atoms of this calculation's molecule (after monomer "
4046 "A's own N_A atoms), in the same order.");
4059 constexpr double kGeometryToleranceBohr = 1
e-3;
4060 auto CheckInternalGeometry = [&](
const QMMolecule& monomer_atoms,
4061 Index offset_in_dimer,
4062 const std::string& label) {
4064 for (
Index i = 0; i < n; ++i) {
4065 for (
Index j = i + 1; j < n; ++j) {
4066 double monomer_distance =
4067 (monomer_atoms[i].
getPos() - monomer_atoms[j].getPos()).norm();
4068 double dimer_distance = (dimer_mol[offset_in_dimer + i].
getPos() -
4069 dimer_mol[offset_in_dimer + j].getPos())
4071 double diff = std::abs(monomer_distance - dimer_distance);
4072 if (diff > kGeometryToleranceBohr) {
4073 throw std::runtime_error(
4074 "BuildDimerGuessFromMonomerFiles: " + label +
4075 "'s own internal geometry does not match this calculation's "
4076 "molecule -- distance between its own atoms " +
4077 std::to_string(i) +
" and " + std::to_string(j) +
" is " +
4078 std::to_string(monomer_distance) +
4079 " Bohr in the monomer file, but " +
4080 std::to_string(dimer_distance) +
4081 " Bohr in this calculation's own molecule (difference " +
4082 std::to_string(diff) +
" Bohr, tolerance " +
4083 std::to_string(kGeometryToleranceBohr) +
4084 " Bohr). This is checked as an INTERNAL, translation/"
4085 "rotation-invariant distance specifically because the "
4086 "monomer's absolute position/orientation is expected to "
4087 "differ between its own standalone optimization and its "
4088 "placement in the dimer -- only its internal geometry "
4089 "should still match.");
4094 CheckInternalGeometry(atomsA, 0,
"Monomer A");
4095 CheckInternalGeometry(atomsB, nA,
"Monomer B");
4099 auto MaxDeviationFromTranslation = [&](
const QMMolecule& monomer_atoms,
4100 Index offset_in_dimer) {
4101 Eigen::Vector3d shift =
4102 dimer_mol[offset_in_dimer].
getPos() - monomer_atoms[0].
getPos();
4103 double max_dev = 0.0;
4104 for (
Index i = 0; i < monomer_atoms.
size(); ++i) {
4105 double dev = (dimer_mol[offset_in_dimer + i].
getPos() -
4106 monomer_atoms[i].getPos() - shift)
4108 max_dev = std::max(max_dev, dev);
4112 auto WarnIfRotated = [&](
const QMMolecule& monomer_atoms,
4113 Index offset_in_dimer,
const std::string& label) {
4114 double dev = MaxDeviationFromTranslation(monomer_atoms, offset_in_dimer);
4115 if (dev > kGeometryToleranceBohr) {
4118 <<
" is rotated with respect to its .orb file (max deviation " << dev
4119 <<
" bohr after translation). Its MO coefficients are not "
4120 "rotated, so the dimer guess will be poor."
4124 WarnIfRotated(atomsA, 0,
"Monomer A");
4125 WarnIfRotated(atomsB, nA,
"Monomer B");
4134 dimer_guess.
QMAtoms() = dimer_mol;
Container to hold Basisfunctions for all atoms.
Index AOBasisSize() const
void Fill(const BasisSet &bs, const QMMolecule &atoms)
void setCenter(const Eigen::Vector3d &r)
const std::array< Eigen::MatrixXd, 3 > & Matrix() const
void Fill(const AOBasis &aobasis) final
void FillPotential(const AOBasis &aobasis, const ECPAOBasis &ecp)
static Eigen::MatrixXd Potential(const AOBasis &basis, const std::vector< Source > &sources)
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
void FillPotential(const AOBasis &aobasis, const QMMolecule &atoms)
@ Permanent
permanent moments only (charge, dipole, quadrupole)
@ Induced
induced dipoles only; sites without one are skipped
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
const Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > & Matrix() const
void push_back(const T &atom)
std::vector< std::string > FindUniqueElements() const
const Eigen::Vector3d & getPos() const
void Load(const std::string &name)
Eigen::MatrixXd CalcERIs(const Eigen::MatrixXd &Dmat, double error) const
Build the Coulomb matrix contribution from the current density matrix.
Eigen::MatrixXd ComputeOverlapPulayGradientUKS(const QMMolecule &mol, const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta) const
std::string auxbasis_name_
Index num_alpha_electrons_
tools::EigenSystem ModelPotentialGuess(const Mat_p_Energy &H0, const QMMolecule &mol, const Vxc_Potential< Vxc_Grid > &vxcpotential) const
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.
void setSCFToleranceFloor(double energy, double error)
Index num_beta_electrons_
Orbitals BuildDimerGuessFromMonomerFiles(const QMMolecule &dimer_mol) const
void ComputePolarizability(Orbitals &orb)
double cdft_population_tolerance_
Eigen::MatrixXd ext_potential_matrix_
void ComputeAndStoreForces(Orbitals &orb, const Eigen::MatrixXd &Dmat, const Vxc_Potential< Vxc_Grid > &vxcpotential) const
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.
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.
void ReportDimensionsAndMemory() const
Basis dimensions and the memory of the large intermediates.
std::string dftbasis_name_
DFTSetupCache * setup_cache_
bool EvaluateUKS(Orbitals &orb, const Mat_p_Energy &H0, const Vxc_Potential< Vxc_Grid > &vxcpotential)
double truncation_threshold_
Eigen::MatrixXd RunAtomicDFT_unrestricted(const QMAtom &uniqueAtom, bool use_hunds_rule_occupation=false) const
Mat_p_Energy IntegrateExternalDensity(const QMMolecule &mol, const Orbitals &extdensity) const
bool compute_polarizability_
void Prepare(Orbitals &orb, Index numofelectrons=-1)
Vxc_Grid external_ewaldgrid_
std::string initial_guess_
UKSConvergenceAcc::SpinFock BuildUKSFock(const Eigen::MatrixXd &H0, const Vxc_Potential< Vxc_Grid > &vxcpotential, const Eigen::MatrixXd &alpha_new, const Eigen::MatrixXd &beta_new)
Eigen::MatrixXd AtomicGuess(const QMMolecule &mol) const
Build an atomic-density based initial guess in the AO basis.
bool EvaluateAndTime(Orbitals &orb)
Evaluate() without the timing report.
std::vector< HirshfeldPartition::AtomicReference > HirshfeldReferences(const QMMolecule &mol) const
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.
std::vector< Eigen::MatrixXd > CoupledResponse(const Vxc_Potential< Vxc_Grid > &vxc, const Eigen::MatrixXd &P0, const Eigen::MatrixXd &C_occ, const Eigen::MatrixXd &C_virt, const std::vector< Eigen::MatrixXd > &U)
double ri_pair_threshold_
Eigen::Vector3d extfield_
double overlap_tolerance_
std::array< Eigen::MatrixXd, 2 > CalcERIs_EXX(const Eigen::MatrixXd &MOCoeff, const Eigen::MatrixXd &Dmat, double error) const
Eigen::MatrixXd UKSOrbitalHessian(const Vxc_Potential< Vxc_Grid > &vxc, const tools::EigenSystem &mos_alpha, const tools::EigenSystem &mos_beta, const Eigen::MatrixXd &F_alpha, const Eigen::MatrixXd &F_beta, const Eigen::MatrixXd &X)
HirshfeldPartition::Constraint BuildCDFTConstraint(const QMMolecule &mol, const CDFTConstraintSpec &spec) const
void Initialize(tools::Property &options)
Read DFT, grid, and SCF settings from the user options tree.
std::string xc_functional_name_
void CalcElDipole(const Orbitals &orb) const
Evaluate and print the electronic dipole moment from the final density.
double ExternalRepulsion(const QMMolecule &mol, const std::vector< std::unique_ptr< StaticSite > > &multipoles) const
ConvergenceAcc::options conv_opt_
void SetupInvariantMatrices()
Precompute AO matrices that remain unchanged throughout the SCF procedure.
tools::EigenSystem ExtendedHuckelDFTGuess(const Mat_p_Energy &H0, const QMMolecule &mol, const Vxc_Potential< Vxc_Grid > &vxcpotential) const
std::map< std::string, Eigen::MatrixXd > ComputeHirshfeldReferenceDensities(const QMMolecule &mol) const
Index max_cdft_iterations_
std::string active_atoms_as_string_
void ComputeAndStoreForcesUKS(Orbitals &orb, const UKSConvergenceAcc::SpinDensity &Dspin, const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta, const Vxc_Potential< Vxc_Grid > &vxcpotential) const
double ewald_nuclear_energy_
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.
bool integrate_ext_density_
std::vector< Eigen::MatrixXd > StaticResponse(const Orbitals &orb, const std::vector< Eigen::MatrixXd > &perturbations, double tolerance=1e-7, Index max_iterations=100)
std::vector< HirshfeldPartition::Constraint > constraints_
bool UsableAsWarmStart(const Orbitals &orb, const std::string &previous_basis, std::string &reason) const
void PrintMOs(const Eigen::VectorXd &MOEnergies, Log::Level level)
Print a one-spin list of orbital energies and occupations to the logger.
Eigen::MatrixXd RKSOrbitalHessian(const Vxc_Potential< Vxc_Grid > &vxc, const tools::EigenSystem &mos, const Eigen::MatrixXd &F, const Eigen::MatrixXd &X)
Mat_p_Energy SetupH0(const QMMolecule &mol) const
Assemble the one-electron core Hamiltonian for the current molecule.
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)
CDFTConstraintSpec cdft_constraint_spec_
Eigen::MatrixXd ComputeNonXCGradientUKS(const QMMolecule &mol, const UKSConvergenceAcc::SpinDensity &Dspin, const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta) const
bool RunCDFT(Orbitals &orb, HirshfeldPartition::Constraint &constraint)
std::string dimer_guess_orbB_name_
std::vector< std::unique_ptr< StaticSite > > * externalsites_
Vxc_Potential< Vxc_Grid > SetupVxc(const QMMolecule &mol)
std::string RISetupKey() const
bool integrate_ext_field_
double NuclearRepulsion(const QMMolecule &mol) const
Compute the classical nucleus-nucleus repulsion energy.
ConvergenceAcc conv_accelerator_
std::string dimer_guess_orbA_name_
static Eigen::MatrixXd RIKGradient(const Eigen::MatrixXd &occ_mo_coeffs, const AOBasis &auxbasis, const AOBasis &dftbasis)
static Eigen::MatrixXd NuclearRepulsionDerivative(const QMMolecule &mol)
static Eigen::MatrixXd RIJGradient(const Eigen::MatrixXd &density, const AOBasis &auxbasis, const AOBasis &dftbasis)
static double ResidentMemoryGB(bool peak)
double IntegratePotential(const Eigen::Vector3d &rvector) const
double IntegrateDensity(const Eigen::MatrixXd &density_matrix)
Container to hold ECPs for all atoms.
std::vector< std::string > Fill(const ECPBasisSet &bs, QMMolecule &atoms)
void Load(const std::string &name)
Takes a density matrix and and an auxiliary basis set and calculates the electron repulsion integrals...
std::array< Eigen::MatrixXd, 2 > CalculateERIs_EXX_4c(const Eigen::MatrixXd &DMAT, double error) const
void Initialize_4c(const AOBasis &dftbasis)
Eigen::MatrixXd CalculateERIs_4c(const Eigen::MatrixXd &DMAT, double error) const
Mat_p_Energy IntegrateEwald(Index basissize) const
double GetWithFallback(const std::string &element, int l, int *used_l=nullptr) const
std::vector< double > & getPotentialValues()
static std::vector< AtomicReference > BuildAtomicReferences(const QMMolecule &mol, const std::string &basisset_name, const std::map< std::string, Eigen::MatrixXd > &reference_densities)
static Eigen::MatrixXd BuildWeightMatrix(const std::vector< AtomicReference > &atoms, Index target_atom_index, const AOBasis &full_dftbasis, const Vxc_Grid &grid)
static Eigen::MatrixXd ComputeCDFTForceContribution(const std::vector< AtomicReference > &atoms, Index target_atom_index, const Eigen::MatrixXd &density_matrix, const QMMolecule &mol, const AOBasis &full_dftbasis, const Vxc_Grid &grid)
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
std::vector< Index > CreateIndexVector(const std::string &Ids) const
Eigen::MatrixXd & matrix()
Container for molecular orbitals and derived one-particle data.
void setScaHFX(double ScaHFX)
Store the fraction of exact exchange associated with the functional.
Index getCharge() const
Return the stored total charge.
void clearPolarizability()
Index getSpin() const
Return the stored spin multiplicity.
bool hasForces() const
Report whether nuclear forces have been stored.
const tools::EigenSystem & MOs_beta() const
Return read-only access to beta-spin molecular orbitals.
bool hasMOs() const
Report whether alpha/restricted molecular orbitals are available.
std::array< Eigen::MatrixXd, 2 > DensityMatrixGroundStateSpinResolved() const
Index getNumberOfAlphaElectrons() const
Return the stored number of alpha electrons.
void setNumberOfAlphaElectrons(Index electrons)
Store the total number of alpha electrons.
Eigen::MatrixXd DensityMatrixFull(const QMState &state) const
void PrepareDimerGuessMixedSpin(const Orbitals &orbitalsA, const Orbitals &orbitalsB)
Guess for a dimer of two monomers with independently ARBITRARY charge and spin, built by combining ea...
void SetupAuxBasis(std::string aux_basis_name)
void setNumberOfBetaElectrons(Index electrons)
Store the total number of beta electrons.
void setForces(const Eigen::MatrixXd &forces)
void setECPName(const std::string &ECP)
Store the effective core potential label.
void setXCGrid(std::string grid)
Store the numerical XC grid quality label.
void setNumberOfOccupiedLevels(Index occupied_levels)
Index getBasisSetSize() const
Return the number of AO basis functions in the DFT basis.
void setQMEnergy(double qmenergy)
Store the total DFT energy.
bool hasECPName() const
Report whether an effective core potential label has been stored.
const tools::EigenSystem & MOs() const
Return read-only access to alpha/restricted molecular orbitals.
const QMMolecule & QMAtoms() const
Return read-only access to the molecular geometry.
void ReadFromCpt(const std::string &filename)
Read the orbital container from a checkpoint file on disk.
void setNumberOfOccupiedLevelsBeta(Index occupied_levels_beta)
Store the number of occupied beta-spin orbitals.
void setChargeAndSpin(Index charge, Index spin)
bool hasDFTbasisName() const
Report whether a DFT basis-set name has been stored.
const std::string & getECPName() const
Return the effective core potential label.
const std::string & getDFTbasisName() const
Return the DFT basis-set name.
bool hasBetaMOs() const
Report whether beta-spin molecular orbitals are available.
void SetupDftBasis(std::string basis_name)
Build and attach the DFT AO basis from the stored molecular geometry.
void setPolarizability(const Eigen::Matrix3d &alpha)
const Eigen::MatrixXd & getForces() const
Return the stored nuclear forces (Natoms x 3, Hartree/Bohr).
Eigen::Vector3d CalcElDipole(const QMState &state) const
Compute the electronic dipole moment associated with a state density.
Index getNumberOfBetaElectrons() const
Return the stored number of beta electrons.
const AOBasis & getDftBasis() const
Return the DFT AO basis, throwing if it has not been initialized.
void setXCFunctionalName(std::string functionalname)
Class to represent Atom/Site in electrostatic+polarization.
bool isChargeResponse() const
Index getElementNumber() const
const std::string & getElement() const
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
Class to represent Atom/Site in electrostatic.
void setMultipole(const Vector9d &multipole, Index rank)
const Eigen::Vector3d & getPos() const
const Vector9d & Q() const
Timestamp returns the current time as a string Example: cout << TimeStamp().
SpinDensity DensityMatrix(const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta) const
void setOverlap(AOOverlap &S, double etol)
void setLogger(Logger *log)
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)
double getDIIsError() const
void setOrbitalHessian(const OrbitalHessian &hessian)
void setCoupledFockBuilder(const CoupledFockBuilder &builder)
Index getGridSize() const
void GridSetup(const std::string &type, const QMMolecule &atoms, const AOBasis &basis)
Index getBoxesSize() const
Eigen::MatrixXd GridWeightGradient(const Eigen::MatrixXd &density_matrix, const QMMolecule &atoms) const
Eigen::MatrixXd PulayGradient(const Eigen::MatrixXd &density_matrix, const AOBasis &dftbasis) const
Eigen::MatrixXd PulayGradientUKS(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta, const AOBasis &dftbasis) const
Mat_p_Energy IntegrateVXC(const Eigen::MatrixXd &density_matrix) const
void setXCfunctional(const std::string &functional)
std::vector< std::array< Eigen::MatrixXd, 2 > > IntegrateFXCSpin(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta, const std::vector< std::array< Eigen::MatrixXd, 2 > > &response) const
static double getExactExchange(const std::string &functional)
std::vector< Eigen::MatrixXd > IntegrateFXC(const Eigen::MatrixXd &density_matrix, const std::vector< Eigen::MatrixXd > &response_matrices) const
Eigen::MatrixXd GridWeightGradientUKS(const Eigen::MatrixXd &dmat_alpha, const Eigen::MatrixXd &dmat_beta, const QMMolecule &atoms) const
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)
Charge transport classes.
std::vector< AOMatrixDerivative > ComputeNuclearAttractionDerivatives(const AOBasis &aobasis, const QMMolecule &mol)
std::vector< AOMatrixDerivative > ComputeKineticDerivatives(const AOBasis &aobasis)
std::vector< AOMatrixDerivative > ComputeOverlapDerivatives(const AOBasis &aobasis)
std::string EigenBackendDescription()
std::array< Eigen::MatrixXd, 3 > AOMatrixDerivative
bool HasLibint2DerivativeSupport()
Provides a means for comparing floating point numbers.
Spin-resolved density matrices returned for open-shell SCF updates.
Eigen::MatrixXd total() const
Return the total density P = P^alpha + P^beta.
std::vector< Index > atom_indices
Interaction of two normalized Gaussian charge distributions exp(-r^2/s_i^2) and exp(-r^2/s_j^2): erf(...
Eigen::MatrixXd weight_matrix
double spin_alpha_coefficient
double spin_beta_coefficient
Eigen::MatrixXd total() const
Eigen::MatrixXd vxc_alpha
Eigen::Matrix< double, 9, 1 > Vector9d