LCOV - code coverage report
Current view: top level - src - pao_param_fock.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:9ccafda) Lines: 97.3 % 74 72
Test Date: 2026-10-15 00:55:44 Functions: 100.0 % 1 1

            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 Common framework for using eigenvectors of a Fock matrix as PAO basis.
      10              : !> \author Ole Schuett
      11              : ! **************************************************************************************************
      12              : MODULE pao_param_fock
      13              :    USE cp_dbcsr_api,                    ONLY: dbcsr_get_block_p,&
      14              :                                               dbcsr_get_info
      15              :    USE kinds,                           ONLY: dp
      16              :    USE mathlib,                         ONLY: diamat_all
      17              :    USE pao_types,                       ONLY: pao_env_type
      18              : #include "./base/base_uses.f90"
      19              : 
      20              :    IMPLICIT NONE
      21              : 
      22              :    PRIVATE
      23              : 
      24              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'pao_param_fock'
      25              : 
      26              :    PUBLIC :: pao_calc_U_block_fock
      27              : 
      28              : CONTAINS
      29              : 
      30              : ! **************************************************************************************************
      31              : !> \brief Calculate new matrix U and optinally its gradient G
      32              : !> \param pao ...
      33              : !> \param iatom ...
      34              : !> \param V ...
      35              : !> \param U ...
      36              : !> \param penalty ...
      37              : !> \param gap ...
      38              : !> \param evals ...
      39              : !> \param M1 ...
      40              : !> \param G ...
      41              : ! **************************************************************************************************
      42         4655 :    SUBROUTINE pao_calc_U_block_fock(pao, iatom, V, U, penalty, gap, evals, M1, G)
      43              :       TYPE(pao_env_type), POINTER                        :: pao
      44              :       INTEGER, INTENT(IN)                                :: iatom
      45              :       REAL(dp), DIMENSION(:, :), POINTER                 :: V, U
      46              :       REAL(dp), INTENT(INOUT), OPTIONAL                  :: penalty
      47              :       REAL(dp), INTENT(OUT)                              :: gap
      48              :       REAL(dp), DIMENSION(:), INTENT(OUT), OPTIONAL      :: evals
      49              :       REAL(dp), DIMENSION(:, :), OPTIONAL, POINTER       :: M1, G
      50              : 
      51              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_calc_U_block_fock'
      52              : 
      53              :       INTEGER                                            :: handle, i, j, m, n
      54         4655 :       INTEGER, DIMENSION(:), POINTER                     :: blk_sizes_pao, blk_sizes_pri
      55              :       LOGICAL                                            :: found
      56              :       REAL(dp)                                           :: alpha, beta, denom, diff
      57         4655 :       REAL(dp), DIMENSION(:), POINTER                    :: H_evals
      58         4655 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_N, D1, D2, H, H0, H_evecs, M2, M3, &
      59         4655 :                                                             M4, M5
      60              : 
      61         4655 :       CALL timeset(routineN, handle)
      62              : 
      63         4655 :       CALL dbcsr_get_block_p(matrix=pao%matrix_H0, row=iatom, col=iatom, block=H0, found=found)
      64         4655 :       CPASSERT(ASSOCIATED(H0))
      65         4655 :       CALL dbcsr_get_block_p(matrix=pao%matrix_N_diag, row=iatom, col=iatom, block=block_N, found=found)
      66         4655 :       CPASSERT(ASSOCIATED(block_N))
      67       144969 :       IF (MAXVAL(ABS(V - TRANSPOSE(V))) > 1e-14_dp) CPABORT("Expect symmetric matrix")
      68              : 
      69              :       ! figure out basis sizes
      70         4655 :       CALL dbcsr_get_info(pao%matrix_Y, row_blk_size=blk_sizes_pri, col_blk_size=blk_sizes_pao)
      71         4655 :       n = blk_sizes_pri(iatom) ! size of primary basis
      72         4655 :       m = blk_sizes_pao(iatom) ! size of pao basis
      73              : 
      74              :       ! calculate H in the orthonormal basis
      75        18620 :       ALLOCATE (H(n, n))
      76      4261994 :       H = MATMUL(MATMUL(block_N, H0 + V), block_N)
      77              : 
      78              :       ! diagonalize H
      79        23275 :       ALLOCATE (H_evals(n), H_evecs(n, n))
      80       285283 :       H_evecs = H
      81         4655 :       CALL diamat_all(H_evecs, H_evals)
      82              : 
      83              :       ! the eigenvectors of H become the rotation matrix U
      84       285283 :       U = H_evecs
      85              : 
      86              :       ! copy eigenvectors around the gap from H_evals into evals array
      87         4655 :       IF (PRESENT(evals)) THEN
      88         3670 :          CPASSERT(MOD(SIZE(evals), 2) == 0) ! gap will be exactely in the middle
      89         3670 :          i = SIZE(evals)/2
      90         3670 :          j = MIN(m, i)
      91         7348 :          evals(1 + i - j:i) = H_evals(1 + m - j:m) ! eigenvalues below gap
      92         3670 :          j = MIN(n - m, i)
      93        22042 :          evals(i:i + j) = H_evals(m:m + j) ! eigenvalues above gap
      94              :       END IF
      95              : 
      96              :       ! calculate homo-lumo gap (it's useful for detecting numerical issues)
      97         4655 :       gap = HUGE(dp)
      98         4655 :       IF (m < n) THEN
      99              :          ! catch special case n==m
     100         4653 :          gap = H_evals(m + 1) - H_evals(m)
     101              :       END IF
     102              : 
     103         4655 :       IF (PRESENT(penalty)) THEN
     104              :          ! penalty terms: occupied and virtual eigenvalues repel each other
     105         4362 :          alpha = pao%penalty_strength
     106         4362 :          beta = pao%penalty_dist
     107         8724 :          DO i = 1, m
     108        26172 :          DO j = m + 1, n
     109        17448 :             diff = H_evals(i) - H_evals(j)
     110        21810 :             penalty = penalty + alpha*EXP(-(diff/beta)**2)
     111              :          END DO
     112              :          END DO
     113              : 
     114              :          ! regularization energy
     115       135222 :          penalty = penalty + pao%regularization*SUM(V**2)
     116              :       END IF
     117              : 
     118         4655 :       IF (PRESENT(G)) THEN ! TURNING POINT (if calc grad) -------------------------
     119              : 
     120          660 :          CPASSERT(PRESENT(M1))
     121              : 
     122              :          ! calculate derivatives between eigenvectors of H
     123         5940 :          ALLOCATE (D1(n, n), M2(n, n), M3(n, n), M4(n, n))
     124         3962 :          DO i = 1, n
     125        20484 :          DO j = 1, n
     126              :             ! ignore changes among occupied or virtual eigenvectors
     127              :             ! They will get filtered out by M2*D1 anyways, however this early
     128              :             ! intervention might stabilize numerics in the case of level-crossings.
     129        19824 :             IF (i <= m .EQV. j <= m) THEN
     130        11238 :                D1(i, j) = 0.0_dp
     131              :             ELSE
     132         5284 :                denom = H_evals(i) - H_evals(j)
     133         5284 :                IF (ABS(denom) > 1e-9_dp) THEN ! avoid division by zero
     134         5284 :                   D1(i, j) = 1.0_dp/denom
     135              :                ELSE
     136            0 :                   D1(i, j) = SIGN(1e+9_dp, denom)
     137              :                END IF
     138              :             END IF
     139              :          END DO
     140              :          END DO
     141          660 :          IF (ASSOCIATED(M1)) THEN
     142       245980 :             M2 = MATMUL(TRANSPOSE(M1), H_evecs)
     143              :          ELSE
     144            0 :             M2 = 0.0_dp
     145              :          END IF
     146        40308 :          M3 = M2*D1 ! Hadamard product
     147       412664 :          M4 = MATMUL(MATMUL(H_evecs, M3), TRANSPOSE(H_evecs))
     148              : 
     149              :          ! gradient contribution from penalty terms
     150          660 :          IF (PRESENT(penalty)) THEN
     151         1878 :             ALLOCATE (D2(n, n))
     152        19406 :             D2 = 0.0_dp
     153         3756 :             DO i = 1, n
     154        19406 :             DO j = 1, n
     155        15650 :                IF (i <= m .EQV. j <= m) CYCLE
     156         5008 :                diff = H_evals(i) - H_evals(j)
     157        18780 :                D2(i, i) = D2(i, i) - 2.0_dp*alpha*diff/beta**2*EXP(-(diff/beta)**2)
     158              :             END DO
     159              :             END DO
     160       530222 :             M4 = M4 + MATMUL(MATMUL(H_evecs, D2), TRANSPOSE(H_evecs))
     161          626 :             DEALLOCATE (D2)
     162              :          END IF
     163              : 
     164              :          ! dH / dV, return to non-orthonormal basis
     165         1980 :          ALLOCATE (M5(n, n))
     166       744712 :          M5 = MATMUL(MATMUL(block_N, M4), block_N)
     167              : 
     168              :          ! add regularization gradient
     169          660 :          IF (PRESENT(penalty)) THEN
     170        38186 :             M5 = M5 + 2.0_dp*pao%regularization*V
     171              :          END IF
     172              : 
     173              :          ! symmetrize
     174        40308 :          G = 0.5_dp*(M5 + TRANSPOSE(M5)) ! the final gradient
     175              : 
     176          660 :          DEALLOCATE (D1, M2, M3, M4, M5)
     177              :       END IF
     178              : 
     179         4655 :       DEALLOCATE (H, H_evals, H_evecs)
     180              : 
     181         4655 :       CALL timestop(handle)
     182         9310 :    END SUBROUTINE pao_calc_U_block_fock
     183              : 
     184         7261 : END MODULE pao_param_fock
        

Generated by: LCOV version 2.0-1