Last commit for src/aenet.f90: 5874abaa643d4472a2aa9d1c5dbe454dadbd8d1f

Initial commit of the AENET code.

Bruno Mundim [2017-01-02 17:48:39]
Initial commit of the AENET code.
!-----------------------------------------------------------------------
! aenet.f90 -- public interface to the aenet routines (aenetLib)
!
! This module also provides the C bindings for libaenet.so/.a .
!-----------------------------------------------------------------------
!+ 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/>.
!-----------------------------------------------------------------------
! 2014-08-31 Nongnuch Artrith, Alexander Urban
!-----------------------------------------------------------------------
module aenet

  use, intrinsic :: iso_c_binding, only: c_double, c_int, c_char, c_bool, &
                                         c_ptr, c_f_pointer, c_null_char

  use aeio,        only: TYPELEN, PATHLEN

  use feedforward, only: ff_eval, ff_deriv

  use geometry,    only: geo_type_conv

  use io,          only: io_cstring_len,      &
                         io_cstring2f

  use lclist,      only: lcl_nmax_nbdist

  use potential,   only: NNPot,               &
                         load_NNPot,          &
                         del_NNPot,           &
                         pot_init,            &
                         pot_final,           &
                         pot_print_info,      &
                         pot_get_range

  use sfsetup,     only: stp_init,            &
                         stp_final,           &
                         stp_nsf_max,         &
                         stp_eval,            &
                         sfval, sfderiv_i, sfderiv_j

  use timing,      only: tng_timing, tng_timing2

  implicit none
  private
  save

  public :: aenet_init,                     &
            aenet_final,                    &
            aenet_all_loaded,               &
            aenet_atomic_energy,            &
            aenet_atomic_energy_and_forces, &
            aenet_convert_atom_types,       &
            aenet_free_atom_energy,         &
            aenet_load_potential,           &
            aenet_print_info

  !---------------------------- constants -----------------------------!

  ! return status
  integer(kind=c_int), bind(C, name='AENET_OK'),         public :: AENET_OK = 0_c_int
  integer(kind=c_int), bind(C, name='AENET_ERR_INIT'),   public :: AENET_ERR_INIT = 1_c_int
  integer(kind=c_int), bind(C, name='AENET_ERR_MALLOC'), public :: AENET_ERR_MALLOC = 2_c_int
  integer(kind=c_int), bind(C, name='AENET_ERR_IO'),     public :: AENET_ERR_IO = 3_c_int
  integer(kind=c_int), bind(C, name='AENET_ERR_TYPE'),   public :: AENET_ERR_TYPE = 4_c_int

  integer(kind=c_int), bind(C, name='AENET_TYPELEN'),    public :: AENET_TYPELEN = TYPELEN
  integer(kind=c_int), bind(C, name='AENET_PATHLEN'),    public :: AENET_PATHLEN = PATHLEN

  !---------------------------- variables -----------------------------!

  integer(kind=c_int), bind(C, name='aenet_nsf_max'), public :: aenet_nsf_max
  integer(kind=c_int), bind(C, name='aenet_nnb_max'), public :: aenet_nnb_max
  real(kind=c_double), bind(C, name='aenet_Rc_min'),  public :: aenet_Rc_min
  real(kind=c_double), bind(C, name='aenet_Rc_max'),  public :: aenet_Rc_max

  !----------------------------- private ------------------------------!

  logical, private :: aenet_is_init = .false.
  logical, private :: aenet_is_loaded = .false.

  integer,                                           private :: aenet_ntypes
  character(len=TYPELEN), dimension(:), allocatable, private :: aenet_atom_types
  type(NNPot),            dimension(:), allocatable, private :: aenet_pot
  double precision,       dimension(:), allocatable, private :: aenet_dE_dG

