LCOV - code coverage report
Current view: top level - src - pao_param_fock.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 72 74 97.3 %
Date: 2024-12-21 06:28:57 Functions: 1 1 100.0 %

          Line data    Source code
       1             : !--------------------------------------------------------------------------------------------------!
       2             : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3             : !   Copyright 2000-2024 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       15519 :    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       15519 :       INTEGER, DIMENSION(:), POINTER                     :: blk_sizes_pao, blk_sizes_pri
      55             :       LOGICAL                                            :: found
      56             :       REAL(dp)                                           :: alpha, beta, denom, diff
      57       15519 :       REAL(dp), DIMENSION(:), POINTER                    :: H_evals
      58       15519 :       REAL(dp), DIMENSION(:, :), POINTER                 :: block_N, D1, D2, H, H0, H_evecs, M2, M3, &
      59       15519 :                                                             M4, M5
      60             : 
      61       15519 :       CALL timeset(routineN, handle)
      62             : 
      63       15519 :       CALL dbcsr_get_block_p(matrix=pao%matrix_H0, row=iatom, col=iatom, block=H0, found=found)
      64       15519 :       CPASSERT(ASSOCIATED(H0))
      65       15519 :       CALL dbcsr_get_block_p(matrix=pao%matrix_N_diag, row=iatom, col=iatom, block=block_N, found=found)
      66       15519 :       CPASSERT(ASSOCIATED(block_N))
      67     1019483 :       IF (MAXVAL(ABS(V - TRANSPOSE(V))) > 1e-14_dp) CPABORT("Expect symmetric matrix")
      68             : 
      69             :       ! figure out basis sizes
      70       15519 :       CALL dbcsr_get_info(pao%matrix_Y, row_blk_size=blk_sizes_pri, col_blk_size=blk_sizes_pao)
      71       15519 :       n = blk_sizes_pri(iatom) ! size of primary basis
      72       15519 :       m = blk_sizes_pao(iatom) ! size of pao basis
      73             : 
      74             :       ! calculate H in the orthonormal basis
      75       62076 :       ALLOCATE (H(n, n))
      76    61788678 :       H = MATMUL(MATMUL(block_N, H0 + V), block_N)
      77             : 
      78             :       ! diagonalize H
      79       77595 :       ALLOCATE (H_evals(n), H_evecs(n, n))
      80     2023447 :       H_evecs = H
      81       15519 :       CALL diamat_all(H_evecs, H_evals)
      82             : 
      83             :       ! the eigenvectors of H become the rotation matrix U
      84     2023447 :       U = H_evecs
      85             : 
      86             :       ! copy eigenvectors around the gap from H_evals into evals array
      87       15519 :       IF (PRESENT(evals)) THEN
      88       12785 :          CPASSERT(MOD(SIZE(evals), 2) == 0) ! gap will be exactely in the middle
      89       12785 :          i = SIZE(evals)/2
      90       12785 :          j = MIN(m, i)
      91       53346 :          evals(1 + i - j:i) = H_evals(1 + m - j:m) ! eigenvalues below gap
      92       12785 :          j = MIN(n - m, i)
      93       58757 :          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       15519 :       gap = HUGE(dp)
      98       15519 :       IF (m < n) & ! catch special case n==m
      99       14976 :          gap = H_evals(m + 1) - H_evals(m)
     100             : 
     101       15519 :       IF (PRESENT(penalty)) THEN
     102             :          ! penalty terms: occupied and virtual eigenvalues repel each other
     103       15199 :          alpha = pao%penalty_strength
     104       15199 :          beta = pao%penalty_dist
     105       67598 :          DO i = 1, m
     106      222318 :          DO j = m + 1, n
     107      154720 :             diff = H_evals(i) - H_evals(j)
     108      207119 :             penalty = penalty + alpha*EXP(-(diff/beta)**2)
     109             :          END DO
     110             :          END DO
     111             : 
     112             :          ! regularization energy
     113     1007749 :          penalty = penalty + pao%regularization*SUM(V**2)
     114             :       END IF
     115             : 
     116       15519 :       IF (PRESENT(G)) THEN ! TURNING POINT (if calc grad) -------------------------
     117             : 
     118        2469 :          CPASSERT(PRESENT(M1))
     119             : 
     120             :          ! calculate derivatives between eigenvectors of H
     121       22221 :          ALLOCATE (D1(n, n), M2(n, n), M3(n, n), M4(n, n))
     122       19048 :          DO i = 1, n
     123      156971 :          DO j = 1, n
     124             :             ! ignore changes among occupied or virtual eigenvectors
     125             :             ! They will get filtered out by M2*D1 anyways, however this early
     126             :             ! intervention might stabilize numerics in the case of level-crossings.
     127      154502 :             IF (i <= m .EQV. j <= m) THEN
     128       84671 :                D1(i, j) = 0.0_dp
     129             :             ELSE
     130       53252 :                denom = H_evals(i) - H_evals(j)
     131       53252 :                IF (ABS(denom) > 1e-9_dp) THEN ! avoid division by zero
     132       53252 :                   D1(i, j) = 1.0_dp/denom
     133             :                ELSE
     134           0 :                   D1(i, j) = SIGN(1e+9_dp, denom)
     135             :                END IF
     136             :             END IF
     137             :          END DO
     138             :          END DO
     139        2469 :          IF (ASSOCIATED(M1)) THEN
     140     3432736 :             M2 = MATMUL(TRANSPOSE(M1), H_evecs)
     141             :          ELSE
     142           0 :             M2 = 0.0_dp
     143             :          END IF
     144      311473 :          M3 = M2*D1 ! Hadamard product
     145     6401966 :          M4 = MATMUL(MATMUL(H_evecs, M3), TRANSPOSE(H_evecs))
     146             : 
     147             :          ! gradient contribution from penalty terms
     148        2469 :          IF (PRESENT(penalty)) THEN
     149        7305 :             ALLOCATE (D2(n, n))
     150      155893 :             D2 = 0.0_dp
     151       18842 :             DO i = 1, n
     152      155893 :             DO j = 1, n
     153      137051 :                IF (i <= m .EQV. j <= m) CYCLE
     154       52976 :                diff = H_evals(i) - H_evals(j)
     155      153458 :                D2(i, i) = D2(i, i) - 2.0_dp*alpha*diff/beta**2*EXP(-(diff/beta)**2)
     156             :             END DO
     157             :             END DO
     158     9187113 :             M4 = M4 + MATMUL(MATMUL(H_evecs, D2), TRANSPOSE(H_evecs))
     159        2435 :             DEALLOCATE (D2)
     160             :          END IF
     161             : 
     162             :          ! dH / dV, return to non-orthonormal basis
     163        7407 :          ALLOCATE (M5(n, n))
     164    12026484 :          M5 = MATMUL(MATMUL(block_N, M4), block_N)
     165             : 
     166             :          ! add regularization gradient
     167        2469 :          IF (PRESENT(penalty)) &
     168      309351 :             M5 = M5 + 2.0_dp*pao%regularization*V
     169             : 
     170             :          ! symmetrize
     171      311473 :          G = 0.5_dp*(M5 + TRANSPOSE(M5)) ! the final gradient
     172             : 
     173        2469 :          DEALLOCATE (D1, M2, M3, M4, M5)
     174             :       END IF
     175             : 
     176       15519 :       DEALLOCATE (H, H_evals, H_evecs)
     177             : 
     178       15519 :       CALL timestop(handle)
     179       31038 :    END SUBROUTINE pao_calc_U_block_fock
     180             : 
     181       25361 : END MODULE pao_param_fock

Generated by: LCOV version 1.15