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>();
220 throw std::runtime_error(
221 "initial_guess=dimer_guess requires both dimer_guess_orbA and "
222 "dimer_guess_orbB to be set to real monomer .orb file paths.");
226 grid_name_ = options.
get(key_xtpdft +
".integration_grid").
as<std::string>();
229 if (options.
exists(key_xtpdft +
".externaldensity")) {
232 options.
get(key_xtpdft +
".externaldensity.orbfile").
as<std::string>();
233 gridquality_ = options.
get(key_xtpdft +
".externaldensity.gridquality")
236 options.
get(key_xtpdft +
".externaldensity.state").
as<std::string>();
239 if (options.
exists(
".externalfield")) {
245 options.
get(key_xtpdft +
".convergence.energy").
as<
double>();
247 options.
get(key_xtpdft +
".convergence.error").
as<
double>();
249 options.
get(key_xtpdft +
".convergence.max_iterations").
as<
Index>();
252 options.
get(key_xtpdft +
".convergence.method").
as<std::string>();
253 if (method ==
"DIIS") {
255 }
else if (method ==
"mixing") {
263 options.
get(key_xtpdft +
".convergence.mixing").
as<
double>();
267 options.
get(key_xtpdft +
".convergence.mixing_max").
as<
double>();
269 options.
get(key_xtpdft +
".convergence.levelshift").
as<
double>();
271 options.
get(key_xtpdft +
".convergence.levelshift_end").
as<
double>();
276 options.
get(key_xtpdft +
".convergence.mixing_end").
as<
double>();
278 options.
get(key_xtpdft +
".convergence.DIIS_maxout").
as<
bool>();
280 options.
get(key_xtpdft +
".convergence.DIIS_length").
as<
Index>();
282 options.
get(key_xtpdft +
".convergence.DIIS_start").
as<
double>();
284 options.
get(key_xtpdft +
".convergence.ADIIS_start").
as<
double>();
286 if (options.
exists(key_xtpdft +
".dft_in_dft.activeatoms")) {
288 options.
get(key_xtpdft +
".dft_in_dft.activeatoms").
as<std::string>();
290 options.
get(key_xtpdft +
".dft_in_dft.threshold").
as<
double>();
292 options.
get(key_xtpdft +
".dft_in_dft.levelshift").
as<
double>();
294 options.
get(key_xtpdft +
".dft_in_dft.truncate_basis").
as<
bool>();
297 options.
get(key_xtpdft +
".dft_in_dft.truncation_threshold")
304 XTP_LOG(level, *
pLog_) <<
" Orbital energies: " << std::flush;
305 XTP_LOG(level, *
pLog_) <<
" index occupation energy(Hartree) " << std::flush;
307 for (
Index i = 0; i < MOEnergies.size(); ++i) {
315 XTP_LOG(level, *
pLog_) << (boost::format(
" %1$5d %2$1d %3$+1.10f") %
316 i % occupancy % MOEnergies(i))
324 const Eigen::VectorXd& beta_energies,
326 XTP_LOG(level, *
pLog_) <<
" UKS orbital energies:" << std::flush;
327 XTP_LOG(level, *
pLog_) <<
" index occ eps_a(Ha) eps_b(Ha)"
331 std::max<Index>(alpha_energies.size(), beta_energies.size());
333 for (
Index i = 0; i < nrows; ++i) {
337 std::string occ =
"0";
338 if (occ_a && occ_b) {
346 std::string eps_a =
" -";
347 std::string eps_b =
" -";
349 if (i < alpha_energies.size()) {
350 eps_a = (boost::format(
"%+1.10f") % alpha_energies(i)).str();
352 if (i < beta_energies.size()) {
353 eps_b = (boost::format(
"%+1.10f") % beta_energies(i)).str();
357 " %1$5d %2$1s %3$15s %4$15s") %
358 i % occ % eps_a % eps_b)
366 " alpha HOMO-LUMO gap: %+1.10f Ha") %
375 " beta HOMO-LUMO gap: %+1.10f Ha") %
387 <<
TimeStamp() <<
" Electric Dipole is[e*bohr]:\n\t\t dx=" << result[0]
388 <<
"\n\t\t dy=" << result[1] <<
"\n\t\t dz=" << result[2] << std::flush;
422 Orbitals& orb,
const Eigen::MatrixXd& Dmat,
427 <<
" Skipping force calculation: RI-J gradient (DFTGradient::"
428 "RIJGradient) only implements the RI path, but this SCF ran "
429 "without an auxiliary basis (conventional 4-center ERIs)."
437 <<
" Skipping force calculation: the libint2 this was built "
438 "against does not support derivative integrals for one or "
439 "more operator categories it needs. Many pre-packaged "
440 "libint2 builds (Homebrew, Ubuntu apt, etc.) do not enable "
441 "this by default -- rebuild libint2 with "
442 "--enable-1body/--enable-eri2/--enable-eri3 to use analytic "
463 <<
"') was used for this SCF, but analytic nuclear forces do "
464 "not yet include the ECP contribution to the force "
465 "(d(V_ECP)/dR) -- computing forces in this configuration "
466 "would silently omit that term rather than fail visibly."
486 std::vector<AOMatrixDerivative> dVne =
488 Eigen::MatrixXd eone_grad = Eigen::MatrixXd::Zero(natoms, 3);
489 for (
Index a = 0; a < natoms; ++a) {
490 for (
Index xyz = 0; xyz < 3; ++xyz) {
491 eone_grad(a, xyz) = Dmat.cwiseProduct(dT[a][xyz] + dVne[a][xyz]).sum();
524 Eigen::MatrixXd W = 2.0 * C_occ * eps_occ.asDiagonal() * C_occ.transpose();
527 Eigen::MatrixXd overlap_pulay_grad = Eigen::MatrixXd::Zero(natoms, 3);
528 for (
Index a = 0; a < natoms; ++a) {
529 for (
Index xyz = 0; xyz < 3; ++xyz) {
530 overlap_pulay_grad(a, xyz) = -W.cwiseProduct(dS[a][xyz]).sum();
534 Eigen::MatrixXd rij_term =
540 Eigen::MatrixXd grad = nucrep_term + eone_grad + overlap_pulay_grad +
541 rij_term + pulay_term + weight_term;
571 Eigen::Vector3d sum = grad.colwise().sum();
572 if (sum.cwiseAbs().maxCoeff() > 1
e-4) {
575 <<
" WARNING: computed forces do not sum to zero across atoms "
576 "(translational invariance check failed, max component="
577 << sum.cwiseAbs().maxCoeff()
578 <<
") -- treat these forces with "
590 Eigen::MatrixXd force = -grad;
594 <<
TimeStamp() <<
" Computed and stored ground-state nuclear forces."
603 for (
Index a = 0; a < natoms; ++a) {
605 (boost::format(
" %1$s"
606 " %2$+1.6f %3$+1.6f %4$+1.6f") %
607 mol[a].getElement() % force(a, 0) % force(a, 1) % force(a, 2))
642 Eigen::MatrixXd C_alpha_occ = MOs_alpha.
eigenvectors().leftCols(n_occ_alpha);
643 Eigen::MatrixXd C_beta_occ = MOs_beta.
eigenvectors().leftCols(n_occ_beta);
644 Eigen::VectorXd eps_alpha_occ = MOs_alpha.
eigenvalues().head(n_occ_alpha);
645 Eigen::VectorXd eps_beta_occ = MOs_beta.
eigenvalues().head(n_occ_beta);
647 C_alpha_occ * eps_alpha_occ.asDiagonal() * C_alpha_occ.transpose() +
648 C_beta_occ * eps_beta_occ.asDiagonal() * C_beta_occ.transpose();
652 Eigen::MatrixXd overlap_pulay_grad = Eigen::MatrixXd::Zero(natoms, 3);
653 for (
Index a = 0; a < natoms; ++a) {
654 for (
Index xyz = 0; xyz < 3; ++xyz) {
655 overlap_pulay_grad(a, xyz) = -W.cwiseProduct(dS[a][xyz]).sum();
658 return overlap_pulay_grad;
666 const Eigen::MatrixXd D_total = Dspin.
total();
675 std::vector<AOMatrixDerivative> dVne =
677 Eigen::MatrixXd eone_grad = Eigen::MatrixXd::Zero(natoms, 3);
678 for (
Index a = 0; a < natoms; ++a) {
679 for (
Index xyz = 0; xyz < 3; ++xyz) {
680 eone_grad(a, xyz) = D_total.cwiseProduct(dT[a][xyz] + dVne[a][xyz]).sum();
684 Eigen::MatrixXd overlap_pulay_grad =
687 Eigen::MatrixXd grad =
703 Eigen::MatrixXd C_alpha_occ =
705 Eigen::MatrixXd C_beta_occ =
721 <<
" Skipping UKS force calculation: RI-J gradient only "
722 "implements the RI path, but this SCF ran without an "
731 <<
" Skipping UKS force calculation: the libint2 this was "
732 "built against does not support derivative integrals for "
733 "one or more operator categories it needs. Many "
734 "pre-packaged libint2 builds (Homebrew, Ubuntu apt, etc.) "
735 "do not enable this by default -- rebuild libint2 with "
736 "--enable-1body/--enable-eri2/--enable-eri3 to use "
748 <<
TimeStamp() <<
" Skipping UKS force calculation: an ECP ('"
750 <<
"') was used for this SCF, but analytic nuclear forces do "
751 "not yet include the ECP contribution to the force "
752 "(d(V_ECP)/dR) -- computing forces in this configuration "
753 "would silently omit that term rather than fail visibly."
758 Eigen::MatrixXd grad =
774 Eigen::Vector3d sum = grad.colwise().sum();
775 if (sum.cwiseAbs().maxCoeff() > 1
e-4) {
778 <<
" WARNING: computed UKS forces do not sum to zero across "
779 "atoms (translational invariance check failed, max "
781 << sum.cwiseAbs().maxCoeff()
782 <<
") -- treat these forces with "
791 Eigen::MatrixXd force = -grad;
795 <<
TimeStamp() <<
" Computed and stored ground-state UKS nuclear forces."
801 for (
Index a = 0; a < force.rows(); ++a) {
802 std::string output = (boost::format(
" %1$s"
803 " %2$+1.6f %3$+1.6f %4$+1.6f") %
804 mol_for_print[a].getElement() % force(a, 0) %
805 force(a, 1) % force(a, 2))
820 const Eigen::MatrixXd& MOCoeff,
const Eigen::MatrixXd& Dmat,
821 double error)
const {
824 return ERIs_.CalculateERIs_EXX_3c(Eigen::MatrixXd::Zero(0, 0), Dmat);
827 return ERIs_.CalculateERIs_EXX_3c(occblock, Dmat);
830 return ERIs_.CalculateERIs_EXX_4c(Dmat, error);
837 double error)
const {
839 return ERIs_.CalculateERIs_3c(Dmat);
841 return ERIs_.CalculateERIs_4c(Dmat, error);
862 <<
TimeStamp() <<
" Filled DFT Vxc matrix " << std::flush;
867 std::array<Eigen::MatrixXd, 2> both =
889 bool converged =
RunCDFT(orb, constraint);
912 std::map<std::string, Eigen::MatrixXd> reference_densities =
922 std::vector<HirshfeldPartition::AtomicReference> atoms =
926 std::array<Eigen::MatrixXd, 2> Dspin =
932 Eigen::MatrixXd density_total = Dspin[0] + Dspin[1];
934 Eigen::MatrixXd cdft_gradient_correction =
937 cdft_gradient_correction +=
939 atoms, atom_index, density_total, orb.
QMAtoms(), full_dftbasis,
949 constraint.
lambda * cdft_gradient_correction);
963 <<
" Forcing closed-shell singlet through UKS development path."
998 double lambda_lo = constraint.
lambda - 0.1;
999 double lambda_hi = constraint.
lambda + 0.1;
1001 auto EvaluateMismatch = [&](
double lambda) ->
double {
1003 bool scf_converged =
EvaluateUKS(orb, H0, vxcpotential);
1004 if (!scf_converged) {
1005 throw std::runtime_error(
1006 "RunCDFT: inner SCF did not converge at lambda=" +
1007 std::to_string(lambda));
1010 std::array<Eigen::MatrixXd, 2> Dspin =
1021 double mismatch_lo = EvaluateMismatch(lambda_lo);
1022 double mismatch_hi = EvaluateMismatch(lambda_hi);
1024 Index bracket_attempts = 0;
1025 constexpr Index kMaxBracketAttempts = 10;
1026 while (mismatch_lo * mismatch_hi > 0.0 &&
1027 bracket_attempts < kMaxBracketAttempts) {
1028 double width = lambda_hi - lambda_lo;
1029 lambda_lo -= 0.5 * width;
1030 lambda_hi += 0.5 * width;
1031 mismatch_lo = EvaluateMismatch(lambda_lo);
1032 mismatch_hi = EvaluateMismatch(lambda_hi);
1035 if (mismatch_lo * mismatch_hi > 0.0) {
1038 <<
" RunCDFT: could not bracket a root for the population "
1040 << kMaxBracketAttempts
1041 <<
" bracket-expansion attempts -- the target population may "
1042 "be unreachable for this system, or the initial "
1043 "lambda guess may be far from the actual root."
1052 double lambda_mid = 0.5 * (lambda_lo + lambda_hi);
1053 double mismatch_mid = EvaluateMismatch(lambda_mid);
1056 <<
TimeStamp() <<
" CDFT outer iteration " << outer_iter + 1 <<
" of "
1058 <<
" population mismatch=" << mismatch_mid << std::flush;
1061 constraint.
lambda = lambda_mid;
1064 <<
TimeStamp() <<
" CDFT converged after " << outer_iter + 1
1065 <<
" outer iterations, lambda=" << lambda_mid << std::flush;
1069 if (mismatch_mid * mismatch_lo < 0.0) {
1070 lambda_hi = lambda_mid;
1071 mismatch_hi = mismatch_mid;
1073 lambda_lo = lambda_mid;
1074 mismatch_lo = mismatch_mid;
1077 }
catch (
const std::runtime_error&) {
1085 <<
" RunCDFT: outer bisection loop did not converge "
1088 constraint.
lambda = 0.5 * (lambda_lo + lambda_hi);
1109 <<
TimeStamp() <<
" Reading guess from orbitals object/file"
1126 throw std::runtime_error(
1127 "initial_guess=dimer_guess is only meaningful for a genuinely "
1128 "unrestricted (UKS) calculation -- it exists specifically to "
1129 "combine two monomers of independently arbitrary charge/spin, "
1130 "which by construction do not reduce to a single restricted "
1131 "MO set. Use force_uks_path or an odd total electron count to "
1132 "route through EvaluateUKS instead.");
1134 throw std::runtime_error(
"Initial guess method not known/implemented");
1140 Eigen::MatrixXd Dmat = spin_dmat.
total();
1143 <<
TimeStamp() <<
" Guess Matrix gives N=" << std::setprecision(9)
1144 << Dmat.cwiseProduct(
dftAOoverlap_.Matrix()).sum() <<
" electrons."
1148 <<
TimeStamp() <<
" STARTING SCF cycle" << std::flush;
1150 <<
" ----------------------------------------------"
1151 "----------------------------"
1154 Eigen::MatrixXd J = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
1157 K = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols());
1160 double start_incremental_F_threshold = 1
e-4;
1162 start_incremental_F_threshold = 0.0;
1175 <<
TimeStamp() <<
" Filled DFT Vxc matrix " << std::flush;
1178 double Eone = Dmat.cwiseProduct(H0.
matrix()).sum();
1179 double Etwo = e_vxc.
energy();
1187 double integral_error =
1195 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
1198 exx = 0.25 *
ScaHFX_ * Dmat.cwiseProduct(K).sum();
1200 <<
TimeStamp() <<
" Filled F+K matrix " << std::flush;
1204 <<
TimeStamp() <<
" Filled F matrix " << std::flush;
1206 Etwo += 0.5 * Dmat.cwiseProduct(J).sum();
1210 double totenergy = Eone + H0.
energy() + Etwo;
1213 << std::setprecision(12) << Eone << std::flush;
1215 << std::setprecision(12) << Etwo << std::flush;
1217 <<
TimeStamp() << std::setprecision(12) <<
" Local Exc contribution "
1218 << e_vxc.
energy() << std::flush;
1222 <<
" Non local Ex contribution " << exx << std::flush;
1225 <<
TimeStamp() <<
" Total Energy " << std::setprecision(12) << totenergy
1245 <<
TimeStamp() <<
" Total Energy has converged to "
1247 <<
"[Ha] after " << this_iter + 1
1248 <<
" iterations. DIIS error is converged up to "
1251 <<
TimeStamp() <<
" Final Single Point Energy "
1252 << std::setprecision(12) << totenergy <<
" Ha" << std::flush;
1254 <<
" Final Local Exc contribution "
1255 << e_vxc.
energy() <<
" Ha" << std::flush;
1258 <<
" Final Non Local Ex contribution "
1259 << exx <<
" Ha" << std::flush;
1264 Index nuclear_charge = 0;
1266 nuclear_charge += atom.getNuccharge();
1284 }
else if (this_iter ==
max_iter_ - 1) {
1286 <<
TimeStamp() <<
" DFT calculation has not converged after "
1288 <<
" iterations. Use more iterations or another convergence "
1289 "acceleration scheme."
1326 conv_uks.
Configure(opt_alpha, opt_beta);
1332 <<
TimeStamp() <<
" Reading UKS guess from orbitals object/file"
1335 MOs_alpha = orb.
MOs();
1344 <<
" Orbital file has no beta MOs, using alpha guess for beta."
1346 MOs_beta = MOs_alpha;
1351 <<
" Building UKS guess from two monomer .orb files (dimer_guess)"
1354 MOs_alpha = dimer_guess_orb.
MOs();
1356 MOs_beta = dimer_guess_orb.
MOs_beta();
1373 throw std::runtime_error(
"Initial guess method not known/implemented");
1386 <<
TimeStamp() <<
" UKS guess gives Nalpha="
1393 <<
TimeStamp() <<
" STARTING UKS SCF cycle" << std::flush;
1395 <<
" ------------------------------------------------------------"
1403 Eigen::MatrixXd H_alpha = H0.
matrix();
1404 Eigen::MatrixXd H_beta = H0.
matrix();
1408 const Eigen::MatrixXd D_total = Dspin.
total();
1410 double E_one = Dspin.
alpha.cwiseProduct(H0.
matrix()).sum() +
1413 double E_coul = 0.0;
1417 double integral_error = std::min(conv_uks.
getDIIsError() * 1
e-5, 1
e-5);
1420 std::array<Eigen::MatrixXd, 2> both_alpha =
CalcERIs_EXX(
1421 Eigen::MatrixXd::Zero(0, 0), Dspin.
alpha, integral_error);
1422 std::array<Eigen::MatrixXd, 2> both_beta =
1425 Eigen::MatrixXd J = both_alpha[0] + both_beta[0];
1426 Eigen::MatrixXd K_alpha = both_alpha[1];
1427 Eigen::MatrixXd K_beta = both_beta[1];
1429 H_alpha += J +
ScaHFX_ * K_alpha;
1430 H_beta += J +
ScaHFX_ * K_beta;
1432 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
1434 (Dspin.
alpha.cwiseProduct(K_alpha).sum() +
1435 Dspin.
beta.cwiseProduct(K_beta).sum());
1437 Eigen::MatrixXd J =
CalcERIs(D_total, integral_error);
1440 E_coul = 0.5 * D_total.cwiseProduct(J).sum();
1445 H_beta += vxc.vxc_beta;
1448 double totenergy = H0.
energy() + E_one + E_coul + E_xc + E_exx;
1467 H_alpha += (c.lambda * c.spin_alpha_coefficient) * c.weight_matrix;
1468 H_beta += (c.lambda * c.spin_beta_coefficient) * c.weight_matrix;
1470 c.spin_alpha_coefficient *
1471 Dspin.
alpha.cwiseProduct(c.weight_matrix).sum() +
1472 c.spin_beta_coefficient *
1473 Dspin.
beta.cwiseProduct(c.weight_matrix).sum();
1474 totenergy += c.lambda * (population - c.target_population);
1479 << std::setprecision(12) << E_one << std::flush;
1481 << std::setprecision(12) << E_coul << std::flush;
1483 << std::setprecision(12) << E_xc << std::flush;
1486 <<
TimeStamp() <<
" EXX contribution " << std::setprecision(12)
1487 << E_exx << std::flush;
1490 <<
TimeStamp() <<
" Total Energy " << std::setprecision(12) << totenergy
1505 [
this, &H0, &vxcpotential](
1506 const Eigen::MatrixXd& alpha_new,
1511 constexpr double kIntegralError = 1
e-8;
1513 std::array<Eigen::MatrixXd, 2> both_alpha_new =
CalcERIs_EXX(
1514 Eigen::MatrixXd::Zero(0, 0), alpha_new, kIntegralError);
1515 std::array<Eigen::MatrixXd, 2> both_beta_new =
CalcERIs_EXX(
1516 Eigen::MatrixXd::Zero(0, 0), beta_new, kIntegralError);
1517 Eigen::MatrixXd J_new = both_alpha_new[0] + both_beta_new[0];
1521 Eigen::MatrixXd D_total_new = alpha_new + beta_new;
1522 Eigen::MatrixXd J_new =
CalcERIs(D_total_new, kIntegralError);
1523 H_new.
alpha += J_new;
1524 H_new.
beta += J_new;
1527 H_new.
alpha += vxc_new.vxc_alpha;
1528 H_new.
beta += vxc_new.vxc_beta;
1532 Dspin = conv_uks.
Iterate(Dspin, Hspin, MOs_alpha, MOs_beta, totenergy);
1534 MOs_beta = MOs_alpha;
1552 Index nuclear_charge = 0;
1554 nuclear_charge += atom.getNuccharge();
1557 CanonicalizeOrbitalPhases(MOs_alpha);
1558 CanonicalizeOrbitalPhases(MOs_beta);
1561 orb.
MOs() = MOs_alpha;
1572 <<
TimeStamp() <<
" UKS converged after " << this_iter + 1
1573 <<
" iterations. Delta E=" << conv_uks.
getDeltaE()
1574 <<
" DIIS error=" << conv_uks.
getDIIsError() << std::flush;
1577 <<
TimeStamp() <<
" Final Single Point Energy "
1578 << std::setprecision(12) << totenergy <<
" Ha" << std::flush;
1580 <<
TimeStamp() << std::setprecision(12) <<
" Final XC contribution "
1581 << E_xc <<
" Ha" << std::flush;
1585 <<
" Final EXX contribution " << E_exx <<
" Ha" << std::flush;
1605 <<
TimeStamp() <<
" UKS calculation has not converged after "
1606 <<
max_iter_ <<
" iterations." << std::flush;
1628 <<
TimeStamp() <<
" Filled DFT Kinetic energy matrix ." << std::flush;
1633 <<
TimeStamp() <<
" Filled DFT nuclear potential matrix." << std::flush;
1635 Eigen::MatrixXd H0 = dftAOkinetic.
Matrix() + dftAOESP.
Matrix();
1637 <<
TimeStamp() <<
" Constructed independent particle hamiltonian "
1641 << std::setprecision(9) << E0 << std::flush;
1648 <<
TimeStamp() <<
" Filled DFT ECP matrix" << std::flush;
1653 <<
" External sites" << std::flush;
1654 bool has_quadrupoles = std::any_of(
1656 [](
const std::unique_ptr<StaticSite>& s) { return s->getRank() == 2; });
1657 std::string header =
1658 " Name Coordinates[a0] charge[e] dipole[e*a0] ";
1659 if (has_quadrupoles) {
1660 header +=
" quadrupole[e*a0^2]";
1665 for (
const std::unique_ptr<StaticSite>& site : *
externalsites_) {
1666 if (counter == limit) {
1669 std::string output =
1670 (boost::format(
" %1$s"
1671 " %2$+1.4f %3$+1.4f %4$+1.4f"
1673 site->getElement() % site->getPos()[0] % site->getPos()[1] %
1674 site->getPos()[2] % site->getCharge())
1676 const Eigen::Vector3d& dipole = site->getDipole();
1677 output += (boost::format(
" %1$+1.4f %2$+1.4f %3$+1.4f") % dipole[0] %
1678 dipole[1] % dipole[2])
1680 if (site->getRank() > 1) {
1681 Eigen::VectorXd quadrupole = site->Q().tail<5>();
1683 (boost::format(
" %1$+1.4f %2$+1.4f %3$+1.4f %4$+1.4f %5$+1.4f") %
1684 quadrupole[0] % quadrupole[1] % quadrupole[2] % quadrupole[3] %
1691 if (counter == limit) {
1694 <<
" sites not displayed)\n"
1701 <<
TimeStamp() <<
" Nuclei-external site interaction energy "
1702 << std::setprecision(9) << ext_multipoles.
energy() << std::flush;
1703 E0 += ext_multipoles.
energy();
1704 H0 += ext_multipoles.
matrix();
1711 E0 += extdensity_result.
energy();
1713 <<
TimeStamp() <<
" Nuclei-external density interaction energy "
1714 << std::setprecision(9) << extdensity_result.
energy() << std::flush;
1715 H0 += extdensity_result.
matrix();
1721 <<
TimeStamp() <<
" Integrating external electric field with F[Hrt]="
1735 <<
TimeStamp() <<
" Filled DFT Overlap matrix." << std::flush;
1752 <<
TimeStamp() <<
" Inverted AUX Coulomb matrix, removed "
1753 <<
ERIs_.Removedfunctions() <<
" functions from aux basis"
1757 <<
" Setup invariant parts of Electron Repulsion integrals "
1761 <<
TimeStamp() <<
" Calculating 4c diagonals. " << std::flush;
1764 <<
TimeStamp() <<
" Calculated 4c diagonals. " << std::flush;
1798std::optional<std::pair<Index, Index>> HundsRuleAlphaBetaElectrons(
1799 Index nuclear_charge) {
1800 switch (nuclear_charge) {
1802 return std::make_pair(1, 0);
1804 return std::make_pair(1, 1);
1806 return std::make_pair(2, 1);
1808 return std::make_pair(2, 2);
1810 return std::make_pair(3, 2);
1812 return std::make_pair(4, 2);
1814 return std::make_pair(5, 2);
1816 return std::make_pair(5, 3);
1818 return std::make_pair(5, 4);
1820 return std::make_pair(5, 5);
1822 return std::make_pair(6, 5);
1824 return std::make_pair(6, 6);
1826 return std::make_pair(7, 6);
1828 return std::make_pair(8, 6);
1830 return std::make_pair(9, 6);
1832 return std::make_pair(9, 7);
1834 return std::make_pair(9, 8);
1836 return std::make_pair(9, 9);
1838 return std::make_pair(10, 9);
1840 return std::make_pair(10, 10);
1843 return std::make_pair(16, 15);
1845 return std::make_pair(17, 15);
1847 return std::make_pair(18, 15);
1849 return std::make_pair(18, 16);
1851 return std::make_pair(18, 17);
1853 return std::make_pair(18, 18);
1856 return std::make_pair(25, 24);
1858 return std::make_pair(26, 24);
1860 return std::make_pair(27, 24);
1862 return std::make_pair(27, 25);
1864 return std::make_pair(27, 26);
1866 return std::make_pair(27, 27);
1868 return std::nullopt;
1874 const QMAtom& uniqueAtom,
bool use_hunds_rule_occupation)
const {
1886 dftbasis.
Fill(basisset, atom);
1896 ecp.
Fill(ecps, atom);
1914 if (use_hunds_rule_occupation) {
1915 auto hunds_rule = HundsRuleAlphaBetaElectrons(numofelectrons);
1916 if (hunds_rule.has_value()) {
1917 alpha_e = hunds_rule->first;
1918 beta_e = hunds_rule->second;
1922 <<
" No Hund's-rule ground-state occupation table "
1923 "entry for nuclear charge "
1925 <<
" (d/f-block elements are not covered -- see "
1926 "HundsRuleAlphaBetaElectrons's own comment for why) -- "
1927 "falling back to the simpler, parity-based alpha/beta split."
1929 use_hunds_rule_occupation =
false;
1932 if (!use_hunds_rule_occupation) {
1933 if ((numofelectrons % 2) != 0) {
1934 alpha_e = numofelectrons / 2 + numofelectrons % 2;
1935 beta_e = numofelectrons / 2;
1937 alpha_e = numofelectrons / 2;
1948 dftAOoverlap.
Fill(dftbasis);
1949 dftAOkinetic.
Fill(dftbasis);
1999 conv_uks.
Configure(opt_alpha, opt_beta);
2003 Eigen::MatrixXd H0 = dftAOkinetic.
Matrix() + dftAOESP.
Matrix();
2020 return Dspin_H.
alpha;
2028 for (
Index this_iter = 0; this_iter < maxiter; this_iter++) {
2029 Eigen::MatrixXd H_alpha = H0;
2030 Eigen::MatrixXd H_beta = H0;
2032 double E_coul = 0.0;
2038 double integral_error = std::min(conv_uks.
getDIIsError() * 1
e-5, 1
e-5);
2041 std::array<Eigen::MatrixXd, 2> both_alpha =
2043 std::array<Eigen::MatrixXd, 2> both_beta =
2046 Eigen::MatrixXd Hartree = both_alpha[0] + both_beta[0];
2047 H_alpha += Hartree +
ScaHFX_ * both_alpha[1];
2048 H_beta += Hartree +
ScaHFX_ * both_beta[1];
2050 E_coul = 0.5 * Dspin.
total().cwiseProduct(Hartree).sum();
2052 (both_alpha[1].cwiseProduct(Dspin.
alpha).sum() +
2053 both_beta[1].cwiseProduct(Dspin.
beta).sum());
2055 Eigen::MatrixXd Hartree =
2059 E_coul = 0.5 * Dspin.
total().cwiseProduct(Hartree).sum();
2064 H_beta += vxc.vxc_beta;
2067 double E_one_alpha = Dspin.
alpha.cwiseProduct(H0).sum();
2068 double E_one_beta = Dspin.
beta.cwiseProduct(H0).sum();
2069 double totenergy = E_one_alpha + E_one_beta + E_coul + E_exx + E_xc;
2072 Dspin = conv_uks.
Iterate(Dspin, Hspin, MOs_alpha, MOs_beta, totenergy);
2075 <<
TimeStamp() <<
" Iter " << this_iter <<
" of " << maxiter <<
" Etot "
2076 << totenergy <<
" diise " << conv_uks.
getDIIsError() <<
"\n\t\t a_gap "
2081 <<
" Nalpha=" << dftAOoverlap.
Matrix().cwiseProduct(Dspin.
alpha).sum()
2082 <<
" Nbeta=" << dftAOoverlap.
Matrix().cwiseProduct(Dspin.
beta).sum()
2086 if (converged || this_iter == maxiter - 1) {
2089 <<
TimeStamp() <<
" Converged after " << this_iter + 1
2090 <<
" iterations" << std::flush;
2093 <<
TimeStamp() <<
" Not converged after " << this_iter + 1
2094 <<
" iterations. Unconverged density.\n\t\t\t"
2095 <<
" DIIsError=" << conv_uks.
getDIIsError() << std::flush;
2104 <<
" gives N=" << std::setprecision(9)
2105 << avgmatrix.cwiseProduct(dftAOoverlap.
Matrix()).sum() <<
" electrons."
2114 <<
TimeStamp() <<
" Scanning molecule of size " << mol.
size()
2115 <<
" for unique elements" << std::flush;
2117 for (
auto element : elements) {
2118 uniqueelements.
push_back(
QMAtom(0, element, Eigen::Vector3d::Zero()));
2122 <<
" unique elements found" << std::flush;
2123 std::vector<Eigen::MatrixXd> uniqueatom_guesses;
2124 for (
QMAtom& unique_atom : uniqueelements) {
2126 <<
TimeStamp() <<
" Calculating atom density for "
2127 << unique_atom.getElement() << std::flush;
2129 uniqueatom_guesses.push_back(dmat_unrestricted);
2132 Eigen::MatrixXd guess =
2135 for (
const QMAtom& atom : mol) {
2137 for (; index < uniqueelements.
size(); index++) {
2138 if (atom.getElement() == uniqueelements[index].getElement()) {
2142 Eigen::MatrixXd& dmat_unrestricted = uniqueatom_guesses[index];
2143 guess.block(start, start, dmat_unrestricted.rows(),
2144 dmat_unrestricted.cols()) = dmat_unrestricted;
2145 start += dmat_unrestricted.rows();
2151std::map<std::string, Eigen::MatrixXd>
2155 <<
TimeStamp() <<
" Scanning molecule of size " << mol.
size()
2156 <<
" for unique elements (Hirshfeld reference densities)" << std::flush;
2158 std::map<std::string, Eigen::MatrixXd> reference_densities;
2159 for (
const std::string& element : elements) {
2160 QMAtom unique_atom(0, element, Eigen::Vector3d::Zero());
2162 <<
TimeStamp() <<
" Calculating Hirshfeld reference density for "
2163 << element << std::flush;
2170 return reference_densities;
2175 std::map<std::string, Eigen::MatrixXd> reference_densities =
2182 full_dftbasis.
Fill(basisset, mol);
2188 std::vector<HirshfeldPartition::AtomicReference> atoms =
2190 reference_densities);
2195 double neutral_reference_population = 0.0;
2197 if (atom_index < 0 || atom_index >=
static_cast<Index>(mol.
size())) {
2198 throw std::runtime_error(
2199 "BuildCDFTConstraint: cdft.indices contains atom index " +
2200 std::to_string(atom_index) +
", but this molecule only has " +
2201 std::to_string(mol.
size()) +
2202 " atoms (0-based indexing -- valid range is 0.." +
2203 std::to_string(mol.
size() - 1) +
").");
2213 atoms, atom_index, full_dftbasis, grid);
2214 neutral_reference_population +=
2215 static_cast<double>(mol[atom_index].getNuccharge());
2226 <<
" atom(s), neutral reference population="
2227 << neutral_reference_population
2240 throw std::runtime_error(
2241 (boost::format(
"Basisset Name in guess orb file "
2242 "and in dftengine option file differ %1% vs %2%") %
2250 "Orbital file has no basisset information,"
2251 "using it as a guess might work or not for calculation with "
2276 throw std::runtime_error(
2277 (boost::format(
"ECPs in orb file: %1% and options %2% differ") %
2284 throw std::runtime_error(
2285 (boost::format(
"Number of electrons in guess orb file "
2286 "and in dftengine differ: "
2287 "alpha %1% vs %2%, beta %3% vs %4%.") %
2293 throw std::runtime_error(
2294 (boost::format(
"Number of levels in guess orb file: "
2295 "%1% and in dftengine: %2% differ.") %
2315 <<
TimeStamp() <<
" Using MKL overload for Eigen " << std::flush;
2319 <<
" Using native Eigen implementation, no BLAS overload "
2324 for (
const QMAtom& atom : mol) {
2326 std::string output = (boost::format(
" %1$s"
2327 " %2$+1.4f %3$+1.4f %4$+1.4f") %
2328 atom.getElement() % pos[0] % pos[1] % pos[2])
2339 <<
dftbasis_.AOBasisSize() <<
" functions" << std::flush;
2347 <<
auxbasis_.AOBasisSize() <<
" functions" << std::flush;
2355 std::vector<std::string> results =
ecp_.Fill(ecpbasisset, mol);
2357 <<
TimeStamp() <<
" Filled ECP Basis" << std::flush;
2358 if (results.size() > 0) {
2359 std::string message =
"";
2360 for (
const std::string& element : results) {
2361 message +=
" " + element;
2364 <<
TimeStamp() <<
" Found no ECPs for elements" << message
2375 Index nuclear_charge = 0;
2376 for (
const QMAtom& atom : mol) {
2377 nuclear_charge += atom.getNuccharge();
2383 if (multiplicity < 1) {
2384 throw std::runtime_error(
"Spin multiplicity must be >= 1.");
2387 if (numofelectrons >= 0) {
2393 Index spin_excess = multiplicity - 1;
2396 throw std::runtime_error(
"Computed a negative number of electrons.");
2400 throw std::runtime_error(
2401 "Spin multiplicity incompatible with total number of electrons.");
2405 throw std::runtime_error(
2406 "Charge and spin multiplicity imply non-integer alpha/beta "
2418 <<
" (charge=" << target_charge <<
", multiplicity=" << multiplicity
2444 <<
" divided into " << grid.
getBoxesSize() <<
" boxes" << std::flush;
2449 double E_nucnuc = 0.0;
2451 for (
Index i = 0; i < mol.
size(); i++) {
2452 const Eigen::Vector3d& r1 = mol[i].
getPos();
2453 double charge1 = double(mol[i].getNuccharge());
2454 for (
Index j = 0; j < i; j++) {
2455 const Eigen::Vector3d& r2 = mol[j].
getPos();
2456 double charge2 = double(mol[j].getNuccharge());
2457 E_nucnuc += charge1 * charge2 / (r1 - r2).norm();
2465 const Eigen::MatrixXd& dmat,
const AOBasis& dftbasis)
const {
2466 Eigen::MatrixXd avdmat = Eigen::MatrixXd::Zero(dmat.rows(), dmat.cols());
2467 for (
const AOShell& shellrow : dftbasis) {
2468 Index size_row = shellrow.getNumFunc();
2469 Index start_row = shellrow.getStartIndex();
2470 for (
const AOShell& shellcol : dftbasis) {
2471 Index size_col = shellcol.getNumFunc();
2472 Index start_col = shellcol.getStartIndex();
2473 Eigen::MatrixXd shelldmat =
2474 dmat.block(start_row, start_col, size_row, size_col);
2475 if (shellrow.getL() == shellcol.getL()) {
2476 double diagavg = shelldmat.diagonal().sum() / double(shelldmat.rows());
2477 Index offdiagelements =
2478 shelldmat.rows() * shelldmat.cols() - shelldmat.cols();
2479 double offdiagavg = (shelldmat.sum() - shelldmat.diagonal().sum()) /
2480 double(offdiagelements);
2481 avdmat.block(start_row, start_col, size_row, size_col).array() =
2483 avdmat.block(start_row, start_col, size_row, size_col)
2487 double avg = shelldmat.sum() / double(shelldmat.size());
2488 avdmat.block(start_row, start_col, size_row, size_col).array() = avg;
2497 const std::vector<std::unique_ptr<StaticSite>>& multipoles)
const {
2499 if (multipoles.size() == 0) {
2505 for (
const QMAtom& atom : mol) {
2507 for (
const std::unique_ptr<StaticSite>& site : *
externalsites_) {
2508 if ((site->getPos() - nucleus.
getPos()).norm() < 1
e-7) {
2510 <<
" External site sits on nucleus, "
2511 "interaction between them is ignored."
2526 Eigen::MatrixXd result =
2528 for (
Index i = 0; i < 3; i++) {
2536 const std::vector<std::unique_ptr<StaticSite>>& multipoles)
const {
2543 <<
TimeStamp() <<
" Filled DFT external multipole potential matrix"
2564 <<
TimeStamp() <<
" Calculated external density" << std::flush;
2567 <<
TimeStamp() <<
" Calculated potential from electron density"
2572 double nuc_energy = 0.0;
2573 for (
const QMAtom& atom : mol) {
2577 const double dist = (atom.getPos() - extatom.getPos()).norm();
2579 double(atom.getNuccharge()) * double(extatom.getNuccharge()) / dist;
2583 <<
TimeStamp() <<
" Calculated potential from nuclei" << std::flush;
2585 <<
TimeStamp() <<
" Electrostatic: " << nuc_energy << std::flush;
2590 const Eigen::MatrixXd& GuessMOs)
const {
2591 Eigen::MatrixXd nonortho =
2593 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(nonortho);
2594 Eigen::MatrixXd result = GuessMOs * es.operatorInverseSqrt();
2607 Eigen::VectorXd eps = Eigen::VectorXd::Zero(nao);
2611 int l =
static_cast<int>(shell.getL());
2612 Index start = shell.getStartIndex();
2613 Index nfunc = shell.getNumFunc();
2615 const QMAtom& atom = mol[shell.getAtomIndex()];
2616 const std::string& element = atom.
getElement();
2621 for (
Index i = 0; i < nfunc; ++i) {
2632 const Index nao =
S.rows();
2634 Eigen::MatrixXd
H = Eigen::MatrixXd::Zero(nao, nao);
2635 constexpr double K = 1.75;
2637 for (
Index mu = 0; mu < nao; ++mu) {
2638 H(mu, mu) = eps(mu);
2639 for (
Index nu = 0; nu < mu; ++nu) {
2640 double hij = K *
S(mu, nu) * 0.5 * (eps(mu) + eps(nu));
2652 <<
TimeStamp() <<
" Building Extended Huckel guess" << std::flush;
2657 <<
TimeStamp() <<
" Solving EHT generalized eigenproblem" << std::flush;
2675 std::array<Eigen::MatrixXd, 2> both =
2703 if (nA + nB != dimer_mol.
size()) {
2704 throw std::runtime_error(
2705 "BuildDimerGuessFromMonomerFiles: monomer A (" + std::to_string(nA) +
2706 " atoms) + monomer B (" + std::to_string(nB) +
2707 " atoms) does not equal this calculation's own molecule (" +
2708 std::to_string(dimer_mol.
size()) +
2709 " atoms) -- wrong monomer file(s), or this calculation's molecule "
2710 "is not simply the concatenation of these two monomers.");
2712 for (
Index i = 0; i < nA; ++i) {
2713 if (atomsA[i].getElement() != dimer_mol[i].getElement()) {
2714 throw std::runtime_error(
2715 "BuildDimerGuessFromMonomerFiles: monomer A's own atom " +
2716 std::to_string(i) +
" (" + atomsA[i].getElement() +
2717 ") does not match this calculation's own atom " + std::to_string(i) +
2718 " (" + dimer_mol[i].getElement() +
2719 ") -- dimer_guess assumes monomer A occupies exactly the first "
2720 "N_A atoms of this calculation's molecule, in the same order.");
2723 for (
Index i = 0; i < nB; ++i) {
2724 if (atomsB[i].getElement() != dimer_mol[nA + i].getElement()) {
2725 throw std::runtime_error(
2726 "BuildDimerGuessFromMonomerFiles: monomer B's own atom " +
2727 std::to_string(i) +
" (" + atomsB[i].getElement() +
2728 ") does not match this calculation's own atom " +
2729 std::to_string(nA + i) +
" (" + dimer_mol[nA + i].getElement() +
2730 ") -- dimer_guess assumes monomer B occupies exactly the "
2731 "remaining atoms of this calculation's molecule (after monomer "
2732 "A's own N_A atoms), in the same order.");
2745 constexpr double kGeometryToleranceBohr = 1
e-3;
2746 auto CheckInternalGeometry = [&](
const QMMolecule& monomer_atoms,
2747 Index offset_in_dimer,
2748 const std::string& label) {
2750 for (
Index i = 0; i < n; ++i) {
2751 for (
Index j = i + 1; j < n; ++j) {
2752 double monomer_distance =
2753 (monomer_atoms[i].
getPos() - monomer_atoms[j].getPos()).norm();
2754 double dimer_distance = (dimer_mol[offset_in_dimer + i].
getPos() -
2755 dimer_mol[offset_in_dimer + j].getPos())
2757 double diff = std::abs(monomer_distance - dimer_distance);
2758 if (diff > kGeometryToleranceBohr) {
2759 throw std::runtime_error(
2760 "BuildDimerGuessFromMonomerFiles: " + label +
2761 "'s own internal geometry does not match this calculation's "
2762 "molecule -- distance between its own atoms " +
2763 std::to_string(i) +
" and " + std::to_string(j) +
" is " +
2764 std::to_string(monomer_distance) +
2765 " Bohr in the monomer file, but " +
2766 std::to_string(dimer_distance) +
2767 " Bohr in this calculation's own molecule (difference " +
2768 std::to_string(diff) +
" Bohr, tolerance " +
2769 std::to_string(kGeometryToleranceBohr) +
2770 " Bohr). This is checked as an INTERNAL, translation/"
2771 "rotation-invariant distance specifically because the "
2772 "monomer's absolute position/orientation is expected to "
2773 "differ between its own standalone optimization and its "
2774 "placement in the dimer -- only its internal geometry "
2775 "should still match.");
2780 CheckInternalGeometry(atomsA, 0,
"Monomer A");
2781 CheckInternalGeometry(atomsB, nA,
"Monomer B");
2790 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