Last commit for src/sfsetup.f90: 5874abaa643d4472a2aa9d1c5dbe454dadbd8d1f

Initial commit of the AENET code.

Bruno Mundim [2017-01-02 17:48:39]
Initial commit of the AENET code.
!-----------------------------------------------------------------------
!     sfsetup.f90 - representation of the local atomic environment
!-----------------------------------------------------------------------
!+ 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-10-20 Alexander Urban (AU), Nongnuch Artrith (NA)
!-----------------------------------------------------------------------

module sfsetup

  use aeio,     only: aeio_readline, &
                      TYPELEN

  use io,       only: io_lower,      &
                      io_readval,    &
                      io_adjustl,    &
                      io_unit

  use symmfunc, only: sf_init,       &
                      sf_final,      &
                      sf_add_rad,    &
                      sf_add_ang,    &
                      sf_fingerprint

  implicit none
  private
  save

  public  :: read_Setup_parameters, &
             load_Setup,            &
             load_Setup_ASCII,      &
             save_Setup,            &
             save_Setup_ASCII,      &
             skip_Setup,            &
             new_Setup,             &
             del_Setup,             &
             stp_init,              &
             stp_final,             &
             stp_get_range,         &
             stp_eval,              &
             stp_normalize,         &
             stp_nsf_max,           &
             stp_print_info,        &
             stp_assert_init,       &
             stp_assert_moduleinit

  private :: read_symmfunc_Behler2011,   &
             setup_symmfunc_Behler2011,  &
             print_info_Behler2011

  !--------------------------------------------------------------------!
  !                    structural fingerprint setup                    !
  !--------------------------------------------------------------------!

  type, public :: Setup

     !-----------------------------------------------------------------!
     ! init          .true., if the setup has been initialized         !
     ! neval         number of evaluations                             !
     ! description   an optional description from the setup file       !
     ! atomtype      species of the central atom                       !
     ! nenv          number of different surrounding species           !
     ! envtypes      species of the surrounding atoms                  !
     !                                                                 !
     ! The global atom type index is determined by the order of type   !
     ! names in the input file.  The *local* type index is given by    !
     ! the order the basis functions were first set up.  For the       !
     ! evaluation of the basis functions, the types from the input     !
     ! structure (global index) need to be assigned to local type IDs. !
     !                                                                 !
     ! gtype(i)      global atom type ID for local type i              !
     ! ltype(i)      local atom type ID for global type i              !
     !                                                                 !
     ! Rc_min/max    minimal interaction radius and max. cutoff        !
     ! sftype        basis function type (e.g. Behler2011)             !
     ! nsf           number of structural fingerprint basis functions  !
     ! sf(i)         function kind of the particular basis type        !
     ! nsfparam      the max. number of parameters of a basis function !
     ! sfparam(i,j)  i-th parameter of the j-th basis function         !
     !               i <= nsfparam                                     !
     ! sfenv(i,j)    i-th environment species for j-th basis function  !
     !                                                                 !
     ! sfval_min(i)  lowest so far encountered value of the i-th SF    !
     ! sfval_max(i)  largest so far encountered value of the i-th SF   !
     ! sfval_avg(i)  current average value of the i-th symm. function  !
     ! sfval_cov(i)  current covariance of the i-th symm. function     !
     ! --> min/max/avg/cov are updated whenever UNSCALED evaluation    !
     !     is requested.  This is usually during the screening of the  !
     !     training set.  During the training and prediciton a scaled  !
     !     value of the SFs is useful, that lies within [-1:1].        !
     !                                                                 !
     ! The scaling is implemented as                                   !
     !                                                                 !
     !  f(i) = max(sval_avg(i)-sfval_min(i),sfval_max(i)-sval_avg(i))  !
     !  s(i) = sfval_min(i)                                            !
     !  sfval(i) = (sfval(i)-s(i))/f(i)                                !
     !                                                                 !
     !-----------------------------------------------------------------!

     logical                                             :: init
     integer                                             :: neval

     character(len=1024)                                 :: description

     character(len=TYPELEN)                              :: atomtype
     integer                                             :: nenv
     character(len=TYPELEN), dimension(:),   allocatable :: envtypes

     integer                                             :: ntypes_global
     integer,                dimension(:),   allocatable :: gtype
     integer,                dimension(:),   allocatable :: ltype

     double precision                                    :: Rc_min
     double precision                                    :: Rc_max

     character(len=100)                                  :: sftype
     integer                                             :: nsf
     integer,                dimension(:),   allocatable :: sf
     integer                                             :: nsfparam
     double precision,       dimension(:,:), allocatable :: sfparam
     integer,                dimension(:,:), allocatable :: sfenv

     double precision,       dimension(:),   allocatable :: sfval_min
     double precision,       dimension(:),   allocatable :: sfval_max
     double precision,       dimension(:),   allocatable :: sfval_avg
     double precision,       dimension(:),   allocatable :: sfval_cov

  end type Setup

  !--------------- memory for basis function operations ---------------!
  ! sfval(i)         value of the i-th basis function                  !
  ! sfderiv_i(i,j)   i-th component of the derivative of the j-th SF   !
  !                  with respect to the central atom                  !
  !                  sfderiv_i(3,nsf_max)                              !
  ! sfderiv_j(i,j,k) i-th component of the derivative of the j-th SF   !
  !                  with respect to the coordinates of atom k         !
  !                  sfderiv_j(3,nsf_max,nnb_max)                      !
  !--------------------------------------------------------------------!

  double precision, dimension(:),     allocatable, public :: sfval
  double precision, dimension(:,:),   allocatable, public :: sfderiv_i
  double precision, dimension(:,:,:), allocatable, public :: sfderiv_j
  integer,                                         public :: nsf_max
  integer,                                         public :: nnb_max

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

  logical, private :: isInit = .false.

  !---------------------------- constants -----------------------------!
  ! NSFPARAM    maximum  number of SF parameters                       !
  ! NENV_MAX    maximum number of types involved in single function    !
  !             (e.g., 1 = distance, 2 = angle, 3 = dihedral)          !
  !--------------------------------------------------------------------!

  integer, parameter :: NSFPARAM = 4
  integer, parameter :: NENV_MAX = 2

