53 const Eigen::VectorXd& s_eig = es.
values;
54 Eigen::VectorXd inv_sqrt = Eigen::VectorXd::Zero(s_eig.size());
56 for (
Index i = 0; i < s_eig.size(); ++i) {
57 if (s_eig(i) < etol) {
60 inv_sqrt(i) = 1.0 / std::sqrt(s_eig(i));
66 const Eigen::MatrixXd U = es.
vectors.leftCols(removed);
70 <<
TimeStamp() <<
" Smallest value of AOOverlap matrix is " << s_eig(0)
74 <<
" basisfunction from inverse overlap matrix (threshold " << etol <<
")"
76 if (removed == 0 && s_eig(0) < 1e3 * etol) {
79 <<
" WARNING: the overlap matrix is nearly singular; if the SCF is "
80 "unstable, raise xtpdft.overlap_tolerance above "
81 << s_eig(0) <<
"." << std::flush;
86 const Eigen::MatrixXd&
H)
const {
92 if (!es.
values.allFinite()) {
93 throw std::runtime_error(
"Matrix Diagonalisation failed");
103 const Eigen::VectorXd& v_ov,
Index nao,
Index nocclevels)
const {
104 Index nvirt = nao - nocclevels;
105 Eigen::MatrixXd kappa = Eigen::MatrixXd::Zero(nao, nao);
106 for (
Index i = 0; i < nocclevels; ++i) {
107 for (
Index a = 0; a < nvirt; ++a) {
108 double val = v_ov(i * nvirt + a);
109 kappa(i, nocclevels + a) = val;
110 kappa(nocclevels + a, i) = -val;
116std::pair<Eigen::MatrixXd, Eigen::MatrixXd>
119 Index nocclevels_alpha,
121 Index nocclevels_beta)
const {
122 Index n_ov_alpha = nocclevels_alpha * (nao_alpha - nocclevels_alpha);
123 Eigen::VectorXd v_alpha = v.head(n_ov_alpha);
124 Eigen::VectorXd v_beta = v.tail(v.size() - n_ov_alpha);
125 Eigen::MatrixXd kappa_alpha =
127 Eigen::MatrixXd kappa_beta =
129 return {kappa_alpha, kappa_beta};
133 const Eigen::VectorXd& v,
const Eigen::MatrixXd& C_alpha,
134 Index nocclevels_alpha,
const Eigen::MatrixXd& C_beta,
136 double finite_diff_step)
const {
137 Index nao_alpha = C_alpha.rows();
138 Index nvirt_alpha = nao_alpha - nocclevels_alpha;
139 Index nao_beta = C_beta.rows();
140 Index nvirt_beta = nao_beta - nocclevels_beta;
141 Index n_ov_alpha = nocclevels_alpha * nvirt_alpha;
142 Index n_ov_beta = nocclevels_beta * nvirt_beta;
145 v, nao_alpha, nocclevels_alpha, nao_beta, nocclevels_beta);
146 kappa_alpha_trial *= finite_diff_step;
147 kappa_beta_trial *= finite_diff_step;
153 auto EvaluateBothGradientsAt = [&](
const Eigen::MatrixXd& kappa_alpha,
154 const Eigen::MatrixXd& kappa_beta)
155 -> std::pair<Eigen::MatrixXd, Eigen::MatrixXd> {
156 Eigen::MatrixXd C_alpha_rot =
158 (Eigen::MatrixXd::Identity(nao_alpha, nao_alpha) + kappa_alpha);
159 Eigen::MatrixXd nonortho_alpha =
160 C_alpha_rot.transpose() *
S_->Matrix() * C_alpha_rot;
161 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_alpha(nonortho_alpha);
162 C_alpha_rot = C_alpha_rot * es_alpha.operatorInverseSqrt();
164 Eigen::MatrixXd C_beta_rot =
165 C_beta * (Eigen::MatrixXd::Identity(nao_beta, nao_beta) + kappa_beta);
166 Eigen::MatrixXd nonortho_beta =
167 C_beta_rot.transpose() *
S_->Matrix() * C_beta_rot;
168 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_beta(nonortho_beta);
169 C_beta_rot = C_beta_rot * es_beta.operatorInverseSqrt();
171 Eigen::MatrixXd C_alpha_occ_rot = C_alpha_rot.leftCols(nocclevels_alpha);
172 Eigen::MatrixXd D_alpha_rot = C_alpha_occ_rot * C_alpha_occ_rot.transpose();
173 Eigen::MatrixXd C_beta_occ_rot = C_beta_rot.leftCols(nocclevels_beta);
174 Eigen::MatrixXd D_beta_rot = C_beta_occ_rot * C_beta_occ_rot.transpose();
176 SpinFock H_rot = coupled_fock_builder(D_alpha_rot, D_beta_rot);
177 Eigen::MatrixXd F_MO_alpha_rot =
178 C_alpha_rot.transpose() * H_rot.
alpha * C_alpha_rot;
179 Eigen::MatrixXd F_MO_beta_rot =
180 C_beta_rot.transpose() * H_rot.
beta * C_beta_rot;
181 return {F_MO_alpha_rot, F_MO_beta_rot};
184 auto [F_MO_alpha_plus, F_MO_beta_plus] =
185 EvaluateBothGradientsAt(kappa_alpha_trial, kappa_beta_trial);
186 auto [F_MO_alpha_minus, F_MO_beta_minus] =
187 EvaluateBothGradientsAt(-kappa_alpha_trial, -kappa_beta_trial);
193 Eigen::VectorXd sigma(n_ov_alpha + n_ov_beta);
194 for (
Index i = 0; i < nocclevels_alpha; ++i) {
195 for (
Index a = 0; a < nvirt_alpha; ++a) {
196 double g_plus_ia = F_MO_alpha_plus(i, nocclevels_alpha + a);
197 double g_minus_ia = F_MO_alpha_minus(i, nocclevels_alpha + a);
198 sigma(i * nvirt_alpha + a) = scale * (g_plus_ia - g_minus_ia);
201 for (
Index i = 0; i < nocclevels_beta; ++i) {
202 for (
Index a = 0; a < nvirt_beta; ++a) {
203 double g_plus_ia = F_MO_beta_plus(i, nocclevels_beta + a);
204 double g_minus_ia = F_MO_beta_minus(i, nocclevels_beta + a);
205 sigma(n_ov_alpha + i * nvirt_beta + a) = scale * (g_plus_ia - g_minus_ia);
213 Index nocclevels,
double& predicted_energy_change)
const {
221 Eigen::MatrixXd F_MO = C.transpose() * H_AO * C;
227 Eigen::MatrixXd kappa = Eigen::MatrixXd::Zero(nao, nao);
234 Eigen::MatrixXd h_matrix = Eigen::MatrixXd::Zero(nao, nao);
239 constexpr double kMinGap = 1
e-3;
242 constexpr double kMaxKappaElement = 0.1;
243 for (
Index i = 0; i < nocclevels; ++i) {
244 for (
Index a = nocclevels; a < nao; ++a) {
245 double gap = std::max(std::abs(eps(a) - eps(i)), kMinGap);
250 double h_ia = 2.0 * gap;
251 double kappa_ia = -g_ia / h_ia;
252 kappa_ia = std::clamp(kappa_ia, -kMaxKappaElement, kMaxKappaElement);
253 kappa(i, a) = kappa_ia;
254 kappa(a, i) = -kappa_ia;
255 h_matrix(i, a) = h_ia;
259 double knorm = kappa.norm();
260 if (knorm >
trust_.Radius()) {
261 kappa *= (
trust_.Radius() / knorm);
268 predicted_energy_change = 0.0;
269 for (
Index i = 0; i < nocclevels; ++i) {
270 for (
Index a = nocclevels; a < nao; ++a) {
271 double kappa_ia = kappa(i, a);
273 0.5 * h_matrix(i, a) * kappa_ia * kappa_ia;
282 Eigen::MatrixXd C_new = C * (Eigen::MatrixXd::Identity(nao, nao) + kappa);
283 Eigen::MatrixXd nonortho = C_new.transpose() *
S_->Matrix() * C_new;
284 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es_ortho(nonortho);
285 return C_new * es_ortho.operatorInverseSqrt();
288std::pair<Eigen::MatrixXd, Eigen::MatrixXd>
291 Index nocclevels_alpha,
const Eigen::MatrixXd& H_AO_beta,
294 double& predicted_energy_change)
const {
296 Index nvirt_alpha = nao_alpha - nocclevels_alpha;
297 Index n_ov_alpha = nocclevels_alpha * nvirt_alpha;
298 const Eigen::MatrixXd& C_alpha = MOs_alpha.
eigenvectors();
299 const Eigen::VectorXd& eps_alpha = MOs_alpha.
eigenvalues();
302 Index nvirt_beta = nao_beta - nocclevels_beta;
303 Index n_ov_beta = nocclevels_beta * nvirt_beta;
304 const Eigen::MatrixXd& C_beta = MOs_beta.
eigenvectors();
305 const Eigen::VectorXd& eps_beta = MOs_beta.
eigenvalues();
307 Index n_ov = n_ov_alpha + n_ov_beta;
309 Eigen::MatrixXd F_MO_alpha = C_alpha.transpose() * H_AO_alpha * C_alpha;
310 Eigen::MatrixXd F_MO_beta = C_beta.transpose() * H_AO_beta * C_beta;
318 Eigen::VectorXd g(n_ov);
319 Eigen::VectorXd diag_h(n_ov);
320 constexpr double kMinGap = 1
e-3;
321 for (
Index i = 0; i < nocclevels_alpha; ++i) {
322 for (
Index a = 0; a < nvirt_alpha; ++a) {
323 g(i * nvirt_alpha + a) =
325 double gap = std::max(
326 std::abs(eps_alpha(nocclevels_alpha + a) - eps_alpha(i)), kMinGap);
327 diag_h(i * nvirt_alpha + a) = 2.0 * gap;
330 for (
Index i = 0; i < nocclevels_beta; ++i) {
331 for (
Index a = 0; a < nvirt_beta; ++a) {
332 g(n_ov_alpha + i * nvirt_beta + a) =
334 double gap = std::max(
335 std::abs(eps_beta(nocclevels_beta + a) - eps_beta(i)), kMinGap);
336 diag_h(n_ov_alpha + i * nvirt_beta + a) = 2.0 * gap;
347 }
catch (
const std::runtime_error& error) {
349 <<
TimeStamp() <<
" Analytic UKS Hessian not available ("
350 << error.what() <<
"); using finite differences" << std::flush;
355 Eigen::MatrixXd HV(
V.rows(),
V.cols());
356 for (
Index k = 0; k <
V.cols(); ++k) {
359 nocclevels_beta, coupled_fock_builder);
365 predicted_energy_change = step.
predicted;
370 <<
TimeStamp() <<
" TRAH step: |g|=" << g.norm()
371 <<
", |x|=" << step.
x.norm() <<
", trust radius=" << trust_radius
372 <<
", alpha=" << step.
alpha <<
", micro-iterations=" << step.
iterations
373 <<
", Hessian products=" << step.
products
374 << (analytic ?
" (analytic)" :
" (finite differences)")
375 << (step.
reused ?
" (subspace reused)" :
"")
377 << (step.
converged ?
"" :
" (not converged)")
378 <<
", predicted dE=" << predicted_energy_change << std::flush;
386 const Eigen::MatrixXd& MOs,
Index nocclevels)
const {
387 if (nocclevels == 0) {
388 return Eigen::MatrixXd::Zero(MOs.rows(), MOs.rows());
390 Eigen::MatrixXd occstates = MOs.leftCols(nocclevels);
391 return occstates * occstates.transpose();
406 const Eigen::MatrixXd& MOs_old,
411 Eigen::VectorXd virt = Eigen::VectorXd::Zero(
H.rows());
412 for (
Index i = nocclevels; i <
H.rows(); ++i) {
420 Eigen::MatrixXd vir =
S_->Matrix() * MOs_old * virt.asDiagonal() *
421 MOs_old.transpose() *
S_->Matrix();
426 const Eigen::MatrixXd& dmat,
const Eigen::MatrixXd&
H)
const {
427 const Eigen::MatrixXd&
S =
S_->Matrix();
432 const Eigen::MatrixXd& err_beta)
const {
433 return std::max(err_alpha.cwiseAbs().maxCoeff(),
434 err_beta.cwiseAbs().maxCoeff());
450 const bool accepted =
455 <<
" Second-order step check: actual dE=" << actual_change
457 <<
", r=" <<
trust_.LastRatio()
458 << (accepted ?
" (accepted)" :
" (rejected)")
459 <<
", new trust radius=" <<
trust_.Radius() << std::flush;
466 <<
" without an accepted step; leaving second-order steps for "
506 totE_.push_back(totE);
566 diis_.Update(drop, err_alpha, err_beta);
568 bool diis_error =
false;
579 const bool failures =
582 if (
opt_alpha_.second_order ==
"always" || failures || stalled) {
584 <<
TimeStamp() <<
" Switching to second-order (TRAH) steps"
585 << (failures ?
" ((A)DIIS failed repeatedly)" :
"")
586 << (stalled ?
" (DIIS error stalled)" :
"") << std::flush;
594 Eigen::MatrixXd H_guess_alpha =
H.alpha;
595 Eigen::MatrixXd H_guess_beta =
H.beta;
600 const std::size_t min_history = near_convergence ? 1 : 2;
605 Eigen::VectorXd coeffs;
611 diis_error = !
adiis_.Info() || coeffs.size() == 0;
613 <<
TimeStamp() <<
" Using ADIIS for next UKS guess" << std::flush;
615 coeffs =
diis_.CalcCoeff();
616 diis_error = !
diis_.Info() || coeffs.size() == 0;
618 <<
TimeStamp() <<
" Using DIIS for next UKS guess" << std::flush;
624 <<
TimeStamp() <<
" (A)DIIS failed using mixing instead"
626 H_guess_alpha =
H.alpha;
627 H_guess_beta =
H.beta;
630 H_guess_alpha.setZero();
631 H_guess_beta.setZero();
632 for (
Index i = 0; i < coeffs.size(); ++i) {
633 if (std::abs(coeffs(i)) < 1
e-8) {
657 double ramp_fraction =
660 double mixingparameter_alpha_current =
663 double mixingparameter_beta_current =
666 dmatout.
alpha = mixingparameter_alpha_current * dmat.
alpha +
667 (1.0 - mixingparameter_alpha_current) * dmatout.
alpha;
668 dmatout.
beta = mixingparameter_beta_current * dmat.
beta +
669 (1.0 - mixingparameter_beta_current) * dmatout.
beta;
671 <<
TimeStamp() <<
" Using coupled UKS mixing with adaptive alpha="
672 << mixingparameter_alpha_current
675 <<
", ramp fraction=" << ramp_fraction <<
")" << std::flush;
693 double mean_ratio = 0.0;
694 Index ratio_count = 0;
701 return ratio_count > 0 &&
714 const double mismatch =
715 std::max((dmat_mos.
alpha - dmat.
alpha).cwiseAbs().maxCoeff(),
716 (dmat_mos.
beta - dmat.
beta).cwiseAbs().maxCoeff());
720 <<
" Density differs from that of the current orbitals (max "
722 << mismatch <<
"); returning the orbital density first" << std::flush;
734 Eigen::MatrixXd C_new_alpha;
735 Eigen::MatrixXd C_new_beta;
749 double predicted_alpha = 0.0;
750 double predicted_beta = 0.0;
764 (C_new_alpha.transpose() *
H.alpha * C_new_alpha).diagonal();
766 (C_new_beta.transpose() *
H.beta * C_new_beta).diagonal();
772 if (
totE_.size() < 2) {
Logger is used for thread-safe output of messages.
Timestamp returns the current time as a string Example: cout << TimeStamp().
double CombinedError(const Eigen::MatrixXd &err_alpha, const Eigen::MatrixXd &err_beta) const
tools::EigenSystem SolveFockmatrix(const Eigen::MatrixXd &H) const
double direct_min_pre_energy_
bool analytic_hessian_failed_
trah::StepOptions trah_options_
std::pair< Eigen::MatrixXd, Eigen::MatrixXd > UnflattenCoupledRotation(const Eigen::VectorXd &v, Index nao_alpha, Index nocclevels_alpha, Index nao_beta, Index nocclevels_beta) const
static constexpr Index kAutoStartIteration
Index consecutive_adiis_failures_
SpinDensity DensityMatrix(const tools::EigenSystem &MOs_alpha, const tools::EigenSystem &MOs_beta) const
void Levelshift(Eigen::MatrixXd &H, const Eigen::MatrixXd &MOs_old, const options &opt, Index nocclevels) const
std::vector< Eigen::MatrixXd > mathist_beta_
Eigen::MatrixXd removed_projector_
double direct_min_step_norm_
static constexpr double kMinTrustRadius
double direct_min_linear_
static constexpr Index kMaxConsecutiveADIISFailures
void setOverlap(AOOverlap &S, double etol)
void setLogger(Logger *log)
void Configure(const options &opt_alpha, const options &opt_beta)
Eigen::MatrixXd direct_min_pre_MOs_beta_
Eigen::VectorXd BuildCoupledSigmaVector(const Eigen::VectorXd &v, const Eigen::MatrixXd &C_alpha, Index nocclevels_alpha, const Eigen::MatrixXd &C_beta, Index nocclevels_beta, const CoupledFockBuilder &coupled_fock_builder, double finite_diff_step=1e-3) const
static constexpr double kEnergyRiseForADIIS
Eigen::MatrixXd BuildErrorMatrix(const Eigen::MatrixXd &dmat, const Eigen::MatrixXd &H) const
ConvergenceAcc::options options
SpinDensity Iterate(const SpinDensity &dmat, SpinFock &H, tools::EigenSystem &MOs_alpha, tools::EigenSystem &MOs_beta, double totE)
std::vector< Eigen::MatrixXd > dmatHist_alpha_
Eigen::MatrixXd direct_min_pre_MOs_alpha_
bool direct_min_floor_hit_
Eigen::MatrixXd DirectMinimizationRotation(const Eigen::MatrixXd &H_AO, const tools::EigenSystem &MOs, Index nocclevels, double &predicted_energy_change) const
Eigen::MatrixXd UnflattenRotation(const Eigen::VectorXd &v_ov, Index nao, Index nocclevels) const
trah::Subspace trah_subspace_
Eigen::VectorXd direct_min_pre_MOs_beta_energies_
OrbitalHessian orbital_hessian_
static constexpr double EnergyGradientScale
std::vector< double > errhist_
std::vector< double > diiserror_history_
std::vector< Eigen::MatrixXd > dmatHist_beta_
std::vector< double > totE_
CoupledFockBuilder coupled_fock_builder_
Eigen::VectorXd direct_min_pre_MOs_alpha_energies_
static constexpr double kDensityMismatchTolerance
SpinFock direct_min_pre_H_
bool TrailingAverageStalled() const
Eigen::MatrixXd DensityMatrixGroundState_unres(const Eigen::MatrixXd &MOs, Index nocclevels) const
static constexpr Index kTrailingWindowSize
double direct_min_predicted_change_
static constexpr double kMeanRatioTolerance
std::function< SpinFock(const Eigen::MatrixXd &, const Eigen::MatrixXd &)> CoupledFockBuilder
SpinDensity SecondOrderStep(const SpinDensity &dmat, const SpinFock &H, tools::EigenSystem &MOs_alpha, tools::EigenSystem &MOs_beta, double totE)
std::vector< Eigen::MatrixXd > mathist_alpha_
Eigen::MatrixXd Sminusahalf
Index total_iteration_count_
static constexpr double kRemovedShift
std::pair< Eigen::MatrixXd, Eigen::MatrixXd > CoupledAugmentedHessianStep(const Eigen::MatrixXd &H_AO_alpha, const tools::EigenSystem &MOs_alpha, Index nocclevels_alpha, const Eigen::MatrixXd &H_AO_beta, const tools::EigenSystem &MOs_beta, Index nocclevels_beta, const CoupledFockBuilder &coupled_fock_builder, double trust_radius, double &predicted_energy_change) const
#define XTP_LOG(level, log)
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)
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.