Loading...
Searching...
No Matches
qe.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * QEInputParser — Quantum ESPRESSO pw.x input file parser.
5 *
6 * Reads QE-style .in files with &control, &system, &electrons, &ions and &cell
7 * namelists and the ATOMIC_SPECIES, ATOMIC_POSITIONS, K_POINTS, CELL_PARAMETERS
8 * and HUBBARD cards.
9 *
10 * The policy on anything else is the one every input reader follows, for one
11 * reason: a drop-in replacement that silently computes different
12 * physics than the code it replaces is worse than one that refuses to run.
13 *
14 * supported mapped onto the Q3 equivalent
15 * recognized but
16 * unimplemented HARD ERROR naming the key and the reason
17 * unknown HARD ERROR naming the key
18 * physics-neutral accepted and ignored, with a note in the log
19 *
20 * This parser ignored everything it did not recognize until 2026-08-08, which
21 * is how a `HUBBARD` card came to be dropped without a word: a PAW+U input ran
22 * to convergence and reported the no-U energy.
23 */
24
25#include <algorithm>
26#include <cctype>
27#include <cmath>
28#include <fstream>
29#include <map>
30#include <set>
31#include <sstream>
32#include <stdexcept>
33#include <string>
34#include <vector>
35
37#include "io/logger.hpp"
40
41namespace q3 {
42
43namespace qe_input {
44
45/**
46 * Keys that steer output, restart or parallel layout and cannot move a
47 * converged result. Accepted with a note.
48 *
49 * `nstep` sits here for the SCF path only — the relax path picks it up as
50 * `relax_maximum_steps`, so it is honoured where it means something and inert
51 * where it does not.
52 */
53inline const std::set<std::string>& physics_neutral_keys() {
54 static const std::set<std::string> keys = {
55 // &CONTROL: what gets written, how loudly, and for how long.
56 "title",
57 "iprint",
58 "disk_io",
59 "wf_collect",
60 "max_seconds",
61 "tprnfor",
62 "tstress",
63 "lkpoint_dir",
64 "wfcdir",
65 // &ELECTRONS: they steer how the SCF walks, never where it converges.
66 "startingwfc",
67 "diago_thr_init",
68 "diago_david_ndim",
69 "diago_full_acc",
70 "diago_cg_maxiter",
71 "adaptive_thr",
72 "conv_thr_init",
73 "conv_thr_multi",
74 };
75 return keys;
76}
77
78/**
79 * Keys Q3 recognizes but does not implement, each with the reason. The reason
80 * is the point: it tells the user whether to wait for a feature or change the
81 * input.
82 */
83inline const std::map<std::string, std::string>& unimplemented_keys() {
84 static const std::map<std::string, std::string> keys = {
85 {"noncolin", "noncollinear magnetism is not implemented"},
86 {"tqr",
87 "real-space augmentation-charge treatment is an approximation with its own "
88 "convergence behaviour, not a speed switch"},
89 {"nr1",
90 "an explicitly forced FFT grid is not implemented; Q3 sizes its grids from the "
91 "cutoffs, and a grid that differs from QE's changes the result"},
92 {"nr2", "see nr1"},
93 {"nr3", "see nr1"},
94 {"nr1s", "see nr1"},
95 {"nr2s", "see nr1"},
96 {"nr3s", "see nr1"},
97 {"la2f", "electron-phonon coupling is not implemented"},
98 {"twochem", "separate electron and hole chemical potentials are not implemented"},
99 {"nelec_cond", "see twochem"},
100 {"degauss_cond", "see twochem"},
101 {"tbeta_smoothing",
102 "smoothing the beta projectors changes the pseudopotential and is "
103 "not implemented"},
104 {"tq_smoothing", "see tbeta_smoothing"},
105 {"xdm_a1", "see xdm"},
106 {"xdm_a2", "see xdm"},
107 {"ts_vdw_isolated", "see ts_vdw"},
108 {"ts_vdw_econv_thr", "see ts_vdw"},
109 {"london_rcut", "see london"},
110 {"london_s8", "see london"},
111 {"london_c6", "see london"},
112 {"real_space",
113 "real-space beta projection is an approximation with its own convergence "
114 "behaviour, not a speed switch, so it is not silently ignored"},
115 {"nosym_evc",
116 "symmetrizing the k-point set without symmetrizing the calculation is not "
117 "implemented; use nosym"},
118 {"lspinorb", "spin-orbit coupling requires noncollinear magnetism, which Q3 does not have"},
119 {"angle1", "noncollinear magnetization directions require noncollinear magnetism"},
120 {"angle2", "noncollinear magnetization directions require noncollinear magnetism"},
121 {"constrained_magnetization", "magnetization constraints are not implemented"},
122 {"tot_magnetization",
123 "fixed total magnetization is not implemented; use starting_magnetization, which sets "
124 "the initial guess only"},
125 {"exx_fraction", "hybrid functionals are not implemented"},
126 {"ecutfock", "hybrid functionals are not implemented"},
127 {"screening_parameter", "screened hybrids are not implemented"},
128 {"exxdiv_treatment", "hybrid functionals are not implemented"},
129 {"nqx1", "hybrid functionals are not implemented"},
130 {"nqx2", "hybrid functionals are not implemented"},
131 {"nqx3", "hybrid functionals are not implemented"},
132 {"lda_plus_u",
133 "the legacy namelist DFT+U syntax is not read; use the HUBBARD card "
134 "(`HUBBARD (atomic)` / `U <species>-<shell> <eV>`), which names the shell "
135 "explicitly instead of deriving it from the element"},
136 {"lda_plus_u_kind", "see lda_plus_u"},
137 {"hubbard_u", "see lda_plus_u"},
138 {"hubbard_j0", "Q3 implements the Dudarev U only"},
139 {"hubbard_alpha", "Q3 implements the Dudarev U only"},
140 {"hubbard_beta", "Q3 implements the Dudarev U only"},
141 {"u_projection_type", "see lda_plus_u; the HUBBARD card carries the projector flavour"},
142 {"nbnd_cond",
143 "conduction-band counting for two-chemical-potential runs is not implemented"},
144 {"nelec", "explicit electron counts are not implemented; use tot_charge"},
145 {"tefield", "sawtooth external fields are not implemented"},
146 {"dipfield", "the dipole correction is not implemented"},
147 {"edir", "see tefield"},
148 {"eamp", "see tefield"},
149 {"eopreg", "see tefield"},
150 {"emaxpos", "see tefield"},
151 {"lelfield", "finite electric fields are not implemented"},
152 {"lberry", "Berry-phase polarization is not implemented"},
153 {"gdir", "see lberry"},
154 {"nppstr", "see lberry"},
155 {"esm_bc", "effective screening medium boundaries are not implemented"},
156 {"london", "the London dispersion correction is not mapped; Q3 implements D3(BJ)"},
157 {"london_s6", "see london"},
158 {"xdm", "the XDM dispersion correction is not implemented"},
159 {"ts_vdw", "the Tkatchenko-Scheffler correction is not implemented"},
160 {"nosym_but_time_reversal", "partial symmetry control is not implemented; use nosym"},
161 {"noinv",
162 "time-reversal control is not implemented separately from nosym, which disables both"},
163 {"force_symmorphic", "restricting to symmorphic operations is not implemented"},
164 {"use_all_frac", "fractional-translation control is not implemented"},
165 {"one_atom_occupations", "per-atom occupation constraints are not implemented"},
166 {"press",
167 "target-pressure relaxation is a Python workflow, not an engine setting: "
168 "q3.workflows.vc_relax.run_vc_relax_workflow(target_pressure_gpa=press/10). "
169 "The engine itself relaxes to zero stress"},
170 {"press_conv_thr", "see press"},
171 {"cell_dofree", "constrained cell relaxation is not implemented"},
172 {"cell_factor", "the variable-cell basis-enlargement factor is not implemented"},
173 {"wmass", "cell dynamics is not implemented"},
174 };
175 return keys;
176}
177
178/**
179 * pw.x's ionic-step settings that Q3 does not use, each with what Q3 does instead.
180 *
181 * A relaxation runs as one SCF per geometry, driven by q3.workflows with ASE's
182 * BFGS. These steer pw.x's own optimizer and extrapolation -- the path to the
183 * minimum, never the minimum itself -- so a deck carrying them is read, and the
184 * log says each one is not used and why.
185 */
186inline const std::map<std::string, std::string>& unused_ionic_step_keys() {
187 static const std::map<std::string, std::string> keys = {
188 {"pot_extrapolation", "each geometry starts from the previous one's saved density"},
189 {"wfc_extrapolation", "each geometry starts its wavefunctions afresh"},
190 {"trust_radius_max", "ASE's BFGS limits a step with its own maxstep"},
191 {"trust_radius_min", "ASE's BFGS limits a step with its own maxstep"},
192 {"trust_radius_ini", "ASE's BFGS limits a step with its own maxstep"},
193 {"bfgs_ndim", "ASE's BFGS keeps the full Hessian"},
194 {"upscale", "each SCF converges to the input's conv_thr"},
195 };
196 return keys;
197}
198
199/**
200 * Keys whose QE default switches the feature off, and the value that means
201 * off.
202 *
203 * Real inputs state defaults explicitly all the time, and `noinv = .false.` is
204 * a request for exactly what Q3 already does — refusing it would be a false
205 * refusal. `.false.` is compared as a Fortran logical (`.F.`, `F` and `false`
206 * all count); any other token is compared literally.
207 */
208inline const std::map<std::string, std::string>& keys_inert_at_default() {
209 static const std::map<std::string, std::string> keys = {
210 {"noncolin", ".false."},
211 {"lspinorb", ".false."},
212 {"tefield", ".false."},
213 {"dipfield", ".false."},
214 {"lelfield", ".false."},
215 {"lberry", ".false."},
216 {"london", ".false."},
217 {"xdm", ".false."},
218 {"ts_vdw", ".false."},
219 {"real_space", ".false."},
220 {"tqr", ".false."},
221 {"twochem", ".false."},
222 {"tbeta_smoothing", ".false."},
223 {"tq_smoothing", ".false."},
224 {"one_atom_occupations", ".false."},
225 {"la2f", ".false."},
226 {"lda_plus_u", ".false."},
227 {"noinv", ".false."},
228 {"nosym_evc", ".false."},
229 {"force_symmorphic", ".false."},
230 {"use_all_frac", ".false."},
231 {"constrained_magnetization", "none"},
232 {"tot_magnetization", "-10000"},
233 {"esm_bc", "pbc"},
234 {"pot_extrapolation", "atomic"},
235 {"wfc_extrapolation", "none"},
236 };
237 return keys;
238}
239
240/**
241 * Keys this parser reads, by namelist.
242 *
243 * This is the one table that has to be kept in step with the dispatch below by
244 * hand. Drift is self-announcing in the safe direction: forgetting to list a
245 * newly handled key makes the parser refuse an input it can actually run, which
246 * fails loudly on the next test run rather than producing a wrong number.
247 */
248inline const std::map<std::string, std::set<std::string>>& honoured_keys_by_namelist() {
249 static const std::map<std::string, std::set<std::string>> keys = {
250 {"control",
251 {"calculation",
252 "pseudo_dir",
253 "prefix",
254 "outdir",
255 "verbosity",
256 "npool",
257 "nstep",
258 "etot_conv_thr",
259 "forc_conv_thr",
260 "restart_mode"}},
261 {"system",
262 {
263 // Cell
264 "ibrav",
265 "celldm",
266 "a",
267 "b",
268 "c",
269 "cosab",
270 "cosac",
271 "cosbc",
272 // Contents and basis
273 "nat",
274 "ntyp",
275 "ecutwfc",
276 "ecutrho",
277 "nbnd",
278 "tot_charge",
279 // Model
280 "input_dft",
281 "vdw_corr",
282 "dftd3_version",
283 "dftd3_threebody",
284 "assume_isolated",
285 // Occupations and spin
286 "occupations",
287 "smearing",
288 "degauss",
289 "nspin",
290 "starting_magnetization",
291 "starting_ns_eigenvalue",
292 "number_of_spin_components", // rejected with guidance, not read
293 // Symmetry
294 "nosym",
295 }},
296 {"electrons",
297 {"mixing_fixed_ns",
298 "conv_thr",
299 "mixing_beta",
300 "mixing_mode",
301 "mixing_ndim",
302 "electron_maxstep",
303 "diagonalization",
304 "scf_must_converge",
305 "startingpot"}},
306 {"ions", {"ion_dynamics"}},
307 {"cell", {"cell_dynamics"}},
308 };
309 return keys;
310}
311
312} // namespace qe_input
313
315 public:
316 InputData parse(const std::string& filename) const override {
317 InputData data;
318 /**
319 * A pw.x deck runs on pw.x's wavefunction grid, sized for 4 x ecutwfc. pw.x has
320 * no switch for it, so neither does this reader. See
321 * InputData::use_exact_wavefunction_grid.
322 */
323 data.use_exact_wavefunction_grid = true;
324 /**
325 * pw.x mixes with mixing_mode = 'plain' unless the deck says otherwise,
326 * and 'plain' is Broyden with no preconditioning. Q3's own default
327 * ("default") adds Kerker whenever smearing is on, which is a different
328 * mixer: on a metallic slab it damps exactly the long-wavelength charge
329 * that converges slowest, and a Ni(111) slab that pw.x converges in 24
330 * iterations took Q3 85. A deck that names `TF` or `local-TF` still gets
331 * its preconditioner below.
332 */
333 data.mixing_preconditioning = "none";
334 /// Raw 1-based starting_ns_eigenvalue (m, ispin, ityp, f), resolved to symbols post-parse.
335 std::vector<RawStartingNsEigenvalue> raw_starting_ns_eigenvalues;
336 std::ifstream input_file(filename);
337 if (!input_file.is_open())
338 throw std::runtime_error("Cannot open input file: " + filename);
339
340 std::string line;
341 std::string section;
342
343 /**
344 * CELL_PARAMETERS card state (applied after the whole file is read,
345 * because celldm(1) may appear before or after the card). The units
346 * string is empty for a card that names none; what that means depends
347 * on whether a lattice parameter is given, so it is resolved then too.
348 */
349 Matrix3x3 cell_parameters_vectors{};
350 std::string cell_parameters_units;
351 int cell_parameters_row = 0;
352 bool has_cell_parameters = false;
353
354 /**
355 * A/B/C/cosAB/cosAC/cosBC state, converted to celldm after the file is
356 * read (which celldm each cosine becomes depends on ibrav).
357 */
358 Real lattice_constant_a = 0.0, lattice_constant_b = 0.0, lattice_constant_c = 0.0;
359 Real cosine_ab = 0.0, cosine_ac = 0.0, cosine_bc = 0.0;
360 bool has_crystallographic_constants = false;
361
362 /**
363 * ATOMIC_POSITIONS units option. The rows are stored as read and
364 * converted after the whole file is read, because the cell they may be
365 * Cartesian in can be given after the card.
366 */
367 std::string atomic_positions_units = "crystal";
368
369 /**
370 * Whether the deck states the two DFT-D3 sub-options itself. pw.x's
371 * defaults for them are not what Q3 implements, so a D3 deck that
372 * leaves either one out is refused once the whole file is read --
373 * QE does not require them to follow vdw_corr. A stated value Q3
374 * does not implement is refused where it is read.
375 */
376 bool has_dftd3_version = false;
377 bool has_dftd3_threebody = false;
378
379 while (std::getline(input_file, line)) {
380 /**
381 * Drop trailing comments before anything reads the line: a key scan
382 * that sees `celldm(1) = 10.2 ! a = 5 in the old cell` would find a
383 * key `a` in the prose.
384 */
385 line = strip_comment(line);
386 std::string trimmed = trim(line);
387 if (trimmed.empty())
388 continue;
389
390 std::string lower = to_lower(trimmed);
391
392 /**
393 * Namelist headers. Every `&name` is matched, not only the ones Q3
394 * reads: an unrecognized header used to leave `section` pointing at
395 * the previous namelist, so `&IONS`'s keys were scanned as
396 * `&ELECTRONS`'s and silently found nothing.
397 *
398 * Fortran lets assignments share the header's line, and the closing
399 * `/` too: `&ELECTRONS conv_thr = 1.0d-10, mixing_beta = 0.3 /`. The
400 * rest of the line therefore goes on below as an ordinary namelist
401 * line. It used to be thrown away with the header, so a deck written
402 * that way ran on Q3's defaults for every key on those lines.
403 */
404 if (lower[0] == '&') {
405 size_t name_end =
406 lower.find_first_not_of("abcdefghijklmnopqrstuvwxyz0123456789_", 1);
407 if (name_end == std::string::npos)
408 name_end = lower.size();
409 section = lower.substr(1, name_end - 1);
410 // `&END` is Fortran's older namelist terminator, equivalent to `/`.
411 if (section == "end") {
412 section = "";
413 continue;
414 }
415 if (qe_input::honoured_keys_by_namelist().count(section) == 0)
416 throw std::runtime_error(
417 "QE namelist '&" + section +
418 "' is not supported. Q3 reads &control, &system, &electrons, &ions and "
419 "&cell, and refuses the rest rather than ignoring the physics in them.");
420 line = trimmed.substr(name_end);
421 trimmed = trim(line);
422 if (trimmed.empty())
423 continue;
424 lower = to_lower(trimmed);
425 }
426
427 /**
428 * The `/` that closes a namelist need not stand alone either: it may
429 * follow the line's last assignment (`ecutwfc = 30.0 /`). Left in
430 * place, it became part of that value -- `30.0 /`, or `'relax' /`
431 * for a string key. It is cut off here, and the namelist closes at
432 * the bottom of the loop, once the assignments before it are read.
433 */
434 bool line_closes_namelist = false;
435 if (qe_input::honoured_keys_by_namelist().count(section) != 0) {
436 line_closes_namelist = strip_namelist_terminator(line, section);
437 trimmed = trim(line);
438 if (trimmed.empty()) {
439 section = "";
440 continue;
441 }
442 lower = to_lower(trimmed);
443 }
444 if (lower[0] == '/') {
445 section = "";
446 continue;
447 }
448
449 // Detect card sections.
450 if (lower.find("atomic_species") != std::string::npos) {
451 section = "atomic_species";
452 continue;
453 }
454 if (lower.find("atomic_positions") != std::string::npos) {
455 section = "atomic_positions";
456 atomic_positions_units = read_atomic_positions_units(lower);
457 continue;
458 }
459 if (lower.find("k_points") != std::string::npos) {
460 section = "k_points";
461 continue;
462 }
463 /**
464 * QE's remaining cards.
465 *
466 * They must be *recognized* even when unsupported: an unrecognized
467 * card header leaves `section` pointing at whatever came before,
468 * and the card's body is then parsed as that section's data. A
469 * HUBBARD card following K_POINTS was silently dropped — a PAW+U
470 * input ran and returned the no-U energy.
471 */
472 /**
473 * A card header never contains '='. Without that guard the
474 * `occupations` *key* in &SYSTEM matches the `OCCUPATIONS` *card*
475 * and a perfectly ordinary input is refused.
476 */
477 bool looks_like_card = lower.find('=') == std::string::npos;
478
479 if (looks_like_card && lower.rfind("hubbard", 0) == 0) {
480 section = "hubbard";
481 /**
482 * `HUBBARD (atomic)` / `(ortho-atomic)`. QE's other projector
483 * flavours (`wf`, `pseudo`, `norm-atomic`) have no Q3
484 * equivalent and are refused rather than silently substituted.
485 */
486 if (lower.find("ortho-atomic") != std::string::npos)
487 data.hubbard_projector_type = "ortho-atomic";
488 else if (lower.find("atomic") != std::string::npos)
489 data.hubbard_projector_type = "atomic";
490 else
491 throw std::runtime_error(
492 "QE HUBBARD card projector type in '" + trimmed +
493 "' is not supported; Q3 implements (atomic) and (ortho-atomic)");
494 continue;
495 }
496 for (const char* unsupported_card : {"occupations",
497 "constraints",
498 "atomic_forces",
499 "atomic_velocities",
500 "additional_k_points",
501 "solvents",
502 "hubbard_v",
503 "total_charge"}) {
504 if (looks_like_card && lower.rfind(unsupported_card, 0) == 0)
505 throw std::runtime_error(
506 std::string("QE card '") + trimmed +
507 "' is not supported. Q3 refuses cards it cannot honour rather than "
508 "ignoring them, because an ignored card produces a converged result "
509 "for a different calculation than the one requested.");
510 }
511
512 if (lower.find("cell_parameters") != std::string::npos) {
513 section = "cell_parameters";
514 cell_parameters_row = 0;
515 cell_parameters_units = read_cell_parameters_units(lower);
516 continue;
517 }
518
519 std::string value;
520
521 /**
522 * Classify every key on the line before any of it is applied, so a
523 * refusal names the offending key rather than failing later on a
524 * missing value.
525 */
526 if (qe_input::honoured_keys_by_namelist().count(section) != 0)
527 classify_namelist_keys(trimmed, section);
528
529 if (section == "control") {
530 if (extract_key_value(line, "pseudo_dir", value))
531 data.pseudopotential_directory = value;
532 if (extract_key_value(line, "calculation", value)) {
533 /**
534 * QE's own vocabulary, mapped to Q3's flavor names. Passing
535 * it through verbatim meant a real QE input file failed on
536 * its very first field with "unsupported calculation type
537 * 'scf'" — the parser could read the file but not run it.
538 */
539 std::string requested = strip_quotes(value);
540 if (requested == "scf")
541 data.calculation = "self_consistent_field";
542 else if (requested == "nscf" || requested == "bands")
543 data.calculation = "non_self_consistent_field";
544 else if (requested == "relax")
545 data.calculation = "relaxation";
546 else if (requested == "vc-relax")
547 data.calculation = "variable_cell_relaxation";
548 else
549 data.calculation = requested;
550 }
551 if (extract_key_value(line, "prefix", value))
552 data.prefix = value;
553 if (extract_key_value(line, "outdir", value))
554 data.output_directory = value;
555 /**
556 * QE's six spellings collapse onto Q3's two, along the same
557 * line QE itself draws: 'high' and 'debug' raise iverbosity
558 * above zero, the other four leave it there. Read rather than
559 * ignored, so a deck that asked pw.x for the detailed output
560 * gets the detailed output here too.
561 */
562 if (extract_key_value(line, "verbosity", value)) {
563 std::string requested = to_lower(strip_quotes(value));
564 if (requested == "high" || requested == "debug")
565 data.verbosity = "high";
566 else if (requested == "low" || requested == "medium" ||
567 requested == "minimal" || requested == "default")
568 data.verbosity = "default";
569 else
570 throw std::runtime_error(
571 "verbosity='" + requested +
572 "' is not a Quantum ESPRESSO verbosity; use 'high', 'debug', "
573 "'default', 'low', 'medium' or 'minimal'");
574 }
575 /**
576 * QE-vocabulary exception: 'npool' mirrors QE's own flag name so
577 * QE-style inputs stay portable. QE itself only accepts -npool on
578 * the command line; accepting it in &control is a Q3 extension.
579 * Everywhere else use the full name: number_of_kpoint_pools.
580 */
581 if (extract_key_value(line, "npool", value))
582 data.number_of_kpoint_pools = std::stoi(value);
583 /**
584 * QE's default, stated explicitly in most real inputs. Only
585 * 'from_scratch' means anything here — Q3 cannot resume a QE
586 * run's saved state.
587 */
588 if (extract_key_value(line, "restart_mode", value)) {
589 std::string requested = to_lower(strip_quotes(value));
590 if (requested != "from_scratch")
591 throw std::runtime_error(
592 "restart_mode='" + requested +
593 "' is not supported; Q3 cannot resume a QE run's saved state");
594 }
595 /**
596 * Relaxation controls. They are inert for `calculation='scf'`,
597 * so they are applied unconditionally rather than deferred
598 * until the calculation type is known — which QE does not
599 * require to appear first.
600 */
601 if (extract_key_value(line, "nstep", value))
602 data.relax_maximum_steps = std::stoi(value);
603 if (extract_key_value(line, "forc_conv_thr", value))
604 data.relax_force_threshold = fortran_to_double(value);
605 if (extract_key_value(line, "etot_conv_thr", value))
606 data.relax_energy_threshold = fortran_to_double(value);
607 } else if (section == "system") {
608 if (extract_key_value(line, "ibrav", value))
609 data.bravais_lattice_index = std::stoi(value);
610 /**
611 * celldm(2..6) carry the axial ratios and cell angles. Dropping
612 * them left every non-cubic Bravais index — hexagonal,
613 * tetragonal, orthorhombic, monoclinic, triclinic — being built
614 * with c/a = 0, i.e. a collapsed cell.
615 */
616 for (int index = 1; index <= 6; index++) {
617 if (!extract_key_value(line, "celldm(" + std::to_string(index) + ")", value))
618 continue;
619 Real dimension = fortran_to_double(value);
620 switch (index) {
621 case 1:
622 data.cell_dimension_1 = dimension;
623 break;
624 case 2:
625 data.cell_dimension_2 = dimension;
626 break;
627 case 3:
628 data.cell_dimension_3 = dimension;
629 break;
630 case 4:
631 data.cell_dimension_4 = dimension;
632 break;
633 case 5:
634 data.cell_dimension_5 = dimension;
635 break;
636 default:
637 data.cell_dimension_6 = dimension;
638 break;
639 }
640 }
641 /**
642 * QE's alternative crystallographic form. Recorded raw and
643 * converted after the file is read, because the celldm(4..6)
644 * each cosine maps onto depends on ibrav, which may appear
645 * later in the namelist.
646 */
647 for (const auto& [key, target] :
648 {std::pair<const char*, Real*>{"A", &lattice_constant_a},
649 {"B", &lattice_constant_b},
650 {"C", &lattice_constant_c},
651 {"cosAB", &cosine_ab},
652 {"cosAC", &cosine_ac},
653 {"cosBC", &cosine_bc}}) {
654 if (extract_key_value(line, key, value)) {
655 *target = fortran_to_double(value);
656 has_crystallographic_constants = true;
657 }
658 }
659 if (extract_key_value(line, "nat", value))
660 data.number_of_atoms = std::stoi(value);
661 if (extract_key_value(line, "ntyp", value))
662 data.number_of_species = std::stoi(value);
663 if (extract_key_value(line, "ecutwfc", value))
664 data.wavefunction_energy_cutoff = fortran_to_double(value);
665 if (extract_key_value(line, "ecutrho", value))
666 data.charge_density_energy_cutoff = fortran_to_double(value);
667 if (extract_key_value(line, "nbnd", value))
668 data.number_of_bands = std::stoi(value);
669 if (extract_key_value(line, "input_dft", value))
670 data.exchange_correlation_functional = strip_quotes(value);
671 if (extract_key_value(line, "nspin", value)) {
672 data.number_of_spin_components = std::stoi(value);
673 if (data.number_of_spin_components != 1 && data.number_of_spin_components != 2)
674 throw std::runtime_error(
675 "nspin must be 1 (unpolarized) or 2 (collinear LSDA), got " + value +
676 " (noncollinear nspin=4 is not supported)");
677 }
678 /**
679 * YAML-manifest vocabulary exception: mirrors nspin's own guard above
680 * (the YAML parser rejects 'nspin' with a message pointing back here) --
681 * reject the manifest key here so a mistyped namelist input cannot
682 * silently fall through and run unpolarized.
683 */
684 if (extract_key_value(line, "number_of_spin_components", value))
685 throw std::runtime_error(
686 "'number_of_spin_components' is not a QE namelist key; use nspin instead");
687 if (extract_key_value(line, "starting_magnetization", value)) {
688 /**
689 * Per-species: starting_magnetization(i) = m. An unindexed
690 * occurrence means species 1 (Fortran scalar-assignment rule).
691 */
692 int species_index = 1;
693 size_t key_position = to_lower(line).find("starting_magnetization");
694 size_t parenthesis_position = line.find('(', key_position);
695 size_t equals_position = line.find('=', key_position);
696 if (parenthesis_position != std::string::npos &&
697 equals_position != std::string::npos &&
698 parenthesis_position < equals_position) {
699 try {
700 species_index = std::stoi(line.substr(parenthesis_position + 1));
701 } catch (const std::exception&) {
702 throw std::runtime_error(
703 "Cannot parse starting_magnetization species index in line: " +
704 trim(line));
705 }
706 if (species_index < 1)
707 throw std::runtime_error(
708 "starting_magnetization index must be >= 1, got " +
709 std::to_string(species_index));
710 }
711 if (static_cast<int>(data.starting_magnetization.size()) < species_index)
712 data.starting_magnetization.resize(species_index, 0.0);
713 data.starting_magnetization[species_index - 1] = fortran_to_double(value);
714 }
715 /**
716 * starting_ns_eigenvalue(m,ispin,ityp) = f.
717 *
718 * Scanned for *every* occurrence on the line rather than via
719 * `extract_key_value`, which returns only the first match: a
720 * seeding deck naturally packs several directives onto one
721 * namelist line, and silently keeping just one of them would
722 * steer the SCF into a minimum the user did not ask for --
723 * the precise failure seeding exists to rule out.
724 *
725 * All three indices are required. The Fortran scalar
726 * shorthand would leave which eigenvalue of which species to
727 * guesswork, and a seed aimed at the wrong manifold selects a
728 * different solution, not a slightly-off number. Species
729 * resolution to a symbol happens post-parse, since ntyp and
730 * ATOMIC_SPECIES may follow this line.
731 */
732 std::string lower_line = to_lower(line);
733 const std::string seeding_key = "starting_ns_eigenvalue";
734 for (size_t key_position = lower_line.find(seeding_key);
735 key_position != std::string::npos;
736 key_position = lower_line.find(seeding_key, key_position + 1)) {
737 if (!is_whole_key_at(lower_line, key_position, seeding_key.size()))
738 continue;
739 size_t parenthesis_position = line.find('(', key_position);
740 size_t closing_position = line.find(')', key_position);
741 size_t equals_position = line.find('=', key_position);
742 if (parenthesis_position == std::string::npos ||
743 closing_position == std::string::npos ||
744 equals_position == std::string::npos ||
745 equals_position < parenthesis_position)
746 throw std::runtime_error(
747 "starting_ns_eigenvalue needs explicit (m,ispin,ityp) indices, got: " +
748 trim(line));
749 std::string index_list = line.substr(
750 parenthesis_position + 1, closing_position - parenthesis_position - 1);
751 std::vector<int> indices;
752 std::stringstream index_stream(index_list);
753 std::string index_token;
754 while (std::getline(index_stream, index_token, ','))
755 try {
756 indices.push_back(std::stoi(trim(index_token)));
757 } catch (const std::exception&) {
758 throw std::runtime_error(
759 "Cannot parse starting_ns_eigenvalue indices in line: " +
760 trim(line));
761 }
762 if (indices.size() != 3)
763 throw std::runtime_error(
764 "starting_ns_eigenvalue needs exactly (m,ispin,ityp), got " +
765 std::to_string(indices.size()) + " indices in: " + trim(line));
766
767 // This directive's value ends at the next top-level comma or comment.
768 size_t value_start = equals_position + 1;
769 while (value_start < line.size() &&
770 (line[value_start] == ' ' || line[value_start] == '\t'))
771 value_start++;
772 size_t value_end = line.find_first_of(",!\n\r", value_start);
773 if (value_end == std::string::npos)
774 value_end = line.size();
775 Real eigenvalue =
776 fortran_to_double(trim(line.substr(value_start, value_end - value_start)));
777
778 if (indices[0] < 1 || indices[1] < 1 || indices[1] > 2 || indices[2] < 1)
779 throw std::runtime_error(
780 "starting_ns_eigenvalue indices must be m >= 1, ispin in {1,2}, "
781 "ityp >= 1, got: " +
782 trim(line));
783 if (eigenvalue < 0.0 || eigenvalue > 1.0)
784 throw std::runtime_error(
785 "starting_ns_eigenvalue value must be in [0, 1], got " +
786 std::to_string(eigenvalue));
787 raw_starting_ns_eigenvalues.push_back(
788 {indices[0], indices[1], indices[2], eigenvalue});
789 }
790 if (extract_key_value(line, "degauss", value))
791 data.smearing_width = fortran_to_double(value);
792 if (extract_key_value(line, "tot_charge", value))
793 data.total_charge = fortran_to_double(value);
794 if (extract_key_value(line, "smearing", value))
795 data.smearing = strip_quotes(value);
796 if (extract_key_value(line, "occupations", value)) {
797 std::string occupations = strip_quotes(value);
798 if (to_lower(occupations) == "smearing" && data.smearing == "none") {
799 data.smearing = "gaussian";
800 }
801 }
802 if (extract_key_value(line, "vdw_corr", value)) {
803 std::string vdw_corr = to_lower(strip_quotes(value));
804 if (vdw_corr == "dft-d3" || vdw_corr == "grimme-d3") {
805 /**
806 * Only the Becke-Johnson-damped two-body term is
807 * implemented -- what D3BJDispersionCalculator computes,
808 * and what pw.x runs for dftd3_version = 4,
809 * dftd3_threebody = .false.. Neither is pw.x's default,
810 * so the deck has to state both; see
811 * refuse_dftd3_defaults_q3_does_not_implement().
812 */
813 data.dispersion_correction = "d3bj";
814 } else if (vdw_corr == "none" || vdw_corr.empty()) {
815 data.dispersion_correction = "none";
816 } else {
817 /**
818 * pw.x's DFT-D2, Tkatchenko-Scheffler, MBD and XDM.
819 * These used to fall through silently and run with no
820 * dispersion correction at all.
821 */
822 throw std::runtime_error(
823 "vdw_corr = '" + vdw_corr +
824 "' is not supported; the only dispersion correction Q3 implements "
825 "is DFT-D3 with Becke-Johnson damping ('dft-d3' or 'grimme-d3')");
826 }
827 }
828 /**
829 * The two D3 sub-options Q3's implementation pins. They used to
830 * be ignored, which meant `vdw_corr='dft-d3'` silently produced
831 * the BJ-damped two-body energy whatever the file asked for.
832 */
833 if (extract_key_value(line, "dftd3_version", value)) {
834 if (std::stoi(value) != 4)
835 throw std::runtime_error(
836 "dftd3_version = " + value +
837 " is not supported; Q3 implements version 4 (Becke-Johnson damping)");
838 has_dftd3_version = true;
839 }
840 if (extract_key_value(line, "dftd3_threebody", value)) {
841 if (fortran_logical_is_true(value))
842 throw std::runtime_error(
843 "dftd3_threebody = .true. is not supported; Q3 implements the "
844 "two-body D3(BJ) term only");
845 has_dftd3_threebody = true;
846 }
847 if (extract_key_value(line, "nosym", value))
848 data.disable_symmetry = fortran_logical_is_true(value);
849 if (extract_key_value(line, "assume_isolated", value)) {
850 /**
851 * QE's spelling of the corrections Q3 has; its remaining
852 * options ('esm', '2D') are different physics, not aliases.
853 */
854 std::string requested = to_lower(strip_quotes(value));
855 if (requested == "makov-payne" || requested == "m-p" || requested == "mp")
856 data.isolated_system_correction = "makov-payne";
857 else if (requested == "martyna-tuckerman" || requested == "m-t" ||
858 requested == "mt")
859 data.isolated_system_correction = "martyna-tuckerman";
860 else if (requested == "none")
861 data.isolated_system_correction = "none";
862 else
863 throw std::runtime_error(
864 "assume_isolated='" + requested +
865 "' is not supported; Q3 implements 'makov-payne' and "
866 "'martyna-tuckerman'");
867 }
868 } else if (section == "ions") {
869 if (extract_key_value(line, "ion_dynamics", value)) {
870 /**
871 * Q3 relaxes with ASE's BFGS. QE's other ion_dynamics
872 * values are molecular dynamics or damped dynamics, which
873 * follow a different trajectory and stop somewhere else.
874 */
875 std::string requested = to_lower(strip_quotes(value));
876 if (requested != "bfgs")
877 throw std::runtime_error(
878 "ion_dynamics='" + requested +
879 "' is not supported; Q3's relaxation is BFGS, and the damped-dynamics "
880 "and molecular-dynamics paths are not implemented");
881 }
882 } else if (section == "cell") {
883 if (extract_key_value(line, "cell_dynamics", value)) {
884 std::string requested = to_lower(strip_quotes(value));
885 if (requested != "bfgs")
886 throw std::runtime_error(
887 "cell_dynamics='" + requested +
888 "' is not supported; Q3's variable-cell relaxation is BFGS on an ASE "
889 "cell filter");
890 }
891 } else if (section == "electrons") {
892 if (extract_key_value(line, "conv_thr", value))
893 data.convergence_threshold = fortran_to_double(value);
894 if (extract_key_value(line, "startingpot", value)) {
895 /// pw.x's two starts: overlapping atoms, or the saved density.
896 static const std::map<std::string, bool> starts_from_saved_density = {
897 {"atomic", false}, {"file", true}};
898 const std::string start = to_lower(strip_quotes(value));
899 const auto found = starts_from_saved_density.find(start);
900 if (found == starts_from_saved_density.end())
901 throw std::runtime_error("startingpot='" + start +
902 "' is not supported; use 'atomic' or 'file'");
903 data.start_from_saved_charge_density = found->second;
904 }
905 if (extract_key_value(line, "mixing_beta", value))
906 data.mixing_beta = fortran_to_double(value);
907 if (extract_key_value(line, "mixing_mode", value)) {
908 /**
909 * QE's vocabulary, translated the way `calculation` is.
910 * `plain` is QE's *default* and appears in most real
911 * inputs; storing it verbatim meant Q3 died in the mixer
912 * factory with "Unknown mixer mode: 'plain'" on files QE
913 * runs without comment. It keeps the unpreconditioned
914 * default set at the top of parse().
915 *
916 * `TF`/`local-TF` are Thomas-Fermi preconditioners on the
917 * same Broyden mixing, and Q3 now has both: `TF` is the
918 * average (global) Thomas-Fermi screening, which is exactly
919 * what Kerker is, and `local-TF` builds the screening from
920 * the local density. Both map onto Broyden with the matching
921 * preconditioner rather than onto a different fixed point.
922 */
923 std::string requested = to_lower(strip_quotes(value));
924 if (requested == "plain") {
925 data.mixing_mode = "broyden";
926 } else if (requested == "tf") {
927 log::info(" QE input requests mixing_mode='{}'; using Broyden with "
928 "Kerker preconditioning, which is that same average "
929 "Thomas-Fermi screening",
930 strip_quotes(value));
931 data.mixing_mode = "broyden";
932 data.mixing_preconditioning = "kerker";
933 } else if (requested == "local-tf") {
934 data.mixing_mode = "broyden";
935 data.mixing_preconditioning = "local-tf";
936 } else {
937 data.mixing_mode = requested;
938 }
939 }
940 if (extract_key_value(line, "mixing_ndim", value))
941 data.mixing_history_size = std::stoi(value);
942 if (extract_key_value(line, "electron_maxstep", value))
943 data.maximum_iterations = std::stoi(value);
944 /**
945 * QE's default is .true.; .false. asks it to carry on with an
946 * unconverged density, which Q3 has no equivalent for and must
947 * not silently upgrade to a converged run.
948 */
949 if (extract_key_value(line, "scf_must_converge", value) &&
950 !fortran_logical_is_true(value))
951 throw std::runtime_error(
952 "scf_must_converge = .false. is not supported; Q3 always requires the "
953 "SCF to converge before reporting a result");
954 if (extract_key_value(line, "mixing_fixed_ns", value)) {
955 /**
956 * Parsed through the same tolerant numeric path as every
957 * other value in this reader: a bare std::stoi throws
958 * std::invalid_argument without naming the key, which is
959 * the one thing this parser exists not to do.
960 */
961 Real fixed_iterations = fortran_to_double(value);
962 if (fixed_iterations < 0.0 || fixed_iterations != std::floor(fixed_iterations))
963 throw std::runtime_error(
964 "mixing_fixed_ns must be a non-negative whole number, got '" + value +
965 "'");
966 data.hubbard_fixed_occupation_iterations = static_cast<int>(fixed_iterations);
967 }
968 if (extract_key_value(line, "diagonalization", value)) {
969 /**
970 * QE's iterative solvers select an algorithm, not physics: both
971 * converge to the same fixed point. `cg` is mapped to Davidson with
972 * a notice rather than refused, because every QE PAW reference has
973 * to be generated with `cg` (its Davidson aborts in `cegterg` on PAW
974 * datasets), and refusing it would make exactly those inputs
975 * unreadable.
976 */
977 std::string requested = strip_quotes(value);
978 if (requested == "cg" || requested == "ppcg" || requested == "paro") {
979 log::info(" QE input requests diagonalization='{}'; using Davidson "
980 "(same fixed point, different iteration path)",
981 requested);
982 data.diagonalization_method = "davidson";
983 } else {
984 data.diagonalization_method = requested;
985 }
986 }
987 } else if (section == "atomic_species") {
988 std::istringstream stream(trimmed);
989 AtomSpecies species;
990 stream >> species.symbol;
991 if (!species.symbol.empty()) {
992 extract_real(stream, species.mass, trimmed, "ATOMIC_SPECIES");
993 stream >> species.pseudopotential_file;
994 data.species.push_back(species);
995 }
996 } else if (section == "atomic_positions") {
997 std::istringstream stream(trimmed);
998 std::string symbol;
999 Real x_coordinate = 0.0, y_coordinate = 0.0, z_coordinate = 0.0;
1000 stream >> symbol;
1001 if (!symbol.empty()) {
1002 extract_real(stream, x_coordinate, trimmed, "ATOMIC_POSITIONS");
1003 extract_real(stream, y_coordinate, trimmed, "ATOMIC_POSITIONS");
1004 extract_real(stream, z_coordinate, trimmed, "ATOMIC_POSITIONS");
1005 const std::array<bool, 3> movable_components =
1006 read_position_flags(stream, trimmed);
1007 int species_index = -1;
1008 for (int index = 0; index < static_cast<int>(data.species.size()); index++) {
1009 if (data.species[index].symbol == symbol) {
1010 species_index = index;
1011 break;
1012 }
1013 }
1014 if (species_index >= 0) {
1015 Atom atom;
1016 atom.species_index = species_index;
1017 atom.position = {x_coordinate, y_coordinate, z_coordinate};
1018 atom.movable_components = movable_components;
1019 data.atoms.push_back(atom);
1020 }
1021 }
1022 } else if (section == "k_points") {
1023 std::istringstream stream(trimmed);
1024 stream >> data.kpoint_grid_dimension_1 >> data.kpoint_grid_dimension_2 >>
1027 section = "";
1028 } else if (section == "hubbard") {
1029 /**
1030 * `U <species>-<shell> <value in eV>`, e.g. `U Ni-3d 4.0`.
1031 * Only the U term: QE's `J`, `V` and `B` parameters describe
1032 * interactions Q3's Dudarev implementation does not have, so
1033 * they are refused rather than dropped.
1034 */
1035 std::istringstream card_stream(trimmed);
1036 std::string parameter, target;
1037 Real value = 0.0;
1038 card_stream >> parameter >> target;
1039 std::string parameter_lower = to_lower(parameter);
1040 if (parameter_lower != "u")
1041 throw std::runtime_error(
1042 "QE HUBBARD parameter '" + parameter +
1043 "' is not supported; Q3 implements the Dudarev U only");
1044 auto separator = target.find('-');
1045 if (separator == std::string::npos)
1046 throw std::runtime_error("QE HUBBARD entry '" + trimmed +
1047 "' is not of the form U <species>-<shell> <eV>");
1048 /**
1049 * Read the value only once the line is known to be a `U` entry:
1050 * QE's `V` takes two targets, so extracting a number here first
1051 * would report the second target as "not a number" and bury the
1052 * accurate refusal above.
1053 */
1054 extract_real(card_stream, value, trimmed, "HUBBARD");
1055 data.hubbard.push_back({target.substr(0, separator),
1056 target.substr(separator + 1),
1057 value * 2.0 / constants::HARTREE_TO_EV});
1058 } else if (section == "cell_parameters") {
1059 if (cell_parameters_row < 3) {
1060 std::istringstream stream(trimmed);
1061 for (int column = 0; column < 3; column++) {
1062 extract_real(stream,
1063 cell_parameters_vectors[cell_parameters_row][column],
1064 trimmed,
1065 "CELL_PARAMETERS");
1066 }
1067 cell_parameters_row++;
1068 if (cell_parameters_row == 3) {
1069 has_cell_parameters = true;
1070 section = "";
1071 }
1072 }
1073 }
1074
1075 if (line_closes_namelist)
1076 section = "";
1077 }
1078
1079 if (data.dispersion_correction == "d3bj")
1080 refuse_dftd3_defaults_q3_does_not_implement(has_dftd3_version, has_dftd3_threebody);
1081
1082 /**
1083 * A/B/C are resolved before CELL_PARAMETERS, because the alat that an
1084 * `(alat)` card is expressed in may have been given in that form.
1085 */
1086 if (has_crystallographic_constants) {
1087 apply_crystallographic_constants(data,
1088 lattice_constant_a,
1089 lattice_constant_b,
1090 lattice_constant_c,
1091 cosine_ab,
1092 cosine_ac,
1093 cosine_bc);
1094 }
1095
1096 if (has_cell_parameters) {
1097 apply_cell_parameters(data, cell_parameters_vectors, cell_parameters_units);
1098 }
1099
1100 // Positions last: converting Cartesian rows needs the finished cell.
1101 convert_atomic_positions_to_crystal(data, atomic_positions_units);
1102
1103 /**
1104 * starting_magnetization(i) indices were only bounds-checked to be >= 1
1105 * while streaming (ntyp may appear after them in the namelist); validate
1106 * against the species count now that the whole file is read.
1107 */
1108 if (static_cast<int>(data.starting_magnetization.size()) > data.number_of_species &&
1109 data.number_of_species > 0) {
1110 throw std::runtime_error("starting_magnetization index " +
1111 std::to_string(data.starting_magnetization.size()) +
1112 " exceeds the number of species (ntyp = " +
1113 std::to_string(data.number_of_species) + ")");
1114 }
1115
1116 /**
1117 * starting_ns_eigenvalue species indices, resolvable only now that
1118 * ATOMIC_SPECIES is read: convert 1-based (m, ispin, ityp) to the
1119 * symbol-keyed, 0-based form the SCF consumes. ityp beyond ntyp is a
1120 * hard error like any other out-of-range species reference.
1121 */
1122 for (const auto& raw_directive : raw_starting_ns_eigenvalues) {
1123 if (raw_directive.species_index > static_cast<int>(data.species.size()))
1124 throw std::runtime_error("starting_ns_eigenvalue species index " +
1125 std::to_string(raw_directive.species_index) +
1126 " exceeds the number of species (" +
1127 std::to_string(data.species.size()) + ")");
1129 data.species[static_cast<size_t>(raw_directive.species_index - 1)].symbol,
1130 raw_directive.eigenvalue_index,
1131 raw_directive.spin_index,
1132 raw_directive.value,
1133 "starting_ns_eigenvalue"));
1134 }
1135
1136 return data;
1137 }
1138
1139 private:
1140 /// Raw 1-based starting_ns_eigenvalue tuple; symbol resolution happens post-parse.
1141 struct RawStartingNsEigenvalue {
1142 int eigenvalue_index;
1143 int spin_index;
1144 int species_index;
1145 Real value;
1146 };
1147
1148 /**
1149 * Convert a CELL_PARAMETERS card into InputData explicit lattice vectors
1150 * (bravais_lattice_index 0, rows in units of alat).
1151 *
1152 * Units handling matches pw.x: 'alat' rows are already in units of celldm(1)
1153 * (which must be present); 'bohr'/'angstrom' rows are absolute, and alat is
1154 * taken from celldm(1) when given, |a1| otherwise. A card that names no
1155 * units -- `units_named` empty -- is alat when celldm(1) or A is given and
1156 * bohr when neither is, which is how pw.x reads that deprecated spelling
1157 * (checked black-box against pw.x 7.6). It used to be read as bohr
1158 * regardless, so a deck that set celldm(1) to one angstrom in bohr and
1159 * wrote its rows in angstrom ran a cell 1.89 times too small on every axis.
1160 */
1161 static void apply_cell_parameters(InputData& data,
1162 const Matrix3x3& raw_vectors,
1163 const std::string& units_named) {
1164 if (data.bravais_lattice_index != 0)
1165 throw std::runtime_error("CELL_PARAMETERS requires ibrav = 0, got ibrav = " +
1166 std::to_string(data.bravais_lattice_index));
1167
1168 std::string units = units_named;
1169 if (units.empty()) {
1170 bool has_lattice_parameter = data.cell_dimension_1 > 0.0;
1171 units = has_lattice_parameter ? "alat" : "bohr";
1172 log::info(" QE input: CELL_PARAMETERS names no units; reading its rows as {} "
1173 "because {} lattice parameter is given, as pw.x does with this deprecated "
1174 "spelling",
1175 units,
1176 has_lattice_parameter ? "a" : "no");
1177 }
1178
1179 if (units == "alat") {
1180 if (data.cell_dimension_1 <= 0.0)
1181 throw std::runtime_error("CELL_PARAMETERS (alat) requires celldm(1) to be set");
1182 data.lattice_vectors = raw_vectors;
1183 data.has_explicit_lattice_vectors = true;
1184 return;
1185 }
1186
1187 Real length_to_bohr = (units == "angstrom") ? constants::ANGSTROM_TO_BOHR : 1.0;
1188 Matrix3x3 vectors_bohr;
1189 for (int row = 0; row < 3; ++row) {
1190 vectors_bohr[row] = raw_vectors[row] * length_to_bohr;
1191 }
1192
1193 /**
1194 * celldm(1) takes precedence when set (QE convention); InputData::
1195 * set_free_lattice_vectors derives alat = |a1| otherwise — the same
1196 * rule the YAML general-lattice path uses, single-sourced.
1197 */
1198 try {
1199 data.set_free_lattice_vectors(vectors_bohr, data.cell_dimension_1);
1200 } catch (const std::runtime_error&) {
1201 throw std::runtime_error("CELL_PARAMETERS: lattice vector a1 has zero length");
1202 }
1203 }
1204
1205 /**
1206 * The units option on a CELL_PARAMETERS header line: `alat`, `bohr` or `angstrom`,
1207 * written bare or in parentheses or braces, or empty for a card that names none.
1208 * Which of the three an empty option means depends on whether the deck gives a
1209 * lattice parameter, so apply_cell_parameters resolves it once the whole file is
1210 * read. An option that is none of the three is refused by name rather than read as
1211 * one of them.
1212 */
1213 static std::string read_cell_parameters_units(const std::string& lower_header) {
1214 const std::string card_name = "cell_parameters";
1215 std::string option;
1216 for (char character : lower_header.substr(lower_header.find(card_name) + card_name.size()))
1217 if (std::string("(){}").find(character) == std::string::npos &&
1218 !std::isspace(static_cast<unsigned char>(character)))
1219 option += character;
1220
1221 if (option.empty() || option == "alat" || option == "bohr" || option == "angstrom")
1222 return option;
1223 throw std::runtime_error("QE CELL_PARAMETERS option '" + option +
1224 "' is not recognized; the card takes alat, bohr or angstrom");
1225 }
1226
1227 /**
1228 * The units option on an ATOMIC_POSITIONS header line.
1229 *
1230 * pw.x's input documentation lists five -- `crystal`, `alat`, `bohr`, `angstrom` and
1231 * `crystal_sg` -- and a card that names none is read as `alat`. Decks write the option
1232 * bare or in parentheses or braces. QE deprecates the option-less spelling but still
1233 * accepts it, so it is honoured here with a note rather than refused.
1234 *
1235 * The option used to be thrown away and every card read as crystal coordinates. The
1236 * angstrom deck in camsai/q3#18 therefore ran a structure with two O-O pairs 1.08 Å
1237 * apart, and its total energy missed pw.x's by 2.4 Ry.
1238 */
1239 static std::string read_atomic_positions_units(const std::string& lower_header) {
1240 const std::string card_name = "atomic_positions";
1241 std::string option;
1242 for (char character : lower_header.substr(lower_header.find(card_name) + card_name.size()))
1243 if (std::string("(){}").find(character) == std::string::npos &&
1244 !std::isspace(static_cast<unsigned char>(character)))
1245 option += character;
1246
1247 if (option.empty()) {
1248 log::info(" QE input: ATOMIC_POSITIONS names no units; reading its rows as alat, "
1249 "as pw.x does with this deprecated spelling");
1250 return "alat";
1251 }
1252 if (option == "crystal" || option == "alat" || option == "bohr" || option == "angstrom")
1253 return option;
1254 if (option == "crystal_sg")
1255 throw std::runtime_error(
1256 "QE ATOMIC_POSITIONS crystal_sg is not supported: it lists only the "
1257 "symmetry-inequivalent atoms and relies on space_group to generate the rest, "
1258 "which Q3 does not do. List every atom under ATOMIC_POSITIONS crystal instead.");
1259 throw std::runtime_error("QE ATOMIC_POSITIONS option '" + option +
1260 "' is not recognized; the card takes crystal, alat, bohr, "
1261 "angstrom or crystal_sg");
1262 }
1263
1264 /**
1265 * Rewrite ATOMIC_POSITIONS rows as the crystal coordinates InputData stores.
1266 *
1267 * `alat`, `bohr` and `angstrom` rows are Cartesian: in units of the lattice parameter,
1268 * in bohr, and in angstrom. Each row r becomes crystal coordinates f by solving
1269 * r = f A, where the rows of A are the lattice vectors in units of alat -- the same cell
1270 * the Crystal is later built from: the CELL_PARAMETERS vectors when that card is
1271 * present, the ibrav cell otherwise. alat is celldm(1) (or A), or |a1| for a
1272 * CELL_PARAMETERS card in bohr or angstrom, which is the value pw.x's input
1273 * documentation gives it and the one apply_cell_parameters() stores.
1274 */
1275 static void convert_atomic_positions_to_crystal(InputData& data, const std::string& units) {
1276 if (units == "crystal" || data.atoms.empty())
1277 return;
1278
1279 bool has_cell = data.has_explicit_lattice_vectors ||
1280 (data.bravais_lattice_index != 0 && data.cell_dimension_1 > 0.0);
1281 if (!has_cell)
1282 throw std::runtime_error(
1283 "QE ATOMIC_POSITIONS " + units +
1284 " rows are Cartesian and need the cell to become crystal coordinates, but the "
1285 "input defines none: ibrav = 0 needs a CELL_PARAMETERS card, and any other ibrav "
1286 "needs celldm(1) or A");
1287
1288 Matrix3x3 lattice_vectors =
1289 data.has_explicit_lattice_vectors
1290 ? data.lattice_vectors
1291 : get_lattice_vectors_from_bravais_index(data.bravais_lattice_index,
1292 data.cell_dimension_1,
1293 data.cell_dimension_2,
1294 data.cell_dimension_3,
1295 data.cell_dimension_4,
1296 data.cell_dimension_5,
1297 data.cell_dimension_6);
1298 if (std::abs(lattice_vectors.determinant()) < math::tolerances::cell_volume_threshold)
1299 throw std::runtime_error("QE ATOMIC_POSITIONS " + units +
1300 " rows cannot be converted: the cell has zero volume");
1301
1302 Real length_to_alat = 1.0;
1303 if (units == "bohr")
1304 length_to_alat = 1.0 / data.cell_dimension_1;
1305 else if (units == "angstrom")
1306 length_to_alat = constants::ANGSTROM_TO_BOHR / data.cell_dimension_1;
1307
1308 Matrix3x3 inverse_lattice = lattice_vectors.inverse();
1309 for (Atom& atom : data.atoms) {
1310 Vector3D cartesian = atom.position * length_to_alat;
1311 Vector3D fractional{0.0, 0.0, 0.0};
1312 for (int column = 0; column < 3; column++)
1313 fractional[column] = cartesian[0] * inverse_lattice[0][column] +
1314 cartesian[1] * inverse_lattice[1][column] +
1315 cartesian[2] * inverse_lattice[2][column];
1316 atom.position = fractional;
1317 }
1318 }
1319
1320 static std::string to_lower(std::string text) {
1321 std::transform(text.begin(), text.end(), text.begin(), ::tolower);
1322 return text;
1323 }
1324
1325 static std::string trim(const std::string& text) {
1326 size_t first = text.find_first_not_of(" \t\r\n");
1327 if (first == std::string::npos)
1328 return "";
1329 size_t last = text.find_last_not_of(" \t\r\n");
1330 return text.substr(first, last - first + 1);
1331 }
1332
1333 static std::string strip_quotes(const std::string& text) {
1334 std::string result = text;
1335 if (result.size() >= 2 && result.front() == '\'' && result.back() == '\'')
1336 result = result.substr(1, result.size() - 2);
1337 if (result.size() >= 2 && result.front() == '"' && result.back() == '"')
1338 result = result.substr(1, result.size() - 2);
1339 return result;
1340 }
1341
1342 /// Convert Fortran-style "d" exponent to "e" for C++ parsing.
1343 static double fortran_to_double(const std::string& text) {
1344 std::string fixed = text;
1345 for (auto& character : fixed) {
1346 if (character == 'd' || character == 'D')
1347 character = 'e';
1348 }
1349 return std::stod(fixed);
1350 }
1351
1352 /**
1353 * Read one whitespace-delimited real from a card line, accepting Fortran's
1354 * `d`/`D` exponent marker.
1355 *
1356 * The namelist keys have gone through fortran_to_double() all along; the
1357 * cards read straight into a `Real` with `>>`, which is not the same thing.
1358 * `>>` stops at the `d` in `2.0d0`: the first field on the line silently
1359 * becomes 2.0, every field after it fails to convert, and C++11 then sets
1360 * each of those to zero. A CELL_PARAMETERS block written in double-precision
1361 * literals -- the ordinary way to write one when the digits matter, as they
1362 * do for phonons -- parsed as a degenerate cell, and the run continued to
1363 * completion reporting `cell volume = 0.0000` instead of stopping.
1364 *
1365 * ATOMIC_SPECIES failed more quietly still: `Ni 58.6934d0 Ni.UPF` left `d0`
1366 * as the next token, so the mass lost its exponent and `d0` was taken as the
1367 * pseudopotential filename.
1368 *
1369 * A token that is present but not a number is refused rather than silently
1370 * becoming zero, which is what produced the degenerate cell above.
1371 */
1372 static void extract_real(std::istringstream& stream,
1373 Real& value,
1374 const std::string& line,
1375 const std::string& card_name) {
1376 std::string token;
1377 if (!(stream >> token))
1378 throw std::runtime_error("QE " + card_name + " line '" + line +
1379 "' ends before all of its numbers were read");
1380 try {
1381 std::string fixed = token;
1382 for (auto& character : fixed) {
1383 if (character == 'd' || character == 'D')
1384 character = 'e';
1385 }
1386 size_t characters_consumed = 0;
1387 value = std::stod(fixed, &characters_consumed);
1388 if (characters_consumed != fixed.size())
1389 throw std::invalid_argument("trailing characters");
1390 } catch (const std::exception&) {
1391 throw std::runtime_error("QE " + card_name + " line '" + line + "': '" + token +
1392 "' is not a number");
1393 }
1394 }
1395
1396 /**
1397 * The optional `if_pos` flags after an ATOMIC_POSITIONS row's coordinates.
1398 *
1399 * pw.x reads three integers there, one per coordinate: 0 holds that coordinate fixed while
1400 * the ions move, and 1, the default, lets it move. They do not change an SCF; they are kept
1401 * on the atom and written to the results' material as ESSE basis.constraints, where the
1402 * relaxation q3.workflows drives reads them and holds those coordinates fixed. Anything
1403 * else after the coordinates is not a set of flags, and is refused rather than dropped.
1404 */
1405 static std::array<bool, 3> read_position_flags(std::istringstream& stream,
1406 const std::string& line) {
1407 std::vector<std::string> trailing_tokens;
1408 std::string token;
1409 while (stream >> token)
1410 trailing_tokens.push_back(token);
1411 if (trailing_tokens.empty())
1412 return {true, true, true};
1413
1414 std::string trailing_text;
1415 for (const std::string& trailing_token : trailing_tokens)
1416 trailing_text += (trailing_text.empty() ? "" : " ") + trailing_token;
1417 bool are_flags =
1418 trailing_tokens.size() == 3 &&
1419 std::all_of(trailing_tokens.begin(),
1420 trailing_tokens.end(),
1421 [](const std::string& flag) { return flag == "0" || flag == "1"; });
1422 if (!are_flags)
1423 throw std::runtime_error("QE ATOMIC_POSITIONS line '" + line + "' has '" +
1424 trailing_text +
1425 "' after its coordinates, which is not three if_pos flags "
1426 "(each 0 or 1)");
1427 return {trailing_tokens[0] == "1", trailing_tokens[1] == "1", trailing_tokens[2] == "1"};
1428 }
1429
1430 /**
1431 * Refuse a DFT-D3 deck that leaves dftd3_version or dftd3_threebody to pw.x.
1432 *
1433 * pw.x defaults them to dftd3_version = 3, which is zero damping, and
1434 * dftd3_threebody = .true. (system_defaults in QE's Modules/read_namelists.f90;
1435 * INPUT_PW documents the same). Q3 implements the two-body term with Becke-Johnson
1436 * damping, which pw.x runs only when the deck states dftd3_version = 4 and
1437 * dftd3_threebody = .false.. A deck that says `vdw_corr = 'dft-d3'` and nothing more
1438 * therefore asks pw.x for zero-damped D3 with the three-body term, and Q3 used to run
1439 * it as two-body D3(BJ) and report success.
1440 *
1441 * Each key left out is named with the default pw.x takes for it and the line that
1442 * asks for what Q3 computes, so the refusal says how to fix the deck.
1443 */
1444 static void refuse_dftd3_defaults_q3_does_not_implement(bool has_dftd3_version,
1445 bool has_dftd3_threebody) {
1446 std::string explanation;
1447 std::string lines_to_add;
1448 if (!has_dftd3_version) {
1449 explanation += " The input does not state dftd3_version, so pw.x uses its default, "
1450 "dftd3_version = 3, which is zero damping.";
1451 lines_to_add += "dftd3_version = 4";
1452 }
1453 if (!has_dftd3_threebody) {
1454 explanation += " The input does not state dftd3_threebody, so pw.x uses its default, "
1455 "dftd3_threebody = .true., which adds the three-body term.";
1456 lines_to_add +=
1457 std::string(lines_to_add.empty() ? "" : " and ") + "dftd3_threebody = .false.";
1458 }
1459 if (lines_to_add.empty())
1460 return;
1461 throw std::runtime_error("QE input selects DFT-D3 with vdw_corr, and Q3 implements only "
1462 "its two-body term with Becke-Johnson damping." +
1463 explanation + " To run two-body D3(BJ) in both codes, add " +
1464 lines_to_add + " to &SYSTEM.");
1465 }
1466
1467 /// Position of the first `target` outside a quoted string, or npos.
1468 static size_t first_unquoted_position(const std::string& line, char target) {
1469 char quote = '\0';
1470 for (size_t position = 0; position < line.size(); position++) {
1471 char character = line[position];
1472 if (quote != '\0') {
1473 if (character == quote)
1474 quote = '\0';
1475 } else if (character == '\'' || character == '"') {
1476 quote = character;
1477 } else if (character == target) {
1478 return position;
1479 }
1480 }
1481 return std::string::npos;
1482 }
1483
1484 /// Remove a trailing `!` comment, respecting quoted strings.
1485 static std::string strip_comment(const std::string& line) {
1486 return line.substr(0, first_unquoted_position(line, '!'));
1487 }
1488
1489 /**
1490 * Remove the `/` that closes the namelist from a line inside it, and say whether the
1491 * line had one.
1492 *
1493 * Fortran ends namelist input at the first slash outside a quoted string, so that is the
1494 * one taken: a slash inside quotes is part of a value (`pseudo_dir = './pseudo/'`), and
1495 * there is no unquoted value a slash can belong to.
1496 *
1497 * Fortran then skips the rest of the line, so anything written after the slash would be
1498 * dropped without a word -- by pw.x as well. It is refused instead, the way this parser
1499 * refuses every other piece of input it would otherwise drop.
1500 */
1501 static bool strip_namelist_terminator(std::string& line, const std::string& section) {
1502 size_t slash_position = first_unquoted_position(line, '/');
1503 if (slash_position == std::string::npos)
1504 return false;
1505 std::string after_slash = trim(line.substr(slash_position + 1));
1506 if (!after_slash.empty())
1507 throw std::runtime_error("QE namelist line '" + trim(line) + "' has '" + after_slash +
1508 "' after the '/' that closes &" + section +
1509 ". Fortran stops reading the namelist at that slash and "
1510 "skips the rest of the line, so Q3 refuses the line rather "
1511 "than drop that text without a word.");
1512 line = line.substr(0, slash_position);
1513 return true;
1514 }
1515
1516 /**
1517 * The key names assigned on one namelist line.
1518 *
1519 * Fortran allows several assignments per line separated by commas, and QE's
1520 * own examples use that form freely (`ibrav= 2, celldm(1) =10.2, nat= 2`),
1521 * so this splits rather than assuming one key per line. Array indices are
1522 * dropped: `celldm(3)` and `starting_magnetization(2)` classify under their
1523 * base names.
1524 */
1525 static std::vector<std::pair<std::string, std::string>>
1526 namelist_keys_on_line(const std::string& line) {
1527 std::vector<std::pair<std::string, std::string>> keys;
1528 std::vector<std::string> fragments;
1529 std::string current;
1530 char quote = '\0';
1531 /**
1532 * Commas inside parentheses are index separators, not entry
1533 * separators: `starting_ns_eigenvalue(5,2,1) = 1.d0` is one
1534 * assignment. Only a top-level comma splits.
1535 */
1536 int parenthesis_depth = 0;
1537 for (char character : line) {
1538 if (quote != '\0') {
1539 if (character == quote)
1540 quote = '\0';
1541 } else if (character == '\'' || character == '"') {
1542 quote = character;
1543 } else if (character == '(') {
1544 parenthesis_depth++;
1545 } else if (character == ')') {
1546 parenthesis_depth = std::max(0, parenthesis_depth - 1);
1547 } else if (character == ',' && parenthesis_depth == 0) {
1548 fragments.push_back(current);
1549 current.clear();
1550 continue;
1551 }
1552 current += character;
1553 }
1554 fragments.push_back(current);
1555
1556 for (const std::string& fragment : fragments) {
1557 std::string trimmed_fragment = trim(fragment);
1558 if (trimmed_fragment.empty())
1559 continue;
1560 size_t equals_position = trimmed_fragment.find('=');
1561 if (equals_position == std::string::npos)
1562 throw std::runtime_error(
1563 "QE namelist entry '" + trimmed_fragment +
1564 "' is not an assignment. Fortran's array-continuation form "
1565 "(`starting_magnetization = 0.5, 0.3`) is not read; give each element its "
1566 "own indexed assignment.");
1567 std::string key = to_lower(trim(trimmed_fragment.substr(0, equals_position)));
1568 size_t parenthesis_position = key.find('(');
1569 if (parenthesis_position != std::string::npos)
1570 key = key.substr(0, parenthesis_position);
1571 if (!key.empty())
1572 keys.emplace_back(
1573 key,
1574 to_lower(strip_quotes(trim(trimmed_fragment.substr(equals_position + 1)))));
1575 }
1576 return keys;
1577 }
1578
1579 /**
1580 * Whether a key's value is the QE default that leaves the feature off.
1581 *
1582 * Numeric defaults are compared as numbers so that `-10000`, `-10000.0` and
1583 * `-1.0d4` all read alike; everything else is compared literally, with
1584 * `.false.` going through the Fortran-logical spellings.
1585 */
1586 static bool is_default_value(const std::string& default_token, const std::string& value) {
1587 if (default_token == ".false.")
1588 return !fortran_logical_is_true(value);
1589 try {
1590 size_t consumed = 0;
1591 Real default_number = std::stod(default_token, &consumed);
1592 if (consumed == default_token.size())
1593 return std::abs(fortran_to_double(value) - default_number) < 1e-9;
1594 } catch (const std::exception&) {
1595 // Not numeric: fall through to the literal comparison.
1596 }
1597 return value == default_token;
1598 }
1599
1600 /**
1601 * Sort every key on a namelist line into honoured / neutral / unimplemented
1602 * / unknown, throwing on the last two.
1603 */
1604 static void classify_namelist_keys(const std::string& line, const std::string& section) {
1605 const auto& honoured = qe_input::honoured_keys_by_namelist().at(section);
1606 for (const auto& [key, value] : namelist_keys_on_line(line)) {
1607 if (honoured.count(key) != 0)
1608 continue;
1609 if (qe_input::physics_neutral_keys().count(key) != 0) {
1610 log::info(" QE input: {} accepted and ignored (does not affect results)", key);
1611 continue;
1612 }
1613 auto unused_ionic_step = qe_input::unused_ionic_step_keys().find(key);
1614 if (unused_ionic_step != qe_input::unused_ionic_step_keys().end()) {
1615 log::info(" QE input: {} is not used ({}); it changes how a relaxation "
1616 "reaches its minimum, not the minimum",
1617 key,
1618 unused_ionic_step->second);
1619 continue;
1620 }
1621 /**
1622 * A feature switched off explicitly asks for what Q3 already does,
1623 * so it is accepted silently — no note, because there is nothing
1624 * for the user to check.
1625 */
1626 auto inert = qe_input::keys_inert_at_default().find(key);
1627 if (inert != qe_input::keys_inert_at_default().end() &&
1628 is_default_value(inert->second, value))
1629 continue;
1630 auto unimplemented = qe_input::unimplemented_keys().find(key);
1631 if (unimplemented != qe_input::unimplemented_keys().end())
1632 throw std::runtime_error("QE namelist key '" + key + "' in &" + section +
1633 " is not supported: " + unimplemented->second +
1634 ". Remove it or use Q3's own input format.");
1635 /**
1636 * A key Q3 reads elsewhere is a misplacement, not an unknown key,
1637 * and saying which namelist it belongs in is more useful than
1638 * saying it was never heard of.
1639 */
1640 for (const auto& [other_section, other_keys] : qe_input::honoured_keys_by_namelist())
1641 if (other_section != section && other_keys.count(key) != 0)
1642 throw std::runtime_error("QE namelist key '" + key + "' belongs to &" +
1643 other_section + ", not &" + section);
1644 throw std::runtime_error(
1645 "QE namelist key '" + key + "' in &" + section +
1646 " is not recognized. Q3 refuses unknown keys rather than ignoring them, because "
1647 "ignoring one that changes physics silently produces a result that does not "
1648 "match the input.");
1649 }
1650 }
1651
1652 /**
1653 * QE's crystallographic cell form (A, B, C in angstrom and the three
1654 * cosines) converted to celldm, following QE's own A/B/C-to-celldm
1655 * convention — including which celldm each cosine lands in, which depends
1656 * on the Bravais index.
1657 */
1658 static void apply_crystallographic_constants(InputData& data,
1659 Real length_a,
1660 Real length_b,
1661 Real length_c,
1662 Real cosine_ab,
1663 Real cosine_ac,
1664 Real cosine_bc) {
1665 if (length_a <= 0.0)
1666 throw std::runtime_error("QE input gives A = " + std::to_string(length_a) +
1667 "; the lattice parameter A must be positive");
1668 if (data.cell_dimension_1 > 0.0)
1669 throw std::runtime_error("QE input gives both celldm(1) and A; they are alternative "
1670 "spellings of the same quantity and QE refuses the pair too");
1671
1672 data.cell_dimension_1 = length_a * constants::ANGSTROM_TO_BOHR;
1673 data.cell_dimension_2 = (length_b > 0.0) ? length_b / length_a : 0.0;
1674 data.cell_dimension_3 = (length_c > 0.0) ? length_c / length_a : 0.0;
1675
1676 int index = data.bravais_lattice_index;
1677 if (index == 14 || index == 0) {
1678 data.cell_dimension_4 = cosine_bc;
1679 data.cell_dimension_5 = cosine_ac;
1680 data.cell_dimension_6 = cosine_ab;
1681 } else if (index == -12 || index == -13) {
1682 data.cell_dimension_5 = cosine_ac;
1683 } else if (index == 5 || index == -5 || index == 12 || index == 13) {
1684 data.cell_dimension_4 = cosine_ab;
1685 }
1686 }
1687
1688 /// Whether a Fortran logical reads as true (`.true.`, `T`, `.t.`).
1689 static bool fortran_logical_is_true(const std::string& value) {
1690 std::string lower = to_lower(trim(strip_quotes(value)));
1691 return lower == ".true." || lower == "t" || lower == ".t." || lower == "true";
1692 }
1693
1694 /**
1695 * Whether `position` starts a whole namelist key rather than the tail of a
1696 * longer one.
1697 *
1698 * Substring matching is not good enough here: `conv_thr` occurs inside both
1699 * `etot_conv_thr` and `forc_conv_thr`, and the single-letter key `A` occurs
1700 * inside nearly everything. A key is preceded by a separator and followed
1701 * by `=`, whitespace, or its array index.
1702 */
1703 static bool is_whole_key_at(const std::string& lower_line, size_t position, size_t length) {
1704 if (position > 0) {
1705 char before = lower_line[position - 1];
1706 if (before != ' ' && before != '\t' && before != ',')
1707 return false;
1708 }
1709 size_t after_position = position + length;
1710 if (after_position >= lower_line.size())
1711 return false;
1712 char after = lower_line[after_position];
1713 return after == '=' || after == ' ' || after == '\t' || after == '(';
1714 }
1715
1716 /// Extract value from "key = value" or "key=value" line.
1717 static bool
1718 extract_key_value(const std::string& line, const std::string& key, std::string& value) {
1719 std::string lower_line = to_lower(line);
1720 std::string lower_key = to_lower(key);
1721 size_t position = lower_line.find(lower_key);
1722 while (position != std::string::npos &&
1723 !is_whole_key_at(lower_line, position, lower_key.size()))
1724 position = lower_line.find(lower_key, position + 1);
1725 if (position == std::string::npos)
1726 return false;
1727
1728 size_t equals_position = line.find('=', position);
1729 if (equals_position == std::string::npos)
1730 return false;
1731
1732 size_t start = equals_position + 1;
1733 while (start < line.size() && line[start] == ' ')
1734 start++;
1735 size_t end = line.find_first_of(",!\n\r", start);
1736 if (end == std::string::npos)
1737 end = line.size();
1738 value = trim(line.substr(start, end - start));
1739 value = strip_quotes(value);
1740 return true;
1741 }
1742};
1743
1744} // namespace q3
InputData parse(const std::string &filename) const override
Definition qe.hpp:316
constexpr Real ANGSTROM_TO_BOHR
Ångström → Bohr (ANGSTROM_AU)
constexpr Real HARTREE_TO_EV
Hartree → eV (AUTOEV)
void section(const std::string &title)
Definition logger.hpp:206
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
constexpr double cell_volume_threshold
const std::map< std::string, std::string > & unused_ionic_step_keys()
Definition qe.hpp:186
const std::map< std::string, std::string > & unimplemented_keys()
Definition qe.hpp:83
const std::set< std::string > & physics_neutral_keys()
Definition qe.hpp:53
const std::map< std::string, std::string > & keys_inert_at_default()
Definition qe.hpp:208
const std::map< std::string, std::set< std::string > > & honoured_keys_by_namelist()
Definition qe.hpp:248
Matrix3x3 get_lattice_vectors_from_bravais_index(int bravais_lattice_index, Real cell_dimension_1, Real cell_dimension_2=0, Real cell_dimension_3=0, Real cell_dimension_4=0, Real cell_dimension_5=0, Real cell_dimension_6=0)
Definition lattice.hpp:82
double Real
HubbardStartingEigenvalue make_hubbard_starting_eigenvalue(const std::string &species_symbol, int one_based_eigenvalue_index, int one_based_spin_index, Real target_occupation, const std::string &context)
Definition hubbard.hpp:65
std::string symbol
std::string pseudopotential_file
int species_index
Definition atom.hpp:20
std::array< bool, 3 > movable_components
Which Cartesian components a relaxation may move (pw.x if_pos; ESSE basis.constraints).
Definition atom.hpp:23
Vector3D position
Definition atom.hpp:21
Parsed input data — holds raw values read from input files.
Definition data.hpp:27
std::string dispersion_correction
Definition data.hpp:78
bool use_exact_wavefunction_grid
Definition data.hpp:303
int number_of_kpoint_pools
Definition data.hpp:280
std::string smearing
Definition data.hpp:75
std::string diagonalization_method
Definition data.hpp:113
Real cell_dimension_4
Definition data.hpp:139
int kpoint_grid_dimension_3
Definition data.hpp:159
Real mixing_beta
Definition data.hpp:59
Real charge_density_energy_cutoff
Definition data.hpp:111
int kpoint_grid_shift_1
Definition data.hpp:160
Real relax_energy_threshold
Definition data.hpp:273
std::vector< AtomSpecies > species
Definition data.hpp:153
std::string prefix
Definition data.hpp:178
int hubbard_fixed_occupation_iterations
Definition data.hpp:107
int maximum_iterations
Definition data.hpp:33
int kpoint_grid_dimension_1
Definition data.hpp:159
std::vector< Atom > atoms
Definition data.hpp:156
Real cell_dimension_6
Definition data.hpp:141
std::vector< HubbardStartingEigenvalue > hubbard_starting_eigenvalues
Definition data.hpp:100
std::string verbosity
Definition data.hpp:186
bool disable_symmetry
Definition data.hpp:192
std::vector< HubbardSpecification > hubbard
Definition data.hpp:85
int kpoint_grid_dimension_2
Definition data.hpp:159
std::string calculation
Definition data.hpp:116
std::string isolated_system_correction
Definition data.hpp:209
int mixing_history_size
Definition data.hpp:60
std::string mixing_preconditioning
Definition data.hpp:73
std::string hubbard_projector_type
Definition data.hpp:93
std::string mixing_mode
Definition data.hpp:58
Real convergence_threshold
Definition data.hpp:32
std::string pseudopotential_directory
Definition data.hpp:163
Real relax_force_threshold
Definition data.hpp:272
int kpoint_grid_shift_2
Definition data.hpp:160
Real smearing_width
Definition data.hpp:76
Real cell_dimension_1
Definition data.hpp:129
int number_of_bands
Definition data.hpp:112
int number_of_atoms
Definition data.hpp:149
int kpoint_grid_shift_3
Definition data.hpp:160
Real wavefunction_energy_cutoff
Definition data.hpp:110
std::string exchange_correlation_functional
Definition data.hpp:41
int bravais_lattice_index
Definition data.hpp:128
int relax_maximum_steps
Definition data.hpp:271
bool start_from_saved_charge_density
Definition data.hpp:260
Real cell_dimension_2
Definition data.hpp:137
Real cell_dimension_5
Definition data.hpp:140
Real cell_dimension_3
Definition data.hpp:138
int number_of_species
Definition data.hpp:150
std::vector< Real > starting_magnetization
Definition data.hpp:49
std::string output_directory
Definition data.hpp:177
Real total_charge
Definition data.hpp:83
int number_of_spin_components
Definition data.hpp:42
3×3 matrix stored row-major: row i = mat[i]