!****************************************************************************
!
! CDDL HEADER START
!
! The contents of this file are subject to the terms of the Common Development
! and Distribution License Version 1.0 (the "License").
!
! You can obtain a copy of the license at
! http://www.opensource.org/licenses/CDDL-1.0.  See the License for the
! specific language governing permissions and limitations under the License.
!
! When distributing Covered Code, include this CDDL HEADER in each file and
! include the License file in a prominent location with the name LICENSE.CDDL.
! If applicable, add the following below this CDDL HEADER, with the fields
! enclosed by brackets "[]" replaced with your own identifying information:
!
! Portions Copyright (c) [yyyy] [name of copyright owner]. All rights reserved.
!
! CDDL HEADER END
!

!
! Copyright (c) 2012, Regents of the University of Minnesota.  All rights reserved.
!
!****************************************************************************
!
! MODULE Exp6_KongChakrabarty_1973_ArNe
!
!
! Compute energy and forces on an isolated cluster of atoms using
! the exp-6 potential shifted to have zero energy at cutoff.
! Parameters due to Hogervorst [1] and mixing rule between species
! due to Kong and Chakrabarty [2].
!
! Author : E. B. Tadmor (10-MAR-12), updated to kim api v1.6 4-AUG-14,
!          updated to kim api v2.0 30-JUN-18 by D. S. Karls.
!
! Support atom species:
!
!  1 = "Ar"
!  2 = "Ne"
!
!  References:
!
!  [1] W. Hogervorst, Physica, vol 51, 77-89 (1971). (Like interactions.)
!
!  [2] C. L. Kong and M. R. Chakrabarty, J. Phys. Chem., Vol. 77, 2668-2670
!      (1973). (Mixing rule.)
!
!****************************************************************************

module Exp6_KongChakrabarty_1973_ArNe

use, intrinsic :: iso_c_binding
use kim_model_headers_module
implicit none

save
private
public Compute_Energy_Forces, &
       compute_arguments_create, &
       compute_arguments_destroy, &
       destroy, &
       speccodeAr, &
       speccodeNe, &
       cutoff, &
       buffer_type

integer(c_int), parameter :: cd = c_double ! used for literal constants

!
! Define global potential parameters
!
real(c_double), parameter :: kb     = 8.6173e-5_cd ! Boltzmann's constant [eV/K]
real(c_double), parameter :: cutoff = 8.15_cd      ! cutoff radius [A] (arbitrary value)
real(c_double), parameter :: eps11  = 138.0_cd*kb  ! Ar-Ar epsilon parameter [eV]
real(c_double), parameter :: rm11   = 3.77_cd      ! Ar-Ar rm parameter [A]
real(c_double), parameter :: alf11  = 14.8_cd      ! Ar-Ar alpha parameter [-]
real(c_double), parameter :: eps22  = 43.0_cd*kb   ! Ne-Ne epsilon parameter [eV]
real(c_double), parameter :: rm22   = 3.03_cd      ! Ne-Ne rm parameter [A]
real(c_double), parameter :: alf22  = 16.0_cd      ! Ne-Ne alpha parameter [-]
real(c_double), parameter :: eps12  = 68.89_cd*kb  ! Ar-Ne epsilon parameter [eV]
real(c_double), parameter :: rm12   = 3.447_cd     ! Ar-Ne rm parameter [A]
real(c_double), parameter :: alf12  = 15.52_cd     ! Ar-Ne alpha parameter [-]
integer(c_int), parameter :: speccodeAr = 1, speccodeNe = 2

type, bind(c) :: buffer_type
  real(c_double) :: influence_distance
  real(c_double) :: cutoff(1)
  real(c_double) :: cutsq(1)
  integer(c_int) :: &
    model_will_not_request_neighbors_of_noncontributing_particles(1)
end type buffer_type

contains

