!
! 3d grid transforms
!
!
!  call Fill3D( lli, levi, ps, field, &
!               lliX, leviX, fieldX, &
!               combkey, status )
!
!    o lli,levi   : output field defintions  (in)
!      ps         : surface pressure in hPa  (in)
!      field      : output field             (out)
!
!    o lliX, leviX : input field definitons  (in)
!      fieldX      : input field             (in)
!
!    o combkey : 'mass-aver', 'sum', 'aver'   (in)
!
!    o note that psX is not required ...
!
!  call FillMass      ( m    , lli, levi, sp          , status )
!
!    ! Fill 3D mass array given surface pressure and grid cell aera's.
!
!  call FillMassChange( dm_dt, lli, levi, sp_t1, sp_t2, status )
!
!    ! Fill 3D mass change between two times given different
!    ! surface pressures;
!    ! note that ( a + b sp2 ) - ( a + b sp1 )
!    ! is not the same as  a + b (sp2 - sp1) ...
!
!

module grid_3d

  implicit none
  
  ! --- in/out -----------------------------
  
  private
  
  public  ::  Fill3D
  public  ::  Regrid3D
  public  ::  FillMass
  public  ::  FillMassChange
  
  ! --- const ---------------------------------
  
  character(len=*), parameter  ::  mname = 'grid_3d'  
    
  
  
