votca 2026-dev
Loading...
Searching...
No Matches
convergenceacc.h
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20#pragma once
21#ifndef VOTCA_XTP_CONVERGENCEACC_H
22#define VOTCA_XTP_CONVERGENCEACC_H
23
24// VOTCA includes
25#include <votca/tools/linalg.h>
26
27// Local VOTCA includes
28#include "adiis.h"
29#include "aomatrix.h"
30#include "diis.h"
31#include "logger.h"
32#include "trah.h"
33
34namespace votca {
35namespace xtp {
36
45 public:
48
51 struct options {
53 bool usediis;
54 bool noisy = false;
56 bool maxout;
58 double diis_start;
59 double levelshift;
61 // Independent from adiis_start deliberately -- see
62 // UKSConvergenceAcc::Iterate's own comment on where this is used
63 // for the full reasoning: ORCA's own DampErr is kept fully
64 // separate from its own DIISStart, and their own guidance for
65 // difficult systems is to make DampErr much SMALLER than default
66 // (keeping damping active LONGER), independent of when DIIS
67 // itself engages. Reusing adiis_start for both purposes could not
68 // represent that independently.
69 double mixingend;
72 // Ceiling mixingparameter can adaptively ramp up toward as the SCF
73 // struggles, rather than staying fixed at mixingparameter (the
74 // BASE/starting value) for the whole run -- matches ORCA's own
75 // static-damping design directly (confirmed from a real ORCA log's
76 // own resolved SCF settings, not the manual's generic defaults):
77 // DampFac (the base, 0.7 by default) and DampMax (the ceiling, 0.98
78 // by default) are two separate parameters there, not one fixed
79 // value. Notably, 0.98 is exactly the value this session's own
80 // hand-tuning independently landed on for a difficult water dimer
81 // case -- this adaptive design is a more principled way to obtain
82 // that same benefit only when actually needed, rather than paying
83 // its cost (slower convergence on easy iterations) for an entire
84 // run regardless of whether the system is struggling at all.
85 double mixingmax;
86 double Econverged;
90 // Maximum iterations for the Davidson eigensolver used by
91 // CoupledAugmentedHessianStep's own direct-minimization fallback --
92 // NOT CDFT-specific, since that fallback can engage for any
93 // sufficiently difficult UKS SCF (see this field's own XML help
94 // text, dftpackage.xml, for the real case that motivated exposing
95 // this at all: a strong CDFT constraint over a large fragment left
96 // the solver still short of its own convergence tolerance at the
97 // previous, hardcoded default of 50).
99 // Second-order (TRAH) SCF steps: "auto" switches to them when (A)DIIS
100 // stalls, "always" uses them from the start, "off" never.
101 std::string second_order = "auto";
102 // Energy rise (Hartree) above the lowest energy reached so far at which
103 // the SCF discards its extrapolation history and restarts from the
104 // lowest-energy density. 0 disables.
105 double energy_reset = 1.0;
106 };
107
109 struct SpinDensity {
110 Eigen::MatrixXd alpha;
111 Eigen::MatrixXd beta;
112
114 Eigen::MatrixXd total() const { return alpha + beta; }
115
117 Eigen::MatrixXd spin() const { return alpha - beta; }
118 };
119
123 opt_ = opt;
124 if (opt_.mode == KSmode::closed) {
125 nocclevels_ = opt_.numberofelectrons / 2;
126 } else if (opt_.mode == KSmode::open) {
127 nocclevels_ = opt_.numberofelectrons;
128 } else if (opt_.mode == KSmode::fractional) {
129 nocclevels_ = 0;
130 } else if (opt_.mode == KSmode::restricted_open) {
132 std::max(opt_.number_alpha_electrons, opt_.number_beta_electrons);
133 }
134 diis_.setHistLength(opt_.histlength);
135 trah_options_.max_iterations = opt_.davidson_max_iter;
136 StartNewSCF();
137 }
138
139 // Forget the lowest-energy point used by the energy-rise reset. Called
140 // at the start of every SCF: successive SCFs on one accelerator (e.g.
141 // the stages of DFT-in-DFT embedding) need not share an energy scale.
142 // The extrapolation history itself is left as it was.
143 void StartNewSCF() {
144 have_best_ = false;
145 energy_resets_ = 0;
147 trah_active_ = false;
148 trah_pending_ = false;
149 trah_disabled_ = false;
150 last_rejected_ = false;
151 trah_subspace_.Clear();
154 error_history_.clear();
155 scf_iterations_ = 0;
156 }
157
162 using OrbitalHessian = std::function<Eigen::MatrixXd(
163 const tools::EigenSystem& MOs, const Eigen::MatrixXd& F,
164 const Eigen::MatrixXd& X)>;
165 void setOrbitalHessian(const OrbitalHessian& hessian) {
166 orbital_hessian_ = hessian;
167 }
168
170 static constexpr double EnergyGradientScale = -4.0;
171 bool SecondOrderActive() const { return trah_active_; }
173 void setLogger(Logger* log) { log_ = log; }
174
176 void PrintConfigOptions() const;
177
180 bool isConverged() const {
181 if (totE_.size() < 2) {
182 return false;
183 } else {
184 // not on the call that undid a rejected second-order step: totE and
185 // the DIIS error then belong to the restored, not the returned, state
186 return !last_rejected_ && std::abs(getDeltaE()) < opt_.Econverged &&
187 getDIIsError() < opt_.error_converged;
188 }
189 }
190
192 double getDeltaE() const {
193 if (totE_.size() < 2) {
194 return 0;
195 } else {
196 return totE_.back() - totE_[totE_.size() - 2];
197 }
198 }
199
200 // Builds X = S^-1/2 for the orthogonal basis the Fock matrix is
201 // diagonalized in. Eigenvalues of S below etol are dropped from X; the
202 // directions they span are pushed to the top of every Fock spectrum
203 // (kRemovedShift) so that they can never be occupied or appear among the
204 // low virtuals. Their MO coefficient vectors are zero.
205 void setOverlap(AOOverlap& S, double etol);
206
208 double getDIIsError() const { return diiserror_; }
209
212 bool getUseMixing() const { return usedmixing_; }
213
214 // Consistency check of the extrapolation history: every stored
215 // (Fock, density) pair must still produce the error matrix DIIS holds at
216 // the same position. Used by the unit tests.
217 bool HistoryIsAligned() const;
218
221 Eigen::MatrixXd Iterate(const Eigen::MatrixXd& dmat, Eigen::MatrixXd& H,
222 tools::EigenSystem& MOs, double totE);
224 tools::EigenSystem SolveFockmatrix(const Eigen::MatrixXd& H) const;
226 void Levelshift(Eigen::MatrixXd& H, const Eigen::MatrixXd& MOs_old) const;
227
230 Eigen::MatrixXd DensityMatrix(const tools::EigenSystem& MOs) const;
231
234 SpinDensity DensityMatrixSpinResolved(const tools::EigenSystem& MOs) const;
235
236 private:
238
241 Eigen::MatrixXd DensityMatrixGroundState(const Eigen::MatrixXd& MOs) const;
244 Eigen::MatrixXd DensityMatrixGroundState_unres(
245 const Eigen::MatrixXd& MOs) const;
247 Eigen::MatrixXd DensityMatrixGroundState_frac(
248 const tools::EigenSystem& MOs) const;
249
253 const Eigen::MatrixXd& MOs) const;
254
255 // Second-order (TRAH) steps, closed shell only; see Iterate.
256 bool SecondOrderAvailable() const {
257 return opt_.mode == KSmode::closed && bool(orbital_hessian_) &&
258 opt_.second_order != "off" && !trah_disabled_;
259 }
260 bool SecondOrderStalled() const;
261 Eigen::MatrixXd SecondOrderStep(const Eigen::MatrixXd& dmat,
262 const Eigen::MatrixXd& H,
263 tools::EigenSystem& MOs, double totE);
265 bool trah_active_ = false;
266 bool trah_pending_ = false;
267 bool trah_disabled_ = false;
268 static constexpr double kInitialTrustRadius = 0.5;
269 static constexpr double kMinTrustRadius = 3e-3;
270 static constexpr double kDensityMismatchTolerance = 1e-8;
273 double trah_pre_energy_ = 0.0;
274 double trah_predicted_ = 0.0;
275 double trah_linear_ = 0.0;
276 double trah_step_norm_ = 0.0;
278 Eigen::MatrixXd trah_pre_H_;
279 // subspace of the last step, reused when a rejected step is repeated
281 bool last_rejected_ = false;
282 // "auto" trigger: kMaxConsecutiveFailures (A)DIIS failures in a row, or,
283 // after kAutoStartIteration iterations, a DIIS error that over the last
284 // kTrailingWindow iterations has on average shrunk by less than a factor
285 // kMeanRatioTolerance per iteration (whether or not (A)DIIS was tried).
288 std::vector<double> error_history_;
289 static constexpr Index kMaxConsecutiveFailures = 5;
290 static constexpr Index kAutoStartIteration = 50;
291 static constexpr Index kTrailingWindow = 10;
292 static constexpr double kMeanRatioTolerance = 1.125;
293
294 bool usedmixing_ = true;
295 double diiserror_ = std::numeric_limits<double>::max();
297 const AOOverlap* S_;
298
299 Eigen::MatrixXd Sminusahalf;
300 // Projector onto the directions removed from Sminusahalf, in the
301 // orthogonal coordinates of SolveFockmatrix; empty if none were removed.
302 Eigen::MatrixXd removed_projector_;
303 static constexpr double kRemovedShift = 1e3; // Hartree
304
305 // Extrapolation history, all three aligned entry by entry: Fock matrix,
306 // the density it was built from, and that pair's DIIS error. DIIS keeps
307 // its own copy of the error matrices and is trimmed at the same index.
308 std::vector<Eigen::MatrixXd> mathist_;
309 std::vector<Eigen::MatrixXd> dmatHist_;
310 std::vector<double> errhist_;
311 // Every energy, in order. Not trimmed with the history: DeltaE and the
312 // energy tests compare consecutive iterations.
313 std::vector<double> totE_;
314
315 // Lowest-energy point so far, for the energy-rise reset.
316 bool have_best_ = false;
317 double best_energy_ = 0.0;
318 Eigen::MatrixXd best_dmat_;
319 Eigen::MatrixXd best_H_;
321 // A reset may not fire again until the history has rebuilt, and only a
322 // few times per SCF: returning to the same point over and over must be
323 // impossible.
324 static constexpr Index kResetCooldown = 3;
325 static constexpr Index kMaxEnergyResets = 5;
327
328 // ADIIS instead of DIIS once the energy rises by more than this
329 // (Hartree) between iterations, even below DIIS_start.
330 static constexpr double kEnergyRiseForADIIS = 1e-4;
331
335};
336
337} // namespace xtp
338} // namespace votca
339
340#endif // VOTCA_XTP_CONVERGENCEACC_H
Eigen::MatrixXd DensityMatrix(const tools::EigenSystem &MOs) const
void Configure(const ConvergenceAcc::options &opt)
static constexpr double kMeanRatioTolerance
trah::StepOptions trah_options_
tools::EigenSystem trah_pre_MOs_
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_
void setLogger(Logger *log)
Attach the logger used for convergence diagnostics.
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
std::vector< double > errhist_
void setOrbitalHessian(const OrbitalHessian &hessian)
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.
static constexpr Index kMaxEnergyResets
Eigen::MatrixXd DensityMatrixGroundState_frac(const tools::EigenSystem &MOs) const
Construct a fractional-occupation density matrix from orbital occupations.
std::vector< double > totE_
static constexpr Index kMaxConsecutiveFailures
Eigen::MatrixXd SecondOrderStep(const Eigen::MatrixXd &dmat, const Eigen::MatrixXd &H, tools::EigenSystem &MOs, double totE)
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
static constexpr double kInitialTrustRadius
std::function< Eigen::MatrixXd( const tools::EigenSystem &MOs, const Eigen::MatrixXd &F, const Eigen::MatrixXd &X)> OrbitalHessian
std::vector< Eigen::MatrixXd > dmatHist_
KSmode
Occupation model used when constructing density matrices.
static constexpr double kEnergyRiseForADIIS
SpinDensity DensityMatrixSpinResolved(const tools::EigenSystem &MOs) const
SpinDensity DensityMatrixGroundState_restricted_open(const Eigen::MatrixXd &MOs) const
Logger is used for thread-safe output of messages.
Definition logger.h:164
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
Spin-resolved density matrices returned for open-shell SCF updates.
Eigen::MatrixXd total() const
Return the total density P = P^alpha + P^beta.
Eigen::MatrixXd spin() const
Return the spin density P^alpha - P^beta.