Loading...
Searching...
No Matches
nonlocal.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Nonlocal (Kleinman-Bylander) pseudopotential stress tensor contribution.
5 *
6 * Computes the nonlocal pseudopotential stress using KB projectors and their
7 * derivatives with respect to strain (Bessel and spherical harmonic components).
8 *
9 * For norm-conserving pseudopotential_set, the bare D matrix (Dion) is used.
10 * For ultrasoft pseudopotential_set, the effective D matrix (Deeq) is used,
11 * and an eigenvalue × q_ij correction is subtracted.
12 */
13
14#include <algorithm>
15#include <array>
16#include <cmath>
17#include <string>
18#include <vector>
19
33
34namespace q3 {
35using namespace q3::blas;
36
38 public:
40 const Crystal& crystal_structure,
41 const PlanewaveBasis& planewave_basis,
42 const KPointsGrid& kpoint_grid,
45 const std::string& derivative_method = "analytical")
46 : crystal_structure_(crystal_structure)
47 , planewave_basis_(planewave_basis)
48 , kpoint_grid_(kpoint_grid)
49 , scf_state_(scf_state)
50 , result_(result)
51 , derivative_method_(derivative_method) {}
52
53 void compute() {
54 bool use_effective_d_matrix = scf_state_.quantum_system().has_ultrasoft();
55 const Matrix3x3& reciprocal_lattice_vectors =
56 crystal_structure_.reciprocal_lattice_vectors();
57 const auto& pseudopotential_basis = scf_state_.quantum_system().pseudopotential_basis();
58
59 int maximum_angular_momentum = 0;
60 int largest_atom_projector_count = 0;
61 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms(); atom_index++) {
62 int species_type_index = crystal_structure_.species_type_index(atom_index);
63 for (int beta_index = 0;
64 beta_index < pseudopotential_basis.number_of_projectors(species_type_index);
65 beta_index++) {
66 maximum_angular_momentum = std::max(
67 maximum_angular_momentum,
68 pseudopotential_basis.beta_angular_momentum(species_type_index, beta_index));
69 }
70 largest_atom_projector_count =
71 std::max(largest_atom_projector_count,
72 pseudopotential_basis.number_of_projector_channels(species_type_index));
73 }
74 int number_of_lm_components =
75 (maximum_angular_momentum + 1) * (maximum_angular_momentum + 1);
76
77 int number_of_kpoints = kpoint_grid_.number_of_kpoints();
78 /**
79 * kpoint_state_index runs over number_of_kpoints * number_of_spin_components "quantum
80 * states" (spin-major: spin_index * number_of_kpoints + kpoint_index, matching
81 * PlanewaveKohnShamQuantumSystem's storage convention) — looping kpoint_index alone
82 * silently drops the second spin channel for LSDA (nspin=2). kpoint_grid_[kpoint_index]
83 * itself is NOT spin-doubled: same physical k-point and already spin-halved weight for
84 * both channels.
85 */
86 for (int spin_index = 0; spin_index < scf_state_.number_of_spin_components();
87 spin_index++) {
88 for (int kpoint_index = 0; kpoint_index < number_of_kpoints; kpoint_index++) {
89 int kpoint_state_index = spin_index * number_of_kpoints + kpoint_index;
90 /**
91 * number_of_plane_waves/g_vector_indices_at_kpoint are PlanewaveBasis-derived
92 * (the G-vector set for a k-point does not depend on spin) — physical
93 * kpoint_index. occupation/wavefunction_coefficient/eigenvalue are
94 * QuantumSystem-derived (per spin channel) — spin-doubled kpoint_state_index.
95 */
96 int number_of_plane_waves_kpoint = scf_state_.number_of_plane_waves(kpoint_index);
97 const auto& plane_wave_indices_kpoint =
98 scf_state_.g_vector_indices_at_kpoint(kpoint_index);
99 Vector3D kpoint_coordinates_crystal = kpoint_grid_[kpoint_index].coordinates;
100 Real kpoint_weight = kpoint_grid_[kpoint_index].weight;
101
102 Vector3D kpoint_coordinates_cartesian = crystal_to_cartesian_kpoint(
103 kpoint_coordinates_crystal, reciprocal_lattice_vectors);
104
105 // Collect occupied band indices and weights
106 std::vector<int> occupied_band_indices;
107 std::vector<Real> occupied_band_weights;
108 for (int band_index = 0; band_index < scf_state_.number_of_bands(); band_index++) {
109 Real occupation = scf_state_.occupation(kpoint_state_index, band_index);
111 continue;
112 occupied_band_indices.push_back(band_index);
113 occupied_band_weights.push_back(kpoint_weight * occupation);
114 }
115 int number_of_occupied_bands = static_cast<int>(occupied_band_indices.size());
116 if (number_of_occupied_bands == 0)
117 continue;
118
119 // Compute k+G vectors in Cartesian coordinates
120 std::vector<Real> kpoint_plus_g_x, kpoint_plus_g_y, kpoint_plus_g_z;
121 std::vector<Real> kpoint_plus_g_norm_inverse;
122 compute_kpoint_plus_g_vectors(kpoint_coordinates_cartesian,
123 plane_wave_indices_kpoint,
124 number_of_plane_waves_kpoint,
125 kpoint_plus_g_x,
126 kpoint_plus_g_y,
127 kpoint_plus_g_z,
128 kpoint_plus_g_norm_inverse);
129
130 /**
131 * The k+G geometry and its spherical harmonics do not depend on the
132 * atom, so they are computed once per k-point. Only the structure
133 * factor does, and it is formed per atom as that atom is reached.
134 * This used to be a 56-byte record per projector per plane wave --
135 * on the 54-atom silicon cell over a gigabyte of the same numbers.
136 */
137 KpointPlusGGeometry geometry;
138 compute_kpoint_plus_g_geometry(kpoint_coordinates_cartesian,
139 plane_wave_indices_kpoint,
140 number_of_plane_waves_kpoint,
141 number_of_lm_components,
142 geometry);
143
144 // Collect occupied wavefunctions into contiguous matrix (NPW × Nocc)
145 std::vector<Complex> wavefunction_matrix(
146 static_cast<std::size_t>(number_of_plane_waves_kpoint) *
147 number_of_occupied_bands);
148 for (int occupied_index = 0; occupied_index < number_of_occupied_bands;
149 occupied_index++) {
150 int band_index = occupied_band_indices[occupied_index];
151 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
152 wavefunction_matrix[static_cast<std::size_t>(occupied_index) *
153 number_of_plane_waves_kpoint +
154 g_index] =
155 scf_state_.wavefunction_coefficient(
156 kpoint_state_index, band_index, g_index);
157 }
158 }
159
160 // Precompute eigenvalues for occupied bands (needed for US)
161 std::vector<Real> occupied_eigenvalues(number_of_occupied_bands);
162 if (use_effective_d_matrix) {
163 for (int band_n = 0; band_n < number_of_occupied_bands; band_n++) {
164 occupied_eigenvalues[band_n] = scf_state_.eigenvalue(
165 kpoint_state_index, occupied_band_indices[band_n]);
166 }
167 }
168
169 int g_zero_plane_wave_index =
170 planewave_basis_.is_gamma_only()
171 ? find_g_zero_plane_wave_index(plane_wave_indices_kpoint,
172 number_of_plane_waves_kpoint)
173 : -1;
174
175 /**
176 * The D matrix couples projectors of the same atom only, so every
177 * term below is a sum over atoms of an atom's own projectors. The
178 * cell is therefore worked through in blocks of consecutive atoms,
179 * each holding its projectors and their six strain derivatives for
180 * as long as it takes to contract them with the occupied bands. The
181 * whole cell at once is what took the 54-atom silicon cell from
182 * 2.3 GiB through the SCF to over 4 GiB here.
183 *
184 * A block carries an eighth as many projectors as there are occupied
185 * bands, so its seven arrays stay below the occupied wavefunctions,
186 * but never fewer than 64: every contraction copies the wavefunctions
187 * into the BLAS's own buffers, and with a handful of projectors per
188 * call that copy, not the product, is the cost. Measured with
189 * Accelerate on this cell, 18 rows per call took five times as long
190 * per projector as the whole cell in one call, and 378 rows were
191 * within 10% of it.
192 */
193 constexpr int minimum_projectors_per_block = 64;
194 int projectors_per_block =
195 std::max(largest_atom_projector_count,
196 std::max(number_of_occupied_bands / 8, minimum_projectors_per_block));
197 BlockWorkspace workspace;
198 workspace.allocate(projectors_per_block, number_of_plane_waves_kpoint);
199
200 std::array<std::vector<Real>, 6> radial_strain_weights;
201 const std::vector<Real>* kpoint_plus_g_components[3] = {
202 &kpoint_plus_g_x, &kpoint_plus_g_y, &kpoint_plus_g_z};
203 for (int strain_pair = 0; strain_pair < 6; strain_pair++) {
204 const auto& component_a =
205 *kpoint_plus_g_components[bessel_direction_a[strain_pair]];
206 const auto& component_b =
207 *kpoint_plus_g_components[bessel_direction_b[strain_pair]];
208 radial_strain_weights[strain_pair].resize(number_of_plane_waves_kpoint);
209 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
210 radial_strain_weights[strain_pair][g_index] =
211 component_a[g_index] * component_b[g_index] *
212 kpoint_plus_g_norm_inverse[g_index];
213 }
214 }
215
216 int block_first_atom = 0;
217 while (block_first_atom < crystal_structure_.number_of_atoms()) {
218 int block_end_atom = block_first_atom;
219 int block_projectors = 0;
220 while (block_end_atom < crystal_structure_.number_of_atoms()) {
221 int atom_projectors = pseudopotential_basis.number_of_projector_channels(
222 crystal_structure_.species_type_index(block_end_atom));
223 if (block_projectors > 0 &&
224 block_projectors + atom_projectors > projectors_per_block) {
225 break;
226 }
227 block_projectors += atom_projectors;
228 block_end_atom++;
229 }
230 if (block_projectors > 0) {
231 accumulate_block(spin_index,
232 kpoint_coordinates_cartesian,
233 plane_wave_indices_kpoint,
234 number_of_plane_waves_kpoint,
235 geometry,
236 kpoint_plus_g_components,
237 radial_strain_weights,
238 wavefunction_matrix,
239 number_of_occupied_bands,
240 occupied_band_weights,
241 occupied_eigenvalues,
242 g_zero_plane_wave_index,
243 use_effective_d_matrix,
244 block_first_atom,
245 block_end_atom,
246 block_projectors,
247 workspace);
248 }
249 block_first_atom = block_end_atom;
250 }
251 }
252 }
253
254 symmetrize_stress_tensor(result_.nonlocal);
255 }
256
257 private:
258 [[nodiscard]] int find_g_zero_plane_wave_index(const std::vector<int>& plane_wave_indices,
259 int number_of_plane_waves) const {
260 for (int plane_wave_index = 0; plane_wave_index < number_of_plane_waves;
261 plane_wave_index++) {
262 if (planewave_basis_.g_vectors()[plane_wave_indices[plane_wave_index]]
263 .magnitude_squared < math::tolerances::numerical_zero_threshold) {
264 return plane_wave_index;
265 }
266 }
267 return -1;
268 }
269
270 /**
271 * Completes a Gamma half-sphere contraction and leaves G = 0 single-counted.
272 */
273 static void complete_gamma_only_overlap_matrix(int number_of_plane_waves,
274 int number_of_left_functions,
275 int number_of_right_functions,
276 const Complex* left_functions,
277 const Complex* right_functions,
278 int g_zero_plane_wave_index,
279 std::vector<Complex>& overlaps) {
280 for (int left_function_index = 0; left_function_index < number_of_left_functions;
281 left_function_index++) {
282 for (int right_function_index = 0; right_function_index < number_of_right_functions;
283 right_function_index++) {
284 Real g_zero_product =
285 g_zero_plane_wave_index >= 0
286 ? (std::conj(left_functions[static_cast<size_t>(left_function_index) *
287 number_of_plane_waves +
288 g_zero_plane_wave_index]) *
289 right_functions[static_cast<size_t>(right_function_index) *
290 number_of_plane_waves +
291 g_zero_plane_wave_index])
292 .real()
293 : 0.0;
294 size_t overlap_index =
295 static_cast<size_t>(right_function_index) * number_of_left_functions +
296 left_function_index;
297 overlaps[overlap_index] =
298 Complex(2.0 * overlaps[overlap_index].real() - g_zero_product, 0.0);
299 }
300 }
301 }
302
303 /**
304 * Convert k-point from crystal to Cartesian coordinates.
305 */
306 static Vector3D crystal_to_cartesian_kpoint(const Vector3D& kpoint_crystal,
307 const Matrix3x3& reciprocal_lattice_vectors) {
308 Vector3D result;
309 for (int direction = 0; direction < 3; direction++) {
310 result[direction] = kpoint_crystal.x * reciprocal_lattice_vectors[0][direction] +
311 kpoint_crystal.y * reciprocal_lattice_vectors[1][direction] +
312 kpoint_crystal.z * reciprocal_lattice_vectors[2][direction];
313 }
314 return result;
315 }
316
317 /**
318 * Compute k+G vectors and their inverse norms for all plane waves at a k-point.
319 */
320 void compute_kpoint_plus_g_vectors(const Vector3D& kpoint_coordinates_cartesian,
321 const std::vector<int>& plane_wave_indices_kpoint,
322 int number_of_plane_waves_kpoint,
323 std::vector<Real>& kpoint_plus_g_x,
324 std::vector<Real>& kpoint_plus_g_y,
325 std::vector<Real>& kpoint_plus_g_z,
326 std::vector<Real>& kpoint_plus_g_norm_inverse) const {
327 kpoint_plus_g_x.resize(number_of_plane_waves_kpoint);
328 kpoint_plus_g_y.resize(number_of_plane_waves_kpoint);
329 kpoint_plus_g_z.resize(number_of_plane_waves_kpoint);
330 kpoint_plus_g_norm_inverse.resize(number_of_plane_waves_kpoint);
331
332 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
333 const auto& g_vector = planewave_basis_.g_vectors()[plane_wave_indices_kpoint[g_index]];
334 kpoint_plus_g_x[g_index] = (kpoint_coordinates_cartesian.x + g_vector.g_cart.x) *
335 crystal_structure_.two_pi_over_lattice_parameter();
336 kpoint_plus_g_y[g_index] = (kpoint_coordinates_cartesian.y + g_vector.g_cart.y) *
337 crystal_structure_.two_pi_over_lattice_parameter();
338 kpoint_plus_g_z[g_index] = (kpoint_coordinates_cartesian.z + g_vector.g_cart.z) *
339 crystal_structure_.two_pi_over_lattice_parameter();
340 Real kpoint_plus_g_absolute =
341 std::sqrt(kpoint_plus_g_x[g_index] * kpoint_plus_g_x[g_index] +
342 kpoint_plus_g_y[g_index] * kpoint_plus_g_y[g_index] +
343 kpoint_plus_g_z[g_index] * kpoint_plus_g_z[g_index]);
344 kpoint_plus_g_norm_inverse[g_index] =
345 (kpoint_plus_g_absolute > math::tolerances::bessel_argument_zero_threshold)
346 ? 1.0 / kpoint_plus_g_absolute
347 : 0.0;
348 }
349 }
350
351 static constexpr int bessel_direction_a[6] = {0, 0, 0, 1, 1, 2};
352 static constexpr int bessel_direction_b[6] = {0, 1, 2, 1, 2, 2};
353
354 /**
355 * The atom-independent part of the projector geometry at one k-point: k+G in
356 * units of 2π/a and in atomic units, and the real spherical harmonics and
357 * their three Cartesian derivatives at every k+G.
358 */
359 struct KpointPlusGGeometry {
360 std::vector<std::array<Real, 3>> direction_reciprocal;
361 std::vector<Real> magnitude_squared_reciprocal;
362 std::vector<Real> magnitude_bohr;
363 std::vector<std::vector<Real>> spherical_harmonics;
364 std::vector<std::vector<std::vector<Real>>> spherical_harmonic_derivatives;
365 };
366
367 void compute_kpoint_plus_g_geometry(const Vector3D& kpoint_coordinates_cartesian,
368 const std::vector<int>& plane_wave_indices_kpoint,
369 int number_of_plane_waves_kpoint,
370 int number_of_lm_components,
371 KpointPlusGGeometry& geometry) const {
372 geometry.direction_reciprocal.resize(number_of_plane_waves_kpoint);
373 geometry.magnitude_squared_reciprocal.resize(number_of_plane_waves_kpoint);
374 geometry.magnitude_bohr.resize(number_of_plane_waves_kpoint);
375 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
376 const auto& g_vector = planewave_basis_.g_vectors()[plane_wave_indices_kpoint[g_index]];
377 Vector3D kpg;
378 kpg.x = kpoint_coordinates_cartesian.x + g_vector.g_cart.x;
379 kpg.y = kpoint_coordinates_cartesian.y + g_vector.g_cart.y;
380 kpg.z = kpoint_coordinates_cartesian.z + g_vector.g_cart.z;
381 Real kpg_norm_recip = kpg.norm();
382 geometry.direction_reciprocal[g_index] = {kpg.x, kpg.y, kpg.z};
383 geometry.magnitude_squared_reciprocal[g_index] = kpg_norm_recip * kpg_norm_recip;
384 geometry.magnitude_bohr[g_index] =
385 kpg_norm_recip * crystal_structure_.two_pi_over_lattice_parameter();
386 }
387 compute_real_spherical_harmonics_batch(number_of_lm_components,
388 number_of_plane_waves_kpoint,
389 geometry.direction_reciprocal,
390 geometry.magnitude_squared_reciprocal,
391 geometry.spherical_harmonics);
392 geometry.spherical_harmonic_derivatives.assign(3, {});
393 for (int polarization_index = 0; polarization_index < 3; polarization_index++) {
395 number_of_lm_components,
396 number_of_plane_waves_kpoint,
397 geometry.direction_reciprocal,
398 geometry.magnitude_squared_reciprocal,
399 polarization_index,
400 geometry.spherical_harmonic_derivatives[polarization_index]);
401 }
402 }
403
404 /// The projectors and the six strain derivatives of each: seven kinds of column.
405 static constexpr int number_of_projector_kinds = 7;
406
407 /**
408 * One block's projectors and strain derivatives in a single column-major
409 * array, allocated once per k-point for the largest block and reused: a
410 * column of plane waves per projector, the block's projectors first and then
411 * each strain component's derivatives in the same order. Holding the seven
412 * kinds side by side lets one matrix product contract all of them with the
413 * bands.
414 */
415 struct BlockWorkspace {
416 std::vector<Complex> columns;
417
418 void allocate(int maximum_projectors, int number_of_plane_waves) {
419 columns.resize(static_cast<std::size_t>(number_of_projector_kinds) *
420 static_cast<std::size_t>(maximum_projectors) * number_of_plane_waves);
421 }
422 };
423
424 /**
425 * Build the projectors of the atoms [first_atom, end_atom) and their strain
426 * derivatives, contract them with the occupied bands, and add the block's
427 * contribution to the nonlocal stress.
428 *
429 * The strain derivative of <β|ψ> along (a,b) has a radial part,
430 * dβ/dq (k+G)_a (k+G)_b / |k+G| Y_lm, and an angular part,
431 * β ∂Y_lm/∂(k+G)_a (k+G)_b. Both enter the (a,b) component with the same
432 * D-matrix element and band weights, so they are summed into one projector
433 * per component before contracting: six products with the bands rather
434 * than twelve, and no band-sized array to reweight. Rebuilding those
435 * reweighted bands for every block is what made the first blocked version
436 * half again as slow as the whole-cell one.
437 */
438 void accumulate_block(int spin_index,
439 const Vector3D& kpoint_coordinates_cartesian,
440 const std::vector<int>& plane_wave_indices_kpoint,
441 int number_of_plane_waves_kpoint,
442 const KpointPlusGGeometry& geometry,
443 const std::vector<Real>* const kpoint_plus_g_components[3],
444 const std::array<std::vector<Real>, 6>& radial_strain_weights,
445 const std::vector<Complex>& wavefunction_matrix,
446 int number_of_occupied_bands,
447 const std::vector<Real>& occupied_band_weights,
448 const std::vector<Real>& occupied_eigenvalues,
449 int g_zero_plane_wave_index,
450 bool use_effective_d_matrix,
451 int first_atom,
452 int end_atom,
453 int number_of_projectors,
454 BlockWorkspace& workspace) {
455 const auto& pseudopotential_basis = scf_state_.quantum_system().pseudopotential_basis();
456 Real inverse_sqrt_cell_volume = 1.0 / std::sqrt(crystal_structure_.cell_volume());
457 Real inverse_cell_volume = 1.0 / crystal_structure_.cell_volume();
458 Real inverse_two_pi_over_lattice_parameter =
459 1.0 / crystal_structure_.two_pi_over_lattice_parameter();
460 std::size_t plane_waves = static_cast<std::size_t>(number_of_plane_waves_kpoint);
461
462 {
463 std::vector<Complex> structure_factor(number_of_plane_waves_kpoint);
464 std::vector<Real> beta_derivative_at_g(number_of_plane_waves_kpoint);
465 std::vector<Real> beta_at_g(number_of_plane_waves_kpoint);
466 int projector_index = 0;
467 for (int atom_index = first_atom; atom_index < end_atom; atom_index++) {
468 int index_type = crystal_structure_.species_type_index(atom_index);
469 Vector3D atomic_position =
470 crystal_structure_.convert_from_crystal_to_cartesian(atom_index);
471 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
472 const auto& g_vector =
473 planewave_basis_.g_vectors()[plane_wave_indices_kpoint[g_index]];
474 Real phase =
475 constants::two_pi * (g_vector.g_cart.dot(atomic_position) +
476 kpoint_coordinates_cartesian.dot(atomic_position));
477 structure_factor[g_index] = compute_phase_factor(phase);
478 }
479 for (int index_beta = 0;
480 index_beta < pseudopotential_basis.number_of_projectors(index_type);
481 index_beta++) {
482 int angular_momentum =
483 pseudopotential_basis.beta_angular_momentum(index_type, index_beta);
484 Complex prefactor =
485 inverse_sqrt_cell_volume * compute_angular_phase_factor(angular_momentum);
486 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
487 beta_derivative_at_g[g_index] = pseudopotential_basis.interpolate_dbeta(
488 index_type, index_beta, geometry.magnitude_bohr[g_index]);
489 beta_at_g[g_index] = pseudopotential_basis.interpolate_beta(
490 index_type, index_beta, geometry.magnitude_bohr[g_index]);
491 }
492 for (int magnetic_projection : harmonic_order_sequence(angular_momentum)) {
493 int lm_index = harmonic_combined_degree_order_index(angular_momentum,
494 magnetic_projection);
495 Complex* projector =
496 workspace.columns.data() +
497 static_cast<std::size_t>(projector_index) * plane_waves;
498 std::array<Complex*, 6> strain_projector;
499 for (int strain_pair = 0; strain_pair < 6; strain_pair++) {
500 strain_projector[strain_pair] =
501 workspace.columns.data() +
502 static_cast<std::size_t>((strain_pair + 1) * number_of_projectors +
503 projector_index) *
504 plane_waves;
505 }
506 for (int g_index = 0; g_index < number_of_plane_waves_kpoint; g_index++) {
507 Complex phased = structure_factor[g_index] * prefactor;
508 Real spherical_harmonic =
509 geometry.spherical_harmonics[g_index][lm_index];
510 projector[g_index] = beta_at_g[g_index] * spherical_harmonic * phased;
511 Complex radial =
512 beta_derivative_at_g[g_index] * spherical_harmonic * phased;
513 /**
514 * β divided by 2π/a to match the unit convention of the
515 * spherical-harmonic derivatives, which are in (2π/a)⁻¹.
516 */
517 Complex angular_scale =
518 beta_at_g[g_index] * inverse_two_pi_over_lattice_parameter * phased;
519 for (int strain_pair = 0; strain_pair < 6; strain_pair++) {
520 int direction_a = bessel_direction_a[strain_pair];
521 int direction_b = bessel_direction_b[strain_pair];
522 strain_projector[strain_pair][g_index] =
523 radial * radial_strain_weights[strain_pair][g_index] +
524 angular_scale *
525 geometry.spherical_harmonic_derivatives[direction_a]
526 [g_index][lm_index] *
527 (*kpoint_plus_g_components[direction_b])[g_index];
528 }
529 }
530 projector_index++;
531 }
532 }
533 }
534 }
535
536 /**
537 * All seven kinds against the occupied bands in one product:
538 * overlaps(7·Nproj, Nocc) = columns†(7·Nproj, NPW) Ψ(NPW, Nocc). Rows
539 * [0, Nproj) are ⟨β|ψ⟩; rows [(s+1)·Nproj, (s+2)·Nproj) are the
540 * derivatives along strain component s.
541 */
542 Complex alpha_blas(1.0, 0.0);
543 Complex beta_blas(0.0, 0.0);
544 int blas_rows = number_of_projector_kinds * number_of_projectors;
545 int blas_k_pw = number_of_plane_waves_kpoint;
546 int blas_n_bands = number_of_occupied_bands;
547 std::vector<Complex> overlaps(static_cast<std::size_t>(blas_rows) *
548 static_cast<std::size_t>(number_of_occupied_bands),
549 Complex(0.0, 0.0));
550 zgemm_("C",
551 "N",
552 &blas_rows,
553 &blas_n_bands,
554 &blas_k_pw,
555 reinterpret_cast<const std::complex<double>*>(&alpha_blas),
556 reinterpret_cast<const std::complex<double>*>(workspace.columns.data()),
557 &blas_k_pw,
558 reinterpret_cast<const std::complex<double>*>(wavefunction_matrix.data()),
559 &blas_k_pw,
560 reinterpret_cast<const std::complex<double>*>(&beta_blas),
561 reinterpret_cast<std::complex<double>*>(overlaps.data()),
562 &blas_rows);
563 if (planewave_basis_.is_gamma_only()) {
564 complete_gamma_only_overlap_matrix(number_of_plane_waves_kpoint,
565 blas_rows,
566 number_of_occupied_bands,
567 workspace.columns.data(),
568 wavefunction_matrix.data(),
569 g_zero_plane_wave_index,
570 overlaps);
571 }
572
573 // Accumulate this block's atoms: projector pairs within each atom, bands inline.
574 int projector_offset = 0;
575 for (int atom_index = first_atom; atom_index < end_atom; atom_index++) {
576 int species_type_index = crystal_structure_.species_type_index(atom_index);
577 int number_of_betas = pseudopotential_basis.number_of_projectors(species_type_index);
578
579 std::vector<int> projector_beta_indices, projector_magnetic_indices;
580 int projector_start_index = projector_offset;
581 for (int beta_index = 0; beta_index < number_of_betas; beta_index++) {
582 int angular_momentum =
583 pseudopotential_basis.beta_angular_momentum(species_type_index, beta_index);
584 for (int magnetic_projection : harmonic_order_sequence(angular_momentum)) {
585 projector_beta_indices.push_back(beta_index);
586 projector_magnetic_indices.push_back(magnetic_projection);
587 projector_offset++;
588 }
589 }
590 int number_of_projectors_atom = static_cast<int>(projector_beta_indices.size());
591
592 for (int projector_a_local = 0; projector_a_local < number_of_projectors_atom;
593 projector_a_local++) {
594 for (int projector_b_local = 0; projector_b_local < number_of_projectors_atom;
595 projector_b_local++) {
596 int angular_momentum_a = pseudopotential_basis.beta_angular_momentum(
597 species_type_index, projector_beta_indices[projector_a_local]);
598 int angular_momentum_b = pseudopotential_basis.beta_angular_momentum(
599 species_type_index, projector_beta_indices[projector_b_local]);
600
601 // Band-independent D matrix element
602 Real d_element =
603 scf_state_.quantum_system().d_matrix_element(atom_index,
604 species_type_index,
605 projector_a_local,
606 projector_b_local,
607 spin_index);
608
609 /**
610 * Augmentation overlap for US, gated per species as well as on
611 * `use_effective_d_matrix`: that flag is `has_ultrasoft()`, a
612 * property of the whole system, true in a mixed cell while
613 * individual species are still norm-conserving. The forces twin in
614 * `forces/.../nonlocal.hpp` carries this same per-species gate.
615 */
616 Real overlap_integral = 0.0;
617 if (use_effective_d_matrix &&
618 pseudopotential_basis.pseudopotential_set()[species_type_index]
619 .has_augmentation_charges() &&
620 angular_momentum_a == angular_momentum_b &&
621 projector_magnetic_indices[projector_a_local] ==
622 projector_magnetic_indices[projector_b_local]) {
623 overlap_integral =
624 static_cast<const UltrasoftPlanewaveKohnShamQuantumSystem&>(
625 scf_state_.quantum_system())
626 .ultrasoft_basis()
627 .augmentation_overlap_integral(
628 species_type_index,
629 projector_beta_indices[projector_a_local],
630 projector_beta_indices[projector_b_local]);
631 }
632
633 if (std::abs(d_element) < math::tolerances::d_matrix_element_threshold &&
634 std::abs(overlap_integral) < math::tolerances::d_matrix_element_threshold)
635 continue;
636
637 int global_a = projector_start_index + projector_a_local;
638 int global_b = projector_start_index + projector_b_local;
639
640 Real diagonal_sum = 0.0;
641 Real eigenvalue_diagonal_sum = 0.0;
642 Real strain_sums[6] = {};
643 Real eigenvalue_strain_sums[6] = {};
644 bool has_augmentation_term =
645 use_effective_d_matrix &&
646 std::abs(overlap_integral) > math::tolerances::d_matrix_element_threshold;
647
648 for (int band_n = 0; band_n < number_of_occupied_bands; band_n++) {
649 Real weight = occupied_band_weights[band_n];
650 std::size_t band_offset = static_cast<std::size_t>(band_n) * blas_rows;
651 Complex becp_a_conjugate = std::conj(overlaps[band_offset + global_a]);
652
653 Complex becp_b_value = overlaps[band_offset + global_b];
654 Real diagonal_term = weight * (becp_a_conjugate * becp_b_value).real();
655 diagonal_sum += diagonal_term;
656
657 Real eigenvalue =
658 has_augmentation_term ? occupied_eigenvalues[band_n] : 0.0;
659 eigenvalue_diagonal_sum += diagonal_term * eigenvalue;
660 for (int strain_pair = 0; strain_pair < 6; strain_pair++) {
661 Real strain_term =
662 weight * 2.0 *
663 (becp_a_conjugate *
664 overlaps[band_offset +
665 static_cast<std::size_t>((strain_pair + 1) *
666 number_of_projectors) +
667 global_b])
668 .real();
669 strain_sums[strain_pair] += strain_term;
670 eigenvalue_strain_sums[strain_pair] += strain_term * eigenvalue;
671 }
672 }
673
674 for (int direction = 0; direction < 3; direction++) {
675 result_.nonlocal[direction][direction] +=
676 d_element * diagonal_sum * inverse_cell_volume;
677 }
678 for (int strain_pair = 0; strain_pair < 6; strain_pair++) {
679 int direction_a = bessel_direction_a[strain_pair];
680 int direction_b = bessel_direction_b[strain_pair];
681 result_.nonlocal[direction_a][direction_b] +=
682 d_element * strain_sums[strain_pair] * inverse_cell_volume;
683 }
684
685 if (has_augmentation_term) {
686 for (int direction = 0; direction < 3; direction++) {
687 result_.nonlocal[direction][direction] -=
688 overlap_integral * eigenvalue_diagonal_sum * inverse_cell_volume;
689 }
690 for (int strain_pair = 0; strain_pair < 6; strain_pair++) {
691 int direction_a = bessel_direction_a[strain_pair];
692 int direction_b = bessel_direction_b[strain_pair];
693 result_.nonlocal[direction_a][direction_b] -=
694 overlap_integral * eigenvalue_strain_sums[strain_pair] *
695 inverse_cell_volume;
696 }
697 }
698 }
699 }
700 }
701 }
702
703 /// Symmetrize the upper triangle of a 3x3 stress tensor.
704 static void symmetrize_stress_tensor(Real stress[3][3]) {
705 for (int direction_a = 0; direction_a < 3; direction_a++) {
706 for (int direction_b = direction_a + 1; direction_b < 3; direction_b++) {
707 stress[direction_b][direction_a] = stress[direction_a][direction_b];
708 }
709 }
710 }
711
712 const Crystal& crystal_structure_;
713 const PlanewaveBasis& planewave_basis_;
714 const KPointsGrid& kpoint_grid_;
717 const std::string derivative_method_;
718};
719
720} // namespace q3
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
int number_of_kpoints() const
NonlocalPseudopotentialStressTensorCalculator(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const KPointsGrid &kpoint_grid, const NormConservingPlanewaveKohnShamSCFState &scf_state, StressTensorContributions &result, const std::string &derivative_method="analytical")
Definition nonlocal.hpp:39
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
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 Real two_pi
Definition constants.hpp:25
Complex phase(const Vector3D &kpoint_crystal, const IntegerVector3D &translation, Real sign)
e^{sign * 2 pi i k.R} for crystal-coordinate k and integer R.
constexpr double numerical_zero_threshold
constexpr double d_matrix_element_threshold
constexpr double occupation_threshold
constexpr double bessel_argument_zero_threshold
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
void compute_spherical_harmonic_derivatives_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, int polarization_index, std::vector< std::vector< Real > > &spherical_harmonic_derivatives)
double Real
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
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]
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
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
Per-contribution stress tensor breakdown (kbar after conversion).
Real norm() const
Definition vector_3d.hpp:68
Real dot(const Vector3D &other) const
Definition vector_3d.hpp:59