LCOV - code coverage report
Current view: top level - src - pao_ml_descriptor.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:71c3ab0) Lines: 97.8 % 184 180
Test Date: 2026-07-25 06:35:44 Functions: 100.0 % 4 4

            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 Feature vectors for describing chemical environments in a rotationally invariant fashion.
      10              : !> \author Ole Schuett
      11              : ! **************************************************************************************************
      12              : MODULE pao_ml_descriptor
      13              :    USE atomic_kind_types,               ONLY: get_atomic_kind
      14              :    USE basis_set_types,                 ONLY: gto_basis_set_type
      15              :    USE cell_types,                      ONLY: cell_type,&
      16              :                                               pbc
      17              :    USE kinds,                           ONLY: dp
      18              :    USE mathconstants,                   ONLY: fourpi,&
      19              :                                               rootpi
      20              :    USE mathlib,                         ONLY: diamat_all
      21              :    USE pao_input,                       ONLY: pao_ml_desc_overlap,&
      22              :                                               pao_ml_desc_pot,&
      23              :                                               pao_ml_desc_r12
      24              :    USE pao_potentials,                  ONLY: pao_calc_gaussian
      25              :    USE pao_types,                       ONLY: pao_env_type
      26              :    USE particle_types,                  ONLY: particle_type
      27              :    USE qs_kind_types,                   ONLY: get_qs_kind,&
      28              :                                               pao_descriptor_type,&
      29              :                                               qs_kind_type
      30              :    USE util,                            ONLY: sort
      31              : #include "./base/base_uses.f90"
      32              : 
      33              :    IMPLICIT NONE
      34              : 
      35              :    PRIVATE
      36              : 
      37              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'pao_ml_descriptor'
      38              : 
      39              :    PUBLIC :: pao_ml_calc_descriptor
      40              : 
      41              : CONTAINS
      42              : 
      43              : ! **************************************************************************************************
      44              : !> \brief Calculates a descriptor for chemical environment of given atom
      45              : !> \param pao ...
      46              : !> \param particle_set ...
      47              : !> \param qs_kind_set ...
      48              : !> \param cell ...
      49              : !> \param iatom ...
      50              : !> \param descriptor ...
      51              : !> \param descr_grad ...
      52              : !> \param forces ...
      53              : ! **************************************************************************************************
      54          208 :    SUBROUTINE pao_ml_calc_descriptor(pao, particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
      55              :       TYPE(pao_env_type), POINTER                        :: pao
      56              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
      57              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
      58              :       TYPE(cell_type), POINTER                           :: cell
      59              :       INTEGER, INTENT(IN)                                :: iatom
      60              :       REAL(dp), ALLOCATABLE, DIMENSION(:), OPTIONAL      :: descriptor
      61              :       REAL(dp), DIMENSION(:), INTENT(IN), OPTIONAL       :: descr_grad
      62              :       REAL(dp), DIMENSION(:, :), INTENT(INOUT), OPTIONAL :: forces
      63              : 
      64              :       CHARACTER(len=*), PARAMETER :: routineN = 'pao_ml_calc_descriptor'
      65              : 
      66              :       INTEGER                                            :: handle
      67              : 
      68          208 :       CALL timeset(routineN, handle)
      69              : 
      70          208 :       CPASSERT(PRESENT(forces) .EQV. PRESENT(descr_grad))
      71              : 
      72          208 :       SELECT CASE (pao%ml_descriptor)
      73              :       CASE (pao_ml_desc_pot)
      74          150 :          CALL calc_descriptor_pot(particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
      75              :       CASE (pao_ml_desc_overlap)
      76          118 :          CALL calc_descriptor_overlap(particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
      77              :       CASE (pao_ml_desc_r12)
      78          320 :          CALL calc_descriptor_r12(particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
      79              :       CASE DEFAULT
      80          208 :          CPABORT("PAO: unknown descriptor")
      81              :       END SELECT
      82              : 
      83          208 :       CALL timestop(handle)
      84          208 :    END SUBROUTINE pao_ml_calc_descriptor
      85              : 
      86              : ! **************************************************************************************************
      87              : !> \brief Calculates a descriptor based on the eigenvalues of V_neighbors
      88              : !> \param particle_set ...
      89              : !> \param qs_kind_set ...
      90              : !> \param cell ...
      91              : !> \param iatom ...
      92              : !> \param descriptor ...
      93              : !> \param descr_grad ...
      94              : !> \param forces ...
      95              : ! **************************************************************************************************
      96           54 :    SUBROUTINE calc_descriptor_pot(particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
      97              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
      98              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
      99              :       TYPE(cell_type), POINTER                           :: cell
     100              :       INTEGER, INTENT(IN)                                :: iatom
     101              :       REAL(dp), ALLOCATABLE, DIMENSION(:), OPTIONAL      :: descriptor
     102              :       REAL(dp), DIMENSION(:), INTENT(IN), OPTIONAL       :: descr_grad
     103              :       REAL(dp), DIMENSION(:, :), INTENT(INOUT), OPTIONAL :: forces
     104              : 
     105              :       CHARACTER(len=*), PARAMETER :: routineN = 'calc_descriptor_pot'
     106              : 
     107              :       INTEGER                                            :: handle, i, idesc, ikind, jatom, jkind, &
     108              :                                                             k, N, natoms, ndesc
     109              :       REAL(dp)                                           :: beta, w, weight
     110              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: V_evals
     111           54 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: block_M, block_V, V_evecs
     112           54 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :, :)          :: block_D
     113              :       REAL(dp), DIMENSION(3)                             :: Ra, Rab, Rb
     114              :       TYPE(gto_basis_set_type), POINTER                  :: basis_set
     115           54 :       TYPE(pao_descriptor_type), DIMENSION(:), POINTER   :: pao_descriptors
     116              : 
     117           54 :       CALL timeset(routineN, handle)
     118              : 
     119           54 :       CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     120           54 :       CALL get_qs_kind(qs_kind_set(ikind), basis_set=basis_set, pao_descriptors=pao_descriptors)
     121           54 :       N = basis_set%nsgf
     122           54 :       natoms = SIZE(particle_set)
     123           54 :       ndesc = SIZE(pao_descriptors)
     124           54 :       IF (ndesc == 0) CPABORT("No PAO_DESCRIPTOR section found")
     125              : 
     126          432 :       ALLOCATE (block_V(N, N), V_evecs(N, N), V_evals(N))
     127          150 :       IF (PRESENT(descriptor)) ALLOCATE (descriptor(N*ndesc))
     128           90 :       IF (PRESENT(forces)) ALLOCATE (block_D(N, N, 3), block_M(N, N))
     129              : 
     130          152 :       DO idesc = 1, ndesc
     131              : 
     132              :          ! construct matrix V_block from neighboring atoms
     133           98 :          block_V = 0.0_dp
     134          294 :          DO jatom = 1, natoms
     135          196 :             IF (jatom == iatom) CYCLE
     136          392 :             Ra = particle_set(iatom)%r
     137          392 :             Rb = particle_set(jatom)%r
     138           98 :             Rab = pbc(ra, rb, cell)
     139           98 :             CALL get_atomic_kind(particle_set(jatom)%atomic_kind, kind_number=jkind)
     140           98 :             CALL get_qs_kind(qs_kind_set(jkind), pao_descriptors=pao_descriptors)
     141           98 :             IF (SIZE(pao_descriptors) /= ndesc) THEN
     142            0 :                CPABORT("Not all KINDs have the same number of PAO_DESCRIPTOR sections")
     143              :             END IF
     144           98 :             weight = pao_descriptors(idesc)%weight
     145           98 :             beta = pao_descriptors(idesc)%beta
     146          392 :             CALL pao_calc_gaussian(basis_set, block_V=block_V, Rab=Rab, lpot=0, beta=beta, weight=weight)
     147              :          END DO
     148              : 
     149              :          ! diagonalize block_V
     150         3038 :          V_evecs(:, :) = block_V(:, :)
     151           98 :          CALL diamat_all(V_evecs, V_evals)
     152              : 
     153              :          ! use eigenvalues of V_block as descriptor
     154           98 :          IF (PRESENT(descriptor)) THEN
     155          528 :             descriptor((idesc - 1)*N + 1:idesc*N) = V_evals(:)
     156              :          END IF
     157              : 
     158              :          ! FORCES ----------------------------------------------------------------------------------
     159          152 :          IF (PRESENT(forces)) THEN
     160           10 :             CPASSERT(PRESENT(descr_grad))
     161           10 :             block_M = 0.0_dp
     162           60 :             DO k = 1, N
     163           50 :                w = descr_grad((idesc - 1)*N + k)
     164         3460 :                block_M(:, :) = block_M(:, :) + w*MATMUL(V_evecs(:, k:k), TRANSPOSE(V_evecs(:, k:k)))
     165              :             END DO
     166           30 :             DO jatom = 1, natoms
     167           20 :                IF (jatom == iatom) CYCLE
     168           40 :                Ra = particle_set(iatom)%r
     169           40 :                Rb = particle_set(jatom)%r
     170           10 :                Rab = pbc(ra, rb, cell)
     171           10 :                CALL get_atomic_kind(particle_set(jatom)%atomic_kind, kind_number=jkind)
     172           10 :                CALL get_qs_kind(qs_kind_set(jkind), pao_descriptors=pao_descriptors)
     173           10 :                weight = pao_descriptors(idesc)%weight
     174           10 :                beta = pao_descriptors(idesc)%beta
     175           10 :                block_D = 0.0_dp
     176           10 :                CALL pao_calc_gaussian(basis_set, block_D=block_D, Rab=Rab, lpot=0, beta=beta, weight=weight)
     177           60 :                DO i = 1, 3
     178          930 :                   forces(iatom, i) = forces(iatom, i) - SUM(block_M*block_D(:, :, i))
     179          950 :                   forces(jatom, i) = forces(jatom, i) + SUM(block_M*block_D(:, :, i))
     180              :                END DO
     181              :             END DO
     182              :          END IF
     183              : 
     184              :       END DO
     185              : 
     186           54 :       CALL timestop(handle)
     187          108 :    END SUBROUTINE calc_descriptor_pot
     188              : 
     189              : ! **************************************************************************************************
     190              : !> \brief Calculates a descriptor based on the eigenvalues of local overlap matrix
     191              : !> \param particle_set ...
     192              : !> \param qs_kind_set ...
     193              : !> \param cell ...
     194              : !> \param iatom ...
     195              : !> \param descriptor ...
     196              : !> \param descr_grad ...
     197              : !> \param forces ...
     198              : ! **************************************************************************************************
     199           42 :    SUBROUTINE calc_descriptor_overlap(particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
     200              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     201              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     202              :       TYPE(cell_type), POINTER                           :: cell
     203              :       INTEGER, INTENT(IN)                                :: iatom
     204              :       REAL(dp), ALLOCATABLE, DIMENSION(:), OPTIONAL      :: descriptor
     205              :       REAL(dp), DIMENSION(:), INTENT(IN), OPTIONAL       :: descr_grad
     206              :       REAL(dp), DIMENSION(:, :), INTENT(INOUT), OPTIONAL :: forces
     207              : 
     208              :       CHARACTER(len=*), PARAMETER :: routineN = 'calc_descriptor_overlap'
     209              : 
     210              :       INTEGER                                            :: handle, idesc, ikind, j, jatom, jkind, &
     211              :                                                             k, katom, kkind, N, natoms, ndesc
     212           42 :       INTEGER, ALLOCATABLE, DIMENSION(:)                 :: neighbor_order
     213              :       REAL(dp) :: beta_sum, deriv, exponent, integral, jbeta, jweight, kbeta, kweight, &
     214              :          normalization, Rij2, Rik2, Rjk2, sbeta, screening_radius, screening_volume, w
     215              :       REAL(dp), ALLOCATABLE, DIMENSION(:)                :: S_evals
     216           42 :       REAL(dp), ALLOCATABLE, DIMENSION(:, :)             :: block_M, block_S, S_evecs
     217              :       REAL(dp), DIMENSION(3)                             :: Ri, Rij, Rik, Rj, Rjk, Rk
     218           42 :       REAL(KIND=dp), ALLOCATABLE, DIMENSION(:)           :: neighbor_dist
     219           42 :       TYPE(pao_descriptor_type), DIMENSION(:), POINTER   :: ipao_descriptors, jpao_descriptors, &
     220           42 :                                                             kpao_descriptors
     221              : 
     222           42 :       CALL timeset(routineN, handle)
     223              : 
     224           42 :       CALL get_atomic_kind(particle_set(iatom)%atomic_kind, kind_number=ikind)
     225           42 :       CALL get_qs_kind(qs_kind_set(ikind), pao_descriptors=ipao_descriptors)
     226              : 
     227           42 :       natoms = SIZE(particle_set)
     228           42 :       ndesc = SIZE(ipao_descriptors)
     229           42 :       IF (ndesc == 0) CPABORT("No PAO_DESCRIPTOR section found")
     230              : 
     231              :       ! determine largest screening radius
     232           42 :       screening_radius = 0.0_dp
     233          118 :       DO idesc = 1, ndesc
     234          118 :          screening_radius = MAX(screening_radius, ipao_descriptors(idesc)%screening_radius)
     235              :       END DO
     236              : 
     237              :       ! estimate maximum number of neighbors within screening
     238           42 :       screening_volume = fourpi/3.0_dp*screening_radius**3
     239           42 :       N = INT(screening_volume/35.0_dp) ! rule of thumb
     240              : 
     241          336 :       ALLOCATE (block_S(N, N), S_evals(N), S_evecs(N, N))
     242          118 :       IF (PRESENT(descriptor)) ALLOCATE (descriptor(N*ndesc))
     243           50 :       IF (PRESENT(forces)) ALLOCATE (block_M(N, N))
     244              : 
     245              :       !find neighbors
     246              :       !TODO: this is a quadratic algorithm, use a neighbor-list instead
     247          210 :       ALLOCATE (neighbor_dist(natoms), neighbor_order(natoms))
     248          168 :       Ri = particle_set(iatom)%r
     249          132 :       DO jatom = 1, natoms
     250          360 :          Rj = particle_set(jatom)%r
     251           90 :          Rij = pbc(Ri, Rj, cell)
     252          402 :          neighbor_dist(jatom) = SQRT(SUM(Rij**2))
     253              :       END DO
     254           42 :       CALL sort(neighbor_dist, natoms, neighbor_order)
     255           42 :       CPASSERT(neighbor_order(1) == iatom) !central atom should be closesd to itself
     256              : 
     257              :       ! check if N was chosen large enough
     258           42 :       IF (natoms > N) THEN
     259            0 :          IF (neighbor_dist(N + 1) < screening_radius) THEN
     260            0 :             CPABORT("PAO heuristic for descriptor size broke down")
     261              :          END IF
     262              :       END IF
     263              : 
     264          118 :       DO idesc = 1, ndesc
     265           76 :          sbeta = ipao_descriptors(idesc)%screening
     266              : 
     267              :          ! construct matrix S_block from neighboring atoms
     268           76 :          block_S = 0.0_dp
     269          234 :          DO j = 1, MIN(natoms, N)
     270          568 :          DO k = 1, MIN(natoms, N)
     271          334 :             jatom = neighbor_order(j)
     272          334 :             katom = neighbor_order(k)
     273              : 
     274              :             ! get weigths and betas
     275          334 :             CALL get_atomic_kind(particle_set(jatom)%atomic_kind, kind_number=jkind)
     276          334 :             CALL get_qs_kind(qs_kind_set(jkind), pao_descriptors=jpao_descriptors)
     277          334 :             CALL get_atomic_kind(particle_set(katom)%atomic_kind, kind_number=kkind)
     278          334 :             CALL get_qs_kind(qs_kind_set(kkind), pao_descriptors=kpao_descriptors)
     279          334 :             IF (SIZE(jpao_descriptors) /= ndesc .OR. SIZE(kpao_descriptors) /= ndesc) THEN
     280            0 :                CPABORT("Not all KINDs have the same number of PAO_DESCRIPTOR sections")
     281              :             END IF
     282          334 :             jweight = jpao_descriptors(idesc)%weight
     283          334 :             jbeta = jpao_descriptors(idesc)%beta
     284          334 :             kweight = kpao_descriptors(idesc)%weight
     285          334 :             kbeta = kpao_descriptors(idesc)%beta
     286          334 :             beta_sum = sbeta + jbeta + kbeta
     287              : 
     288              :             ! get distances
     289         1336 :             Rj = particle_set(jatom)%r
     290         1336 :             Rk = particle_set(katom)%r
     291          334 :             Rij = pbc(Ri, Rj, cell)
     292          334 :             Rik = pbc(Ri, Rk, cell)
     293          334 :             Rjk = pbc(Rj, Rk, cell)
     294         1336 :             Rij2 = SUM(Rij**2)
     295         1336 :             Rik2 = SUM(Rik**2)
     296         1336 :             Rjk2 = SUM(Rjk**2)
     297              : 
     298              :             ! calculate integral over three Gaussians
     299          334 :             exponent = -(sbeta*jbeta*Rij2 + sbeta*kbeta*Rik2 + jbeta*kbeta*Rjk2)/beta_sum
     300          334 :             integral = EXP(exponent)*rootpi/SQRT(beta_sum)
     301          334 :             normalization = SQRT(jbeta*kbeta)/rootpi**2
     302         1160 :             block_S(j, k) = jweight*kweight*normalization*integral
     303              :          END DO
     304              :          END DO
     305              : 
     306              :          ! diagonalize V_block
     307      1653532 :          S_evecs(:, :) = block_S(:, :)
     308           76 :          CALL diamat_all(S_evecs, S_evals)
     309              : 
     310              :          ! use eigenvalues of S_block as descriptor
     311           76 :          IF (PRESENT(descriptor)) THEN
     312        10360 :             descriptor((idesc - 1)*N + 1:idesc*N) = S_evals(:)
     313              :          END IF
     314              : 
     315              :          ! FORCES ----------------------------------------------------------------------------------
     316          118 :          IF (PRESENT(forces)) THEN
     317            6 :             CPASSERT(PRESENT(descr_grad))
     318            6 :             block_M = 0.0_dp
     319          888 :             DO k = 1, N
     320          882 :                w = descr_grad((idesc - 1)*N + k)
     321     38510772 :                block_M(:, :) = block_M(:, :) + w*MATMUL(S_evecs(:, k:k), TRANSPOSE(S_evecs(:, k:k)))
     322              :             END DO
     323              : 
     324           20 :             DO j = 1, MIN(natoms, N)
     325           54 :             DO k = 1, MIN(natoms, N)
     326           34 :                jatom = neighbor_order(j)
     327           34 :                katom = neighbor_order(k)
     328              : 
     329              :                ! get weigths and betas
     330           34 :                CALL get_atomic_kind(particle_set(jatom)%atomic_kind, kind_number=jkind)
     331           34 :                CALL get_qs_kind(qs_kind_set(jkind), pao_descriptors=jpao_descriptors)
     332           34 :                CALL get_atomic_kind(particle_set(katom)%atomic_kind, kind_number=kkind)
     333           34 :                CALL get_qs_kind(qs_kind_set(kkind), pao_descriptors=kpao_descriptors)
     334           34 :                jweight = jpao_descriptors(idesc)%weight
     335           34 :                jbeta = jpao_descriptors(idesc)%beta
     336           34 :                kweight = kpao_descriptors(idesc)%weight
     337           34 :                kbeta = kpao_descriptors(idesc)%beta
     338           34 :                beta_sum = sbeta + jbeta + kbeta
     339              : 
     340              :                ! get distances
     341          136 :                Rj = particle_set(jatom)%r
     342          136 :                Rk = particle_set(katom)%r
     343           34 :                Rij = pbc(Ri, Rj, cell)
     344           34 :                Rik = pbc(Ri, Rk, cell)
     345           34 :                Rjk = pbc(Rj, Rk, cell)
     346          136 :                Rij2 = SUM(Rij**2)
     347          136 :                Rik2 = SUM(Rik**2)
     348          136 :                Rjk2 = SUM(Rjk**2)
     349              : 
     350              :                ! calculate integral over three Gaussians
     351           34 :                exponent = -(sbeta*jbeta*Rij2 + sbeta*kbeta*Rik2 + jbeta*kbeta*Rjk2)/beta_sum
     352           34 :                integral = EXP(exponent)*rootpi/SQRT(beta_sum)
     353           34 :                normalization = SQRT(jbeta*kbeta)/rootpi**2
     354           34 :                deriv = 2.0_dp/beta_sum*block_M(j, k)
     355           34 :                w = jweight*kweight*normalization*integral*deriv
     356          136 :                forces(iatom, :) = forces(iatom, :) - sbeta*jbeta*Rij*w
     357          136 :                forces(jatom, :) = forces(jatom, :) + sbeta*jbeta*Rij*w
     358          136 :                forces(iatom, :) = forces(iatom, :) - sbeta*kbeta*Rik*w
     359          136 :                forces(katom, :) = forces(katom, :) + sbeta*kbeta*Rik*w
     360          136 :                forces(jatom, :) = forces(jatom, :) - jbeta*kbeta*Rjk*w
     361          218 :                forces(katom, :) = forces(katom, :) + jbeta*kbeta*Rjk*w
     362              :             END DO
     363              :             END DO
     364              :          END IF
     365              :       END DO
     366              : 
     367           42 :       CALL timestop(handle)
     368           84 :    END SUBROUTINE calc_descriptor_overlap
     369              : 
     370              : ! **************************************************************************************************
     371              : !> \brief Calculates a descriptor based on distance between two atoms
     372              : !> \param particle_set ...
     373              : !> \param qs_kind_set ...
     374              : !> \param cell ...
     375              : !> \param iatom ...
     376              : !> \param descriptor ...
     377              : !> \param descr_grad ...
     378              : !> \param forces ...
     379              : ! **************************************************************************************************
     380          112 :    SUBROUTINE calc_descriptor_r12(particle_set, qs_kind_set, cell, iatom, descriptor, descr_grad, forces)
     381              :       TYPE(particle_type), DIMENSION(:), POINTER         :: particle_set
     382              :       TYPE(qs_kind_type), DIMENSION(:), POINTER          :: qs_kind_set
     383              :       TYPE(cell_type), POINTER                           :: cell
     384              :       INTEGER, INTENT(IN)                                :: iatom
     385              :       REAL(dp), ALLOCATABLE, DIMENSION(:), OPTIONAL      :: descriptor
     386              :       REAL(dp), DIMENSION(:), INTENT(IN), OPTIONAL       :: descr_grad
     387              :       REAL(dp), DIMENSION(:, :), INTENT(INOUT), OPTIONAL :: forces
     388              : 
     389              :       REAL(dp), DIMENSION(3)                             :: G, R1, R12, R2
     390              : 
     391          112 :       CPASSERT(SIZE(particle_set) == 2)
     392              : 
     393              :       MARK_USED(qs_kind_set)
     394              :       MARK_USED(iatom)
     395              :       MARK_USED(cell)
     396              : 
     397          448 :       R1 = particle_set(1)%r
     398          448 :       R2 = particle_set(2)%r
     399          112 :       R12 = pbc(R1, R2, cell)
     400              : 
     401          112 :       IF (PRESENT(descriptor)) THEN
     402          104 :          ALLOCATE (descriptor(1))
     403          416 :          descriptor(1) = SQRT(SUM(R12**2))
     404              :       END IF
     405              : 
     406          112 :       IF (PRESENT(forces)) THEN
     407            8 :          CPASSERT(PRESENT(descr_grad))
     408           56 :          G = R12/SQRT(SUM(R12**2))*descr_grad(1)
     409           32 :          forces(1, :) = forces(1, :) + G
     410           32 :          forces(2, :) = forces(2, :) - G
     411              :       END IF
     412          112 :    END SUBROUTINE calc_descriptor_r12
     413              : 
     414          932 : END MODULE pao_ml_descriptor
        

Generated by: LCOV version 2.0-1