c
c
c     ###################################################
c     ##  COPYRIGHT (C)  1990  by  Jay William Ponder  ##
c     ##              All Rights Reserved              ##
c     ###################################################
c
c     #################################################################
c     ##                                                             ##
c     ##  program optimize  --  variable metric Cartesian optimizer  ##
c     ##                                                             ##
c     #################################################################
c
c
c     "optimize" performs energy minimization in Cartesian coordinate
c     space using an optimally conditioned variable metric method
c
c
      program optimize
      implicit none
      include 'sizes.i'
      include 'atoms.i'
      include 'files.i'
      include 'iounit.i'
      include 'scales.i'
      include 'usage.i'
      integer i,j,imin,nvar,freeunit
      real*8 optimiz1,minimum,grdmin,gnorm,grms
      real*8 xx(maxopt),derivs(3,maxatm)
      character*60 minfile
      character*80 string
      logical exist
      external optimiz1,writeout
c
c
c     set up the structure and mechanics calculation
c
      call initial
      call getxyz
      call mechanic
c
c     get termination criterion as RMS gradient per atom
c
      grdmin = -1.0d0
      call nextarg (string,exist)
      if (exist)  read (string,*,err=10,end=10)  grdmin
   10 continue
      if (grdmin .le. 0.0d0) then
         write (iout,20)
   20    format (/,' Enter RMS Gradient per Atom Criterion',
     &              ' [0.01] :  ',$)
         read (input,30)  grdmin
   30    format (f20.0)
      end if
      if (grdmin .le. 0.0d0)  grdmin = 0.01d0
c
c     write out a copy of coordinates for later update
c
      imin = freeunit ()
      minfile = filename(1:leng)//'.xyz'
      call version (minfile,'new')
      open (unit=imin,file=minfile,status='new')
      call prtxyz (imin)
      close (unit=imin)
      outfile = minfile
c
c     set scaling parameter for function and derivative values;
c     use square root of median eigenvalue of typical Hessian
c
      scale = 12.0d0
c
c     scale the coordinates of each active atom
c
      nvar = 0
      do i = 1, n
         if (use(i)) then
            nvar = nvar + 1
            xx(nvar) = x(i) * scale
            nvar = nvar + 1
            xx(nvar) = y(i) * scale
            nvar = nvar + 1
            xx(nvar) = z(i) * scale
         end if
      end do
c
c     make the call to the optimization routine
c
      call vmetric (nvar,xx,minimum,grdmin,optimiz1,writeout)
c
c     unscale the final coordinates for active atoms
c
      nvar = 0
      do i = 1, n
         if (use(i)) then
            nvar = nvar + 1
            x(i) = xx(nvar) / scale
            nvar = nvar + 1
            y(i) = xx(nvar) / scale
            nvar = nvar + 1
            z(i) = xx(nvar) / scale
         end if
      end do
c
c     write out final function value and gradient
c
      call gradient (minimum,derivs)
      gnorm = 0.0d0
      do i = 1, n
         if (use(i)) then
            do j = 1, 3
               gnorm = gnorm + derivs(j,i)**2
            end do
         end if
      end do
      gnorm = sqrt(gnorm)
      grms = gnorm / sqrt(dble(nvar/3))
      if (grms .gt. 0.0001d0) then
         write (iout,40)  minimum,grms,gnorm
   40    format (/,' Final Function Value :',f15.4,
     &           /,' Final RMS Gradient :  ',f15.4,
     &           /,' Final Gradient Norm : ',f15.4)
      else
         write (iout,50)  minimum,grms,gnorm
   50    format (/,' Final Function Value :',f15.4,
     &           /,' Final RMS Gradient :  ',d15.4,
     &           /,' Final Gradient Norm : ',d15.4)
      end if
c
c     write the final coordinates into a file
c
      imin = freeunit ()
      open (unit=imin,file=minfile,status='old')
      rewind (unit=imin)
      call prtxyz (imin)
      close (unit=imin)
      end
c
c
c     ################################################################
c     ##                                                            ##
c     ##  function optimiz1  --  function & gradient for optimizer  ##
c     ##                                                            ##
c     ################################################################
c
c
c     "optimiz1" is a service routine that computes the energy
c     and gradient for optimally conditioned variable metric
c     optimization in Cartesian coordinate space
c
c
      function optimiz1 (xx,g)
      implicit none
      include 'sizes.i'
      include 'atoms.i'
      include 'scales.i'
      include 'usage.i'
      integer i,nvar
      real*8 optimiz1,e,derivs(3,maxatm)
      real*8 xx(maxopt),g(maxopt)
c
c
c     translate optimization parameters to atomic coordinates
c
      nvar = 0
      do i = 1, n
         if (use(i)) then
            nvar = nvar + 1
            x(i) = xx(nvar) / scale
            nvar = nvar + 1
            y(i) = xx(nvar) / scale
            nvar = nvar + 1
            z(i) = xx(nvar) / scale
         end if
      end do
c
c     compute and store the energy and gradient values
c
      call gradient (e,derivs)
      optimiz1 = e
c
c     store atom gradients as optimization gradient, also
c     translate the coordinates of each active atom; the
c     latter may be needed when using periodic boundaries
c
      nvar = 0
      do i = 1, n
         if (use(i)) then
            nvar = nvar + 1
            xx(nvar) = x(i) * scale
            g(nvar) = derivs(1,i) / scale
            nvar = nvar + 1
            xx(nvar) = y(i) * scale
            g(nvar) = derivs(2,i) / scale
            nvar = nvar + 1
            xx(nvar) = z(i) * scale
            g(nvar) = derivs(3,i) / scale
         end if
      end do
      return
      end
