Loading...
Searching...
No Matches
effective_potential_non_local.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * PlanewaveEffectiveNonlocalPotential — planewave implementation of the effective
5 * D-matrix computation (QE's "newd").
6 *
7 * Inherits from the abstract EffectiveNonlocalPotential interface.
8 * Stores the ultrasoft-specific dependencies (augmentation tables, spherical
9 * harmonics, Gaunt coefficients, projector mappings) as members and delegates
10 * the actual computation to the existing free function.
11 *
12 * Computes the effective D-matrix for all atoms by combining the bare
13 * D-ion matrix with the G-space integral of V_eff(G) * Q_ij(G).
14 *
15 * For norm-conserving species, copies the bare d_ion_matrix.
16 * For ultrasoft species, adds ∫ V_eff(G) * Q_ij(G) * e^{-iG·τ} dG.
17 */
18
19#include <cmath>
20#include <complex>
21#include <vector>
22
32
33namespace q3 {
34
35// Forward declaration of free function used by the wrapper class below.
36inline std::vector<std::vector<Real>> compute_ultrasoft_effective_d_matrix(
37 const Crystal& crystal_structure,
38 const PlanewaveBasis& planewave_basis,
39 const PseudopotentialSet& pseudopotential_set,
40 const std::vector<Complex>& total_potential_g,
41 const AugmentationQTable& augmentation_q_table,
42 const SphericalHarmonicsTable& spherical_harmonics_table,
43 const GauntCoefficientTable& gaunt_table,
44 const std::vector<int>& projector_channels_per_type,
45 const std::vector<std::vector<int>>& projector_to_beta_index,
46 const std::vector<std::vector<int>>& projector_combined_lm_index,
47 const std::vector<std::vector<int>>& projector_angular_momentum);
48
49/**
50 * Planewave implementation of the effective D-matrix computation.
51 *
52 * Stores ultrasoft-specific dependencies as members and implements
53 * the abstract EffectiveNonlocalPotential::compute() interface.
54 */
56 public:
58 const PseudopotentialSet& pseudopotential_set,
59 const AugmentationQTable& augmentation_q_table,
60 const SphericalHarmonicsTable& spherical_harmonics_table,
61 const GauntCoefficientTable& gaunt_table,
62 const std::vector<int>& projector_channels_per_type,
63 const std::vector<std::vector<int>>& projector_to_beta_index,
64 const std::vector<std::vector<int>>& projector_combined_lm_index,
65 const std::vector<std::vector<int>>& projector_angular_momentum)
66 : pseudopotential_set_(pseudopotential_set)
67 , augmentation_q_table_(augmentation_q_table)
68 , spherical_harmonics_table_(spherical_harmonics_table)
69 , gaunt_table_(gaunt_table)
70 , projector_channels_per_type_(projector_channels_per_type)
71 , projector_to_beta_index_(projector_to_beta_index)
72 , projector_combined_lm_index_(projector_combined_lm_index)
73 , projector_angular_momentum_(projector_angular_momentum) {}
74
76
77 /// Compute the effective D-matrix for all atoms.
78 [[nodiscard]] std::vector<std::vector<Real>>
79 compute(const Crystal& crystal_structure,
80 const PlanewaveBasis& planewave_basis,
81 const std::vector<Complex>& total_potential_g) const override {
82 return compute_ultrasoft_effective_d_matrix(crystal_structure,
83 planewave_basis,
84 pseudopotential_set_,
85 total_potential_g,
86 augmentation_q_table_,
87 spherical_harmonics_table_,
88 gaunt_table_,
89 projector_channels_per_type_,
90 projector_to_beta_index_,
91 projector_combined_lm_index_,
92 projector_angular_momentum_);
93 }
94
95 private:
96 const PseudopotentialSet& pseudopotential_set_;
97 const AugmentationQTable& augmentation_q_table_;
98 const SphericalHarmonicsTable& spherical_harmonics_table_;
99 const GauntCoefficientTable& gaunt_table_;
100 const std::vector<int>& projector_channels_per_type_;
101 const std::vector<std::vector<int>>& projector_to_beta_index_;
102 const std::vector<std::vector<int>>& projector_combined_lm_index_;
103 const std::vector<std::vector<int>>& projector_angular_momentum_;
104};
105
106/**
107 * Compute the effective D-matrix (QE's "newd") for all atoms.
108 *
109 * @return effective_d_matrix[atom_index][projector_i * number_of_projectors + projector_j]
110 */
111inline std::vector<std::vector<Real>> compute_ultrasoft_effective_d_matrix(
112 const Crystal& crystal_structure,
113 const PlanewaveBasis& planewave_basis,
114 const PseudopotentialSet& pseudopotential_set,
115 const std::vector<Complex>& total_potential_g,
116 const AugmentationQTable& augmentation_q_table,
117 const SphericalHarmonicsTable& spherical_harmonics_table,
118 const GauntCoefficientTable& gaunt_table,
119 const std::vector<int>& projector_channels_per_type,
120 const std::vector<std::vector<int>>& projector_to_beta_index,
121 const std::vector<std::vector<int>>& projector_combined_lm_index,
122 const std::vector<std::vector<int>>& projector_angular_momentum) {
123
124 int number_of_atoms = crystal_structure.number_of_atoms();
125 int number_of_g_vectors = planewave_basis.number_of_g_vectors();
126 Real cell_volume = crystal_structure.cell_volume();
127 const auto& g_vector_magnitudes = planewave_basis.g_vector_magnitudes();
128
129 int maximum_angular_momentum_q_plus_one = augmentation_q_table.maximum_angular_momentum_q() + 1;
130 int number_of_lm_projectors =
131 maximum_angular_momentum_q_plus_one * maximum_angular_momentum_q_plus_one;
132
133 std::vector<std::vector<Real>> effective_d_matrix(number_of_atoms);
134
135 for (int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
136 int species_type_index = crystal_structure.species_type_index(atom_index);
137 int number_of_projectors_per_species =
138 pseudopotential_set[species_type_index].number_of_projectors;
139 int number_of_projectors = projector_channels_per_type[species_type_index];
140 effective_d_matrix[atom_index].assign(number_of_projectors * number_of_projectors, 0.0);
141
142 Vector3D atomic_position = crystal_structure.convert_from_crystal_to_cartesian(atom_index);
143
144 // Initialize with dvan (bare D matrix)
145 for (int projector_index_i = 0; projector_index_i < number_of_projectors;
146 projector_index_i++) {
147 for (int projector_index_j = 0; projector_index_j < number_of_projectors;
148 projector_index_j++) {
149 if (projector_combined_lm_index[species_type_index][projector_index_i] ==
150 projector_combined_lm_index[species_type_index][projector_index_j]) {
151 effective_d_matrix[atom_index][projector_index_i * number_of_projectors +
152 projector_index_j] =
153 pseudopotential_set[species_type_index].d_ion_matrix
154 [projector_to_beta_index[species_type_index][projector_index_i]]
155 [projector_to_beta_index[species_type_index][projector_index_j]];
156 }
157 }
158 }
159
160 // Precompute V(G) × structure_factor(G) for this atom
161 std::vector<Complex> potential_g_structure_factor_product(number_of_g_vectors);
162 for (int g_index = 0; g_index < number_of_g_vectors; g_index++) {
163 const auto& g_vector = planewave_basis.g_vectors()[g_index];
164 Real phase_angle = constants::two_pi * g_vector.g_cart.dot(atomic_position);
165 potential_g_structure_factor_product[g_index] =
166 total_potential_g[g_index] * compute_phase_factor(phase_angle);
167 }
168
169 // Add G-space integral: ∫ V_eff(G) * Q_ij(G) * e^{-iG·τ} dG
170 for (int projector_index_i = 0; projector_index_i < number_of_projectors;
171 projector_index_i++) {
172 for (int projector_index_j = projector_index_i;
173 projector_index_j < number_of_projectors;
174 projector_index_j++) {
175 int beta_index_i = projector_to_beta_index[species_type_index][projector_index_i];
176 int beta_index_j = projector_to_beta_index[species_type_index][projector_index_j];
177 int combined_lm_index_i =
178 projector_combined_lm_index[species_type_index][projector_index_i];
179 int combined_lm_index_j =
180 projector_combined_lm_index[species_type_index][projector_index_j];
181 int angular_momentum_i =
182 projector_angular_momentum[species_type_index][projector_index_i];
183 int angular_momentum_j =
184 projector_angular_momentum[species_type_index][projector_index_j];
185
186 int projector_pair_index =
187 beta_index_i * number_of_projectors_per_species + beta_index_j;
188
189 if (combined_lm_index_i >= number_of_lm_projectors ||
190 combined_lm_index_j >= number_of_lm_projectors) {
191 continue;
192 }
193 if (gaunt_table.number_of_nonzero_product_indices[combined_lm_index_i]
194 [combined_lm_index_j] == 0) {
195 continue;
196 }
197
198 // Pre-compute valid Gaunt contributions outside the G-vector loop
199 struct GauntTerm {
200 int product_lm_index;
201 int angular_momentum;
202 int projector_pair_index_for_augmentation_function;
203 Real gaunt_coefficient_value;
204 };
205 std::vector<GauntTerm> valid_gaunt_terms;
206 for (int gaunt_index = 0;
207 gaunt_index <
208 gaunt_table.number_of_nonzero_product_indices[combined_lm_index_i]
209 [combined_lm_index_j];
210 gaunt_index++) {
211 int product_lm_index =
212 gaunt_table.nonzero_product_indices[combined_lm_index_i]
213 [combined_lm_index_j][gaunt_index];
214 Real gaunt_coefficient_value =
215 gaunt_table.coefficients[product_lm_index][combined_lm_index_i]
216 [combined_lm_index_j];
217 int angular_momentum =
218 static_cast<int>(std::sqrt(static_cast<double>(product_lm_index)));
219 if (angular_momentum < std::abs(angular_momentum_i - angular_momentum_j) ||
220 angular_momentum > angular_momentum_i + angular_momentum_j ||
221 (angular_momentum + angular_momentum_i + angular_momentum_j) % 2 != 0) {
222 continue;
223 }
224 valid_gaunt_terms.push_back({product_lm_index,
225 angular_momentum,
226 projector_pair_index,
227 gaunt_coefficient_value});
228 }
229
230 if (valid_gaunt_terms.empty()) {
231 continue;
232 }
233
234 Complex sum_integral(0.0, 0.0);
235 for (int g_index = 0; g_index < number_of_g_vectors; g_index++) {
236 Real q_g_real = 0.0;
237 Real q_g_imag = 0.0;
238 Real g_magnitude = g_vector_magnitudes[g_index];
239 for (const auto& term : valid_gaunt_terms) {
240 Real augmentation_function_value =
241 augmentation_q_table.interpolate_augmentation_function(
242 species_type_index,
243 term.projector_pair_index_for_augmentation_function,
244 term.angular_momentum,
245 g_magnitude);
246 Real spherical_harmonic_value =
247 spherical_harmonics_table.value(g_index, term.product_lm_index);
248 Real integrand_contribution =
249 augmentation_function_value * spherical_harmonic_value;
250 switch (term.angular_momentum % 4) {
251 case 0:
252 q_g_real += term.gaunt_coefficient_value * integrand_contribution;
253 break;
254 case 1:
255 q_g_imag -= term.gaunt_coefficient_value * integrand_contribution;
256 break;
257 case 2:
258 q_g_real -= term.gaunt_coefficient_value * integrand_contribution;
259 break;
260 case 3:
261 q_g_imag += term.gaunt_coefficient_value * integrand_contribution;
262 break;
263 }
264 }
265 sum_integral += potential_g_structure_factor_product[g_index] *
266 Complex(q_g_real, -q_g_imag);
267 }
268
269 effective_d_matrix[atom_index]
270 [projector_index_i * number_of_projectors + projector_index_j] +=
271 std::real(sum_integral) * cell_volume;
272 if (projector_index_i != projector_index_j) {
273 effective_d_matrix[atom_index][projector_index_j * number_of_projectors +
274 projector_index_i] +=
275 std::real(sum_integral) * cell_volume;
276 }
277 }
278 }
279 }
280
281 return effective_d_matrix;
282}
283
284} // namespace q3
int maximum_angular_momentum_q() const override
Real interpolate_augmentation_function(int species_type_index, int projector_pair_index, int angular_momentum, Real q_value) const override
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
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
~PlanewaveEffectiveNonlocalPotential() override=default
PlanewaveEffectiveNonlocalPotential(const PseudopotentialSet &pseudopotential_set, const AugmentationQTable &augmentation_q_table, const SphericalHarmonicsTable &spherical_harmonics_table, const GauntCoefficientTable &gaunt_table, const std::vector< int > &projector_channels_per_type, const std::vector< std::vector< int > > &projector_to_beta_index, const std::vector< std::vector< int > > &projector_combined_lm_index, const std::vector< std::vector< int > > &projector_angular_momentum)
std::vector< std::vector< Real > > compute(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const std::vector< Complex > &total_potential_g) const override
Compute the effective D-matrix for all atoms.
Real value(int g_index, int combined_lm_index) const
constexpr Real two_pi
Definition constants.hpp:25
double Real
std::vector< std::vector< Real > > compute_ultrasoft_effective_d_matrix(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< Complex > &total_potential_g, const AugmentationQTable &augmentation_q_table, const SphericalHarmonicsTable &spherical_harmonics_table, const GauntCoefficientTable &gaunt_table, const std::vector< int > &projector_channels_per_type, const std::vector< std::vector< int > > &projector_to_beta_index, const std::vector< std::vector< int > > &projector_combined_lm_index, const std::vector< std::vector< int > > &projector_angular_momentum)
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
std::complex< double > Complex
std::vector< std::vector< std::vector< Real > > > coefficients
std::vector< std::vector< std::vector< int > > > nonzero_product_indices
std::vector< std::vector< int > > number_of_nonzero_product_indices