Loading...
Searching...
No Matches
init.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * SCFIterator — generic self-consistent field iteration control.
5 *
6 * Owns iteration limits, convergence state, and the template-method run() loop.
7 * Representation-specific data and physics live in derived iterators.
8 *
9 * The quantum system snapshot is provided via SCFIteratorConfig at construction time.
10 * run() decomposes into: initialize() → iterate() → report().
11 */
12
13#include "io/logger.hpp"
16
17#include <chrono>
18#include <optional>
19#include <vector>
20
21namespace q3 {
22
24 public:
26 : crystal_structure_(config.crystal_structure)
27 , convergence_threshold_(config.convergence_threshold)
28 , maximum_iterations_(config.maximum_iterations)
29 , number_of_spin_components_(config.number_of_spin_components) {}
30
31 virtual ~SCFIterator() = default;
32
33 /**
34 * The iteration in progress while run() iterates, numbered from 1.
35 *
36 * After run() it is the number of iterations performed, on either exit:
37 * the iteration the run converged on, or the whole budget when it did not.
38 */
39 [[nodiscard]] int iteration() const {
40 return iteration_;
41 }
42 [[nodiscard]] bool is_converged() const {
43 return is_converged_;
44 }
45 [[nodiscard]] int number_of_spin_components() const {
46 return number_of_spin_components_;
47 }
48
49 /**
50 * Total energy (Ry) after each completed iteration, oldest first.
51 *
52 * Recorded by the iterate() loop for every flavor, since the sequence is
53 * what shows *how* a run converged rather than only where it landed: a
54 * monotone descent, an oscillation, and a run that never moved all report
55 * the same final energy. The last entry equals total_energy() unless
56 * finalize() ran, which recomputes the energy components from the restored
57 * density after convergence.
58 *
59 * Empty before run(); one entry per iteration performed thereafter.
60 */
61 [[nodiscard]] const std::vector<Real>& total_energy_history_in_ry() const {
62 return total_energy_history_in_ry_;
63 }
64
65 virtual Real total_energy() const = 0;
66
67 /// Set the SCF convergence threshold (in Ry).
69 convergence_threshold_ = threshold;
70 }
71 /// Set the maximum number of SCF iterations.
72 void set_maximum_iterations(int maximum_iterations) {
73 maximum_iterations_ = maximum_iterations;
74 }
75
76 /// Transfer ownership of a Crystal into this iterator.
77 void set_owned_crystal(Crystal crystal) {
78 owned_crystal_ = std::move(crystal);
79 crystal_structure_ = &owned_crystal_.value();
80 }
81
82 void run() {
83 auto timer_start = std::chrono::steady_clock::now();
84 initialize();
85 auto timer_after_initialization = std::chrono::steady_clock::now();
86 iterate();
87 auto timer_after_iteration = std::chrono::steady_clock::now();
88 report();
89 auto timer_end = std::chrono::steady_clock::now();
90
91 auto duration_milliseconds = [](auto start, auto end) {
92 return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
93 };
94 log::info(" Timing: init={}ms iterate={}ms report={}ms total={}ms",
95 duration_milliseconds(timer_start, timer_after_initialization),
96 duration_milliseconds(timer_after_initialization, timer_after_iteration),
97 duration_milliseconds(timer_after_iteration, timer_end),
98 duration_milliseconds(timer_start, timer_end));
99 }
100
101 protected:
102 // Configuration — set from SCFIteratorConfig at construction.
106
108 bool is_converged_{false};
109
110 private:
111 int number_of_spin_components_;
112 /// Appended by iterate(); read through total_energy_history_in_ry().
113 std::vector<Real> total_energy_history_in_ry_;
114 /**
115 * Optional owned Crystal — used when the factory constructs the iterator
116 * from InputData (the iterator owns the crystal rather than borrowing it).
117 */
118 std::optional<Crystal> owned_crystal_;
119
120 protected:
121 /// Base implementation prints the SCF banner. Children call SCFIterator::initialize() last.
122 virtual void initialize() {
123 iteration_ = 0;
124 is_converged_ = false;
125 /**
126 * Run state, like the two above: a second run() reports its own
127 * trajectory rather than one appended to the previous run's.
128 */
129 total_energy_history_in_ry_.clear();
130
131 log::section("Self-consistent Calculation:");
132 }
133
134 /// Main iteration loop.
135 virtual void iterate() {
136 /**
137 * iteration_ is assigned from the loop counter rather than being it, so
138 * it never steps past the last iteration performed. Were it the counter,
139 * a run that exhausted its budget would leave it one past that budget,
140 * and the count read after run() -- by the SCF state, the checkpoint
141 * header and the results file -- would name an iteration that never ran.
142 */
143 for (int iteration_number = 1; iteration_number <= maximum_iterations_;
144 ++iteration_number) {
145 iteration_ = iteration_number;
147 total_energy_history_in_ry_.push_back(total_energy());
148
150 is_converged_ = true;
151 finalize();
152 return;
153 }
154
156 }
157 }
158
159 /// Report convergence or non-convergence after iterate() finishes.
160 virtual void report() {
161 if (!is_converged_) {
163 } else {
165 }
166 }
167
168 virtual void perform_iteration() = 0;
169 virtual bool is_iteration_converged() const = 0;
170 virtual void finalize() = 0;
171 virtual void report_iteration() = 0;
172 virtual void report_non_convergence() = 0;
173 virtual void report_convergence() = 0;
174
175 public:
176 /**
177 * Write binary checkpoint files (charge density, wavefunctions) to the given directory.
178 *
179 * Each level of the hierarchy overrides this, calling the parent first,
180 * then adding its own checkpoint data. Default no-op.
181 */
182 virtual void write_checkpoints(const std::string& output_directory,
183 const std::string& prefix) const {
184 (void)output_directory;
185 (void)prefix;
186 }
187};
188
189} // namespace q3
void run()
Definition init.hpp:82
Crystal * crystal_structure_
Definition init.hpp:103
virtual void report_non_convergence()=0
virtual void finalize()=0
Real convergence_threshold_
Definition init.hpp:104
virtual Real total_energy() const =0
virtual ~SCFIterator()=default
virtual void report_iteration()=0
virtual void write_checkpoints(const std::string &output_directory, const std::string &prefix) const
Definition init.hpp:182
virtual void initialize()
Base implementation prints the SCF banner. Children call SCFIterator::initialize() last.
Definition init.hpp:122
SCFIterator(SCFIteratorConfig config)
Definition init.hpp:25
virtual void perform_iteration()=0
int iteration() const
Definition init.hpp:39
bool is_converged_
Definition init.hpp:108
virtual bool is_iteration_converged() const =0
virtual void report()
Report convergence or non-convergence after iterate() finishes.
Definition init.hpp:160
int number_of_spin_components() const
Definition init.hpp:45
void set_convergence_threshold(Real threshold)
Set the SCF convergence threshold (in Ry).
Definition init.hpp:68
void set_owned_crystal(Crystal crystal)
Transfer ownership of a Crystal into this iterator.
Definition init.hpp:77
const std::vector< Real > & total_energy_history_in_ry() const
Definition init.hpp:61
virtual void iterate()
Main iteration loop.
Definition init.hpp:135
int maximum_iterations_
Definition init.hpp:105
bool is_converged() const
Definition init.hpp:42
virtual void report_convergence()=0
void set_maximum_iterations(int maximum_iterations)
Set the maximum number of SCF iterations.
Definition init.hpp:72
void section(const std::string &title)
Definition logger.hpp:206
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
double Real