Loading...
Searching...
No Matches
projector.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Kleinman-Bylander projector computation.
5 *
6 * Free functions for computing <β|k+G> projectors and the S matrix
7 * (overlap) for US-PP generalized eigenvalue problems.
8 */
9
10#include <cmath>
11#include <complex>
12#include <vector>
13
23
24namespace q3 {
25
26/**
27 * Compute <β_ib|k+G> for all atoms and projectors at a given k-point.
28 * kleinman_bylander_potential[ikb][ig] = (1/√Ω) * β(|k+G|) * Y_lm(k̂+Ĝ) * S(atom) * (-i)^l
29 *
30 * Uses pre-tabulated β(q) from the BetaFunctionTable for O(1) lookup
31 * instead of recomputing the full radial Bessel integral per G-vector.
32 *
33 * Optimized to precompute per-G quantities (|k+G|, Y_lm, structure factors)
34 * once and reuse across all atoms and projectors.
35 */
37 const Crystal& crystal_structure,
38 const PlanewaveBasis& planewave_basis,
39 const PseudopotentialSet& pseudopotential_set,
40 const BetaFunctionTable& beta_function_table,
41 const std::vector<int>& g_vector_indices,
42 int number_of_plane_waves,
43 int total_number_of_projectors,
44 Matrix<Complex>& kleinman_bylander_potential) {
45
46 kleinman_bylander_potential.resize(
47 total_number_of_projectors, number_of_plane_waves, Complex(0.0, 0.0));
48
49 const Matrix3x3& reciprocal_lattice_vectors = crystal_structure.reciprocal_lattice_vectors();
50 Vector3D kpoint_cartesian;
51 for (int dimension = 0; dimension < 3; dimension++) {
52 kpoint_cartesian[dimension] = k_crystal.x * reciprocal_lattice_vectors[0][dimension] +
53 k_crystal.y * reciprocal_lattice_vectors[1][dimension] +
54 k_crystal.z * reciprocal_lattice_vectors[2][dimension];
55 }
56
57 Real two_pi_over_lattice_parameter = crystal_structure.two_pi_over_lattice_parameter();
58 Real cell_volume = crystal_structure.cell_volume();
59 Real inverse_sqrt_cell_volume = 1.0 / std::sqrt(cell_volume);
60
61 // --- Precompute per-G quantities once ---
62
63 // 1. k+G cartesian vectors and |k+G|² for all G-vectors
64 std::vector<std::array<Real, 3>> k_plus_g_cartesian_array(number_of_plane_waves);
65 std::vector<Real> k_plus_g_magnitude_squared(number_of_plane_waves);
66 std::vector<Real> k_plus_g_magnitude(number_of_plane_waves);
67
68 for (int g_index = 0; g_index < number_of_plane_waves; g_index++) {
69 Vector3D k_plus_g =
70 kpoint_cartesian + planewave_basis.g_vectors()[g_vector_indices[g_index]].g_cart;
71 k_plus_g_cartesian_array[g_index] = {k_plus_g.x, k_plus_g.y, k_plus_g.z};
72 Real magnitude_squared =
73 k_plus_g.x * k_plus_g.x + k_plus_g.y * k_plus_g.y + k_plus_g.z * k_plus_g.z;
74 k_plus_g_magnitude_squared[g_index] = magnitude_squared;
75 k_plus_g_magnitude[g_index] = std::sqrt(magnitude_squared) * two_pi_over_lattice_parameter;
76 }
77
78 /**
79 * Precompute structure factors per atom for all G-vectors
80 * structure_factor[atom][g] = exp(-i * 2π * (k+G)·r_atom)
81 * = exp(-i * 2π * G·r_atom) * exp(-i * 2π * k·r_atom)
82 * The k·r part is constant per atom, only G·r varies.
83 */
84 int number_of_atoms = crystal_structure.number_of_atoms();
85 std::vector<std::vector<Complex>> structure_factors(number_of_atoms);
86
87 for (int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
88 Vector3D atomic_position = crystal_structure.convert_from_crystal_to_cartesian(atom_index);
89 Real k_dot_r = kpoint_cartesian.dot(atomic_position);
90 structure_factors[atom_index].resize(number_of_plane_waves);
91
92 for (int g_index = 0; g_index < number_of_plane_waves; g_index++) {
93 Real phase_angle = constants::two_pi *
94 (planewave_basis.g_vectors()[g_vector_indices[g_index]].g_cart.dot(
95 atomic_position) +
96 k_dot_r);
97 structure_factors[atom_index][g_index] = compute_phase_factor(phase_angle);
98 }
99 }
100
101 // Determine maximum angular momentum across all species
102 int maximum_angular_momentum = 0;
103 for (int species_index = 0; species_index < static_cast<int>(pseudopotential_set.size());
104 species_index++) {
105 for (const auto& projector : pseudopotential_set[species_index].projectors) {
106 maximum_angular_momentum =
107 std::max(maximum_angular_momentum, projector.angular_momentum);
108 }
109 }
110 int number_of_lm_components = (maximum_angular_momentum + 1) * (maximum_angular_momentum + 1);
111
112 // Batch-compute all Y_lm(k+G) using the optimized batch function
113 std::vector<std::vector<Real>> spherical_harmonics_values;
114 compute_real_spherical_harmonics_batch(number_of_lm_components,
115 number_of_plane_waves,
116 k_plus_g_cartesian_array,
117 k_plus_g_magnitude_squared,
118 spherical_harmonics_values);
119
120 // --- Build projectors using precomputed data ---
121
122 int combined_projector_index = 0;
123 for (int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
124 int species_type_index = crystal_structure.species_type_index(atom_index);
125 const auto& atom_structure_factors = structure_factors[atom_index];
126
127 for (int beta_index = 0;
128 beta_index <
129 static_cast<int>(pseudopotential_set[species_type_index].projectors.size());
130 beta_index++) {
131 int angular_momentum =
132 pseudopotential_set[species_type_index].projectors[beta_index].angular_momentum;
133 Complex angular_phase_factor = compute_angular_phase_factor(angular_momentum);
134 Complex combined_prefactor = inverse_sqrt_cell_volume * angular_phase_factor;
135
136 for (int magnetic_projection : harmonic_order_sequence(angular_momentum)) {
137 int lm_index =
138 harmonic_combined_degree_order_index(angular_momentum, magnetic_projection);
139
140 for (int g_index = 0; g_index < number_of_plane_waves; g_index++) {
141 Real beta_value_at_q = beta_function_table.interpolate(
142 species_type_index, beta_index, k_plus_g_magnitude[g_index]);
143
144 kleinman_bylander_potential[combined_projector_index][g_index] =
145 beta_value_at_q * spherical_harmonics_values[g_index][lm_index] *
146 atom_structure_factors[g_index] * combined_prefactor;
147 }
148 combined_projector_index++;
149 }
150 }
151 }
152}
153} // namespace q3
Real interpolate(int species_type_index, int beta_index, Real q_value) const
Definition init.hpp:65
Vector3D convert_from_crystal_to_cartesian(int atom_index) const
Definition crystal.hpp:225
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
int species_type_index(int atom_index) const
Definition crystal.hpp:273
int number_of_atoms() const
Definition crystal.hpp:209
void resize(size_t rows, size_t cols)
Resizes the matrix, invalidating previous content structure if dimensions change.
Definition matrix.hpp:26
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
int size() const
Definition set.hpp:98
constexpr Real two_pi
Definition constants.hpp:25
void compute_real_spherical_harmonics_batch(int number_of_lm_components, int number_of_g_vectors, const std::vector< std::array< Real, 3 > > &g_vectors, const std::vector< Real > &g_squared, std::vector< std::vector< Real > > &spherical_harmonics_values)
int harmonic_combined_degree_order_index(int degree, int order)
Definition harmonics.hpp:61
double Real
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
void compute_kleinman_bylander_projectors(const Vector3D &k_crystal, const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const BetaFunctionTable &beta_function_table, const std::vector< int > &g_vector_indices, int number_of_plane_waves, int total_number_of_projectors, Matrix< Complex > &kleinman_bylander_potential)
Definition projector.hpp:36
Complex compute_angular_phase_factor(int angular_momentum)
std::complex< double > Complex
std::vector< int > harmonic_order_sequence(int degree)
Definition harmonics.hpp:40
3×3 matrix stored row-major: row i = mat[i]
Real dot(const Vector3D &other) const
Definition vector_3d.hpp:59