!
! KIM-API: An API for interatomic models
! Copyright (c) 2013--2022, Regents of the University of Minnesota.
! All rights reserved.
!
! Contributors:
!    Ryan S. Elliott
!    Ellad B. Tadmor
!    Valeriu Smirichinski
!    Stephen M. Whalen
!
! SPDX-License-Identifier: LGPL-2.1-or-later
!
! This library is free software; you can redistribute it and/or
! modify it under the terms of the GNU Lesser General Public
! License as published by the Free Software Foundation; either
! version 2.1 of the License, or (at your option) any later version.
!
! This library 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
! Lesser General Public License for more details.
!
! You should have received a copy of the GNU Lesser General Public License
! along with this library; if not, write to the Free Software Foundation,
! Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
!

!****************************************************************************
!**
!**  MODULE ex_model_driver_P_LJ
!**
!**  Lennard-Jones pair potential KIM Model Driver
!**  shifted to have zero energy at the cutoff radius
!**
!**  Language: Fortran 2003
!**
!****************************************************************************

module ex_model_driver_p_lj

  use, intrinsic :: iso_c_binding
  use kim_model_driver_headers_module
  implicit none

  save
  private
  public &
    BUFFER_TYPE, &
    Compute_Energy_Forces, &
    compute_arguments_create, &
    compute_arguments_destroy, &
    refresh, &
    write_model, &
    destroy, &
    calc_phi, &
    calc_phi_dphi, &
    calc_phi_dphi_d2phi, &
    speccode

  ! Below are the definitions and values of all Model parameters
  integer(c_int), parameter          :: cd = c_double  ! for literal constants
  integer(c_int), parameter          :: DIM = 3        ! dimensionality of space
  integer(c_int), parameter          :: speccode = 1   ! internal species code

  !-----------------------------------------------------------------------------
  !
  !  Definition of Buffer type
  !
  !-----------------------------------------------------------------------------
  type, bind(c) :: BUFFER_TYPE
    character(c_char) :: species_name(100)
    real(c_double) :: influence_distance(1)
    real(c_double) :: Pcutoff(1)
    real(c_double) :: cutsq(1)
    integer(c_int) :: &
      model_will_not_request_neighbors_of_noncontributing_particles(1)
    real(c_double) :: epsilon(1)
    real(c_double) :: sigma(1)
    real(c_double) :: shift(1)
  end type BUFFER_TYPE

