46 const Eigen::VectorXd& s_eig = es.
values;
47 Eigen::VectorXd inv_sqrt = Eigen::VectorXd::Zero(s_eig.size());
49 for (
Index i = 0; i < s_eig.size(); ++i) {
50 if (s_eig(i) < etol) {
53 inv_sqrt(i) = 1.0 / std::sqrt(s_eig(i));
63 const Eigen::MatrixXd U = es.
vectors.leftCols(removed);
67 <<
TimeStamp() <<
" Smallest value of AOOverlap matrix is " << s_eig(0)
71 <<
" basisfunction from inverse overlap matrix (threshold " << etol <<
")"
76 <<
" removed direction(s) appear as zero orbitals at +" <<
kRemovedShift
77 <<
" Hartree." << std::flush;
78 }
else if (s_eig(0) < 1e3 * etol) {
81 <<
" WARNING: the overlap matrix is nearly singular; if the SCF is "
82 "unstable, raise xtpdft.overlap_tolerance above "
83 << s_eig(0) <<
"." << std::flush;
112 const bool accepted =
115 <<
TimeStamp() <<
" Second-order step check: actual dE=" << actual
117 << (accepted ?
" (accepted)" :
" (rejected)")
118 <<
", new trust radius=" <<
trust_.Radius() << std::flush;
125 <<
" without an accepted step; leaving second-order steps for "
142 totE_.push_back(totE);
146 const Eigen::VectorXd MOs_old_energies = MOs.
eigenvalues();
148 const Eigen::MatrixXd&
S =
S_->Matrix();
149 const Eigen::MatrixXd errormatrix =
151 diiserror_ = errormatrix.cwiseAbs().maxCoeff();
172 <<
" Ha); discarding the (A)DIIS history and restarting from that "
179 Eigen::MatrixXd H_restart =
best_H_;
180 if (
opt_.levelshift > 0.0) {
215 diis_.Update(drop, errormatrix);
228 <<
TimeStamp() <<
" Switching to second-order (TRAH) steps"
229 << (
opt_.second_order ==
"always"
231 :
" ((A)DIIS failed repeatedly or the DIIS error stalled)")
239 bool diis_error =
false;
240 Eigen::MatrixXd H_guess =
H;
247 const std::size_t min_history = near_convergence ? 1 : 2;
250 Eigen::VectorXd coeffs;
257 diis_error = !
adiis_.Info();
259 <<
TimeStamp() <<
" Using ADIIS for next guess" << std::flush;
261 coeffs =
diis_.CalcCoeff();
262 diis_error = !
diis_.Info();
264 <<
TimeStamp() <<
" Using DIIS for next guess" << std::flush;
269 <<
TimeStamp() <<
" (A)DIIS failed using mixing instead"
274 for (
Index i = 0; i < coeffs.size(); i++) {
275 if (std::abs(coeffs(i)) < 1
e-8) {
299 (
mathist_.size() <= 2 && !near_convergence)) {
302 opt_.mixingparameter * dmat + (1.0 -
opt_.mixingparameter) * dmatout;
304 <<
TimeStamp() <<
" Using Mixing with alpha=" <<
opt_.mixingparameter
313 const std::vector<Eigen::MatrixXd>& errors =
diis_.ErrorHistory();
318 const Eigen::MatrixXd&
S =
S_->Matrix();
319 for (std::size_t k = 0; k <
mathist_.size(); ++k) {
320 const Eigen::MatrixXd
e =
324 if ((
e - errors[k]).cwiseAbs().maxCoeff() > 1
e-12 ||
325 std::abs(
e.cwiseAbs().maxCoeff() -
errhist_[k]) > 1
e-12) {
340 double mean_ratio = 0.0;
356 const Eigen::MatrixXd&
H,
361 const double mismatch = (dmat_mos - dmat).cwiseAbs().maxCoeff();
365 <<
" Density differs from that of the current orbitals (max "
367 << mismatch <<
"); returning the orbital density first" << std::flush;
373 const Eigen::MatrixXd F_MO = C.transpose() *
H * C;
375 const Index nvirt = C.cols() - nocc;
378 Eigen::VectorXd diag(nocc * nvirt);
379 constexpr double kMinGap = 1
e-3;
380 for (
Index i = 0; i < nocc; ++i) {
381 for (
Index a = 0; a < nvirt; ++a) {
382 diag(i * nvirt + a) =
384 std::max(std::abs(F_MO(nocc + a, nocc + a) - F_MO(i, i)), kMinGap);
395 }
catch (
const std::runtime_error& error) {
398 <<
" Second-order step not possible, continuing without: "
399 << error.what() << std::flush;
406 <<
TimeStamp() <<
" TRAH step: |g|=" << g.norm()
407 <<
", |x|=" << step.
x.norm() <<
", trust radius=" <<
trust_.Radius()
408 <<
", alpha=" << step.
alpha <<
", micro-iterations=" << step.
iterations
409 <<
", Hessian products=" << step.
products
410 << (step.
reused ?
" (subspace reused)" :
"")
412 << (step.
converged ?
"" :
" (not converged)")
413 <<
", predicted dE=" << step.
predicted << std::flush;
427 MOs.
eigenvalues() = (C_new.transpose() *
H * C_new).diagonal();
434 <<
TimeStamp() <<
" Convergence Options:" << std::flush;
436 <<
"\t\t Delta E [Ha]: " <<
opt_.Econverged << std::flush;
438 <<
"\t\t DIIS max error: " <<
opt_.error_converged << std::flush;
441 <<
"\t\t DIIS histlength: " <<
opt_.histlength << std::flush;
443 <<
"\t\t ADIIS start: " <<
opt_.adiis_start << std::flush;
445 <<
"\t\t DIIS start: " <<
opt_.diis_start << std::flush;
446 std::string del =
"oldest";
451 <<
"\t\t Deleting " << del <<
" element from DIIS hist" << std::flush;
454 <<
"\t\t Levelshift[Ha]: " <<
opt_.levelshift << std::flush;
456 <<
"\t\t Levelshift end: " <<
opt_.levelshiftend << std::flush;
458 <<
"\t\t Mixing Parameter alpha: " <<
opt_.mixingparameter << std::flush;
460 <<
"\t\t Mixing end: " <<
opt_.mixingend << std::flush;
462 <<
"\t\t Second-order (TRAH) steps: " <<
opt_.second_order << std::flush;
464 <<
"\t\t Energy reset [Ha]: " <<
opt_.energy_reset
465 << (
opt_.energy_reset > 0.0 ?
"" :
" (off)") << std::flush;
475 const Eigen::MatrixXd&
H)
const {
482 if (!es.
values.allFinite()) {
483 throw std::runtime_error(
"Matrix Diagonalisation failed");
500 const Eigen::MatrixXd& MOs_old)
const {
501 if (
opt_.levelshift < 1
e-9) {
504 Eigen::VectorXd virt = Eigen::VectorXd::Zero(
H.rows());
506 virt(i) =
opt_.levelshift;
510 <<
TimeStamp() <<
" Using levelshift:" <<
opt_.levelshift <<
" Hartree"
512 Eigen::MatrixXd vir =
S_->Matrix() * MOs_old * virt.asDiagonal() *
513 MOs_old.transpose() *
S_->Matrix();
539 const Eigen::MatrixXd& MOs)
const {
540 const Eigen::MatrixXd occstates = MOs.leftCols(
nocclevels_);
541 Eigen::MatrixXd dmatGS = 2.0 * occstates * occstates.transpose();
551 const Eigen::MatrixXd& MOs)
const {
553 return Eigen::MatrixXd::Zero(MOs.rows(), MOs.rows());
555 Eigen::MatrixXd occstates = MOs.leftCols(
nocclevels_);
556 Eigen::MatrixXd dmatGS = occstates * occstates.transpose();
574 if (
opt_.numberofelectrons == 0) {
579 Eigen::VectorXd occupation = Eigen::VectorXd::Zero(MOs.
eigenvalues().size());
580 std::vector<std::vector<Index> > degeneracies;
581 double buffer = 1
e-4;
582 degeneracies.push_back(std::vector<Index>{0});
583 for (
Index i = 1; i < occupation.size(); i++) {
585 MOs.
eigenvalues()(degeneracies[degeneracies.size() - 1][0]) + buffer) {
586 degeneracies[degeneracies.size() - 1].push_back(i);
588 degeneracies.push_back(std::vector<Index>{i});
591 Index numofelec =
opt_.numberofelectrons;
592 for (
const std::vector<Index>& deglevel : degeneracies) {
593 Index numofpossibleelectrons = 2 *
Index(deglevel.size());
594 if (numofpossibleelectrons <= numofelec) {
595 for (
Index i : deglevel) {
598 numofelec -= numofpossibleelectrons;
600 double occ = double(numofelec) / double(deglevel.size());
601 for (
Index i : deglevel) {
607 Eigen::MatrixXd dmatGS = MOs.
eigenvectors() * occupation.asDiagonal() *
618 const Eigen::MatrixXd& MOs)
const {
621 std::min(
opt_.number_alpha_electrons,
opt_.number_beta_electrons);
622 const Index n_socc_alpha =
opt_.number_alpha_electrons - n_docc;
625 result.
alpha = Eigen::MatrixXd::Zero(MOs.rows(), MOs.rows());
626 result.
beta = Eigen::MatrixXd::Zero(MOs.rows(), MOs.rows());
629 const Eigen::MatrixXd docc = MOs.leftCols(n_docc);
630 const Eigen::MatrixXd d_docc = docc * docc.transpose();
631 result.
alpha += d_docc;
632 result.
beta += d_docc;
635 if (n_socc_alpha > 0) {
636 const Eigen::MatrixXd socc = MOs.middleCols(n_docc, n_socc_alpha);
637 result.
alpha += socc * socc.transpose();
653 return {0.5 * d, 0.5 * d};
656 return {d, Eigen::MatrixXd::Zero(d.rows(), d.cols())};
659 return {0.5 * d, 0.5 * d};
666 return spin_dmat.
total();
bool HistoryIsAligned() const
Eigen::MatrixXd DensityMatrix(const tools::EigenSystem &MOs) const
static constexpr double kMeanRatioTolerance
trah::StepOptions trah_options_
tools::EigenSystem trah_pre_MOs_
Index iterations_since_reset_
static constexpr Index kTrailingWindow
static constexpr double kRemovedShift
Eigen::MatrixXd DensityMatrixGroundState_unres(const Eigen::MatrixXd &MOs) const
std::vector< double > error_history_
std::vector< Eigen::MatrixXd > mathist_
OrbitalHessian orbital_hessian_
Eigen::MatrixXd Iterate(const Eigen::MatrixXd &dmat, Eigen::MatrixXd &H, tools::EigenSystem &MOs, double totE)
double getDIIsError() const
Return the DIIS commutator norm from the latest iteration.
static constexpr double kDensityMismatchTolerance
static constexpr Index kAutoStartIteration
tools::EigenSystem SolveFockmatrix(const Eigen::MatrixXd &H) const
Solve the generalized eigenvalue problem for the current Fock matrix.
static constexpr double kMinTrustRadius
Eigen::MatrixXd trah_pre_H_
Eigen::MatrixXd Sminusahalf
std::vector< double > errhist_
void PrintConfigOptions() const
Print the active convergence-acceleration settings to the logger.
void Levelshift(Eigen::MatrixXd &H, const Eigen::MatrixXd &MOs_old) const
Apply a virtual-space level shift in the molecular-orbital basis.
static constexpr double EnergyGradientScale
static constexpr Index kResetCooldown
double getDeltaE() const
Return the total-energy change between the two most recent SCF iterations.
bool SecondOrderStalled() const
static constexpr Index kMaxEnergyResets
Eigen::MatrixXd DensityMatrixGroundState_frac(const tools::EigenSystem &MOs) const
Construct a fractional-occupation density matrix from orbital occupations.
bool SecondOrderAvailable() const
std::vector< double > totE_
static constexpr Index kMaxConsecutiveFailures
trah::Subspace trah_subspace_
Eigen::MatrixXd SecondOrderStep(const Eigen::MatrixXd &dmat, const Eigen::MatrixXd &H, tools::EigenSystem &MOs, double totE)
Index consecutive_failures_
void setOverlap(AOOverlap &S, double etol)
Precompute overlap-dependent quantities used when solving the Fock matrix.
Eigen::MatrixXd removed_projector_
Eigen::MatrixXd DensityMatrixGroundState(const Eigen::MatrixXd &MOs) const
Eigen::MatrixXd best_dmat_
std::vector< Eigen::MatrixXd > dmatHist_
static constexpr double kEnergyRiseForADIIS
SpinDensity DensityMatrixSpinResolved(const tools::EigenSystem &MOs) const
SpinDensity DensityMatrixGroundState_restricted_open(const Eigen::MatrixXd &MOs) const
Timestamp returns the current time as a string Example: cout << TimeStamp().
#define XTP_LOG(level, log)
void Cite(const std::string &key)
marks a reference as used (thread safe; unknown keys are printed as such)
std::function< Eigen::MatrixXd(const Eigen::MatrixXd &)> HessianProduct
Hessian applied to the columns of V (n x k), returning n x k.
StepResult Step(const Eigen::VectorXd &g, const Eigen::VectorXd &diag, const HessianProduct &hessian, double trust_radius, const StepOptions &opt=StepOptions(), Subspace *subspace=nullptr)
Eigen::MatrixXd RotateOrbitals(const Eigen::MatrixXd &C, const Eigen::VectorXd &x, Index nocc)
Eigen::VectorXd OccVirt(const Eigen::MatrixXd &M, Index nocc)
Charge transport classes.
SymmetricEigenSystem SymmetricEigen(const Eigen::MatrixXd &A, bool vectors=true)
With vectors = false only the eigenvalues are computed.
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.