!-------------------------------------- LICENCE BEGIN ------------------------------------
!Environment Canada - Atmospheric Science and Technology License/Disclaimer,
!                     version 3; Last Modified: May 7, 2008.
!This is free but copyrighted software; you can use/redistribute/modify it under the terms
!of the Environment Canada - Atmospheric Science and Technology License/Disclaimer
!version 3 or (at your option) any later version that should be found at:
!http://collaboration.cmc.ec.gc.ca/science/rpn.comm/license.html
!
!This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
!without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
!See the above mentioned License/Disclaimer for more details.
!You should have received a copy of the License/Disclaimer along with this software;
!if not, you can write to: EC-RPN COMM Group, 2121 TransCanada, suite 500, Dorval (Quebec),
!CANADA, H9P 1J3; or send e-mail to service.rpn@ec.gc.ca
!-------------------------------------- LICENCE END --------------------------------------
!

      SUBROUTINE CONJGRAD(KN,PB,PX,PWORK,KDW,KITER,PRESID 2,5
     S     ,MATVEC,KINFO,LDPRINT,kelem,cdfam)
#if defined (DOC)
*
***s/r CONJGRAD  - Solution of a linear system AX - B = 0 with a conjugate
*     .            gradient method (A is assumed to be a symmetric positive
*     .            definite matrix)
*
*     . Reference : Gollub, G.H. and C.F. van Loan, 1983
*     .             Matrix Computations. John Hopkins University Press
*     .             The algorithm used here corresponds to eq.(10.3-3)
*     .             in this reference
*
*Author  : P. Gauthier *ARMA/AES  January 23, 1996
*Revision:
*          S. Pellerin *ARMA/AES Aug. 98.
*                   - Built-up of O matrix based on assimilated elements
*                     and observation family instead of NPOS
*                     (agument to matvec call).
*
*           JM Belanger CMDA/SMC  Jan 2001 
*                   . 32 bits conversion 
*                     (Replace BLAS single precision by double)
*
*Arguments
*     .  KN           :  order of the system
*     .  PB(KN)       :  right-hand-side of the system
*     .  PX           :  initial first estimate of the solution
*     .  PWORK(KN,4)  :  workspace needed by the algorithm (4*KN)
*     .  KDW          :  unused parameter introduced to make the interface identical
*     .                  to that of CG of the Templates library
*     .  KITER        :  -INPUT-   maximum number of iterations allowed
*     .                  -OUTPUT-  actual number of iterations required to achieve convergence
*     .  PRESID       :  accuracy criterion ||AX -B||/||B|| < PRESID
*     .  MATVEC  (external subroutine)
*          The user must provide a subroutine to perform the
*          matrix-vector product
*
*               y := alpha*A*x + beta*y,
*
*          where alpha and beta are scalars, x and y are vectors,
*          and A is a matrix. Vector x must remain unchanged.
*          The solution is over-written on vector y.
*
*          The call is:
*
*             CALL MATVEC( ALPHA, X, BETA, Y )
*     .  kelem  : variable (observation) code (BUFR)
*     .  cdfam  : family of the observation
*
*     .  PSOLVE  (external subroutine) - THIS OPTION IS NOT USED FOR NOW -
*          The user must provide a subroutine to perform the
*          preconditioner solve routine for the linear system
*
*               M*x = b,
*
*          where x and b are vectors, and M a matrix. Vector b must
*          remain unchanged.
*          The solution is over-written on vector x.
*
*          The call is:
*
*             CALL PSOLVE( X, B )
*
*     The call sequence for the subroutine should then include psolve
*       ex:
*
*     SUBROUTINE CONJGRAD(KN,PB,PX,PWORK,KDW,KITER,PRESID
*    S     ,MATVEC,PSOLVE,KINFO,LDPRINT)
*     EXTERNAL MATVEC, DDOT, DAXPY,DCOPY, PSOLVE, DOTOBS, DNRM2
*
*          The preconditioner is passed into the routine in a common block.
*
*          The matrix is passed into the routine in a common block.
*     .  KINFO  :   -OUTPUT- status parameter
*     .  LDPRINT:   -INPUT - Switch to control printing
#endif
      IMPLICIT NONE
#include "comlun.cdk"
*
*     Global Variables
*
*     Arguments
*
      INTEGER KN, KINFO, KITER, KDW, kelem
      REAL*8    PX(KN), PB(KN), PWORK(KN,5), PRESID
      LOGICAL LDPRINT
      character*2 cdfam
