module m_berror_stats
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:	 module m_berror_stats
!   prgmmr:	 j guo <jguo@nasa.gov>
!      org:	 NASA/GSFC, Global Modeling and Assimilation Office, 900.3
!     date:	 2010-03-24
!
! abstract:  a module of berror_stats input
!
! program history log:
!   2010-03-24  j guo   - added this document block
!
!   input argument list: see Fortran 90 style document below
!
!   output argument list: see Fortran 90 style document below
!
! attributes:
!   language: Fortran 90 and/or above
!   machine:
!
!$$$  end subprogram documentation block

! module interface:

!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !MODULE: m_berror_stats - a module of berror_stats input
!
! !DESCRIPTION:
!
! !INTERFACE:

      use kinds,only : i_kind
      use constants, only: one

      implicit none

      private	! except

        ! reconfigurable parameters, via NAMELIST/setup/
      public :: berror_stats	! reconfigurable filename

        ! interfaces to file berror_stats.
      public :: berror_get_dims	! get dimensions, jfunc::createj_func()
      public :: berror_read_bal	! get cross-cov.stats., balmod::prebal()
      public :: berror_read_wgt	! get auto-cov.stats., prewgt()

      	! external interfaces relating to internal procedures.
      interface berror_get_dims; module procedure get_dims; end interface
      interface berror_read_bal; module procedure read_bal; end interface
      interface berror_read_wgt; module procedure read_wgt; end interface

! !REVISION HISTORY:
! 	30Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- initial prototype/prolog/code to wrap up all file
!		  "berror_stats" related operations.
!       25Feb10 - Zhu
!               - made changes for generalizing control variables
!               - remove berror_nvars
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname='m_berror_stats'

  	! Reconfigurable parameters, vai NAMELISt/setup/
  character(len=256),save :: berror_stats = "berror_stats"	! filename

  integer(i_kind),parameter :: default_unit_ = 22
  integer(i_kind),parameter :: ERRCODE=2
contains
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !IROUTINE: get_dims - get dimensions
!
! !DESCRIPTION:
!
! !INTERFACE:

    subroutine get_dims(msig,mlat,mlon,unit)

      implicit none

      integer(i_kind)         ,intent(  out) :: msig  ! dimension of levels
      integer(i_kind)         ,intent(  out) :: mlat  ! dimension of latitudes
      integer(i_kind)         ,intent(  out) :: mlon  ! dimension of latitudes
      integer(i_kind),optional,intent(in   ) :: unit  ! logical unit [22]

! !REVISION HISTORY:
! 	30Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- the main body of the code is extracted from jfunc.f90
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname_=myname//'::get_dims'

  integer(i_kind) :: inerr

! Read dimension of stats file
  inerr=default_unit_
  if(present(unit)) inerr = unit
  open(inerr,file=berror_stats,form='unformatted',status='old')
  rewind inerr
  read(inerr) msig,mlat,mlon
  close(inerr)
end subroutine get_dims
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !IROUTINE: read_bal - get cross-corr. coefficients
!
! !DESCRIPTION:
!
! !INTERFACE:

    subroutine read_bal(agvin,bvin,wgvin,mype,unit)
      use kinds,only : r_single
      use gridmod,only : nlat,nlon,nsig

      implicit none

      real(r_single),dimension(nlat,nsig,nsig),intent(  out) :: agvin
      real(r_single),dimension(nlat,nsig)     ,intent(  out) :: bvin,wgvin
      integer(i_kind)                         ,intent(in   ) :: mype  ! "my" processor ID
      integer(i_kind),optional                ,intent(in   ) :: unit ! an alternative unit

! !REVISION HISTORY:
! 	30Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- the main body of code for input is extracted from
!		  prebal() in balmod.f90.
!       25Feb10 - Zhu 
!               - change the structure of background error file
!               - read in agvin,wgvin,bvin only
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname_=myname//'::read_bal'

!   workspaces/variables for data not returned

  integer(i_kind):: nsigstat,nlatstat,nlonstat
  integer(i_kind):: inerr


!   Open background error statistics file
    inerr=default_unit_
    if(present(unit)) inerr=unit
    open(inerr,file=berror_stats,form='unformatted',status='old')