contains

  !--------------------------------------------------------------------!
  !                  Initialization and Finalization                   !
  !--------------------------------------------------------------------!

  subroutine aenet_init(atom_types, stat)

    implicit none

    character(len=*), dimension(:), intent(in)  :: atom_types
    integer,                        intent(out) :: stat

    integer :: ok

    stat = AENET_OK
    if (aenet_is_init) then
       stat = AENET_ERR_INIT
       return
    end if

    aenet_ntypes = size(atom_types)
    allocate(aenet_pot(aenet_ntypes),        &
             aenet_atom_types(aenet_ntypes), &
             stat=ok)
    if (ok /= 0) then
       aenet_ntypes = 0
       stat = AENET_ERR_MALLOC
       return
    end if
    aenet_atom_types = atom_types
    aenet_is_init = .true.

  end subroutine aenet_init

  subroutine aenet_init_C(ntypes, atom_types, stat) bind(C, name='aenet_init')

    implicit none

    integer(kind=c_int), value,             intent(in)  :: nTypes
    type(c_ptr), dimension(ntypes), target, intent(in)  :: atom_types
    integer(kind=c_int),                    intent(out) :: stat

    character,                dimension(:), pointer :: fstringp
    character(len=TYPELEN+1), dimension(ntypes)     :: fatom_types

    integer :: i, slen

    do i = 1, ntypes
       call c_f_pointer(atom_types(i), fstringp, [TYPELEN+1])
       fatom_types(i) = transfer(fstringp(1:TYPELEN+1), fatom_types(i))
       slen = index(fatom_types(i), c_null_char) - 1
       fatom_types(i)(slen+1:TYPELEN+1) = ' '
    end do

    call aenet_init(fatom_types, stat)

  end subroutine aenet_init_C

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

  subroutine aenet_final(stat) bind(C)

    implicit none

    integer(kind=c_int), intent(out) :: stat

    integer :: itype
    integer :: ok

    stat = AENET_OK
    if (aenet_is_init) then
       call stp_final(aenet_ntypes, aenet_pot(1:aenet_ntypes)%stp)
       do itype = 1, aenet_ntypes
          call del_NNPot(aenet_pot(itype))
       end do
       deallocate(aenet_pot, aenet_atom_types, stat=ok)
       if (ok /= 0) then
          stat = AENET_ERR_MALLOC
          return
       end if
       aenet_ntypes = 0
       aenet_is_init = .false.
       aenet_is_loaded = .false.
    end if

    if (allocated(aenet_dE_dG)) deallocate(aenet_dE_dG)

  end subroutine aenet_final

  !--------------------------------------------------------------------!
  !                               output                               !
  !--------------------------------------------------------------------!

  subroutine aenet_print_info() bind(C)

    implicit none

    integer :: ipot

    if (.not. aenet_is_init) then
       write(*,*) "Nothing to print. AenetLib is not initialized."
    else
       do ipot = 1, aenet_ntypes
          if (aenet_pot(ipot)%init) then
             call pot_print_info(aenet_pot(ipot))
          end if
       end do
    end if

  end subroutine aenet_print_info

  !--------------------------------------------------------------------!
  !                        Load ANN potentials                         !
  !--------------------------------------------------------------------!

  subroutine aenet_load_potential(type_id, filename, stat)

    implicit none

    integer,          intent(in)  :: type_id
    character(len=*), intent(in)  :: filename
    integer,          intent(out) :: stat

    integer :: ok
    logical :: fexists

    stat = AENET_OK
    if (.not. aenet_is_init) then
       stat = AENET_ERR_INIT
       return
    end if

    if ((type_id <= 0) .or. (type_id > aenet_ntypes)) then
       stat = AENET_ERR_TYPE
       return
    end if

    inquire(file=trim(filename), exist=fexists)
    if (.not. fexists) then
       stat = AENET_ERR_IO
       return
    end if

    aenet_pot(type_id) = load_NNPot(aenet_atom_types, filename)

    ! when all potentials are loaded, determine array sizes
    if (aenet_all_loaded()) then
       call pot_get_range(aenet_ntypes, aenet_pot, aenet_Rc_min, aenet_Rc_max)
       if (stat /= 0) return
       aenet_nnb_max = lcl_nmax_nbdist(aenet_Rc_min, aenet_Rc_max)
       call stp_init(aenet_ntypes, aenet_pot(1:aenet_ntypes)%stp, aenet_nnb_max)
       aenet_nsf_max = stp_nsf_max()
       allocate(aenet_dE_dG(aenet_nsf_max), stat=ok)
       if (ok /= 0) then
          stat = AENET_ERR_MALLOC
          return
       end if
       aenet_is_loaded = .true.
    end if

  end subroutine aenet_load_potential

  subroutine aenet_load_potential_C(type_id, filename, stat) &
       bind(C, name='aenet_load_potential')

    implicit none

    integer(kind=c_int), value,           intent(in)  :: type_id
    character(kind=c_char), dimension(*), intent(in)  :: filename
    integer(kind=c_int),                  intent(out) :: stat

    integer :: slen
    character(len=1024) :: ffilename

    slen = io_cstring_len(filename)
    if (slen > 1024) then
       stat = aenet_ERR_MALLOC
       return
    end if
    ffilename = io_cstring2f(filename, slen)

    call aenet_load_potential(type_id, trim(ffilename), stat)

  end subroutine aenet_load_potential_C

  function aenet_all_loaded() result(all_loaded) bind(C)

    implicit none

    logical(kind=c_bool) :: all_loaded
    integer :: ipot

    all_loaded = .true.
    do ipot = 1, aenet_ntypes
       if (.not. aenet_pot(ipot)%init) then
          all_loaded = .false.
          return
       end if
    end do

  end function aenet_all_loaded

  !--------------------------------------------------------------------!
  !                            information                             !
  !--------------------------------------------------------------------!

  function aenet_free_atom_energy(type_id) result(E_atom) bind(C)

    implicit none

    integer(kind=c_int), intent(in) :: type_id
    real(kind=c_double)             :: E_atom

    E_atom = aenet_pot(type_id)%E_atom

  end function aenet_free_atom_energy

  !--------------------------------------------------------------------!
  !                             Evaluation                             !
  !                                                                    !
  ! Attention: all routines require synchronized atom type IDs, i.e.,  !
  !            the IDs passed to the evaluation routines must be       !
  !            compatible with the ANN potential type IDs.             !
  !                                                                    !
  ! Notes:     * Coordinates are Cartesian.                            !
  !                                                                    !
  !--------------------------------------------------------------------!

  subroutine aenet_atomic_energy(coo_i, type_i, n_j, coo_j, type_j, &
                                 E_i, stat) bind(C)

    implicit none

    real(kind=c_double), dimension(3),     intent(in)  :: coo_i
    integer(kind=c_int), value,            intent(in)  :: type_i
    integer(kind=c_int), value,            intent(in)  :: n_j
    real(kind=c_double), dimension(3,n_j), intent(in)  :: coo_j
    integer(kind=c_int), dimension(n_j),   intent(in)  :: type_j
    real(kind=c_double),                   intent(out) :: E_i
    integer(kind=c_int),                   intent(out) :: stat

    double precision, dimension(1) :: E_i_arr
    integer :: nsf

    stat = AENET_OK
    if (.not. (aenet_is_init .and. aenet_is_loaded)) then
       stat = aenet_ERR_INIT
       return
    end if

    nsf = aenet_pot(type_i)%stp%nsf
    call stp_eval(type_i, coo_i, n_j, coo_j, type_j, &
                  aenet_pot(type_i)%stp, scaled=.true.)
    call ff_eval(aenet_pot(type_i)%net, nsf, sfval, 1, E_i_arr)
    E_i = aenet_pot(type_i)%E_scale*E_i_arr(1) + aenet_pot(type_i)%E_shift

    E_i = E_i + aenet_pot(type_i)%E_atom

  end subroutine aenet_atomic_energy

  subroutine aenet_atomic_energy_and_forces( &
       coo_i, type_i, index_i, n_j, coo_j, type_j, index_j, natoms, &
       E_i, F, stat) bind(C)

    implicit none

    real(kind=c_double), dimension(3),        intent(in)    :: coo_i
    integer(kind=c_int), value,               intent(in)    :: type_i
    integer(kind=c_int), value,               intent(in)    :: index_i
    integer(kind=c_int), value,               intent(in)    :: n_j
    real(kind=c_double), dimension(3,n_j),    intent(in)    :: coo_j
    integer(kind=c_int), dimension(n_j),      intent(in)    :: type_j
    integer(kind=c_int), dimension(n_j),      intent(in)    :: index_j
    integer(kind=c_int), value,               intent(in)    :: natoms
    real(kind=c_double),                      intent(out)   :: E_i
    real(kind=c_double), dimension(3,natoms), intent(inout) :: F
    integer(kind=c_int),                      intent(out)   :: stat

    double precision, dimension(1)              :: E_i_arr
    integer                                     :: nsf, j

    stat = AENET_OK
    if (.not. (aenet_is_init .and. aenet_is_loaded)) then
       stat = aenet_ERR_INIT
       return
    end if

    nsf = aenet_pot(type_i)%stp%nsf
    call stp_eval(type_i, coo_i, n_j, coo_j, type_j, &
                  aenet_pot(type_i)%stp, scaled=.true., deriv=.true.)

    call ff_eval(aenet_pot(type_i)%net, nsf, sfval, 1, E_i_arr)
    call ff_deriv(aenet_pot(type_i)%net, nsf, 1, aenet_dE_dG(1:nsf))

    E_i = aenet_pot(type_i)%E_scale*E_i_arr(1) + aenet_pot(type_i)%E_shift
    E_i = E_i + aenet_pot(type_i)%E_atom

    F(1:3, index_i) = F(1:3, index_i) - aenet_pot(type_i)%E_scale &
                    * matmul(sfderiv_i(1:3,1:nsf), aenet_dE_dG(1:nsf))

    do j = 1, n_j
       F(1:3, index_j(j)) = F(1:3, index_j(j)) - aenet_pot(type_i)%E_scale &
                          * matmul(sfderiv_j(1:3,1:nsf,j), aenet_dE_dG(1:nsf))
    end do

  end subroutine aenet_atomic_energy_and_forces

  !--------------------------------------------------------------------!
  !                       convert atom type IDs                        !
  !--------------------------------------------------------------------!

  subroutine aenet_convert_atom_types(&
       atom_types_in, type_id_in, type_id_out, stat)

    implicit none

    character(len=*), dimension(:), intent(in)  :: atom_types_in
    integer,          dimension(:), intent(in)  :: type_id_in
    integer,          dimension(:), intent(out) :: type_id_out
    integer,                        intent(out) :: stat

    integer :: iat,it,  nTypes_in, natoms_in

    stat = AENET_OK
    if (.not. aenet_is_init) then
       stat = aenet_ERR_INIT
       return
    end if

    ntypes_in = size(atom_types_in)
    natoms_in = size(type_id_in)

    do iat = 1, natoms_in
       it = geo_type_conv(type_id_in(iat), ntypes_in, atom_types_in, &
                          aenet_ntypes, aenet_atom_types)
       if (it == 0) then
          stat = aenet_ERR_TYPE
          return
       end if
       type_id_out(iat) = it
    end do

  end subroutine aenet_convert_atom_types

  subroutine aenet_convert_atom_types_C(ntypes_in, atom_types_in, &
                         natoms_in, type_id_in, type_id_out, stat &
                         ) bind(C, name="aenet_convert_atom_types")

    implicit none

    integer(kind=c_int), value,                        intent(in)  :: ntypes_in
    type(c_ptr),         dimension(ntypes_in), target, intent(in)  :: atom_types_in
    integer(kind=c_int), value,                        intent(in)  :: natoms_in
    integer(kind=c_int), dimension(natoms_in),         intent(in)  :: type_id_in
    integer(kind=c_int), dimension(natoms_in),         intent(out) :: type_id_out
    integer(kind=c_int),                               intent(out) :: stat

    character,                dimension(:), pointer :: fstringp
    character(len=TYPELEN+1), dimension(ntypes_in)  :: fatom_types

    integer :: i, slen

    do i = 1, ntypes_in
       call c_f_pointer(atom_types_in(i), fstringp, [TYPELEN+1])
       fatom_types(i) = transfer(fstringp(1:TYPELEN+1), fatom_types(i))
       slen = index(fatom_types(i), c_null_char) - 1
       fatom_types(i)(slen+1:TYPELEN+1) = ' '
    end do

    call aenet_convert_atom_types(&
         fatom_types, type_id_in, type_id_out, stat)

  end subroutine aenet_convert_atom_types_C

  !--------------------------------------------------------------------!
  !                         internal routines                          !
  !--------------------------------------------------------------------!

end module aenet
ViewGit