module general_tll2xy_mod
!$$$   module documentation block
!                .      .    .                                       .
! module:    generic_tll2xy_mod  generalized conversion from latlon to xy
!   prgmmr: parrish          org: np22                date: 2010-10-28
!
! abstract: This module contains generalized tll2xy and related routines to convert
!             earth lat lon coordinates to grid coordinates for an orthogonal
!             non-staggered grid for which the earth lat and lon is known for each grid point.
!            NOTE: all routines tested against same routines in gridmod, using region_lat, region_lon first,
!                then random x,y,ug,vg.  get exact match between gridmod routines and the corresponding
!                  general_ routines here.  See subroutine test1_general_ll2xy at end of this module.
!
! program history log:
!   2010-10-28  parrish - initial documentation
!   2013-01-23  parrish - modify so calls to ll2rpolar and rpolar2ll avoid type mismatch error
!                           when using WCOSS intel debug compile.
!
! subroutines included:
!   sub general_create_llxy_transform - initialize type(llxy_cons) for desired grid
!   sub general_tll2xy                - convert from lat-lon to grid coordinates for desired grid
!   sub general_txy2ll                - convert from grid coordinates to lat-lon for desired grid
!   sub general_rotate_wind_ll2xy     - rotate earth wind to grid wind for desired grid
!   sub general_rotate_wind_xy2ll     - rotate grid wind to earth wind for desired grid
!
! Variable Definitions:
!   def llxy_cons:                    - type variable which is used to make parameter set previously
!                                         hardwired in gridmod.f90 portable for arbitrary grids.
!
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentation block

   use kinds, only: r_kind,i_kind,i_byte

   implicit none

! set default to private
   private
! set subroutines to public
   public :: general_create_llxy_transform
   public :: general_tll2xy
   public :: general_txy2ll
   public :: general_rotate_wind_ll2xy
   public :: general_rotate_wind_xy2ll
! set passed variables to public
   public :: llxy_cons

   type llxy_cons
! The following is for the generalized transform
      real(r_kind) rlon_min_dd,rlon_max_dd,rlat_min_dd,rlat_max_dd
      real(r_kind) pihalf,sign_pole,rlambda0
      real(r_kind) atilde_x,btilde_x,atilde_y,btilde_y
      real(r_kind) btilde_xinv,btilde_yinv
      integer(i_kind) nlon,nlat,nxtilde,nytilde
      integer(i_kind),pointer::i0_tilde(:,:) => NULL()
      integer(i_kind),pointer::j0_tilde(:,:) => NULL()
      integer(i_byte),pointer::ip_tilde(:,:) => NULL()
      integer(i_byte),pointer::jp_tilde(:,:) => NULL()
      real(r_kind),pointer::xtilde0(:,:) => NULL()
      real(r_kind),pointer::ytilde0(:,:) => NULL()
      real(r_kind),pointer::cos_beta_ref(:,:) => NULL()
      real(r_kind),pointer::sin_beta_ref(:,:) => NULL()
      real(r_kind),pointer::region_lat(:,:) => NULL()
      real(r_kind),pointer::region_lon(:,:) => NULL()
      logical:: lallocated = .false.

   end type llxy_cons

!  other declarations  ...

   contains

 subroutine general_create_llxy_transform(region_lat,region_lon,nlat,nlon,mype,gt)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    general_create_llxy_transform
!   prgmmr:  parrish
!
! abstract:  copy routines from gridmod.f90 to make a general purpose module which allows
!     conversion of earth lat lons to grid coordinates for any non-staggered rectangular 
!     orthogonal grid with known earth lat and lon of each grid point.
!     set up constants to allow conversion between earth lat lon and analysis grid units.
!     There is no need to specify details of the analysis grid projection.  All that is required
!     is the earth latitude and longitude in radians of each analysis grid point.
!
! program history log:
!   2010-10-28  parrish - initial documentation
!   2012-11-28  tong - added gt%lallocated=.true. after arrays of gt are allocated and 
!                      removed the duplicate allocation of gt%region_lat,gt%region_lon
!                      at the begining
!
!   input argument list:
!    glats       - earth latitudes of each grid point for desired grid.
!    glons       - earth longitudes of each grid point for desired grid.
!    mype        - mpi task id
!
!   output argument list:
!    gt          - variable of type llxy_cons, which contains all information needed for 
!                   conversion between earth lat lon and this grid grid units.
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: r_kind,i_kind
  use constants, only: zero,one,half,pi
  implicit none

  integer(i_kind),intent(in   ) :: nlat,nlon
  real(r_kind)   ,intent(in   ) :: region_lat(nlat,nlon),region_lon(nlat,nlon)
  integer(i_kind),intent(in   ) :: mype
  type(llxy_cons),intent(inout) :: gt

  real(r_kind),parameter:: rbig =1.0e30_r_kind
  real(r_kind) xbar_min,xbar_max,ybar_min,ybar_max
  real(r_kind) clon,slon,r_of_lat,xbar,ybar
  integer(i_kind) i,j,istart0,iend,iinc,itemp,ilast,jlast
  real(r_kind) glats(nlon,nlat),glons(nlon,nlat)
  real(r_kind),allocatable:: clata(:,:),slata(:,:),clona(:,:),slona(:,:)
  real(r_kind) clat0,slat0,clon0,slon0
  real(r_kind) clat_m1,slat_m1,clon_m1,slon_m1
  real(r_kind) clat_p1,slat_p1,clon_p1,slon_p1
  real(r_kind) x,y,z,xt,yt,zt,xb,yb,zb
  real(r_kind) rlonb_m1,clonb_m1,slonb_m1
  real(r_kind) rlonb_p1,clonb_p1,slonb_p1
  real(r_kind) crot,srot

  do j=1,nlon
     do i=1,nlat
        glats(j,i)=region_lat(i,j)
        glons(j,i)=region_lon(i,j)
     end do
  end do
  gt%pihalf=half*pi
  gt%nlon=nlon
  gt%nlat=nlat
  gt%rlon_min_dd=one
  gt%rlat_min_dd=one
  gt%rlon_max_dd=nlon
  gt%rlat_max_dd=nlat

!  define xtilde, ytilde grid, transform

!      glons,glats are lons, lats of input grid points of dimension nlon,nlat
  call general_get_xytilde_domain(gt,gt%nlon,gt%nlat,glons,glats,gt%nxtilde,gt%nytilde, &
                   xbar_min,xbar_max,ybar_min,ybar_max)
  if(gt%lallocated) then
     deallocate(gt%i0_tilde,gt%j0_tilde,gt%ip_tilde,gt%jp_tilde,gt%xtilde0,gt%ytilde0)
     deallocate(gt%cos_beta_ref,gt%sin_beta_ref,gt%region_lat,gt%region_lon)
     gt%lallocated=.false.
  end if
  allocate(gt%i0_tilde(gt%nxtilde,gt%nytilde),gt%j0_tilde(gt%nxtilde,gt%nytilde))
  allocate(gt%ip_tilde(gt%nxtilde,gt%nytilde),gt%jp_tilde(gt%nxtilde,gt%nytilde))
  allocate(gt%xtilde0(gt%nlon,gt%nlat),gt%ytilde0(gt%nlon,gt%nlat))
  allocate(gt%cos_beta_ref(gt%nlon,gt%nlat),gt%sin_beta_ref(gt%nlon,gt%nlat))
  allocate(gt%region_lat(gt%nlat,gt%nlon),gt%region_lon(gt%nlat,gt%nlon))

  gt%lallocated=.true.

  do j=1,nlon
     do i=1,nlat
        gt%region_lat(i,j)=region_lat(i,j)
        gt%region_lon(i,j)=region_lon(i,j)
     end do
  end do

! define atilde_x, btilde_x, atilde_y, btilde_y

  gt%btilde_x   =(gt%nxtilde -one     )/(xbar_max-xbar_min)
  gt%btilde_xinv=(xbar_max-xbar_min)/(gt%nxtilde -one     )
  gt%atilde_x   =one-gt%btilde_x*xbar_min
  gt%btilde_y   =(gt%nytilde -one     )/(ybar_max-ybar_min)
  gt%btilde_yinv=(ybar_max-ybar_min)/(gt%nytilde -one     )
  gt%atilde_y   =one-gt%btilde_y*ybar_min

