!-------------------------------------- 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 minvar 1,25
      use mod4dv, only : l4dvar
      use oda_shared, only : dg_vbar
!
!Purpose:
!Setup of 3D/4D VAR minimization
!
*Author  : P. Gauthier *ARMA/AES  June 9, 1992
*Revision:
*
*     .  P. Koclas  *CMC/CMDA February 94
*          -comdeck comgdpar added for argument in call to postproc
*     .    -new definition for zxmin
*     .  C. Charette *ARMA/AES Jan 96
*     .    -Etikette for analysed increments
*          -Open restart files and call to DEMARRE if LRSTART = .T.
*          -Write restart files. Do test of the gradient
*           according the status of the minimization
*          -Added call supost. Added trial fields to RPN standard file
*     .  S. Pellerin *ARMA/AES Sept 97.
*          -Control of GRTEST and its output to fst file
*     .  S. Pellerin *ARMA/AES .
*          -Added call to PRINTREV
*     .  C. Charette *ARMA/AES Jul  98.
*          -Changed etiket of gradient fields (NGRTEST=1)
*     .  S. Pellerin *ARMA/SMC May 2000
*          -Control for minimization with preproc and postproc
*           subroutine calls at cnt0 level
*     .  C. Charette *ARMA/SMC Nov 2000
*          -Move call RSETCMA completely to the end
*     .  B. Brasnett *CMC/CMDA Dec 200
*          -Control the use of VarQC
*     .  JM Belanger CMDA/SMC April 2001
*          - 32 bits conversion
*          - Replace call to M1QN3 by N1QN3
*     .  S. Pellerin SMC/ARMA Nov. 2001
*          - Send of an end of simulation signal to TLM-ADJ unit
*     .  M. Buehner *ARMA/SMC April 2002
*          - Added call to suprecon for Hessian eigenvector
C preconditioning
*     .  P. Gauthier *ARMA/MSC July 2003
*     .    - Numerous changes to insure that the printouts do reflect
C what the minimization
*     .      has seen. Improvements to the clarity of printouts.
*     .      PRNTHDR and PRNTBDY have been restricted to print results
C for assimilated data only
*     .      GRTEST is now called after the minimization when NGRTEST =
C 1 (in NAMELIST)
*     .  S. Pellerin *ARMA/SMC August 2004
*          - Modification of the preconditioning file for multi-dataset
*            outer-loop need
*     .  S. Pellerin *ARMA/MSC January 2005
*          - Dynamical Multi-Inc. : Call to rw_vatra
*     .  M. Tanguay  *ARMN/MSC Jan. 2005
*          - Introduction of minimizer N1CG1
*     .  J. Halle *CMDA/SMC May. 2006
*          - Added non-linear treatment of 'TO' data
*          - Req'd addition of partov.cdk, comtov.cdk, and namtov.cdk.
*     .  S. Pellerin, ARMA, August 2008
*          - Added calls to 'tmg_*' subroutines
*     .  S. Pellerin, ARMA, January 2009
*          - Subroutine minimize renamed under minvar
*          - Introduction of the new simulator (simvar)
*          - Introduction of a new global vbar variable (instead of vazxbar)
*          - Introduction of new grtest2
*     .         L. Fillion  *ARMN/EC 14 Aug 2007 - Update to v_10_0_3.
*     .         M. Tanguay  *ARMN/EC 06 Dec 2007.
*               - LAM4D Limited-Area: Add grd_typ in rw_vatra parameters
*     .         L. Fillion  *ARMN/EC 3 June 2008
*               - Introduce lwrthess to allow not writing Hessian (i.e. requires less cpu when debugging...).
*     .   L. Fillion  *ARMN/EC 11 Feb 2009
*               - Upgrade to v_10_2_2.
*    -------------------

*Arguments
*    -NONE-
      IMPLICIT NONE
*implicits
#include "comdim.cdk"
#include "comlun.cdk"
#include "comcst.cdk"
#include "comcva.cdk"
#include "comgdpar.cdk"
#include "comvarqc.cdk"
#include "comvfiles.cdk"
#include "comanl.cdk"
#include "cvcord.cdk"
#include "partov.cdk"
#include "comtov.cdk"
#include "comgrd_param.cdk"
#include "comin.cdk"
*
C
      INTEGER IZS(1),IZTRL(10)

      REAL ZZSUNUSED(1)

      REAL*8 DLDS(1)
      LOGICAL LLTEST,llvarqc, llstop, lldf1, lrdvatra, llvazx, llxbar
