module aniso_ens_util
!$$$  subprogram documentation block
!                .      .    .                                       .
! module:  aniso_ens_util
! prgmmr: sato             org: np22                date: 2008-10-03
!
! abstract: some utilities for ensemble based anisotropy,
!           extracted from anisofilter.
!
! program history log:
!   2008-10-03  sato
!
! subroutines included:
!
!   ens_uv_to_psichi   - convert uv field to psichi for regional mode
!   set_grdparm212     - projection parameter settings for 212 grid
!   set_grdparm221     - projection parameter settings for 221 grid
!   ens_intpcoeffs_reg - prepare interpolation coefficient
!   fillanlgrd         - fill analysis grid with ensemble input
!   ens_mirror         - mirroring for the out of the input domain
!   pges_minmax
!   check_32primes
!   intp_spl
!   ens_fill
!   ens_unfill
!
! variable definitions:
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentatio block

implicit none

! set default to private
  private
! define subroutines as public
  public :: ens_uv_to_psichi
  public :: set_grdparm212
  public :: set_grdparm221
  public :: ens_intpcoeffs_reg
  public :: fillanlgrd
  public :: ens_mirror
  public :: pges_minmax
  public :: check_32primes
  public :: intp_spl
  public :: ens_fill
  public :: ens_unfill

contains

!=======================================================================
subroutine ens_uv_to_psichi(u,v,truewind)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:  ens_uv_to_psichi
! prgmmr: pondeca          org: np22                date: 2007-03-08
!
! abstract: given earth u and v on analysis grid, rotate wind to get
!           grid-relative u and v components, compute divergence and
!           vorticity and call subroutine that converts into
!           streamfunction and velocity potential.
!
! program history log:
!   2008-08-21  pondeca
!
!   input argument list:
!    u(nlat,nlon)              - earth relative u-field on analysis grid
!    v(nlat,nlon)              - earth relative v-field on analysis grid
!    truewind
!   output argument list:
!    u(nlat,nlon)              - streamfunction on analysis grid
!    v(nlat,nlon)              - velocity potential on analysis grid
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentation block
  use kinds, only: i_kind,r_kind,r_single
  use constants, only: ione,one,two
  use gridmod, only: nlon,nlat,region_lon,region_lat,region_dx,region_dy, &
                     rotate_wind_ll2xy
  use wind_fft, only: divvort_to_psichi

  implicit none

! Declare passed variables
  real(r_single),intent(inout) :: u(nlat,nlon),v(nlat,nlon)
  logical       ,intent(in   ) :: truewind


! Declare local variables
  integer(i_kind) i,j,im,ip,jm,jp

  real(r_kind) rlon,rlat,dlon,dlat
  real(r_kind) ue,ve,ug,vg
  real(r_kind) dxi,dyi
  real(r_single),allocatable,dimension(:,:)::div,vort
  real(r_single),allocatable,dimension(:,:)::divb,vortb
  real(r_single),allocatable,dimension(:,:,:)::tqg
  real(r_single),allocatable,dimension(:,:)::dxy,dxyb,tdxyb

  integer(i_kind) ijext,n0,n1,nxb,nyb
  integer(i_kind) nxs,nxe,nys,nye
  integer(i_kind) mmaxb,nmaxb

  logical lprime, no_wgt

!==========================================================================
!==>rotation to get analysis grid-relative wind
!==========================================================================
! print*,'in ens_uv_to_psichi: region_lon,min,max=', &
!                              minval(region_lon),maxval(region_lon)
! print*,'in ens_uv_to_psichi: region_lat,min,max=', &
!                              minval(region_lat),maxval(region_lat)
  do i=1,nlat
     do j=1,nlon
        rlon=region_lon(i,j)
        rlat=region_lat(i,j)
        dlon=float(j)*one
        dlat=float(i)*one
        ue=u(i,j)
        ve=v(i,j)
        call rotate_wind_ll2xy(ue,ve,ug,vg,rlon,dlon,dlat)
        u(i,j)=ug
        v(i,j)=vg
     enddo
  enddo
  if (truewind) return
!==========================================================================
!==>divergence and vorticity computation
!==========================================================================
  allocate(div(nlat,nlon))
  allocate(vort(nlat,nlon))
  do i=1,nlat
     im=max(ione,i-ione)
     ip=min(nlat,i+ione)
     do j=1,nlon
        jm=max(ione,j-ione)
        jp=min(nlon,j+ione)
        dxi=one/((jp-jm)*region_dx(i,j))
        dyi=one/((ip-im)*region_dy(i,j))
        div(i,j)= (u(i,jp)-u(i,jm))*dxi + (v(ip,j)-v(im,j))*dyi
        vort(i,j)=(v(i,jp)-v(i,jm))*dxi - (u(ip,j)-u(im,j))*dyi
     enddo
  enddo

  allocate(dxy(nlat,nlon))
  dxy=(region_dx+region_dy)/two

!==========================================================================
!==>expand domain in preparation for fft use
!==========================================================================
  n0=max(nlat,nlon)
  ijext=4_i_kind
