Loading...
Searching...
No Matches
upf_reader.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * UPFReader — read a dataset from a UPF v1 or v2 file.
5 *
6 * Detects the format version and delegates to the appropriate readers:
7 *
8 * v1: UPFv1Reader → (if US) UltrasoftUPFv1Reader
9 * v2: UPFv2Reader → (if US or PAW) UltrasoftUPFv2Reader → (if PAW) PawUPFv2Reader
10 *
11 * The readers are organized by version (v1/, v2/) and pseudopotential type
12 * (core, ultrasoft, paw), each in its own header file.
13 *
14 * A PAW dataset reads through the ultrasoft chain first because it carries the
15 * ultrasoft augmentation apparatus verbatim; PawUPFv2Reader then adds the
16 * one-center data.
17 *
18 * UPF only. A file of unknown format goes through PseudopotentialFactory::read()
19 * (factory.hpp), which picks the reader from the file's contents.
20 */
21
22#include <fstream>
23#include <iterator>
24#include <memory>
25#include <string>
26#include <vector>
27
37
38namespace q3 {
39
40/// Reads one dataset from a UPF v1 or v2 file.
41class UPFReader {
42 public:
43 static std::unique_ptr<Pseudopotential> read(const std::string& filename) {
44 std::ifstream file_stream(filename);
45 if (!file_stream.is_open())
46 throw std::runtime_error("Cannot open PP file: " + filename);
47
48 std::string first_line;
49 std::getline(file_stream, first_line);
50
51 /**
52 * Determine whether this is an ultrasoft PP by reading the header type first.
53 * We need to peek at the pseudopotential type to decide which subclass to create.
54 */
55 bool is_ultrasoft_type = false;
56 bool is_paw_type = false;
57 std::string pseudopotential_type_string;
58
59 if (first_line.find("version=\"2") != std::string::npos) {
60 // UPF v2: read file content, check header for pseudo_type
61 file_stream.seekg(0);
62 std::string content((std::istreambuf_iterator<char>(file_stream)),
63 std::istreambuf_iterator<char>());
64 file_stream.close();
65
66 auto header_position = content.find("<PP_HEADER");
67 if (header_position != std::string::npos) {
68 auto end_position = content.find("/>", header_position);
69 if (end_position == std::string::npos)
70 end_position = content.find(">", header_position);
71 std::string header_tag =
72 content.substr(header_position, end_position - header_position + 2);
73 pseudopotential_type_string = upf_utilities::canonical_pseudopotential_type(
74 upf_utilities::xml_attribute(header_tag, "pseudo_type"));
75 }
76
77 is_paw_type = (pseudopotential_type_string == "PAW");
78 is_ultrasoft_type = (pseudopotential_type_string == "US");
79
80 // Create the appropriate subclass
81 std::unique_ptr<Pseudopotential> pseudopotential;
82 if (is_paw_type) {
83 pseudopotential = std::make_unique<PawPseudopotential>();
84 } else if (is_ultrasoft_type) {
85 pseudopotential = std::make_unique<UltrasoftPseudopotential>();
86 } else {
87 pseudopotential = std::make_unique<Pseudopotential>();
88 }
89
90 UPFv2Reader(*pseudopotential, content, filename).read();
91 if (is_ultrasoft_type || is_paw_type) {
92 UltrasoftUPFv2Reader(static_cast<UltrasoftPseudopotential&>(*pseudopotential),
93 content)
94 .read();
95 }
96 if (is_paw_type) {
98 static_cast<PawPseudopotential&>(*pseudopotential), content, filename)
99 .read();
100 }
101
103 return pseudopotential;
104 } else {
105 // UPF v1: sequential line-by-line parsing
106 file_stream.close();
107 std::ifstream v1_stream(filename);
108
109 /**
110 * For v1, we need to read the header first to determine the type.
111 * Read a temporary PP to get the type, then re-read with the correct class.
112 * Since v1 readers mutate the passed reference, we read into a temporary first.
113 */
114 auto temporary_pseudopotential = std::make_unique<Pseudopotential>();
115 UPFv1Reader(*temporary_pseudopotential, v1_stream).read();
116
117 is_ultrasoft_type = (temporary_pseudopotential->pseudopotential_type == "US");
118
119 std::unique_ptr<Pseudopotential> pseudopotential;
120 if (is_ultrasoft_type) {
121 // Create UltrasoftPseudopotential and copy base fields
122 auto ultrasoft_pseudopotential = std::make_unique<UltrasoftPseudopotential>();
123 // Copy all base class fields
124 static_cast<Pseudopotential&>(*ultrasoft_pseudopotential) =
125 std::move(*temporary_pseudopotential);
126 pseudopotential = std::move(ultrasoft_pseudopotential);
127 } else {
128 pseudopotential = std::move(temporary_pseudopotential);
129 }
130
131 if (is_ultrasoft_type) {
132 std::ifstream us_stream(filename);
133 Pseudopotential dummy_for_seek;
134 UPFv1Reader(dummy_for_seek, us_stream).read();
135 UltrasoftUPFv1Reader(static_cast<UltrasoftPseudopotential&>(*pseudopotential),
136 us_stream)
137 .read();
138 }
139
141 return pseudopotential;
142 }
143 }
144};
145
146} // namespace q3
PAW pseudopotential — extends ultrasoft with one-center (on-site) data.
Definition init.hpp:33
Base class for all pseudopotential types.
Definition init.hpp:35
Reads one dataset from a UPF v1 or v2 file.
static std::unique_ptr< Pseudopotential > read(const std::string &filename)
void read()
Definition init.hpp:31
void read()
Definition init.hpp:42
Ultrasoft pseudopotential — extends base with augmentation data.
Definition init.hpp:27
void check_atomic_wavefunction_norm(Pseudopotential &pseudopotential)
Validate atomic wavefunction norms and mark zero-norm wavefunctions.
std::string canonical_pseudopotential_type(const std::string &raw_type)
Definition utilities.hpp:60
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