C
      INTEGER IMPRES, IMODE, ITERMAX, iterdone, ISIMMAX, INDIC, IITNOVQC
      INTEGER IERR,ITERTOT,isimdone,isimtot,jdata, ISIMNOVQC
      integer ireslun,ibrpstamp,isim3d
      INTEGER IPRECON,ILEN
      REAL*8 ZJSP, ZXMIN, ZDF1, ZEPS0, ZEPS1
      REAL*8 DLGNORM, DLXNORM, ZJOTOV
C
      INTEGER FNOM,FCLOS,II, remove_c
      EXTERNAL FNOM,FCLOS,MVPROD
      EXTERNAL  TESTSP, M1QN2, N1QN3
     S     , DSCALQN, DCANAB, DCANONB, PRSCAL , DEMARRE
     S     , TRANSFER, SUPOST, simvar
      real*8 :: zeps0_000,zdf1_000
      integer :: iterdone_000,isimdone_000
      character(len=128) :: clfname
C
      logical bid_L
C
C     Additional parameters for minimization (N1CG1)
C     ----------------------------------------------
      real*8 xx,bb,a_not_use,r8_not_use,pj0
      integer in_mode(3),out_mode,i_not_use,nilm,ilm0(10),ilm1(10)
      integer i_pmat1,nl_stat, dl_bidon
C
C     ------------------------------------------
C
      allocate(dg_vbar(nvadim),STAT=nl_stat)
      if (nl_stat .ne. 0) then
        write(nulout,*) 'MINVAR: PROBLEM allocating state vectors'
        call abort3d(NULOUT,'MINVAR: allocation problem')
      endif
      dg_vbar = 0.d0

C     Initialization and allocation specific to (N1CG1)
C     -------------------------------------------------
      if( N1GC.EQ.4.and.lvarqc ) call abort3d(nulout
     &     ,' MINIMIZE N1CG1 with lvarqc not done ')
      if( BFGSB.EQ.1 ) call abort3d(nulout
     &     ,' MINIMIZE n1cg1: option not validated ')
C
C     Define i_pmat1 even if BFGSB.NE.1 (Otherwise abort in n1cg1)
C     ------------------------------------------------------------
      i_pmat1 = NVADIM**2
C
      in_mode(2) = 0
      in_mode(3) = IMODE3
C
      nilm = NVAMAJ + 3
      if( nilm.gt.10 .and. N1GC.EQ.4)  call abort3d(nulout
     &     ,' MINIMIZE n1cg1: ilm0,ilm1 incorrect ')
C
C     -------------------------------------------------
c Prepare for non-linear processing of 'TO' data:
c    - fill PZDBLPRIM8 with Z" = H(xb_lr) + Z'
c                              = H(xb_lr) - H(xb_hr) + Z
c
      if ( lnlvtov ) then
        call tmg_start(60,'RTTOV')
        call tovs_obs ('LR', ZJOTOV)
        call tmg_stop(60)
      endif
c
      ITERTOT  = 0
      isimtot = 0
C
      ZXMIN  =  RPRECIS
C     *    2. Set-up the minimization
C     .  -----------------------
      llvarqc = lvarqc

c     If minimization start without qcvar : turn off varqc to compute
c     innovations and test the gradients

      ireslun=0
      lldf1 = .true.
      if(niterjob /= -1 .and. lrestart) then
        if( N1GC.EQ.4 ) call abort3d(nulout,' MINIMIZE N1CG1 with restart not done ')
        write(nulout,*) 'Minimize : Restart mode'
        lrdvatra = .true.
        clfname = crestart
        imode = 2
        llvazx = .true.
        llxbar = .true.
      elseif (lpcon .and. nprecon <= 0) then
        write(nulout,*) 'Minimize : Preconditioning mode'
        lrdvatra = .true.
        clfname = cpconf
        imode = 2
        llvazx = .false.
        llxbar = lxbar ! from namcva (default is .true.)
      else
        lrdvatra = .false.
        imode = 0
        zeps0 = repsg
      endif
      if(.not.(imode.eq.0.or.imode.eq.2)) call abort3d(nulout,' RW_VATRA not done ')

      if (lrdvatra) then
        ibrpstamp = nbrpstamp ! ibrpstamp is a I/O argument of rw_vatra
