28 INTEGER,
PARAMETER :: base_block = 2, base_cell = 8, base_n = 16, &
29 base_ncol = 3, ncell = 31, nkp = 64, &
30 nround = 16, nselected = 3
31 INTEGER,
PARAMETER :: large_block = 1000, large_ncol = 1000, &
33 COMPLEX(KIND=dp),
ALLOCATABLE,
DIMENSION(:, :, :) :: inverse_k
34 COMPLEX(KIND=dp),
ALLOCATABLE,
DIMENSION(:, :, :, :) :: state_inverse_k
35 COMPLEX(KIND=dp),
DIMENSION(2, 3, nkp) :: direct, transformed
36 COMPLEX(KIND=dp),
DIMENSION(2, 3, nround) :: reconstructed, round_k
37 COMPLEX(KIND=dp),
DIMENSION(2, 3, nselected) :: selected_transformed
38 INTEGER :: d, i, icell, info, j, jcell, ik, ix, iy, iz
39 INTEGER,
ALLOCATABLE,
DIMENSION(:) :: correction_indices
40 INTEGER,
DIMENSION(3) :: base_dims, delta, large_dims
41 INTEGER,
DIMENSION(3, base_cell) :: base_index
42 INTEGER,
DIMENSION(3) :: nkp_grid
43 INTEGER,
DIMENSION(3, ncell) :: index_to_cell
44 INTEGER,
DIMENSION(3, nround) :: round_index
45 INTEGER,
DIMENSION(nselected) :: selected_kpoints
47 REAL(kind=
dp),
DIMENSION(3, 3) :: hmat
48 REAL(kind=
dp) :: base_residual_norm, corrected_projection_error, &
49 corrected_residual_norm, cost_ratio, &
50 expected_cost_ratio, imaginary_residual, &
51 large_block_real, large_ncell_real, &
52 large_ncol_real, projection_error
53 REAL(kind=
dp),
DIMENSION(3, base_cell) :: base_xkp
54 REAL(kind=
dp),
DIMENSION(base_n, base_n) :: base_overlap
55 REAL(kind=
dp),
DIMENSION(base_n, base_n) :: defect_operator
56 REAL(kind=
dp),
DIMENSION(base_n, base_ncol) :: base_input, base_input_rotated, &
57 base_output, base_output_rotated, &
58 base_state_output, defect_residual
59 REAL(kind=
dp),
ALLOCATABLE,
DIMENSION(:, :) :: coarse_inverse, defect_base_output, &
60 defect_corrected_output, local_operator, &
61 selected_output, selected_rhs
62 REAL(kind=
dp),
DIMENSION(base_ncol, base_ncol) :: column_rotation
63 REAL(kind=
dp),
DIMENSION(3, nkp) :: xkp, xkp_bad
64 REAL(kind=
dp),
DIMENSION(2, 3, ncell) :: values_rs
65 REAL(kind=
dp),
DIMENSION(2, 3, nround) :: round_rs
69 CALL init_fft(
"FFTSG", alltoall=.false., pool_limit=4, &
70 wisdom_file=
"", plan_style=1)
73 hmat = reshape([4.0_dp, 0.1_dp, 0.2_dp, &
74 0.0_dp, 5.0_dp, 0.3_dp, &
75 0.0_dp, 0.0_dp, 6.0_dp], [3, 3])
78 DO iz = 0, nkp_grid(3) - 1
79 DO iy = 0, nkp_grid(2) - 1
80 DO ix = 0, nkp_grid(1) - 1
82 xkp(:, ik) = [(real(ix,
dp) + 0.5_dp)/real(nkp_grid(1),
dp), &
83 REAL(iy,
dp)/
REAL(nkp_grid(2), dp), &
84 (
REAL(iz, dp) + 0.25_dp)/
REAL(nkp_grid(3),
dp)]
88 IF (.NOT.
regular_kpoint_grid(xkp, nkp_grid)) error stop
"Regular k-point grid not recognized"
91 index_to_cell(:, icell) = [
modulo(3*icell + 1, 11) - 5, &
92 modulo(5*icell + 2, 9) - 4, &
93 modulo(7*icell + 3, 7) - 3]
95 values_rs(1, d, icell) = sin(0.13_dp*real(icell + 2*d,
dp))
96 values_rs(2, d, icell) = cos(0.17_dp*real(2*icell - d,
dp))
100 CALL cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, transformed, used_fft)
101 IF (.NOT. used_fft) error stop
"Complete regular grid did not use lattice FFT"
102 CALL direct_reference(values_rs, index_to_cell, xkp, direct)
103 IF (maxval(abs(transformed - direct)) > 2.0e-12_dp)
THEN
104 error stop
"Shifted lattice FFT differs from direct transform"
107 selected_kpoints = [2, 31, 64]
108 CALL cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, selected_transformed, &
109 used_fft, selected_kpoints=selected_kpoints)
110 IF (.NOT. used_fft) error stop
"Selected distributed output did not use lattice FFT"
111 IF (maxval(abs(selected_transformed - direct(:, :, selected_kpoints))) > 2.0e-12_dp)
THEN
112 error stop
"Selected lattice-FFT output differs from the full transform"
115 CALL cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, transformed, used_fft, 2, hmat)
116 IF (.NOT. used_fft) error stop
"Derivative did not use lattice FFT"
117 CALL direct_reference(values_rs, index_to_cell, xkp, direct, 2, hmat)
118 IF (maxval(abs(transformed - direct)) > 5.0e-12_dp)
THEN
119 error stop
"Lattice-FFT derivative differs from direct transform"
124 DO iy = 0, nkp_grid(2) - 1
125 DO ix = 0, nkp_grid(1) - 1
127 xkp(:, ik) = [(real(ix,
dp) + 0.5_dp)/real(nkp_grid(1),
dp), &
128 REAL(iy,
dp)/
REAL(nkp_grid(2), dp), 0.0_dp]
132 transformed(:, :, 1:ik), used_fft)
133 IF (.NOT. used_fft) error stop
"Singleton grid direction did not use padded lattice FFT"
134 CALL direct_reference(values_rs, index_to_cell, xkp(:, 1:ik), direct(:, :, 1:ik))
135 IF (maxval(abs(transformed(:, :, 1:ik) - direct(:, :, 1:ik))) > 2.0e-12_dp)
THEN
136 error stop
"Padded lattice FFT differs from direct transform"
140 DO iy = 0, nkp_grid(2) - 1
141 DO ix = 0, nkp_grid(1) - 1
143 round_index(:, icell) = [ix, iy, 0]
144 round_rs(:, :, icell) = values_rs(:, :, icell)
147 CALL cell_to_k_grid_fft(round_rs, round_index, xkp(:, 1:ik), nkp_grid, round_k, used_fft)
148 IF (.NOT. used_fft) error stop
"Round-trip forward transform did not use lattice FFT"
149 CALL k_grid_to_cell_fft(round_k, xkp(:, 1:ik), nkp_grid, round_index, reconstructed, used_fft)
150 IF (.NOT. used_fft) error stop
"Round-trip inverse transform did not use lattice FFT"
151 IF (maxval(abs(reconstructed - cmplx(round_rs, kind=
dp))) > 3.0e-12_dp)
THEN
152 error stop
"Forward/inverse lattice-FFT round trip is inconsistent"
158 xkp_bad(1, nkp) = xkp_bad(1, nkp) + 0.03125_dp
159 CALL cell_to_k_grid_fft(values_rs, index_to_cell, xkp_bad, nkp_grid, transformed, used_fft)
160 IF (used_fft) error stop
"Irregular k-point list incorrectly used lattice FFT"
161 CALL direct_reference(values_rs, index_to_cell, xkp_bad, direct)
162 IF (maxval(abs(transformed - direct)) > 1.0e-14_dp)
THEN
163 error stop
"Direct lattice-transform fallback is inconsistent"
167 base_dims = [4, 2, 1]
169 base_overlap = 0.0_dp
170 DO jcell = 1, base_cell
171 DO icell = 1, base_cell
172 delta =
modulo(base_index(:, icell) - base_index(:, jcell), base_dims)
174 IF (all(delta == 0))
THEN
175 base_overlap((icell - 1)*base_block + i, (jcell - 1)*base_block + i) = &
176 2.8_dp + 0.2_dp*real(i, kind=
dp)
177 ELSE IF ((delta(1) == 1 .OR. delta(1) == base_dims(1) - 1) .AND. &
179 base_overlap((icell - 1)*base_block + i, (jcell - 1)*base_block + i) = -0.12_dp
180 ELSE IF (delta(1) == 0 .AND. delta(2) == 1)
THEN
181 base_overlap((icell - 1)*base_block + i, (jcell - 1)*base_block + i) = -0.08_dp
184 IF (all(delta == 0))
THEN
185 base_overlap((icell - 1)*base_block + 1, (jcell - 1)*base_block + 2) = 0.15_dp
186 base_overlap((icell - 1)*base_block + 2, (jcell - 1)*base_block + 1) = 0.15_dp
192 base_input(i, j) = sin(0.11_dp*real(2*i + 3*j, kind=
dp))
196 IF (info /= 0) error stop
"Positive lattice overlap failed block Cholesky inversion"
197 IF (.NOT. used_fft) error stop
"Block-circulant overlap construction did not use lattice FFT"
198 IF (projection_error > 2.0e-15_dp) error stop
"Exact block-circulant overlap was changed by projection"
200 projection_error, used_fft, info))
THEN
201 error stop
"Cost-aware lattice-FFT AUTO accepted a tiny eight-cell operator"
204 projection_error, used_fft, info))
THEN
205 error stop
"Cost-aware lattice-FFT AUTO rejected an economical eight-cell operator"
208 projection_error, used_fft, info))
THEN
209 error stop
"Conservative lattice-FFT AUTO accepted fewer than eight cells"
212 error stop
"Conservative lattice-FFT AUTO accepted a large projection error"
215 projection_error, used_fft, info))
THEN
216 error stop
"Storage-aware lattice-FFT AUTO accepted too many state operators"
219 error stop
"Lattice-FFT cost estimate missed the tiny-batch overhead"
221 large_dims = [100, 100, 1]
222 large_ncell_real = real(product(large_dims), kind=
dp)
223 large_block_real = real(large_block, kind=
dp)
224 large_ncol_real = real(large_ncol, kind=
dp)
225 expected_cost_ratio = ( &
226 large_ncell_real*large_block_real**2*large_ncol_real + &
227 4.0_dp*large_ncell_real*large_block_real*large_ncol_real* &
228 log(large_ncell_real)/log(2.0_dp) + &
229 8.0_dp*large_ncell_real*large_block_real + &
230 2.0_dp*large_ncell_real*large_block_real*large_ncol_real* &
231 log(real(large_num_pe + 1, kind=
dp))/log(2.0_dp))/ &
232 ((large_ncell_real*large_block_real)**2*large_ncol_real)
234 IF (abs(cost_ratio - expected_cost_ratio) > 1.0e-14_dp*expected_cost_ratio)
THEN
235 error stop
"Lattice-FFT cost estimate overflowed for a large problem"
238 error stop
"Lattice-FFT storage estimate is inconsistent"
241 error stop
"Lattice local-correction cost estimate is inconsistent"
244 error stop
"Lattice local-correction storage estimate is inconsistent"
247 used_fft, imaginary_residual)
248 IF (.NOT. used_fft) error stop
"Block-circulant inverse application did not use lattice FFT"
249 IF (imaginary_residual > 3.0e-13_dp) error stop
"Lattice inverse did not reconstruct a real matrix"
250 IF (maxval(abs(matmul(base_overlap, base_output) - base_input)) > 2.0e-11_dp)
THEN
251 error stop
"FFT block-circulant inverse differs from the dense operator inverse"
254 ALLOCATE (state_inverse_k(base_block, base_block, base_cell, base_ncol))
256 state_inverse_k(:, :, :, j) = (0.5_dp + 0.25_dp*real(j, kind=
dp))*inverse_k
259 base_state_output, used_fft, imaginary_residual)
260 IF (.NOT. used_fft) error stop
"State-dependent inverse application did not use lattice FFT"
262 IF (maxval(abs(base_state_output(:, j) - &
263 (0.5_dp + 0.25_dp*real(j, kind=
dp))*base_output(:, j))) > 3.0e-12_dp)
THEN
264 error stop
"State-dependent lattice inverse used the wrong column operator"
267 DEALLOCATE (state_inverse_k)
269 column_rotation = 0.0_dp
270 column_rotation(1, 1) = cos(0.31_dp)
271 column_rotation(1, 2) = -sin(0.31_dp)
272 column_rotation(2, 1) = sin(0.31_dp)
273 column_rotation(2, 2) = cos(0.31_dp)
274 column_rotation(3, 3) = 1.0_dp
275 base_input_rotated = matmul(base_input, column_rotation)
277 base_output_rotated, used_fft)
278 IF (maxval(abs(base_output_rotated - matmul(base_output, column_rotation))) > 3.0e-12_dp)
THEN
279 error stop
"FFT lattice base is not right-covariant under orbital rotations"
283 defect_operator = base_overlap
284 defect_operator(5, 5) = defect_operator(5, 5) + 0.90_dp
285 defect_operator(6, 6) = defect_operator(6, 6) + 0.60_dp
287 IF (info /= 0 .OR. .NOT. used_fft) error stop
"Defective lattice operator failed construction"
288 IF (projection_error <= 1.0e-3_dp) error stop
"Localized defect did not break lattice translations"
290 error stop
"Lattice-FFT AUTO accepted a localized defect"
293 defect_operator, base_dims, 1, correction_indices, local_operator, coarse_inverse, &
294 corrected_projection_error, info)
295 IF (info /= 0) error stop
"Local correction coarse inverse failed"
296 IF (any(correction_indices /= [5, 6])) error stop
"Local correction selected the wrong defect cell"
297 IF (corrected_projection_error >= projection_error)
THEN
298 error stop
"Local correction did not reduce the non-circulant residual"
301 ALLOCATE (defect_base_output(base_n, base_ncol), defect_corrected_output(base_n, base_ncol))
304 coarse_inverse, base_input, defect_corrected_output)
305 defect_residual = matmul(defect_operator, defect_base_output) - base_input
306 base_residual_norm = norm2(defect_residual)
307 defect_residual = matmul(defect_operator, defect_corrected_output) - base_input
308 corrected_residual_norm = norm2(defect_residual)
309 IF (corrected_residual_norm >= base_residual_norm)
THEN
310 error stop
"Balanced local correction did not improve inverse application"
312 IF (sum(base_input*defect_corrected_output) <= 0.0_dp)
THEN
313 error stop
"Balanced local correction lost positive definiteness"
316 ALLOCATE (selected_rhs(base_n,
SIZE(correction_indices)), &
317 selected_output(base_n,
SIZE(correction_indices)))
318 selected_rhs(:, :) = local_operator
320 coarse_inverse, selected_rhs, selected_output)
321 selected_rhs = 0.0_dp
322 DO i = 1,
SIZE(correction_indices)
323 selected_rhs(correction_indices(i), i) = 1.0_dp
325 IF (maxval(abs(selected_output - selected_rhs)) > 3.0e-11_dp)
THEN
326 error stop
"Balanced local correction is not exact in its selected subspace"
328 DEALLOCATE (coarse_inverse, correction_indices, defect_base_output, defect_corrected_output, &
329 local_operator, selected_output, selected_rhs)
330 DEALLOCATE (inverse_k)
348 operator_columns, coarse_inverse, input, output)
350 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(IN) :: inverse_k
351 INTEGER,
DIMENSION(3),
INTENT(IN) :: lattice_dims
352 INTEGER,
DIMENSION(:),
INTENT(IN) :: correction_indices
353 REAL(KIND=
dp),
DIMENSION(:, :),
INTENT(IN) :: operator_columns, coarse_inverse, input
354 REAL(KIND=
dp),
DIMENSION(:, :),
INTENT(OUT) :: output
358 REAL(KIND=
dp),
ALLOCATABLE,
DIMENSION(:, :) :: base_input, coarse_input, &
359 coarse_projected, coarse_residual, &
362 ALLOCATE (base_input(
SIZE(input, 1),
SIZE(input, 2)), &
363 coarse_input(
SIZE(correction_indices),
SIZE(input, 2)), &
364 coarse_projected(
SIZE(correction_indices),
SIZE(input, 2)), &
365 coarse_residual(
SIZE(correction_indices),
SIZE(input, 2)), &
366 coarse_rhs(
SIZE(correction_indices),
SIZE(input, 2)))
367 DO i = 1,
SIZE(correction_indices)
368 coarse_input(i, :) = input(correction_indices(i), :)
370 coarse_rhs(:, :) = matmul(coarse_inverse, coarse_input)
371 base_input(:, :) = input - matmul(operator_columns, coarse_rhs)
373 IF (.NOT. used_fft) error stop
"Balanced correction base did not use lattice FFT"
374 coarse_projected(:, :) = matmul(transpose(operator_columns), output)
375 coarse_residual(:, :) = matmul(coarse_inverse, coarse_projected)
376 output(correction_indices, :) = output(correction_indices, :) + coarse_rhs - coarse_residual
377 DEALLOCATE (base_input, coarse_input, coarse_projected, coarse_residual, coarse_rhs)
390 SUBROUTINE direct_reference(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat)
392 REAL(KIND=
dp),
DIMENSION(:, :, :),
INTENT(IN) :: values_rs
393 INTEGER,
DIMENSION(:, :),
INTENT(IN) :: index_to_cell
394 REAL(KIND=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
395 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(OUT) :: values_k
396 INTEGER,
INTENT(IN),
OPTIONAL :: deriv_direction
397 REAL(KIND=
dp),
DIMENSION(3, 3),
INTENT(IN), &
400 COMPLEX(KIND=dp) :: factor
402 REAL(KIND=
dp),
DIMENSION(3) :: cell_index_real, cell_vector
405 DO ik = 1,
SIZE(xkp, 2)
406 DO icell = 1,
SIZE(values_rs, 3)
407 factor = exp(
gaussi*
twopi*sum(xkp(:, ik)*real(index_to_cell(:, icell), kind=
dp)))
408 IF (
PRESENT(deriv_direction))
THEN
409 cell_index_real = real(index_to_cell(:, icell), kind=
dp)
410 cell_vector = matmul(hmat, cell_index_real)
411 factor = factor*
gaussi*cell_vector(deriv_direction)
413 values_k(:, :, ik) = values_k(:, :, ik) + factor*values_rs(:, :, icell)
417 END SUBROUTINE direct_reference
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
subroutine apply_balanced_local_inverse(inverse_k, lattice_dims, correction_indices, operator_columns, coarse_inverse, input, output)
Apply the balanced two-level inverse used by the production local correction.
program kpoint_lattice_fft_unittest
Defines the basic variable types.
integer, parameter, public dp
Batched lattice Fourier transforms for complete regular k-point grids.
subroutine, public k_grid_to_cell_fft(values_k, xkp, nkp_grid, index_to_cell, values_rs, used_fft)
Transform complete reciprocal-grid data back to image-cell values.
subroutine, public cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, values_k, used_fft, deriv_direction, hmat, selected_kpoints)
Transform a batch of real matrices from image cells to every supplied k point.
logical function, public regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset)
Test and map a complete, uniformly shifted reciprocal grid.
Block-circulant inverse operators for replicated-cell OT preconditioners.
subroutine, public build_lattice_local_correction(operator_matrix, lattice_dims, local_cell_count, correction_indices, operator_columns, coarse_inverse, corrected_projection_error, info)
Build a balanced local correction for selected non-circulant cell blocks.
subroutine, public apply_lattice_inverse_dense(inverse_k, lattice_dims, matrix_in, matrix_out, used_fft, imaginary_residual)
Apply inverse Fourier blocks to a dense cell-major real matrix.
subroutine, public apply_lattice_state_inverse_dense(inverse_k, lattice_dims, matrix_in, matrix_out, used_fft, imaginary_residual, state_indices)
Apply one inverse Fourier operator to every column of a dense real matrix.
subroutine, public build_lattice_inverse(operator_matrix, lattice_dims, inverse_k, projection_error, used_fft, info)
Project a cell-major SPD operator onto lattice translations and invert its Fourier blocks.
pure real(kind=dp) function, public lattice_local_storage_ratio(lattice_dims, operator_count, local_cell_count)
Estimate balanced-local-correction storage relative to one dense real operator.
pure real(kind=dp) function, public lattice_fft_storage_ratio(lattice_dims, operator_count)
Estimate reciprocal-block storage relative to one dense real operator.
pure logical function, public lattice_fft_auto_select(lattice_dims, block_size, rhs_count, operator_count, num_pe, projection_error, used_fft, info)
Conservative error-, work-, and storage-aware AUTO selector.
subroutine, public lattice_grid(lattice_dims, index_to_cell, xkp)
Enumerate a regular direct/reciprocal lattice with the first direction varying fastest.
pure real(kind=dp) function, public lattice_local_cost_ratio(lattice_dims, local_cell_count)
Estimate balanced-local-correction work relative to a dense inverse application.
pure real(kind=dp) function, public lattice_fft_cost_ratio(lattice_dims, block_size, rhs_count, num_pe)
Estimate lattice-FFT application work relative to a dense inverse application.
Definition of mathematical constants and functions.
complex(kind=dp), parameter, public gaussi
real(kind=dp), parameter, public twopi
complex(kind=dp), parameter, public z_zero
Interface to the message passing library MPI.
subroutine, public mp_world_init(mp_comm)
initializes the system default communicator
subroutine, public mp_world_finalize()
Finalize the system default communicator and MPI when CP2K owns MPI.