Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
pseudopotential.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * PseudopotentialProvenanceRecord — a plane-wave provenance record whose
5 * potentials are pseudopotentials, and which therefore has datasets to identify.
6 *
7 * Everything a reader needs in order to reproduce a run reduces, on the
8 * potential side, to *which files were read*. That is a fact about this
9 * representation and nothing above it, so it is contributed here: the record
10 * for the quantum system is built first, and this subclass makes it a record
11 * for a pseudopotential calculation.
12 *
13 * Reading the datasets a second time here is deliberate. The calculation is
14 * over by the time this runs, and reporting on it must not be able to end it:
15 * every failure below degrades one species' entry to what is still known rather
16 * than propagating. An entry that admits it does not know the hash is honest;
17 * an aborted run that had already computed its answer is not.
18 */
19
20#include <exception>
21#include <filesystem>
22#include <string>
23#include <vector>
24
25#include <nlohmann/json.hpp>
26
27#include "io/logger.hpp"
31#include "utils/sha256.hpp"
32
33namespace q3 {
34
35/**
36 * Map Q3's pseudopotential type tag to the consumer schema's spelling.
37 *
38 * Q3 carries the UPF-style short form ("NC", "US", "PAW"); the registry's
39 * identity block uses spelled-out names. Anything unrecognized passes through
40 * verbatim rather than being coerced into one of the three, so a new type shows
41 * up as itself instead of silently registering as norm-conserving.
42 */
43[[nodiscard]] inline std::string
44pseudopotential_kind_name(const std::string& pseudopotential_type) {
45 if (pseudopotential_type == "NC") {
46 return "norm_conserving";
47 }
48 if (pseudopotential_type == "US") {
49 return "ultrasoft";
50 }
51 if (pseudopotential_type == "PAW") {
52 return "projector_augmented_wave";
53 }
54 return pseudopotential_type;
55}
56
58 public:
59 using PlanewaveProvenanceRecord::PlanewaveProvenanceRecord;
60
61 protected:
62 void add_representation_method_details(nlohmann::json& method) const override {
64 method["pseudopotentials"] = pseudopotentials_json();
65 }
66
67 private:
68 /**
69 * Per-species pseudopotential identity: which file, and what is in it.
70 *
71 * The SHA-256 of the file contents is the identity the registry already
72 * uses, and the valence electron count is what a verifier integrates a
73 * submitted density against as a pre-flight check, so both are worth the
74 * extra read.
75 */
76 [[nodiscard]] nlohmann::json pseudopotentials_json() const {
77 nlohmann::json pseudopotentials = nlohmann::json::array();
78
79 std::vector<AtomSpecies> species = input().species;
80 PseudopotentialSet loaded_set;
81 bool set_is_loaded = false;
82 try {
83 loaded_set = load_pseudopotentials(species, input().pseudopotential_directory);
84 set_is_loaded = true;
85 } catch (const std::exception& error) {
87 "Provenance: cannot re-read pseudopotentials ({}); recording file identity only.",
88 error.what());
89 }
90
91 for (std::size_t species_index = 0; species_index < input().species.size();
92 species_index++) {
93 const AtomSpecies& one_species = input().species[species_index];
94 nlohmann::json identity = species_identity_json(one_species);
95
96 if (set_is_loaded && species_index < static_cast<std::size_t>(loaded_set.size())) {
97 const Pseudopotential& pseudopotential =
98 loaded_set[static_cast<int>(species_index)];
99 identity["kind"] = pseudopotential_kind_name(pseudopotential.pseudopotential_type);
100 identity["valence_electron_count"] = pseudopotential.valence_charge;
101 }
102
103 pseudopotentials.push_back(identity);
104 }
105 return pseudopotentials;
106 }
107
108 /// One species' file identity: its dataset's name, and the hash of its contents.
109 [[nodiscard]] nlohmann::json species_identity_json(const AtomSpecies& one_species) const {
110 nlohmann::json identity;
111 identity["element_symbol"] = one_species.symbol;
112 identity["content_sha256"] = "";
113
114 /**
115 * Everything below is inside the guard, path resolution included.
116 * resolve_pseudopotential_path() calls std::filesystem::exists(), which
117 * throws for anything other than a plain not-found -- a stale mount or
118 * a permissions change during an hours-long run reaches it as
119 * "Permission denied", not as false. Outside the guard that exception
120 * would escape Flavor::run() before the results file is written, and a
121 * converged answer would be lost to a failure in the code that only
122 * reports on it.
123 */
124 try {
125 /**
126 * The file's name, deliberately not the path it was read from.
127 * Input parsers resolve pseudopotential paths against the manifest,
128 * so the stored value is typically absolute and machine-specific --
129 * and this block is retained, copied between machines, and
130 * registered. A record that carries someone's home directory is not
131 * portable, and the path is not what identifies the dataset anyway:
132 * content_sha256 is, and the name is the human label beside it.
133 */
134 identity["source_filename"] =
135 std::filesystem::path(one_species.pseudopotential_file).filename().string();
136
137 const std::string resolved_path = resolve_pseudopotential_path(
138 one_species.pseudopotential_file, input().pseudopotential_directory);
139 identity["content_sha256"] = SHA256::hexadecimal_digest_of_file(resolved_path);
140 } catch (const std::exception& error) {
141 log::warn("Provenance: cannot identify the dataset for species '{}' ({}); "
142 "recording an empty digest.",
143 one_species.symbol,
144 error.what());
145 }
146 return identity;
147 }
148};
149
150} // namespace q3
void add_representation_method_details(nlohmann::json &method) const override
Definition init.hpp:32
void add_representation_method_details(nlohmann::json &method) const override
int size() const
Definition set.hpp:98
const InputData & input() const
The input the calculation consumed, for a subclass to report on in turn.
Definition init.hpp:92
static std::string hexadecimal_digest_of_file(const std::string &path)
Definition sha256.hpp:63
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
PseudopotentialSet load_pseudopotentials(std::vector< AtomSpecies > &species, const std::string &pseudopotential_directory)
Definition loader.hpp:191
std::string pseudopotential_kind_name(const std::string &pseudopotential_type)
std::string resolve_pseudopotential_path(const std::string &pseudopotential_file, const std::string &pseudopotential_directory)
Definition loader.hpp:34
std::vector< AtomSpecies > species
Definition data.hpp:153