Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
factory.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * create_input_parser — factory for input file parsers.
5 *
6 * The parser comes from the registered input format (input_format_registry.hpp)
7 * that the path or `--format` names. Core registers two:
8 * ".in" / ".qe" → QEInputParser
9 * all others → YAMLInputParser
10 * and a build-time add-on may register more. A format recognised by file name
11 * or directory contents outranks one recognised by extension, which outranks
12 * the YAML fallback, so a path goes to the most specific format that claims it.
13 *
14 * Also provides parse_input(), which every entry point reads its input through,
15 * and substitute_material(), the `--material` override.
16 */
17
18#include <cctype>
19#include <filesystem>
20#include <memory>
21#include <stdexcept>
22#include <string>
23
28
29namespace q3 {
30
31/**
32 * The registered format an input is read as.
33 *
34 * @param format an explicit format name, as `--format` gives it; empty detects
35 * from the path. Detection cannot see through a renamed file or
36 * a nested directory, so the name is the way to say what an
37 * input is. An unregistered name is an error rather than a
38 * silent fall-through to YAML.
39 */
40inline const InputFormat& input_format_for(const std::string& filename,
41 const std::string& format = "") {
42 const auto& registry = input_format_registry();
43 if (!format.empty()) {
44 if (const InputFormat* named = registry.find(format))
45 return *named;
46 throw std::runtime_error("Unknown input format '" + format +
47 "'; expected one of: " + registry.canonical_names(", "));
48 }
49 if (const InputFormat* detected = detect_input_format(filename))
50 return *detected;
51 throw std::runtime_error(
52 "No registered input format recognises '" + filename +
53 "'; the formats this build reads are: " + registry.canonical_names(", "));
54}
55
56/// Create the input parser for this path, or for the format `--format` names.
57inline std::unique_ptr<InputParser> create_input_parser(const std::string& filename,
58 const std::string& format = "") {
59 return input_format_for(filename, format).create_parser();
60}
61
62/**
63 * What an argument-less invocation should read, or empty if nothing.
64 *
65 * Some formats are run with no arguments from the directory that holds the
66 * input, so an entry point given no input path asks the registered formats
67 * whether one claims the working directory. Every entry point shares this
68 * rule rather than each inventing its own, and a missing argument stays an
69 * error when no format claims the directory.
70 */
71inline std::string resolve_argument_less_input() {
72 return format_claiming_directory(".") != nullptr ? "." : std::string{};
73}
74
75/**
76 * Substitute the structure of *input* with the ESSE material in *material_file*
77 * (`--material <file>`): its cell and positions replace the input's, as a YAML
78 * input's `material` key would. Everything else — settings, species, the order
79 * of the atoms and their constraints — stays the input's, so a pw.x deck runs at
80 * a new geometry unchanged. The cell keeps the input's lattice parameter, so its
81 * alat-based units stay fixed across a relaxation's steps, as in pw.x.
82 */
83inline void substitute_material(InputData& input, const std::string& material_file) {
84 InputData material;
86 if (material.atoms.size() != input.atoms.size())
87 throw std::runtime_error("--material " + material_file + " has " +
88 std::to_string(material.atoms.size()) + " atoms; the input has " +
89 std::to_string(input.atoms.size()));
90 if (!material.has_explicit_lattice_vectors)
91 throw std::runtime_error("--material " + material_file + " must give lattice.vectors");
92 Matrix3x3 lattice_vectors_bohr;
93 for (int row = 0; row < 3; ++row)
94 lattice_vectors_bohr[row] = material.lattice_vectors[row] * material.cell_dimension_1;
95 input.set_free_lattice_vectors(lattice_vectors_bohr, input.cell_dimension_1);
96 for (size_t atom_index = 0; atom_index < input.atoms.size(); ++atom_index)
97 input.atoms[atom_index].position = material.atoms[atom_index].position;
98}
99
100/**
101 * Parse input using the given or auto-detected format.
102 *
103 * A relaxation input ('relax', 'vc-relax') runs here as one SCF at its own
104 * geometry, logged as such: a relaxation is driven from Python, one SCF per
105 * geometry, on whichever binary it is given.
106 *
107 * Every entry point reads its input through here, so this is also where an
108 * input that names no exchange-correlation functional gets the one its
109 * pseudopotentials state. The InputData returned names the functional the run
110 * will use, which `--check`, the provenance record and the SCF all read. It
111 * stays empty only for an input missing a dataset, which cannot run.
112 *
113 * The name is lower-cased here, whichever reader or dataset supplied it and
114 * however the input spelled it: `pbe`, the spelling the example inputs and
115 * ESSE use. The readers keep what they read, so this is the one place the
116 * convention is applied.
117 *
118 * The format's canonical name is recorded as InputData::source_format, so the
119 * finished run can be handed back to that same format for any companion
120 * outputs its conventions expect (io/output/companion_outputs.hpp).
121 */
122inline InputData parse_input(const std::string& filename, const std::string& format = "") {
123 const InputFormat& input_format = input_format_for(filename, format);
124 InputData input = input_format.create_parser()->parse(filename);
125 input.source_format = input_format.names().front();
126 if (input.calculation == "relaxation" || input.calculation == "variable_cell_relaxation") {
127 log::info(" Calculation '{}' runs as one SCF at this geometry, starting from a saved "
128 "density when one fits: a step of the relaxation q3.workflows."
129 "run_relax_workflow / run_vc_relax_workflow drives",
130 input.calculation);
131 input.calculation = "self_consistent_field";
133 input.is_relaxation_step = true;
134 }
135 if (input.exchange_correlation_functional.empty())
138 for (auto& character : input.exchange_correlation_functional)
139 character = static_cast<char>(std::tolower(static_cast<unsigned char>(character)));
140 return input;
141}
142
143} // namespace q3
One input format a run can be read from; see the file comment.
virtual std::unique_ptr< InputParser > create_parser() const =0
A parser for inputs of this format.
virtual std::vector< std::string > names() const =0
static void parse_material(const nlohmann::json &material_json, InputData &data)
Parse ESSE material JSON into InputData fields; parse_input() also reads --material here.
Definition yaml.hpp:277
nlohmann::json read_json_file(const std::string &path)
Definition utils.hpp:34
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
std::unique_ptr< InputParser > create_input_parser(const std::string &filename, const std::string &format="")
Create the input parser for this path, or for the format --format names.
Definition factory.hpp:57
void substitute_material(InputData &input, const std::string &material_file)
Definition factory.hpp:83
InputData parse_input(const std::string &filename, const std::string &format="")
Definition factory.hpp:122
const InputFormat * detect_input_format(const std::filesystem::path &path)
std::string functional_from_pseudopotentials(const std::vector< AtomSpecies > &species, const std::string &pseudopotential_directory)
Definition loader.hpp:109
const InputFormat * format_claiming_directory(const std::filesystem::path &directory)
std::string resolve_argument_less_input()
Definition factory.hpp:71
Registry< InputFormat > & input_format_registry()
The registry every entry point reads input formats from.
const InputFormat & input_format_for(const std::string &filename, const std::string &format="")
Definition factory.hpp:40
Parsed input data — holds raw values read from input files.
Definition data.hpp:27
std::vector< AtomSpecies > species
Definition data.hpp:153
bool is_relaxation_step
Definition data.hpp:268
std::vector< Atom > atoms
Definition data.hpp:156
bool has_explicit_lattice_vectors
Definition data.hpp:148
std::string calculation
Definition data.hpp:116
std::string pseudopotential_directory
Definition data.hpp:163
void set_free_lattice_vectors(const Matrix3x3 &vectors_bohr, Real preset_lattice_parameter=0.0)
Definition data.hpp:317
Real cell_dimension_1
Definition data.hpp:129
std::string source_format
Definition data.hpp:174
std::string exchange_correlation_functional
Definition data.hpp:41
bool start_from_saved_charge_density
Definition data.hpp:260
Matrix3x3 lattice_vectors
Definition data.hpp:147
3×3 matrix stored row-major: row i = mat[i]