29#include "./base/base_uses.f90"
35 CHARACTER(len=*),
PARAMETER,
PRIVATE :: moduleN =
'kpoint_lattice_fft'
47 LOGICAL :: regular = .false.
48 INTEGER :: nfft(3) = 0
49 INTEGER,
ALLOCATABLE :: cells(:, :), grid_index(:, :), cell_index(:, :)
50 REAL(kind=
dp),
ALLOCATABLE :: xkp(:, :), weight(:)
51 COMPLEX(KIND=dp),
ALLOCATABLE :: cell_phase(:)
52 COMPLEX(KIND=dp),
CONTIGUOUS,
POINTER :: fft_in(:, :, :) => null(), fft_out(:, :, :) => null()
66 LOGICAL FUNCTION regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset, allow_incomplete)
RESULT(regular)
68 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
69 INTEGER,
DIMENSION(3),
INTENT(IN) :: nkp_grid
70 INTEGER,
DIMENSION(:, :),
INTENT(OUT),
OPTIONAL :: grid_index
71 REAL(kind=
dp),
DIMENSION(3),
INTENT(OUT),
OPTIONAL :: k_offset
72 LOGICAL,
INTENT(IN),
OPTIONAL :: allow_incomplete
74 REAL(kind=
dp),
PARAMETER :: map_tolerance = 2.0e-10_dp
76 INTEGER :: d, ik, m, nkp
77 INTEGER,
DIMENSION(3) :: index
78 LOGICAL :: require_complete
79 LOGICAL,
ALLOCATABLE,
DIMENSION(:, :, :) :: occupied
80 REAL(kind=
dp) :: offset(3), scaled
83 IF (
SIZE(xkp, 1) < 3 .OR. any(nkp_grid <= 0))
RETURN
85 require_complete = .true.
86 IF (
PRESENT(allow_incomplete)) require_complete = .NOT. allow_incomplete
87 IF (nkp < 1 .OR. nkp > product(nkp_grid))
RETURN
88 IF (require_complete .AND. product(nkp_grid) /= nkp)
RETURN
89 IF (
PRESENT(grid_index))
THEN
90 IF (
SIZE(grid_index, 1) < 3 .OR.
SIZE(grid_index, 2) < nkp)
RETURN
94 scaled =
modulo(real(nkp_grid(d), kind=
dp)*xkp(d, 1), 1.0_dp)
95 IF (abs(scaled) < map_tolerance .OR. abs(scaled - 1.0_dp) < map_tolerance) scaled = 0.0_dp
96 offset(d) = scaled/real(nkp_grid(d), kind=
dp)
99 ALLOCATE (occupied(nkp_grid(1), nkp_grid(2), nkp_grid(3)), source=.false.)
102 scaled = real(nkp_grid(d), kind=
dp)*(xkp(d, ik) - offset(d))
104 IF (abs(scaled - real(m, kind=
dp)) > map_tolerance)
RETURN
105 index(d) =
modulo(m, nkp_grid(d)) + 1
107 IF (occupied(index(1), index(2), index(3)))
RETURN
108 occupied(index(1), index(2), index(3)) = .true.
109 IF (
PRESENT(grid_index)) grid_index(1:3, ik) = index
113 IF (regular .AND.
PRESENT(k_offset)) k_offset = offset
127 INTEGER,
DIMENSION(3),
INTENT(IN) :: n
128 INTEGER,
DIMENSION(3),
INTENT(OUT) :: nfft
129 LOGICAL,
INTENT(IN),
OPTIONAL :: allow_arbitrary
131 INTEGER :: attempt, d, radix_length
135 IF (any(n <= 0))
RETURN
136 IF (
PRESENT(allow_arbitrary))
THEN
145 IF (nfft(d) >= 3)
THEN
147 IF (radix_length == nfft(d))
EXIT
149 nfft(d) = nfft(d) + n(d)
151 IF (attempt > 15)
RETURN
170 used_fft, deriv_direction, hmat, selected_kpoints)
172 REAL(kind=
dp),
DIMENSION(:, :, :),
INTENT(IN) :: values_rs
173 INTEGER,
DIMENSION(:, :),
INTENT(IN) :: index_to_cell
174 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
175 INTEGER,
DIMENSION(3),
INTENT(IN) :: nkp_grid
176 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(OUT) :: values_k
177 LOGICAL,
INTENT(OUT),
OPTIONAL :: used_fft
178 INTEGER,
INTENT(IN),
OPTIONAL :: deriv_direction
179 REAL(kind=
dp),
DIMENSION(3, 3),
INTENT(IN), &
181 INTEGER,
DIMENSION(:),
INTENT(IN),
OPTIONAL :: selected_kpoints
183 COMPLEX(KIND=dp),
ALLOCATABLE,
DIMENSION(:) :: cell_factor
184 COMPLEX(KIND=dp),
CONTIGUOUS,
DIMENSION(:, :, :), &
185 POINTER :: fft_in, fft_out
186 INTEGER :: d, handle, i, icell, ik, ik_out, j, &
188 INTEGER,
ALLOCATABLE,
DIMENSION(:, :) :: cell_index, grid_index
189 INTEGER,
DIMENSION(3) :: n, nfft
192 REAL(kind=
dp),
DIMENSION(3) :: cell_vector, k_offset
194 CALL timeset(
"cell_to_k_grid_fft", handle)
196 IF (
SIZE(index_to_cell, 1) < 3 .OR. &
197 SIZE(index_to_cell, 2) /=
SIZE(values_rs, 3))
THEN
198 cpabort(
"Inconsistent real-space image-cell mapping in lattice FFT")
201 IF (
PRESENT(selected_kpoints))
THEN
202 nout =
SIZE(selected_kpoints)
203 IF (any(selected_kpoints < 1) .OR. any(selected_kpoints >
SIZE(xkp, 2)))
THEN
204 cpabort(
"Selected lattice-FFT k-point index is out of range")
207 IF (
SIZE(values_k, 1) /=
SIZE(values_rs, 1) .OR. &
208 SIZE(values_k, 2) /=
SIZE(values_rs, 2) .OR. &
209 SIZE(values_k, 3) /= nout)
THEN
210 cpabort(
"Inconsistent input and output matrix batches in lattice FFT")
212 IF (
PRESENT(deriv_direction))
THEN
213 IF (.NOT.
PRESENT(hmat))
THEN
214 CALL cp_abort(__location__,
"Lattice-FFT derivative requested without a cell matrix")
216 IF (deriv_direction < 1 .OR. deriv_direction > 3)
THEN
217 cpabort(
"Lattice-FFT derivative direction must be 1, 2, or 3")
221 ALLOCATE (grid_index(3,
SIZE(xkp, 2)))
225 IF (.NOT. regular)
THEN
226 CALL direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat, &
228 IF (
PRESENT(used_fft)) used_fft = .false.
229 DEALLOCATE (grid_index)
230 CALL timestop(handle)
234 NULLIFY (fft_in, fft_out)
235 CALL fft_alloc(fft_in, nfft)
236 CALL fft_alloc(fft_out, nfft)
237 ALLOCATE (cell_index(3,
SIZE(values_rs, 3)), cell_factor(
SIZE(values_rs, 3)))
238 DO icell = 1,
SIZE(values_rs, 3)
240 cell_index(d, icell) =
modulo(index_to_cell(d, icell), n(d))*(nfft(d)/n(d)) + 1
242 arg = sum(k_offset*real(index_to_cell(1:3, icell), kind=
dp))
244 IF (
PRESENT(deriv_direction))
THEN
245 cell_vector = matmul(hmat, real(index_to_cell(1:3, icell), kind=
dp))
246 cell_factor(icell) = cell_factor(icell)*
gaussi*cell_vector(deriv_direction)
251 DO j = 1,
SIZE(values_rs, 2)
252 DO i = 1,
SIZE(values_rs, 1)
254 DO icell = 1,
SIZE(values_rs, 3)
255 fft_in(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell)) = &
256 fft_in(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell)) + &
257 cell_factor(icell)*values_rs(i, j, icell)
259 CALL fft3d(
bwfft, nfft, fft_in, fft_out, status=stat)
263 IF (
PRESENT(selected_kpoints)) ik = selected_kpoints(ik_out)
264 values_k(i, j, ik_out) = &
265 fft_out(grid_index(1, ik), grid_index(2, ik), grid_index(3, ik))
272 CALL direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat, &
276 IF (
PRESENT(used_fft)) used_fft = regular
278 CALL fft_dealloc(fft_in)
279 CALL fft_dealloc(fft_out)
280 DEALLOCATE (cell_factor, cell_index, grid_index)
281 CALL timestop(handle)
297 weights, allow_incomplete)
299 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(IN) :: values_k
300 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
301 INTEGER,
DIMENSION(3),
INTENT(IN) :: nkp_grid
302 INTEGER,
DIMENSION(:, :),
INTENT(IN) :: index_to_cell
303 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(OUT) :: values_rs
304 LOGICAL,
INTENT(OUT),
OPTIONAL :: used_fft
305 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN),
OPTIONAL :: weights
306 LOGICAL,
INTENT(IN),
OPTIONAL :: allow_incomplete
328 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
329 INTEGER,
DIMENSION(3),
INTENT(IN) :: nkp_grid
330 INTEGER,
DIMENSION(:, :),
INTENT(IN) :: index_to_cell
331 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN),
OPTIONAL :: weights
332 LOGICAL,
INTENT(IN),
OPTIONAL :: allow_incomplete
334 INTEGER :: d, icell, ncell, nkp
335 REAL(kind=
dp),
DIMENSION(3) :: k_offset
339 ncell =
SIZE(index_to_cell, 2)
340 IF (
SIZE(xkp, 1) < 3 .OR.
SIZE(index_to_cell, 1) < 3 .OR. nkp == 0)
THEN
341 CALL cp_abort(__location__,
"Inconsistent reciprocal-grid mapping in inverse lattice FFT")
343 ALLOCATE (work%xkp(3, nkp), work%cells(3, ncell), work%weight(nkp), work%grid_index(3, nkp))
344 work%xkp(:, :) = xkp(1:3, :)
345 work%cells(:, :) = index_to_cell(1:3, :)
346 work%weight(:) = 1.0_dp/real(nkp,
dp)
347 IF (
PRESENT(weights))
THEN
348 cpassert(
SIZE(weights) == nkp)
349 work%weight(:) = weights
351 work%regular =
regular_kpoint_grid(xkp, nkp_grid, work%grid_index, k_offset, allow_incomplete)
352 IF (work%regular) work%regular =
lattice_fft_shape(nkp_grid, work%nfft, allow_incomplete)
353 IF (.NOT. work%regular)
RETURN
355 CALL fft_alloc(work%fft_in, work%nfft)
356 CALL fft_alloc(work%fft_out, work%nfft)
357 ALLOCATE (work%cell_index(3, ncell), work%cell_phase(ncell))
360 work%cell_index(d, icell) =
modulo(index_to_cell(d, icell), nkp_grid(d))* &
361 (work%nfft(d)/nkp_grid(d)) + 1
363 work%cell_phase(icell) = exp(-
gaussi*
twopi*sum(k_offset*real(index_to_cell(1:3, icell),
dp)))
379 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(IN) :: values_k
380 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(OUT) :: values_rs
381 LOGICAL,
INTENT(OUT),
OPTIONAL :: used_fft
383 INTEGER :: handle, i, icell, ik, j, stat
385 CALL timeset(
"k_grid_to_cell_fft", handle)
386 cpassert(
ALLOCATED(work%xkp))
387 IF (
SIZE(values_k, 3) /=
SIZE(work%xkp, 2) .OR. &
388 SIZE(values_rs, 1) /=
SIZE(values_k, 1) .OR. &
389 SIZE(values_rs, 2) /=
SIZE(values_k, 2) .OR. &
390 SIZE(values_rs, 3) /=
SIZE(work%cells, 2))
THEN
391 CALL cp_abort(__location__,
"Inconsistent matrix batches in inverse lattice FFT")
395 IF (work%regular)
THEN
396 DO j = 1,
SIZE(values_k, 2)
397 DO i = 1,
SIZE(values_k, 1)
399 DO ik = 1,
SIZE(values_k, 3)
400 work%fft_in(work%grid_index(1, ik), work%grid_index(2, ik), work%grid_index(3, ik)) = &
401 work%weight(ik)*values_k(i, j, ik)
403 CALL fft3d(
fwfft, work%nfft, work%fft_in, work%fft_out, status=stat)
405 DO icell = 1,
SIZE(values_rs, 3)
407 values_rs(i, j, icell) = real(product(work%nfft),
dp)*work%cell_phase(icell)* &
408 work%fft_out(work%cell_index(1, icell), work%cell_index(2, icell), &
409 work%cell_index(3, icell))
415 IF (stat /= 0) work%regular = .false.
416 IF (.NOT. work%regular)
THEN
417 CALL direct_k_to_cell(values_k, work%xkp, work%cells, values_rs, work%weight)
419 IF (
PRESENT(used_fft)) used_fft = work%regular
420 CALL timestop(handle)
432 IF (
ASSOCIATED(work%fft_in))
CALL fft_dealloc(work%fft_in)
433 IF (
ASSOCIATED(work%fft_out))
CALL fft_dealloc(work%fft_out)
434 IF (
ALLOCATED(work%xkp))
DEALLOCATE (work%xkp, work%cells, work%weight, work%grid_index)
435 IF (
ALLOCATED(work%cell_index))
DEALLOCATE (work%cell_index, work%cell_phase)
436 work%regular = .false.
448 SUBROUTINE direct_k_to_cell(values_k, xkp, index_to_cell, values_rs, weights)
450 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(IN) :: values_k
451 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
452 INTEGER,
DIMENSION(:, :),
INTENT(IN) :: index_to_cell
453 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(OUT) :: values_rs
454 REAL(kind=
dp),
DIMENSION(:),
INTENT(IN),
OPTIONAL :: weights
456 COMPLEX(KIND=dp) :: factor
458 REAL(kind=
dp) :: arg, normalization
461 normalization = 1.0_dp/real(
SIZE(xkp, 2), kind=
dp)
462 DO icell = 1,
SIZE(index_to_cell, 2)
463 DO ik = 1,
SIZE(xkp, 2)
464 IF (
PRESENT(weights)) normalization = weights(ik)
465 arg = sum(xkp(1:3, ik)*real(index_to_cell(1:3, icell), kind=
dp))
467 values_rs(:, :, icell) = values_rs(:, :, icell) + factor*values_k(:, :, ik)
471 END SUBROUTINE direct_k_to_cell
483 SUBROUTINE direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, &
484 deriv_direction, hmat, selected_kpoints)
486 REAL(kind=
dp),
DIMENSION(:, :, :),
INTENT(IN) :: values_rs
487 INTEGER,
DIMENSION(:, :),
INTENT(IN) :: index_to_cell
488 REAL(kind=
dp),
DIMENSION(:, :),
INTENT(IN) :: xkp
489 COMPLEX(KIND=dp),
DIMENSION(:, :, :),
INTENT(OUT) :: values_k
490 INTEGER,
INTENT(IN),
OPTIONAL :: deriv_direction
491 REAL(kind=
dp),
DIMENSION(3, 3),
INTENT(IN), &
493 INTEGER,
DIMENSION(:),
INTENT(IN),
OPTIONAL :: selected_kpoints
495 COMPLEX(KIND=dp) :: factor
496 INTEGER :: icell, ik, ik_out
498 REAL(kind=
dp),
DIMENSION(3) :: cell_vector
501 DO ik_out = 1,
SIZE(values_k, 3)
503 IF (
PRESENT(selected_kpoints)) ik = selected_kpoints(ik_out)
504 DO icell = 1,
SIZE(values_rs, 3)
505 arg = sum(xkp(1:3, ik)*real(index_to_cell(1:3, icell), kind=
dp))
507 IF (
PRESENT(deriv_direction))
THEN
508 cell_vector = matmul(hmat, real(index_to_cell(1:3, icell), kind=
dp))
509 factor = factor*
gaussi*cell_vector(deriv_direction)
511 values_k(:, :, ik_out) = values_k(:, :, ik_out) + factor*values_rs(:, :, icell)
515 END SUBROUTINE direct_cell_to_k
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
logical function, public fft_supports_arbitrary_lengths()
Query whether the active FFT backend supports arbitrary transform lengths.
Defines the basic variable types.
integer, parameter, public dp
Batched lattice Fourier transforms on regular k-point grids.
subroutine, public k_grid_to_cell_prepare(work, xkp, nkp_grid, index_to_cell, weights, allow_incomplete)
Prepare one inverse lattice transform for repeated matrix batches.
logical function, public regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset, allow_incomplete)
Test and map a uniformly shifted reciprocal grid or an explicitly allowed subset.
subroutine, public k_grid_to_cell_execute(work, values_k, values_rs, used_fft)
Execute a matrix batch using the prepared inverse lattice transform. Call outside OpenMP worker regio...
subroutine, public k_grid_to_cell_release(work)
Release call-scoped inverse lattice maps and FFT buffers.
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 lattice_fft_shape(n, nfft, allow_arbitrary)
Find backend-supported FFT dimensions that preserve the original lattice grid. Sample the padded tran...
subroutine, public k_grid_to_cell_fft(values_k, xkp, nkp_grid, index_to_cell, values_rs, used_fft, weights, allow_incomplete)
Transform reciprocal-grid data back to image-cell values.
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