! define xtilde0,ytilde0
  do j=1,gt%nlat
     do i=1,gt%nlon
        r_of_lat=gt%pihalf+gt%sign_pole*glats(i,j)
        clon=cos(glons(i,j)+gt%rlambda0)
        slon=sin(glons(i,j)+gt%rlambda0)
        xbar=r_of_lat*clon
        ybar=r_of_lat*slon
        gt%xtilde0(i,j)=gt%atilde_x+gt%btilde_x*xbar
        gt%ytilde0(i,j)=gt%atilde_y+gt%btilde_y*ybar
     end do
  end do

!  now get i0_tilde, j0_tilde, ip_tilde,jp_tilde
  ilast=1 ; jlast=1
  istart0=gt%nxtilde
  iend=1
  iinc=-1
  do j=1,gt%nytilde
     itemp=istart0
     istart0=iend
     iend=itemp
     iinc=-iinc
     ybar=j
     do i=istart0,iend,iinc
        xbar=i
        call general_nearest_3(ilast,jlast,gt%i0_tilde(i,j),gt%j0_tilde(i,j), &
                       gt%ip_tilde(i,j),gt%jp_tilde(i,j),xbar,ybar,gt%nlon,gt%nlat,gt%xtilde0,gt%ytilde0)
     end do
  end do

!   new, more accurate and robust computation of cos_beta_ref and sin_beta_ref which is independent
!     of sign_pole and works for any orientation of grid on sphere (only restriction for now is that
!     x-y coordinate of analysis grid is right handed).
  allocate(clata(gt%nlon,gt%nlat),slata(gt%nlon,gt%nlat),clona(gt%nlon,gt%nlat),slona(gt%nlon,gt%nlat))
  do j=1,gt%nlat
     do i=1,gt%nlon
        clata(i,j)=cos(glats(i,j))
        slata(i,j)=sin(glats(i,j))
        clona(i,j)=cos(glons(i,j))
        slona(i,j)=sin(glons(i,j))
     end do
  end do
  do j=1,gt%nlat
     do i=2,gt%nlon-1

!     do all interior lon points to 2nd order accuracy

!   transform so pole is at rlat0,rlon0 and 0 meridian is tangent to earth latitude at rlat0,rlon0.

        clat0=clata(i,j) ; slat0=slata(i,j) ; clon0=clona(i,j) ; slon0=slona(i,j)

!    now obtain new coordinates for m1 and p1 points.

        clat_m1=clata(i-1,j) ; slat_m1=slata(i-1,j) ; clon_m1=clona(i-1,j) ; slon_m1=slona(i-1,j)
        clat_p1=clata(i+1,j) ; slat_p1=slata(i+1,j) ; clon_p1=clona(i+1,j) ; slon_p1=slona(i+1,j)

        x=clat_m1*clon_m1 ; y=clat_m1*slon_m1 ; z=slat_m1
        xt=x*clon0+y*slon0 ; yt=-x*slon0+y*clon0 ; zt=z
        yb=zt*clat0-xt*slat0
        xb=yt
        zb=xt*clat0+zt*slat0

        rlonb_m1=atan2(-yb,-xb)   !  the minus signs here are so line for m1 is directed same
        clonb_m1=cos(rlonb_m1)
        slonb_m1=sin(rlonb_m1)

        x=clat_p1*clon_p1 ; y=clat_p1*slon_p1 ; z=slat_p1
        xt=x*clon0+y*slon0 ; yt=-x*slon0+y*clon0 ; zt=z
        yb=zt*clat0-xt*slat0
        xb=yt
        zb=xt*clat0+zt*slat0
        rlonb_p1=atan2(yb,xb)
        clonb_p1=cos(rlonb_p1)
        slonb_p1=sin(rlonb_p1)
        crot=half*(clonb_m1+clonb_p1)
        srot=half*(slonb_m1+slonb_p1)
        gt%cos_beta_ref(i,j)=crot*clon0-srot*slon0
        gt%sin_beta_ref(i,j)=srot*clon0+crot*slon0
     end do
!               now do i=1 and i=gt%nlon at 1st order accuracy
     i=1

!   transform so pole is at rlat0,rlon0 and 0 meridian is tangent to earth latitude at rlat0,rlon0.

        clat0=clata(i,j) ; slat0=slata(i,j) ; clon0=clona(i,j) ; slon0=slona(i,j)
!    now obtain new coordinates for m1 and p1 points.

        clat_p1=clata(i+1,j) ; slat_p1=slata(i+1,j) ; clon_p1=clona(i+1,j) ; slon_p1=slona(i+1,j)

        x=clat_p1*clon_p1 ; y=clat_p1*slon_p1 ; z=slat_p1
        xt=x*clon0+y*slon0 ; yt=-x*slon0+y*clon0 ; zt=z
        yb=zt*clat0-xt*slat0
        xb=yt
        zb=xt*clat0+zt*slat0
        rlonb_p1=atan2(yb,xb)
        clonb_p1=cos(rlonb_p1)
        slonb_p1=sin(rlonb_p1)
        crot=clonb_p1
        srot=slonb_p1
        gt%cos_beta_ref(i,j)=crot*clon0-srot*slon0
        gt%sin_beta_ref(i,j)=srot*clon0+crot*slon0

     i=gt%nlon

!   transform so pole is at rlat0,rlon0 and 0 meridian is tangent to earth latitude at rlat0,rlon0.

        clat0=clata(i,j) ; slat0=slata(i,j) ; clon0=clona(i,j) ; slon0=slona(i,j)

!    now obtain new coordinates for m1 and p1 points.

        clat_m1=clata(i-1,j) ; slat_m1=slata(i-1,j) ; clon_m1=clona(i-1,j) ; slon_m1=slona(i-1,j)

        x=clat_m1*clon_m1 ; y=clat_m1*slon_m1 ; z=slat_m1
        xt=x*clon0+y*slon0 ; yt=-x*slon0+y*clon0 ; zt=z
        yb=zt*clat0-xt*slat0
        xb=yt
        zb=xt*clat0+zt*slat0

        rlonb_m1=atan2(-yb,-xb)   !  the minus signs here are so line for m1 is directed same
        clonb_m1=cos(rlonb_m1)
        slonb_m1=sin(rlonb_m1)

        crot=clonb_m1
        srot=slonb_m1
        gt%cos_beta_ref(i,j)=crot*clon0-srot*slon0
        gt%sin_beta_ref(i,j)=srot*clon0+crot*slon0
  end do
  deallocate(clata,slata,clona,slona)

end subroutine general_create_llxy_transform

subroutine general_tll2xy(gt,rlon,rlat,x,y,outside)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    general_tll2xy
!   prgmmr:  parrish
!
! abstract:  copy routines from gridmod.f90 to make a general purpose module which allows
!     conversion of earth lat lons to grid coordinates for any non-staggered rectangular 
!     orthogonal grid with known earth lat and lon of each grid point.
!     general_tll2xy converts earth lon-lat to x-y grid units of a
!           general regional rectangular domain.  Also, decides if
!           point is inside this domain.  As a result, there is
!           no restriction on type of horizontal coordinate for
!           a regional run, other than that it not have periodicity
!           or polar singularities.
!           This is done by first converting rlon, rlat to an
!           intermediate coordinate xtilde,ytilde, which has
!           precomputed pointers and constants for final conversion
!           to the desired x,y via 3 point inverse interpolation.
!           All of the information needed is derived from arrays
!           specifying earth latitude and longitude of every point
!           on the input grid.  Currently, the input x-y grid that
!           this is based on must be non-staggered.  This restriction
!           will eventually be lifted so we can run directly from
!           model grids that are staggered without first resorting
!           to interpolation of the guess to a non-staggered grid.
!
! program history log:
!   2010-10-28  parrish - initial documentation
!
!   input argument list:
!    gt          - type(llxy_cons) contains transform information to convert lat,lon to grid units x,y
!    rlon,rlat   - earth longitude and latitude (radians)
!
!   output argument list:
!    x,y         - grid coordinates in grid units.
!    outside     - if .false., then point is inside grid domain
!                    otherwise point is outside domain.
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

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

    type(llxy_cons),intent(in) :: gt
    real(r_kind),intent(in   ) :: rlon
    real(r_kind),intent(in   ) :: rlat
    real(r_kind),intent(  out) :: x
    real(r_kind),intent(  out) :: y
    logical     ,intent(  out) :: outside

    real(r_kind) clon,slon,r_of_lat,xtilde,ytilde
    real(r_kind) dtilde,etilde
    real(r_kind) d1tilde,d2tilde,e1tilde,e2tilde,detinv
    integer(i_kind) itilde,jtilde
    integer(i_kind) i0,j0,ip,jp