*
        if(n1gc.eq.3) then
          call rw_vatra (clfname,0,
     %       isim3d,ibrpstamp,zeps0_000,zdf1_000,iterdone_000
     &       ,isimdone_000,iztrl,vatra,nmtra,dg_vbar,nvadim,
     &       vazx,ntrunc,nksdim,nvamaj,llxbar,llvazx,n1gc,imode,grd_typ)
*
        elseif(n1gc.eq.4) then
          if(grd_typ.eq.'LU') then
            call abort3d(nulout,'n1gc.eq.4, grd_typ=LU not yet implemented')
          else
          call rw_vatra (clfname,0,
     %       isim3d,ibrpstamp,zeps0_000,zdf1_000,iterdone_000
     &       ,isimdone_000,ilm0,vatra,nmtra,dg_vbar,nvadim,
     &       vazx,ntrunc,nksdim,nvamaj,llxbar,llvazx,n1gc,imode,grd_typ)
          endif
        endif
!
        if(niterjob /= -1 .and. lrestart) then
          ierr = remove_c(clfname)
          nsim3d = isim3d
          write(nulout,*) 'MINVAR : NSIM3D read from precon file: '
     &         ,nsim3d 
          zeps0  = zeps0_000
          zdf1   = zdf1_000
          lldf1 = .false. ! don't re-compute df1 base on Cost function
          iterdone = iterdone_000
          isimdone = isimdone_000 - 1
          itermax = min(nitermax - iterdone + 1, niterjob)
          isimmax = nsimmax - isimdone + 1
          if (iterdone < nwoqcv) lvarqc = .false.
        else
          if (ibrpstamp == nbrpstamp .and. lxbar) then
            zeps0  = zeps0_000
            zdf1   = zdf1_000
            lldf1 = .false.     ! don't re-compute df1 base on Cost function
          else
            zeps0 = repsg
            lldf1 = .true.      ! Compute df1 base on Cost function
          endif
        endif
      endif

      if(niterjob == -1 .or. .not. lrestart) then
        iterdone = 0
        isimdone = 0
        if (niterjob /= -1) then
          itermax = min(nitermax,niterjob)
        else
          itermax = nitermax
        endif
        isimmax = nsimmax
c
        if (nwoqcv > 0) lvarqc = .false.
c
        if(ngrtest.ne.0) then
          lmin = .true.
          call grtest2(simvar,nulout,nvadim,vazx,ngrange)
        endif

      endif
C
      zeps1 = zeps0

      itertot = iterdone
      isimtot = isimdone

C     *    2. Set-up the minimization
C     .  -----------------------
c
c     Calculate Hessian Eigenvectors
c
      IF(NPRECON.ge.1) THEN
        NCV=2*NPRECON
        NCORDIM=NVADIM
        ILEN = NCORDIM*NCV
        CALL HPALLOC(PTRRNK1,MAX(ILEN,1),IERR,8)
        ILEN = NCORDIM*NPRECON
        CALL HPALLOC(PTRRNK2,MAX(ILEN,1),IERR,8)
        ILEN = NPRECON
        CALL HPALLOC(PTHESEVAL,MAX(ILEN,1),IERR,8)
        CALL SUPRECON
      ENDIF
C
C     *    .   Test the gradient again at the initial point WITH PRECON
C     .   --------------------------------------------------------
C
C     .   2.1.4 Test the complete functional
C
      IF(NPRECON.gt.0 .and. ngrtest.ne.0) THEN
        WRITE(NULOUT,*) '***STARTING GRTEST WITH PRECON***'
        call grtest2(simvar,nulout,nvadim,vazx,ngrange)
      ENDIF
C
C     *    .   2.2  Compute the value of J(X) and the gradient
C     .        at the initial point
C
      INDIC =2

      call simvar(indic,nvadim,vazx,zjsp,vazg)

      if (lldf1) ZDF1     =  rdf1fac * ABS(ZJSP)

C     Put QCVAR logical to its original values

      lvarqc=llvarqc

C
      CALL PRSCAL(NVADIM,VAZG,VAZG,DLGNORM)
      DLGNORM = DSQRT(DLGNORM)
      CALL PRSCAL(NVADIM,VAZX,VAZX,DLXNORM)
      DLXNORM = DSQRT(DLXNORM)
      WRITE(NULOUT,*)' |X| = ', DLXNORM
      WRITE(NULOUT,FMT=9220) ZJSP, DLGNORM
 9220 FORMAT(/4X,'J(X) = ',G23.16,4X,'|Grad J(X)| = ',G23.16)
