Last commit for src/ext/symmfunc.f90: 5874abaa643d4472a2aa9d1c5dbe454dadbd8d1f

Initial commit of the AENET code.

Bruno Mundim [2017-01-02 17:48:39]
Initial commit of the AENET code.
!-----------------------------------------------------------------------
!                         symmetry functions
!           --> translation/rotation invariant coordinates
!
! This module implements the symmetry function basis by Behler following
! the original publication: J. Behler, J. Chem. Phys. 134 (2011) 074106.
!-----------------------------------------------------------------------
!+ This file is part of the AENET package.
!+
!+ Copyright (C) 2012-2016 Nongnuch Artrith and Alexander Urban
!+
!+ This program is free software: you can redistribute it and/or modify
!+ it under the terms of the GNU General Public License as published by
!+ the Free Software Foundation, either version 3 of the License, or
!+ (at your option) any later version.
!+
!+ This program is distributed in the hope that it will be useful, but
!+ WITHOUT ANY WARRANTY; without even the implied warranty of
!+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
!+ General Public License for more details.
!+
!+ You should have received a copy of the GNU General Public License
!+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
!-----------------------------------------------------------------------
! 2011-04-25 Alexander Urban (AU)
! 2011-07-20 AU --- prefactors for angular functions added
! 2013-05-07 AU --- earlier evaluation of these prefactors
! 2013-09-30 AU --- correct wrong behavior of angular functions
!-----------------------------------------------------------------------

module symmfunc

  implicit none

  double precision, parameter, private :: PI    = 3.14159265358979d0
  double precision, parameter, private :: PI2   = 2.0d0*PI
  double precision, parameter, private :: EPS   = 1.0d-12
  integer,          parameter, private :: NG    = 5

  !--------------------------------------------------------------------!
  ! sf_nG_type(i)    : number of symmetry functions for species i      !
  !                                                                    !
  ! sf_nTypes        : number of different atom/point types            !
  ! sf_nPairs        : number type pairs (= nTypes!)                   !
  ! sf_nGmax         : max. num. symmetry functs. per type pair/triple !
  ! sf_idx2(i,j)     : pair index for species i-j                      !
  ! sf_idx3(i,j,k)   : triple index for species i-j-k                  !
  ! sf_nG(i,pij)     : num. symmetry function type i for type pair pij !
  ! sf_pG?(i,iG,pij) : parameter i for the iG-th symm.-func. of type ? !
  !                    for type pair pij                               !
  !                    Parameters:  G1:  Rc                            !
  !                                 G2:  Rc, Rs, eta                   !
  !                                 G3:  Rc, kappa                     !
  !                                 G4:  Rc, lambda, zeta, eta         !
  !                                 G5:  Rc, lambda, zeta, eta         !
  !--------------------------------------------------------------------!

  integer,          dimension(:),     allocatable, public  :: sf_nG_type

  integer,                                         private :: sf_nTypes
  integer,                                         private :: sf_nPairs
  integer,                                         private :: sf_nTriples
  integer,                                         private :: sf_nGMax
  integer,          dimension(:,:),   allocatable, private :: sf_idx2
  integer,          dimension(:,:,:), allocatable, private :: sf_idx3
  integer,          dimension(:),     allocatable, private :: sf_iG02
  integer,          dimension(:),     allocatable, private :: sf_iG03
  integer,          dimension(:,:),   allocatable, private :: sf_nG2
  integer,          dimension(:,:),   allocatable, private :: sf_nG3
  double precision, dimension(:,:),   allocatable, private :: sf_pG1
  double precision, dimension(:,:,:), allocatable, private :: sf_pG2
  double precision, dimension(:,:,:), allocatable, private :: sf_pG3
  double precision, dimension(:,:,:), allocatable, private :: sf_pG4
  double precision, dimension(:,:,:), allocatable, private :: sf_pG5

  !--------------------------------------------------------------------!

  integer, public :: stdout = 6
  integer, public :: stderr = 0

  !--------------------------------------------------------------------!

  logical, private :: isInit  = .false.
  integer, private :: memSize = 0

