!--------------------------------------- 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 --------------------------------------


module writeIncrement_mod 2,11
  use MathPhysConstants_mod
  use EarthConstants_mod
  use varNameList_mod
  use controlVector_mod
  use bmatrix_mod
  use mpivar_mod
  use gridStateVector_mod
  use verticalCoord_mod
  use HorizontalCoord_mod
  use timeCoord_mod
  use physicsFunctions_mod
  implicit none
  save
  private

  public :: calcWriteIncrement,writeIncrement

  ! this is set to true when supost runs
  logical :: initialized = .false. 

  character(len=12) :: cetikinc_orig
  logical :: basic_tt = .false.
  logical :: basic_hu = .false.

  ! namelist variables
  character(len=4)  :: cppcvar(20)
  character(len=12) :: cetikinc
  integer :: nppcvar, randSeed
  logical :: write4dInc, useTL_LQtoHU, removeMean, pertBhiOnly
  real(8) :: e1_scaleFactor, e2_scaleFactor

  contains


    SUBROUTINE SUPOST 2,1
      !
      !**s/r SUPOST  - initialize the post-processing of the model state
      !
      implicit none
      integer :: ierr, jvar, ihu, itt, ivt, imin, igz, ip0, ilq
      integer :: nulnam, fnom, fclos
      logical :: lvtout, lgzout
      namelist /nampost/nppcvar, cppcvar, cetikinc, write4dInc, useTL_LQtoHU,  &
                        e1_scaleFactor,e2_scaleFactor, randSeed, removeMean, pertBhiOnly

      write(*,*) '========================================='
      write(*,*) 'supost:  initialization of postprocessing'
      write(*,*) '========================================='
      initialized=.true.
      !
      ! 1. Set default values
      !
      cetikinc = 'UNDEFINED***'
      nppcvar=6
      cppcvar(:) = '    '
      cppcvar(1) = 'UU'
      cppcvar(2) = 'VV'
      cppcvar(3) = 'TT'
      cppcvar(4) = 'LQ'
      cppcvar(5) = 'P0'
      cppcvar(6) = 'TG'
      write4dInc = .false.
      useTL_LQtoHU = .false.
      e1_scaleFactor = 0.66d0
      e2_scaleFactor = 0.33d0
      randSeed = 1
      removeMean = .true.
      pertBhiOnly = .true.
      !
      ! 2. Read the parameters from NAMPOST
      !
      nulnam=0
      ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
      read(nulnam,nml=nampost,iostat=ierr)
      if(ierr.ne.0) call abort3d('supost: Error reading namelist')
      if(mpi_myid.eq.0) write(*,nml=nampost)
      ierr=fclos(nulnam)

      cetikinc_orig = cetikinc
      !
      ! reordering cppcvar for dependent variables in writeIncrement
      !
      ip0 = 0
      ihu = 0
      itt = 0
      ivt = 0
      lgzout = .false.
      lvtout = .false.

      do jvar = 1,nppcvar
        if(cppcvar(jvar).eq.'P0') then
          ip0 = jvar
        endif
        if(cppcvar(jvar).eq.'TT') then
          itt = jvar
        endif
        if(cppcvar(jvar).eq.'GZ') then
          igz = jvar
          lgzout = .true.
          basic_tt = .true.
          basic_hu = .true.
        endif
        if(cppcvar(jvar).eq.'HU') then
          ihu = jvar
          basic_hu = .true.
        endif
        if(cppcvar(jvar).eq.'VT') then
          ivt = jvar
          lvtout = .true.
          basic_hu = .true.
        endif
        if(cppcvar(jvar).eq.'LQ') then
          ilq = jvar
        endif
        if(cppcvar(jvar).eq.'ES') then
          basic_hu = .true.
        endif
      enddo

      if(basic_tt.and.mpi_myid.eq.0) write(*,*) 'SUPOST: TT trial field will be read'
      if(basic_hu.and.mpi_myid.eq.0) write(*,*) 'SUPOST: HU trial field will be read'

      if((lgzout.or.lvtout).and.ihu.eq.0) then
        ! Make sure that HU is part of the list
        ihu = nppcvar+1
        cppcvar(ihu) = 'HU'
        nppcvar = ihu
      endif

      if(ihu.ne.0) then
        ! Make sure that TT is part of the list
        imin = min(itt,ihu)
        if (imin.eq.0) then
          ! TT is not requested: put it in the list before HU
          cppcvar(ihu) = 'TT'
          cppcvar(nppcvar+1) = 'HU'
          nppcvar = nppcvar + 1
        else
          ! TT is requested: make sure that TT is before HU in the list
          cppcvar(max(itt,ihu)) = 'HU'
          cppcvar(imin) = 'TT'
        endif
      endif

      if(ip0 .gt. 1) then
        do jvar = ip0, 2,-1
          cppcvar(jvar) = cppcvar(jvar -1)
        enddo
        cppcvar(1) = 'P0'
      endif
      !
      ! 4. Print the values
      !
      DO jvar = 1, NPPCVAR
        if(mpi_myid.eq.0) WRITE(*,FMT='(4X,"VAR NO.",I3,":",2X,"CPPCVAR= ",A5)') jvar,CPPCVAR(jvar)
      ENDDO

    END SUBROUTINE SUPOST



    subroutine calcWriteIncrement(vco_anl,vco_trl,indexAnalysis) 1,51
      !
      !  s/r calcWriteIncrement - calculate and write analysis increment after minimization
      !
      implicit none

      type(struct_vco), pointer :: vco_anl, vco_trl
      integer :: indexAnalysis

      type(struct_hco),      pointer :: hco_anl
      type(struct_gsv) :: statevector,statevectorg,statevectorp
      integer :: jlev, jj, ji, jstep, jvar, perturbLoop, numPerturbLoop, nlev_T, nlev_M, iseed
      integer :: ierr, fnom, fstouv, fstfrm, fclos
      integer :: Vcode_anl,status
      integer :: datestamplist(tim_nstepobsinc)
      real(8)  :: hu_anl,deltaHours, gasdev, zdum, scaleFactor
      real(8), allocatable :: zes(:,:,:)
      real(8), allocatable :: ztv(:,:,:)
      real(8), allocatable :: zgz_M(:,:,:),zgz_T(:,:,:)
      real(8), allocatable :: zhu(:,:,:)
      real(8), allocatable :: cv_pert_mpilocal(:), cv_pert_mpiglobal(:)
      real(8), allocatable :: scaleFactorBhi(:)
      real(8), pointer :: cv_pert_bens_mpilocal(:), field(:,:,:,:)
      real(8), pointer :: lq_inc_ptr(:,:,:,:), hu_trl_ptr(:,:,:,:)
      character(len=4) :: flnum, flnum2
      character(len=1) :: flnum3
      character(len=128) :: incFileName
      integer :: get_max_rss
      logical :: globalGSVpresent

      write(*,*) '-------------------------------'
      write(*,*) '--Starting subroutine calcWriteIncrement--'
      write(*,*) '-------------------------------'
      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
      call rpn_comm_barrier("GRID",ierr)
      call tmg_start(3,'WRITEINCR')
      !
      ! Read namelist and do some setup
      !
      if(.not.initialized) call supost
      !
      ! Check that we have at least as many processors as increment timesteps
      !
      if(mpi_nprocs .lt. tim_nstepobsinc) then
        write(*,*) 'mpi_nprocs=',mpi_nprocs,', nstepobsinc=',tim_nstepobsinc
        call abort3d('calcWriteIncrement: number of cpus < number of increment timesteps, aborting!')
      endif
      !
      ! Determine which MPI tasks will have mpiglobal statevectors (increment, background, perturbation)
      ! if 3D increment: background fields only on myid=0, so only do calculations for myid=0
      ! if 4D increment: background fields for jstep on myid=jstep-1, do calculations for first numstep procs
      !
      if( (write4dInc      .and. mpi_myid .lt. tim_nstepobsinc) .or.  &
          (.not.write4dInc .and. mpi_myid .eq. 0          ) ) then
        globalGSVpresent = .true.
      else
        globalGSVpresent = .false.
      endif

      !
      ! Setup statevector for storing the analysis increment
      !
      hco_anl => hco_Get('Analysis')
      call gsv_setVco(statevector,vco_anl)
      call gsv_setHco(statevector,hco_anl)
      nlev_T = vco_getNumLev(vco_anl,'TH')
      nlev_M = vco_getNumLev(vco_anl,'MM')

      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
      call gsv_allocate(statevector,tim_nstepobsinc, &
           datestamp=tim_getDatestamp(),mpi_local=.true.)
      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'

      !
      ! Prepare background fields (only P0, TT, HU) at each analysis time needed for computing
      ! diagnostic fields and make mpiglobal (result only on myid=increment_time_step-1)
      !
      call rpn_comm_barrier("GRID",ierr)
      call tmg_start(95,'POST_SUBASIC')
      write(*,*)' calcWriteIncrement: Read in background variables to enable calculation of diagnostic variables'

      call gsv_setVco(statevectorg,vco_anl)
      call gsv_setHco(statevectorg,hco_anl )
      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'

      call gsv_allocate(statevectorg,1,datestamp=tim_getDatestamp(),mpi_local=.false.)

      do jstep=1,tim_nstepobsinc
        datestamplist(jstep)=gsv_getDateStamp(statevector,jstep)
      enddo
      if(write4dInc) then
        ! read in the background fields at all increment timesteps
        call subasic_gd(statevectorg,vco_trl,tim_nstepobsinc,datestamplist,indexAnalysis)
      else
        ! only read in the background fields at the "analysis" timestep (usually the middle)
        call subasic_gd(statevectorg,vco_trl,1,datestamplist(statevector%anltime),indexAnalysis)
      endif
      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
      call tmg_stop(95)

      !
      ! Compute the analysis increment and make mpiglobal (result only on myid=increment_time_step-1)
      !
      call tmg_start(91,'POST_COMPUTEDX')

      call bmat_sqrtB(cvm_vazx,cvm_nvadim,statevector)

      if(write4dInc) then
        ! mpiglobal result for 4D increment only on myid=(increment_time_step -1)
        call gsv_commMPIGlobal(statevector)
        write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
      else
        ! mpiglobal result for 3D increment only on myid=0 
        call gsv_commMPIGlobal3D(statevector)
        write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
      endif
      call tmg_stop(91)

      !
      ! Allocate fields to hold diagnostic fields
      !
      if(globalGSVpresent) then
        if(write4dInc) then
          jstep=mpi_myid+1
        else
          jstep=statevector%anltime
        endif

        write(*,*) 'calcWriteIncrement: computing the diag variables for timestep:',jstep
        write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'

        allocate( zes(hco_anl%ni, nlev_T, hco_anl%nj) )
        allocate( ztv(hco_anl%ni, nlev_T, hco_anl%nj) )
        allocate( zgz_M(hco_anl%ni, nlev_M, hco_anl%nj) )
        allocate( zgz_T(hco_anl%ni, nlev_T, hco_anl%nj) )
        allocate( zhu(hco_anl%ni, nlev_T, hco_anl%nj) )
      endif

      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'

      !
      ! If ens-EnVar, loop over additional types of perturbations to analysis increment
      !
      if(indexAnalysis.gt.0) then
        numPerturbLoop = 2
      else
        numPerturbLoop = 0
      endif
      do perturbLoop = 0, numPerturbLoop

        if(indexAnalysis.gt.1 .and. perturbLoop.eq.1) then ! THIS IS FOR E1 INCREMENTS

          ! setup statevectorp for storing the perturbation
          call gsv_setVco(statevectorp,vco_anl)
          call gsv_setHco(statevectorp,hco_anl)

          write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
          call gsv_allocate(statevectorp,tim_nstepobsinc, &
               datestamp=tim_getDatestamp(),mpi_local=.true.)
          write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'

          ! compute the (unscaled) perturbation and make it mpiglobal
          call tmg_start(91,'POST_COMPUTEDX')
          iseed = abs(randSeed) + indexAnalysis
          zdum = gasdev(-iseed)
          allocate(cv_pert_mpiglobal(cvm_nvadim_mpiglobal))
          do jj = 1, cvm_nvadim_mpiglobal
            cv_pert_mpiglobal(jj) = gasdev(1)
          enddo

          allocate(cv_pert_mpilocal(cvm_nvadim))
          call bmat_reduceToMPILocal(cv_pert_mpilocal,   & ! OUT
                                     cv_pert_mpiglobal,  & ! IN
                                     jj )         ! OUT
          deallocate(cv_pert_mpiglobal)

          if(pertBhiOnly) then
            ! set Bensemble component of control vector to zero
            cv_pert_bens_mpilocal => cvm_getSubVector(cv_pert_mpilocal,2)
            cv_pert_bens_mpilocal(:) = 0.0d0
          endif

          call bmat_sqrtB(cv_pert_mpilocal,cvm_nvadim,statevectorp)
          deallocate(cv_pert_mpilocal)

          if(pertBhiOnly) then
            ! undo the Bhi scaleFactor
            allocate(scaleFactorBhi(max(nLev_M,nLev_T)))
            call bhi_getScaleFactor(scaleFactorBhi)
            ! for 3D variables
            do jvar=1,vnl_numvarmax3D 
              if(gsv_varExist(vnl_varNameList3D(jvar))) then
                field => gsv_getField(statevectorp,vnl_varNameList3D(jvar))
                do jlev = 1, gsv_getNumLev(statevectorp,vnl_vartypeFromVarname(vnl_varNameList3D(jvar)))   
                  if(scaleFactorBhi(jlev).gt.0.0d0) then
                    field(:,jlev,:,:)=field(:,jlev,:,:)/scaleFactorBhi(jlev)
                  endif
                enddo
              endif
            enddo
            ! for 2D variables
            do jvar=1,vnl_numvarmax2D 
              if(gsv_varExist(vnl_varNameList2D(jvar))) then
                field => gsv_getField(statevectorp,vnl_varNameList2D(jvar))
                jlev = max(nLev_M,nLev_T)
                if(scaleFactorBhi(jlev).gt.0.0d0) then
                  field(:,1,:,:)=field(:,1,:,:)/scaleFactorBhi(jlev)
                endif
              endif
            enddo
            deallocate(scaleFactorBhi)
          endif

          if(write4dInc) then
            ! mpiglobal result for 4D increment only on myid=(increment_time_step -1)
            call gsv_commMPIGlobal(statevectorp)
            write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
          else
            ! mpiglobal result for 3D increment only on myid=0 
            call gsv_commMPIGlobal3D(statevectorp)
            write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
          endif
          call tmg_stop(91)

          if(globalGSVpresent) then
            ! add the perturbation times e1_scaleFactor
            write(*,*) 'calcWriteIncrement: adding E1 perturbation, scaleFactor=',e1_scaleFactor
            call gsv_add(statevectorp,statevector,e1_scaleFactor)
          endif

        elseif(indexAnalysis.gt.1 .and. perturbLoop.eq.2) then ! THIS IS FOR E2 INCREMENTS

          if(globalGSVpresent) then
            ! add the perturbation again (accumulatively) with modified scaleFactor
            write(*,*) 'calcWriteIncrement: adding E2 perturbation, scaleFactor=',e2_scaleFactor
            scaleFactor = e2_scaleFactor - e1_scaleFactor
            call gsv_add(statevectorp,statevector,scaleFactor)
          endif

        endif

        if(perturbLoop.eq.0) then ! THIS IS FOR UNPERTURBED INCREMENTS
          cetikinc = cetikinc_orig
        elseif(perturbLoop.eq.1) then ! THIS IS FOR E1 INCREMENTS
          if(len_trim(cetikinc_orig).le.9) then
            cetikinc = trim(cetikinc_orig) // '_E1'
          else
            cetikinc = cetikinc_orig
          endif
        elseif(perturbLoop.eq.2) then ! THIS IS FOR E2 INCREMENTS
          if(len_trim(cetikinc_orig).le.9) then
            cetikinc = trim(cetikinc_orig) // '_E2'
          else
            cetikinc = cetikinc_orig
          endif
        endif
        !
        ! Compute diagnostic variables and write entire increment to file
        !
        call tmg_start(92,'POST_DIAG')
        if(globalGSVpresent) then
          !
          ! Compute HU increment from LQ increment (always)
          !
          hu_trl_ptr => gsv_getField(statevectorg,'HU')  ! this is HU_b
          lq_inc_ptr => gsv_getField(statevector ,'HU')  ! this is delta LQ
