22#include <boost/filesystem.hpp>
23#include <boost/format.hpp>
54void CanonicalizeOrbitalPhases(Eigen::MatrixXd& coeffs) {
55 constexpr double tol = 1
e-14;
57 for (
Index col = 0; col < coeffs.cols(); ++col) {
58 Eigen::Index pivot = 0;
59 const double maxabs = coeffs.col(col).cwiseAbs().maxCoeff(&pivot);
65 if (coeffs(pivot, col) < 0.0) {
66 coeffs.col(col) *= -1.0;
71void CanonicalizeOrbitalPhases(tools::EigenSystem& mos) {
72 CanonicalizeOrbitalPhases(mos.eigenvectors());
102 const std::string key_xtpdft =
"xtpdft";
105 if (options.
exists(
".auxbasisset")) {
112 options.
get(key_xtpdft +
".fock_matrix_reset").
as<
Index>();
114 if (options.
exists(
".ecp")) {
118 if (options.
exists(key_xtpdft +
".force_uks_path")) {
122 if (options.
exists(key_xtpdft +
".compute_forces")) {
137 throw std::runtime_error(
138 "compute_forces=true was requested, but the libint2 this was "
139 "built against does not support derivative integrals for one "
140 "or more operator categories it needs (one-body, the two-center "
141 "Coulomb metric, or three-center RI -- see "
142 "libint2_derivative_calls.cc's own compile guards for exactly "
143 "which). Many pre-packaged libint2 builds (Homebrew, Ubuntu "
144 "apt, etc.) do not enable derivative-integral support for all "
145 "of these by default; rebuild libint2 with "
146 "--enable-1body/--enable-eri2/--enable-eri3 to use this "
162 throw std::runtime_error(
163 "compute_forces=true was requested together with an ECP ('" +
165 "'), but analytic nuclear forces do not yet include the ECP "
166 "contribution to the force (d(V_ECP)/dR) -- computing forces "
167 "in this configuration would silently omit that term rather "
168 "than fail visibly. Either drop the ECP or do not request "
169 "compute_forces until this is implemented.");
172 if (options.
exists(key_xtpdft +
".cdft.enabled")) {
185 std::string indices_str =
186 options.
get(key_xtpdft +
".cdft.indices").
as<std::string>();
187 if (indices_str.empty()) {
188 throw std::runtime_error(
189 "cdft.enabled=true was requested, but cdft.indices is empty -- "
190 "specify which atoms (0-based, e.g. '1 3 13:17', same syntax "
191 "already used for diabatization.xml's own fragment indices) "
192 "make up the constrained fragment.");
197 options.
get(key_xtpdft +
".cdft.charge").
as<
double>();
199 options.
get(key_xtpdft +
".cdft.initial_lambda").
as<
double>();
201 options.
get(key_xtpdft +
".cdft.max_iterations").
as<
Index>();
203 options.
get(key_xtpdft +
".cdft.population_tolerance").
as<
double>();
205 options.
get(key_xtpdft +
".cdft.guess_strategy").
as<std::string>();
222 throw std::runtime_error(
223 "initial_guess=dimer_guess requires both dimer_guess_orbA and "
224 "dimer_guess_orbB to be set to real monomer .orb file paths.");
228 grid_name_ = options.
get(key_xtpdft +
".integration_grid").
as<std::string>();
231 if (options.
exists(key_xtpdft +
".externaldensity")) {
234 options.
get(key_xtpdft +
".externaldensity.orbfile").
as<std::string>();
235 gridquality_ = options.
get(key_xtpdft +
".externaldensity.gridquality")
238 options.
get(key_xtpdft +
".externaldensity.state").
as<std::string>();
241 if (options.
exists(
".externalfield")) {
247 options.
get(key_xtpdft +
".convergence.energy").
as<
double>();
249 options.
get(key_xtpdft +
".convergence.error").
as<
double>();
251 options.
get(key_xtpdft +
".convergence.max_iterations").
as<
Index>();
254 options.
get(key_xtpdft +
".convergence.method").
as<std::string>();
255 if (method ==
"DIIS") {
257 }
else if (method ==
"mixing") {
265 options.
get(key_xtpdft +
".convergence.mixing").
as<
double>();
269 options.
get(key_xtpdft +
".convergence.mixing_max").
as<
double>();
271 options.
get(key_xtpdft +
".convergence.levelshift").
as<
double>();
273 options.
get(key_xtpdft +
".convergence.levelshift_end").
as<
double>();
278 options.
get(key_xtpdft +
".convergence.mixing_end").
as<
double>();
280 options.
get(key_xtpdft +
".convergence.DIIS_maxout").
as<
bool>();
282 options.
get(key_xtpdft +
".convergence.DIIS_length").
as<
Index>();
284 options.
get(key_xtpdft +
".convergence.DIIS_start").
as<
double>();
286 options.
get(key_xtpdft +
".convergence.ADIIS_start").
as<
double>();
288 options.
get(key_xtpdft +
".convergence.davidson_max_iter").
as<
Index>();
290 if (options.
exists(key_xtpdft +
".dft_in_dft.activeatoms")) {
292 options.
get(key_xtpdft +
".dft_in_dft.activeatoms").
as<std::string>();
294 options.
get(key_xtpdft +
".dft_in_dft.threshold").
as<
double>();
296 options.
get(key_xtpdft +
".dft_in_dft.levelshift").
as<
double>();
298 options.
get(key_xtpdft +
".dft_in_dft.truncate_basis").
as<
bool>();
301 options.
get(key_xtpdft +
".dft_in_dft.truncation_threshold")
308 XTP_LOG(level, *
pLog_) <<
" Orbital energies: " << std::flush;
309 XTP_LOG(level, *
pLog_) <<
" index occupation energy(Hartree) " << std::flush;
311 for (
Index i = 0; i < MOEnergies.size(); ++i) {
319 XTP_LOG(level, *
pLog_) << (boost::format(
" %1$5d %2$1d %3$+1.10f") %
320 i % occupancy % MOEnergies(i))
328 const Eigen::VectorXd& beta_energies,
330 XTP_LOG(level, *
pLog_) <<
" UKS orbital energies:" << std::flush;
331 XTP_LOG(level, *
pLog_) <<
" index occ eps_a(Ha) eps_b(Ha)"
335 std::max<Index>(alpha_energies.size(), beta_energies.size());
337 for (
Index i = 0; i < nrows; ++i) {
341 std::string occ =
"0";
342 if (occ_a && occ_b) {
350 std::string eps_a =
" -";
351 std::string eps_b =
" -";
353 if (i < alpha_energies.size()) {
354 eps_a = (boost::format(
"%+1.10f") % alpha_energies(i)).str();
356 if (i < beta_energies.size()) {
357 eps_b = (boost::format(
"%+1.10f") % beta_energies(i)).str();
361 " %1$5d %2$1s %3$15s %4$15s") %
362 i % occ % eps_a % eps_b)
370 " alpha HOMO-LUMO gap: %+1.10f Ha") %
379 " beta HOMO-LUMO gap: %+1.10f Ha") %
391 <<
TimeStamp() <<
" Electric Dipole is[e*bohr]:\n\t\t dx=" << result[0]
392 <<
"\n\t\t dy=" << result[1] <<
"\n\t\t dz=" << result[2] << std::flush;
426 Orbitals& orb,
const Eigen::MatrixXd& Dmat,
431 <<
" Skipping force calculation: RI-J gradient (DFTGradient::"
432 "RIJGradient) only implements the RI path, but this SCF ran "
433 "without an auxiliary basis (conventional 4-center ERIs)."
441 <<
" Skipping force calculation: the libint2 this was built "
442 "against does not support derivative integrals for one or "
443 "more operator categories it needs. Many pre-packaged "
444 "libint2 builds (Homebrew, Ubuntu apt, etc.) do not enable "
445 "this by default -- rebuild libint2 with "
446 "--enable-1body/--enable-eri2/--enable-eri3 to use analytic "
467 <<
"') was used for this SCF, but analytic nuclear forces do "
468 "not yet include the ECP contribution to the force "
469 "(d(V_ECP)/dR) -- computing forces in this configuration "
470 "would silently omit that term rather than fail visibly."
479 << natoms <<
" atoms)" << std::flush;
493 <<
" Computing one-electron (kinetic + nuclear "
494 "attraction) derivatives"
497 std::vector<AOMatrixDerivative> dVne =
499 Eigen::MatrixXd eone_grad = Eigen::MatrixXd::Zero(natoms, 3);
500 for (
Index a = 0; a < natoms; ++a) {
501 for (
Index xyz = 0; xyz < 3; ++xyz) {
502 eone_grad(a, xyz) = Dmat.cwiseProduct(dT[a][xyz] + dVne[a][xyz]).sum();
506 <<
TimeStamp() <<
" One-electron derivatives done" << std::flush;
537 Eigen::MatrixXd W = 2.0 * C_occ * eps_occ.asDiagonal() * C_occ.transpose();
540 <<
TimeStamp() <<
" Computing overlap (Pulay) derivatives"
543 Eigen::MatrixXd overlap_pulay_grad = Eigen::MatrixXd::Zero(natoms, 3);
544 for (
Index a = 0; a < natoms; ++a) {
545 for (
Index xyz = 0; xyz < 3; ++xyz) {
546 overlap_pulay_grad(a, xyz) = -W.cwiseProduct(dS[a][xyz]).sum();
550 <<
TimeStamp() <<
" Overlap derivatives done" << std::flush;
553 <<
TimeStamp() <<
" Computing RI-J (Coulomb) gradient" << std::flush;
554 Eigen::MatrixXd rij_term =
557 <<
TimeStamp() <<
" RI-J gradient done" << std::flush;
560 <<
TimeStamp() <<
" Computing XC grid (Pulay + weight) gradient terms"
566 <<
TimeStamp() <<
" XC grid gradient terms done" << std::flush;
568 Eigen::MatrixXd grad = nucrep_term + eone_grad + overlap_pulay_grad +
569 rij_term + pulay_term + weight_term;
592 <<
TimeStamp() <<
" Computing RI-K (exact exchange) gradient"
596 <<
TimeStamp() <<
" RI-K gradient done" << std::flush;
604 Eigen::Vector3d sum = grad.colwise().sum();
605 if (sum.cwiseAbs().maxCoeff() > 1
e-4) {
608 <<
" WARNING: computed forces do not sum to zero across atoms "
609 "(translational invariance check failed, max component="
610 << sum.cwiseAbs().maxCoeff()
611 <<
") -- treat these forces with "
623 Eigen::MatrixXd force = -grad;
627 <<
TimeStamp() <<
" Computed and stored ground-state nuclear forces."
636 for (
Index a = 0; a < natoms; ++a) {
638 (boost::format(
" %1$s"
639 " %2$+1.6f %3$+1.6f %4$+1.6f") %
640 mol[a].getElement() % force(a, 0) % force(a, 1) % force(a, 2))
675 Eigen::MatrixXd C_alpha_occ = MOs_alpha.
eigenvectors().leftCols(n_occ_alpha);
676 Eigen::MatrixXd C_beta_occ = MOs_beta.
eigenvectors().leftCols(n_occ_beta);
677 Eigen::VectorXd eps_alpha_occ = MOs_alpha.
eigenvalues().head(n_occ_alpha);
678 Eigen::VectorXd eps_beta_occ = MOs_beta.
eigenvalues().head(n_occ_beta);
680 C_alpha_occ * eps_alpha_occ.asDiagonal() * C_alpha_occ.transpose() +
681 C_beta_occ * eps_beta_occ.asDiagonal() * C_beta_occ.transpose();
685 Eigen::MatrixXd overlap_pulay_grad = Eigen::MatrixXd::Zero(natoms, 3);
686 for (
Index a = 0; a < natoms; ++a) {
687 for (
Index xyz = 0; xyz < 3; ++xyz) {
688 overlap_pulay_grad(a, xyz) = -W.cwiseProduct(dS[a][xyz]).sum();
691 return overlap_pulay_grad;
699 const Eigen::MatrixXd D_total = Dspin.
total();
708 std::vector<AOMatrixDerivative> dVne =
710 Eigen::MatrixXd eone_grad = Eigen::MatrixXd::Zero(natoms, 3);
711 for (
Index a = 0; a < natoms; ++a) {
712 for (
Index xyz = 0; xyz < 3; ++xyz) {
713 eone_grad(a, xyz) = D_total.cwiseProduct(dT[a][xyz] + dVne[a][xyz]).sum();
717 Eigen::MatrixXd overlap_pulay_grad =
720 Eigen::MatrixXd grad =
736 Eigen::MatrixXd C_alpha_occ =
738 Eigen::MatrixXd C_beta_occ =
754 <<
" Skipping UKS force calculation: RI-J gradient only "
755 "implements the RI path, but this SCF ran without an "
764 <<
" Skipping UKS force calculation: the libint2 this was "
765 "built against does not support derivative integrals for "
766 "one or more operator categories it needs. Many "
767 "pre-packaged libint2 builds (Homebrew, Ubuntu apt, etc.) "
768 "do not enable this by default -- rebuild libint2 with "
769 "--enable-1body/--enable-eri2/--enable-eri3 to use "
781 <<
TimeStamp() <<
" Skipping UKS force calculation: an ECP ('"
783 <<
"') was used for this SCF, but analytic nuclear forces do "
784 "not yet include the ECP contribution to the force "
785 "(d(V_ECP)/dR) -- computing forces in this configuration "
786 "would silently omit that term rather than fail visibly."
791 Eigen::MatrixXd grad =
807 Eigen::Vector3d sum = grad.colwise().sum();
808 if (sum.cwiseAbs().maxCoeff() > 1
e-4) {
811 <<
" WARNING: computed UKS forces do not sum to zero across "
812 "atoms (translational invariance check failed, max "
814 << sum.cwiseAbs().maxCoeff()
815 <<
") -- treat these forces with "
824 Eigen::MatrixXd force = -grad;
828 <<
TimeStamp() <<
" Computed and stored ground-state UKS nuclear forces."
834 for (
Index a = 0; a < force.rows(); ++a) {
835 std::string output = (boost::format(
" %1$s"
836 " %2$+1.6f %3$+1.6f %4$+1.6f") %
837 mol_for_print[a].getElement() % force(a, 0) %
838 force(a, 1) % force(a, 2))
853 const Eigen::MatrixXd& MOCoeff,
const Eigen::MatrixXd& Dmat,
854 double error)
const {
857 return ERIs_.CalculateERIs_EXX_3c(Eigen::MatrixXd::Zero(0, 0), Dmat);
860 return ERIs_.CalculateERIs_EXX_3c(occblock, Dmat);
863 return ERIs_.CalculateERIs_EXX_4c(Dmat, error);
870 double error)
const {
872 return ERIs_.CalculateERIs_3c(Dmat);
874 return ERIs_.CalculateERIs_4c(Dmat, error);
895 <<
TimeStamp() <<
" Filled DFT Vxc matrix " << std::flush;
900 std::array<Eigen::MatrixXd, 2> both =
934 bool converged =
RunCDFT(orb, constraint);
937 if (converged && original_compute_forces) {
953 <<
" CDFT converged -- computing the ordinary DFT force once, "
954 "for the final, converged density only"
985 std::map<std::string, Eigen::MatrixXd> reference_densities =
995 std::vector<HirshfeldPartition::AtomicReference> atoms =
999 std::array<Eigen::MatrixXd, 2> Dspin =
1005 Eigen::MatrixXd density_total = Dspin[0] + Dspin[1];
1007 Eigen::MatrixXd cdft_gradient_correction =
1010 cdft_gradient_correction +=
1012 atoms, atom_index, density_total, orb.
QMAtoms(), full_dftbasis,
1022 constraint.
lambda * cdft_gradient_correction);
1036 <<
" Forcing closed-shell singlet through UKS development path."
1071 double lambda_lo = constraint.
lambda - 0.1;
1072 double lambda_hi = constraint.
lambda + 0.1;
1074 auto EvaluateMismatch = [&](
double lambda) ->
double {
1077 <<
TimeStamp() <<
" CDFT: starting inner SCF at lambda=" << lambda
1079 bool scf_converged =
EvaluateUKS(orb, H0, vxcpotential);
1080 if (!scf_converged) {
1081 throw std::runtime_error(
1082 "RunCDFT: inner SCF did not converge at lambda=" +
1083 std::to_string(lambda));
1096 std::array<Eigen::MatrixXd, 2> Dspin =
1107 double mismatch_lo = EvaluateMismatch(lambda_lo);
1108 double mismatch_hi = EvaluateMismatch(lambda_hi);
1110 Index bracket_attempts = 0;
1111 constexpr Index kMaxBracketAttempts = 10;
1112 while (mismatch_lo * mismatch_hi > 0.0 &&
1113 bracket_attempts < kMaxBracketAttempts) {
1114 double width = lambda_hi - lambda_lo;
1115 lambda_lo -= 0.5 * width;
1116 lambda_hi += 0.5 * width;
1117 mismatch_lo = EvaluateMismatch(lambda_lo);
1118 mismatch_hi = EvaluateMismatch(lambda_hi);
1121 if (mismatch_lo * mismatch_hi > 0.0) {
1124 <<
" RunCDFT: could not bracket a root for the population "
1126 << kMaxBracketAttempts
1127 <<
" bracket-expansion attempts -- the target population may "
1128 "be unreachable for this system, or the initial "
1129 "lambda guess may be far from the actual root."
1138 double lambda_mid = 0.5 * (lambda_lo + lambda_hi);
1139 double mismatch_mid = EvaluateMismatch(lambda_mid);
1142 <<
TimeStamp() <<
" CDFT outer iteration " << outer_iter + 1 <<
" of "
1144 <<
" population mismatch=" << mismatch_mid << std::flush;
1147 constraint.
lambda = lambda_mid;
1150 <<
TimeStamp() <<
" CDFT converged after " << outer_iter + 1
1151 <<
" outer iterations, lambda=" << lambda_mid << std::flush;
1155 if (mismatch_mid * mismatch_lo < 0.0) {
1156 lambda_hi = lambda_mid;
1157 mismatch_hi = mismatch_mid;
1159 lambda_lo = lambda_mid;
1160 mismatch_lo = mismatch_mid;
1163 }
catch (
const std::runtime_error&) {
1171 <<
" RunCDFT: outer bisection loop did not converge "
1174 constraint.
lambda = 0.5 * (lambda_lo + lambda_hi);
1195 <<
TimeStamp() <<
" Reading guess from orbitals object/file"
1212 throw std::runtime_error(
1213 "initial_guess=dimer_guess is only meaningful for a genuinely "
1214 "unrestricted (UKS) calculation -- it exists specifically to "
1215 "combine two monomers of independently arbitrary charge/spin, "
1216 "which by construction do not reduce to a single restricted "
1217 "MO set. Use force_uks_path or an odd total electron count to "
1218 "route through EvaluateUKS instead.");
1220 throw std::runtime_error(
"Initial guess method not known/implemented");
1226 Eigen::MatrixXd Dmat = spin_dmat.
total();
1229 <<
TimeStamp() <<
" Guess Matrix gives N=" << std::setprecision(9)
1230 << Dmat.cwiseProduct(
dftAOoverlap_.Matrix()).sum() <<
" electrons."
1234 <<
TimeStamp() <<
" STARTING SCF cycle" << std::flush;
1236 <<
" ----------------------------------------------"
1237 "----------------------------"
1240 Eigen::MatrixXd J = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
1243 K = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
1246 double start_incremental_F_threshold = 1
e-4;
1248 start_incremental_F_threshold = 0.0;
1261 <<
TimeStamp() <<
" Filled DFT Vxc matrix " << std::flush;
1264 double Eone = Dmat.cwiseProduct(H0.
matrix()).sum();
1265 double Etwo = e_vxc.
energy();
1273 double integral_error =
1281 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
1284 exx = 0.25 *
ScaHFX_ * Dmat.cwiseProduct(K).sum();
1286 <<
TimeStamp() <<
" Filled F+K matrix " << std::flush;
1290 <<
TimeStamp() <<
" Filled F matrix " << std::flush;
1292 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
1296 double totenergy = Eone + H0.
energy() + Etwo;
1299 << std::setprecision(12) << Eone << std::flush;
1301 << std::setprecision(12) << Etwo << std::flush;
1303 <<
TimeStamp() << std::setprecision(12) <<
" Local Exc contribution "
1304 << e_vxc.
energy() << std::flush;
1308 <<
" Non local Ex contribution " << exx << std::flush;
1311 <<
TimeStamp() <<
" Total Energy " << std::setprecision(12) << totenergy
1331 <<
TimeStamp() <<
" Total Energy has converged to "
1333 <<
"[Ha] after " << this_iter + 1
1334 <<
" iterations. DIIS error is converged up to "
1337 <<
TimeStamp() <<
" Final Single Point Energy "
1338 << std::setprecision(12) << totenergy <<
" Ha" << std::flush;
1340 <<
" Final Local Exc contribution "
1341 << e_vxc.
energy() <<
" Ha" << std::flush;
1344 <<
" Final Non Local Ex contribution "
1345 << exx <<
" Ha" << std::flush;
1350 Index nuclear_charge = 0;
1352 nuclear_charge += atom.getNuccharge();
1370 }
else if (this_iter ==
max_iter_ - 1) {
1372 <<
TimeStamp() <<
" DFT calculation has not converged after "
1374 <<
" iterations. Use more iterations or another convergence "
1375 "acceleration scheme."
1412 conv_uks.
Configure(opt_alpha, opt_beta);
1418 <<
TimeStamp() <<
" Reading UKS guess from orbitals object/file"
1421 MOs_alpha = orb.
MOs();
1430 <<
" Orbital file has no beta MOs, using alpha guess for beta."
1432 MOs_beta = MOs_alpha;
1437 <<
" Building UKS guess from two monomer .orb files (dimer_guess)"
1440 MOs_alpha = dimer_guess_orb.
MOs();
1442 MOs_beta = dimer_guess_orb.
MOs_beta();
1459 throw std::runtime_error(
"Initial guess method not known/implemented");
1472 <<
TimeStamp() <<
" UKS guess gives Nalpha="
1479 <<
TimeStamp() <<
" STARTING UKS SCF cycle" << std::flush;
1481 <<
" ------------------------------------------------------------"
1489 Eigen::MatrixXd H_alpha = H0.
matrix();
1490 Eigen::MatrixXd H_beta = H0.
matrix();
1494 const Eigen::MatrixXd D_total = Dspin.
total();
1496 double E_one = Dspin.
alpha.cwiseProduct(H0.
matrix()).sum() +
1499 double E_coul = 0.0;
1503 double integral_error = std::min(conv_uks.
getDIIsError() * 1
e-5, 1
e-5);
1506 std::array<Eigen::MatrixXd, 2> both_alpha =
CalcERIs_EXX(
1507 Eigen::MatrixXd::Zero(0, 0), Dspin.
alpha, integral_error);
1508 std::array<Eigen::MatrixXd, 2> both_beta =
1511 Eigen::MatrixXd J = both_alpha[0] + both_beta[0];
1512 Eigen::MatrixXd K_alpha = both_alpha[1];
1513 Eigen::MatrixXd K_beta = both_beta[1];
1515 H_alpha += J +
ScaHFX_ * K_alpha;
1516 H_beta += J +
ScaHFX_ * K_beta;
1518 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
1520 (Dspin.
alpha.cwiseProduct(K_alpha).sum() +
1521 Dspin.
beta.cwiseProduct(K_beta).sum());
1523 Eigen::MatrixXd J =
CalcERIs(D_total, integral_error);
1526 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
1531 H_beta += vxc.vxc_beta;
1534 double totenergy = H0.
energy() + E_one + E_coul + E_xc + E_exx;
1553 H_alpha += (c.lambda * c.spin_alpha_coefficient) * c.weight_matrix;
1554 H_beta += (c.lambda * c.spin_beta_coefficient) * c.weight_matrix;
1556 c.spin_alpha_coefficient *
1557 Dspin.
alpha.cwiseProduct(c.weight_matrix).sum() +
1558 c.spin_beta_coefficient *
1559 Dspin.
beta.cwiseProduct(c.weight_matrix).sum();
1560 totenergy += c.lambda * (population - c.target_population);
1565 << std::setprecision(12) << E_one << std::flush;
1567 << std::setprecision(12) << E_coul << std::flush;
1569 << std::setprecision(12) << E_xc << std::flush;
1572 <<
TimeStamp() <<
" EXX contribution " << std::setprecision(12)
1573 << E_exx << std::flush;
1576 <<
TimeStamp() <<
" Total Energy " << std::setprecision(12) << totenergy
1591 [
this, &H0, &vxcpotential](
1592 const Eigen::MatrixXd& alpha_new,
1597 constexpr double kIntegralError = 1
e-8;
1599 std::array<Eigen::MatrixXd, 2> both_alpha_new =
CalcERIs_EXX(
1600 Eigen::MatrixXd::Zero(0, 0), alpha_new, kIntegralError);
1601 std::array<Eigen::MatrixXd, 2> both_beta_new =
CalcERIs_EXX(
1602 Eigen::MatrixXd::Zero(0, 0), beta_new, kIntegralError);
1603 Eigen::MatrixXd J_new = both_alpha_new[0] + both_beta_new[0];
1607 Eigen::MatrixXd D_total_new = alpha_new + beta_new;
1608 Eigen::MatrixXd J_new =
CalcERIs(D_total_new, kIntegralError);
1609 H_new.
alpha += J_new;
1610 H_new.
beta += J_new;
1613 H_new.
alpha += vxc_new.vxc_alpha;
1614 H_new.
beta += vxc_new.vxc_beta;
1618 Dspin = conv_uks.
Iterate(Dspin, Hspin, MOs_alpha, MOs_beta, totenergy);
1620 MOs_beta = MOs_alpha;
1638 Index nuclear_charge = 0;
1640 nuclear_charge += atom.getNuccharge();
1643 CanonicalizeOrbitalPhases(MOs_alpha);
1644 CanonicalizeOrbitalPhases(MOs_beta);
1647 orb.
MOs() = MOs_alpha;
1658 <<
TimeStamp() <<
" UKS converged after " << this_iter + 1
1659 <<
" iterations. Delta E=" << conv_uks.
getDeltaE()
1660 <<
" DIIS error=" << conv_uks.
getDIIsError() << std::flush;
1663 <<
TimeStamp() <<
" Final Single Point Energy "
1664 << std::setprecision(12) << totenergy <<
" Ha" << std::flush;
1666 <<
TimeStamp() << std::setprecision(12) <<
" Final XC contribution "
1667 << E_xc <<
" Ha" << std::flush;
1671 <<
" Final EXX contribution " << E_exx <<
" Ha" << std::flush;
1691 <<
TimeStamp() <<
" UKS calculation has not converged after "
1692 <<
max_iter_ <<
" iterations." << std::flush;
1714 <<
TimeStamp() <<
" Filled DFT Kinetic energy matrix ." << std::flush;
1719 <<
TimeStamp() <<
" Filled DFT nuclear potential matrix." << std::flush;
1721 Eigen::MatrixXd H0 = dftAOkinetic.
Matrix() + dftAOESP.
Matrix();
1723 <<
TimeStamp() <<
" Constructed independent particle hamiltonian "
1727 << std::setprecision(9) << E0 << std::flush;
1734 <<
TimeStamp() <<
" Filled DFT ECP matrix" << std::flush;
1739 <<
" External sites" << std::flush;
1740 bool has_quadrupoles = std::any_of(
1742 [](
const std::unique_ptr<StaticSite>& s) { return s->getRank() == 2; });
1743 std::string header =
1744 " Name Coordinates[a0] charge[e] dipole[e*a0] ";
1745 if (has_quadrupoles) {
1746 header +=
" quadrupole[e*a0^2]";
1751 for (
const std::unique_ptr<StaticSite>& site : *
externalsites_) {
1752 if (counter == limit) {
1755 std::string output =
1756 (boost::format(
" %1$s"
1757 " %2$+1.4f %3$+1.4f %4$+1.4f"
1759 site->getElement() % site->getPos()[0] % site->getPos()[1] %
1760 site->getPos()[2] % site->getCharge())
1762 const Eigen::Vector3d& dipole = site->getDipole();
1763 output += (boost::format(
" %1$+1.4f %2$+1.4f %3$+1.4f") % dipole[0] %
1764 dipole[1] % dipole[2])
1766 if (site->getRank() > 1) {
1767 Eigen::VectorXd quadrupole = site->Q().tail<5>();
1769 (boost::format(
" %1$+1.4f %2$+1.4f %3$+1.4f %4$+1.4f %5$+1.4f") %
1770 quadrupole[0] % quadrupole[1] % quadrupole[2] % quadrupole[3] %
1777 if (counter == limit) {
1780 <<
" sites not displayed)\n"
1787 <<
TimeStamp() <<
" Nuclei-external site interaction energy "
1788 << std::setprecision(9) << ext_multipoles.
energy() << std::flush;
1789 E0 += ext_multipoles.
energy();
1790 H0 += ext_multipoles.
matrix();
1797 E0 += extdensity_result.
energy();
1799 <<
TimeStamp() <<
" Nuclei-external density interaction energy "
1800 << std::setprecision(9) << extdensity_result.
energy() << std::flush;
1801 H0 += extdensity_result.
matrix();
1807 <<
TimeStamp() <<
" Integrating external electric field with F[Hrt]="
1821 <<
TimeStamp() <<
" Filled DFT Overlap matrix." << std::flush;
1838 <<
TimeStamp() <<
" Inverted AUX Coulomb matrix, removed "
1839 <<
ERIs_.Removedfunctions() <<
" functions from aux basis"
1843 <<
" Setup invariant parts of Electron Repulsion integrals "
1847 <<
TimeStamp() <<
" Calculating 4c diagonals. " << std::flush;
1850 <<
TimeStamp() <<
" Calculated 4c diagonals. " << std::flush;
1884std::optional<std::pair<Index, Index>> HundsRuleAlphaBetaElectrons(
1885 Index nuclear_charge) {
1886 switch (nuclear_charge) {
1888 return std::make_pair(1, 0);
1890 return std::make_pair(1, 1);
1892 return std::make_pair(2, 1);
1894 return std::make_pair(2, 2);
1896 return std::make_pair(3, 2);
1898 return std::make_pair(4, 2);
1900 return std::make_pair(5, 2);
1902 return std::make_pair(5, 3);
1904 return std::make_pair(5, 4);
1906 return std::make_pair(5, 5);
1908 return std::make_pair(6, 5);
1910 return std::make_pair(6, 6);
1912 return std::make_pair(7, 6);
1914 return std::make_pair(8, 6);
1916 return std::make_pair(9, 6);
1918 return std::make_pair(9, 7);
1920 return std::make_pair(9, 8);
1922 return std::make_pair(9, 9);
1924 return std::make_pair(10, 9);
1926 return std::make_pair(10, 10);
1929 return std::make_pair(16, 15);
1931 return std::make_pair(17, 15);
1933 return std::make_pair(18, 15);
1935 return std::make_pair(18, 16);
1937 return std::make_pair(18, 17);
1939 return std::make_pair(18, 18);
1942 return std::make_pair(25, 24);
1944 return std::make_pair(26, 24);
1946 return std::make_pair(27, 24);
1948 return std::make_pair(27, 25);
1950 return std::make_pair(27, 26);
1952 return std::make_pair(27, 27);
1954 return std::nullopt;
1960 const QMAtom& uniqueAtom,
bool use_hunds_rule_occupation)
const {
1972 dftbasis.
Fill(basisset, atom);
1982 ecp.
Fill(ecps, atom);
2000 if (use_hunds_rule_occupation) {
2001 auto hunds_rule = HundsRuleAlphaBetaElectrons(numofelectrons);
2002 if (hunds_rule.has_value()) {
2003 alpha_e = hunds_rule->first;
2004 beta_e = hunds_rule->second;
2008 <<
" No Hund's-rule ground-state occupation table "
2009 "entry for nuclear charge "
2011 <<
" (d/f-block elements are not covered -- see "
2012 "HundsRuleAlphaBetaElectrons's own comment for why) -- "
2013 "falling back to the simpler, parity-based alpha/beta split."
2015 use_hunds_rule_occupation =
false;
2018 if (!use_hunds_rule_occupation) {
2019 if ((numofelectrons % 2) != 0) {
2020 alpha_e = numofelectrons / 2 + numofelectrons % 2;
2021 beta_e = numofelectrons / 2;
2023 alpha_e = numofelectrons / 2;
2034 dftAOoverlap.
Fill(dftbasis);
2035 dftAOkinetic.
Fill(dftbasis);
2085 conv_uks.
Configure(opt_alpha, opt_beta);
2089 Eigen::MatrixXd H0 = dftAOkinetic.
Matrix() + dftAOESP.
Matrix();
2106 return Dspin_H.
alpha;
2114 for (
Index this_iter = 0; this_iter < maxiter; this_iter++) {
2115 Eigen::MatrixXd H_alpha = H0;
2116 Eigen::MatrixXd H_beta = H0;
2118 double E_coul = 0.0;
2124 double integral_error = std::min(conv_uks.
getDIIsError() * 1
e-5, 1
e-5);
2127 std::array<Eigen::MatrixXd, 2> both_alpha =
2129 std::array<Eigen::MatrixXd, 2> both_beta =
2132 Eigen::MatrixXd Hartree = both_alpha[0] + both_beta[0];
2133 H_alpha += Hartree +
ScaHFX_ * both_alpha[1];
2134 H_beta += Hartree +
ScaHFX_ * both_beta[1];
2136 E_coul = 0.5 * Dspin.
total().cwiseProduct(Hartree).sum();
2138 (both_alpha[1].cwiseProduct(Dspin.
alpha).sum() +
2139 both_beta[1].cwiseProduct(Dspin.
beta).sum());
2141 Eigen::MatrixXd Hartree =
2145 E_coul = 0.5 * Dspin.
total().cwiseProduct(Hartree).sum();
2150 H_beta += vxc.vxc_beta;
2153 double E_one_alpha = Dspin.
alpha.cwiseProduct(H0).sum();
2154 double E_one_beta = Dspin.
beta.cwiseProduct(H0).sum();
2155 double totenergy = E_one_alpha + E_one_beta + E_coul + E_exx + E_xc;
2158 Dspin = conv_uks.
Iterate(Dspin, Hspin, MOs_alpha, MOs_beta, totenergy);
2161 <<
TimeStamp() <<
" Iter " << this_iter <<
" of " << maxiter <<
" Etot "
2162 << totenergy <<
" diise " << conv_uks.
getDIIsError() <<
"\n\t\t a_gap "
2167 <<
" Nalpha=" << dftAOoverlap.
Matrix().cwiseProduct(Dspin.
alpha).sum()
2168 <<
" Nbeta=" << dftAOoverlap.
Matrix().cwiseProduct(Dspin.
beta).sum()
2172 if (converged || this_iter == maxiter - 1) {
2175 <<
TimeStamp() <<
" Converged after " << this_iter + 1
2176 <<
" iterations" << std::flush;
2179 <<
TimeStamp() <<
" Not converged after " << this_iter + 1
2180 <<
" iterations. Unconverged density.\n\t\t\t"
2181 <<
" DIIsError=" << conv_uks.
getDIIsError() << std::flush;
2190 <<
" gives N=" << std::setprecision(9)
2191 << avgmatrix.cwiseProduct(dftAOoverlap.
Matrix()).sum() <<
" electrons."
2200 <<
TimeStamp() <<
" Scanning molecule of size " << mol.
size()
2201 <<
" for unique elements" << std::flush;
2203 for (
auto element : elements) {
2204 uniqueelements.
push_back(
QMAtom(0, element, Eigen::Vector3d::Zero()));
2208 <<
" unique elements found" << std::flush;
2209 std::vector<Eigen::MatrixXd> uniqueatom_guesses;
2210 for (
QMAtom& unique_atom : uniqueelements) {
2212 <<
TimeStamp() <<
" Calculating atom density for "
2213 << unique_atom.getElement() << std::flush;
2215 uniqueatom_guesses.push_back(dmat_unrestricted);
2218 Eigen::MatrixXd guess =
2221 for (
const QMAtom& atom : mol) {
2223 for (; index < uniqueelements.
size(); index++) {
2224 if (atom.getElement() == uniqueelements[index].getElement()) {
2228 Eigen::MatrixXd& dmat_unrestricted = uniqueatom_guesses[index];
2229 guess.block(start, start, dmat_unrestricted.rows(),
2230 dmat_unrestricted.cols()) = dmat_unrestricted;
2231 start += dmat_unrestricted.rows();
2237std::map<std::string, Eigen::MatrixXd>
2241 <<
TimeStamp() <<
" Scanning molecule of size " << mol.
size()
2242 <<
" for unique elements (Hirshfeld reference densities)" << std::flush;
2244 std::map<std::string, Eigen::MatrixXd> reference_densities;
2245 for (
const std::string& element : elements) {
2246 QMAtom unique_atom(0, element, Eigen::Vector3d::Zero());
2248 <<
TimeStamp() <<
" Calculating Hirshfeld reference density for "
2249 << element << std::flush;
2256 return reference_densities;
2261 std::map<std::string, Eigen::MatrixXd> reference_densities =
2268 full_dftbasis.
Fill(basisset, mol);
2274 std::vector<HirshfeldPartition::AtomicReference> atoms =
2276 reference_densities);
2281 double neutral_reference_population = 0.0;
2283 if (atom_index < 0 || atom_index >=
static_cast<Index>(mol.
size())) {
2284 throw std::runtime_error(
2285 "BuildCDFTConstraint: cdft.indices contains atom index " +
2286 std::to_string(atom_index) +
", but this molecule only has " +
2287 std::to_string(mol.
size()) +
2288 " atoms (0-based indexing -- valid range is 0.." +
2289 std::to_string(mol.
size() - 1) +
").");
2299 atoms, atom_index, full_dftbasis, grid);
2300 neutral_reference_population +=
2301 static_cast<double>(mol[atom_index].getNuccharge());
2312 <<
" atom(s), neutral reference population="
2313 << neutral_reference_population
2326 throw std::runtime_error(
2327 (boost::format(
"Basisset Name in guess orb file "
2328 "and in dftengine option file differ %1% vs %2%") %
2336 "Orbital file has no basisset information,"
2337 "using it as a guess might work or not for calculation with "
2362 throw std::runtime_error(
2363 (boost::format(
"ECPs in orb file: %1% and options %2% differ") %
2370 throw std::runtime_error(
2371 (boost::format(
"Number of electrons in guess orb file "
2372 "and in dftengine differ: "
2373 "alpha %1% vs %2%, beta %3% vs %4%.") %
2379 throw std::runtime_error(
2380 (boost::format(
"Number of levels in guess orb file: "
2381 "%1% and in dftengine: %2% differ.") %
2401 <<
TimeStamp() <<
" Using MKL overload for Eigen " << std::flush;
2405 <<
" Using native Eigen implementation, no BLAS overload "
2410 for (
const QMAtom& atom : mol) {
2412 std::string output = (boost::format(
" %1$s"
2413 " %2$+1.4f %3$+1.4f %4$+1.4f") %
2414 atom.getElement() % pos[0] % pos[1] % pos[2])
2425 <<
dftbasis_.AOBasisSize() <<
" functions" << std::flush;
2433 <<
auxbasis_.AOBasisSize() <<
" functions" << std::flush;
2441 std::vector<std::string> results =
ecp_.Fill(ecpbasisset, mol);
2443 <<
TimeStamp() <<
" Filled ECP Basis" << std::flush;
2444 if (results.size() > 0) {
2445 std::string message =
"";
2446 for (
const std::string& element : results) {
2447 message +=
" " + element;
2450 <<
TimeStamp() <<
" Found no ECPs for elements" << message
2461 Index nuclear_charge = 0;
2462 for (
const QMAtom& atom : mol) {
2463 nuclear_charge += atom.getNuccharge();
2469 if (multiplicity < 1) {
2470 throw std::runtime_error(
"Spin multiplicity must be >= 1.");
2473 if (numofelectrons >= 0) {
2479 Index spin_excess = multiplicity - 1;
2482 throw std::runtime_error(
"Computed a negative number of electrons.");
2486 throw std::runtime_error(
2487 "Spin multiplicity incompatible with total number of electrons.");
2491 throw std::runtime_error(
2492 "Charge and spin multiplicity imply non-integer alpha/beta "
2504 <<
" (charge=" << target_charge <<
", multiplicity=" << multiplicity
2530 <<
" divided into " << grid.
getBoxesSize() <<
" boxes" << std::flush;
2535 double E_nucnuc = 0.0;
2537 for (
Index i = 0; i < mol.
size(); i++) {
2538 const Eigen::Vector3d& r1 = mol[i].
getPos();
2539 double charge1 = double(mol[i].getNuccharge());
2540 for (
Index j = 0; j < i; j++) {
2541 const Eigen::Vector3d& r2 = mol[j].
getPos();
2542 double charge2 = double(mol[j].getNuccharge());
2543 E_nucnuc += charge1 * charge2 / (r1 - r2).norm();
2551 const Eigen::MatrixXd& dmat,
const AOBasis& dftbasis)
const {
2552 Eigen::MatrixXd avdmat = Eigen::MatrixXd::Zero(dmat.rows(), dmat.cols());
2553 for (
const AOShell& shellrow : dftbasis) {
2554 Index size_row = shellrow.getNumFunc();
2555 Index start_row = shellrow.getStartIndex();
2556 for (
const AOShell& shellcol : dftbasis) {
2557 Index size_col = shellcol.getNumFunc();
2558 Index start_col = shellcol.getStartIndex();
2559 Eigen::MatrixXd shelldmat =
2560 dmat.block(start_row, start_col, size_row, size_col);
2561 if (shellrow.getL() == shellcol.getL()) {
2562 double diagavg = shelldmat.diagonal().sum() / double(shelldmat.rows());
2563 Index offdiagelements =
2564 shelldmat.rows() * shelldmat.cols() - shelldmat.cols();
2565 double offdiagavg = (shelldmat.sum() - shelldmat.diagonal().sum()) /
2566 double(offdiagelements);
2567 avdmat.block(start_row, start_col, size_row, size_col).array() =
2569 avdmat.block(start_row, start_col, size_row, size_col)
2573 double avg = shelldmat.sum() / double(shelldmat.size());
2574 avdmat.block(start_row, start_col, size_row, size_col).array() = avg;
2583 const std::vector<std::unique_ptr<StaticSite>>& multipoles)
const {
2585 if (multipoles.size() == 0) {
2591 for (
const QMAtom& atom : mol) {
2593 for (
const std::unique_ptr<StaticSite>& site : *
externalsites_) {
2594 if ((site->getPos() - nucleus.
getPos()).norm() < 1
e-7) {
2596 <<
" External site sits on nucleus, "
2597 "interaction between them is ignored."
2612 Eigen::MatrixXd result =
2614 for (
Index i = 0; i < 3; i++) {
2622 const std::vector<std::unique_ptr<StaticSite>>& multipoles)
const {
2629 <<
TimeStamp() <<
" Filled DFT external multipole potential matrix"
2650 <<
TimeStamp() <<
" Calculated external density" << std::flush;
2653 <<
TimeStamp() <<
" Calculated potential from electron density"
2658 double nuc_energy = 0.0;
2659 for (
const QMAtom& atom : mol) {
2663 const double dist = (atom.getPos() - extatom.getPos()).norm();
2665 double(atom.getNuccharge()) * double(extatom.getNuccharge()) / dist;
2669 <<
TimeStamp() <<
" Calculated potential from nuclei" << std::flush;
2671 <<
TimeStamp() <<
" Electrostatic: " << nuc_energy << std::flush;
2676 const Eigen::MatrixXd& GuessMOs)
const {
2677 Eigen::MatrixXd nonortho =
2679 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(nonortho);
2680 Eigen::MatrixXd result = GuessMOs * es.operatorInverseSqrt();
2693 Eigen::VectorXd eps = Eigen::VectorXd::Zero(nao);
2697 int l =
static_cast<int>(shell.getL());
2698 Index start = shell.getStartIndex();
2699 Index nfunc = shell.getNumFunc();
2701 const QMAtom& atom = mol[shell.getAtomIndex()];
2702 const std::string& element = atom.
getElement();
2707 for (
Index i = 0; i < nfunc; ++i) {
2718 const Index nao =
S.rows();
2720 Eigen::MatrixXd
H = Eigen::MatrixXd::Zero(nao, nao);
2721 constexpr double K = 1.75;
2723 for (
Index mu = 0; mu < nao; ++mu) {
2724 H(mu, mu) = eps(mu);
2725 for (
Index nu = 0; nu < mu; ++nu) {
2726 double hij = K *
S(mu, nu) * 0.5 * (eps(mu) + eps(nu));
2738 <<
TimeStamp() <<
" Building Extended Huckel guess" << std::flush;
2743 <<
TimeStamp() <<
" Solving EHT generalized eigenproblem" << std::flush;
2761 std::array<Eigen::MatrixXd, 2> both =
2789 if (nA + nB != dimer_mol.
size()) {
2790 throw std::runtime_error(
2791 "BuildDimerGuessFromMonomerFiles: monomer A (" + std::to_string(nA) +
2792 " atoms) + monomer B (" + std::to_string(nB) +
2793 " atoms) does not equal this calculation's own molecule (" +
2794 std::to_string(dimer_mol.
size()) +
2795 " atoms) -- wrong monomer file(s), or this calculation's molecule "
2796 "is not simply the concatenation of these two monomers.");
2798 for (
Index i = 0; i < nA; ++i) {
2799 if (atomsA[i].getElement() != dimer_mol[i].getElement()) {
2800 throw std::runtime_error(
2801 "BuildDimerGuessFromMonomerFiles: monomer A's own atom " +
2802 std::to_string(i) +
" (" + atomsA[i].getElement() +
2803 ") does not match this calculation's own atom " + std::to_string(i) +
2804 " (" + dimer_mol[i].getElement() +
2805 ") -- dimer_guess assumes monomer A occupies exactly the first "
2806 "N_A atoms of this calculation's molecule, in the same order.");
2809 for (
Index i = 0; i < nB; ++i) {
2810 if (atomsB[i].getElement() != dimer_mol[nA + i].getElement()) {
2811 throw std::runtime_error(
2812 "BuildDimerGuessFromMonomerFiles: monomer B's own atom " +
2813 std::to_string(i) +
" (" + atomsB[i].getElement() +
2814 ") does not match this calculation's own atom " +
2815 std::to_string(nA + i) +
" (" + dimer_mol[nA + i].getElement() +
2816 ") -- dimer_guess assumes monomer B occupies exactly the "
2817 "remaining atoms of this calculation's molecule (after monomer "
2818 "A's own N_A atoms), in the same order.");
2831 constexpr double kGeometryToleranceBohr = 1
e-3;
2832 auto CheckInternalGeometry = [&](
const QMMolecule& monomer_atoms,
2833 Index offset_in_dimer,
2834 const std::string& label) {
2836 for (
Index i = 0; i < n; ++i) {
2837 for (
Index j = i + 1; j < n; ++j) {
2838 double monomer_distance =
2839 (monomer_atoms[i].
getPos() - monomer_atoms[j].getPos()).norm();
2840 double dimer_distance = (dimer_mol[offset_in_dimer + i].
getPos() -
2841 dimer_mol[offset_in_dimer + j].getPos())
2843 double diff = std::abs(monomer_distance - dimer_distance);
2844 if (diff > kGeometryToleranceBohr) {
2845 throw std::runtime_error(
2846 "BuildDimerGuessFromMonomerFiles: " + label +
2847 "'s own internal geometry does not match this calculation's "
2848 "molecule -- distance between its own atoms " +
2849 std::to_string(i) +
" and " + std::to_string(j) +
" is " +
2850 std::to_string(monomer_distance) +
2851 " Bohr in the monomer file, but " +
2852 std::to_string(dimer_distance) +
2853 " Bohr in this calculation's own molecule (difference " +
2854 std::to_string(diff) +
" Bohr, tolerance " +
2855 std::to_string(kGeometryToleranceBohr) +
2856 " Bohr). This is checked as an INTERNAL, translation/"
2857 "rotation-invariant distance specifically because the "
2858 "monomer's absolute position/orientation is expected to "
2859 "differ between its own standalone optimization and its "
2860 "placement in the dimer -- only its internal geometry "
2861 "should still match.");
2866 CheckInternalGeometry(atomsA, 0,
"Monomer A");
2867 CheckInternalGeometry(atomsB, nA,
"Monomer B");
2876 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)
void Fill(const AOBasis &aobasis) final
const Eigen::MatrixXd & Matrix() const
void FillPotential(const AOBasis &aobasis, const QMMolecule &atoms)
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.
Index num_beta_electrons_
Orbitals BuildDimerGuessFromMonomerFiles(const QMMolecule &dimer_mol) const
double cdft_population_tolerance_
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.
std::string dftbasis_name_
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
void Prepare(Orbitals &orb, Index numofelectrons=-1)
std::string initial_guess_
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_
std::array< Eigen::MatrixXd, 2 > CalcERIs_EXX(const Eigen::MatrixXd &MOCoeff, const Eigen::MatrixXd &Dmat, double error) const
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
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< HirshfeldPartition::Constraint > constraints_
void PrintMOs(const Eigen::VectorXd &MOEnergies, Log::Level level)
Print a one-spin list of orbital energies and occupations to the logger.
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)
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)
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
double GetWithFallback(const std::string &element, int l, int *used_l=nullptr) const
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
Logger is used for thread-safe output of messages.
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.
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.
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.
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)
const std::string & getElement() const
Index getNuccharge() const
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
Class to represent Atom/Site in electrostatic.
const Eigen::Vector3d & getPos() const
Timestamp returns the current time as a string Example: cout << TimeStamp().
tools::EigenSystem SolveFockmatrix(const Eigen::MatrixXd &H) const
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 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)
static double getExactExchange(const std::string &functional)
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.
bool XTP_HAS_MKL_OVERLOAD()
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::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
Eigen::MatrixXd weight_matrix
double spin_alpha_coefficient
double spin_beta_coefficient
Eigen::MatrixXd total() const
Eigen::MatrixXd vxc_alpha