Loading...
Searching...
No Matches
plane_wave_selector.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * PlaneWaveSelector — selects and sorts active plane waves at one k-point.
5 *
6 * When an FFT grid is provided and band_size > 1, only G-vectors that map to
7 * the local FFT slab are selected (band/G-vector parallelism). Otherwise, all
8 * qualifying G-vectors are selected (serial or k-point-only parallelism).
9 *
10 * Note: slab filtering may yield zero plane waves for a rank if its FFT slabs
11 * do not overlap with the cutoff sphere. The caller (build_kpoint_plane_wave_data)
12 * detects this via a collective check and re-initializes with band_size=1.
13 */
14
15#include <algorithm>
16#include <numeric>
17#include <vector>
18
24
25namespace q3 {
26
28 public:
29 /**
30 * Compute |k+G|² for all G-vectors at a k-point. Returns sorted indices
31 * and kinetic energies.
32 *
33 * When fft_wave is non-null and band_size > 1, only G-vectors whose first
34 * Miller index falls into the local FFT slab are selected. This gives each
35 * band rank its own subset of plane waves for the wavefunction.
36 *
37 * Safety: if slab filtering would yield zero plane waves, the filter is
38 * disabled and ALL qualifying G-vectors are returned (replicated mode).
39 * This prevents zero-dimension BLAS crashes on small systems.
40 */
41 static void select(const GVectorsGrid& g_vectors_grid,
42 const Vector3D& k_coordinates,
43 const Crystal& crystal_structure,
44 Real energy_cutoff_wavefunction,
45 std::vector<int>& g_vector_indices,
46 std::vector<Real>& kinetic_energy_squared,
47 const FFTGrid* fft_wave = nullptr,
48 int band_size = 1) {
49 Real two_pi_over_lattice_parameter_squared =
50 crystal_structure.two_pi_over_lattice_parameter_squared();
51 const Matrix3x3& reciprocal_lattice = crystal_structure.reciprocal_lattice_vectors();
52
53 Vector3D k_cartesian;
54 for (int dimension = 0; dimension < 3; dimension++) {
55 k_cartesian[dimension] = k_coordinates.x * reciprocal_lattice[0][dimension] +
56 k_coordinates.y * reciprocal_lattice[1][dimension] +
57 k_coordinates.z * reciprocal_lattice[2][dimension];
58 }
59
60 Real wavefunction_cutoff_squared =
61 energy_cutoff_wavefunction / two_pi_over_lattice_parameter_squared;
62
63 g_vector_indices.clear();
64 kinetic_energy_squared.clear();
65
66 const auto& g_vectors = g_vectors_grid.g_vectors();
67 int total_g = static_cast<int>(g_vectors.size());
68
69 /**
70 * Under a shared pool each rank keeps only the G-vectors on the sticks
71 * it owns. The grid decides ownership; this used to encode the slab
72 * rule here, which is one reason the slab layout was hard to replace.
73 * Ownership is checked against the wavefunction grid, where the
74 * transforms run, not the density grid the G-vectors were generated on.
75 */
76 bool filter_by_ownership = (fft_wave != nullptr && band_size > 1);
77
78 for (int g_index = 0; g_index < total_g; g_index++) {
79 if (filter_by_ownership &&
80 !fft_wave->owns_stick(static_cast<int>(g_vectors[g_index].miller.x),
81 static_cast<int>(g_vectors[g_index].miller.y))) {
82 continue;
83 }
84
85 Vector3D k_plus_g = k_cartesian + g_vectors[g_index].g_cart;
86 Real k_plus_g_squared = k_plus_g.norm2();
87 if (k_plus_g_squared <=
88 wavefunction_cutoff_squared + math::tolerances::energy_cutoff_rounding_tolerance) {
89 g_vector_indices.push_back(g_index);
90 kinetic_energy_squared.push_back(k_plus_g_squared *
91 two_pi_over_lattice_parameter_squared);
92 }
93 }
94
95 std::vector<int> order(g_vector_indices.size());
96 std::iota(order.begin(), order.end(), 0);
97 std::sort(order.begin(), order.end(), [&kinetic_energy_squared](int a, int b) {
98 return kinetic_energy_squared[a] < kinetic_energy_squared[b];
99 });
100
101 std::vector<int> sorted_g_vector_indices(g_vector_indices.size());
102 std::vector<Real> sorted_kinetic_energies(kinetic_energy_squared.size());
103 for (size_t index = 0; index < order.size(); index++) {
104 sorted_g_vector_indices[index] = g_vector_indices[order[index]];
105 sorted_kinetic_energies[index] = kinetic_energy_squared[order[index]];
106 }
107 g_vector_indices = std::move(sorted_g_vector_indices);
108 kinetic_energy_squared = std::move(sorted_kinetic_energies);
109 }
110};
111
112} // namespace q3
Real two_pi_over_lattice_parameter_squared() const
Definition crystal.hpp:199
const Matrix3x3 & reciprocal_lattice_vectors() const
Definition crystal.hpp:205
const std::vector< GVector > & g_vectors() const
static void select(const GVectorsGrid &g_vectors_grid, const Vector3D &k_coordinates, const Crystal &crystal_structure, Real energy_cutoff_wavefunction, std::vector< int > &g_vector_indices, std::vector< Real > &kinetic_energy_squared, const FFTGrid *fft_wave=nullptr, int band_size=1)
constexpr double energy_cutoff_rounding_tolerance
double Real
3×3 matrix stored row-major: row i = mat[i]
Real norm2() const
Definition vector_3d.hpp:65