Loading...
Searching...
No Matches
init.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * UPF v2 core reader.
5 *
6 * Reads the common (norm-conserving) sections of a UPF v2 format file:
7 * PP_HEADER (XML attributes), PP_MESH/PP_R/PP_RAB, PP_NLCC, PP_LOCAL,
8 * PP_NONLOCAL (PP_BETA.N projectors + PP_DIJ), PP_PSWFC (PP_CHI.N), PP_RHOATOM.
9 *
10 * UPF v2 uses XML-like formatting where section data is embedded between
11 * opening and closing tags, and header fields are XML attributes.
12 * The entire file content is read into memory for string-based parsing.
13 *
14 * After this reader runs, the Pseudopotential struct has all fields populated
15 * that are common to both norm-conserving and ultrasoft pseudopotential_set.
16 * Ultrasoft-specific sections (PP_AUGMENTATION) are handled by UltrasoftUPFv2Reader.
17 */
18
19#include <sstream>
20#include <stdexcept>
21#include <string>
22
23#include "io/logger.hpp"
26
27namespace q3 {
28
30 public:
31 /**
32 * @param source_description File path this content came from, used to name
33 * the file in errors about malformed fields.
34 */
35 UPFv2Reader(Pseudopotential& pseudopotential,
36 const std::string& file_content,
37 const std::string& source_description)
38 : pseudopotential_(pseudopotential)
39 , content_(file_content)
40 , source_description_(source_description) {}
41
42 void read() {
43 read_header();
44 read_mesh();
45 read_core_charge_density();
46 read_local_potential();
47 read_beta_projectors();
48 read_d_matrix();
49 read_pseudo_wavefunctions();
50 read_atomic_charge_density();
51 }
52
53 private:
54 void read_header() {
55 using namespace upf_utilities;
56 auto position = content_.find("<PP_HEADER");
57 if (position == std::string::npos)
58 throw std::runtime_error("No PP_HEADER found in UPF v2 file");
59 auto end_position = content_.find("/>", position);
60 if (end_position == std::string::npos)
61 end_position = content_.find(">", position);
62 std::string header_tag = content_.substr(position, end_position - position + 2);
63
64 pseudopotential_.element = xml_attribute(header_tag, "element");
65 // Trim whitespace from element
66 while (!pseudopotential_.element.empty() && pseudopotential_.element.back() == ' ')
67 pseudopotential_.element.pop_back();
68 while (!pseudopotential_.element.empty() && pseudopotential_.element.front() == ' ')
69 pseudopotential_.element.erase(pseudopotential_.element.begin());
70
71 pseudopotential_.pseudopotential_type =
72 canonical_pseudopotential_type(xml_attribute(header_tag, "pseudo_type"));
73 pseudopotential_.valence_charge =
74 required_real_attribute(header_tag, "z_valence", source_description_);
75 pseudopotential_.mesh_size =
76 required_integer_attribute(header_tag, "mesh_size", source_description_);
77 pseudopotential_.number_of_projectors =
78 required_integer_attribute(header_tag, "number_of_proj", source_description_);
79 pseudopotential_.maximum_angular_momentum =
80 required_integer_attribute(header_tag, "l_max", source_description_);
81
82 std::string core_correction = xml_attribute(header_tag, "core_correction");
83 pseudopotential_.has_nonlinear_core_correction =
84 (core_correction == "T" || core_correction == "t" || core_correction == "true" ||
85 core_correction == "True");
86
87 pseudopotential_.xc_functional = xml_attribute(header_tag, "functional");
88
89 std::string number_of_wavefunctions_string = xml_attribute(header_tag, "number_of_wfc");
90 if (!number_of_wavefunctions_string.empty())
91 pseudopotential_.number_of_wavefunctions = std::stoi(number_of_wavefunctions_string);
92
93 log::info(" UPFv2: {} ({}) Z={} mesh={} num_beta={} max_l={} has_nlcc={}",
94 pseudopotential_.element,
95 pseudopotential_.pseudopotential_type,
96 pseudopotential_.valence_charge,
97 pseudopotential_.mesh_size,
98 pseudopotential_.number_of_projectors,
99 pseudopotential_.maximum_angular_momentum,
100 pseudopotential_.has_nonlinear_core_correction);
101 }
102
103 void read_mesh() {
104 using namespace upf_utilities;
105 // Read mesh attribute from PP_MESH tag if present
106 auto mesh_position = content_.find("<PP_MESH");
107 if (mesh_position != std::string::npos) {
108 auto tag_end = content_.find(">", mesh_position);
109 std::string tag_line = content_.substr(mesh_position, tag_end - mesh_position);
110 std::string mesh_attribute = xml_attribute(tag_line, "mesh");
111 if (!mesh_attribute.empty()) {
112 pseudopotential_.mesh_size = std::stoi(mesh_attribute);
113 }
114 }
115
116 // Read r data from PP_R tag
117 auto position = content_.find("<PP_R ");
118 if (position == std::string::npos)
119 position = content_.find("<PP_R>");
120 if (position != std::string::npos) {
121 auto end_position = content_.find("</PP_R>", position);
122 std::string section = content_.substr(position, end_position - position);
123 pseudopotential_.radial_grid = xml_data(section);
124 }
125
126 // Read rab data from PP_RAB tag
127 position = content_.find("<PP_RAB ");
128 if (position == std::string::npos)
129 position = content_.find("<PP_RAB>");
130 if (position != std::string::npos) {
131 auto end_position = content_.find("</PP_RAB>", position);
132 std::string section = content_.substr(position, end_position - position);
133 pseudopotential_.radial_grid_derivative = xml_data(section);
134 }
135 }
136
137 void read_core_charge_density() {
138 using namespace upf_utilities;
139 if (!pseudopotential_.has_nonlinear_core_correction)
140 return;
141 auto position = content_.find("<PP_NLCC");
142 if (position != std::string::npos) {
143 auto end_position = content_.find("</PP_NLCC>", position);
144 std::string section = content_.substr(position, end_position - position);
145 pseudopotential_.charge_core.radial_values = xml_data(section);
146 }
147 }
148
149 void read_local_potential() {
150 using namespace upf_utilities;
151 auto position = content_.find("<PP_LOCAL");
152 if (position != std::string::npos) {
153 auto end_position = content_.find("</PP_LOCAL>", position);
154 std::string section = content_.substr(position, end_position - position);
155 pseudopotential_.potential_local.radial_values = xml_data(section);
156 }
157 }
158
159 void read_beta_projectors() {
160 using namespace upf_utilities;
161 pseudopotential_.projectors.resize(pseudopotential_.number_of_projectors);
162 for (int beta_index = 0; beta_index < pseudopotential_.number_of_projectors; beta_index++) {
163 std::string tag = "<PP_BETA." + std::to_string(beta_index + 1);
164 auto position = content_.find(tag);
165 if (position == std::string::npos) {
166 throw std::runtime_error("Cannot find " + tag + " in UPF v2 file");
167 }
168
169 // Read attributes from the opening tag
170 auto tag_end = content_.find(">", position);
171 std::string tag_line = content_.substr(position, tag_end - position);
172
173 std::string angular_momentum_string = xml_attribute(tag_line, "angular_momentum");
174 pseudopotential_.projectors[beta_index].angular_momentum =
175 angular_momentum_string.empty() ? 0 : std::stoi(angular_momentum_string);
176
177 std::string cutoff_index_string = xml_attribute(tag_line, "cutoff_radius_index");
178 pseudopotential_.projectors[beta_index].number_of_radial_cutoff_points =
179 cutoff_index_string.empty() ? pseudopotential_.mesh_size
180 : std::stoi(cutoff_index_string);
181
182 // Read beta data
183 auto close_position =
184 content_.find("</PP_BETA." + std::to_string(beta_index + 1) + ">", position);
185 if (close_position == std::string::npos) {
186 // Try generic </PP_BETA>
187 close_position = content_.find("</PP_BETA>", position);
188 }
189 std::string section = content_.substr(tag_end + 1, close_position - tag_end - 1);
190 pseudopotential_.projectors[beta_index].radial_values = xml_data(section);
191
192 // Ensure beta has mesh_size entries (pad with zeros if needed)
193 pseudopotential_.projectors[beta_index].radial_values.resize(pseudopotential_.mesh_size,
194 0.0);
195 }
196 }
197
198 void read_d_matrix() {
199 using namespace upf_utilities;
200 auto position = content_.find("<PP_DIJ");
201 if (position != std::string::npos) {
202 auto end_position = content_.find("</PP_DIJ>", position);
203 std::string section = content_.substr(position, end_position - position);
204 std::vector<Real> dij_flat = xml_data(section);
205
206 pseudopotential_.d_ion_matrix.resize(
207 pseudopotential_.number_of_projectors,
208 std::vector<Real>(pseudopotential_.number_of_projectors, 0.0));
209 int flat_index = 0;
210 for (int row = 0; row < pseudopotential_.number_of_projectors &&
211 flat_index < static_cast<int>(dij_flat.size());
212 row++) {
213 for (int column = 0; column < pseudopotential_.number_of_projectors &&
214 flat_index < static_cast<int>(dij_flat.size());
215 column++) {
216 pseudopotential_.d_ion_matrix[row][column] = dij_flat[flat_index++];
217 }
218 }
219 }
220 }
221
222 void read_pseudo_wavefunctions() {
223 using namespace upf_utilities;
224 auto position = content_.find("<PP_PSWFC>");
225 if (position == std::string::npos)
226 position = content_.find("<PP_PSWFC ");
227 if (position == std::string::npos)
228 return;
229
230 auto end_pswfc = content_.find("</PP_PSWFC>", position);
231 if (end_pswfc == std::string::npos)
232 return;
233
234 pseudopotential_.wavefunctions_atomic.resize(pseudopotential_.number_of_wavefunctions);
235 for (int wavefunction_index = 0;
236 wavefunction_index < pseudopotential_.number_of_wavefunctions;
237 wavefunction_index++) {
238 std::string tag = "<PP_CHI." + std::to_string(wavefunction_index + 1);
239 auto wavefunction_position = content_.find(tag, position);
240 if (wavefunction_position == std::string::npos || wavefunction_position >= end_pswfc)
241 continue;
242
243 auto wavefunction_tag_end = content_.find(">", wavefunction_position);
244 std::string tag_line = content_.substr(wavefunction_position,
245 wavefunction_tag_end - wavefunction_position);
246
247 pseudopotential_.wavefunctions_atomic[wavefunction_index].label =
248 xml_attribute(tag_line, "label");
249 std::string angular_momentum_string = xml_attribute(tag_line, " l");
250 if (angular_momentum_string.empty())
251 angular_momentum_string = xml_attribute(tag_line, "\nl");
252 if (angular_momentum_string.empty())
253 angular_momentum_string = xml_attribute(tag_line, "\tl");
254 pseudopotential_.wavefunctions_atomic[wavefunction_index].angular_momentum =
255 angular_momentum_string.empty() ? 0 : std::stoi(angular_momentum_string);
256 std::string occupation_string = xml_attribute(tag_line, "occupation");
257 pseudopotential_.wavefunctions_atomic[wavefunction_index].occupations =
258 occupation_string.empty() ? 0.0 : std::stod(occupation_string);
259
260 std::string close_tag = "</PP_CHI." + std::to_string(wavefunction_index + 1) + ">";
261 auto wavefunction_end = content_.find(close_tag, wavefunction_position);
262 if (wavefunction_end == std::string::npos)
263 wavefunction_end = content_.find("</PP_CHI>", wavefunction_position);
264
265 if (wavefunction_end != std::string::npos) {
266 std::string section = content_.substr(wavefunction_tag_end + 1,
267 wavefunction_end - wavefunction_tag_end - 1);
268 pseudopotential_.wavefunctions_atomic[wavefunction_index].radial_values =
269 xml_data(section);
270 pseudopotential_.wavefunctions_atomic[wavefunction_index].radial_values.resize(
271 pseudopotential_.mesh_size, 0.0);
272 }
273 }
274 }
275
276 void read_atomic_charge_density() {
277 using namespace upf_utilities;
278 auto position = content_.find("<PP_RHOATOM");
279 if (position != std::string::npos) {
280 auto end_position = content_.find("</PP_RHOATOM>", position);
281 std::string section = content_.substr(position, end_position - position);
282 pseudopotential_.charge_atomic.radial_values = xml_data(section);
283 }
284 }
285
286 Pseudopotential& pseudopotential_;
287 const std::string& content_;
288 std::string source_description_;
289};
290
291} // 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
UPFv2Reader(Pseudopotential &pseudopotential, const std::string &file_content, const std::string &source_description)
Definition init.hpp:35
void read()
Definition init.hpp:42
void section(const std::string &title)
Definition logger.hpp:206
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
std::vector< Real > xml_data(const std::string &section)
Extract numeric data from an XML section body, skipping lines that contain XML tags.
std::string xml_attribute(const std::string &text, const std::string &attribute_name)
Extract an XML attribute value from a block of text (e.g. attr="value" or attr='value').
Definition utilities.hpp:99
std::vector< Real > radial_values
std::vector< Real > radial_values
std::vector< Real > radial_values