LCOV - code coverage report
Current view: top level - src - pao_potentials.F (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:2fce0f8) Lines: 129 133 97.0 %
Date: 2024-12-21 06:28:57 Functions: 3 3 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 Factory routines for potentials used e.g. by pao_param_exp and pao_ml
      10             : !> \author Ole Schuett
      11             : ! **************************************************************************************************
      12             : MODULE pao_potentials
      13             :    USE ai_overlap,                      ONLY: overlap_aab
      14             :    USE ao_util,                         ONLY: exp_radius
      15             :    USE atomic_kind_types,               ONLY: get_atomic_kind
      16             :    USE basis_set_types,                 ONLY: gto_basis_set_type
      17             :    USE cell_types,                      ONLY: cell_type,&
      18             :                                               pbc
      19             :    USE kinds,                           ONLY: dp
      20             :    USE mathconstants,                   ONLY: gamma1
      21             :    USE mathlib,                         ONLY: multinomial
      22             :    USE orbital_pointers,                ONLY: indco,&
      23             :                                               ncoset,&
      24             :                                               orbital_pointers_maxl => current_maxl
      25             :    USE particle_types,                  ONLY: particle_type
      26             :    USE qs_environment_types,            ONLY: get_qs_env,&
      27             :                                               qs_environment_type
      28             :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      29             :                                               qs_kind_type
      30             : #include "./base/base_uses.f90"
      31             : 
      32             :    IMPLICIT NONE
      33             : 
      34             :    PRIVATE
      35             : 
      36             :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'pao_potentials'
      37             : 
      38             :    PUBLIC :: pao_guess_initial_potential, pao_calc_gaussian
      39             : 
      40             : CONTAINS
      41             : 
      42             : ! **************************************************************************************************
      43             : !> \brief Makes an educated guess for the initial potential based on positions of neighboring atoms
      44             : !> \param qs_env ...
      45             : !> \param iatom ...
      46             : !> \param block_V ...
      47             : ! **************************************************************************************************
      48          76 :    SUBROUTINE pao_guess_initial_potential(qs_env, iatom, block_V)
      49             :       TYPE(qs_environment_type), POINTER                 :: qs_env
      50             :       INTEGER, INTENT(IN)                                :: iatom
      51             :       REAL(dp), DIMENSION(:, :), INTENT(OUT)             :: block_V
      52             : 
      53             :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_guess_initial_potential'
      54             : 
      55             :       INTEGER                                            :: handle, ikind, jatom, natoms
      56             :       REAL(dp), DIMENSION(3)                             :: Ra, Rab, Rb
      57             :       TYPE(cell_type), POINTER                           :: cell
      58             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
      59          76 :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
      60          76 :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
      61             : 
      62          76 :       CALL timeset(routineN, handle)
      63             : 
      64             :       CALL get_qs_env(qs_env, &
      65             :                       cell=cell, &
      66             :                       particle_set=particle_set, &
      67             :                       qs_kind_set=qs_kind_set, &
      68          76 :                       natom=natoms)
      69             : 
      70          76 :       CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
      71          76 :       CALL get_qs_kind(qs_kind_set(ikind), basis_set=basis_set)
      72             : 
      73             :       ! construct matrix block_V from neighboring atoms
      74        4508 :       block_V = 0.0_dp
      75         270 :       DO jatom = 1, natoms
      76         194 :          IF (jatom == iatom) CYCLE
      77         472 :          Ra = particle_set(iatom)%r
      78         472 :          Rb = particle_set(jatom)%r
      79         118 :          Rab = pbc(ra, rb, cell)
      80         270 :          CALL pao_calc_gaussian(basis_set, block_V, Rab=Rab, lpot=0, beta=1.0_dp, weight=-1.0_dp)
      81             :       END DO
      82             : 
      83          76 :       CALL timestop(handle)
      84          76 :    END SUBROUTINE pao_guess_initial_potential
      85             : 
      86             : ! **************************************************************************************************
      87             : !> \brief Calculates potential term of the form r**lpot * Exp(-beta*r**2)
      88             : !>        One needs to call init_orbital_pointers(lpot) before calling pao_calc_gaussian().
      89             : !> \param basis_set ...
      90             : !> \param block_V potential term that is returned
      91             : !> \param block_D derivative of potential term wrt to Rab
      92             : !> \param Rab ...
      93             : !> \param lpot polynomial prefactor, r**lpot
      94             : !> \param beta exponent of the Gaussian
      95             : !> \param weight ...
      96             : !> \param min_shell ...
      97             : !> \param max_shell ...
      98             : !> \param min_l ...
      99             : !> \param max_l ...
     100             : ! **************************************************************************************************
     101         478 :    SUBROUTINE pao_calc_gaussian(basis_set, block_V, block_D, Rab, lpot, beta, weight, min_shell, max_shell, min_l, max_l)
     102             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     103             :       REAL(dp), DIMENSION(:, :), INTENT(OUT), OPTIONAL   :: block_V
     104             :       REAL(dp), DIMENSION(:, :, :), INTENT(OUT), &
     105             :          OPTIONAL                                        :: block_D
     106             :       REAL(dp), DIMENSION(3)                             :: Rab
     107             :       INTEGER, INTENT(IN)                                :: lpot
     108             :       REAL(dp), INTENT(IN)                               :: beta, weight
     109             :       INTEGER, INTENT(IN), OPTIONAL                      :: min_shell, max_shell, min_l, max_l
     110             : 
     111             :       CHARACTER(len=*), PARAMETER                        :: routineN = 'pao_calc_gaussian'
     112             : 
     113             :       INTEGER :: handle, i, ic, iset, ishell, ishell_abs, jset, jshell, jshell_abs, la1_max, &
     114             :          la1_min, la2_max, la2_min, lb_max, lb_min, N, na1, na2, nb, ncfga1, ncfga2, ncfgb, &
     115             :          npgfa1, npgfa2, npgfb
     116             :       REAL(dp)                                           :: coeff, norm2
     117         478 :       REAL(dp), DIMENSION(:), POINTER                    :: rpgfa1, rpgfa2, rpgfb, zeta1, zeta2, zetb
     118         478 :       REAL(dp), DIMENSION(:, :), POINTER                 :: new_block_V, sab
     119         478 :       REAL(dp), DIMENSION(:, :, :), POINTER              :: dab, new_block_D, saab
     120         478 :       REAL(dp), DIMENSION(:, :, :, :), POINTER           :: daab
     121             : 
     122         478 :       CALL timeset(routineN, handle)
     123             : 
     124         478 :       CPASSERT(PRESENT(block_V) .NEQV. PRESENT(block_D)) ! just to keep the code simpler
     125         478 :       CPASSERT(PRESENT(min_shell) .EQV. PRESENT(max_shell))
     126         478 :       CPASSERT(PRESENT(min_l) .EQV. PRESENT(max_l))
     127             : 
     128         478 :       CPASSERT(MOD(lpot, 2) == 0) ! otherwise it's not rotationally invariant
     129         478 :       CPASSERT(orbital_pointers_maxl >= lpot) ! can't call init_orbital_pointers here, it's not thread-safe
     130             : 
     131         478 :       N = basis_set%nsgf ! primary basis-size
     132             : 
     133         478 :       IF (PRESENT(block_V)) THEN
     134         468 :          CPASSERT(SIZE(block_V, 1) == N .AND. SIZE(block_V, 2) == N)
     135        1872 :          ALLOCATE (new_block_V(N, N))
     136       26084 :          new_block_V = 0.0_dp
     137             :       END IF
     138         478 :       IF (PRESENT(block_D)) THEN
     139          10 :          CPASSERT(SIZE(block_D, 1) == N .AND. SIZE(block_D, 2) == N .AND. SIZE(block_D, 3) == 3)
     140          50 :          ALLOCATE (new_block_D(N, N, 3))
     141         940 :          new_block_D = 0.0_dp
     142             :       END IF
     143             : 
     144             :       ! setup description of potential
     145         478 :       lb_min = lpot
     146         478 :       lb_max = lpot
     147         478 :       ncfgb = ncoset(lb_max) - ncoset(lb_min - 1)
     148         478 :       npgfb = 1 ! number of exponents
     149         478 :       nb = npgfb*ncfgb
     150             : 
     151             :       ! initialize exponents
     152         478 :       ALLOCATE (rpgfb(npgfb), zetb(npgfb))
     153         478 :       rpgfb(1) = exp_radius(0, beta, 1.0E-12_dp, 1.0_dp) ! TODO get the EPS parameter from somewhere / precompute this elsewhere
     154         478 :       zetb(1) = beta
     155             : 
     156             :       ! loop over all set/shell combination and fill block_V
     157         958 :       DO iset = 1, basis_set%nset
     158        1442 :       DO jset = 1, basis_set%nset
     159        2560 :       DO ishell = 1, basis_set%nshell(iset)
     160        7612 :       DO jshell = 1, basis_set%nshell(jset)
     161        5532 :          IF (PRESENT(min_shell) .AND. PRESENT(max_shell)) THEN
     162           0 :             ishell_abs = SUM(basis_set%nshell(1:iset - 1)) + ishell
     163           0 :             jshell_abs = SUM(basis_set%nshell(1:jset - 1)) + jshell
     164           0 :             IF (MIN(ishell_abs, jshell_abs) /= min_shell) CYCLE
     165           0 :             IF (MAX(ishell_abs, jshell_abs) /= max_shell) CYCLE
     166             :          END IF
     167        5532 :          IF (PRESENT(min_l) .AND. PRESENT(min_l)) THEN
     168        3036 :             IF (MIN(basis_set%l(ishell, iset), basis_set%l(jshell, jset)) /= min_l) CYCLE
     169        1436 :             IF (MAX(basis_set%l(ishell, iset), basis_set%l(jshell, jset)) /= max_l) CYCLE
     170             :          END IF
     171             : 
     172             :          ! setup iset
     173        3038 :          la1_max = basis_set%l(ishell, iset)
     174        3038 :          la1_min = basis_set%l(ishell, iset)
     175        3038 :          npgfa1 = basis_set%npgf(iset)
     176        3038 :          ncfga1 = ncoset(la1_max) - ncoset(la1_min - 1)
     177        3038 :          na1 = npgfa1*ncfga1
     178        3038 :          zeta1 => basis_set%zet(:, iset)
     179        3038 :          rpgfa1 => basis_set%pgf_radius(:, iset)
     180             : 
     181             :          ! setup jset
     182        3038 :          la2_max = basis_set%l(jshell, jset)
     183        3038 :          la2_min = basis_set%l(jshell, jset)
     184        3038 :          npgfa2 = basis_set%npgf(jset)
     185        3038 :          ncfga2 = ncoset(la2_max) - ncoset(la2_min - 1)
     186        3038 :          na2 = npgfa2*ncfga2
     187        3038 :          zeta2 => basis_set%zet(:, jset)
     188        3038 :          rpgfa2 => basis_set%pgf_radius(:, jset)
     189             : 
     190             :          ! calculate integrals
     191        3038 :          IF (PRESENT(block_V)) THEN
     192       14740 :             ALLOCATE (saab(na1, na2, nb))
     193      899278 :             saab = 0.0_dp
     194             :             CALL overlap_aab(la1_max=la1_max, la1_min=la1_min, npgfa1=npgfa1, rpgfa1=rpgfa1, zeta1=zeta1, &
     195             :                              la2_max=la2_max, la2_min=la2_min, npgfa2=npgfa2, rpgfa2=rpgfa2, zeta2=zeta2, &
     196             :                              lb_max=lb_max, lb_min=lb_min, npgfb=npgfb, rpgfb=rpgfb, zetb=zetb, &
     197        2948 :                              rab=Rab, saab=saab)
     198             :          END IF
     199             : 
     200        3038 :          IF (PRESENT(block_D)) THEN
     201         540 :             ALLOCATE (daab(na1, na2, nb, 3))
     202       40530 :             daab = 0.0_dp
     203             :             CALL overlap_aab(la1_max=la1_max, la1_min=la1_min, npgfa1=npgfa1, rpgfa1=rpgfa1, zeta1=zeta1, &
     204             :                              la2_max=la2_max, la2_min=la2_min, npgfa2=npgfa2, rpgfa2=rpgfa2, zeta2=zeta2, &
     205             :                              lb_max=lb_max, lb_min=lb_min, npgfb=npgfb, rpgfb=rpgfb, zetb=zetb, &
     206          90 :                              rab=Rab, daab=daab)
     207             :          END IF
     208             : 
     209             :          ! sum potential terms: POW(x**2 + y**2 + z**2, lpot/2)
     210        3038 :          IF (PRESENT(block_V)) THEN
     211       11792 :             ALLOCATE (sab(na1, na2))
     212      415060 :             sab = 0.0_dp
     213       11166 :             DO ic = 1, ncfgb
     214       32872 :                coeff = multinomial(lpot/2, indco(:, ncoset(lpot - 1) + ic)/2)
     215     1787390 :                sab = sab + coeff*saab(:, :, ic)
     216             :             END DO
     217             :             CALL my_contract(sab=sab, block=new_block_V, basis_set=basis_set, &
     218        2948 :                              iset=iset, ishell=ishell, jset=jset, jshell=jshell)
     219        2948 :             DEALLOCATE (sab, saab)
     220             :          END IF
     221             : 
     222        4634 :          IF (PRESENT(block_D)) THEN
     223         450 :             ALLOCATE (dab(na1, na2, 3))
     224       40260 :             dab = 0.0_dp
     225         180 :             DO ic = 1, ncfgb
     226         360 :                coeff = multinomial(lpot/2, indco(:, ncoset(lpot - 1) + ic)/2)
     227       80520 :                dab = dab + coeff*daab(:, :, ic, :)
     228             :             END DO
     229         360 :             DO i = 1, 3
     230             :                CALL my_contract(sab=dab(:, :, i), block=new_block_D(:, :, i), basis_set=basis_set, &
     231         360 :                                 iset=iset, ishell=ishell, jset=jset, jshell=jshell)
     232             :             END DO
     233          90 :             DEALLOCATE (dab, daab)
     234             :          END IF
     235             :       END DO
     236             :       END DO
     237             :       END DO
     238             :       END DO
     239             : 
     240         478 :       DEALLOCATE (rpgfb, zetb)
     241             : 
     242             :       ! post-processing
     243         478 :       norm2 = (2.0_dp*beta)**(-0.5_dp - lpot)*gamma1(lpot)
     244         478 :       IF (PRESENT(block_V)) THEN
     245       26084 :          block_V = block_V + weight*new_block_V/SQRT(norm2)
     246         468 :          DEALLOCATE (new_block_V)
     247       51700 :          block_V = 0.5_dp*(block_V + TRANSPOSE(block_V)) ! symmetrize
     248             :       END IF
     249             : 
     250         478 :       IF (PRESENT(block_D)) THEN
     251         940 :          block_D = block_D + weight*new_block_D/SQRT(norm2)
     252          10 :          DEALLOCATE (new_block_D)
     253          40 :          DO i = 1, 3
     254        1840 :             block_D(:, :, i) = 0.5_dp*(block_D(:, :, i) + TRANSPOSE(block_D(:, :, i))) ! symmetrize
     255             :          END DO
     256             :       END IF
     257             : 
     258         478 :       CALL timestop(handle)
     259         478 :    END SUBROUTINE pao_calc_gaussian
     260             : 
     261             : ! **************************************************************************************************
     262             : !> \brief Helper routine, contracts a basis block
     263             : !> \param sab ...
     264             : !> \param block ...
     265             : !> \param basis_set ...
     266             : !> \param iset ...
     267             : !> \param ishell ...
     268             : !> \param jset ...
     269             : !> \param jshell ...
     270             : ! **************************************************************************************************
     271        3218 :    SUBROUTINE my_contract(sab, block, basis_set, iset, ishell, jset, jshell)
     272             :       REAL(dp), DIMENSION(:, :), INTENT(IN), TARGET      :: sab
     273             :       REAL(dp), DIMENSION(:, :), INTENT(OUT), TARGET     :: block
     274             :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     275             :       INTEGER, INTENT(IN)                                :: iset, ishell, jset, jshell
     276             : 
     277             :       INTEGER                                            :: a, b, c, d, ipgf, jpgf, l1, l2, n1, n2, &
     278             :                                                             nn1, nn2, sgfa1, sgfa2, sgla1, sgla2
     279        3218 :       REAL(dp), DIMENSION(:, :), POINTER                 :: S, T1, T2, V
     280             : 
     281             :       ! first and last indices of given shell in block.
     282             :       ! This matrix is in the contracted spherical basis.
     283        3218 :       sgfa1 = basis_set%first_sgf(ishell, iset)
     284        3218 :       sgla1 = basis_set%last_sgf(ishell, iset)
     285        3218 :       sgfa2 = basis_set%first_sgf(jshell, jset)
     286        3218 :       sgla2 = basis_set%last_sgf(jshell, jset)
     287             : 
     288             :       ! prepare the result matrix
     289        3218 :       V => block(sgfa1:sgla1, sgfa2:sgla2)
     290             : 
     291             :       ! Calculate strides of sphi matrix.
     292             :       ! This matrix is in the uncontraced cartesian basis.
     293             :       ! It contains all shells of the set.
     294             :       ! Its index runs over all primitive gaussians of the set
     295             :       ! and then for each gaussian over all configurations of *the entire set*. (0->lmax)
     296        3218 :       nn1 = ncoset(basis_set%lmax(iset))
     297        3218 :       nn2 = ncoset(basis_set%lmax(jset))
     298             : 
     299             :       ! Calculate strides of sab matrix
     300             :       ! This matrix is also in the uncontraced cartensian basis,
     301             :       ! however it contains only a single shell.
     302             :       ! Its index runs over all primitive gaussians of the set
     303             :       ! and then for each gaussian over all configrations of *the given shell*.
     304        3218 :       l1 = basis_set%l(ishell, iset)
     305        3218 :       l2 = basis_set%l(jshell, jset)
     306        3218 :       n1 = ncoset(l1) - ncoset(l1 - 1)
     307        3218 :       n2 = ncoset(l2) - ncoset(l2 - 1)
     308             : 
     309       21688 :       DO ipgf = 1, basis_set%npgf(iset)
     310      130794 :       DO jpgf = 1, basis_set%npgf(jset)
     311             :          ! prepare first trafo-matrix
     312      109106 :          a = (ipgf - 1)*nn1 + ncoset(l1 - 1) + 1
     313      109106 :          T1 => basis_set%sphi(a:a + n1 - 1, sgfa1:sgla1)
     314             : 
     315             :          ! prepare second trafo-matrix
     316      109106 :          b = (jpgf - 1)*nn2 + ncoset(l2 - 1) + 1
     317      109106 :          T2 => basis_set%sphi(b:b + n2 - 1, sgfa2:sgla2)
     318             : 
     319             :          ! prepare SAB matrix
     320      109106 :          c = (ipgf - 1)*n1 + 1
     321      109106 :          d = (jpgf - 1)*n2 + 1
     322      109106 :          S => sab(c:c + n1 - 1, d:d + n2 - 1)
     323             : 
     324             :          ! do the transformation
     325    10895956 :          V = V + MATMUL(TRANSPOSE(T1), MATMUL(S, T2))
     326             :       END DO
     327             :       END DO
     328             : 
     329        3218 :    END SUBROUTINE my_contract
     330             : 
     331      109106 : END MODULE pao_potentials

Generated by: LCOV version 1.15