Last commit for src/input.f90: 5874abaa643d4472a2aa9d1c5dbe454dadbd8d1f

Initial commit of the AENET code.

Bruno Mundim [2017-01-02 17:48:39]
Initial commit of the AENET code.
!-----------------------------------------------------------------------
!                   input.f90 - input file handling
!-----------------------------------------------------------------------
!+ 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/>.
!-----------------------------------------------------------------------
! 2012-05-27 Nongnuch Artrith (NA), Alexander Urban (AU)
!-----------------------------------------------------------------------

module input

  use aeio,     only: aeio_header,             &
                      aeio_readline,           &
                      aeio_assert_file_exists, &
                      PATHLEN, LINELEN, TYPELEN

  use geometry, only: geo_itype_of_name

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

  implicit none
  private
  save

  public  :: read_InpGenerate,  &
             read_InpPredict,   &
             read_InpTrain,     &
             read_InpMC,        &
             del_InputData,     &
             inp_read_networks, &
             inp_print_info

  private :: inp_read_value,    &
             inp_read_value_c1, &
             inp_read_value_d1, &
             inp_read_value_i1

  !--------------------------------------------------------------------!
  !                      generic input data type                       !
  !--------------------------------------------------------------------!

  type, public :: InputData

     !-----------------------------------------------------------------!
     ! init             .true. when memory has been allocated          !
     ! file             name of the input file the data was read from  !
     ! outFileName      name of the output file                        !
     ! mode             run mode (predict, mc)                         !
     ! verbosity        verbosity level (0=low, 1=normal, 2=high)      !
     !                                                                 !
     ! nTypes           number of atomic species                       !
     ! typeName(i)      name of i-th atomic species                    !
     ! atomicEnergy(i)  atomic energy of i-th atomic species           !
     !                                                                 !
     ! activeType(i)    == 1, if type i is "active"                    !
     !                                                                 !
     ! netFile(i)       path to NN potential file for species i        !
     ! netArch(i)       architecture (as string) of NN for species i   !
     !                                                                 !
     ! do_forces        .true. if forces shall be calculated           !
     ! do_timing        .true. if timings shall be saved               !
     !                                                                 !
     ! nStrucs          number of structures to run calculations for   !
     ! strucFile(i)     path to i-th atomic structure file             !
     !                                                                 !
     !----------------- structural fingerprint basis ------------------!
     ! setupFile(i)     path to the basis function setup for i         !
     !                                                                 !
     !--------------------------- training ----------------------------!
     ! trn_file         name of the training set file                  !
     ! trn_testset      percentage of datapoints to be used for testing!
     ! trn_maxenergy    max. formation energy per atom for training    !
     ! trn_steps        number of training iterations/epochs           !
     ! trn_method       short name of the training method/algorithm    !
     ! trn_methodName   long name of the training method/algorithm     !
     ! trn_nparams      number of parameters of the training method    !
     ! trn_param(i)     i-th parameter of the training algorithm       !
     ! do_save_energies if .true. save testing and training energies   !
     !                  of all structures in the reference data set    !
     !                                                                 !
     !--------------------- structural relaxation ---------------------!
     ! do_relax         .true. if structural relaxation requested      !
     ! relax_method     name of the optimization algorithm             !
     ! relax_steps      max number of relaxation steps                 !
     ! relax_F_conv     convergence criterion for the forces           !
     ! relax_E_conv     convergence criterion for the energy           !
     ! relax_dmax       max. change of coordinate during relax step    !
     !                  (in Angstrom)                                  !
     !                                                                 !
     !-------------------------- Monte-Carlo --------------------------!
     ! nSteps           total nuber of MC steps to be run              !
     ! nSweeps          number of trials per MC step                   !
     ! ensemble         NVT or mVT                                     !
     ! T                temperature                                    !
     ! mu(i)            chemical potential of species i                !
     ! mc_ngroups       number of groups of species                    !
     ! mc_group(i)      group of species i; all species of one group   !
     !                  are exchangeable in an MC simulation           !
     ! mc_ntypes_group(i) number of atom types in group i              !
     ! mc_group_type(i,j) j-th type in i-th group                      !
     !                                                                 !
     !-----------------------------------------------------------------!
     ! do_debug         activate debugging options                     !
     !-----------------------------------------------------------------!

     logical                                             :: init = .false.
     character(len=PATHLEN)                              :: file
     character(len=PATHLEN)                              :: outFileName
     character(len=32)                                   :: mode
     integer                                             :: verbosity

     integer                                             :: nTypes
     character(len=TYPELEN), dimension(:),   allocatable :: typeName
     double precision,       dimension(:),   allocatable :: atomicEnergy

     integer,                dimension(:),   allocatable :: activeType

     character(len=PATHLEN), dimension(:),   allocatable :: netFile
     character(len=LINELEN), dimension(:),   allocatable :: netArch

     logical                                             :: do_forces
     logical                                             :: do_timing

     logical                                             :: do_relax
     character(len=32)                                   :: relax_method
     integer                                             :: relax_steps
     double precision                                    :: relax_F_conv
     double precision                                    :: relax_E_conv
     double precision,       dimension(3)                :: relax_dmax

     integer                                             :: nStrucs
     character(len=PATHLEN), dimension(:),   allocatable :: strucFile

     character(len=PATHLEN)                              :: trn_file
     double precision                                    :: trn_testset
     double precision                                    :: trn_maxenergy
     integer                                             :: trn_steps
     character(len=32)                                   :: trn_method
     character(len=128)                                  :: trn_methodName
     integer                                             :: trn_nparams
     double precision,       dimension(:),   allocatable :: trn_param
     logical                                             :: do_save_energies

     character(len=PATHLEN), dimension(:),   allocatable :: setupFile

     integer                                             :: nSteps
     integer                                             :: nSweeps
     character(len=3)                                    :: ensemble
     double precision                                    :: T
     double precision                                    :: T_final
     logical                                             :: mc_relax_final
     double precision,       dimension(:),   allocatable :: mu
     integer                                             :: mc_ngroups
     integer,                dimension(:),   allocatable :: mc_group
     integer,                dimension(:),   allocatable :: mc_ntypes_group
     integer,                dimension(:,:), allocatable :: mc_group_type

     logical                                             :: do_debug
  end type InputData

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

  interface inp_read_value
     module procedure inp_read_value_c1, inp_read_value_d1, &
                      inp_read_value_i1, inp_read_value_l1
  end interface inp_read_value

  !----------------------- return status values -----------------------!

  integer, parameter, private :: S_OK    = 0
  integer, parameter, private :: S_WARN  = 1
  integer, parameter, private :: S_ERROR = 2
  integer, parameter, private :: S_NOT   = 3