100 continue
  n1=n0+2*ijext
  call check_32primes(n1,lprime)
  if (.not.lprime) then
     ijext=ijext+ione
     goto 100
  endif

  nxs=ijext+ione
  nxe=ijext+nlon
  nys=ijext+ione
  nye=ijext+nlat

  nxb=n1
  nyb=n1

  allocate(divb(1:nyb,1:nxb))
  allocate(vortb(1:nyb,1:nxb))
  allocate(dxyb(1:nyb,1:nxb))

  no_wgt=.false.
  call ens_fill(divb ,nxb,nyb,div ,nlat,nlon,ijext,no_wgt)
  call ens_fill(vortb,nxb,nyb,vort,nlat,nlon,ijext,no_wgt)
  no_wgt=.true.
  call ens_fill(dxyb ,nxb,nyb,dxy ,nlat,nlon,ijext,no_wgt)

  allocate(tqg(1:nxb,1:nyb,2))  !note reverse order of indices
  allocate(tdxyb(1:nxb,1:nyb)) !note reverse order of indices

  do i=1,nyb
     do j=1,nxb
        tqg(j,i,1)=divb(i,j)
        tqg(j,i,2)=vortb(i,j)
        tdxyb(j,i)=dxyb(i,j)
     enddo
  enddo

  mmaxb=nxb/3-ione
  nmaxb=nyb/3-ione

  call divvort_to_psichi(nxb,nyb,mmaxb,nmaxb,tdxyb,tqg)

  do i=1,nyb
     do j=1,nxb
        divb(i,j)=tqg(j,i,1)  !vel potential
        vortb(i,j)=tqg(j,i,2)  !streamfunction
     enddo
  enddo

  call ens_unfill(vortb,nxb,nyb,u,nlat,nlon)
  call ens_unfill(divb ,nxb,nyb,v,nlat,nlon)

  !in future may add call here to get unbalanced part of chi

  deallocate(div)
  deallocate(vort)
  deallocate(divb)
  deallocate(vortb)
  deallocate(tqg)
  deallocate(dxy)
  deallocate(dxyb)
  deallocate(tdxyb)
end subroutine ens_uv_to_psichi
!  --------------------------------------------------------------
!=======================================================================
!=======================================================================
subroutine set_grdparm212(iy,jx,jxp,alat1,elon1,ds,elonv,alatan)
!$$$  subprogram documentation block
!                .      .    .                                      .
! subprogram:    set_grdparm212
!   prgmmr:
!
! abstract:
!
! program history log:
!   2009-09-15  lueken - added subprogram doc block
!
!   input argument list:
!
!   output argument list:
!    iy,ix,jxp
!    alat1,elon1,ds,elonv,alatan
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: i_kind, r_kind
  implicit none

  integer(i_kind),intent(  out) :: iy,jx,jxp
  real(r_kind)   ,intent(  out) :: alat1,elon1,ds,elonv,alatan

  iy=129_i_kind
  jx=185_i_kind
  jxp=jx
  ds=40635.25_r_kind
  alat1=12.190_r_kind
  elon1=226.541_r_kind
  elonv=265.000_r_kind
  alatan=25.000_r_kind
end subroutine set_grdparm212
!=======================================================================
!=======================================================================
subroutine set_grdparm221(iy,jx,jxp,alat1,elon1,ds,elonv,alatan)
!$$$  subprogram documentation block
!                .      .    .                                      .
! subprogram:    set_grdparm221
!   prgmmr:
!
! abstract:
!
! program history log:
!   2009-09-15  lueken - added subprogram doc block
!
!   input argument list:
!
!   output argument list:
!    iy,ix,jxp
!    alat1,elon1,ds,elonv,alatan
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: i_kind, r_kind
  use constants, only: one
  implicit none

  integer(i_kind),intent(  out) :: iy,jx,jxp
  real(r_kind)   ,intent(  out) :: alat1,elon1,ds,elonv,alatan

  iy=277_i_kind
  jx=349_i_kind
  jxp=jx
  ds=32463.41_r_kind
  alat1=one
  elon1=214.500_r_kind
  elonv=253.000_r_kind
  alatan=50.000_r_kind
