(git:5e7fe52)
Loading...
Searching...
No Matches
kpoint_lattice_fft.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Batched lattice Fourier transforms for complete regular k-point grids.
10!>
11!> Real-space image cells are folded modulo the reciprocal grid. Shifted Monkhorst-Pack
12!> meshes are handled by a phase twist before the FFT. Non-regular or incomplete k-point
13!> lists transparently use the direct phase sum.
14! **************************************************************************************************
16 USE fft_tools, ONLY: bwfft,&
18 fwfft,&
19 fft3d,&
20 fft_alloc,&
21 fft_dealloc,&
23 USE kinds, ONLY: dp
24 USE mathconstants, ONLY: gaussi,&
25 twopi,&
26 z_zero
27#include "./base/base_uses.f90"
28
29 IMPLICIT NONE
30
31 PRIVATE
32
33 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'kpoint_lattice_fft'
34
35 PUBLIC :: cell_to_k_grid_fft, &
38
39CONTAINS
40
41! **************************************************************************************************
42!> \brief Test and map a complete, uniformly shifted reciprocal grid.
43!> \param xkp reciprocal coordinates, in units of reciprocal lattice vectors
44!> \param nkp_grid regular-grid dimensions
45!> \param grid_index FFT-grid index for every k point
46!> \param k_offset common reciprocal-coordinate offset
47!> \return true for a complete regular grid
48! **************************************************************************************************
49 LOGICAL FUNCTION regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset) RESULT(regular)
50
51 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: xkp
52 INTEGER, DIMENSION(3), INTENT(IN) :: nkp_grid
53 INTEGER, DIMENSION(:, :), INTENT(OUT), OPTIONAL :: grid_index
54 REAL(kind=dp), DIMENSION(3), INTENT(OUT), OPTIONAL :: k_offset
55
56 REAL(kind=dp), PARAMETER :: map_tolerance = 2.0e-10_dp
57
58 INTEGER :: d, ik, m, nkp
59 INTEGER, DIMENSION(3) :: index
60 LOGICAL, ALLOCATABLE, DIMENSION(:, :, :) :: occupied
61 REAL(kind=dp) :: offset(3), scaled
62
63 regular = .false.
64 IF (SIZE(xkp, 1) < 3 .OR. any(nkp_grid <= 0)) RETURN
65 nkp = SIZE(xkp, 2)
66 IF (product(nkp_grid) /= nkp) RETURN
67 IF (PRESENT(grid_index)) THEN
68 IF (SIZE(grid_index, 1) < 3 .OR. SIZE(grid_index, 2) < nkp) RETURN
69 END IF
70
71 DO d = 1, 3
72 scaled = modulo(real(nkp_grid(d), kind=dp)*xkp(d, 1), 1.0_dp)
73 IF (abs(scaled) < map_tolerance .OR. abs(scaled - 1.0_dp) < map_tolerance) scaled = 0.0_dp
74 offset(d) = scaled/real(nkp_grid(d), kind=dp)
75 END DO
76
77 ALLOCATE (occupied(nkp_grid(1), nkp_grid(2), nkp_grid(3)), source=.false.)
78 DO ik = 1, nkp
79 DO d = 1, 3
80 scaled = real(nkp_grid(d), kind=dp)*(xkp(d, ik) - offset(d))
81 m = nint(scaled)
82 IF (abs(scaled - real(m, kind=dp)) > map_tolerance) THEN
83 DEALLOCATE (occupied)
84 RETURN
85 END IF
86 index(d) = modulo(m, nkp_grid(d)) + 1
87 END DO
88 IF (occupied(index(1), index(2), index(3))) THEN
89 DEALLOCATE (occupied)
90 RETURN
91 END IF
92 occupied(index(1), index(2), index(3)) = .true.
93 IF (PRESENT(grid_index)) grid_index(1:3, ik) = index
94 END DO
95 regular = all(occupied)
96 DEALLOCATE (occupied)
97
98 IF (regular .AND. PRESENT(k_offset)) k_offset = offset
99
100 END FUNCTION regular_kpoint_grid
101
102! **************************************************************************************************
103!> \brief Transform a batch of real matrices from image cells to every supplied k point.
104!> \param values_rs real-space matrices; the last dimension enumerates image cells
105!> \param index_to_cell integer lattice vector for every image cell
106!> \param xkp reciprocal coordinates of all requested k points
107!> \param nkp_grid dimensions of the candidate regular reciprocal grid
108!> \param values_k complex matrices at every k point
109!> \param used_fft reports whether the regular-grid FFT path was used
110!> \param deriv_direction optional Cartesian k derivative (1, 2, or 3)
111!> \param hmat direct-lattice cell matrix, required for a derivative
112!> \param selected_kpoints optional indices of k points to retain in values_k
113! **************************************************************************************************
114 SUBROUTINE cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, values_k, &
115 used_fft, deriv_direction, hmat, selected_kpoints)
116
117 REAL(kind=dp), DIMENSION(:, :, :), INTENT(IN) :: values_rs
118 INTEGER, DIMENSION(:, :), INTENT(IN) :: index_to_cell
119 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: xkp
120 INTEGER, DIMENSION(3), INTENT(IN) :: nkp_grid
121 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT) :: values_k
122 LOGICAL, INTENT(OUT), OPTIONAL :: used_fft
123 INTEGER, INTENT(IN), OPTIONAL :: deriv_direction
124 REAL(kind=dp), DIMENSION(3, 3), INTENT(IN), &
125 OPTIONAL :: hmat
126 INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: selected_kpoints
127
128 COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:) :: cell_factor
129 COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:, :, :), &
130 POINTER :: fft_in, fft_out
131 INTEGER :: attempt, d, handle, i, icell, ik, &
132 ik_out, j, nout, radix_length, stat
133 INTEGER, ALLOCATABLE, DIMENSION(:, :) :: cell_index, grid_index
134 INTEGER, DIMENSION(3) :: n, nfft
135 LOGICAL :: compatible, regular
136 REAL(kind=dp) :: arg
137 REAL(kind=dp), DIMENSION(3) :: cell_vector, k_offset
138
139 CALL timeset("cell_to_k_grid_fft", handle)
140
141 IF (SIZE(index_to_cell, 1) < 3 .OR. &
142 SIZE(index_to_cell, 2) /= SIZE(values_rs, 3)) THEN
143 cpabort("Inconsistent real-space image-cell mapping in lattice FFT")
144 END IF
145 nout = SIZE(xkp, 2)
146 IF (PRESENT(selected_kpoints)) THEN
147 nout = SIZE(selected_kpoints)
148 IF (any(selected_kpoints < 1) .OR. any(selected_kpoints > SIZE(xkp, 2))) THEN
149 cpabort("Selected lattice-FFT k-point index is out of range")
150 END IF
151 END IF
152 IF (SIZE(values_k, 1) /= SIZE(values_rs, 1) .OR. &
153 SIZE(values_k, 2) /= SIZE(values_rs, 2) .OR. &
154 SIZE(values_k, 3) /= nout) THEN
155 cpabort("Inconsistent input and output matrix batches in lattice FFT")
156 END IF
157 IF (PRESENT(deriv_direction)) THEN
158 IF (.NOT. PRESENT(hmat)) cpabort("Lattice-FFT derivative requested without a cell matrix")
159 IF (deriv_direction < 1 .OR. deriv_direction > 3) THEN
160 cpabort("Lattice-FFT derivative direction must be 1, 2, or 3")
161 END IF
162 END IF
163
164 ALLOCATE (grid_index(3, SIZE(xkp, 2)))
165 regular = regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset)
166 n = nkp_grid
167 nfft = n
168 IF (regular) THEN
169 DO d = 1, 3
170 ! FFTSG rejects some short or non-radix lengths. Embed the N-point
171 ! transform in a supported integer multiple L: real-space samples
172 ! occupy every L/N-th slot, while the first N output frequencies
173 ! are exactly the desired N-point DFT. This keeps common 4x4x1
174 ! and 2D/1D Monkhorst-Pack grids on the FFT path.
175 compatible = .false.
176 DO attempt = 0, 15
177 IF (nfft(d) >= 3) THEN
178 CALL fft_radix_operations(nfft(d), radix_length, fft_radix_next)
179 IF (radix_length == nfft(d)) THEN
180 compatible = .true.
181 EXIT
182 END IF
183 END IF
184 nfft(d) = nfft(d) + n(d)
185 END DO
186 IF (.NOT. compatible) regular = .false.
187 END DO
188 END IF
189 IF (.NOT. regular) THEN
190 CALL direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat, &
191 selected_kpoints)
192 IF (PRESENT(used_fft)) used_fft = .false.
193 DEALLOCATE (grid_index)
194 CALL timestop(handle)
195 RETURN
196 END IF
197
198 NULLIFY (fft_in, fft_out)
199 CALL fft_alloc(fft_in, nfft)
200 CALL fft_alloc(fft_out, nfft)
201 ALLOCATE (cell_index(3, SIZE(values_rs, 3)), cell_factor(SIZE(values_rs, 3)))
202 DO icell = 1, SIZE(values_rs, 3)
203 DO d = 1, 3
204 cell_index(d, icell) = modulo(index_to_cell(d, icell), n(d))*(nfft(d)/n(d)) + 1
205 END DO
206 arg = sum(k_offset*real(index_to_cell(1:3, icell), kind=dp))
207 cell_factor(icell) = exp(gaussi*twopi*arg)
208 IF (PRESENT(deriv_direction)) THEN
209 cell_vector = matmul(hmat, real(index_to_cell(1:3, icell), kind=dp))
210 cell_factor(icell) = cell_factor(icell)*gaussi*cell_vector(deriv_direction)
211 END IF
212 END DO
213
214 stat = 0
215 DO j = 1, SIZE(values_rs, 2)
216 DO i = 1, SIZE(values_rs, 1)
217 fft_in = z_zero
218 DO icell = 1, SIZE(values_rs, 3)
219 fft_in(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell)) = &
220 fft_in(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell)) + &
221 cell_factor(icell)*values_rs(i, j, icell)
222 END DO
223 CALL fft3d(bwfft, nfft, fft_in, fft_out, status=stat)
224 IF (stat /= 0) EXIT
225 DO ik_out = 1, nout
226 ik = ik_out
227 IF (PRESENT(selected_kpoints)) ik = selected_kpoints(ik_out)
228 values_k(i, j, ik_out) = &
229 fft_out(grid_index(1, ik), grid_index(2, ik), grid_index(3, ik))
230 END DO
231 END DO
232 IF (stat /= 0) EXIT
233 END DO
234
235 IF (stat /= 0) THEN
236 CALL direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat, &
237 selected_kpoints)
238 regular = .false.
239 END IF
240 IF (PRESENT(used_fft)) used_fft = regular
241
242 CALL fft_dealloc(fft_in)
243 CALL fft_dealloc(fft_out)
244 DEALLOCATE (cell_factor, cell_index, grid_index)
245 CALL timestop(handle)
246
247 END SUBROUTINE cell_to_k_grid_fft
248
249! **************************************************************************************************
250!> \brief Transform complete reciprocal-grid data back to image-cell values.
251!> \param values_k complex matrices on a complete regular reciprocal grid
252!> \param xkp reciprocal coordinates, in units of reciprocal lattice vectors
253!> \param nkp_grid dimensions of the reciprocal grid
254!> \param index_to_cell integer lattice vector for every requested image cell
255!> \param values_rs reconstructed complex matrices in the requested image cells
256!> \param used_fft reports whether the regular-grid FFT path was used
257! **************************************************************************************************
258 SUBROUTINE k_grid_to_cell_fft(values_k, xkp, nkp_grid, index_to_cell, values_rs, used_fft)
259
260 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: values_k
261 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: xkp
262 INTEGER, DIMENSION(3), INTENT(IN) :: nkp_grid
263 INTEGER, DIMENSION(:, :), INTENT(IN) :: index_to_cell
264 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT) :: values_rs
265 LOGICAL, INTENT(OUT), OPTIONAL :: used_fft
266
267 COMPLEX(KIND=dp) :: phase
268 COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:, :, :), &
269 POINTER :: fft_in, fft_out
270 INTEGER :: attempt, d, handle, i, icell, ik, j, &
271 radix_length, stat
272 INTEGER, ALLOCATABLE, DIMENSION(:, :) :: cell_index, grid_index
273 INTEGER, DIMENSION(3) :: n, nfft
274 LOGICAL :: compatible, regular
275 REAL(kind=dp) :: arg, normalization
276 REAL(kind=dp), DIMENSION(3) :: k_offset
277
278 CALL timeset("k_grid_to_cell_fft", handle)
279
280 IF (SIZE(xkp, 1) < 3 .OR. SIZE(index_to_cell, 1) < 3 .OR. &
281 SIZE(values_k, 3) /= SIZE(xkp, 2)) THEN
282 cpabort("Inconsistent reciprocal-grid mapping in inverse lattice FFT")
283 END IF
284 IF (SIZE(values_rs, 1) /= SIZE(values_k, 1) .OR. &
285 SIZE(values_rs, 2) /= SIZE(values_k, 2) .OR. &
286 SIZE(values_rs, 3) /= SIZE(index_to_cell, 2)) THEN
287 cpabort("Inconsistent input and output matrix batches in inverse lattice FFT")
288 END IF
289
290 ALLOCATE (grid_index(3, SIZE(xkp, 2)))
291 regular = regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset)
292 n = nkp_grid
293 nfft = n
294 IF (regular) THEN
295 DO d = 1, 3
296 compatible = .false.
297 DO attempt = 0, 15
298 IF (nfft(d) >= 3) THEN
299 CALL fft_radix_operations(nfft(d), radix_length, fft_radix_next)
300 IF (radix_length == nfft(d)) THEN
301 compatible = .true.
302 EXIT
303 END IF
304 END IF
305 nfft(d) = nfft(d) + n(d)
306 END DO
307 IF (.NOT. compatible) regular = .false.
308 END DO
309 END IF
310 IF (.NOT. regular) THEN
311 CALL direct_k_to_cell(values_k, xkp, index_to_cell, values_rs)
312 IF (PRESENT(used_fft)) used_fft = .false.
313 DEALLOCATE (grid_index)
314 CALL timestop(handle)
315 RETURN
316 END IF
317
318 CALL fft_alloc(fft_in, nfft)
319 CALL fft_alloc(fft_out, nfft)
320 ALLOCATE (cell_index(3, SIZE(index_to_cell, 2)))
321 DO icell = 1, SIZE(index_to_cell, 2)
322 DO d = 1, 3
323 cell_index(d, icell) = modulo(index_to_cell(d, icell), n(d))*(nfft(d)/n(d)) + 1
324 END DO
325 END DO
326 normalization = real(product(nfft), kind=dp)/real(product(n), kind=dp)
327
328 stat = 0
329 DO j = 1, SIZE(values_k, 2)
330 DO i = 1, SIZE(values_k, 1)
331 fft_in = z_zero
332 DO ik = 1, SIZE(values_k, 3)
333 fft_in(grid_index(1, ik), grid_index(2, ik), grid_index(3, ik)) = values_k(i, j, ik)
334 END DO
335 CALL fft3d(fwfft, nfft, fft_in, fft_out, status=stat)
336 IF (stat /= 0) EXIT
337 DO icell = 1, SIZE(index_to_cell, 2)
338 arg = sum(k_offset*real(index_to_cell(1:3, icell), kind=dp))
339 phase = exp(-gaussi*twopi*arg)
340 values_rs(i, j, icell) = normalization*phase* &
341 fft_out(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell))
342 END DO
343 END DO
344 IF (stat /= 0) EXIT
345 END DO
346
347 IF (stat /= 0) THEN
348 CALL direct_k_to_cell(values_k, xkp, index_to_cell, values_rs)
349 regular = .false.
350 END IF
351 IF (PRESENT(used_fft)) used_fft = regular
352
353 CALL fft_dealloc(fft_in)
354 CALL fft_dealloc(fft_out)
355 DEALLOCATE (cell_index, grid_index)
356 CALL timestop(handle)
357
358 END SUBROUTINE k_grid_to_cell_fft
359
360! **************************************************************************************************
361!> \brief Direct inverse phase sum for arbitrary reciprocal-point lists.
362!> \param values_k ...
363!> \param xkp ...
364!> \param index_to_cell ...
365!> \param values_rs ...
366! **************************************************************************************************
367 SUBROUTINE direct_k_to_cell(values_k, xkp, index_to_cell, values_rs)
368
369 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(IN) :: values_k
370 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: xkp
371 INTEGER, DIMENSION(:, :), INTENT(IN) :: index_to_cell
372 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT) :: values_rs
373
374 COMPLEX(KIND=dp) :: factor
375 INTEGER :: icell, ik
376 REAL(kind=dp) :: arg, normalization
377
378 values_rs = z_zero
379 normalization = 1.0_dp/real(SIZE(xkp, 2), kind=dp)
380 DO icell = 1, SIZE(index_to_cell, 2)
381 DO ik = 1, SIZE(xkp, 2)
382 arg = sum(xkp(1:3, ik)*real(index_to_cell(1:3, icell), kind=dp))
383 factor = normalization*exp(-gaussi*twopi*arg)
384 values_rs(:, :, icell) = values_rs(:, :, icell) + factor*values_k(:, :, ik)
385 END DO
386 END DO
387
388 END SUBROUTINE direct_k_to_cell
389
390! **************************************************************************************************
391!> \brief Direct phase-sum fallback for arbitrary reciprocal-point lists.
392!> \param values_rs ...
393!> \param index_to_cell ...
394!> \param xkp ...
395!> \param values_k ...
396!> \param deriv_direction ...
397!> \param hmat ...
398!> \param selected_kpoints ...
399! **************************************************************************************************
400 SUBROUTINE direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, &
401 deriv_direction, hmat, selected_kpoints)
402
403 REAL(kind=dp), DIMENSION(:, :, :), INTENT(IN) :: values_rs
404 INTEGER, DIMENSION(:, :), INTENT(IN) :: index_to_cell
405 REAL(kind=dp), DIMENSION(:, :), INTENT(IN) :: xkp
406 COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT) :: values_k
407 INTEGER, INTENT(IN), OPTIONAL :: deriv_direction
408 REAL(kind=dp), DIMENSION(3, 3), INTENT(IN), &
409 OPTIONAL :: hmat
410 INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL :: selected_kpoints
411
412 COMPLEX(KIND=dp) :: factor
413 INTEGER :: icell, ik, ik_out
414 REAL(kind=dp) :: arg
415 REAL(kind=dp), DIMENSION(3) :: cell_vector
416
417 values_k = z_zero
418 DO ik_out = 1, SIZE(values_k, 3)
419 ik = ik_out
420 IF (PRESENT(selected_kpoints)) ik = selected_kpoints(ik_out)
421 DO icell = 1, SIZE(values_rs, 3)
422 arg = sum(xkp(1:3, ik)*real(index_to_cell(1:3, icell), kind=dp))
423 factor = exp(gaussi*twopi*arg)
424 IF (PRESENT(deriv_direction)) THEN
425 cell_vector = matmul(hmat, real(index_to_cell(1:3, icell), kind=dp))
426 factor = factor*gaussi*cell_vector(deriv_direction)
427 END IF
428 values_k(:, :, ik_out) = values_k(:, :, ik_out) + factor*values_rs(:, :, icell)
429 END DO
430 END DO
431
432 END SUBROUTINE direct_cell_to_k
433
434END MODULE kpoint_lattice_fft
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
subroutine, public fft_radix_operations(radix_in, radix_out, operation)
Determine the allowed lengths of FFT's '''.
Definition fft_tools.F:231
integer, parameter, public bwfft
Definition fft_tools.F:146
integer, parameter, public fwfft
Definition fft_tools.F:146
integer, parameter, public fft_radix_next
Definition fft_tools.F:147
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
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.
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