Loading...
Searching...
No Matches
from_scf_state.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * build_mean_field_record() — populates a MeanFieldRecord from a live SCF state.
5 *
6 * This is the in-process half of the converter seam (see
7 * mean_field_record/init.hpp): it flattens the engine's object graph -- crystal
8 * structure, plane-wave basis, k-point grid, quantum system -- into the plain
9 * record the format writers consume. The standalone converter builds the same
10 * record from `<prefix>.save/` instead, so both paths drive identical writer
11 * code.
12 *
13 * Copies are deliberate and cheap relative to what is about to be written: the
14 * heavy data (wavefunction coefficients) is NOT copied -- it stays behind
15 * ScfStateMeanFieldBandSource, which hands out the quantum system's own
16 * pointers exactly as the exporters used to fetch them.
17 */
18
19#include <memory>
20#include <stdexcept>
21#include <vector>
22
27
28namespace q3 {
29
30/**
31 * Band source backed by a live quantum system.
32 *
33 * Holds a reference, not a copy: the record is built and consumed inside the
34 * flavor's own scope, where the SCF state outlives it. A file-backed source is
35 * used when that is not true.
36 */
38 public:
40 : scf_state_(scf_state) {}
41
42 [[nodiscard]] const Complex* coefficients(int kpoint_state_index,
43 int band_index) const override {
44 return scf_state_.quantum_system().wavefunction_band_data(kpoint_state_index, band_index);
45 }
46
47 private:
49};
50
51/**
52 * Flattens `scf_state` into a MeanFieldRecord.
53 *
54 * Throws if the state is missing the structural pieces every format needs,
55 * rather than letting each writer discover the null pointer separately -- the
56 * writers used to each repeat that check.
57 */
58[[nodiscard]] inline MeanFieldRecord
60 if (!scf_state.planewave_basis || !scf_state.kpoint_grid || !scf_state.crystal_structure) {
61 throw std::runtime_error(
62 "build_mean_field_record: the SCF state is missing planewave_basis, kpoint_grid or "
63 "crystal_structure, so no mean-field record can be built from it.");
64 }
65
66 const Crystal& crystal_structure = *scf_state.crystal_structure;
67 const PlanewaveBasis& planewave_basis = *scf_state.planewave_basis;
68 const KPointsGrid& kpoint_grid = *scf_state.kpoint_grid;
69 const auto& quantum_system = scf_state.quantum_system();
70
71 MeanFieldRecord record;
72
73 record.bravais_lattice_index = crystal_structure.bravais_lattice_index();
74 record.lattice_parameter = crystal_structure.lattice_parameter();
76 record.cell_volume = crystal_structure.cell_volume();
77 record.lattice_vectors = crystal_structure.lattice_vectors();
78 record.reciprocal_lattice_vectors = crystal_structure.reciprocal_lattice_vectors();
79
80 int number_of_atoms = crystal_structure.number_of_atoms();
81 record.atom_positions_cartesian.reserve(number_of_atoms);
82 record.atomic_numbers.reserve(number_of_atoms);
83 for (int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
84 record.atom_positions_cartesian.push_back(
85 crystal_structure.convert_from_crystal_to_cartesian(atom_index));
86 record.atomic_numbers.push_back(crystal_structure.atomic_number(atom_index));
87 }
88
90
91 const auto& fft_rho = planewave_basis.fft_rho();
92 record.fft_grid_dimensions = {
93 fft_rho.grid_dimension_1(), fft_rho.grid_dimension_2(), fft_rho.grid_dimension_3()};
94 const auto& g_vectors = planewave_basis.g_vectors();
95 record.charge_density_miller_indices.reserve(g_vectors.size());
96 for (const auto& g_vector : g_vectors) {
97 record.charge_density_miller_indices.push_back(g_vector.miller);
98 }
99 record.is_gamma_only = planewave_basis.is_gamma_only();
102
103 record.kpoint_grid_dimensions = kpoint_grid.grid_dimensions();
104 record.kpoint_grid_shifts = kpoint_grid.grid_shifts();
105 int number_of_kpoints = kpoint_grid.number_of_kpoints();
106 record.kpoint_coordinates.reserve(number_of_kpoints);
107 record.kpoint_weights.reserve(number_of_kpoints);
108 for (int kpoint_index = 0; kpoint_index < number_of_kpoints; kpoint_index++) {
109 record.kpoint_coordinates.push_back(kpoint_grid[kpoint_index].coordinates);
110 record.kpoint_weights.push_back(kpoint_grid[kpoint_index].weight);
111 }
112 const auto& plane_waves_per_kpoint = planewave_basis.kpoint_number_of_plane_waves();
113 record.plane_waves_per_kpoint.assign(plane_waves_per_kpoint.begin(),
114 plane_waves_per_kpoint.end());
115 record.kpoint_g_vector_indices = planewave_basis.kpoint_g_vector_indices();
116
117 record.number_of_bands = quantum_system.number_of_bands();
119
120 int number_of_kpoint_states = record.number_of_kpoint_states();
121 record.eigenvalues_rydberg.resize(number_of_kpoint_states);
122 record.occupations.resize(number_of_kpoint_states);
123 for (int state_index = 0; state_index < number_of_kpoint_states; state_index++) {
124 record.eigenvalues_rydberg[state_index].resize(record.number_of_bands);
125 record.occupations[state_index].resize(record.number_of_bands);
126 for (int band_index = 0; band_index < record.number_of_bands; band_index++) {
127 record.eigenvalues_rydberg[state_index][band_index] =
128 quantum_system.eigenvalue(state_index, band_index);
129 record.occupations[state_index][band_index] =
130 quantum_system.occupation_by_kpoint_state_index_and_band_index(state_index,
131 band_index);
132 }
133 }
134
135 record.band_source = std::make_shared<ScfStateMeanFieldBandSource>(scf_state);
136
137 /**
138 * The engine stores rho(r); the record carries rho(G), so the transform
139 * happens here -- once, while a PlanewaveBasis is still in hand -- rather
140 * than inside each format writer, where a file-backed converter could not
141 * perform it. See MeanFieldRecord::charge_density_g.
142 */
143 record.charge_density_g.resize(scf_state.charge_density_real.size());
144 for (size_t spin_index = 0; spin_index < scf_state.charge_density_real.size(); spin_index++) {
145 planewave_basis.rho_r_to_g(scf_state.charge_density_real[spin_index],
146 record.charge_density_g[spin_index]);
147 }
148
150
151 /**
152 * The per-spin copy when the state has one -- an NSCF run fills it from the
153 * checkpoint's V_eff, and a spin-polarized SCF from its own -- and otherwise
154 * the single potential, which is the whole story for an unpolarized run
155 * (PlanewaveKohnShamSCFState::total_potential_g_by_spin).
156 */
157 if (!scf_state.total_potential_g_by_spin.empty()) {
159 } else if (!scf_state.total_potential_g.empty()) {
161 }
162
163 return record;
164}
165
166/**
167 * Adds the nonlocal pseudopotential -- species, D blocks, projector source -- to
168 * a record built by build_mean_field_record(), for the BerkeleyGW `VKB` file.
169 *
170 * Refuses rather than writing a file that describes a different Hamiltonian
171 * from the one the bands solve: an ultrasoft or PAW dataset turns H ψ = E S ψ
172 * into a generalized problem whose S no `VKB` reader applies, and DFT+U adds a
173 * projector term that D does not carry.
174 */
177 const auto& quantum_system = scf_state.quantum_system();
178 const auto& pseudopotential_basis = quantum_system.pseudopotential_basis();
179 quantum_system.require_identity_overlap(
180 "mean_field_export_kleinman_bylander_projectors (BerkeleyGW VKB)");
181 if (!scf_state.hubbard_manifolds.empty()) {
182 throw std::runtime_error(
183 "mean_field_export_kleinman_bylander_projectors is not available with DFT+U: the "
184 "Hubbard term is a nonlocal potential of its own, which a BerkeleyGW VKB file "
185 "cannot carry.");
186 }
187
188 const Crystal& crystal_structure = *scf_state.crystal_structure;
189 int number_of_atoms = crystal_structure.number_of_atoms();
190 int number_of_species = static_cast<int>(pseudopotential_basis.pseudopotential_set().size());
191
192 record.projectors_per_species.resize(number_of_species);
193 for (int species_index = 0; species_index < number_of_species; species_index++) {
194 record.projectors_per_species[species_index] =
195 pseudopotential_basis.number_of_projector_channels(species_index);
196 }
197 record.atom_species_indices.resize(number_of_atoms);
198 for (int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
199 record.atom_species_indices[atom_index] = crystal_structure.species_type_index(atom_index);
200 }
201
202 record.nonlocal_coefficients.assign(record.number_of_spin_components, {});
203 for (int spin_index = 0; spin_index < record.number_of_spin_components; spin_index++) {
204 auto& coefficients_by_atom = record.nonlocal_coefficients[spin_index];
205 coefficients_by_atom.resize(number_of_atoms);
206 for (int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
207 int species_index = record.atom_species_indices[atom_index];
208 int number_of_channels = record.projectors_per_species[species_index];
209 auto& block = coefficients_by_atom[atom_index];
210 block.resize(static_cast<size_t>(number_of_channels) * number_of_channels);
211 for (int channel_j = 0; channel_j < number_of_channels; channel_j++) {
212 for (int channel_i = 0; channel_i < number_of_channels; channel_i++) {
213 block[static_cast<size_t>(channel_i) +
214 static_cast<size_t>(channel_j) * number_of_channels] =
215 quantum_system.d_matrix_element(
216 atom_index, species_index, channel_i, channel_j, spin_index);
217 }
218 }
219 }
220 }
221
222 record.projector_source = std::make_shared<ScfStateMeanFieldProjectorSource>(scf_state);
223}
224
225} // namespace q3
Vector3D convert_from_crystal_to_cartesian(int atom_index) const
Definition crystal.hpp:225
int bravais_lattice_index() const
Definition crystal.hpp:190
Real cell_volume() const
Definition crystal.hpp:193
Real two_pi_over_lattice_parameter() const
Definition crystal.hpp:196
const Matrix3x3 & reciprocal_lattice_vectors() const
Definition crystal.hpp:205
const Matrix3x3 & lattice_vectors() const
Definition crystal.hpp:202
int species_type_index(int atom_index) const
Definition crystal.hpp:273
int atomic_number(int atom_index) const
Definition crystal.hpp:338
int number_of_atoms() const
Definition crystal.hpp:209
Real lattice_parameter() const
Definition crystal.hpp:180
int number_of_kpoints() const
const std::array< int, 3 > & grid_dimensions() const
const std::array< int, 3 > & grid_shifts() const
const NormConservingPlanewaveBasis & pseudopotential_basis() const
Definition init.hpp:65
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
Real wavefunction_energy_cutoff() const
Definition init.hpp:342
void rho_r_to_g(const std::vector< Real > &charge_density_real_space, std::vector< Complex > &charge_density_g_space) const
Definition init.hpp:334
FFTGrid & fft_rho()
Definition init.hpp:286
const std::vector< std::vector< int > > & kpoint_g_vector_indices() const
Definition init.hpp:272
Real charge_density_energy_cutoff() const
Definition init.hpp:345
const std::vector< int > & kpoint_number_of_plane_waves() const
Definition init.hpp:276
bool is_gamma_only() const
Definition init.hpp:157
Complex * wavefunction_band_data(int kpoint_state_index, int band_index)
Definition init.hpp:107
const Complex * coefficients(int kpoint_state_index, int band_index) const override
ScfStateMeanFieldBandSource(const NormConservingPlanewaveKohnShamSCFState &scf_state)
static std::vector< SymmetryOperation > get_symmetry_operations(const Crystal &crystal, Real symmetry_precision=math::tolerances::default_symmetry_precision)
Definition symmetry.hpp:484
void add_kleinman_bylander_projectors_to_mean_field_record(MeanFieldRecord &record, const NormConservingPlanewaveKohnShamSCFState &scf_state)
MeanFieldRecord build_mean_field_record(const NormConservingPlanewaveKohnShamSCFState &scf_state)
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< Real > > occupations
Indexed [kpoint_state_index][band_index]; max 1 per (k, spin) state.
Definition init.hpp:131
std::vector< std::vector< int > > kpoint_g_vector_indices
Per physical k-point, indices into charge_density_miller_indices.
Definition init.hpp:123
Real wavefunction_energy_cutoff
Definition init.hpp:108
int number_of_spin_components
Definition init.hpp:127
std::array< int, 3 > kpoint_grid_shifts
Definition init.hpp:116
std::vector< SymmetryOperation > symmetry_operations
Definition init.hpp:92
Matrix3x3 reciprocal_lattice_vectors
Definition init.hpp:79
std::vector< std::vector< Complex > > charge_density_g
Definition init.hpp:148
std::vector< std::vector< std::vector< Real > > > nonlocal_coefficients
Definition init.hpp:181
std::vector< std::vector< Real > > eigenvalues_rydberg
Indexed [kpoint_state_index][band_index]; Rydberg.
Definition init.hpp:129
Real lattice_parameter
Definition init.hpp:75
std::vector< int > projectors_per_species
Projector channels – one per (β, m) – of each species.
Definition init.hpp:175
std::vector< Complex > exchange_correlation_potential_g
Definition init.hpp:154
int bravais_lattice_index
QE ibrav convention; 0 means explicit/free lattice vectors.
Definition init.hpp:74
Matrix3x3 lattice_vectors
Definition init.hpp:78
std::vector< std::vector< Complex > > self_consistent_potential_g
Definition init.hpp:162
std::vector< int > plane_waves_per_kpoint
Definition init.hpp:121
std::vector< int > atomic_numbers
Definition init.hpp:84
std::array< int, 3 > fft_grid_dimensions
Charge-density ("dense") FFT grid dimensions.
Definition init.hpp:96
std::array< int, 3 > kpoint_grid_dimensions
Definition init.hpp:115
Real charge_density_energy_cutoff
Plane-wave kinetic-energy cutoffs, Rydberg (BGW's ecutrho/ecutwfc).
Definition init.hpp:107
int number_of_kpoint_states() const
(k-point, spin) state count – the row count of eigenvalues/occupations.
Definition init.hpp:197
std::shared_ptr< const MeanFieldBandSource > band_source
Definition init.hpp:132
std::vector< IntegerVector3D > charge_density_miller_indices
Definition init.hpp:104
std::vector< int > atom_species_indices
0-based species index of each atom, parallel to atomic_numbers.
Definition init.hpp:173
std::vector< Vector3D > atom_positions_cartesian
Cartesian positions in alat units, parallel to atomic_numbers.
Definition init.hpp:83
Real two_pi_over_lattice_parameter
Definition init.hpp:76
std::vector< Real > kpoint_weights
Engine-convention weights; formats renormalize as they require.
Definition init.hpp:120
const NormConservingPlanewaveKohnShamQuantumSystem & quantum_system() const override
Definition init.hpp:82
std::vector< HubbardManifold > hubbard_manifolds
DFT+U (Hubbard) resolved manifolds at convergence; empty means no correction is active.
Definition init.hpp:62
std::vector< std::vector< Real > > charge_density_real
Definition init.hpp:37
std::vector< Complex > total_potential_g
Definition init.hpp:38
const KPointsGrid * kpoint_grid
Definition init.hpp:56
std::vector< Complex > exchange_correlation_potential_g
Definition init.hpp:50
const Crystal * crystal_structure
Definition init.hpp:55
int number_of_spin_components() const
Definition init.hpp:106
const PlanewaveBasis * planewave_basis
Definition init.hpp:53
std::vector< std::vector< Complex > > total_potential_g_by_spin
Definition init.hpp:49