Loading...
Searching...
No Matches
local.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Local pseudopotential stress tensor contribution.
5 *
6 * Computes the stress from the local (ionic) pseudopotential in reciprocal
7 * space. This includes both the diagonal (energy) part and the off-diagonal
8 * (derivative of V_loc with respect to strain) part.
9 *
10 * The algorithm is the same for norm-conserving and ultrasoft pseudopotential_set.
11 */
12
13#include <cmath>
14#include <complex>
15#include <vector>
16
25
26namespace q3 {
27
29 public:
31 const Crystal& crystal_structure,
32 const PlanewaveBasis& planewave_basis,
33 const NormConservingPlanewaveBasis& pseudopotential_basis,
36 : crystal_structure_(crystal_structure)
37 , planewave_basis_(planewave_basis)
38 , pseudopotential_basis_(pseudopotential_basis)
39 , scf_state_(scf_state)
40 , result_(result) {}
41
42 void compute() {
43 const auto& charge_density_g = scf_state_.charge_density_g;
44 Real reciprocal_space_multiplicity = planewave_basis_.is_gamma_only() ? 2.0 : 1.0;
45 Real evloc = 0.0;
46 {
47 int local_gstart = planewave_basis_.first_nonzero_g_index();
48 if (local_gstart == 1) {
49 for (int species_index = 0;
50 species_index <
51 static_cast<int>(pseudopotential_basis_.pseudopotential_set().size());
52 species_index++) {
53 Complex structure_factor_zero(0.0, 0.0);
54 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms();
55 atom_index++) {
56 if (crystal_structure_.species_type_index(atom_index) != species_index)
57 continue;
58 structure_factor_zero += Complex(1.0, 0.0);
59 }
60 evloc += (std::conj(charge_density_g[0]) *
61 pseudopotential_basis_.local_potential_g(species_index)[0] *
62 structure_factor_zero)
63 .real();
64 }
65 }
66 for (int g_vector_index = local_gstart;
67 g_vector_index < planewave_basis_.number_of_g_vectors();
68 g_vector_index++) {
69 const auto& g_vector = planewave_basis_.g_vectors()[g_vector_index];
70 Complex local_potential_total(0.0, 0.0);
71 for (int species_index = 0;
72 species_index <
73 static_cast<int>(pseudopotential_basis_.pseudopotential_set().size());
74 species_index++) {
75 Complex structure_factor(0.0, 0.0);
76 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms();
77 atom_index++) {
78 if (crystal_structure_.species_type_index(atom_index) != species_index)
79 continue;
80 Vector3D atomic_position =
81 crystal_structure_.convert_from_crystal_to_cartesian(atom_index);
82 Real phase = constants::two_pi * g_vector.g_cart.dot(atomic_position);
83 structure_factor += compute_phase_factor(phase);
84 }
85 local_potential_total +=
86 pseudopotential_basis_.local_potential_g(species_index)[g_vector_index] *
87 structure_factor;
88 }
89 evloc +=
90 reciprocal_space_multiplicity *
91 (std::conj(charge_density_g[g_vector_index]) * local_potential_total).real();
92 }
93 }
94 for (int direction_a = 0; direction_a < 3; direction_a++) {
95 result_.local[direction_a][direction_a] += evloc;
96 }
97
98 Real stress_xx = 0, stress_yx = 0, stress_yy = 0, stress_zx = 0, stress_zy = 0,
99 stress_zz = 0;
100 for (int g_vector_index = 1; g_vector_index < planewave_basis_.number_of_g_vectors();
101 g_vector_index++) {
102 const auto& g_vector = planewave_basis_.g_vectors()[g_vector_index];
103 Real g_vector_squared = g_vector.magnitude_squared;
104 if (g_vector_squared < math::tolerances::numerical_zero_threshold)
105 continue;
106 Real g_vector_magnitude =
107 std::sqrt(g_vector_squared) * crystal_structure_.two_pi_over_lattice_parameter();
108
109 Complex derivative_local_potential_times_structure_factor(0.0, 0.0);
110 for (int species_index = 0;
111 species_index <
112 static_cast<int>(pseudopotential_basis_.pseudopotential_set().size());
113 species_index++) {
114 /**
115 * Use analytical derivative from pre-tabulated V_loc(q) instead of
116 * finite-difference with two expensive radial integrals per species.
117 */
118 Real derivative_local_potential_d_q =
119 pseudopotential_basis_.interpolate_local_potential_derivative(
120 species_index, g_vector_magnitude);
121 Real derivative_local_potential_d_g_squared =
122 derivative_local_potential_d_q / (2.0 * g_vector_magnitude);
123
124 Complex structure_factor(0.0, 0.0);
125 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms();
126 atom_index++) {
127 if (crystal_structure_.species_type_index(atom_index) != species_index)
128 continue;
129 Vector3D atomic_position =
130 crystal_structure_.convert_from_crystal_to_cartesian(atom_index);
131 Real phase = constants::two_pi * g_vector.g_cart.dot(atomic_position);
132 structure_factor += compute_phase_factor(phase);
133 }
134 derivative_local_potential_times_structure_factor +=
135 derivative_local_potential_d_g_squared * structure_factor;
136 }
137
138 Real stress_partial_contribution = reciprocal_space_multiplicity * 2.0 *
139 (std::conj(charge_density_g[g_vector_index]) *
140 derivative_local_potential_times_structure_factor)
141 .real();
142 stress_xx += stress_partial_contribution * g_vector.g_cart.x * g_vector.g_cart.x;
143 stress_yx += stress_partial_contribution * g_vector.g_cart.y * g_vector.g_cart.x;
144 stress_yy += stress_partial_contribution * g_vector.g_cart.y * g_vector.g_cart.y;
145 stress_zx += stress_partial_contribution * g_vector.g_cart.z * g_vector.g_cart.x;
146 stress_zy += stress_partial_contribution * g_vector.g_cart.z * g_vector.g_cart.y;
147 stress_zz += stress_partial_contribution * g_vector.g_cart.z * g_vector.g_cart.z;
148 }
149 result_.local[0][0] +=
150 stress_xx * crystal_structure_.two_pi_over_lattice_parameter_squared();
151 result_.local[1][0] +=
152 stress_yx * crystal_structure_.two_pi_over_lattice_parameter_squared();
153 result_.local[1][1] +=
154 stress_yy * crystal_structure_.two_pi_over_lattice_parameter_squared();
155 result_.local[2][0] +=
156 stress_zx * crystal_structure_.two_pi_over_lattice_parameter_squared();
157 result_.local[2][1] +=
158 stress_zy * crystal_structure_.two_pi_over_lattice_parameter_squared();
159 result_.local[2][2] +=
160 stress_zz * crystal_structure_.two_pi_over_lattice_parameter_squared();
161
162 result_.local[0][1] = result_.local[1][0];
163 result_.local[0][2] = result_.local[2][0];
164 result_.local[1][2] = result_.local[2][1];
165 }
166
167 private:
168 const Crystal& crystal_structure_;
169 const PlanewaveBasis& planewave_basis_;
170 const NormConservingPlanewaveBasis& pseudopotential_basis_;
173};
174
175} // namespace q3
Vector3D convert_from_crystal_to_cartesian(int atom_index) const
Definition crystal.hpp:225
Real two_pi_over_lattice_parameter_squared() const
Definition crystal.hpp:199
Real two_pi_over_lattice_parameter() const
Definition crystal.hpp:196
int species_type_index(int atom_index) const
Definition crystal.hpp:273
int number_of_atoms() const
Definition crystal.hpp:209
LocalPseudopotentialStressTensorCalculator(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const NormConservingPlanewaveBasis &pseudopotential_basis, const NormConservingPlanewaveKohnShamSCFState &scf_state, StressTensorContributions &result)
Definition local.hpp:30
const std::vector< Real > & local_potential_g(int species_type_index) const
Definition init.hpp:165
Real interpolate_local_potential_derivative(int element_type, Real q_value) const
Definition init.hpp:323
const PseudopotentialSet & pseudopotential_set() const
Definition init.hpp:156
int number_of_g_vectors() const
Definition init.hpp:145
const std::vector< GVector > & g_vectors() const
Definition init.hpp:148
int first_nonzero_g_index() const
Definition init.hpp:151
bool is_gamma_only() const
Definition init.hpp:157
constexpr Real two_pi
Definition constants.hpp:25
constexpr double numerical_zero_threshold
double Real
Complex compute_phase_factor(Real phase)
Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
std::complex< double > Complex
std::vector< Complex > charge_density_g
Definition init.hpp:36
Per-contribution stress tensor breakdown (kbar after conversion).