contains !-------------------------------------------------------------!

  subroutine sf_init(ntypes, nGmax)

    implicit none

    integer, intent(in) :: ntypes
    integer, intent(in) :: nGmax

    integer :: i, j, k, idx2, idx3

    sf_nTypes = ntypes
    sf_nGMax  = nGmax

    ! initialize type pair/triple index:
    ! note: for angular symmetry functions A-B-C = A-C-B
    allocate(sf_idx2(ntypes,ntypes), sf_idx3(ntypes,ntypes,ntypes))
    idx2 = 0
    idx3 = 0
    do i = 1, ntypes
       do j = 1, ntypes
          idx2 = idx2 + 1
          sf_idx2(i,j) = idx2
          idx3 = idx3 + 1
          sf_idx3(i,j,j) = idx3
          do k = j+1, ntypes
             idx3 = idx3 + 1
             sf_idx3(i,j,k) = idx3
             sf_idx3(i,k,j) = idx3
          end do
       end do
    end do
    sf_nPairs = idx2
    sf_nTriples = idx3

    allocate(sf_nG_type(ntypes),            &
             sf_nG2(NG, sf_nPairs),         &
             sf_nG3(NG, sf_nTriples),       &
             sf_iG02(sf_nPairs),            &
             sf_iG03(sf_nTriples),          &
             sf_pG1(nGmax, sf_nPairs),      &
             sf_pG2(3, nGmax, sf_nPairs),   &
             sf_pG3(2, nGmax, sf_nPairs),   &
             sf_pG4(4, nGmax, sf_nTriples), &
             sf_pG5(4, nGmax, sf_nTriples)  )
    sf_nG_type(:) = 0
    sf_nG2(:,:) = 0
    sf_nG3(:,:) = 0

    isInit = .true.

  end subroutine sf_init

  !--------------------------------------------------------------------!

  subroutine sf_final()

    implicit none

    if (isInit) then
       deallocate(sf_idx2, sf_idx3, sf_nG2, sf_nG3, sf_pG1, sf_pG2, &
                  sf_pG3,  sf_pG4, sf_pG5, sf_nG_type, sf_iG02, sf_iG03)
       isInit = .false.
    end if

  end subroutine sf_final

  !--------------------------------------------------------------------!
  !                      parameter initialization                      !
  !--------------------------------------------------------------------!

  subroutine sf_add_rad(funct, type1, type2, Rc, Rs, eta, kappa)

    implicit none

    integer,                    intent(in) :: funct
    integer,                    intent(in) :: type1, type2
    double precision, optional, intent(in) :: Rc, Rs, eta, kappa

    integer :: iG, pij

    pij = sf_idx2(type1,type2)

    if (sf_nG2(funct,pij) >= sf_nGMax) then
       write(stderr,*) "Error: Memory exceeded in `sf_add()'."
       write(stderr,*) sf_nG2(funct,pij), sf_nGMax
       stop
    end if

    if (.not. present(Rc)) then
       write(stderr,*) "Error: Rc undefined in `sf_add()'"
       stop
    end if

    iG = sf_nG2(funct,pij) + 1
    select case(funct)
    case(1)
       sf_nG_type(type1) = sf_nG_type(type1) + 1
       sf_nG2(funct,pij) = iG
       sf_pG1(iG,pij)   = Rc
    case(2)
       if (.not. (present(Rs) .and. present(eta))) then
          write(stderr,*) "Error: needed for G2: Rs, eta"
          stop
       end if
       sf_nG_type(type1) = sf_nG_type(type1) + 1
       sf_nG2(funct,pij) = iG
       sf_pG2(1,iG,pij) = Rc
       sf_pG2(2,iG,pij) = Rs
       sf_pG2(3,iG,pij) = eta
    case(3)
       if (.not. present(kappa)) then
          write(stderr,*) "Error: needed for G3: kappa"
          stop
       end if
       sf_nG_type(type1) = sf_nG_type(type1) + 1
       sf_nG2(funct,pij) = iG
       sf_pG3(1,iG,pij) = Rc
       sf_pG3(2,iG,pij) = kappa
    case default
       write(stderr,*) "Error: Not a radial symmetry function type: ", funct
       stop
    end select

    call sf_build_index()

  end subroutine sf_add_rad

  !--------------------------------------------------------------------!

  subroutine sf_add_ang(funct, type1, type2, type3, Rc, eta, lambda, zeta)

    implicit none

    integer,                    intent(in) :: funct
    integer,                    intent(in) :: type1, type2, type3
    double precision, optional, intent(in) :: Rc, eta, lambda, zeta

    integer :: iG, tijk

    tijk = sf_idx3(type1, type2, type3)

    if (sf_nG3(funct,tijk) >= sf_nGMax) then
       write(stderr,*) "Error: Memory exceeded in `sf_add()'."
       write(stderr,*) sf_nG3(funct,tijk), sf_nGMax
       stop
    end if

    if (.not. present(Rc)) then
       write(stderr,*) "Error: Rc undefined in `sf_add()'"
       stop
    end if

    iG = sf_nG3(funct,tijk) + 1
    select case(funct)
    case(4)
       if (.not. (present(lambda) .and. present(zeta) &
           .and. present(eta))) then
          write(stderr,*) "Error: needed for G4: eta, lambda, zeta"
          stop
       end if
       sf_nG_type(type1) = sf_nG_type(type1) + 1
       sf_nG3(funct,tijk) = iG
       sf_pG4(1,iG,tijk) = Rc
       sf_pG4(2,iG,tijk) = lambda
       sf_pG4(3,iG,tijk) = zeta
       sf_pG4(4,iG,tijk) = eta
    case(5)
       if (.not. (present(lambda) .and. present(zeta) &
           .and. present(eta))) then
          write(stderr,*) "Error: needed for G5: eta, lambda, zeta"
          stop
       end if
       sf_nG_type(type1) = sf_nG_type(type1) + 1
       sf_nG3(funct,tijk) = iG
       sf_pG5(1,iG,tijk) = Rc
       sf_pG5(2,iG,tijk) = lambda
       sf_pG5(3,iG,tijk) = zeta
       sf_pG5(4,iG,tijk) = eta
    case default
       write(stderr,*) "Error: Not an angular symmetry function type: ", funct
       stop
    end select

    call sf_build_index()

  end subroutine sf_add_ang

  !--------------------------------------------------------------------!
  ! The functions are organized in memory not in the same order as     !
  ! they are added, but rather depending on their type and species.    !
  ! The procedure `sf_build_index()' generates the indices for radial  !
  ! and angular functions.                                             !
  !--------------------------------------------------------------------!

  subroutine sf_build_index()

    implicit none

    integer :: pij, tijk, i
    integer :: itype1, itype2, itype3

    do itype1 = 1, sf_nTypes
       i = 0
       do itype2 = 1, sf_nTypes
          pij = sf_idx2(itype1, itype2)
          sf_iG02(pij) = i
          i = i + sum(sf_nG2(:,pij))
          do itype3 = itype2, sf_nTypes
             tijk = sf_idx3(itype1, itype2, itype3)
             sf_iG03(tijk) = i
             i = i + sum(sf_nG3(:,tijk))
          end do
       end do
    end do

  end subroutine sf_build_index

  !--------------------------------------------------------------------!
  !               evaluate finger print for single atom                !
  !--------------------------------------------------------------------!

  subroutine sf_fingerprint(type1, Xi, nx, X, typej, n, G, dGi, dGj)

    implicit none

    !------------------------------------------------------------------!
    ! type1     : type index of the central particle                   !
    ! Xi(1:3)   : cartesian coordinates of the central particle        !
    ! nx        : number of other particles                            !
    ! X(1:3,j)  : cartesian coordinates of the j-th particle           !
    ! typej(j)  : atom type index of j-th atom                         !
    ! n         : number of symmetry functions                         !
    ! G(j)      : (out) value of the j-th symmetry function            !
    ! dGi(1:3,j): (out, optional) derivative of the j-th symmetry      !
    !             function wrt. the central particle's coordinates     !
    ! dGj(:,j,k): (out, optional) derivative of the j-th symmetry      !
    !             function wrt. the coordinates of the k-th particle   !
    ! Note: dGi and dGj have to be present/absent simultaneously       !
    !------------------------------------------------------------------!

    integer,                                       intent(in)  :: type1
    double precision, dimension(3),                intent(in)  :: Xi
    integer,                                       intent(in)  :: nx
    double precision, dimension(3,nx),             intent(in)  :: X
    integer,          dimension(nx),               intent(in)  :: typej
    integer,                                       intent(in)  :: n
    double precision, dimension(n),                intent(out) :: G
    double precision, dimension(3,n),    optional, intent(out) :: dGi
    double precision, dimension(3,n,nx), optional, intent(out) :: dGj

    integer          :: j, k, pij, tijk
    integer          :: iG_ang0, iG
    double precision :: Rij, Rik, Rjk, cost
    logical          :: deriv

    double precision, dimension(3) :: R1, R2, R3

    if (present(dGi) .and. present(dGj)) then
       deriv = .true.
    else
       deriv = .false.
    end if

    if (.not. isInit) return

    if (n < sf_nG_type(type1)) then
       write(stderr,*) "Error: number of symmetry functions exceeded."
       stop
    end if

    G(1:n)  = 0.0d0
    iG_ang0 = 0
    if (deriv) then
       dGi(1:3,1:n) = 0.0d0
       dGj(1:3,1:n,1:nx) = 0.0d0
    end if
    jloop : do j = 1, nx
       pij = sf_idx2(type1, typej(j))

       R1  = X(:,j) - Xi(:)
       Rij = sqrt(sum(R1*R1))
       R1  = R1/Rij

       iG = sf_iG02(pij)

       ! radial symmetry functions:
       if (deriv) then
          call sf_G1_update(pij, R1(1:3), Rij, n, G, iG, dGi, dGj(1:3,1:n,j))
          call sf_G2_update(pij, R1(1:3), Rij, n, G, iG, dGi, dGj(1:3,1:n,j))
          call sf_G3_update(pij, R1(1:3), Rij, n, G, iG, dGi, dGj(1:3,1:n,j))
       else
          call sf_G1_update(pij, R1(1:3), Rij, n, G, iG)
          call sf_G2_update(pij, R1(1:3), Rij, n, G, iG)
          call sf_G3_update(pij, R1(1:3), Rij, n, G, iG)
       end if

       ! if (iG_ang0 == 0) iG_ang0 = sum(sf_nG2(:,:))
       kloop : do k = j+1, nx
          tijk = sf_idx3(type1, typej(j), typej(k))

          R2  = X(:,k) - Xi(:)
          Rik = sqrt(sum(R2*R2))
          if (Rik <= 1.0d-8) then
             write(stderr, *) "Warning: an atom in the neighbor list is " &
                  // "identical to the central atom (sf_fingerprint)"
          else
             R2  = R2/Rik
          end if

          R3  = X(:,k) - X(:,j)
          Rjk = sqrt(sum(R3*R3))
          if (Rjk <= 1.0d-8) then
             write(stderr, *) "Warning: redundant atoms in neighbor " &
                              // "list (sf_fingerprint)"
          else
             R3  = R3/Rjk
          end if

          ! cos(theta_ijk)
          cost = sum(R1*R2)
          cost = max(cost,-1.0d0)
          cost = min(cost, 1.0d0)

          iG = sf_iG03(tijk)

          ! angular symmetry functions:
          if (deriv) then
             call sf_G4_update(tijk, R1(1:3), R2(1:3), R3(1:3), Rij, Rik, Rjk, cost, &
                               n, G, iG, dGi, dGj(1:3,1:n,j), dGj(1:3,1:n,k))
             call sf_G5_update(tijk, R1(1:3), R2(1:3), Rij, Rik, cost, &
                               n, G, iG, dGi, dGj(1:3,1:n,j), dGj(1:3,1:n,k))
          else
             call sf_G4_update(tijk, R1(1:3), R2(1:3), R3(1:3), Rij, Rik, Rjk, &
                               cost, n, G, iG)
             call sf_G5_update(tijk, R1(1:3), R2(1:3), Rij, Rik, cost, &
                               n, G, iG)
          end if

       end do kloop
    end do jloop

  end subroutine sf_fingerprint



  !==================== radial symmetry functions =====================!



  !--------------------------------------------------------------------!
  !            radial symm. function 1 (eq. 5 in Ref. [1])             !
  !                                                                    !
  ! here: vecRij = vec{R}_ij/||vec{R})_ij||                            !
  !--------------------------------------------------------------------!

  subroutine sf_G1_ij(Rij, Rc, Gij, dGij)

    implicit none

    double precision, intent(in)  :: Rij
    double precision, intent(in)  :: Rc
    double precision, intent(out) :: Gij
    double precision, intent(out) :: dGij

    double precision :: fc, dfc

    call sf_cut(Rij, Rc, fc, dfc)

    Gij  = fc
    dGij = dfc

  end subroutine sf_G1_ij

  !--------------------------------------------------------------------!

  subroutine sf_G1_update(pij, vecRij, Rij, n, G, iG, dGi, dGj)

    implicit none

    integer,                                    intent(in)    :: pij
    double precision, dimension(3),             intent(in)    :: vecRij
    double precision,                           intent(in)    :: Rij
    integer,                                    intent(in)    :: n
    double precision, dimension(n),             intent(inout) :: G
    integer,                                    intent(inout) :: iG
    double precision, dimension(3,n), optional, intent(inout) :: dGi
    double precision, dimension(3,n), optional, intent(inout) :: dGj

    integer          :: iG1
    double precision :: G1, dG1
    double precision :: Rc

    do iG1 = 1, sf_nG2(1,pij)
       iG    = iG + 1
       Rc    = sf_pG1(iG1,pij)
       call sf_G1_ij(Rij, Rc, G1, dG1)
       G(iG) = G(iG) + G1
       if (present(dGi)) dGi(1:3,iG) = dGi(1:3,iG) - dG1*vecRij(1:3)
       if (present(dGj)) dGj(1:3,iG) = dGj(1:3,iG) + dG1*vecRij(1:3)
    end do

  end subroutine sf_G1_update

  !--------------------------------------------------------------------!
  !            radial symm. function 2 (eq. 6 in Ref. [1])             !
  !--------------------------------------------------------------------!

  subroutine sf_G2_ij(Rij, Rc, Rs, eta, Gij, dGij)

    implicit none

    double precision, intent(in)  :: Rij
    double precision, intent(in)  :: Rc
    double precision, intent(in)  :: Rs
    double precision, intent(in)  :: eta
    double precision, intent(out) :: Gij
    double precision, intent(out) :: dGij

    double precision :: fc, dfc
    double precision :: fexp, arg

    call sf_cut(Rij, Rc, fc, dfc)

    arg  = Rij - Rs
    fexp = exp(-eta*arg*arg)

    Gij  = fexp*fc
    dGij = fexp*( dfc - 2.0d0*eta*arg*fc  )

  end subroutine sf_G2_ij

  !--------------------------------------------------------------------!

  subroutine sf_G2_update(pij, vecRij, Rij, n, G, iG, dGi, dGj)

    implicit none

    integer,                                    intent(in)    :: pij
    double precision, dimension(3),             intent(in)    :: vecRij
    double precision,                           intent(in)    :: Rij
    integer,                                    intent(in)    :: n
    double precision, dimension(n),             intent(inout) :: G
    integer,                                    intent(inout) :: iG
    double precision, dimension(3,n), optional, intent(inout) :: dGi
    double precision, dimension(3,n), optional, intent(inout) :: dGj

    integer          :: iG2
    double precision :: Rc, Rs, eta
    double precision :: G2, dG2

    do iG2 = 1, sf_nG2(2,pij)
       iG    = iG + 1
       Rc    = sf_pG2(1,iG2,pij)
       Rs    = sf_pG2(2,iG2,pij)
       eta   = sf_pG2(3,iG2,pij)
       call sf_G2_ij(Rij, Rc, Rs, eta, G2, dG2)
       G(iG) = G(iG) + G2
       if (present(dGi)) dGi(1:3,iG) = dGi(1:3,iG) - dG2*vecRij(1:3)
       if (present(dGj)) dGj(1:3,iG) = dGj(1:3,iG) + dG2*vecRij(1:3)
    end do

  end subroutine sf_G2_update

  !--------------------------------------------------------------------!
  !            radial symm. function 3 (eq. 6 in Ref. [1])             !
  !--------------------------------------------------------------------!

  subroutine sf_G3_ij(Rij, Rc, kappa, Gij, dGij)

    implicit none

    double precision, intent(in)  :: Rij
    double precision, intent(in)  :: Rc
    double precision, intent(in)  :: kappa
    double precision, intent(out) :: Gij
    double precision, intent(out) :: dGij

    double precision :: fc, dfc
    double precision :: fcos, fsin, arg

    call sf_cut(Rij, Rc, fc, dfc)

    arg  = kappa*Rij
    fcos = cos(arg)
    fsin = sin(arg)

    Gij  = fcos*fc
    dGij = fcos*dfc - kappa*fsin*fc

  end subroutine sf_G3_ij

  !--------------------------------------------------------------------!

  subroutine sf_G3_update(pij, vecRij, Rij, n, G, iG, dGi, dGj)

    implicit none

    integer,                                    intent(in)    :: pij
    double precision, dimension(3),             intent(in)    :: vecRij
    double precision,                           intent(in)    :: Rij
    integer,                                    intent(in)    :: n
    double precision, dimension(n),             intent(inout) :: G
    integer,                                    intent(inout) :: iG
    double precision, dimension(3,n), optional, intent(inout) :: dGi
    double precision, dimension(3,n), optional, intent(inout) :: dGj

    integer          :: iG3
    double precision :: Rc, kappa
    double precision :: G3, dG3

    do iG3 = 1, sf_nG2(3,pij)
       iG    = iG + 1
       Rc    = sf_pG3(1,iG3,pij)
       kappa = sf_pG3(2,iG3,pij)
       call sf_G3_ij(Rij, Rc, kappa, G3, dG3)
       G(iG) = G(iG) + G3
       if (present(dGi)) dGi(1:3,iG) = dGi(1:3,iG) - dG3*vecRij(1:3)
       if (present(dGj)) dGj(1:3,iG) = dGj(1:3,iG) + dG3*vecRij(1:3)
    end do

  end subroutine sf_G3_update



  !==================== angular symmetry functions ====================!

  !--------------------------------------------------------------------!
  !          first angular symm. function (eq. 8 in Ref. [1])          !
  !--------------------------------------------------------------------!

  !--------------------------------------------------------------------!
  !                        angle dependend part                        !
  ! This is function F_1(R_ij,R_ik) in the documentation.              !
  !--------------------------------------------------------------------!

  subroutine sf_F1_ijk(vecRij, vecRik, Rij, Rik, cost, lambda, &
                       zeta, Fijk, dFijk_dRj, dFijk_dRk)

    implicit none

    double precision, dimension(3), intent(in)  :: vecRij, vecRik
    double precision,               intent(in)  :: Rij, Rik
    double precision,               intent(in)  :: cost
    double precision,               intent(in)  :: lambda
    double precision,               intent(in)  :: zeta
    double precision,               intent(out) :: Fijk
    double precision, dimension(3), intent(out) :: dFijk_dRj
    double precision, dimension(3), intent(out) :: dFijk_dRk

    double precision :: arg, prefactor

    if (abs(cost) > 1.0d0) write(*,*) "cos(theta) = ", cost

    arg = 0.5d0*(1.0d0 + lambda*cost)
    prefactor = 0.5d0*zeta*lambda*arg**(zeta-1.0d0)

    Fijk  = arg**zeta

    dFijk_dRj = prefactor*( -cost*( vecRij/Rij ) &
                            + vecRik/Rij )

    dFijk_dRk = prefactor*( -cost*( vecRik/Rik ) &
                            + vecRij/Rik )

  end subroutine sf_F1_ijk

  !--------------------------------------------------------------------!
  !                      distance dependend part                       !
  ! This is function F_2(R) in the documentation.                      !
  ! --> very similar to sf_G2_ij(), may be combined in future.         !
  !--------------------------------------------------------------------!

  subroutine sf_F2_ij(Rij, Rc, eta, Fij, dFij)

    implicit none

    double precision, intent(in)  :: Rij
    double precision, intent(in)  :: Rc
    double precision, intent(in)  :: eta
    double precision, intent(out) :: Fij
    double precision, intent(out) :: dFij

    double precision :: fc, dfc
    double precision :: fexp

    call sf_cut(Rij, Rc, fc, dfc)

    fexp = exp(-eta*Rij*Rij)

    Fij  = fexp*fc
    dFij = fexp*( dfc - 2.0d0*eta*Rij*fc  )

  end subroutine sf_F2_ij

  !--------------------------------------------------------------------!

  subroutine sf_G4_update(tijk, vecRij, vecRik, vecRjk, Rij, Rik, Rjk, &
                          cost, n, G, iG, dGi, dGj, dGk)

    implicit none

    integer,                                    intent(in)    :: tijk
    double precision, dimension(3),             intent(in)    :: vecRij, vecRik, vecRjk
    double precision,                           intent(in)    :: Rij, Rik, Rjk
    double precision,                           intent(in)    :: cost
    integer,                                    intent(in)    :: n
    double precision, dimension(n),             intent(inout) :: G
    integer,                                    intent(inout) :: iG
    double precision, dimension(3,n), optional, intent(inout) :: dGi
    double precision, dimension(3,n), optional, intent(inout) :: dGj
    double precision, dimension(3,n), optional, intent(inout) :: dGk

    integer                        :: iG4
    double precision               :: Rc, lambda, zeta, eta
    double precision               :: G4
    double precision, dimension(3) :: dG4, dF1j, dF1k

    double precision :: F1, F2ij, dF2ij, F2ik, dF2ik, F2jk, dF2jk

    do iG4 = 1, sf_nG3(4,tijk)
       iG     = iG + 1
       Rc     = sf_pG4(1,iG4,tijk)
       lambda = sf_pG4(2,iG4,tijk)
       zeta   = sf_pG4(3,iG4,tijk)
       eta    = sf_pG4(4,iG4,tijk)

       call sf_F1_ijk(vecRij, vecRik, Rij, Rik, cost, lambda, &
                      zeta, F1, dF1j, dF1k)
       call sf_F2_ij(Rij, Rc, eta, F2ij, dF2ij)
       call sf_F2_ij(Rik, Rc, eta, F2ik, dF2ik)
       call sf_F2_ij(Rjk, Rc, eta, F2jk, dF2jk)

       G4    = F1*F2ij*F2ik*F2jk
       ! factor of 2 for k<j in sf_fingerprint()
       G(iG) = G(iG) + 2.0d0*G4

       if (present(dGi)) then
          dG4(1:3) = F2jk*( -(dF1j(1:3)+dF1k(1:3))*F2ij*F2ik &
                            - vecRij(1:3)*F1*dF2ij*F2ik      &
                            - vecRik(1:3)*F1*F2ij*dF2ik )
          dGi(1:3,iG) = dGi(1:3,iG) + 2.0d0*dG4(1:3)
       end if
       if (present(dGj)) then
          dG4(1:3) =  dF1j(1:3)*F2ij*F2ik*F2jk           &
                      + vecRij(1:3)*F1*dF2ij*F2ik*F2jk   &
                      - vecRjk(1:3)*F1*F2ij*F2ik*dF2jk
          dGj(1:3,iG) = dGj(1:3,iG) + 2.0d0*dG4(1:3)
       end if
       if (present(dGk)) then
          dG4(1:3) =  dF1k(1:3)*F2ij*F2ik*F2jk           &
                      + vecRik(1:3)*F1*F2ij*dF2ik*F2jk   &
                      + vecRjk(1:3)*F1*F2ij*F2ik*dF2jk
          dGk(1:3,iG) = dGk(1:3,iG) + 2.0d0*dG4(1:3)
       end if

    end do

  end subroutine sf_G4_update

  !--------------------------------------------------------------------!
  !         second angular symm. function (eq. 9 in Ref. [1])          !
  !--------------------------------------------------------------------!

  subroutine sf_G5_update(tijk, vecRij, vecRik, Rij, Rik, cost, n, &
                          G, iG, dGi, dGj, dGk)

    implicit none

    integer,                                    intent(in)    :: tijk
    double precision, dimension(3),             intent(in)    :: vecRij, vecRik
    double precision,                           intent(in)    :: Rij, Rik
    double precision,                           intent(in)    :: cost
    integer,                                    intent(in)    :: n
    double precision, dimension(n),             intent(inout) :: G
    integer,                                    intent(inout) :: iG
    double precision, dimension(3,n), optional, intent(inout) :: dGi
    double precision, dimension(3,n), optional, intent(inout) :: dGj
    double precision, dimension(3,n), optional, intent(inout) :: dGk

    integer                        :: iG5
    double precision               :: Rc, lambda, zeta, eta
    double precision               :: G5
    double precision, dimension(3) :: dG5, dF1j, dF1k

    double precision :: F1, F2ij, dF2ij, F2ik, dF2ik

    do iG5 = 1, sf_nG3(5,tijk)
       iG     = iG + 1
       Rc     = sf_pG5(1,iG5,tijk)
       lambda = sf_pG5(2,iG5,tijk)
       zeta   = sf_pG5(3,iG5,tijk)
       eta    = sf_pG5(4,iG5,tijk)

       call sf_F1_ijk(vecRij, vecRik, Rij, Rik, cost, lambda, &
                      zeta, F1, dF1j, dF1k)
       call sf_F2_ij(Rij, Rc, eta, F2ij, dF2ij)
       call sf_F2_ij(Rik, Rc, eta, F2ik, dF2ik)

       G5    = F1*F2ij*F2ik
       ! factor of two for k<j in sf_fingerprint()
       G(iG) = G(iG) + 2.0d0*G5

       if (present(dGi)) then
          dG5(1:3) = -(dF1j(1:3)+dF1k(1:3))*F2ij*F2ik        &
                   - vecRij(1:3)*F1*dF2ij*F2ik &
                   - vecRik(1:3)*F1*F2ij*dF2ik
          dGi(1:3,iG) = dGi(1:3,iG) + 2.0d0*dG5(1:3)
       end if
       if (present(dGj)) then
          dG5(1:3) = dF1j(1:3)*F2ij*F2ik        &
                   + vecRij(1:3)*F1*dF2ij*F2ik
          dGj(1:3,iG) = dGj(1:3,iG) + 2.0d0*dG5(1:3)
       end if
       if (present(dGk)) then
          dG5(1:3) = dF1k(1:3)*F2ij*F2ik        &
                   + vecRik(1:3)*F1*F2ij*dF2ik
          dGk(1:3,iG) = dGk(1:3,iG) + 2.0d0*dG5(1:3)
       end if

    end do

  end subroutine sf_G5_update


  !======================= auxiliary functions ========================!



  !--------------------------------------------------------------------!
  !                      cosine cut-off function                       !
  !                        (eq. 4 in Ref. [1])                         !
  !--------------------------------------------------------------------!

  subroutine sf_cut(Rij, Rc, fc, dfc)

    implicit none

    double precision, intent(in)  :: Rij, Rc
    double precision, intent(out) :: fc, dfc

    if (Rij >= Rc) then
       fc  = 0.0d0
       dfc = 0.0d0
    else
       fc  =  0.5d0*(cos(PI*Rij/Rc) + 1.0d0)
       dfc = -0.5d0*PI/Rc*sin(PI*Rij/Rc)
    end if

  end subroutine sf_cut

end module symmfunc
ViewGit