subroutine genstats_gps(bwork,awork,toss_gps_sub,conv_diagsave,mype)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    genstats_gps    generate statistics for gps observations
!   prgmmr: treadon          org: np20                date: 2005-12-21
!
! abstract:  For gps observations, this routine
!              a) collects statistics for runtime diagnostic output
!              f) adjusts observation error ratio based on superobs factor
!
! program history log:
!   2005-12-21  treadon
!   2006-02-24  derber  - modify to take advantage of convinfo module
!   2006-07-28  derber  - modify to use new inner loop obs data structure
!   2006-09-20  cucurull - replace superobs factor for obs in a top (non-full) layer 
!   2007-03-01  treadon - add array toss_gps
!   2007-03-19  tremolet - binning of observations
!   2007-06-21 cucurull - add conv_diagsave and mype in argument list; 
!                         modify qc and output for diagnostic file based on toss_gps
!                         print out diagnostic files if requested
!                         add wgtlim and huge_single in constants module
!   2008-02-27 cucurull - modify diagnostics output file
!   2008-04-14 treadon  - compute super_gps within this routine
!   2008-06-04 safford  - rm unused vars and uses
!   2008-09-05 lueken   - merged ed's changes into q1fy09 code
!   2008-25-08 todling  - adapt obs-binning change to GSI-May2008
!   2009-02-05 cucurull - modify latitude range four statistics output
!   2009-10-22     shen - add high_gps
!   2010-04-09 cucurull - fix several bugs for high_gps (diag information, counters, 
!                       - consider failure of gross check, obs-binning structures, QC for CL profiles)
!                       - reorganize high_gps structure
!                       - modify dimension of diagnostic structure
!
!   input argument list:
!     toss_gps_sub  - array of qc'd profile heights
!     conv_diagsave - logical to save innovation dignostics
!     mype          - mpi task id
!
!   output argument list:
!     bwork    - array containing information about obs-ges statistics
!     awork    - array containing information for data counts and gross checks
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$
  use kinds, only: r_kind,i_kind,r_single
  use obsmod, only: gps_allhead,gps_allptr,nprof_gps,&
       destroy_genstats_gps,gpsptr,obs_diag,lobsdiagsave
  use gridmod, only: nsig,regional
  use constants, only: tiny_r_kind,half,izero,ione,wgtlim,one,two,zero,five
  use qcmod, only: npres_print,ptop,pbot
  use mpimod, only: ierror,mpi_comm_world,mpi_rtype,mpi_sum,mpi_max
  use jfunc, only: jiter,last,miter
  use gsi_4dvar, only: nobs_bins
  use convinfo, only: nconvtype
  implicit none

  real(r_kind),parameter:: ten = 10.0_r_kind
  real(r_kind),parameter:: r1em3 = 1.0e-3_r_kind

! Declare passed variables
  logical                                          ,intent(in):: conv_diagsave
  integer(i_kind)                                  ,intent(in) :: mype
  real(r_kind),dimension(100_i_kind+7*nsig)        ,intent(inout):: awork
  real(r_kind),dimension(npres_print,nconvtype,5,3),intent(inout):: bwork
  real(r_kind),dimension(max(ione,nprof_gps))      ,intent(in):: toss_gps_sub

! Declare local parameters

! Declare local variables
  logical:: luse,muse
  integer(i_kind):: k,jsig,icnt,khgt,kprof,ikx,nn,j,nchar,nreal,mreal,ii
  real(r_kind):: pressure,arg,wgross,wgt,term,cg_gps,valqc,ressw2,elev,satid
  real(r_kind):: ress,val,ratio_errors,ratio_errors_reg,val2
  real(r_kind):: exp_arg,data_ikx,data_rinc,cg_term,rat_err2,elat
  real(r_kind):: wnotgross,data_ipg,data_ier,data_ib,factor,super_gps_up,rhgt
  real(r_kind),dimension(nsig,max(ione,nprof_gps)):: super_gps_sub,super_gps
  real(r_kind),dimension(max(ione,nprof_gps)):: toss_gps
  real(r_kind),dimension(max(ione,nprof_gps)):: high_gps,high_gps_sub
  real(r_single),allocatable,dimension(:,:)::sdiag
  character(8),allocatable,dimension(:):: cdiag
  
  real(r_kind),parameter:: r20 = 20.0_r_kind
  real(r_kind),parameter:: scale = 100.0_r_kind
  type(obs_diag), pointer :: obsptr => NULL()
  

!*******************************************************************************
! Check to see if there are any profiles to process.  If none, return.
  if (nprof_gps==izero) then
     if (mype==izero) write(6,*)'GENSTATS_GPS:  no profiles to process (nprof_gfs=',nprof_gps,'), EXIT routine'
     return
  endif

! Reduce sub-domain specific QC'd profile height cutoff values to
! maximum global value for each profile
  toss_gps=zero
  call mpi_allreduce(toss_gps_sub,toss_gps,nprof_gps,mpi_rtype,mpi_max,&
       mpi_comm_world,ierror)