!   first compute xtilde, ytilde

    clon=cos(rlon+gt%rlambda0)
    slon=sin(rlon+gt%rlambda0)
    r_of_lat=gt%pihalf+gt%sign_pole*rlat

    xtilde=gt%atilde_x+gt%btilde_x*r_of_lat*clon
    ytilde=gt%atilde_y+gt%btilde_y*r_of_lat*slon

!  next get interpolation information

    itilde=max(1,min(nint(xtilde),gt%nxtilde))
    jtilde=max(1,min(nint(ytilde),gt%nytilde))

    i0     =   gt%i0_tilde(itilde,jtilde)
    j0     =   gt%j0_tilde(itilde,jtilde)
    ip     =i0+gt%ip_tilde(itilde,jtilde)
    jp     =j0+gt%jp_tilde(itilde,jtilde)
    dtilde =xtilde-gt%xtilde0(i0,j0)
    etilde =ytilde-gt%ytilde0(i0,j0)
    d1tilde=(gt%xtilde0(ip,j0)-gt%xtilde0(i0,j0))*(ip-i0)
    d2tilde=(gt%xtilde0(i0,jp)-gt%xtilde0(i0,j0))*(jp-j0)
    e1tilde=(gt%ytilde0(ip,j0)-gt%ytilde0(i0,j0))*(ip-i0)
    e2tilde=(gt%ytilde0(i0,jp)-gt%ytilde0(i0,j0))*(jp-j0)
    detinv =one/(d1tilde*e2tilde-d2tilde*e1tilde)
    x = i0+detinv*(e2tilde*dtilde-d2tilde*etilde)
    y = j0+detinv*(d1tilde*etilde-e1tilde*dtilde)

    outside=x < gt%rlon_min_dd .or. x > gt%rlon_max_dd .or. &
            y < gt%rlat_min_dd .or. y > gt%rlat_max_dd

end subroutine general_tll2xy

subroutine general_txy2ll(gt,x,y,rlon,rlat)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    general_txy2ll
!   prgmmr:  parrish
!
! abstract: convert x-y grid units to earth lat-lon coordinates
!
! program history log:
!   2010-10-28 parrish - initial documentation
!
!   input argument list:
!    gt          - type(llxy_cons) contains transform information to convert x,y to lat,lon
!    x,y         - grid coordinates in grid units.
!
!   output argument list:
!    rlon,rlat   - earth longitude and latitude (radians)
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

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

    type(llxy_cons),intent(in) :: gt
    real(r_kind),intent(in   ) :: x
    real(r_kind),intent(in   ) :: y
    real(r_kind),intent(  out) :: rlon
    real(r_kind),intent(  out) :: rlat

    real(r_kind) r_of_lat,xtilde,ytilde
    real(r_kind) dtilde,etilde,xbar,ybar
    real(r_kind) d1tilde,d2tilde,e1tilde,e2tilde
    integer(i_kind) i0,j0,ip,jp

    i0=nint(x)
    j0=nint(y)
    i0=max(1,min(i0,gt%nlon))
    j0=max(1,min(j0,gt%nlat))
    ip=i0+nint(sign(one,x-i0))
    jp=j0+nint(sign(one,y-j0))
    if(ip<1) then
       i0=2
       ip=1
    end if
    if(jp<1) then
       j0=2
       jp=1
    end if
    if(ip>gt%nlon) then
       i0=gt%nlon-1
       ip=gt%nlon
    end if
    if(jp>gt%nlat) then
       j0=gt%nlat-1
       jp=gt%nlat
    end if
    d1tilde=(gt%xtilde0(ip,j0)-gt%xtilde0(i0,j0))*(ip-i0)
    d2tilde=(gt%xtilde0(i0,jp)-gt%xtilde0(i0,j0))*(jp-j0)
    e1tilde=(gt%ytilde0(ip,j0)-gt%ytilde0(i0,j0))*(ip-i0)
    e2tilde=(gt%ytilde0(i0,jp)-gt%ytilde0(i0,j0))*(jp-j0)
    dtilde =d1tilde*(x-i0) +d2tilde*(y-j0)
    etilde =e1tilde*(x-i0) +e2tilde*(y-j0)
    xtilde =dtilde         +gt%xtilde0(i0,j0)
    ytilde =etilde         +gt%ytilde0(i0,j0)

    xbar=(xtilde-gt%atilde_x)*gt%btilde_xinv
    ybar=(ytilde-gt%atilde_y)*gt%btilde_yinv
    r_of_lat=sqrt(xbar**2+ybar**2)
    rlat=(r_of_lat-gt%pihalf)*gt%sign_pole
    rlon=atan2(ybar,xbar)-gt%rlambda0

end subroutine general_txy2ll

subroutine general_nearest_3(ilast,jlast,i0,j0,ip,jp,x,y,nx0,ny0,x0,y0)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    nearest_3
!   prgmmr:
!
! abstract: find closest 3 points to (x,y) on grid defined by x0,y0
!
! program history log:
!   2009-08-04  lueken - added subprogram doc block
!
!   input argument list:
!    ilast,jlast
!    nx0,ny0
!    x,y
!    x0,y0
!
!   output argument list:
!    ilast,jlast
!    i0,j0
!    ip,jp
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

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

  integer(i_kind),intent(inout) :: ilast,jlast
  integer(i_kind),intent(  out) :: i0,j0
  integer(i_byte),intent(  out) :: ip,jp
  integer(i_kind),intent(in   ) :: nx0,ny0
  real(r_kind)   ,intent(in   ) :: x,y
  real(r_kind)   ,intent(in   ) :: x0(nx0,ny0),y0(nx0,ny0)
 
  real(r_kind) dista,distb,dist2,dist2min
  integer(i_kind) i,inext,j,jnext

  do
     i0=ilast
     j0=jlast
     dist2min=huge(dist2min)
     inext=0
     jnext=0
     do j=max(j0-1,1),min(j0+1,ny0)
        do i=max(i0-1,1),min(i0+1,nx0)
           dist2=(x-x0(i,j))**2+(y-y0(i,j))**2
           if(dist2<dist2min) then
              dist2min=dist2
              inext=i
              jnext=j
           end if
        end do
     end do
     if(inext==i0.and.jnext==j0) exit
     ilast=inext
     jlast=jnext
  end do

!  now find which way to go in x for second point

  ip=0
  if(i0==nx0)  ip=-1
  if(i0==1) ip=1
  if(ip==0) then
     dista=(x-x0(i0-1,j0))**2+(y-y0(i0-1,j0))**2
     distb=(x-x0(i0+1,j0))**2+(y-y0(i0+1,j0))**2
     if(distb<dista) then
        ip=1
     else
        ip=-1
     end if
  end if

!  repeat for y for 3rd point

  jp=0
  if(j0==ny0  ) jp=-1
  if(j0==1 ) jp=1
  if(jp==0) then
     dista=(x-x0(i0,j0-1))**2+(y-y0(i0,j0-1))**2
     distb=(x-x0(i0,j0+1))**2+(y-y0(i0,j0+1))**2
     if(distb<dista) then
        jp=1
     else
        jp=-1
     end if
  end if

  ilast=i0
  jlast=j0
    
end subroutine general_nearest_3

subroutine general_get_xytilde_domain(gt,nx0,ny0,rlons0,rlats0, &
                                  nx,ny,xminout,xmaxout,yminout,ymaxout)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    get_xytilde_domain
!   prgmmr:
!
! abstract:
!
! program history log:
!   2009-08-04  lueken - added subprogram doc block
!
!   input argument list:
!    nx0,ny0
!    rlons0,rlats0
!
!   output argument list:
!    nx,ny
!    xminout,xmaxout,yminout,ymaxout
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

   use kinds, only: r_kind,i_kind
   use constants, only: one, deg2rad,half,zero,r10
