LCOV - code coverage report
Current view: top level - src - kpoint_mo_symmetry_methods.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 88.1 % 143 126
Test Date: 2026-11-04 00:58:17 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 Utilities for transforming k-point MO coefficients under symmetry operations
      10              : ! **************************************************************************************************
      11              : MODULE kpoint_mo_symmetry_methods
      12              :    USE cp_cfm_types,                    ONLY: cp_cfm_get_info,&
      13              :                                               cp_cfm_get_submatrix,&
      14              :                                               cp_cfm_set_submatrix,&
      15              :                                               cp_cfm_to_cfm,&
      16              :                                               cp_cfm_type
      17              :    USE kinds,                           ONLY: dp
      18              :    USE kpoint_types,                    ONLY: find_kpoint_rotation_slot,&
      19              :                                               kind_rotmat_type,&
      20              :                                               kpoint_sym_type,&
      21              :                                               kpoint_type
      22              :    USE mathconstants,                   ONLY: twopi
      23              : #include "./base/base_uses.f90"
      24              : 
      25              :    IMPLICIT NONE
      26              :    PRIVATE
      27              : 
      28              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'kpoint_mo_symmetry_methods'
      29              : 
      30              :    PUBLIC :: kpoint_same_periodic, &
      31              :              kpoint_transform_scf_mo, kpoint_cell_gauge_action
      32              : 
      33              : CONTAINS
      34              : 
      35              : ! **************************************************************************************************
      36              : !> \brief AO action in the cell Fourier gauge H(k)=sum_R exp(+ikR) H(R).
      37              : !> \param kpoints Symmetry geometry and Gaussian basis rotation blocks
      38              : !> \param representative Source irreducible point
      39              : !> \param operation Orbit operation; zero identity, minus one pure time reversal
      40              : !> \param TARGET Target fractional k point, modulo reciprocal vectors
      41              : !> \param action Scalar AO action, excluding conjugation and physical spin rotation
      42              : !> \param rotation Cartesian spatial operation
      43              : !> \param antiunitary Whether the source coefficients must be conjugated
      44              : !> \param status Zero on success, negative for unavailable or inconsistent mappings
      45              : !> \note An atom shifted to image L contributes exp(-i*k_target*L). No atomic-gauge
      46              : !>       reciprocal-vector phase or legacy SCF phase calibration belongs in this cell gauge.
      47              : ! **************************************************************************************************
      48           16 :    SUBROUTINE kpoint_cell_gauge_action(kpoints, representative, operation, TARGET, action, &
      49              :                                        rotation, antiunitary, status)
      50              :       TYPE(kpoint_type), POINTER                         :: kpoints
      51              :       INTEGER, INTENT(IN)                                :: representative, operation
      52              :       REAL(dp), INTENT(IN)                               :: target(3)
      53              :       COMPLEX(dp), INTENT(OUT)                           :: action(:, :)
      54              :       REAL(dp), INTENT(OUT)                              :: rotation(3, 3)
      55              :       LOGICAL, INTENT(OUT)                               :: antiunitary
      56              :       INTEGER, INTENT(OUT)                               :: status
      57              : 
      58              :       INTEGER                                            :: a, b, first, i, kind, n, na, nb, slot
      59           16 :       INTEGER, ALLOCATABLE                               :: offset(:), seen(:), width(:)
      60              :       REAL(dp)                                           :: angle
      61              :       TYPE(kpoint_sym_type), POINTER                     :: sym
      62              : 
      63         8128 :       action = 0.0_dp
      64           16 :       rotation = 0.0_dp
      65           16 :       antiunitary = .FALSE.
      66           16 :       status = -1
      67           16 :       n = SIZE(action, 1)
      68           16 :       IF (n < 1 .OR. SIZE(action, 2) /= n .OR. .NOT. ASSOCIATED(kpoints)) RETURN
      69           16 :       IF (representative < 1 .OR. representative > kpoints%nkp) RETURN
      70           16 :       IF (operation == 0 .OR. operation == -1) THEN
      71            2 :          antiunitary = operation == -1
      72            2 :          IF (operation == 0) THEN
      73            0 :             IF (.NOT. kpoint_same_periodic(TARGET, kpoints%xkp(:, representative))) RETURN
      74              :          ELSE
      75            8 :             IF (.NOT. kpoint_same_periodic(TARGET, -kpoints%xkp(:, representative))) RETURN
      76              :          END IF
      77           28 :          DO i = 1, n
      78           28 :             action(i, i) = 1.0_dp
      79              :          END DO
      80            8 :          DO i = 1, 3
      81            8 :             rotation(i, i) = 1.0_dp
      82              :          END DO
      83            2 :          status = 0
      84            2 :          RETURN
      85              :       END IF
      86           14 :       IF (operation < 1 .OR. .NOT. ASSOCIATED(kpoints%kp_sym)) RETURN
      87           14 :       sym => kpoints%kp_sym(representative)%kpoint_sym
      88           14 :       IF (.NOT. ASSOCIATED(sym)) RETURN
      89           14 :       IF (.NOT. sym%apply_symmetry .OR. operation > sym%nwred) RETURN
      90           14 :       IF (.NOT. kpoint_same_periodic(TARGET, sym%xkp(:, operation))) RETURN
      91           14 :       IF (.NOT. ASSOCIATED(kpoints%atype) .OR. .NOT. ASSOCIATED(kpoints%kind_rotmat)) RETURN
      92           14 :       IF (.NOT. ASSOCIATED(sym%fcell_gauge)) RETURN
      93           14 :       slot = find_kpoint_rotation_slot(kpoints, sym%rotp(operation))
      94           14 :       IF (slot == 0) RETURN
      95           14 :       na = SIZE(kpoints%atype)
      96           70 :       ALLOCATE (offset(na), width(na), seen(na))
      97           14 :       first = 1
      98           14 :       seen = 0
      99           38 :       DO a = 1, na
     100           24 :          kind = kpoints%atype(a)
     101           24 :          IF (kind < 1 .OR. kind > SIZE(kpoints%kind_rotmat, 2)) RETURN
     102           24 :          IF (.NOT. ASSOCIATED(kpoints%kind_rotmat(slot, kind)%rmat)) RETURN
     103           24 :          offset(a) = first
     104           24 :          width(a) = SIZE(kpoints%kind_rotmat(slot, kind)%rmat, 2)
     105           38 :          first = first + width(a)
     106              :       END DO
     107           14 :       IF (first /= n + 1) RETURN
     108           38 :       DO a = 1, na
     109           24 :          b = sym%f0(a, operation)
     110           24 :          IF (b < 1 .OR. b > na) RETURN
     111           24 :          seen(b) = seen(b) + 1
     112           24 :          IF (seen(b) /= 1 .OR. kpoints%atype(b) /= kpoints%atype(a)) RETURN
     113           24 :          kind = kpoints%atype(a)
     114           24 :          nb = width(b)
     115           24 :          IF (SIZE(kpoints%kind_rotmat(slot, kind)%rmat, 1) /= nb) RETURN
     116           96 :          angle = -twopi*DOT_PRODUCT(TARGET, REAL(sym%fcell_gauge(:, a, operation), dp))
     117              :          action(offset(b):offset(b) + nb - 1, offset(a):offset(a) + width(a) - 1) = &
     118         4406 :             CMPLX(COS(angle), SIN(angle), dp)*kpoints%kind_rotmat(slot, kind)%rmat
     119              :       END DO
     120          182 :       rotation = sym%rot(:, :, operation)
     121           14 :       antiunitary = sym%rotp(operation) < 0
     122           14 :       status = 0
     123           16 :    END SUBROUTINE kpoint_cell_gauge_action
     124              : 
     125              : ! **************************************************************************************************
     126              : !> \brief Compare two fractional k-points modulo reciprocal lattice vectors.
     127              : !> \param xkp_a first k-point
     128              : !> \param xkp_b second k-point
     129              : !> \return true if the k-points are equivalent
     130              : ! **************************************************************************************************
     131       155900 :    LOGICAL FUNCTION kpoint_same_periodic(xkp_a, xkp_b) RESULT(same)
     132              :       REAL(KIND=dp), DIMENSION(3), INTENT(IN)            :: xkp_a, xkp_b
     133              : 
     134              :       REAL(KIND=dp), DIMENSION(3)                        :: diff
     135              : 
     136       623600 :       diff(1:3) = xkp_a(1:3) - xkp_b(1:3)
     137       623600 :       diff(1:3) = diff(1:3) - REAL(NINT(diff(1:3)), KIND=dp)
     138       623600 :       same = SUM(ABS(diff(1:3))) < 1.0e-8_dp
     139              : 
     140       155900 :    END FUNCTION kpoint_same_periodic
     141              : 
     142              : ! **************************************************************************************************
     143              : !> \brief Transform one SCF MO coefficient matrix to an equivalent full-mesh k-point.
     144              : !> \param src source MO coefficients
     145              : !> \param dst transformed MO coefficients
     146              : !> \param qs_kpoint SCF k-point object containing symmetry operations
     147              : !> \param ikred representative k-point index
     148              : !> \param isym symmetry operation index; 0 direct copy, -1 time reversal
     149              : !> \param success true if the transformation was performed
     150              : !> \param reason diagnostic message
     151              : ! **************************************************************************************************
     152         2728 :    SUBROUTINE kpoint_transform_scf_mo(src, dst, qs_kpoint, ikred, &
     153              :                                       isym, success, reason)
     154              :       TYPE(cp_cfm_type), INTENT(IN)                      :: src, dst
     155              :       TYPE(kpoint_type), POINTER                         :: qs_kpoint
     156              :       INTEGER, INTENT(IN)                                :: ikred, isym
     157              :       LOGICAL, INTENT(OUT)                               :: success
     158              :       CHARACTER(LEN=*), INTENT(OUT)                      :: reason
     159              : 
     160              :       COMPLEX(KIND=dp)                                   :: coeff, phase
     161         2728 :       COMPLEX(KIND=dp), ALLOCATABLE, DIMENSION(:, :)     :: dst_coeff, src_coeff
     162              :       INTEGER :: iao, iatom, ikind, imo, irow, irow_source, irow_target, irow_work, nao, natom, &
     163              :          nmo, rot_slot, source_atom, source_dim, target_atom, target_dim
     164         2728 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: ao_size, ao_start
     165              :       LOGICAL                                            :: reverse_phase, time_reversal
     166              :       REAL(KIND=dp)                                      :: arg, coskl, sinkl
     167              :       REAL(KIND=dp), DIMENSION(3)                        :: xkp_phase
     168         2728 :       REAL(KIND=dp), DIMENSION(:, :), POINTER            :: rotmat
     169         2728 :       TYPE(kind_rotmat_type), DIMENSION(:), POINTER      :: kind_rot
     170              :       TYPE(kpoint_sym_type), POINTER                     :: kpsym
     171              : 
     172         2728 :       success = .FALSE.
     173         2728 :       reason = ""
     174         2728 :       IF (isym == 0) THEN
     175          126 :          CALL cp_cfm_to_cfm(src, dst)
     176          126 :          success = .TRUE.
     177          126 :          RETURN
     178              :       END IF
     179              : 
     180         2602 :       CALL cp_cfm_get_info(src, nrow_global=nao, ncol_global=nmo)
     181        15612 :       ALLOCATE (src_coeff(nao, nmo), dst_coeff(nao, nmo))
     182         2602 :       CALL cp_cfm_get_submatrix(src, src_coeff)
     183         2602 :       dst_coeff(:, :) = CMPLX(0.0_dp, 0.0_dp, KIND=dp)
     184              : 
     185         2602 :       IF (isym == -1) THEN
     186        10874 :          dst_coeff(:, :) = CONJG(src_coeff)
     187           62 :          CALL cp_cfm_set_submatrix(dst, dst_coeff)
     188           62 :          success = .TRUE.
     189           62 :          RETURN
     190              :       END IF
     191              : 
     192         2540 :       IF (.NOT. ASSOCIATED(qs_kpoint%kp_sym)) THEN
     193            0 :          reason = "SCF symmetry operation data are not available"
     194            0 :          RETURN
     195              :       END IF
     196         2540 :       kpsym => qs_kpoint%kp_sym(ikred)%kpoint_sym
     197         2540 :       IF (.NOT. ASSOCIATED(kpsym)) THEN
     198            0 :          reason = "SCF k-point symmetry operation is not available"
     199            0 :          RETURN
     200              :       END IF
     201         2540 :       IF (isym > kpsym%nwred) THEN
     202            0 :          reason = "SCF k-point symmetry operation index is out of range"
     203            0 :          RETURN
     204              :       END IF
     205         2540 :       IF (.NOT. ASSOCIATED(qs_kpoint%atype) .OR. .NOT. ASSOCIATED(qs_kpoint%kind_rotmat)) THEN
     206            0 :          reason = "SCF atom mappings or basis rotation matrices are not available"
     207            0 :          RETURN
     208              :       END IF
     209              : 
     210         2540 :       rot_slot = find_kpoint_rotation_slot(qs_kpoint, kpsym%rotp(isym))
     211         2540 :       IF (rot_slot == 0) THEN
     212            0 :          reason = "could not match the SCF symmetry operation to a basis rotation"
     213            0 :          RETURN
     214              :       END IF
     215         2540 :       kind_rot => qs_kpoint%kind_rotmat(rot_slot, :)
     216         2540 :       natom = SIZE(qs_kpoint%atype)
     217        10160 :       ALLOCATE (ao_start(natom), ao_size(natom))
     218        16572 :       irow = 1
     219        16572 :       DO iatom = 1, natom
     220        14032 :          ikind = qs_kpoint%atype(iatom)
     221        14032 :          IF (.NOT. ASSOCIATED(kind_rot(ikind)%rmat)) THEN
     222            0 :             reason = "a required basis rotation matrix is not available"
     223            0 :             RETURN
     224              :          END IF
     225        14032 :          ao_start(iatom) = irow
     226        14032 :          ao_size(iatom) = SIZE(kind_rot(ikind)%rmat, 2)
     227        16572 :          irow = irow + ao_size(iatom)
     228              :       END DO
     229         2540 :       IF (irow - 1 /= nao) THEN
     230            0 :          reason = "atom-resolved AO dimensions do not match the MO coefficient matrix"
     231            0 :          RETURN
     232              :       END IF
     233              : 
     234         2540 :       time_reversal = kpsym%rotp(isym) < 0
     235         2540 :       reverse_phase = qs_kpoint%gamma_centered .AND. ANY(MOD(qs_kpoint%nkp_grid, 2) == 0)
     236         2540 :       IF (ASSOCIATED(kpsym%phase_mode)) THEN
     237         2540 :          IF (kpsym%phase_mode(isym) > 0) reverse_phase = kpsym%phase_mode(isym) == 2
     238              :       END IF
     239        10160 :       xkp_phase(1:3) = kpsym%xkp(1:3, isym)
     240        16572 :       DO iatom = 1, natom
     241        14032 :          source_atom = iatom
     242        14032 :          target_atom = kpsym%f0(iatom, isym)
     243        14032 :          ikind = qs_kpoint%atype(source_atom)
     244        14032 :          rotmat => kind_rot(ikind)%rmat
     245        14032 :          source_dim = ao_size(source_atom)
     246        14032 :          target_dim = ao_size(target_atom)
     247        14032 :          IF (SIZE(rotmat, 1) /= target_dim .OR. SIZE(rotmat, 2) /= source_dim) THEN
     248            0 :             reason = "basis rotation dimensions do not match the atom/AO symmetry transform"
     249            0 :             RETURN
     250              :          END IF
     251              :          arg = REAL(kpsym%fcell_gauge(1, source_atom, isym), KIND=dp)*xkp_phase(1) + &
     252              :                REAL(kpsym%fcell_gauge(2, source_atom, isym), KIND=dp)*xkp_phase(2) + &
     253        14032 :                REAL(kpsym%fcell_gauge(3, source_atom, isym), KIND=dp)*xkp_phase(3)
     254              :          ! Match symtrans_phase: SCF AOs use the lattice-periodic gauge, so reciprocal
     255              :          ! folding adds no atom-position phase to the coefficients.
     256        14032 :          IF (reverse_phase) arg = -arg
     257        14032 :          coskl = COS(twopi*arg)
     258        14032 :          sinkl = SIN(twopi*arg)
     259        72780 :          DO imo = 1, nmo
     260       383056 :             DO irow_work = 1, source_dim
     261       312816 :                irow_source = ao_start(source_atom) + irow_work - 1
     262       312816 :                coeff = src_coeff(irow_source, imo)
     263       312816 :                IF (time_reversal) coeff = CONJG(coeff)
     264       312816 :                phase = CMPLX(coskl, sinkl, KIND=dp)*coeff
     265      2338800 :                DO iao = 1, target_dim
     266      1969776 :                   irow_target = ao_start(target_atom) + iao - 1
     267              :                   dst_coeff(irow_target, imo) = dst_coeff(irow_target, imo) + &
     268      2282592 :                                                 rotmat(iao, irow_work)*phase
     269              :                END DO
     270              :             END DO
     271              :          END DO
     272              :       END DO
     273              : 
     274         2540 :       CALL cp_cfm_set_submatrix(dst, dst_coeff)
     275         2540 :       success = .TRUE.
     276              : 
     277         8058 :    END SUBROUTINE kpoint_transform_scf_mo
     278              : 
     279              : END MODULE kpoint_mo_symmetry_methods
        

Generated by: LCOV version 2.0-1