C
C     *    .   2.4. Constants required by MODULOPT
C     .        ------------------------------
C
      IMPRES   =  NIMPRES
C
C     *    3. Iterations of the minimization algorithm
C     .  ----------------------------------------
C
C
C     *    .   3.2 Starting point of the minimization written to
C     .       RPN standard output file
C
      IF(N1GC.EQ.3) THEN
      WRITE(NULOUT,FMT=9320)ZXMIN,ZDF1,ZEPS0,IMPRES,NITERMAX,NSIMMAX
      ELSEIF(N1GC.EQ.4) THEN
      WRITE(NULOUT,FMT=9321)ZXMIN,ZDF1,ZEPS0,EPSNEG,SELECT0,BFGSB,IMODE3,IMPRES,NITERMAX,NSIMMAX
      ENDIF
c
 9320 FORMAT(//,10X,' Minimization N1QN3 starts ...',/
     S     10x,'DXMIN =',G23.16,2X,'DF1 =',G23.16,2X,'EPSG =',G23.16
     S     /,10X,'IMPRES =',I3,2X,'NITER = ',I3,2X,'NSIM = ',I3,//
     S     ,15X,'-STARTING POINT IS WRITTEN TO FILE ...')
 9321 FORMAT(//,10X,' Minimization N1CG1 starts ...',/
     S     10x,'DXMIN =',G23.16,2X,'DF1 =',G23.16,2X,'EPSG =',G23.16,2X,'EPSNEG =',G23.16,/,
     $     10x,'SELECT0 = ',I3,2X,'BFGSB = ',I3,2X,'IMODE3 = ',I3,
     S     /,10X,'IMPRES =',I3,2X,'NITER = ',I3,2X,'NSIM = ',I3,2X,'NHESS = ',I3,//
     S     ,15X,'-STARTING POINT IS WRITTEN TO FILE ...')
C
C     *    .   3.3 Beginning the minimization
C     .       --------------------------
c
      lminend = .false.
      llstop = .false.
      if (lvarqc .and. nwoqcv > 0 .and. iterdone < nwoqcv) then
        iitnovqc = min(nwoqcv - iterdone,itermax)
        isimnovqc = isimmax
        lvarqc = .false.
        call tmg_start(21,'QN')
        call n1qn3(simvar, dscalqn, dcanonb, dcanab, nvadim, vazx,
     &       zjsp,vazg, zxmin, zdf1, zeps1, impres, nulout, imode
     &       ,iitnovqc, isimnovqc ,iztrl, vatra, nmtra, izs, zzsunused
     &       ,dlds)
        call tmg_stop (21)

        isimnovqc = isimnovqc - 1
        itermax = itermax - iitnovqc + 1
        isimmax = isimmax - isimnovqc + 1

        itertot = itertot + iitnovqc
        isimtot = isimtot + isimnovqc

        zeps1 = zeps0/zeps1
        zeps0 = zeps1
        lvarqc = .true.

        if (imode == 4 .and. itertot < nitermax) then
          imode = 2
          INDIC = 2
          call simvar(indic,nvadim,vazx,zjsp,vazg)
c          CALL SIM3D(INDIC,NVADIM,VAZX,ZJSP,VAZG)
        else
          llstop = .true.
        endif
      endif

      if (.not. llstop) then
C
        if(N1GC.EQ.3) then
C
          call tmg_start(21,'QN')
          call n1qn3(simvar, dscalqn, dcanonb, dcanab, nvadim, vazx,
     &         zjsp,vazg, zxmin, zdf1, zeps1, impres, nulout, imode,
     &         itermax,isimmax, iztrl, vatra, nmtra, izs, zzsunused,
     &         dlds)
          call tmg_stop (21)
          itertot = itertot + itermax
          isimtot = isimtot + isimmax
C
          zeps1 = zeps0/zeps1
C
        elseif(N1GC.EQ.4) then
C
C       Set VAZB = - VAZG (Valid only if VAZX = ZERO)
C       ---------------------------------------------
          VAZB = - VAZG
C
C       Keep constant term in pj0 (Valid only if VAZX = ZERO)
C       -----------------------------------------------------
          pj0 = ZJSP
