Loading...
Searching...
No Matches
kinetic.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Kinetic stress tensor contribution.
5 *
6 * Computes the kinetic-energy contribution to the stress tensor:
7 * σ_αβ^kin = (1/Ω) Σ_{nk} f_{nk} Σ_G (k+G)_α (k+G)_β |c_{nk}(G)|²
8 *
9 * This is a purely planewave contribution, independent of pseudopotential type.
10 */
11
12#include <cmath>
13
21
22namespace q3 {
23
25 public:
26 KineticStressTensorCalculator(const Crystal& crystal_structure,
27 const PlanewaveBasis& planewave_basis,
28 const KPointsGrid& kpoint_grid,
31 : crystal_structure_(crystal_structure)
32 , planewave_basis_(planewave_basis)
33 , kpoint_grid_(kpoint_grid)
34 , scf_state_(scf_state)
35 , result_(result) {}
36
37 void compute() {
38 const Matrix3x3& reciprocal_lattice_vectors =
39 crystal_structure_.reciprocal_lattice_vectors();
40 int number_of_kpoints = kpoint_grid_.number_of_kpoints();
41 Real reciprocal_space_multiplicity = planewave_basis_.is_gamma_only() ? 2.0 : 1.0;
42
43 /**
44 * kpoint_state_index runs over kpoint_grid_.number_of_kpoints() * number_of_spin_components
45 * "quantum states" (spin-major: spin_index * number_of_kpoints + kpoint_index — matching
46 * PlanewaveKohnShamQuantumSystem's storage convention). Summing only kpoint_index without
47 * the spin_index loop silently drops the second spin channel for LSDA (nspin=2), exactly
48 * halving this contribution — kpoint_grid_[kpoint_index] itself is NOT spin-doubled (same
49 * physical k-point, same already spin-halved weight, for both channels).
50 */
51 for (int spin_index = 0; spin_index < scf_state_.number_of_spin_components();
52 spin_index++) {
53 for (int kpoint_index = 0; kpoint_index < number_of_kpoints; kpoint_index++) {
54 int kpoint_state_index = spin_index * number_of_kpoints + kpoint_index;
55 /**
56 * number_of_plane_waves/g_vector_indices_at_kpoint are PlanewaveBasis-derived
57 * (the G-vector set for a k-point does not depend on spin) — physical
58 * kpoint_index. occupation/wavefunction_coefficient are QuantumSystem-derived
59 * (per spin channel) — spin-doubled kpoint_state_index.
60 */
61 int number_of_plane_waves = scf_state_.number_of_plane_waves(kpoint_index);
62 const auto& plane_wave_indices_kpoint =
63 scf_state_.g_vector_indices_at_kpoint(kpoint_index);
64 Vector3D kpoint_coordinates_crystal = kpoint_grid_[kpoint_index].coordinates;
65 Real kpoint_weight = kpoint_grid_[kpoint_index].weight;
66 int number_of_bands = scf_state_.number_of_bands();
67
68 Vector3D kpoint_coordinates_cartesian;
69 for (int direction = 0; direction < 3; direction++) {
70 kpoint_coordinates_cartesian[direction] =
71 kpoint_coordinates_crystal.x * reciprocal_lattice_vectors[0][direction] +
72 kpoint_coordinates_crystal.y * reciprocal_lattice_vectors[1][direction] +
73 kpoint_coordinates_crystal.z * reciprocal_lattice_vectors[2][direction];
74 }
75
76 for (int band_index = 0; band_index < number_of_bands; band_index++) {
77 Real occupation = scf_state_.occupation(kpoint_state_index, band_index);
79 continue;
80
81 for (int g_vector_index = 0; g_vector_index < number_of_plane_waves;
82 g_vector_index++) {
83 const auto& g_vector =
84 planewave_basis_.g_vectors()[plane_wave_indices_kpoint[g_vector_index]];
85 Vector3D kpoint_plus_g_vector;
86 kpoint_plus_g_vector.x =
87 (kpoint_coordinates_cartesian.x + g_vector.g_cart.x) *
88 crystal_structure_.two_pi_over_lattice_parameter();
89 kpoint_plus_g_vector.y =
90 (kpoint_coordinates_cartesian.y + g_vector.g_cart.y) *
91 crystal_structure_.two_pi_over_lattice_parameter();
92 kpoint_plus_g_vector.z =
93 (kpoint_coordinates_cartesian.z + g_vector.g_cart.z) *
94 crystal_structure_.two_pi_over_lattice_parameter();
95
96 Real wavefunction_coefficient_squared =
97 std::norm(scf_state_.wavefunction_coefficient(
98 kpoint_state_index, band_index, g_vector_index));
99
100 for (int direction_a = 0; direction_a < 3; direction_a++) {
101 for (int direction_b = direction_a; direction_b < 3; direction_b++) {
102 result_.kinetic[direction_a][direction_b] +=
103 reciprocal_space_multiplicity * kpoint_weight * occupation *
105 kpoint_plus_g_vector[direction_a] *
106 kpoint_plus_g_vector[direction_b] *
107 wavefunction_coefficient_squared /
108 crystal_structure_.cell_volume();
109 }
110 }
111 }
112 }
113 }
114 }
115 for (int direction_a = 0; direction_a < 3; direction_a++) {
116 for (int direction_b = direction_a + 1; direction_b < 3; direction_b++) {
117 result_.kinetic[direction_b][direction_a] =
118 result_.kinetic[direction_a][direction_b];
119 }
120 }
121 }
122
123 private:
124 const Crystal& crystal_structure_;
125 const PlanewaveBasis& planewave_basis_;
126 const KPointsGrid& kpoint_grid_;
129};
130
131} // namespace q3
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 number_of_kpoints() const
KineticStressTensorCalculator(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const KPointsGrid &kpoint_grid, const NormConservingPlanewaveKohnShamSCFState &scf_state, StressTensorContributions &result)
Definition kinetic.hpp:26
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
bool is_gamma_only() const
Definition init.hpp:157
constexpr Real electron_charge_squared_rydberg
Definition constants.hpp:32
constexpr double occupation_threshold
double Real
3×3 matrix stored row-major: row i = mat[i]
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
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).