Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
interpolate_command.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * InterpolateCommand — `q3_utility interpolate <input.yml>`: evaluate a saved
5 * interpolation record at the k-points an input asks for, and on a dense mesh
6 * for a density of states.
7 *
8 * The input is an ordinary Q3 manifest with `calculation: interpolation` and
9 * a `control.interpolation` block. Its `outdir`/`prefix` name the run whose
10 * NSCF built the record (`<outdir>/<prefix>.save/interpolation.q3ip`); its
11 * `control.kpoints_list`, or a `band_path` whose vertices carry coordinates,
12 * names the k-points; `control.interpolation.dos_mesh` with `dos_broadening`
13 * and `dos_energy_steps` asks for the DOS. Nothing here touches plane waves:
14 * the record is read, the method's interpolator comes from the umbrella
15 * factory, and the layer-5 calculators evaluate it -- which is why this is a
16 * utility subcommand and not an engine flavor.
17 *
18 * Output: `<outdir>/<prefix>_interpolation_results.json` (or `--output`),
19 * with the `eigenvalues` at the k-points evaluated, listed as the engine's
20 * own results file lists them so the Python readers read both the same way,
21 * the `density_of_states` as ESSE's record, the method's diagnostics, and
22 * the same provenance stamp every result carries.
23 */
24
25#include <algorithm>
26#include <array>
27#include <filesystem>
28#include <stdexcept>
29#include <string>
30#include <vector>
31
32#include "io/input/data.hpp"
37#include "io/logger.hpp"
48
49namespace q3 {
50
52 public:
53 static void print_usage() {
54 log::error("Usage: q3_utility interpolate <input.yml> [--output <file>]");
55 log::error(" <input.yml> a manifest with calculation: interpolation and a "
56 "control.interpolation block;");
57 log::error(" its control.outdir/prefix name the run whose NSCF saved the "
58 "record");
59 log::error(" --output <file> where to write the JSON results (default "
60 "<outdir>/<prefix>_interpolation_results.json)");
61 }
62
63 /// Returns the process exit code; argument errors print usage, everything else throws.
64 static int run(const std::vector<std::string>& arguments) {
65 std::string input_file;
66 std::string output_file;
67 for (std::size_t index = 0; index < arguments.size(); index++) {
68 const std::string& argument = arguments[index];
69 auto take_value = [&](const char* flag) -> std::string {
70 if (index + 1 >= arguments.size()) {
71 throw std::runtime_error(std::string(flag) + " needs a value");
72 }
73 return arguments[++index];
74 };
75 if (argument == "--output") {
76 output_file = take_value("--output");
77 } else if (argument == "-h" || argument == "--help") {
79 return 0;
80 } else if (argument.rfind("-", 0) == 0) {
81 log::error("Unknown option '{}'", argument);
83 return 1;
84 } else if (input_file.empty()) {
85 input_file = argument;
86 } else {
87 log::error("Unexpected extra argument '{}'", argument);
89 return 1;
90 }
91 }
92 if (input_file.empty()) {
94 return 1;
95 }
96 evaluate(input_file, output_file);
97 return 0;
98 }
99
100 private:
101 /// The record the input names, evaluated where the input asks, written as the results file.
102 static void evaluate(const std::string& input_file, const std::string& output_file) {
103 const InputData input = evaluation_input(input_file);
104 const std::string record_path =
106 const InterpolationRecord record = saved_record(record_path, input.interpolation);
107
110 const QuantumStateInterpolator& interpolator = *built.interpolator;
111 log::info("q3_utility interpolate: {} from {}", interpolator.description(), record_path);
112
114 record.fermi_energy_rydberg);
115 document.describe_model(record.method, record_path, interpolator);
116 document.add_construction(
117 built.wannier,
118 WannierQualityAssessor(record.real_lattice_bohr, atom_positions_crystal(input))
119 .assess(built.wannier));
120
121 const std::vector<Vector3D> kpoints = evaluation_kpoints(input);
122 if (!kpoints.empty()) {
123 document.add_bands(kpoints,
124 InterpolatedBandStructureCalculator(interpolator).compute(kpoints));
125 log::info("evaluated {} k-points", kpoints.size());
126 }
128 const std::array<int, 3>& mesh = input.interpolation.dos_mesh;
129 document.add_density_of_states(
130 InterpolatedDensityOfStatesCalculator(interpolator)
131 .compute(
132 mesh[0], mesh[1], mesh[2], input.dos_broadening, input.dos_energy_steps));
133 log::info(
134 "evaluated a density of states on a {}x{}x{} mesh", mesh[0], mesh[1], mesh[2]);
135 }
136 if (kpoints.empty() && !input.interpolation.wants_density_of_states()) {
137 log::warn("nothing to evaluate: give control.kpoints_list (or a band_path whose "
138 "vertices carry coordinates) for bands, or control.interpolation.dos_mesh "
139 "for a density of states");
140 }
141
142 document.write(output_file.empty() ? input.output_directory + "/" + input.prefix +
143 "_interpolation_results.json"
144 : output_file);
145 }
146
147 /// The manifest, refused unless it is an evaluation request.
148 [[nodiscard]] static InputData evaluation_input(const std::string& input_file) {
149 InputData input = parse_input(input_file);
150 if (input.calculation != "interpolation") {
151 throw std::runtime_error(
152 "q3_utility interpolate evaluates inputs with calculation: interpolation; this one "
153 "has calculation: " +
154 input.calculation +
155 ". A build request (calculation: nscf with a control.interpolation block) runs "
156 "with q3, which saves the record this command reads.");
157 }
158 if (!input.interpolation.requested()) {
159 throw std::runtime_error(
160 "the input has no control.interpolation block, so there is nothing to evaluate");
161 }
162 return input;
163 }
164
165 /// The record an NSCF run saved at `record_path`, refused if the manifest names another method.
166 [[nodiscard]] static InterpolationRecord
167 saved_record(const std::string& record_path, const InterpolationSpecification& specification) {
168 if (!std::filesystem::exists(record_path)) {
169 throw std::runtime_error(
170 "no interpolation record at '" + record_path +
171 "'. An NSCF run (calculation: nscf) whose control.interpolation block asked for "
172 "it writes the record into its own outdir/prefix; point this input's "
173 "control.outdir and control.prefix at that run, or import Wannier90 files with "
174 "`q3_utility convert --from wannier90`.");
175 }
176 InterpolationRecord record = read_interpolation_record(record_path);
177 if (!specification.method.empty() && specification.method != record.method) {
178 throw std::runtime_error("the input asks for control.interpolation.method '" +
179 specification.method + "' but the record at '" + record_path +
180 "' was built by '" + record.method + "'");
181 }
182 return record;
183 }
184
185 /// The atoms of the manifest's material, which the Wannier centres are judged against.
186 [[nodiscard]] static std::vector<Vector3D> atom_positions_crystal(const InputData& input) {
187 std::vector<Vector3D> positions;
188 positions.reserve(input.atoms.size());
189 for (const Atom& atom : input.atoms) {
190 positions.push_back(atom.position);
191 }
192 return positions;
193 }
194
195 /**
196 * The k-points to evaluate: an explicit `kpoints_list` as given, or a
197 * `band_path` whose vertices carry coordinates, walked
198 * `band_path_points_per_segment` steps per segment plus the final vertex
199 * -- the same sampling the band-structure workflow uses.
200 *
201 * Labels without coordinates are refused at parse time (yaml.hpp): this
202 * command has no high-symmetry table (that lookup lives in
203 * q3.workflows.kpath), and evaluating a label at the origin would
204 * silently return Gamma's eigenvalues under another name.
205 */
206 [[nodiscard]] static std::vector<Vector3D> evaluation_kpoints(const InputData& input) {
207 std::vector<Vector3D> kpoints;
208 if (!input.kpoints_list.empty()) {
209 kpoints.reserve(input.kpoints_list.size());
210 for (const auto& entry : input.kpoints_list) {
211 kpoints.push_back(entry.first);
212 }
213 return kpoints;
214 }
215 if (input.band_path.empty()) {
216 return kpoints;
217 }
218 const int per_segment = std::max(1, input.band_path_points_per_segment);
219 for (std::size_t segment = 0; segment + 1 < input.band_path.size(); segment++) {
220 const Vector3D& start = input.band_path[segment].second;
221 const Vector3D& end = input.band_path[segment + 1].second;
222 for (int step = 0; step < per_segment; step++) {
223 const Real fraction = static_cast<Real>(step) / static_cast<Real>(per_segment);
224 kpoints.push_back(start + (end - start) * fraction);
225 }
226 }
227 kpoints.push_back(input.band_path.back().second);
228 return kpoints;
229 }
230};
231
232} // namespace q3
static int run(const std::vector< std::string > &arguments)
Returns the process exit code; argument errors print usage, everything else throws.
virtual std::string description() const =0
One line for logs: the method and the size of the model.
void warn(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:187
void error(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:192
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
std::string interpolation_record_path(const std::string &output_directory, const std::string &prefix)
Where a run with prefix in output_directory keeps its interpolation record.
double Real
InputData parse_input(const std::string &filename, const std::string &format="")
Definition factory.hpp:122
InterpolationRecord read_interpolation_record(const std::string &file_path)
QuantumStateInterpolatorConstruction create_quantum_state_interpolator(const InterpolationRecord &record, const InterpolationSpecification &specification={})
Definition factory.hpp:51
nlohmann::json build_provenance_json(const InputData &input)
Build the provenance block for a completed run, from the input it consumed.
Definition factory.hpp:44
Parsed input data — holds raw values read from input files.
Definition data.hpp:27
std::string prefix
Definition data.hpp:178
InterpolationSpecification interpolation
Definition data.hpp:248
int dos_energy_steps
Definition data.hpp:235
Real dos_broadening
Definition data.hpp:232
std::string output_directory
Definition data.hpp:177
Matrix3x3 real_lattice_bohr
Rows a1, a2, a3, Cartesian, in bohr.
Definition record.hpp:34
std::string method
The method that built the payload: "wannier".
Definition record.hpp:31
std::array< int, 3 > dos_mesh
Evaluation step: the dense k-mesh for the interpolated DOS; {0,0,0} = none.
std::unique_ptr< QuantumStateInterpolator > interpolator