!  define parameters for xy domain which optimally overlays input grid

  implicit none

  type(llxy_cons),intent(inout) :: gt
  integer(i_kind),intent(in   ) :: nx0,ny0
  real(r_kind)   ,intent(in   ) :: rlons0(nx0,ny0),rlats0(nx0,ny0)

  integer(i_kind),intent(  out) :: nx,ny
  real(r_kind)   ,intent(  out) :: xminout,xmaxout,yminout,ymaxout

  real(r_kind),parameter:: r37=37.0_r_kind

  real(r_kind) area,areamax,areamin,extra,rlats0max,rlats0min,testlambda
  real(r_kind) xthis,ythis
  integer(i_kind) i,ip1,j,jp1,m

  real(r_kind) coslon0(nx0,ny0),sinlon0(nx0,ny0)
  real(r_kind) coslat0(nx0,ny0),sinlat0(nx0,ny0)
  real(r_kind) count,delbar
  real(r_kind) dx,dy,disti,distj,distmin,distmax
  real(r_kind) xmin,xmax,ymin,ymax

!  get range of lats for input grid

  rlats0max=maxval(rlats0) ; rlats0min=minval(rlats0)

!   assign hemisphere ( parameter sign_pole )

  if(rlats0min>-r37*deg2rad) gt%sign_pole=-one   !  northern hemisphere xy domain
  if(rlats0max< r37*deg2rad) gt%sign_pole= one   !  southern hemisphere xy domain


!   get optimum rotation angle rlambda0

  areamin= huge(areamin)
  areamax=-huge(areamax)
  do m=0,359
     testlambda=m*deg2rad
     xmax=-huge(xmax)
     xmin= huge(xmin)
     ymax=-huge(ymax)
     ymin= huge(ymin)
     do j=1,ny0,ny0-1
        do i=1,nx0
           xthis=(gt%pihalf+gt%sign_pole*rlats0(i,j))*cos(rlons0(i,j)+testlambda)
           ythis=(gt%pihalf+gt%sign_pole*rlats0(i,j))*sin(rlons0(i,j)+testlambda)
           xmax=max(xmax,xthis)
           ymax=max(ymax,ythis)
           xmin=min(xmin,xthis)
           ymin=min(ymin,ythis)
        end do
     end do
     do j=1,ny0
        do i=1,nx0,nx0-1
           xthis=(gt%pihalf+gt%sign_pole*rlats0(i,j))*cos(rlons0(i,j)+testlambda)
           ythis=(gt%pihalf+gt%sign_pole*rlats0(i,j))*sin(rlons0(i,j)+testlambda)
           xmax=max(xmax,xthis)
           ymax=max(ymax,ythis)
           xmin=min(xmin,xthis)
           ymin=min(ymin,ythis)
        end do
     end do
     area=(xmax-xmin)*(ymax-ymin)
     areamax=max(area,areamax)
     if(area<areamin) then
        areamin =area
        gt%rlambda0=testlambda
        xmaxout =xmax
        xminout =xmin
        ymaxout =ymax
        yminout =ymin
     end if
  end do


!   now determine resolution of input grid and choose nx,ny of xy grid accordingly
!                 (currently hard-wired at 1/2 the average input grid increment)

  do j=1,ny0
     do i=1,nx0
        coslon0(i,j)=cos(one*rlons0(i,j)) ; sinlon0(i,j)=sin(one*rlons0(i,j))
        coslat0(i,j)=cos(one*rlats0(i,j)) ; sinlat0(i,j)=sin(one*rlats0(i,j))
     end do
  end do

  delbar=zero
  count =zero
  do j=1,ny0-1
     jp1=j+1
     do i=1,nx0-1
        ip1=i+1
        disti=acos(sinlat0(i,j)*sinlat0(ip1,j)+coslat0(i,j)*coslat0(ip1,j)* &
                  (sinlon0(i,j)*sinlon0(ip1,j)+coslon0(i,j)*coslon0(ip1,j)))
        distj=acos(sinlat0(i,j)*sinlat0(i,jp1)+coslat0(i,j)*coslat0(i,jp1)* &
                  (sinlon0(i,j)*sinlon0(i,jp1)+coslon0(i,j)*coslon0(i,jp1)))
        distmax=max(disti,distj)
        distmin=min(disti,distj)
        delbar=delbar+distmax
        count=count+one
     end do
  end do
  delbar=delbar/count
  dx=half*delbar
  dy=dx

!   add extra space to computational grid to push any boundary problems away from
!     area of interest

  extra=r10*dx
  xmaxout=xmaxout+extra
  xminout=xminout-extra
  ymaxout=ymaxout+extra
  yminout=yminout-extra
  nx=1+(xmaxout-xminout)/dx
  ny=1+(ymaxout-yminout)/dy
 
end subroutine general_get_xytilde_domain

!-------------------------------------------------------------------------
!    NOAA/NCEP, National Centers for Environmental Prediction GSI        !
!-------------------------------------------------------------------------
!BOP
!
! !IROUTINE:  general_rotate_wind_ll2xy ---  Rotate earth vector wind
!
! !INTERFACE:
!
  subroutine general_rotate_wind_ll2xy(gt,u0,v0,u,v,rlon0,x,y)

! !USES:

    use kinds, only: r_kind,i_kind
    use constants, only: one,two,pi,rad2deg,one_tenth
    implicit none

! !INPUT PARAMETERS:

    type(llxy_cons),intent(in) :: gt
    real(r_kind),intent(in   ) :: u0,v0        ! earth wind component
    real(r_kind),intent(in   ) :: rlon0        ! earth   lon (radians)
    real(r_kind),intent(in   ) :: x,y          ! local x,y coordinate (grid units)

! !OUTPUT PARAMETERS:

    real(r_kind),intent(  out) :: u,v          ! rotated coordinate of winds

! !DESCRIPTION: to convert earth vector wind components to corresponding
!           local x,y coordinate
!
! !REVISION HISTORY:
!   2003-09-30  parrish
!   2004-05-13  kleist, documentation
!   2004-07-15  todling, protex-compliant prologue
!   2010-09-08  parrish, remove sign_pole variable--no longer needed, due to more accurate and
!                 robust computation of reference wind rotation angle defined by
!                 cos_beta_ref, sin_beta_ref.
!
! !REMARKS:
!   language: f90
!   machine:  ibm rs/6000 sp; SGI Origin 2000; Compaq/HP
!
! !AUTHOR:
!   parrish          org: np22                date: 2003-09-30
!
!EOP
!-------------------------------------------------------------------------

  real(r_kind) beta,delx,delxp,dely,delyp
  real(r_kind) sin_beta,cos_beta
  integer(i_kind) ix,iy

!  interpolate departure from longitude part of angle between earth positive east and local positive x

  ix=x
  iy=y
  ix=max(1,min(ix,gt%nlon-1))
  iy=max(1,min(iy,gt%nlat-1))
  delx=x-ix
  dely=y-iy
  delxp=one-delx
  delyp=one-dely
  cos_beta=gt%cos_beta_ref(ix  ,iy  )*delxp*delyp+gt%cos_beta_ref(ix+1,iy  )*delx *delyp+ &
           gt%cos_beta_ref(ix  ,iy+1)*delxp*dely +gt%cos_beta_ref(ix+1,iy+1)*delx *dely
  sin_beta=gt%sin_beta_ref(ix  ,iy  )*delxp*delyp+gt%sin_beta_ref(ix+1,iy  )*delx *delyp+ &
           gt%sin_beta_ref(ix  ,iy+1)*delxp*dely +gt%sin_beta_ref(ix+1,iy+1)*delx *dely
  beta=atan2(sin_beta,cos_beta)

!  now rotate;

  u= u0*cos(beta-rlon0)+v0*sin(beta-rlon0)
  v=-u0*sin(beta-rlon0)+v0*cos(beta-rlon0)

end subroutine general_rotate_wind_ll2xy

!-------------------------------------------------------------------------
!    NOAA/NCEP, National Centers for Environmental Prediction GSI        !
!-------------------------------------------------------------------------
!BOP
!
! !IROUTINE:  rotate_wind_xy2ll ---  Unrotate earth vector wind
!
! !INTERFACE:
!
  subroutine general_rotate_wind_xy2ll(gt,u,v,u0,v0,rlon0,x,y)

! !USES:

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

! !INPUT PARAMETERS:

    type(llxy_cons),intent(in) :: gt
    real(r_kind),intent(in   ) :: u,v         ! rotated coordinate winds
    real(r_kind),intent(in   ) :: rlon0       ! earth   lon     (radians)
    real(r_kind),intent(in   ) :: x,y         ! rotated lon/lat (radians)