contains


  
  !--------------------------------------------------------------------------
  !                    TM5                                                  !
  !--------------------------------------------------------------------------
  !BOP
  !
  ! !IROUTINE:  Regrid3D
  !
  ! !DESCRIPTION:
  ! Performs vertical and horizontal regridding of 3D data sets. This is a
  ! convenient wrapper around FillGrid and FillLevels.
  ! 
  ! Available methods:
  !    for horizontal regridding : 'sum', 'aver', 'area-aver', 'weight'
  !    for vertical   regridding : 'sum', 'aver', 'mass-aver', 'top', 'bottom'
  !
  ! See FillGrid and FillLevels for detailed documentation.
  !
  !\\
  !\\
  ! !INTERFACE:
  !
  subroutine Regrid3D( outHInfo, outVInfo, OutData, &
                       inHInfo,  inVInfo,  InData,  &
                       hcomb,    vcomb,    sp,      &
                       nuv,      nw,       status   )
    !
    ! !USES:
    !
    use grid_type_ll ,         only : TllGridInfo, FillGrid
    use grid_type_hyb,         only : TLevelInfo,  FillLevels
    !
    ! !INPUT PARAMETERS:
    !
    type(TllGridInfo), intent(in)  ::  outHInfo      ! Output grid : horizontal info (LonLat) 
    type(TLevelInfo),  intent(in)  ::  outVInfo      ! Output grid : vertical info             
    character(len=*),  intent(in)  ::  nuv           ! Horizontal location ('n'=center, 'u'=west edge, 'v'=north edge)    
    character(len=*),  intent(in)  ::  nw            ! Vertical location ('n'=center, 'w'=edge)    
    real,              intent(in)  ::  sp(:,:)       ! Surface pressure in Pa                      
    type(TllGridInfo), intent(in)  ::  inHInfo       ! Input grid : horizontal info             
    type(TLevelInfo),  intent(in)  ::  inVInfo       ! Input grid : vertical info                
    real,              intent(in)  ::  InData(:,:,:) ! Input data                                  
    character(len=*),  intent(in)  ::  hcomb         ! combination key for horizontal regridding   
    character(len=*),  intent(in)  ::  vcomb         ! combination key for vertical regridding     
    !
    ! !OUTPUT PARAMETERS:
    !
    real,    intent(out)           ::  OutData(:,:,:)  ! output data
    integer, intent(out)           ::  status          ! return code
    !
    ! !REVISION HISTORY: 
    !   15 Dec 2010 - P. Le Sager - written, based on Fill3D.
    !
    ! !REMARKS:
    !  (1) Note that surface pressure on the input grid is not needed.
    !  (2) For now, only data on horizontal grid centers can be regriddded.
    !        So, you must use 'n' for NUV (pls, 15-12-2010)
    !
    !EOP
    !------------------------------------------------------------------------
    !BOC

    ! --- local -------------------------    
    character(len=*), parameter ::  name = mname//'/Regrid3D'
    real, allocatable           ::  field_ll(:,:,:)
    integer                     ::  l

    ! init
    ! -----------------    
    ! output horizontal grid, input levels grid
    allocate( field_ll( outHInfo%nlon, outHInfo%nlat, inVInfo%nlev ) )

    
    ! horizontal
    ! -----------------
    do l = 1, inVInfo%nlev

       call FillGrid( outHInfo , nuv, field_ll(:,:,l),               &
                      inHInfo,   nuv, InData(:,:,l),   hcomb, status )
       
       if (status<0) then
          write (*,'("ERROR - only part of target grid filled")')
          write (*,'("ERROR in ",a)') name; status=1; return
       endif
       
       if (status/=0) then
          write (*,'("ERROR in ",a)') name; status=1
          return
       endif
    end do

    ! vertical
    ! -----------------
    call FillLevels( outVInfo, nw, sp, OutData, inVInfo, field_ll, vcomb, status )
    if (status/=0) then; write (*,'("ERROR in ",a)') name; status=1; return
    end if  
             
    ! done
    ! -----------------
    deallocate( field_ll )
    status = 0
       
  end subroutine Regrid3D
  !EOC



  ! =========================================
  
    
  subroutine Fill3D( lli, levi, nw, ps, field, &
                     lliX, leviX, fieldX, &
                     combkey, status )
                     
    use grid_type_ll , only : TllGridInfo, FillGrid
    use grid_type_hyb, only : TLevelInfo, FillLevels
  
    ! --- in/out --------------------------------

    type(TllGridInfo), intent(in)        ::  lli
    type(TLevelInfo), intent(in)         ::  levi
    character(len=*), intent(in)         ::  nw
    real, intent(in)                     ::  ps(:,:)        ! Pa
    real, intent(out)                    ::  field(:,:,:)
    type(TllGridInfo), intent(in)        ::  lliX
    type(TLevelInfo), intent(in)         ::  leviX
    real, intent(in)                     ::  fieldX(:,:,:)
    character(len=*), intent(in)         ::  combkey
    integer, intent(out)                 ::  status

    ! --- const ---------------------------------

    character(len=*), parameter  ::  name = mname//'/Fill3D'
 
    ! --- local -------------------------
    
    real, allocatable     ::  field_ll(:,:,:)
    integer               ::  l
  
    ! --- begin -------------------------
    
    ! output horizontal grid, input levels
    allocate( field_ll(lli%nlon,lli%nlat,leviX%nlev) )

    select case ( combkey )
    
      !
      ! mass average
      !
      
      case ( 'mass-aver' )
      
        ! horizontal
        do l = 1, leviX%nlev
          call FillGrid( lli , 'n', field_ll(:,:,l), &
                         lliX, 'n', fieldX(:,:,l), 'area-aver', status )
          if (status<0) then
            write (*,'("ERROR - only part of target grid filled")')
            write (*,'("ERROR in ",a)') name; status=1; return
          end if
          if (status/=0) then; write (*,'("ERROR in ",a)') name; status=1; return; end if
        end do

        ! vertical
        call FillLevels( levi, nw, ps, field, leviX, field_ll, 'mass-aver', status )
        if (status/=0) then; write (*,'("ERROR in ",a)') name; status=1; return; end if
      
      !
      ! other (should be supported by FillGrid and FillLevels)
      !
    
      case default
      
        ! horizontal
        do l = 1, leviX%nlev
          call FillGrid( lli , 'n', field_ll(:,:,l), &
                         lliX, 'n', fieldX(:,:,l), combkey, status )
          if (status<0) then
            write (*,'("ERROR - only part of target grid filled")')
            write (*,'("ERROR in ",a)') name; status=1; return
          end if
          if (status/=0) then; write (*,'("ERROR in ",a)') name; status=1; return; end if
        end do

        ! vertical
        call FillLevels( levi, nw, ps, field, leviX, field_ll, combkey, status )
        if (status/=0) then; write (*,'("ERROR in ",a)') name; status=1; return; end if

    end select
    
    ! done
    deallocate( field_ll )
    
    ! ok
    status = 0
    
  end subroutine Fill3D
  


  ! **************************************************************  

  
  !
  !   p = a + b sp
  ! 


  subroutine FillMass( m, lli, levi, sp, status )

    use Binas        , only : grav
    use grid_type_ll , only : TllGridInfo, AreaOper
    use grid_type_hyb, only : TLevelInfo

    ! --- begin ---------------------------------

    real, intent(out)                    ::  m(:,:,:)   ! kg
    type(TllGridInfo), intent(in)        ::  lli
    type(TLevelInfo), intent(in)         ::  levi
    real, intent(in)                     ::  sp(:,:)       ! Pa
    integer, intent(out)                 ::  status

    ! --- const ---------------------------------

    character(len=*), parameter  ::  rname = mname//'/FillMass'

    ! --- local -------------------------

    integer               ::  l

    ! --- begin -------------------------

    ! check shape of target grid:
    if ( (size(m,1) /= lli%nlon ) .or. (size(m,2) /= lli%nlat) .or. &
         (size(m,3) /= levi%nlev) ) then
      write (*,'("ERROR - target array does not match with grid definition:")')
      write (*,'("ERROR -   lli    : ",i3," x ",i3         )') lli%nlon, lli%nlat
      write (*,'("ERROR -   levi   : ",i3                  )') levi%nlev
      write (*,'("ERROR -   ll     : ",i3," x ",i3," x ",i3)') shape(m)
      write (*,'("ERROR in ",a)') rname; status=1; return
    end if

    ! Pa = kg g / A   ->  kg = A * Pa/g

    ! loop over levels
    do l = 1, levi%nlev
      m(:,:,l) = levi%da(l) + levi%db(l) * sp / grav    ! Pa/g = kg/m2
      call AreaOper( lli, m(:,:,l), '*', 'm2', status )          ! kg
      if (status/=0) then; write (*,'("ERROR in ",a)') rname; status=1; return; end if
    end do

    ! ok
    status = 0

  end subroutine FillMass


  ! ***


  ! 
  ! p1 = a + b sp1
  ! p2 = a + b sp2
  ! 
  ! p2 - p1 = b (sp2 - sp1)
  !
  ! m = (p2 - p1)/g * A
  !
  

  subroutine FillMassChange( dm, lli, levi, sp1, sp2, status )

    use Binas        , only : grav
    use grid_type_ll , only : TllGridInfo, AreaOper
    use grid_type_hyb, only : TLevelInfo

    ! --- begin ---------------------------------

    real, intent(out)                    ::  dm(:,:,:)   ! kg
    type(TllGridInfo), intent(in)        ::  lli
    type(TLevelInfo), intent(in)         ::  levi
    real, intent(in)                     ::  sp1(:,:)       ! Pa
    real, intent(in)                     ::  sp2(:,:)       ! Pa
    integer, intent(out)                 ::  status

    ! --- const ---------------------------------

    character(len=*), parameter  ::  rname = mname//'/FillMassChange'

    ! --- local -------------------------

    integer               ::  l

    ! --- begin -------------------------

    ! check shape of target grid:
    if ( (size(dm,1) /= lli%nlon ) .or. (size(dm,2) /= lli%nlat) .or. &
         (size(dm,3) /= levi%nlev) ) then
      write (*,'("ERROR - target array does not match with grid definition:")')
      write (*,'("ERROR -   lli    : ",i3," x ",i3         )') lli%nlon, lli%nlat
      write (*,'("ERROR -   levi   : ",i3                  )') levi%nlev
      write (*,'("ERROR -   ll     : ",i3," x ",i3," x ",i3)') shape(dm)
      write (*,'("ERROR in ",a)') rname; status=1; return
    end if

    ! Pa = kg g / A   ->  kg = A * Pa/g

    ! loop over levels
    do l = 1, levi%nlev
      dm(:,:,l) = abs(levi%db(l)) * ( sp2 - sp1 ) / grav    ! Pa/g = kg/m2
      call AreaOper( lli, dm(:,:,l), '*', 'm2', status )          ! kg
      if (status/=0) then; write (*,'("ERROR in ",a)') rname; status=1; return; end if
    end do


    ! ok
    status = 0

  end subroutine FillMassChange




end module grid_3d
