MADNESS 0.10.1
Restart.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30*/
31
32/// \file Restart.h
33/// \brief the header of a restartdata archive, in one place
34
35#ifndef MADNESS_CHEM_RESTART_H__INCLUDED
36#define MADNESS_CHEM_RESTART_H__INCLUDED
37
39#include <madness/mra/mra.h>
40
41#include <iomanip>
42#include <optional>
43#include <string>
44
45namespace madness {
46
47/// what the functions in a restartdata archive actually ARE
48///
49/// moldft stores psi; nemo stores the regularized F = psi/R; znemo the complex
50/// variant. Both engines write the same filename with the same version tag, so
51/// without this the two are indistinguishable and loading one as the other is
52/// silently wrong.
53///
54/// The numeric values are part of the archive format: they are written as an int.
55/// Add new members at the end and NEVER renumber an existing one, or archives
56/// already on disk change meaning.
57enum class Representation : int {
58 unknown = 0, ///< not recorded; a version-4 archive, which predates the field
59 mo = 1, ///< moldft orbitals psi
60 nemo = 2, ///< nemo's regularized orbitals F = psi/R
61 znemo = 3, ///< complex regularized orbitals
62};
63
64inline std::string to_string(const Representation r) {
65 switch (r) {
66 case Representation::mo: return "mo";
67 case Representation::nemo: return "nemo";
68 case Representation::znemo: return "znemo";
69 default: return "unknown";
70 }
71}
72
73/// map a stored int back to a Representation, tolerating values we do not know
75 switch (i) {
76 case 1: return Representation::mo;
77 case 2: return Representation::nemo;
78 case 3: return Representation::znemo;
79 default: return Representation::unknown; // includes a newer writer's value
80 }
81}
82
83/// the header of a `<prefix>.restartdata` archive
84///
85/// This is the single definition of that header. Before it existed the layout
86/// was open-coded in four places -- SCF::save_mos, SCF::load_mos,
87/// MolecularOrbitals::{save,read}_restartdata, and two response tools -- which
88/// had to be kept in field order by hand.
89///
90/// The orbitals follow the header in the archive, one block per spin (see
91/// MolecularOrbitals::{save,load}_mos). Everything needed to decide whether an
92/// archive is usable lives *in the header*, so it can be inspected without
93/// reading a single MRA coefficient -- see peek_restartdata().
94///
95/// ## Versioning
96///
97/// Version 5 appends to version 4 rather than reordering it, so the v4 layout is
98/// a strict prefix of v5. read() therefore dispatches on the stored version and
99/// reads the tail only when it is there; v4 archives written by any earlier
100/// MADNESS remain loadable, and the fields they lack take the defaults below.
101/// write() always emits the current version.
102///
103/// If you add a field: append it, bump CURRENT_VERSION, and give it a default
104/// that means "unknown" so a v4 archive stays meaningful. Do NOT reorder.
106
107 static constexpr unsigned int CURRENT_VERSION = 5;
108
109 /// version of the archive this was read from, or CURRENT_VERSION for a fresh one
110 unsigned int version = CURRENT_VERSION;
111
112 // --- version 4 fields, in archive order -------------------------------
113 double current_energy = 1.e10;
114 bool spin_restricted = true;
115 double L = 0.0;
116 int k = 0;
118 std::string xc;
119 std::string localize;
120 double converged_for_thresh = 1.e10;
121
122 // --- appended in version 5 --------------------------------------------
123
124 /// density convergence the orbitals were converged to. Not stored by v4, so
125 /// a v4 archive reads back as "unconverged" on this axis rather than
126 /// claiming a convergence it never recorded.
127 double converged_for_dconv = 1.e10;
128
129 /// what the stored functions are; see Representation
131
132 /// nuclear correlation factor, e.g. "slater:2.0". Meaningful for nemo/znemo
133 /// only; an empty string means none was recorded.
134 std::string ncf;
135
136 /// molecular smoothing parameter. Changes the nuclear potential, so orbitals
137 /// from a different eprec solve a different Hamiltonian and are not the same
138 /// solution -- unlike k or thresh, this cannot be repaired by reprojecting.
139 double eprec = 0.0;
140
141 /// MADNESS version that wrote the archive, for provenance in bug reports
142 std::string madness_version;
143
144 // NB: no truncate_mode, k-per-function, thresh-per-function or tree_state
145 // here. FunctionImpl::store/load already serialize those with every orbital
146 // (mra/funcimpl.h), so the loaded functions carry them -- which is how
147 // SCF::load_mos can query amo[0].k() and amo[0].thresh() after loading.
148 // Duplicating them in the header would create two sources of truth in one
149 // file. Only what is needed BEFORE any orbital is read belongs here.
150
151 /// write the header at the current archive position, always at CURRENT_VERSION
152 template <typename Archive>
153 void write(Archive& ar) const {
154 const unsigned int v = CURRENT_VERSION;
155 ar & v;
158 // version 5 tail. The representation goes out as an int; see the warning
159 // on Representation about never renumbering.
160 const int rep = static_cast<int>(representation);
162 }
163
164 /// read the header from the current archive position
165 ///
166 /// Accepts version 4 and 5. Throws on anything else, since the field order
167 /// would be unknown and the archive position left wrong for the orbitals.
168 template <typename Archive>
169 void read(Archive& ar) {
170 ar & version;
172 "unsupported restartdata version: only 4 and 5 can be read");
173
176
177 if (version >= 5) {
178 int rep = 0;
181 }
182 // else: the v5 members keep their defaults, which all read as "unknown"
183 }
184
185 /// true if these orbitals are at least as converged as the request
186 bool is_converged_to(const double thresh, const double dconv) const {
188 }
189
190 /// true if the stored functions are what the asking engine expects
191 ///
192 /// A v4 archive records no representation; treat that as compatible rather
193 /// than rejecting every pre-existing archive, and rely on the geometry and
194 /// convergence checks instead.
198
199 std::string print_to_string() const {
200 std::stringstream ss;
201 ss << "restartdata v" << version
202 << " representation " << madness::to_string(representation)
203 << (ncf.empty() ? std::string() : " ncf " + ncf)
204 << " k " << k << " L " << L
205 << " converged to thresh " << converged_for_thresh
206 << " dconv " << converged_for_dconv
207 << " energy " << current_energy;
208 return ss.str();
209 }
210};
211
212/// read ONLY the header of a restartdata archive, without loading any orbitals
213///
214/// This is what makes "is the archive on disk good enough?" answerable cheaply:
215/// SCF::load_mos pulls every MRA function off disk before any of the header can
216/// be looked at.
217///
218/// @param[in] world the world; the parallel archive broadcasts, so all ranks
219/// see the same result and the return value is collective
220/// @param[in] filename archive name WITHOUT the chunk suffix, e.g. "mad.restartdata"
221/// @return the header, or nullopt if the archive is absent or unreadable
222inline std::optional<RestartMetadata>
223peek_restartdata(World& world, const std::string& filename) {
224 try {
226 ar(world, filename.c_str());
227 RestartMetadata meta;
228 meta.read(ar);
229 return meta;
230 } catch (...) {
231 // absent, truncated, or a version we cannot parse. The caller decides
232 // whether that is benign (nothing to restart from) or alarming (a file
233 // exists but cannot be read) -- see restartdata_exists().
234 return std::nullopt;
235 }
236}
237
238/// the header plus the alpha orbital bookkeeping, still without any MRA data
239///
240/// The archive is laid out as
241/// header, then per spin: uint nmo, Tensor eps, Tensor occ, vector<int> set,
242/// then nmo MRA functions
243/// (SCF::save_mos / SCF::load_mos). So nmo, the orbital energies and the
244/// occupations of the ALPHA block sit immediately after the header and cost
245/// nothing to read. The beta block is on the far side of every alpha function,
246/// so it is deliberately not part of a cheap peek.
249 std::size_t nmo_alpha = 0;
250 Tensor<double> aeps; ///< alpha orbital energies
251 Tensor<double> aocc; ///< alpha occupations
252 std::vector<int> aset; ///< localization sets
253};
254
255/// read the header and the alpha orbital energies/occupations, no MRA functions
256///
257/// @param[in] filename archive name WITHOUT the chunk suffix
258/// @return nullopt if the archive is absent or cannot be parsed
259inline std::optional<RestartSummary>
260peek_restartdata_summary(World& world, const std::string& filename) {
261 try {
263 ar(world, filename.c_str());
265 s.meta.read(ar);
266 unsigned int nmo = 0;
267 ar & nmo;
268 s.nmo_alpha = nmo;
269 ar & s.aeps & s.aocc & s.aset;
270 return s;
271 } catch (...) {
272 return std::nullopt;
273 }
274}
275
276/// strip the decorations a user is likely to type, leaving the archive base name
277///
278/// Accepts `he`, `he.restartdata` and `he.restartdata.00000` alike -- the chunk
279/// suffix in particular is what tab completion puts on the command line, and
280/// making the user delete it by hand is a pointless trap.
281inline std::string restartdata_basename(std::string name) {
282 const std::string chunk = ".00000";
283 if (name.size() > chunk.size() and
284 name.compare(name.size() - chunk.size(), chunk.size(), chunk) == 0)
285 name.erase(name.size() - chunk.size());
286 const std::string suffix = ".restartdata";
287 if (name.size() > suffix.size() and
288 name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0)
289 name.erase(name.size() - suffix.size());
290 return name;
291}
292
293/// true if the first chunk of a restartdata archive is present on disk
294///
295/// Lets a caller tell "no archive" from "archive present but unreadable", which
296/// want different reactions: the first is normal, the second is a corrupt file
297/// or a format mismatch and should be reported rather than silently recomputed.
298inline bool restartdata_exists(const std::string& filename) {
299 return std::filesystem::exists(filename + ".00000");
300}
301
302/// print everything a restartdata archive can say about itself, and nothing more
303///
304/// This is what answers "why did/didn't my restart fire?" without starting a
305/// calculation. It reports only what is actually recorded: fields a version-4
306/// archive never stored are shown as "not recorded" rather than as their
307/// placeholder values, because 1e10 masquerading as a convergence threshold has
308/// misled people before.
309///
310/// @param[in] prefix archive base name; `.restartdata`/`.00000` are optional
311/// @return false if there was nothing to read
312inline bool print_restartdata_info(World& world, const std::string& prefix) {
313
314 const std::string base = restartdata_basename(prefix);
315 const std::string archive = base + ".restartdata";
316
317 if (not restartdata_exists(archive)) {
318 if (world.rank() == 0)
319 print("no restart data: there is no file", archive + ".00000");
320 return false;
321 }
322
323 const auto summary = peek_restartdata_summary(world, archive);
324 if (not summary.has_value()) {
325 if (world.rank() == 0) {
326 print(archive + ".00000", "exists but could not be read.");
327 print("It is truncated, or was written by a newer MADNESS than this one");
328 print("(this build reads restartdata versions 4 and", RestartMetadata::CURRENT_VERSION, ").");
329 }
330 return false;
331 }
332 if (world.rank() != 0) return true;
333
334 const RestartMetadata& m = summary.value().meta;
335 auto or_unset = [](const double v, const double unset = 1.e10) {
336 std::stringstream ss;
337 if (v == unset) ss << "not recorded";
338 else ss << std::scientific << std::setprecision(2) << v;
339 return ss.str();
340 };
341
342 print("");
343 print("restart data in", archive + ".00000");
344 print(" format version ", m.version);
345 print(" written by MADNESS ", m.madness_version.empty() ? "not recorded" : m.madness_version);
346 print(" representation ", madness::to_string(m.representation),
347 m.representation == Representation::mo ? "(moldft orbitals psi)" :
348 m.representation == Representation::nemo ? "(nemo orbitals F = psi/R)" :
349 m.representation == Representation::znemo ? "(complex regularized orbitals)" :
350 "(version-4 archive; predates the field)");
351 if (not m.ncf.empty()) print(" nuclear corr. factor", m.ncf);
352 print(" box size L ", m.L);
353 print(" polynomial order k ", m.k);
354 print(" eprec ", or_unset(m.eprec, 0.0));
355 // the energy is what most people open this for, so print it at full width
356 // rather than in the scientific short form used for the thresholds
357 if (m.current_energy == 1.e10) {
358 print(" energy not recorded");
359 } else {
360 std::stringstream se;
361 se << std::fixed << std::setprecision(10) << m.current_energy;
362 print(" energy ", se.str());
363 }
364 print(" converged to thresh ", or_unset(m.converged_for_thresh));
365 print(" converged to dconv ", or_unset(m.converged_for_dconv));
366 print(" xc / localize ", m.xc, "/", m.localize);
367 print(" alpha orbitals ", summary.value().nmo_alpha);
368 if (summary.value().aeps.size() > 0)
369 print(" alpha eigenvalues ", summary.value().aeps);
370 if (summary.value().aocc.size() > 0)
371 print(" alpha occupations ", summary.value().aocc);
372 print(" spin restricted ", m.spin_restricted ? "yes" : "no");
373 if (not m.spin_restricted) {
374 print(" (beta orbital energies sit after the alpha functions in the");
375 print(" archive, so they are not part of a header-only peek)");
376 }
377 print(" geometry:");
378 m.molecule.print();
379 print("");
380 print("A restart is used as-is only if this archive is converged at least as");
381 print("tightly as the run asks for -- compare `converged to thresh/dconv`");
382 print("against `protocol` (its last entry) and `dconv`. It is used as a guess,");
383 print("with iterations continuing, otherwise. Anything printed as");
384 print("\"not recorded\" counts as not converged.");
385 print("");
386 return true;
387}
388
389} // namespace madness
390
391#endif // MADNESS_CHEM_RESTART_H__INCLUDED
Definition molecule.h:129
A tensor is a multidimensional array.
Definition tensor.h:318
A parallel world class.
Definition world.h:134
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
An archive for storing local or parallel data, wrapping a BinaryFstreamInputArchive.
Definition parallel_archive.h:366
static const double v
Definition hatom_sf_dirac.cc:20
#define MADNESS_CHECK_THROW(condition, msg)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:207
Main include file for MADNESS and defines Function interface.
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
std::string to_string(const Representation r)
Definition Restart.h:64
static const char * filename
Definition legendre.cc:96
std::string restartdata_basename(std::string name)
Definition Restart.h:281
Representation representation_from_int(const int i)
map a stored int back to a Representation, tolerating values we do not know
Definition Restart.h:74
@ unknown
Definition funcdefaults.h:68
bool restartdata_exists(const std::string &filename)
Definition Restart.h:298
std::optional< RestartSummary > peek_restartdata_summary(World &world, const std::string &filename)
Definition Restart.h:260
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:227
bool print_restartdata_info(World &world, const std::string &prefix)
Definition Restart.h:312
std::optional< RestartMetadata > peek_restartdata(World &world, const std::string &filename)
Definition Restart.h:223
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
std::string name(const FuncType &type, const int ex=-1)
Definition ccpairfunction.h:28
Representation
Definition Restart.h:57
@ mo
moldft orbitals psi
@ unknown
not recorded; a version-4 archive, which predates the field
@ znemo
complex regularized orbitals
@ nemo
nemo's regularized orbitals F = psi/R
static const double m
Definition relops.cc:9
static const double thresh
Definition rk.cc:45
Definition Restart.h:105
void read(Archive &ar)
Definition Restart.h:169
bool is_converged_to(const double thresh, const double dconv) const
true if these orbitals are at least as converged as the request
Definition Restart.h:186
double converged_for_dconv
Definition Restart.h:127
std::string ncf
Definition Restart.h:134
void write(Archive &ar) const
write the header at the current archive position, always at CURRENT_VERSION
Definition Restart.h:153
double eprec
Definition Restart.h:139
std::string print_to_string() const
Definition Restart.h:199
bool representation_matches(const Representation wanted) const
Definition Restart.h:195
static constexpr unsigned int CURRENT_VERSION
Definition Restart.h:107
int k
Definition Restart.h:116
std::string madness_version
MADNESS version that wrote the archive, for provenance in bug reports.
Definition Restart.h:142
unsigned int version
version of the archive this was read from, or CURRENT_VERSION for a fresh one
Definition Restart.h:110
bool spin_restricted
Definition Restart.h:114
double L
Definition Restart.h:115
Molecule molecule
Definition Restart.h:117
std::string localize
Definition Restart.h:119
double converged_for_thresh
Definition Restart.h:120
std::string xc
Definition Restart.h:118
Representation representation
what the stored functions are; see Representation
Definition Restart.h:130
double current_energy
Definition Restart.h:113
Definition Restart.h:247
std::vector< int > aset
localization sets
Definition Restart.h:252
Tensor< double > aeps
alpha orbital energies
Definition Restart.h:250
RestartMetadata meta
Definition Restart.h:248
std::size_t nmo_alpha
Definition Restart.h:249
Tensor< double > aocc
alpha occupations
Definition Restart.h:251
Definition dirac-hatom.cc:112