contains

  function read_Setup_parameters(inFile, global_types) result(stp)

    implicit none

    character(len=*),               intent(in) :: inFile
    character(len=*), dimension(:), intent(in) :: global_types
    type(Setup)                                :: stp

    integer,           parameter :: nc = 1024
    character(len=nc)            :: line
    integer                      :: stat, iline
    character(len=20)            :: keyword
    integer                      :: isf

    integer                      :: ntypes_global
    integer                      :: u_stp, i

    stp%Rc_min = 0.0d0
    stp%Rc_max = 0.0d0
    stp%neval  = 0
    stp%nsf = 0

    ntypes_global = size(global_types(:))

    u_stp = io_unit()
    open(u_stp, file=trim(inFile), status='old', action='read')
    iline = 0
    do
       call aeio_readline(u_stp, iline, line, stat)
       if (stat /= 0) exit

       read(line,*) keyword
       select case(io_lower(keyword))
       case('atom')
          read(line(5:nc), *) stp%atomtype
       case('env')
          read(line(4:nc), *) stp%nenv
          allocate(stp%envtypes(stp%nenv))
          do i = 1, stp%nenv
             call aeio_readline(u_stp, iline, line, stat)
             read(line,*) stp%envtypes(i)
          end do
       case('descr')
          stp%description = ' '
          read(u_stp, '(A)') line
          iline = iline + 1
          do while(index(io_lower(line),'end descr') <= 0)
             if (len_trim(stp%description) < len(stp%description)) then
                stp%description = trim(stp%description) // trim(line) // "$$"
             end if
             read(u_stp, '(A)') line
             iline = iline + 1
          end do
       case('rmin')
          read(line(5:nc), *) stp%Rc_min
       case('symmfunc', 'functions')
          if (stp%nsf > 0) then
             write(0,*) "Error: Multiple basis definitions found in " // &
                        "structural fingerprint setup."
             stop
          end if
          call io_readval(line, 'type', stp%sftype)
          read(u_stp, *) stp%nsf
          stp%Rc_max   =    0.0d0
          stp%nsfparam = NSFPARAM
          allocate(stp%sf(stp%nsf), stp%sfparam(NSFPARAM,stp%nsf), &
                   stp%sfval_min(stp%nsf), stp%sfval_max(stp%nsf), &
                   stp%sfval_avg(stp%nsf), stp%sfval_cov(stp%nsf), &
                   stp%sfenv(NENV_MAX,stp%nsf))
          stp%sf(:)        = 0
          stp%sfparam(:,:) = 0.0d0
          stp%sfenv(:,:)   = 0
          stp%sfval_min(:) = 0.0d0
          stp%sfval_max(:) = 0.0d0
          stp%sfval_avg(:) = 0.0d0
          stp%sfval_cov(:) = 0.0d0
          do isf = 1, stp%nsf
             select case(trim(stp%sftype))
             case('Behler2011')
                call read_symmfunc_Behler2011(u_stp, isf, stp, iline)
             case default
                write(0,*) "Error: Unknown basis function type: ", &
                     trim(stp%sftype)
                deallocate(stp%sf, stp%sfparam)
                stop
             end select
          end do
       case('basis')
          if (stp%nsf > 0) then
             write(0,*) "Error: Multiple basis definitions found in " // &
                        "structural fingerprint setup."
             stop
          end if
          call io_readval(line, 'type', stp%sftype)
          select case(io_lower(stp%sftype))
          case default
             write(0,*) "Error: Unknown basis type: ", trim(stp%sftype)
             deallocate(stp%sf, stp%sfparam)
             stop
          end select
       case default
          write(0,*) "Error: Unknown keyword : ", trim(keyword)
          write(0,*) "       file            : ", trim(inFile)
          write(0,*) "       line            : ", trim(io_adjustl(iline))
       end select

    end do
    close(u_stp)

    if (stp%Rc_min == 0.0d0) then
       write(0,*) "Warning: RMIN not set in : ", trim(inFile)
       write(0,*) "         --> setting Rc_min = 1.0"
       stp%Rc_min = 1.0d0
    end if

    if (stp%Rc_max == 0.0d0) then
       write(0,*) "Error: Cutoff undetermined not set in : ", trim(inFile)
       stop
    end if

    ! connect local atom type IDs with global ones
    allocate(stp%gtype(stp%nenv), stp%ltype(ntypes_global))
    call stp_set_global_types(stp, ntypes_global, global_types)

    stp%init = .true.

  end function read_Setup_parameters

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

  function new_Setup(nsf, nenv, ntypes_global) result(stp)

    implicit none

    integer, intent(in) :: nsf, nenv, ntypes_global

    type(Setup) :: stp

    allocate(stp%envtypes(nenv),        &
             stp%sf(nsf),               &
             stp%sfparam(NSFPARAM,nsf), &
             stp%sfenv(NENV_MAX,nsf),   &
             stp%sfval_min(nsf),        &
             stp%sfval_max(nsf),        &
             stp%sfval_avg(nsf),        &
             stp%sfval_cov(nsf),        &
             stp%gtype(nenv),          &
             stp%ltype(ntypes_global))

    stp%description = ' '
    stp%atomtype    = ' '
    stp%nenv        = nenv
    stp%envtypes    = ' '
    stp%Rc_min      = -1.0d0
    stp%Rc_max      = -1.0d0
    stp%sftype      = ' '

    stp%ntypes_global = ntypes_global
    stp%ltype         = 0
    stp%gtype         = 0

    stp%nsf         = nsf
    stp%nsfparam    = NSFPARAM

    stp%sf(:)        = 0
    stp%sfparam(:,:) = 0.0d0
    stp%sfenv(:,:)   = 0
    stp%sfval_min(:) = 0.0d0
    stp%sfval_max(:) = 0.0d0
    stp%sfval_avg(:) = 0.0d0
    stp%sfval_cov(:) = 0.0d0

    stp%neval        = 0
    stp%init         = .true.

  end function new_Setup

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

  subroutine del_Setup(stp)

    implicit none

    type(Setup), intent(inout) :: stp

    if (stp%init) then
       deallocate(stp%sf, stp%sfparam, stp%sfval_min, stp%sfval_max, &
                  stp%sfval_avg, stp%sfval_cov, stp%envtypes, stp%sfenv, &
                  stp%gtype, stp%ltype)
       stp%init = .false.
    end if

  end subroutine del_Setup

  !--------------------------------------------------------------------!
  !           print out information about a specific set-up            !
  !--------------------------------------------------------------------!

  subroutine stp_print_info(stp)

    implicit none

    type(Setup), intent(in) :: stp

    integer :: i

    call stp_assert_init(stp)

    write(*,*) 'Structural fingerprint (SF) set-up for ', &
         trim(adjustl(stp%atomtype))
    write(*,*)

    call stp_print_descr(stp%description)

    if (stp%nenv>0) then
       write(*,'(1x,"environment types: ")', advance='no')
       do i = 1, stp%nenv
          write(*,'(A2,1x)', advance='no') stp%envtypes(i)
       end do
    end if
    write(*,*)
    write(*,*) 'minimal distance : ', trim(io_adjustl(stp%Rc_min,2)), ' Angstrom'
    write(*,*) 'maximal cut-off  : ', trim(io_adjustl(stp%Rc_max,2)), ' Angstrom'
    write(*,*) 'size of basis    : ', trim(io_adjustl(stp%nsf))
    write(*,*) 'evaluations      : ', trim(io_adjustl(stp%neval))
    write(*,*)

    select case(trim(io_lower(stp%sftype)))
    case('behler2011')
       call print_info_Behler2011(stp)
    end select


  end subroutine stp_print_info

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

  subroutine stp_print_descr(descr)

    implicit none

    character(len=*), intent(in) :: descr

    integer :: i1, i2, l

    l = len_trim(descr)

    i1 = 1
    i2 = index(descr,'$$')
    do while(i2 >= i1)
       write(*,*) descr(i1:i2-1)
       i1 = i2 + 2
       i2 = i1 + index(descr(i1:l),'$$') - 1
    end do
    if (len_trim(descr(i1:l)) > 0) then
       write(*,*) trim(descr(i1:l))
    end if
    write(*,*)

  end subroutine stp_print_descr

  !--------------------------------------------------------------------!
  !             save setup to file / load setup from file              !
  !--------------------------------------------------------------------!

  subroutine save_Setup(stp, file, unit)

    implicit none

    type(Setup),                intent(in) :: stp
    character(len=*), optional, intent(in) :: file
    integer,          optional, intent(in) :: unit

    integer :: u

    call stp_assert_init(stp)

    if (present(unit)) then
       u = unit
    else if (present(file)) then
       u = io_unit()
       open(u, file=trim(file), status='replace', action='write', &
            form='unformatted')
    else
       write(0,*) "Error: neither unit number nor file name given"
       write(0,*) "       in `save_Setup'."
       return
    end if

    write(u) stp%description
    write(u) stp%atomtype
    write(u) stp%nenv
    write(u) stp%envtypes(:)
    write(u) stp%Rc_min
    write(u) stp%Rc_max
    write(u) stp%sftype
    write(u) stp%nsf
    write(u) stp%nsfparam
    write(u) stp%sf(:)
    write(u) stp%sfparam(:,:)
    write(u) stp%sfenv(:,:)
    write(u) stp%neval
    write(u) stp%sfval_min(:)
    write(u) stp%sfval_max(:)
    write(u) stp%sfval_avg(:)
    write(u) stp%sfval_cov(:)

    if (.not. present(unit)) then
       close(u)
    end if

  end subroutine save_Setup

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

  subroutine save_Setup_ASCII(stp, file, unit)

    implicit none

    type(Setup),                intent(in) :: stp
    character(len=*), optional, intent(in) :: file
    integer,          optional, intent(in) :: unit

    integer :: i, j
    integer :: u

    character(len=*), parameter :: dfrmt = '(4(1x,ES24.17))'
    character(len=*), parameter :: ifrmt = '(4(1x,I17))'

    call stp_assert_init(stp)

    if (present(unit)) then
       u = unit
    else if (present(file)) then
       u = io_unit()
       open(u, file=trim(file), status='replace', action='write')
    else
       write(0,*) "Error: neither unit number nor file name given"
       write(0,*) "       in `save_Setup'."
       return
    end if

    write(u,'(A)') trim(stp%description)
    write(u,'(A)') stp%atomtype
    write(u,*) stp%nenv
    write(u,'(A)') (stp%envtypes(i), i=1,stp%nenv)
    write(u,*) stp%Rc_min
    write(u,*) stp%Rc_max
    write(u,'(A)') stp%sftype
    write(u,*) stp%nsf
    write(u,*) stp%nsfparam
    write(u,ifrmt) (stp%sf(i), i=1,stp%nsf)
    write(u,dfrmt) ((stp%sfparam(i,j), i=1,stp%nsfparam), j=1,stp%nsf)
    write(u,ifrmt) ((stp%sfenv(i,j), i=1,stp%nenv), j=1,stp%nsf)
    write(u,*) stp%neval
    write(u,dfrmt) (stp%sfval_min(i), i=1,stp%nsf)
    write(u,dfrmt) (stp%sfval_max(i), i=1,stp%nsf)
    write(u,dfrmt) (stp%sfval_avg(i), i=1,stp%nsf)
    write(u,dfrmt) (stp%sfval_cov(i), i=1,stp%nsf)

    if (.not. present(unit)) close(u)

  end subroutine save_Setup_ASCII

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

  function load_Setup(global_types, file, unit) result(stp)

    implicit none

    character(len=*), dimension(:), intent(in) :: global_types
    character(len=*), optional,     intent(in) :: file
    integer,          optional,     intent(in) :: unit
    type(Setup)                                :: stp

    integer :: u

    if (present(unit)) then
       u = unit
    else if (present(file)) then
       u = io_unit()
       open(u, file=trim(file), status='old', action='read', &
            form='unformatted')
    else
       write(0,*) "Error: neither unit number nor file name given"
       write(0,*) "       in `load_Setup'."
       return
    end if

    stp%ntypes_global = size(global_types(:))

    read(u) stp%description
    read(u) stp%atomtype
    read(u) stp%nenv
    allocate(stp%envtypes(stp%nenv))
    read(u) stp%envtypes
    read(u) stp%Rc_min
    read(u) stp%Rc_max
    read(u) stp%sftype
    read(u) stp%nsf
    read(u) stp%nsfparam
    allocate(stp%sf(stp%nsf),                    &
             stp%sfparam(stp%nsfparam, stp%nsf), &
             stp%sfenv(NENV_MAX,stp%nsf),        &
             stp%sfval_min(stp%nsf),             &
             stp%sfval_max(stp%nsf),             &
             stp%sfval_avg(stp%nsf),             &
             stp%sfval_cov(stp%nsf)              )
    read(u) stp%sf(:)
    read(u) stp%sfparam(:,:)
    read(u) stp%sfenv(:,:)
    read(u) stp%neval
    read(u) stp%sfval_min(:)
    read(u) stp%sfval_max(:)
    read(u) stp%sfval_avg(:)
    read(u) stp%sfval_cov(:)

    if (.not. present(unit)) then
       close(u)
    end if

    ! connect local atom type IDs with global ones
    allocate(stp%gtype(stp%nenv), stp%ltype(stp%ntypes_global))
    call stp_set_global_types(stp, stp%ntypes_global, global_types)

    stp%init = .true.

  end function load_Setup

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

  function load_Setup_ASCII(global_types, file, unit) result(stp)

    implicit none

    character(len=*), dimension(:), intent(in) :: global_types
    character(len=*), optional,     intent(in) :: file
    integer,          optional,     intent(in) :: unit
    type(Setup)                                :: stp

    integer :: i, j
    integer :: u

    character(len=*), parameter :: dfrmt = '(4(1x,ES24.17))'
    character(len=*), parameter :: ifrmt = '(4(1x,I17))'

    if (present(unit)) then
       u = unit
    else if (present(file)) then
       u = io_unit()
       open(u, file=trim(file), status='old', action='read')
    else
       write(0,*) "Error: neither unit number nor file name given"
       write(0,*) "       in `load_Setup'."
       return
    end if

    stp%ntypes_global = size(global_types(:))

    read(u,'(A)') stp%description
    read(u,'(A)') stp%atomtype
    read(u,*) stp%nenv
    allocate(stp%envtypes(stp%nenv))
    read(u,'(A)') (stp%envtypes(i), i=1,stp%nenv)
    read(u,*) stp%Rc_min
    read(u,*) stp%Rc_max
    read(u,'(A)') stp%sftype
    read(u,*) stp%nsf
    read(u,*) stp%nsfparam
    allocate(stp%sf(stp%nsf),                    &
             stp%sfparam(stp%nsfparam, stp%nsf), &
             stp%sfenv(NENV_MAX,stp%nsf),        &
             stp%sfval_min(stp%nsf),             &
             stp%sfval_max(stp%nsf),             &
             stp%sfval_avg(stp%nsf),             &
             stp%sfval_cov(stp%nsf)              )
    read(u,ifrmt) (stp%sf(i), i=1,stp%nsf)
    read(u,dfrmt) ((stp%sfparam(i,j), i=1,stp%nsfparam), j=1,stp%nsf)
    read(u,ifrmt) ((stp%sfenv(i,j), i=1,stp%nenv), j=1,stp%nsf)
    read(u,*) stp%neval
    read(u,dfrmt) (stp%sfval_min(i), i=1,stp%nsf)
    read(u,dfrmt) (stp%sfval_max(i), i=1,stp%nsf)
    read(u,dfrmt) (stp%sfval_avg(i), i=1,stp%nsf)
    read(u,dfrmt) (stp%sfval_cov(i), i=1,stp%nsf)

    if (.not. present(unit)) then
       close(u)
    end if

    ! connect local atom type IDs with global ones
    allocate(stp%gtype(stp%nenv), stp%ltype(stp%ntypes_global))
    call stp_set_global_types(stp, stp%ntypes_global, global_types)

    stp%init = .true.

  end function load_Setup_ASCII

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

  subroutine skip_Setup(u)

    implicit none

    integer, intent(in) :: u

    read(u) ! stp%description
    read(u) ! stp%atomtype
    read(u) ! stp%nenv
    read(u) ! stp%envtypes
    read(u) ! stp%Rc_min
    read(u) ! stp%Rc_max
    read(u) ! stp%sftype
    read(u) ! stp%nsf
    read(u) ! stp%nsfparam
    read(u) ! stp%sf(:)
    read(u) ! stp%sfparam(:,:)
    read(u) ! stp%sfenv(:,:)
    read(u) ! stp%neval
    read(u) ! stp%sfval_min(:)
    read(u) ! stp%sfval_max(:)
    read(u) ! stp%sfval_avg(:)
    read(u) ! stp%sfval_cov(:)

  end subroutine skip_Setup

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

  subroutine stp_set_global_types(stp, ntypes_global, global_types)

    implicit none

    type(Setup),                                intent(inout) :: stp
    integer,                                    intent(in)    :: ntypes_global
    character(len=*), dimension(ntypes_global), intent(in)    :: global_types

    integer :: i, j

    ! atom type indices that have no corresponding entry are set to 0

    do i = 1, ntypes_global
       stp%ltype(i) = 0
       env : do j = 1, stp%nenv
          if (trim(global_types(i)) == trim(stp%envtypes(j))) then
             stp%ltype(i) = j
             exit env
          end if
       end do env
    end do

    ! reverse direction, because we do not know, if the sets of types
    ! are identical
    do i = 1, stp%nenv
       stp%gtype(i) = 0
       global : do j = 1, ntypes_global
          if (trim(global_types(j)) == trim(stp%envtypes(i))) then
             stp%gtype(i) = j
             exit global
          end if
       end do global
    end do

  end subroutine stp_set_global_types



  !====================================================================!
  !                                                                    !
  !       structural fingerprint basis function set-up module          !
  !                                                                    !
  !====================================================================!



  !--------------------------------------------------------------------!
  !                    initialization/finalization                     !
  !--------------------------------------------------------------------!

  subroutine stp_init(ntypes, stp, N_nb_max)

    implicit none

    integer,                        intent(in)  :: ntypes
    type(Setup), dimension(ntypes), intent(in)  :: stp
    integer,                        intent(in)  :: N_nb_max

    integer            :: itype
    character(len=100) :: sftype
    integer            :: nG_max

    if (isInit) then
       write(0,*) "Error: module already initialized in `stp_init'."
       stop
    end if

    sftype = trim(stp(1)%sftype)
    do itype = 1, ntypes
       if (trim(stp(itype)%sftype) /= trim(sftype)) then
          write(0,*) "Error: Mixing of basis functions of different " &
                         // "types not yet implemented."
          write(0,*) trim(sftype), trim(stp(itype)%sftype)
          stop
       end if
    end do

    select case(trim(io_lower(sftype)))
    case('behler2011')
       nG_max = 0
       do itype = 1, ntypes
          nG_max = max(nG_max, stp(itype)%nsf)
       end do
       call sf_init(ntypes, nG_max)
       do itype = 1, ntypes
          call setup_symmfunc_Behler2011(itype, stp(itype))
       end do
    case default
       write(0,*) "Error: Unknown basis function type : ", trim(sftype)
       stop
    end select

    ! store max number of SFs in module
    nsf_max = stp_nsf_max(stp)

    ! max number of atoms in interaction range
    nnb_max = N_nb_max

    ! allocate workspace for basis function evaluation:
    allocate(sfval(nsf_max), sfderiv_i(3,nsf_max), sfderiv_j(3,nsf_max,nnb_max))
    sfval(:) = 0.0d0
    sfderiv_i(:,:) = 0.0d0
    sfderiv_j(:,:,:) = 0.0d0

    isInit = .true.

  end subroutine stp_init

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

  subroutine stp_final(nTypes, stp)

    implicit none

    integer,                        intent(in) :: nTypes
    type(Setup), dimension(nTypes), intent(in) :: stp

    integer :: itype

    if (.not. isInit) return

    do itype = 1, nTypes
       select case(trim(io_lower(stp(itype)%sftype)))
       case('behler2011')
          ! multiple calls to sf_final() do not cause harm
          call sf_final()
       end select
    end do

    deallocate(sfval, sfderiv_i, sfderiv_j)

    isInit = .false.

  end subroutine stp_final

  !--------------------------------------------------------------------!
  !              determine interaction range from setups               !
  !--------------------------------------------------------------------!

  subroutine stp_get_range(nTypes, stp, Rc_min, Rc_max)

    implicit none

    integer,                        intent(in)  :: nTypes
    type(Setup), dimension(nTypes), intent(in)  :: stp
    double precision,               intent(out) :: Rc_min
    double precision,               intent(out) :: Rc_max

    integer :: itype

    call stp_assert_init(stp(1))

    Rc_min = stp(1)%Rc_min
    Rc_max = stp(1)%Rc_max

    do itype = 1, nTypes
       call stp_assert_init(stp(itype))
       Rc_min = min(Rc_min, stp(itype)%Rc_min)
       Rc_max = max(Rc_max, stp(itype)%Rc_max)
    end do

  end subroutine stp_get_range

  !--------------------------------------------------------------------!
  !                      basis function evaluation                     !
  !--------------------------------------------------------------------!

  subroutine stp_eval(itype0, coo0, n, coo1, type1, stp, deriv, scaled)

    implicit none

    integer,                                            intent(in)    :: itype0
    double precision, dimension(3),                     intent(in)    :: coo0
    integer,                                            intent(in)    :: n
    double precision, dimension(3,n),                   intent(in)    :: coo1
    integer,          dimension(n),                     intent(in)    :: type1
    type(Setup),                                        intent(inout) :: stp
    logical,                                  optional, intent(in)    :: deriv
    logical,                                  optional, intent(in)    :: scaled

    integer               :: type0_loc
    integer, dimension(n) :: type1_loc

    integer          :: i, nsf
    logical          :: do_deriv, do_scale

    call stp_assert_moduleinit()
    call stp_assert_init(stp)

    if (present(deriv)) then
       do_deriv = deriv
    else
       do_deriv = .false.
    end if

    if (present(scaled)) then
       do_scale = scaled
    else
       do_scale = .false.
    end if

    ! convert global atom type IDs to setup local IDs
    type0_loc = stp%ltype(itype0)
    do i = 1, n
       type1_loc(i) = stp%ltype(type1(i))
    end do

    select case(trim(io_lower(stp%sftype)))
    case('behler2011')
       nsf = stp%nsf
       if (do_deriv) then
          call sf_fingerprint(type0_loc, coo0, n, coo1, type1_loc, nsf, &
                              sfval(1:nsf), sfderiv_i(1:3,1:nsf), &
                              sfderiv_j(1:3,1:nsf,1:n))
       else
          call sf_fingerprint(type0_loc, coo0, n, coo1, type1_loc, nsf, &
                              sfval(1:nsf))
       end if
    end select

    if (do_scale) then
       call stp_normalize(stp, deriv=do_deriv)
    else
       if (stp%neval == 0) then
          stp%sfval_min(1:nsf) = sfval(1:nsf)
          stp%sfval_max(1:nsf) = sfval(1:nsf)
          stp%sfval_avg(1:nsf) = sfval(1:nsf)
          stp%sfval_cov(1:nsf) = sfval(1:nsf)*sfval(1:nsf)
       else
          do i = 1, stp%nsf
             stp%sfval_min(i) = min(stp%sfval_min(i), sfval(i))
             stp%sfval_max(i) = max(stp%sfval_max(i), sfval(i))
             stp%sfval_avg(i) = (dble(stp%neval)*stp%sfval_avg(i) &
                                + sfval(i))/(dble(stp%neval+1))
             stp%sfval_cov(i) = (dble(stp%neval)*stp%sfval_cov(i) &
                                + sfval(i)*sfval(i))/(dble(stp%neval+1))
          end do
       end if
    end if

    stp%neval = stp%neval + 1

  end subroutine stp_eval

  !--------------------------------------------------------------------!
  !          normalization of basis function values to [-1,1]          !
  !--------------------------------------------------------------------!

  subroutine stp_normalize(stp, deriv)

    implicit none

    type(Setup),       intent(inout) :: stp
    logical, optional, intent(in)    :: deriv

    double precision :: scale, shift, s
    integer          :: isf
    logical          :: do_deriv

    if (present(deriv)) then
       do_deriv = deriv
    else
       do_deriv = .false.
    end if

    do isf = 1, stp%nsf
       shift = stp%sfval_avg(isf)
       ! scale covariance to 1
       ! s = sqrt(stp%sfval_cov(isf) + shift*shift - 2.0d0*shift*stp%sfval_avg(isf))
       s = sqrt(stp%sfval_cov(isf) - shift*shift)
       if (s <= 1.0d-10) then
          write(0,*) "Warning: Invalid scaling encountered in ", &
                               "'stp_normalize()'."
          write(0,*) "         This means at least one fingerprint ", &
                               "function for ", trim(adjustl(stp%atomtype)), &
                               " is always equal to zero!"
          write(0,*) "         Maybe an atomic species is not present ", &
                               "in the reference set?"
          write(0,*) "         type       = ", trim(adjustl(stp%sftype)), &
                               " ", trim(io_adjustl(stp%sf(isf)))
          write(0,*) "         covariance = ", stp%sfval_cov(isf)
          write(0,*) "         average    = ", stp%sfval_avg(isf)
          write(0,*) "         min, max   = ", stp%sfval_min(isf), &
                                               stp%sfval_max(isf)
          scale = 0.0d0
       else
          scale = 1.0d0/s
       end if
       sfval(isf) = scale*(sfval(isf)-shift)
       if (do_deriv) then
          sfderiv_i(1:3,isf)   = scale*sfderiv_i(1:3,isf)
          sfderiv_j(1:3,isf,:) = scale*sfderiv_j(1:3,isf,:)
       end if
    end do

  end subroutine stp_normalize

  !--------------------------------------------------------------------!
  !            maximum number of basis functions per setup             !
  !--------------------------------------------------------------------!

  function stp_nsf_max(stp) result(N_sf_max)

    implicit none

    type(Setup), dimension(:), optional, intent(in) :: stp
    integer                                         :: N_sf_max

    integer :: itype, nTypes

    if (present(stp)) then
       nTypes = size(stp(:))
       N_sf_max = 0
       do itype = 1, nTypes
          call stp_assert_init(stp(itype))
          N_sf_max = max(N_sf_max, stp(itype)%nsf)
       end do
    else if (isInit) then
       N_sf_max = nsf_max
    else
       write(0,*) "Error: module not initialized in `stp_nsf_max()'."
       stop
    end if

  end function stp_nsf_max

  !--------------------------------------------------------------------!
  !                        auxiliary procedures                        !
  !--------------------------------------------------------------------!

  subroutine stp_assert_init(stp)
    implicit none
    type(Setup), intent(in) :: stp
    if (.not. stp%init) then
       write(*,*) 'Error: The basis function set-up is not initialized.'
       write(*,*)
       stop
    end if
  end subroutine stp_assert_init

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

  subroutine stp_assert_moduleinit()
    implicit none
    if (.not. isInit) then
       write(*,*) 'Error: Structural fingerprint setup module NOT initialized.'
       write(*,*)
       stop
    end if
  end subroutine stp_assert_moduleinit





  !====================================================================!
  !                                                                    !
  !            procedures for different basis function types           !
  !                                                                    !
  !====================================================================!


  !--------------------------------------------------------------------!
  ! J. Behler, J. Chem. Phys. 134 (2011) 074106                        !
  !                                                                    !
  !      parameters                                                    !
  !                                                                    !
  ! G1:  Rc                                                            !
  ! G2:  Rc, Rs, eta                                                   !
  ! G3:  Rc, kappa                                                     !
  ! G4:  Rc, lambda, zeta, eta                                         !
  ! G5:  Rc, lambda, zeta, eta                                         !
  !--------------------------------------------------------------------!

  subroutine read_symmfunc_Behler2011(u_stp, isf, stp, iline)

    implicit none

    integer,     intent(in)    :: u_stp
    integer,     intent(in)    :: isf
    type(Setup), intent(inout) :: stp
    integer,     intent(inout) :: iline

    character(len=1024)        :: line, typename

    read(u_stp, '(A)') line
    iline = iline + 1

    call io_readval(line, 'G', stp%sf(isf))
    select case(stp%sf(isf))
    case(1)
       call io_readval(line, 'type2',  typename)
       stp%sfenv(1,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'Rc',     stp%sfparam(1,isf))
    case(2)
       call io_readval(line, 'type2',  typename)
       stp%sfenv(1,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'Rc',     stp%sfparam(1,isf))
       call io_readval(line, 'Rs',     stp%sfparam(2,isf))
       call io_readval(line, 'eta',    stp%sfparam(3,isf))
    case(3)
       call io_readval(line, 'type2',  typename)
       stp%sfenv(1,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'Rc',     stp%sfparam(1,isf))
       call io_readval(line, 'kappa',  stp%sfparam(2,isf))
    case(4)
       call io_readval(line, 'type2',  typename)
       stp%sfenv(1,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'type3',  typename)
       stp%sfenv(2,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'Rc',     stp%sfparam(1,isf))
       call io_readval(line, 'lambda', stp%sfparam(2,isf))
       call io_readval(line, 'zeta',   stp%sfparam(3,isf))
       call io_readval(line, 'eta',    stp%sfparam(4,isf))
    case(5)
       call io_readval(line, 'type2',  typename)
       stp%sfenv(1,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'type3',  typename)
       stp%sfenv(2,isf) = stp_typeindex(stp, typename)
       call io_readval(line, 'Rc',     stp%sfparam(1,isf))
       call io_readval(line, 'lambda', stp%sfparam(2,isf))
       call io_readval(line, 'zeta',   stp%sfparam(3,isf))
       call io_readval(line, 'eta',    stp%sfparam(4,isf))
    case default
       write(0,*) "Error: Symmetry function type not implemented : ", &
                  trim(io_adjustl(stp%sf(isf)))
       write(0,*) "in   : `read_symmfunc_Behler2011'"
       stop
    end select

    stp%Rc_max = max(stp%Rc_max, stp%sfparam(1,isf))

  end subroutine read_symmfunc_Behler2011

  !--------------------------------------------------------------------!
  ! set up symmetry functions of the Behler2011 type as specified by   !
  ! the provided set-up `stp'                                          !
  !--------------------------------------------------------------------!

  subroutine setup_symmfunc_Behler2011(itype, stp)

    implicit none

    integer,     intent(in) :: itype
    type(Setup), intent(in) :: stp

    integer          :: type1, type2, type3
    double precision :: Rc, Rs, eta, kappa, lambda, zeta
    integer          :: isf, funct

    type1 = itype

    SFs : do isf = 1, stp%nsf

       funct = stp%sf(isf)

       select case(funct)
       case(1)
          type2  = stp%sfenv(1,isf)
          Rc     = stp%sfparam(1,isf)
          call sf_add_rad(funct, type1, type2, Rc=Rc)
       case(2)
          type2  = stp%sfenv(1,isf)
          Rc     = stp%sfparam(1,isf)
          Rs     = stp%sfparam(2,isf)
          eta    = stp%sfparam(3,isf)
          call sf_add_rad(funct, type1, type2, Rc=Rc, Rs=Rs, eta=eta)
       case(3)
          type2  = stp%sfenv(1,isf)
          Rc     = stp%sfparam(1,isf)
          kappa  = stp%sfparam(2,isf)
          call sf_add_rad(funct, type1, type2, Rc=Rc, kappa=kappa)
       case(4)
          type2  = stp%sfenv(1,isf)
          type3  = stp%sfenv(2,isf)
          Rc     = stp%sfparam(1,isf)
          lambda = stp%sfparam(2,isf)
          zeta   = stp%sfparam(3,isf)
          eta    = stp%sfparam(4,isf)
          call sf_add_ang(funct, type1, type2, type3, Rc=Rc, lambda=lambda, &
                      zeta=zeta, eta=eta)
       case(5)
          type2  = stp%sfenv(1,isf)
          type3  = stp%sfenv(2,isf)
          Rc     = stp%sfparam(1,isf)
          lambda = stp%sfparam(2,isf)
          zeta   = stp%sfparam(3,isf)
          eta    = stp%sfparam(4,isf)
          call sf_add_ang(funct, type1, type2, type3, Rc=Rc, lambda=lambda, &
                      zeta=zeta, eta=eta)
       case default
          write(0,*) "Error: Symmetry function type not implemented : ", &
                     trim(io_adjustl(stp%sf(isf)))
          write(0,*) "in   : `setup_symmfunc_Behler2011'"
          stop
       end select

   end do SFs

  end subroutine setup_symmfunc_Behler2011

  !--------------------------------------------------------------------!
  !                        print info to stdout                        !
  !--------------------------------------------------------------------!

  subroutine print_info_Behler2011(stp)

    implicit none

    type(Setup), intent(in) :: stp

    integer            :: isf, iG
    double precision   :: Rc, Rs, eta, kappa, lambda, zeta
    character(len=128) :: frmt
    integer            :: type2, type3

    write(*,*) 'Basis function type Behler2011'
    write(*,*)
    write(*,*) '[see also: J. Behler, J. Chem. Phys. 134 (2011) 074106]'
    write(*,*)
    write(*,*)

    write(*,'(6x,"G",2x,"parameters")')
    write(*,*)

    do isf = 1, stp%nsf
       iG = stp%sf(isf)
       select case(iG)
       case(1)
          type2 = stp%sfenv(1,isf)
          Rc = stp%sfparam(1,isf)
          write(*,'(1x,I3,2x,I1,2x,"type2=",A2,"  Rc = ",F7.3)') &
               isf, iG, stp%envtypes(type2), Rc
       case(2)
          type2 = stp%sfenv(1,isf)
          Rc  = stp%sfparam(1,isf)
          Rs  = stp%sfparam(2,isf)
          eta = stp%sfparam(3,isf)
          write(*,'(1x,I3,2x,I1,2x,"type2=",A2,"  Rc = ",F7.3,"  Rs = ",F7.3,"  eta = ",F7.3)') &
               isf, iG, stp%envtypes(type2), Rc, Rs, eta
       case(3)
          type2 = stp%sfenv(1,isf)
          Rc    = stp%sfparam(1,isf)
          kappa = stp%sfparam(2,isf)
          write(*,'(1x,I3,2x,I1,2x,"type2=",A2,"  Rc = ",F7.3,"  kappa = ",F7.3)') &
               isf, iG, stp%envtypes(type2), Rc, kappa
       case(4,5)
          type2  = stp%sfenv(1,isf)
          type3  = stp%sfenv(2,isf)
          Rc     = stp%sfparam(1,isf)
          lambda = stp%sfparam(2,isf)
          zeta   = stp%sfparam(3,isf)
          eta    = stp%sfparam(4,isf)
          frmt = '(1x,I3,2x,I1,2x,"type2=",A2,"  type3=",A2,' &
               // '"  Rc = ",F7.3,"  lambda = ",F7.3,' &
               // '"  zeta = ",F7.3,"  eta = ",F7.3)'
          write(*,frmt) isf, iG, stp%envtypes(type2), &
               stp%envtypes(type3), Rc, lambda, zeta, eta
       end select
    end do
    write(*,*)

  end subroutine print_info_Behler2011

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

  function stp_typeindex(stp, typename) result(itype)

    implicit none

    type(Setup),      intent(in) :: stp
    character(len=*), intent(in) :: typename
    integer                      :: itype

    integer :: i

    itype = 0
    do i = 1, stp%nenv
       if (trim(typename) == trim(stp%envtypes(i))) then
          itype = i
          exit
       end if
    end do

  end function stp_typeindex

end module sfsetup
ViewGit