Loading...
Searching...
No Matches
init.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * UPF v1 core reader.
5 *
6 * Reads the common (norm-conserving) sections of a UPF v1 format file:
7 * PP_HEADER, PP_R, PP_RAB, PP_LOCAL, PP_NONLOCAL (beta projectors + D matrix),
8 * PP_PSWFC (atomic wavefunctions), PP_RHOATOM, and PP_NLCC (core charge).
9 *
10 * After this reader runs, the Pseudopotential struct has all fields populated
11 * that are common to both norm-conserving and ultrasoft pseudopotential_set.
12 * Ultrasoft-specific sections (PP_QIJ) are handled by UltrasoftUPFv1Reader.
13 */
14
15#include <fstream>
16#include <sstream>
17#include <stdexcept>
18#include <string>
19
20#include "io/logger.hpp"
23
24namespace q3 {
25
27 public:
28 UPFv1Reader(Pseudopotential& pseudopotential, std::ifstream& file_stream)
29 : pseudopotential_(pseudopotential), file_stream_(file_stream) {}
30
31 void read() {
32 read_header();
33 read_mesh();
34 read_local_potential();
35 read_beta_projectors();
36 read_d_matrix();
37 read_pseudo_wavefunctions();
38 read_atomic_charge_density();
39 read_core_charge_density();
40 }
41
42 private:
43 void read_header() {
44 using namespace upf_utilities;
45 find_tag(file_stream_, "<PP_HEADER>");
46 std::string line;
47 std::getline(file_stream_, line); // version
48 std::getline(file_stream_, line);
49 {
50 std::istringstream stream(line);
51 stream >> pseudopotential_.element;
52 }
53 std::getline(file_stream_, line);
54 {
55 std::istringstream stream(line);
56 std::string token;
57 stream >> token;
58 std::string canonical_type = canonical_pseudopotential_type(token);
59 if (canonical_type == "PAW")
60 /**
61 * Reject here, before the later sections are parsed: a PAW v1 file
62 * is missing the sections this reader requires, and letting it fail
63 * there would misreport a recognized-but-unsupported type as a
64 * malformed file (or worse, run it down the NC path).
65 */
66 throw UnsupportedPseudopotentialTypeError(
67 "PAW pseudopotential not supported: UPF v1 header declares type PAW. "
68 "Q3 reads PAW datasets in UPF v2 format only; convert the file to v2, "
69 "or use an NC or US file instead.");
70 pseudopotential_.pseudopotential_type = canonical_type;
71 }
72 std::getline(file_stream_, line);
73 {
74 std::istringstream stream(line);
75 std::string token;
76 stream >> token;
77 pseudopotential_.has_nonlinear_core_correction =
78 (token == "T" || token == "t" || token == ".TRUE." || token == ".true.");
79 }
80 std::getline(file_stream_, line);
81 pseudopotential_.xc_functional = trim(line.substr(0, line.find("Exchange")));
82 std::getline(file_stream_, line);
83 {
84 std::istringstream stream(line);
85 stream >> pseudopotential_.valence_charge;
86 }
87 std::getline(file_stream_, line); // total energy
88 std::getline(file_stream_, line); // suggested cutoff
89 std::getline(file_stream_, line);
90 {
91 std::istringstream stream(line);
92 stream >> pseudopotential_.maximum_angular_momentum;
93 }
94 std::getline(file_stream_, line);
95 {
96 std::istringstream stream(line);
97 stream >> pseudopotential_.mesh_size;
98 }
99 std::getline(file_stream_, line);
100 {
101 std::istringstream stream(line);
102 stream >> pseudopotential_.number_of_wavefunctions >>
103 pseudopotential_.number_of_projectors;
104 }
105 std::getline(file_stream_, line);
106 for (int wavefunction_index = 0;
107 wavefunction_index < pseudopotential_.number_of_wavefunctions;
108 wavefunction_index++)
109 std::getline(file_stream_, line);
110
111 check_header_is_usable();
112 }
113
114 /**
115 * Reject a header whose required quantities did not parse.
116 *
117 * v1 headers are positional, read with stream extraction rather than named
118 * attributes, so a truncated or misaligned file does not fail here — the
119 * extraction just leaves the field at zero and the reader carries on to
120 * request zero-length grids, failing much later with nothing pointing back
121 * at the header. These bounds turn that into an immediate, nameable error.
122 * The v2 reader gets the same protection per-attribute through
123 * required_integer_attribute / required_real_attribute.
124 */
125 void check_header_is_usable() {
126 auto reject = [this](const std::string& detail) {
127 throw std::runtime_error("Malformed UPF v1 file for element '" +
128 pseudopotential_.element + "': " + detail +
129 ". The PP_HEADER block is truncated or misaligned.");
130 };
131 if (pseudopotential_.mesh_size <= 0)
132 reject("mesh size read as " + std::to_string(pseudopotential_.mesh_size));
133 if (pseudopotential_.valence_charge <= 0.0)
134 reject("valence charge read as " + std::to_string(pseudopotential_.valence_charge));
135 if (pseudopotential_.number_of_projectors < 0)
136 reject("projector count read as " +
137 std::to_string(pseudopotential_.number_of_projectors));
138 if (pseudopotential_.maximum_angular_momentum < 0)
139 reject("maximum angular momentum read as " +
140 std::to_string(pseudopotential_.maximum_angular_momentum));
141 }
142
143 void read_mesh() {
144 using namespace upf_utilities;
145 find_tag(file_stream_, "<PP_R>");
146 pseudopotential_.radial_grid = read_reals(file_stream_, pseudopotential_.mesh_size);
147 find_tag(file_stream_, "<PP_RAB>");
148 pseudopotential_.radial_grid_derivative =
149 read_reals(file_stream_, pseudopotential_.mesh_size);
150 }
151
152 void read_local_potential() {
153 using namespace upf_utilities;
154 find_tag(file_stream_, "<PP_LOCAL>");
155 pseudopotential_.potential_local.radial_values =
156 read_reals(file_stream_, pseudopotential_.mesh_size);
157 }
158
159 void read_beta_projectors() {
160 using namespace upf_utilities;
161 find_tag(file_stream_, "<PP_NONLOCAL>");
162 pseudopotential_.projectors.resize(pseudopotential_.number_of_projectors);
163 for (int beta_index = 0; beta_index < pseudopotential_.number_of_projectors; beta_index++) {
164 find_tag(file_stream_, "<PP_BETA>");
165 // Next line: "idx l Beta L" -- parse with istringstream
166 std::string beta_line;
167 std::getline(file_stream_, beta_line);
168 {
169 std::istringstream stream(beta_line);
170 int projector_index, angular_momentum;
171 stream >> projector_index >> angular_momentum;
172 pseudopotential_.projectors[beta_index].angular_momentum = angular_momentum;
173 }
174 // Next line: nr_cutoff
175 std::getline(file_stream_, beta_line);
176 {
177 std::istringstream stream(beta_line);
178 int cutoff_index;
179 stream >> cutoff_index;
180 pseudopotential_.projectors[beta_index].number_of_radial_cutoff_points =
181 cutoff_index;
182 }
183 pseudopotential_.projectors[beta_index].radial_values =
184 read_reals(file_stream_, pseudopotential_.mesh_size);
185 pseudopotential_.projectors[beta_index].radial_values.resize(pseudopotential_.mesh_size,
186 0.0);
187 }
188 }
189
190 void read_d_matrix() {
191 using namespace upf_utilities;
192 find_tag(file_stream_, "<PP_DIJ>");
193 int number_of_d_entries;
194 {
195 std::string dij_line;
196 std::getline(file_stream_, dij_line);
197 std::istringstream stream(dij_line);
198 stream >> number_of_d_entries;
199 }
200 pseudopotential_.d_ion_matrix.resize(
201 pseudopotential_.number_of_projectors,
202 std::vector<Real>(pseudopotential_.number_of_projectors, 0.0));
203 for (int entry_index = 0; entry_index < number_of_d_entries; entry_index++) {
204 int row_index, column_index;
205 Real value;
206 file_stream_ >> row_index >> column_index >> value;
207 file_stream_.clear(); // In case there's trailing text
208 pseudopotential_.d_ion_matrix[row_index - 1][column_index - 1] = value;
209 pseudopotential_.d_ion_matrix[column_index - 1][row_index - 1] = value;
210 }
211 }
212
213 void read_pseudo_wavefunctions() {
214 using namespace upf_utilities;
215 find_tag(file_stream_, "<PP_PSWFC>");
216 pseudopotential_.wavefunctions_atomic.resize(pseudopotential_.number_of_wavefunctions);
217 for (int wavefunction_index = 0;
218 wavefunction_index < pseudopotential_.number_of_wavefunctions;
219 wavefunction_index++) {
220 std::string line;
221 do {
222 std::getline(file_stream_, line);
223 } while (trim(line).empty());
224 std::istringstream stream(line);
225 std::string label;
226 int angular_momentum;
227 Real occupations;
228 stream >> label >> angular_momentum >> occupations;
229 pseudopotential_.wavefunctions_atomic[wavefunction_index].label = label;
230 pseudopotential_.wavefunctions_atomic[wavefunction_index].angular_momentum =
231 angular_momentum;
232 pseudopotential_.wavefunctions_atomic[wavefunction_index].occupations = occupations;
233 pseudopotential_.wavefunctions_atomic[wavefunction_index].radial_values =
234 read_reals(file_stream_, pseudopotential_.mesh_size);
235 }
236 }
237
238 void read_atomic_charge_density() {
239 using namespace upf_utilities;
240 find_tag(file_stream_, "<PP_RHOATOM>");
241 pseudopotential_.charge_atomic.radial_values =
242 read_reals(file_stream_, pseudopotential_.mesh_size);
243 }
244
245 void read_core_charge_density() {
246 using namespace upf_utilities;
247 if (!pseudopotential_.has_nonlinear_core_correction)
248 return;
249
250 file_stream_.clear();
251 file_stream_.seekg(0);
252 if (find_tag(file_stream_, "<PP_NLCC>")) {
253 pseudopotential_.charge_core.radial_values =
254 read_reals(file_stream_, pseudopotential_.mesh_size);
255 log::info(" PP {}: NLCC core charge read ({} points)",
256 pseudopotential_.element,
257 static_cast<int>(pseudopotential_.charge_core.radial_values.size()));
258 } else {
259 log::warn(" PP {}: has_nlcc=true but PP_NLCC section not found",
260 pseudopotential_.element);
261 pseudopotential_.has_nonlinear_core_correction = false;
262 }
263 }
264
265 Pseudopotential& pseudopotential_;
266 std::ifstream& file_stream_;
267};
268
269} // namespace q3
Base class for all pseudopotential types.
Definition init.hpp:35
std::vector< Real > radial_grid_derivative
Definition init.hpp:77
std::vector< Projector > projectors
Definition init.hpp:80
std::vector< WavefunctionAtomic > wavefunctions_atomic
Definition init.hpp:82
std::string pseudopotential_type
Definition init.hpp:67
int number_of_projectors
Definition init.hpp:72
ChargeAtomic charge_atomic
Definition init.hpp:83
bool has_nonlinear_core_correction
Definition init.hpp:74
std::string element
Definition init.hpp:66
int number_of_wavefunctions
Definition init.hpp:73
std::vector< Real > radial_grid
Definition init.hpp:76
std::vector< std::vector< Real > > d_ion_matrix
Definition init.hpp:81
std::string xc_functional
Definition init.hpp:68
PotentialLocal potential_local
Definition init.hpp:78
int maximum_angular_momentum
Definition init.hpp:70
ChargeCore charge_core
Definition init.hpp:84
void read()
Definition init.hpp:31
UPFv1Reader(Pseudopotential &pseudopotential, std::ifstream &file_stream)
Definition init.hpp:28
void warn(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:187
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
std::string trim(const std::string &input)
Remove leading and trailing whitespace from a string.
Definition utilities.hpp:27
std::vector< Real > read_reals(std::ifstream &file_stream, int count)
Read a specified number of real values from a stream.
Definition utilities.hpp:77
bool find_tag(std::ifstream &file_stream, const std::string &tag)
Advance the stream until a line containing the given tag is found.
Definition utilities.hpp:89
double Real
std::vector< Real > radial_values
std::vector< Real > radial_values
std::vector< Real > radial_values