Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
init.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * NormConservingPlanewaveBasis — static pseudopotential data in the plane-wave basis.
5 *
6 * Owns all norm-conserving pseudopotential data that is computed once from
7 * (Crystal, PlanewaveBasis, PseudopotentialSet) and never changes during SCF:
8 * - PseudopotentialSet (owned copy)
9 * - BetaFunctionTable (tabulated β(q) projectors)
10 * - ProjectorMappingTable (KB projector → (l,m) channel expansion)
11 * - CoreChargeData (NLCC core charge density)
12 * - Local potential V_loc(G) per species and total
13 * - Atomic charge density ρ_at(G) per species
14 * - Mesh truncation sizes for radial integration
15 *
16 * The ultrasoft subclass UltrasoftPlanewaveBasis adds
17 * augmentation Q-tables, spherical harmonics, and Gaunt coefficients.
18 */
19
20#include <cmath>
21#include <memory>
22#include <vector>
23
29
35
42
43namespace q3 {
44
45// Forward declarations
46class Crystal;
47
49 public:
51 virtual ~NormConservingPlanewaveBasis() = default;
52
53 /**
54 * Initialize all pseudopotential data from crystal, basis, and PP files.
55 * Must be called after PlanewaveBasis::generate().
56 */
57 virtual void init(const Crystal& crystal_structure,
62 int number_of_species_types = static_cast<int>(pseudopotential_set_.size());
63
64 Real cell_volume = crystal_structure.cell_volume();
65 const auto& g_vector_magnitudes = planewave_basis.g_vector_magnitudes();
66 // Step 1: Compute mesh truncation sizes for radial integration
67 compute_mesh_truncation_sizes(number_of_species_types);
68
69 /**
70 * Step 2: Tabulate beta functions on a uniform q-grid, with the table
71 * implementation chosen by what the datasets carry (see the factory).
72 */
74 beta_function_table_->initialize(
75 planewave_basis, pseudopotential_set_, number_of_species_types);
76
77 // Step 3: Tabulate local potential V_loc(q) on uniform q-grid
81 cell_volume,
82 number_of_species_types);
83
84 // Step 4: Tabulate core charge density ρ_core(q) on uniform q-grid
88 number_of_species_types);
89
90 // Step 5: Compute local potential in G-space from pre-tabulated values
91 int number_of_g_vectors = planewave_basis.number_of_g_vectors();
92 local_potential_g_per_species_.resize(number_of_species_types);
93 for (int type_index = 0; type_index < number_of_species_types; type_index++) {
94 local_potential_g_per_species_[type_index].resize(number_of_g_vectors);
95 for (int g_vector_index = 0; g_vector_index < number_of_g_vectors; g_vector_index++) {
96 local_potential_g_per_species_[type_index][g_vector_index] =
98 g_vector_magnitudes[g_vector_index]);
99 }
100 }
101 // Compute structure factors and total Vloc(G)
102 total_local_potential_g_.resize(number_of_g_vectors, Complex(0.0, 0.0));
103 for (int g_vector_index = 0; g_vector_index < number_of_g_vectors; g_vector_index++) {
104 for (int type_index = 0; type_index < number_of_species_types; type_index++) {
105 Complex structure_factor(0.0, 0.0);
106 for (int atom_index = 0; atom_index < crystal_structure.number_of_atoms();
107 atom_index++) {
108 if (crystal_structure.species_type_index(atom_index) == type_index) {
109 Vector3D atomic_position =
110 crystal_structure.convert_from_crystal_to_cartesian(atom_index);
111 const auto& g_vector = planewave_basis.g_vectors()[g_vector_index];
112 Real phase = constants::two_pi * g_vector.g_cart.dot(atomic_position);
113 structure_factor += compute_phase_factor(phase);
114 }
115 }
116 total_local_potential_g_[g_vector_index] +=
117 local_potential_g_per_species_[type_index][g_vector_index] * structure_factor;
118 }
119 }
120
121 // Step 6: Compute atomic charge density in G-space
125 g_vector_magnitudes,
126 beta_function_table_->q_spacing(),
127 beta_function_table_->number_of_q_points());
128
129 // Step 7: Build projector mapping tables
131 crystal_structure, pseudopotential_set_, number_of_species_types);
132
133 // Step 8: Compute NLCC core charge density in G-space from pre-tabulated values
134 core_charge_data_.initialize(crystal_structure,
137 g_vector_magnitudes,
138 cell_volume,
140 }
141
142 // ─── PlanewaveBasis access ──────────────────────────────────────
143
144 /**
145 * Access the plane-wave basis set this PP basis was built from.
146 */
147 [[nodiscard]] const PlanewaveBasis& planewave_basis() const {
148 return *planewave_basis_ptr_;
149 }
150
151 // ─── Pseudopotential accessors ──────────────────────────────────
152
153 /**
154 * Access the collection of pseudopotentials for all species.
155 */
156 [[nodiscard]] const PseudopotentialSet& pseudopotential_set() const {
158 }
159
160 /**
161 * Return the local pseudopotential in G-space for a single species.
162 * @param species_type_index Index into the pseudopotential set (0-based)
163 * @return V_loc(|G|) values indexed by G-vector index
164 */
165 [[nodiscard]] const std::vector<Real>& local_potential_g(int species_type_index) const {
166 return local_potential_g_per_species_[species_type_index];
167 }
168
169 /**
170 * Return the total local pseudopotential in G-space (structure-factor-weighted sum
171 * over all atoms and species).
172 */
173 [[nodiscard]] const std::vector<Complex>& total_local_potential_g() const {
175 }
176
177 /**
178 * Total number of Kleinman-Bylander projectors across all atoms
179 * (sum of projector channels per atom, accounting for angular momentum degeneracy).
180 */
184
185 /**
186 * Number of radial beta projectors for a given species (before angular expansion).
187 */
188 [[nodiscard]] int number_of_projectors(int species_type) const {
189 return static_cast<int>(pseudopotential_set_[species_type].projectors.size());
190 }
191
192 /**
193 * Angular momentum quantum number l for a specific beta projector of a species.
194 */
195 [[nodiscard]] int beta_angular_momentum(int species_type, int projector_index) const {
196 return pseudopotential_set_[species_type].projectors[projector_index].angular_momentum;
197 }
198
199 /**
200 * Number of expanded projector channels for a species
201 * (sum of 2l+1 over all beta projectors, i.e. including magnetic quantum numbers).
202 */
203 [[nodiscard]] int number_of_projector_channels(int species_type) const {
204 return projector_mapping_table_.number_of_channels(species_type);
205 }
206
207 /**
208 * Bare D-matrix element D^0_{ij} from the pseudopotential file (species-level).
209 * For norm-conserving PPs, this is the sole nonlocal coupling.
210 * For ultrasoft PPs, this is the ion-bare part before screening.
211 * @param species_type Species index
212 * @param projector_a Beta projector index i (radial)
213 * @param projector_b Beta projector index j (radial)
214 */
215 [[nodiscard]] Real
216 bare_d_matrix_element(int species_type, int projector_a, int projector_b) const {
217 return pseudopotential_set_[species_type].d_ion_matrix[projector_a][projector_b];
218 }
219
220 /**
221 * Map an expanded projector channel index back to its radial beta projector index.
222 */
223 [[nodiscard]] int projector_to_beta_index(int species_type, int projector_index) const {
224 return projector_mapping_table_.to_beta_index(species_type, projector_index);
225 }
226
227 /**
228 * Combined (l,m) index for an expanded projector channel (QE convention: l² + offset).
229 */
230 [[nodiscard]] int projector_combined_lm_index(int species_type, int projector_index) const {
231 return projector_mapping_table_.combined_lm_index(species_type, projector_index);
232 }
233
234 /**
235 * Angular momentum l of an expanded projector channel.
236 */
237 [[nodiscard]] int projector_angular_momentum(int species_type, int projector_index) const {
238 return projector_mapping_table_.angular_momentum(species_type, projector_index);
239 }
240
241 /**
242 * Magnetic quantum number m of an expanded projector channel.
243 */
244 [[nodiscard]] int projector_magnetic_quantum_number(int species_type,
245 int projector_index) const {
246 return projector_mapping_table_.magnetic_quantum_number(species_type, projector_index);
247 }
248
249 /**
250 * Atomic charge density in G-space for a species (used as initial guess).
251 */
252 [[nodiscard]] const std::vector<Real>& atomic_charge_density_g(int species_type) const {
253 return atomic_charge_density_g_[species_type];
254 }
255
256 /**
257 * Whether any species has nonlinear core correction (NLCC) for XC.
258 */
259 [[nodiscard]] bool has_nonlinear_core_correction() const {
261 }
262
263 /**
264 * NLCC core charge density in G-space (structure-factor-weighted sum over atoms).
265 */
266 [[nodiscard]] const std::vector<Complex>& core_charge_density_reciprocal() const {
268 }
269
270 /**
271 * Interpolate NLCC core charge density from pre-tabulated values.
272 * Uses 4-point Lagrange interpolation on the uniform q-grid.
273 * @param element_type Species index
274 * @param q_value Wavevector magnitude |q| in atomic units (1/Bohr)
275 * @return Interpolated core charge density at |q|
276 */
277 [[nodiscard]] Real interpolate_rho_core(int element_type, Real q_value) const {
278 return core_charge_density_table_.interpolate(element_type, q_value);
279 }
280
281 /**
282 * Access the pre-tabulated beta function table for direct interpolation.
283 */
284 [[nodiscard]] const BetaFunctionTable& beta_function_table() const {
285 return *beta_function_table_;
286 }
287
288 /**
289 * Interpolate beta projector function β(|q|) from the pre-tabulated q-grid.
290 * @param species_type_index Species index
291 * @param beta_index Radial beta projector index
292 * @param q_value Wavevector magnitude |q| in atomic units (1/Bohr)
293 */
294 [[nodiscard]] Real
295 interpolate_beta(int species_type_index, int beta_index, Real q_value) const {
296 return beta_function_table_->interpolate(species_type_index, beta_index, q_value);
297 }
298
299 /**
300 * Interpolate the derivative dβ/dq of a beta projector function.
301 * Uses 4-point Lagrange derivative interpolation.
302 * @param species_type_index Species index
303 * @param beta_index Radial beta projector index
304 * @param q_value Wavevector magnitude |q| in atomic units (1/Bohr)
305 */
306 [[nodiscard]] Real
307 interpolate_dbeta(int species_type_index, int beta_index, Real q_value) const {
308 return beta_function_table_->interpolate_derivative(
309 species_type_index, beta_index, q_value);
310 }
311
312 /**
313 * Interpolate local potential V_loc(|q|) from pre-tabulated uniform q-grid.
314 * Replaces the expensive per-call radial integration with O(1) Lagrange interpolation.
315 */
316 [[nodiscard]] Real interpolate_local_potential(int element_type, Real q_value) const {
317 return local_potential_table_.interpolate(element_type, q_value);
318 }
319
320 /**
321 * Interpolate derivative dV_loc/dq from pre-tabulated uniform q-grid.
322 */
323 [[nodiscard]] Real interpolate_local_potential_derivative(int element_type,
324 Real q_value) const {
325 return local_potential_table_.interpolate_derivative(element_type, q_value);
326 }
327
328 /**
329 * Interpolate core charge density ρ_core(|q|) from pre-tabulated uniform q-grid.
330 * Replaces the expensive per-call radial integration with O(1) Lagrange interpolation.
331 */
332 [[nodiscard]] Real interpolate_core_charge_density(int element_type, Real q_value) const {
333 return core_charge_density_table_.interpolate(element_type, q_value);
334 }
335
336 /**
337 * Interpolate derivative dρ_core/dq from pre-tabulated uniform q-grid.
338 */
339 [[nodiscard]] Real interpolate_core_charge_density_derivative(int element_type,
340 Real q_value) const {
341 return core_charge_density_table_.interpolate_derivative(element_type, q_value);
342 }
343
344 /**
345 * Access the local potential interpolation table.
346 */
347 [[nodiscard]] const LocalPotentialTable& local_potential_table() const {
349 }
350
351 /**
352 * Access the core charge density interpolation table.
353 */
356 }
357
358 // ─── Projector mapping table access ─────────────────────────────
359
360 /**
361 * Access the projector mapping table for expanded projector channel queries.
362 */
363 [[nodiscard]] const ProjectorMappingTable& projector_mapping_table() const {
365 }
366
367 // ─── Ultrasoft detection ────────────────────────────────────────
368
369 /**
370 * Whether this basis uses ultrasoft pseudopotentials.
371 * Base class returns false; UltrasoftPlanewaveBasis returns true.
372 */
373 [[nodiscard]] virtual bool is_ultrasoft() const {
374 return false;
375 }
376
377 protected:
379
381 std::vector<std::vector<Real>> local_potential_g_per_species_;
382 std::vector<std::vector<Real>> atomic_charge_density_g_;
383 std::vector<Complex> total_local_potential_g_;
384
385 std::unique_ptr<BetaFunctionTable> beta_function_table_;
390
392 std::vector<int> mesh_sizes_;
393
394 // ─── Init helper (mesh truncation) ──────────────────────────────
395
396 /**
397 * Compute mesh truncation sizes for radial integration of each species.
398 */
399 void compute_mesh_truncation_sizes(int number_of_species_types) {
400 std::vector<int> mesh_truncation_sizes(number_of_species_types);
401 for (int type_index = 0; type_index < number_of_species_types; type_index++) {
402 const auto& pseudopotential = pseudopotential_set_[type_index];
404 mesh_truncation_sizes[type_index] = pseudopotential.mesh_size;
405 for (int radial_index = 0; radial_index < pseudopotential.mesh_size;
406 radial_index++) {
407 if (pseudopotential.radial_grid[radial_index] >
409 mesh_truncation_sizes[type_index] = radial_index + 1;
410 break;
411 }
412 }
413 mesh_truncation_sizes[type_index] =
414 2 * ((mesh_truncation_sizes[type_index] + 1) / 2) - 1;
415 if (mesh_truncation_sizes[type_index] > pseudopotential.mesh_size) {
416 mesh_truncation_sizes[type_index] = pseudopotential.mesh_size;
417 if (mesh_truncation_sizes[type_index] % 2 == 0) {
418 mesh_truncation_sizes[type_index]--;
419 }
420 }
421 } else {
422 mesh_truncation_sizes[type_index] = pseudopotential.mesh_size;
423 if (mesh_truncation_sizes[type_index] % 2 == 0) {
424 mesh_truncation_sizes[type_index]--;
425 }
426 }
427 }
428 mesh_sizes_ = mesh_truncation_sizes;
429 }
430};
431
432} // namespace q3
bool has_nonlinear_core_correction() const
const std::vector< Complex > & core_charge_density_reciprocal() const
void initialize(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< Real > &g_vector_magnitudes, Real cell_volume, const CoreChargeDensityTable &core_charge_density_table)
Real interpolate(int species_type_index, Real q_value) const
Real interpolate_derivative(int species_type_index, Real q_value) const
void initialize(const UniformQGrid &q_grid, const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, int number_of_species_types)
Vector3D convert_from_crystal_to_cartesian(int atom_index) const
Definition crystal.hpp:225
Real cell_volume() const
Definition crystal.hpp:193
int species_type_index(int atom_index) const
Definition crystal.hpp:273
int number_of_atoms() const
Definition crystal.hpp:209
Real interpolate(int species_type_index, Real q_value) const
Real interpolate_derivative(int species_type_index, Real q_value) const
void initialize(const UniformQGrid &q_grid, const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, Real cell_volume, int number_of_species_types)
const std::vector< Complex > & core_charge_density_reciprocal() const
Definition init.hpp:266
int projector_magnetic_quantum_number(int species_type, int projector_index) const
Definition init.hpp:244
Real interpolate_local_potential(int element_type, Real q_value) const
Definition init.hpp:316
int projector_angular_momentum(int species_type, int projector_index) const
Definition init.hpp:237
std::unique_ptr< BetaFunctionTable > beta_function_table_
Definition init.hpp:385
virtual ~NormConservingPlanewaveBasis()=default
std::vector< int > mesh_sizes_
Definition init.hpp:392
Real interpolate_rho_core(int element_type, Real q_value) const
Definition init.hpp:277
std::vector< std::vector< Real > > local_potential_g_per_species_
Definition init.hpp:381
CoreChargeDensityTable core_charge_density_table_
Definition init.hpp:387
void compute_mesh_truncation_sizes(int number_of_species_types)
Definition init.hpp:399
const std::vector< Real > & atomic_charge_density_g(int species_type) const
Definition init.hpp:252
const BetaFunctionTable & beta_function_table() const
Definition init.hpp:284
bool has_nonlinear_core_correction() const
Definition init.hpp:259
int total_number_of_projectors() const
Definition init.hpp:181
ProjectorMappingTable projector_mapping_table_
Definition init.hpp:388
Real bare_d_matrix_element(int species_type, int projector_a, int projector_b) const
Definition init.hpp:216
int number_of_projectors(int species_type) const
Definition init.hpp:188
int beta_angular_momentum(int species_type, int projector_index) const
Definition init.hpp:195
Real interpolate_core_charge_density(int element_type, Real q_value) const
Definition init.hpp:332
const std::vector< Complex > & total_local_potential_g() const
Definition init.hpp:173
Real interpolate_core_charge_density_derivative(int element_type, Real q_value) const
Definition init.hpp:339
const std::vector< Real > & local_potential_g(int species_type_index) const
Definition init.hpp:165
Real interpolate_dbeta(int species_type_index, int beta_index, Real q_value) const
Definition init.hpp:307
PseudopotentialSet pseudopotential_set_
Definition init.hpp:380
const CoreChargeDensityTable & core_charge_density_table() const
Definition init.hpp:354
const LocalPotentialTable & local_potential_table() const
Definition init.hpp:347
std::vector< std::vector< Real > > atomic_charge_density_g_
Definition init.hpp:382
int projector_combined_lm_index(int species_type, int projector_index) const
Definition init.hpp:230
Real interpolate_local_potential_derivative(int element_type, Real q_value) const
Definition init.hpp:323
const PlanewaveBasis & planewave_basis() const
Definition init.hpp:147
CoreChargeData core_charge_data_
Definition init.hpp:389
const PseudopotentialSet & pseudopotential_set() const
Definition init.hpp:156
Real interpolate_beta(int species_type_index, int beta_index, Real q_value) const
Definition init.hpp:295
int projector_to_beta_index(int species_type, int projector_index) const
Definition init.hpp:223
LocalPotentialTable local_potential_table_
Definition init.hpp:386
virtual bool is_ultrasoft() const
Definition init.hpp:373
int number_of_projector_channels(int species_type) const
Definition init.hpp:203
virtual void init(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set)
Definition init.hpp:57
const PlanewaveBasis * planewave_basis_ptr_
Definition init.hpp:378
const ProjectorMappingTable & projector_mapping_table() const
Definition init.hpp:363
std::vector< Complex > total_local_potential_g_
Definition init.hpp:383
int number_of_g_vectors() const
Definition init.hpp:145
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
const std::vector< Real > & g_vector_magnitudes() const
Definition init.hpp:154
int number_of_channels(int species_type) const
int to_beta_index(int species_type, int channel_index) const
int magnetic_quantum_number(int species_type, int channel_index) const
void build(const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set, int number_of_species_types)
int angular_momentum(int species_type, int channel_index) const
int combined_lm_index(int species_type, int channel_index) const
PseudopotentialSet clone() const
Deep-clone the entire set (preserves derived types).
Definition set.hpp:139
int size() const
Definition set.hpp:98
constexpr Real two_pi
Definition constants.hpp:25
constexpr bool use_radial_integration_truncation
constexpr double radial_integration_truncation_radius
std::vector< std::vector< Real > > tabulate_atomic_charge_density_in_g_space(const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, const std::vector< Real > &g_vector_magnitudes, Real q_spacing, int number_of_q_points)
std::unique_ptr< BetaFunctionTable > create_beta_function_table(const PseudopotentialSet &pseudopotential_set)
Definition factory.hpp:24
double Real
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
std::complex< double > Complex