!-------------------------------------------------------------------------------
!
!  Calculate exp-6 pair potential phi(r) and its derivative dphi(r)
!  Note: potential is shifted to have zero energy at r=cutoff
!
!-------------------------------------------------------------------------------
! The "recursive" keyword is added below make this routine thread safe
recursive subroutine calc_phi_dphi(r,phi,dphi,eps,rm,alf,cutoff)
  implicit none

  !-- Transferred variables
  real(c_double), intent(in)  :: r
  real(c_double), intent(in)  :: eps,rm,alf,cutoff
  real(c_double), intent(out) :: phi,dphi

  !-- Local variables
  real(c_double) soa,amp,ror,cor,phicut

  if (r .gt. cutoff) then
    ! Argument exceeds cutoff radius
    phi = 0.0_cd
    dphi = 0.0_cd
  else
    soa = 6.0_cd/alf
    amp = eps/(1.0_cd-soa)
    cor = cutoff/rm
    ror = r/rm
    phicut = amp*(soa*exp(alf*(1.0_cd-cor))-cor**(-6))
    phi = amp*(soa*exp(alf*(1.0_cd-ror))-ror**(-6)) - phicut
    dphi = -6.0_cd*amp/rm*(exp(alf*(1.0_cd-ror))-ror**(-7))
  endif

  return

end subroutine calc_phi_dphi