!   Read header.  Ensure that vertical resolution is consistent
!   with that specified via the user namelist

    rewind inerr
    read(inerr) nsigstat,nlatstat,nlonstat

    if(mype==0) then
      if (nsig/=nsigstat .or. nlat/=nlatstat .or. nlon/=nlonstat) then
         write(6,*) myname_,'(PREBAL):  ***ERROR*** resolution of ', &
           '"',trim(berror_stats),'"', &
              'incompatiable with guess'
         write(6,*) myname_,'(PREBAL):  ***ERROR*** nsigstat,nlatstat,nlonstat=', &
           nsigstat,nlatstat,nlonstat
         write(6,*) myname_,'(PREBAL):  ***ERROR*** expects nsig,nlat,nlon=', &
           nsig,nlat,nlon

         call stop2(ERRCODE)
       end if

       write(6,*) myname_,'(PREBAL):  get balance variables', &
         '"',trim(berror_stats),'".  ', &
         'mype,nsigstat,nlatstat,nlonstat =', &
          mype,nsigstat,nlatstat,nlonstat
    end if

!   Read background error file to get balance variables
    read(inerr) agvin,bvin,wgvin
    close(inerr)

    return
end subroutine read_bal
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !IROUTINE: read_wgt - read auto-corr. coeffs.
!
! !DESCRIPTION:
!
! !INTERFACE:

    subroutine read_wgt(corz,corp,hwll,hwllp,vz,corsst,hsst,mype,unit)

      use kinds,only : r_single,r_kind
      use gridmod,only : nlat,nlon,nsig
      use control_vectors,only: nrf,nrf2,nrf3,nrf_var,nrf2_loc,nrf3_loc,nrf3_oz
      use jfunc,only: varq,qoption

      implicit none

      real(r_single),dimension(nlat,nsig,nrf3),intent(out) :: corz 
      real(r_single),dimension(nlat,nrf2),intent(out) :: corp  

      real(r_single),dimension(nlat,nsig,nrf3),intent(out) :: hwll
      real(r_single),dimension(nlat,nrf2)     ,intent(out) :: hwllp
      real(r_single),dimension(nsig,nlat,nrf3),intent(out) :: vz

      real(r_single),dimension(nlat,nlon),intent(out) :: corsst
      real(r_single),dimension(nlat,nlon),intent(out) :: hsst

      integer(i_kind)                    ,intent(in   ) :: mype  ! "my" processor ID
      integer(i_kind),optional           ,intent(in   ) :: unit ! an alternative unit

! !REVISION HISTORY:
! 	30Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- the main body of the code for input is extracted from
!		  prewgt() in prewgt.f90.
!       25Feb10 - Zhu 
!               - change the structure of background error file
!               - make changes for generalizing control variables
!               - move varq here from prewgt
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname_=myname//'::read_wgt'

!  workspace variables not returned
  real(r_single),dimension(nlat,nsig,nsig):: agvin
  real(r_single),dimension(nlat,nsig) :: wgvin,bvin
 
  integer(i_kind) :: i,n,k
  integer(i_kind) :: inerr,istat
  integer(i_kind) :: nsigstat,nlatstat,nlonstat
  integer(i_kind) :: loc,nn,isig
  real(r_kind) :: corq2x
  character*5 var
  logical,dimension(nrf):: nrf_err

  real(r_single),allocatable,dimension(:,:):: hwllin
  real(r_single),allocatable,dimension(:,:):: corzin
  real(r_single),allocatable,dimension(:,:):: corq2
  real(r_single),allocatable,dimension(:,:):: vscalesin

! Open background error statistics file
  inerr=default_unit_
  if(present(unit)) inerr=unit
  open(inerr,file=berror_stats,form='unformatted',status='old')

! Read header.  Ensure that vertical resolution is consistent
! with that specified via the user namelist

  rewind inerr
  read(inerr)nsigstat,nlatstat,nlonstat
  if(mype==0) then
     if(nsigstat/=nsig .or. nlatstat/=nlat .or.nlonstat/=nlon) then
        write(6,*)'PREBAL: **ERROR** resolution of berror_stats incompatiable with GSI'
        write(6,*)'PREBAL:  berror nsigstat,nlatstat,nlonstat=', nsigstat,nlatstat,nlonstat, &
             ' -vs- GSI nsig,nlat,nlon=',nsig,nlat,nlon
        call stop2(101)
     end if

     write(6,*) myname_,'(PREWGT):  read error amplitudes ', &
       '"',trim(berror_stats),'".  ', &
       'mype,nsigstat,nlatstat,nlonstat =', &
        mype,nsigstat,nlatstat,nlonstat
  end if
  read(inerr) agvin,bvin,wgvin