contains

  !-----------------------------------------------------------------------------
  !
  !  Calculate pair potential phi(r)
  !
  !-----------------------------------------------------------------------------
  recursive subroutine calc_phi(model_epsilon, &
                                model_sigma, &
                                model_shift, &
                                model_cutoff, r, phi)
    implicit none

    !-- Transferred variables
    real(c_double), intent(in)  :: model_epsilon
    real(c_double), intent(in)  :: model_sigma
    real(c_double), intent(in)  :: model_shift
    real(c_double), intent(in)  :: model_cutoff
    real(c_double), intent(in)  :: r
    real(c_double), intent(out) :: phi

    !-- Local variables
    real(c_double) rsq, sor, sor6, sor12

    rsq = r * r             !  r^2
    sor = model_sigma / r   !  (sig/r)
    sor6 = sor * sor * sor     !
    sor6 = sor6 * sor6       !  (sig/r)^6
    sor12 = sor6 * sor6       !  (sig/r)^12
    if (r > model_cutoff) then
      ! Argument exceeds cutoff radius
      phi = 0.0_cd
    else
      phi = 4.0_cd * model_epsilon * (sor12 - sor6) + model_shift
    end if

  end subroutine calc_phi

  !-----------------------------------------------------------------------------
  !
  !  Calculate pair potential phi(r) and its derivative dphi(r)
  !
  !-----------------------------------------------------------------------------
  recursive subroutine calc_phi_dphi(model_epsilon, &
                                     model_sigma, &
                                     model_shift, &
                                     model_cutoff, r, phi, dphi)
    implicit none

    !-- Transferred variables
    real(c_double), intent(in)  :: model_epsilon
    real(c_double), intent(in)  :: model_sigma
    real(c_double), intent(in)  :: model_shift
    real(c_double), intent(in)  :: model_cutoff
    real(c_double), intent(in)  :: r
    real(c_double), intent(out) :: phi, dphi

    !-- Local variables
    real(c_double) rsq, sor, sor6, sor12

    rsq = r * r             !  r^2
    sor = model_sigma / r   !  (sig/r)
    sor6 = sor * sor * sor     !
    sor6 = sor6 * sor6       !  (sig/r)^6
    sor12 = sor6 * sor6       !  (sig/r)^12
    if (r > model_cutoff) then
      ! Argument exceeds cutoff radius
      phi = 0.0_cd
      dphi = 0.0_cd
    else
      phi = 4.0_cd * model_epsilon * (sor12 - sor6) + model_shift
      dphi = 24.0_cd * model_epsilon * (-2.0_cd * sor12 + sor6) / r
    end if

  end subroutine calc_phi_dphi

  !-----------------------------------------------------------------------------
  !
  !  Calculate pair potential phi(r) and its derivatives dphi(r) and d2phi(r)
  !
  !-----------------------------------------------------------------------------
  recursive subroutine calc_phi_dphi_d2phi(model_epsilon, &
                                           model_sigma, &
                                           model_shift, &
                                           model_cutoff, r, phi, dphi, d2phi)
    implicit none

    !-- Transferred variables
    real(c_double), intent(in)  :: model_epsilon
    real(c_double), intent(in)  :: model_sigma
    real(c_double), intent(in)  :: model_shift
    real(c_double), intent(in)  :: model_cutoff
    real(c_double), intent(in)  :: r
    real(c_double), intent(out) :: phi, dphi, d2phi

    !-- Local variables
    real(c_double) rsq, sor, sor6, sor12

    rsq = r * r             !  r^2
    sor = model_sigma / r   !  (sig/r)
    sor6 = sor * sor * sor     !
    sor6 = sor6 * sor6       !  (sig/r)^6
    sor12 = sor6 * sor6       !  (sig/r)^12
    if (r > model_cutoff) then
      ! Argument exceeds cutoff radius
      phi = 0.0_cd
      dphi = 0.0_cd
      d2phi = 0.0_cd
    else
      phi = 4.0_cd * model_epsilon * (sor12 - sor6) + model_shift
      dphi = 24.0_cd * model_epsilon * (-2.0_cd * sor12 + sor6) / r
      d2phi = 24.0_cd * model_epsilon * (26.0_cd * sor12 - 7.0_cd * sor6) / rsq
    end if

  end subroutine calc_phi_dphi_d2phi

  !-----------------------------------------------------------------------------
  !
  ! Compute energy and forces on particles from the positions.
  !
  !-----------------------------------------------------------------------------
  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
    real(c_double) :: r, Rsqij, phi, dphi, d2phi, dEidr, d2Eidr
    integer(c_int) :: i, j, jj, numnei
    integer(c_int) :: ierr2
    integer(c_int) :: comp_force, comp_energy, comp_enepot, comp_process_dEdr, &
                      comp_process_d2Edr2
    type(BUFFER_TYPE), pointer :: buf; type(c_ptr) :: pbuf

    real(c_double), target :: Rij(DIM)
    real(c_double), target :: Rij_pairs(DIM, 2)
    real(c_double), target :: r_pairs(2)
    integer(c_int), target :: i_pairs(2), j_pairs(2)

    !-- KIM variables
    real(c_double) :: model_cutoff
    integer(c_int), pointer :: N
    real(c_double), pointer :: energy
    real(c_double), pointer :: coor(:, :)
    real(c_double), pointer :: force(:, :)
    real(c_double), pointer :: enepot(:)
    integer(c_int), pointer :: nei1part(:)
    integer(c_int), pointer :: particleSpeciesCodes(:)
    integer(c_int), pointer :: particleContributing(:)

    ! get model buffer from KIM object
    call kim_get_model_buffer_pointer(model_compute_handle, pbuf)
    call c_f_pointer(pbuf, buf)

    model_cutoff = buf%influence_distance(1)

    ! Check to see if we have been asked to compute the forces, energyperpart,
    ! energy and d1Edr
    !
    ierr = 0
    call kim_is_callback_present( &
      model_compute_arguments_handle, &
      KIM_COMPUTE_CALLBACK_NAME_PROCESS_DEDR_TERM, comp_process_dedr, ierr2)
    ierr = ierr + ierr2
    call kim_is_callback_present( &
      model_compute_arguments_handle, &
      KIM_COMPUTE_CALLBACK_NAME_PROCESS_D2EDR2_TERM, comp_process_d2edr2, ierr2)
    ierr = ierr + ierr2
    if (ierr /= 0) then
      call kim_log_entry(model_compute_arguments_handle, &
                         KIM_LOG_VERBOSITY_ERROR, "get_compute")
      ierr = 1
      return
    end if

    ! 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
    call kim_get_argument_pointer( &
      model_compute_arguments_handle, &
      KIM_COMPUTE_ARGUMENT_NAME_PARTIAL_PARTICLE_ENERGY, n, enepot, ierr2)
    ierr = ierr + ierr2
    if (ierr /= 0) then
      call kim_log_entry(model_compute_arguments_handle, &
                         KIM_LOG_VERBOSITY_ERROR, "get_argument_pointer")
      ierr = 1
      return
    end if

    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
    if (associated(enepot)) then
      comp_enepot = 1
    else
      comp_enepot = 0
    end if

    ! Check to be sure that the species are correct
    !

    ierr = 1 ! assume an error
    do i = 1, N
      if (particleSpeciesCodes(i) /= speccode) then
        call kim_log_entry( &
          model_compute_arguments_handle, KIM_LOG_VERBOSITY_ERROR, &
          "Unexpected species code detected")
        ierr = 1
        return
      end if
    end do
    ierr = 0 ! everything is ok

    ! Initialize potential energies, forces
    !
    if (comp_enepot == 1) enepot = 0.0_cd
    if (comp_energy == 1) energy = 0.0_cd
    if (comp_force == 1) force = 0.0_cd

    !
    !  Compute energy and forces
    !

    !  Loop over particles and compute energy and forces
    !
    do i = 1, N

      if (particleContributing(i) == 1) then
        ! Set up neighbor list for next particle
        !
        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, &
            "kim_api_get_neigh")
          ierr = 1
          return
        end if

        ! Loop over the neighbors of particle i
        !
        do jj = 1, numnei

          j = nei1part(jj)                        ! get neighbor ID

          ! compute relative position vector
          !
          Rij(:) = coor(:, j) - coor(:, i)       ! distance vector between i j

          ! compute energy and forces
          !
          Rsqij = dot_product(Rij, Rij)                ! compute square distance
          if (Rsqij < buf%cutsq(1)) then         ! particles are interacting?

            r = sqrt(Rsqij)                          ! compute distance
            if (comp_process_d2Edr2 == 1) then
              call calc_phi_dphi_d2phi(buf%epsilon(1), &
                                       buf%sigma(1), &
                                       buf%shift(1), &
                                       buf%Pcutoff(1), &
                                       r, phi, dphi, d2phi) ! compute pair potential
              !   and it derivatives
              dEidr = 0.5_cd * dphi               !      regular contribution
              d2Eidr = 0.5_cd * d2phi
            elseif (comp_force == 1 .or. comp_process_dEdr == 1) then
              call calc_phi_dphi(buf%epsilon(1), &
                                 buf%sigma(1), &
                                 buf%shift(1), &
                                 buf%Pcutoff(1), &
                                 r, phi, dphi)        ! compute pair potential
              !   and it derivative

              dEidr = 0.5_cd * dphi                !      regular contribution
            else
              call calc_phi(buf%epsilon(1), &
                            buf%sigma(1), &
                            buf%shift(1), &
                            buf%Pcutoff(1), &
                            r, phi)                  ! compute just pair potential
            end if

            ! contribution to energy
            !
            if (comp_enepot == 1) then
              enepot(i) = enepot(i) + 0.5_cd * phi    ! accumulate energy
            end if
            if (comp_energy == 1) then
              energy = energy + 0.5_cd * phi       !      add half v to total energy
            end if

            ! contribution to process_dEdr
            !
            if (comp_process_dEdr == 1) then
              call kim_process_dedr_term( &
                model_compute_arguments_handle, deidr, r, rij, i, j, ierr)
            end if

            ! contribution to process_d2Edr2
            if (comp_process_d2Edr2 == 1) then
              r_pairs(1) = r
              r_pairs(2) = r
              Rij_pairs(:, 1) = Rij
              Rij_pairs(:, 2) = Rij
              i_pairs(1) = i
              i_pairs(2) = i
              j_pairs(1) = j
              j_pairs(2) = j

              call kim_process_d2edr2_term( &
                model_compute_arguments_handle, d2eidr, &
                r_pairs, Rij_pairs, i_pairs, j_pairs, ierr)
            end if

            ! contribution to forces
            !
            if (comp_force == 1) then
              force(:, i) = force(:, i) + dEidr * Rij / r ! accumulate force on particle i
              force(:, j) = force(:, j) - dEidr * Rij / r ! accumulate force on particle j
            end if

          end if

        end do  ! loop on jj

      end if  ! if particleContributing

    end do  ! do i

    ! Everything is great
    !
    ierr = 0
    return

  end subroutine Compute_Energy_Forces

  !-----------------------------------------------------------------------------
  !
  ! Model driver refresh routine
  !
  !-----------------------------------------------------------------------------
  recursive subroutine refresh(model_refresh_handle, ierr) bind(c)
    implicit none

    !-- transferred variables
    type(kim_model_refresh_handle_type), intent(in) :: model_refresh_handle
    integer(c_int), intent(out) :: ierr

    !-- Local variables
    real(c_double) energy_at_cutoff
    type(BUFFER_TYPE), pointer :: buf; type(c_ptr) :: pbuf

    ! get model buffer from KIM object
    call kim_get_model_buffer_pointer(model_refresh_handle, pbuf)
    call c_f_pointer(pbuf, buf)

    call kim_set_influence_distance_pointer(model_refresh_handle, &
                                            buf%influence_distance(1))
    call kim_set_neighbor_list_pointers( &
      model_refresh_handle, 1, buf%influence_distance, &
      buf%model_will_not_request_neighbors_of_noncontributing_particles)

    ! Set new values in KIM object and buffer
    !
    buf%influence_distance(1) = buf%Pcutoff(1)
    buf%cutsq(1) = (buf%Pcutoff(1))**2
    ! calculate pair potential at r=cutoff with shift=0.0
    call calc_phi(buf%epsilon(1), &
                  buf%sigma(1), &
                  0.0_cd, &
                  buf%Pcutoff(1), &
                  buf%Pcutoff(1), energy_at_cutoff)
    buf%shift(1) = -energy_at_cutoff

    ierr = 0
    return

  end subroutine refresh

  !-----------------------------------------------------------------------------
  !
  ! Model driver write_model routine
  !
  !-----------------------------------------------------------------------------
  recursive subroutine write_model( &
    model_write_parameterized_model_handle, ierr) bind(c)
    implicit none

    !-- transferred variables
    type(kim_model_write_parameterized_model_handle_type), intent(in) &
      :: model_write_parameterized_model_handle
    integer(c_int), intent(out) :: ierr

    !-- Local variables
    integer i
    type(BUFFER_TYPE), pointer :: buf; type(c_ptr) :: pbuf
    character(len=512, kind=c_char) :: path
    character(len=512, kind=c_char) :: model_name
    character(len=512, kind=c_char) :: string_buffer
    character(len=100, kind=c_char) :: species_name

    ! get model buffer from KIM object
    call kim_get_model_buffer_pointer( &
      model_write_parameterized_model_handle, pbuf)
    call c_f_pointer(pbuf, buf)

    call kim_get_path(model_write_parameterized_model_handle, path)
    call kim_get_model_name(model_write_parameterized_model_handle, model_name)

    write (string_buffer, '(A)') trim(model_name)//".params"
    call kim_set_parameter_file_name(model_write_parameterized_model_handle, &
                                     string_buffer)
    write (string_buffer, '(A)') trim(path)//"/"//trim(string_buffer)

    open (42, FILE=trim(string_buffer), &
          STATUS="REPLACE", ACTION="WRITE", iostat=ierr)
    if (ierr /= 0) then
      call kim_log_entry( &
        model_write_parameterized_model_handle, KIM_LOG_VERBOSITY_ERROR, &
        "Unable to open parameter file for writing.")
      return
    end if

    do i = 1, 100
      species_name(i:i) = buf%species_name(i)
    end do
    write (42, '(A)') trim(species_name)
    write (42, '(ES20.10)') buf%Pcutoff(1)
    write (42, '(ES20.10)') buf%epsilon(1)
    write (42, '(ES20.10)') buf%sigma(1)

    ierr = 0
    return

  end subroutine write_model

  !-----------------------------------------------------------------------------
  !
  ! Model driver destroy routine
  !
  !-----------------------------------------------------------------------------
  recursive subroutine destroy(model_destroy_handle, ierr) bind(c)
    implicit none

    !-- Transferred variables
    type(kim_model_destroy_handle_type), intent(in) :: model_destroy_handle
    integer(c_int), intent(out) :: ierr

    !-- Local variables
    type(BUFFER_TYPE), pointer :: buf; type(c_ptr) :: pbuf

    ! get model buffer from KIM object
    call kim_get_model_buffer_pointer(model_destroy_handle, pbuf)
    call c_f_pointer(pbuf, buf)

    deallocate (buf)

    ierr = 0
    return

  end subroutine destroy

  !-----------------------------------------------------------------------------
  !
  ! Model driver compute arguments create routine
  !
  !-----------------------------------------------------------------------------
  recursive subroutine compute_arguments_create( &
    model_compute_handle, model_compute_arguments_create_handle, ierr) bind(c)
    implicit none

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

    integer(c_int) ierr2

    ! avoid unsed dummy argument warnings
    if (model_compute_handle == 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, ierr)
    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
    call kim_set_argument_support_status( &
      model_compute_arguments_create_handle, &
      KIM_COMPUTE_ARGUMENT_NAME_PARTIAL_PARTICLE_ENERGY, &
      KIM_SUPPORT_STATUS_OPTIONAL, ierr2)
    ierr = ierr + ierr2
    if (ierr /= 0) then
      call kim_log_entry( &
        model_compute_arguments_create_handle, KIM_LOG_VERBOSITY_ERROR, &
        "Unable to register arguments support_statuss")
      ierr = 1
      goto 42
    end if

    ! register callbacks
    call kim_set_callback_support_status( &
      model_compute_arguments_create_handle, &
      KIM_COMPUTE_CALLBACK_NAME_PROCESS_DEDR_TERM, &
      KIM_SUPPORT_STATUS_OPTIONAL, ierr)
    call kim_set_callback_support_status( &
      model_compute_arguments_create_handle, &
      KIM_COMPUTE_CALLBACK_NAME_PROCESS_D2EDR2_TERM, &
      KIM_SUPPORT_STATUS_OPTIONAL, ierr2)
    ierr = ierr + ierr2
    if (ierr /= 0) then
      call kim_log_entry( &
        model_compute_arguments_create_handle, KIM_LOG_VERBOSITY_ERROR, &
        "Unable to register callbacks support_statuss")
      ierr = 1
      goto 42
    end if

    ierr = 0