!$OMP PARALLEL DO PRIVATE(jj,jlev,ji,hu_anl)
          do jj = 1,statevectorg%nj
            do jlev = 1,nlev_T
              do ji = 1,statevectorg%ni
                ! choose either tangent linear or nonlinear operator for LQ to HU
                if(useTL_LQtoHU) then
                  zhu(ji,jlev,jj) = lq_inc_ptr(ji,jlev,jj,1)*hu_trl_ptr(ji,jlev,jj,1)
                else
                  hu_anl = log(hu_trl_ptr(ji,jlev,jj,1)) + lq_inc_ptr(ji,jlev,jj,1)
                  zhu(ji,jlev,jj) = EXP(hu_anl) - hu_trl_ptr(ji,jlev,jj,1)
                endif
              enddo
            enddo
          enddo
!$OMP END PARALLEL DO
          !
          ! Compute ES increment (if requested)
          !
          do jvar = 1,nppcvar
            if(cppcvar(jvar).eq.'ES') call lq2esgd(zes,statevector,statevectorg)
          enddo          
          !
          ! Compute VT increment (if VT or GZ requested)
          !
          VARLOOP: do jvar = 1,nppcvar
            if(cppcvar(jvar).eq.'VT'.or.cppcvar(jvar).eq.'GZ') then
              call lt2tvgd(ztv,statevector,statevectorg)
              exit VARLOOP
            endif
          enddo VARLOOP
          !
          ! Compute GZ increment (if requested)
          !
          do jvar = 1,nppcvar
            if(cppcvar(jvar).eq.'GZ') then
              status = vgd_get(statevectorg%vco%vgrid,key='ig_1 - vertical coord code',value=Vcode_anl)
              if(Vcode_anl.eq.5001) then 
                call ltt2phigd(zgz_T,ztv,statevectorg)
              else
                call ltt2phigd_gem4(zgz_M,zgz_T,ztv,statevector,statevectorg)
              endif
            endif
          enddo
          write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
        endif
        call tmg_stop(92)

        if(globalGSVpresent) then
          !
          ! Now, write out the increment, including diagostic variables
          !
          incFileName = './rebm'
          if(indexAnalysis.gt.0) then
            write(flnum3,'(I1.1)') perturbLoop
            incFileName = trim(incFileName) // trim(flnum3)
          endif
          if(write4dInc) then
            call difdatr(datestamplist(jstep),tim_getDatestamp(),deltaHours)
            if(nint(deltaHours*60.0d0).lt.0) then
              write(flnum,'(I4.3)') nint(deltaHours*60.0d0)
            else
              write(flnum,'(I3.3)') nint(deltaHours*60.0d0)
            endif
            write(*,*) 'calcWriteIncrement: dates=',dateStampList(jstep),tim_getDatestamp(),deltaHours,nint(deltaHours*60.0d0)
            write(*,*) 'calcWriteIncrement: flnum=###',trim(flnum),'###'
            incFileName = trim(incFileName) // '_' // trim(flnum) // 'm'
          endif
          if(indexAnalysis.gt.0) then
            write(flnum2,'(I4.4)') (indexAnalysis-1)
            incFileName = trim(incFileName) // '_' // trim(flnum2)
          endif
          call writeIncrement(incFileName,statevector,zes,ztv,zgz_T,zhu,dateStampList(jstep))
          write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 
        endif ! globalGSVpresent

      enddo ! perturbLoop

      if(globalGSVpresent) then
        deallocate(zes)
        deallocate(ztv)
        deallocate(zgz_M)
        deallocate(zgz_T)
        deallocate(zhu)
      endif

      ! free up some memory related to increment
      if(indexAnalysis.gt.1) call gsv_deallocate(statevectorp)
      call gsv_deallocate(statevectorg)
      call gsv_deallocate(statevector)

      call tmg_stop (3)
      write(*,*) 'LEAVING calcWriteIncrement'

    END SUBROUTINE calcWriteIncrement



    SUBROUTINE writeIncrement(incFileName,statevector,zes,ztv,zgz_T,zhu,datestamp,val_ip3) 2,9
      !
      !**s/r writeIncrement  - Transfer of the contents of statevector into an RPN
      !                        standard file
      !
      implicit none
      character(len=*) :: incFileName
      type(struct_gsv) :: statevector
      real(8) :: zes(:,:,:),ztv(:,:,:),zgz_T(:,:,:),zhu(:,:,:)
      integer :: datestamp
      integer, optional :: val_ip3

      integer write_encode_hyb,fnom,fstouv,fstfrm,fclos,vfstecr
      integer jvar,jlev,ierr,numlev,nulfile
      integer inpak_inc,status,Vcode_anl
      real(8) zwork
      real(8), allocatable :: zbuffer(:)
      real   zptop_r4, zpref_r4,zrcoef_r4
      integer nip1,nip2,nip3,ndeet,npas,nidatyp,nig1,nig2,nig3,nig4
      character(len=1)  :: cgrtyp
      character(len=2)  :: cltypinc
      character(len=12) :: cletiket
      real(8) :: zlowvar(statevector%ni,statevector%nj)
      logical llimplemented,FlipLatitude

      WRITE(*,FMT='(/,4X,"Starting writeIncrement",//)')
      !
      ! Read namelist and do some setup
      !
      if(.not.initialized) call supost

      nulfile = 0
      write(*,*)'writeIncrement: increment file name = ',trim(incFileName)
      ierr    = fnom(nulfile,trim(incFileName),'RND',0)
      if(ierr.ge.0)then
        write(*,*)'writeIncrement: increment file opened with unit number ',nulfile
        ierr  =  fstouv(nulfile,'RND')
      else
        call abort3d('writeIncrement: problem opening increment file, aborting!')
      end if

      if(nulfile.eq.0) then
        write(*,*) 'writeIncrement: unit number for increment file not valid!'
        return
      endif

      if(present(val_ip3)) then
        WRITE(*,FMT='(/,4X,''Transfer of the gridpoint model state on file at iteration No.'',I3)') val_ip3
      endif
      !
      ! Setup packing for each variable
      !
      inpak_inc  = -32                  ! 32 bits are needed by AAI
      write(*,*)'************************************** '
      write(*,*) 'PACKING for increments   is ',inpak_inc
      write(*,*)'************************************** '
      !     
      ! Write TIC-TAC if needed
      !
      if ( statevector % hco % grtyp == 'Z' ) then
        ndeet      =  0
        NIP1       =  statevector % hco % ig1
        NIP2       =  statevector % hco % ig2
        NIP3       =  0
        NPAS       =  0
        NIDATYP    =  1
        CGRTYP     = 'E'
        CLTYPINC   = 'X'
        cletiket   =  cetikinc

        call cxgaig ( CGRTYP,   &                                     ! IN
                      NIG1, NIG2, NIG3, NIG4, &                       ! OUT
                      real(statevector % hco % xlat1), real(statevector % hco % xlon1), & ! IN
                      real(statevector % hco % xlat2), real(statevector % hco % xlon2) ) ! IN

        allocate(zbuffer(statevector % hco % ni))
        zbuffer(:)= statevector % hco % lon(:) * MPC_DEGREES_PER_RADIAN_R8
        IERR = VFSTECR(zbuffer,zwork,inpak_inc, &
               nulfile,datestamp,ndeet,npas,statevector%ni,1,1,nip1,  &
               nip2,nip3,cltypinc,'>>',cletiket,cgrtyp,nig1,          &
               nig2,nig3,nig4,nidatyp,.true.)
        deallocate(zbuffer)

        allocate(zbuffer(statevector % hco % nj))
        zbuffer(:)= statevector % hco % lat(:) * MPC_DEGREES_PER_RADIAN_R8
        IERR = VFSTECR(zbuffer,zwork,inpak_inc, &
               nulfile,datestamp,ndeet,npas,1,statevector%nj,1,nip1,  &
               nip2,nip3,cltypinc,'^^',cletiket,cgrtyp,nig1,          &
               nig2,nig3,nig4,nidatyp,.true.)
        deallocate(zbuffer)

      end if
      !
      ! Write analysis increments
      !
      ndeet=0
      NIP2       =  0
      if(present(val_ip3)) then
        NIP3     = val_ip3
      else
        NIP3     =  0
      endif
      NPAS       =  0
      NIDATYP    =  1
      CGRTYP     = statevector % hco % grtyp
      CLTYPINC   = 'R'
      NIG1       =  statevector % hco % ig1
      if ( statevector % hco % grtyp == 'G' .and.  &
           statevector % hco % ig2   ==  1   ) then
        FlipLatitude  = .true. 
        NIG2          = 0
      else
        FlipLatitude  = .false.
        NIG2          =  statevector % hco % ig2
      end if
      NIG3       =  statevector % hco % ig3
      NIG4       =  statevector % hco % ig4
      cletiket=cetikinc

      write(*,*) 'Writing fields for datestamp= ',datestamp

      status = vgd_get(statevector%vco%vgrid,key='ig_1 - vertical coord code',value=Vcode_anl)
      if(Vcode_anl.eq.5001) then 

        write(*,*) 'Writing variable HY in analysis-increment file'
        zptop_r4 = statevector%vco%dpt_M*MPC_MBAR_PER_PA_R8
        zpref_r4 = statevector%vco%dprf_M*MPC_MBAR_PER_PA_R8
        zrcoef_r4 = statevector%vco%drcf1
        ierr    = write_encode_hyb(nulfile,'HY',nip2,nip3,cletiket,  &
                                   datestamp,zptop_r4,zpref_r4,zrcoef_r4)

      elseif(Vcode_anl.eq.5002) then

        write(*,*) 'Writing Vgrid descriptor record in analysis-increment file'
        status = vgd_write(statevector%vco%vgrid,nulfile,'fst')

      else
        write(*,*) 'Vcode_anl=',Vcode_anl
        call abort3d('writeIncrement: unknown value of Vcode!')
      endif

      !
      ! Analysis grid hybrid vertical coordinate parameters
      !
      write(*,*)' '
      write(*,*)'************************************** '
      write(*,*) ' The hybride coordinate parameters from increment', &
                 ' analysis  grid are:'
      write(*,*) ' PTOP = ',statevector%vco%dpt_M*MPC_MBAR_PER_PA_R8,' MB'
      write(*,*) ' PREF = ',statevector%vco%dprf_M*MPC_MBAR_PER_PA_R8,' MB'
      write(*,*) ' RCOEF= ',statevector%vco%drcf1
      write(*,*)'************************************** '
      write(*,*)' '

      do jvar = 1, nppcvar
        !
        ! Some variable may be request for other to be computed but not
        ! necessaraly wanted as output..
        !
        llimplemented = .true.

        write(*,*) 'Writing variable ',cppcvar(jvar)

        numlev = gsv_getNumLev(statevector,vnl_vartypeFromVarname(cppcvar(jvar)))
        write(*,*) 'numlev=',numlev

        do jlev=1,numlev
          call gdout2(cppcvar(jvar),ZLOWVAR,jlev,  &
               llimplemented,nip1,FlipLatitude,statevector,zes,ztv,zgz_T,zhu)
          if(.not.llimplemented) exit

          IERR  = VFSTECR(ZLOWVAR,zwork,inpak_inc  &
             ,nulfile,datestamp,ndeet,npas,statevector%ni,statevector%nj,1,nip1  &
             ,nip2,nip3,cltypinc,cppcvar(jvar),cletiket,cgrtyp,nig1  &
             ,nig2,nig3,nig4,nidatyp,.true.)

          if(nip1.eq.0) exit

        enddo

      enddo

      ierr =  fstfrm(nulfile)
      ierr =  fclos(nulfile)        

      write(*,*) 'END of writeIncrement'

    END SUBROUTINE writeIncrement



    subroutine lq2esgd(pesinc,statevector,statevectorg) 1,10
      !
      !**S/R lq2esgd  -  Computes increments of ES=T-TD FROM T AND lnq
      !                  increments in Grid-Point Space.
      !
      !Arguments:
      !
      ! Out:
      !      pesinc : Dew-point depression increment on the analysis grid
      ! IN:
      !      statevector    : increment
      !      statevectorg   : Background state on analysis grid
      !
      !Object:   For Postprocessing analysis increment (called by DIAG3DVAR):
      !          calculate the TLM OF dew point depression from TLM specific
      !          humidity, temperature and pressure.  No ice phase is
      !          CONSIDERED.
      !
      IMPLICIT NONE
      type(struct_gsv) :: statevector,statevectorg
      real(8) pesinc(:,:,:)
      integer ji,jj,jlev,nlev_T,status
      REAL(8) ZE, ZEL, ZTD, ZTDL, ZGAMMA
      real(8), pointer :: ps_trl_ptr(:,:,:,:),ps_inc_ptr(:,:,:,:),hu_trl_ptr(:,:,:,:),hu_inc_ptr(:,:,:,:),tt_inc_ptr(:,:,:,:)
      real(8), pointer :: zpres(:,:,:) => null()
      real(8), pointer :: dPdPsfc(:,:,:) => null()
      real(8), allocatable :: ps_trl(:,:)

      write(*,FMT='(/,4X,"Starting LQ2ESGD",//)')
      ps_trl_ptr => gsv_getField(statevectorg,'P0')
      ps_inc_ptr => gsv_getField(statevector ,'P0')
      hu_trl_ptr => gsv_getField(statevectorg,'HU')
      hu_inc_ptr => gsv_getField(statevector ,'HU')
      tt_inc_ptr => gsv_getField(statevector ,'TT')

      nlev_T = gsv_getNumLev(statevectorg,'TH')

      allocate(ps_trl(statevectorg%ni,statevectorg%nj))
      ps_trl(:,:) = ps_trl_ptr(:,1,:,1)

      status=vgd_levels(statevectorg%vco%vgrid,ip1_list=statevectorg%vco%ip1_T,  &
                        levels=zpres,sfc_field=ps_trl,in_log=.false.)
      status = vgd_dpidpis(statevectorg%vco%vgrid,statevectorg%vco%ip1_T,dPdPsfc,ps_trl)

