! **********************************************************************
! ** Nudged elastic band method **
! **********************************************************************
!!****h* coords/neb
!!
!! NAME
!! neb
!!
!! FUNCTION
!! Nudged Elastic band method
!!
!!
!! DATA
!! $Date: 2011-01-20 14:43:17 +0100 (Thu, 20. Jan 2011) $
!! $Rev: 465 $
!! $Author: twk $
!! $URL: http://ccpforge.cse.rl.ac.uk/svn/dl-find/branches/release_chemsh3.4/dlf_neb.f90 $
!! $Id: dlf_neb.f90 465 2011-01-20 13:43:17Z twk $
!!
!! COPYRIGHT
!!
!! Copyright 2007 Johannes Kaestner (kaestner@theochem.uni-stuttgart.de),
!! Tom Keal (thomas.keal@stfc.ac.uk)
!!
!! This file is part of DL-FIND.
!!
!! DL-FIND 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 3 of the
!! License, or (at your option) any later version.
!!
!! DL-FIND 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 DL-FIND. If not, see
!! .
!!
!! SYNOPSIS
module dlf_neb
use dlf_parameter_module, only: rk
type neb_type
integer :: nimage ! total number of images
integer :: iimage ! current image
integer :: varperimage ! number of variables to be
! optimised per image
integer :: step ! number of iterations done
integer :: maximage ! number of the image closest to
! the climbing image
integer :: mode ! 0: endpoints free,
! 1: endpoints move perp. to tau
! 2: endpoints frozen
real(rk) :: k ! force constant
logical :: tclimb ! a climbing images is used
real(rk) :: tolfreeze ! gradient tolerance for freezing
! images
real(rk) :: tolclimb ! gradient tolerance for starting
! climbing image process
logical :: allfrozen ! all images but climbing image
! are frozen
real(rk),allocatable :: ene(:) ! (nimage) energies of the images
integer ,allocatable :: cstart(:) ! (nimage) start position of image
! coordinate array
integer ,allocatable :: cend(:) ! (nimage) end position of image
! coordinate array
! needed for freezing windows
logical :: tfreeze ! decide if windows to be frozen
logical ,allocatable :: frozen(:) ! (nimage) image not recalculated
! due to small gradient
real(rk),allocatable :: gradt(:) ! (nimage*varperimage) true
! internal gradient
real(rk),allocatable :: tau(:) ! (nimage*varperimage) Tangent
! vectors
! Cartesian coordinates of each image are required for HDLCs
real(rk),allocatable :: xcoords(:,:) ! (3*glob%nat,nimage) Cartesian
! coordinates of each image
logical :: optcart ! use internals only for initial
! path, then Cartesians
end type neb_type
integer,parameter :: unitp=1000 ! base unit for path xyz files
logical,parameter :: xyzall=.true. ! xyz files of all atoms or only active atoms?
integer,parameter :: maxxyzfile=300 ! larger than 9999 will require modification of file names
type(neb_type),save :: neb
! temp: using L-BFGS with a memory of only 2 converges reasonably well with the string method
logical, parameter:: string=.false.
end module dlf_neb
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_init
!!
!! FUNCTION
!!
!! Initialise NEB calculations:
!! * allocate arrays concerning internal coordinates
!! * set default values for the calculation
!! * define the starting chain (call dlf_neb_definepath for that)
!!
!! SYNOPSIS
subroutine dlf_neb_init(nimage,icoord)
!! SOURCE
use dlf_parameter_module, only: rk,ik
use dlf_global, only: glob,stderr,stdout,printf,printl
use dlf_neb, only: neb,unitp,xyzall,maxxyzfile
use dlf_allocate, only: allocate,deallocate
implicit none
integer, intent(in) :: nimage
integer, intent(in) :: icoord ! choice of NEB details
integer :: iimage,ivar,iat
real(rk) :: tolg
character(20) :: filename
real(rk), allocatable :: tmpcoords(:,:)
integer :: useimage(nimage)
integer :: iarr3(3),nframe,iframe
! **********************************************************************
neb%nimage=nimage
neb%iimage=1
neb%k=glob%nebk
neb%step=0
neb%tfreeze=.true.
! check nimage
if(nimage<3) call dlf_fail("More than 2 images have to be used in NEB")
! check coords2
if(.not.glob%tcoords2) then
call dlf_fail("Second set of coordinates required for NEB!")
end if
iarr3=shape(glob%xcoords2)
nframe=iarr3(3)
if(nframe>nimage-2) then
nframe=nimage-2
write(stdout,'("Warning: too many frames input in NEB. Only the &
&first",i4," frames are used")') nframe
end if
! set mode
if( (icoord<100 .or. icoord>199))&
call dlf_fail("Wrong icoord in NEB")
neb%mode=(icoord-100)/10
if(neb%mode>5) call dlf_fail("Mode in NEB has to be 0-5")
neb%optcart=.false.
if(neb%mode>2) then
neb%optcart=.true.
neb%mode=neb%mode-3
end if
ivar=mod(icoord,10)
select case (ivar)
! Cartesian coordinates
case (0)
! define the number of internal variables (covering all images)
call dlf_direct_get_nivar(neb%varperimage)
! calculate iweights
call allocate( glob%iweight,neb%varperimage*nimage)
! first image
ivar=1
if(glob%tatoms) then
do iat=1,glob%nat
if(glob%spec(iat)>=0) then
glob%iweight(ivar:ivar+2)=glob%weight(iat)
ivar=ivar+3
else if(glob%spec(iat)==-1) then
else if(glob%spec(iat)>=-4) then
glob%iweight(ivar:ivar+1)=glob%weight(iat)
ivar=ivar+2
else
glob%iweight(ivar)=glob%weight(iat)
ivar=ivar+1
end if
end do
if(ivar-1/=neb%varperimage) then
call dlf_fail("Error in Cartesian iweight calculation in NEB")
end if
! copy weights to all other images
do iimage=2,nimage
ivar=neb%varperimage*(iimage-1)+1
glob%iweight(ivar:ivar+neb%varperimage-1)=glob%iweight(1:neb%varperimage)
end do
else
glob%iweight(:)=1.D0
end if
! HDLC
case(1:4)
call dlf_hdlc_init(glob%nat,glob%spec,mod(glob%icoord,10),glob%ncons, &
glob%icons,glob%nconn,glob%iconn)
! create hdlc with start and endpoint as coordinates
call allocate(tmpcoords,3,(1+nframe)*glob%nat)
tmpcoords(:,1:glob%nat)=glob%xcoords(:,:)
tmpcoords(:,glob%nat+1:(1+nframe)*glob%nat)= &
reshape(glob%xcoords2(:,:,:),(/3,nframe*glob%nat/))
call dlf_hdlc_create(glob%nat,glob%spec,glob%znuc,1+nframe, &
tmpcoords,glob%weight,glob%mass)
call deallocate(tmpcoords)
call dlf_hdlc_get_nivar(neb%varperimage)
! calculate iweights
call allocate( glob%iweight,neb%varperimage*nimage)
! first image
call dlf_hdlc_getweight(glob%nat,neb%varperimage,glob%weight, &
glob%iweight(1:neb%varperimage))
! copy weights to all other images
do iimage=2,nimage
ivar=neb%varperimage*(iimage-1)+1
glob%iweight(ivar:ivar+neb%varperimage-1)=glob%iweight(1:neb%varperimage)
end do
case default
write(stderr,'("Coordinate type ",i2," not supported in NEB")') icoord
end select
glob%nivar=neb%varperimage*nimage
call allocate( glob%icoords,glob%nivar)
call allocate( glob%igradient,glob%nivar)
call allocate( glob%step,glob%nivar)
call allocate( neb%ene,nimage)
call allocate( neb%cstart,nimage)
call allocate( neb%cend,nimage)
! strictly only necessary for HDLCs
if(glob%tatoms) then
call allocate( neb%xcoords,3*glob%nat,nimage)
else
call allocate( neb%xcoords,glob%nvar,nimage)
end if
call allocate(neb%frozen,nimage)
neb%frozen(:)=.false.
call allocate(neb%gradt,glob%nivar)
call allocate(neb%tau,glob%nivar)
! set the positions of the images in the internal arrays
do iimage=1,nimage
neb%cstart(iimage)=(iimage-1)*neb%varperimage+1
neb%cend(iimage)=neb%cstart(iimage)+neb%varperimage-1
end do
! ====================================================================
! Define the starting path: linear transit guess
! ====================================================================
! align the input coordinates to closest overlap
useimage(:)=0
useimage(1)=1
if(glob%tatoms) then
neb%xcoords(:,1)=reshape(glob%xcoords(:,:),(/3*glob%nat/))
do iframe=1,min(nframe,nimage-1)
if(glob%nat>1) call dlf_cartesian_align(glob%nat,glob%xcoords,glob%xcoords2(:,:,iframe))
neb%xcoords(:,1+iframe)=reshape(glob%xcoords2(:,:,iframe),(/3*glob%nat/))
useimage(1+iframe)=1
end do
! future climbing image - coords for first image:
neb%xcoords(:,nimage)=reshape(glob%xcoords(:,:),(/3*glob%nat/))
! set i- and x-coordinates of all images
call dlf_neb_definepath(nimage,useimage)
! future climbing image again:
neb%xcoords(:,nimage)=reshape(glob%xcoords(:,:),(/3*glob%nat/))
else
neb%xcoords(:,1)=reshape(glob%xcoords(:,:),(/glob%nvar/))
do iframe=1,nframe
neb%xcoords(:,1+iframe)=reshape(glob%xcoords2(:,:,iframe),(/glob%nvar/))
useimage(1+iframe)=1
end do
neb%xcoords(:,nimage)=reshape(glob%xcoords(:,:),(/glob%nvar/))
! set i- and x-coordinates of all images
call dlf_neb_definepath(nimage,useimage)
neb%xcoords(:,nimage)=reshape(glob%xcoords(:,:),(/glob%nvar/))
end if
if(neb%optcart) then
! use the path guessed in internal coordinates, but optimise in
! Cartesians
glob%icoord=glob%icoord-mod(glob%icoord,10)-30
call dlf_direct_get_nivar(ivar)
call dlf_hdlc_destroy
write(stdout,"('Initial path set. Switching to Cartesian coordinates.')")
if(ivar/=neb%varperimage) then
neb%varperimage=ivar
glob%nivar=neb%varperimage*nimage
call deallocate( glob%icoords)
call deallocate( glob%igradient)
call deallocate( glob%step)
call deallocate( glob%iweight)
call allocate( glob%icoords,glob%nivar)
call allocate( glob%igradient,glob%nivar)
call allocate( glob%step,glob%nivar)
call allocate( glob%iweight,glob%nivar)
if(neb%tfreeze) then
call deallocate(neb%gradt)
call allocate(neb%gradt,glob%nivar)
call deallocate(neb%tau)
call allocate(neb%tau,glob%nivar)
end if
do iimage=1,nimage
neb%cstart(iimage)=(iimage-1)*neb%varperimage+1
neb%cend(iimage)=neb%cstart(iimage)+neb%varperimage-1
end do
end if ! (ivar/=neb%varperimage)
! calculate iweights
! first image
ivar=1
do iat=1,glob%nat
if(glob%spec(iat)>=0) then
glob%iweight(ivar:ivar+2)=glob%weight(iat)
ivar=ivar+3
else if(glob%spec(iat)==-1) then
else if(glob%spec(iat)>=-4) then
glob%iweight(ivar:ivar+1)=glob%weight(iat)
ivar=ivar+2
else
glob%iweight(ivar)=glob%weight(iat)
ivar=ivar+1
end if
end do
if(ivar-1/=neb%varperimage) then
call dlf_fail("Error in Cartesian iweight calculation in NEB")
end if
! copy weights to all other images
do iimage=2,nimage
ivar=neb%varperimage*(iimage-1)+1
glob%iweight(ivar:ivar+neb%varperimage-1)=glob%iweight(1:neb%varperimage)
end do
end if
! set x-coordinates of the first image
! nothing to do ...
neb%gradt(:)=0.D0
glob%igradient(:)=0.D0
! open units for writing the path xyz files
if(printf>=4) then
do iimage=1,nimage
if(iimage>maxxyzfile) exit
if(iimage<10) then
write(filename,'("000",i1)') iimage
else if(iimage<100) then
write(filename,'("00",i2)') iimage
else if(iimage<1000) then
write(filename,'("0",i3)') iimage
else
write(filename,'(i4)') iimage
end if
filename="neb_"//trim(adjustl(filename))//".xyz"
if (glob%iam == 0) open(unit=unitp+iimage,file=filename)
end do
! write initial xyz coordinates
if (glob%iam == 0) then
do iimage=1,nimage
if(iimage>maxxyzfile) exit
if(xyzall) then
call write_xyz(unitp+iimage,glob%nat,glob%znuc,neb%xcoords(:,iimage))
else
call write_xyz_active(unitp+iimage,glob%nat,glob%znuc,glob%spec,neb%xcoords(:,iimage))
end if
end do
end if
end if
! initialise climbing image, freezing
call convergence_get("TOLG",tolg)
! set freezing criterion
neb%tolfreeze= glob%neb_freeze_test * tolg
neb%tolclimb= glob%neb_climb_test * tolg
neb%tclimb=.false.
neb%frozen(neb%nimage)=.true.
neb%allfrozen=.false.
! commented out so climbing image can be switched off altogether.
! TODO will need extra code to handle convergence/result in this case
! if(neb%tolclimb< neb%tolfreeze) neb%tolclimb=neb%tolfreeze
end subroutine dlf_neb_init
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_destroy
!!
!! FUNCTION
!!
!! deallocate arrays concerning internal coordinates
!!
!! SYNOPSIS
subroutine dlf_neb_destroy
!! SOURCE
use dlf_parameter_module, only: rk,ik
use dlf_global, only: glob,stderr,printf
use dlf_neb, only: neb,unitp,maxxyzfile
use dlf_allocate, only: deallocate
implicit none
integer :: iimage
! **********************************************************************
! deallocate arrays
call deallocate( glob%icoords)
call deallocate( glob%igradient)
call deallocate( glob%step)
call deallocate( glob%iweight)
call deallocate(neb%ene)
call deallocate(neb%cstart)
call deallocate(neb%cend)
call deallocate(neb%xcoords)
call deallocate(neb%frozen)
call deallocate(neb%gradt)
call deallocate(neb%tau)
select case (mod(glob%icoord,10))
! HDLC
case(1:4)
call dlf_hdlc_destroy()
end select
! close units for writing the path xyz files
if(printf>=4 .and. glob%iam == 0) then
do iimage=1,neb%nimage
if(iimage>maxxyzfile) exit
close(unit=unitp+iimage)
end do
end if
end subroutine dlf_neb_destroy
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_xtoi
!!
!! FUNCTION
!!
!! transform Cartesian coordinates into the NEB array
!!
!! This does not only SET the internal (NEB) coordinates, but also sets
!! the xcoords of the next image from neb%xcoords:
!! * First image : i-coordinates and gradient are set to x-coordinates
!! and gradient. x-coordinates are set to second image
!! * Nth image : i-coordinates and gradient are set to x-coordinates
!! and gradient. x-coordinates are set to N+1's image
!! * Last image : i-coordinates and gradient are set to x-coordinates
!! and gradient. Spring forces are added (calls
!! dlf_neb_improved_tangent_neb for that). trerun_energy=false
!!
!! SYNOPSIS
subroutine dlf_neb_xtoi(trerun_energy,external_iimage)
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stderr,stdout,printf
use dlf_neb, only: neb,unitp,xyzall,maxxyzfile
implicit none
logical, intent(out) :: trerun_energy
integer, intent(out) :: external_iimage ! Which image is the next to calculate
integer :: status ! For parallel NEB tested here
integer :: cstart,cend,jimage
! **********************************************************************
! start and endpoint of current image in array icoords and igradient
cstart=neb%cstart(neb%iimage)
cend=neb%cend(neb%iimage)
! Parallel NEB: X->I transformation is potentially costly so parallelised
if (glob%dotask) then
call dlf_direct_xtoi(glob%nvar,neb%varperimage,glob%xcoords,glob%xgradient, &
glob%icoords(cstart:cend),glob%igradient(cstart:cend))
neb%ene(neb%iimage)=glob%energy
! store true gradient (to calculate the work even if this image is frozen)
neb%gradt(cstart:cend)=glob%igradient(cstart:cend)
else
! Explicitly set to zero to ensure that the allreduce at the end of the
! cycle gives the correct result
glob%icoords(cstart:cend) = 0.d0
glob%igradient(cstart:cend) = 0.d0
neb%ene(neb%iimage) = 0.d0
neb%gradt(cstart:cend) = 0.d0
end if
! write xyz file
if(printf>=4.and.glob%tatoms.and.neb%iimage<=maxxyzfile .and. glob%iam == 0) then
if(xyzall) then
call write_xyz(unitp+neb%iimage,glob%nat,glob%znuc,glob%xcoords(:,:))
else
call write_xyz_active(unitp+neb%iimage,glob%nat,glob%znuc,glob%spec,glob%xcoords(:,:))
end if
end if
if(neb%iimagex
if(printf>=4.and.glob%tatoms.and.neb%iimage<=maxxyzfile) then
glob%xcoords=reshape(neb%xcoords(:,jimage),(/3,glob%nat/))
if (glob%iam == 0) then
if(xyzall) then
call write_xyz(unitp+neb%iimage,glob%nat,glob%znuc,&
glob%xcoords(:,:))
else
call write_xyz_active(unitp+neb%iimage,glob%nat,glob%znuc,&
glob%spec,glob%xcoords(:,:))
end if
end if
end if
else
neb%iimage=jimage
exit
end if
end do
if(neb%iimage==neb%nimage.and.neb%frozen(neb%iimage)) trerun_energy=.false.
end if
if(trerun_energy) then
cstart=neb%cstart(neb%iimage)
cend=neb%cend(neb%iimage)
external_iimage=neb%iimage
if(glob%tatoms) then
glob%xcoords=reshape(neb%xcoords(:,neb%iimage),(/3,glob%nat/))
else
glob%xcoords=reshape(neb%xcoords(:,neb%iimage),(/1,glob%nvar/))
end if
!-------------
return
!-------------
end if
end if
! ====================================================================
! LAST IMAGE, ALL GRADIENTS ARE GATHERED
! ====================================================================
! Parallel NEB: check no gradient evaluations in other workgroups failed
! and make all coordinates and gradients available to all workgroups
if (glob%ntasks > 1) then
! If it has reached here all gradients have succeeded in this workgroup
status = 0
call dlf_tasks_int_sum(status, 1)
if (status > 0) then
call dlf_fail("Task-farmed gradient evaluations failed")
end if
if (glob%serial_cycle == 0) then
call dlf_tasks_real_sum(glob%icoords, glob%nivar)
call dlf_tasks_real_sum(glob%igradient, glob%nivar)
call dlf_tasks_real_sum(neb%ene, neb%nimage)
call dlf_tasks_real_sum(neb%gradt, glob%nivar)
end if
end if
! For convergence testing when there is no climbing image,
! set global energy to the energy of the highest image
glob%energy = neb%ene(1)
do jimage=2, neb%nimage-1
if (neb%ene(jimage) > glob%energy) glob%energy = neb%ene(jimage)
end do
call dlf_neb_improved_tangent_neb
trerun_energy=.false.
! this is not used, as no image is calculated until neb_itox is called
external_iimage=1
end subroutine dlf_neb_xtoi
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_improved_tangent_neb
!!
!! FUNCTION
!!
!! transform the forces according to the
!! IMPROVED-TANGENT NUDGED ELASTIC BAND by
!! G. Henkelman and H. Jonsson J. Chem. Phys. 113, 9978 (2000)
!! including a climbing image as an additional image once the maximum
!! perpendicular gradient is below tolclimb
!!
!! SYNOPSIS
subroutine dlf_neb_improved_tangent_neb
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stderr,stdout,pi,printl,printf
use dlf_neb, only: neb,unitp,xyzall,string
implicit none
integer :: cstart,cend
integer :: iimage,jimage,ivar
logical,parameter :: dbg=.false.
real(rk) :: emin,emax,svar,svar2
logical :: tclimbnext,tchk
! bookkeeping
character(20) :: str
real(rk) :: ftan(neb%nimage) ! tangential force
real(rk) :: fper(neb%nimage) ! perpendicular force
real(rk) :: path(neb%varperimage,neb%nimage) ! path(:,2)
! is the vector img2->img3
! including weights
real(rk) :: dist(neb%nimage) ! distance between images
real(rk) :: ang(neb%nimage) ! angles between image-connections
real(rk) :: ang13(neb%nimage)! angles between further image-connections
real(rk) :: dwork(neb%nimage)! Delta work (grad x path) from
! last to present image
real(rk) :: delta(neb%nimage)
logical :: tmass
integer :: iat
! **********************************************************************
neb%step=neb%step+1
if(neb%tfreeze) then
neb%frozen(1:neb%nimage-1)=.false.
end if
ftan(:)=0.D0
fper(:)=0.D0
ang(:)=0.D0
ang13(:)=0.D0
dwork(:)=0.D0
dist(:)=0.D0
path(:,:)=0.D0
! ====================================================================
! Calculate distances and angles
! ====================================================================
! path, dist, and work
do iimage=1,neb%nimage-1
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
if(iimage=1.D0) then
ang(iimage)=0.D0
else
ang(iimage)=acos(svar)
end if
if(iimage=1.D0) then
ang13(iimage)=0.D0
else
ang13(iimage)=acos(svar)
end if
end if
end do
! ==================================================================
! Calculate the tangent vector tau
! ==================================================================
if(1==2.and.string) then
call string_get_tau
else
do iimage=1,neb%nimage-1
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
if(iimage==1) then
! Tau+
neb%tau(cstart:cend)=path(:,iimage)
else if(iimage==neb%nimage-1) then
! Tau-
neb%tau(cstart:cend)=path(:,iimage-1)
else
if(neb%ene(iimage+1)>neb%ene(iimage) .and. &
neb%ene(iimage)>neb%ene(iimage-1)) then
! Tau+
neb%tau(cstart:cend)=path(:,iimage)
if(dbg) print*,"Image",iimage," Tau+"
else if(neb%ene(iimage+1)0) then
cstart=neb%cstart(neb%nimage)
cend=neb%cend(neb%nimage)
! find image that is closest to the climbing image
ivar=-1 ! in the end: number of closest image
jimage=-1 ! in the end: number of 2nd-closest image
emax=1.D90 ! in the end: distance CI -- closest
svar=1.D90 ! in the end: distance CI -- 2nd-closest
do iimage=1,neb%nimage-1
! should this be weighted ??
svar2=sum((glob%icoords(cstart:cend)-glob%icoords( &
neb%cstart(iimage):neb%cend(iimage)))**2)
if(svar2=2) then
write(stdout,'("Climbing image in cycle ",i7," is closest to &
&image ",i4," (next: ",i4,")")') &
neb%step,neb%maximage,jimage
end if
if(printl>=4) write(stdout,'("Distance to 2nd closest image is",f10.5)') sqrt(svar)
else
if(printl>=2) then
write(stdout,'("No climbing image used in cycle ",i7)') neb%step
end if
end if
! ====================================================================
! Calculate the Forces
! ====================================================================
tclimbnext=.true.
do iimage=2, neb%nimage-2
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
! make sure frozen images can be unfrozen if required
glob%igradient(cstart:cend)=neb%gradt(cstart:cend)
! ================================================================
! project out the parallel component of the true force
! ================================================================
! improve - blas
if(dbg) print*,"Image",iimage,"F_t||=",sum(glob%igradient(cstart:cend)*neb%tau(cstart:cend))
glob%igradient(cstart:cend)=glob%igradient(cstart:cend)- &
sum(glob%igradient(cstart:cend)*neb%tau(cstart:cend))*neb%tau(cstart:cend)
fper(iimage)=dsqrt(sum((glob%igradient(cstart:cend))**2)/dble(neb%varperimage))
if((.not.neb%tclimb).and.maxval(abs(glob%igradient(cstart:cend))) > &
neb%tolclimb) then
tclimbnext=.false.
end if
! ==================================================================
! decide on freezing
! ==================================================================
! this is now done on the unweighted gradient. Is this a good idea?
if(neb%tfreeze.and.maxval(abs(glob%igradient(cstart:cend)))<=neb%tolfreeze) then
neb%frozen(iimage)=.true.
glob%igradient(cstart:cend)=0.D0
else
! ================================================================
! add the parallel component of the spring force
! ================================================================
if(.not.string) then
glob%igradient(cstart:cend)=glob%igradient(cstart:cend)+ &
neb%k*(dist(iimage-1)-dist(iimage))*neb%tau(cstart:cend)
end if
end if
ftan(iimage)=neb%k*(dist(iimage-1)-dist(iimage))
if(string) ftan(iimage)=0.D0
end do
! ==================================================================
! string method: add step(s) for L-BFGS to learn
! ==================================================================
!delta=0.00001D0
!!$ call random_number(delta)
!!$ delta=(delta-0.5D0)*0.01D0
!!$ print*,"delta",delta
!!$ if(string.and.1==1) then
!!$ do iimage=2, neb%nimage-2
!!$ cstart=neb%cstart(iimage)
!!$ cend=neb%cend(iimage)
!!$ ! move this image
!!$ glob%icoords(cstart:cend)=glob%icoords(cstart:cend) +delta*neb%tau(cstart:cend)
!!$ glob%igradient(cstart:cend)=glob%igradient(cstart:cend)+delta*2.D0*neb%k*neb%tau(cstart:cend)
!!$ jimage=iimage-1
!!$ cstart=neb%cstart(jimage)
!!$ cend=neb%cend(jimage)
!!$ glob%igradient(cstart:cend)=glob%igradient(cstart:cend)-delta*neb%k*neb%tau(cstart:cend)
!!$ jimage=iimage+1
!!$ cstart=neb%cstart(jimage)
!!$ cend=neb%cend(jimage)
!!$ glob%igradient(cstart:cend)=glob%igradient(cstart:cend)-delta*neb%k*neb%tau(cstart:cend)
!!$ end do
!!$ call dlf_lbfgs_step(glob%icoord,glob%igradient,glob%step)
!!$ do iimage=2, neb%nimage-2
!!$ cstart=neb%cstart(iimage)
!!$ cend=neb%cend(iimage)
!!$ ! move this image back
!!$ glob%icoords(cstart:cend)=glob%icoords(cstart:cend) -delta*neb%tau(cstart:cend)
!!$ glob%igradient(cstart:cend)=glob%igradient(cstart:cend)-delta*2.D0*neb%k*neb%tau(cstart:cend)
!!$ jimage=iimage-1
!!$ cstart=neb%cstart(jimage)
!!$ cend=neb%cend(jimage)
!!$ glob%igradient(cstart:cend)=glob%igradient(cstart:cend)+delta*neb%k*neb%tau(cstart:cend)
!!$ jimage=iimage+1
!!$ cstart=neb%cstart(jimage)
!!$ cend=neb%cend(jimage)
!!$ glob%igradient(cstart:cend)=glob%igradient(cstart:cend)+delta*neb%k*neb%tau(cstart:cend)
!!$ end do
!!$ end if
! spawn a climbing image next cycle if all gradients are below neb%tolclimb
if(tclimbnext) neb%tclimb=.true.
if(neb%maximage>0) then
! ==================================================================
! climbing image
! ==================================================================
iimage=neb%nimage
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
! send information on energy, gradient, and step to convergence tester
call convergence_set_info("of NEB climbing image",neb%varperimage,&
neb%ene(iimage),glob%igradient(cstart:cend),glob%step(cstart:cend))
! send information to set_tsmode
call dlf_formstep_set_tsmode(1,-1,neb%ene(iimage)) ! send energy
call dlf_formstep_set_tsmode(glob%nvar,0,neb%xcoords(:,iimage)) ! TS-geometry
call dlf_formstep_set_tsmode(neb%varperimage,11,neb%tau(cstart:cend)) ! TS-mode
! modify gradient
! improve - blas
ftan(iimage)=sum(glob%igradient(cstart:cend)*neb%tau(cstart:cend))
glob%igradient(cstart:cend)=glob%igradient(cstart:cend) - &
2.D0 * ftan(iimage) * neb%tau(cstart:cend)
fper(iimage)=sqrt(sum(( glob%igradient(cstart:cend)- &
1.D0*ftan(iimage)*neb%tau(cstart:cend))**2)/dble(neb%varperimage))
else
! No climbing image, so set approximate tsmode info using the highest
! energy image.
! Note the convergence info will be set in the main routine as usual
! using information from ALL images
iimage = 1
do jimage = 2, neb%nimage - 1
if (neb%ene(jimage) > neb%ene(iimage)) iimage = jimage
end do
cstart = neb%cstart(iimage)
cend = neb%cend(iimage)
call dlf_formstep_set_tsmode(1,-1,neb%ene(iimage)) ! send energy
call dlf_formstep_set_tsmode(glob%nvar,0,neb%xcoords(:,iimage)) ! TS-geometry
call dlf_formstep_set_tsmode(neb%varperimage,11,neb%tau(cstart:cend)) ! TS-mode
end if
! ====================================================================
! check first and last image
! ====================================================================
if(neb%mode==2) then
if(.not.neb%tfreeze) call dlf_fail("NEB: mode=2 and tfreeze=false is invalid")
neb%frozen(1)=.true.
neb%frozen(neb%nimage-1)=.true.
glob%igradient(neb%cstart(1):neb%cend(1))=0.D0
glob%igradient(neb%cstart(neb%nimage-1):neb%cend(neb%nimage-1))=0.D0
else
if (neb%mode==1) then
! first image
cstart=neb%cstart(1)
cend=neb%cend(1)
glob%igradient(cstart:cend)=glob%igradient(cstart:cend)- &
sum(glob%igradient(cstart:cend)*neb%tau(cstart:cend))*neb%tau(cstart:cend)
fper(1)=sqrt(sum(glob%igradient(cstart:cend)**2)/dble(neb%varperimage))
! last image
cstart=neb%cstart(neb%nimage-1)
cend=neb%cend(neb%nimage-1)
glob%igradient(cstart:cend)=glob%igradient(cstart:cend)- &
sum(glob%igradient(cstart:cend)*neb%tau(cstart:cend))*neb%tau(cstart:cend)
fper(neb%nimage-1)=sqrt(sum(glob%igradient(cstart:cend)**2)/dble(neb%varperimage))
end if
if(neb%mode==0) then
cstart=neb%cstart(1)
cend=neb%cend(1)
fper(1)=sqrt(sum(glob%igradient(cstart:cend)**2)/dble(neb%varperimage))
cstart=neb%cstart(neb%nimage-1 )
cend=neb%cend(neb%nimage-1 )
fper(neb%nimage-1 )=sqrt(sum(glob%igradient(cstart:cend)**2)/dble(neb%varperimage))
end if
if(neb%tfreeze) then
iimage=1
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
if(maxval(abs(glob%igradient(cstart:cend)))<=neb%tolfreeze) then
neb%frozen(iimage)=.true.
glob%igradient(cstart:cend)=0.D0
end if
iimage=neb%nimage-1
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
if(maxval(abs(glob%igradient(cstart:cend)))<=neb%tolfreeze) then
neb%frozen(iimage)=.true.
glob%igradient(cstart:cend)=0.D0
end if
end if
end if
! ====================================================================
! check if all images (but the climbing image) are frozen
! ====================================================================
if(neb%allfrozen) then
cstart=neb%cstart(1)
cend=neb%cend(neb%nimage-1)
! set complete gradient but frozen image to 0
glob%igradient(cstart:cend)=0.D0
else
tchk=.true.
do iimage=1,neb%nimage-1
if(.not.neb%frozen(iimage)) tchk=.false.
end do
if(tchk) then
! first time that all are frozen
neb%allfrozen=.true.
if(neb%frozen(neb%nimage)) then
if(printl>=2) then
write(stdout,"('All images are frozen and no climbing image &
&exists.')")
write(stdout,"('Either the climbing image threshold &
&was not reached or the path is monotonic.')")
write(stdout,"('Maximum gradient component converged to the frozen &
&image tolerance of ',es10.4)") neb%tolfreeze
end if
! set complete gradient to 0
glob%igradient(:)=0.D0
glob%step(:)=0.D0
! send information on energy, gradient, and step to convergence tester -
! all set to zero
call convergence_set_info("",neb%varperimage,&
glob%oldenergy_conv,glob%igradient(cstart:cend),glob%step(cstart:cend))
else
if(printl>=2) write(stdout,"('All images but the climbing image are frozen')")
if(neb%maximage<0) call dlf_fail("All images frozen and no climbing image present")
cstart=neb%cstart(1)
cend=neb%cend(neb%nimage-1)
! set complete gradient but frozen image to 0
glob%igradient(cstart:cend)=0.D0
call dlf_formstep_restart
end if
end if
end if
! ====================================================================
! write some report
! ====================================================================
if(printl>=4) then
write(stdout,"('NEB Report')")
write(stdout,"(' Energy F_tang F_perp Dist&
& Angle 1-3 Ang 1-2 Sum ')")
do iimage=1,neb%nimage-1
str=""
!if(iimage1.and.&
! ang(iimage)+ang(iimage+1)>ang13(iimage)*1.2D0) &
! str="!"
if(neb%frozen(iimage)) str=trim(str)//" frozen"
!if(iimage==neb%maximage) str=trim(str)//" Climbing image"
write(stdout,"('Img ',i4,f15.7,3f10.5,3f8.2,2x,a)") &
iimage,neb%ene(iimage),ftan(iimage),fper(iimage),dist(iimage), &
ang(iimage)*180.D0/pi,ang13(iimage)*180.D0/pi,&
(ang(iimage)+ang(min(iimage+1,neb%nimage-1)))*180.D0/pi,str
end do
if(neb%maximage>0) then
iimage=neb%nimage
write(stdout,"('Cimg',i4,f15.7,3f10.5,3f8.2)") &
neb%maximage,neb%ene(iimage),ftan(iimage),fper(iimage),dist(iimage), &
ang(iimage)*180.D0/pi,ang13(iimage)*180.D0/pi,&
(ang(iimage)+ang(min(iimage+1,neb%nimage-1)))*180.D0/pi
end if
write(stdout,"('Total path length ',f10.5)") sum(dist(1:neb%nimage-2))
end if
! write information files
if(printl>=2.and.printf>=2) then
tmass=(neb%varperimage==glob%nat*3)
! list of energies (and work?)
if (glob%iam == 0) then
open(unit=501,file="nebinfo")
if(tmass) then
write(501,"('# Path length Energy Work effective Mass')")
else
write(501,"('# Path length Energy Work')")
end if
end if
do iimage=1,neb%nimage-1
if(iimage==1) then
svar=0.D0
else
svar=sum(dist(1:iimage-1))
end if
if (glob%iam == 0) then
if(tmass) then
! neb%tau is normalized tangent
svar2=0.D0 ! reduced mass
do iat=1,glob%nat
svar2=svar2+sum((neb%tau(neb%cstart(iimage)+(iat-1)*3:&
neb%cstart(iimage)+(iat-1)*3+2))**2)/glob%mass(iat)
end do
svar2=1.D0/svar2
write(501,"(f10.5,2f15.10)") svar,neb%ene(iimage)-neb%ene(1),sum(dwork(1:iimage)),svar2
else
write(501,"(f10.5,2f15.10)") svar,neb%ene(iimage)-neb%ene(1),sum(dwork(1:iimage))
end if
end if
end do
if (glob%iam == 0) close(501)
! nebpath.xyz
if (glob%iam == 0) open(unit=501,file="nebpath.xyz")
do iimage=1,neb%nimage-1
! here, always write the whole system
!if(xyzall) then
if (glob%iam == 0) call write_xyz(501,glob%nat,glob%znuc,&
neb%xcoords(:,iimage))
!else
! call write_xyz_active(501,glob%nat,glob%znuc,glob%spec,neb%xcoords(:,iimage))
!end if
! write chemshell fragments as well (or any other format used by dlf_put_coords)
svar=neb%ene(iimage)
! The commented out line ensured that the final energy given to chemshell
! was that of the climbing image. A better solution is not to save the energy
! in put_coords if the mode < 0
! if(neb%maximage>0) svar=neb%ene(neb%nimage)
call dlf_put_coords(glob%nat,-iimage,svar,neb%xcoords(:,iimage),glob%iam)
end do
if (glob%iam == 0) close(501)
end if
end subroutine dlf_neb_improved_tangent_neb
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_checkstep
!!
!! FUNCTION
!!
!! Set step of frozen images to zero
!!
!! In case of large angles between the images, it sets the images back
!! to a line connecting the neighbouring images.
!!
!! SYNOPSIS
subroutine dlf_neb_checkstep
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stdout,pi
use dlf_neb, only: neb,string
implicit none
integer :: iimage,jimage,mimage
real(rk) :: svar,angle12(neb%nimage),angle13(neb%nimage)
logical :: treduce(neb%nimage)
! **********************************************************************
!if(.not.neb%tfreeze) return
do iimage=1,neb%nimage
if(neb%frozen(iimage)) then
glob%step(neb%cstart(iimage):neb%cend(iimage))=0.D0
end if
end do
! calculate the angles:
angle12(:)=0.D0
angle13(:)=0.D0
do iimage=2,neb%nimage-2
call angle(iimage-1,iimage,iimage,iimage+1,angle12(iimage))
if(iimage 90.D0/180.D0*pi ) treduce(iimage)=.true.
end do
! if(.not.string) then ! it turned out not to be a good idea to reduce the images in string ...
do iimage=2,neb%nimage-2
if(.not.treduce(iimage)) cycle
if(iimage==neb%maximage) cycle
PRINT*,"-------- Path corrections -----------"
do jimage=iimage+1,neb%nimage-2
if(.not.treduce(jimage)) exit
if(jimage==neb%maximage) exit
treduce(jimage)=.false. ! will be covered now
end do
print*,"Resetting images",iimage," to ",jimage-1
do mimage=iimage,jimage-1
svar=dble(mimage - (iimage-1))/dble(jimage - (iimage-1))
glob%step(neb%cstart(mimage):neb%cend(mimage))= &
(1.D0-svar) * (&
glob%icoords(neb%cstart(iimage-1):neb%cend(iimage-1)) + &
glob%step(neb%cstart(iimage-1):neb%cend(iimage-1)) ) + &
svar * ( &
glob%icoords(neb%cstart(jimage):neb%cend(jimage)) + &
glob%step(neb%cstart(jimage):neb%cend(jimage))) &
-glob%icoords(neb%cstart(mimage):neb%cend(mimage))
end do
END do
! end if
treduce(:)=.false.
if(string.and.(.not.neb%allfrozen)) call string_reparametrise(treduce)
contains
subroutine angle(img1,img2,img3,img4,ang)
implicit none
integer,intent(in) :: img1,img2,img3,img4
real(rk),intent(out) :: ang
real(rk) :: svar
integer :: img,iarr(4)
real(rk) :: path(neb%varperimage,2)
! ********************************************************************
iarr(:)=(/img1,img2,img3,img4/)
do img=2,4,2
path(:,img/2)=(glob%icoords(neb%cstart(iarr(img)):neb%cend(iarr(img))) + &
glob%step (neb%cstart(iarr(img)):neb%cend(iarr(img))) - &
glob%icoords(neb%cstart(iarr(img-1)):neb%cend(iarr(img-1))) - &
glob%step (neb%cstart(iarr(img-1)):neb%cend(iarr(img-1))) ) * &
glob%iweight(1:neb%varperimage)
end do
svar=sum(path(:,1)*path(:,2))/dsqrt(sum(path(:,1)**2)) &
/ dsqrt(sum(path(:,2)**2))
if(svar>=1.D0) then
ang=0.D0
else
ang=acos(svar)
end if
end subroutine angle
end subroutine dlf_neb_checkstep
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_itox
!!
!! FUNCTION
!!
!! transform NEB array to Cartesian coordinates
!!
!! All images will be transformed to xyz coordinates here. If
!! unsuccessful the algorithm will fall back to Cartesian coordinates.
!! This however, will end the usage of constraints!
!!
!! Initiate the climbing image if required
!!
!! SYNOPSIS
subroutine dlf_neb_itox(external_iimage)
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stdout,printf,printl
use dlf_allocate, only: allocate,deallocate
use dlf_neb, only: neb,unitp,xyzall,maxxyzfile
implicit none
integer ,intent(out) :: external_iimage
integer :: cstart,cend,iimage,mx(1),ivar
logical :: tok,tfail
real(rk), allocatable :: xtmp(:,:)
integer , allocatable :: useimage(:)
real(rk) :: svar,svar2
! **********************************************************************
! ====================================================================
! Determine the initial position of the climbing image
! ====================================================================
if(neb%tclimb .and. neb%frozen(neb%nimage)) then
! initialise climbing image
mx=maxloc(neb%ene(2:neb%nimage-2))
ivar=mx(1)+1 ! maximum image
cstart=neb%cstart(neb%nimage)
cend=neb%cend(neb%nimage)
! make sure no climbing image is spawned if there is no maximum on the path
if(ivar==2 .and. neb%ene(1)>neb%ene(ivar)) neb%tclimb=.false.
if(ivar==neb%nimage-2 .and. neb%ene(neb%nimage-1)>neb%ene(ivar)) neb%tclimb=.false.
if(neb%tclimb) then
if(printl>=4) write(stdout,'("Spawning climbing image")')
!Comment: The climbing image is set according to the energies before the
! step but the geometry after the step. This may lead to
! difficulties if the step is large.
! interpolate maximum from nearby 3 images
svar=(-(neb%ene(ivar-1)-neb%ene(ivar))-(neb%ene(ivar+1)-neb%ene(ivar)))/(-2.D0)
if(svar>0.d0) then
! the parabola through the three points has a minimum, no maximum.
! In this case, use the geometry of ivar as initial climbing image
glob%icoords(cstart:cend)=glob%icoords(neb%cstart(ivar):neb%cend(ivar))
glob%xcoords(:,neb%nimage)=glob%xcoords(:,ivar)
if(printl>=4) write(stdout,'("Initial climbing image is identical&
& to image",i4)') ivar
else
svar2=(neb%ene(ivar+1)-neb%ene(ivar-1))/2.D0 - 2.D0*svar
svar=-svar2/(2.D0*svar)
if(svar>1.D0) then
! interpolate between images ivar and ivar+1
glob%icoords(cstart:cend)= &
(2.D0-svar) * glob%icoords(neb%cstart(ivar):neb%cend(ivar)) + &
(svar-1.D0) * glob%icoords(neb%cstart(ivar+1):neb%cend(ivar+1))
neb%xcoords(:,neb%nimage)= &
(2.D0-svar) * neb%xcoords(:,ivar) + &
(svar-1.D0) * neb%xcoords(:,ivar+1)
if(printl>=4) write(stdout,'("Initial climbing image is ",f5.1,"% of&
& image ",i4," and ",f5.1,"% of image ",i4)') (2.D0-svar)*100.D0,&
ivar,(svar-1.D0)*100.D0,ivar+1
else
! interpolate between images ivar-1 and ivar
glob%icoords(cstart:cend)= &
(1.D0-svar) * glob%icoords(neb%cstart(ivar-1):neb%cend(ivar-1)) + &
svar * glob%icoords(neb%cstart(ivar):neb%cend(ivar))
neb%xcoords(:,neb%nimage)= &
(1.D0-svar) * neb%xcoords(:,ivar-1) + &
svar * neb%xcoords(:,ivar)
if(printl>=4) write(stdout,'("Initial climbing image is ",f5.1,"% of&
& image ",i4," and ",f5.1,"% of image ",i4)') (1.D0-svar)*100.D0,&
ivar-1,svar*100.D0,ivar
end if
end if ! (svar>0.d0)
call dlf_formstep_restart
neb%frozen(neb%nimage)=.false.
neb%maximage=ivar
else
if(printl>=4) write(stdout,'("No climbing image spawned because the energy &
&maximum is an endpoint of the path")')
end if !(neb%tclimb)
end if ! (neb%tclimb .and. neb%frozen(neb%nimage))
if(neb%iimage/=neb%nimage) print*,"Warning, NEB images have been tampered with!"
if (glob%tatoms) then
call allocate( xtmp,3*glob%nat,neb%nimage)
else
call allocate( xtmp,glob%nvar,neb%nimage)
end if
tfail=.false.
xtmp(:,:)=neb%xcoords(:,:) ! starting guess and frozen structures
do iimage=1,neb%nimage
if(neb%tfreeze) then
if(neb%frozen(iimage)) cycle
end if
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
call dlf_direct_itox(glob%nvar,neb%varperimage, &
glob%icoords(cstart:cend),xtmp(:,iimage),tok)
if(.not.tok) then
tfail=.true.
exit
end if
end do
if(tfail) then
! reset and restart
call allocate(useimage,neb%nimage)
useimage(:)=1
if(printl>=2) write(stdout, &
"('HDLC coordinate breakdown. Recalculating HDLCs and &
&restarting optimiser and NEB.')")
call dlf_hdlc_reset
call dlf_hdlc_create(glob%nat,glob%spec,glob%znuc,neb%nimage, &
neb%xcoords,glob%weight,glob%mass)
! calculate iweights
! first image
call dlf_hdlc_getweight(glob%nat,glob%nivar,glob%weight, &
glob%iweight(1:neb%varperimage))
! copy weights to all other images
do iimage=2,neb%nimage
ivar=neb%varperimage*(iimage-1)+1
glob%iweight(ivar:ivar+neb%varperimage-1)=glob%iweight(1:neb%varperimage)
end do
call dlf_formstep_restart
! recalculate NEB images
call dlf_neb_definepath(neb%nimage,useimage)
call deallocate(useimage)
else
! all coordinate conversion were successful
neb%xcoords(:,:)=xtmp
end if
call deallocate(xtmp)
! now all images are present in xcoords (neb%xcoords)
! find first non-frozen image to calculate the energy on
if(neb%tfreeze) then
do iimage=1,neb%nimage
if(neb%frozen(iimage)) then
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
glob%igradient(cstart:cend)=0.D0
! Parallel NEB: this is to avoid multiple counting at the end of xtoi
if (glob%mytask /= 0) then
glob%icoords(cstart:cend) = 0.d0
neb%ene(iimage) = 0.d0
neb%gradt(cstart:cend) = 0.d0
end if
! write xyz file
if(printf>=4.and.glob%tatoms.and.neb%iimage<=maxxyzfile .and. glob%iam == 0) then
if(xyzall) then
call write_xyz(unitp+iimage,glob%nat,glob%znuc,neb%xcoords(:,iimage))
else
call write_xyz_active(unitp+iimage,glob%nat,glob%znuc,glob%spec,neb%xcoords(:,iimage))
end if
end if
else
neb%iimage=iimage
exit
end if
end do
else
neb%iimage=1
end if
external_iimage=neb%iimage
if(glob%tatoms) then
glob%xcoords=reshape(neb%xcoords(:,neb%iimage),(/3,glob%nat/))
else
glob%xcoords=reshape(neb%xcoords(:,neb%iimage),(/1,glob%nvar/))
end if
end subroutine dlf_neb_itox
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/dlf_neb_definepath
!!
!! FUNCTION
!!
!! Define a NEB path in internal coordinates using input guesses or old
!! structures in x-coordinates
!!
!! useimage(iimage)=0 -> this image is interpolated, x-information is
!! not used
!!
!! images are not reordered, the first image has to be "used", the
!! later ones are uniformly distributed
!!
!! at the end, we have icoords and consistent xcoords - or an error termination
!!
!! INPUTS
!!
!! neb%xcoords glob%spec
!!
!!
!! OUTPUTS
!!
!! glob%icoords
!!
!! SYNOPSIS
subroutine dlf_neb_definepath(nimage,useimage)
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stdout,printl
use dlf_allocate, only: allocate,deallocate
use dlf_neb, only: neb,unitp
implicit none
integer ,intent(in) :: nimage
integer ,intent(inout):: useimage(nimage)
integer :: xok(nimage)
integer :: iimage,jimage,kimage,cstart,cend,lowok,ivar
integer :: maxuse,iat,nuse,map(nimage)
real(rk) :: svar,dist(nimage),lastdist
logical :: tok,tchk
! **********************************************************************
glob%igradient=0.D0
glob%icoords=0.D0
dist(:)=0.D0 ! dist is a cumulative distance from the first image
maxuse=1
nuse=0 ! number of images to use to construct the path
! get internal coordinates of well-defined images
lastdist=0.D0
do iimage=1,nimage
if(useimage(iimage)==0) cycle
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
!print*," ((((((((((((((((((((( IMAGE",iimage," )))))))))))))))))))))))))))"
call dlf_direct_xtoi(glob%nvar,neb%varperimage,neb%xcoords(:,iimage), &
glob%xgradient,glob%icoords(cstart:cend),glob%igradient(cstart:cend))
! distance to last used image
if(iimage>1) then
svar=sqrt(sum(( &
glob%iweight(1:neb%varperimage)*( &
glob%icoords(cstart:cend) - glob%icoords(neb%cstart(maxuse):neb%cend(maxuse))) &
) **2 ) )
!svar=svar+sum(dist)
dist(iimage)=svar+lastdist
lastdist=dist(iimage)
end if
maxuse=iimage
if(maxuse=nimage) maxuse=nimage-1
! if all images are well-defined, return
if(minval(useimage)==1) return
if(dist(maxuse)<1.D-3) call dlf_fail("Start and endpoint too close in NEB")
! print alignment of images along the distance
if(printl>=4) then
write(stdout,'("Constructing initial NEB path from input images")')
write(stdout,'("Length of the initial path: ",f10.5)') dist(maxuse)
write(stdout,'("Using ",i3," input images to construct a path of ",&
&i3," total images")') nuse,nimage-1
lastdist=0.D0
do iimage=1,maxuse
if(useimage(iimage)==0) cycle
write(stdout,'("Image ",i3," along path: ",f10.5," dist to prev. &
&image:",f10.5)') iimage,dist(iimage),dist(iimage)-lastdist
lastdist=dist(iimage)
end do
end if
! if all requested images are provided by the user, do not interpolate
if(nuse/=nimage-1) then
! distribute defined images as equally as possible according to dist
map(:)=0 ! map(iimage) is the number, iimage should have after distribution
do iimage=maxuse,2,-1
if(useimage(iimage)==0) cycle
svar=1.D0+dist(iimage)/dist(maxuse)*dble(nimage-2)
map(iimage)=nint(svar)
!print*,"Image",iimage,"svar",svar,"map(iimage)",map(iimage)
if(map(iimage)>=nimage) then
call dlf_fail("Problem with image distribution in NEB.")
end if
end do
! assign images
do iimage=maxuse,2,-1
tok=.true.
do jimage=maxuse,2,-1
if(jimage==iimage) cycle
if(map(jimage)==map(iimage)) tok=.false.
end do
if(.not.tok) then
! see if map(iimage)=iimage would be OK
tok=.true.
do jimage=maxuse,2,-1
if(map(jimage)==iimage) tok=.false.
end do
if(tok) map(iimage)=iimage
end if
if(.not.tok) then
! Drop current image
map(iimage)=0
useimage(iimage)=0
if(printl>=2) write(stdout,'("Dropping input image ",i3," because &
&there are too may input images in this area")') iimage
cycle
end if
! swap images
if(printl>=4) then
write(stdout,'("Using input image",i4," as image",i4)') iimage,&
map(iimage)
end if
if(map(iimage)==iimage) cycle
! move image
ivar=map(iimage)
! move icoords
glob%icoords(neb%cstart(ivar):neb%cend(ivar))= &
glob%icoords(neb%cstart(iimage):neb%cend(iimage))
! move xcoords
neb%xcoords(:,ivar)=neb%xcoords(:,iimage)
! move use info
useimage(ivar)=1
useimage(iimage)=0
end do
end if
if(printl>=4) then
do iimage=1,nimage-1
if(useimage(iimage)==1) then
write(stdout,'("Image",i3," is obtained from input")') iimage
else
write(stdout,'("Image",i3," is interpolated")') iimage
end if
end do
end if
! linear transit between defined images
if(useimage(1)==0) call dlf_fail("First image undefined in NEB")
if(useimage(nimage-1)==0) call dlf_fail("Last image undefined in NEB")
do iimage=1,nimage-1
if(useimage(iimage)==0) cycle
! iimage is defined
do jimage=iimage+1,nimage-1
if(useimage(jimage)/=0) exit
end do
if(jimage==iimage+1) cycle
! interpolate from iimage to jimage
do kimage=iimage+1,jimage-1
cstart=neb%cstart(kimage)
cend=neb%cend(kimage)
svar=dble(kimage-iimage)/dble(jimage-iimage) ! 0..1
glob%icoords(cstart:cend)= &
(1.D0-svar)*glob%icoords( neb%cstart(iimage) : neb%cend(iimage) ) + &
svar*glob%icoords( neb%cstart(jimage) : neb%cend(jimage) )
end do
end do
! now all icoords are defined
! find consistent xcoords
xok=0
xok(1)=1
xok(nimage-1)=1
!do iimage=2,nimage-2
iimage=1
do while (iimagex )))))))))))))))))))))))))))"
call dlf_direct_itox(glob%nvar,neb%varperimage, &
glob%icoords(cstart:cend),neb%xcoords(:,iimage),tok)
if(tok) then
xok(iimage)=1
else
!print*,"Failed at image ",iimage
! jump to next defined image
iimage=jimage
end if
end do
!print*,"JK Converted:",xok
!print*,"iimage",iimage
if(minval(xok(1:nimage-1))<1) then
! at least one image could not be converted. Try from last image back
! lowok=iimage-1
do iimage=nimage-2,2,-1
if(xok(iimage)==1) cycle
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
! find the next defined image
do jimage=iimage-1,2,-1
if(xok(jimage)==1) exit
end do
svar=1.D0/dble(iimage-jimage+1)
neb%xcoords(:,iimage)=(1.D0-svar)*neb%xcoords(:,iimage+1) &
+ svar*neb%xcoords(:,jimage)
!print*," ((((((((((((((((((((( IMAGE",iimage," i->x back))))))))))))))))))))))))"
call dlf_direct_itox(glob%nvar,neb%varperimage, &
glob%icoords(cstart:cend),neb%xcoords(:,iimage),tok)
if(tok) then
xok(iimage)=1
else
!print*,"Failed at image ",iimage
exit
end if
end do
end if
if(minval(xok(1:nimage-1))<1) then
write(stdout,"('Image conversion success: ',10i4)") xok
call dlf_fail("Failed converting images to Cartesians")
end if
! Set xcoords of frozen atoms of all images to those in image 1
! This may be improved by specifying a different image (TS?)
IF(glob%tatoms) then
do iimage=2,nimage-1
do iat=1,glob%nat
if(glob%spec(iat)/=-1) cycle
ivar=3*(iat-1)+1
neb%xcoords(ivar:ivar+2,iimage)=neb%xcoords(ivar:ivar+2,1)
end do
end do
end IF
! Another test to see if the conversion works with the input created above
do iimage=2,nimage-2
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
call dlf_direct_itox(glob%nvar,neb%varperimage, &
glob%icoords(cstart:cend),neb%xcoords(:,iimage),tok)
if(.not.tok) then
!print*,"Failed at image ",iimage
call dlf_fail("NEB internal image initialisation &
&failed in final test.")
end if
end do
end subroutine dlf_neb_definepath
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/string_get_tau
!!
!! FUNCTION
!!
!! Define the string path as bsplines and calculate the tangents
!!
!! INPUTS
!!
!! glob%icoords
!!
!! OUTPUTS
!!
!! neb%tau
!!
!! SYNOPSIS
subroutine string_get_tau
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stdout,printl
use dlf_neb, only: neb,unitp
use bspline, only: spline_init, spline_create, spline_get, &
spline_destroy
implicit none
integer :: ivar,iimage,cstart,cend
real(rk) :: stringpos(neb%nimage-1) ! 0..1
real(rk) :: stringval(neb%nimage-1), svar
! **********************************************************************
call spline_init(neb%nimage-1,neb%varperimage)
! define x-values of the splines
do iimage=1,neb%nimage-1
stringpos(iimage)=dble(iimage-1)/dble(neb%nimage-2)
end do
do ivar=1,neb%varperimage
! define y-value of the spline
do iimage=1,neb%nimage-1
stringval(iimage)=glob%icoords(neb%cstart(iimage)+ivar-1)
end do
call spline_create(ivar,stringpos,stringval)
! get derivatives of the spline
do iimage=1,neb%nimage-1
call spline_get(ivar,stringpos(iimage),svar, &
neb%tau(neb%cstart(iimage)+ivar-1))
end do
end do
call spline_destroy
! normalise tau
do iimage=1,neb%nimage-1
cstart=neb%cstart(iimage)
cend=neb%cend(iimage)
neb%tau(cstart:cend)=neb%tau(cstart:cend)/sqrt(sum(neb%tau(cstart:cend)**2))
end do
end subroutine string_get_tau
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!!****f* neb/string_reparametrise
!!
!! FUNCTION
!!
!! Reparametrise the string path: the input images (beads on the string)
!! are glob%icoords+glob%step.
!!
!! Modify glob%step to make them evenly distributed along the path
!!
!! The string is defined with equally-spaced alpha (ranging from 0 to 1)
!! but will have different spacing in i-coordinate space. Then, alpha
!! values for equal i-coordinate spacing are calculated, and the step is
!! modified.
!!
!! INPUTS
!!
!! glob%icoords, glob%step
!!
!! OUTPUTS
!!
!! glob%step
!!
!! SYNOPSIS
subroutine string_reparametrise(treduce)
!! SOURCE
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stdout,printl
use dlf_neb, only: neb,unitp
use bspline, only: spline_init, spline_create, spline_get, &
spline_destroy
use dlf_stat ! REMOVE JK
implicit none
logical,intent(in) :: treduce(neb%nimage)
integer :: ivar,iimage,cstart,cend
real(rk) :: stringpos(neb%nimage-1) ! 0..1
real(rk) :: stringval(neb%nimage-1), svar
integer, parameter :: npoint=300 ! number of points interpolated
real(rk) :: length(npoint) ! length of the string up to that point
real(rk) :: energy(npoint) ! energy of the string (interpolated)
real(rk) :: length_img(neb%nimage-1) ! ength of the string up to that
! image
integer :: ipoint,low,high,jimage,count
real(rk) :: step,alpha,yvar,yvar2,dyvar,new_length,ene
integer :: step_print
real(rk):: svar2
real(rk) :: tmp_step(glob%nivar),tmp_grad(glob%nivar),tmp_tau(neb%varperimage)
! **********************************************************************
step_print=40
if(printl>=4) write(stdout,'(a)') "Reparametrising string"
count=0
do iimage=1,neb%nimage-1
if(.not.treduce(iimage)) then
count=count+1
else
write(*,'("Image",i4," is reset to string")') iimage
end if
end do
print*,"Number of images considered for the string:",count
! call spline_init(neb%nimage-1,neb%varperimage)
! call spline_init(count,neb%varperimage)
call spline_init(count,neb%varperimage+1)
! define x-values of the splines - equally spaced
count=0
do iimage=1,neb%nimage-1
if(.not.treduce(iimage)) then
count=count+1
stringpos(count)=dble(iimage-1)/dble(neb%nimage-2)
end if
end do
do ivar=1,neb%varperimage+1
! define y-value of the spline: coordinates and energy
count=0
do iimage=1,neb%nimage-1
if(.not.treduce(iimage)) then
count=count+1
if(ivar<=neb%varperimage) then
stringval(count)=glob%icoords(neb%cstart(iimage)+ivar-1) + &
glob%step(neb%cstart(iimage)+ivar-1)
else
stringval(count)=neb%ene(iimage)
end if
end if
end do
call spline_create(ivar,stringpos,stringval)
end do
! energy
do ipoint=1,npoint
alpha=dble(ipoint-1)/dble(npoint-1)
call spline_get(neb%varperimage+1,alpha,energy(ipoint),dyvar)
! print*,ipoint,energy(ipoint)
end do
! calculate the length of the string (arc length)
length(1)=0.D0
step=1.D0/dble(npoint-1)
do ipoint=2,npoint
length(ipoint)=0.D0
alpha=dble(ipoint-1)/dble(npoint-1)
do ivar=1,neb%varperimage
call spline_get(ivar,alpha,yvar,dyvar)
call spline_get(ivar,alpha-step,yvar2,dyvar)
length(ipoint)=length(ipoint)+(yvar2-yvar)**2
end do
ene=0.5D0*(energy(ipoint)+energy(ipoint-1))-minval(energy)
ene=ene/(maxval(energy)-minval(energy)) ! 0..1
svar=0.0D0 ! energy weighting parameter 0=no weighting
length(ipoint)=length(ipoint-1)+dsqrt(length(ipoint)) * &
! weighting
(1.D0-svar+ene*2.D0*svar)
! length(ipoint)=length(ipoint-1)+dsqrt(length(ipoint))
end do
if(printl>=4) write(stdout,'("Length of the string ",f10.5)') &
length(npoint)
! calculate current positions of the images along the string
! only important for frozen images - will be wrong for reduced ones
length_img(:)=0.D0
do iimage=1,neb%nimage-1
! int rounds to the lower value
ipoint=int(dble(iimage-1)/dble(neb%nimage-2)*dble(npoint-1))+1 ! point left of the image
svar=dble(iimage-1)/dble(neb%nimage-2)*dble(npoint-1)-dble(ipoint-1)
if(ipoint==npoint) then
length_img(iimage)=length(npoint)
else
length_img(iimage)=(1.d0-svar)*length(ipoint)+svar*length(ipoint+1)
end if
end do
if(printl>=6) write(stdout,'("Positions of images along the string: ",6f10.5)') &
length_img(:)
! set non-frozen images to the optimum position on the string
low=1
high=neb%nimage-1
svar2=0.D0
do iimage=1,neb%nimage-1
if(neb%frozen(iimage)) then
low=iimage
else
high=neb%nimage-1
do jimage=iimage+1,neb%nimage-1
if(neb%frozen(jimage)) then
high=jimage
exit
end if
end do
! position of iimage on the arc - equal arc lenth spacing
svar=dble(iimage-low)/dble(high-low)
new_length=(1.D0-svar)*length_img(low)+svar*length_img(high)
! new_length is now the lenght value at which the image should be placed
! find the string variable (0..1) that corresponds to this length value
do ipoint=1,npoint
if(length(ipoint)>=new_length) exit
end do
! the new image should be located at string(alpha):
if(ipoint>1) then
svar=(new_length-length(ipoint-1))/(length(ipoint)-length(ipoint-1))
alpha=(dble(ipoint-2)+svar)/dble(npoint-1)
else
alpha=0.D0
end if
! recalculate the step
do ivar=1,neb%varperimage
call spline_get(ivar,alpha,yvar,dyvar)
svar2=svar2+(yvar - &
glob%icoords(neb%cstart(iimage)+ivar-1))**2
glob%step(neb%cstart(iimage)+ivar-1) = yvar - &
glob%icoords(neb%cstart(iimage)+ivar-1)
end do
end if
end do
do iimage=1,neb%nimage-1
svar=sqrt(sum((glob%step(neb%cstart(iimage):neb%cend(iimage)))**2))
write(*,"('Img ',i3,' Step lenth:',f10.5)") iimage,svar
end do
call spline_destroy
end subroutine string_reparametrise
!!****
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
subroutine dlf_checkpoint_neb_write
use dlf_parameter_module, only: rk
use dlf_global, only: glob,stderr
use dlf_neb, only: neb
use dlf_checkpoint, only: tchkform,write_separator
implicit none
! **********************************************************************
if(tchkform) then
open(unit=100,file="dlf_neb.chk",form="formatted")
call write_separator(100,"NEB Sizes")
write(100,*) neb%nimage,neb%varperimage
call write_separator(100,"NEB Parameters")
write(100,*) neb%iimage,neb%step,neb%maximage,neb%mode,neb%k &
,neb%optcart
call write_separator(100,"NEB Arrays")
write(100,*) neb%ene,neb%frozen,neb%gradt,neb%xcoords,neb%frozen
call write_separator(100,"END")
else
open(unit=100,file="dlf_neb.chk",form="unformatted")
call write_separator(100,"NEB Sizes")
write(100) neb%nimage,neb%varperimage
call write_separator(100,"NEB Parameters")
write(100) neb%iimage,neb%step,neb%maximage,neb%mode,neb%k &
,neb%optcart
call write_separator(100,"NEB Arrays")
write(100) neb%ene,neb%frozen,neb%gradt,neb%xcoords,neb%frozen
call write_separator(100,"END")
end if
close(100)
end subroutine dlf_checkpoint_neb_write
! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
subroutine dlf_checkpoint_neb_read(tok)
use dlf_parameter_module, only: rk
use dlf_global, only: stdout,printl
use dlf_neb, only: neb
use dlf_checkpoint, only: tchkform, read_separator
implicit none
logical,intent(out) :: tok
logical :: tchk
integer :: nimage,varperimage
! **********************************************************************
tok=.false.
! check if checkpoint file exists
INQUIRE(FILE="dlf_neb.chk",EXIST=tchk)
if(.not.tchk) then
write(stdout,10) "File dlf_neb.chk not found"
return
end if
if(tchkform) then
open(unit=100,file="dlf_neb.chk",form="formatted")
else
open(unit=100,file="dlf_neb.chk",form="unformatted")
end if
call read_separator(100,"NEB Sizes",tchk)
if(.not.tchk) return
if(tchkform) then
read(100,*,end=201,err=200) nimage,varperimage
else
read(100,end=201,err=200) nimage,varperimage
end if
if(neb%nimage/=nimage) then
write(stdout,10) "Different numbers of NEB images"
close(100)
return
end if
if(neb%varperimage/=varperimage) then
write(stdout,10) "Different numbers of variables per NEB image"
close(100)
return
end if
call read_separator(100,"NEB Parameters",tchk)
if(.not.tchk) return
if(tchkform) then
read(100,*,end=201,err=200) neb%iimage,neb%step, &
neb%maximage,neb%mode,neb%k,neb%optcart
else
read(100,end=201,err=200) neb%iimage,neb%step, &
neb%maximage,neb%mode,neb%k,neb%optcart
end if
call read_separator(100,"NEB Arrays",tchk)
if(.not.tchk) return
if(tchkform) then
read(100,*,end=201,err=200) neb%ene,neb%frozen,neb%gradt &
,neb%xcoords,neb%frozen
else
read(100,end=201,err=200) neb%ene,neb%frozen,neb%gradt &
,neb%xcoords,neb%frozen
end if
call read_separator(100,"END",tchk)
if(.not.tchk) return
close(100)
tok=.true.
if(printl >= 6) write(stdout,"('NEB checkpoint file successfully read')")
return
! return on error
close(100)
200 continue
write(stdout,10) "Error reading file"
return
201 continue
write(stdout,10) "Error (EOF) reading file"
return
10 format("Checkpoint reading WARNING: ",a)
end subroutine dlf_checkpoint_neb_read
!!****