2024-07-25 10:27:17 +02:00

9447 lines
321 KiB
Fortran

! TODO: Remove build_projection and use dgelss if possible
!************************************************************************
module optim
!************************************************************************
! OPTIMIZATION MODULE
! for basis set and geometry optimization procedures
! The calling routines are in basopt.f and geomopt.f
!************************************************************************
double precision, parameter :: ang2bohr = 1.d0/0.52917720859d0
double precision, parameter :: PI = 3.141592653589793d0
integer,parameter :: lstr=512 ! assumed max length of a line in the file MINP
integer,parameter :: iterfile=98 ! file ITER it stores the interation number of geomopt
integer,parameter :: icoordfile = 78 ! file number for COORD.int
integer,parameter :: minptmpf = 79 ! MINP.tmp
integer,parameter :: mheaderf=80, mfooterf=81 ! MINP.header and MINP.footer - temporary files without geom spec
integer,parameter :: cenergies=99 ! COORD.xyz.energies file for MOLDEN
integer,parameter :: crmsforce=97 ! COORD.xyz.rforce file for MOLDEN (rms force)
integer,parameter :: cmaxforce=96 ! COORD.xyz.mforce file for MOLDEN (max force)
integer,parameter :: coordfile=100 ! COORD.xyz.i file current geometry at step i
integer,parameter :: geomsfile=101 ! COORD.xyz.geometries file for MOLDEN it contains all the geoms during the optimization
integer,parameter :: pmatfile=102 ! scr file to hold projection matrix element, PMAT.int
integer,parameter :: ifcfile=20 ! file iface; MRCC interface file
integer, parameter :: bond_code=1 ! Code for regular bonds
integer, parameter :: angle_code=2 ! Code for bond angles
integer, parameter :: dangle_code=3 ! Code for dihedral angles
integer, parameter :: abond_code=4 ! Code for auxiliary bonds
integer, parameter :: fbond_code=5 ! Code for fragment bonds
integer, parameter :: hbond_code=6 ! Code for H-bonds
integer, parameter :: afbond_code=7 ! Code for auxiliary fragment bond
integer, parameter :: improper_code=8 ! Code for improper dihedrals
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! ARRAYS, TYPES
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
character(len=lstr), dimension(:), allocatable :: alloc_pname ! array of types/parameter names
double precision, dimension(:), allocatable :: alloc_fgopt ! array of parameter values
integer, dimension(:), allocatable :: alloc_igopt ! whether or not the given parameter should be optimized
character(len=2), dimension(:), allocatable :: alloc_cgopt ! to hold the special marks '++', '--', or ' '
integer, dimension(:), allocatable :: alloc_atnums ! array of atomic numbers
character(len=2), dimension(:), allocatable :: alloc_atsymbols! array of atomic symbols
integer, dimension(:,:), allocatable :: alloc_rim ! redundant internal coordinate matrix
double precision, dimension(:,:), allocatable :: alloc_coords ! Cartesian coords
double precision, dimension(:), allocatable :: alloc_intcoords! internal coords
double precision, dimension(:,:), allocatable :: alloc_hessian ! initial Hessian matrix
type fragment_type
integer :: natoms
integer, allocatable :: atom_list(:)
end type
type coord_sys_type
character*16 :: name
integer :: natoms ! number of atoms including dummy atoms
integer :: natoms_original ! number of atoms excluding dummy
integer :: natoms_extra ! number of dummy atoms
integer :: ncoord_ric ! number of redundant internal coordinates
integer :: ncoord ! number of (internal) coordinates
integer :: nonred ! number of nonredundant coordinates
integer :: nfrozen ! number of frozen coordinates
integer, allocatable :: frozen(:) ! frozen coordinates
double precision, allocatable :: cvals(:) ! coordinate constrain values
double precision, allocatable :: coord(:)
double precision, allocatable :: cart_coord(:,:)
double precision, allocatable :: Umatrix(:,:)
!rim=redundant internal matrix
integer, allocatable :: rim(:,:)
end type
contains
!************************************************************************
double precision function epsilon_()
!************************************************************************
! it gives back machine epsilon
!************************************************************************
implicit none
epsilon_ = 1.d0
do while (1.d0 + 0.5d0*epsilon_ .gt. 1.d0)
epsilon_ = 0.5d0*epsilon_
enddo
end function epsilon_
!************************************************************************
!************************************************************************
subroutine cfillzero(str_array,n)
!************************************************************************
! This subroutine initialize a character array *
!************************************************************************
implicit none
integer,intent(in) :: n
character(len=*),dimension(:),intent(inout) :: str_array
!local
integer :: i
do i=1,n
str_array(i) = ''
enddo
return
end subroutine cfillzero
!************************************************************************
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! SIMPLEX ALGORITHM
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
subroutine simplex(f2min,args,ftol,xtol,maxiter,maxfun,fopt, &
niter,nfeval,plevel,bool)
!************************************************************************
! The downhill simplex algorithm of Nelder and Mead;
! J.A. Nelder, R. Mead: Computer J., 7, 308 (1965);
! written by J Csontos
!
! The implementation is based on that of the numpy project
!
implicit none
double precision, intent(in) :: ftol,xtol
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval
double precision, dimension(:), intent(inout) :: args
logical, intent(out) :: bool
! local variables
logical :: doshrink=.False.
integer :: i,j,ndim
double precision :: functol,argtol,fxr,fxe,fxc,fxcc
! vertj is an array for the function values at the vertices of the simplex
double precision, dimension(:),allocatable :: vertj,xavg,xr,xe,xc
double precision, dimension(:),allocatable :: fsim,xcc
integer, dimension(:),allocatable :: order
double precision, dimension(:,:),allocatable :: sim
double precision, dimension(:,:),allocatable :: tmp
! the parameters for the transformation of the simplex
! rho -> factor of reflection
! psi -> factor of contraction
! chi -> factor of expansion
! del -> factor for generating the initial simplex
! zdel -> factor for generating the initial simplex
double precision, parameter :: rho=1.d0,chi=2.d0, &
psi=0.5d0, sigma=0.5d0
double precision, parameter :: del=0.05d0, zdel=0.00025d0
double precision, parameter :: eps=1d-6
interface
double precision function f2min(z)
double precision, dimension(:), intent(inout) :: z
end function
end interface
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
!
! ftol -> convergence criterion for the function values
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
bool = .False.
ndim = size(args) ! dimension of f2min
nfeval = 0
allocate(vertj(ndim))
allocate(xavg(ndim))
allocate(xr(ndim))
allocate(xe(ndim))
allocate(xc(ndim))
allocate(xcc(ndim))
allocate(fsim(ndim+1))
allocate(order(ndim+1))
allocate(sim(ndim,ndim+1))
allocate(tmp(ndim,ndim+1))
do j=1,ndim+1
do i=1,ndim
sim(i,j)=0.d0
enddo
fsim(j)=0.d0 ! array for function values at the vertices
enddo
fsim(1)=f2min(args)
nfeval=nfeval+1
! generation of the initial simplex
! row-index stands for the coordinates of a given vertex (n)
! column index stands for the vertices (n+1)
do i=1,ndim
sim(i,1)=args(i)
vertj(i)=0.d0 ! array for function arguments at the vertices
enddo
do j=2,ndim+1
do i=1,ndim
if(i.eq.(j-1)) then
if(dabs(args(i)-0.d0).lt.eps) then
sim(i,j)=zdel
else
sim(i,j)=(1.d0+del)*args(i)
endif
else
sim(i,j)=args(i)
endif
vertj(i)=sim(i,j)
enddo
fsim(j)=f2min(vertj)
enddo
nfeval=nfeval+ndim
! rearrange the simplex points according to increasing function values
! best point first, worst point last
! the first column of sim contains the coordinates of the best point, etc.
call quick_sort(fsim,order)
call sort_mat(sim,order)
niter=0
if(plevel.gt.1) then
write(*,'(25x,3(a22))') &
'energy of worst point','energy change','step change'
write(*,*)
endif
do while(nfeval<maxfun .and. niter<maxiter)
argtol = fxtol(sim)
functol = fftol(fsim)
if(argtol.le.xtol.and.functol.le.ftol) exit ! convergence reached
! average arguments values without the worst point in the simplex
call fxavg(sim,xavg)
call transform(sim,xavg,xr)
fxr = f2min(xr)
nfeval = nfeval+1
doshrink = .False.
if(fxr.lt.fsim(1)) then
call expansion(sim,xavg,xe)
fxe = f2min(xe)
nfeval = nfeval+1
if(fxe.lt.fxr) then
call columnR(sim,xe)
fsim(ndim+1) = fxe
else
call columnR(sim,xr)
fsim(ndim+1) = fxr
endif
else
if(fxr.lt.fsim(ndim)) then
call columnR(sim,xr)
fsim(ndim+1) = fxr
else
if(fxr.lt.fsim(ndim+1)) then
call contraction(sim,xavg,xc)
fxc = f2min(xc)
nfeval = nfeval+1
if(fxc.le.fxr) then
call columnR(sim,xc)
fsim(ndim+1) = fxc
else
doshrink = .True.
endif
else
call icontraction(sim,xavg,xcc)
fxcc = f2min(xcc)
nfeval = nfeval+1
if(fxcc.lt.fsim(ndim+1)) then
call columnR(sim,xcc)
fsim(ndim+1) = fxcc
else
doshrink = .True.
endif
if(doshrink) then
do i=2,ndim+1
call shrink(sim)
fsim(i) = f2min(sim(1:,i))
enddo
nfeval = nfeval+ndim
endif
endif
endif
endif
! again rearrange the simplex points according to increasing function values
! best point first, worst point last
! the first column of sim contains the coordinates of the best point, etc.
call quick_sort(fsim,order)
call sort_mat(sim,order)
niter = niter+1
if(plevel.gt.1) then
write(*,'(5x,a,i5,3(2x,f20.12))') &
'Iteration info ',niter,fsim(ndim+1),functol,argtol
endif
fopt = minval(fsim)
args = sim(1:,1)
enddo
if(nfeval.ge.maxfun) then
write(*,*) 'Maximum number of function evaluations &
has been exceeded.'
else
if(niter.ge.maxiter) then
write(*,*) 'Maximum number of iterations &
has been exceeded.'
else
write(*,*) 'Optimization terminated successfully.'
bool = .True.
endif
endif
deallocate(vertj)
deallocate(xavg)
deallocate(xr)
deallocate(xe)
deallocate(xc)
deallocate(xcc)
deallocate(fsim)
deallocate(order)
deallocate(sim)
deallocate(tmp)
!************************************************************************
contains
!************************************************************************
!************************************************************************
subroutine sort_mat(A,order)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! this subroutine rearrange its columns based on the given order
implicit none
double precision, dimension(:,:), intent(inout) :: A
integer, dimension(size(A)+1), intent(in) :: order
! local variables
integer :: i,j,ndim
double precision, dimension(:,:), allocatable :: tmp
ndim=size(A,1)
allocate(tmp(ndim,ndim+1))
do j=1,ndim+1
do i=1,ndim
tmp(i,j) = A(i,order(j))
enddo
enddo
A = tmp
deallocate(tmp)
end subroutine sort_mat
!************************************************************************
subroutine shrink(A)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! this subroutine shrinks the simplex around the best point
implicit none
double precision, dimension(:,:), intent(inout) :: A
! local variables
integer :: i,j,ndim
ndim=size(A,1)
do j=2,ndim+1
do i=1,ndim
A(i,j) = A(i,1) + sigma*(A(i,j) - A(i,1))
enddo
enddo
if(plevel.gt.2) write(*,*) 'shrink'
end subroutine shrink
!************************************************************************
subroutine columnR(A,column)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! we only care about the last column - that is the worst point
! this subroutine replaces it with the vector column
implicit none
double precision, dimension(:,:), intent(inout) :: A
double precision, dimension(size(A,1)), intent(in) :: column
! local variables
integer :: i,ndim
ndim=size(A,1)
do i=1,ndim
A(i,ndim+1) = column(i)
enddo
end subroutine columnR
!************************************************************************
subroutine icontraction(A,vec,vertex)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! we only care about the last column - that is the worst point
! this subroutine transform it using vec to a hopefully better place
! vertex is the new point
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(size(A,1)), intent(in) :: vec
double precision, dimension(size(A,1)), intent(out) :: vertex
! local variables
integer :: i,ndim
ndim=size(A,1)
do i=1,ndim
vertex(i) = (1.d0-psi)*vec(i) + psi*A(i,ndim+1)
enddo
if(plevel.gt.2) write(*,*) 'icontraction'
end subroutine icontraction
!************************************************************************
subroutine contraction(A,vec,vertex)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! we only care about the last column - that is the worst point
! this subroutine transform it using vec to a hopefully better place
! vertex is the new point
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(size(A,1)), intent(in) :: vec
double precision, dimension(size(A,1)), intent(out) :: vertex
! local variables
integer :: i,ndim
ndim=size(A,1)
do i=1,ndim
vertex(i) = (1.d0+psi*rho)*vec(i) - psi*rho*A(i,ndim+1)
enddo
if(plevel.gt.2) write(*,*) 'contraction'
end subroutine contraction
!************************************************************************
subroutine expansion(A,vec,vertex)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! we only care about the last column - that is the worst point
! this subroutine transform it using vec to a hopefully better place
! vertex is the new point
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(size(A,1)), intent(in) :: vec
double precision, dimension(size(A,1)), intent(out) :: vertex
! local variables
integer :: i,ndim
ndim=size(A,1)
do i=1,ndim
vertex(i) = (1.d0+rho*chi)*vec(i) - rho*chi*A(i,ndim+1)
enddo
if(plevel.gt.2) write(*,*) 'expansion'
end subroutine expansion
!************************************************************************
subroutine transform(A,vec,vertex)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! we only care about the last column - that is the worst point
! this subroutine transform it using vec to a hopefully better place
! vertex is the new point
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(size(A,1)), intent(in) :: vec
double precision, dimension(size(A,1)), intent(out) :: vertex
! local variables
integer :: i,ndim
ndim=size(A,1)
do i=1,ndim
vertex(i) = (1.d0+rho)*vec(i) - rho*A(i,ndim+1)
enddo
if(plevel.gt.2) write(*,*) 'transform'
end subroutine transform
!************************************************************************
double precision function fftol(y)
! y is a (ndim+1) dimension vector and it contains
! the fucntion values at the vertices
! this function returns the largest difference
! relative to the lowest function value
implicit none
double precision, dimension(:), intent(in) :: y
! local variables
integer :: i,ndim
double precision, dimension(size(y)-1) :: z
ndim=size(y)
do i=2,ndim
z(i-1)=dabs(y(i)-y(1))
enddo
fftol=maxval(z) ! it retuns the maximum of the vector z
return
end function fftol
!************************************************************************
double precision function fxtol(A)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! this function returns the maximum displacement of the
! x-coordinates relative to the first point
implicit none
double precision, dimension(:,:), intent(in) :: A
! local variables
integer :: i,j,ndim
double precision, dimension(size(A,1),size(A,1)) :: B
ndim=size(A,1)
do j=2,ndim+1
do i=1,ndim
B(i,j-1)=dabs(A(i,j)-A(i,1))
enddo
enddo
fxtol=maxval(B) ! it retuns the maximum of the array B
return
end function fxtol
!************************************************************************
subroutine fxavg(A,avg)
! A is a (ndim)*(ndim+1) dimension matrix
! the columns of A contains the points of the simplex
! we don't care about the last column - that is the worst point
! this subroutine calculates the average of the arguments and
! return the average x-coordinates in vector avg
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(size(A,1)), intent(out) :: avg
! local variables
integer :: i,j,ndim
double precision :: sum_j
ndim=size(A,1)
do i=1,ndim
sum_j = 0.d0
do j=1,ndim
sum_j = sum_j + A(i,j)
enddo
avg(i) = sum_j/ndim
enddo
end subroutine fxavg
!************************************************************************
end subroutine simplex
!************************************************************************
!************************************************************************
recursive subroutine quick_sort(list, order)
! Quick sort routine from:
! Brainerd, W.S., Goldberg, C.H. & Adams, J.C. (1990) "Programmer's Guide to
! Fortran 90", McGraw-Hill ISBN 0-07-000248-7, pages 149-150.
! Modified by Alan Miller to include an associated integer array which gives
! the positions of the elements in the original order.
implicit none
double precision, dimension (:), intent(inout) :: list
integer, dimension (:), intent(out) :: order
! local variable
integer :: i
do i = 1, size(list)
order(i) = i
enddo
call quick_sort_1(1, size(list))
!************************************************************************
contains
!************************************************************************
recursive subroutine quick_sort_1(left_end, right_end)
integer, intent(in) :: left_end, right_end
! local variables
integer :: i, j, itemp
double precision :: reference, temp
integer, parameter :: max_simple_sort_size = 6
if (right_end < left_end + max_simple_sort_size) then
! Use interchange sort for small lists
call interchange_sort(left_end, right_end)
else
! Use partition ("quick") sort
reference = list((left_end + right_end)/2)
i = left_end - 1; j = right_end + 1
do
! Scan list from left end until element >= reference is found
do
i = i + 1
if (list(i) >= reference) exit
enddo
! Scan list from right end until element <= reference is found
do
j = j - 1
if (list(j) <= reference) exit
enddo
if (i < j) then
! Swap two out-of-order elements
temp = list(i); list(i) = list(j); list(j) = temp
itemp = order(i); order(i) = order(j); order(j) = itemp
elseif (i == j) then
i = i + 1
exit
else
exit
endif
enddo
if (left_end < j) call quick_sort_1(left_end, j)
if (i < right_end) call quick_sort_1(i, right_end)
endif
end subroutine quick_sort_1
!************************************************************************
!************************************************************************
subroutine interchange_sort(left_end, right_end)
integer, intent(in) :: left_end, right_end
! local variables
integer :: i, j, itemp
double precision :: temp
do i = left_end, right_end - 1
do j = i+1, right_end
if (list(i) > list(j)) then
temp = list(i); list(i) = list(j); list(j) = temp
itemp = order(i); order(i) = order(j); order(j) = itemp
endif
enddo
enddo
end subroutine interchange_sort
!************************************************************************
end subroutine quick_sort
!************************************************************************
!************************************************************************
subroutine build_unit_Hessian(H)
!************************************************************************
implicit none
double precision,dimension(:,:),intent(inout) :: H
!local
integer :: i,j,ndim
ndim = size(H,1)
do j=1,ndim ! initialization of arrays
do i=1,ndim
if(i .eq. j) then
H(i,j) = 1.d0
else
H(i,j) = 0.d0
endif
enddo
enddo
end subroutine build_unit_Hessian
!************************************************************************
!************************************************************************
subroutine get_Lindh_params(natoms,atnums,i,j,alpha,r)
!************************************************************************
implicit none
integer :: natoms
integer, intent(in) :: atnums(natoms)
integer, intent(in) :: i,j
double precision,intent(out) :: alpha,r
!local
double precision, dimension(3,3) :: A_ij,R_ij
A_ij = reshape((/1.0000,0.3949,0.3949,&
0.3949,0.2800,0.2800,&
0.3949,0.2800,0.2800/), shape(A_ij))
R_ij = reshape((/1.35,2.10,2.53,&
2.10,2.87,3.40,&
2.53,3.40,3.40/), shape(R_ij))
if(i.gt.natoms) then
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
elseif(atnums(i).le.2) then
if(j.gt.natoms) then
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
elseif(atnums(j).le.2) then
alpha = A_ij(1,1)
r = R_ij(1,1)
elseif(atnums(j).le.10) then
alpha = A_ij(1,2)
r = R_ij(1,2)
elseif(atnums(j).le.18) then
alpha = A_ij(1,3)
r = R_ij(1,3)
else
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
write(*,*)'No Lindh parameter is available for this atom:', &
atnums(j)
endif
elseif(atnums(i).le.10) then
if(j.gt.natoms) then
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
elseif(atnums(j).le.2) then
alpha = A_ij(2,1)
r = R_ij(2,1)
elseif(atnums(j).le.10) then
alpha = A_ij(2,2)
r = R_ij(2,2)
elseif(atnums(j).le.18) then
alpha = A_ij(2,3)
r = R_ij(2,3)
else
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
write(*,*)'No Lindh parameter is available for this atom:', &
atnums(j)
endif
elseif(atnums(i).le.18) then
if(j.gt.natoms) then
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
elseif(atnums(j).le.2) then
alpha = A_ij(3,1)
r = R_ij(3,1)
elseif(atnums(j).le.10) then
alpha = A_ij(3,2)
r = R_ij(3,2)
elseif(atnums(j).le.18) then
alpha = A_ij(3,3)
r = R_ij(3,3)
else
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
write(*,*)'No Lindh parameter is available for this atom:', &
atnums(j)
endif
else
alpha = 0.d0 ! no Lindh parameter for this atom
r = 0.d0 ! no Lindh parameter for this atom
write(*,*)'No Lindh parameter is available for this atom:', &
atnums(i)
endif
end subroutine get_Lindh_params
!************************************************************************
!************************************************************************
subroutine build_Lindh_Hessian(H,red_int_matrix,coords,atnums)
!************************************************************************
implicit none
double precision,dimension(:,:),intent(inout) :: H
integer,dimension(:,:),intent(in) :: red_int_matrix
double precision,dimension(:,:),intent(in) :: coords
integer,dimension(:),intent(in) :: atnums
!local
double precision, parameter :: k_r = 0.45 !Lindh et al, CPL, 241, 423 (1995)
double precision, parameter :: k_f = 0.15 !Lindh et al, CPL, 241, 423 (1995)
double precision, parameter :: k_t = 0.005 !Lindh et al, CPL, 241, 423 (1995)
integer :: i,j,k,l,m,ndim,itype,ncol,natoms
double precision :: r_ij,r_jk,r_kl
double precision :: a_ij,a_jk,a_kl
double precision :: rho_ij,rho_jk,rho_kl
double precision :: r_refij,r_refjk,r_refkl
double precision :: tmp
ndim = size(H,1)
ncol = size(H,2)
natoms=size(atnums)
H=0.0d0
do m=1,ndim
itype=red_int_matrix(1,m) ! type of internal coord
i =red_int_matrix(2,m) ! 1st atom
j =red_int_matrix(3,m) ! 2nd atom
k =red_int_matrix(4,m) ! 3rd atom
l =red_int_matrix(5,m) ! 4th atom
if(itype.eq.1 .or. itype.eq.4 .or. itype.eq.6 .or.&
itype.eq.5 .or. itype.eq.7) then ! internal bond
call get_Lindh_params(natoms,atnums,i,j,a_ij,r_refij)
r_ij = distance2(coords(1:3,i),coords(1:3,j))
rho_ij = dexp(a_ij*(r_refij**2 - r_ij**2))
tmp = (k_r*rho_ij)
elseif(itype.eq.2) then ! angle
call get_Lindh_params(natoms,atnums,i,j,a_ij,r_refij)
call get_Lindh_params(natoms,atnums,j,k,a_jk,r_refjk)
r_ij = distance2(coords(1:3,i),coords(1:3,j))
r_jk = distance2(coords(1:3,j),coords(1:3,k))
rho_ij = dexp(a_ij*(r_refij**2 - r_ij**2))
rho_jk = dexp(a_jk*(r_refjk**2 - r_jk**2))
tmp = (k_f*rho_ij*rho_jk)
elseif(itype.eq.3) then ! dihedral
call get_Lindh_params(natoms,atnums,i,j,a_ij,r_refij)
call get_Lindh_params(natoms,atnums,j,k,a_jk,r_refjk)
call get_Lindh_params(natoms,atnums,k,l,a_kl,r_refkl)
r_ij = distance2(coords(1:3,i),coords(1:3,j))
r_jk = distance2(coords(1:3,j),coords(1:3,k))
r_kl = distance2(coords(1:3,k),coords(1:3,l))
rho_ij = dexp(a_ij*(r_refij**2 - r_ij**2))
rho_jk = dexp(a_jk*(r_refjk**2 - r_jk**2))
rho_kl = dexp(a_kl*(r_refkl**2 - r_kl**2))
tmp = (k_t*rho_ij*rho_jk*rho_kl)
else
tmp = 1.0d0
endif
if(tmp .lt. 1.0d-8) tmp=1.0d0
if(ncol==1) then
H(m,1)=tmp
else
H(m,m)=tmp
endif
enddo
end subroutine build_Lindh_Hessian
!************************************************************************
!************************************************************************
subroutine build_initial_Hessian(H,red_int_matrix,coords)
!************************************************************************
implicit none
double precision,dimension(:,:),intent(inout) :: H
integer,dimension(:,:),intent(in) :: red_int_matrix
double precision,dimension(:,:),intent(in) :: coords
!local
integer :: i,j,k,l,m,n,ndim,itype
ndim = size(H,1)
do n=1,ndim ! initialization of arrays
do m=1,ndim
if(m .eq. n) then
itype=red_int_matrix(1,m) ! type of internal coord
i =red_int_matrix(2,m) ! 1st atom
j =red_int_matrix(3,m) ! 2nd atom
k =red_int_matrix(4,m) ! 3rd atom
l =red_int_matrix(5,m) ! 4th atom
if(itype.eq.1) then ! regular bond
H(m,m) = 1/0.5d0
endif
if(itype.eq.2) then ! angle
H(m,m) = 1/0.2d0
endif
if(itype.eq.3) then ! dihedral
H(m,m) = 1/0.1d0
endif
if(itype.eq.4) then ! auxiliary bond
H(m,m) = 1/0.2d0
endif
else
H(m,n) = 0.d0
endif
enddo
enddo
! call print_matrixR(H,9)
! call mrccend(1)
end subroutine build_initial_Hessian
!************************************************************************
!************************************************************************
subroutine get_Schlegel_param(atnums,i,j,B)
!************************************************************************
implicit none
integer,dimension(:),intent(in) :: atnums
integer, intent(in) :: i,j
double precision,intent(out) :: B
!local
integer :: natoms
double precision, dimension(3,3) :: B_ij
B_ij = reshape((/-0.244, 0.352, 0.660,&
0.352, 1.085, 1.522,&
0.660, 1.552, 2.068/), shape(B_ij))
natoms = size(atnums)
if(i.gt.natoms) then
B=0.0d0
elseif(atnums(i).le.2) then
if(j.gt.natoms) then
B = 0.0d0
elseif(atnums(j).le.2) then
B = B_ij(1,1)
elseif(atnums(j).le.10) then
B = B_ij(1,2)
elseif(atnums(j).le.18) then
B = B_ij(1,3)
else
B = 0.d0 ! no Schlegel parameter for this atom
write(*,*) &
'No Schlegel parameter is available for this atom:', &
atnums(j)
endif
elseif(atnums(i).le.10) then
if(j.gt.natoms) then
B=0.0d0
elseif(atnums(j).le.2) then
B = B_ij(2,1)
elseif(atnums(j).le.10) then
B = B_ij(2,2)
elseif(atnums(j).le.18) then
B = B_ij(2,3)
else
B = 0.d0 ! no Schlegel parameter for this atom
write(*,*) &
'No Schlegel parameter is available for this atom:', &
atnums(j)
endif
elseif(atnums(i).le.18) then
if(j.gt.natoms) then
B=0.0d0
elseif(atnums(j).le.2) then
B = B_ij(3,1)
elseif(atnums(j).le.10) then
B = B_ij(3,2)
elseif(atnums(j).le.18) then
B = B_ij(3,3)
else
B = 0.d0 ! no Schlegel parameter for this atom
write(*,*) &
'No Schlegel parameter is available for this atom:', &
atnums(j)
endif
else
B = 0.d0 ! no Schlegel parameter for this atom
write(*,*) &
'No Schlegel parameter is available for this atom:', &
atnums(j)
endif
end subroutine get_Schlegel_param
!************************************************************************
!************************************************************************
subroutine build_Schlegel_Hessian(H,red_int_matrix,coords,atnums)
!************************************************************************
implicit none
double precision,dimension(:,:),intent(inout) :: H
integer,dimension(:,:),intent(in) :: red_int_matrix
double precision,dimension(:,:),intent(in) :: coords
integer,dimension(:),intent(in) :: atnums
!local
integer :: i,j,k,l,m,n,ndim,itype,natoms
double precision :: r,r_cov,B,F
double precision, parameter :: fac = 1.3d0 !Schlegel, TCA, 66, 333 (1984) factror for minimal basis calcs
double precision, parameter :: A = 1.734d0 !Schlegel, TCA, 66, 333 (1984) constant A for bonds
double precision, parameter :: AbH = 0.160d0 !Schlegel, TCA, 66, 333 (1984) constant A for bends with hydrogens
double precision, parameter :: Ab = 0.250d0 !Schlegel, TCA, 66, 333 (1984) constant A for bends
double precision, parameter :: At = 0.0023d0 !Schlegel, TCA, 66, 333 (1984) constant A for torsions
double precision, parameter :: Bt = 0.0700d0 !Schlegel, TCA, 66, 333 (1984) constant B for torsions
double precision, parameter :: Aoup = 0.0450d0 !Schlegel, TCA, 66, 333 (1984) constant A for out of plane motions
! d = 1.d0-r1*(r2 x r3)/(|r1||r2||r3|)
! H = Aoup*d**4
ndim = size(H,1)
natoms=size(atnums)
do n=1,ndim ! initialization of arrays
do m=1,ndim
if(m .eq. n) then
itype=red_int_matrix(1,m) ! type of internal coord
i =red_int_matrix(2,m) ! 1st atom
j =red_int_matrix(3,m) ! 2nd atom
k =red_int_matrix(4,m) ! 3rd atom
l =red_int_matrix(5,m) ! 4th atom
if(itype.eq.bond_code .or. itype.eq.abond_code .or. &
itype.eq.fbond_code .or. itype.eq.afbond_code .or. &
itype.eq.hbond_code) then ! internal bond
call get_Schlegel_param(atnums,i,j,B)
r = distance2(coords(1:3,i),coords(1:3,j))
F = fac*A/((r-B)**3)
H(m,m) = F
endif
if(itype.eq.angle_code) then ! angle
if(i.gt.natoms .or. j.gt.natoms) then
F = fac*Ab
elseif(atnums(i).eq.1 .or. atnums(k).eq.1) then
F = fac*AbH
else
F = fac*Ab
endif
H(m,m) = F
endif
if(itype.eq.dangle_code) then ! dihedral
if(j.gt.natoms) then
r_cov = 0.0d0
else
r_cov = get_cov_radius(atnums(j))
endif
if(k.le.natoms) r_cov = r_cov + get_cov_radius(atnums(k))
r = distance2(coords(1:3,j),coords(1:3,k))
F = fac*(At - Bt*(r - r_cov))
H(m,m) = dabs(F)
endif
else
H(m,n) = 0.d0
endif
enddo
enddo
! call print_matrixR(H,9)
! call mrccend(1)
end subroutine build_Schlegel_Hessian
!************************************************************************
!************************************************************************
subroutine wrt_molden(niter,energy,gnorm,gmax)
! it writes energies and geometries suitable to append to file MOLDEN
implicit none
integer, intent(in) :: niter
double precision, intent(in) :: energy,gnorm,gmax
!local
character(len=512) :: si,scommand
if(niter .eq. 0) then
call ishell('echo "[GEOCONV]" > COORD.xyz.energies')
call ishell('echo "energy" >> COORD.xyz.energies')
open(cenergies,file='COORD.xyz.energies',position='append')
write(cenergies,103) energy
close(cenergies)
call ishell('echo "rms-force" >> COORD.xyz.rforce')
open(crmsforce,file='COORD.xyz.rforce',position='append')
write(crmsforce,103) gnorm
close(crmsforce)
call ishell('echo "max-force" >> COORD.xyz.mforce')
open(cmaxforce,file='COORD.xyz.mforce',position='append')
write(cmaxforce,103) gmax
close(cmaxforce)
call ishell('cp COORD.xyz COORD.xyz.0')
call ishell('echo "[GEOMETRIES] (XYZ)" > COORD.xyz.geometries')
call wrt_xyz_geoms(niter)
else
open(cenergies,file='COORD.xyz.energies',position='append')
write(cenergies,103) energy
close(cenergies)
open(crmsforce,file='COORD.xyz.rforce',position='append')
write(crmsforce,103) gnorm
close(crmsforce)
open(cmaxforce,file='COORD.xyz.mforce',position='append')
write(cmaxforce,103) gmax
close(cmaxforce)
si = int2char(niter)
scommand='cp COORD.xyz COORD.xyz.'//trim(adjustl(si))
call ishell(scommand)
call wrt_xyz_geoms(niter)
endif
103 format(f22.12,a2)
end subroutine wrt_molden
!************************************************************************
subroutine wrt_xyz_geoms(niter)
! it appends the current geometry to the file COORD.xyx.geometries
implicit none
integer, intent(in) :: niter
!local
character(len=512) :: fname,line,s10
integer :: i,natoms,l
fname = 'COORD.xyz.'//trim(adjustl(int2char(niter)))
open(unit=coordfile,file=fname,status='old') ! current geometry at step niter
open(unit=geomsfile,file='COORD.xyz.geometries',position='append')! all geometries concatenated to this file
read(coordfile,'(I10)') natoms ! first line, number of atoms
s10 = int2char(natoms)
l = len_trim(adjustl(s10))
write(geomsfile,'(a)') s10(1:l)
read(coordfile,104) line ! comment line
l = len_trim(adjustl(line))
write(geomsfile,'(a)') line(1:l)
do i=1,natoms
read(coordfile,104) line
l = len_trim(adjustl(line))
write(geomsfile,'(a)') line(1:l)
enddo
close(coordfile)
close(geomsfile)
104 format(a512)
end subroutine wrt_xyz_geoms
!************************************************************************
subroutine wrt_iter(niter)
! it writes the current iteration number into the file ITER
implicit none
integer, intent(in) :: niter
open(unit=iterfile,file='ITER') ! # interations is written in the file ITER
write(iterfile,*) niter
close(iterfile)
end subroutine wrt_iter
!************************************************************************
subroutine read_iter(niter)
!it reads the current iteration number from the file ITER
implicit none
integer, intent(out) :: niter
open(unit=iterfile,file='ITER') ! # interation number is read from the file ITER
read(iterfile,*) niter
close(iterfile)
end subroutine read_iter
!************************************************************************
subroutine outer_product(vecA,vecB,op)
! it returns the outer product, vecA'vecB, of vecA and vecB
! row vectors are assumed, therefore transpose of vecA times vecB will
! be returned (dimA,dimB)
implicit none
double precision, dimension(:), intent(in) :: vecA,vecB
double precision, dimension(size(vecA),size(vecB)) :: op
!local
integer i,j,dimA,dimB
dimA = size(vecA)
dimB = size(vecB)
do j=1,dimB
do i=1,dimA
op(i,j) = vecA(i) * vecB(j)
enddo
enddo
end subroutine outer_product
!************************************************************************
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! GDIIS ALGORITHM
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
subroutine diis_extrap(niter,ndmax,xk,gk,Hk,sk)
!************************************************************************
! DIIS extrapolation
! nact - iteration step
! nmax - dimension of the vector to be extrapolated
! pvec - the updated vector
! evec - the original vector
! ndmax - maximum no. of iterations
!************************************************************************
implicit none
integer,intent(in) :: niter,ndmax
double precision, dimension(:), intent(inout) :: xk,gk ! geometry and gradient
double precision, dimension(:,:), intent(in) :: Hk ! inverse Hessian matrix
double precision, dimension(:), intent(inout) :: sk ! step(error)
!local
double precision, dimension(:), allocatable :: pvec ! updated vector
double precision, dimension(:), allocatable :: evec ! original vector
integer :: nmax
double precision, dimension(:), allocatable :: cvec ! vector of coefficients
double precision, dimension(:,:), allocatable :: bmat ! scalar product B matrix
double precision, dimension(:,:), allocatable :: invbmat ! inverse of the B matrix
double precision, dimension(:), allocatable :: bvec ! inverse of the B matrix
integer, parameter :: diisfile=110,errfile=111,ifltln=8 !MRCC common file!
integer, parameter :: gdiisfile=112
nmax = size(xk)
allocate(pvec(nmax))
allocate(evec(nmax))
allocate(cvec(ndmax))
allocate(bmat(ndmax,ndmax))
allocate(invbmat(ndmax,ndmax))
allocate(bvec(ndmax*ndmax))
cvec = 0.d0
! evec = -1.d0*matrix_dot_vec(Hk,gk)
call dgemv('n',nmax,nmax,-1.0d0,Hk,nmax,gk,1,0.0d0,evec,1)
write(*,*) 'trust diis evec'
call print_vectorR(evec,9)
! evec = sk
write(*,*) 'trust diis xk'
call print_vectorR(xk,9)
write(*,*) 'trust diis gk'
call print_vectorR(gk,9)
pvec = xk
call diismod(niter,nmax,pvec,evec,gk,ndmax,diisfile,errfile,&
gdiisfile,ifltln,bmat,bvec,invbmat,cvec)
! xk = xk - 1.d0*matrix_dot_vec(Hk,gk)
call dgemv('n',nmax,nmax,-1.0d0,Hk,nmax,gk,1,1.0d0,xk,1)
write(*,*) 'trust diis xk after', cvec
call print_vectorR(xk,9)
write(*,*) 'trust diis gk'
call print_vectorR(gk,9)
deallocate(pvec)
deallocate(evec)
deallocate(cvec)
deallocate(bmat)
deallocate(invbmat)
deallocate(bvec)
end subroutine diis_extrap
!************************************************************************
!************************************************************************
subroutine diismod(nact,nmax,pvec,evec,gvec,ndmax,diisfile,&
errfile,gdiisfile,ifltln,bmat,bvec,invbmat,&
cvec)
!************************************************************************
!* DIIS extrapolation *
!* nact - iteration step
!* nmax - dimension of the vector to be extrapolated
!* pvec - the updated vector
!* evec - the original vector
!* ndmax - maximum no. of iterations
!************************************************************************
implicit none
integer nact,iact,i,j,nmax,ndmax,iact1,iact2,diisfile
integer gdiisfile,errfile,ifltln
integer ipiv(ndmax),ndiis
real*8 bmat(ndmax,ndmax),bvec(ndmax**2),invbmat(ndmax,ndmax),ddot
real*8 cvec(ndmax),sum,sum1,evec(nmax),pvec(nmax)
real*8 gvec(nmax)
real*8, allocatable :: scr(:)
!
allocate(scr(nmax))
ndiis=8
open(diisfile,status='unknown',access='direct',recl=nmax*ifltln)
open(errfile,status='unknown',access='direct',recl=nmax*ifltln)
open(gdiisfile,status='unknown',access='direct',recl=nmax*ifltln)
! Save amplitudes and calculate new error vector
! call dfillzero(pvec,nmax)
write(diisfile,rec=nact) pvec
! do j=1,nmax
! pvec(j)=pvec(j)-evec(j)
! enddo
! write(errfile,rec=nact) pvec
write(errfile,rec=nact) evec
write(gdiisfile,rec=nact) gvec
! Update the scalar product matrix (B matrix)
do iact1=max(1,nact-ndiis+1),nact
read(errfile,rec=iact1) pvec
do iact2=max(1,nact-ndiis+1),iact1
read(errfile,rec=iact2) scr
sum=ddot(nmax,pvec,1,scr,1)
bmat(iact1-max(0,nact-ndiis),iact2-max(0,nact-ndiis))=sum
bmat(iact2-max(0,nact-ndiis),iact1-max(0,nact-ndiis))=sum
enddo
enddo
! Invert B matrix
do j=1,min(nact,ndiis)
do i=1,j
bvec(i+(j-1)*j/2)=bmat(i,j)
enddo
enddo
call dsptrf('U',min(nact,ndiis),bvec,ipiv,invbmat,i)
call dsptri('U',min(nact,ndiis),bvec,ipiv,invbmat,i)
do j=1,min(nact,ndiis)
do i=1,j
invbmat(i,j)=bvec(i+(j-1)*j/2)
invbmat(j,i)=invbmat(i,j)
enddo
enddo
! Denominator
sum=0.d0
do i=1,min(nact,ndiis)
do j=1,min(nact,ndiis)
sum=sum+invbmat(i,j)
enddo
enddo
sum=1.d0/sum
! Coefficients
do i=1,min(nact,ndiis)
sum1=0.d0
do j=1,min(nact,ndiis)
sum1=sum1+invbmat(i,j)
enddo
cvec(i)=sum*sum1
enddo
! New amplitudes
do i=1,nmax
pvec(i)=0.d0
enddo
do iact=1,min(nact,ndiis)
sum=cvec(iact)
read(diisfile,rec=iact+max(0,nact-ndiis)) scr
do j=1,nmax
pvec(j)=pvec(j)+sum*scr(j)
enddo
enddo
! New gradient
! do i=1,nmax
! gvec(i)=0.d0
! enddo
! do iact=1,min(nact,ndiis)
! sum=cvec(iact)
! read(gdiisfile,rec=iact+max(0,nact-ndiis)) scr
! do j=1,nmax
! gvec(j)=gvec(j)+sum*scr(j)
! enddo
! enddo
close(diisfile)
close(errfile)
close(gdiisfile)
!
deallocate(scr)
return
end subroutine diismod
!***********************************************************************
!************************************************************************
subroutine gdiisdel(nact,nmax,ifltln)
!************************************************************************
! this subroutine deletes the last gdiis information
!* DIIS extrapolation *
!* nact - iteration step
!* nmax - dimension of the vector to be extrapolated
!* pvec - the updated vector
!* evec - the original vector
!* ndmax - maximum no. of iterations
!************************************************************************
implicit none
integer, intent(in) :: nact,nmax,ifltln
!local
integer, parameter :: diisfile=110,errfile=111, gdiisfile=112
integer, parameter :: diisftmp=210,errftmp=211, gdiisftmp=212
integer :: i,istat
real*8,allocatable :: pvec(:)
!
#if !defined (gfortran) && !defined (G95)
integer*4 system
external system
#endif
allocate(pvec(nmax))
open(diisfile, file='DIIS.geo', status='unknown',access='direct',recl=nmax*ifltln)
open(diisftmp, file='DIIS.tmp', status='unknown',access='direct',recl=nmax*ifltln)
open(errfile, file='DIISE.geo',status='unknown',access='direct',recl=nmax*ifltln)
open(errftmp, file='DIISE.tmp',status='unknown',access='direct',recl=nmax*ifltln)
open(gdiisfile,file='DIISG.geo',status='unknown',access='direct',recl=nmax*ifltln)
open(gdiisftmp,file='DIISG.tmp',status='unknown',access='direct',recl=nmax*ifltln)
do i=1,nact-1
read (diisfile, rec=i) pvec
write(diisftmp, rec=i) pvec
read (errfile, rec=i) pvec
write(errftmp, rec=i) pvec
read (gdiisfile, rec=i) pvec
write(gdiisftmp, rec=i) pvec
enddo
close(diisfile)
close(diisftmp)
close(errfile)
close(errftmp)
close(gdiisfile)
close(gdiisftmp)
!
istat=system('test -e DIIS.tmp')
if(istat.eq.0) istat=system('mv DIIS.tmp DIIS.geo')
istat=system('test -e DIISE.tmp')
if(istat.eq.0) istat=system('mv DIISE.tmp DIISE.geo')
istat=system('test -e DIISG.tmp')
if(istat.eq.0) istat=system('mv DIISG.tmp DIISG.geo')
deallocate(pvec)
return
end subroutine gdiisdel
!***********************************************************************
!************************************************************************
subroutine gdiismod(nact,nmax,pvec,evec,gvec,ndmax,ifltln,bmat,&
bvec,invbmat,cvec)
!************************************************************************
!* DIIS extrapolation *
!* nact - iteration step
!* nmax - dimension of the vector to be extrapolated
!* pvec - the updated vector
!* evec - the original vector
!* ndmax - maximum no. of iterations
!************************************************************************
implicit none
integer, intent(in) ::nact,nmax,ndmax,ifltln
!local
integer, parameter :: diisfile=110,errfile=111, gdiisfile=112
integer :: iact,i,j,iact1,iact2
integer :: ipiv(ndmax),ndiis
real*8 bmat(ndmax,ndmax),bvec(ndmax**2),invbmat(ndmax,ndmax),ddot
real*8 cvec(ndmax),sum,sum1,evec(nmax),pvec(nmax)
real*8 gvec(nmax)
real*8,allocatable :: scr(:)
!
allocate(scr(nmax))
ndiis=4
open(diisfile, file='DIIS.geo', status='unknown',access='direct',recl=nmax*ifltln)
open(errfile, file='DIISE.geo',status='unknown',access='direct',recl=nmax*ifltln)
open(gdiisfile,file='DIISG.geo',status='unknown',access='direct',recl=nmax*ifltln)
! Save amplitudes and calculate new error vector
! call dfillzero(pvec,nmax)
write(diisfile,rec=nact) pvec
! do j=1,nmax
! pvec(j)=pvec(j)-evec(j)
! enddo
! write(errfile,rec=nact) pvec
write(errfile,rec=nact) evec
write(gdiisfile,rec=nact) gvec
! Update the scalar product matrix (B matrix)
do iact1=max(1,nact-ndiis+1),nact
read(errfile,rec=iact1) pvec
do iact2=max(1,nact-ndiis+1),iact1
read(errfile,rec=iact2) scr
sum=ddot(nmax,pvec,1,scr,1)
bmat(iact1-max(0,nact-ndiis),iact2-max(0,nact-ndiis))=sum
bmat(iact2-max(0,nact-ndiis),iact1-max(0,nact-ndiis))=sum
enddo
enddo
! Invert B matrix
do j=1,min(nact,ndiis)
do i=1,j
bvec(i+(j-1)*j/2)=bmat(i,j)
enddo
enddo
call dsptrf('U',min(nact,ndiis),bvec,ipiv,invbmat,i)
call dsptri('U',min(nact,ndiis),bvec,ipiv,invbmat,i)
do j=1,min(nact,ndiis)
do i=1,j
invbmat(i,j)=bvec(i+(j-1)*j/2)
invbmat(j,i)=invbmat(i,j)
enddo
enddo
! Denominator
sum=0.d0
do i=1,min(nact,ndiis)
do j=1,min(nact,ndiis)
sum=sum+invbmat(i,j)
enddo
enddo
sum=1.d0/sum
! Coefficients
do i=1,min(nact,ndiis)
sum1=0.d0
do j=1,min(nact,ndiis)
sum1=sum1+invbmat(i,j)
enddo
cvec(i)=sum*sum1
enddo
! New amplitudes
do i=1,nmax
pvec(i)=0.d0
enddo
do iact=1,min(nact,ndiis)
sum=cvec(iact)
read(diisfile,rec=iact+max(0,nact-ndiis)) scr
do j=1,nmax
pvec(j)=pvec(j)+sum*scr(j)
enddo
enddo
! New gradient
do i=1,nmax
gvec(i)=0.d0
enddo
do iact=1,min(nact,ndiis)
sum=cvec(iact)
read(gdiisfile,rec=iact+max(0,nact-ndiis)) scr
do j=1,nmax
gvec(j)=gvec(j)+sum*scr(j)
enddo
enddo
write(*,*)'inside gdiismod'
call print_vectorR(gvec,9)
close(diisfile)
close(errfile)
close(gdiisfile)
!
return
deallocate(scr)
end subroutine gdiismod
!***********************************************************************
!************************************************************************
subroutine gdiismod2(nact,nmax,pvec,evec,gvec,ndmax,ifltln,bmat,&
bvec,invbmat,cvec)
!************************************************************************
!* DIIS extrapolation *
!* nact - iteration step
!* nmax - dimension of the vector to be extrapolated
!* pvec - the updated vector
!* evec - the original vector
!* ndmax - maximum no. of iterations
!************************************************************************
implicit none
integer, intent(in) ::nact,nmax,ndmax,ifltln
!local
integer, parameter :: diisfile=110,errfile=111, gdiisfile=112
integer :: iact,i,j,iact1,iact2
integer :: ipiv(ndmax),ndiis
real*8 bmat(ndmax,ndmax),bvec(ndmax**2),invbmat(ndmax,ndmax),ddot
real*8 cvec(ndmax),sum,sum1,evec(nmax),pvec(nmax)
real*8 gvec(nmax)
real*8,allocatable :: scr(:)
!
allocate(scr(nmax))
ndiis=4
open(diisfile, file='DIIS.geo', status='unknown',access='direct',recl=nmax*ifltln)
open(errfile, file='DIISE.geo',status='unknown',access='direct',recl=nmax*ifltln)
open(gdiisfile,file='DIISG.geo',status='unknown',access='direct',recl=nmax*ifltln)
! Save amplitudes and calculate new error vector
! call dfillzero(pvec,nmax)
write(diisfile,rec=nact) pvec
! do j=1,nmax
! pvec(j)=pvec(j)-evec(j)
! enddo
! write(errfile,rec=nact) pvec
write(errfile,rec=nact) evec
write(gdiisfile,rec=nact) gvec
! Update the scalar product matrix (B matrix)
do iact1=max(1,nact-ndiis+1),nact
read(errfile,rec=iact1) pvec
do iact2=max(1,nact-ndiis+1),iact1
read(errfile,rec=iact2) scr
sum=ddot(nmax,pvec,1,scr,1)
bmat(iact1-max(0,nact-ndiis),iact2-max(0,nact-ndiis))=sum
bmat(iact2-max(0,nact-ndiis),iact1-max(0,nact-ndiis))=sum
enddo
enddo
! Invert B matrix
do j=1,min(nact,ndiis)
do i=1,j
bvec(i+(j-1)*j/2)=bmat(i,j)
enddo
enddo
call dsptrf('U',min(nact,ndiis),bvec,ipiv,invbmat,i)
call dsptri('U',min(nact,ndiis),bvec,ipiv,invbmat,i)
do j=1,min(nact,ndiis)
do i=1,j
invbmat(i,j)=bvec(i+(j-1)*j/2)
invbmat(j,i)=invbmat(i,j)
enddo
enddo
! Denominator
sum=0.d0
do i=1,min(nact,ndiis)
do j=1,min(nact,ndiis)
sum=sum+invbmat(i,j)
enddo
enddo
sum=1.d0/sum
! Coefficients
do i=1,min(nact,ndiis)
sum1=0.d0
do j=1,min(nact,ndiis)
sum1=sum1+invbmat(i,j)
enddo
cvec(i)=sum*sum1
enddo
! New amplitudes
do i=1,nmax
pvec(i)=0.d0
enddo
do iact=1,min(nact,ndiis)
sum=cvec(iact)
read(diisfile,rec=iact+max(0,nact-ndiis)) scr
do j=1,nmax
pvec(j)=pvec(j)+sum*scr(j)
enddo
enddo
!csonti print vector differences
do iact=1,min(nact,ndiis)
read(diisfile,rec=iact+max(0,nact-ndiis)) scr
write(*,*)'vec diffs trust', dot_product(pvec-scr,pvec-scr)
enddo
!csonti
! New gradient
do i=1,nmax
gvec(i)=0.d0
enddo
do iact=1,min(nact,ndiis)
sum=cvec(iact)
read(gdiisfile,rec=iact+max(0,nact-ndiis)) scr
do j=1,nmax
gvec(j)=gvec(j)+sum*scr(j)
enddo
enddo
write(*,*)'inside gdiismod'
call print_vectorR(gvec,9)
close(diisfile)
close(errfile)
close(gdiisfile)
!
deallocate(scr)
return
end subroutine gdiismod2
!***********************************************************************
!************************************************************************
subroutine gdiis_del(niter,ndim)
!************************************************************************
implicit none
integer,intent(in) :: niter,ndim
!local
integer, parameter :: ifltln=8 !MRCC common file!
call gdiisdel(niter,ndim,ifltln)
end subroutine gdiis_del
!************************************************************************
!************************************************************************
subroutine gdiis_extrap2(niter,ndmax,xk,gk,ek)
!************************************************************************
implicit none
integer,intent(in) :: niter,ndmax
double precision, dimension(:), intent(inout) :: xk,gk ! geometry and gradient
double precision, dimension(:), intent(in) :: ek ! error vector
!local
integer :: nmax
double precision, dimension(:), allocatable :: cvec ! vector of coefficients
double precision, dimension(:,:), allocatable :: bmat ! scalar product B matrix
double precision, dimension(:,:), allocatable :: invbmat ! inverse of the B matrix
double precision, dimension(:), allocatable :: bvec ! inverse of the B matrix
integer, parameter :: ifltln=8 !MRCC common file!
nmax = size(xk)
allocate(cvec(ndmax))
allocate(bmat(ndmax,ndmax))
allocate(invbmat(ndmax,ndmax))
allocate(bvec(ndmax*ndmax))
cvec = 0.d0
! write(*,*) 'trust before diis ek'
! call print_vectorR(ek,9)
! write(*,*) 'trust before diis xk'
! call print_vectorR(xk,9)
! write(*,*) 'trust before diis gk'
! call print_vectorR(gk,9)
call gdiismod2(niter,nmax,xk,ek,gk,ndmax,ifltln,&
bmat,bvec,invbmat,cvec)
! write(*,*) 'trust after diis ek'
! call print_vectorR(ek,9)
! write(*,*) 'trust after diis xk'
! call print_vectorR(xk,9)
! write(*,*) 'trust after diis gk'
! call print_vectorR(gk,9)
write(*,*) 'trust diis coefficients'
call print_vectorR(cvec,9)
deallocate(cvec)
deallocate(bmat)
deallocate(invbmat)
deallocate(bvec)
end subroutine gdiis_extrap2
!************************************************************************
!************************************************************************
subroutine gdiis_extrap(niter,ndmax,xk,gk,ek)
!************************************************************************
implicit none
integer,intent(in) :: niter,ndmax
double precision, dimension(:), intent(inout) :: xk,gk ! geometry and gradient
double precision, dimension(:), intent(in) :: ek ! error vector
!local
integer :: nmax
double precision, dimension(:), allocatable :: cvec ! vector of coefficients
double precision, dimension(:,:), allocatable :: bmat ! scalar product B matrix
double precision, dimension(:,:), allocatable :: invbmat ! inverse of the B matrix
double precision, dimension(:), allocatable :: bvec ! inverse of the B matrix
integer, parameter :: ifltln=8 !MRCC common file!
nmax = size(xk)
allocate(cvec(ndmax))
allocate(bmat(ndmax,ndmax))
allocate(invbmat(ndmax,ndmax))
allocate(bvec(ndmax*ndmax))
cvec = 0.d0
! write(*,*) 'trust before diis ek'
! call print_vectorR(ek,9)
! write(*,*) 'trust before diis xk'
! call print_vectorR(xk,9)
! write(*,*) 'trust before diis gk'
! call print_vectorR(gk,9)
call gdiismod(niter,nmax,xk,ek,gk,ndmax,ifltln,&
bmat,bvec,invbmat,cvec)
! write(*,*) 'trust after diis ek'
! call print_vectorR(ek,9)
! write(*,*) 'trust after diis xk'
! call print_vectorR(xk,9)
! write(*,*) 'trust after diis gk'
! call print_vectorR(gk,9)
write(*,*) 'trust diis coefficients'
call print_vectorR(cvec,9)
deallocate(cvec)
deallocate(bmat)
deallocate(invbmat)
deallocate(bvec)
end subroutine gdiis_extrap
!************************************************************************
!************************************************************************
subroutine gdiis(fegrad,args,Hk,&
ftol,gtol,xtol,maxiter,maxfun,finit,&
fopt,niter,nfeval,ngeval,plevel,lmolden,bool)
!************************************************************************
! The Broyden-Fletcher-Goldfarb-Shanno algorithm;
! written by J Csontos
!
! Hk is the inverse of the Hessian, see Nocedal and Wright, equation 8.16
implicit none
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: eta=1.0d-3
! double precision, parameter :: r=1.0d-2
! double precision, parameter :: etol=1.0d-6
! local variables
integer :: i,ndim
double precision :: gnorm,snorm,gmax,smax
double precision :: pred,ared,Delta_k
double precision :: fval,fval_prev
double precision, dimension(:), allocatable :: gfk,gfk_prev,xk,xk_prev
double precision, dimension(:), allocatable :: sk,ek,yk,Bs
double precision, dimension(:), allocatable :: results
double precision, dimension(:,:), allocatable :: Hk,Pmat,Pinv
logical :: notConverged,lBaker
interface
subroutine fegrad(z,fegrad_arr)
double precision, dimension(:), intent(inout) :: z
double precision, dimension(size(z)+1) :: fegrad_arr
end subroutine
end interface
! Atest = reshape((/1,2,4,0,-1,1,2,3,8/), shape(Atest))
! call matrix_inverse(Atest,Ctest)
! Atest = matmul(Atest,Ctest)
! write(*,*) ' matrix test i/o:'
! call print_matrixR(Ctest,3)
! write(*,*) ' matrix test i/o:'
! call print_matrixR(Atest,3)
! call mrccend(1)
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
allocate(gfk(size(args)))
allocate(gfk_prev(size(args)))
allocate(xk(size(args)))
allocate(xk_prev(size(args)))
allocate(sk(size(args)))
allocate(ek(size(args)))
allocate(yk(size(args)))
allocate(Bs(size(args)))
allocate(results(size(args)+1))
allocate(Hk(size(args),size(args)))
allocate(Pmat(size(args),size(args)))
allocate(Pinv(size(args),size(args)))
ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
nfeval = 0
ngeval = 0
niter = 0
bool = .False.
lBaker = .True. ! Baker's convergence criteria are set
! call print_matrixR(Hk,9)
call print_inverse(Hk)
call fegrad(args,results)
finit = results(ndim+1) ! initial fucntion value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
xk = args
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,0.d0,0.d0,0.d0
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
niter = niter+1
! ek = -1.d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,ek,1)
write(*,*)'Iteration info gfk*ek',&
dot_product(gfk,ek),vecnorm(ek,2)
write(*,*)'vector ek:'
call print_vectorR(ek,9)
! alpha = direction_alpha(ek,gfk)
gfk_prev=gfk
xk_prev=xk
call gdiis_extrap(niter,20,xk,gfk,ek) ! xk, gfk are updated
call read_Pmat(ndim,Pmat)
write(*,*) 'Pmat*Pinv i/o:'
! gfk = matrix_dot_vec(Pmat,gfk)
call dgemv('n',ndim,ndim,1.0d0,Pmat,ndim,gfk,1,0.0d0,sk,1)
call dcopy(ndim,sk,1,gfk,1)
! sk = - 1.0d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,sk,1)
xk = xk + sk
call quadratic(sk,gfk_prev,Hk,pred,Bs) !csonti test
call fegrad(xk,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
gfk(i)= results(i)
yk(i) = gfk(i) - gfk_prev(i)
enddo
sk = xk - xk_prev
ared = fval_prev - fval ! actual reduction in function value
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
if( gmax <= 3.d-4 .and. (ared<=1.d-6 .or. smax<=3.d-4) ) then
notConverged = .False.
endif
else
if( gnorm <= gtol) notConverged = .False.
endif
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(niter.eq.1) then
write(*,'(5x,a)') &
'All quantities are in atomic units.'
write(*,'(5x,a,a5,6(a22))') &
'Iteration info ','step','energy','rms force','max force',&
'rms step','max step','energy change'
endif
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
! call bfgs_update_damped(sk,yk,Hk,q,Bs)
! sk = matrix_dot_vec(Pmat,sk)
call dgemv('n',ndim,ndim,1.0d0,Pmat,ndim,sk,1,0.0d0,pinv,1)
call dcopy(ndim,pinv,1,sk,1)
call bfgs_update(sk,yk,Hk)
! write(*,*) 'Projection matrix after i/o:'
! call print_matrixR(Pmat,10)
! write(*,*) 'Projection matrix inverse after i/o:'
! call print_matrixR(Pinv,10)
! One = matmul(Hk,Pinv)
! write(*,*) 'Pmat*Pinv i/o:'
! call print_matrixR(One,10)
! Hk=matmul(Hk,Pmat)
! Hk=matmul(Pmat,Hk)
call matrix_pseudoinverse(Pmat,Pinv)
call dgemm('n','n',ndim,ndim,ndim,1.0d0,Hk,ndim,Pinv,ndim,&
0.0d0,Pmat,ndim)
call dgemm('n','n',ndim,ndim,ndim,1.0d0,Pinv,ndim,Pmat,ndim,&
0.0d0,Hk,ndim)
! Hk=matmul(Hk,Pinv)
! Hk=matmul(Pinv,Hk)
! call matrix_inverse(Pmat,Hk)
! call print_inverse(Hk)
enddo
fopt = fval
args = xk
if(.not. notConverged .and. &
niter .lt. maxiter) then
bool=.True.
if(lmolden) then
call ishell('cat COORD.xyz.energies >> MOLDEN')
call ishell('cat COORD.xyz.rforce >> MOLDEN')
call ishell('cat COORD.xyz.mforce >> MOLDEN')
call ishell('cat COORD.xyz.geometries >> MOLDEN')
call ishell('rm -f COORD.xyz.* COORD.xyz.energies')
call ishell('rm -f COORD.xyz.rforce COORD.xyz.mforce')
call ishell('rm -f COORD.xyz.geometries')
endif
endif
deallocate(gfk)
deallocate(gfk_prev)
deallocate(xk)
deallocate(xk_prev)
deallocate(sk)
deallocate(ek)
deallocate(yk)
deallocate(Bs)
deallocate(results)
deallocate(Hk)
deallocate(Pmat)
deallocate(Pinv)
end subroutine gdiis
!************************************************************************
!************************************************************************
subroutine gdiis2(fegrad,args,Hk,&
ftol,gtol,xtol,maxiter,maxfun,finit,&
fopt,niter,nfeval,ngeval,plevel,lmolden,bool)
!************************************************************************
! The Broyden-Fletcher-Goldfarb-Shanno algorithm;
! written by J Csontos
!
! Hk is the inverse of the Hessian, see Nocedal and Wright, equation 8.16
implicit none
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: eta=1.0d-3
! double precision, parameter :: r=1.0d-2
! double precision, parameter :: etol=1.0d-6
! local variables
integer :: i,ndim,nditer
double precision :: gnorm,snorm,gmax,smax
double precision :: pred,ared,Delta_k
double precision :: fval,fval_prev
double precision, dimension(:), allocatable :: gfk,gfk_prev,xk
double precision, dimension(:), allocatable :: sk,ek,yk,xk_prev
double precision, dimension(:), allocatable :: results
double precision, dimension(:,:), allocatable :: Hk
logical :: notConverged,lBaker
interface
subroutine fegrad(z,fegrad_arr)
double precision, dimension(:), intent(inout) :: z
double precision, dimension(size(z)+1) :: fegrad_arr
end subroutine
end interface
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
allocate(gfk(size(args)))
allocate(gfk_prev(size(args)))
allocate(xk(size(args)))
allocate(xk_prev(size(args)))
allocate(sk(size(args)))
allocate(ek(size(args)))
allocate(yk(size(args)))
allocate(results(size(args)+1))
allocate(Hk(size(args),size(args)))
ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
nfeval = 0
ngeval = 0
niter = 0
nditer = 0
bool = .False.
lBaker = .True. ! Baker's convergence criteria are set
! call print_matrixR(Hk,9)
call fegrad(args,results)
finit = results(ndim+1) ! initial fucntion value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
xk = args
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,0.d0,0.d0,0.d0
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
niter = niter+1
nditer = nditer+1
! ek = -1.d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,ek,1)
! call dogleg(Delta_k,ek,gfk,Hk,sk,pred,Bs) ! paramater pred is equal to the predicted reduction in the function value
! write(*,*)'Iteration info gfk*ek', dot_product(gfk,ek)
! alpha = direction_alpha(ek,gfk)
gfk_prev=gfk
xk_prev=xk
if(niter>0) call gdiis_extrap2(nditer,20,xk,gfk,ek) ! xk, gfk are updated
! sk = - 1.0d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,sk,1)
! if(vecnorm(sk,2)>Delta_k ) then
! sk = (Delta_k/vecnorm(ek,2))*sk
! endif
! write(*,*) 'trust gdiis predicted, s-length', pred,vecnorm(sk,2)
xk = xk + sk
sk = xk - xk_prev
! call quadratic(sk,gfk_prev,Hk,pred,Bs) !csonti test
write(*,*) 'trust gdiis predicted, s-length', pred,vecnorm(sk,2)
! if(vecnorm(xk-xk_prev,2)>Delta_k ) then
! sk = (Delta_k/vecnorm(xk-xk_prev,2))*(xk-xk_prev)
! xk = xk_prev + sk
! endif
call fegrad(xk,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
gfk(i)= results(i)
yk(i) = gfk(i) - gfk_prev(i)
enddo
ared = fval_prev - fval ! actual reduction in function value
! if(ared>0) then
! nditer = nditer + 1
! call gdiis_extrap(nditer,20,sk,yk,ek)
! endif
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
if( gmax <= 3.d-4 .and. (ared<=1.d-6 .or. smax<=3.d-4) ) then
notConverged = .False.
endif
else
if( gnorm <= gtol) notConverged = .False.
endif
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(niter.eq.1) then
write(*,'(5x,a)') &
'All quantities are in atomic units.'
write(*,'(5x,a,a5,6(a22))') &
'Iteration info ','step','energy','rms force','max force',&
'rms step','max step','energy change'
endif
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
! call quadratic(sk,gfk_prev,Hk,pred,Bs)
! q = ared/pred
! if(q .le. eta) then
! xk = xk_prev
! gfk = gfk_prev
! fval = fval_prev
! call gdiis_del(nditer,size(xk)) ! last step deleted
! nditer = nditer-1
! call trustR_update(ared,pred,Delta_k,snorm)
! if(q<0) then
! call dogleg(Delta_k,ek,gfk,Hk,sk,pred,Bs) ! paramater pred is equal to the predicted reduction in the function value
! xk = xk + ek
! results = fegrad(xk)
! fval_prev = fval
! fval = results(ndim+1)
! write(*,*) 'trust dogleg step energy', fval
! nfeval = nfeval + 1
! ngeval = ngeval + 1
! do i=1,ndim
! gfk(i)= results(i)
! enddo
! ared = fval_prev - fval ! actual reduction in function value
! snorm = vecnorm(sk,2)
! call trustR_update(ared,pred,Delta_k,vecnorm(ek,2))
! call bfgs_update(sk,yk,Hk)
! endif
! else
! call trustR_update(ared,pred,Delta_k,snorm)
if(vecnorm(sk,2)<Delta_k) call bfgs_update(sk,yk,Hk)
! endif
! write(*,*) 'trust skyk',dot_product(sk,yk),&
! 1e-2*dot_product(sk,Bs),ared,pred,ared/pred,nditer
! if(ared > 0) then
! call bfgs_update(sk,yk,Hk)
! endif
enddo
fopt = fval
args = xk
if(.not. notConverged .and. &
niter .lt. maxiter) then
bool=.True.
if(lmolden) then
call ishell('cat COORD.xyz.energies >> MOLDEN')
call ishell('cat COORD.xyz.rforce >> MOLDEN')
call ishell('cat COORD.xyz.mforce >> MOLDEN')
call ishell('cat COORD.xyz.geometries >> MOLDEN')
call ishell('rm -f COORD.xyz.* COORD.xyz.energies')
call ishell('rm -f COORD.xyz.rforce COORD.xyz.mforce')
call ishell('rm -f COORD.xyz.geometries')
endif
endif
deallocate(gfk)
deallocate(gfk_prev)
deallocate(xk)
deallocate(xk_prev)
deallocate(sk)
deallocate(ek)
deallocate(yk)
deallocate(results)
deallocate(Hk)
end subroutine gdiis2
!************************************************************************
!************************************************************************
subroutine gdiis_o(fegrad,args,Hk,&
ftol,gtol,xtol,maxiter,maxfun,finit,&
fopt,niter,nfeval,ngeval,plevel,lmolden,bool)
!************************************************************************
! The Broyden-Fletcher-Goldfarb-Shanno algorithm;
! written by J Csontos
!
! Hk is the inverse of the Hessian, see Nocedal and Wright, equation 8.16
implicit none
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: eta=1.0d-3
! double precision, parameter :: r=1.0d-2
! double precision, parameter :: etol=1.0d-6
! local variables
integer :: i,ndim
double precision :: gnorm,snorm,gmax,smax
double precision :: ared,Delta_k
double precision :: fval,fval_prev
double precision, dimension(:),allocatable :: gfk,gfk_prev,xk,xk_prev
double precision, dimension(:),allocatable :: sk,ek,yk
double precision, dimension(:),allocatable :: results
double precision, dimension(:,:),allocatable :: Hk
logical :: notConverged,lBaker
interface
subroutine fegrad(z,fegrad_arr)
double precision, dimension(:), intent(inout) :: z
double precision, dimension(size(z)+1) :: fegrad_arr
end subroutine
end interface
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
allocate(gfk(size(args)))
allocate(gfk_prev(size(args)))
allocate(xk(size(args)))
allocate(xk_prev(size(args)))
allocate(sk(size(args)))
allocate(ek(size(args)))
allocate(yk(size(args)))
allocate(results(size(args)+1))
allocate(Hk(size(args),size(args)))
ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
nfeval = 0
ngeval = 0
niter = 0
bool = .False.
lBaker = .True. ! Baker's convergence criteria are set
! call print_matrixR(Hk,9)
call fegrad(args,results)
finit = results(ndim+1) ! initial fucntion value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
xk = args
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,0.d0,0.d0,0.d0
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
niter = niter+1
! call print_inverse(Hk)
! ek = -1.d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,ek,1)
write(*,*)'Iteration info gfk*ek',&
dot_product(gfk,ek),vecnorm(ek,2)
! alpha = direction_alpha(ek,gfk)
gfk_prev=gfk
xk_prev=xk
call gdiis_extrap(niter,20,xk,gfk,ek) ! xk, gfk are updated
! sk = - 1.0d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,sk,1)
xk = xk + sk
call fegrad(xk,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
gfk(i)= results(i)
yk(i) = gfk(i) - gfk_prev(i)
enddo
sk = xk - xk_prev
ared = fval_prev - fval ! actual reduction in function value
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
if( gmax <= 3.d-4 .and. (ared<=1.d-6 .or. smax<=3.d-4) ) then
notConverged = .False.
endif
else
if( gnorm <= gtol) notConverged = .False.
endif
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(niter.eq.1) then
write(*,'(5x,a)') &
'All quantities are in atomic units.'
write(*,'(5x,a,a5,6(a22))') &
'Iteration info ','step','energy','rms force','max force',&
'rms step','max step','energy change'
endif
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
call bfgs_update(sk,yk,Hk)
enddo
fopt = fval
args = xk
if(.not. notConverged .and. &
niter .lt. maxiter) then
bool=.True.
if(lmolden) then
call ishell('cat COORD.xyz.energies >> MOLDEN')
call ishell('cat COORD.xyz.rforce >> MOLDEN')
call ishell('cat COORD.xyz.mforce >> MOLDEN')
call ishell('cat COORD.xyz.geometries >> MOLDEN')
call ishell('rm -f COORD.xyz.* COORD.xyz.energies')
call ishell('rm -f COORD.xyz.rforce COORD.xyz.mforce')
call ishell('rm -f COORD.xyz.geometries')
endif
endif
deallocate(gfk)
deallocate(gfk_prev)
deallocate(xk)
deallocate(xk_prev)
deallocate(sk)
deallocate(ek)
deallocate(yk)
deallocate(results)
deallocate(Hk)
end subroutine gdiis_o
!************************************************************************
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! BFGS ALGORITHM
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
subroutine bfgs_B(fegrad,args,Bk,ftol,gtol,xtol,maxiter,maxfun,&
finit,fopt,niter,nfeval,ngeval,plevel,lmolden,bool,csys)
!************************************************************************
! The Broyden-Fletcher-Goldfarb-Shanno algorithm;
! written by J Csontos
!
! Bk is the Hessian, see Nocedal and Wright, equation 8.19
! Hessian update is utilized
! (it is not efficient at the moment, it should use Cholesky factors)
implicit none
type(coord_sys_type) :: csys,csys_k
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: eta=1.0d-3
! double precision, parameter :: r=1.0d-2
! double precision, parameter :: etol=1.0d-6
double precision, dimension(size(args),size(args)) :: Bk
! local variables
integer :: i,ndim,lwork,rank
double precision :: gnorm,snorm,gmax,smax
double precision :: pred,ared,q,Delta_k
double precision :: fval,fval_prev
double precision, dimension(:),allocatable :: gfk
double precision, dimension(:),allocatable :: sk,pk,yk,Bs
double precision, dimension(:),allocatable :: results
double precision, dimension(:),allocatable :: work,sing_vals
double precision, dimension(:,:),allocatable :: Hk
double precision, dimension(:,:),allocatable :: Pmat
logical :: converged,notConverged,lBaker
external fegrad
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
allocate(gfk(ndim))
allocate(sk(ndim))
allocate(pk(ndim))
allocate(yk(ndim))
allocate(Bs(ndim))
allocate(results(ndim+1))
allocate(Hk(ndim,ndim))
allocate(Pmat(ndim,ndim))
allocate(sing_vals(ndim))
call dgelss(ndim,ndim,1,Hk,ndim,pk,ndim,sing_vals,&
-1.0d0,rank,sing_vals,-1,i)
lwork=int(sing_vals(1))
allocate(work(lwork))
nfeval = 0
ngeval = 0
niter = 0
bool = .False.
lBaker = .False. !.True. ! Baker's convergence criteria are set
snorm = 0.0d0
smax = 0.0d0
ared = 0.0d0
call fegrad(csys,results)
finit = results(ndim+1) ! initial function value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
csys_k%ncoord=0
call copy_coord_sys(csys,csys_k)
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
call dcopy(ndim*ndim,Bk,1,Hk,1)
if(csys%nfrozen /= 0) then
call get_constrain_term(csys,gfk,pk)
call daxpy(ndim,1.0d0,gfk,1,pk,1)
else
call dcopy(ndim,gfk,1,pk,1)
endif
call dgelss(ndim,ndim,1,Hk,ndim,pk,ndim,sing_vals,&
1.0d-5,rank,work,lwork,i)
call dscal(ndim,-1.0d0,pk,1)
call dogleg_B(Delta_k,pk,gfk,Bk,sk,pred,Bs) ! paramater pred is equal to the predicted reduction in the function value
niter = niter+1
! xk = xk + sk
call update_coordinates(sk,csys_k)
call fegrad(csys_k,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
yk(i) = results(i) - gfk(i)
gfk(i)= results(i)
enddo
ared = fval_prev - fval ! actual reduction in function value
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
! if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
! if( gmax <= 3.d-4 .and. (dabs(ared)<=1.d-6 .or. smax<=3.d-4) ) then
! notConverged = .False.
! endif
! else
! if( gmax <= gtol .and. (dabs(ared)<=ftol .or. smax <= xtol) ) then
! notConverged = .False.
! endif
! endif
converged=gopt_conv(gmax,ared,smax,gtol,ftol,xtol,lbaker)
notconverged=.not.converged
if(.not. notConverged) then
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
q = ared/pred
call trustR_update(ared,pred,Delta_k,snorm)
call bfgs_update_damped_B(sk,yk,Bk,q,Bs)
call read_Pmat(ndim,Pmat)
call dgemm('n','n',ndim,ndim,ndim,1.0d0,Bk,ndim,Pmat,ndim,&
0.0d0,Hk,ndim)
call dgemm('n','n',ndim,ndim,ndim,1.0d0,Pmat,ndim,Hk,ndim,&
0.0d0,Bk,ndim)
enddo
fopt = fval
call copy_coord_sys(csys_k,csys)
args = csys%coord
call dealloc_coord_sys(csys_k)
if(niter .ge. maxiter) then
write(*,*)'ERROR: Maximum number of iterations exceeded.'
endif
if(.not. notConverged .and. niter .lt. maxiter) then
bool=.True.
if(lmolden) call update_molden
endif
deallocate(gfk)
deallocate(sk)
deallocate(pk)
deallocate(yk)
deallocate(Bs)
deallocate(results)
deallocate(Hk)
deallocate(Pmat)
deallocate(work)
deallocate(sing_vals)
end subroutine bfgs_B
!************************************************************************
!************************************************************************
subroutine print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
!************************************************************************
! it prints information about the current geometry optimization step
!************************************************************************
integer,intent(in) :: niter
double precision,intent(in) :: fval,gnorm,gmax,snorm,smax,ared
write(*,*)
write(*,1003)
write(*,'(15x,a,I10)') 'GEOMETRY OPTIMIZATION STEP', niter
write(*,*)
write(*,'(5x,(a22),f20.12)') 'energy',fval
write(*,'(5x,(a22),f20.12)') 'rms force',gnorm
write(*,'(5x,(a22),f20.12)') 'max force',gmax
write(*,'(5x,(a22),f20.12)') 'rms step',snorm
write(*,'(5x,(a22),f20.12)') 'max step',smax
write(*,'(5x,(a22),f20.12)') 'energy change',ared
write(*,*)
write(*,'(14x,a)') &
'All quantities are in atomic units'
write(*,1003)
1003 format(1x,70('@'))
end subroutine print_iter_info
!************************************************************************
!************************************************************************
subroutine bfgs(fegrad,args,Hk,&
ftol,gtol,xtol,maxiter,maxfun,finit,&
fopt,niter,nfeval,ngeval,plevel,lmolden,bool)
!************************************************************************
! The Broyden-Fletcher-Goldfarb-Shanno algorithm;
! written by J Csontos
!
! Hk is the inverse of the Hessian, see Nocedal and Wright, equation 8.16
! inverse Hessian update is utilized
implicit none
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: eta=1.0d-3
! double precision, parameter :: r=1.0d-2
! double precision, parameter :: etol=1.0d-6
! local variables
integer :: i,ndim
double precision :: gnorm,snorm,gmax,smax,alpha
double precision :: pred,ared,q,Delta_k
double precision :: fval,fval_prev
double precision, dimension(:),allocatable :: gfk,gfk_prev,xk
double precision, dimension(:),allocatable :: sk,pk,yk,Bs
double precision, dimension(:),allocatable :: results
double precision, dimension(:,:),allocatable :: Hk,Hinv
logical :: lmessage,notConverged,lBaker
interface
subroutine fegrad(z,fegrad_arr)
double precision, dimension(:), intent(inout) :: z
double precision, dimension(size(z)+1) :: fegrad_arr
end subroutine
end interface
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
allocate(gfk(size(args)))
allocate(gfk_prev(size(args)))
allocate(xk(size(args)))
allocate(sk(size(args)))
allocate(pk(size(args)))
allocate(yk(size(args)))
allocate(Bs(size(args)))
allocate(results(size(args)+1))
allocate(Hk(size(args),size(args)))
allocate(Hinv(size(args),size(args)))
ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
nfeval = 0
ngeval = 0
niter = 0
bool = .False.
lBaker = .True. ! Baker's convergence criteria are set
! call print_matrixR(Hk,9)
call matrix_pseudoinverse(Hk,Hinv)
Hk = Hinv
call fegrad(args,results)
finit = results(ndim+1) ! initial fucntion value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
xk = args
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,0.d0,0.d0,0.d0
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
! pk = -1.d0*matrix_dot_vec(Hk,gfk)
call dgemv('n',ndim,ndim,-1.0d0,Hk,ndim,gfk,1,0.0d0,pk,1)
write(*,*)'Iteration info gfk*pk', dot_product(gfk,pk)
call dogleg(Delta_k,pk,gfk,Hk,sk,pred,Bs) ! paramater pred is equal to the predicted reduction in the function value
! call ddogleg(Delta_k,pk,gfk,Hk,sk,pred,Bs) !csonti test
alpha = direction_alpha(sk,gfk)
niter = niter+1
xk = xk + sk
call fegrad(xk,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
yk(i) = results(i) - gfk(i)
gfk_prev(i) = gfk(i)
gfk(i)= results(i)
enddo
ared = fval_prev - fval ! actual reduction in function value
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
if( gmax <= 3.d-4 .and. (ared<=1.d-6 .or. smax<=3.d-4) ) then
notConverged = .False.
endif
else
if( gnorm <= gtol) notConverged = .False.
endif
if(.not. notConverged) then
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(niter.eq.1) then
write(*,'(5x,a)') &
'All quantities are in atomic units.'
write(*,'(5x,a,a5,6(a22))') &
'Iteration info ','step','energy','rms force','max force',&
'rms step','max step','energy change'
endif
if(plevel.gt.1) then
write(*,'(5x,a,i5,6(2x,f20.12))') &
'Iteration info ',niter,fval,gnorm,gmax,snorm,smax,ared
endif
q = ared/pred
! if(q .le. eta) then
! xk = xk - sk
! gfk = gfk_prev
! fval = fval_prev
! endif
call trustR_update(ared,pred,Delta_k,snorm)
call bfgs_update_damped(sk,yk,Hk,q,Bs)
! write(*,*) 'Pmat*Pinv i/o:'
! call read_Pmat(ndim,Pmat)
! call print_matrixR(Pmat,10)
! Hk = matmul(Hk,Pmat)
! Hk = matmul(Pmat,Hk)
! write(*,*) 'Hk before inverse:'
! call print_matrixR(Hk,10)
! call matrix_inverse(Hk,Hinv)
! write(*,*) 'Hinv:'
! call print_matrixR(Hinv,10)
! One = matmul(Hk,Hinv)
! write(*,*) 'One:'
! call print_matrixR(One,10)
! Atest = reshape((/1,2,4,0,-1,1,2,3,8/), shape(Atest))
! write(*,*) ' matrix A:'
! call print_matrixR(Atest,3)
! call matrix_inverse(Atest,Ctest)
! write(*,*) ' matrix Ainv:'
! call print_matrixR(Ctest,3)
! Atest = matmul(Atest,Ctest)
! write(*,*) ' matrix test i/o:'
! call print_matrixR(Atest,3)
! call mrccend(1)
! sk = matrix_dot_vec(Pmat,sk)
! call bfgs_update(sk,yk,Hk)
! write(*,*) 'Projection matrix after i/o:'
! call print_matrixR(Pmat,10)
! write(*,*) 'Projection matrix inverse after i/o:'
! call print_matrixR(Pinv,10)
! One = matmul(Hk,Pinv)
! write(*,*) 'Pmat*Pinv i/o:'
! call print_matrixR(One,10)
! Hk=matmul(Hk,Pmat)
! Hk=matmul(Pmat,Hk)
! call matrix_pseudoinverse(Pmat,Pinv)
! Hk=matmul(Hk,Pinv)
! Hk=matmul(Pinv,Hk)
! call matrix_inverse(Pmat,Hk)
! call print_inverse(Hk)
enddo
fopt = fval
args = xk
if(.not. notConverged .and. &
niter .lt. maxiter .and. &
.not.lmessage) then
bool=.True.
if(lmolden) then
call ishell('cat COORD.xyz.energies >> MOLDEN')
call ishell('cat COORD.xyz.rforce >> MOLDEN')
call ishell('cat COORD.xyz.mforce >> MOLDEN')
call ishell('cat COORD.xyz.geometries >> MOLDEN')
call ishell('rm -f COORD.xyz.* COORD.xyz.energies')
call ishell('rm -f COORD.xyz.rforce COORD.xyz.mforce')
call ishell('rm -f COORD.xyz.geometries')
endif
endif
deallocate(gfk)
deallocate(gfk_prev)
deallocate(xk)
deallocate(sk)
deallocate(pk)
deallocate(yk)
deallocate(Bs)
deallocate(results)
deallocate(Hk)
deallocate(Hinv)
end subroutine bfgs
!************************************************************************
subroutine lbfgs(fegrad,args,H0,ftol,gtol,xtol,maxiter,maxfun, &
finit,fopt,niter,nfeval,ngeval,plevel,lmolden,&
bool,mem,csys)
!************************************************************************
! The Broyden-Fletcher-Goldfarb-Shanno algorithm;
!
implicit none
type(coord_sys_type) :: csys,csys_k,csys_prev
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel,mem
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: Delta_max=5d0
double precision, dimension(size(args),1) :: H0
! local variables
integer :: i,ndim
double precision :: gnorm,snorm,gmax,smax
double precision :: ared,Delta_k
double precision :: fval,fval_prev
double precision :: sn,ddot
double precision, dimension(:),allocatable :: gfk,gfk_prev,xk
double precision, dimension(:),allocatable :: sk,pk,yk
double precision, dimension(:),allocatable :: results
double precision, dimension(:,:),allocatable :: mem_sk,mem_yk
logical :: lmessage,notConverged,lBaker
external fegrad
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
allocate(gfk(ndim))
allocate(gfk_prev(ndim))
allocate(xk(ndim))
allocate(sk(ndim))
allocate(pk(ndim))
allocate(yk(ndim))
allocate(results(ndim+1))
allocate(mem_sk(ndim,mem))
allocate(mem_yk(ndim,mem))
nfeval = 0
ngeval = 0
niter = 0
bool = .False.
lBaker = .True. ! Baker's convergence criteria are set
call fegrad(csys,results)
finit = results(ndim+1) ! initial fucntion value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
call copy_coord_sys(csys,csys_k)
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
call lbfgs_step(ndim,H0,gfk,mem_sk,mem_yk,sk,niter,mem)
sn = vecnorm(sk,2)
if(sn>delta_k) call dscal(ndim,delta_k/sn,sk,1)
write(*,*) 'lbfgs step'
write(*,'(100ES12.3)') sk
write(*,*) 'delta: ',delta_k
niter = niter+1
! xk = xk + sk
call copy_coord_sys(csys_k,csys_prev)
call update_coordinates(sk,csys_k)
call fegrad(csys_k,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
yk(i) = results(i) - gfk(i)
gfk_prev(i) = gfk(i)
gfk(i)= results(i)
enddo
ared = fval_prev - fval ! actual reduction in function value
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
if( gmax <= 3.d-4 .and. (ared<=1.d-6 .or. smax<=3.d-4) ) then
notConverged = .False.
endif
else
if( gnorm <= gtol) notConverged = .False.
endif
if(.not. notConverged) then
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
if(niter>1) then
call lbfgs_trustr_update(ndim,fval,fval_prev,sk,&
mem_sk(:,mod(niter-2,mem)+1),gfk,gfk_prev,sn,delta_k,&
delta_max)
endif
if(fval>fval_prev .or. ddot(ndim,gfk,1,sk,1)>0.0d0) then
delta_k=0.5d0*delta_k
call copy_coord_sys(csys_prev,csys_k)
gfk = gfk_prev
fval = fval_prev
niter=niter-1
else
mem_sk(:,mod(niter-1,mem)+1) = sk
mem_yk(:,mod(niter-1,mem)+1) = yk
endif
enddo
fopt = fval
call copy_coord_sys(csys_k,csys)
args = csys%coord
call dealloc_coord_sys(csys_k)
call dealloc_coord_sys(csys_prev)
if(.not. notConverged .and. &
niter .lt. maxiter .and. &
.not.lmessage) then
bool=.True.
if(lmolden) call update_molden
endif
deallocate(gfk)
deallocate(gfk_prev)
deallocate(xk)
deallocate(sk)
deallocate(pk)
deallocate(yk)
deallocate(results)
deallocate(mem_sk)
deallocate(mem_yk)
end subroutine lbfgs
!************************************************************************
subroutine lbfgs_trustr_update(ndim,ener,ener_old,s,s_old,g,g_old,&
sn,delta,delta_max)
!************************************************************************
! L-BFGS trus radius update, Phys. Chem. Chem. Phys., 2000, 2, 2177
!************************************************************************
implicit none
integer, intent(in) :: ndim
double precision, intent(in) :: ener,ener_old,sn,delta_max
double precision, intent(in) :: s(ndim),s_old(ndim)
double precision, intent(in) :: g(ndim),g_old(ndim)
double precision, intent(inout) :: delta
! local
double precision, parameter :: w1 = 1.0d-4
double precision, parameter :: w2 = 0.9d0
double precision :: dener,gs,gs_old,a
double precision :: ddot
logical lwolfe
dener = ener-ener_old
gs_old = ddot(ndim,g_old,1,s_old,1)
gs = ddot(ndim,g,1,s_old,1)
lwolfe = (dener<w1*gs_old .and. gs>w2*gs_old)
if(dener<w1*gs_old .and. sn>delta) then
a=1.25d0
else
a=1.0d0
endif
if(lwolfe) then
delta=min(2*a*delta,delta_max)
else
delta=min(delta_max,a*delta,sn)
endif
end subroutine
!************************************************************************
subroutine lbfgs_step(ndim,H0,grad,mem_sk,mem_yk,sk,niter,mem)
!************************************************************************
! Calculates th L-BFGS step vector
! Nocedal Wright, Numerical optimization, algorithm 7.4
!************************************************************************
implicit none
integer, intent(in) :: ndim,niter,mem
double precision, intent(in) :: H0(ndim,1),grad(ndim)
double precision, intent(in) :: mem_sk(ndim,mem),mem_yk(ndim,mem)
double precision, intent(out) :: sk(ndim)
!local
integer :: i,m,k
double precision :: beta
double precision, allocatable :: alpha(:),rho(:)
allocate(alpha(mem))
allocate(rho(mem))
m=min(niter,mem)
if(m==0) then
do i=1,ndim
sk(i)=H0(i,1)*grad(i)
enddo
else
sk=grad
do k=niter-1,niter-m,-1
i=mod(k,mem)+1
rho(i)=1.0d0/dot_product(mem_sk(:,1),mem_yk(:,i))
alpha(i)=rho(i)*dot_product(mem_sk(:,i),sk)
sk=sk-alpha*mem_yk(:,i)
enddo
do i=1,ndim
sk(i)=H0(i,1)*sk(i)
enddo
do k=niter-m,niter-1
i=mod(k,mem)+1
beta=rho(i)*dot_product(mem_yk(:,i),sk)
sk=sk+(alpha(i)-beta)*mem_sk(:,i)
enddo
endif
sk=-sk
deallocate(alpha)
deallocate(rho)
end subroutine
!************************************************************************
subroutine bfgs_rfo(fegrad,args,Bk,ftol,gtol,xtol,maxiter,&
maxfun,finit,fopt,niter,nfeval,ngeval,plevel,lmolden,&
bool,csys)
!************************************************************************
! Rational function optimization with BFGS Hessian
!************************************************************************
implicit none
type(coord_sys_type) csys,csys_k,csys_prev
double precision, intent(in) :: ftol,gtol,xtol
double precision, intent(out) :: finit
double precision, intent(out) :: fopt
integer, intent(in) :: maxfun,maxiter,plevel
integer, intent(out) :: niter,nfeval,ngeval
double precision, dimension(:), intent(inout) :: args
logical, intent(in) :: lmolden ! Is molden file required?
logical, intent(out) :: bool
double precision, parameter :: Delta_0=0.5d0
double precision, parameter :: Lb=0.0d0
double precision, parameter :: Ub=2.0d0
double precision, dimension(size(args),size(args)) :: Bk
! local variables
integer :: i,ndim,natoms
double precision :: gnorm,snorm,gmax,smax
double precision :: pred,ared,q,Delta_k
double precision :: fval,fval_prev
double precision, dimension(:),allocatable :: gfk,gfk_prev,gfk_c
double precision, dimension(:),allocatable :: sk,yk,Bs
double precision, dimension(:),allocatable :: results
double precision, dimension(:,:),allocatable :: Pmat,PB
logical :: lmessage,notConverged,lBaker,converged
external fegrad
! ***
! f2min -> external function to be minimized
! args() -> independent variables of f2min;
! -> as an input: initial guess
! -> as an output: coordinates of the minimum
! grad -> gradient of f2min
!
! gtol -> convergence criterion (largest element of the gradient vector)
! xtol -> convergence criterion for the function arguments
! maxiter -> maximum number of iterations allowed
! maxfun -> maximum number of function evaluations allowed
! finit -> initial value of f2min
! fopt -> minimal value of f2min
! niter -> number of iterations performed
! nfeval -> number of function evaluations performed
! ngeval -> number of gradient evaluations performed
! plevel -> determine the amount of information to be printed (the larger the more)
! ***
! ndim = size(args) ! dimension of the grad vector (fegrad(1:ndim)); the energy is stored in fegrad(ndim+1)
ndim=csys%ncoord
natoms=csys%natoms
allocate(gfk(ndim))
allocate(gfk_c(ndim))
allocate(gfk_prev(ndim))
allocate(sk(ndim))
allocate(yk(ndim))
allocate(Bs(ndim))
allocate(results(ndim+1))
allocate(Pmat(ndim,ndim))
allocate(PB(ndim,ndim))
nfeval = 0
ngeval = 0
niter = 0
bool = .False.
lBaker = .true. ! Baker's convergence criteria are set
call fegrad(csys,results)
finit = results(ndim+1) ! initial fucntion value
ngeval = ngeval+1
nfeval = nfeval+1
do i=1,ndim
gfk(i) = results(i)
enddo
fval = finit ! current function value
fval_prev = fval + 5000.0d0 ! previous function value
call copy_coord_sys(csys,csys_k)
gnorm = vecnorm(gfk,2) ! Euclidean
gmax = vecnorm(gfk,1000) ! infinit
call wrt_iter(niter) ! # interations is written in the file ITER
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
Delta_k = Delta_0
notConverged = .True.
if( gnorm <= gtol) notConverged = .False.
if(.not. notConverged) then
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
endif
do while(notConverged .and. niter < maxiter)! csonti introduce ftol and xtol here
niter = niter+1
if(csys%nfrozen /= 0) then
call get_constrain_term(csys,gfk,gfk_c)
call daxpy(ndim,1.0d0,gfk,1,gfk_c,1)
else
gfk_c=gfk
endif
! call rfo_step_new(ndim,Bk,gfk_c,sk,delta_k,csys_k,pred)
call rfo_step(ndim,Bk,gfk_c,sk,delta_k,csys_k%nonred)
pred=-rfo_pred(ndim,gfk,sk,Bk,Bs)
! xk = xk + sk
call copy_coord_sys(csys_k,csys_prev)
call update_coordinates(sk,csys_k)
call fegrad(csys_k,results)
fval_prev = fval
fval = results(ndim+1)
nfeval = nfeval + 1
ngeval = ngeval + 1
do i=1,ndim
yk(i) = results(i) - gfk(i)
gfk_prev(i) = gfk(i)
gfk(i)= results(i)
enddo
ared = fval - fval_prev ! actual reduction in function value
gnorm = vecnorm(gfk_c,2) ! Euclidean
gmax = vecnorm(gfk_c,1000) ! infinit
snorm = vecnorm(sk,2)
smax = vecnorm(sk,1000)
! if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
! if( gmax <= 3.d-4 .and. (dabs(ared)<=1.d-6 .or. smax<=3.d-4) ) then
! notConverged = .False.
! endif
! else
! if( gmax <= gtol .and. (dabs(ared)<=ftol .or. smax <= xtol)) then
! notConverged = .False.
! endif
! endif
converged=gopt_conv(gmax,ared,smax,gtol,ftol,xtol,lbaker)
notconverged=.not.converged
if(.not. notConverged) then
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
exit
endif
call wrt_iter(niter)
if(lmolden) call wrt_molden(niter,fval,gnorm,gmax)! MOLDEN info
if(plevel.gt.1) then
call print_iter_info(niter,fval,gnorm,gmax,snorm,smax,ared)
endif
q = ared/pred
if(q.le.Lb .or. q.gt.Ub) then
write(*,*) 'Discarding RFO step'
call copy_coord_sys(csys_prev,csys_k)
gfk = gfk_prev
fval = fval_prev
endif
! call rfo_trustR_update(ared,pred,Delta_k,snorm)
call trustR_update(ared,pred,Delta_k,snorm)
call bfgs_update_damped_B(sk,yk,Bk,q,Bs)
call read_Pmat(ndim,Pmat)
call dgemm('n','n',ndim,ndim,ndim,1.0d0,Bk,ndim,Pmat,ndim,&
0.0d0,PB,ndim)
call dgemm('n','n',ndim,ndim,ndim,1.0d0,Pmat,ndim,PB,ndim,&
0.0d0,Bk,ndim)
enddo
fopt = fval
call copy_coord_sys(csys_k,csys)
args = csys%coord
call dealloc_coord_sys(csys_k)
call dealloc_coord_sys(csys_prev)
if(.not. notConverged .and. niter .lt. maxiter) then
bool=.True.
if(lmolden) call update_molden
endif
deallocate(gfk)
deallocate(gfk_c)
deallocate(gfk_prev)
deallocate(sk)
deallocate(yk)
deallocate(Bs)
deallocate(results)
deallocate(Pmat)
deallocate(PB)
end subroutine
!************************************************************************
subroutine update_molden
!************************************************************************
! Update MOLDEN file
!************************************************************************
implicit none
call ishell('cat COORD.xyz.energies >> MOLDEN')
call ishell('cat COORD.xyz.rforce >> MOLDEN')
call ishell('cat COORD.xyz.mforce >> MOLDEN')
call ishell('cat COORD.xyz.geometries >> MOLDEN')
call ishell('rm -f COORD.xyz.* COORD.xyz.energies')
call ishell('rm -f COORD.xyz.rforce COORD.xyz.mforce')
call ishell('rm -f COORD.xyz.geometries')
end subroutine
!************************************************************************
double precision function rfo_pred(ndim,grad,sk,B,Bs)
!************************************************************************
! Predected energy change for RFO
!************************************************************************
implicit none
integer, intent(in) :: ndim
double precision, intent(in) :: grad(ndim),sk(ndim)
double precision, intent(in) :: B(ndim,ndim),Bs(ndim)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
rfo_pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
return
end function
!************************************************************************
subroutine rfo_step_new(ndim,Bk,grad,dq,trust_radius,csys,q_pred)
!************************************************************************
! Calculating RFO step vector, Theor Chem Acc (1998) 100:265-274
!************************************************************************
implicit none
type(coord_sys_type) :: csys
integer, intent(in) :: ndim
double precision :: q_pred
double precision, intent(in) :: Bk(ndim,ndim) !Hessian
double precision :: grad(ndim) !gradient vector
double precision, intent(out) :: dq(ndim) !step vector
double precision, intent(in) :: trust_radius
!local
integer :: nonred ! number of coordinates
integer :: info,lwork,n,nn
double precision :: work1(1)
double precision, allocatable :: eigvec(:,:) !storing eigenvectors of hessian
double precision, allocatable :: eigval(:) !storing eigenvalues of hessian
double precision, allocatable :: grad_proj(:) !storing the projected gradient
double precision, allocatable :: grad_deloc(:) !storing the delocalized gradient
double precision, allocatable :: dq_proj(:) !step in the projected space
double precision, allocatable :: Bk_deloc(:,:) !delocalized hessian
double precision, allocatable :: work(:)
! nonred=csys%ncoord_ric
info=0
nonred=csys%nonred
allocate(Bk_deloc(nonred,nonred))
allocate(grad_deloc(nonred))
allocate(grad_proj(nonred))
allocate(dq_proj(nonred))
call rfo_deloc_hessian(csys,Bk,Bk_deloc,grad,grad_deloc)
! call dcopy(nonred**2,Bk,1,Bk_deloc,1)
! call dcopy(nonred,grad,1,grad_deloc,1)
! Calculating eigenvectors
allocate(eigvec(nonred,nonred))
allocate(eigval(nonred))
call dcopy(nonred*nonred,Bk_deloc,1,eigvec,1)
call dsyev('v','u',nonred,eigvec,nonred,eigval,work1,-1,info)
lwork=int(work1(1))
allocate(work(lwork))
call dsyev('v','u',nonred,eigvec,nonred,eigval,work,lwork,info)
write(*,'(A)') 'hessian eigvals'
write(*,'(9999999ES14.4)') eigval
if(info/=0) then
write(*,'(A)') 'Diagonalization failed in RFO step calculation'
write(*,'(A)') 'Aborting execution'
call mrccend(1)
endif
n=csys%nonred
nn=csys%ncoord_ric-n+1
! Projecting the gradient to the eigenspace
call dgemv('t',nonred,nonred,1.0d0,eigvec,nonred,grad_deloc,1,&
0.0d0,grad_proj,1)
! call dgemv('t',n,nonred,1.0d0,eigvec(1,nn),nonred,grad_deloc,1,&
! 0.0d0,grad_proj,1)
! getting the rfo step in the active space
call calc_dq_rfo(nonred,nonred,eigval,eigvec,grad_proj,dq_proj,&
trust_radius,q_pred)
! call calc_dq_rfo(nonred,n,eigval(nn),eigvec(1,nn),grad_proj,dq_proj,&
! trust_radius,q_pred)
! transfoming the step back to full space
call dcopy(csys%ncoord_ric,dq_proj,1,dq,1)
call dgemv('n',csys%ncoord_ric,nonred,1.0d0,csys%umatrix,&
csys%ncoord_ric,dq_proj,1,0.0d0,dq,1)
! call dgemv('n',nonred,n,1.0d0,eigvec(1,nn),nonred,dq_proj,1,0.d0,&
! dq,1)
if(allocated(eigvec)) deallocate(eigvec)
if(allocated(eigval)) deallocate(eigval)
if(allocated(work)) deallocate(work)
if(allocated(grad_proj)) deallocate(grad_proj)
if(allocated(grad_deloc))deallocate(grad_deloc)
if(allocated(Bk_deloc)) deallocate(Bk_deloc)
if(allocated(dq_proj)) deallocate(dq_proj)
contains
subroutine calc_dq_rfo(full_dim,ndim,eigval,eigvec,grad_proj,dq,&
trust_radius,q_red)
implicit none
integer :: ndim,full_dim
double precision :: eigval(ndim),eigvec(full_dim,ndim)
double precision :: grad_proj(ndim),dq(ndim),trust_radius
!local
integer :: k,lwork,p,state
double precision :: alpha,alpha_old,alpha_old2,work1(1),q_red
double precision :: alpha1,alpha2,norm
double precision, allocatable :: aug_hess(:,:),lambda(:)
double precision, allocatable :: work(:)
double precision :: dnrm2
logical :: lconv
k=0
p=0
state=0
alpha1 = 0.0d0
alpha2 = 1.0d0
alpha=1.0d0
alpha_old=0.0d0
! call get_newton_step(ndim,eigval,grad_proj,dq,q_red)
! lconv = check_convergence(ndim,k,dq,trust_radius,&
! alpha,alpha_old)
! if(lconv) then
! write(*,'(A,ES14.4)') 'Newton-step accepted! Norm: ',&
! dnrm2(ndim,dq,1)
! return
! endif
k=1
allocate(aug_hess(ndim+1,ndim+1))
allocate(lambda(ndim+1))
call solve_rfo_eq(ndim,alpha,aug_hess,dq,lambda,work1,-1,q_red)
lwork=int(work1(1))
allocate(work(lwork))
do
call build_aug_hess(ndim,eigval,grad_proj,alpha,aug_hess)
call solve_rfo_eq(ndim,alpha,aug_hess,dq,lambda,work,lwork,&
q_red)
lconv = check_convergence(ndim,k,dq,trust_radius,&
alpha,alpha_old)
! alpha_old2=alpha_old
alpha_old=alpha
! if(.not. lconv) then
! call update_alpha(ndim,alpha,alpha_old2,dq,trust_radius,&
! grad_proj,eigval,lambda(1),p)
if(lconv) exit
! bisection search
! alpha1 -> upper bound in norm
! alpha2 -> lower bound in norm
select case(state)
case(0) ! searching alpha2
if(dnrm2(ndim,dq,1) < trust_radius) then
alpha = 0.5d0*(alpha1 + alpha2)
state = 1
else
alpha2 = alpha2 + 10.0d0
alpha = alpha2
endif
case(1)
norm = dnrm2(ndim,dq,1)
if(norm < trust_radius) then
alpha2 = alpha
else
alpha1 = alpha
endif
alpha = 0.5d0*(alpha1 + alpha2)
end select
k=k+1
enddo
deallocate(aug_hess)
deallocate(work)
deallocate(lambda)
end subroutine
subroutine get_newton_step(ndim,eigval,grad_proj,dq,pred)
implicit none
integer :: ndim,i
double precision :: eigval(ndim),grad_proj(ndim),dq(ndim)
double precision :: pred
double precision :: ddot
do i=1,ndim
dq(i) = -grad_proj(i)/eigval(i)
enddo
pred = 0.5d0*ddot(ndim,grad_proj,1,dq,1)
end subroutine
subroutine build_aug_hess(ndim,eigval,grad_proj,alpha,aug_hess)
! Builds augmented hessian matrix
implicit none
integer :: i
integer, intent(in) :: ndim
double precision, intent(in) :: eigval(ndim),grad_proj(ndim),alpha
double precision :: aug_hess(ndim+1,ndim+1)
! local
double precision :: sqrt_alpha, inv_alpha
sqrt_alpha=dsqrt(alpha)
inv_alpha=1.0d0/alpha
aug_hess = 0.0d0
do i=2,ndim+1
aug_hess(i,1) = sqrt_alpha * grad_proj(i-1)
aug_hess(1,i) = sqrt_alpha * grad_proj(i-1)
aug_hess(i,i) = inv_alpha * eigval(i-1)
enddo
end subroutine
subroutine solve_rfo_eq(ndim,alpha,aug_hess,dq,lambda,work,lwork,&
q_pred)
! Calculates the RFO step vector for a given nu parameter
implicit none
integer :: lwork
integer, intent(in) :: ndim
double precision :: aug_hess(ndim+1,ndim+1)
double precision :: alpha,q_pred,lambda(ndim+1)
double precision :: work(*)
double precision, intent(out) :: dq(ndim)
!local
integer idx_min
integer i,j
double precision :: dnrm2
double precision, parameter :: eps = 1.0d-8
idx_min = 1 ! eigenvalue index to minimize along
if(lwork == -1) then
call dsyev('v','u',ndim+1,aug_hess,ndim+1,lambda,work,-1,i)
return
endif
! solving the generalized eigenvector equation
call dsyev('v','u',ndim+1,aug_hess,ndim+1,lambda,work,lwork,i)
! transfroming back the eigenvectors
call dscal(ndim,1.0d0/dsqrt(alpha),aug_hess(2,idx_min),1)
! getting the actual step
dq = aug_hess(2:ndim+1,idx_min) / aug_hess(1,idx_min)
q_pred = lambda(1)/(aug_hess(1,idx_min)**2)
return
end subroutine
subroutine update_alpha(ndim,alpha,alpha_old,dq,trust_radius,&
grad_proj,eigval,lambda_min,p)
! update the alpha parameter with Newton's method
implicit none
integer :: ndim,p
double precision :: alpha,alpha_old,trust_radius,lambda_min
double precision :: dq(ndim),eigval(ndim),grad_proj(ndim)
double precision :: dnrm2
! local
integer :: i
double precision :: dq2,norm,dq_da,alpha_save
norm=dnrm2(ndim,dq,1)
dq2=norm*norm
dq_da=0.0d0
do i=p+1,ndim
dq_da=dq_da+grad_proj(i)**2/(eigval(i)-lambda_min*alpha)**3
enddo
dq_da=2.0d0*lambda_min/(1+dq2*alpha)*dq_da
alpha_save = alpha
alpha = alpha + 2.0d0*(trust_radius*norm-dq2)/dq_da
if(alpha < 0.0d0) alpha=0.5d0*(alpha_save+alpha_old)
end subroutine
logical function check_convergence(ndim,niter,dq,trust_radius,&
alpha,alpha_old) result(lconv)
! checking the convergence of the rfo step
implicit none
integer :: niter,ndim
double precision :: dq(ndim),trust_radius,alpha,alpha_old
double precision :: norm
double precision :: eps1
double precision, parameter :: eps2 = 1.0d-5
double precision :: dnrm2
norm = dnrm2(ndim,dq,1)
write(*,'(A,ES12.4)') 'norm: ',norm
write(*,'(A,ES12.4)') 'trust radius: ',trust_radius
write(*,'(A,ES12.4)') 'alpha: ',alpha
write(*,'(A,ES12.4)') 'alpha_old: ',alpha_old
if(niter == 0) then
lconv = (norm < trust_radius)
else
eps1 = trust_radius/1.0d2
lconv = (dabs(norm-trust_radius) < eps1 .or. &
dabs(alpha_old-alpha) < eps2)
endif
write(*,'(A,L)') 'conv: ',lconv
end function
end subroutine rfo_step_new
!************************************************************************
subroutine rfo_deloc_hessian(csys,H,H_deloc,grad,grad_deloc)
!************************************************************************
! Delocalize Hessian for RFO step
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: H(csys%ncoord,csys%ncoord)
double precision :: grad(csys%ncoord)
double precision :: H_deloc(csys%nonred,csys%nonred)
double precision :: grad_deloc(csys%nonred)
! local
integer :: i
double precision, allocatable :: B(:,:)
double precision, allocatable :: gmat(:,:)
if(csys%nonred == csys%ncoord) then
call dcopy(csys%nonred*csys%nonred,H,1,H_deloc,1)
call dcopy(csys%nonred,grad,1,grad_deloc,1)
return
endif
allocate(B(csys%ncoord,3*csys%natoms))
allocate(gmat(csys%ncoord,csys%ncoord))
call build_Wilson_Bmat(csys%natoms,csys%cart_coord,csys%rim,B)
call active_subspace(csys%ncoord,csys%natoms,i,B,Gmat)
call get_deloc_matrix(csys%ncoord,csys%nonred,Gmat,csys%Umatrix)
call dgemm('n','n',csys%ncoord,csys%nonred,csys%ncoord,1.0d0,&
H,csys%ncoord,csys%Umatrix,csys%ncoord,0.0d0,gmat,&
csys%ncoord)
call dgemm('t','n',csys%nonred,csys%nonred,csys%ncoord,1.0d0,&
csys%umatrix,csys%ncoord,gmat,csys%ncoord,0.0d0,&
H_deloc,csys%nonred)
call dgemv('t',csys%ncoord,csys%nonred,1.0d0,csys%umatrix,&
csys%ncoord,grad,1,0.0d0,grad_deloc,1)
deallocate(B)
deallocate(gmat)
end subroutine
!************************************************************************
subroutine rfo_step(ndim,Bk,grad,dq,trust_radius,nonred)
!************************************************************************
! Calculating RFO step vector, Theor Chem Acc (1998) 100:265-274
!************************************************************************
implicit none
integer, intent(in) :: ndim ! number of coordinates
integer, intent(in) :: nonred ! number of nonredundant coordinates
double precision, intent(in) :: Bk(ndim,ndim) !Hessian
double precision :: grad(ndim) !gradient vector
double precision, intent(out) :: dq(ndim) !step vector
double precision, intent(in) :: trust_radius
!local
integer info,lwork,nred
double precision :: work1(1),nu1,nu2,nu,norm
double precision :: delta
double precision, allocatable :: eigvec(:,:) !storing eigenvectors of hessian
double precision, allocatable :: eigval(:) !storing eigenvalues of hessian
double precision, allocatable :: grad_proj(:) !storing the projected gradient
double precision, allocatable :: work(:)
double precision :: dnrm2
logical :: conv
info=0
delta=trust_radius/1.0d2
allocate(eigvec(ndim,ndim))
allocate(eigval(ndim))
allocate(grad_proj(ndim))
call dcopy(ndim*ndim,Bk,1,eigvec,1)
call dcopy(ndim,grad,1,grad_proj,1)
call dscal(ndim,-1.0d0,grad_proj,1)
call dgelss(ndim,ndim,1,eigvec,ndim,grad_proj,ndim,eigval,&
1.0d-10,nred,work1,-1,info)
lwork=int(work1(1))
allocate(work(lwork))
call dgelss(ndim,ndim,1,eigvec,ndim,grad_proj,ndim,eigval,&
1.0d-10,nred,work,lwork,info)
deallocate(work)
call dcopy(ndim,grad_proj,1,dq,1)
norm=dnrm2(ndim,dq,1)
if(norm < trust_radius) then
! write(*,'(A,ES12.3)') 'Newton-step accepted. Norm: ',norm
call dealloc
return
endif
! Calculating eigenvectors
call dcopy(ndim*ndim,Bk,1,eigvec,1)
call dsyev('v','u',ndim,eigvec,ndim,eigval,work1,-1,info)
lwork=int(work1(1))
allocate(work(lwork))
call dsyev('v','u',ndim,eigvec,ndim,eigval,work,lwork,info)
if(info/=0) then
write(*,'(A)') 'Diagonalization failed in RFO step calculation'
write(*,'(A)') 'Aborting execution'
call mrccend(1)
endif
nred=ndim-nonred+1 ! nonredundant coordinates start at this index
! Projecting the gradient to the eigenspace
call dgemv('t',ndim,ndim,1.0d0,eigvec,ndim,grad,1,0.0d0,&
grad_proj,1)
! finding best nu with bisection method
! for nu2=eigval(1): ||dq|| == inf
! we have to find nu1: ||dq|| < trust_radius
nu2=eigval(nred)
nu1=nu2-10.0d0
do
call calc_dq_rfo(ndim,nred,eigval,eigvec,grad_proj,nu1,dq,norm)
if(norm<trust_radius) then
exit
else
nu2=nu1
endif
nu1=nu1-10.0d0
enddo
! bisection method
conv=.false.
do while(.not.conv)
nu=(nu1+nu2)/2.0d0
call calc_dq_rfo(ndim,nred,eigval,eigvec,grad_proj,nu,dq,norm)
if(norm<=trust_radius .and. norm>=trust_radius-delta) then
conv=.true.
elseif(norm<trust_radius) then
nu1=nu
else
nu2=nu
endif
enddo
! write(*,'(A,ES12.3)') 'Trust-region step accepted. Norm: ',norm
! write(*,'(A,ES12.3,A,ES12.3)') 'nu: ',nu,' eigval1: ',eigval(nred)
call dealloc
contains
subroutine calc_dq_rfo(ndim,nred,eigval,eigvec,grad_proj,nu,dq,norm)
! Calculates the RFO step vector for a given nu parameter
implicit none
integer, intent(in) :: ndim,nred
double precision, intent(in) :: eigval(ndim),eigvec(ndim,ndim)
double precision, intent(in) :: grad_proj(ndim),nu
double precision, intent(out) :: dq(ndim),norm
!local
integer i,j
double precision :: dnrm2
dq=0.0d0
do i=nred,ndim
do j=1,ndim
dq(j)=dq(j)-grad_proj(i)/(eigval(i)-nu)*eigvec(j,i)
enddo
enddo
norm=dnrm2(ndim,dq,1)
end subroutine
subroutine dealloc
! Deallocates memory
implicit none
if(allocated(eigvec)) deallocate(eigvec)
if(allocated(eigval)) deallocate(eigval)
if(allocated(work)) deallocate(work)
if(allocated(grad_proj)) deallocate(grad_proj)
end subroutine
end subroutine rfo_step
!************************************************************************
integer function get_nonred(natoms,nintcoord,Bmatrix)result(nonred)
!************************************************************************
! Counts the number of nonredundant coordinates based on Wilson's B
!************************************************************************
implicit none
integer, intent(in) :: natoms,nintcoord
double precision, intent(in) :: Bmatrix(nintcoord,3*natoms)
integer :: info,lwork,rank
double precision :: work1(1)
double precision, parameter :: delta=1.0d-10
double precision, allocatable :: Gmat(:,:),sval(:),work(:)
rank=min(3*natoms,nintcoord)
allocate(Gmat(nintcoord,3*natoms))
allocate(sval(rank))
call dcopy(nintcoord*3*natoms,Bmatrix,1,Gmat,1)
call dgesvd('n','n',nintcoord,3*natoms,Gmat,nintcoord,sval,&
work1,1,work1,1,work1,-1,info)
lwork=int(work1(1))
allocate(work(lwork))
call dgesvd('n','n',nintcoord,3*natoms,Gmat,nintcoord,sval,&
work1,1,work1,1,work,lwork,info)
nonred=0
write(*,*) 'sval'
write(*,'(100ES12.3)') sval
do while(sval(nonred+1)>delta)
nonred=nonred+1
enddo
deallocate(work)
deallocate(Gmat)
deallocate(sval)
return
end function
!************************************************************************
subroutine bfgs_update_B(sk,yk,Bk)
!************************************************************************
!************************************************************************
! BFGS Update
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Nocedal and Wright, eq. 18.22, pg. 538
implicit none
double precision, dimension(:), intent(in) :: sk
double precision, dimension(:), intent(in) :: yk
double precision, dimension(:,:),intent(inout) ::Bk
!local
double precision :: sBs,denom,rho_k,r_k
double precision, dimension(:,:), allocatable :: Id,A1,A2,A3
double precision, dimension(:), allocatable ::Bs
integer :: i,j,ndim
ndim = size(sk)
allocate(Id(ndim,ndim))
allocate(A1(ndim,ndim))
allocate(Bs(ndim))
allocate(A2(ndim,ndim))
allocate(A3(ndim,ndim))
do j=1,ndim ! initialization of arrays
do i=1,ndim
if(i .eq. j) then
Id(i,j) = 1.d0
else
Id(i,j) = 0.d0
endif
A1(i,j) = 0.d0
A2(i,j) = 0.d0
A3(i,j) = 0.d0
enddo
enddo
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
denom = dot_product(yk,sk)
if(denom < 1.d-6) then ! csonti
rho_k = 1.d6
else
rho_k = 1.d0/denom
endif
! Bs = matrix_dot_vec(Bk,sk)
call dgemv('n',ndim,ndim,1.0d0,Bk,ndim,sk,1,0.0d0,Bs,1)
sBs = dot_product(sk,Bs) ! r_k = 1/sBs
if(sBs < 1.d-6) then ! csonti
r_k = 1.d6
else
r_k = 1.d0/sBs
endif
call outer_product(Bs,sk,A1)
A2 = matmul(A1,Bk)
call outer_product(yk,yk,A3)
do j=1,ndim
do i=1,ndim
A2(i,j) = -1.d0*r_k * A2(i,j)
A3(i,j) = rho_k * A3(i,j)
enddo
enddo
do j=1,ndim
do i=1,ndim
Bk(i,j) = Bk(i,j) + A2(i,j) + A3(i,j)
enddo
enddo
! call print_matrixR(Bk,9)
deallocate(Id)
deallocate(A1)
deallocate(Bs)
deallocate(A2)
deallocate(A3)
end subroutine bfgs_update_B
!************************************************************************
!************************************************************************
subroutine bfgs_update_damped_B(sk,yk,Bk,q,Bs)
!************************************************************************
!************************************************************************
! BFGS Update
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Nocedal and Wright, eq. 18.22, pg. 538
implicit none
double precision, dimension(:), intent(in) :: sk, Bs
double precision, dimension(:), intent(inout) :: yk
double precision, intent(in) :: q
double precision, dimension(:,:),intent(inout) ::Bk
!local
double precision :: sBs,thres,theta_k,denom,rho_k,r_k,sy
integer :: ndim
ndim = size(sk)
sy = dot_product(sk,yk)
sBs = dot_product(sk,Bs) ! r_k = 1/sBs
thres = 0.2d0*sBs
if(sy .ge. thres) then
theta_k = 1.0d0
else
theta_k = (0.8d0*sBs)/(sBs-sy)
endif
yk(:) = theta_k*yk(:) + (1.d0-theta_k)*Bs(:)
denom = dot_product(yk,sk)
rho_k = min(1.d0/denom, 1.0d6)
r_k = min(1.0d0/sBs, 1.0d6)
call dsyr('u',ndim,-r_k,Bs,1,Bk,ndim)
call dsyr('u',ndim,rho_k,yk,1,Bk,ndim)
call filllo(Bk,ndim)
end subroutine bfgs_update_damped_B
!************************************************************************
!************************************************************************
subroutine bfgs_update(sk,yk,Hk)
!************************************************************************
!************************************************************************
! BFGS Update
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Nocedal and Wright, eq. 18.22, pg. 538
implicit none
double precision, dimension(:), intent(in) :: sk
double precision, dimension(:), intent(in) :: yk
double precision, dimension(:,:),intent(inout) ::Hk
!local
double precision :: denom,rho_k
double precision, dimension(:,:), allocatable :: Id,A1,A2,A3
integer :: i,j,ndim
ndim = size(sk)
allocate(Id(ndim,ndim))
allocate(A1(ndim,ndim))
allocate(A2(ndim,ndim))
allocate(A3(ndim,ndim))
do j=1,ndim ! initialization of arrays
do i=1,ndim
if(i .eq. j) then
Id(i,j) = 1.d0
else
Id(i,j) = 0.d0
endif
A1(i,j) = 0.d0
A2(i,j) = 0.d0
A3(i,j) = 0.d0
enddo
enddo
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
denom = dot_product(yk,sk)
if(denom < 1.d-12) then
rho_k = 1.d3
else
rho_k = 1.d0/denom
endif
call outer_product(sk,yk,A1)
call outer_product(yk,sk,A2)
call outer_product(sk,sk,A3)
do j=1,ndim
do i=1,ndim
A1(i,j) = Id(i,j) - rho_k * A1(i,j)
A2(i,j) = Id(i,j) - rho_k * A2(i,j)
A3(i,j) = rho_k * A3(i,j)
enddo
enddo
Hk = matmul( A1,matmul( Hk,A2) )
do j=1,ndim
do i=1,ndim
Hk(i,j) = Hk(i,j) + A3(i,j)
enddo
enddo
! call print_matrixR(Hk,9)
deallocate(Id)
deallocate(A1)
deallocate(A2)
deallocate(A3)
end subroutine bfgs_update
!************************************************************************
!************************************************************************
subroutine bfgs_update_damped(sk,yk,Hk,q,Bs)
!************************************************************************
!************************************************************************
! BFGS Update
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Nocedal and Wright, eq. 18.22, pg. 538
implicit none
double precision, dimension(:), intent(in) :: sk, Bs
double precision, dimension(:), intent(inout) :: yk
double precision, intent(in) :: q
double precision, dimension(:,:),intent(inout) ::Hk
!local
double precision :: sy,sBs,thres,theta_k,denom,rho_k
double precision, dimension(:,:), allocatable :: Id,A1,A2,A3
integer :: i,j,ndim
ndim = size(sk)
allocate(Id(ndim,ndim))
allocate(A1(ndim,ndim))
allocate(A2(ndim,ndim))
allocate(A3(ndim,ndim))
do j=1,ndim ! initialization of arrays
do i=1,ndim
if(i .eq. j) then
Id(i,j) = 1.d0
else
Id(i,j) = 0.d0
endif
A1(i,j) = 0.d0
A2(i,j) = 0.d0
A3(i,j) = 0.d0
enddo
enddo
! Bs = matrix_dot_vec(Hk,sk)
sy = dot_product(sk,yk)
sBs = dot_product(sk,Bs)
thres = 0.2d0*sBs
if(sy .ge. thres) then
theta_k = 1.0d0
else
! theta_k = (0.8d0*sBs)/(sBs-sy)
! theta_k = (0.8d0*thres)/(thres-sy)
theta_k = (0.8d0*sBs)/(sBs-sy)
endif
yk(:) = theta_k*yk(:) + (1.d0-theta_k)*Bs(:)
write(*,'(A,3f12.6)')'trust theta_k: ',theta_k,sy,0.2d0*sBs
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
denom = dot_product(yk,sk)
if(denom < 1.d-12) then
rho_k = 1.d3
else
rho_k = 1.d0/denom
endif
call outer_product(sk,yk,A1)
call outer_product(yk,sk,A2)
call outer_product(sk,sk,A3)
do j=1,ndim
do i=1,ndim
A1(i,j) = Id(i,j) - rho_k * A1(i,j)
A2(i,j) = Id(i,j) - rho_k * A2(i,j)
A3(i,j) = rho_k * A3(i,j)
enddo
enddo
Hk = matmul( A1,matmul( Hk,A2) )
do j=1,ndim
do i=1,ndim
Hk(i,j) = Hk(i,j) + A3(i,j)
enddo
enddo
! BFGS Update END
! endif
! else
! Hk = Id
! endif
! write(*,*)'inverse Hessian matrix'
! call print_matrixR(Hk,9)
deallocate(Id)
deallocate(A1)
deallocate(A2)
deallocate(A3)
end subroutine bfgs_update_damped
!************************************************************************
subroutine rfo_trustr_update(ared,pred,Delta_k,snorm)
!************************************************************************
! Trust radius update for RFO opzimization
! Theor Chem Acc (1998) 100:265-274
!************************************************************************
implicit none
double precision, intent(in) :: ared,pred,snorm
double precision, intent(inout) :: Delta_k
!local
double precision :: r
double precision, parameter :: sf = 2.0d0
double precision, parameter :: Lb=0.0d0
double precision, parameter :: Ub=2.0d0
double precision, parameter :: delta_e=0.75d0
double precision, parameter :: delta_i=0.80d0
double precision, parameter :: eps1=1.0d-4
double precision, parameter :: delta_min=1.0d-3
double precision :: r_le,r_ue,r_ui,r_li
r=ared/pred
r_le=Lb+delta_e
r_ue=Ub-delta_e
r_ui=Ub-delta_i
r_li=Lb+delta_i
if(r<r_le .or. r>r_ue) then
Delta_k=snorm/sf
elseif(r<=r_ui .and. r>=r_li) then
Delta_k=dsqrt(sf)*Delta_k
endif
delta_k=max(delta_k,delta_min)
end subroutine
!************************************************************************
subroutine trustR_update(ared,pred,Delta_k,snorm)
!************************************************************************
! it updates the trust radius (Delta_k)
!************************************************************************
implicit none
double precision, intent(in) :: ared,pred,snorm
double precision, intent(inout) :: Delta_k
!local
double precision :: q
double precision, parameter :: Delta_max=0.8d0
double precision, parameter :: Delta_min=0.0001d0
double precision, parameter :: scale_up=1.2d0
double precision, parameter :: scale_down=0.67d0
q=ared/pred
if(q < 0.25d0) then
Delta_k = scale_down*snorm!Delta_k!
if(Delta_k < Delta_min) Delta_k = Delta_min
! write(*,'(A,5f12.6)') &
! 'The trust radius was decreased in this step to',&
! Delta_k,q,snorm,ared,pred
else
if(q > 0.75d0 .and. snorm > 0.8d0*Delta_k) then
Delta_k = min(scale_up*Delta_k,Delta_max)
! write(*,'(A,5f12.6)') &
! 'The trust radius was increased in this step to',&
! Delta_k,q,snorm,ared,pred
else
Delta_k = 1.0d0*Delta_k
! write(*,'(A,5f12.6)') &
! 'The trust radius was not changed in this step',&
! Delta_k,q,snorm,ared,pred
endif
endif
return
end subroutine trustR_update
!************************************************************************
!************************************************************************
double precision function direction_alpha(step,gradient)
!************************************************************************
! it calculates the angle between the step and gradient vector
! that should be between -90 and -180 somewhere
!************************************************************************
implicit none
double precision, dimension(:), intent(in) :: step,gradient
!local
double precision :: rtmp
rtmp = dot_product(step,gradient)/&
(sqrt(dot_product(gradient,gradient))&
*sqrt(dot_product(step,step)))
if(rtmp> 1.d0) rtmp = 1.d0
if(rtmp<-1.d0) rtmp = -1.d0
direction_alpha = (-180.d0/PI)*dacos(rtmp)
if(direction_alpha > -95.d0) then
write(*,*) 'Warning: the optimizer might move uphill!!!'
write(*,'(A,f8.1)')&
'Iteration info direction alpha: ',direction_alpha
endif
end function direction_alpha
!************************************************************************
!************************************************************************
subroutine ddogleg(Delta,pB,grad,H,sk,pred,Bs)
!************************************************************************
! double dogleg Dennis, Robert, Schnabel section 6.4.2 pg 139-147.
implicit none
double precision, intent(in) :: Delta ! trust radius
double precision, dimension(:), intent(in) :: pB !
double precision, dimension(:), intent(in) :: grad ! gradient vector
double precision, dimension(:,:), intent(in) :: H ! the inverse Hessian matrix, B^-1 = H
double precision, dimension(:), intent(out) :: sk ! the dogleg step
double precision, intent(out) :: pred ! predicted function value
double precision, dimension(:), intent(out) :: Bs
!local
integer :: ndim
double precision :: pB_norm,pU_norm,nom,denom,a,hb,c,kappa,kappa2
double precision :: gBg,gHg,gamma_,eta_
double precision, dimension(:),allocatable :: pU,pBU,gtmp
double precision, dimension(:,:), allocatable :: B,E
ndim = size(grad)
allocate(pU(ndim))
allocate(pBU(ndim))
allocate(gtmp(ndim))
allocate(B(ndim,ndim))
! allocate(E(ndim,ndim)) ! just for test remove csonti
call matrix_pseudoinverse(H,B)
pB_norm = vecnorm(pB,2)
! CASE A -- the full Newton step is inside the trust radius
if(pB_norm .le. Delta) then
write(*,*) &
'The full Newton step is inside the trust radius'
sk = pB
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
endif
! gtmp = matrix_dot_vec(B,grad)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,grad,1,0.0d0,gtmp,1)
! gtmp = dot_product(B,transpose(grad))
! call print_vectorR(gtmp,8)
nom = dot_product(grad,grad)
denom = dot_product(grad,gtmp)
pU = -1.d0*(nom/denom)*grad
! CASE B -- the Cauchy step reaches outside the border of the trust radius; let's scale it back to the border
pU_norm = vecnorm(pU,2)
if(pU_norm .gt. Delta) then
sk = (Delta/pU_norm)*pU
! sk = sk + (Delta/pB_norm)*pB
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
write(*,'(A)',advance='no') &
'Let us scale the Cauchy step (steepest descent) back '
write(*,*) 'to the trust border'
call dealloc_mem
return
endif
! CASE C -- quadratic part; we need the point of intersection with the trust radius
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
call dgemv('n',ndim,ndim,1.0d0,B,ndim,grad,1,0.0d0,gtmp,1)
gBg = dot_product(grad,gtmp)
call dgemv('n',ndim,ndim,1.0d0,H,ndim,grad,1,0.0d0,gtmp,1)
gHg = dot_product(grad,gtmp)
gamma_ = (dot_product(grad,grad))**2/(gBg*gHg)
eta_ = 0.8d0*gamma_ + 0.2d0
sk = eta_*pB
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
pBU = sk - pU
a = dot_product(pBU,pBU)
hb = dot_product(pU,pBU) !half b, hb=0.5*b
c = dot_product(pU,pU) - Delta**2
kappa = (-1.d0*hb + sqrt(hb**2 - a*c))/a! kappa = (tau-1)
kappa2 = (-1.d0*hb - sqrt(hb**2 - a*c))/a! kappa = (tau-1)
write(*,'(A)',advance='no') &
'We need the intersection of the trust radius with that of'
write(*,*) 'the dogleg step (Cauchy step + Newton step)'
write(*,*) 'Trust radius tau is equal to ',kappa+1.d0
write(*,*) 'Trust radius tau2 is equal to ',kappa2+1.d0
sk = pU + kappa*pBU
Bs = matrix_dot_vec(B,sk)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
contains
subroutine dealloc_mem
implicit none
if(allocated(B)) deallocate(B) ! deallocation in case A, B, and C !csonti
if(allocated(E)) deallocate(E)
if(allocated(pU)) deallocate(pU)
if(allocated(pBU)) deallocate(pBU)
if(allocated(gtmp)) deallocate(gtmp)
end subroutine dealloc_mem
end subroutine ddogleg
!************************************************************************
!************************************************************************
subroutine dogleg_B(Delta,pB,grad,B,sk,pred,Bs)
!************************************************************************
! see Nocedal and Wright, equations 4.10-4.13, pg. 71-73.
implicit none
double precision, intent(in) :: Delta ! trust radius
double precision, dimension(:), intent(in) :: pB !
double precision, dimension(:), intent(in) :: grad ! gradient vector
double precision, dimension(:,:), intent(in) :: B ! the Hessian matrix
double precision, dimension(:), intent(out) :: sk ! the dogleg step
double precision, intent(out) :: pred ! predicted function value
double precision, dimension(:), intent(out) :: Bs
!local
integer :: ndim
double precision :: pB_norm,pU_norm,nom,denom,a,hb,c,kappa
double precision, dimension(:),allocatable :: pU,pBU,gtmp
ndim = size(grad)
allocate(pU(ndim))
allocate(pBU(ndim))
allocate(gtmp(ndim))
pB_norm = vecnorm(pB,2)
! CASE A -- the full Newton step is inside the trust radius
if(pB_norm .le. Delta) then
sk = pB
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
endif
! gtmp = matrix_dot_vec(B,grad)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,grad,1,0.0d0,gtmp,1)
! gtmp = dot_product(B,transpose(grad))
! call print_vectorR(gtmp,8)
nom = dot_product(grad,grad)
denom = dot_product(grad,gtmp)
pU = -1.d0*(nom/denom)*grad
! CASE B -- the Cauchy step reaches outside the border of the trust radius; let's scale it back to the border
pU_norm = vecnorm(pU,2)
if(pU_norm .gt. Delta) then
sk = (Delta/pU_norm)*pU
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
endif
! CASE C -- quadratic part; we need the point of intersection with the trust radius
pBU = pB - pU
a = dot_product(pBU,pBU)
hb = dot_product(pU,pBU) !half b, hb=0.5*b
c = dot_product(pU,pU) - Delta**2
kappa = (-1.d0*hb + sqrt(hb**2 - a*c))/a! kappa = (tau-1)
! write(*,'(A)',advance='no') &
! 'We need the intersection of the trust radius with that of'
! write(*,*) 'the dogleg step (Cauchy step + Newton step)'
! write(*,*) 'Trust radius tau is equal to ',kappa+1.d0
sk = pU + kappa*pBU
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
contains
subroutine dealloc_mem
implicit none
if(allocated(pU)) deallocate(pU)
if(allocated(pBU)) deallocate(pBU)
if(allocated(gtmp)) deallocate(gtmp)
end subroutine dealloc_mem
end subroutine dogleg_B
!************************************************************************
subroutine dogleg(Delta,pB,grad,H,sk,pred,Bs)
!************************************************************************
! see Nocedal and Wright, equations 4.10-4.13, pg. 71-73.
implicit none
double precision, intent(in) :: Delta ! trust radius
double precision, dimension(:), intent(in) :: pB !
double precision, dimension(:), intent(in) :: grad ! gradient vector
double precision, dimension(:,:), intent(in) :: H ! the inverse Hessian matrix, B^-1 = H
double precision, dimension(:), intent(out) :: sk ! the dogleg step
double precision, intent(out) :: pred ! predicted function value
double precision, dimension(:), intent(out) :: Bs
!local
integer :: ndim
double precision :: pB_norm,pU_norm,nom,denom,a,hb,c,kappa
double precision, dimension(:),allocatable :: pU,pBU,gtmp
double precision, dimension(:,:), allocatable :: B
! double precision, dimension(:,:), allocatable :: E
ndim = size(grad)
allocate(pU(ndim))
allocate(pBU(ndim))
allocate(gtmp(ndim))
allocate(B(ndim,ndim))
! allocate(E(ndim,ndim)) ! just for test remove csonti
call matrix_pseudoinverse(H,B)
! write(*,*)'inverse Hessian'
! call print_matrixR(H,9)
! write(*,*)'Hessian'
! call print_matrixR(B,9)
! E = matmul(H,B)
! write(*,*)'H*B'
! call print_matrixR(E,9)
pB_norm = vecnorm(pB,2)
! CASE A -- the full Newton step is inside the trust radius
if(pB_norm .le. Delta) then
! write(*,*) &
! 'The full Newton step is inside the trust radius'
sk = pB
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
endif
! gtmp = matrix_dot_vec(B,grad)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,grad,1,0.0d0,gtmp,1)
! gtmp = dot_product(B,transpose(grad))
! call print_vectorR(gtmp,8)
nom = dot_product(grad,grad)
denom = dot_product(grad,gtmp)
pU = -1.d0*(nom/denom)*grad
! CASE B -- the Cauchy step reaches outside the border of the trust radius; let's scale it back to the border
pU_norm = vecnorm(pU,2)
if(pU_norm .gt. Delta) then
sk = (Delta/pU_norm)*pU
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
! write(*,'(A)',advance='no') &
! 'Let us scale the Cauchy step (steepest descent) back '
! write(*,*) 'to the trust border'
call dealloc_mem
return
endif
! CASE C -- quadratic part; we need the point of intersection with the trust radius
pBU = pB - pU
a = dot_product(pBU,pBU)
hb = dot_product(pU,pBU) !half b, hb=0.5*b
c = dot_product(pU,pU) - Delta**2
kappa = (-1.d0*hb + sqrt(hb**2 - a*c))/a! kappa = (tau-1)
! write(*,'(A)',advance='no') &
! 'We need the intersection of the trust radius with that of'
! write(*,*) 'the dogleg step (Cauchy step + Newton step)'
! write(*,*) 'Trust radius tau is equal to ',kappa+1.d0
sk = pU + kappa*pBU
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
call dealloc_mem
return
contains
subroutine dealloc_mem
implicit none
if(allocated(B)) deallocate(B) ! deallocation in case A, B, and C !csonti
! if(allocated(E)) deallocate(E)
if(allocated(pU)) deallocate(pU)
if(allocated(pBU)) deallocate(pBU)
if(allocated(gtmp)) deallocate(gtmp)
end subroutine dealloc_mem
end subroutine dogleg
!************************************************************************
!************************************************************************
subroutine print_inverse(H)
!************************************************************************
! see Nocedal and Wright, equations 4.10-4.13, pg. 71-73.
implicit none
double precision, dimension(:,:), intent(in) :: H ! the inverse Hessian matrix, B^-1 = H
!local
integer :: ndim
double precision, dimension(:,:), allocatable :: B
ndim = size(H,1)
allocate(B(ndim,ndim))
call matrix_pseudoinverse(H,B)
write(*,*)'HESSIAN MATRIX'
call print_MatrixR(B,9)
if(allocated(B)) deallocate(B) ! deallocation in case A, B, and C !csonti
return
end subroutine print_inverse
!************************************************************************
!************************************************************************
subroutine quadratic(sk,grad,H,pred,Bs)
!************************************************************************
! see Nocedal and Wright, equations 4.10-4.13, pg. 71-73.
implicit none
double precision, dimension(:), intent(in) :: sk !
double precision, dimension(:), intent(in) :: grad ! gradient vector
double precision, dimension(:,:), intent(in) :: H ! the inverse Hessian matrix, B^-1 = H
double precision, intent(out) :: pred ! predicted function value
double precision, dimension(:), intent(out) :: Bs
!local
integer :: ndim
double precision, dimension(:,:), allocatable :: B
ndim = size(grad)
allocate(B(ndim,ndim))
call matrix_pseudoinverse(H,B)
! Bs = matrix_dot_vec(B,sk)
call dgemv('n',ndim,ndim,1.0d0,B,ndim,sk,1,0.0d0,Bs,1)
pred = -1.d0*(dot_product(grad,sk)+0.5d0*dot_product(sk,Bs))
if(allocated(B)) deallocate(B) ! deallocation in case A, B, and C !csonti
return
end subroutine quadratic
!************************************************************************
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! LINEAR ALGEBRA
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
double precision function vecnorm(vec,p)
! it returns the usual p-norm of the vector vec
! the infinit norm can be called with p>999
implicit none
double precision, dimension(:),intent(in) :: vec
integer, intent(in) :: p
!local
integer i,ndim
ndim = size(vec)
if(p .gt. 999) then ! this is the infinity norm
if ( dabs(maxval(vec)) .gt. dabs(minval(vec)) ) then
vecnorm = dabs(maxval(vec))
else
vecnorm = dabs(minval(vec))
endif
else
vecnorm = 0.d0
do i=1,ndim
vecnorm = vecnorm + dabs( vec(i) )**(p)
enddo
vecnorm = vecnorm**(1.d0/dble(p))
endif
return
end function vecnorm
!************************************************************************
!************************************************************************
function vec_norm_comp(A)
!************************************************************************
! it returns the components of the normal vector corresponding to A
! A is a 3-dimensional vector
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A
double precision, dimension(3) :: vec_norm_comp
!local
integer :: i
do i=1,3
vec_norm_comp(i) = A(i)/vec_length(A)
enddo
return
end function vec_norm_comp
!************************************************************************
!************************************************************************
function vec_comp(A,B)
!************************************************************************
! it returns the components of the vector pointing from A to B
! A and B are 3-dimensional points
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A,B
double precision, dimension(3) :: vec_comp
!local
integer :: i
do i=1,3
vec_comp(i) = B(i)-A(i)
enddo
return
end function vec_comp
!************************************************************************
!************************************************************************
double precision function vec_length(A)
!************************************************************************
! it returns the length of A (Euclidean norm, ||A||)
! for a 3D vector
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A
vec_length = 0.d0
vec_length = sqrt( vec_dot(A,A) )
return
end function vec_length
!************************************************************************
!************************************************************************
double precision function vec_dot(A,B)
!************************************************************************
! it returns the dot product of vectors A and B
! A and B are 3-dimensional vectors
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A,B
!local
integer :: i
vec_dot = 0.d0
do i=1,3
vec_dot = vec_dot + A(i)*B(i)
enddo
return
end function vec_dot
!************************************************************************
!************************************************************************
function vec_cross(A,B)
!************************************************************************
! it returns the cross product of vectors A and B
! A and B are 3-dimensional vectors
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A,B
double precision, dimension(3) :: vec_cross
!local
double precision,dimension(3) :: AxB
call crproduct(A,B,AxB)
vec_cross = AxB
return
end function vec_cross
!************************************************************************
!************************************************************************
double precision function vec_mixed(A,B,C)
!************************************************************************
! it returns the mixed product of vectors A, B, and C
!
! AxB.C
!
! A, B, and C are 3-dimensional vectors
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A,B,C
vec_mixed = vec_dot(vec_cross(A,B),C)
return
end function vec_mixed
!************************************************************************
!************************************************************************
subroutine crproduct(a,b,acb)
!************************************************************************
! Computes cross product of a,b in 3 dimensions
!************************************************************************
real*8 a(3),b(3),acb(3)
!
acb(1)=a(2)*b(3)-a(3)*b(2)
acb(2)=a(3)*b(1)-a(1)*b(3)
acb(3)=a(1)*b(2)-a(2)*b(1)
return
end subroutine crproduct
!
!************************************************************************
!************************************************************************
function w_prime_comp(u,v)
!************************************************************************
! it returns the component of the vector w'
! JCP, 117, 9160 (2002), Eq. 24
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: u,v
double precision, dimension(3) :: w_prime_comp
!local
double precision, dimension(3) :: vtmp
double precision, parameter :: eps=1.d-6 ! csonti more precise definition is required
double precision,dimension(3),parameter::v2=(/ 1.d0,-1.d0,1.d0/),&
v3=(/-1.d0, 1.d0,1.d0/)
vtmp = vec_cross(u,v)
if(vec_length(vtmp).ge.eps) then ! u and v are not parallel
w_prime_comp = vtmp
else
vtmp = vec_cross(u,v2)
if(vec_length(vtmp).ge.eps) then
w_prime_comp = vtmp
else
w_prime_comp = vec_cross(u,v3)
endif
endif
return
end function w_prime_comp
!************************************************************************
subroutine build_projection_hessian(B,P,csys)
!************************************************************************
! it builds the projection matrix P from B
!
! P = B*B+
!
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision,dimension(:,:),intent(in) :: B !m x n
double precision,dimension(size(B,1),size(B,1)),intent(inout)::P !m x m
!local
double precision,dimension(:,:),allocatable :: Bp !n x m
integer :: i,n,m
m = size(B,1)
n = size(B,2)
allocate(Bp(n,m),stat=i)
if(i /= 0) then
write(*,*) &
'Memory allocation was not successfull in build_projection'
call mrccend(1)
endif
call matrix_pseudoinverse(B,Bp)
call dgemm('n','n',m,m,n,1.d0,B,m,Bp,n,0.d0,P,m)
deallocate(Bp)
end subroutine
!************************************************************************
subroutine build_projection(B,P,csys)
!************************************************************************
! it builds the projection matrix P from B
!
! P = B*B+
!
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision,dimension(:,:),intent(in) :: B !m x n
double precision,dimension(size(B,1),size(B,1)),intent(inout)::P !m x m
!local
double precision,dimension(:,:),allocatable :: Bp !n x m
double precision,dimension(:,:),allocatable :: B2
integer :: i,n,m
m = size(B,1)
n = size(B,2)
allocate(Bp(n,m),stat=i)
if(i /= 0) then
write(*,*) &
'Memory allocation was not successfull in build_projection'
call mrccend(1)
endif
if(csys%nfrozen /= 0) then
allocate(b2(m,n))
call dcopy(n*m,b,1,b2,1)
call freezing_bmat_int(csys,b2)
call matrix_pseudoinverse(B2,Bp)
call dgemm('n','n',m,m,n,1.d0,B2,m,Bp,n,0.d0,P,m)
deallocate(b2)
else
call matrix_pseudoinverse(B,Bp)
call dgemm('n','n',m,m,n,1.d0,B,m,Bp,n,0.d0,P,m)
endif
deallocate(Bp)
end subroutine build_projection
!************************************************************************
!************************************************************************
subroutine matrix_inverse_sym(B,Binv)
!************************************************************************
! it computes Binv the inverse of symmetric positive definite matrix B
! using lapack routines:
!
! dpotrf - compute the Cholesky factorization of a real symmetric
! positive definite matrix
! dpotri - compute the inverse of a real symmetric positive
! definite matrix A using the Cholesky factorization
! A = U**T*U or A = L*L**T computed by DPOTRF
!
! it returns Binv
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: B !n x n
double precision,dimension(size(B,1),size(B,1)),&
intent(inout)::Binv!n x n
!local
integer :: n,info
external dpotrf
external dpotri
info=0
n = size(B,1)
Binv = B ! to keep B intact
! write(*,*) 'B in inverse routine'
! call print_matrixR(Binv,9)
call dpotrf('L',n,Binv,n,info)
if(info .ne. 0) then
write(*,*) &
'Cholesky factorization failed in matrix_inverse_sym',info
call mrccend(1)
endif
! write(*,*) 'LU decomposition of B'
! call print_matrixR(Binv,9)
call dpotri('L',n,Binv,n,info)
if(info .ne. 0) then
write(*,*) &
'Matrix inversion failed in matrix_inverse_sym'
call mrccend(1)
endif
! call print_matrixR(Binv,9)
end subroutine matrix_inverse_sym
!************************************************************************
!************************************************************************
subroutine matrix_inverse(B,Binv)
!************************************************************************
! it computes Binv the inverse of matrix B using lapack routines:
!
! dgetrf - LU decomposition (partial pivoting with raw interchanges)
! dgetri - This method inverts U and then computes inv(A) by solving
! the system inv(A)*L = inv(U) for inv(A)
!
! it returns Binv
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: B !n x n
double precision,dimension(size(B,1),size(B,1)),&
intent(inout)::Binv!n x n
!local
integer,dimension(size(B,1)) :: ipiv
integer :: n,res
integer :: LWORK,info
double precision,dimension(:),allocatable :: WORK !
external dgetrf
external dgetri
info=0
n = size(B,1)
LWORK = n*n
allocate(WORK(LWORK),STAT=res)
if(res .eq. 0) then
Binv = B ! to keep B intact
! write(*,*) 'B in inverse routine'
! call print_matrixR(Binv,9)
call dgetrf(n,n,Binv,n,IPIV,info)
if(info .ne. 0) then
write(*,*) &
'LU decomposition failed in subroutine matrix_inverse'
call mrccend(1)
endif
! write(*,*) 'LU decomposition of B'
! call print_matrixR(Binv,9)
call dgetri(n,Binv,n,IPIV,WORK,LWORK,info)
if(info .ne. 0) then
write(*,*) &
'Matrix inversion failed in subroutine matrix_inverse'
call mrccend(1)
endif
! call print_matrixR(Binv,9)
deallocate(WORK)
else
write(*,*) &
'Memory allocation was not successfull in matrix_nverse'
call mrccend(1)
endif
end subroutine matrix_inverse
!************************************************************************
!************************************************************************
subroutine matrix_diag(B,evec,eval)
!************************************************************************
! it computes the diagonal form of B using lapack routines:
!
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: B !n x n
double precision,dimension(size(B,1),size(B,1)),&
intent(inout) :: evec!n x n matrix of eigenvectors
double precision,dimension(size(B,1)),intent(inout) :: eval
!local
double precision,dimension(:) ,allocatable :: WORK !
integer :: res,info,LWORK,n
info=0
n = size(B,1)
evec = B ! keep B
LWORK=-1 ! check for optimal LWORK value
allocate(WORK(1))
call dsyev('V','U',n,evec,n,eval,WORK,LWORK,info) ! lapack routine for diagonalization
LWORK=WORK(1)
deallocate(WORK)
allocate(WORK(LWORK),STAT=res)
if(res .eq. 0) then
call dsyev('V','U',n,evec,n,eval,WORK,LWORK,info)
if(info.ne.0) then
write(*,*) 'Fatal error at the diagonalization!'
call mrccend(1)
endif
deallocate(WORK)
else
write(*,*) &
'Memory allocation was not successfull in matrix_diag'
call mrccend(1)
endif
end subroutine matrix_diag
!************************************************************************
!************************************************************************
subroutine matrix_pseudoinverse(B,Bp)
!************************************************************************
! it computes B+ the pseudoinverse of matrix B using singular value
! decomposition:
!
! B = U * SIGMA * V^T ; the svd of B
! B+ = V * (U * SIGMA^-1)^T
!
! it returns Bp
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: B !m x n
double precision,dimension(size(B,2),size(B,1)),intent(inout)::Bp!n x m
!local
double precision,dimension(:,:),allocatable :: U !m x m
double precision,dimension(:,:),allocatable :: VT !n x n
double precision,dimension(:), allocatable :: s
double precision,dimension(:,:),allocatable :: USi !
double precision,dimension(:,:),allocatable :: O ! temp matrix
double precision,dimension(:), allocatable :: WORK !
integer,dimension(:),allocatable :: IWORK
integer :: n,m,res
integer :: LWORK,info
info=0
m = size(B,1)
n = size(B,2)
allocate(U(m,m),STAT=res)
if(res /= 0) call dealloc_mem(res)
allocate(VT(n,n),STAT=res)
if(res /= 0) call dealloc_mem(res)
allocate(s(min(m,n)),STAT=res)
if(res /= 0) call dealloc_mem(res)
allocate(USi(m,n),STAT=res)
if(res /= 0) call dealloc_mem(res)
allocate(O(m,n),STAT=res)
if(res /= 0) call dealloc_mem(res)
allocate(IWORK(8*min(m,n)),STAT=res)
if(res /= 0) call dealloc_mem(res)
LWORK=-1 ! check for optimal LWORK value
allocate(WORK(1))
call dgesdd('A',m,n,B,m,s,U,m,VT,n,WORK,LWORK,IWORK,info) ! lapack routine for SVD
LWORK=int(WORK(1))
deallocate(WORK)
allocate(WORK(LWORK),STAT=res)
if(res /= 0) call dealloc_mem(res)
O = B ! to keep B intact
call dgesdd('A',m,n,O,m,s,U,m,VT,n,WORK,LWORK,IWORK,info) ! lapack routine for SVD
if(info/=0) then
write(*,'(A)') ' Matrix pseudoinverse calculation failed!'
write(*,'(A)') ' SVD did not converge!'
write(*,*) 'Error code: ',info
call mrccend(1)
endif
call matrix_pseudo1(U,s,USi)
call matrix_pseudo2(VT,USi,Bp)
call dealloc_mem(0)
return
contains
subroutine dealloc_mem(res)
implicit none
integer res
if(allocated(WORK)) deallocate(WORK)
if(allocated(U)) deallocate(U)
if(allocated(VT)) deallocate(VT)
if(allocated(s)) deallocate(s)
if(allocated(USi)) deallocate(USi)
if(allocated(O)) deallocate(O)
if(allocated(IWORK)) deallocate(IWORK)
if(res /= 0) then
write(*,*) &
'Memory allocation was not successfull in matrix_pseudoinverse'
call mrccend(1)
endif
end subroutine dealloc_mem
end subroutine matrix_pseudoinverse
!************************************************************************
!************************************************************************
subroutine matrix_pseudo2(VT,USi,Bp)
!************************************************************************
! it computes the matrix multiplication, V * USi^T,
! where USi = U * SIGMA^-1
!
! B = U * SIGMA * V^T the svd of B
! B+ = V * SIGMA^-1^T * U^T the pseudoinverse of B or otherwise
! B+ = V * (U * SIGMA^-1)^T
!
! it returns B+ /Bp/ the pseudoinverse of B
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: VT
double precision,dimension(:,:),intent(in) :: USi
double precision,&
dimension(size(VT,2),size(USi,1)),intent(inout) :: Bp
!local
integer :: n,m
m = size(VT,2)
n = size(USi,1)
call dgemm('t','t',m,n,m,1.d0,VT,m,USi,n,0.d0,Bp,m)
end subroutine matrix_pseudo2
!************************************************************************
!************************************************************************
subroutine matrix_pseudo1(U,s,USi)
!************************************************************************
! it computes the matrix multiplication, U*SIGMA^-1, by scaling the
! column vectors of U with the blas routine sdcal
! s is an array which elements are the column by column scaling
! factors (the diagonal elements of SIGMA)
!
! B = U * SIGMA * V^T the svd of B
! B+ = V * SIGMA^-1^T * U^T the pseudoinverse of B or otherwise
! B+ = V * (U * SIGMA^-1)^T
!
! it returns USi = U * SIGMA^-1
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: U
double precision,dimension(:) :: s
double precision,dimension(:,:),intent(inout) :: USi
!local
integer :: i,k,n,m
double precision, parameter :: eps = 1.0d-7
n = size(U,1)
m = size(USi,2)
k = size(s)
do i=1,m
if(i .le. k) then
USi(1:n,i) = U(1:n,i)
if( s(i) .gt. eps ) then ! the criterion is from Pulay - JCP, 96, 2856 (1992)
call dscal(n,1.d0/s(i),USi(1,i),1)
else
call dscal(n,0.d0,USi(1,i),1)
endif
else
USi(1:n,i) = 0.0d0
endif
enddo
end subroutine matrix_pseudo1
!************************************************************************
!************************************************************************
function matrix_dot_vec(A,vec)
!************************************************************************
! it returns as A*vec
! just a simple test routine
! A is an mxn and the length of vec is n
!************************************************************************
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(size(A,2)) :: vec
double precision, dimension(size(A,1)) :: matrix_dot_vec
!local
integer :: i,j,m,n
m = size(A,1)
n = size(A,2)
do i=1,m
matrix_dot_vec(i)=0.d0
do j=1,n
matrix_dot_vec(i) = matrix_dot_vec(i) + A(i,j)*vec(j)
! write(*,*) A(i,j), vec(j)
enddo
enddo
end function matrix_dot_vec
!************************************************************************
!************************************************************************
double precision function distance2(A,B)
!************************************************************************
! it returns the Euclidean distance between two 3D-points
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A,B
!local
integer :: i
distance2 = 0.d0
do i=1,3
distance2 = distance2 + (B(i)-A(i))**2
enddo
distance2 = dsqrt(distance2)
return
end function distance2
!************************************************************************
!************************************************************************
double precision function angle3(A,B,C)
!************************************************************************
! it returns the bond angle between atoms A-B-C;
! the angle is measured at vertex atom B
!
! A C
! \ /
! B
!
! i.e. the angle between bonds B-A and B-C is returned in radial
! JCP, 117, 9160 (2002), Eq. 23
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C
!local
double precision, dimension(3) :: up,vp,u,v
double precision :: dtmp
double precision, parameter :: eps = 1.d-8 ! csonti constraint
up = vec_comp(B,A) ! u'
vp = vec_comp(B,C) ! v'
u = vec_norm_comp(up)
v = vec_norm_comp(vp)
dtmp = vec_dot(u,v)
if( dtmp .gt. 1.d0 ) then
if( dtmp-1.d0 .gt. eps ) then
write(*,*) 'Something is wrong in function angle3!!!'
call mrccend(1)
else
dtmp = 1.d0
endif
elseif( dtmp .lt. -1.d0 ) then
if( -1.d0-dtmp .gt. eps ) then
write(*,*) 'Something is wrong in function angle3!!!'
call mrccend(1)
else
dtmp = -1.d0
endif
endif
angle3 = dacos(dtmp)
return
end function angle3
!************************************************************************
!************************************************************************
double precision function dihedral4(A,B,C,D)
!************************************************************************
! it returns the dihedral angle between bonds A-B and C-D;
! i.e. the angle between the planes defined by points A,B,C and B,C,D
!
! side view:
!
! A D
! \ /
! B -- C
!
! bond view (along B--C):
!
! A (D)
! \ / this conformation has a + sign
! B(C)
!
! (D) A
! \ / this conformation has a - sign
! B(C)
!
! A,B are in the front C,D are in the back
!
! the angle is restricted to be in the range -Pi,Pi and measured in radian
! JCP, 117, 9160 (2002), Eq. 31 as well as JCC, 16, 527 (1995)
!
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C, D
!local
double precision, dimension(3) :: up,vp,wp,u,v,w,uxw,vxw
double precision :: sinu,sinv,dtmp
double precision, parameter :: eps = 1.d-8 ! csonti constraint
up = vec_comp(B,A) ! u'
vp = vec_comp(C,D) ! v'
wp = vec_comp(B,C) ! w'
u = vec_norm_comp(up)
v = vec_norm_comp(vp)
w = vec_norm_comp(wp)
uxw = vec_cross(u,w)
vxw = vec_cross(v,w)
sinu= dsqrt(1.d0 - (vec_dot(u,w))**2)
sinv= dsqrt(1.d0 - (vec_dot(v,w))**2)
dtmp = vec_dot(uxw,vxw) / (sinu*sinv)
if( dtmp .gt. 1.d0 ) then
if( dtmp .gt. 1.0d0+eps) then
write(*,'(1X,A)') 'Warning in dihedral4! '
write(*,'(1X,A)') 'cosine of the angle is greater than 1'
write(*,'(1X,A)') 'resetting it to 1'
endif
dtmp = 1.d0
elseif( dtmp .lt. -1.d0 ) then
if( dtmp .lt. -1.0d0-eps ) then
write(*,'(1X,A)') 'Warning in dihedral4! '
write(*,'(1X,A)') 'cosine of the angle is less than -1'
write(*,'(1X,A)') 'resetting it to -1'
endif
dtmp = -1.d0
endif
dihedral4 = dacos( dtmp )
if( vec_mixed(w,u,v) .lt. 0.d0 ) dihedral4 = -1.d0*dihedral4
return
end function dihedral4
!************************************************************************
double precision function improper(A,B,C,D)
!************************************************************************
! The atoms A,B and C are connected to the central atom D. The A,B and
! D atoms define a plane. It returns the angle between the plane and the
! DC vector
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C, D
double precision vec1(3),vec2(3),normal(3)
double precision length
vec1 = A-D
vec2 = B-D
call crproduct(vec1,vec2,normal)
vec1 = C-D
length=vec_length(vec1)*vec_length(normal)
improper = dasin(vec_dot(vec1,normal)/length)
return
end function
!************************************************************************
logical function is_parallel(A,B,C)
!************************************************************************
! it checks whether or not the lines (A,B) and (B,C) are parallel
! or A, B, and C are on the same line
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C
!local
double precision, dimension(3) :: up,vp,u,v,uxv
is_parallel=.False.
up = vec_comp(B,A) ! u'
vp = vec_comp(B,C) ! v'
u = vec_norm_comp(up)
v = vec_norm_comp(vp)
uxv = vec_cross(u,v)
if(vec_dot(uxv,uxv) .lt. 1e-6) is_parallel=.True.
! write(*,*) 'parallel',is_parallel, A,B,C,up,vp
return
end function is_parallel
!************************************************************************
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! WILSON'S B-matrix related
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
subroutine build_Wilson_Bmat(natoms,coords,red_int_matrix,&
Bmatrix)
!************************************************************************
! it builds Wilson's B-matrix for redundant internal coordinates
!************************************************************************
implicit none
integer, intent(in) :: natoms
double precision, dimension(3,natoms), intent(in) :: coords
integer, dimension(:,:),intent(in) :: red_int_matrix
double precision :: Bmatrix(size(red_int_matrix,2),3*natoms)
!local
integer :: i,ncol
ncol = size(red_int_matrix,2)
do i=1,ncol
call zero_line_i(Bmatrix,i)
if(red_int_matrix(1,i) .ne. 0) then
call Bmat_line(natoms,coords,red_int_matrix(1:,i),i,&
Bmatrix)
! the columns of red_int_matrix contain the info for the given internal coordinate
! based on this info the appropriate line/row is filled in the B-matrix
endif
enddo
end subroutine build_Wilson_Bmat
!************************************************************************
subroutine Bmat_line(natoms,coords,column,idx,Bmatrix)
!************************************************************************
! it builds one line in Wilson's B-matrix
!************************************************************************
implicit none
integer, intent(in) :: natoms
double precision, dimension(3,natoms), intent(in) :: coords
integer, dimension(:), intent(in) :: column
integer, intent(in) :: idx
double precision, dimension(:,:), intent(inout)::Bmatrix
!local
integer :: btype,i,j,k,l
double precision, dimension(6) :: bond_vec
double precision, dimension(9) :: angle_vec
double precision, dimension(12) :: dihedral_vec
btype = column(1) ! type of int. coord
i = column(2) ! atom1
j = column(3) ! atom2
k = column(4) ! atom3
l = column(5) ! atom4
! write(*,*)'i,j,k,l: ',i,j,k,l
if(btype .eq. 1 .or. btype .eq. 4 .or. btype .eq. 5 .or. &
btype .eq. 6 .or. btype .eq. 7) then ! bond
bond_vec = Bmat_bond(coords(1:3,i),coords(1:3,j))
Bmatrix(idx,3*(i-1)+1:3*(i-1)+3) = bond_vec(1:3)
Bmatrix(idx,3*(j-1)+1:3*(j-1)+3) = bond_vec(4:6)
elseif(column(1) .eq. 2) then ! angle
angle_vec = Bmat_angle(coords(1:3,i),&
coords(1:3,j),&
coords(1:3,k))
Bmatrix(idx,3*(i-1)+1:3*(i-1)+3) = angle_vec(1:3)
Bmatrix(idx,3*(j-1)+1:3*(j-1)+3) = angle_vec(4:6)
Bmatrix(idx,3*(k-1)+1:3*(k-1)+3) = angle_vec(7:9)
elseif(column(1) .eq. 3) then ! dihedral
dihedral_vec = Bmat_dihedral(coords(1:3,i),&
coords(1:3,j),&
coords(1:3,k),&
coords(1:3,l))
Bmatrix(idx,3*(i-1)+1:3*(i-1)+3) = dihedral_vec(1:3)
Bmatrix(idx,3*(j-1)+1:3*(j-1)+3) = dihedral_vec(4:6)
Bmatrix(idx,3*(k-1)+1:3*(k-1)+3) = dihedral_vec(7:9)
Bmatrix(idx,3*(l-1)+1:3*(l-1)+3) = dihedral_vec(10:12)
elseif(column(1) == improper_code) then
dihedral_vec = bmat_improper(coords(1,l),coords(1,i),&
coords(1,k),coords(1,j))
Bmatrix(idx,3*(i-1)+1:3*(i-1)+3) = dihedral_vec(4:6)
Bmatrix(idx,3*(j-1)+1:3*(j-1)+3) = dihedral_vec(10:12)
Bmatrix(idx,3*(k-1)+1:3*(k-1)+3) = dihedral_vec(7:9)
Bmatrix(idx,3*(l-1)+1:3*(l-1)+3) = dihedral_vec(1:3)
endif
end subroutine Bmat_line
!************************************************************************
!************************************************************************
subroutine zero_line_i(A,i)
implicit none
double precision,dimension(:,:),intent(inout) :: A
integer, intent(in) :: i
!local
integer :: k,ndim
ndim = size(A,2)
do k=1,ndim
A(i,k) = 0.d0
enddo
end subroutine zero_line_i
!************************************************************************
!************************************************************************
subroutine active_subspace(nintcoord,natoms,nonred,Bmatrix,Gmat)
!************************************************************************
! Delocalizing RIC B matrix: J. Chem. Phys. 105, 192 (1996)
!************************************************************************
implicit none
integer :: nintcoord,natoms
integer :: nonred
double precision :: Bmatrix(nintcoord,3*natoms)
double precision :: Gmat(nintcoord,nintcoord)
integer :: info,lwork,i
double precision, parameter :: delta = 1.0d-7
double precision :: work1(1)
double precision, allocatable :: eigval(:),work(:)
allocate(eigval(nintcoord))
! Eigendecomposition of G=BB^T
call dsyrk('u','n',nintcoord,3*natoms,1.0d0,Bmatrix,nintcoord,&
0.0d0,Gmat,nintcoord)
call dsyev('v','u',nintcoord,Gmat,nintcoord,eigval,work1,-1,info)
lwork=int(work1(1))
allocate(work(lwork))
call dsyev('v','u',nintcoord,Gmat,nintcoord,eigval,work,lwork,&
info)
! Counting nonredundant coordinates
nonred=0
do i=1,nintcoord
if(dabs(eigval(i))>delta) nonred=nonred+1
enddo
deallocate(eigval)
deallocate(work)
end subroutine
!************************************************************************
subroutine get_deloc_matrix(nintcoord,nonred,Gmat,Umatrix)
!************************************************************************
! Returns the delocalization matrix U
! G contains the eigenvector of BB^T returned by active_subspace
!************************************************************************
implicit none
integer :: nintcoord,nonred
double precision, intent(in) :: Gmat(nintcoord,nintcoord)
double precision, intent(out) :: Umatrix(nintcoord,nonred)
integer :: nred
nred=nintcoord-nonred
call dcopy(nintcoord*nonred,Gmat(1,nred+1),1,Umatrix,1)
end subroutine
!************************************************************************
subroutine delocalize(nintcoord,nonred,n,Umatrix,mat,mat_deloc)
!************************************************************************
! Delocalizes a matrix with the U matrix returned by get_deloc_matrix
!************************************************************************
implicit none
integer, intent(in) :: nintcoord,nonred,n
double precision, intent(in) :: Umatrix(nintcoord,nonred)
double precision, intent(in) :: mat(nintcoord,n)
double precision, intent(out) :: mat_deloc(nonred,n)
call dgemm('t','n',nonred,n,nintcoord,1.0d0,Umatrix,nintcoord,&
mat,nintcoord,0.0d0,mat_deloc,nonred)
end subroutine
!************************************************************************
subroutine delocalize_mat(csys,mat,mat_deloc)
!************************************************************************
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: mat(csys%ncoord_ric,csys%ncoord_ric)
double precision :: mat_deloc(csys%ncoord,csys%ncoord)
double precision, allocatable :: work(:,:)
allocate(work(csys%ncoord,csys%ncoord_ric))
call dgemm('t','n',csys%ncoord,csys%ncoord_ric,csys%ncoord_ric,&
1.0d0,csys%Umatrix,csys%ncoord_ric,0.0d0,work,csys%ncoord)
call dgemm('n','n',csys%ncoord,csys%ncoord,csys%ncoord_ric,&
1.0d0,work,csys%ncoord,0.0d0,mat_deloc,csys%ncoord)
deallocate(work)
end subroutine
!************************************************************************
function Bmat_bond(A, B)
!************************************************************************
! it returns the component of Wilson's B-matrix for the bonds
! between A and B
! JCP, 117, 9160 (2002), Eq. 17
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B
double precision, dimension(6) :: Bmat_bond
!local
double precision, dimension(3) :: up,u
up = vec_comp(A,B) ! u'=B-A
u = vec_norm_comp(up)
Bmat_bond(1:3) = -1.d0*u !A
Bmat_bond(4:6) = +1.d0*u !B
return
end function Bmat_bond
!************************************************************************
!************************************************************************
function Bmat_angle(A, B, C)
!************************************************************************
! it returns the component of Wilson's B-matrix for the bond angles
! of A-B-C; the angle is measured at vertex atom B
! i.e. the angle between bonds B-A and B-C
! JCP, 117, 9160 (2002), Eq. 25 !!!!!!it is wrong in this article!!!!!!
! W. F. MURPHY. Can. J. Chem. 69, 1672 (1991)
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C
double precision, dimension(9) :: Bmat_angle
!local
double precision, dimension(3) :: up,vp,u,v,wp,w,uxw,wxv
double precision :: lu,lv,angle
angle = angle3(A, B, C)
up = vec_comp(B,A)
lu = vec_length(up)*dsin(angle) ! lambda_u
vp = vec_comp(B,C)
lv = vec_length(vp)*dsin(angle) ! lambda_v
u = vec_norm_comp(up)
v = vec_norm_comp(vp)
wp = w_prime_comp(u,v)
w = vec_norm_comp(wp)
uxw = vec_cross(u,w)
wxv = vec_cross(w,v)
Bmat_angle(1:3) = uxw/lu
Bmat_angle(4:6) = (-1.d0*uxw/lu - 1.d0*wxv/lv)
Bmat_angle(7:9) = wxv/lv
return
end function Bmat_angle
!************************************************************************
!************************************************************************
function Bmat_dihedral(A, B, C, D)
!************************************************************************
! it returns the component of Wilson's B-matrix for the dihedral angles
! the angle is restricted to be in the range -Pi,Pi and measured in radian
! JCP, 117, 9160 (2002), Eq. 34
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C, D
double precision, dimension(12) :: Bmat_dihedral
!local
double precision, dimension(3) :: up,vp,wp, u,v,w, uxw,vxw
double precision :: lu,lv,lw, sinu2,sinv2, cosu,cosv, su,sv,cu,cv
up = vec_comp(B,A)
lu = vec_length(up) ! lambda_u
vp = vec_comp(C,D)
lv = vec_length(vp) ! lambda_v
wp = vec_comp(B,C)
lw = vec_length(wp) ! lambda_w
u = vec_norm_comp(up)
v = vec_norm_comp(vp)
w = vec_norm_comp(wp)
uxw = vec_cross(u,w)
vxw = vec_cross(v,w)
sinu2= (1.d0 - (vec_dot(u,w))**2)
sinv2= (1.d0 - (vec_dot(v,w))**2)
cosu= 1.d0*vec_dot(u,w)
cosv= -1.d0*vec_dot(v,w)
su = 1.d0/(lu*sinu2)
sv = 1.d0/(lv*sinv2)
cu = cosu/(lw*sinu2)
cv = cosv/(lw*sinv2)
Bmat_dihedral(1:3) = 1.d0*uxw*su + 0.d0 + 0.d0
Bmat_dihedral(4:6) = -1.d0*uxw*su + 0.d0 + 1.d0*(uxw*cu+vxw*cv)
Bmat_dihedral(7:9) = 0.d0 + vxw*sv -1.d0*(uxw*cu+vxw*cv)
Bmat_dihedral(10:12) = 0.d0 -1.d0*vxw*sv +0.d0
return
end function Bmat_dihedral
!************************************************************************
function Bmat_improper(A, B, C, D)
!************************************************************************
! it returns the component of Wilson's B-matrix for the improper dihedral
! angles. Atoms A,B and C are connected to the central atom D, the plane
! is defined by the atoms B,C and D.
!************************************************************************
implicit none
double precision, dimension(3), intent(in) :: A, B, C, D
double precision, dimension(12) :: Bmat_improper
!local
double precision, dimension(3) :: e41,e42,e43
double precision :: r41,r42,r43
double precision :: sin_theta,cos_theta,tan_theta,cos_phi,sin_phi
e41=A-D
e42=B-D
e43=C-D
r41=vec_length(e41)
r42=vec_length(e42)
r43=vec_length(e43)
e41=e41/r41
e42=e42/r42
e43=e43/r43
cos_phi = vec_dot(e42,e43)
sin_phi = dsqrt(1.0d0 - cos_phi*cos_phi)
sin_theta = vec_mixed(e42,e43,e41)/sin_phi
cos_theta = dsqrt(1.0d0 - sin_theta*sin_theta)
tan_theta = sin_theta/cos_theta
bmat_improper(1:3) = (vec_cross(e42,e43)/(cos_theta*sin_phi)-&
tan_theta*e41)/r41
bmat_improper(4:6) = (vec_cross(e43,e41)/(cos_theta*sin_phi)-&
tan_theta/(sin_phi*sin_phi)*(e42-cos_phi*e43))/r42
bmat_improper(7:9) = (vec_cross(e41,e42)/(cos_theta*sin_phi)-&
tan_theta/(sin_phi*sin_phi)*(e43-cos_phi*e42))/r43
bmat_improper(10:12) = -bmat_improper(1:3)-bmat_improper(4:6)-&
bmat_improper(7:9)
end function
!*************************************************************************
subroutine build_red_int_matrix(natoms,atnums,coords,&
red_int_matrix,natomswdummy,coordswdummy)
!************************************************************************
! it builds red_int_matrix (matrix of redundant internal coordinates)
! its format as follows:
!
! red_int_matrix(1,i) ! type
! red_int_matrix(2,i) ! first atom
! red_int_matrix(3,i) ! second atom
! red_int_matrix(4,i) ! third atom
! red_int_matrix(5,i) ! fourth atom
!
! i refers to the i^th internal coordinate
!
! atnums is an array of atomic numbers
!************************************************************************
implicit none
integer :: natoms,atnums(natoms),natomswdummy
integer, allocatable :: red_int_matrix(:,:)
double precision, target :: coords(3,natoms)
double precision, allocatable, target :: coordswdummy(:,:)
!local
integer, allocatable, target :: bond_matrix(:,:)
integer, allocatable, target :: bond_matrix_new(:,:)
integer, pointer :: bm(:,:)
integer, allocatable :: rim_angles(:,:)
integer, allocatable :: rim_dummy_angles(:,:)
integer, allocatable :: rim_dihedrals(:,:)
integer, allocatable :: dummy_bonds(:)
integer :: nb,na,nd,nlin,nintcoord
integer :: ndummy
integer :: ii,i
double precision, pointer :: cart_coords(:,:)
allocate(bond_matrix(natoms,natoms))
! build bond matrix from covalent and interfragment bonds
call build_bond_matrix(natoms,atnums,coords,bond_matrix)
nb = nint_bonds(bond_matrix)
! write(*,'(A,I4)') 'nb: ',nb
! get the bond angles
na = nint_angles(bond_matrix)
allocate(rim_angles(5,na))
rim_angles=0
call get_angles(bond_matrix,rim_angles)
! check for linear angles
! linear angles are removed and dummy atoms are added if needed
call check_linear(natoms,coords,na,rim_angles,bond_matrix,&
natomswdummy,coordswdummy,dummy_bonds,nlin)
ndummy = natomswdummy - natoms
if(ndummy == 0) then
bm => bond_matrix
cart_coords => coords
else
! building new bond matrix with dummy atoms
allocate(bond_matrix_new(natomswdummy,natomswdummy))
allocate(rim_dummy_angles(5,2*ndummy))
bm => bond_matrix_new
cart_coords => coordswdummy
call add_dummy_atoms(natoms,natomswdummy,ndummy,bond_matrix,&
bond_matrix_new,rim_angles,rim_dummy_angles,dummy_bonds)
endif
! get the dihedral angles
! dihedral angles can contain dummy atoms as well
nd = nint_dihedrals(bm,cart_coords,ndummy)
allocate(rim_dihedrals(5,nd))
rim_dihedrals = 0
call get_dihedrals(natomswdummy,bm,cart_coords,rim_dihedrals,nd,&
ndummy,dummy_bonds,rim_angles)
! write(*,*) 'rim angles'
! write(*,'(5I4)') rim_angles
! write(*,*) 'rim dummy angles'
! write(*,'(5I4)') rim_dummy_angles
! write(*,*) 'rim dihedrals'
! write(*,'(5I4)') rim_dihedrals
! Building the final redundant internal matrix
nintcoord = (nb + ndummy) + (na-nlin+2*ndummy) + nd ! # internal coordinates
allocate(red_int_matrix(5,nintcoord))
red_int_matrix=0
! Get bonds
call get_bonds(bm,red_int_matrix)
! copy angles
ii=nb+ndummy
do i=1,na
if(rim_angles(1,i) /= 0) then
ii=ii+1
red_int_matrix(:,ii)=rim_angles(:,i)
endif
enddo
! copy dummy angles
do i=1,2*ndummy
ii=ii+1
red_int_matrix(:,ii) = rim_dummy_angles(:,i)
enddo
! copy dihedral angles
do i=1,nd
ii=ii+1
red_int_matrix(:,ii)=rim_dihedrals(:,i)
enddo
! deallocate memory
if(allocated(bond_matrix)) deallocate(bond_matrix)
if(allocated(bond_matrix_new)) deallocate(bond_matrix_new)
if(allocated(rim_angles)) deallocate(rim_angles)
if(allocated(rim_dummy_angles))deallocate(rim_dummy_angles)
if(allocated(rim_dihedrals)) deallocate(rim_dihedrals)
if(allocated(dummy_bonds)) deallocate(dummy_bonds)
end subroutine build_red_int_matrix
!************************************************************************
subroutine print_intcoords(rim,coords,inttype)
!************************************************************************
! it prints the internal coordinates using the redundant internal
! coordinate matrix (rim) and the cartesian coordinates (coords)
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: rim ! redundant internal coordinate matrix
double precision, dimension(:,:),intent(in) :: coords ! cartesian coordinates
character(len=*), intent(in) :: inttype ! type of the internal coordinate
!local
integer :: natoms,nintcoord,code,i,k,l,m,n,ivalue
double precision :: fvalue
natoms = size(coords,2)
nintcoord = size(rim,2)
if(inttype(1:4) .eq. 'bond') then
ivalue = 1
elseif(inttype(1:5) .eq. 'angle') then
ivalue = 2
elseif(inttype(1:8) .eq. 'dihedral') then
ivalue = 3
elseif(inttype(1:8) .eq. 'aux bond') then
ivalue = 4
else
write(*,*) 'Invalid internal coordinate type was asked in &
subroutine print_intcoords!!!'
call mrccend(1)
endif
do i=1,nintcoord
code = rim(1,i)
k = rim(2,i)
l = rim(3,i)
m = rim(4,i)
n = rim(5,i)
if(code .gt. 0) then
if(code .eq. ivalue) then
if(ivalue .eq. 1) then
fvalue = &
distance2(coords(1:3,k),&
coords(1:3,l))
write(*,101) 'bond:',k,'-',l, fvalue
elseif(ivalue .eq. 2) then
fvalue = &
angle3(coords(1:3,k),&
coords(1:3,l),&
coords(1:3,m))
fvalue = 180.d0*fvalue/PI
write(*,102) 'angle:',k,'-',l,'-',m, fvalue
elseif(ivalue .eq. 3) then
fvalue = &
dihedral4(coords(1:3,k),&
coords(1:3,l),&
coords(1:3,m),&
coords(1:3,n))
fvalue = 180.d0*fvalue/PI
write(*,103) 'dihedral:',k,'-',l,'-',m,'-',n, fvalue
elseif(ivalue .eq. 4) then
fvalue = &
distance2(coords(1:3,k),&
coords(1:3,l))
write(*,101) 'aux bond:',k,'-',l, fvalue
endif
endif
else
write(*,*) 'Invalid internal coordinate index in the &
redundant internal coordinate matrix!!!'
call mrccend(1)
endif
enddo
101 format(A20,I4,A4,I4,F12.6)
102 format(A20,I4,A4,I4,A4,I4,F12.6)
103 format(A20,I4,A4,I4,A4,I4,A4,I4,F12.6)
end subroutine print_intcoords
!************************************************************************
!************************************************************************
subroutine build_bond_matrix(natoms,atnum,coords,bond_matrix)
!************************************************************************
! it builds the bond matrix which is like the distance matrix but
! (i,j)=1 if there is a "regular" bond zero otherwise
! auxiliary bonds are indicated by the integer 4, i.e., (i,j)=4
! H-bonds will be indicated by 5 and inter-fragment bonds by 6
!************************************************************************
implicit none
integer :: natoms
integer, intent(in) :: atnum(natoms) ! atomic/proton number
double precision, intent(in) :: coords(3,natoms)
integer, intent(out) :: bond_matrix(natoms,natoms)
bond_matrix=0
call regular_bonds(natoms,atnum,coords,bond_matrix)
call fragment_bonds(natoms,coords,bond_matrix)
return
end subroutine build_bond_matrix
!************************************************************************
subroutine regular_bonds(natoms,atnum,coords,bond_matrix)
!************************************************************************
! Adds regular and auxiliary bonds to the bond matrix
!************************************************************************
implicit none
integer :: natoms
integer, intent(in) :: atnum(natoms) ! atomic/proton number
double precision, intent(in) :: coords(3,natoms)
integer :: bond_matrix(natoms,natoms)
!local
double precision d,cr,rb,ab
integer :: i,j
do j=1,natoms
do i=j+1,natoms
d = distance2(coords(1:3,j),coords(1:3,i))
cr = get_cov_radius(atnum(j))+get_cov_radius(atnum(i)) ! sum of covalent radii
rb = 1.3d0*cr ! regular bond
ab = 2.6d0*cr ! auxiliary bond !csonti
if( d .le. rb ) then
! write(*,*) 'Regular bond between ', i,j,d
bond_matrix(i,j) = 1
bond_matrix(j,i) = 1
elseif (d .le. ab) then
! write(*,*) 'Auxiliary bond between ', i,j,d
bond_matrix(i,j) = 4
bond_matrix(j,i) = 4
endif
enddo ! i
enddo ! j
end subroutine
!************************************************************************
subroutine fragment_bonds(natoms,coords,bond_matrix)
!************************************************************************
! Adds bonds between fragments
!************************************************************************
implicit none
integer :: natoms
double precision, intent(in) :: coords(3,natoms)
integer :: bond_matrix(natoms,natoms)
!local
integer :: iatoms,jatoms
integer :: nfrag,n_fbonds
integer, allocatable :: fragment_code(:)
allocate(fragment_code(natoms))
call graph_coloring(natoms,bond_matrix,fragment_code)
nfrag=maxval(fragment_code)
if(nfrag /= 1) then
n_fbonds=nfrag*(nfrag-1)/2
call update_bond_matrix(natoms,natoms,fragment_code,&
n_fbonds,coords,bond_matrix)
do iatoms=1,natoms
do jatoms=1,iatoms-1
if(bond_matrix(jatoms,iatoms)==afbond_code) &
n_fbonds=n_fbonds+1
enddo
enddo
endif
deallocate(fragment_code)
return
end subroutine
!************************************************************************
subroutine get_bonds(bond_matrix,red_int_matrix)
!************************************************************************
! the first few columns (bond-related part) of the redundant internal
! coordinate matrix, red_int_matrix, are set up.
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: bond_matrix
integer, dimension(:,:),intent(inout) :: red_int_matrix
!local
integer :: i,j,k,natoms,code
natoms = size(bond_matrix,1)
k = 0
do j=1,natoms
do i=j+1,natoms
code = bond_matrix(i,j)
if(code==bond_code .or. code==abond_code .or. &
code==fbond_code .or. code==afbond_code .or. &
code==hbond_code) then
k = k+1
red_int_matrix(1,k) = code ! type: regular bond
red_int_matrix(2,k) = j ! first atom
red_int_matrix(3,k) = i ! second atom
red_int_matrix(4,k) = 0 ! third atom
red_int_matrix(5,k) = 0 ! fourth atom
endif
enddo ! i
enddo ! j
return
end subroutine get_bonds
!************************************************************************
!************************************************************************
integer function nint_bonds(bond_matrix)
!************************************************************************
! number of internal coordinate bonds is returned
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: bond_matrix
!local
integer :: i,j,k,natoms,code
natoms = size(bond_matrix,1)
k = 0
do j=1,natoms
do i=j+1,natoms
code = bond_matrix(i,j)
if(code .ne. 0) k = k+1
enddo ! i
enddo ! j
nint_bonds = k
return
end function nint_bonds
!************************************************************************
!************************************************************************
subroutine get_angles(bond_matrix,red_int_matrix)
!************************************************************************
! it determines the angles between bonds
! the second (angle-related) part of the redundant internal
! coordinate matrix, red_int_matrix, is also set up.
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: bond_matrix
integer, dimension(:,:),intent(inout) :: red_int_matrix
!local
integer :: i,j,natoms,ncol
integer :: first = 1
integer :: first_zero !first zero position in red_int_matrix
natoms = size(bond_matrix,1)
ncol = size(red_int_matrix,2)
do i=1,ncol
if(red_int_matrix(1,i) .eq. 0) then
first_zero = i
exit
endif
enddo
do j=1,natoms
call rec_g_angle(first,bond_matrix(1:natoms,j),red_int_matrix,&
first_zero,j)
enddo
end subroutine get_angles
!************************************************************************
!************************************************************************
recursive subroutine rec_g_angle(first,column,red_int_matrix,&
first_zero,cidx)
!************************************************************************
implicit none
integer, intent(inout) :: first,first_zero
integer, dimension(:), intent(in) :: column
integer, dimension(:,:), intent(inout) :: red_int_matrix
integer, intent(in) :: cidx !column index of bond matrix
!local
integer :: i,k,last,nbonds
logical :: lfirst
integer :: first_bond !first occasion of a bond in the given column of the bond matrix
last = size(column)
first_bond = 0
nbonds = 0
lfirst = .True.
do i=first,last
if(is_primary_bond(column(i))) then
nbonds = nbonds+1
if (lfirst) then
first_bond = i
lfirst = .False.
endif
endif
if(nbonds .eq. 2) exit
enddo
if(nbonds .eq. 2) then
do k=first_bond+1,last
if(is_primary_bond(column(k))) then
red_int_matrix(1,first_zero) = 2 ! type: angle
red_int_matrix(2,first_zero) = first_bond ! first atom
red_int_matrix(3,first_zero) = cidx ! second, vertex atom
red_int_matrix(4,first_zero) = k ! third atom
red_int_matrix(5,first_zero) = 0 ! fourth atom
first_zero = first_zero+1
endif
enddo
if(first_bond .lt. last) then
first_bond = first_bond+1
call rec_g_angle(first_bond,column,red_int_matrix,&
first_zero,cidx)
endif
endif
return
end subroutine rec_g_angle
!************************************************************************
!************************************************************************
integer function nint_angles(bond_matrix)
!************************************************************************
! number of internal coordinate angles is returned
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: bond_matrix
!local
integer :: j,natoms
integer :: first = 1
nint_angles = 0
natoms = size(bond_matrix,1)
nint_angles = 0
do j=1,natoms
call rec_n_angle(first,nint_angles,bond_matrix(1:natoms,j))
enddo
return
end function nint_angles
!************************************************************************
!************************************************************************
recursive subroutine rec_n_angle(first,nint_angles,column)
!************************************************************************
implicit none
integer, intent(inout) :: first,nint_angles
integer, dimension(:), intent(in) :: column
!local
integer :: i,j,k,last,nbonds
logical :: lfirst
integer :: first_bond !first occasion of a bond in the given column of the bond matrix
last = size(column)
first_bond = 0
nbonds = 0
lfirst = .True.
do i=first,last
if(is_primary_bond(column(i))) then
nbonds = nbonds+1
if (lfirst) then
first_bond = i
lfirst = .False.
endif
endif
if(nbonds .eq. 2) exit
enddo
if(nbonds .eq. 2) then
j = 0
do k=first_bond+1,last
if(is_primary_bond(column(k))) then
j = j+1
endif
enddo
nint_angles = nint_angles + j
if(first_bond .lt. last) then
first_bond = first_bond+1
call rec_n_angle(first_bond,nint_angles,column)
endif
endif
return
end subroutine rec_n_angle
!************************************************************************
!************************************************************************
subroutine get_angles_o(bond_matrix,red_int_matrix)
!************************************************************************
! it determines the angles between bonds
! the second (angle-related) part of the redundant internal
! coordinate matrix, red_int_matrix, is also set up.
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: bond_matrix
integer, dimension(:,:),intent(inout) :: red_int_matrix
!local
integer :: i,j,k,natoms,nbonds,ncol
logical :: lfirst
integer :: first_bond !first occasion of a bond in the given column of the bond matrix
integer :: first_zero !first zero position in red_int_matrix
natoms = size(bond_matrix,1)
ncol = size(red_int_matrix,2)
do i=1,ncol
if(red_int_matrix(1,i) .eq. 0) then
first_zero = i
exit
endif
enddo
first_bond=0
do j=1,natoms
nbonds = 0
lfirst = .True.
do i=1,natoms
if(bond_matrix(i,j) .eq. 1) then
nbonds = nbonds+1
if (lfirst) then
first_bond=i
lfirst = .False.
endif
endif
if(nbonds .eq. 2) exit
enddo
if(nbonds .eq. 2) then
do k=first_bond+1,natoms ! csonti implement a recursive algorithm here
if(bond_matrix(k,j) .eq. 1) then
red_int_matrix(1,first_zero) = 2 ! type: angle
red_int_matrix(2,first_zero) = first_bond ! first atom
red_int_matrix(3,first_zero) = j ! second, vertex atom
red_int_matrix(4,first_zero) = k ! third atom
red_int_matrix(5,first_zero) = 0 ! fourth atom
first_zero = first_zero+1
endif
enddo
endif
enddo
end subroutine get_angles_o
!************************************************************************
!************************************************************************
integer function nint_angles_o(bond_matrix)
!************************************************************************
! number of internal coordinate angles is returned
!************************************************************************
implicit none
integer, dimension(:,:),intent(in) :: bond_matrix
!local
integer :: i,j,k,l,natoms,nbonds
logical :: lfirst
integer :: first_bond !first occasion of a bond in the given column of the bond matrix
nint_angles_o = 0
natoms = size(bond_matrix,1)
first_bond=0
do j=1,natoms
nbonds = 0
lfirst = .True.
do i=1,natoms
if(bond_matrix(i,j) .eq. 1) then
nbonds = nbonds+1
if (lfirst) then
first_bond=i
lfirst = .False.
endif
endif
if(nbonds .eq. 2) exit
enddo
if(nbonds .eq. 2) then
l = 0
do k=first_bond+1,natoms ! csonti implement a recursive algorithm here
if(bond_matrix(k,j) .eq. 1) then
l = l+1
endif
enddo
nint_angles_o = nint_angles_o + l
endif
enddo
return
end function nint_angles_o
!************************************************************************
!************************************************************************
subroutine get_dihedrals(natoms,bond_matrix,coords,red_int_matrix,&
ndihedral,ndummy,dummy_bonds,rim_angles)
!************************************************************************
! it determines the dihedral angles between the first and last bonds of
! three consecutive bonds
! the third (dihedral-related) part of the redundant internal
! coordinate matrix, red_int_matrix, is also set up.
!************************************************************************
implicit none
integer :: natoms,ndihedral,ndummy
integer :: dummy_bonds(ndummy)
integer :: bond_matrix(natoms,natoms),red_int_matrix(5,ndihedral)
integer :: rim_angles(5,*)
double precision :: coords(3,natoms)
!local
integer :: natoms_original,idummy,iidummy,bonds(3),center
integer :: i,j,k,l,nrow,ncol,n
integer :: first_zero !first zero position in red_int_matrix
logical :: no_regular !are there any regular (A-B-C-D type) dihedral
logical :: bool1, bool2
nrow = 5
ncol = ndihedral
first_zero = 1
no_regular = .True.
do i=1,natoms
do j=1,natoms
if(is_primary_bond(bond_matrix(i,j))) then
do k=1,natoms
if(is_primary_bond(bond_matrix(j,k)) .and. (k.ne.i)) then
do l=i,natoms
if(is_primary_bond(bond_matrix(k,l)) .and. &
( (l .ne. j) .and. (l .ne. i) ) ) then
bool1 = is_parallel(coords(1:3,i),&
coords(1:3,j),coords(1:3,k))
bool2 = is_parallel(coords(1:3,j),&
coords(1:3,k),coords(1:3,l))
if((.not. bool1) .and. (.not. bool2)) then ! none of the ends of the dihedral coincides with a line (0 or 180 degree bond angle)
red_int_matrix(1,first_zero) = 3 ! type: dihedral
red_int_matrix(2,first_zero) = i ! first atom
red_int_matrix(3,first_zero) = j ! second, vertex atom
red_int_matrix(4,first_zero) = k ! third atom
red_int_matrix(5,first_zero) = l ! fourth atom
first_zero = first_zero + 1
no_regular = .False. ! regular dihedral angles are present
endif
endif
enddo !l
endif
enddo !k
endif
enddo !j
enddo !i
! improper dihedral angles for dummy atoms
natoms_original = natoms - ndummy
do iidummy=1,ndummy
idummy=natoms_original + iidummy
n = dummy_bonds(iidummy)
i = rim_angles(2,n)
j = rim_angles(3,n) ! central atom
k = rim_angles(4,n)
! a plane is defined by the dummy atom, the central atom and one
! of the other atoms in the linear bond angle.
! The angle of this plane and the other half of the linear angle
! gives the improper dihedral angle
red_int_matrix(1,first_zero) = improper_code
red_int_matrix(2,first_zero) = idummy
red_int_matrix(3,first_zero) = j
red_int_matrix(4,first_zero) = i
red_int_matrix(5,first_zero) = k
first_zero=first_zero+1
no_regular = .false.
enddo
! improper dihedral angle for four atom molecules
if(natoms .ge. 4 .and. no_regular) then
! We need central atoms with 3 neighbours
do center = 1,natoms
n=0
bonds=0
do j=1,natoms
if(is_primary_bond(bond_matrix(j,center))) then
n=n+1
bonds(n)=j
if(n==3) exit
endif
enddo
if(n==3) then
! The central atom and two of its neighbours define a plane.
! The angle of the plane and the third bond gives the
! improper dihedral angle
red_int_matrix(1,first_zero) = improper_code
red_int_matrix(2,first_zero) = bonds(1)
red_int_matrix(3,first_zero) = center
red_int_matrix(4,first_zero) = bonds(2)
red_int_matrix(5,first_zero) = bonds(3)
first_zero=first_zero+1
endif
enddo
endif
return
end subroutine get_dihedrals
!************************************************************************
!************************************************************************
integer function nint_dihedrals(bond_matrix,coords,ndummy)
!************************************************************************
! number of internal coordinate dihedrals is returned
!************************************************************************
implicit none
integer :: ndummy
integer, dimension(:,:),intent(in) :: bond_matrix
double precision,dimension(3,size(bond_matrix,1)),intent(in):: coords
!local
integer :: i,j,k,l,natoms,n_linear,n,center
logical :: bool1, bool2
natoms = size(bond_matrix,1)
nint_dihedrals = 0
n_linear = 0
do i=1,natoms
do j=1,natoms
if(is_primary_bond(bond_matrix(i,j))) then
do k=1,natoms
if(is_primary_bond(bond_matrix(j,k)) .and. (k .ne. i) ) then
do l=i,natoms
if(is_primary_bond(bond_matrix(k,l)) .and. &
( (l .ne. j) .and. (l .ne. i) ) ) then
nint_dihedrals = nint_dihedrals + 1
bool1 = is_parallel(coords(1:3,i),&
coords(1:3,j),coords(1:3,k))
bool2 = is_parallel(coords(1:3,j),&
coords(1:3,k),coords(1:3,l))
if(bool1 .or. bool2) then ! at least one end of the dihedral coincides with a line (0 or 180 degree bond angle)
n_linear = n_linear + 1
nint_dihedrals = nint_dihedrals - 1
endif
endif
enddo !l
endif
enddo !k
endif
enddo !j
enddo !i
! improper dihedral angles
! First, we add one improper dihedral for each dummy atom
nint_dihedrals = nint_dihedrals+ndummy
! for four atom molecules we might need improper dihedrals too
if(natoms .ge. 4 .and. (nint_dihedrals .eq. 0) ) then
do center = 1,natoms
n=0
do j=1,natoms
if(is_primary_bond(bond_matrix(j,center))) n=n+1
enddo
if(n >= 3) nint_dihedrals=nint_dihedrals+1
enddo
endif
return
end function nint_dihedrals
!************************************************************************
!************************************************************************
logical function is_primary_bond(b)
!************************************************************************
! Determines if a bond is primary bond: regular bond, interfrag. bond
! or H-bond
!************************************************************************
implicit none
integer b
if(b==bond_code .or. b==fbond_code .or. b==hbond_code) then
is_primary_bond=.true.
else
is_primary_bond=.false.
endif
return
end function
!************************************************************************
subroutine get_intcoords(natoms,cartcoords,nintcoord,&
red_int_matrix,intcoords)
!************************************************************************
! it returns the values of the internal coordinates
! using the matrix of redundant internal coordinates and
! the Cartesian coordinates
! no constrains are handled at the moment (csonti)
!************************************************************************
implicit none
integer, intent(in) :: natoms
double precision, dimension(3,natoms), intent(in):: cartcoords
integer, intent(in) :: nintcoord
integer, dimension(:,:),intent(in) :: red_int_matrix
double precision, dimension(nintcoord) :: intcoords
!local
integer :: i,k,l,m,n,nrow,ncol,rim
nrow = size(red_int_matrix,1)
ncol = size(red_int_matrix,2)
do i=1,ncol
rim = red_int_matrix(1,i) ! type of the internal coordinate
if (rim .eq. 0) then ! last element
exit
elseif(rim==bond_code.or.rim==abond_code.or.rim==fbond_code &
.or.rim==hbond_code.or.rim==afbond_code) then ! bond
k = red_int_matrix(2,i)
l = red_int_matrix(3,i)
! write(*,*) 'distance',k,l,&
! distance2(cartcoords(1:3,k),cartcoords(1:3,l))
intcoords(i) = &
distance2(cartcoords(1:3,k),cartcoords(1:3,l))
elseif(rim .eq. 2) then ! angle
k = red_int_matrix(2,i)
l = red_int_matrix(3,i)
m = red_int_matrix(4,i)
! write(*,*) 'angle',k,l,m,&
! angle3(cartcoords(1:3,k),&
! cartcoords(1:3,l),&
! cartcoords(1:3,m))
intcoords(i) = &
angle3(cartcoords(1:3,k),&
cartcoords(1:3,l),&
cartcoords(1:3,m))
elseif(rim .eq. 3) then ! dihedral
k = red_int_matrix(2,i)
l = red_int_matrix(3,i)
m = red_int_matrix(4,i)
n = red_int_matrix(5,i)
! write(*,*) 'dihedral',k,l,m,n,&
! dihedral4(cartcoords(1:3,k),&
! cartcoords(1:3,l),&
! cartcoords(1:3,m),&
! cartcoords(1:3,n))
intcoords(i) = &
dihedral4(cartcoords(1:3,k),&
cartcoords(1:3,l),&
cartcoords(1:3,m),&
cartcoords(1:3,n))
elseif(rim == improper_code) then
k = red_int_matrix(2,i)
l = red_int_matrix(3,i)
m = red_int_matrix(4,i)
n = red_int_matrix(5,i)
intcoords(i)=improper(cartcoords(1,k),cartcoords(1,m),&
cartcoords(1,n),cartcoords(1,l))
endif
enddo
return
end subroutine get_intcoords
!************************************************************************
!************************************************************************
subroutine read_intcoords(nintcoord,intcoords)
!************************************************************************
! it reads values of the internal coordinates from a scrfile
!************************************************************************
implicit none
integer, intent(in) :: nintcoord
double precision, dimension(nintcoord), intent(out) :: intcoords
!local
integer :: i
open(icoordfile,file='COORD.int',&
status='old',form='unformatted')
do i=1,nintcoord
read(icoordfile) intcoords(i)
enddo
close(icoordfile)
end subroutine read_intcoords
!************************************************************************
!************************************************************************
subroutine wrt_intcoords(nintcoord,intcoords)
!************************************************************************
! it writes values of the internal coordinates into a scrfile
!************************************************************************
implicit none
integer, intent(in) :: nintcoord
double precision, dimension(nintcoord), intent(in) :: intcoords
!local
integer :: i
open(icoordfile,file='COORD.int',&
status='unknown',form='unformatted')
do i=1,nintcoord
write(icoordfile) intcoords(i)
enddo
close(icoordfile)
end subroutine wrt_intcoords
!************************************************************************
subroutine pretty_print_intcoords(csys)
!************************************************************************
!* Print internal coordinates to screen
!************************************************************************
implicit none
type(coord_sys_type), target :: csys
integer :: nintcoord
double precision, pointer :: intcoord(:)
double precision, allocatable, target :: intcoords_ric(:)
integer :: i,naux
logical :: lprint
nintcoord = csys%ncoord_ric
if(trim(csys%name) == 'ric') then
intcoord => csys%coord
else
allocate(intcoords_ric(csys%ncoord_ric))
call get_intcoords(csys%natoms,csys%cart_coord,&
csys%ncoord_ric,csys%rim,intcoords_ric)
intcoord => intcoords_ric
endif
! counting auxiliary bonds
naux=0
do i=1,nintcoord
if(csys%rim(1,i)==abond_code .or. csys%rim(1,i)==afbond_code)&
naux=naux+1
enddo
write(*,*)
write(*,'(A,I10)') ' Number of redundant internal coordinates: ',&
nintcoord
write(*,'(A,I10)') ' Number of auxiliary bonds (not printed) : ',&
naux
write(*,'(A,I10)') ' Number of frozen internal coordinates : ',&
csys%nfrozen
write(*,*)
! bond length
lprint=.false.
do i=1,nintcoord
if(csys%rim(1,i)==1) then
lprint=.true.
exit
endif
enddo
if(lprint) then
write(*,'(A)') ' Bond length: '
write(*,'(A)') ' Atom1 Atom2 Length'
do i=1,nintcoord
if(csys%rim(1,i)==1) then
write(*,'(I5,3X,I5,3X,f17.10)') &
csys%rim(2,i),csys%rim(3,i),intcoord(i)
endif
enddo
write(*,*)
endif
! bond angels
lprint=.false.
do i=1,nintcoord
if(csys%rim(1,i)==2) then
lprint=.true.
exit
endif
enddo
if(lprint) then
write(*,'(A)') ' Bond angles'
write(*,'(A)') ' Atom1 Atom2 Atom3 Angle'
do i=1,nintcoord
if(csys%rim(1,i)==2) then
write(*,'(I5,3X,I5,3X,I5,3X,f17.10)') &
csys%rim(2,i),csys%rim(3,i),csys%rim(4,i),intcoord(i)
endif
enddo
write(*,*)
endif
! dihedral angels
lprint=.false.
do i=1,nintcoord
if(csys%rim(1,i)==3) then
lprint=.true.
exit
endif
enddo
if(lprint) then
write(*,'(A)') ' Dihedral angles'
write(*,'(A)') ' Atom1 Atom2 Atom3 Atom4 Angle'
do i=1,nintcoord
if(csys%rim(1,i)==3) then
write(*,'(I5,3X,I5,3X,I5,3X,I5,3X,f17.10)') &
csys%rim(2,i),csys%rim(3,i),csys%rim(4,i),&
csys%rim(5,i),intcoord(i)
endif
enddo
write(*,*)
endif
! improper diedral angles
lprint=.false.
do i=1,nintcoord
if(csys%rim(1,i)==improper_code) then
lprint=.true.
exit
endif
enddo
if(lprint) then
write(*,'(A)') ' Improper dihedral angles'
write(*,'(A)') ' Atom1 Atom2 Atom3 Atom4 Angle'
do i=1,nintcoord
if(csys%rim(1,i)==improper_code) then
write(*,'(I5,3X,I5,3X,I5,3X,I5,3X,f17.10)') &
csys%rim(2,i),csys%rim(3,i),csys%rim(4,i),&
csys%rim(5,i),intcoord(i)
endif
enddo
write(*,*)
endif
! interfragment bonds
lprint=.false.
do i=1,nintcoord
if(csys%rim(1,i)==fbond_code) then
lprint=.true.
exit
endif
enddo
if(lprint) then
write(*,'(A)') ' Interfragment Bond length: '
write(*,'(A)') ' Atom1 Atom2 Length'
do i=1,nintcoord
if(csys%rim(1,i)==fbond_code) then
write(*,'(I5,3X,I5,3X,f17.10)') &
csys%rim(2,i),csys%rim(3,i),intcoord(i)
endif
enddo
write(*,*)
endif
if(allocated(intcoords_ric)) deallocate(intcoords_ric)
end subroutine
!************************************************************************
subroutine wrt_Pmat(nintcoord,Pmat)
!************************************************************************
! it writes the projection matrix into a scrfile
!************************************************************************
implicit none
integer, intent(in) :: nintcoord
double precision,dimension(nintcoord,nintcoord),intent(in) :: Pmat
!local
integer :: i,j
open(pmatfile,file='PMAT.int',&
status='unknown',form='unformatted')
do j=1,nintcoord
do i=1,nintcoord
write(pmatfile) Pmat(i,j)
enddo
enddo
close(pmatfile)
end subroutine wrt_Pmat
!************************************************************************
!************************************************************************
subroutine read_Pmat(nintcoord,Pmat)
!************************************************************************
! it reads the projection matrix from a scrfile
!************************************************************************
implicit none
integer, intent(in) :: nintcoord
double precision,dimension(nintcoord,nintcoord),intent(out):: Pmat
!local
integer :: i,j
open(pmatfile,file='PMAT.int',&
status='old',form='unformatted')
do j=1,nintcoord
do i=1,nintcoord
read(pmatfile) Pmat(i,j)
enddo
enddo
close(pmatfile)
end subroutine read_Pmat
!************************************************************************
!************************************************************************
subroutine step_transform(x0,Bmatrix,Bp,sq,csys)
!************************************************************************
! transformation of the projected internal coordinate step sq
! to Cartesian coordinates; JCP, 117, 9160 (2002), section 5
!
! x0 - initial Cartesian coordinate vector
! q0 - initial internal coordinate vector
! B - Wilson's B-matrix
! Bp - pseudo-inverse /B+/ of Wilson's B-matrix
! sq - internal coordinate step
!
!************************************************************************
implicit none
type(coord_sys_type), target :: csys
double precision :: Bp(csys%ncoord,3*csys%natoms)
double precision :: Bmatrix(csys%ncoord_ric,3*csys%natoms)
double precision, intent(in) :: sq(csys%ncoord)
double precision, intent(in) :: x0(3*csys%natoms)
!local
integer :: natoms,nintcoord,nintcoord_ric
integer :: k,lwork,nsol
double precision :: work1(1)
double precision :: dqk1_norm,dnrm2,step_norm,int_step_norm
double precision, parameter :: eps = 1.d-7 ! JCP, 117, 9160 (2002), section 5
double precision, allocatable :: xk1(:),x1(:),q0(:),Gmat(:,:)
double precision, allocatable :: solution(:),work(:)
double precision, allocatable, target :: qk_ric(:),qk_deloc(:)
double precision, pointer :: qk(:),xk(:,:)
logical :: conv
nintcoord = csys%ncoord
nintcoord_ric = csys%ncoord_ric
natoms = csys%natoms
nsol=max(nintcoord,3*natoms)
xk => csys%cart_coord
allocate(q0(nintcoord_ric))
allocate(qk_ric(nintcoord_ric))
if(trim(csys%name) == 'ric') then
qk => qk_ric
elseif(trim(csys%name) == 'deloc-ic') then
allocate(qk_deloc(nintcoord))
qk => qk_deloc
endif
allocate(xk1(3*natoms))
allocate(x1(3*natoms))
allocate(solution(nsol))
allocate(Gmat(nintcoord,nintcoord))
call get_intcoords(natoms,xk,nintcoord,csys%rim,q0)
conv= .False.
k = 0
! x1=x0+(B^+)sq
call dcopy(nintcoord,sq,1,solution,1)
call back_transform(natoms,csys,Bmatrix,Gmat,x1,solution,eps,&
work1,-1)
lwork=int(work1(1))
allocate(work(lwork))
call back_transform(natoms,csys,Bmatrix,Gmat,x1,solution,eps,&
work,lwork)
call daxpy(3*natoms,1.0d0,x0,1,x1,1)
call dcopy(3*natoms,x1,1,xk,1) ! initial estimate
do while ( (.not.conv) .and. k .le. 25)
k = k+1
call get_intcoords(natoms,xk,nintcoord_ric,csys%rim,qk_ric)
! dq = qk-q0
! We have to take the difference in redundant ic. (before
! delocalization) to be able to fix the angle periodicity
call daxpy(nintcoord_ric,-1.0d0,q0,1,qk_ric,1)
call fix_angles(qk_ric,csys%rim,nintcoord_ric)
if(trim(csys%name) == 'deloc-ic') then
call delocalize(nintcoord_ric,nintcoord,1,csys%Umatrix,&
qk_ric,qk_deloc)
endif
! dqk=sq-(qk-q0)=sq-dq
call dscal(nintcoord,-1.0d0,qk,1)
call daxpy(nintcoord,1.0d0,sq,1,qk,1)
if(trim(csys%name) == 'ric') then
call fix_angles(qk,csys%rim,nintcoord)
endif
if(k .eq. 1) then
dqk1_norm = vecnorm(qk,2)
else
if(vecnorm(qk,2) .gt. dqk1_norm) then
call dcopy(3*natoms,x1,1,xk,1)
exit
endif
endif
! xk1 = xk + (B^+)dqk
call dcopy(nintcoord,qk,1,solution,1)
call back_transform(natoms,csys,Bmatrix,Gmat,xk1,solution,eps,&
work,lwork)
conv = (dnrm2(3*natoms,xk1,1)/dsqrt(dble(3*natoms)) < eps)
call daxpy(3*natoms,1.0d0,xk,1,xk1,1)
! conv = conv .or. check_conv(xk1,xk,eps)
call dcopy(3*natoms,xk1,1,xk,1)
enddo
! if(conv) then
! write(*,'(A,I3,A)') 'Step transform converged in ', k, ' steps'
! else
! write(*,'(A)') 'Step transform failed to converge'
! write(*,'(A,I3)') 'Number of iteration steps: ', k
! endif
! x1=xk-csys%cart_coord
call dcopy(3*natoms,xk,1,x1,1)
call daxpy(3*natoms,-1.0d0,x0,1,x1,1)
! step_norm = dnrm2(3*natoms,x1,1)
! write(*,'(A,ES15.3)') 'Step norm in Cartesian coordinates: ',&
! step_norm
! int_step_norm = dnrm2(nintcoord,sq,1)
! write(*,'(A,ES15.3)') 'Step norm in internal coordinates: ',&
! int_step_norm
deallocate(q0)
deallocate(qk_ric)
if(allocated(qk_deloc)) deallocate(qk_deloc)
deallocate(xk1)
deallocate(x1)
deallocate(work)
deallocate(solution)
deallocate(Gmat)
end subroutine step_transform
!************************************************************************
subroutine back_transform(natoms,csys,Bmatrix,Gmat,x,s,delta,&
work,lwork)
!************************************************************************
! Calculates the x=B^T(BB^T)^(-1)*s product
! B: Wilson B matrix
! s: coordinates in internal system
!************************************************************************
implicit none
integer :: natoms,lwork
type(coord_sys_type) :: csys
double precision :: delta
double precision :: Bmatrix(csys%ncoord,3*natoms)
double precision :: Gmat(csys%ncoord,csys%ncoord)
double precision :: x(3,natoms)
double precision :: s(csys%ncoord)
double precision :: work(*)
integer :: rank,lwork2,i,nintcoord
if(lwork == -1) then
call dgelss(csys%ncoord,csys%ncoord,1,Bmatrix,csys%ncoord,s,&
csys%ncoord,work,delta,rank,work(1),-1,i)
work(1) = work(1) + dble(csys%ncoord)
work(1) = max(work(1),dble(csys%ncoord*3*natoms))
return
endif
nintcoord=size(csys%rim,2)
if(trim(csys%name) /= 'ric') then
call build_wilson_bmat(natoms,csys%cart_coord,csys%rim,work)
if(csys%nfrozen /= 0) call freezing_bmat_int(csys,work)
call delocalize(nintcoord,csys%ncoord,3*natoms,csys%Umatrix,&
work,Bmatrix)
else
call build_wilson_bmat(natoms,csys%cart_coord,csys%rim,Bmatrix)
if(csys%nfrozen /= 0) call freezing_bmat_int(csys,work)
endif
call dsyrk('u','n',csys%ncoord,3*natoms,1.0d0,Bmatrix,csys%ncoord,&
0.0d0,Gmat,csys%ncoord)
call filllo(Gmat,csys%ncoord)
lwork2=lwork-csys%ncoord
call dgelss(csys%ncoord,csys%ncoord,1,Gmat,csys%ncoord,s,&
csys%ncoord,work,delta,rank,work(1+csys%ncoord),lwork2,i)
call dgemv('t',csys%ncoord,3*natoms,1.0d0,Bmatrix,csys%ncoord,s,1,&
0.0d0,x,1)
end subroutine
!************************************************************************
subroutine transform_grad(natoms,grad_x,grad_int,csys)
!************************************************************************
! Transform Cartesian gradient to internal
!************************************************************************
implicit none
integer :: natoms
type(coord_sys_type) :: csys
double precision :: grad_x(3*natoms),grad_int(csys%ncoord)
!local
integer :: nintcoord,rank,lwork,info
double precision :: work1(1)
double precision, allocatable, target :: Bmatrix_ric(:,:)
double precision, allocatable, target :: Bmatrix_deloc(:,:)
double precision, contiguous, pointer :: Bmatrix(:,:)
double precision, allocatable :: Gmat(:),GB(:,:),eigval(:)
double precision, allocatable :: work(:)
lwork=0
nintcoord=size(csys%rim,2)
allocate(Bmatrix_ric(nintcoord,3*natoms))
if(trim(csys%name) /= 'ric') then
allocate(Bmatrix_deloc(csys%ncoord,3*natoms))
call build_wilson_bmat(natoms,csys%cart_coord,csys%rim,&
Bmatrix_ric)
! if(csys%nfrozen /= 0) call freezing_bmat_cart(csys,bmatrix_ric)
call delocalize(nintcoord,csys%ncoord,3*natoms,csys%Umatrix,&
Bmatrix_ric,Bmatrix_deloc)
Bmatrix => Bmatrix_deloc
else
Bmatrix => Bmatrix_ric
call build_wilson_bmat(natoms,csys%cart_coord,csys%rim,Bmatrix)
! if(csys%nfrozen /= 0) call freezing_bmat_cart(csys,bmatrix_ric)
endif
allocate(Gmat(max(3*natoms,csys%ncoord)**2))
allocate(eigval(max(3*natoms,csys%ncoord)))
! ! building projection to the internal coordinate space
allocate(GB(3*natoms,csys%ncoord))
! call dsyrk('u','t',3*natoms,csys%ncoord,1.0d0,Bmatrix,&
! csys%ncoord,0.0d0,Gmat,3*natoms)
! call filllo(Gmat,3*natoms)
!
! GB=transpose(Bmatrix)
! call dgelss(3*natoms,3*natoms,csys%ncoord,Gmat,3*natoms,GB,&
! 3*natoms,eigval,1.0d-7,rank,work1,-1,info)
! lwork=int(work1(1))
! allocate(work(lwork))
! call dgelss(3*natoms,3*natoms,csys%ncoord,Gmat,3*natoms,GB,&
! 3*natoms,eigval,1.0d-7,rank,work,lwork,info)
! call dgemm('n','n',csys%ncoord,csys%ncoord,3*natoms,1.0d0,&
! Bmatrix,csys%ncoord,GB,3*natoms,0.0d0,Gmat,csys%ncoord)
! call wrt_pmat(csys%ncoord,Gmat)
call build_projection(Bmatrix_ric,gmat,csys)
call wrt_pmat(csys%ncoord,Gmat)
! transforming gradient
call dgemv('n',csys%ncoord,3*natoms,1.0d0,Bmatrix,csys%ncoord,&
grad_x,1,0.0d0,grad_int,1)
call dsyrk('u','n',csys%ncoord,3*natoms,1.0d0,Bmatrix,&
csys%ncoord,0.0d0,Gmat,csys%ncoord)
call filllo(Gmat,csys%ncoord)
call dgelss(csys%ncoord,csys%ncoord,1,Gmat,csys%ncoord,&
grad_int,csys%ncoord,eigval,1.0d-7,rank,work1,-1,info)
if(lwork<int(work1(1))) then
if(allocated(work)) deallocate(work)
lwork=int(work1(1))
allocate(work(lwork))
endif
call dgelss(csys%ncoord,csys%ncoord,1,Gmat,csys%ncoord,&
grad_int,csys%ncoord,eigval,1.0d-7,rank,work,lwork,info)
deallocate(work)
deallocate(Gmat)
deallocate(GB)
deallocate(Bmatrix_ric)
deallocate(eigval)
if(allocated(Bmatrix_deloc)) deallocate(Bmatrix_deloc)
end subroutine
!************************************************************************
subroutine freezing_bmat_cart(csys,bmatrix)
!************************************************************************
! Calculates the B_proj=(1-(B^+)_constr*B_constr)*B matrix. It is
! required to freeze coordinates in cartesian->internal transformation.
! The B matrix is the Wilson B matrix in redundant IC, B_constr is the B
! matrix of the frozen coordinates.
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: bmatrix(csys%ncoord_ric,3*csys%natoms)
! local
integer :: i,ii,n,rank,lwork
double precision :: work1(1)
double precision, parameter :: rcond=1.0d-7
double precision, allocatable :: bmat_frozen(:,:),gmat(:,:)
double precision, allocatable :: work(:),s(:)
n=max(csys%ncoord_ric,3*csys%natoms)
allocate(bmat_frozen(csys%ncoord_ric,3*csys%natoms))
allocate(gmat(n,n))
allocate(s(n))
bmat_frozen=0.0d0
gmat=0.0d0
! Extract the B matrix of the frozen coordinates
do ii=1,csys%nfrozen
i=csys%frozen(ii)
bmat_frozen(i,:)=bmatrix(i,:)
gmat(i,:)=bmatrix(i,:)
enddo
! Calculate (B^+)_constr*B_constr
call dgelss(csys%ncoord_ric,3*csys%natoms,3*csys%natoms,&
bmat_frozen,csys%ncoord_ric,gmat,n,s,rcond,rank,work1,-1,i)
lwork = int(work1(1))
allocate(work(lwork))
call dgelss(csys%ncoord_ric,3*csys%natoms,3*csys%natoms,&
bmat_frozen,csys%ncoord_ric,gmat,n,s,rcond,rank,work,lwork,i)
! Calculate the projector 1-(B^+)_constr*B_constr
call dscal(n*n,-1.0d0,gmat,1)
do i=1,3*csys%natoms
gmat(i,i)=gmat(1,1)+1.0d0
enddo
! Get the B_proj matrix
call dgemm('n','n',csys%ncoord_ric,3*csys%natoms,3*csys%natoms,&
1.0d0,bmatrix,csys%ncoord_ric,gmat,n,0.0d0,bmat_frozen,&
csys%ncoord_ric)
call dcopy(3*csys%natoms*csys%ncoord_ric,bmat_frozen,1,bmatrix,1)
deallocate(bmat_frozen)
deallocate(gmat)
deallocate(s)
deallocate(work)
end subroutine
!************************************************************************
subroutine freezing_bmat_int(csys,bmatrix)
!************************************************************************
! B_proj=(B_constr*(B^+)_constr-1)*B=((B^T)^+_constr*B^T_constr-1)^T*B
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: bmatrix(csys%ncoord_ric,3*csys%natoms)
! local
integer :: i,ii,n,rank,lwork
double precision :: work1(1)
double precision, parameter :: rcond=1.0d-7
double precision, allocatable :: bmat_frozen(:,:),gmat(:,:)
double precision, allocatable :: work(:),s(:)
n=max(csys%ncoord_ric,3*csys%natoms)
allocate(bmat_frozen(3*csys%natoms,csys%ncoord_ric))
allocate(gmat(n,n))
allocate(s(n))
bmat_frozen=0.0d0
gmat=0.0d0
! Extract B^T of the frozen coordinates
do ii=1,csys%nfrozen
i=csys%frozen(ii)
bmat_frozen(:,i)=bmatrix(i,:)
gmat(:,i)=bmat_frozen(:,i)
enddo
!(B^T)^+_constr*B^T_constr
call dgelss(3*csys%natoms,csys%ncoord_ric,csys%ncoord_ric,&
bmat_frozen,3*csys%natoms,gmat,n,s,rcond,rank,work1,-1,i)
lwork = int(work1(1))
allocate(work(lwork))
call dgelss(3*csys%natoms,csys%ncoord_ric,csys%ncoord_ric,&
bmat_frozen,3*csys%natoms,gmat,n,s,rcond,rank,work,lwork,i)
!(B^T)^+_constr*B^T_constr-1
do i=1,csys%ncoord_ric
gmat(i,i)=gmat(1,1)-1.0d0
enddo
!((B^T)^+_constr*B^T_constr-1)^T*B
call dgemm('t','n',csys%ncoord_ric,3*csys%natoms,csys%ncoord_ric,&
1.0d0,gmat,n,bmatrix,csys%ncoord_ric,0.0d0,bmat_frozen,&
csys%ncoord_ric)
call dcopy(3*csys%natoms*csys%ncoord_ric,bmat_frozen,1,bmatrix,1)
deallocate(gmat)
deallocate(bmat_frozen)
deallocate(s)
deallocate(work)
end subroutine
!************************************************************************
subroutine fix_angles(q,red_int_matrix,nintcoord)
!************************************************************************
! it handles the periodicity of angles and dihedrals
!************************************************************************
implicit none
integer nintcoord,red_int_matrix(5,nintcoord)
double precision q(nintcoord)
integer i
do i=1,nintcoord
if((red_int_matrix(1,i) .eq. 2 .or. red_int_matrix(1,i) .eq. 3&
.or. red_int_matrix(1,i) .eq. 8).and. dabs(q(i)) .gt. PI) then
if(q(i) .gt. 0.d0) then
q(i) = q(i) - 2.0d0*PI
else
q(i) = q(i) + 2.0d0*PI
endif
endif
enddo
end subroutine
!************************************************************************
logical function check_conv(xk,x,eps)
!************************************************************************
! it returns .True. if xk is "close" to x;
! the rmsd of xk from x is less than eps
!************************************************************************
implicit none
double precision, dimension(:), intent(in) :: xk,x
double precision, intent(in) :: eps
!local
double precision :: s
integer :: i,ndim
check_conv = .False.
ndim=size(xk)
if(ndim .ne. size(x)) then
write(*,*) 'Error in check_conv!!!'
return
endif
s = 0.d0
do i=1,ndim
s = s + dabs( xk(i) - x(i) )**2
enddo
s = dsqrt(s/ndim)
if(s .le. eps) check_conv = .True.
return
end function check_conv
!************************************************************************
subroutine project_hessian(natoms,nintcoord,hessian,hessian_proj,&
csys)
!************************************************************************
! Projects Hessian matrix with P=BB^+ or with UP=UBB^+ in case of
! delocalized coordinates
!************************************************************************
implicit none
integer, intent(in) :: natoms,nintcoord
type(coord_sys_type), intent(in) :: csys
double precision, intent(in) :: hessian(nintcoord,nintcoord)
double precision :: hessian_proj(csys%ncoord,csys%ncoord)
! local
double precision, allocatable :: bmatrix_ric(:,:)
double precision, contiguous, pointer :: pmat(:,:)
double precision, allocatable, target :: pmat_ric(:,:)
double precision, allocatable, target :: pmat_deloc(:,:)
double precision, allocatable :: work(:,:)
allocate(Pmat_ric(nintcoord,nintcoord))
allocate(Bmatrix_ric(nintcoord,3*natoms))
call build_Wilson_Bmat(natoms,csys%cart_coord,csys%rim,&
Bmatrix_ric)
call build_projection_hessian(Bmatrix_ric,Pmat_ric,csys)
if(trim(csys%name) == 'ric') then
pmat => pmat_ric
elseif(trim(csys%name) == 'deloc-ic') then
allocate(pmat_deloc(csys%ncoord,nintcoord))
call delocalize(nintcoord,csys%ncoord,nintcoord,csys%Umatrix,&
pmat_ric,pmat_deloc)
pmat => pmat_deloc
endif
allocate(work(nintcoord,csys%ncoord))
call dgemm('n','t',nintcoord,csys%ncoord,nintcoord,1.0d0,&
hessian,nintcoord,Pmat,csys%ncoord,0.0d0,work,nintcoord)
call dgemm('n','n',csys%ncoord,csys%ncoord,nintcoord,1.0d0,&
Pmat,csys%ncoord,work,nintcoord,0.0d0,hessian_proj,&
csys%ncoord)
deallocate(work)
deallocate(Pmat_ric)
if(allocated(pmat_deloc)) deallocate(pmat_deloc)
end subroutine
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! STRING HANDLING
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
integer function integer_(str)
!************************************************************************
! it returns an integer from string (str)
!************************************************************************
implicit none
character(len=*), intent(in) :: str
!local
character(len=len(str)) :: stmp
integer :: code ! ASCII
integer :: i
stmp = trim(adjustl(str))
do i=1,len_trim(stmp)
code = ichar(stmp(i:i))
if(code .lt. 48 .or. code .gt. 57) then
write(*,*) 'Non numeric character in ', str,code
write(*,*) 'invalid conversion to integer'
call mrccend(1)
endif
enddo
read(str,*) integer_
end function integer_
!************************************************************************
!************************************************************************
function int2char(i)
!************************************************************************
! it converts an integer to a string
!************************************************************************
implicit none
integer,intent(in) :: i
character(len=512) :: int2char
!local
character(len=512) :: stmp
character(len=512) :: fmts
fmts = '(I0)'
write(stmp,fmts) i
int2char=trim(adjustl(stmp))
end function int2char
!************************************************************************
!************************************************************************
double precision function double_(str)
!************************************************************************
! it returns a double from string (str)
!************************************************************************
implicit none
character(len=*), intent(in) :: str
read(str,*) double_
end function double_
!************************************************************************
!************************************************************************
subroutine str_srch(str,substr,pos)
!************************************************************************
! it search for the position (pos) of the substring (substr) in the
! string (str)
! if the string contains the substring it returns the position of its
! first character, otherwise returns 0
!************************************************************************
implicit none
character(len=*),intent(in) :: str
character(len=*),intent(in) :: substr
integer,intent(out) :: pos
!local
integer :: lstr,lsub,i
logical :: bool
lstr = len(str)
lsub = len(substr)
if(lstr.lt.lsub) then
write(*,*) 'The substring is longer than the string!!!'
return
endif
bool=.True.
pos=0
i=1
do while (i .le. lstr-lsub+1 .and. bool)
if(str(i:i+lsub-1) .eq. substr(1:lsub)) then
pos=i
bool = .False.
endif
i=i+1
enddo
! strb=adjustl(trim(strb))
! stra=adjustl(trim(stra))
! write(*,*) 'Characters before ',ch,' ',strb
! write(*,*) 'Characters after ',ch,' ',stra
return
end subroutine str_srch
!************************************************************************
!************************************************************************
subroutine str_cut(str,ch,strb,stra)
!************************************************************************
! it cuts the string at first occurence of character ch
! then returns strb (part before character ch) and
! stra (part after character ch)
!************************************************************************
implicit none
character(len=*),intent(in) :: str
character(len=1),intent(in) :: ch
character(len=*),intent(out) :: strb,stra
!local
integer :: i,length
character(len=len(str)) :: stmp
length = len(stmp)
strb=''
stra=''
i=1
do while (i.le.length .and. str(i:i).ne.ch)
strb(i:i)=str(i:i)
i=i+1
enddo
stra(:)=str(i+1:)
! strb=adjustl(trim(strb))
! stra=adjustl(trim(stra))
! write(*,*) 'Characters before ',ch,' ',strb
! write(*,*) 'Characters after ',ch,' ',stra
return
end subroutine str_cut
!************************************************************************
!************************************************************************
subroutine str_del(str,pos,len)
!************************************************************************
! it deletes 'len' characters from position 'pos' from the string (str)
!************************************************************************
implicit none
character(len=*), intent(inout) :: str
integer, intent(in) :: pos,len
str(pos:) = str(pos+len:)
end subroutine str_del
!************************************************************************
!************************************************************************
subroutine str_dtest(str,double,bool,sm)
!************************************************************************
! it converts the string (str) to double (double)
! using the function double_(str)
! and also checks for special marks (bool) and returns them in sm
!************************************************************************
implicit none
character(len=*), intent(in) :: str
double precision, intent(out) :: double
logical, intent(inout) :: bool
character(len=2), intent(inout) :: sm
character(len=len(trim(adjustl(str)))) :: stmp
double = 0.d0
sm = ' ' ! special mark i.e., ++ or --; default return value is ' '
stmp = trim(adjustl(str))
if(index(stmp,'++').gt.0) then
bool = .True.
sm = '++'
call str_del(stmp,index(stmp,'++'),2)
double = double_(stmp)
else
if(index(stmp,'--').gt.0) then
bool = .False.
sm = '--'
call str_del(stmp,index(stmp,'--'),2)
double = double_(stmp)
else
double = double_(stmp)
endif
endif
end subroutine str_dtest
!************************************************************************
!************************************************************************
subroutine str_itest(str,int_,bool,sm)
!************************************************************************
! it converts the string (str) to integer (int_)
! using the function integer_(str)
! and also checks for special marks (bool) and returns them in sm
!************************************************************************
implicit none
character(len=*), intent(in) :: str
integer, intent(out) :: int_
logical, intent(inout) :: bool
character(len=2), intent(inout) :: sm
character(len=len(trim(adjustl(str)))) :: stmp
int_ = 0
sm = ' ' ! special mark i.e., ++ or --; default return value is ' '
stmp = trim(adjustl(str))
if(index(stmp,'++').gt.0) then
bool = .True.
sm = '++'
call str_del(stmp,index(stmp,'++'),2)
int_ = integer_(stmp)
else
if(index(stmp,'--').gt.0) then
bool = .False.
sm = '--'
call str_del(stmp,index(stmp,'--'),2)
int_ = integer_(stmp)
else
int_ = integer_(stmp)
endif
endif
end subroutine str_itest
!************************************************************************
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! PERIODIC TABLE RELATED
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
integer function get_atomic_number(atsymbol)
!************************************************************************
! Get atomic number from atomic symbol or from atomic number :)
! /if atomic number is given as a string(atsymbol) instead of an integer/
! this seemed practical during file processing
!************************************************************************
character(len=*), intent(in) :: atsymbol
!local
integer, parameter :: natmax = 118
character(len=3) :: chl3
character(len=2) :: atsym(0:natmax),chl2a,chl2b
integer :: length,atnum,code
logical :: bool
data atsym /'Bf',&
'H ','He','Li','Be','B ','C ','N ','O ','F ','Ne',&
'Na','Mg','Al','Si','P ','S ','Cl','Ar','K ','Ca',&
'Sc','Ti','V ','Cr','Mn','Fe','Co','Ni','Cu','Zn',&
'Ga','Ge','As','Se','Br','Kr','Rb','Sr','Y ','Zr',&
'Nb','Mo','Tc','Ru','Rh','Pd','Ag','Cd','In','Sn',&
'Sb','Te','I ','Xe','Cs','Ba','La','Ce','Pr','Nd',&
'Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb',&
'Lu','Hf','Ta','W ','Re','Os','Ir','Pt','Au','Hg',&
'Tl','Pb','Bi','Po','At','Rn','Fr','Ra','Ac','Th',&
'Pa','U ','Np','Pu','Am','Cm','Bk','Cf','Es','Fm',&
'Md','No','Lr','Rf','Db','Sg','Bh','Hs','Mt','Ds',&
'Rg','Cn','Nh','Fl','Mc','Lv','Ts','Og'/
!
length = len_trim(atsymbol)
if(length .gt. 3 .or. length .lt. 1) then
write(*,*) 'Unknown atomic symbol0 '//trim(atsymbol)//'!'
call mrccend(1)
endif
chl3 = trim(adjustl(atsymbol)) ! remove blanks
code = ichar(chl3(1:1))
if(code .gt. 47 .and. code .lt. 58) then ! atomic number in atsymbol
atnum = integer_(chl3)
if(atnum .gt. natmax .or. atnum .lt. 0) then
write(*,*) 'Unknown atomic symbol1 '//trim(atsymbol)//'!'
call mrccend(1)
else
get_atomic_number = atnum
endif
else ! atomic symbol
if(length .ne. 3) then
chl2a = trim(adjustl(atsymbol)) ! remove blanks
call lowercase(chl2a,chl2a,2)
bool = .False.
do i=1,natmax
call lowercase(atsym(i),chl2b,2)
if(chl2a .eq. chl2b) then
get_atomic_number = i
bool = .True.
endif
enddo
if(.not. bool) then
write(*,*) 'Unknown atomic symbol2 '//trim(atsymbol)//'!'
call mrccend(1)
endif
else
write(*,*) 'Unknown atomic symbol3 '//trim(atsymbol)//'!'
call mrccend(1)
endif
endif
return
end function get_atomic_number
!************************************************************************
!************************************************************************
double precision function get_cov_radius(atnum)
!************************************************************************
! it returns the covalent radius of an atom
! Values are extracted from Chemistry A European Journal, 15, 186 (2009)
!************************************************************************
implicit none
integer, intent(in) :: atnum
!local
integer, parameter :: maxnum = 118
double precision, dimension(maxnum) :: radii
integer :: i
data (radii(i), i = 1, maxnum)&
/&
32, 46, & ! H -> He
133, 102, 85, 75, 71, 63, 64, 67, & ! Li -> Ne
155, 139, 126, 116, 111, 103, 99, 96, & ! Na -> Ar
196, 171, & ! K -> Ca
148, 136, 134, 122, 119, 116, 111, 110, 112, 118, & ! Sc -> Zn Transition Metals
124, 121, 121, 116, 114, 117, & ! Ga -> Kr
210, 185, & ! Rb -> Sr
163, 154, 147, 138, 128, 125, 125, 120, 128, 136, & ! Y -> Cd Transition Metals
142, 140, 140, 136, 133, 131, & ! In -> Xe
232, 196, & ! Cs -> Ba
180,163,176,174,173,172,168,169,168,167,166,165,164,170,162, & ! La -> Lu Rare Earth Elements
152, 146, 137, 131, 129, 122, 123, 124, 133, & ! Hf -> Hg Transition Metals
144, 144, 151, 145, 147, 142, & ! Tl -> Rn!
223, 201, & ! Fr -> Ra
186,175,169,170,171,172,166,166,168,168,165,167,173,176,161, & ! Ac -> Lr Rare Earth Elements
157, 149, 143, 141, 134, 129, 128, 121, 122, & ! Rf -> Cn Transition Metals
136 ,143 ,162 ,175 ,165 ,157 & ! Nh -> Og [Pyykko & Atsumi;2009;10.1002/chem.200800987]
/
!
if (atnum .lt. 1 .or. atnum .gt. maxnum) then
write(*,*) 'Not valid atomic number: ', atnum
write(*,*) 'Allowed atomic numbers are between 1 and ', maxnum
else
get_cov_radius = radii(atnum)/100.d0 ! pm to Angstrom
get_cov_radius = get_cov_radius*ang2bohr ! Angstrom to Bohr
endif
end function get_cov_radius
!************************************************************************
!************************************************************************
double precision function get_atomic_mass(atnum)
!************************************************************************
! it returns the atomic mass (values were copied from integ.f; 2015-09-15)
!************************************************************************
implicit none
integer, intent(in) :: atnum
!local
integer, parameter :: maxnum = 118+1
double precision, dimension(0:maxnum-1) :: atmas
integer :: i
data (atmas(i), i=0, maxnum-1) &
/ &
0.d0,&
1.007825035d+00, 4.00260324d+00, 7.0160030d+00 , &
9.0121822d+00, 11.0093054d+00, 12.0000000d+00 , &
14.003074002d+00, 15.99491463d+00, 18.99840322d+00 , &
19.9924356d+00, 22.9897677d+00, 23.9850423d+00 , &
26.9815386d+00, 27.9769271d+00, 30.9737620d+00 , &
31.97207070d+00,34.968852721d+00, 39.9623837d+00 , &
38.9637074d+00, 39.9625906d+00, 44.9559100d+00 , &
47.9479473d+00, 50.9439617d+00, 51.9405098d+00 , &
54.9380471d+00, 55.9349393d+00, 58.9331976d+00 , &
57.9353462d+00, 62.9295989d+00, 63.9291448d+00 , &
68.925580d+00, 73.9211774d+00, 74.9215942d+00 , &
79.9165196d+00, 78.9183361d+00, 83.911507d+00 , &
84.911794d+00, 87.9056188d+00, 88.905849d+00 , &
89.9047026d+00, 92.9063772d+00, 97.9054073d+00 , &
97.907215d+00, 101.9043485d+00, 102.905500d+00 , &
105.903478d+00, 106.905092d+00, 113.903357d+00 , &
114.903882d+00, 119.9021991d+00, 120.9038212d+00, &
129.906229d+00, 126.904473d+00, 131.904144d+00 , &
132.905429d+00, 137.905232d+00, 138.906347d+00 , &
139.905433d+00, 140.907647d+00, 141.907719d+00 , &
144.912743d+00, 151.919728d+00, 152.921225d+00 , &
157.924019d+00, 158.925342d+00, 163.929171d+00 , &
164.930319d+00, 165.930290d+00, 168.934212d+00 , &
173.938859d+00, 174.940770d+00, 179.9465457d+00 , &
180.947462d+00, 183.950928d+00, 186.955744d+00 , &
191.961467d+00, 192.962917d+00, 194.964766d+00 , &
196.966543d+00, 201.970617d+00, 204.974401d+00 , &
207.976627d+00, 208.980374d+00, 208.982404d+00 , &
209.987126d+00, 222.017571d+00, 223.019733d+00 , &
226.025403d+00, 227.027750d+00, 232.038051d+00 , &
231.035880d+00, 238.050785d+00, 237.048168d+00 , &
244.064199d+00, 243.061373d+00, 247.070347d+00 , &
247.070300d+00, 251.079580d+00, 252.082944d+00 , &
257.095099d+00, 258.098570d+00, 259.100931d+00 , &
260.105320d+00, 261.108690d+00, 262.113760d+00 , &
263.118220d+00, 262.122930d+00, 269.134100d+00 , &
267.138000d+00, 268.143500d+00, 272.000000d+00 , &
277.000000d+00, 283.000000d+00, 289.000000d+00 , &
294.000000d+00, 300.000000d+00, 306.000000d+00 , &
310.000000d+00 /
if (atnum .lt. 0 .or. atnum .gt. maxnum-1) then
write(*,*) 'Not valid atomic number: ', atnum
write(*,*) 'Allowed atomic numbers are between 0 and ', maxnum-1
else
get_atomic_mass = atmas(atnum)
endif
end function get_atomic_mass
!************************************************************************
!************************************************************************
integer function nint_fragment(natoms,natomswdummy,atnum,coords,&
bond_matrix)
!************************************************************************
! Calculates number of interfragment bonds
!************************************************************************
implicit none
integer natoms,natomswdummy,atnum(natoms)
integer, allocatable :: fragment_code(:)
integer bond_matrix(natomswdummy,natomswdummy),nfrag,iatoms,jatoms
double precision coords(3,natoms)
allocate(fragment_code(natomswdummy))
call graph_coloring(natomswdummy,bond_matrix,fragment_code)
nfrag=maxval(fragment_code)
nint_fragment=nfrag*(nfrag-1)/2
call update_bond_matrix(natoms,natomswdummy,fragment_code,&
nint_fragment,coords,bond_matrix)
do iatoms=1,natoms
do jatoms=1,iatoms-1
if(bond_matrix(jatoms,iatoms)==afbond_code) &
nint_fragment=nint_fragment+1
enddo
enddo
deallocate(fragment_code)
end function
!************************************************************************
subroutine update_bond_matrix(natoms,natomswdummy,fragment_code,&
nint_fragment,coords,bond_matrix)
!************************************************************************
!* Calculates interfragment bonds
!************************************************************************
implicit none
integer natoms,natomswdummy
integer fragment_code(natomswdummy),nint_fragment
integer nfrag,bond_matrix(natomswdummy,natomswdummy)
integer i,j
double precision coords(3,natomswdummy),fdist
type(fragment_type), allocatable :: flist(:)
if(nint_fragment==0) return
nfrag=maxval(fragment_code)
allocate(flist(nfrag))
call build_fragments(flist,nfrag,fragment_code,natoms)
do i=1,nfrag
do j=i+1,nfrag
call fragment_bond(flist(i),flist(j),coords,bond_matrix,&
natoms,natomswdummy,fdist)
call aux_fragment_bond(flist(i),flist(j),coords,bond_matrix,&
natoms,natomswdummy,fdist)
enddo
enddo
do i=1,nfrag
deallocate(flist(i)%atom_list)
enddo
deallocate(flist)
end subroutine
!************************************************************************
subroutine build_fragments(flist,nfrag,fragment_code,natoms)
!************************************************************************
! Build fragments form fragment_code array
!************************************************************************
implicit none
integer natoms,nfrag,fragment_code(natoms),i,j,n
type(fragment_type) flist(nfrag)
do i=1,nfrag
n=0
do j=1,natoms
if(fragment_code(j)==i) n=n+1
enddo
flist(i)%natoms=n
allocate(flist(i)%atom_list(n))
n=0
do j=1,natoms
if(fragment_code(j)==i) then
n=n+1
flist(i)%atom_list(n)=j
endif
enddo
write(*,*) 'fragment',i
write(*,'(100I4)') flist(i)%atom_list
enddo
end subroutine
!************************************************************************
subroutine fragment_bond(f1,f2,coords,bond_matrix,natoms,&
natomswdummy,min_dist)
!************************************************************************
!* Calculates the interfragment bond between two fragments
!************************************************************************
implicit none
integer natoms,natomswdummy
integer bond_matrix(natomswdummy,natomswdummy)
integer atom1,atom2,i,j,iatoms,jatoms
double precision coords(3,natomswdummy),dist,min_dist
type(fragment_type) f1,f2
min_dist=1.0d32
do i=1,f1%natoms
iatoms=f1%atom_list(i)
if(iatoms > natoms) continue
do j=1,f2%natoms
jatoms=f2%atom_list(j)
if(jatoms > natoms) continue
dist=(coords(1,iatoms)-coords(1,jatoms))**2+&
(coords(2,iatoms)-coords(2,jatoms))**2+&
(coords(3,iatoms)-coords(3,jatoms))**2
dist=dsqrt(dist)
if(dist<min_dist) then
min_dist=dist
atom1=iatoms
atom2=jatoms
endif
enddo
enddo
bond_matrix(atom1,atom2)=fbond_code
bond_matrix(atom2,atom1)=fbond_code
end subroutine
!************************************************************************
subroutine aux_fragment_bond(f1,f2,coords,bond_matrix,natoms,&
natomswdummy,min_dist)
!************************************************************************
!* Calculates the auxiliary interfragment bonds between two fragments
!************************************************************************
implicit none
integer natoms,natomswdummy
integer bond_matrix(natomswdummy,natomswdummy)
integer i,j,iatoms,jatoms
double precision coords(3,natomswdummy),dist,min_dist,threshold
type(fragment_type) f1,f2
threshold=2.0d0*ang2bohr
do i=1,f1%natoms
iatoms=f1%atom_list(i)
if(iatoms > natoms) continue
do j=1,f2%natoms
jatoms=f2%atom_list(j)
if(jatoms > natoms) continue
dist=(coords(1,iatoms)-coords(1,jatoms))**2+&
(coords(2,iatoms)-coords(2,jatoms))**2+&
(coords(3,iatoms)-coords(3,jatoms))**2
dist=dsqrt(dist)
if((dist<1.3d0*min_dist.or.dist<threshold).and.&
bond_matrix(iatoms,jatoms)==0) then
bond_matrix(iatoms,jatoms)=afbond_code
bond_matrix(jatoms,iatoms)=afbond_code
endif
enddo
enddo
end subroutine
!************************************************************************
subroutine graph_coloring(natoms,bond_matrix,fragment_code)
!************************************************************************
! Colors each fragment in a graph
! natoms: number of nodes
! bond_matrix: adjacency matrix
! fragment_code: coloring
!************************************************************************
implicit none
integer natoms,bond_matrix(natoms,natoms),fragment_code(natoms)
integer nfragment,node,stack_size,i
integer, allocatable :: stack(:)
allocate(stack(natoms))
fragment_code=0
nfragment=1
stack(1)=1
stack_size=1
do while(stack_size/=0)
! Pop a node from stack and mark it as part of the current fragment
node=stack(stack_size)
fragment_code(node)=nfragment
stack_size=stack_size-1
! Check the neighbours of the current node
! if they have not been visited yet add them to the stack
do i=1,natoms
if(bond_matrix(i,node)==1.and.fragment_code(i)==0) then
stack_size=stack_size+1
stack(stack_size)=i
endif
enddo
! If the stack is empty check for unvisited nodes
if(stack_size==0) then
do i=1,natoms
if(fragment_code(i)==0) then
stack_size=1
stack(1)=i
nfragment=nfragment+1
exit
endif
enddo
endif
enddo
deallocate(stack)
end subroutine
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! I/O
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!************************************************************************
double precision function get_last_energy()
!************************************************************************
! it extracts the last energy from the file iface
!************************************************************************
implicit none
!local
character(len=512) :: line,stmp
character(len=8) :: cscr8
character(len=15) :: cscr15
integer :: check,i
check = 0
open(ifcfile,file='iface',status='old')
do while (check.eq.0)
read(ifcfile,200,iostat=check) line ! lines before keyword geom
stmp=adjustl(trim(line))
enddo
read(stmp,7596) cscr8,cscr15,i,i,i,get_last_energy
close(ifcfile)
200 format(a512)
7596 format(a8,1x,a15,1x,3(i2,1x),1pe23.15)
return
end function get_last_energy
!************************************************************************
!************************************************************************
subroutine print_vectorR(vec,cols)
!************************************************************************
! nice :) printout for real values
!************************************************************************
implicit none
double precision,dimension(:),intent(in) :: vec
integer,intent(in) :: cols ! # columns on the screen (<=99)
!local
integer :: i,j,k,m,n,n_columns
character(len=10) :: formats
character(len=2) :: snum
snum = int2char(cols)
n_columns = size(vec)
m = mod(n_columns,cols)
n = n_columns/cols
formats = '(A6,'//snum//'I12)'
if(n .gt. 0) then
do j=1,n
write(*,formats,advance='no') 'col-',(cols*(j-1)+i, i=1,cols)
write(*,*) ''
write(*,998,advance='no') '| '
do k=1,cols
write(*,999,advance='no') vec(cols*(j-1)+k)
enddo
write(*,*)
write(*,*)
enddo
! remainder
write(*,formats,advance='no') 'col-',(cols*n+i, i=1,m)
write(*,*) ''
write(*,998,advance='no') '| '
do k=1,m
write(*,999,advance='no') vec(cols*n+k)
enddo
write(*,*)
else
write(*,formats,advance='no') 'col-',(cols*n+i, i=1,m)
write(*,*) ''
write(*,998,advance='no') '| '
do k=1,m
write(*,999,advance='no') vec(k)
enddo
write(*,*)
endif
write(*,*) ''
998 format(A6)
999 format(f14.8)
end subroutine print_vectorR
!************************************************************************
!************************************************************************
subroutine print_matrixR(A,cols)
!************************************************************************
! nice :) printout for real values
!************************************************************************
implicit none
double precision,dimension(:,:),intent(in) :: A
integer,intent(in) :: cols ! # columns on the screen (<=99)
!local
integer :: i,j,k,m,n,n_rows,n_columns
character(len=10) :: formats
character(len=2) :: snum
snum = int2char(cols)
n_rows = size(A,1)
n_columns = size(A,2)
m = mod(n_columns,cols)
n = n_columns/cols
formats = '(A6,'//snum//'I12)'
if(n .gt. 0) then
do j=1,n
write(*,formats,advance='no') 'col-',(cols*(j-1)+i, i=1,cols)
write(*,*) ''
write(*,997) 'row|'
do i=1,n_rows
write(*,998,advance='no') i,'| '
do k=1,cols
write(*,999,advance='no') A(i,cols*(j-1)+k)
enddo
write(*,*)
enddo
write(*,*)
enddo
! remainder
write(*,formats,advance='no') 'col-',(cols*n+i, i=1,m)
write(*,*) ''
write(*,997) 'row|'
do i=1,n_rows
write(*,998,advance='no') i,'| '
do k=1,m
write(*,999,advance='no') A(i,cols*n+k)
enddo
write(*,*)
enddo
else
write(*,formats,advance='no') 'col-',(cols*n+i, i=1,m)
write(*,*) ''
write(*,997) 'row|'
do i=1,n_rows
write(*,998,advance='no') i,'| '
do k=1,m
write(*,999,advance='no') A(i,k)
enddo
write(*,*)
enddo
endif
write(*,*) ''
997 format(A6)
998 format(I4,A2)
999 format(f14.8)
!99 format(es13.5e2)
end subroutine print_matrixR
!************************************************************************
!************************************************************************
subroutine print_matrixI(A,cols)
!************************************************************************
! nice :) printout for integer values
!************************************************************************
implicit none
integer,dimension(:,:),intent(in) :: A
integer,intent(in) :: cols ! # columns on the screen (<=99)
!local
integer :: i,j,k,m,n,n_rows,n_columns
character(len=9) :: formats
character(len=2) :: snum
snum = int2char(cols)
n_rows = size(A,1)
n_columns = size(A,2)
m = mod(n_columns,cols)
n = n_columns/cols
formats = '(A6,'//snum//'I8)'
if(n .gt. 0) then
do j=1,n
write(*,formats,advance='no') 'col-',(cols*(j-1)+i, i=1,cols)
write(*,*) ''
write(*,997) 'row|'
do i=1,n_rows
write(*,998,advance='no') i,'| '
do k=1,cols
write(*,999,advance='no') A(i,cols*(j-1)+k)
enddo
write(*,*)
enddo
write(*,*)
enddo
! remainder
write(*,formats,advance='no') 'col-',(cols*n+i, i=1,m)
write(*,*) ''
write(*,997) 'row|'
do i=1,n_rows
write(*,998,advance='no') i,'| '
do k=1,m
write(*,999,advance='no') A(i,cols*n+k)
enddo
write(*,*)
enddo
else
write(*,formats,advance='no') 'col-',(cols*n+i, i=1,m)
write(*,*) ''
write(*,997) 'row|'
do i=1,n_rows
write(*,998,advance='no') i,'| '
do k=1,m
write(*,999,advance='no') A(i,k)
enddo
write(*,*)
enddo
endif
write(*,*) ''
997 format(A6)
998 format(I4,A2)
999 format(I8)
end subroutine print_matrixI
!************************************************************************
subroutine copy_coord_sys(src,dest)
!************************************************************************
! Copies coordinate system information from src to dest
!************************************************************************
implicit none
type(coord_sys_type) :: src,dest
integer :: nrow,ncol,nrow2,ncol2
if(src%ncoord /= dest%ncoord) then
if(allocated(dest%coord)) deallocate(dest%coord)
if(allocated(dest%Umatrix)) deallocate(dest%Umatrix)
if(allocated(dest%rim)) deallocate(dest%rim)
if(allocated(dest%cart_coord)) deallocate(dest%cart_coord)
if(allocated(dest%frozen)) deallocate(dest%frozen)
endif
dest%name=src%name
dest%nonred=src%nonred
dest%ncoord=src%ncoord
dest%ncoord_ric=src%ncoord_ric
dest%nfrozen=src%nfrozen
dest%natoms=src%natoms
dest%natoms_original=src%natoms_original
dest%natoms_extra=src%natoms_extra
if(allocated(src%coord)) then
nrow=size(src%coord)
if(allocated(dest%coord)) then
nrow2=size(dest%coord)
if(nrow/=nrow2) then
deallocate(dest%coord)
allocate(dest%coord(nrow))
endif
else
allocate(dest%coord(nrow))
endif
dest%coord=src%coord
endif
if(allocated(src%Umatrix)) then
nrow=size(src%Umatrix,1)
ncol=size(src%Umatrix,2)
if(allocated(dest%Umatrix)) then
nrow2=size(dest%Umatrix,1)
ncol2=size(dest%Umatrix,2)
if(nrow/=nrow2 .or. ncol/=ncol2) then
deallocate(dest%Umatrix)
allocate(dest%Umatrix(nrow,ncol))
endif
else
allocate(dest%Umatrix(nrow,ncol))
endif
dest%Umatrix=src%Umatrix
endif
if(allocated(src%rim)) then
nrow=size(src%rim,1)
ncol=size(src%rim,2)
if(allocated(dest%rim)) then
nrow2=size(dest%rim,1)
ncol2=size(dest%rim,2)
if(nrow/=nrow2 .or. ncol/=ncol2) then
deallocate(dest%rim)
allocate(dest%rim(nrow,ncol))
endif
else
allocate(dest%rim(nrow,ncol))
endif
dest%rim=src%rim
endif
if(allocated(src%cart_coord)) then
nrow=size(src%cart_coord,1)
ncol=size(src%cart_coord,2)
if(allocated(dest%cart_coord)) then
nrow2=size(dest%cart_coord,1)
ncol2=size(dest%cart_coord,2)
if(nrow/=nrow2 .or. ncol/=ncol2) then
deallocate(dest%cart_coord)
allocate(dest%cart_coord(nrow,ncol))
endif
else
allocate(dest%cart_coord(nrow,ncol))
endif
dest%cart_coord=src%cart_coord
endif
if(src%nfrozen /= 0) then
if(allocated(dest%frozen)) then
if(dest%nfrozen /= src%nfrozen) then
deallocate(dest%frozen)
allocate(dest%frozen(src%nfrozen))
endif
else
allocate(dest%frozen(src%nfrozen))
endif
if(allocated(dest%cvals)) then
if(dest%nfrozen /= src%nfrozen) then
deallocate(dest%cvals)
allocate(dest%cvals(src%nfrozen))
endif
else
allocate(dest%cvals(src%nfrozen))
endif
dest%frozen=src%frozen
dest%cvals=src%cvals
endif
end subroutine
!************************************************************************
subroutine dealloc_coord_sys(sys)
!************************************************************************
! Deallocate coordinate system memory
!************************************************************************
implicit none
type(coord_sys_type) sys
if(allocated(sys%coord)) deallocate(sys%coord)
if(allocated(sys%Umatrix)) deallocate(sys%Umatrix)
if(allocated(sys%rim)) deallocate(sys%rim)
if(allocated(sys%cart_coord)) deallocate(sys%cart_coord)
if(allocated(sys%frozen)) deallocate(sys%frozen)
if(allocated(sys%cvals)) deallocate(sys%cvals)
end subroutine
!************************************************************************
subroutine build_coordinate_system(natoms,cart_coord,csys,atnums)
!************************************************************************
! Builds the necessary coordinate system related quantities
!************************************************************************
implicit none
integer :: natoms,atnums(natoms)
double precision :: cart_coord(3,natoms)
type(coord_sys_type) :: csys
call dealloc_coord_sys(csys)
if(trim(csys%name) == 'cartesian') then
write(*,'(A)') 'Cartesian coordinates are not supported!!'
call mrccend(1)
elseif(trim(csys%name) == 'ric') then
call build_ric
elseif(trim(csys%name) == 'deloc-ic') then
call build_deloc_ic
endif
contains
!************************************************************************
subroutine copy_cart(natoms,csys,cart_coord)
implicit none
integer, intent(in) :: natoms
double precision, intent(in) :: cart_coord(3,natoms)
type(coord_sys_type), intent(inout) :: csys
allocate(csys%cart_coord(3,natoms))
call dcopy(3*natoms,cart_coord,1,csys%cart_coord,1)
end subroutine
!************************************************************************
subroutine build_ric
implicit none
integer :: natoms_new
double precision, allocatable :: cart_coord_new(:,:)
double precision, allocatable :: Gmat(:,:),Bmatrix_ric(:,:)
call build_red_int_matrix(natoms,atnums,cart_coord,csys%rim,&
natoms_new,cart_coord_new)
csys%natoms=natoms_new
csys%natoms_original=natoms
csys%natoms_extra=natoms_new-natoms
if(csys%natoms_extra == 0) then
call copy_cart(natoms,csys,cart_coord)
else
call copy_cart(natoms_new,csys,cart_coord_new)
endif
csys%ncoord=size(csys%rim,2)
csys%ncoord_ric=csys%ncoord
allocate(csys%coord(csys%ncoord))
call get_intcoords(csys%natoms,csys%cart_coord,csys%ncoord,csys%rim,&
csys%coord)
call freeze_coordinates(csys,csys%coord)
! We need the number of nonredundant coordinates (e.g. for rfo)
allocate(Bmatrix_ric(csys%ncoord,3*csys%natoms))
allocate(Gmat(csys%ncoord,csys%ncoord))
bmatrix_ric=0.0d0
! Build B matrix for RIC
call build_Wilson_Bmat(csys%natoms,csys%cart_coord,csys%rim,&
Bmatrix_ric)
! Calculate the active subspace of RIC
call active_subspace(csys%ncoord,csys%natoms,csys%nonred,&
Bmatrix_ric,Gmat)
allocate(csys%Umatrix(csys%ncoord_ric,csys%nonred))
if(allocated(cart_coord_new)) deallocate(cart_coord_new)
deallocate(Bmatrix_ric)
deallocate(Gmat)
end subroutine
!************************************************************************
subroutine build_deloc_ic
implicit none
integer :: natoms_new
integer :: nintcoord
double precision, allocatable :: intcoord(:)
double precision, allocatable :: cart_coord_new(:,:)
double precision, allocatable :: Gmat(:,:),Bmatrix_ric(:,:)
! build redundant internal coordinate matrix and get the
! Cartesian coordinates with the dummy atoms
call build_red_int_matrix(natoms,atnums,cart_coord,csys%rim,&
natoms_new,cart_coord_new)
csys%natoms=natoms_new
csys%natoms_original=natoms
csys%natoms_extra=natoms_new-natoms
if(csys%natoms_extra == 0) then
call copy_cart(natoms,csys,cart_coord)
else
call copy_cart(natoms_new,csys,cart_coord_new)
endif
! Building internal coordinate system
nintcoord=size(csys%rim,2)
csys%ncoord_ric=nintcoord
allocate(intcoord(nintcoord))
allocate(Bmatrix_ric(nintcoord,3*csys%natoms))
allocate(Gmat(nintcoord,nintcoord))
! generate redundant internal coordinates
call get_intcoords(csys%natoms,csys%cart_coord,nintcoord,&
csys%rim,intcoord)
call fix_angles(intcoord,csys%rim,nintcoord)
call freeze_coordinates(csys,intcoord)
! Build B matrix for RIC
call build_Wilson_Bmat(csys%natoms,csys%cart_coord,csys%rim,&
Bmatrix_ric)
! Calculate the active subspace of RIC
call active_subspace(nintcoord,csys%natoms,csys%ncoord,&
Bmatrix_ric,Gmat)
csys%nonred=csys%ncoord
allocate(csys%Umatrix(nintcoord,csys%ncoord))
allocate(csys%coord(csys%ncoord))
! Delocalize RIC
call get_deloc_matrix(nintcoord,csys%ncoord,Gmat,csys%Umatrix)
call delocalize(nintcoord,csys%ncoord,1,csys%Umatrix,intcoord,&
csys%coord)
if(allocated(cart_coord_new)) deallocate(cart_coord_new)
deallocate(intcoord)
deallocate(Bmatrix_ric)
deallocate(Gmat)
end subroutine
end subroutine build_coordinate_system
!************************************************************************
subroutine freeze_coordinates(csys,coord_ric)
!************************************************************************
! Freezing coordinates
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: coord_ric(*)
! local
integer :: nf,ifr,idummy,iidummy,i
ifr=0
nf=0
! for each dummy atom we freeze the bond length, bond angle and
! a dihedral angle
if(csys%natoms_extra /= 0) nf=nf+3*csys%natoms_extra
! freezing coordinates requested by the user
! ....
csys%nfrozen=nf
if(nf == 0) return
allocate(csys%frozen(nf))
allocate(csys%cvals(nf))
! freeze dummy atoms
if(csys%natoms_extra /= 0) then
do iidummy = 1,csys%natoms_extra
idummy=csys%natoms_original+iidummy
! check for the bond
do i=1,csys%ncoord_ric
if(csys%rim(1,i)==bond_code.and.&
(csys%rim(2,i)==idummy.or.csys%rim(3,i)==idummy)) then
ifr=ifr+1
csys%frozen(ifr)=i
csys%cvals(ifr)=coord_ric(i)
exit
endif
enddo
! check for bond angle
do i=1,csys%ncoord_ric
if(csys%rim(1,i)==angle_code.and.&
(csys%rim(2,i)==idummy.or.csys%rim(4,i)==idummy)) then
ifr=ifr+1
csys%frozen(ifr)=i
csys%cvals(ifr)=coord_ric(i)
exit
endif
enddo
! check for dihedral angle
do i=1,csys%ncoord_ric
if((csys%rim(1,i)==dangle_code.or.&
csys%rim(1,i)==improper_code).and.&
(csys%rim(2,i)==idummy.or.csys%rim(3,i)==idummy.or.&
csys%rim(4,i)==idummy.or.csys%rim(5,i)==idummy)) then
ifr=ifr+1
csys%frozen(ifr)=i
csys%cvals(ifr)=coord_ric(i)
exit
endif
enddo
enddo
endif
write(*,*) 'freezing coordinates'
write(*,'(10000I5)') csys%frozen
end subroutine
!************************************************************************
subroutine check_linear(natoms,cart_coord,n_angles,rim,&
bond_matrix,natoms_new,cart_coord_new,dummy_bonds,n_bad_angles)
!************************************************************************
! Check for linear angles and add dummy atoms if needed
! the linear angles are removed
! the angle 'i' is removed if rim_angles(1,i)==0
!************************************************************************
! cart_coord_new : the new set of Cartesian coordinates
! if there are no linear angles it is not allocated
!
! natoms_new : the number of atoms including dummy atoms
! natoms_new==natoms if no dummy atoms are required
!
! dummy_bonds : it stores which angle is corrected with a given
! dummy atom
! j=dummy_bonds(i) means that the ith dummy bond
! corrects the rim(:,j) angle, and the ith dummy atom
! is bonded to the central atom of rim(:,j)
!************************************************************************
implicit none
integer :: natoms,natoms_new,n_angles,n_bad_angles
integer :: rim(5,n_angles)
integer :: bond_matrix(natoms,natoms)
integer, allocatable :: dummy_bonds(:)
double precision :: cart_coord(3,natoms)
double precision, allocatable :: cart_coord_new(:,:)
! local
integer :: nn,ii,i,j,k,l,m,idummy
integer, allocatable :: bad_angles(:),dummy_atoms(:)
double precision :: angle,normal(3),vec(3)
logical :: lplanar
n_bad_angles=0
natoms_new=0
if(natoms < 3) then
natoms_new=natoms
return
endif
allocate(bad_angles(n_angles))
allocate(dummy_atoms(n_angles))
do i=1,n_angles
k = rim(2,i)
l = rim(3,i)
m = rim(4,i)
angle = angle3(cart_coord(1:3,k),&
cart_coord(1:3,l),&
cart_coord(1:3,m))
! The angle is problematic if angle>175deg or angle<5deg
if(angle>3.05432619099008 .or. angle<0.0872664625997165) then
! count how many atoms are connected to the central atom
n_bad_angles=n_bad_angles+1
nn=0
do j=1,natoms
if(bond_matrix(j,l) == 1) nn=nn+1
enddo
if(nn<4) then
natoms_new=natoms_new+1
bad_angles(n_bad_angles)=i
dummy_atoms(n_bad_angles)=1
elseif(nn>=4) then
lplanar = is_planar(natoms,l,bond_matrix(1,l),cart_coord)
if(lplanar) then
natoms_new=natoms_new+1
bad_angles(n_bad_angles)=i
dummy_atoms(n_bad_angles)=1
else
bad_angles(n_bad_angles)=i
! just remove the angle but do not add dummy atom
dummy_atoms(n_bad_angles)=0
endif
endif
endif
enddo
if(n_bad_angles>0) then
allocate(cart_coord_new(3,natoms+natoms_new))
allocate(dummy_bonds(natoms_new))
call dcopy(3*natoms,cart_coord,1,cart_coord_new,1)
idummy=0
do ii=1,n_bad_angles
i=bad_angles(ii)
rim(1,i) = 0
if(dummy_atoms(ii) == 0) continue
idummy=idummy+1
l = rim(2,i)
k = rim(3,i)
m = rim(4,i)
write(*,'(3I6)') l,k,m
vec=cart_coord(:,l)-cart_coord(:,k)
call get_normal_vector(vec, normal)
cart_coord_new(:,natoms+idummy)=normal+cart_coord(:,k)
dummy_bonds(idummy)=i
enddo
endif
natoms_new=natoms+natoms_new
deallocate(dummy_atoms)
deallocate(bad_angles)
end subroutine
!**********************************************************************
subroutine add_dummy_atoms(natoms,natomswdummy,ndummy,&
bond_matrix,bond_matrix_new,rim,rim_dummy,dummy_bonds)
!**********************************************************************
! Adds the dummy atoms required because of the linear bond angles.
! A new bond matrix is formed containinng the dummy atoms and a new
! redundant internal matrix is calculated which stores the bond angles
! of the dummy atoms.
!**********************************************************************
implicit none
integer :: ndummy,natoms,natomswdummy
integer :: rim(5,*),rim_dummy(5,2*ndummy)
integer :: dummy_bonds(ndummy)
integer :: bond_matrix(natoms,natoms)
integer :: bond_matrix_new(natomswdummy,natomswdummy)
!local
integer :: idummy,iangle,k,l,m
bond_matrix_new = 0
rim_dummy = 0
bond_matrix_new(1:natoms,1:natoms)=bond_matrix(:,:)
do idummy=1,ndummy
iangle = dummy_bonds(idummy)
l = rim(2,iangle)
k = rim(3,iangle) ! central atom
m = rim(4,iangle)
! Add the bond of the dummy atom and the central atom to the
! bond matrix
bond_matrix_new(idummy+natoms,k) = bond_code
bond_matrix_new(k,idummy+natoms) = bond_code
! Each dummy atom form two bond angles with the atoms of the
! linear bond: dummy-k-l; dummy-k-m
rim_dummy(1,2*(idummy-1)+1) = 2
rim_dummy(2,2*(idummy-1)+1) = natoms+idummy
rim_dummy(3,2*(idummy-1)+1) = k
rim_dummy(4,2*(idummy-1)+1) = l
rim_dummy(1,2*(idummy-1)+2) = 2
rim_dummy(2,2*(idummy-1)+2) = natoms+idummy
rim_dummy(3,2*(idummy-1)+2) = k
rim_dummy(4,2*(idummy-1)+2) = m
enddo
end subroutine
!**********************************************************************
logical function is_planar(natoms,atom,bonds,coords)
!**********************************************************************
! Checks if the atoms bonded to "atom" are in one plane
!**********************************************************************
implicit none
integer :: natoms,atom,bonds(natoms)
double precision :: coords(3,natoms)
!local
integer i,n
double precision :: normal(3),vec1(3),vec2(3)
double precision, parameter :: eps = 1.48352986419518 ! 85deg in rad
! first we need 3 non collinear points
! The first one is coords(:,atom)
n=0
i=1
do while(i<=natoms.and. n<2)
if(bonds(i) /= 0) then
if(n == 0) then
n=n+1
vec1=coords(:,i)
else
if(.not.is_parallel(coords(:,i),coords(:,atom),vec1)) then
n=n+1
vec2=coords(:,i)
endif
endif
endif
i=i+1
enddo
! if we could not find 3 points then it is a linear
if(n<2) then
is_planar=.true.
return
endif
! the three points define a plane, it normal vector is the cross
! product of the two (non parallel) vectors of the plane
vec1=vec1-coords(:,atom)
vec2=vec2-coords(:,atom)
call crproduct(vec1,vec2,normal)
normal=normal/vec_length(normal)
! check the dot product of the bond vectors and the normal vector
! of the plane
is_planar = .true.
do while(i<=natoms .and. is_planar)
if(bonds(i) /= 0) then
vec1=coords(:,i)-coords(:,atom)
vec1=vec1/vec_length(vec1)
if(dacos(vec_dot(normal,vec1)).lt.eps) is_planar=.false.
endif
i=i+1
enddo
end function
!************************************************************************
subroutine get_normal_vector(vec,normal)
!************************************************************************
! get the normal vector perpendicular to vec
! orthogonality: normal*vec=sum_i normal_i*vec_i=0
! if vec_1!=0, a possible solution:
! normal_2=normal_3=1, normal_1=-=(vec_2+vec_3)/vec_1
!************************************************************************
implicit none
integer :: i
double precision :: vec(3),normal(3)
double precision :: vec_norm,n_norm
double precision, parameter :: delta = 1.0d-10
vec_norm=max(dabs(vec(1)),dabs(vec(2)),dabs(vec(3)))
if(vec_norm < delta*delta) then
normal=1.0d0
return
endif
i=1
do while(dabs(vec(i)) < delta)
i=i+1
enddo
select case(i)
case(1)
normal(1)=-(vec(2)+vec(3))/vec(1)
normal(2)=1.0d0
normal(3)=1.0d0
case(2)
normal(1)=1.0d0
normal(2)=-(vec(1)+vec(3))/vec(2)
normal(3)=1.0d0
case(3)
normal(1)=1.0d0
normal(2)=1.0d0
normal(3)=-(vec(1)+vec(2))/vec(3)
end select
vec_norm=dsqrt(vec(1)*vec(1)+vec(2)*vec(2)+vec(3)*vec(3))
n_norm=dsqrt(normal(1)*normal(1)+&
normal(2)*normal(2)+&
normal(3)*normal(3))
normal=normal*vec_norm/n_norm
return
end subroutine
!************************************************************************
subroutine constrain_der(csys,der)
!************************************************************************
! Calculates of the derivatives of constraints wrt the coordinates
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: der(csys%ncoord,csys%nfrozen)
! local
integer :: i,j
if(trim(csys%name) == 'ric') then
der=0.0d0
do i=1,csys%nfrozen
j=csys%frozen(i)
der(j,i)=1.0d0
enddo
endif
end subroutine
!************************************************************************
subroutine get_multipliers(csys,der,grad,lambda)
!************************************************************************
! Gets the lagrange multipliers in the case of constrained optimization
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: der(csys%ncoord,csys%nfrozen)
double precision :: grad(csys%ncoord)
double precision :: lambda(csys%nfrozen)
! local
integer :: rank,lwork,info,nsol
double precision :: work1(1)
double precision, allocatable :: der2(:),solution(:)
double precision, allocatable :: s(:),work(:)
double precision, parameter :: rcond = 1.0d-7
info=0
nsol=max(csys%ncoord,csys%nfrozen)
allocate(der2(csys%ncoord*csys%nfrozen))
allocate(solution(nsol))
allocate(s(nsol))
call dcopy(csys%ncoord*csys%nfrozen,der,1,der2,1)
call dcopy(csys%ncoord,grad,1,solution,1)
call dgelss(csys%ncoord,csys%nfrozen,1,der2,csys%ncoord,solution,&
nsol,s,rcond,rank,work1,-1,info)
lwork=int(work1(1))
allocate(work(lwork))
call dgelss(csys%ncoord,csys%nfrozen,1,der2,csys%ncoord,solution,&
nsol,s,rcond,rank,work,lwork,info)
if(info /= 0) then
write(*,'(A)') ' Error in calcualtion of Lagrange multipliers'
write(*,'(A)') 'Error code: ',info
call mrccend(1)
endif
call dcopy(csys%nfrozen,solution,1,lambda,1)
! We solved der*lambda=grad but we have to solve der*lambda=-grad
call dscal(csys%nfrozen,-1.0d0,lambda,1)
deallocate(work)
deallocate(der2)
deallocate(solution)
deallocate(s)
end subroutine
!************************************************************************
subroutine get_constrain_term(csys,grad,constrain)
!************************************************************************
! Calculates the term sum_i lambda_i*dC_i/dq in the Lagrange eq.
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: constrain(csys%ncoord)
double precision :: grad(csys%ncoord)
! local
double precision, allocatable :: der(:,:)
double precision, allocatable :: lambda(:)
allocate(der(csys%ncoord,csys%nfrozen))
allocate(lambda(csys%nfrozen))
call constrain_der(csys,der)
call get_multipliers(csys,der,grad,lambda)
call dgemv('n',csys%ncoord,csys%nfrozen,1.0d0,der,csys%ncoord,&
lambda,1,0.0d0,constrain,1)
deallocate(der)
deallocate(lambda)
end subroutine
!************************************************************************
subroutine build_hessian(natoms,atnums,csys,hessian,hessian_type)
!************************************************************************
! Builds Hessian for geometry optimization
! TODO: LBFGS!!!!
!************************************************************************
implicit none
integer, intent(in) :: natoms,atnums(natoms)
type(coord_sys_type), intent(in) :: csys
double precision, intent(out) :: hessian(csys%ncoord,csys%ncoord)
character*16, intent(in) :: hessian_type
! local
integer :: nintcoord
double precision, allocatable :: hessian_ric(:,:)
if(trim(csys%name) == 'cartesian') then
call mrccend(1)
elseif(trim(csys%name) == 'ric' .or.&
trim(csys%name) == 'deloc-ic') then
nintcoord=csys%ncoord_ric
allocate(hessian_ric(nintcoord,nintcoord))
call build_hessian_ric(natoms,nintcoord,atnums,hessian_ric,&
hessian_type,csys)
! call dummy_hessian(csys,hessian_ric)
call project_hessian(csys%natoms,nintcoord,hessian_ric,hessian,&
csys)
deallocate(hessian_ric)
endif
contains
subroutine build_hessian_ric(natoms,nintcoord,atnums,hessian,&
hessian_type,csys)
implicit none
type(coord_sys_type) :: csys
integer, intent(in) :: nintcoord,natoms,atnums(natoms)
double precision, intent(out) :: hessian(nintcoord,nintcoord)
character*16, intent(in) :: hessian_type
if(trim(hessian_type) == 'lindh') then
call build_lindh_hessian(hessian,csys%rim,csys%cart_coord,&
atnums)
elseif(trim(hessian_type) == 'schlegel') then
call build_schlegel_hessian(hessian,csys%rim,csys%cart_coord,&
atnums)
elseif(trim(hessian_type) == 'unit') then
call build_unit_hessian(hessian)
elseif(trim(hessian_type) == 'diag') then
call build_initial_hessian(hessian,csys%rim,csys%cart_coord)
endif
end subroutine
subroutine build_deloc_ic_hessian
implicit none
integer :: nintcoord
double precision, allocatable :: hessian_ric(:,:),work(:,:)
nintcoord=size(csys%rim,2)
allocate(hessian_ric(nintcoord,nintcoord))
call build_hessian_ric(natoms,nintcoord,atnums,hessian_ric,&
hessian_type,csys)
allocate(work(nintcoord,csys%ncoord))
call dgemm('t','n',csys%ncoord,nintcoord,nintcoord,1.0d0,&
csys%Umatrix,nintcoord,hessian_ric,nintcoord,0.0d0,work,&
csys%ncoord)
call dgemm('n','n',csys%ncoord,csys%ncoord,nintcoord,1.0d0,&
work,csys%ncoord,csys%Umatrix,nintcoord,0.0d0,hessian,&
csys%ncoord)
deallocate(hessian_ric)
deallocate(work)
end subroutine
end subroutine
!************************************************************************
subroutine dummy_hessian(csys,hessian_ric)
!************************************************************************
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision :: hessian_ric(csys%ncoord_ric,csys%ncoord_ric)
! local
integer :: natoms,i,ii
natoms=csys%natoms_original
do ii=1,csys%nfrozen
i=csys%frozen(ii)
hessian_ric(i,i)=0.0d0
enddo
end subroutine
!************************************************************************
subroutine project_coordinates(s,csys)
!************************************************************************
! projects coordinates with P=BB^+
!************************************************************************
implicit none
type(coord_sys_type), intent(in) :: csys
double precision :: s(csys%ncoord)
! local
integer :: nintcoord,natoms
double precision, contiguous, pointer :: p(:,:)
double precision, allocatable :: bmatrix_ric(:,:)
double precision, allocatable, target :: p_ric(:,:),p_deloc(:,:)
double precision, allocatable :: temp(:)
natoms=csys%natoms
nintcoord=csys%ncoord_ric
allocate(bmatrix_ric(nintcoord,3*natoms))
allocate(p_ric(nintcoord,nintcoord))
call build_wilson_bmat(natoms,csys%cart_coord,csys%rim,&
Bmatrix_ric)
call build_projection(bmatrix_ric,p_ric,csys)
if(trim(csys%name) == 'deloc-ic') then
call delocalize(nintcoord,csys%ncoord,nintcoord,csys%Umatrix,&
p_ric,p_deloc)
p => p_deloc
elseif(trim(csys%name) == 'ric') then
p => p_ric
endif
allocate(temp(csys%ncoord))
call dgemv('n',csys%ncoord,csys%ncoord,1.0d0,p,csys%ncoord,s,1,&
0.0d0,temp,1)
call dcopy(csys%ncoord,temp,1,s,1)
deallocate(bmatrix_ric)
deallocate(p_ric)
if(allocated(p_deloc)) deallocate(p_deloc)
deallocate(temp)
end subroutine
!************************************************************************
subroutine update_coordinates(sk,csys)
!************************************************************************
! updates coordinates
!************************************************************************
implicit none
type(coord_sys_type) :: csys
double precision, intent(inout) :: sk(csys%ncoord)
integer :: nintcoord,i,ii
double precision, allocatable :: x0(:)
double precision, allocatable :: Bmatrix(:,:),Bp(:,:)
double precision, allocatable :: sk_frozen(:)
nintcoord = csys%ncoord_ric
call project_coordinates(sk,csys)
call daxpy(csys%ncoord,1.0d0,sk,1,csys%coord,1)
if(trim(csys%name) == 'ric') then
call fix_angles(csys%coord,csys%rim,csys%ncoord)
endif
allocate(x0(3*csys%natoms))
allocate(Bmatrix(nintcoord,3*csys%natoms))
allocate(Bp(3*csys%natoms,nintcoord))
call dcopy(3*csys%natoms,csys%cart_coord,1,x0,1)
call step_transform(x0,Bmatrix,Bp,sk,csys)
if(csys%nfrozen /= 0) then
write(*,'(A)') 'Correcting frozen coordinates'
allocate(sk_frozen(csys%ncoord))
sk_frozen=0.0d0
do ii=1,csys%nfrozen
i=csys%frozen(ii)
sk_frozen(i) = csys%cvals(ii) - csys%coord(i)
enddo
call fix_angles(sk_frozen,csys%rim,csys%ncoord)
call daxpy(csys%ncoord,1.0d0,sk_frozen,1,csys%coord,1)
call dcopy(3*csys%natoms,csys%cart_coord,1,x0,1)
call step_transform(x0,Bmatrix,Bp,sk_frozen,csys)
deallocate(sk_frozen)
endif
call get_intcoords(csys%natoms,csys%cart_coord,csys%ncoord_ric,&
csys%rim,csys%coord)
deallocate(x0)
deallocate(Bmatrix)
deallocate(Bp)
end subroutine
!************************************************************************
logical function gopt_conv(gmax,ared,smax,gtol,ftol,xtol,lbaker)
!************************************************************************
! check convergence of geometry optimization
!************************************************************************
implicit none
double precision gmax,ared,smax
double precision gtol,ftol,xtol
logical lbaker
gopt_conv=.false.
if(lBaker) then !Baker conv criteria; JCC, 14, 1085 (1993)
if( gmax <= 3.d-4 .and. (dabs(ared)<=1.d-6 .or. smax<=3.d-4) ) then
gopt_conv = .true.
endif
else
if( gmax <= gtol .and. (dabs(ared)<=ftol .or. smax <= xtol) ) then
gopt_conv = .true.
endif
endif
return
end function
!************************************************************************
end module optim
!************************************************************************