!$OMP PARALLEL DO PRIVATE(jlev,jj,ji,ze,zel,ztd,zgamma,ztdl)
      do jj=1,statevectorg%nj
        do ji=1,statevectorg%ni

          do jlev=1,nlev_T
            !
            ! First do the forward branch to get vapour pressure from q
            !
            ZE = FOEFQ8(max(hu_trl_ptr(ji,jlev,jj,1),1.d-12), zpres(ji,jj,jlev) )
            !
            ! TLM of the vapor pressure from q (specific humidity)
            !
            ZEL = FOEFQL(hu_inc_ptr(ji,jlev,jj,1),ps_inc_ptr(ji,1,jj,1),  &
                         hu_trl_ptr(ji,jlev,jj,1),zpres(ji,jj,jlev),dPdPsfc(ji,jj,jlev))
            !
            ! TLM of the dewpoint temperature calculation from Teten's relation
            !
            ZTD=FOTW8(ZE)
            ZGAMMA=FODTW8(ZTD,ZE)
            ZTDL = ZGAMMA*ZEL

            pesinc(ji,jlev,jj) = tt_inc_ptr(ji,jlev,jj,1) - ZTDL

          enddo
        enddo
      enddo
!$OMP END PARALLEL DO

      ! array allocated by vgrid
      deallocate(zpres,dPdPsfc)

    END subroutine lq2esgd



    subroutine lt2tvgd(ptv,statevector,statevectorg) 1,4
      !
      ! s/r lt2tvgd: TL transform from delT to delTv
      !
      IMPLICIT NONE
      type(struct_gsv) :: statevector,statevectorg
      real(8) :: ptv(:,:,:)
      real(8), pointer :: hu_trl_ptr(:,:,:,:),tt_inc_ptr(:,:,:,:),hu_inc_ptr(:,:,:,:)
      integer :: ji,jj,jlev,nlev_T

      WRITE(*,FMT='(/,4X,"Starting LT2TVGD",//)')
      hu_trl_ptr => gsv_getField(statevectorg,'HU')
      tt_inc_ptr => gsv_getField(statevector ,'TT')
      hu_inc_ptr => gsv_getField(statevector ,'HU')

      nlev_T = gsv_getNumLev(statevector,'TH')