! !OUTPUT PARAMETERS:

    real(r_kind),intent(  out) :: u0,v0       ! earth winds

! !DESCRIPTION: rotate u,v in local x,y coordinate to u0,v0 in earth 
!           lat, lon coordinate
!
! !REVISION HISTORY:
!   2003-09-30  parrish
!   2004-05-13  kleist, documentation
!   2004-07-15  todling, protex-compliant prologue
!   2004-07-20  todling, fixed description
!   2010-09-08  parrish, remove sign_pole variable--no longer needed, due to more accurate and
!                 robust computation of reference wind rotation angle defined by
!                 cos_beta_ref, sin_beta_ref.
!
! !REMARKS:
!   language: f90
!   machine:  ibm rs/6000 sp; SGI Origin 2000; Compaq/HP
!
! !AUTHOR:
!   parrish          org: np22                date: 2003-09-30
!
!EOP
!-------------------------------------------------------------------------
  real(r_kind) beta,delx,delxp,dely,delyp
  real(r_kind) sin_beta,cos_beta
  integer(i_kind) ix,iy

!  interpolate departure from longitude part of angle between earth 
!  positive east and local positive x

  ix=x
  iy=y
  ix=max(1,min(ix,gt%nlon-1))
  iy=max(1,min(iy,gt%nlat-1))
  delx=x-ix
  dely=y-iy
  delxp=one-delx
  delyp=one-dely
  cos_beta=gt%cos_beta_ref(ix  ,iy  )*delxp*delyp+gt%cos_beta_ref(ix+1,iy  )*delx *delyp+ &
           gt%cos_beta_ref(ix  ,iy+1)*delxp*dely +gt%cos_beta_ref(ix+1,iy+1)*delx *dely
  sin_beta=gt%sin_beta_ref(ix  ,iy  )*delxp*delyp+gt%sin_beta_ref(ix+1,iy  )*delx *delyp+ &
           gt%sin_beta_ref(ix  ,iy+1)*delxp*dely +gt%sin_beta_ref(ix+1,iy+1)*delx *dely
  beta=atan2(sin_beta,cos_beta)

!  now rotate;

  u0= u*cos(beta-rlon0)-v*sin(beta-rlon0)
  v0= u*sin(beta-rlon0)+v*cos(beta-rlon0)

end subroutine general_rotate_wind_xy2ll

end module general_tll2xy_mod

subroutine merge_grid_e_to_grid_a_initialize(region_lat_e,region_lon_e,region_lat_a,region_lon_a, &
                                   nlat_e,nlon_e,nlat_a,nlon_a,nord_e2a,nord_blend,nmix,gt_e,gt_a,p_e2a)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    merge_grid_e_to_grid_a_initialize
!   prgmmr:  parrish
!
! abstract:  initialize parameters needed for routines merge_grid_e_to_grid_a and merge_vgrid_e_to_vgrid_a.
!
! program history log:
!   2010-10-28  parrish - initial documentation
!   2012-03-01  tong - modified the way to call create_egrid2points_slow. If nmix <= 0, the orginal 
!               way to call the subroutine will cause segmentation fault
!
!   input argument list:
!    region_lat_e - earth lats for e grid (radians) 
!    region_lon_e - earth lons for e grid (radians)
!    region_lat_a - earth lats for a grid (radians)
!    region_lon_a - earth lons for a grid (radians)
!    nlat_e       - "y" dimension of e grid
!    nlon_e       - "x" dimension of e grid
!    nlat_a       - "y" dimension of a grid
!    nlon_a       - "x" dimension of a grid
!    nord_e2a     - order of accuracy of interpolation from grid e to grid a (1=bilinear, 2=biquadratic, etc)
!    nord_blend   - order of continuity of blend function (1=continuous 1st derivative, etc)
!    nmix         - width of blend zone on edge of e grid in e grid units.
!
!   output argument list:
!    gt_e         - navigation structure variable for grid e
!    gt_a         - navigation structure variable for grid a
!    p_e2a        - structure variable containing interpolation info for interpolation from e grid to a grid
!
! attributes:
!   language: f90
!   machine:
!
!$$$ end documentation block

  use kinds, only: r_kind,i_kind
  use general_tll2xy_mod, only: llxy_cons,general_create_llxy_transform,general_tll2xy
  use egrid2agrid_mod, only: egrid2agrid_parm,create_egrid2points_slow
  use mpimod, only: mype
  use constants, only: zero,one
  implicit none

  type(llxy_cons),intent(inout):: gt_e,gt_a
  type(egrid2agrid_parm),intent(inout) :: p_e2a
  integer(i_kind),intent(in):: nlat_e,nlon_e,nlat_a,nlon_a,nord_e2a,nord_blend,nmix
  real(r_kind),intent(in)::region_lat_e(nlat_e,nlon_e),region_lon_e(nlat_e,nlon_e)
  real(r_kind),intent(in)::region_lat_a(nlat_a,nlon_a),region_lon_a(nlat_a,nlon_a)

  integer(i_kind) i,j,np
  real(r_kind),allocatable::xa_e(:,:),ya_e(:,:),xe(:),ye(:)
  logical outside
  real(r_kind) diffmax,range_lat,range_lon

! define navigation for e grid and a grid

  if(nlat_e == nlat_a .and. nlon_e == nlon_a) then
     range_lat=max(abs(region_lat_a(nlat_a,1)-region_lat_a(1,1)),abs(region_lat_a(nlat_a,nlon_a)-region_lat_a(1,nlon_a)))
     range_lat=max(range_lat,abs(region_lat_a(nlat_a,nlon_a/2)-region_lat_a(1,nlon_a/2)))
     range_lat=max(range_lat,abs(region_lat_e(nlat_e,1)-region_lat_e(1,1)))
     range_lat=max(range_lat,abs(region_lat_e(nlat_e,nlon_e)-region_lat_e(1,nlon_e)))
     range_lat=max(range_lat,abs(region_lat_e(nlat_e,nlon_e/2)-region_lat_e(1,nlon_e/2)))
     if(nlat_a == 1) range_lat=one
     range_lon=max(abs(region_lon_a(1,nlon_a)-region_lon_a(1,1)),abs(region_lon_a(nlat_a,nlon_a)-region_lon_a(nlat_a,1)))
     range_lon=max(range_lon,abs(region_lon_a(nlat_a/2,nlon_a)-region_lon_a(nlat_a/2,1)))
     range_lon=max(range_lon,abs(region_lon_e(1,nlon_e)-region_lon_e(1,1)))
     range_lon=max(range_lon,abs(region_lon_e(nlat_e,nlon_e)-region_lon_e(nlat_e,1)))
     range_lon=max(range_lon,abs(region_lon_e(nlat_e/2,nlon_e)-region_lon_e(nlat_e/2,1)))
     if(nlon_a == 1) range_lon=one
     diffmax=zero
     do j=1,nlon_a
        do i=1,nlat_a
           diffmax=max(diffmax,abs(region_lat_a(i,j)-region_lat_e(i,j))/range_lat)
        end do
     end do
     do j=1,nlon_a
        do i=1,nlat_a
           diffmax=max(diffmax,abs(region_lon_a(i,j)-region_lon_e(i,j))/range_lon)
        end do
     end do
     if(diffmax < .0000001_r_kind)then
        p_e2a%identity=.true.
        return
     end if
  end if

  call general_create_llxy_transform(region_lat_e,region_lon_e,nlat_e,nlon_e,mype,gt_e)
  call general_create_llxy_transform(region_lat_a,region_lon_a,nlat_a,nlon_a,mype,gt_a)

!    obtain xa_e, ya_e, the coordinates of the a grid points in e-grid measure.

  allocate(xa_e(nlat_a,nlon_a),ya_e(nlat_a,nlon_a))
  do j=1,nlon_a
     do i=1,nlat_a
        call general_tll2xy(gt_e,region_lon_a(i,j),region_lat_a(i,j),xa_e(i,j),ya_e(i,j),outside)
     end do
  end do

  allocate(xe(nlon_e),ye(nlat_e))
  do j=1,nlon_e
     xe(j)=j
  end do
  do i=1,nlat_e
     ye(i)=i
  end do
  np=nlat_a*nlon_a
  if(nord_blend > 0 .and. nmix > 0)then
     call create_egrid2points_slow(np,ya_e,xa_e,nlat_e,ye,nlon_e,xe,nord_e2a,p_e2a,nord_blend,nmix)
  else
     call create_egrid2points_slow(np,ya_e,xa_e,nlat_e,ye,nlon_e,xe,nord_e2a,p_e2a)
  end if

  deallocate(xe,ye,xa_e,ya_e)

