Loading...
Searching...
No Matches
qe.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * QEPlanewaveKohnShamWavefunctionCheckpoint — QE-format binary writer.
5 *
6 * Serializes converged Kohn-Sham wavefunctions to disk in the Fortran
7 * unformatted record layout of Quantum ESPRESSO's wfcN.dat (non-spin-polarized
8 * runs; see TODOs.md for LSDA). One file is written per k-point,
9 * containing Miller indices and complex wavefunction coefficients for
10 * all bands.
11 *
12 * Writes the k-points the state holds, which under k-point pools are only this
13 * pool's. Each file is named and labelled by the k-point's place in the global
14 * grid, so every pool writing its own share leaves the same set of files a
15 * serial run does, with no gather and no two processes opening the same file.
16 */
17
18#include <cstdint>
19#include <fstream>
20#include <string>
21#include <vector>
22
24#include "io/checkpoint/qe.hpp"
25#include "io/logger.hpp"
28
29namespace q3 {
30
32 public:
34
35 void write_to_file(const std::string& path) const override {
37 log::error("planewave_basis is null in NormConservingPlanewaveKohnShamSCFState during "
38 "wavefunction serialization");
39 return;
40 }
42 log::error("execution_policy is null in NormConservingPlanewaveKohnShamSCFState during "
43 "wavefunction serialization");
44 return;
45 }
46 const auto& planewave_basis = *scf_state_.planewave_basis;
47 const auto& execution_policy = *scf_state_.execution_policy;
48 const auto& g_vectors = planewave_basis.g_vectors();
49 const auto& quantum_system = scf_state_.quantum_system();
50 const auto& g_vector_indices_all = planewave_basis.kpoint_g_vector_indices();
51 const auto& number_of_plane_waves_all = planewave_basis.kpoint_number_of_plane_waves();
52 int number_of_bands = scf_state_.quantum_system().number_of_bands();
53 int number_of_kpoints = kpoint_grid_.number_of_kpoints();
54
55 // QE writes b1, b2, b3 in 1/Bohr (it passes tpiba*bg).
56 const Matrix3x3 reciprocal_lattice_vectors = crystal_structure_.reciprocal_vectors_bohr();
57
58 for (int kpoint_index = 0; kpoint_index < number_of_kpoints; kpoint_index++) {
59 const int global_kpoint_index = execution_policy.global_kpoint_index(kpoint_index);
60
61 std::string file_path =
62 path + "/wfc" + std::to_string(global_kpoint_index + 1) + ".dat";
63 // Buffer must be declared BEFORE the ofstream so it outlives the stream.
64 std::vector<char> file_buffer(4 * 1024 * 1024);
65 std::ofstream output_file;
66 output_file.rdbuf()->pubsetbuf(file_buffer.data(), file_buffer.size());
67 output_file.open(file_path, std::ios::binary);
68 if (!output_file.is_open()) {
69 log::warn("Cannot open {} for writing", file_path);
70 continue;
71 }
72
73 int number_of_plane_waves = number_of_plane_waves_all[kpoint_index];
74 const auto& g_vector_indices = g_vector_indices_all[kpoint_index];
75 const auto& kpoint = kpoint_grid_[kpoint_index];
76
77 write_kpoint_header(output_file, global_kpoint_index, kpoint);
78 write_dimensions(output_file, number_of_plane_waves, number_of_bands);
79 write_reciprocal_lattice_vectors(output_file, reciprocal_lattice_vectors);
80 write_miller_indices(output_file, g_vectors, g_vector_indices, number_of_plane_waves);
81 write_band_coefficients(
82 output_file, quantum_system, kpoint_index, number_of_bands, number_of_plane_waves);
83
84 log::info(" Wrote wavefunction: {} ({} bands, {} PW)",
85 file_path,
86 number_of_bands,
87 number_of_plane_waves);
88 }
89 }
90
91 private:
92 QEBinaryRecordFormat record_format_;
93
94 /**
95 * Record 1: k-point header (index, coordinates, spin, gamma flag, scale factor).
96 *
97 * Quantum ESPRESSO writes this record as one Fortran list,
98 * `ik, xk(3), ispin, gamma_only, scalef` (Modules/io_base.f90, write_wfc),
99 * and Fortran unformatted I/O packs a list with no alignment padding:
100 * int32 at byte 0, three doubles at 4, int32 at 28, a 4-byte LOGICAL at
101 * 32 and a double at 36 -- 44 bytes. The fields are therefore appended
102 * one by one into a byte buffer rather than dumped from a C struct, whose
103 * alignment would put four padding bytes after the leading integer and
104 * shift every later field. Writing field by field also means no byte of
105 * the record can come from uninitialized memory.
106 *
107 * Units follow QE: xk is Cartesian in 1/Bohr (QE passes tpiba*xk).
108 */
109 void
110 write_kpoint_header(std::ofstream& output_file, int kpoint_index, const KPoint& kpoint) const {
111 const Vector3D kpoint_cartesian = kpoint_cartesian_inverse_bohr(kpoint);
112 PackedRecordBuffer record;
113 record.append(static_cast<int32_t>(kpoint_index + 1));
114 record.append(static_cast<double>(kpoint_cartesian.x));
115 record.append(static_cast<double>(kpoint_cartesian.y));
116 record.append(static_cast<double>(kpoint_cartesian.z));
117 record.append(static_cast<int32_t>(1)); // spin index
118 record.append(static_cast<int32_t>(scf_state_.planewave_basis->is_gamma_only() ? 1 : 0));
119 record.append(1.0); // scale factor
120 record_format_.write_record(
121 output_file, record.data(), static_cast<int32_t>(record.size()));
122 }
123
124 /**
125 * Fractional k-point coordinates (in units of the reciprocal basis) to
126 * Cartesian 1/Bohr, the convention of QE's on-disk xk.
127 */
128 [[nodiscard]] Vector3D kpoint_cartesian_inverse_bohr(const KPoint& kpoint) const {
129 const Matrix3x3 reciprocal_vectors_bohr = crystal_structure_.reciprocal_vectors_bohr();
130 Vector3D cartesian;
131 for (int dimension = 0; dimension < 3; dimension++) {
132 cartesian[dimension] = kpoint.coordinates.x * reciprocal_vectors_bohr[0][dimension] +
133 kpoint.coordinates.y * reciprocal_vectors_bohr[1][dimension] +
134 kpoint.coordinates.z * reciprocal_vectors_bohr[2][dimension];
135 }
136 return cartesian;
137 }
138
139 /**
140 * Record 2: dimensions (number of plane waves, max index, polarizations, bands).
141 */
142 void write_dimensions(std::ofstream& output_file,
143 int number_of_plane_waves,
144 int number_of_bands) const {
145 struct DimensionsRecord {
146 int32_t number_of_plane_waves_wavefunction;
147 int32_t maximum_wavefunction_index;
148 int32_t number_of_polarizations;
149 int32_t number_of_bands;
150 };
151 /**
152 * Four same-sized members leave this one with no padding today, so
153 * the fill is not repairing anything. It is here so that adding a
154 * member of a different width -- which would introduce padding
155 * exactly as it did in the k-point header above -- cannot silently
156 * start writing uninitialized bytes to disk again.
157 */
158 DimensionsRecord record;
159 std::memset(&record, 0, sizeof(record));
160 record.number_of_plane_waves_wavefunction = number_of_plane_waves;
161 record.maximum_wavefunction_index = number_of_plane_waves;
162 record.number_of_polarizations = 1;
163 record.number_of_bands = number_of_bands;
164 record_format_.write_record(output_file, &record, sizeof(record));
165 }
166
167 /**
168 * Record 3: reciprocal lattice vectors b1, b2, b3 (3x3 doubles, 1/Bohr).
169 */
170 void write_reciprocal_lattice_vectors(std::ofstream& output_file,
171 const Matrix3x3& reciprocal_lattice_vectors) const {
172 double vectors_array[9];
173 vectors_array[0] = reciprocal_lattice_vectors[0].x;
174 vectors_array[1] = reciprocal_lattice_vectors[0].y;
175 vectors_array[2] = reciprocal_lattice_vectors[0].z;
176 vectors_array[3] = reciprocal_lattice_vectors[1].x;
177 vectors_array[4] = reciprocal_lattice_vectors[1].y;
178 vectors_array[5] = reciprocal_lattice_vectors[1].z;
179 vectors_array[6] = reciprocal_lattice_vectors[2].x;
180 vectors_array[7] = reciprocal_lattice_vectors[2].y;
181 vectors_array[8] = reciprocal_lattice_vectors[2].z;
182 record_format_.write_record(output_file, vectors_array, sizeof(vectors_array));
183 }
184
185 /**
186 * Record 4: Miller indices for all G-vectors at this k-point.
187 *
188 * QE writes `mill(1:3, 1:igwx)`: Fortran column-major with the component
189 * index first, so the three components of each G-vector are contiguous --
190 * h0 k0 l0 h1 k1 l1 ... (interleaved, not all-h then all-k then all-l).
191 */
192 void write_miller_indices(std::ofstream& output_file,
193 const std::vector<GVector>& g_vectors,
194 const std::vector<int>& g_vector_indices,
195 int number_of_plane_waves) const {
196 std::vector<int32_t> miller_indices(3 * number_of_plane_waves);
197 for (int plane_wave_index = 0; plane_wave_index < number_of_plane_waves;
198 plane_wave_index++) {
199 int g_index = g_vector_indices[plane_wave_index];
200 miller_indices[3 * plane_wave_index + 0] = g_vectors[g_index].miller.x;
201 miller_indices[3 * plane_wave_index + 1] = g_vectors[g_index].miller.y;
202 miller_indices[3 * plane_wave_index + 2] = g_vectors[g_index].miller.z;
203 }
204 record_format_.write_record(output_file,
205 miller_indices.data(),
206 static_cast<int32_t>(miller_indices.size() * sizeof(int32_t)));
207 }
208
209 /**
210 * Records 5..5+number_of_bands-1: complex wavefunction coefficients per band.
211 */
212 void write_band_coefficients(std::ofstream& output_file,
213 const PlanewaveKohnShamQuantumSystem& quantum_system,
214 int kpoint_index,
215 int number_of_bands,
216 int number_of_plane_waves) const {
217 for (int band_index = 0; band_index < number_of_bands; band_index++) {
218 const Complex* band_data =
219 quantum_system.wavefunction_band_data(kpoint_index, band_index);
220 record_format_.write_record(
221 output_file,
222 band_data,
223 static_cast<int32_t>(number_of_plane_waves * sizeof(Complex)));
224 }
225 }
226};
227
228} // namespace q3
Matrix3x3 reciprocal_vectors_bohr() const
Definition crystal.hpp:331
int number_of_kpoints() const
bool is_gamma_only() const
Definition init.hpp:157
const NormConservingPlanewaveKohnShamSCFState & scf_state_
Definition init.hpp:26
PlanewaveKohnShamWavefunctionCheckpoint(const NormConservingPlanewaveKohnShamSCFState &scf_state)
Definition init.hpp:19
void write_record(std::ofstream &output_file, const void *data, int32_t number_of_bytes) const override
Definition qe.hpp:16
void write_to_file(const std::string &path) const override
Write binary data to a file or directory.
Definition qe.hpp:35
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
void info(spdlog::format_string_t< Args... > fmt, Args &&... args)
Definition logger.hpp:183
std::complex< double > Complex
3×3 matrix stored row-major: row i = mat[i]
const NormConservingPlanewaveKohnShamQuantumSystem & quantum_system() const override
Definition init.hpp:82
const execution::PlanewaveExecutionPolicy * execution_policy
Definition init.hpp:64
const PlanewaveBasis * planewave_basis
Definition init.hpp:53