!$OMP PARALLEL DO PRIVATE(jj,jlev,ji)
      do jj = 1, statevector%nj
        do jlev = 1, nlev_T
          do ji = 1, statevector%ni
            ptv(ji,jlev,jj)=(1.D0+MPC_DELTA_R8*hu_trl_ptr(ji,jlev,jj,1))*tt_inc_ptr(ji,jlev,jj,1) +  &
                MPC_DELTA_R8*hu_trl_ptr(ji,jlev,jj,1)*tt_inc_ptr(ji,jlev,jj,1)*hu_inc_ptr(ji,jlev,jj,1)
          enddo
        enddo
      enddo
!$OMP END PARALLEL DO

    END subroutine lt2tvgd



    SUBROUTINE ltt2phigd_gem4(delGz_M,delGz_T,delTv,statevector,statevectorg) 1,8
      !
      !**s/r ltt2phigd_gem4 - Temperature to geopotential transformation on GEM4 staggered levels
      !               NOTE: we assume 
      !                     1) nlev_T = nlev_M+1 
      !                     2) GZ_T(nlev_T) = GZ_M(nlev_M), both at the surface
      !                     3) a thermo level exists at the top, higher than the highest momentum level
      !                     4) the placement of the thermo levels means that GZ_T is the average of 2 nearest GZ_M
      !                        (according to Ron and Claude)
      !
      !Author  : M. Buehner, February 2014
      !
      implicit none

      real(8) :: delGZ_M(:,:,:),delGZ_T(:,:,:)
      real(8) :: delTv(:,:,:)
      type(struct_gsv) :: statevector,statevectorg

      integer :: jlat,jlon,lev_M,lev_T,nlev_M,nlev_T
      integer :: status
      real(8) :: hu,tt,ratioP1
      real(8), allocatable :: tv(:),ratioP(:)
      real(8), allocatable :: delLnP_M(:),delLnP_T(:)
      real(8), pointer :: Psfc(:,:),ps_ptr(:,:,:,:),zpres3d_ptr(:,:,:)
      real(8), pointer :: dP_dPsfc_M(:,:,:),dP_dPsfc_T(:,:,:)
      real(8), pointer :: Pres_M(:,:,:),Pres_T(:,:,:)
      real(8), pointer :: hu_ptr(:,:,:,:),tt_ptr(:,:,:,:),delPsfc(:,:,:,:)

      write(*,FMT='(/,4X,"Starting LTT2PHIGD_GEM4",//)')

      nlev_T = gsv_getNumLev(statevectorg,'TH')
      nlev_M = gsv_getNumLev(statevectorg,'MM')

      if(nlev_T .ne. nlev_M+1) call abort3d('ltt2phi_gem4: nlev_T is not equal to nlev_M+1!')

      delPsfc => gsv_getField(statevector ,'P0')

      ! compute pressure on all levels
      ps_ptr => gsv_getField(statevectorg,'P0')
      Psfc => ps_ptr(:,1,:,1)
      status=vgd_levels(statevectorg%vco%vgrid,ip1_list=statevectorg%vco%ip1_M,  &
                        levels=Pres_M,sfc_field=Psfc,in_log=.false.)
      status=vgd_levels(statevectorg%vco%vgrid,ip1_list=statevectorg%vco%ip1_T,  &
                        levels=Pres_T,sfc_field=Psfc,in_log=.false.)

      ! compute dP_dPsfc on all levels
      status = vgd_dpidpis(statevectorg%vco%vgrid,statevectorg%vco%ip1_M,dP_dPsfc_M,Psfc)
      status = vgd_dpidpis(statevectorg%vco%vgrid,statevectorg%vco%ip1_T,dP_dPsfc_T,Psfc)

      allocate(tv(nlev_T))
      allocate(ratioP(nlev_T))
      allocate(delLnP_M(nlev_M))
      allocate(delLnP_T(nlev_T))
      hu_ptr => gsv_getField(statevectorg,'HU')
      tt_ptr => gsv_getField(statevectorg,'TT')

      ! loop over all horizontal gridpoints
      do jlat = 1, statevectorg%nj
        do jlon = 1, statevectorg%ni
 
          ! initialize GZ increment to zero
          delGz_M(jlon,:,jlat) = 0.0d0
          delGz_T(jlon,:,jlat) = 0.0d0

          ! compute lnP increment on momentum and thermo levels
          do lev_M = 1, nlev_M
            delLnP_M(lev_M) = dP_dPsfc_M(jlon,jlat,lev_M)*delPsfc(jlon,1,jlat,1)/  &
                              Pres_M(jlon,jlat,lev_M)
          enddo
          do lev_T = 1, nlev_T
            delLnP_T(lev_T) = dP_dPsfc_T(jlon,jlat,lev_T)*delPsfc(jlon,1,jlat,1)/  &
                              Pres_T(jlon,jlat,lev_T)
          enddo

          ! compute background virtual temperature
          do lev_T = 1, nlev_T
            hu = hu_ptr(jlon,lev_T,jlat,1)
            tt = tt_ptr(jlon,lev_T,jlat,1)
            tv(lev_T) = fotvt8(tt,hu)
          enddo
    
          ! compute natural log of momenutum level pressure ratios for each layer
          do lev_M = 1,(nlev_M-1)
            lev_T = lev_M+1 ! thermo level just below momentum level
            ratioP(lev_T) = log( Pres_M(jlon,jlat,lev_M+1) /  &
                                 Pres_M(jlon,jlat,lev_M  ) )
          enddo

          ! compute GZ increment on momentum levels
          do lev_M = (nlev_M-1), 1, -1
            lev_T = lev_M+1 ! thermo level just below momentum level being computed
            delGz_M(jlon,lev_M,jlat) = delGz_M(jlon,lev_M+1,jlat) +   &
                             MPC_RGAS_DRY_AIR_R8*( ratioP(lev_T)*delTv(jlon,lev_T,jlat) +  & 
                                                   tv(lev_T)*(delLnP_M(lev_M+1) -  &
                                                   delLnP_M(lev_M)) )
          enddo

          ! compute GZ increment for top thermo level (from top momentum level)
          ratioP1 = log( Pres_M(jlon,jlat,1) /  &
                         Pres_T(jlon,jlat,1) )
          delGz_T(jlon,1,jlat) = delGz_M(jlon,1,jlat) +  &
                       MPC_RGAS_DRY_AIR_R8*( ratioP1*delTv(jlon,1,jlat) +  &
                                             tv(1)*(delLnP_M(1) - delLnP_T(1)) )

          ! compute GZ increment on remaining thermo levels by simple averaging
          do lev_T = 2, (nlev_T-1)
            lev_M = lev_T ! momentum level just below thermo level being computed
            delGz_T(jlon,lev_T,jlat) = 0.5d0*( delGz_M(jlon,lev_M-1,jlat) +  &
                                               delGz_M(jlon,lev_M,jlat) )
          enddo

        enddo
      enddo

      deallocate(tv)
      deallocate(ratioP)
      deallocate(delLnP_M)
      deallocate(delLnP_T)
      ! arrays allocated by vgrid
      deallocate(Pres_M,Pres_T)
      deallocate(dP_dPsfc_M,dP_dPsfc_T)

    end subroutine ltt2phigd_gem4



    SUBROUTINE ltt2phigd(pgz,ptt,statevectorg) 1,5
      !
      !**s/r ltt2phigd  - Grid-point version of ltt2phi.ftn
      !
      !Arguments
      !     in-
      !   ptt    : 4D Temperature Incr. appearing on r.h.s. of TL-eq.
      !     out-
      !   pgz    : 4D GZ fields computed from TL-Hydrostatic equation

      IMPLICIT NONE

      type(struct_gsv) :: statevectorg
      real(8) pgz(:,:,:)
      real(8) ptt(:,:,:)

      real(8), allocatable :: vma(:),vmb(:),vmc(:),vmd(:),vme(:),vmf(:)
      INTEGER JLEV, JLAT, JLON, NLEV_T
      real(8)  zalpha
      real(8), allocatable :: zprof(:)
      real(8), pointer :: ps_ptr(:,:,:,:), zpres3d_ptr(:,:,:)
      real(8), pointer :: zps2d(:,:) 
      integer status

      write(*,FMT='(/,4X,"Starting LTT2PHIGD",//)')

      nlev_T = gsv_getNumLev(statevectorg,'TH')
      zalpha=-1.0D0

      allocate(vma(nlev_T))
      allocate(vmb(nlev_T))
      allocate(vmc(nlev_T))
      allocate(vmd(nlev_T))
      allocate(vme(nlev_T))
      allocate(vmf(nlev_T))
      allocate(zprof(nlev_T))
      !
      ! Prepare r.h.s. for TL-Hydrostatic equation
      !
      ps_ptr => gsv_getField(statevectorg,'P0')
      zps2d => ps_ptr(:,1,:,1)
      status=vgd_levels(statevectorg%vco%vgrid,  &
                        ip1_list=statevectorg%vco%ip1_T,  &
                        levels=zpres3d_ptr,sfc_field=zps2d,in_log=.false.)
      if(status.ne.VGD_OK)then
        call abort3d('ERROR with vgd_levels for desired levels ')
      endif

      do jlat = 1, statevectorg%nj
        do jlon = 1, statevectorg%ni
          do jlev = 1,nlev_T
            zprof(jlev) = zpres3d_ptr(jlon,jlat,jlev)
          enddo
          call matapat(zprof,zalpha,nlev_T,vma,vmb,vmc,vmd,vme,vmf)
          call lvtapgd(pgz,ptt,jlon,jlat) 
        enddo
      enddo

      deallocate(vma)
      deallocate(vmb)
      deallocate(vmc)
      deallocate(vmd)
      deallocate(vme)
      deallocate(vmf)
      deallocate(zprof)
      ! array allocated by vgrid
      deallocate(zpres3d_ptr)

      RETURN

      CONTAINS


      subroutine lvtapgd(pgz,ptt,ki,kj) 1
        !S/P LVTAPGD:
        !
        !         CALCULE Y A PARTIR DE R PAR SOLUTION DE L'EQUATION R*CON=S**E*D(Y)
        !         AVEC UN SCHEME DU 4EME ORDRE DU A J. COTE.
        !         NOTE: CET ALGORITHME EST EXACTEMENT REVERSIBLE (VOIR VPAT).
        !
        !         ON DOIT FOURNIR LA COND
        !         A LA LIMITE INF. Y(N). LA MATRICE MATAP A ETE CALCULEE DANS LA
        !         SUBR. MATAPAT.
        !
        IMPLICIT NONE
        integer ki,kj

        INTEGER  IKLEVM2, JLEV, IK
        REAL(8)    ZAK, ZBK, ZCK, ZCON
        real(8)    pgz(:,:,:)
        real(8)    ptt(:,:,:)
        !
        ! ptt : working vector of virtual temperatures.
        !
        ZCON = -MPC_RGAS_DRY_AIR_R8
        ZAK = -2.0D0*ZCON*VMA(nlev_T)
        ZBK = -2.0D0*ZCON*VMB(nlev_T)
        ZCK = -2.0D0*ZCON*VMC(nlev_T)
        pgz(ki,nlev_T,kj) = 0.0D0
        pgz(ki,nlev_T-1,kj)=ZAK*ptt(ki,nlev_T-1,kj)+  &
            ZBK*ptt(ki,nlev_T,kj)+  &
            ZCK*ptt(ki,nlev_T-2,kj)+  &
            pgz(ki,nlev_T,kj)

        IKLEVM2 = nlev_T-2
        do JLEV = 1, IKLEVM2
          IK = nlev_T-1-JLEV
          ZAK = -2.0D0*ZCON*VMA(IK+1)
          ZBK = -2.0D0*ZCON*VMB(IK+1)
          ZCK = -2.0D0*ZCON*VMC(IK+1)
          pgz(ki,ik,kj)= ZAK*ptt(ki,IK,kj)+  &
              ZBK*ptt(ki,IK+1,kj)+  &
              ZCK*ptt(ki,IK+2,kj)+  &
              pgz(ki,IK+2,kj)
        enddo

      END SUBROUTINE lvtapgd

    END subroutine ltt2phigd



    SUBROUTINE GDOUT2(varName,pptrans,KLEV,lplok,kip1,FlipLatitude,statevector,zes,ztv,zgz_T,zhu) 1,7
      !
      !**s/r GDOUT2  - Transfer of the content of COMGD0 on a RPN
      !     .          standard file.
      !
      !Arguments
      !     i   varName : variable name
      !     i   KLEV    : index of the level to be transferred
      ! OUTPUT
      !     o   pptrans : vector containing the variable
      !     o   lplok   : logical indicating if the variable has been
      !                   implemented
      !     o   kip1    : ip1 of the corresponding level

      IMPLICIT NONE
      INTEGER klev,kip1
      type(struct_gsv) :: statevector
      real(8), pointer :: field_ptr(:,:,:,:)
      real(8) :: pptrans(:,:)
      real(8) :: zes(:,:,:)
      real(8) :: ztv(:,:,:)
      real(8) :: zgz_T(:,:,:)
      real(8) :: zhu(:,:,:)
      character(len=*) :: varName
      logical lplok, FlipLatitude

      INTEGER JLON, JGL
      REAL(8) ZTEMP, ZGEOP, ZDAM, ZCON

      !
      lplok = .true.
      !
      IF(trim(varName).EQ.'VT') THEN
        !
        ! Virtual temperature field
        !
        kIP1      =  statevector%vco%ip1_T(klev)
        DO JLON = 1, statevector%ni
          DO JGL = 1, statevector%nj
            PPTRANS(JLON,JGL) = ZTV(JLON,KLEV,JGL)
          END DO
        END DO
      ELSE IF(trim(varName).EQ.'GZ') THEN
        !
        ! Geopotential field
        !
        kIP1      =  statevector%vco%ip1_T(klev)
        ZGEOP  = 10.0d0 * RG
        ZDAM   = 1.0d0/ZGEOP
        DO JLON = 1, statevector%ni
          DO JGL = 1, statevector%nj
            PPTRANS(JLON,JGL) = ZDAM * zgz_T(JLON,KLEV,JGL)
          END DO
        END DO
      ELSE IF(trim(varName).EQ.'UU'.or.trim(varName).EQ.'VV') THEN
        !
        ! Wind component (in Knots)
        !
        kIP1      =  statevector%vco%ip1_M(klev)
        field_ptr => gsv_getField(statevector,varName)
        DO JGL = 1, statevector%nj
          DO JLON = 1, statevector%ni
            PPTRANS(JLON,JGL) = field_ptr(JLON,KLEV,JGL,1)*MPC_KNOTS_PER_M_PER_S_R8
          END DO
        END DO
      ELSE IF(trim(varName).EQ.'ES') THEN
        !
        ! Humidity field T - Td
        !
        kIP1      =  statevector%vco%ip1_T(klev)
        DO JLON = 1, statevector%ni
          DO JGL = 1, statevector%nj
            PPTRANS(JLON,JGL) = zes(JLON,KLEV,JGL)
          END DO
        END DO

      ELSE IF(trim(varName).EQ.'LQ') THEN
        !
        ! Humidity field ln(HU)
        !
        kIP1      =  statevector%vco%ip1_T(klev)
        field_ptr => gsv_getField(statevector,'HU')
        DO JLON = 1, statevector%ni
          DO JGL = 1, statevector%nj
            PPTRANS(JLON,JGL) = field_ptr(JLON,KLEV,JGL,1)
          END DO
        END DO

      ELSE IF(trim(varName).EQ.'HU') THEN
        !
        ! Humidity field HU
        !
          kIP1      =  statevector%vco%ip1_T(klev)
          DO JLON = 1, statevector%ni
            DO JGL = 1, statevector%nj
              PPTRANS(JLON,JGL) = zhu(JLON,KLEV,JGL)
            END DO
          END DO
      ELSE IF(trim(varName).EQ.'P0') THEN
        !
        ! Surface Pressure from units of Pascal to millibar
        !
        kip1=0
        field_ptr => gsv_getField(statevector,varName)
        DO JLON = 1, statevector%ni
          DO JGL = 1, statevector%nj
            PPTRANS(JLON,JGL) = field_ptr(JLON,1,JGL,1)*MPC_MBAR_PER_PA_R8
          END DO
        END DO
      else
        !
        ! All remaining 2D and 3D variables that do not require unit conversions
        !
        if(gsv_varExist(varName)) then
          field_ptr => gsv_getField(statevector,varName)
          if(vnl_vartypeFromVarname(varName).eq.'SF') then
            kip1=0
            do jlon = 1, statevector%ni
              do jgl = 1, statevector%nj
                pptrans(jlon,jgl) = field_ptr(jlon,1,jgl,1)
              enddo
            enddo
          else
            if(vnl_vartypeFromVarname(varName).eq.'MM') then
              kIP1      =  statevector%vco%ip1_M(klev)
            else
              kIP1      =  statevector%vco%ip1_T(klev)
            endif
            do jlon = 1, statevector%ni
              do jgl = 1, statevector%nj
                pptrans(jlon,jgl) = field_ptr(jlon,klev,jgl,1)
              enddo
            enddo
          endif
        else
          LPLOK = .FALSE.
          WRITE(*,*)' ****************************************'
          WRITE(*,'(" GDOUT2: THE FOLLOWING FIELD IS NOT SUPPORTED  varName= ",A2)')varName
          WRITE(*,*)' ****************************************'
        endif
      ENDIF

      IF (lplok .and. FlipLatitude) THEN
         DO JLON   = 1, statevector%ni
            DO JGL = 1, statevector%nj/2
               ZTEMP = pptrans(JLON,JGL)
               pptrans(JLON,JGL)                 = pptrans(JLON,statevector%nj-JGL+1)
               pptrans(JLON,statevector%nj-JGL+1)= ZTEMP
            end do
         end do
      END IF

    END subroutine gdout2



    subroutine subasic_gd(statevectorg,vco_trl,numStep,datestamplist,indexAnalysis) 2,14
      !
      !**s/r subasic_gd - Get some background fields on analysis grid.
      !                   These fields are needed for:
      !                   Postprocessing diagnostic analysis increments on the analysis grid
      !                   using TL observation operators.
      !                   IMPORTANT: when numStep>1, each 3D background state is
      !                   stored in statevectorg only when myid = timestep-1, i.e. 
      !                   one 3D state per processor
      !
      IMPLICIT NONE
      INTEGER INI,INJ,INK,IG1,IG2,IG3,IG4,IERR,numStep
      integer :: indexAnalysis
      INTEGER :: datestamplist(numStep)
      CHARACTER(len=2) :: CLTYPVAR
      CHARACTER(len=1) :: CLGRTYP
      CHARACTER(len=4) :: CLNOMVAR
      CHARACTER(len=12) :: CLETIKET
      INTEGER FNOM,FSTFRM,FCLOS,FSTOUV,FSTINF

      type(struct_gsv) :: statevectorg
      type(struct_vco), pointer :: vco_trl

      type(struct_vco), pointer :: vco_anl
      type(struct_hco), pointer :: hco_anl

      logical ltrial 
      integer datestamp,jfile,jstep
      integer anl_gid,ezqkdef
      REAL*8, allocatable :: zttg(:,:,:), zqqg(:,:,:), zpsg(:,:)
      real*8, pointer :: ps_ptr(:,:,:,:),tt_ptr(:,:,:,:),hu_ptr(:,:,:,:)
      integer ntrials, nlev_T
      integer :: nultrl(tim_nstepobs)  
      CHARACTER(len=2)   :: flnum 
      CHARACTER(len=4)   :: flnum2
      CHARACTER(len=128) :: trialfile 

      WRITE(*,FMT='(/,4X,"Starting SUBASIC_GD",//)')

      vco_anl => gsv_getVco(statevectorg)

      nlev_T = gsv_getNumLev(statevectorg,'TH')
      allocate(zpsg(statevectorg%ni,statevectorg%nj))
      allocate(zttg(statevectorg%ni,statevectorg%nj,nlev_T))
      allocate(zqqg(statevectorg%ni,statevectorg%nj,nlev_T))
      !
      ! Open all of the Trial fields
      !
      call tmg_start(96,'POST_READBASIC')
      nultrl(:)=0
      ntrials=0 
      do 
        write(flnum,'(I2.2)') ntrials+1
        trialfile='./trlm_' // trim(flnum)
        if(indexAnalysis.gt.0) then
          write(flnum2,'(I4.4)') (indexAnalysis-1)
          trialfile = trim(trialfile) // '_' // trim(flnum2)
        endif
        inquire(file=trim(trialfile),exist=ltrial)
        if(ltrial) then
          ntrials=ntrials+1
          ierr=fnom(nultrl(ntrials),trim(trialfile),'RND+OLD+R/O',0)
          write(*,*) 'ITRIAL - File :', trim(trialfile)
          write(*,*) ' opened as unit file ',nultrl(ntrials)
          ierr =  fstouv(nultrl(ntrials),'RND+OLD')
        else if ( (.not. ltrial) .and. ntrials >0 ) then
          exit  
        else if ( (.not. ltrial) .and. ntrials == 0 ) then
          CALL ABORT3D('SUBASIC_GD:NO TRIAL FILE')
        endif 
      enddo 
      if(ntrials.ne.tim_nstepobs) then
        write(*,*) 'ntrials, tim_nstepobs = ',ntrials, tim_nstepobs
        call abort3d('subasic_gd: ntrials <> tim_nstepobs')
      endif
      call tmg_stop(96)
      !
      ! Define horizontal analysis grid
      !
      hco_anl => hco_Get('Analysis')
      anl_gid = hco_anl % EZscintID

      !
      ! Read trial fields and interpolate them (horizontal & vertical) to analysis grid
      !
      CLETIKET = ' '
      CLTYPVAR = 'P'

      do jstep=1,numStep

        datestamp=datestamplist(jstep)
        !
        ! Surface-pressure
        !
        write(*,*)'subasic_gd: reading P0'
        CLNOMVAR = 'P0'

        write(*,*) 'subasic_gd: datestamp = ',datestamp

        ! read the background P0, interpolate to analysis grid and put result on proc jstep-1
        ! NOTE: all processors participate in reading the file
        call vhfstfld(zpsg,statevectorg%ni,statevectorg%nj,anl_gid, &
             1,clnomvar,datestamp,nultrl,ntrials,jstep,vco_anl,vco_trl)

        ! copy interpolated P0 to statevectorg
        call tmg_start(96,'POST_READBASIC')
        if(mpi_myid.eq.(jstep-1)) then
          ps_ptr => gsv_getField(statevectorg,'P0')
          CALL INITGDG2(ps_ptr(:,:,:,1),zpsg,statevectorg%ni,statevectorg%nj,1,CLNOMVAR)
        endif
        call tmg_stop(96)

        if(basic_tt) then
          !
          ! Temperature
          !
          write(*,*)'subasic_gd: reading TT'
          CLNOMVAR = 'TT'

          ! read the background TT, interpolate to analysis grid and put result on proc jstep-1
          ! NOTE: all processors participate in reading the file
          call vhfstfld(zttg,statevectorg%ni,statevectorg%nj,anl_gid,  &
               nlev_T,clnomvar,datestamp,nultrl,ntrials,jstep,vco_anl,vco_trl)

          ! copy interpolated TT to statevectorg
          call tmg_start(96,'POST_READBASIC')
          if(mpi_myid.eq.(jstep-1)) then
            tt_ptr => gsv_getField(statevectorg,'TT')
            CALL INITGDG2(tt_ptr(:,:,:,1),zttg,statevectorg%ni,statevectorg%nj,nlev_T,CLNOMVAR)
          endif
          call tmg_stop(96)
        endif

        if(basic_hu) then
          !
          ! Specific-Humidity
          !
          write(*,*)'subasic_gd: reading HU'
          CLNOMVAR = 'HU'

          ! read the background HU, interpolate to analysis grid and put result on proc jstep-1
          ! NOTE: all processors participate in reading the file
          call vhfstfld(zqqg,statevectorg%ni,statevectorg%nj,anl_gid, &
               nlev_T,clnomvar,datestamp,nultrl,ntrials,jstep,vco_anl,vco_trl)

          ! copy interpolated HU to statevectorg
          call tmg_start(96,'POST_READBASIC')
          if(mpi_myid.eq.(jstep-1)) then
            hu_ptr => gsv_getField(statevectorg,'HU')
            CALL INITGDG2(hu_ptr(:,:,:,1),zqqg,statevectorg%ni,statevectorg%nj,nlev_T,CLNOMVAR)
          endif
          call tmg_stop(96)
        endif

      enddo ! jstep
      !
      ! Close the Trials files
      !
      call tmg_start(96,'POST_READBASIC')
      do jfile=1,ntrials
        ierr=fstfrm(nultrl(jfile))  
        ierr=fclos(nultrl(jfile))  
      enddo
      write(*,*) 'Trial files have been closed'
      call tmg_stop(96)

      deallocate(zpsg)
      deallocate(zttg)
      deallocate(zqqg)

    end subroutine subasic_gd



    subroutine vhfstfld(pvar,ini_anl,inj_anl,ktrggid,knk,varName,kstampv,kulfst,ktrials,kstep,vco_anl,vco_trl) 3,18
      !
      !**s/r vhfstfld  - Interpolate background fields on analysis grid.
      !                  These fields are need for posprocessing diagnostic
      !                  analysis increments on the analysis grid using
      !                  TL observation operators.
      !
      !Author  : S. Pellerin *ARMA/SMC May 2000
      !
      !Arguments
      !    Output:
      !      pvar(ini_anl,inj_anl,knk) : Interpolated Output variable 
      !    Input:
      !      ini_anl,inj_anl: dimensions of horizontal analysis grid
      !      ktrggid        : grid id of output variable
      !      knk            : Number of level of targetted variable
      !      varName        : Variable nomvar 
      !      kstampv        : Valid CMC date-time stamp values for reserch in
      !                       fst source file
      !      kulfst         : Unit of pre-opened standard file containing src fields
      !      ktrials        :  number of trial files.
      IMPLICIT NONE

      type(struct_vco), pointer :: vco_anl, vco_trl
      integer :: ktrials,kstep
      integer :: kulfst(ktrials)
      integer :: INI_ANL,INJ_ANL,KNK,ktrggid,kstampv
      integer :: INI,INJ,INK,IG1,IG2,IG3,IG4,fstinf
      integer :: inlev
      real*8 :: pvar(ini_anl,inj_anl,knk)
      character(len=*) :: varName

      integer, parameter :: kmaxlev=200
      integer :: jlev,jlat,jlon
      integer :: iip1s(kmaxlev),iip1,iip2,iip3,itrlnlev,itrlgid
      integer :: ikey,ezgprm,vfstluk,fstluk,ezsetopt,ezsint
      integer :: ezdefset,iset
      integer :: ikind,imode,ip1style,ip1kind
      integer :: koutmpg
      real*8 ::  zeta(kmaxlev)
      real*8, allocatable :: zhighvar(:,:)
      real*8, allocatable :: zlowvar(:,:,:)
      real*8, allocatable :: zpstrl(:,:),zps(:,:)
      real*8, pointer :: zprestrl(:,:,:),zpresanl(:,:,:)
      character(len=1) :: clstring
      integer :: status,tag,pe_send,pe_recv,nsize,ierr
      character(len=2) :: CLTYPVAR
      character(len=1) :: CLGRTYP
      character(len=12) :: CLETIKET
      integer :: nsizes(0:(mpi_nprocs-1)), displs(0:(mpi_nprocs-1))

      WRITE(*,FMT='(/,4X,"Starting VHFSTFLD",//)')

      ierr = ezsetopt('INTERP_DEGREE','LINEAR')
      !
      ! get field parameters from trial field
      !
      ! Special case for GZ which is present at both thermo and momentum
      ! levels trial field file. 
      ! We will process GZ on momentum levels only.
      ! We will therefore get the parameters for UU which is present
      ! on momentum levels.

      if (varName .eq. 'GZ') then
        call abort3d('vhfstfld (subasic_gd): use of GZ background state not supported!')
      else
        call getfldprm2(IIP1S,IIP2,IIP3,ITRLNLEV,CLETIKET,CLTYPVAR  &
             ,ITRLGID,varName,kstampv,kmaxlev,kulfst  &
             ,ip1style,ip1kind,ktrials,koutmpg)
      endif

      ! Convert ip1 to real value P
      imode = -1
      ikind = ip1kind
      do jlev = 1,itrlnlev
        call VCONVIP( iip1s(jlev), zeta(jlev), ikind, imode, clstring, .false. )
      enddo

      call vsort(zeta,itrlnlev)

      ! Convert real value P to IP
      imode = ip1style
      ikind = ip1kind
      do jlev = 1,itrlnlev
        call VCONVIP(iip1s(jlev),zeta(jlev),ikind,imode,clstring, .false.)
      enddo

      iset = ezdefset(ktrggid,itrlgid)

      ierr = ezgprm(itrlgid,clgrtyp,ini,inj,ig1,ig2,ig3,ig4)
      allocate(zhighvar(ini,inj))
      allocate(zlowvar(ini_anl,inj_anl,itrlnlev))

      write(*,*)'vhfstfld: reading ',varName

      call rpn_comm_barrier("GRID",ierr)
      call tmg_start(96,'POST_READBASIC')
      do JLEV = (1+mpi_myid),itrlnlev,mpi_nprocs

        ! Read the high-res trial field
        ikey = fstinf(koutmpg, INI, INJ, INK, kstampv, cletiket, iip1s(jlev), iip2, iip3,cltypvar,varName)

        if (ikey.lt.0) then
          write(*,*) 'Problems finding variable ',varName,' at level ',iip1s(jlev),' in trial file'
          call abort3d('VHFSTFLD')
        endif

        ikey = vfstluk(zhighvar,ikey,INI,INJ,INK)
        !
        ! Do horizontal interpolation
        !
        if(trim(varName).ne.'UV') then
          call vezsint(zlowvar(:,:,jlev),zhighvar,ini_anl,inj_anl,1,ini,inj,1)
        else
          call abort3d('vhfstfld: vector interpolation no longer supported')
        endif

      enddo !jlev
      call tmg_stop(96)
      !
      ! Send all vertical levels to myid = kstep-1
      !
      call rpn_comm_barrier("GRID",ierr)
      call tmg_start(94,'POST_COMM1')

      do jlev = 1,itrlnlev
        pe_send = mod(jlev-1,mpi_nprocs)
        pe_recv = kstep-1
        tag = pe_send+1
        nsize=ini_anl*inj_anl

        if(pe_send.ne.pe_recv) then
          if(mpi_myid.eq.pe_send) then
            call rpn_comm_send(zlowvar(:,:,jlev),nsize,"mpi_double_precision",pe_recv,tag,"GRID",ierr)
          elseif(mpi_myid.eq.pe_recv) then
            call rpn_comm_recv(zlowvar(:,:,jlev),nsize,"mpi_double_precision",pe_send,tag,"GRID",status,ierr)
          endif
        endif !pe_send <> pe_recv (i.e. kstep-1)

      enddo !jlev

      call tmg_stop(94)

      deallocate(zhighvar)

      call tmg_start(97,'POST_VINTERP')
      ! From now on, only the processor with the data does work
      if(mpi_myid.eq.kstep-1) then
        !
        ! Do vertical interpolation:
        !
        if (itrlnlev.gt.1) then

          write(*,*) 'vhfstfld: Reading P0 trial field for vertical interpolation'

          call getfldprm(IIP1,IIP2,IIP3,INLEV,CLETIKET,CLTYPVAR,  &
                         ITRLGID,'P0',kstampv,kmaxlev,koutmpg,  &
                         ip1style,ip1kind)

          ikey = FSTINF(koutmpg, INI, INJ, INK, kstampv, cletiket,  &
                        iip1, iip2, iip3,cltypvar,'P0')

          if(ikey.lt.0) then
            write(*,*) ' ******* ERROR ******* '
            write(*,*) 'No P0 found in ',koutmpg
            call abort3d('VHFSTFLD')
          endif

          allocate(zpstrl(ini,inj))

          ikey = VFSTLUK(zpstrl, ikey, INI, INJ, INK)

          zpstrl(:,:)=zpstrl(:,:)*MPC_PA_PER_MBAR_R8

          allocate(zps(ini_anl,inj_anl))
          !
          ! Interpolation of high res. P0 to low res. variable grid
          !
          call vezsint(zps,zpstrl,ini_anl,inj_anl,1,ini,inj,1)

          status=vgd_levels(vco_anl%vgrid,ip1_list=vco_anl%ip1_T,  &
                            levels=zpresanl,sfc_field=zps,in_log=.false.)

          if(status.ne.VGD_OK)then
            call abort3d('ERROR with vgd_levels for anl levels')
          endif

          if (trim(varName) .eq. 'GZ') then
            call abort3d('vhfstfld (subasic_gd): use of GZ background state not supported!')
          else
            status=vgd_levels(vco_trl%vgrid,ip1_list=vco_trl%ip1_T,  &
                              levels=zprestrl,sfc_field=zps,in_log=.false.)
          endif

          if(status.ne.VGD_OK)then
             call abort3d('ERROR with vgd_levels for trl levels')
          endif

          call vintgd(pvar,zpresanl,knk,zlowvar,zprestrl,itrlnlev,ini_anl,inj_anl)

          deallocate(zpresanl)
          deallocate(zps)
          deallocate(zprestrl)

        else

          if (knk.ne.itrlnlev) then
            write(*,*) ' *********** ERROR ***********'
            write(*,*) 'Number of level inconsistancies'
            write(*,*) knk,' levels asked on output and '
            write(*,*) itrlnlev,' levels found in standard file'
            write(*,*) ' *********** ERROR ***********'
            call abort3d('VHFSTFLD')
          else
            jlev=1
            do jlat = 1, inj_anl
              do jlon = 1, ini_anl
                pvar(jlon,jlat,jlev) = zlowvar(jlon,jlat,jlev)
              enddo
            enddo
          endif

        endif
        !
        ! End of vertical interpolation
        !
      endif
      call tmg_stop(97)

      deallocate(zlowvar)

      write(*,*) 'END of VHFSTFLD'

    end subroutine VHFSTFLD

end module writeIncrement_mod