42  continue
    return

  end subroutine compute_arguments_create

  !-----------------------------------------------------------------------------
  !
  ! Model driver compute arguments destroy routine
  !
  !-----------------------------------------------------------------------------
  recursive subroutine compute_arguments_destroy( &
    model_compute_handle, model_compute_arguments_destroy_handle, ierr) bind(c)
    implicit none

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

    ! avoid unsed dummy argument warnings
    if (model_compute_handle == KIM_MODEL_COMPUTE_NULL_HANDLE) continue
    if (model_compute_arguments_destroy_handle == &
        KIM_MODEL_COMPUTE_ARGUMENTS_DESTROY_NULL_HANDLE) continue

    ierr = 0
    return
  end subroutine compute_arguments_destroy

end module ex_model_driver_p_lj

!-----------------------------------------------------------------------------
!
! Model driver create routine (REQUIRED)
!
!-----------------------------------------------------------------------------
recursive subroutine model_driver_create_routine( &
  model_driver_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 ex_model_driver_p_lj
  use kim_model_driver_headers_module
  implicit none
  integer(c_int), parameter :: cd = c_double ! used for literal constants

  !-- Transferred variables
  type(kim_model_driver_create_handle_type), intent(in) &
    :: model_driver_create_handle
  type(kim_length_unit_type), intent(in), value :: requested_length_unit
  type(kim_energy_unit_type), intent(in), value :: requested_energy_unit
  type(kim_charge_unit_type), intent(in), value :: requested_charge_unit
  type(kim_temperature_unit_type), intent(in), value :: &
    requested_temperature_unit
  type(kim_time_unit_type), intent(in), value :: requested_time_unit
  integer(c_int), intent(out) :: ierr

  !-- Local variables
  integer i
  integer(c_int) :: number_of_parameter_files
  character(len=1024, kind=c_char) :: parameter_file_directory_name
  character(len=1024, kind=c_char) :: parameter_file_basename
  character(len=1024, kind=c_char) :: parameter_file_name
  integer(c_int) :: ierr2
  type(BUFFER_TYPE), pointer :: buf
  type(kim_species_name_type) species_name
  ! define variables for all model parameters to be read in
  real(c_double) factor
  character(len=100, kind=c_char) in_species
  real(c_double) in_cutoff
  real(c_double) in_epsilon
  real(c_double) in_sigma
  real(c_double) energy_at_cutoff

  ! register units
  call kim_set_units( &
    model_driver_create_handle, &
    REQUESTED_LENGTH_UNIT, &
    REQUESTED_ENERGY_UNIT, &
    KIM_CHARGE_UNIT_UNUSED, &
    KIM_TEMPERATURE_UNIT_UNUSED, &
    KIM_TIME_UNIT_UNUSED, ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "Unable to set units")
    ierr = 1
    goto 42
  end if

  ! register numbering
  call kim_set_model_numbering( &
    model_driver_create_handle, KIM_NUMBERING_ONE_BASED, ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "Unable to set numbering")
    ierr = 1
    goto 42
  end if

  ! store callback pointers in KIM object
  call kim_set_routine_pointer( &
    model_driver_create_handle, KIM_MODEL_ROUTINE_NAME_COMPUTE, &
    KIM_LANGUAGE_NAME_FORTRAN, 1, c_funloc(Compute_Energy_Forces), ierr)
  call kim_set_routine_pointer( &
    model_driver_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_driver_create_handle, KIM_MODEL_ROUTINE_NAME_REFRESH, &
    KIM_LANGUAGE_NAME_FORTRAN, 1, c_funloc(refresh), ierr2)
  ierr = ierr + ierr2
  call kim_set_routine_pointer( &
    model_driver_create_handle, &
    KIM_MODEL_ROUTINE_NAME_WRITE_PARAMETERIZED_MODEL, &
    KIM_LANGUAGE_NAME_FORTRAN, 0, c_funloc(write_model), ierr2)
  ierr = ierr + ierr2
  call kim_set_routine_pointer( &
    model_driver_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_driver_create_handle, KIM_MODEL_ROUTINE_NAME_DESTROY, &
    KIM_LANGUAGE_NAME_FORTRAN, 1, c_funloc(destroy), ierr2)
  ierr = ierr + ierr2
  if (ierr /= 0) then
    call kim_log_entry( &
      model_driver_create_handle, KIM_LOG_VERBOSITY_ERROR, &
      "Unable to store callback pointers")
    ierr = 1
    goto 42
  end if

  ! process parameter files
  call kim_get_number_of_parameter_files( &
    model_driver_create_handle, number_of_parameter_files)
  if (number_of_parameter_files /= 1) then
    call kim_log_entry( &
      model_driver_create_handle, KIM_LOG_VERBOSITY_ERROR, &
      "Wrong number of parameter files")
    ierr = 1
    goto 42
  end if

  ! Read in model parameters from parameter file
  !
  call kim_get_parameter_file_directory_name( &
    model_driver_create_handle, parameter_file_directory_name)
  call kim_get_parameter_file_basename( &
    model_driver_create_handle, 1, parameter_file_basename, ierr)
  if (ierr /= 0) then
    call kim_log_entry( &
      model_driver_create_handle, KIM_LOG_VERBOSITY_ERROR, &
      "Unable to get parameter file basename")
    ierr = 1
    goto 42
  end if
  parameter_file_name = trim(parameter_file_directory_name)//"/"// &
                        trim(parameter_file_basename)
  open (10, file=parameter_file_name, status="old")
  read (10, *, iostat=ierr, err=100) in_species
  read (10, *, iostat=ierr, err=100) in_cutoff
  read (10, *, iostat=ierr, err=100) in_epsilon
  read (10, *, iostat=ierr, err=100) in_sigma
  close (10)

  goto 200
100 continue
  ! reading parameters failed
  call kim_log_entry(model_driver_create_handle, &
                     KIM_LOG_VERBOSITY_ERROR, "Unable to read LJ parameters")
  ierr = 1
  goto 42

200 continue

  ! register species
  call kim_from_string(in_species, species_name)
  call kim_set_species_code( &
    model_driver_create_handle, species_name, speccode, ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "Unable to set species code")
    ierr = 1
    goto 42
  end if

  ! convert to appropriate units
  call kim_convert_unit( &
    KIM_LENGTH_UNIT_A, &
    KIM_ENERGY_UNIT_EV, &
    KIM_CHARGE_UNIT_E, &
    KIM_TEMPERATURE_UNIT_K, &
    KIM_TIME_UNIT_PS, &
    requested_length_unit, &
    requested_energy_unit, &
    requested_charge_unit, &
    requested_temperature_unit, &
    requested_time_unit, &
    1.0_cd, 0.0_cd, 0.0_cd, 0.0_cd, 0.0_cd, factor, ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "kim_api_convert_to_act_unit")
    ierr = 1
    goto 42
  end if
  in_cutoff = in_cutoff * factor

  call kim_convert_unit( &
    KIM_LENGTH_UNIT_A, &
    KIM_ENERGY_UNIT_EV, &
    KIM_CHARGE_UNIT_E, &
    KIM_TEMPERATURE_UNIT_K, &
    KIM_TIME_UNIT_PS, &
    requested_length_unit, &
    requested_energy_unit, &
    requested_charge_unit, &
    requested_temperature_unit, &
    requested_time_unit, &
    0.0_cd, 1.0_cd, 0.0_cd, 0.0_cd, 0.0_cd, factor, ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "kim_api_convert_to_act_unit")
    ierr = 1
    goto 42
  end if
  in_epsilon = in_epsilon * factor

  call kim_convert_unit( &
    KIM_LENGTH_UNIT_A, &
    KIM_ENERGY_UNIT_EV, &
    KIM_CHARGE_UNIT_E, &
    KIM_TEMPERATURE_UNIT_K, &
    KIM_TIME_UNIT_PS, &
    requested_length_unit, &
    requested_energy_unit, &
    requested_charge_unit, &
    requested_temperature_unit, &
    requested_time_unit, &
    1.0_cd, 0.0_cd, 0.0_cd, 0.0_cd, 0.0_cd, factor, ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "kim_api_convert_to_act_unit")
    ierr = 1
    goto 42
  end if
  in_sigma = in_sigma * factor

  allocate (buf)

  ! setup buffer
  ! set value of parameters
  do i = 1, 100
    buf%species_name(i) = in_species(i:i)
  end do
  buf%influence_distance(1) = in_cutoff
  buf%Pcutoff(1) = in_cutoff
  buf%cutsq(1) = in_cutoff**2
  buf%model_will_not_request_neighbors_of_noncontributing_particles = 1
  buf%epsilon(1) = in_epsilon
  buf%sigma(1) = in_sigma
  call calc_phi(in_epsilon, &
                in_sigma, &
                0.0_cd, &
                in_cutoff, &
                in_cutoff, energy_at_cutoff)
  buf%shift(1) = -energy_at_cutoff

  ! store model cutoff in KIM object
  call kim_set_influence_distance_pointer( &
    model_driver_create_handle, buf%influence_distance(1))
  call kim_set_neighbor_list_pointers( &
    model_driver_create_handle, 1, buf%influence_distance, &
    buf%model_will_not_request_neighbors_of_noncontributing_particles)

  ! end setup buffer

  ! store in model buffer
  call kim_set_model_buffer_pointer( &
    model_driver_create_handle, c_loc(buf))

  ! set pointers to parameters in KIM object
  call kim_set_parameter_pointer( &
    model_driver_create_handle, buf%pcutoff, "cutoff", &
    "Distance beyond which particles do not interact with one another.", ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "set_parameter")
    ierr = 1
    goto 42
  end if

  call kim_set_parameter_pointer( &
    model_driver_create_handle, buf%epsilon, "epsilon", &
    "Maximum depth of the potential well.", ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "set_parameter")
    ierr = 1
    goto 42
  end if

  call kim_set_parameter_pointer( &
    model_driver_create_handle, buf%sigma, "sigma", &
    "Distance at which energy is zero and force is repulsive.", ierr)
  if (ierr /= 0) then
    call kim_log_entry(model_driver_create_handle, &
                       KIM_LOG_VERBOSITY_ERROR, "set_parameter")
    ierr = 1
    goto 42
  end if

  ierr = 0
42 continue
  return

end subroutine model_driver_create_routine