end subroutine merge_grid_e_to_grid_a_initialize

subroutine merge_grid_e_to_grid_a(e_in,a_in,a_out,gt_e,gt_a,p_e2a)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    merge_grid_e_to_grid_a  merge scalar field from one grid to another
!   prgmmr: parrish          org: np22                date: 2010-11-02
!
! abstract: interpolate input scalar field e_in from e grid to a grid, blend with existing
!             field a_in, and return result in a_out.
!
! program history log:
!   2010-11-02  parrish, initial documentation
!
!   input argument list:
!      e_in:             input field on e-grid
!      a_in:             input field on a-grid
!      gt_e:             structure variable containing navigation info and dimensions for e_in
!      gt_a:             same for a_in, a_out
!      p_e2a:            structure variable containing info for interpolating from e-grid to a-grid.
!
!   output argument list:
!      a_out:            blended field with interpolated e-grid field smoothly replacing
!                           region of a-grid covered by e-grid.
!              
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentation block
  use kinds, only: r_kind,i_kind
  use general_tll2xy_mod, only: llxy_cons
  use egrid2agrid_mod, only: egrid2agrid_parm,egrid2points
  use constants, only: one
  implicit none

  type(llxy_cons),intent(in):: gt_e,gt_a
  type(egrid2agrid_parm),intent(in):: p_e2a
  real(r_kind),intent(in),dimension(gt_e%nlat,gt_e%nlon):: e_in
  real(r_kind),intent(in),dimension(gt_a%nlat,gt_a%nlon):: a_in
  real(r_kind),intent(out),dimension(gt_a%nlat,gt_a%nlon):: a_out

  integer(i_kind) i,ii,j

  call egrid2points(p_e2a,e_in,a_out)
  ii=0
  do j=1,gt_a%nlon
     do i=1,gt_a%nlat
        ii=ii+1
!   blend smoothly with a_in
        a_out(i,j)=p_e2a%blend(ii)*a_out(i,j)+(one-p_e2a%blend(ii))*a_in(i,j)
!        a_out(i,j)=blend(i,j)*a_out(i,j)+(one-blend(i,j))*a_in(i,j)
     end do
  end do

end subroutine merge_grid_e_to_grid_a

subroutine merge_vgrid_e_to_vgrid_a(eu_in,ev_in,au_in,av_in,au_out,av_out,gt_e,gt_a,p_e2a)
!$$$  subprogram documentation block
!                .      .    .                                       .
! subprogram:    merge_vgrid_e_to_vgrid_a  merge vector field from one grid to another
!   prgmmr: parrish          org: np22                date: 2010-11-02
!
! abstract: interpolate input vector field eu_in,ev_in from e grid to a grid, blend with existing
!             vector field au_in, av_in, and return result in au_out,av_out.
!
! program history log:
!   2010-11-02  parrish, initial documentation
!
!   input argument list:
!      eu_in:            input u component on e-grid
!      ev_in:            input v component on e-grid
!      au_in:            input u component on a-grid
!      av_in:            input v component on a-grid
!      gt_e:             structure variable containing navigation info and dimensions for eu_in, ev_in
!      gt_a:             same for au_in, av_in, au_out,av_out
!      p_e2a:            structure variable containing info for interpolating from e-grid to a-grid.
!
!   output argument list:
!      au_out:           blended u component with interpolated e-grid u component smoothly replacing
!                           region of a-grid covered by e-grid.
!      av_out:           same for v component
!              
!
! attributes:
!   language: f90
!   machine:  ibm RS/6000 SP
!
!$$$ end documentation block
  use kinds, only: r_kind,i_kind
  use general_tll2xy_mod, only: llxy_cons,general_rotate_wind_ll2xy,general_rotate_wind_xy2ll
  use egrid2agrid_mod, only: egrid2agrid_parm,egrid2points
  use constants, only: one
  implicit none

  type(llxy_cons),intent(in):: gt_e,gt_a
  type(egrid2agrid_parm),intent(in):: p_e2a
  real(r_kind),intent(in),dimension(gt_e%nlat,gt_e%nlon):: eu_in,ev_in
  real(r_kind),intent(in),dimension(gt_a%nlat,gt_a%nlon):: au_in,av_in
  real(r_kind),intent(out),dimension(gt_a%nlat,gt_a%nlon):: au_out,av_out

  integer(i_kind) i,ii,j
  real(r_kind) xa,ya,au_earth,av_earth

  call egrid2points(p_e2a,eu_in,au_out)
  call egrid2points(p_e2a,ev_in,av_out)
  ii=0
  do j=1,gt_a%nlon
     xa=j
     do i=1,gt_a%nlat
        ya=i
        ii=ii+1
!    rotate vector from e grid orientation to earth lat-lon orientation
        call general_rotate_wind_xy2ll(gt_e,au_out(i,j),av_out(i,j),au_earth,av_earth, &
                                       gt_a%region_lon(i,j),p_e2a%xa_e(ii),p_e2a%ya_e(ii))
!    rotate vector from earth lat-lon orientation to a grid orientation
        call general_rotate_wind_ll2xy(gt_a,au_earth,av_earth,au_out(i,j),av_out(i,j), &
                                       gt_a%region_lon(i,j),xa,ya)
!   blend smoothly with au_in, av_in
        au_out(i,j)=p_e2a%blend(ii)*au_out(i,j)+(one-p_e2a%blend(ii))*au_in(i,j)
        av_out(i,j)=p_e2a%blend(ii)*av_out(i,j)+(one-p_e2a%blend(ii))*av_in(i,j)
!        au_out(i,j)=blend(i,j)*au_out(i,j)+(one-blend(i,j))*au_in(i,j)
!        av_out(i,j)=blend(i,j)*av_out(i,j)+(one-blend(i,j))*av_in(i,j)
     end do
  end do

end subroutine merge_vgrid_e_to_vgrid_a

subroutine test1_general_ll2xy

!  compare subroutines tll2xy with general_tll2xy, etc. also for txy2ll, rotate_wind_xy2ll, rotate_wind_ll2xy
!   using analysis grid defined by region_lon,region_lat, nlon, nlat from gridmod.f90

  use gridmod, only: region_lat,region_lon,nlat,nlon,tll2xy,txy2ll,rotate_wind_xy2ll,rotate_wind_ll2xy
  use general_tll2xy_mod, only: llxy_cons,general_create_llxy_transform,general_tll2xy,general_txy2ll
  use general_tll2xy_mod, only: general_rotate_wind_ll2xy,general_rotate_wind_xy2ll
  use mpimod, only: mype
  use kinds, only: r_kind,i_kind
  use constants, only: zero
  implicit none

  type(llxy_cons) gt_a
  integer(i_kind) i,j
  real(r_kind) grid_lats(nlat,nlon),grid_lons(nlat,nlon)
  real(r_kind) grid_lats_test(nlat,nlon),grid_lons_test(nlat,nlon)
  real(r_kind) rlat,rlon,y,x,rlat_test,rlon_test,errmax_lat,errmax_lon
  real(r_kind) u,v,ug,vg,utest,vtest,ugtest,vgtest,errmax_w,errmax_wg
  real(r_kind) xmax,ymax,wgmax
  logical outside

  call general_create_llxy_transform(region_lat,region_lon,nlat,nlon,mype,gt_a)

!             first convert region_lat,region_lon to grid units for both tll2xy and general_tll2xy
  do j=1,nlon
     do i=1,nlat
        call general_tll2xy(gt_a,region_lon(i,j),region_lat(i,j), &
                            grid_lons_test(i,j),grid_lats_test(i,j),outside)
        call tll2xy(region_lon(i,j),region_lat(i,j),grid_lons(i,j),grid_lats(i,j),outside)
     end do
  end do
