LCOV - code coverage report
Current view: top level - src - kpoint_lattice_fft.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:24d69ee) Lines: 91.2 % 114 104
Test Date: 2026-09-03 07:32:15 Functions: 100.0 % 3 3

            Line data    Source code
       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              : ! **************************************************************************************************
      15              : MODULE kpoint_lattice_fft
      16              :    USE fft_tools,                       ONLY: BWFFT,&
      17              :                                               FFT_RADIX_NEXT,&
      18              :                                               fft3d,&
      19              :                                               fft_alloc,&
      20              :                                               fft_dealloc,&
      21              :                                               fft_radix_operations,&
      22              :                                               fft_type
      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, &
      36              :              regular_kpoint_grid
      37              : 
      38              : CONTAINS
      39              : 
      40              : ! **************************************************************************************************
      41              : !> \brief Test and map a complete, uniformly shifted reciprocal grid.
      42              : !> \param xkp reciprocal coordinates, in units of reciprocal lattice vectors
      43              : !> \param nkp_grid regular-grid dimensions
      44              : !> \param grid_index FFT-grid index for every k point
      45              : !> \param k_offset common reciprocal-coordinate offset
      46              : !> \return true for a complete regular grid
      47              : ! **************************************************************************************************
      48           28 :    LOGICAL FUNCTION regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset) RESULT(regular)
      49              : 
      50              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: xkp
      51              :       INTEGER, DIMENSION(3), INTENT(IN)                  :: nkp_grid
      52              :       INTEGER, DIMENSION(:, :), INTENT(OUT), OPTIONAL    :: grid_index
      53              :       REAL(KIND=dp), DIMENSION(3), INTENT(OUT), OPTIONAL :: k_offset
      54              : 
      55              :       REAL(KIND=dp), PARAMETER                           :: map_tolerance = 2.0E-10_dp
      56              : 
      57              :       INTEGER                                            :: d, ik, m, nkp
      58              :       INTEGER, DIMENSION(3)                              :: index
      59           28 :       LOGICAL, ALLOCATABLE, DIMENSION(:, :, :)           :: occupied
      60              :       REAL(KIND=dp)                                      :: offset(3), scaled
      61              : 
      62           28 :       regular = .FALSE.
      63          112 :       IF (SIZE(xkp, 1) < 3 .OR. ANY(nkp_grid <= 0)) RETURN
      64           28 :       nkp = SIZE(xkp, 2)
      65          112 :       IF (PRODUCT(nkp_grid) /= nkp) RETURN
      66           28 :       IF (PRESENT(grid_index)) THEN
      67           18 :          IF (SIZE(grid_index, 1) < 3 .OR. SIZE(grid_index, 2) < nkp) RETURN
      68              :       END IF
      69              : 
      70          112 :       DO d = 1, 3
      71           84 :          scaled = MODULO(REAL(nkp_grid(d), KIND=dp)*xkp(d, 1), 1.0_dp)
      72           84 :          IF (ABS(scaled) < map_tolerance .OR. ABS(scaled - 1.0_dp) < map_tolerance) scaled = 0.0_dp
      73          112 :          offset(d) = scaled/REAL(nkp_grid(d), KIND=dp)
      74              :       END DO
      75              : 
      76          140 :       ALLOCATE (occupied(nkp_grid(1), nkp_grid(2), nkp_grid(3)), source=.FALSE.)
      77         1628 :       DO ik = 1, nkp
      78         6406 :          DO d = 1, 3
      79         4806 :             scaled = REAL(nkp_grid(d), KIND=dp)*(xkp(d, ik) - offset(d))
      80         4806 :             m = NINT(scaled)
      81         4806 :             IF (ABS(scaled - REAL(m, KIND=dp)) > map_tolerance) THEN
      82            2 :                DEALLOCATE (occupied)
      83            2 :                RETURN
      84              :             END IF
      85         6404 :             INDEX(d) = MODULO(m, nkp_grid(d)) + 1
      86              :          END DO
      87         1600 :          IF (occupied(INDEX(1), INDEX(2), INDEX(3))) THEN
      88            0 :             DEALLOCATE (occupied)
      89            0 :             RETURN
      90              :          END IF
      91         1600 :          occupied(INDEX(1), INDEX(2), INDEX(3)) = .TRUE.
      92         4506 :          IF (PRESENT(grid_index)) grid_index(1:3, ik) = index
      93              :       END DO
      94         2084 :       regular = ALL(occupied)
      95           26 :       DEALLOCATE (occupied)
      96              : 
      97           26 :       IF (regular .AND. PRESENT(k_offset)) k_offset = offset
      98              : 
      99              :    END FUNCTION regular_kpoint_grid
     100              : 
     101              : ! **************************************************************************************************
     102              : !> \brief Transform a batch of real matrices from image cells to every supplied k point.
     103              : !> \param values_rs real-space matrices; the last dimension enumerates image cells
     104              : !> \param index_to_cell integer lattice vector for every image cell
     105              : !> \param xkp reciprocal coordinates of all requested k points
     106              : !> \param nkp_grid dimensions of the candidate regular reciprocal grid
     107              : !> \param values_k complex matrices at every k point
     108              : !> \param used_fft reports whether the regular-grid FFT path was used
     109              : !> \param deriv_direction optional Cartesian k derivative (1, 2, or 3)
     110              : !> \param hmat direct-lattice cell matrix, required for a derivative
     111              : !> \param selected_kpoints optional indices of k points to retain in values_k
     112              : ! **************************************************************************************************
     113           36 :    SUBROUTINE cell_to_k_grid_fft(values_rs, index_to_cell, xkp, nkp_grid, values_k, &
     114           18 :                                  used_fft, deriv_direction, hmat, selected_kpoints)
     115              : 
     116              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN)      :: values_rs
     117              :       INTEGER, DIMENSION(:, :), INTENT(IN)               :: index_to_cell
     118              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: xkp
     119              :       INTEGER, DIMENSION(3), INTENT(IN)                  :: nkp_grid
     120              :       COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT)  :: values_k
     121              :       LOGICAL, INTENT(OUT), OPTIONAL                     :: used_fft
     122              :       INTEGER, INTENT(IN), OPTIONAL                      :: deriv_direction
     123              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN), &
     124              :          OPTIONAL                                        :: hmat
     125              :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: selected_kpoints
     126              : 
     127           18 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:)        :: cell_factor
     128              :       COMPLEX(KIND=dp), CONTIGUOUS, DIMENSION(:, :, :), &
     129           18 :          POINTER                                         :: fft_in, fft_out
     130              :       INTEGER                                            :: attempt, d, handle, i, icell, ik, &
     131              :                                                             ik_out, j, nout, radix_length, stat
     132           18 :       INTEGER, ALLOCATABLE, DIMENSION(:, :)              :: cell_index, grid_index
     133              :       INTEGER, DIMENSION(3)                              :: n, nfft
     134              :       LOGICAL                                            :: compatible, regular
     135              :       REAL(KIND=dp)                                      :: arg
     136              :       REAL(KIND=dp), DIMENSION(3)                        :: cell_vector, k_offset
     137              : 
     138           18 :       CALL timeset("cell_to_k_grid_fft", handle)
     139              : 
     140           18 :       IF (SIZE(index_to_cell, 1) < 3 .OR. &
     141              :           SIZE(index_to_cell, 2) /= SIZE(values_rs, 3)) THEN
     142            0 :          CPABORT("Inconsistent real-space image-cell mapping in lattice FFT")
     143              :       END IF
     144           18 :       nout = SIZE(xkp, 2)
     145           18 :       IF (PRESENT(selected_kpoints)) THEN
     146            2 :          nout = SIZE(selected_kpoints)
     147           14 :          IF (ANY(selected_kpoints < 1) .OR. ANY(selected_kpoints > SIZE(xkp, 2))) THEN
     148            0 :             CPABORT("Selected lattice-FFT k-point index is out of range")
     149              :          END IF
     150              :       END IF
     151              :       IF (SIZE(values_k, 1) /= SIZE(values_rs, 1) .OR. &
     152           18 :           SIZE(values_k, 2) /= SIZE(values_rs, 2) .OR. &
     153              :           SIZE(values_k, 3) /= nout) THEN
     154            0 :          CPABORT("Inconsistent input and output matrix batches in lattice FFT")
     155              :       END IF
     156           18 :       IF (PRESENT(deriv_direction)) THEN
     157            2 :          IF (.NOT. PRESENT(hmat)) CPABORT("Lattice-FFT derivative requested without a cell matrix")
     158            2 :          IF (deriv_direction < 1 .OR. deriv_direction > 3) THEN
     159            0 :             CPABORT("Lattice-FFT derivative direction must be 1, 2, or 3")
     160              :          END IF
     161              :       END IF
     162              : 
     163           54 :       ALLOCATE (grid_index(3, SIZE(xkp, 2)))
     164           18 :       regular = regular_kpoint_grid(xkp, nkp_grid, grid_index, k_offset)
     165           18 :       n = nkp_grid
     166           18 :       nfft = n
     167           18 :       IF (regular .AND. fft_type /= 3) THEN
     168           32 :          DO d = 1, 3
     169              :             ! FFTSG rejects some short or non-radix lengths.  Embed the N-point
     170              :             ! transform in a supported integer multiple L: real-space samples
     171              :             ! occupy every L/N-th slot, while the first N output frequencies
     172              :             ! are exactly the desired N-point DFT.  This keeps common 4x4x1
     173              :             ! and 2D/1D Monkhorst-Pack grids on the FFT path.
     174           30 :             compatible = .FALSE.
     175           30 :             DO attempt = 0, 15
     176           30 :                IF (nfft(d) >= 3) THEN
     177           26 :                   CALL fft_radix_operations(nfft(d), radix_length, FFT_RADIX_NEXT)
     178           26 :                   IF (radix_length == nfft(d)) THEN
     179              :                      compatible = .TRUE.
     180              :                      EXIT
     181              :                   END IF
     182              :                END IF
     183            6 :                nfft(d) = nfft(d) + n(d)
     184              :             END DO
     185           32 :             IF (.NOT. compatible) regular = .FALSE.
     186              :          END DO
     187              :       END IF
     188           16 :       IF (.NOT. regular) THEN
     189              :          CALL direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat, &
     190            4 :                                selected_kpoints)
     191            2 :          IF (PRESENT(used_fft)) used_fft = .FALSE.
     192            2 :          DEALLOCATE (grid_index)
     193            2 :          CALL timestop(handle)
     194              :          RETURN
     195              :       END IF
     196              : 
     197           16 :       NULLIFY (fft_in, fft_out)
     198           16 :       CALL fft_alloc(fft_in, nfft)
     199           16 :       CALL fft_alloc(fft_out, nfft)
     200           80 :       ALLOCATE (cell_index(3, SIZE(values_rs, 3)), cell_factor(SIZE(values_rs, 3)))
     201         1696 :       DO icell = 1, SIZE(values_rs, 3)
     202         6720 :          DO d = 1, 3
     203         6720 :             cell_index(d, icell) = MODULO(index_to_cell(d, icell), n(d))*(nfft(d)/n(d)) + 1
     204              :          END DO
     205         6720 :          arg = SUM(k_offset*REAL(index_to_cell(1:3, icell), KIND=dp))
     206         1680 :          cell_factor(icell) = EXP(gaussi*twopi*arg)
     207         1696 :          IF (PRESENT(deriv_direction)) THEN
     208          992 :             cell_vector = MATMUL(hmat, REAL(index_to_cell(1:3, icell), KIND=dp))
     209           62 :             cell_factor(icell) = cell_factor(icell)*gaussi*cell_vector(deriv_direction)
     210              :          END IF
     211              :       END DO
     212              : 
     213           16 :       stat = 0
     214           48 :       DO j = 1, SIZE(values_rs, 2)
     215         3680 :          DO i = 1, SIZE(values_rs, 1)
     216         3648 :             fft_in = z_zero
     217       649536 :             DO icell = 1, SIZE(values_rs, 3)
     218              :                fft_in(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell)) = &
     219              :                   fft_in(cell_index(1, icell), cell_index(2, icell), cell_index(3, icell)) + &
     220       649536 :                   cell_factor(icell)*values_rs(i, j, icell)
     221              :             END DO
     222         3648 :             CALL fft3d(BWFFT, nfft, fft_in, fft_out, status=stat)
     223         3648 :             IF (stat /= 0) EXIT
     224       235844 :             DO ik_out = 1, nout
     225       232164 :                ik = ik_out
     226       232164 :                IF (PRESENT(selected_kpoints)) ik = selected_kpoints(ik_out)
     227              :                values_k(i, j, ik_out) = &
     228       235812 :                   fft_out(grid_index(1, ik), grid_index(2, ik), grid_index(3, ik))
     229              :             END DO
     230              :          END DO
     231           48 :          IF (stat /= 0) EXIT
     232              :       END DO
     233              : 
     234           16 :       IF (stat /= 0) THEN
     235              :          CALL direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, deriv_direction, hmat, &
     236            0 :                                selected_kpoints)
     237            0 :          regular = .FALSE.
     238              :       END IF
     239           16 :       IF (PRESENT(used_fft)) used_fft = regular
     240              : 
     241           16 :       CALL fft_dealloc(fft_in)
     242           16 :       CALL fft_dealloc(fft_out)
     243           16 :       DEALLOCATE (cell_factor, cell_index, grid_index)
     244           16 :       CALL timestop(handle)
     245              : 
     246           36 :    END SUBROUTINE cell_to_k_grid_fft
     247              : 
     248              : ! **************************************************************************************************
     249              : !> \brief Direct phase-sum fallback for arbitrary reciprocal-point lists.
     250              : !> \param values_rs ...
     251              : !> \param index_to_cell ...
     252              : !> \param xkp ...
     253              : !> \param values_k ...
     254              : !> \param deriv_direction ...
     255              : !> \param hmat ...
     256              : !> \param selected_kpoints ...
     257              : ! **************************************************************************************************
     258            4 :    SUBROUTINE direct_cell_to_k(values_rs, index_to_cell, xkp, values_k, &
     259            2 :                                deriv_direction, hmat, selected_kpoints)
     260              : 
     261              :       REAL(KIND=dp), DIMENSION(:, :, :), INTENT(IN)      :: values_rs
     262              :       INTEGER, DIMENSION(:, :), INTENT(IN)               :: index_to_cell
     263              :       REAL(KIND=dp), DIMENSION(:, :), INTENT(IN)         :: xkp
     264              :       COMPLEX(KIND=dp), DIMENSION(:, :, :), INTENT(OUT)  :: values_k
     265              :       INTEGER, INTENT(IN), OPTIONAL                      :: deriv_direction
     266              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN), &
     267              :          OPTIONAL                                        :: hmat
     268              :       INTEGER, DIMENSION(:), INTENT(IN), OPTIONAL        :: selected_kpoints
     269              : 
     270              :       COMPLEX(KIND=dp)                                   :: factor
     271              :       INTEGER                                            :: icell, ik, ik_out
     272              :       REAL(KIND=dp)                                      :: arg
     273              :       REAL(KIND=dp), DIMENSION(3)                        :: cell_vector
     274              : 
     275         1282 :       values_k = z_zero
     276          130 :       DO ik_out = 1, SIZE(values_k, 3)
     277          128 :          ik = ik_out
     278          128 :          IF (PRESENT(selected_kpoints)) ik = selected_kpoints(ik_out)
     279         4098 :          DO icell = 1, SIZE(values_rs, 3)
     280        15872 :             arg = SUM(xkp(1:3, ik)*REAL(index_to_cell(1:3, icell), KIND=dp))
     281         3968 :             factor = EXP(gaussi*twopi*arg)
     282         3968 :             IF (PRESENT(deriv_direction)) THEN
     283            0 :                cell_vector = MATMUL(hmat, REAL(index_to_cell(1:3, icell), KIND=dp))
     284            0 :                factor = factor*gaussi*cell_vector(deriv_direction)
     285              :             END IF
     286        39808 :             values_k(:, :, ik_out) = values_k(:, :, ik_out) + factor*values_rs(:, :, icell)
     287              :          END DO
     288              :       END DO
     289              : 
     290            2 :    END SUBROUTINE direct_cell_to_k
     291              : 
     292              : END MODULE kpoint_lattice_fft
        

Generated by: LCOV version 2.0-1