! Read amplitudes
  nrf_err=.false.
  read: do
     read(inerr,iostat=istat) var, isig
     if (istat/=0) exit

     allocate ( corzin(nlat,isig) )
     if (var=='q') allocate ( corq2(nlat,isig) )
     allocate ( hwllin(nlat,isig) )
     if (isig>1) allocate ( vscalesin(nlat,isig) )

     if (var/='sst') then
        if (var=='q' .or. var=='Q') then
           read(inerr) corzin,corq2
        else
           read(inerr) corzin
        end if
        read(inerr) hwllin
        if (isig>1) read(inerr) vscalesin
     else
        read(inerr) corsst
        read(inerr) hsst
     end if

!    load the variances
     do n=1,nrf
        if (var==nrf_var(n)) then
           nrf_err(n)=.true.
           loc=n
           exit
        end if
     end do

     if (isig>1) then
        do n=1,nrf3
           if (nrf3_loc(n)==loc) then
              do k=1,isig
                 do i=1,nlat
                    corz(i,k,n)=corzin(i,k)
                    vz(k,i,n)=vscalesin(i,k)
                 end do
              end do
              if (var=='q' .and. qoption==2)then
                 do k=1,isig
                    do i=1,nlat
                       corq2x=corq2(i,k)
                       varq(i,k)=min(max(corq2x,0.0015_r_kind),one)
                    enddo
                 enddo
                 do k=1,isig
                    do i=1,nlat
                       corz(i,k,n)=one
                    end do
                 end do
              end if
              do k=1,isig
                 do i=1,nlat
                    hwll(i,k,n)=hwllin(i,k)
                 end do
              end do
              exit
           end if ! end of nrf3_loc
        end do ! end of nrf3
     end if ! end of isig

     if (isig==1) then
       do n=1,nrf2
          if (nrf2_loc(n)==loc .and. var/='sst') then
             do i=1,nlat
                corp(i,n)=corzin(i,1)
                hwllp(i,n)=hwllin(i,1)
             end do
             exit
          end if
       end do
     end if

     deallocate(corzin,hwllin)
     if (isig>1) deallocate(vscalesin)
     if (var=='q') deallocate(corq2)
  enddo read 
  close(inerr)

! corz, hwll & vz for undefined variable
  do n=1,nrf3
     loc=nrf3_loc(n)
     if (nrf_err(loc)) cycle
     if (n==nrf3_oz) then
        call setcoroz_(corz(1,1,n),mype)
        call sethwlloz_(hwll(1,1,n),mype)
        call setvscalesoz_(vz(1,1,n))
	nrf_err(loc)=.true.
     end if   
  end do

! Do final check to make sure that background errors have been loaded for all variables
  if(mype==0) then
     do n=1,nrf
        if (.not. nrf_err(n)) then
           write(6,*) 'READ_WGT: ***ERROR*** fail to load error variance for ', nrf_var(n)
	   call stop2(333)
        end if
     end do
  end if

  return
end subroutine read_wgt

!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !IROUTINE: setcoroz_ - a modeled corr.coeffs. of ozone
!
! !DESCRIPTION:
!
! !INTERFACE:

    subroutine setcoroz_(coroz,mype)
      use kinds,    only: r_single,r_kind
      use constants,only: zero,rozcon,one
      use mpimod,   only: npe,mpi_rtype,mpi_sum,mpi_comm_world

      use gridmod,  only: nlat,nsig
      use gridmod,  only: lon1,lat1

      use guess_grids,only: ntguessig
      use guess_grids,only: ges_oz   ! ozone fields
      use guess_grids,only: ges_prsi ! interface pressures (kPa)

      implicit none

      real(r_single),dimension(nlat,nsig),intent(  out) :: coroz ! of ozone
      integer(i_kind)              ,intent(in   ) :: mype     ! ID of this processor

! !REVISION HISTORY:
! 	31Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- adopted from PREWGT of previous version
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname_=myname//'::setcoroz_'
  real(r_kind),parameter:: r25 = one/25.0_r_kind

!! -- workspace and working variables

    real(r_kind),dimension(nsig+1,npe) :: work_oz,work_oz1
    real(r_kind),dimension(nsig) :: ozmz
    real(r_kind) :: asum,bsum

    integer(i_kind) :: mlat,msig
    integer(i_kind) :: i,j,k,n,mm1
    integer(i_kind) :: ierror

!! -- synity check
    if(mype==0) then
       write(6,*) myname_,'(PREWGT): mype = ',mype
    endif

    mlat=size(coroz,1)
    msig=size(coroz,2)
    if(mlat/=nlat .or. msig/=nsig) then
       write(6,*) myname_,'(PREWGT): shape mismatching on PE ',mype
       write(6,*) myname_,'(PREWGT): shape(coroz) = ',shape(coroz)
       write(6,*) myname_,'(PREWGT): while expecting nlat = ',nlat
       write(6,*) myname_,'(PREWGT): while expecting nsig = ',nsig
       call stop2(ERRCODE)
    endif