!              compare grid_lons_test, grid_lats_test to grid_lons,grid_lats--should be identical
  if(mype==0) then
      write(0,*)' min,max grid_lons=',minval(grid_lons),maxval(grid_lons)
      write(0,*)' min,max grid_lons_test=',minval(grid_lons_test),maxval(grid_lons_test)
      write(0,*)' min,max grid_lats=',minval(grid_lats),maxval(grid_lats)
      write(0,*)' min,max grid_lats_test=',minval(grid_lats_test),maxval(grid_lats_test)
      write(0,*)' max err grid_lons_test=',maxval(abs(grid_lons-grid_lons_test))
      write(0,*)' max err grid_lats_test=',maxval(abs(grid_lats-grid_lats_test))
  end if
  errmax_lat=zero
  errmax_lon=zero
  errmax_w=zero
  errmax_wg=zero
  xmax=zero
  ymax=zero
  wgmax=zero
  
  
!             repeat for random x,y and random ug,vg to also test rotate_wind_xy2ll, rotate_wind_ll2xy
!                  against general_rotate_wind_xy2ll, general_rotate_wind_ll2xy
  do i=1,nlon*nlat
     call random_number(x)
     call random_number(y)
     call random_number(ug)
     call random_number(vg)
     wgmax=max(sqrt(ug**2+vg**2),wgmax)
     x=-4._r_kind+(nlon+8._r_kind)*x
     y=-4._r_kind+(nlat+8._r_kind)*y
     xmax=max(xmax,x)
     ymax=max(ymax,y)
     call general_txy2ll(gt_a,x,y,rlon_test,rlat_test)
     call txy2ll(x,y,rlon,rlat)
     errmax_lat=max(abs(rlat_test-rlat),errmax_lat)
     errmax_lon=max(abs(rlon_test-rlon),errmax_lon)
     call general_rotate_wind_xy2ll(gt_a,ug,vg,utest,vtest,rlon_test,x,y)
     call rotate_wind_xy2ll(ug,vg,u,v,rlon,x,y)
     errmax_w=max(sqrt((u-utest)**2+(v-vtest)**2),errmax_w)
     call general_rotate_wind_ll2xy(gt_a,utest,vtest,ugtest,vgtest,rlon_test,x,y)
     call rotate_wind_ll2xy(u,v,ug,vg,rlon,x,y)
     errmax_wg=max(sqrt((ug-ugtest)**2+(vg-vgtest)**2),errmax_wg)
  end do
!     check differences--should be zero
  if(mype==0) then
     write(0,*)' for txy2ll, errmax_lon,lat=',errmax_lon,errmax_lat
     write(0,*)' for rotate_wind, errmax_w,wg=',errmax_w,errmax_wg
     write(0,*)' for rotate_wind, xmax,ymax,wgmax=',xmax,ymax,wgmax
  end if
     
end subroutine test1_general_ll2xy

subroutine test3_egrid2points

!    test new subroutine egrid2points, which interpolates from e-grid to a-grid, where e-grid can be
!      different projection than a-grid, and need not completely cover a-grid.

!  (there seems to be a problem with interpolating vector fields that is related to how they are rotated
!    after interpolation--I am looking into this).

  use gridmod, only: region_lat,region_lon,nlat,nlon,tll2xy,txy2ll,rotate_wind_xy2ll,rotate_wind_ll2xy
  use general_tll2xy_mod, only: llxy_cons,general_create_llxy_transform,general_tll2xy,general_txy2ll
  use general_tll2xy_mod, only: general_rotate_wind_ll2xy,general_rotate_wind_xy2ll
  use mpimod, only: mype
  use kinds, only: r_kind,i_kind,r_single
  use constants, only: zero,one,two,pi,rad2deg
  use egrid2agrid_mod, only: egrid2agrid_parm,create_egrid2points_slow,egrid2points
  implicit none

  type(llxy_cons) gt_e,gt_a
  type(egrid2agrid_parm) p_e2a
  integer(i_kind) i,j,nx3,nye,nxe,np,nord_e2a
  real(r_kind) y(1),x(1),errmax,fmax
  real(r_kind) region_lat1(1),region_lon1(1)
  real(r_kind) rotate3,xmin,xmax,ymin,ymax
  real(r_kind),allocatable,dimension(:,:)::region_lat_e,region_lon_e
  real(r_kind),allocatable,dimension(:,:)::testlona,testlata,mask,test_stream_e,test_stream_a
  real(r_kind),allocatable,dimension(:,:)::exact_stream_a
  real(r_single),allocatable:: out1(:,:),out2(:,:)
  real(r_kind),allocatable,dimension(:,:)::ue,ve,ua,va,uearth_a,vearth_a,uearth_e,vearth_e,ua_exact,va_exact
  real(r_kind) uearth_test,vearth_test,xe,ye,xa,ya
  integer(i_kind) nmix,nord_blend
  integer(i_kind) ii
  logical outside

  nmix=10
  nord_blend=4
  

!  1.  define subgrid which is rotated with respect to analysis grid
!       the way perhaps to do this is to define a rotated polar-stereo grid 
!        then get lats and lons of this grid using rpolar2ll.

                       if(mype==0) write(0,*)' at 1 in test3'
  rotate3=pi/4._r_kind
  xmin=huge(xmin)
  xmax=-huge(xmax)
  ymin=huge(ymin)
  ymax=-huge(ymax)
  do j=nlon/3,2*nlon/3
     do i=nlat/3,2*nlat/3
        region_lat1(1)=region_lat(i,j)
        region_lon1(1)=region_lon(i,j)
        call ll2rpolar(region_lat1,region_lon1,1,x,y, &
                       region_lat(nlat/2,nlon/2),region_lon(nlat/2,nlon/2),rotate3)
        xmin=min(x(1),xmin)
        xmax=max(x(1),xmax)
        ymin=min(y(1),ymin)
        ymax=max(y(1),ymax)
     end do
  end do
                       if(mype==0) write(0,*)' min,max(region_lat)=',minval(region_lat),maxval(region_lat)
  xmin=.8_r_kind*xmin
  xmax=.8_r_kind*xmax
  ymin=.8_r_kind*ymin
  ymax=.8_r_kind*ymax
                 if(mype==0) write(0,*)' xmin,max,ymin,max=',xmin,xmax,ymin,ymax
  nxe=nlon/3
  nye=nlat/3
  allocate(region_lat_e(nye,nxe),region_lon_e(nye,nxe))
  do j=1,nxe
     x(1)=xmin+(xmax-xmin)*(j-one)/(nxe-one)
     do i=1,nye
        y(1)=ymin+(ymax-ymin)*(i-one)/(nye-one)
        call rpolar2ll(x,y,1,region_lat1,region_lon1, &
                       region_lat(nlat/2,nlon/2),region_lon(nlat/2,nlon/2),rotate3)
        region_lat_e(i,j)=region_lat1(1)
        region_lon_e(i,j)=region_lon1(1)
     end do
  end do
  allocate(out1(nxe,nye))
  do i=1,nye
     do j=1,nxe
        out1(j,i)=region_lat_e(i,j)*rad2deg
     end do
  end do
  call outgrads1(out1,nxe,nye,'region_lat_e')
  do i=1,nye
     do j=1,nxe
        out1(j,i)=region_lon_e(i,j)*rad2deg
     end do
  end do
  call outgrads1(out1,nxe,nye,'region_lon_e')
                       if(mype==0) write(0,*)' min,max(region_lat_e)=',minval(region_lat_e),maxval(region_lat_e)

!  initialize merge_grid_e_to_grid_a

  nord_e2a=4
        if(mype==0) write(0,*)' at 2 in test3'
        if(mype==0) write(0,*)' min,max(region_lat_e)=',minval(region_lat_e),maxval(region_lat_e)
        if(mype==0) write(0,*)' min,max(region_lon_e)=',minval(region_lon_e),maxval(region_lon_e)
        if(mype==0) write(0,*)' min,max(region_lat)=',minval(region_lat),maxval(region_lat)
        if(mype==0) write(0,*)' min,max(region_lon)=',minval(region_lon),maxval(region_lon)
        if(mype==0) write(0,*)' nye,nxe,nlat,nlon,nord_e2a,nord_blend,nmix=',&
                                nye,nxe,nlat,nlon,nord_e2a,nord_blend,nmix
  call merge_grid_e_to_grid_a_initialize(region_lat_e,region_lon_e,region_lat,region_lon, &
                  nye,nxe,nlat,nlon,nord_e2a,nord_blend,nmix,gt_e,gt_a,p_e2a)

