Loading...
Searching...
No Matches
record_writer.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * BerkeleyGWRecordWriter — the records BerkeleyGW's mean-field files share
5 * after their header.
6 *
7 * Every file carries the charge-density G-set as one [nrecord=1][ng][gvec]
8 * group; the k-point-dependent files (WFN, VKB) carry the same per-k-point
9 * counts, weights and coordinates after the header, and the same
10 * [nrecord][ngk][gvec_k] group before each k-point's data. BerkeleyGW compares
11 * these between files -- ParaBands refuses a VKB whose k-points, weights or
12 * plane-wave counts differ from its WFN's -- so they are written by one class
13 * rather than kept in step by hand. Layout and authority: see header.hpp.
14 */
15
16#include <cstdint>
17#include <fstream>
18#include <stdexcept>
19#include <string>
20#include <vector>
21
22#include "io/checkpoint/qe.hpp"
24
25namespace q3 {
26
27/// Writes the records BerkeleyGW's files share; see the file comment.
29 public:
30 explicit BerkeleyGWRecordWriter(const MeanFieldRecord& record) : record_(record) {}
31 /// See the file writers' own deleted rvalue constructors: this holds a reference.
32 explicit BerkeleyGWRecordWriter(const MeanFieldRecord&&) = delete;
33
34 /// [nrecord=1][ng][gvec(3,ng)] -- the charge-density G-set, in the record's order.
35 void write_charge_density_gvectors(std::ofstream& output_file) const {
36 std::vector<int32_t> miller_indices(3 * record_.charge_density_miller_indices.size());
37 for (size_t g_index = 0; g_index < record_.charge_density_miller_indices.size();
38 g_index++) {
39 const auto& miller = record_.charge_density_miller_indices[g_index];
40 miller_indices[3 * g_index + 0] = miller.x;
41 miller_indices[3 * g_index + 1] = miller.y;
42 miller_indices[3 * g_index + 2] = miller.z;
43 }
44 write_block(output_file,
45 static_cast<int32_t>(record_.charge_density_miller_indices.size()),
46 miller_indices.data(),
47 miller_indices.size() * sizeof(int32_t));
48 }
49
50 /// ngk(nk) -- plane waves per k-point.
51 void write_plane_wave_counts(std::ofstream& output_file) const {
52 std::vector<int32_t> counts(record_.plane_waves_per_kpoint.begin(),
53 record_.plane_waves_per_kpoint.end());
54 record_format_.write_record(
55 output_file, counts.data(), static_cast<int32_t>(counts.size() * sizeof(int32_t)));
56 }
57
58 /**
59 * kw(nk), normalized so sum(kw) = 1 as BerkeleyGW requires.
60 *
61 * The converter gets there by writing the k-point weight times the number
62 * of spin components over 2, which leans on the invariant that the weights
63 * sum to 2. Q3's generated grids share that invariant
64 * (KPointsGrid::scale_weights()), but an explicit `control.kpoints_list`
65 * reaches here from the input verbatim -- and a list written on
66 * BerkeleyGW's own sum-to-1 convention, the natural choice for the NSCF
67 * run BerkeleyGW is fed from, would land on disk as sum(kw) = 0.5. So the
68 * weights are divided by their actual sum: identical whenever the
69 * invariant holds, correct when it does not.
70 */
71 void write_normalized_kpoint_weights(std::ofstream& output_file) const {
72 std::vector<double> weights(record_.kpoint_weights.begin(), record_.kpoint_weights.end());
73 double weight_sum = 0.0;
74 for (double weight : weights) {
75 weight_sum += weight;
76 }
77 if (weight_sum <= 0.0) {
78 throw std::runtime_error(
79 "BerkeleyGW export: k-point weights sum to " + std::to_string(weight_sum) +
80 ", so they cannot be normalized to BerkeleyGW's required sum of 1. "
81 "Check control.kpoints_list.");
82 }
83 for (auto& weight : weights) {
84 weight /= weight_sum;
85 }
86 record_format_.write_record(
87 output_file, weights.data(), static_cast<int32_t>(weights.size() * sizeof(double)));
88 }
89
90 /**
91 * kpt(3,nk), crystal coordinates. Q3 holds k-points in crystal coordinates
92 * throughout (kpoint.hpp), so unlike the converter, which converts from
93 * Cartesian, nothing is transformed here.
94 */
95 void write_kpoint_coordinates(std::ofstream& output_file) const {
96 std::vector<double> coordinates(3 * record_.kpoint_coordinates.size());
97 for (size_t kpoint_index = 0; kpoint_index < record_.kpoint_coordinates.size();
98 kpoint_index++) {
99 const Vector3D& point = record_.kpoint_coordinates[kpoint_index];
100 coordinates[3 * kpoint_index + 0] = point.x;
101 coordinates[3 * kpoint_index + 1] = point.y;
102 coordinates[3 * kpoint_index + 2] = point.z;
103 }
104 record_format_.write_record(output_file,
105 coordinates.data(),
106 static_cast<int32_t>(coordinates.size() * sizeof(double)));
107 }
108
109 /// [nrecord=1][ngk][gvec_k(3,ngk)] -- the plane waves of one k-point, in the record's order.
110 void write_kpoint_gvectors(std::ofstream& output_file, int kpoint_index) const {
111 int number_of_plane_waves = record_.plane_waves_per_kpoint[kpoint_index];
112 const auto& g_vector_indices = record_.kpoint_g_vector_indices[kpoint_index];
113 std::vector<int32_t> miller_indices(3 * static_cast<size_t>(number_of_plane_waves));
114 for (int plane_wave_index = 0; plane_wave_index < number_of_plane_waves;
115 plane_wave_index++) {
116 const auto& miller =
117 record_.charge_density_miller_indices[g_vector_indices[plane_wave_index]];
118 miller_indices[3 * plane_wave_index + 0] = miller.x;
119 miller_indices[3 * plane_wave_index + 1] = miller.y;
120 miller_indices[3 * plane_wave_index + 2] = miller.z;
121 }
122 write_block(output_file,
123 number_of_plane_waves,
124 miller_indices.data(),
125 miller_indices.size() * sizeof(int32_t));
126 }
127
128 /// [nrecord=1][count][values] -- one data block of `count` complex values per spin.
129 void write_complex_block(std::ofstream& output_file,
130 int32_t count,
131 const Complex* values,
132 size_t number_of_values) const {
133 write_block(output_file, count, values, number_of_values * sizeof(Complex));
134 }
135
136 private:
137 const MeanFieldRecord& record_;
138 QEBinaryRecordFormat record_format_;
139
140 /**
141 * Every G-vector or data block is preceded by the number of records it is
142 * split into, and its length (the BerkeleyGW specification: "for ease of
143 * writing files from a code parallelized over G-vectors"). This writer never
144 * splits, so the first is always 1.
145 */
146 void write_block(std::ofstream& output_file,
147 int32_t count,
148 const void* payload,
149 size_t payload_bytes) const {
150 int32_t number_of_records = 1;
151 record_format_.write_record(output_file, &number_of_records, sizeof(number_of_records));
152 record_format_.write_record(output_file, &count, sizeof(count));
153 record_format_.write_record(output_file, payload, static_cast<int32_t>(payload_bytes));
154 }
155};
156
157} // namespace q3
Writes the records BerkeleyGW's files share; see the file comment.
void write_kpoint_gvectors(std::ofstream &output_file, int kpoint_index) const
[nrecord=1][ngk][gvec_k(3,ngk)] – the plane waves of one k-point, in the record's order.
BerkeleyGWRecordWriter(const MeanFieldRecord &&)=delete
See the file writers' own deleted rvalue constructors: this holds a reference.
void write_plane_wave_counts(std::ofstream &output_file) const
ngk(nk) – plane waves per k-point.
void write_charge_density_gvectors(std::ofstream &output_file) const
[nrecord=1][ng][gvec(3,ng)] – the charge-density G-set, in the record's order.
void write_kpoint_coordinates(std::ofstream &output_file) const
void write_complex_block(std::ofstream &output_file, int32_t count, const Complex *values, size_t number_of_values) const
[nrecord=1][count][values] – one data block of count complex values per spin.
BerkeleyGWRecordWriter(const MeanFieldRecord &record)
void write_normalized_kpoint_weights(std::ofstream &output_file) const
void write_record(std::ofstream &output_file, const void *data, int32_t number_of_bytes) const override
Definition qe.hpp:16
std::complex< double > Complex
std::vector< Vector3D > kpoint_coordinates
Crystal (fractional) coordinates, one per physical k-point.
Definition init.hpp:118
std::vector< std::vector< int > > kpoint_g_vector_indices
Per physical k-point, indices into charge_density_miller_indices.
Definition init.hpp:123
std::vector< int > plane_waves_per_kpoint
Definition init.hpp:121
std::vector< IntegerVector3D > charge_density_miller_indices
Definition init.hpp:104
std::vector< Real > kpoint_weights
Engine-convention weights; formats renormalize as they require.
Definition init.hpp:120