Loading...
Searching...
No Matches
init.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * PlanewaveExecutionPolicy — abstract interface for planewave-layer collective operations.
5 *
6 * Extends QuantumSystemExecutionPolicy (band groups over layer-0 process
7 * groups: dot_product, add_scaled_vector, norm, band_size) with k-point pool
8 * collective operations specific to the planewave representation.
9 *
10 * Two categories of collective operations:
11 *
12 * 1. Across-pool reductions (inter-pool communicator)
13 * - reduce_scalar: k-point energy sums (band energy, smearing entropy)
14 * - reduce_vector: k-point force/stress sums (nonlocal, kinetic)
15 *
16 * 2. Within-pool reductions (band communicator)
17 * - reduce_within_pool_scalar: real-space slab integrals (XC energy, DCC, delta-eSCF)
18 * - reduce_within_pool_vector: real-space slab force/stress (local, XC, core-correction)
19 *
20 * With 1-rank-per-pool (current default), the band communicator is trivial so
21 * reduce_within_pool_* are no-ops. With multiple ranks per pool the FFTW-MPI slab
22 * decomposition means each rank only owns a subset of grid points and both categories
23 * are required for correctness.
24 *
25 * This interface has no knowledge of MPI or CUDA — implementations live in
26 * planewave/execution/serial.hpp and planewave/execution/mpi.hpp.
27 */
28
29#include "generated/q3/results/runtime_log/execution.hpp"
36
37#include <cstdint>
38#include <memory>
39#include <vector>
40
41namespace q3 {
42namespace execution {
43
45 public FFTGridFactory {
46 public:
47 ~PlanewaveExecutionPolicy() override = default;
48
49 /**
50 * Disable G-vector distribution, making band-group reductions into no-ops.
51 *
52 * Called when slab-based PW distribution yields zero plane waves for some
53 * ranks. Default is a no-op (serial policy never distributes G-vectors).
54 */
55 virtual void disable_band_distribution() {}
56
57 /**
58 * Reconfigure the k-point pool topology after initial setup.
59 *
60 * Called by the factory when the requested pool count exceeds the number of
61 * available IBZ k-points. The implementation clamps the pool count to
62 * new_number_of_pools and reconfigures all MPI communicators accordingly.
63 *
64 * The default implementation is a no-op: the serial policy always has
65 * exactly one pool so clamping is never needed there.
66 *
67 * @param new_number_of_pools New (clamped) pool count.
68 */
69 virtual void reconfigure_topology(int /*new_number_of_pools*/) {}
70
71 /**
72 * Number of k-point pools in the current topology.
73 *
74 * Returns 1 in serial mode. In MPI mode returns the number of pools
75 * configured by configure_topology(). Used by the factory to validate
76 * that no pool is left empty (which would cause MPI collective mismatches).
77 */
78 virtual int number_of_pools() const = 0;
79
80 /**
81 * Whether the pool count came from the caller rather than a default.
82 *
83 * The factory needs the distinction: an explicit choice is honoured as
84 * given, while an unrequested one is chosen from the k-point count. Serial
85 * runs report true, since their single pool is not a fallback.
86 */
87 virtual bool number_of_pools_was_requested() const {
88 return true;
89 }
90
91 /**
92 * Whether one process owns every k-point and every G-vector coefficient.
93 *
94 * Writers without a gather implementation may serialize state only when
95 * this is true. It is stronger than checking the current group size because
96 * a small-basis fallback can disable coefficient distribution without
97 * turning a multi-process FFT grid into a serial one.
98 */
99 virtual bool one_process_owns_complete_state() const = 0;
100
101 /**
102 * Whether every process holds the whole of each k-point it works on: all
103 * of that k-point's plane-wave coefficients, and the complete density grid.
104 *
105 * True exactly when each k-point pool is a single process. Pools still
106 * divide the k-points between them, so this is weaker than
107 * one_process_owns_complete_state(), but it is enough for a writer whose
108 * output splits by k-point: each process writes its own k-points under
109 * their global_kpoint_index(), and what pools reduce -- the density, the
110 * potential built from it -- is already whole on every process. Ranks
111 * sharing a pool split both the coefficients and the grid, and nothing
112 * gathers them yet.
113 */
114 virtual bool each_process_owns_whole_kpoints() const = 0;
115
116 /**
117 * The position in the global k-point grid of this pool's k-point
118 * `local_kpoint_index`, as ordered in local_kpoint_grid().
119 *
120 * Identity in serial; under pools, the inverse of the round-robin
121 * distribution.
122 */
123 virtual int global_kpoint_index(int local_kpoint_index) const = 0;
124
125 virtual void reduce_charge_density(std::vector<std::vector<Real>>& charge_density) const = 0;
126
127 /**
128 * ── Across-pool reductions (inter-pool communicator) ──────────────────────
129 *
130 * Use for k-point-local quantities that must be summed across pools.
131 * With 1-rank-per-pool these happen over MPI_COMM_WORLD; with multiple
132 * ranks per pool they use the inter-pool communicator (one rank per pool).
133 */
134
135 /**
136 * Reduce a scalar k-point sum across all pools (MPI_SUM).
137 *
138 * Used for band_energy and smearing_entropy after local k-point accumulation.
139 *
140 * @param value In-out: local k-point contribution, replaced by global sum.
141 */
142 virtual void reduce_scalar(Real& value) const = 0;
143
144 /**
145 * Reduce a vector of k-point-local values across all pools (MPI_SUM).
146 *
147 * Used for nonlocal forces and kinetic/nonlocal stress contributions.
148 *
149 * @param values In-out: local k-point contributions, replaced by global sum.
150 */
151 virtual void reduce_vector(std::vector<Real>& values) const = 0;
152
153 /**
154 * Reduce a scalar across all pools using MPI_MAX.
155 *
156 * Used for summary statistics such as max_plane_waves_per_kpoint where
157 * each pool holds only its local k-points and the global maximum is needed.
158 *
159 * @param value In-out: local maximum, replaced by global maximum.
160 */
161 virtual void reduce_scalar_max(int& value) const = 0;
162
163 /**
164 * Reduce a Real scalar across all pools using MPI_MAX.
165 *
166 * Used for global eigenvalue range (e.g., HOMO across pools).
167 *
168 * @param value In-out: local maximum, replaced by global maximum.
169 */
170 virtual void reduce_scalar_max(Real& value) const = 0;
171
172 /**
173 * Reduce a Real scalar across all pools using MPI_MIN.
174 *
175 * Used for global eigenvalue range (e.g., LUMO across pools).
176 *
177 * @param value In-out: local minimum, replaced by global minimum.
178 */
179 virtual void reduce_scalar_min(Real& value) const = 0;
180
181 /**
182 * Reduce an integer scalar within the band group using MPI_MIN.
183 *
184 * Used during initialization to check if any rank in the pool received
185 * zero plane waves from slab distribution.
186 *
187 * @param value In-out: local value, replaced by band-group minimum.
188 */
189 virtual void reduce_band_group_scalar_min(int& value) const = 0;
190
191 /**
192 * ── Within-pool reductions (band communicator) ────────────────────────────
193 *
194 * Use for real-space slab integrals. With 1-rank-per-pool these are no-ops.
195 * With multiple ranks per pool, FFTW-MPI distributes the real-space grid
196 * across the band communicator; each rank integrates only its local slabs
197 * and these methods sum the partial results within the pool.
198 */
199
200 /**
201 * Reduce a real-space slab integral scalar within this pool (MPI_SUM).
202 *
203 * Used for XC energy, double-counting correction, and delta-eSCF correction.
204 *
205 * @param value In-out: partial slab integral, replaced by pool-total.
206 */
207 virtual void reduce_within_pool_scalar(Real& value) const = 0;
208
209 /**
210 * Reduce a real-space slab integral vector within this pool (MPI_SUM).
211 *
212 * Used for local forces, XC stress, local-PP stress, and core-correction
213 * forces/stress computed from real-space grid slabs.
214 *
215 * @param values In-out: partial slab contributions, replaced by pool-total.
216 */
217 virtual void reduce_within_pool_vector(std::vector<Real>& values) const = 0;
218
219 /**
220 * create_fft_grid() is inherited (pure) from FFTGridFactory — the layer-3
221 * interface through which PlanewaveFFTPair obtains grids without an
222 * upward dependency on this class.
223 */
224
225 /**
226 * Compute the local k-point grid assigned to this process's pool.
227 *
228 * Serial: returns the global grid unchanged (identity).
229 * MPI: distributes k-points round-robin across pools.
230 *
231 * @param global_kpoint_grid The full IBZ k-point grid.
232 * @return The local subset owned by this pool.
233 */
234 virtual KPointsGrid local_kpoint_grid(const KPointsGrid& global_kpoint_grid) const = 0;
235
236 /**
237 * Assemble fixed-width data for local k-point states into global order.
238 *
239 * Local state order is spin-major, with physical k-points ordered as they
240 * appear in local_kpoint_grid(). The returned data uses the same spin-major
241 * convention over the complete global grid. Serial policies return the
242 * input unchanged; MPI policies gather one contribution per pool.
243 */
244 virtual std::vector<Real>
245 assemble_global_kpoint_state_data(const std::vector<Real>& local_data,
246 int local_number_of_physical_kpoints,
247 int number_of_spin_components,
248 int number_of_values_per_state) const = 0;
249
250 /**
251 * What the run executed on -- processes, k-point pools, ranks per pool,
252 * the threads each process may use, whether reciprocal space was split,
253 * and the peak resident memory of the largest rank -- as results.json's
254 * runtime_log.execution, so a results file says which policy produced it.
255 *
256 * Collective under MPI: every rank must call it, for the memory reduction.
257 */
258 [[nodiscard]] generated::ResultsExecution execution_record() const {
259 generated::ResultsExecution record;
260 record.number_of_processes = number_of_processes();
261 record.number_of_threads_per_process = threading::maximum_threads();
262 record.number_of_kpoint_pools = number_of_pools();
263 record.band_group_size = record.number_of_processes / record.number_of_kpoint_pools;
264 record.reciprocal_space_vectors_are_distributed = band_size() > 1;
265 Real peak_resident_megabytes =
266 static_cast<Real>(ProfilingCounters::instance().peak_resident_bytes_now()) / 1048576.0;
267 reduce_scalar_max(peak_resident_megabytes);
268 record.peak_resident_memory_bytes =
269 static_cast<int64_t>(static_cast<std::uint64_t>(peak_resident_megabytes * 1048576.0));
270 return record;
271 }
272};
273
274} // namespace execution
275} // namespace q3
virtual int number_of_processes() const =0
virtual void reduce_scalar_min(Real &value) const =0
virtual bool number_of_pools_was_requested() const
Definition init.hpp:87
virtual void reduce_scalar_max(Real &value) const =0
virtual void reduce_scalar_max(int &value) const =0
virtual void reduce_band_group_scalar_min(int &value) const =0
virtual int global_kpoint_index(int local_kpoint_index) const =0
virtual void reduce_charge_density(std::vector< std::vector< Real > > &charge_density) const =0
virtual KPointsGrid local_kpoint_grid(const KPointsGrid &global_kpoint_grid) const =0
virtual void reduce_vector(std::vector< Real > &values) const =0
virtual bool one_process_owns_complete_state() const =0
generated::ResultsExecution execution_record() const
Definition init.hpp:258
virtual void reduce_within_pool_vector(std::vector< Real > &values) const =0
virtual bool each_process_owns_whole_kpoints() const =0
virtual int number_of_pools() const =0
virtual std::vector< Real > assemble_global_kpoint_state_data(const std::vector< Real > &local_data, int local_number_of_physical_kpoints, int number_of_spin_components, int number_of_values_per_state) const =0
virtual void reduce_within_pool_scalar(Real &value) const =0
virtual void reduce_scalar(Real &value) const =0
virtual void reconfigure_topology(int)
Definition init.hpp:69
~PlanewaveExecutionPolicy() override=default
double Real
static ProfilingCounters & instance()
Definition counters.hpp:220
std::uint64_t peak_resident_bytes_now()
Definition counters.hpp:205