195 Real two_pi_over_lattice_parameter_squared =
198 auto [convergence_alpha, convergence_beta] =
210 Real total_valence_charge = 0.0;
211 Vector3D ionic_center_alat{0.0, 0.0, 0.0};
212 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
213 Real valence_charge =
215 total_valence_charge += valence_charge;
219 ionic_center_alat = ionic_center_alat / total_valence_charge;
230 Real shortest_lattice_vector_length_alat = std::min(
231 {lattice_vectors[0].norm(), lattice_vectors[1].norm(), lattice_vectors[2].norm()});
232 Real locality_radius_in_bohr = 0.5 * shortest_lattice_vector_length_alat * alat;
246 std::vector<Real> kernel_real_space(local_grid_points);
247 std::vector<Real> total_charge_density_real(local_grid_points, 0.0);
248 Real charge_density_magnitude_within_locality_radius = 0.0;
249 Real charge_density_magnitude_total = 0.0;
251 for (
int local_flat_index = 0; local_flat_index < local_grid_points; local_flat_index++) {
253 int index_3 = global_flat_index % grid_dimension_3;
254 int index_2 = (global_flat_index / grid_dimension_3) % grid_dimension_2;
255 int index_1 = global_flat_index / (grid_dimension_3 * grid_dimension_2);
256 Vector3D fractional_grid_point{
static_cast<Real>(index_1) / grid_dimension_1,
257 static_cast<Real>(index_2) / grid_dimension_2,
258 static_cast<Real>(index_3) / grid_dimension_3};
259 Vector3D grid_point_alat = lattice_vectors_transpose * fractional_grid_point;
261 Real distance_from_origin_in_bohr = minimum_image_distance(grid_point_alat,
263 lattice_vectors_transpose,
264 lattice_inverse_transpose) *
266 kernel_real_space[local_flat_index] = evaluate_smoothed_coulomb_kernel_real_space(
267 distance_from_origin_in_bohr, convergence_alpha);
269 Real total_density_at_point = 0.0;
270 for (
int spin_index = 0; spin_index < number_of_spin_components; spin_index++) {
271 total_density_at_point +=
274 total_charge_density_real[local_flat_index] = total_density_at_point;
276 Real distance_from_center_in_bohr = minimum_image_distance(grid_point_alat,
278 lattice_vectors_transpose,
279 lattice_inverse_transpose) *
281 Real density_magnitude = std::abs(total_density_at_point);
282 charge_density_magnitude_total += density_magnitude;
283 if (distance_from_center_in_bohr <= locality_radius_in_bohr) {
284 charge_density_magnitude_within_locality_radius += density_magnitude;
288 std::vector<Real> locality_magnitudes = {
289 charge_density_magnitude_within_locality_radius,
290 charge_density_magnitude_total,
295 charge_density_magnitude_within_locality_radius = locality_magnitudes[0];
296 charge_density_magnitude_total = locality_magnitudes[1];
298 if (charge_density_magnitude_total > 0.0) {
299 Real localized_fraction =
300 charge_density_magnitude_within_locality_radius / charge_density_magnitude_total;
301 if (localized_fraction < locality_warning_fraction_threshold) {
303 "MartynaTuckermanCorrectionCalculator: only {:.1f}% of |charge density| lies "
304 "within the cheap Wigner-Seitz-proxy locality radius ({:.4f} bohr, half the "
305 "shortest lattice vector, centered on the ionic center) -- the "
306 "Martyna-Tuckerman post-SCF correction assumes the density is well localized "
307 "inside the cell (see this class's docstring); this system may violate that "
308 "assumption and the reported correction may not be accurate.",
309 localized_fraction * 100.0,
310 locality_radius_in_bohr);
318 std::vector<Complex> kernel_g_space;
319 planewave_basis.
rho_r_to_g(kernel_real_space, kernel_g_space);
320 std::vector<Complex> electron_density_g_space;
321 planewave_basis.
rho_r_to_g(total_charge_density_real, electron_density_g_space);
327 Real energy_sum = 0.0;
328 for (
int g_index = 0; g_index < number_of_g_vectors; g_index++) {
330 Real g_squared_in_inverse_bohr_squared =
333 Complex ionic_structure_factor(0.0, 0.0);
334 for (
int atom_index = 0; atom_index < number_of_atoms; atom_index++) {
342 Complex ionic_charge_density_g = ionic_structure_factor / cell_volume;
344 Real reciprocal_space_kernel = evaluate_smoothed_coulomb_kernel_reciprocal_space(
345 g_squared_in_inverse_bohr_squared, convergence_alpha, convergence_beta);
346 Real gaussian_filter =
347 std::exp(-g_squared_in_inverse_bohr_squared * convergence_beta / 4.0);
348 Real kernel_difference =
349 (cell_volume * kernel_g_space[g_index].real() - reciprocal_space_kernel) *
350 gaussian_filter * gaussian_filter;
352 Complex total_charge_density_g =
353 ionic_charge_density_g - electron_density_g_space[g_index];
362 Real gamma_factor = (is_gamma_only && g_index >= first_nonzero_g_index) ? 2.0 : 1.0;
363 energy_sum += std::norm(total_charge_density_g) * kernel_difference * gamma_factor;
366 Real martyna_tuckerman_energy_in_ry =
372 result.
terms_in_ry = {{
"energy", martyna_tuckerman_energy_in_ry}};