Loading...
Searching...
No Matches
qe_reader.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * QEPlanewaveKohnShamWavefunctionReader — read one `wfc<k>.dat` back.
5 *
6 * The counterpart of the writer in qe.hpp, and the only way to get a single
7 * band's coefficients out of a finished run without rerunning it. The records
8 * are the ones that writer emits: the k-point header, the dimensions, the
9 * reciprocal lattice, the Miller indices, and then one record per band.
10 *
11 * The layout is Quantum ESPRESSO's, which is also what the Q3 writer emits:
12 * a packed 44-byte k-point header (`ik, xk(3), ispin, gamma_only, scalef`,
13 * with `xk` Cartesian in 1/bohr), reciprocal vectors in 1/bohr, and Miller
14 * indices interleaved per G-vector, `mill(3, ngw)`. Files from Q3 builds that
15 * predate that layout carried a 48-byte header with alignment padding and
16 * component-blocked Miller indices; nothing later in such a file tells the two
17 * index orders apart, so the header length is checked and an old file is
18 * refused instead of being read into a plausible, wrong band.
19 */
20
21#include <cstdint>
22#include <cstring>
23#include <fstream>
24#include <stdexcept>
25#include <string>
26#include <vector>
27
29
30namespace q3 {
31
33 public:
34 /// Read `path`, replacing whatever this reader held; throws on a malformed file.
35 void read(const std::string& path) {
36 std::ifstream stream(path, std::ios::binary);
37 if (!stream)
38 throw std::runtime_error("Cannot read wavefunction file: " + path);
39 path_ = path;
40
41 const std::vector<char> header = read_record(stream);
42 if (header.size() != kpoint_header_bytes) {
43 throw_corrupt("its k-point header is " + std::to_string(header.size()) +
44 " bytes, not the packed " + std::to_string(kpoint_header_bytes) +
45 " (a file from a Q3 build that padded the header is not readable)");
46 }
47 std::memcpy(&kpoint_coordinates_.x, header.data() + 4, sizeof(double));
48 std::memcpy(&kpoint_coordinates_.y, header.data() + 12, sizeof(double));
49 std::memcpy(&kpoint_coordinates_.z, header.data() + 20, sizeof(double));
50 std::memcpy(&is_gamma_only_flag_, header.data() + 32, sizeof(int32_t));
51
52 const std::vector<char> dimensions = read_record(stream);
53 if (dimensions.size() != 4 * sizeof(int32_t))
54 throw_corrupt("its dimension record is not four integers");
55 const auto* dimension_values = reinterpret_cast<const int32_t*>(dimensions.data());
56 const int number_of_plane_waves = dimension_values[0];
57 number_of_bands_ = dimension_values[3];
58 if (number_of_plane_waves <= 0 || number_of_bands_ <= 0) {
59 throw_corrupt("it declares " + std::to_string(number_of_plane_waves) +
60 " plane wave(s) and " + std::to_string(number_of_bands_) + " band(s)");
61 }
62
63 const std::vector<char> reciprocal = read_record(stream);
64 if (reciprocal.size() != 9 * sizeof(double))
65 throw_corrupt("its reciprocal-lattice record is not nine reals");
66 const auto* vectors = reinterpret_cast<const double*>(reciprocal.data());
67 for (int row = 0; row < 3; row++)
68 for (int column = 0; column < 3; column++)
69 reciprocal_lattice_vectors_[row][column] = vectors[row * 3 + column];
70
71 const std::vector<char> millers = read_record(stream);
72 if (millers.size() != 3 * static_cast<std::size_t>(number_of_plane_waves) * sizeof(int32_t))
73 throw_corrupt("its Miller-index record is the wrong length");
74 const auto* miller_values = reinterpret_cast<const int32_t*>(millers.data());
75 miller_indices_.resize(number_of_plane_waves);
76 for (int index = 0; index < number_of_plane_waves; index++) {
77 miller_indices_[index] = {miller_values[3 * index],
78 miller_values[3 * index + 1],
79 miller_values[3 * index + 2]};
80 }
81
82 band_coefficients_.assign(number_of_bands_, {});
83 for (int band = 0; band < number_of_bands_; band++) {
84 const std::vector<char> coefficients = read_record(stream);
85 if (coefficients.size() !=
86 static_cast<std::size_t>(number_of_plane_waves) * sizeof(Complex)) {
87 throw_corrupt("the record for band " + std::to_string(band + 1) +
88 " is the wrong length");
89 }
90 const auto* values = reinterpret_cast<const Complex*>(coefficients.data());
91 band_coefficients_[band].assign(values, values + number_of_plane_waves);
92 }
93 }
94
95 [[nodiscard]] int number_of_bands() const {
96 return number_of_bands_;
97 }
98 [[nodiscard]] int number_of_plane_waves() const {
99 return static_cast<int>(miller_indices_.size());
100 }
101 [[nodiscard]] bool is_gamma_only() const {
102 return is_gamma_only_flag_ != 0;
103 }
104 /// Cartesian coordinates, in 1/bohr, of the k-point this file belongs to.
105 [[nodiscard]] const Vector3D& kpoint_coordinates() const {
106 return kpoint_coordinates_;
107 }
108 [[nodiscard]] const Matrix3x3& reciprocal_lattice_vectors() const {
109 return reciprocal_lattice_vectors_;
110 }
111 [[nodiscard]] const std::vector<IntegerVector3D>& miller_indices() const {
112 return miller_indices_;
113 }
114 /// Coefficients of one band, parallel to `miller_indices()`.
115 [[nodiscard]] const std::vector<Complex>& band_coefficients(int band_index) const {
116 return band_coefficients_[band_index];
117 }
118
119 private:
120 /// int32 + three doubles + two int32 + one double, packed as Fortran writes it.
121 static constexpr std::size_t kpoint_header_bytes = 44;
122
123 std::string path_;
124 Vector3D kpoint_coordinates_;
125 int32_t is_gamma_only_flag_ = 0;
126 int number_of_bands_ = 0;
127 Matrix3x3 reciprocal_lattice_vectors_{};
128 std::vector<IntegerVector3D> miller_indices_;
129 std::vector<std::vector<Complex>> band_coefficients_;
130
131 [[noreturn]] void throw_corrupt(const std::string& reason) const {
132 throw std::runtime_error("'" + path_ + "' is not a readable wavefunction file: " + reason +
133 ".");
134 }
135
136 std::vector<char> read_record(std::ifstream& stream) const {
137 int32_t prefix_length = 0;
138 stream.read(reinterpret_cast<char*>(&prefix_length), sizeof(prefix_length));
139 if (!stream || prefix_length < 0)
140 throw_corrupt("it ends where a record marker was expected");
141 std::vector<char> payload(static_cast<std::size_t>(prefix_length));
142 stream.read(payload.data(), prefix_length);
143 int32_t suffix_length = 0;
144 stream.read(reinterpret_cast<char*>(&suffix_length), sizeof(suffix_length));
145 if (!stream || suffix_length != prefix_length)
146 throw_corrupt("a record's trailing length marker does not match its leading one");
147 return payload;
148 }
149};
150
151} // namespace q3
const std::vector< Complex > & band_coefficients(int band_index) const
Coefficients of one band, parallel to miller_indices().
const Vector3D & kpoint_coordinates() const
Cartesian coordinates, in 1/bohr, of the k-point this file belongs to.
const std::vector< IntegerVector3D > & miller_indices() const
void read(const std::string &path)
Read path, replacing whatever this reader held; throws on a malformed file.
Definition qe_reader.hpp:35
const Matrix3x3 & reciprocal_lattice_vectors() const
std::complex< double > Complex
3×3 matrix stored row-major: row i = mat[i]