contains

  !--------------------------------------------------------------------!
  !                         default parameters                         !
  !--------------------------------------------------------------------!

  subroutine inp_defaults(inp)

    implicit none

    type(InputData), intent(inout) :: inp

    inp%outFileName  = "OUT"

    inp%nTypes       = 0
    inp%nStrucs      = 0
    inp%nSteps       = 0
    inp%nSweeps      = huge(1)
    inp%ensemble     = 'mvt'

    inp%do_forces    = .false.
    inp%do_timing    = .false.

    inp%do_relax     = .false.
    inp%relax_method = 'bfgs'
    inp%relax_steps  = 99
    inp%relax_F_conv = 0.01d0
    inp%relax_E_conv = 0.001d0
    inp%relax_dmax   = (/1.0d0, 1.0d0, 1.0d0/)

    inp%trn_testset      = 10.0d0
    inp%trn_steps        = 10
    inp%trn_method       = 'bfgs'
    inp%trn_methodName   = 'Limited Memory BFGS'
    inp%trn_maxenergy    = huge(1.0d0)
    inp%trn_nparams      = 0
    inp%do_save_energies = .false.

    inp%T            = 0.0d0
    inp%T_final      = 0.0d0

    inp%mc_relax_final = .false.
    inp%mc_ngroups     = 0

    inp%do_debug     = .false.

  end subroutine inp_defaults

  !====================================================================!
  !                                                                    !
  !                 parsers for different input files                  !
  !                                                                    !
  !====================================================================!

  !---------------------------- generate.x ----------------------------!

  function read_InpGenerate(file) result(inp)

    implicit none

    character(len=*), intent(in) :: file
    type(InputData)              :: inp

    integer                :: iline, stat
    integer                :: u

    call aeio_assert_file_exists(file)
    inp%file        = trim(file)
    inp%mode        = 'generate'
    call inp_defaults(inp)
    inp%outFileName = 'refdata.train'

    u = io_unit()
    open(u, file=trim(adjustl(file)), status='old', action='read')

    call inp_read_types_and_energies(inp, u, iline, stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no atom TYPES specified in file `", trim(file), "'"
       return
    end if

    call inp_read_setups(inp, u, iline, stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no basis function SETUPS specified in file `", &
                  trim(file), "'"
       return
    end if

    call inp_read_value(u, 'output', inp%outFileName)
    call inp_read_value(u, 'timing', inp%do_timing)
    call inp_read_value(u, 'debug',  inp%do_debug)
    call inp_read_files(inp, u, iline, countonly=.true.)
    call inp_read_verbosity(inp, u)

    close(u)

    inp%init = .true.

  end function read_InpGenerate

  !----------------------------- train.x ------------------------------!

  function read_InpTrain(file) result(inp)

    implicit none

    character(len=*), intent(in) :: file
    type(InputData)              :: inp

    integer                :: iline
    integer                :: u

    call aeio_assert_file_exists(file)
    inp%file        = trim(file)
    inp%mode        = 'train'
    call inp_defaults(inp)
    inp%outFileName = ''

    u = io_unit()
    open(u, file=trim(adjustl(file)), status='old', action='read')

    call inp_read_method(inp, u, iline)

    call inp_read_value(u, 'trainingset',   inp%trn_file)
    call inp_read_value(u, 'testpercent',   inp%trn_testset)
    call inp_read_value(u, 'iterations',    inp%trn_steps)
    call inp_read_value(u, 'maxenergy',     inp%trn_maxenergy)
    call inp_read_value(u, 'timing',        inp%do_timing)
    call inp_read_value(u, 'debug',         inp%do_debug)
    call inp_read_value(u, 'save_energies', inp%do_save_energies)
    call inp_read_verbosity(inp, u)

    close(u)

    inp%init = .true.

  end function read_InpTrain

  !---------------------------- predict.x -----------------------------!

  function read_InpPredict(file) result(inp)

    implicit none

    character(len=*), intent(in) :: file
    type(InputData)              :: inp

    integer                :: iline, stat
    integer                :: u

    call aeio_assert_file_exists(file)
    inp%file = trim(file)
    inp%mode = 'predict'
    call inp_defaults(inp)

    u = io_unit()
    open(u, file=trim(adjustl(file)), status='old', action='read')

    call inp_read_types(inp, u, iline, stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no atom TYPES specified in file `", trim(file), "'"
       return
    end if

    call inp_read_networks(inp, u, iline, stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no NETWORKS specified in file `", trim(file), "'"
       return
    end if

    call inp_read_value(u, 'forces', inp%do_forces)
    call inp_read_value(u, 'timing', inp%do_timing)
    call inp_read_value(u, 'debug',  inp%do_debug)
    call inp_read_relax(inp, u, iline)
    call inp_read_files(inp, u, iline)
    call inp_read_verbosity(inp, u)

    close(u)

    inp%init = .true.

  end function read_InpPredict

  !------------------------------- mc.x -------------------------------!

  function read_InpMC(file) result(inp)

    implicit none

    character(len=*), intent(in) :: file
    type(InputData)              :: inp

    integer                :: iline, stat
    integer                :: u
    integer                :: itype

    call aeio_assert_file_exists(file)
    inp%file = trim(file)
    inp%mode = 'mc'
    call inp_defaults(inp)

    u = io_unit()
    open(u, file=trim(adjustl(file)), status='old', action='read')

    call inp_read_types(inp, u, iline, stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no atom TYPES specified in file `", trim(file), "'"
       return
    end if

    call inp_read_networks(inp, u, iline, stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no NETWORKS specified in file `", trim(file), "'"
       return
    end if

    allocate(inp%strucFile(1))
    inp%nStrucs = 1
    call inp_read_value(u, 'structure', inp%strucFile(1), stat)
    if (stat == S_NOT) then
       write(0,*) "Error: no input STRUCTURE specified in file `", trim(file), "'"
       return
    end if

    call inp_read_potentials(inp, u, iline, stat)
    if (stat == S_NOT) then
       ! no chemical potentials in input file --> set all to zero
       allocate(inp%mu(inp%nTypes), inp%activeType(inp%nTypes))
       do itype = 1, inp%nTypes
          inp%mu(itype) = 0.0d0
          inp%activeType(:) = 1
       end do
    end if

    ! if no type groups are specified, all types are in the same group
    call inp_read_mc_groups(inp, u, iline, stat)

    call inp_read_value(u, 'timing',      inp%do_timing)
    call inp_read_value(u, 'steps',       inp%nSteps)
    call inp_read_value(u, 'mctrials',    inp%nSweeps)
    call inp_read_value(u, 'ensemble',    inp%ensemble)
    call inp_read_value(u, 'debug',       inp%do_debug)
    call inp_read_value(u, 'relax_final', inp%mc_relax_final)
    call inp_read_temperature(inp, u, iline, stat)
    call inp_read_relax(inp, u, iline)
    call inp_read_files(inp, u, iline)
    call inp_read_verbosity(inp, u)

    inp%ensemble = io_lower(inp%ensemble)

    if (inp%mc_relax_final) inp%do_relax = .false.

    close(u)

    inp%init = .true.

  end function read_InpMC

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

  subroutine del_InputData(inp)

    implicit none

    type(InputData), intent(inout) :: inp

    if (.not. inp%init) return

    if (allocated(inp%typeName))        deallocate(inp%typeName)
    if (allocated(inp%atomicEnergy))    deallocate(inp%atomicEnergy)
    if (allocated(inp%netFile))         deallocate(inp%netFile)
    if (allocated(inp%netArch))         deallocate(inp%netArch)
    if (allocated(inp%strucFile))       deallocate(inp%strucFile)
    if (allocated(inp%trn_param))       deallocate(inp%trn_param)
    if (allocated(inp%setupFile))       deallocate(inp%setupFile)
    if (allocated(inp%mu))              deallocate(inp%mu)
    if (allocated(inp%activeType))      deallocate(inp%activeType)
    if (allocated(inp%mc_group))        deallocate(inp%mc_group)
    if (allocated(inp%mc_ntypes_group)) deallocate(inp%mc_ntypes_group)
    if (allocated(inp%mc_group_type))   deallocate(inp%mc_group_type)

    inp%init = .false.

  end subroutine del_InputData

  !--------------------------------------------------------------------!
  !                         input data summary                         !
  !--------------------------------------------------------------------!

  subroutine inp_print_info(inp)

    implicit none

    type(InputData) :: inp
    integer         :: itype

    if (.not. inp%init) return

    call aeio_header("Input Data Summary")
    write(*,*)

    if (inp%mode == 'mc') then
       write(*,*) 'Monte-Carlo Parameters'
       write(*,*) '----------------------'
       write(*,*)
       write(*,*) 'Temperature : ', trim(io_adjustl(inp%T)), ' K'
       write(*,*) 'MC steps    : ', trim(io_adjustl(inp%nSteps))
       write(*,*)
       write(*,*) 'Chemical Potentials:'
       write(*,*)
       do itype = 1, inp%nTypes
          write(*,'(1x,4x,A2,2x,F10.6)') inp%typeName(itype), inp%mu(itype)
       end do
       write(*,*)
    end if

  end subroutine inp_print_info

  !--------------------------------------------------------------------!
  !     simply read value(s) for specific keyword (implementation)     !
  !--------------------------------------------------------------------!

  subroutine inp_read_value_c1(unit, keyword, dest, stat)

    implicit none

    integer,           intent(in)    :: unit
    character(len=*),  intent(in)    :: keyword
    character(len=*),  intent(inout) :: dest
    integer, optional, intent(out)   :: stat

    integer                :: iline, ipos
    character(len=LINELEN) :: line
    character(len=10)      :: str

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, keyword, iline, line=line)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       ipos = 1
       call io_readnext(line, ipos, str)
       call io_readnext(line, ipos, dest)
    end if

  end subroutine inp_read_value_c1

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

  subroutine inp_read_value_i1(unit, keyword, dest, stat)

    implicit none

    integer,           intent(in)    :: unit
    character(len=*),  intent(in)    :: keyword
    integer,           intent(inout) :: dest
    integer, optional, intent(out)   :: stat

    integer                :: iline, ipos
    character(len=LINELEN) :: line
    character(len=10)      :: str

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, keyword, iline, line=line)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       ipos = 1
       call io_readnext(line, ipos, str)
       call io_readnext(line, ipos, dest)
    end if

  end subroutine inp_read_value_i1

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

  subroutine inp_read_value_d1(unit, keyword, dest, stat)

    implicit none

    integer,           intent(in)    :: unit
    character(len=*),  intent(in)    :: keyword
    double precision,  intent(inout) :: dest
    integer, optional, intent(out)   :: stat

    integer                :: iline, ipos
    character(len=LINELEN) :: line
    character(len=10)      :: str

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, keyword, iline, line=line)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       ipos = 1
       call io_readnext(line, ipos, str)
       call io_readnext(line, ipos, dest)
    end if

  end subroutine inp_read_value_d1

  !--------------------------------------------------------------------!
  ! Flags can be specified in three different ways in the input file   !
  !                                                                    !
  !   (1) KEYWORD                                                      !
  !   (2) KEYWORD .true.                                               !
  !   (3) KEYWORD .false.                                              !
  !                                                                    !
  ! Both, (1) and (2) will result in the return value '.true.', while  !
  ! (3) will result in '.false.'.  If KEYWORD is not found, the input  !
  ! value of flag will be returned.                                    !
  !--------------------------------------------------------------------!

  subroutine inp_read_value_l1(unit, keyword, flag, stat)

    implicit none

    integer,           intent(in)    :: unit
    character(len=*),  intent(in)    :: keyword
    logical,           intent(inout) :: flag
    integer, optional, intent(out)   :: stat

    integer                :: iline, ipos
    character(len=LINELEN) :: line
    character(len=10)      :: str

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, keyword, iline, line=line)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       ipos = 1
       call io_readnext(line, ipos, str)
       call io_readnext(line, ipos, str)
       if (ipos > 0) then
          read(str,*) flag
       else
          flag = .true.
       end if
    end if

  end subroutine inp_read_value_l1

  !--------------------------------------------------------------------!
  !                  procedures for specific keywords                  !
  !--------------------------------------------------------------------!

  subroutine inp_read_files(inp, unit, iline, stat, countonly)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat
    logical, optional, intent(in)    :: countonly

    logical :: do_strucs
    integer :: i

    if (present(stat)) stat = S_OK
    if (present(countonly)) then
       do_strucs = (.not. countonly)
    else
       do_strucs = .true.
    end if

    call inp_find_keyword(unit, 'files', iline)
    if (iline > 0) then
       call aeio_readline(unit, iline, inp%nStrucs)
       if (do_strucs) then
          allocate(inp%strucFile(inp%nStrucs))
          do i = 1, inp%nStrucs
             call aeio_readline(unit, iline, inp%strucFile(i))
          end do
       end if
    else
       if (present(stat)) stat = S_NOT
    end if

  end subroutine inp_read_files

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

  subroutine inp_read_method(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    character(LINELEN) :: line

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'method', iline)
    if (iline > 0) then
       call aeio_readline(unit, iline, line)
       read(line,*) inp%trn_method
       select case(trim(io_lower(inp%trn_method)))
       case('bfgs')
          inp%trn_methodName = 'Limited Memory BFGS'
       case('ekf')
          inp%trn_methodName = 'Extended Kalman Filter'
          inp%trn_nparams = 6
          allocate(inp%trn_param(inp%trn_nparams))
          inp%trn_param(1) = 0.999d0
          call io_readval(line, 'lambda', inp%trn_param(1))
          inp%trn_param(2) = 0.97d0
          call io_readval(line, 'lambda0', inp%trn_param(2))
          inp%trn_param(3) = 10.0d0
          call io_readval(line, 'state', inp%trn_param(3))
          inp%trn_param(4) = 0.0d0
          call io_readval(line, 'pnoise', inp%trn_param(4))
          inp%trn_param(5) = 0.6d0
          call io_readval(line, 'adaptive', inp%trn_param(5))
          inp%trn_param(6) = 100.0
          call io_readval(line, 'wgroup', inp%trn_param(6))
       case('lm')
          inp%trn_methodName = 'Levenberg-Marquardt'
          inp%trn_nparams = 5
          allocate(inp%trn_param(inp%trn_nparams))
          inp%trn_param(1) = 1000.0d0
          call io_readval(line, 'batchsize', inp%trn_param(1))
          inp%trn_param(2) = 0.1d0
          call io_readval(line, 'learnrate', inp%trn_param(2))
          inp%trn_param(3) = 1.0d0
          call io_readval(line, 'iter', inp%trn_param(3))
          inp%trn_param(4) = 1.0d-3
          call io_readval(line, 'conv', inp%trn_param(4))
          inp%trn_param(5) = 10.0d0
          call io_readval(line, 'adjust', inp%trn_param(5))
       case('online_sd','online_gd')
          inp%trn_methodName = 'Gardient Descent (online)'
          inp%trn_nparams = 2
          allocate(inp%trn_param(2))
          call io_readval(line, 'alpha', inp%trn_param(1))
          call io_readval(line, 'gamma', inp%trn_param(2))
       end select
    else
       if (present(stat)) stat = S_NOT
    end if

  end subroutine inp_read_method

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

  subroutine inp_read_networks(inp, unit, iline, stat, readarch, file)

    implicit none

    type(InputData),            intent(inout) :: inp
    integer,          optional, intent(in)    :: unit
    integer,          optional, intent(out)   :: iline
    integer,          optional, intent(out)   :: stat
    logical,          optional, intent(in)    :: readarch
    character(len=*), optional, intent(in)    :: file

    character(len=LINELEN) :: line
    integer                :: itype
    character(len=32)      :: name
    integer                :: nnets, ipos
    integer                :: u, i, il
    logical                :: do_arch

    if (present(stat)) stat = S_OK

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

    if (present(readarch)) then
       do_arch = readarch
    else
       do_arch = .false.
    end if

    call inp_find_keyword(u, 'networks', il)
    if (present(iline)) iline = il
    if (il == 0) then
       if (present(stat)) stat = S_NOT
    else if (inp%nTypes < 1) then
       write(0,*) "Error: atom types have to be defined before networks."
       if (present(stat)) stat = S_ERROR
    else
       if (do_arch) then
          allocate(inp%netFile(inp%nTypes), inp%netArch(inp%nTypes))
       else
          allocate(inp%netFile(inp%nTypes))
       end if
       nnets = inp%nTypes
       do i = 1, nnets
          call aeio_readline(u, il, line)
          ipos = 1
          call io_readnext(line, ipos, name)
          itype = geo_itype_of_name(name, inp%typeName)
          call io_readnext(line, ipos, inp%netFile(itype))
          if (do_arch) inp%netArch(itype) = line(ipos:len_trim(line))
       end do ! inet
    end if

    if (present(file)) close(u)

  end subroutine inp_read_networks

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

  subroutine inp_read_types(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    integer :: i

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'types', iline)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       call aeio_readline(unit, iline, inp%nTypes)
       allocate(inp%typeName(inp%nTypes))
       do i = 1, inp%nTypes
          call aeio_readline(unit, iline, inp%typeName(i))
       end do
    end if

  end subroutine inp_read_types

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

  subroutine inp_read_types_and_energies(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    integer :: i
    character(len=LINELEN) :: line

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'types', iline)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       call aeio_readline(unit, iline, inp%nTypes)
       allocate(inp%typeName(inp%nTypes), &
                inp%atomicEnergy(inp%nTypes))
       do i = 1, inp%nTypes
          call aeio_readline(unit, iline, line)
          read(line,*) inp%typeName(i), inp%atomicEnergy(i)
       end do
    end if

  end subroutine inp_read_types_and_energies

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

  subroutine inp_read_potentials(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    integer                :: i, itype, ipos
    character(len=LINELEN) :: line, kwd
    character(len=10)      :: name

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'potentials', iline)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       allocate(inp%mu(inp%nTypes), inp%activeType(inp%nTypes))
       do i = 1, inp%nTypes
          call aeio_readline(unit, iline, line)
          ipos = 1
          call io_readnext(line, ipos, name)
          itype = geo_itype_of_name(name, inp%typeName)
          call io_readnext(line, ipos, kwd)
          if (trim(io_lower(kwd)) == 'fix') then
             inp%activeType(itype) = 0
             inp%mu(itype) = 0.0d0
          else
             inp%activeType(itype) = 1
             read(kwd, *) inp%mu(itype)
          end if
       end do
    end if

  end subroutine inp_read_potentials

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

  subroutine inp_read_mc_groups(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    integer                :: ipos, ig
    integer                :: it, itype
    character(len=LINELEN) :: line
    character(len=10)      :: name

    if (present(stat)) stat = S_OK

    allocate(inp%mc_group(inp%nTypes))

    call inp_find_keyword(unit, 'groups', iline)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
       ! all types are in the same MC group
       inp%mc_group(:) = 1
       inp%mc_ngroups  = 1
       allocate(inp%mc_ntypes_group(inp%mc_ngroups), &
                inp%mc_group_type(inp%mc_ngroups,inp%nTypes))
       inp%mc_ntypes_group(1) = inp%nTypes
       do itype = 1, inp%nTypes
          inp%mc_group_type(1,itype) = itype
       end do
    else
       call aeio_readline(unit, iline, line)
       read(line, *) inp%mc_ngroups
       allocate(inp%mc_ntypes_group(inp%mc_ngroups), &
                inp%mc_group_type(inp%mc_ngroups,inp%nTypes))
       inp%mc_group(:) = 0
       do ig = 1, inp%mc_ngroups
          call aeio_readline(unit, iline, line)
          ipos = 1
          ! first entry on line is the number of typenames to follow
          call io_readnext(line, ipos, name)
          read(name, *) inp%mc_ntypes_group(ig)
          do it = 1, inp%mc_ntypes_group(ig)
             call io_readnext(line, ipos, name)
             itype = geo_itype_of_name(name, inp%typeName)
             inp%mc_group_type(ig,it) = itype
             if (inp%mc_group(itype) /= 0) then
                write(0,*) "Error: Species ", &
                           trim(adjustl(inp%typeName(itype))), &
                           " can not be in multiple MC groups."
                stop
             end if
             inp%mc_group(itype) = ig
          end do
          ! single species groups musty be inactive
          if (inp%mc_ntypes_group(ig) == 1) then
             itype = inp%mc_group_type(ig,1)
             if (inp%activeType(itype) /= 0) then
                write(0,*) 'Warning: setting status of species ', &
                     trim(adjustl(inp%typeName(itype))), " to `inactive'."
                inp%activeType(itype) = 0
                inp%mu(itype) = 0.0d0
             end if
          end if
       end do
    end if

  end subroutine inp_read_mc_groups

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

  subroutine inp_read_relax(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    character(len=LINELEN) :: line

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'relax', iline)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       inp%do_relax  = .true.
       inp%do_forces = .true.
       call aeio_readline(unit, iline, line)
       call io_readval(line, 'method', inp%relax_method)
       call io_readval(line, 'F_conv', inp%relax_F_conv)
       call io_readval(line, 'E_conv', inp%relax_E_conv)
       call io_readval(line, 'steps',  inp%relax_steps)
       call io_readval(line, 'dmax',   inp%relax_dmax, 3)
    end if

  end subroutine inp_read_relax

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

  subroutine inp_read_setups(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    character(len=LINELEN) :: line
    integer                :: itype1
    character(len=32)      :: name1
    integer                :: nsetups, ipos
    integer                :: i

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'setups', iline)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else if (inp%nTypes < 1) then
       write(0,*) "Error: atom types have to be defined before networks."
       if (present(stat)) stat = S_ERROR
    else
       allocate(inp%setupFile(inp%nTypes))
       nsetups = inp%nTypes
       do i = 1, nsetups
          call aeio_readline(unit, iline, line)
          ipos = 1
          call io_readnext(line, ipos, name1)
          itype1 = geo_itype_of_name(name1, inp%typeName)
          call io_readnext(line, ipos, inp%setupFile(itype1))
       end do ! inet
    end if

  end subroutine inp_read_setups

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

  subroutine inp_read_temperature(inp, unit, iline, stat)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit
    integer,           intent(out)   :: iline
    integer, optional, intent(out)   :: stat

    character(len=LINELEN) :: line, str
    integer                :: ipos

    if (present(stat)) stat = S_OK

    call inp_find_keyword(unit, 'temperature', iline, line=line)
    if (iline == 0) then
       if (present(stat)) stat = S_NOT
    else
       ipos = 1
       call io_readnext(line, ipos, str)
       call io_readnext(line, ipos, inp%T)
       call io_readnext(line, ipos, str)
       if (ipos > 0) then
          read(str,*) inp%T_final
       else
          inp%T_final = inp%T
       end if
    end if

  end subroutine inp_read_temperature

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

  subroutine inp_read_verbosity(inp, unit)

    implicit none

    type(InputData),   intent(inout) :: inp
    integer,           intent(in)    :: unit

    character(len=LINELEN) :: str

    str = 'normal'
    call inp_read_value(unit, 'verbosity', str)
    select case(trim(adjustl(io_lower(str))))
    case('low')
       inp%verbosity = 0
    case('normal')
       inp%verbosity = 1
    case('high')
       inp%verbosity = 2
    end select

  end subroutine inp_read_verbosity

  !--------------------------------------------------------------------!
  !                         read until keyword                         !
  !--------------------------------------------------------------------!

  subroutine inp_find_keyword(unit, keyword, iline, line)

    implicit none

    integer,                          intent(in)  :: unit
    character(len=*),                 intent(in)  :: keyword
    integer,                          intent(out) :: iline
    character(len=LINELEN), optional, intent(out) :: line

    integer                :: stat
    character(len=LINELEN) :: kwd, ln

    rewind(unit)
    kwd = ''
    iline = 0
    skip : do
       call aeio_readline(unit, iline, ln, stat)
       if (stat /= 0) then
          ! end of file and keyword not found
          iline = 0
          exit skip
       end if
       iline = iline + 1
       read(ln, *) kwd
       if (trim(io_lower(kwd))==trim(io_lower(keyword))) then
          ! keyword found
          if (present(line)) line = ln
          exit skip
       end if
    end do skip

  end subroutine inp_find_keyword

end module input
ViewGit