C
C       Print cost function at iter 0 (Valid only if VAZX = ZERO)
C       ---------------------------------------------------------
          WRITE(NULOUT,FMT=9700) 0,0.d0+pj0,pj0,0.d0
 9700     FORMAT (4x,"n1cga:",i5,1x,e15.8,1x,e15.8,1x,e15.8)
C
          if(imode.eq.0) in_mode(1) = 0
          if(imode.eq.2) in_mode(1) = 1
C
          call n1cg1 (mvprod,NVADIM,VAZX,VAZB,a_not_use,pj0,xx,bb,.false
     &         .,EPSNEG,zeps1,itermax,impres,NULOUT,in_mode,out_mode
     &         ,VWORK,NWORK,imode,r8_not_use,i_not_use,NVAMAJ,ilm0,nilm
     &         ,VATRA,NMTRA,BFGSB,r8_not_use,i_pmat1,  NVAMAJ,ilm1,nilm
     &         ,VATR1,NMTRA,SELECT0,izs,zzsunused,dlds)
C
          zeps1 = zeps0/zeps1
          zeps0 = zeps1
C
          imode = out_mode
C
          isimmax = itermax
C
          itertot = itertot + itermax
          isimtot = isimtot + isimmax
C
C       Call 4D-Var simulator without gradient to get ZJSP
C       --------------------------------------------------
          INDIC = 2
          call simvar(indic,nvadim,vazx,zjsp,vazg)
c          CALL SIM3D(INDIC,NVADIM,VAZX,ZJSP,VAZG)
C
        endif
C
      endif

c     if lrestart remain false... postmin will execute
      lrestart = .false.
      ireslun=0
      if (niterjob /= -1 .and. itertot < nitermax .and. imode == 4) then
        clfname = crestart
c     Careful: lrestart before minimisation was used to trigger a
C     read of restart file... now it is set to .true. to avoid
C     execution of postmin.
        lrestart = .true.
      else
        do jdata = 1, nvadim
          dg_vbar(jdata) = vazx(jdata) + dg_vbar(jdata)
        enddo
        clfname = 'pm1q'
      endif

      if(lwrthess) then
        if(n1gc.eq.3) then
        call rw_vatra (clfname,1,
     %     nsim3d,nbrpstamp,zeps1,zdf1,itertot,isimtot,
     %     iztrl,vatra,nmtra,
     %     dg_vbar,nvadim,
     %     vazx,ntrunc,nksdim,nvamaj,.true.,llvazx,n1gc,imode,grd_typ)

        elseif(n1gc.eq.4) then
          if(grd_typ.eq.'LU') then
            call abort3d(nulout,'n1gc.eq.4, grd_typ=LU not yet implemented')
          else
            call rw_vatra (clfname,1,
     %       nsim3d,nbrpstamp,zeps1,zdf1,itertot,isimtot,
     %       ilm1,vatr1,nmtra,
     %       dg_vbar,nvadim,
     %       vazx,ntrunc,nksdim,nvamaj,.true.,llvazx,n1gc,imode,grd_typ)
          endif
        endif
      endif

      WRITE(NULOUT,FMT=9500) imode,iterdone,itertot-iterdone,itertot
     &     ,isimdone,isimtot-isimdone,isimtot
 9500 FORMAT(//,20X,20('*'),2X
     &     ,/,20X,'              Minimization ended with MODE:',I4
     S     ,/,20X,' Number of iterations done in previous job:',I4
     S     ,/,20X,'          Number of iterations in this job:',I4
     S     ,/,20X,'                Total number of iterations:',I4
     S     ,/,20X,'Number of simulations done in previous job:',I4
     S     ,/,20X,'         Number of simulations in this job:',I4
     S     ,/,20X,'               Total number of simulations:',I4)
c
      niter = itertot
C
C     *    4. Test the gradient at the final point if NGRTEST=1
C     .  ------------------------------------
C
 400  CONTINUE
C
      if ((NGRTEST.ne.0 .and. IMODE.ne.4 .and. .not. LRSTART)) then
        WRITE(NULOUT,FMT=9400)
 9400   FORMAT(//,12X,40('**'),/,12X,
     S       'TESTING THE GRADIENT AT THE FINAL POINT',/,40('**'))
                                !
        call grtest2(simvar,nulout,nvadim,vazx,ngrange)
                                !
      END IF

      if(l4dvar) then
        ldiag4d = .false.
        lminend = .true.
        call endsim2(nvadim,vazx)
      endif
C
      RETURN
      END