! Compute superobs factor on sub-domains using global QC'd profile height
  super_gps_sub=zero
  super_gps = zero
  high_gps_sub = zero
  high_gps = zero
  DO ii=1,nobs_bins
     gps_allptr => gps_allhead(ii)%head
     do while (associated(gps_allptr))

!       Load local work variables
        ratio_errors = gps_allptr%ratio_err
        data_ier     = gps_allptr%obserr
        luse         = gps_allptr%luse
        khgt         = gps_allptr%loc
        rhgt         = gps_allptr%loc
        elev         = gps_allptr%rdiag(7)
        kprof        = gps_allptr%kprof

!       Accumulate superobs factors and get highest good gps obs within a profile
        if (rhgt >toss_gps(kprof)) then
           if(ratio_errors*data_ier>tiny_r_kind) then
              high_gps_sub(kprof)=max(high_gps_sub(kprof),elev)
              if(luse) then
                 k=min(max(ione,khgt),nsig)
                 super_gps_sub(k,kprof)=super_gps_sub(k,kprof)+one
              endif
           endif
        endif

        gps_allptr => gps_allptr%llpoint
 
!    End loop over observations
     end do



  END DO
! Reduce sub-domain specifc superobs factors to global values for each profile
  call mpi_allreduce(super_gps_sub,super_gps,nsig*nprof_gps,mpi_rtype,mpi_sum,&
          mpi_comm_world,ierror)

! Reduce sub-domain specific high_gps values to global values for each profile
  call mpi_allreduce(high_gps_sub,high_gps,nprof_gps,mpi_rtype,mpi_max,&
       mpi_comm_world,ierror)
  

! If generating diagnostic output, need to determine dimension of output arrays.
  if (conv_diagsave) then
     icnt = zero
     DO ii=1,nobs_bins
        gps_allptr => gps_allhead(ii)%head
        do while (associated(gps_allptr))
           luse         = gps_allptr%luse
           if(luse)icnt=icnt+ione
           gps_allptr => gps_allptr%llpoint
        end do
     END DO
     if(icnt > izero)then
       nreal =19_i_kind
       if (lobsdiagsave) nreal=nreal+4*miter+ione
       allocate(cdiag(icnt),sdiag(nreal,icnt))
     end if
  endif



! Loop over data to apply final qc, superobs factors, accumulate
! statistics and (optionally) load diagnostic output arrays
  icnt=izero
  DO ii=1,nobs_bins
     gps_allptr => gps_allhead(ii)%head
     do while (associated(gps_allptr))

!       Load local work variables
        ratio_errors = gps_allptr%ratio_err
        data_ier     = gps_allptr%obserr
        luse         = gps_allptr%luse
        muse         = gps_allptr%muse
        val          = gps_allptr%dataerr
        data_ipg     = gps_allptr%pg
        data_ib      = gps_allptr%b
        khgt         = gps_allptr%loc
        kprof        = gps_allptr%kprof
        data_ikx     = gps_allptr%type
        satid        = gps_allptr%rdiag(1)
        pressure     = gps_allptr%rdiag(6)
        data_rinc    = gps_allptr%rdiag(5)
        elat         = gps_allptr%rdiag(3)
        ikx          = nint(data_ikx)
        gpsptr       => gps_allptr%mmpoint
        if(muse .and. associated(gpsptr))then
           obsptr       => gpsptr%diags
        endif



!       Transfer diagnostic information to output arrays
        if(conv_diagsave .and. luse) then
           icnt=icnt+ione
           cdiag(icnt) = gps_allptr%cdiag
           do j=1,nreal
              sdiag(j,icnt)= gps_allptr%rdiag(j)
           enddo
        endif

!       Determine model level to which observation is mapped to
        k=min(max(ione,khgt),nsig)
 
!       Normalize ratio_errors by superobs factor.  Update ratio_error 
!       term used in minimization
        super_gps_up=zero
 
        if (super_gps(k,kprof)>tiny_r_kind) then
           do j=min(k+ione,nsig),nsig
              super_gps_up = max(super_gps_up,super_gps(j,kprof))
           enddo

           if (super_gps_up >tiny_r_kind) then
              factor = one / sqrt(super_gps(k,kprof))
           else
              factor = one / sqrt(max(super_gps(k-1,kprof),super_gps(k,kprof)))
           endif
           ratio_errors = ratio_errors * factor
           if(conv_diagsave .and. luse) then
              if(gps_allptr%rdiag(16) >tiny_r_kind) sdiag(16,icnt)=ratio_errors*data_ier
           endif
 
!          Adjust error ratio for observations used in inner loop
           if (associated(gpsptr)) then
              gpsptr%raterr2 = ratio_errors **2
              if(associated(obsptr))then
                 obsptr%wgtjo=(ratio_errors*data_ier)**2
              end if
           endif
        endif