!! -- The first part is taken from read_guess().

! Calculate global means for ozone
! Calculate sums for ozone to estimate variance.
  mm1=mype+1
  work_oz = zero
  do k = 1,nsig
     do j = 2,lon1+1
        do i = 2,lat1+1
           work_oz(k,mm1) = work_oz(k,mm1) + ges_oz(i,j,k,ntguessig)* &
                rozcon*(ges_prsi(i,j,k,ntguessig)-ges_prsi(i,j,k+1,ntguessig))
        end do
     end do
  end do
  work_oz(nsig+1,mm1)=float(lon1*lat1)

  call mpi_allreduce(work_oz,work_oz1,(nsig+1)*npe,mpi_rtype,mpi_sum,&
       mpi_comm_world,ierror)
  if(ierror/=0) then
     write(6,*) myname_,'(PREWGT): MPI_allreduce() error on PE ',mype
     call stop2(ierror)
  endif

!! -- All it does above, through mm1 plus mpi_allreduce() to work_oz1[],
!! seems no more than a mpi_allgatherv() to me.  The "reduce" part is
!! actually done below ...

  bsum=zero
  do n=1,npe
     bsum=bsum+work_oz1(nsig+1,n)
  end do
  do k=1,nsig
     ozmz(k)=zero
     asum=zero
     do n=1,npe
        asum=asum+work_oz1(k,n)
     end do
     if (bsum>zero) ozmz(k)=asum/bsum
  enddo

!! -- now this part is taken from prewgt().

!   load variances onto subdomains
  do k=1,nsig
     coroz(:,k) = max(ozmz(k),0.0002_r_kind)*r25
  enddo

end subroutine setcoroz_
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !IROUTINE: sethwlloz_ - a modeled hwll of ozone
!
! !DESCRIPTION:
!
! !INTERFACE:

    subroutine sethwlloz_(hwlloz,mype)
      use kinds,   only: r_single,r_kind
      use mpimod,  only: levs_id
      use gridmod, only: nnnn1o,nsig,nlon,nlat
      use constants,only: two,three,pi,rearth_equator
      implicit none

      real(r_single),dimension(nlat,nsig),intent(  out) :: hwlloz
      integer(i_kind)              ,intent(in   ) :: mype ! ID of this processor

! !REVISION HISTORY:
! 	31Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- initial prototype/prolog/code
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname_=myname//'::sethwlloz_'

  real(r_kind),parameter :: r400=400._r_kind
  real(r_kind),parameter :: r800=800._r_kind
  real(r_kind),parameter :: r40000=40000._r_kind

  integer(i_kind) :: k,k1
  real(r_kind) :: fact
  real(r_kind) :: s2u
    
  if(mype==0) then
     write(6,*) myname_,'(PREWGT): mype = ',mype
  endif

  s2u=(two*pi*rearth_equator)/nlon
  do k=1,nnnn1o
     k1=levs_id(k)
     if(k1>0) then
     write(6,*) myname_,'(PREWGT): mype = ',mype, k1
        if(k1<=nsig*3/4)then
        !  fact=1./hwl
           fact=r40000/(r400*nlon)
        else
           fact=r40000/(nlon*(r800-r400*(nsig-k1)/(nsig-nsig*3/4)))
        endif
        fact=fact*three
        hwlloz(:,k1)=s2u/fact
     endif
  enddo


  if(mype==0) then
     write(6,*) myname_,'(PREWGT): mype = ',mype, 'finish sethwlloz_'
  endif


end subroutine sethwlloz_
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
! NASA/GSFC, Global Modeling and Assimilation Office, 900.3, GEOS/DAS  !
!BOP -------------------------------------------------------------------
!
! !IROUTINE: setvscalesoz_ - a modeled vscales for ozone
!
! !DESCRIPTION:
!
! !INTERFACE:

    subroutine setvscalesoz_(vscalesoz)
      use gridmod,only : nlat,nlon,nsig
      use kinds,only: r_single,r_kind
      implicit none

      real(r_single),dimension(nsig,nlat),intent(  out) :: vscalesoz

! !REVISION HISTORY:
! 	31Jul08	- Jing Guo <guo@gmao.gsfc.nasa.gov>
!		- initial prototype/prolog/code
!EOP ___________________________________________________________________

  character(len=*),parameter :: myname_=myname//'::setvscalesoz_'
  real(r_kind),parameter:: eight_tenths = 0.8_r_kind

  	! a fixed value is used.
  vscalesoz(:,:)=eight_tenths

end subroutine setvscalesoz_
end module m_berror_stats