end subroutine set_grdparm221
!=======================================================================
!=======================================================================
subroutine ens_intpcoeffs_reg(ngrds,igbox,iref,jref,igbox0f,ensmask,enscoeff,gblend,mype)
!
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    ens_intpcoeffs_reg
!   prgmmr: pondeca          org: np22                date: 2006-07-06
!
! abstract: compute coefficients of horizontal bilinear
!           interpolation of ensemble field to analysis grid.
!           also load mask fields with a value of 1 if grid point
!           of analysis grid is within bounds of the ensemble
!           grid and -1 otherwise. supported ensemble grids are
!           awips 212, 221, and 3 (1dg x 1dg global grid)
!
! (pt#2) i+1,j |            | i+1,j+1  (pt#4)
!            --+------------+---
!              |            | dyg1
!              |     *      + -
!              |    X,Y     | dyg
!              |            |
!            --+-----+------+---
! (pt#1)    i,j|<dxg>|<dxg1>| i,j+1     (pt#3)
!
!     note: fields are assumed to be
!           dimensioned as field(nlatdirection,nlondirection)
!
! program history log:
!   2007-02-24  pondeca
!
!   input argument list:
!     mype     - mpi task id
!     ngrds
!
!   output argument list:
!     enscoeff(:,:,:,1) - interpolation coeffs for grid 212
!     enscoeff(:,:,:,2) - interpolation coeffs for grid 221
!     enscoeff(:,:,:,3) - interpolation coeffs for global grid
!     igbox(4,3) - first indice is associated with the corner i and j
!                  values of the largest rectangular portion of the
!                  analysis grid that falls completely inside the
!                  ensemble grid. second index is for grids 212, 221 and
!                  global grid.
!     igbox0f(4,3) - same as igbox but values valid for filter grid
!     gblend(nlatf,nlonf,2) - blending functions for grids 212 and 221
!     iref
!     jref
!     ensmask
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$
  use kinds, only: i_kind, r_kind
  use constants, only: zero, one, half, izero, ione, rad2deg
  use mpimod, only: npe, mpi_sum, mpi_itype, mpi_comm_world
  use gridmod, only: nlat,nlon,tll2xy, &
                     region_lon, region_lat, &
                     rlon_min_dd,rlon_max_dd, &
                     rlat_min_dd,rlat_max_dd
  use anberror, only: pf2aP1

  implicit none

! Declare passed variables
  integer(i_kind),intent(in   ) :: mype,ngrds
  integer(i_kind),intent(  out) :: igbox(4,ngrds),igbox0f(4,ngrds)
  integer(i_kind),intent(  out) :: iref(nlat,nlon,ngrds)
  integer(i_kind),intent(  out) :: jref(nlat,nlon,ngrds)
  real(r_kind),   intent(  out) :: ensmask(nlat,nlon,ngrds)
  real(r_kind)   ,intent(  out) :: enscoeff(4,nlat,nlon,ngrds)
  real(r_kind),   intent(  out) :: gblend(pf2aP1%nlatf,pf2aP1%nlonf,2)

! Declare local variables
  integer(i_kind),parameter::ijadjust=4_i_kind
  integer(i_kind) iy,jx,jxp
  integer(i_kind) i,j,kg,ierr8,ierror
  integer(i_kind) iarea(npe),iarea2(npe),iprod,ilateral,jlateral
  integer(i_kind) ilower,iupper,jleft,jright
  integer(i_kind) ilower_prev,iupper_prev,jleft_prev,jright_prev
  integer(i_kind) i1,i2,j1,j2,ij,mype1
  integer(i_kind) iimin(npe),iimax(npe),jjmin(npe),jjmax(npe)
  integer(i_kind) iimin2(npe),iimax2(npe),jjmin2(npe),jjmax2(npe)
  integer(i_kind) iimin0(3),iimax0(3),jjmin0(3),jjmax0(3)
  real(r_kind) rlat,rlon,alat1,elon1,ds,elonv,alatan,xg,yg
  real(r_kind) dxg,dyg,dxg1,dyg1
  real(r_kind) dlon,dlat
  real(r_kind) dlonmin,dlonmax,dlatmin,dlatmax
  real(r_kind) dist1,dist2
  real(r_kind) gblend_l(pf2aP1%nlonf,2)
  real(r_kind) gblend_r(pf2aP1%nlonf,2)
  real(r_kind) gblend_b(pf2aP1%nlatf,2)
  real(r_kind) gblend_t(pf2aP1%nlatf,2)
  real(r_kind) r360
  logical outside
  logical ltest,ltest1,ltest2,ltest3,ltest4
!*****************************************************************
  r360=360._r_kind
!=================================================================
  enscoeff(:,:,:,:)=-1.e+20_r_kind
  ensmask(:,:,1:2)=-one
  ensmask(:,:,3)  = one  !model grid is always fully contained in global
!                         ensemble grid

  do kg=1,3

     if      (kg==ione) then      !grid #212
        call set_grdparm212(iy,jx,jxp,alat1,elon1,ds,elonv,alatan)
     else if (kg==2_i_kind) then  !grid #221
        call set_grdparm221(iy,jx,jxp,alat1,elon1,ds,elonv,alatan)
     else if (kg==3_i_kind) then  !1dg x 1dg global grid
        iy=181_i_kind
        jx=360_i_kind
        jxp=jx+ione
     end if

     do j=1,nlon
        do i=1,nlat
 
           ! latlon for the analysing grid
           rlat=region_lat(i,j)*rad2deg
           rlon=region_lon(i,j)*rad2deg
           if (rlon<zero) rlon=rlon+r360

           ! correspoinding ensemble grid
           if (kg==ione .or. kg==2_i_kind) then
              call w3fb11(rlat,rlon,alat1,elon1,ds,elonv,alatan,xg,yg)
           else if(kg==3_i_kind) then
              xg=rlon+one
              if (rlon>=r360) xg=rlon+one-r360
              yg=rlat+90._r_kind+one
           end if

           dxg=xg-float(floor(xg))
           dyg=yg-float(floor(yg))
           dxg1=one-dxg
           dyg1=one-dyg

           if (xg>=one .and. xg<=float(jxp) .and.  &
               yg>=one .and. yg<=float(iy) ) then

              enscoeff(1,i,j,kg)=dxg1*dyg1
              enscoeff(2,i,j,kg)=dxg1*dyg
              enscoeff(3,i,j,kg)=dxg*dyg1
              enscoeff(4,i,j,kg)=dxg*dyg

              iref(i,j,kg)=floor(yg)
              jref(i,j,kg)=floor(xg)
 
              if (kg==ione .or. kg==2_i_kind) ensmask(i,j,kg)=one

           end if

        end do
     end do
  end do

!==>determine rectangle of analysis grid that falls inside the ensemble grid

  do kg=1,2
     dlonmin=+huge(dlonmin)
     dlonmax=-huge(dlonmax)
     dlatmin=+huge(dlatmin)
     dlatmax=-huge(dlatmax)

     if      (kg==ione) then      !grid #212
        call set_grdparm212(iy,jx,jxp,alat1,elon1,ds,elonv,alatan)
     else if (kg==2_i_kind) then  !grid #221
        call set_grdparm221(iy,jx,jxp,alat1,elon1,ds,elonv,alatan)
     endif

     do j=1,iy
        yg=float(j)*one
        do i=1,jx
           xg=float(i)*one
           call w3fb12(xg,yg,alat1,elon1,ds,elonv,alatan,rlat,rlon,ierr8)
           rlon=rlon/rad2deg
           rlat=rlat/rad2deg
           call tll2xy(rlon,rlat,dlon,dlat,outside)
           dlon=min(rlon_max_dd,max(rlon_min_dd,dlon))
           dlat=min(rlat_max_dd,max(rlat_min_dd,dlat))
           dlonmin=min(dlonmin,dlon)
           dlonmax=max(dlonmax,dlon)
           dlatmin=min(dlatmin,dlat)
           dlatmax=max(dlatmax,dlat)
        end do
     end do

     i1=ceiling(dlatmin)
     i2=floor  (dlatmax)
     j1=ceiling(dlonmin)
     j2=floor  (dlonmax)

     if (mype==izero) print*,'in ens_intpcoeffs_reg: kg, i1,i2,j1,j2=',kg, i1,i2,j1,j2

!==>adjust limits by trial and error

     ilateral=2_i_kind
     jlateral=2_i_kind
100  continue
     ilower=i1+ilateral
     iupper=i2-ilateral
     jleft=j1+jlateral
     jright=j2-jlateral
     ltest=any(ensmask(ilower:iupper,jleft:jright,kg)<zero)
     if (ltest) then
        ilateral=ilateral+2_i_kind
        jlateral=jlateral+2_i_kind
        goto 100
     endif
     ilateral=ilateral+4_i_kind  !increase if necessary
     jlateral=jlateral+4_i_kind
 
     if (mype==izero) print*,'in ens_intpcoeffs_reg: kg,ilateral,jlateral=',kg,ilateral,jlateral

     ilower_prev=i1+ilateral
     iupper_prev=i2-ilateral
     jleft_prev =j1+jlateral
     jright_prev=j2-jlateral

     mype1=mype+ione

     ij=izero
     iarea(:)=izero
     iimin(:)=izero
     iimax(:)=izero
     jjmin(:)=izero
     jjmax(:)=izero
     do ilower=(i1+ilateral),i1,-ione
        do iupper=(i2-ilateral),i2
           do jleft=(j1+jlateral),j1,-ione
              do jright=(j2-jlateral),j2
                 ij=ij+ione
                 if (mod(ij-ione,npe) == mype)then
                    ltest1=any(ensmask(ilower:ilower_prev,jleft:jright,kg)<zero)
                    ltest2=any(ensmask(iupper_prev:iupper,jleft:jright,kg)<zero)
                    ltest3=any(ensmask(ilower:iupper,jleft:jleft_prev,kg)<zero)
                    ltest4=any(ensmask(ilower:iupper,jright_prev:jright,kg)<zero)
                    if (.not.ltest1  .and. .not.ltest2 .and. .not.ltest3 .and. .not.ltest4) then
                       iprod=(iupper-ilower)*(jright-jleft)
                       if (iprod > iarea(mype1)) then
                          iarea(mype1)=iprod
                          iimin(mype1)=ilower
                          iimax(mype1)=iupper
                          jjmin(mype1)=jleft
                          jjmax(mype1)=jright
                       end if
                    end if
                    ilower_prev=ilower
                    iupper_prev=iupper
                    jleft_prev =jleft
                    jright_prev=jright
                 end if
              end do
           end do
        end do
     end do

     iarea2(:)=izero
     iimin2(:)=izero
     iimax2(:)=izero
     jjmin2(:)=izero
     jjmax2(:)=izero

     call mpi_allreduce(iarea,iarea2,npe,mpi_itype, &
                        mpi_sum,mpi_comm_world,ierror)
     call mpi_allreduce(iimin,iimin2,npe,mpi_itype, &
                        mpi_sum,mpi_comm_world,ierror)
     call mpi_allreduce(iimax,iimax2,npe,mpi_itype, &
                        mpi_sum,mpi_comm_world,ierror)
     call mpi_allreduce(jjmin,jjmin2,npe,mpi_itype, &
                        mpi_sum,mpi_comm_world,ierror)
     call mpi_allreduce(jjmax,jjmax2,npe,mpi_itype, &
                        mpi_sum,mpi_comm_world,ierror)

     iprod=izero
     do i=1,npe
        if (iarea2(i) > iprod) then
           iprod=iarea2(i)
           j=i
        end if
     end do

     iimin0(kg)=iimin2(j)
     iimax0(kg)=iimax2(j)
     jjmin0(kg)=jjmin2(j)
     jjmax0(kg)=jjmax2(j)

  end do

!check limits
  if (mype==izero) then
     do kg=1,2
        do i=iimin0(kg),iimax0(kg)
           do j=jjmin0(kg),jjmax0(kg)
              if (ensmask(i,j,kg) < zero) then
                 print*,'in ens_intpcoeffs_reg: trouble: kg,i,j,ensmask(i,j,kg)=',&
                         kg,i,j,ensmask(i,j,kg)
              end if
           end do
        end do
     end do
  end if

  iimin0(3)=ione
  iimax0(3)=nlat
  jjmin0(3)=ione
  jjmax0(3)=nlon

  do kg=1,3
     igbox(1,kg)=iimin0(kg)
     igbox(2,kg)=iimax0(kg)
     igbox(3,kg)=jjmin0(kg)
     igbox(4,kg)=jjmax0(kg)
     igbox0f(1,kg)=one+float((igbox(1,kg)-ione))/pf2aP1%grid_ratio_lat + ijadjust
     igbox0f(2,kg)=one+float((igbox(2,kg)-ione))/pf2aP1%grid_ratio_lat - ijadjust
     igbox0f(3,kg)=one+float((igbox(3,kg)-ione))/pf2aP1%grid_ratio_lon + ijadjust
     igbox0f(4,kg)=one+float((igbox(4,kg)-ione))/pf2aP1%grid_ratio_lon - ijadjust
  end do

!==> compute blending functions

  do i=1,pf2aP1%nlatf
     dist1=float(igbox0f(1,1)-i)
     dist2=float(i-igbox0f(2,1))
     gblend_b(i,1)=half*(one-tanh(dist1)) !relax to zero
     gblend_t(i,1)=half*(one-tanh(dist2)) !outside 212 grid

     dist1=float(igbox0f(1,2)-i)
     dist2=float(i-igbox0f(2,2))
     gblend_b(i,2)=half*(one-tanh(dist1)) !relax to zero
     gblend_t(i,2)=half*(one-tanh(dist2)) !outside 221 grid
  end do

  do j=1,pf2aP1%nlonf
     dist1=float(igbox0f(3,1)-j)
     dist2=float(j-igbox0f(4,1))
     gblend_l(j,1)=half*(one-tanh(dist1)) !relax to zero
     gblend_r(j,1)=half*(one-tanh(dist2)) !outside 212 grid
 
     dist1=float(igbox0f(3,2)-j)
     dist2=float(j-igbox0f(4,2))
     gblend_l(j,2)=half*(one-tanh(dist1)) !relax to zero
     gblend_r(j,2)=half*(one-tanh(dist2)) !outside 221 grid
  end do

  do i=1,pf2aP1%nlatf
     do j=1,pf2aP1%nlonf
        gblend(i,j,1)=gblend_b(i,1)*gblend_t(i,1)*gblend_l(j,1)*gblend_r(j,1)
        gblend(i,j,2)=gblend_b(i,2)*gblend_t(i,2)*gblend_l(j,2)*gblend_r(j,2)
     end do
  end do

  if(mype==izero) then
     open(94,file='gblend.dat',form='unformatted')
     write(94) gblend
     close(94)
  end if

end subroutine ens_intpcoeffs_reg
!=======================================================================
!=======================================================================
subroutine fillanlgrd(workin,ngrds,igrid,nx,ny,workout, &
                       iref,jref,igbox,enscoeff)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    fillanlgrd
!   prgmmr: pondeca          org: np22                date: 2007-04-05
!
! abstract: perform horizontal interpolation of ensemble field to analysis
!           grid.
!
! program history log:
!   2007-04-05  pondeca
!
!   input argument list:
!     workin(nx,ny)  - ensemble field
!     igrid          - grid number. currently 212,221 or 3
!     nx,ny          - horizontal dimensions of ensemble field
!     ngrds          - number of supported ensemble grids. currently 3
!     enscoeff       - coefficients of bilienar interpolation from
!                      ensemble grid to analysis grid
!     igbox          - i and j corner values of portion of anl grid that
!                      falls completly inside e-grid
!     iref
!     jref
!     igbox
!
!   output argument list:
!    workout(nlat,nlon) - ensemble field on analysis grid
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$
  use kinds, only: i_kind, r_single, r_kind
  use constants, only: ione
  use gridmod, only: nlat,nlon
  implicit none


! Declare passed variables
  integer(i_kind)                    ,intent(in   ) :: igrid,ngrds,nx,ny,igbox(4,ngrds)
  integer(i_kind)                    ,intent(in   ) :: iref(nlat,nlon,ngrds)
  integer(i_kind)                    ,intent(in   ) :: jref(nlat,nlon,ngrds)
  real(r_kind)                       ,intent(in   ) :: enscoeff(4,nlat,nlon,ngrds)
  real(r_single),dimension(nx,ny)    ,intent(in   ) :: workin
  real(r_single),dimension(nlat,nlon),intent(inout) :: workout

! Declare local variables
  integer(i_kind) i,j,kg,ii,jj,iip,jjp,nxp
  integer(i_kind) nxs,nxe,nys,nye

  real(r_single),allocatable,dimension(:,:):: tworkin
  real(r_single),allocatable,dimension(:,:):: asmall

!* *****************************************************************************
  select case(igrid)
     case(212); kg=ione
     case(221); kg=2_i_kind
     case(  3); kg=3_i_kind
     case default
        print*,'in fillanlgrd: warning, unknown grid, igrid=',igrid
  end select

! print*,'in fillanlgrd: igrid,nx,ny=',igrid,nx,ny

  nxp=nx
  if (kg ==3_i_kind) nxp=nx+ione

  allocate(tworkin(ny,nxp))

  do i=1,ny
     do j=1,nx
        tworkin(i,j)=workin(j,i)
     enddo
  enddo

  if (kg == 3_i_kind) tworkin(:,nxp)=tworkin(:,1)

  nxs=igbox(3,kg)
  nxe=igbox(4,kg)
  nys=igbox(1,kg)
  nye=igbox(2,kg)

  allocate(asmall(nys:nye,nxs:nxe))

  do i=nys,nye
     do j=nxs,nxe

        ii=iref(i,j,kg)
        jj=jref(i,j,kg)
        iip=min(ii+ione,ny)
        jjp=min(jj+ione,nxp)
        asmall(i,j)=real(enscoeff(1,i,j,kg)*real(tworkin(ii ,jj ),r_kind)+ &
                         enscoeff(2,i,j,kg)*real(tworkin(iip,jj ),r_kind)+ &
                         enscoeff(3,i,j,kg)*real(tworkin(ii ,jjp),r_kind)+ &
                         enscoeff(4,i,j,kg)*real(tworkin(iip,jjp),r_kind),r_single)
     enddo
  enddo

  if (igrid == 3_i_kind) then
     workout(:,:)=asmall(:,:)  !this should be ok. index ranges are the same for this case
  else
     call ens_mirror(asmall,workout,nxs,nxe,nys,nye,nlon,nlat)
  endif

  deallocate(tworkin)
  deallocate(asmall)
end subroutine fillanlgrd
!=======================================================================
!=======================================================================
subroutine ens_mirror(asmall,alarge,nxs,nxe,nys,nye,nxb,nyb)
!-------------------------------------------------------------------------
!    NOAA/NCEP, National Centers for Environmental Prediction GSI        !
!-------------------------------------------------------------------------
!$$$   subprogram documentation block
!                .      .    .                                       .
! subprogram:    mirror
! prgmmr: pondeca          org: np23                date: 2007-03-01
!
! abstract: given a grid alarge that contains the smaller grid asmall,
!           polulate alarge using the values of asmall. simply copy the
!           the field values from asmall to alarge where the two grids
!           overlap. then starting at each boundary of asmall, perform
!           a mirroring of the field and populate the remaining points
!           of alarge.
!
! program history log:
!   2007-03-01  pondeca
!
!   input argument list:
!    nxs,nxe,nys,nye
!    nxb,nyb
!    asmall
!    alarge
!
!   output argument list:
!    alarge
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

       use kinds, only: i_kind,r_single
       use constants, only: ione,zero_single

       implicit none

! Declare passed variables
       integer(i_kind),intent(in   ) :: nxs,nxe,nys,nye
       integer(i_kind),intent(in   ) :: nxb,nyb

       real(r_single) ,intent(in   ) :: asmall(nys:nye,nxs:nxe)
       real(r_single) ,intent(inout) :: alarge(1:nyb,1:nxb)

! Declare local variables
       integer(i_kind) i,j,m,n,k

!==>   use values from small domain to populate big domain where
!      there is overlapping

       alarge(:,:)=zero_single
       do j=nxs,nxe
          do i=nys,nye
             alarge(i,j)=asmall(i,j)
          enddo
       enddo

!==> start by mirroring field in the i-direction

       do j=nxs,nxe
          do m=nye+ione,nyb
             k=(m-nye-ione)/(nye-nys)
             n=2*(nye+k*(nye-nys))-m
             alarge(m,j)=alarge(n,j)
          enddo

          do m=nys-ione,1,-1
             k=(nys-m-ione)/(nye-nys)
             n=2*(nys-k*(nye-nys))-m
             alarge(m,j)=alarge(n,j)
          enddo
       enddo

!==> fill out remaining part of large grid

       do i=1,nyb
          do m=nxs-ione,1,-1
             k=(nxs-m-ione)/(nxe-nxs)
             n=2*(nxs-k*(nxe-nxs))-m
             alarge(i,m)=alarge(i,n)
          enddo

          do m=nxe+ione,nxb
             k=(m-nxe-ione)/(nxe-nxs)
             n=2*(nxe+k*(nxe-nxs))-m
             alarge(i,m)=alarge(i,n)
          enddo
       enddo

end subroutine ens_mirror
!=======================================================================
subroutine pges_minmax(mype,pmin,pmax)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    pges_minmax
!   prgmmr: pondeca          org: np22                date: 2007-04-05
!
! abstract: computes vertical profiles of minimum an maximum layer
!           values of the guess pressure field. values in hPa
!
! program history log:
!   2007-04-05  pondeca
!   2010-04-01  treadon - move strip to gridmod
!
!   input argument list:
!     mype     - mpi task id
!
!   output argument list:
!     pmin(nsig) - vertical profile of layer minimum guess pressure value
!     pmax(nsig) - vertical profile of layer maximum guess pressure value
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentation block
  use kinds, only: i_kind, r_kind
  use constants, only: izero,ione
  use gridmod, only: lat1,lon1,nsig,strip
  use guess_grids, only: ges_prsl
  use mpimod,only: mpi_real8, mpi_min, mpi_max, mpi_comm_world
  implicit none

! Declare passed variables
  integer(i_kind),intent(in   ) :: mype
  real(r_kind)   ,intent(  out) :: pmin(nsig),pmax(nsig)

! Declare local variables
  integer(i_kind):: k,ierror
  real(r_kind):: p3d(lat1,lon1,nsig)
  real(r_kind):: p1,p2

  call strip(ges_prsl,p3d,nsig)

  do k=1,nsig
     p1=minval(p3d(:,:,k))
     p2=maxval(p3d(:,:,k))
     call mpi_allreduce(p1,pmin(k),ione,mpi_real8,mpi_min,mpi_comm_world,ierror)
     call mpi_allreduce(p2,pmax(k),ione,mpi_real8,mpi_max,mpi_comm_world,ierror)
  enddo

  pmin(:)=pmin(:)*10._r_kind
  pmax(:)=pmax(:)*10._r_kind

  if (mype==izero) then
     do k=1,nsig
        print*,'in pges_minmax,k,pmin(k),pmax(k)=',k,pmin(k),pmax(k)
     enddo
  endif

end subroutine pges_minmax
!=======================================================================
!=======================================================================
subroutine check_32primes(n,lprime)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    check_32primes
!   pgrmmr:
!
! abstract: check to see i n has only primes of 3 and 2
!
! program history log:
!   2009-09-15  lueken - added subprogram doc block
!
!   input argument list:
!    n
!    lprime
!
!   output argument list:
!    lprime
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: i_kind
  use constants, only: izero,ione
  implicit none

  integer(i_kind),intent(in   ) :: n
  logical        ,intent(inout) :: lprime

  integer(i_kind) nn,nfax,ii

  lprime=.false.
  nfax = izero
  nn=n

!
! check for factors of 3
!
  do 10 ii = 1,20
     if (nn==3*(nn/3)) then
        nfax = nfax+ione
        nn = nn/3
     else
        go to 20
     end if
10 continue
20 continue
!
! check for factors of 2
!
  do 30 ii = nfax+ione,20
     if (nn==2*(nn/2)) then
        nfax = nfax +ione
        nn = nn/2
     else
        go to 40
     end if
30 continue
40 continue
  if (nn==ione) lprime=.true.

  return

end subroutine check_32primes
!=======================================================================
!=======================================================================
subroutine intp_spl(xi,yi,xo,yo,ni,no)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    intp_spl
!   prgmmr: sato             org: np22                date: 2008-03-31
!
! abstract: 3rd-order spline inter/extra-polation
!
! program history log:
!   2008-03-31  sato
!
!   input argument list:
!     xi - input x values
!     yi - input y values
!     xo - x valurs for y output
!     ni - input dimension
!     no - output dimension
!
!   output argument list:
!     yo - output y values
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentation block
  use kinds, only: i_kind, r_kind, r_single
  use constants, only: zero,two,three,ione
  implicit none

  integer(i_kind),intent(in   ) :: ni,no
  real(r_kind)   ,intent(in   ) :: xi(ni)
  real(r_kind)   ,intent(in   ) :: yi(ni)
  real(r_kind)   ,intent(in   ) :: xo(no)

  real(r_kind)   ,intent(  out) :: yo(no)

  real(r_kind),dimension(ni):: hi,bi,di,gi,ui,ri,pi,qi,si
  real(r_kind):: xe
  integer(i_kind):: k,kk,k0

  do k=1,ni-ione
     hi(k)=xi(k+ione)-xi(k)
  end do

  do k=2,ni-ione
     bi(k)=two*(hi(k)+hi(k-ione))
     di(k)=three*((yi(k+ione)-yi(k))/hi(k)-(yi(k)-yi(k-ione))/hi(k-ione))
  end do

  gi(2)=hi(2)/bi(2)
  do k=3,ni-ione
     gi(k)=hi(k)/(bi(k)-hi(k-ione)*gi(k-ione))
  end do

  ui(2)=di(2)/bi(2)
  do k=3,ni-ione
     ui(k)=(di(k)-hi(k-ione)*ui(k-ione))/(bi(k)-hi(k-ione)*gi(k-ione))
  end do

  ri(ni)=zero
  do k=ni-ione,2,-ione
     ri(k)=ui(k)-gi(k)*ri(k+ione)
  end do
  ri(1)=zero

  do k=1,ni-ione
     pi(k)=yi(k)
     qi(k)=(yi(k+ione)-yi(k))/hi(k)-hi(k)*(ri(k+ione)+two*ri(k))/three
     si(k)=(ri(k+ione)-ri(k))/(three*hi(k))
  end do

  do kk=1,no
     yo(kk)=huge(yo(kk))

! NOTE
! The lower extrapolation must be not so far from 1000mb. -> Extrapolation
! But the upper extrapolation might be far from the top level for Q. -> Use top end values
!
     if     (xo(kk)>=xi(1) ) then
!       yo(kk)=yi(1)  ! use the end value
        k0=ione       ! extrapolation
     else if(xo(kk)<=xi(ni)) then
        yo(kk)=yi(ni) ! use the end value
!       k0=ni-ione    ! extrapolation
     else
        do k=1,ni-ione
           if( xo(kk)<xi(k) .and. xo(kk)>=xi(k+ione) ) then
              k0=k
              exit
           end if
        end do
     end if
     if(yo(kk)==huge(yo(kk))) then
        xe=xo(kk)-xi(k0)
        yo(kk)=pi(k0)+qi(k0)*xe+ri(k0)*xe**2+si(k0)*xe**3
     end if
  end do
end subroutine intp_spl

subroutine ens_fill(ur,na,nb,u,nxx,ny,itap,no_wgt_in)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    ens_fill
!   prgmmr:
!
! abstract:
!
! program history log:
!   2009-09-15  lueken - added subprogram doc block
!
!   input argument list:
!    no_wgt_in
!    nxx,ny,na,nb,itap
!    u
!
!   output argument list:
!    ur
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: i_kind, r_single, r_kind
  use constants, only: zero, half, one, four, ione
  implicit none

  integer(i_kind),intent(in   ) :: nxx,ny,na,nb,itap
  real(r_single) ,intent(in   ) :: u(nxx,ny)
  real(r_single) ,intent(inout) :: ur(na,nb)
  logical        ,intent(in   ) :: no_wgt_in

  real(r_kind):: wt(itap)
  real(r_kind):: pionp1,xi
  integer(i_kind)::i,j,im,jm,ip,jp
  integer(i_kind)::nah,nbh,naq,nbq,ndx,ndxh,ndy,ndyh,ndj,ndi,ndip,ndjp,ndipx
  logical:: no_wgt

  no_wgt=.false.
  if(no_wgt_in) no_wgt=.true.

  pionp1=four*atan(one)/float(itap+ione)

  do i=1,itap
     xi=float(i)
     wt(i)=half+half*cos(pionp1*xi)
  enddo

  if(no_wgt) wt=one

  nah=na/2; nbh=nb/2
  naq=na/4; nbq=nb/4

  ndx =nah-nxx
  ndxh=ndx/2
  ndy =nbh-ny
  ndyh=ndy/2

  ur=zero

  ndj=nbq+ndyh
  ndi=naq+ndxh

!!!!!!! inner !!!!!
  do j=1,ny
     jp=j+ndj
     do i=1,nxx
        ip=i+ndi
        ur(ip,jp)= u(i,j)
     end do
  end do

!!!!!!! lower !!!!!
  ndjp=ndj+ione
  do j=1,itap
     jm=ndjp-j
     do i=ndi+ione,ndi+nxx
        ur(i,jm)=ur(i,ndjp)*wt(j)
     end do
  end do

!!!!!!! upper !!!!!
  ndjp=ndj+ny
  do j=1,itap
     jp=ndjp+j
     do i=ndi+ione,ndi+nxx
        ur(i,jp)=ur(i,ndjp)*wt(j)
     end do
  end do

!!!!!!! left+right  !!!!!
  ndip=ndi+ione
  ndipx=ndi+nxx
  do j=ndj+ione-itap,ndj+ny+itap
     do i=1,itap
        im=ndip-i
        ur(im,j)=ur(ndip ,j)*wt(i)
     end do
     do i=1,itap
        ip=ndipx+i
        ur(ip,j)=ur(ndipx,j)*wt(i)
     end do
  end do

  return
end subroutine ens_fill

subroutine ens_unfill(ur,na,nb,u,nxx,ny)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    ens_unfill
!   prgmmr:
!
! abstract:
!
! program history list:
!   2009-09-15  lueken - added subprogram doc block
!
!   input argument list:
!    na,nb,nxx,ny
!    ur
!
!   output argument list:
!    u
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: i_kind, r_single, r_kind
  implicit none

  integer(i_kind),intent(in   ) :: na,nb,nxx,ny
  real(r_single) ,intent(inout) :: u(nxx,ny)
  real(r_single) ,intent(in   ) :: ur(na,nb)

  integer(i_kind)::i,j,ip,jp
  integer(i_kind)::nah,nbh,naq,nbq,ndx,ndxh,ndy,ndyh,ndj,ndi

  nah=na/2; nbh=nb/2
  naq=na/4; nbq=nb/4

  ndx =nah-nxx
  ndxh=ndx/2
  ndy =nbh-ny
  ndyh=ndy/2

  ndj=nbq+ndyh
  ndi=naq+ndxh

  do j=1,ny
     jp=j+ndj
     do i=1,nxx
        ip=i+ndi
        u(i,j)=ur(ip,jp)
     end do
  end do

  return
end subroutine ens_unfill

end module aniso_ens_util