!       For given profile, check if observation level is below level at 
!       which profile data is tossed.   If so, set error parameter to 
!       zero (effectively tossing the obs).
 
        rhgt = gps_allptr%loc
        mreal = 19_r_kind
        if (rhgt<=toss_gps(kprof)) then
           if(ratio_errors*data_ier > tiny_r_kind) then ! obs was good
              if (luse) then
                 if(conv_diagsave) then
                   sdiag(10,icnt) = five
                   sdiag(12,icnt) = -one
                   sdiag(16,icnt) = zero
                   if(lobsdiagsave) sdiag(mreal+jiter,icnt) = -one
                 endif
                 if(elat > r20) then
                    awork(22) = awork(22)+one
                 else if(elat< -r20)then
                    awork(23) = awork(23)+one
                 else
                    awork(24) = awork(24)+one
                 end if
              endif
           endif
           ratio_errors = zero
           if (associated(gpsptr)) then
              gpsptr%raterr2 = ratio_errors **2
              if(associated(obsptr))then
                 obsptr%wgtjo=zero
                 obsptr%muse(jiter)=.false.
              end if
           endif
        else  ! obs above the highest obs that failed the stats QC
            if(regional) then
                high_gps(kprof)=r1em3*high_gps(kprof)
                ratio_errors_reg=one
                if(ratio_errors*data_ier > tiny_r_kind) then
                   if((satid==41_i_kind).or.(satid==722_i_kind).or.(satid==723_i_kind).or.(satid==4_i_kind)) then
                      if ((high_gps(kprof)) < ten)  ratio_errors_reg=zero
                   else ! OL
                      if ((high_gps(kprof)) < five) ratio_errors_reg=zero                 
                   endif
                endif
                if (ratio_errors_reg == zero) then
                    if (associated(gpsptr)) then
                         gpsptr%raterr2 = ratio_errors **2
                         if(associated(obsptr))then
                           obsptr%wgtjo=zero
                           obsptr%muse(jiter)=.false.
                         end if
                    endif
                    if (luse) then
                        if(conv_diagsave) then
                          sdiag(10,icnt) = five
                          sdiag(12,icnt) = -one
                          sdiag(16,icnt) = zero
                          if(lobsdiagsave) sdiag(mreal+jiter,icnt) = -one
                        endif
                        if(elat > r20) then
                          awork(22) = awork(22)+one
                        else if(elat< -r20)then
                          awork(23) = awork(23)+one
                        else
                          awork(24) = awork(24)+one
                        end if
                    end if
                endif
              endif ! regional
        endif

!       Compute penalty terms
        if (ratio_errors*data_ier <= tiny_r_kind) muse = .false.
        if(luse)then
           val2     = val*val
           exp_arg  = -half*val2
           rat_err2 = ratio_errors**2
           if (data_ipg > tiny_r_kind) then
              cg_gps=cg_term/data_ib
              wnotgross= one-data_ipg
              wgross   = data_ipg*cg_gps
              arg      = exp(exp_arg)
              term     = log(wnotgross*arg+wgross)
              wgt      = wnotgross*arg/(wnotgross*arg+wgross)
           else
              term = exp_arg
              wgt  = one
           endif
           if(conv_diagsave) sdiag(13,icnt) = wgt/wgtlim
           valqc = -two*rat_err2*term
         

!          Accumulate statistics for obs belonging to this task
           if(muse)then
              if(wgt < wgtlim) awork(21) = awork(21)+one

!             Accumulate values for penalty and data count
              jsig=max(ione,khgt)
              awork(jsig+3*nsig+100_i_kind)=awork(jsig+3*nsig+100_i_kind)+valqc
              awork(jsig+5*nsig+100_i_kind)=awork(jsig+5*nsig+100_i_kind)+one
              awork(jsig+6*nsig+100_i_kind)=awork(jsig+6*nsig+100_i_kind)+val2*rat_err2
           endif


!          Loop over pressure level groupings and obs to accumulate
!          statistics as a function of observation type.
           do k = 1,npres_print
              if(pressure>=ptop(k) .and. pressure<=pbot(k))then
                 ress=data_rinc*scale
                 ressw2=ress*ress
                 nn=ione
                 if (.not. muse) then
                    nn=2_i_kind
                    if(ratio_errors*data_ier >=tiny_r_kind)nn=3_i_kind
                 end if

                 bwork(k,ikx,1,nn)  = bwork(k,ikx,1,nn)+one           ! count
                 bwork(k,ikx,2,nn)  = bwork(k,ikx,2,nn)+ress          ! (o-g)
                 bwork(k,ikx,3,nn)  = bwork(k,ikx,3,nn)+ressw2        ! (o-g)**2
                 bwork(k,ikx,4,nn)  = bwork(k,ikx,4,nn)+val2*rat_err2 ! penalty
                 bwork(k,ikx,5,nn)  = bwork(k,ikx,5,nn)+valqc         ! nonlin qc penalty

              end if
           end do
        end if
 
        gps_allptr => gps_allptr%llpoint

!    End loop over observations

     end do
  END DO

! If requested, write information to diagnostic file
  if(conv_diagsave .and. icnt > izero)then
     nchar = ione
     write(7)'gps',nchar,nreal,icnt,mype
     write(7)cdiag,sdiag
     deallocate(cdiag,sdiag)
  endif


! Destroy arrays holding gps data
  call destroy_genstats_gps

end subroutine genstats_gps