Loading...
Searching...
No Matches
nonlocal.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Nonlocal (Kleinman-Bylander) pseudopotential force contribution.
5 *
6 * Computes the Hellmann-Feynman force from the nonlocal pseudopotential
7 * using KB projectors and their derivatives with respect to atomic positions.
8 *
9 * For norm-conserving PPs the bare ionic D matrix is used -- `d_ion_matrix`,
10 * read from the UPF `PP_DIJ` record. For ultrasoft and PAW it is the screened
11 * D matrix, D_ion plus the integral of v_eff against Q_ij, which is per spin
12 * channel and is supplied by the nonlocal effective potential.
13 *
14 * Gamma-point correction
15 * ----------------------
16 * When the gamma-only optimisation is active (1×1×1 k-grid, no shift), Q3
17 * stores only the positive half-sphere G-vectors. The half-sphere overlaps
18 * A_half = <β|(-iG)|ψ>_half and B_half = <β|ψ>_half relate to their
19 * full-sphere counterparts by:
20 *
21 * A_full = 2 · Re[A_half], B_full = 2 · Re[B_half]
22 *
23 * so the correct Hellmann-Feynman force is:
24 *
25 * F = 2D · Re[A_full · conj(B_full)] = 2D · 4 · Re[A_half] · Re[B_half]
26 *
27 * The naive formula Re[A_half · conj(B_half)] is wrong because the Im·Im
28 * cross term is non-zero for the complex half-sphere coefficients produced
29 * by the Davidson solver, causing incorrect forces at the gamma point.
30 */
31
32#include <cmath>
33#include <complex>
34#include <vector>
35
47
48namespace q3 {
49using namespace q3::blas;
50
52 public:
54 const PlanewaveBasis& planewave_basis,
55 const KPointsGrid& kpoint_grid,
57 : crystal_structure_(crystal_structure)
58 , planewave_basis_(planewave_basis)
59 , kpoint_grid_(kpoint_grid)
60 , scf_state_(scf_state) {}
61
62 [[nodiscard]] std::vector<Vector3D> compute() const {
63 bool is_gamma_only = planewave_basis_.is_gamma_only();
64 int number_of_kpoints = kpoint_grid_.number_of_kpoints();
65 std::vector<Vector3D> nonlocal_force(crystal_structure_.number_of_atoms(), {0.0, 0.0, 0.0});
66 int total_number_of_kpoint_sets =
67 number_of_kpoints * (scf_state_.number_of_spin_components() == 2 ? 2 : 1);
68 const bool has_ultrasoft = scf_state_.quantum_system().has_ultrasoft();
69
70 for (int kpoint_spin_index = 0; kpoint_spin_index < total_number_of_kpoint_sets;
71 kpoint_spin_index++) {
72 int kpoint_index = (kpoint_spin_index % number_of_kpoints);
73 int number_of_plane_waves = scf_state_.number_of_plane_waves(kpoint_index);
74 const auto& plane_wave_indices = scf_state_.g_vector_indices_at_kpoint(kpoint_index);
75 Vector3D kpoint_vector = kpoint_grid_[kpoint_index].coordinates;
76 Real kpoint_weight = kpoint_grid_[kpoint_index].weight;
77
78 const Matrix3x3& reciprocal_lattice_vectors =
79 crystal_structure_.reciprocal_lattice_vectors();
80 Vector3D kpoint_cartesian;
81 for (int direction = 0; direction < 3; direction++) {
82 kpoint_cartesian[direction] =
83 kpoint_vector.x * reciprocal_lattice_vectors[0][direction] +
84 kpoint_vector.y * reciprocal_lattice_vectors[1][direction] +
85 kpoint_vector.z * reciprocal_lattice_vectors[2][direction];
86 }
87
88 Matrix<Complex> projector_functions;
90 kpoint_vector,
91 crystal_structure_,
92 planewave_basis_,
95 plane_wave_indices,
96 number_of_plane_waves,
98 projector_functions);
99
100 // Collect occupied band indices and weights
101 std::vector<int> occupied_band_indices;
102 std::vector<Real> occupied_band_weights;
103 std::vector<Real> occupied_band_occupations;
104 /**
105 * Needed for the ultrasoft/PAW overlap term below, which is
106 * band-dependent through the eigenvalue where the D term is not.
107 */
108 std::vector<Real> occupied_band_eigenvalues;
109 for (int band_index = 0; band_index < scf_state_.number_of_bands(); band_index++) {
110 Real occupation = scf_state_.occupation(kpoint_spin_index, band_index);
112 continue;
113 occupied_band_indices.push_back(band_index);
114 occupied_band_weights.push_back(kpoint_weight * occupation);
115 occupied_band_occupations.push_back(occupation);
116 occupied_band_eigenvalues.push_back(
117 scf_state_.eigenvalue(kpoint_spin_index, band_index));
118 }
119 int number_of_occupied_bands = static_cast<int>(occupied_band_indices.size());
120 if (number_of_occupied_bands == 0)
121 continue;
122
123 int number_of_projectors =
125
126 // Precompute -i(k+G) components
127 std::vector<std::vector<Complex>> minus_i_k_plus_g(
128 3, std::vector<Complex>(number_of_plane_waves));
129 for (int plane_wave_index = 0; plane_wave_index < number_of_plane_waves;
130 plane_wave_index++) {
131 const auto& g_vector =
132 planewave_basis_.g_vectors()[plane_wave_indices[plane_wave_index]];
133 for (int direction = 0; direction < 3; direction++) {
134 Real k_plus_g_direction =
135 (g_vector.g_cart[direction] + kpoint_cartesian[direction]) *
136 crystal_structure_.two_pi_over_lattice_parameter();
137 minus_i_k_plus_g[direction][plane_wave_index] =
138 Complex(0.0, -k_plus_g_direction);
139 }
140 }
141
142 /**
143 * We need 4 overlaps per band:
144 * 0: <beta | psi>
145 * 1,2,3: <beta | -i(k+G) psi> for x,y,z
146 */
147 std::vector<std::vector<Complex>> batched_overlaps(
148 4, std::vector<Complex>(number_of_projectors * number_of_occupied_bands));
149
150 std::vector<Complex> modified_wavefunction(number_of_plane_waves *
151 number_of_occupied_bands);
152
153 Complex alpha_blas(1.0, 0.0);
154 Complex beta_blas(0.0, 0.0);
155 int blas_rows = number_of_projectors;
156 int blas_columns = number_of_occupied_bands;
157 int blas_inner_dimension = number_of_plane_waves;
158
159 for (int component = 0; component < 4; component++) {
160 for (int occupied_band_index = 0; occupied_band_index < number_of_occupied_bands;
161 occupied_band_index++) {
162 int band_index = occupied_band_indices[occupied_band_index];
163 int offset = occupied_band_index * number_of_plane_waves;
164 for (int g_index = 0; g_index < number_of_plane_waves; g_index++) {
165 Complex psi = scf_state_.wavefunction_coefficient(
166 kpoint_spin_index, band_index, g_index);
167 if (component == 0) {
168 modified_wavefunction[offset + g_index] = psi;
169 } else {
170 modified_wavefunction[offset + g_index] =
171 psi * minus_i_k_plus_g[component - 1][g_index];
172 }
173 }
174 }
175
176 zgemm_("C",
177 "N",
178 &blas_rows,
179 &blas_columns,
180 &blas_inner_dimension,
181 reinterpret_cast<const std::complex<double>*>(&alpha_blas),
182 reinterpret_cast<const std::complex<double>*>(projector_functions.data()),
183 &blas_inner_dimension,
184 reinterpret_cast<const std::complex<double>*>(modified_wavefunction.data()),
185 &blas_inner_dimension,
186 reinterpret_cast<const std::complex<double>*>(&beta_blas),
187 reinterpret_cast<std::complex<double>*>(batched_overlaps[component].data()),
188 &blas_rows);
189 }
190
191 /**
192 * When ranks share a k-point pool, each holds only its slab of this
193 * k-point's plane waves, so every overlap above is a partial sum
194 * over G. The force multiplies two of them, so they are summed
195 * across the pool here, before any product. Summing the per-rank
196 * forces instead drops every cross term between two ranks' slabs:
197 * the energy, linear in the projections the SCF already reduced,
198 * stayed exact while the forces came out up to 0.6 Ry/bohr wrong
199 * (camsai/q3#22). The DFT+U force reduces its overlaps the same
200 * way. A no-op when a pool has one rank.
201 */
202 if (scf_state_.execution_policy != nullptr) {
203 for (auto& component_overlaps : batched_overlaps)
205 component_overlaps.data(), static_cast<int>(component_overlaps.size()));
206 }
207
208 /**
209 * The G = 0 term that `2·Re[B_half]` double counts.
210 *
211 * Q3's gamma-only half sphere *includes* G = 0 (the selection rule
212 * is `index_1 >= 0` when the other two vanish), so
213 * `2·Re[<beta|psi>_half] = <beta|psi>_full + beta(0)psi(0)`. The
214 * derivative overlap needs no such correction — its summand carries
215 * a factor G_alpha, which is zero there — which is why leaving it
216 * out skewed the components unevenly rather than scaling them: it
217 * only touches projectors with beta(0) != 0, i.e. l = 0. Silicon's
218 * Gamma force came out right in x and 1.36x too large in y.
219 *
220 * Quantum ESPRESSO corrects the same double count, in a different
221 * place: it subtracts inside the overlap routine itself, so both
222 * overlaps are corrected there and the correction on the derivative
223 * one is a no-op for the same reason it is skipped here. The
224 * derivation above rests on this file's own selection rule; the
225 * upstream agreement is corroboration, not its basis.
226 */
227 std::vector<Real> gamma_g0_overlap;
228 if (is_gamma_only) {
229 gamma_g0_overlap.assign(
230 static_cast<size_t>(number_of_projectors) * number_of_occupied_bands, 0.0);
231 int g0_index = -1;
232 for (int plane_wave_index = 0; plane_wave_index < number_of_plane_waves;
233 plane_wave_index++) {
234 if (planewave_basis_.g_vectors()[plane_wave_indices[plane_wave_index]]
235 .magnitude_squared < math::tolerances::numerical_zero_threshold) {
236 g0_index = plane_wave_index;
237 break;
238 }
239 }
240 if (g0_index >= 0) {
241 for (int projector = 0; projector < number_of_projectors; projector++) {
242 for (int occupied_band_index = 0;
243 occupied_band_index < number_of_occupied_bands;
244 occupied_band_index++) {
245 Complex psi = scf_state_.wavefunction_coefficient(
246 kpoint_spin_index,
247 occupied_band_indices[occupied_band_index],
248 g0_index);
249 gamma_g0_overlap[static_cast<size_t>(projector) *
250 number_of_occupied_bands +
251 occupied_band_index] =
252 (std::conj(projector_functions[projector][g0_index]) * psi).real();
253 }
254 }
255 }
256 }
257
258 // Accumulate forces
259 int current_projector_index = 0;
260 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms();
261 atom_index++) {
262 int species_index = crystal_structure_.species_type_index(atom_index);
263 int number_of_beta_functions =
265 species_index);
266
267 std::vector<int> beta_indices, magnetic_quantum_numbers, angular_momenta;
268 int starting_projector_index = current_projector_index;
269 for (int beta_function_index = 0; beta_function_index < number_of_beta_functions;
270 beta_function_index++) {
271 int angular_momentum =
273 species_index, beta_function_index);
274 for (int magnetic_projection : harmonic_order_sequence(angular_momentum)) {
275 beta_indices.push_back(beta_function_index);
276 magnetic_quantum_numbers.push_back(magnetic_projection);
277 angular_momenta.push_back(angular_momentum);
278 current_projector_index++;
279 }
280 }
281 int number_of_projectors_on_atom = static_cast<int>(beta_indices.size());
282
283 for (int projector_a_index = 0; projector_a_index < number_of_projectors_on_atom;
284 projector_a_index++) {
285 for (int projector_b_index = 0;
286 projector_b_index < number_of_projectors_on_atom;
287 projector_b_index++) {
288 /**
289 * D is per spin channel for ultrasoft and PAW, and the
290 * loop above is spin-major over `kpoint_spin_index`, so
291 * the channel is the quotient by the k-point count.
292 */
293 Real hamiltonian_d_coefficient =
294 scf_state_.quantum_system().d_matrix_element(atom_index,
295 species_index,
296 projector_a_index,
297 projector_b_index,
298 kpoint_spin_index /
299 number_of_kpoints);
300
301 /**
302 * The generalized-eigenvalue term, for ultrasoft and PAW.
303 *
304 * These solve H|psi> = eps S|psi> with S = 1 + sum q_ij
305 * |p_i><p_j|, so S moves with the atom too and the force
306 * contracts (D_ij - eps_nk q_ij), not D_ij alone. The
307 * stress has always carried this correction; the force
308 * did not, which left it ~6% high on silicon even after
309 * the augmentation term was repaired -- and unlike a
310 * missing augmentation term, this one is band-dependent,
311 * so it cannot be absorbed into any scale factor.
312 *
313 * q_ij is diagonal in (l, m): the augmentation overlap
314 * integral is a radial quantity and the angular parts
315 * are orthonormal.
316 */
317 /**
318 * `has_ultrasoft` is a whole-system flag: a cell with
319 * one PAW species and one norm-conserving species takes
320 * the ultrasoft iterator. Asking the norm-conserving
321 * species for an augmentation overlap dereferences a
322 * null `as_ultrasoft`, and the assert that would catch
323 * it is compiled out in Release. Gate per species.
324 */
325 Real augmentation_overlap = 0.0;
326 if (has_ultrasoft &&
327 scf_state_.quantum_system()
329 .pseudopotential_set()[species_index]
331 angular_momenta[projector_a_index] ==
332 angular_momenta[projector_b_index] &&
333 magnetic_quantum_numbers[projector_a_index] ==
334 magnetic_quantum_numbers[projector_b_index]) {
335 augmentation_overlap =
336 static_cast<const UltrasoftPlanewaveKohnShamQuantumSystem&>(
337 scf_state_.quantum_system())
338 .ultrasoft_basis()
339 .augmentation_overlap_integral(species_index,
340 beta_indices[projector_a_index],
341 beta_indices[projector_b_index]);
342 }
343
344 if (std::abs(hamiltonian_d_coefficient) <
346 std::abs(augmentation_overlap) <
348 continue;
349 }
350
351 int global_projector_a_index = starting_projector_index + projector_a_index;
352 int global_projector_b_index = starting_projector_index + projector_b_index;
353
354 Vector3D force_sum = {0.0, 0.0, 0.0};
355 Vector3D eigenvalue_weighted_sum = {0.0, 0.0, 0.0};
356
357 for (int occupied_band_index = 0;
358 occupied_band_index < number_of_occupied_bands;
359 occupied_band_index++) {
360 Real weight = occupied_band_weights[occupied_band_index];
361 Real eigenvalue = occupied_band_eigenvalues[occupied_band_index];
362
363 Complex overlap_b_0 =
364 batched_overlaps[0][occupied_band_index * number_of_projectors +
365 global_projector_b_index];
366 Complex overlap_b_0_conj = std::conj(overlap_b_0);
367
368 for (int direction = 0; direction < 3; direction++) {
369 Complex overlap_a_dir =
370 batched_overlaps[direction + 1]
371 [occupied_band_index * number_of_projectors +
372 global_projector_a_index];
373
374 Complex spin_summed_force_contribution =
375 overlap_a_dir * overlap_b_0_conj;
376 // k-point weight already includes the spin degeneracy factor of 2.
377 Real term =
378 is_gamma_only
379 // A_full · B_full; see the G=0 note above.
380 ? (2.0 * overlap_a_dir.real()) *
381 (2.0 * overlap_b_0.real() -
382 gamma_g0_overlap[static_cast<size_t>(
383 global_projector_b_index) *
384 number_of_occupied_bands +
385 occupied_band_index])
386 : spin_summed_force_contribution.real();
387 force_sum[direction] += weight * term;
388 eigenvalue_weighted_sum[direction] += weight * eigenvalue * term;
389 }
390 }
391
392 for (int direction = 0; direction < 3; direction++) {
393 nonlocal_force[atom_index][direction] +=
394 2.0 * (hamiltonian_d_coefficient * force_sum[direction] -
395 augmentation_overlap * eigenvalue_weighted_sum[direction]);
396 }
397 }
398 }
399 }
400 }
401
402 return nonlocal_force;
403 }
404
405 private:
406 const Crystal& crystal_structure_;
407 const PlanewaveBasis& planewave_basis_;
408 const KPointsGrid& kpoint_grid_;
410};
411
412} // namespace q3
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
int number_of_kpoints() const
T * data()
Returns the underlying flat data pointer.
Definition matrix.hpp:75
NonlocalPseudopotentialForceCalculator(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const KPointsGrid &kpoint_grid, const NormConservingPlanewaveKohnShamSCFState &scf_state)
Definition nonlocal.hpp:53
std::vector< Vector3D > compute() const
Definition nonlocal.hpp:62
const BetaFunctionTable & beta_function_table() const
Definition init.hpp:284
int total_number_of_projectors() const
Definition init.hpp:181
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
const PseudopotentialSet & pseudopotential_set() const
Definition init.hpp:156
const NormConservingPlanewaveBasis & pseudopotential_basis() const
Definition init.hpp:65
virtual Real d_matrix_element(int atom_index, int species_type, int projector_index_i, int projector_index_j, int spin=0) const
Definition init.hpp:84
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
bool is_gamma_only() const
Definition init.hpp:157
bool has_augmentation_charges() const
Definition set.hpp:129
virtual void reduce_within_group(ComplexValue *data, int number_of_elements) const =0
void zgemm_(const char *transa, const char *transb, const int *m, const int *n, const int *k, const std::complex< double > *alpha, const std::complex< double > *a, const int *lda, const std::complex< double > *b, const int *ldb, const std::complex< double > *beta, std::complex< double > *c, const int *ldc)
constexpr double numerical_zero_threshold
constexpr double d_matrix_element_threshold
constexpr double occupation_threshold
double Real
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
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]
const NormConservingPlanewaveKohnShamQuantumSystem & quantum_system() const override
Definition init.hpp:82
const std::vector< int > & g_vector_indices_at_kpoint(int kpoint_index) const
Definition init.hpp:80
int number_of_plane_waves(int kpoint_index) const
Definition init.hpp:77
Real occupation(int kpoint_index, int band_index) const
Definition init.hpp:90
const execution::PlanewaveExecutionPolicy * execution_policy
Definition init.hpp:64
int number_of_spin_components() const
Definition init.hpp:106
Real eigenvalue(int kpoint_index, int band_index) const
Definition init.hpp:87
Complex wavefunction_coefficient(int kpoint_index, int band_index, int g_vector_index) const
Definition init.hpp:84