*
*     Local variables
*
      INTEGER JITER, ITERMAX,IZ, IR, IP, IQ, IX, JOUT, ITERTEMP
      INTEGER IKOUNT,jj
      REAL*8 DDOT,ZRHOM1, ZRHOM2, ZBETAM1,ZALPHA, ZERR0, ZERRABS
     S     , ZERRREL,ZONE,ZMONE, beta, DNRM2
      EXTERNAL MATVEC, DDOT, DAXPY, DOTOBS, DNRM2
*
*     1.  Initial set-ups
*
 100  CONTINUE
C
 9000 FORMAT(//,6X,' CONJGRAD- solving with a conjugate gradient '
     S     ,'algorithm',//)
      IF ( LDPRINT) THEN
         WRITE(NULOUT,FMT=9000)
      ENDIF
C
      ITERMAX = KITER
      IZ = 1
      IR = 2
      IP = 3
      IQ = 4
      IX = 5
*
      KINFO = 1
      ZONE  =  1.0
      ZMONE = -1.0
*
*     .  1.1 Compute the initial gradient
*
      CALL DCOPY(KN,PX(1),1,PWORK(1,IX),1)
      CALL DCOPY(KN,PB(1),1,PWORK(1,IR),1)

      beta=1.0D0
      CALL MATVEC(ZMONE,PWORK(1,IX),beta,PWORK(1,IR),kelem,cdfam)
      ZERR0 =  DNRM2(KN,PB(1),1)

*
*     2.  Main iterative loop
*
 200  CONTINUE
      IKOUNT=0
      DO JOUT = 1, KITER, KN
         ITERTEMP = MIN(KN,KITER -JOUT + 1)
         DO JITER = 1, ITERTEMP
*
*     .    This assumes that the identity is a good preconditioner
*
            CALL DCOPY(KN,PWORK(1,IR),1,PWORK(1,IZ),1)
*
            CALL DOTOBS(KN,PWORK(1,IR),PWORK(1,IZ),ZRHOM1)

C     ZRHOM1 = DDOT(KN,PWORK(1,IR),1,PWORK(1,IZ),1)
            IF (JITER.EQ.1) THEN
               CALL DCOPY(KN,PWORK(1,IZ),1,PWORK(1,IP),1)
            ELSE
               ZBETAM1 = ZRHOM1/ZRHOM2
               CALL DCOPY(KN,PWORK(1,IZ),1,PWORK(1,IQ),1)
               CALL DAXPY(KN,ZBETAM1,PWORK(1,IP),1,PWORK(1,IQ),1)
               CALL DCOPY(KN,PWORK(1,IQ),1,PWORK(1,IP),1)
            END IF
            CALL MATVEC(ZONE,PWORK(1,IP),0.0D0,PWORK(1,IQ),kelem,cdfam)
            CALL DOTOBS(KN,PWORK(1,IP),PWORK(1,IQ),ZALPHA)
*     ZALPHA = DDOT(KN,PWORK(1,IP),1,PWORK(1,IQ),1)

            ZALPHA = ZRHOM1/ZALPHA
            CALL DAXPY(KN,ZALPHA,PWORK(1,IP),1,PWORK(1,IX),1)
            CALL DAXPY(KN,-ZALPHA,PWORK(1,IQ),1,PWORK(1,IR),1)
            ZRHOM2 = ZRHOM1
*
*     Test if convergence has been achieved
*
            CALL DCOPY(KN,PB(1),1,PWORK(1,IQ),1)
            CALL MATVEC(ZMONE,PWORK(1,IX),ZONE,PWORK(1,IQ),kelem,cdfam)

            ZERRABS =  DNRM2(KN,PWORK(1,IQ),1)
            ZERRREL = ZERRABS/ZERR0

            IKOUNT  = IKOUNT + 1
            IF(ZERRREL.LE.PRESID) THEN
               KITER = JITER + JOUT - 1
               KINFO = 0
               GOTO 300
            END IF
         END DO
      END DO
*
*     3.
*
 300  CONTINUE
      IF (KINFO.NE.0) THEN
         WRITE(NULOUT,9301)KINFO
      END IF
*
*     Move result to output field
*     =====================================
      CALL DCOPY(KN,PWORK(1,IX),1,PX(1),1)
*     =====================================
*

      IF ( LDPRINT ) THEN
         WRITE(NULOUT,9300)ITERMAX, IKOUNT, ZERR0, ZERRABS, ZERRREL
      ENDIF
 9301 FORMAT(//,'In CONJGRAD, convergence has not been achieved. KINFO = ',I2)
 9300 FORMAT(4X,'Max. Number of Iterations requested: ',I6,/
     S     ,4X,'Number of Iterations               : ',I6,/
     S     ,4X,'Norm of Initial residual           : ',G12.6,/
     S     ,4X,'Norm of Final residual             : ',G12.6,/
     S     ,4x,'Accuracy achieved                  : ',G12.6)
*
      RETURN
      END