!-------------------------------------------------------------------------------
!
! Compute energy and forces on atoms from the positions.
!
!-------------------------------------------------------------------------------
! The "recursive" keyword is added below make this routine thread safe
recursive subroutine Compute_Energy_Forces(model_compute_handle, &
  model_compute_arguments_handle, ierr) bind(c)
  implicit none

  !-- Transferred variables
  type(kim_model_compute_handle_type), intent(in) :: model_compute_handle
  type(kim_model_compute_arguments_handle_type), intent(in) :: &
    model_compute_arguments_handle
  integer(c_int), intent(out) :: ierr

  !--Local Variables
  integer(c_int), parameter :: DIM=3
  integer(c_int) :: i, j, jj, numnei, ierr2
  integer(c_int) :: comp_energy, comp_force
  real(c_double) Rij(DIM), Rsqij, r, phi, dphi

  !-- KIM variables
  integer(c_int), pointer :: N
  real(c_double), pointer :: energy
  real(c_double), pointer :: coor(:,:)
  real(c_double), pointer :: force(:,:)
  integer(c_int), pointer :: nei1part(:)
  integer(c_int), pointer :: particleSpeciesCodes(:)
  integer(c_int), pointer :: particleContributing(:)
  type(buffer_type), pointer :: buf; type(c_ptr) :: pbuf

  ! Avoid unused argument warning
  if (model_compute_handle .eq. KIM_MODEL_COMPUTE_NULL_HANDLE) continue

  ! Get buffer address from KIM API
  call kim_get_model_buffer_pointer(model_compute_handle, pbuf)
  call c_f_pointer(pbuf, buf)

  ierr = 0
  ierr2 = 0

  ! Unpack data from KIM object
  !
  ierr = 0
  call kim_get_argument_pointer( &
    model_compute_arguments_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_NUMBER_OF_PARTICLES, N, ierr2)
  ierr = ierr + ierr2
  call kim_get_argument_pointer( &
    model_compute_arguments_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_PARTICLE_SPECIES_CODES, N, particleSpeciesCodes, &
    ierr2)
  ierr = ierr + ierr2
  call kim_get_argument_pointer( &
    model_compute_arguments_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_PARTICLE_CONTRIBUTING, N, particleContributing, &
    ierr2)
  ierr = ierr + ierr2
  call kim_get_argument_pointer( &
    model_compute_arguments_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_COORDINATES, dim, N, coor, ierr2)
  ierr = ierr + ierr2
  call kim_get_argument_pointer( &
    model_compute_arguments_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_PARTIAL_ENERGY, energy, ierr2)
  ierr = ierr + ierr2
  call kim_get_argument_pointer( &
    model_compute_arguments_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_PARTIAL_FORCES, dim, N, force, ierr2)
  ierr = ierr + ierr2
  if (ierr /= 0) then
    call kim_log_entry(model_compute_arguments_handle, &
      KIM_LOG_VERBOSITY_ERROR, &
      "Failed to retrieve data from KIM API compute-arguments object")
    return
  endif

  ! Check to see if we have been asked to compute the energy and/or forces
  !
  if (associated(energy)) then
    comp_energy = 1
  else
    comp_energy = 0
  end if
  if (associated(force)) then
    comp_force = 1
  else
    comp_force = 0
  end if

  ! Check to be sure that the species are correct
  !
  ierr = 1 ! assume an error
  do i = 1,N
    if (particleSpeciesCodes(i).ne.speccodeAr .and. particleSpeciesCodes(i).ne.speccodeNe) then
      call kim_log_entry(model_compute_arguments_handle, &
        KIM_LOG_VERBOSITY_ERROR, "Unexpected species code detected")
      return
    endif
  enddo
  ierr = 0 ! everything is ok

  ! Compute energy and forces
  if (comp_energy.eq.1) energy = 0.0_cd
  if (comp_force.eq.1)  force  = 0.0_cd

  do i=1,N
    if (particleContributing(i) == 1) then
      ! Get neighbor list of current atom
      call kim_get_neighbor_list( &
         model_compute_arguments_handle, 1, i, numnei, nei1part, ierr)
      if (ierr /= 0) then
         ! some sort of problem, exit
         call kim_log_entry(model_compute_arguments_handle, &
           KIM_LOG_VERBOSITY_ERROR, "GetNeighborList failed")
         ierr = 1
         return
      endif

      do jj = 1, numnei

        j = nei1part(jj) ! Get index of neighbor

        if ( .not.( (particleContributing(j) == 1) .and. &
             j.lt.i) ) then ! effective half list
          Rij(:) = coor(:,j) - coor(:,i)
          Rsqij = dot_product(Rij,Rij)

          if ( Rsqij.lt.buf%cutsq(1) ) then
             r = sqrt(Rsqij)
             if (particleSpeciesCodes(i).eq.1 .and. particleSpeciesCodes(j).eq.1) then
                call calc_phi_dphi(r,phi,dphi,eps11,rm11,alf11,buf%cutoff(1))
             elseif (particleSpeciesCodes(i).eq.2 .and. particleSpeciesCodes(j).eq.2) then
                call calc_phi_dphi(r,phi,dphi,eps22,rm22,alf22,buf%cutoff(1))
             else
                call calc_phi_dphi(r,phi,dphi,eps12,rm12,alf12,buf%cutoff(1))
             endif
             if (comp_energy.eq.1) then
               if (particleContributing(j) == 1) then
                 energy = energy + phi
               else
                 energy = energy + 0.5_cd*phi
               endif
             endif

             if (comp_force.eq.1) then
               if (particleContributing(j).eq.1) then
                 force(:,i) = force(:,i) + dphi*Rij/r
                 force(:,j) = force(:,j) - dphi*Rij/r
               else
                 force(:,i) = force(:,i) + 0.5*dphi*Rij/r
                 force(:,j) = force(:,j) - 0.5*dphi*Rij/r
               endif
             endif

          endif
        endif
     enddo ! End loop over neighbors of atom i
    endif ! Check on whether particle is contributing
enddo ! End primary loop over particles

! No errors
ierr = 0

return
end subroutine Compute_Energy_Forces