!  2.  interpolate earth lat field and lon field from subgrid to analysis grid.  make plots of
!             result.
  allocate(testlata(nlat,nlon),testlona(nlat,nlon),test_stream_e(nye,nxe),test_stream_a(nlat,nlon))
  allocate(exact_stream_a(nlat,nlon))
  do j=1,nxe
     do i=1,nye
        test_stream_e(i,j)=cos(region_lat_e(i,j))*sin(region_lat_e(i,j))*sin(region_lon_e(i,j))
     end do
  end do
  do j=1,nlon
     do i=1,nlat
        exact_stream_a(i,j)=cos(region_lat(i,j))*sin(region_lat(i,j))*sin(region_lon(i,j))
     end do
  end do
        if(mype==0) write(0,*)' min,max(test_stream_e)=',minval(test_stream_e),maxval(test_stream_e)
        if(mype==0) write(0,*)' min,max(exact_stream_a)=',minval(exact_stream_a),maxval(exact_stream_a)
        if(mype==0) write(0,*)' at 3 in test3'
  call merge_grid_e_to_grid_a(test_stream_e,exact_stream_a,test_stream_a,gt_e,gt_a,p_e2a)
        if(mype==0) write(0,*)' at 4 in test3'
        if(mype==0) write(0,*)' min,max(test_stream_a)=',minval(test_stream_a),maxval(test_stream_a)
  call merge_grid_e_to_grid_a(region_lat_e,region_lat,testlata,gt_e,gt_a,p_e2a)
  call merge_grid_e_to_grid_a(region_lon_e,region_lon,testlona,gt_e,gt_a,p_e2a)

  deallocate(out1)
  allocate(out1(nlon,nlat),out2(nlon,nlat))
  do i=1,nlat
     do j=1,nlon
        out1(j,i)=testlata(i,j)*rad2deg
     end do
  end do
  call outgrads1(out1,nlon,nlat,'testlata')
  do i=1,nlat
     do j=1,nlon
        out1(j,i)=testlona(i,j)*rad2deg
     end do
  end do
  call outgrads1(out1,nlon,nlat,'testlona')
  do i=1,nlat
     do j=1,nlon
        out1(j,i)=test_stream_a(i,j)
     end do
  end do
  call outgrads1(out1,nlon,nlat,'test_stream_a')
  do i=1,nlat
     do j=1,nlon
        out1(j,i)=exact_stream_a(i,j)
     end do
  end do
  call outgrads1(out1,nlon,nlat,'exact_stream_a')
  ii=0
  do j=1,nlon
     do i=1,nlat
        ii=ii+1
        out1(j,i)=p_e2a%blend(ii)
     end do
  end do
  call outgrads1(out1,nlon,nlat,'blend')
                       if(mype==0) write(0,*)' at 8 in test3'
  errmax=zero
  fmax=zero
  do j=1,nlon
     do i=1,nlat
        errmax=max(abs(testlata(i,j)-region_lat(i,j)),errmax)
        fmax=max(abs(region_lat(i,j)),fmax)
     end do
  end do
  if(mype==0) write(0,*)' for interpolated lat from e to a, errmax,fmax=',rad2deg*errmax,rad2deg*fmax
  errmax=zero
  fmax=zero
  do j=1,nlon
     do i=1,nlat
        errmax=max(abs(testlona(i,j)-region_lon(i,j)),errmax)
        fmax=max(abs(region_lon(i,j)),fmax)
     end do
  end do
  if(mype==0) write(0,*)' for interpolated lon from e to a, errmax,fmax=',rad2deg*errmax,rad2deg*fmax
!<><><><><><

!  3.  test with wind from large scale analytic stream function.
!
!       psi = cos(lat)*sin(lat)*sin(lon)
!
!       u = -dpsi/dlat = -sin(lon)*cos(2*lat)
!
!       v = (dpsi/dlon)/(1/cos(lat)) = sin(lat)*cos(lon)
!
  allocate(ue(nye,nxe),ve(nye,nxe))
  allocate(uearth_e(nye,nxe),vearth_e(nye,nxe))
  allocate(ua(nlat,nlon),va(nlat,nlon))
  allocate(ua_exact(nlat,nlon),va_exact(nlat,nlon))
  allocate(uearth_a(nlat,nlon),vearth_a(nlat,nlon))
                       if(mype==0) write(0,*)' at 9 in test3'
  do j=1,nxe
     xe=j
     do i=1,nye
        ye=i
!                      if(mype==0) write(0,*)' at 9.1 in test3,i,j,nye,nxe=',i,j,nye,nxe
        vearth_test=sin(region_lat_e(i,j))*cos(region_lon_e(i,j))
!               if(mype==0) write(0,*)' at 9.2 in test3,i,j,nye,nxe,vearth_test=',i,j,nye,nxe,vearth_test
        uearth_test=-sin(region_lon_e(i,j))*cos(two*region_lat_e(i,j))
!               if(mype==0) write(0,*)' at 9.3 in test3,i,j,nye,nxe,vearth_test=',i,j,nye,nxe,uearth_test
!           reorient from earth coordinates to e-grid coordinates
        call general_rotate_wind_ll2xy(gt_e,uearth_test,vearth_test,ue(i,j),ve(i,j), &
                                       region_lon_e(i,j),xe,ye)
!               if(mype==0) write(0,*)' at 9.4 in test3,i,j,nye,nxe,ue,ve,region_lon_e,xe,ye=',&
!                                         i,j,nye,nxe,ue(i,j),ve(i,j),region_lon_e(i,j)*rad2deg,xe,ye
     end do
  end do
                       if(mype==0) write(0,*)' at 10 in test3'
   do j=1,nlon
      xa=j
      do i=1,nlat
         ya=i
         vearth_test=sin(region_lat(i,j))*cos(region_lon(i,j))
         uearth_test=-sin(region_lon(i,j))*cos(two*region_lat(i,j))
!           reorient from earth coordinates to a-grid coordinates
         call general_rotate_wind_ll2xy(gt_a,uearth_test,vearth_test,ua_exact(i,j),va_exact(i,j), &
                                        region_lon(i,j),xa,ya)
      end do
   end do
                       if(mype==0) write(0,*)' at 11 in test3'
  call merge_vgrid_e_to_vgrid_a(ue,ve,ua_exact,va_exact,ua,va,gt_e,gt_a,p_e2a)
                       if(mype==0) write(0,*)' at 12 in test3'
   do i=1,nlat
      do j=1,nlon
         out1(j,i)=ua(i,j)
      end do
   end do
                       if(mype==0) write(0,*)' at 13 in test3'
   do i=1,nlat
      do j=1,nlon
         out2(j,i)=va(i,j)
      end do
   end do
                       if(mype==0) write(0,*)' at 14 in test3'
   call outgrads1(out1,nlon,nlat,'u1')
   call outgrads1(out2,nlon,nlat,'v1')
                       if(mype==0) write(0,*)' at 15 in test3'
   do i=1,nlat
      do j=1,nlon
         out1(j,i)=ua_exact(i,j)
      end do
   end do
   do i=1,nlat
      do j=1,nlon
         out2(j,i)=va_exact(i,j)
      end do
   end do
                       if(mype==0) write(0,*)' at 16 in test3'
   call outgrads1(out1,nlon,nlat,'u1exact')
   call outgrads1(out2,nlon,nlat,'v1exact')
                       if(mype==0) write(0,*)' at 17 in test3'
   ii=0
   do j=1,nlon
      do i=1,nlat
         ii=ii+1
         out1(j,i)=p_e2a%xa_e(ii)
      end do
   end do
   ii=0
   do j=1,nlon
      do i=1,nlat
         ii=ii+1
         out2(j,i)=p_e2a%ya_e(ii)
      end do
   end do
                       if(mype==0) write(0,*)' at 18 in test3'
   call outgrads1(out1,nlon,nlat,'xa_e')
   call outgrads1(out2,nlon,nlat,'ya_e')
                       if(mype==0) write(0,*)' at 19 in test3'

  deallocate(region_lat_e,region_lon_e)
  deallocate(out1,out2)
  deallocate(testlata,testlona,test_stream_e,test_stream_a,exact_stream_a)
  deallocate(ue,ve)
  deallocate(uearth_e,vearth_e,ua,va,ua_exact,va_exact,uearth_a,vearth_a)

end subroutine test3_egrid2points
