Loading...
Searching...
No Matches
construction_inputs.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * WannierConstructionInputs — a record's inputs, checked once and resolved
5 * once for one construction.
6 *
7 * The strategies under subspace/ and gauge/ read the same things: the inputs
8 * as the record stores them, the k-mesh stencil built from the grid and the
9 * neighbour directions, and the projections and the overlaps as matrices --
10 * whole, restricted to the bands a classification keeps, or rotated into a
11 * gauge. Resolving them here is what lets a strategy take one argument;
12 * refusing a malformed record here, before any strategy runs, is what lets
13 * every strategy assume a well-formed one.
14 *
15 * The stencil and the overlaps are optional because a record imported from
16 * another code's projections and eigenvalues alone carries neither -- and
17 * the projection gauge on an isolated manifold needs neither. Whatever reads
18 * a phase asks spread_is_defined() first.
19 *
20 * A view over the record it was built from, alive for one build() call; it
21 * copies nothing but the projections and the overlaps, which it holds as
22 * matrices.
23 */
24
25#include <cstddef>
26#include <numeric>
27#include <optional>
28#include <stdexcept>
29#include <string>
30#include <vector>
31
38
39namespace q3 {
40
42 public:
45 if (number_of_kpoints == 0) {
46 throw std::invalid_argument("WannierConstructionInputs: the inputs hold no k-points");
47 }
48 if (static_cast<int>(record.eigenvalues_rydberg.size()) != number_of_kpoints ||
49 static_cast<int>(record.projection_matrices.size()) != number_of_kpoints) {
50 throw std::invalid_argument(
51 "WannierConstructionInputs: the eigenvalues and projections do not cover the "
52 "inputs' " +
53 std::to_string(number_of_kpoints) + " k-points");
54 }
55 for (int axis = 0; axis < 3; axis++) {
56 if (record.kpoint_grid_dimensions[axis] <= 0) {
57 throw std::invalid_argument(
58 "WannierConstructionInputs: the inputs do not state the k-point grid their "
59 "matrices were built on");
60 }
61 }
62 /**
63 * The model is a Fourier sum over the Wigner-Seitz supercell of the
64 * grid, and that transform is exact only over the whole grid: a
65 * record holding some other number of k-points is not a smaller
66 * problem, it is a wrong one.
67 */
68 const int grid_points = record.kpoint_grid_dimensions[0] *
70 if (number_of_kpoints != grid_points) {
71 throw std::invalid_argument(
72 "WannierConstructionInputs: the inputs hold " + std::to_string(number_of_kpoints) +
73 " k-points and state a " + std::to_string(record.kpoint_grid_dimensions[0]) + "x" +
74 std::to_string(record.kpoint_grid_dimensions[1]) + "x" +
75 std::to_string(record.kpoint_grid_dimensions[2]) + " grid, which has " +
76 std::to_string(grid_points));
77 }
78 for (int kpoint_index = 0; kpoint_index < number_of_kpoints; kpoint_index++) {
79 if (static_cast<int>(record.eigenvalues_rydberg[kpoint_index].size()) <
81 throw std::invalid_argument("WannierConstructionInputs: k-point " +
82 std::to_string(kpoint_index) +
83 " carries fewer eigenvalues than number_of_bands");
84 }
85 const std::size_t projection_elements =
86 static_cast<std::size_t>(record.number_of_bands) *
87 static_cast<std::size_t>(record.number_of_trial_orbitals);
88 if (record.projection_matrices[kpoint_index].size() != projection_elements) {
89 throw std::invalid_argument(
90 "WannierConstructionInputs: the projection at k-point " +
91 std::to_string(kpoint_index) + " holds " +
92 std::to_string(record.projection_matrices[kpoint_index].size()) +
93 " elements, not number_of_bands x number_of_trial_orbitals = " +
94 std::to_string(projection_elements));
95 }
96 projections_.emplace_back(record.number_of_bands,
98 record.projection_matrices[kpoint_index]);
99 }
100 if (!record.neighbor_directions.empty()) {
101 stencil_ =
103 }
104 if (record.has_overlaps()) {
105 if (!stencil_) {
106 throw std::invalid_argument(
107 "WannierConstructionInputs: the record carries neighbour overlaps but no "
108 "neighbour directions, so nothing says which k-point each overlap reaches");
109 }
110 overlaps_.emplace(
112 }
113 }
114
115 /// The inputs as the record stores them.
116 [[nodiscard]] const WannierInputs& record() const {
117 return record_;
118 }
119 [[nodiscard]] int number_of_kpoints() const {
120 return record_.number_of_kpoints();
121 }
122 [[nodiscard]] int number_of_wannier_functions() const {
123 return record_.number_of_trial_orbitals;
124 }
125
126 /// Whether the record names its neighbour directions, so k + b is resolved.
127 [[nodiscard]] bool has_stencil() const {
128 return stencil_.has_value();
129 }
130 [[nodiscard]] const KPointStencil& stencil() const {
131 if (!stencil_) {
132 throw std::logic_error("WannierConstructionInputs: the record carries no neighbour "
133 "directions, so it has no stencil");
134 }
135 return *stencil_;
136 }
137 /// Whether the record carries M_mn(k,b); implies has_stencil().
138 [[nodiscard]] bool has_overlaps() const {
139 return overlaps_.has_value();
140 }
141 [[nodiscard]] const BlochOverlaps& overlaps() const {
142 if (!overlaps_) {
143 throw std::logic_error(
144 "WannierConstructionInputs: the record carries no neighbour overlaps");
145 }
146 return *overlaps_;
147 }
148
149 /// A(k) restricted to the bands `classification` keeps at each k-point: kept x N, band as row.
150 [[nodiscard]] std::vector<DenseMatrix<Complex>>
151 projections_restricted_to(const BandClassification& classification) const {
152 std::vector<int> every_orbital(static_cast<std::size_t>(number_of_wannier_functions()));
153 std::iota(every_orbital.begin(), every_orbital.end(), 0);
154 std::vector<DenseMatrix<Complex>> restricted;
155 restricted.reserve(projections_.size());
156 for (std::size_t kpoint_index = 0; kpoint_index < projections_.size(); kpoint_index++) {
157 restricted.push_back(projections_[kpoint_index].submatrix(
158 classification.kpoints[kpoint_index].kept_bands, every_orbital));
159 }
160 return restricted;
161 }
162
163 /**
164 * M_mn(k,b) in a gauge: restricted to the bands `classification` keeps
165 * and rotated by `frames`, C(k)^dagger M(k,b) C(k+b). What a spread is
166 * evaluated on; defined only where has_overlaps().
167 */
168 [[nodiscard]] BlochOverlaps
170 const std::vector<DenseMatrix<Complex>>& frames) const {
171 return overlaps()
172 .restricted_to(classification.kpoints, stencil())
173 .transformed_by(frames, frames, stencil());
174 }
175
176 /// The direction of a two-point axis, if any; KPointStencil::two_point_axis_direction says why.
177 [[nodiscard]] std::optional<int> two_point_axis_direction() const {
178 return stencil_ ? stencil_->two_point_axis_direction() : std::nullopt;
179 }
180
181 /**
182 * Whether the spread is defined at all: it needs the overlaps, and a
183 * mesh without a two-point axis. The subspace iteration is untouched by
184 * the two-point rule -- it never reads a phase -- which is why a
185 * two-point axis is usable without localisation and not with it.
186 */
187 [[nodiscard]] bool spread_is_defined() const {
188 return overlaps_.has_value() && !two_point_axis_direction().has_value();
189 }
190
191 private:
192 const WannierInputs& record_;
193 std::optional<KPointStencil> stencil_;
194 std::optional<BlochOverlaps> overlaps_;
195 /// A(k) as the record gives it, number_of_bands x number_of_trial_orbitals, one per k-point.
196 std::vector<DenseMatrix<Complex>> projections_;
197};
198
199} // namespace q3
BlochOverlaps transformed_by(const std::vector< DenseMatrix< Complex > > &left, const std::vector< DenseMatrix< Complex > > &right, const KPointStencil &stencil) const
left(k)^dagger M(k, b) right(k + b) at every (k, b).
BlochOverlaps restricted_to(const std::vector< KPointBandClassification > &classification, const KPointStencil &stencil) const
static KPointStencil from_grid(const std::array< int, 3 > &grid_dimensions, const std::vector< KPointNeighborDirection > &directions)
From a full grid and its neighbour shells: k + b resolved for every k and every direction.
std::optional< int > two_point_axis_direction() const
The direction of a two-point axis, if any; KPointStencil::two_point_axis_direction says why.
std::vector< DenseMatrix< Complex > > projections_restricted_to(const BandClassification &classification) const
A(k) restricted to the bands classification keeps at each k-point: kept x N, band as row.
WannierConstructionInputs(const WannierInputs &record)
bool has_overlaps() const
Whether the record carries M_mn(k,b); implies has_stencil().
const WannierInputs & record() const
The inputs as the record stores them.
const BlochOverlaps & overlaps() const
bool has_stencil() const
Whether the record names its neighbour directions, so k + b is resolved.
const KPointStencil & stencil() const
BlochOverlaps overlaps_in_gauge(const BandClassification &classification, const std::vector< DenseMatrix< Complex > > &frames) const
Every k-point's verdict, and the summary of the whole.
std::vector< KPointBandClassification > kpoints
std::vector< KPointNeighborDirection > neighbor_directions
Definition inputs.hpp:49
std::vector< std::vector< Real > > eigenvalues_rydberg
Eigenvalues in Rydberg, indexed [kpoint_index][band_index].
Definition inputs.hpp:74
int number_of_kpoints() const
Definition inputs.hpp:89
std::array< int, 3 > kpoint_grid_dimensions
Definition inputs.hpp:37
int number_of_trial_orbitals
Definition inputs.hpp:72
std::vector< std::vector< Complex > > projection_matrices
Definition inputs.hpp:68
bool has_overlaps() const
Whether the record carries M_mn(k,b): the spread and the subspace iteration need it.
Definition inputs.hpp:94
std::vector< std::vector< std::vector< Complex > > > overlap_matrices
Definition inputs.hpp:60