!-------------------------------------------------------------------------------
!
! Model destroy routine
!
!-------------------------------------------------------------------------------
! The "recursive" keyword is added below make this routine thread safe
recursive subroutine destroy(model_destroy_handle, ierr) bind(c)
  use, intrinsic :: iso_c_binding
  implicit none

  !-- Transferred variables
  type(kim_model_destroy_handle_type), intent(inout) :: model_destroy_handle
  integer(c_int), intent(out) :: ierr
  type(buffer_type), pointer :: buf; type(c_ptr) :: pbuf

  call kim_get_model_buffer_pointer(model_destroy_handle, pbuf)
  call c_f_pointer(pbuf, buf)

  call kim_log_entry(model_destroy_handle, KIM_LOG_VERBOSITY_INFORMATION, &
    "deallocating model buffer")
  deallocate(buf)
  ierr = 0  ! everything is good
end subroutine destroy

!-------------------------------------------------------------------------------
!
! Model compute arguments create routine (REQUIRED)
!
!-------------------------------------------------------------------------------
! The "recursive" keyword is added below make this routine thread safe
recursive subroutine compute_arguments_create(model_compute_handle, &
  model_compute_arguments_create_handle, ierr) bind(c)
  use, intrinsic :: iso_c_binding
  use kim_model_compute_arguments_create_module
  implicit none

  !-- Transferred variables
  type(kim_model_compute_handle_type), intent(in) :: model_compute_handle
  type(kim_model_compute_arguments_create_handle_type), intent(inout) :: &
    model_compute_arguments_create_handle
  integer(c_int), intent(out) :: ierr

  integer(c_int) :: ierr2

  ! Avoid unused argument warning
  if (model_compute_handle .eq. KIM_MODEL_COMPUTE_NULL_HANDLE) continue

  ierr = 0
  ierr2 = 0

  ! register arguments
  call kim_set_argument_support_status( &
    model_compute_arguments_create_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_PARTIAL_ENERGY, &
    KIM_SUPPORT_STATUS_OPTIONAL, ierr2)
  ierr = ierr + ierr2
  call kim_set_argument_support_status( &
    model_compute_arguments_create_handle, &
    KIM_COMPUTE_ARGUMENT_NAME_PARTIAL_FORCES, &
    KIM_SUPPORT_STATUS_OPTIONAL, ierr2)
  ierr = ierr + ierr2

  ! register call backs (ProcessDEDrTerm, ProcessD2EDr2Term
  ! NONE

  if (ierr /= 0) then
    ierr = 1
    call kim_log_entry(model_compute_arguments_create_handle, &
      KIM_LOG_VERBOSITY_ERROR, &
      "Unable to successfully create compute-arguments object")
  endif

  return
end subroutine compute_arguments_create

!-------------------------------------------------------------------------------
!
! Model compute arguments destroy routine (REQUIRED)
!
!-------------------------------------------------------------------------------
! The "recursive" keyword is added below make this routine thread safe
recursive subroutine compute_arguments_destroy(model_compute_handle, &
  model_compute_arguments_destroy_handle, ierr) bind(c)
  use, intrinsic :: iso_c_binding
  implicit none

  !-- Transferred variables
  type(kim_model_compute_handle_type), intent(in) :: model_compute_handle
  type(kim_model_compute_arguments_destroy_handle_type), intent(inout) :: &
    model_compute_arguments_destroy_handle
  integer(c_int), intent(out) :: ierr

  ! avoid unused dummy argument warnings
  if (model_compute_handle .eq. KIM_MODEL_COMPUTE_NULL_HANDLE) continue
  if (model_compute_arguments_destroy_handle .eq. &
    KIM_MODEL_COMPUTE_ARGUMENTS_DESTROY_NULL_HANDLE) continue

  ierr = 0
  return
end subroutine compute_arguments_destroy

end module Exp6_KongChakrabarty_1973_ArNe

!-------------------------------------------------------------------------------
!
! Model initialization routine (REQUIRED)
!
!-------------------------------------------------------------------------------
! The "recursive" keyword is added below make this routine thread safe
recursive subroutine create(model_create_handle, requested_length_unit, &
  requested_energy_unit, requested_charge_unit, requested_temperature_unit, &
  requested_time_unit, ierr) bind(c)
  use, intrinsic :: iso_c_binding
  use Exp6_KongChakrabarty_1973_ArNe
  use kim_model_headers_module
  implicit none

  !-- Transferred variables
  type(kim_model_create_handle_type), intent(inout) :: model_create_handle
  type(kim_length_unit_type), intent(in) :: requested_length_unit
  type(kim_energy_unit_type), intent(in) :: requested_energy_unit
  type(kim_charge_unit_type), intent(in) :: requested_charge_unit
  type(kim_temperature_unit_type), intent(in) :: requested_temperature_unit
  type(kim_time_unit_type), intent(in) :: requested_time_unit
  integer(c_int), intent(out) :: ierr

  !-- KIM variables
  integer(c_int) :: ierr2
  type(buffer_type), pointer :: buf

  ierr = 0
  ierr2 = 0

  call kim_set_units(model_create_handle, &
    KIM_LENGTH_UNIT_A, &
    KIM_ENERGY_UNIT_EV, &
    KIM_CHARGE_UNIT_UNUSED, &
    KIM_TEMPERATURE_UNIT_UNUSED, &
    KIM_TIME_UNIT_UNUSED, &
    ierr2)
  ierr = ierr + ierr2

  ! register species
  call kim_set_species_code(model_create_handle, &
    KIM_SPECIES_NAME_AR, speccodeAr, ierr2)
  ierr = ierr + ierr2
  call kim_set_species_code(model_create_handle, &
    KIM_SPECIES_NAME_NE, speccodeNe, ierr2)
  ierr = ierr + ierr2

  ! register numbering
  call kim_set_model_numbering(model_create_handle, &
    KIM_NUMBERING_ONE_BASED, ierr2);
  ierr = ierr + ierr2

  ! register function pointers
  call kim_set_routine_pointer(model_create_handle, &
    KIM_MODEL_ROUTINE_NAME_COMPUTE, KIM_LANGUAGE_NAME_FORTRAN, &
    1, c_funloc(Compute_Energy_Forces), ierr2)
  ierr = ierr + ierr2
  call kim_set_routine_pointer( &
    model_create_handle, KIM_MODEL_ROUTINE_NAME_COMPUTE_ARGUMENTS_CREATE, &
    KIM_LANGUAGE_NAME_FORTRAN, 1, c_funloc(compute_arguments_create), ierr2)
  ierr = ierr + ierr2
  call kim_set_routine_pointer( &
    model_create_handle, KIM_MODEL_ROUTINE_NAME_COMPUTE_ARGUMENTS_DESTROY, &
    KIM_LANGUAGE_NAME_FORTRAN, 1, c_funloc(compute_arguments_destroy), ierr2)
  ierr = ierr + ierr2
  call kim_set_routine_pointer(model_create_handle, &
    KIM_MODEL_ROUTINE_NAME_DESTROY, KIM_LANGUAGE_NAME_FORTRAN, &
    1, c_funloc(destroy), ierr2)
  ierr = ierr + ierr2

  ! allocate buffer
  allocate(buf)

  ! store model buffer in KIM object
  call kim_set_model_buffer_pointer(model_create_handle, &
    c_loc(buf))

  ! set buffer values
  buf%influence_distance = cutoff
  buf%cutoff(1) = cutoff
  buf%cutsq(1) = cutoff*cutoff
  buf%model_will_not_request_neighbors_of_noncontributing_particles(1) = 1

  ! register influence distance
  call kim_set_influence_distance_pointer( &
    model_create_handle, buf%influence_distance)

  ! register cutoff
  call kim_set_neighbor_list_pointers(model_create_handle, &
    1, buf%cutoff, &
    buf%model_will_not_request_neighbors_of_noncontributing_particles)

  if (ierr /= 0) then
    ierr = 1
    call kim_log_entry(model_create_handle, KIM_LOG_VERBOSITY_ERROR, &
      "Unable to successfully initialize model")
  endif
end subroutine create
