Loading...
Searching...
No Matches
ewald.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Ewald stress tensor contribution.
5 *
6 * Computes the Ewald (ion-ion electrostatic) contribution to the stress tensor
7 * using the Ewald summation technique in both G-space and R-space.
8 *
9 * Differentiates the Ewald summation of P. P. Ewald, Ann. Phys. 369, 253
10 * (1921), with respect to strain, in the stress formalism of O. H. Nielsen &
11 * R. M. Martin, Phys. Rev. B 32, 3780 (1985).
12 *
13 * This is a purely planewave contribution, independent of pseudopotential type.
14 */
15
16#include <cmath>
17
24
25namespace q3 {
26
28 public:
29 EwaldStressTensorCalculator(const Crystal& crystal_structure,
30 const PlanewaveBasis& planewave_basis,
32 : crystal_structure_(crystal_structure)
33 , planewave_basis_(planewave_basis)
34 , result_(result) {}
35
36 void compute() {
37 Real lattice_parameter = crystal_structure_.lattice_parameter();
38 Real gcutm = planewave_basis_.charge_density_energy_cutoff() /
39 crystal_structure_.two_pi_over_lattice_parameter_squared();
40 Real total_charge = 0.0;
41 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms(); atom_index++) {
42 total_charge += crystal_structure_.valence_charge(
43 crystal_structure_.species_type_index(atom_index));
44 }
45
46 Real alpha_ewald = 2.9;
47 while (alpha_ewald > 0.1) {
48 alpha_ewald -= 0.1;
49 Real upperbound =
50 constants::electron_charge_squared_rydberg * total_charge * total_charge *
51 std::sqrt(2.0 * alpha_ewald / constants::pi) *
52 std::erfc(std::sqrt(crystal_structure_.two_pi_over_lattice_parameter_squared() *
53 gcutm / (4.0 * alpha_ewald)));
55 break;
56 }
57
58 int g_vector_start = planewave_basis_.first_nonzero_g_index();
59 Real reciprocal_space_multiplicity = planewave_basis_.is_gamma_only() ? 2.0 : 1.0;
60
61 Real stress_diagonal_contribution = 0.0;
62 if (g_vector_start == 1) {
63 stress_diagonal_contribution =
65 (4.0 * alpha_ewald) * (total_charge / crystal_structure_.cell_volume()) *
66 (total_charge / crystal_structure_.cell_volume());
67 }
68
69 Real stress_xx = 0, stress_yx = 0, stress_yy = 0, stress_zx = 0, stress_zy = 0,
70 stress_zz = 0;
71 for (int g_vector_index = g_vector_start;
72 g_vector_index < planewave_basis_.number_of_g_vectors();
73 g_vector_index++) {
74 const auto& g_vector = planewave_basis_.g_vectors()[g_vector_index];
75 Real g_vector_squared = g_vector.magnitude_squared *
76 crystal_structure_.two_pi_over_lattice_parameter_squared();
77 if (g_vector_squared < math::tolerances::numerical_zero_threshold)
78 continue;
79 Real g_squared_over_four_alpha = g_vector_squared / (4.0 * alpha_ewald);
80
81 Complex structure_factor(0.0, 0.0);
82 for (int atom_index = 0; atom_index < crystal_structure_.number_of_atoms();
83 atom_index++) {
84 int species_index = crystal_structure_.species_type_index(atom_index);
85 Real valence_charge = crystal_structure_.valence_charge(species_index);
86 Vector3D atomic_position =
87 crystal_structure_.convert_from_crystal_to_cartesian(atom_index);
88 Real phase = constants::two_pi * g_vector.g_cart.dot(atomic_position);
89 structure_factor += valence_charge * compute_phase_factor_conjugate(phase);
90 }
91 structure_factor /= Complex(crystal_structure_.cell_volume(), 0.0);
92
93 Real ewald_stress_term = reciprocal_space_multiplicity * constants::two_pi *
95 std::exp(-g_squared_over_four_alpha) / g_vector_squared *
96 std::norm(structure_factor);
97 stress_diagonal_contribution -= ewald_stress_term;
98
99 Real factor = ewald_stress_term *
100 crystal_structure_.two_pi_over_lattice_parameter_squared() * 2.0 *
101 (g_squared_over_four_alpha + 1.0) / g_vector_squared;
102 stress_xx += factor * g_vector.g_cart.x * g_vector.g_cart.x;
103 stress_yx += factor * g_vector.g_cart.y * g_vector.g_cart.x;
104 stress_yy += factor * g_vector.g_cart.y * g_vector.g_cart.y;
105 stress_zx += factor * g_vector.g_cart.z * g_vector.g_cart.x;
106 stress_zy += factor * g_vector.g_cart.z * g_vector.g_cart.y;
107 stress_zz += factor * g_vector.g_cart.z * g_vector.g_cart.z;
108 }
109
110 result_.ewald[0][0] = stress_diagonal_contribution + stress_xx;
111 result_.ewald[0][1] = stress_yx;
112 result_.ewald[1][1] = stress_diagonal_contribution + stress_yy;
113 result_.ewald[0][2] = stress_zx;
114 result_.ewald[1][2] = stress_zy;
115 result_.ewald[2][2] = stress_diagonal_contribution + stress_zz;
116
117 const Matrix3x3& lattice_vectors = crystal_structure_.lattice_vectors();
118 Real rmax_bohr = 4.0 / std::sqrt(alpha_ewald);
119 Real rmax_alat = rmax_bohr / lattice_parameter;
120 Real volume_alat =
121 std::abs(lattice_vectors[0].dot(lattice_vectors[1].cross(lattice_vectors[2])));
122
123 int limit_x = std::max(
124 1,
125 static_cast<int>(std::ceil(
126 rmax_alat * lattice_vectors[1].cross(lattice_vectors[2]).norm() / volume_alat)));
127 int limit_y = std::max(
128 1,
129 static_cast<int>(std::ceil(
130 rmax_alat * lattice_vectors[2].cross(lattice_vectors[0]).norm() / volume_alat)));
131 int limit_z = std::max(
132 1,
133 static_cast<int>(std::ceil(
134 rmax_alat * lattice_vectors[0].cross(lattice_vectors[1]).norm() / volume_alat)));
135
136 for (int atom_index_a = 0; atom_index_a < crystal_structure_.number_of_atoms();
137 atom_index_a++) {
138 for (int atom_index_b = 0; atom_index_b < crystal_structure_.number_of_atoms();
139 atom_index_b++) {
140 /**
141 * The image loop is sized for a separation in the home cell, and positions
142 * outside [0, 1) are legal input; see Crystal::home_cell_separation().
143 */
144 Vector3D atom_displacement_cartesian =
145 crystal_structure_.home_cell_separation(atom_index_a, atom_index_b);
146
147 for (int lattice_index_x = -limit_x; lattice_index_x <= limit_x;
148 lattice_index_x++) {
149 for (int lattice_index_y = -limit_y; lattice_index_y <= limit_y;
150 lattice_index_y++) {
151 for (int lattice_index_z = -limit_z; lattice_index_z <= limit_z;
152 lattice_index_z++) {
153 if (atom_index_a == atom_index_b && lattice_index_x == 0 &&
154 lattice_index_y == 0 && lattice_index_z == 0)
155 continue;
156 Vector3D displacement_vector;
157 for (int direction = 0; direction < 3; direction++) {
158 displacement_vector[direction] =
159 atom_displacement_cartesian[direction] +
160 (lattice_index_x * lattice_vectors[0][direction] +
161 lattice_index_y * lattice_vectors[1][direction] +
162 lattice_index_z * lattice_vectors[2][direction]);
163 }
164 Real displacement_magnitude =
165 displacement_vector.norm() * lattice_parameter;
166 if (displacement_magnitude <
168 continue;
169 if (displacement_magnitude > rmax_bohr)
170 continue;
171
172 Real valence_charge_a = crystal_structure_.valence_charge(
173 crystal_structure_.species_type_index(atom_index_a));
174 Real valence_charge_b = crystal_structure_.valence_charge(
175 crystal_structure_.species_type_index(atom_index_b));
176 Real ewald_factor =
178 (2.0 * crystal_structure_.cell_volume()) * lattice_parameter *
179 lattice_parameter * valence_charge_a * valence_charge_b /
180 (displacement_magnitude * displacement_magnitude *
181 displacement_magnitude) *
182 (std::erfc(std::sqrt(alpha_ewald) * displacement_magnitude) +
183 displacement_magnitude *
184 std::sqrt(8.0 * alpha_ewald / constants::two_pi) *
185 std::exp(-alpha_ewald * displacement_magnitude *
186 displacement_magnitude));
187
188 for (int direction_a = 0; direction_a < 3; direction_a++) {
189 for (int direction_b = direction_a; direction_b < 3;
190 direction_b++) {
191 result_.ewald[direction_a][direction_b] +=
192 ewald_factor * displacement_vector[direction_a] *
193 displacement_vector[direction_b];
194 }
195 }
196 }
197 }
198 }
199 }
200 }
201
202 for (int direction_a = 0; direction_a < 3; direction_a++) {
203 for (int direction_b = direction_a + 1; direction_b < 3; direction_b++) {
204 result_.ewald[direction_b][direction_a] = result_.ewald[direction_a][direction_b];
205 }
206 }
207
208 for (int direction_a = 0; direction_a < 3; direction_a++) {
209 for (int direction_b = 0; direction_b < 3; direction_b++) {
210 result_.ewald[direction_a][direction_b] = -result_.ewald[direction_a][direction_b];
211 }
212 }
213 }
214
215 private:
216 const Crystal& crystal_structure_;
217 const PlanewaveBasis& planewave_basis_;
219};
220
221} // 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 valence_charge(int species_index) const
Definition crystal.hpp:317
Real cell_volume() const
Definition crystal.hpp:193
const Matrix3x3 & lattice_vectors() const
Definition crystal.hpp:202
int species_type_index(int atom_index) const
Definition crystal.hpp:273
Vector3D home_cell_separation(int atom_index_a, int atom_index_b) const
Definition crystal.hpp:253
int number_of_atoms() const
Definition crystal.hpp:209
Real lattice_parameter() const
Definition crystal.hpp:180
EwaldStressTensorCalculator(const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, StressTensorContributions &result)
Definition ewald.hpp:29
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
Real charge_density_energy_cutoff() const
Definition init.hpp:345
bool is_gamma_only() const
Definition init.hpp:157
constexpr Real electron_charge_squared_rydberg
Definition constants.hpp:32
constexpr Real pi
Definition constants.hpp:24
constexpr Real two_pi
Definition constants.hpp:25
constexpr double numerical_zero_threshold
constexpr double ewald_convergence_threshold
constexpr double zero_vector_magnitude_threshold
double Real
std::complex< double > Complex
Complex compute_phase_factor_conjugate(Real phase)
Compute e^{+iφ} = cos(φ) + i·sin(φ) (conjugate structure factor phase).
3×3 matrix stored row-major: row i = mat[i]
Per-contribution stress tensor breakdown (kbar after conversion).
Real norm() const
Definition vector_3d.hpp:68