!******************************************************************************
!  module gi_module
!
!  This routine is originally coded by Dr. Enomoto. 
!******************************************************************************
! 2003/11/20
!   Module name is changed to gi_module. 
!------------------------------------------------------------------------------
! 2003/08/22
!******************************************************************************

  module gi_module

    use vtype

    implicit none

    private

    interface
       subroutine findfu( fn, ios, fu, mode )
         use vtype
         implicit none
         character(len=*), intent(in )           :: fn
         integer(i4b)    , intent(out)           :: ios, fu
         character(len=*), intent(in ), optional :: mode
       end subroutine findfu
    end interface

    type, public :: gi_file_att_type
       integer( kind=i4b )               :: un
       character( len =extstr )          :: grd, title
       real( kind=sp )                   :: undef, x1, x2
       integer( kind=i4b )               :: xdef, ydef, zdef, tdef
       character( len =extstr )          :: date, tunit
       real( kind=sp )         , pointer :: zlevs(:), ylevs(:)
       integer( kind=i4b )               :: nvars
       type( gi_var_att_type  ), pointer :: vars(:)
    end type gi_file_att_type

    type, public :: gi_var_att_type
       character( len=extstr ) :: var_name, var_title, var_unit
       integer( kind=i4b )     :: nz, shift 
    end type gi_var_att_type


    interface gi_read_data
       module procedure gi_read_data_4byte, gi_read_data_8byte
    end interface

    interface gi_write_data
       module procedure gi_write_data_4byte, gi_write_data_8byte
    end interface

    interface gi_setdim
       module procedure gi_setdim_levels, gi_setdim_linear
    end interface

    interface gi_write_3ddata
       module procedure gi_write_3ddata_4byte, gi_write_3ddata_8byte
    end interface

    interface gi_read_3ddata
       module procedure gi_read_3ddata_4byte, gi_read_3ddata_8byte
    end interface


    public :: &
         gi_check_reclunit, &
         gi_parse_ctl, &
         gi_cp_file_atts, &
         gi_set_vars, &
         gi_generate_ctl, &
         gi_open_data, &
         gi_close_data, &
         gi_read_data, &
         gi_write_data, &
         gi_file_att_init, &
         gi_setgrdfn, &
         gi_setundef, &
         gi_setdim, &
         gi_addvar, &
         gi_write_3ddata, &
         gi_read_3ddata


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

  contains

    !**************************************************************************
    ! subroutine gi_check_reclunit
    !
    !**************************************************************************

    subroutine gi_check_reclunit()


      ! Local variables
      !
      character( len=extstr ) :: reclunit


      call getenv("F_RECLUNIT", reclunit)
      if ((reclunit/="byte").and.(reclunit/="BYTE")) then
         print *, "### Error: F_RECLUNIT/=BYTE"
         stop
      end if


    end subroutine gi_check_reclunit

    !**************************************************************************
    ! subroutine gi_parse_ctl
    !
    ! ctl     : File name of GrADs control file. This does not include 
    !         : extention (.ctl). 
    ! f       : GrADs file attributes
    !**************************************************************************

    subroutine gi_parse_ctl(ctl, f)


      character(len=*)        , intent(in   ) :: ctl
      type( gi_file_att_type ), intent(inout) :: f


      ! Local variables
      !
      character(len=extstr)                :: fn
      integer(kind=i4b)                    :: un, ios, i
      character(len=extstr), dimension(10) :: str


      fn = trim(ctl)//".ctl"
      call findfu(fn, ios, un)
      if( ios /= 0 ) then
         write( 6, * ) 'STOP in parse_ctl: ', ios
         stop
      endif
      open(unit=un, file=trim(ctl)//".ctl", status="old", &
           action="read", iostat=ios)
      if (ios/=0) then
         print *, "### File open error in gi_parse_ctl: ", trim(ctl)//".ctl"
         stop
      end if

      ! DSET
      !
      read (un,*) str(1:2)
      if (str(2)(1:1)=="^") then
         f % grd = str(2)(2:len_trim(str(2)))
      else
         f % grd = trim(str(2))
      end if

      ! TITLE
      !
      read (un,*) str(1)
      f % title = trim(str(1))

      ! UNDEF
      !
      read (un,*) str(1), f % undef

      ! XDEF
      !
      read (un,*) str(1), f % xdef, str(2), f % x1, f % x2

      ! YDEF
      !
      read (un,*) str(1), f % ydef
      allocate(f % ylevs(f % ydef))
      do i = 1, f % ydef
         read (un,*) f % ylevs(i)
      end do

      ! ZDEF
      !
      read (un,*) str(1), f % zdef, str(2)
      allocate( f % zlevs(f % zdef) )
      do i = 1, f % zdef
         read (un,*) f % zlevs(i)
      end do

      ! TDEF
      !
      read(unit=un, fmt="(a5,i6,a7,a17,a20)") &
           str(1), f % tdef, str(2), f % date, f % tunit
!      print *, "DAT start time : ",trim( f % date ),"   ",trim( f % tunit )

      ! VARS
      !
      read (un, *) str(1), f % nvars
      allocate( f % vars (f % nvars)   )
       
      do i = 1, f % nvars
         read(unit=un, fmt="(a16,i3,a3,a32,a16)") &
              str(1), f % vars(i) % nz, str(2), str(3), str(4)
         if ( f % vars(i) % nz == 0 ) then 
            f % vars(i) % nz = 1
         end if
         f % vars(i) % var_name = trim(str(1))
         f % vars(i) % var_title = trim(str(3))
         f % vars(i) % var_unit = trim(str(4))
      end do
      f % vars(1) % shift = 0
      do i = 2, f % nvars
         f % vars(i) % shift = &
              f % vars(i-1) % shift + f % vars(i-1) % nz
      end do
        
      close(un)

    end subroutine gi_parse_ctl

    !**************************************************************************
    ! subroutine gi_cp_file_atts
    !
    ! f      : GrADs file attributes (source)
    ! g      : GrADs file attributes (destination)
    !**************************************************************************

    subroutine gi_cp_file_atts( f, g )


      type( gi_file_att_type ), intent(in   ) :: f
      type( gi_file_att_type ), intent(inout) :: g


      g % undef = f % undef
      g % x1    = f % x1
      g % x2    = f % x2
      g % xdef  = f % xdef
      g % ydef  = f % ydef
      g % zdef  = f % zdef
      g % tdef  = f % tdef
      g % date  = f % date
      g % tunit = f % tunit
      allocate(g % ylevs(size(f % ylevs)))
      g % ylevs = f % ylevs
      allocate(g % zlevs(size(f % zlevs)))
      g % zlevs = f % zlevs


    end subroutine gi_cp_file_atts

    !**************************************************************************
    ! subroutine gi_set_vars
    !
    ! f      : GrADs file attributes
    ! vars   : Variable attributes
    !**************************************************************************

    subroutine gi_set_vars(f, vars)


      type( gi_file_att_type ), intent(inout) :: f
      type( gi_var_att_type ) , intent(in   ) :: vars(:)


      f % nvars = size(vars)
      allocate(f % vars(f % nvars))
      f % vars  = vars

    end subroutine gi_set_vars

    !**************************************************************************
    ! subroutine gi_generate_ctl
    !
    ! fname    : GrADs file name
    ! f        : GrADs file attributes
    !**************************************************************************

    subroutine gi_generate_ctl(fname, f)


      character(len=*)        , intent(in   ) :: fname
      type( gi_file_att_type ), intent(inout) :: f


      ! Local variables
      !
      character(len=extstr) :: fn
      integer(kind=i4b)     :: un, ios, i


      fn = trim(fname)//".ctl"
      call findfu(fn, ios, un)
      if( ios /= 0 ) then
         write( 6, * ) 'STOP in generate_ctl: ', ios
         stop
      endif
      open(unit=un, file=trim(fname)//".ctl", iostat=ios, &
           action="write", status="replace")

      if(ios/=0) then
         print *, "### File open error in gi_generate_ctl: ", &
              trim(fname)//".ctl"
         stop
      end if

!      f % grd = trim(fname) // ".grd"
      write(unit=un, fmt="(a,a20)") "DSET    ^", f % grd
      write(unit=un, fmt="(a)") "TITLE   AFES"
      write(unit=un, fmt="(a,1pe9.2)") "UNDEF   ", f % undef
      write(unit=un, fmt="(a,i6,a,0p2f9.4)") &
           "XDEF    ", f % xdef, "  LINEAR  ", f % x1, f % x2
      write(unit=un, fmt="(a,i6,a)") "YDEF    ", f % ydef, "  LEVELS"
      write(unit=un, fmt="((f16.10))") f % ylevs
      write(unit=un, fmt="(a,i6,a)") "ZDEF    ", f % zdef, "  LEVELS"
      write(unit=un, fmt="((f20.10))") f % zlevs
      write(unit=un, fmt="(a5,i6,a7,a17,a20)") &
           "TDEF ", f % tdef, " LINEAR", f % date, f % tunit
      write(unit=un, fmt="(a,i3)") "VARS", f % nvars
      do i = 1, f % nvars
         write(unit=un, fmt="(a16,i3,a,1x,a32,a16)") &
              f % vars(i) % var_name, f % vars(i) % nz, &
              "  0", f % vars(i) % var_title, f % vars(i) % var_unit
      end do
      write(un, fmt="(a)") 'ENDVARS'

      close(un)


    end subroutine gi_generate_ctl

    !**************************************************************************
    ! subroutine gi_open_data
    !
    ! dir     : Directory which contains GrADs data file
    ! f       : GrADs file attributes
    ! ac      : action   --- "read" or "write"
    !**************************************************************************

    subroutine gi_open_data( dir, f, ac )


      character(len=*)        , intent(in   ) :: dir
      type( gi_file_att_type ), intent(inout) :: f
      character(len=*)        , intent(in   ) :: ac


      ! Local variables
      !
      character(len=extstr)   :: fn
      integer(kind=i4b)       :: un, ios
      character( len=extstr ) :: st
      character( len=extstr ) :: machtype
      integer(i4b)            :: bytef = 0


!      call getenv( "MACHTYPE", machtype )
!      select case( machtype )
!      case ( "i386" )
         bytef = 4
!      case ( "i686" )
!         bytef = 4
!      case ( "alpha" )
!         bytef = 1
!      case default
!         write( 6, * ) "Unexpected MACHTYPE, ", trim( machtype ), "."
!         stop
!      end select


      fn = trim( dir ) // "/" // f % grd
      call findfu( fn, ios, un )
      if( ios /= 0 ) then
         write( 6, * ) 'STOP in open_data: ', ios
         stop
      endif
      f % un = un
      select case (ac)
      case ("read")
         st = "old"
      case ("write")
         st = "replace"
      end select

      open(unit=f % un, file=fn, status=st, action=ac, &
           access="direct", iostat = ios, recl = f % xdef * f % ydef * bytef)

      if (ios/=0) then
         print *, "### File open error in gi_open_data: ", trim(f % grd)
         stop
      end if


    end subroutine gi_open_data

    !**************************************************************************
    ! subroutine gi_close_data
    !
    ! f      : GrADs file attributes
    !**************************************************************************

    subroutine gi_close_data( f )


      type( gi_file_att_type ), intent(inout) :: f


      close( f % un )
      if( associated( f % ylevs ) ) deallocate( f % ylevs )
      if( associated( f % zlevs ) ) deallocate( f % zlevs )
      if( associated( f % vars  ) ) deallocate( f % vars  )


    end subroutine gi_close_data

    !**************************************************************************
    ! subroutine gi_read_data_4byte
    !
    ! f       : GrADs file attributes
    ! n       : Index for variables
    ! t       : Index for time
    ! l       : Index for vertical levels
    ! x4      : 2 dimensional array for variable (4 byte variable)
    !**************************************************************************

    subroutine gi_read_data_4byte( f, n, t, l, x4 )


      type( gi_file_att_type ), intent(in ) :: f
      integer( i4b )          , intent(in ) :: n, t, l
      real( sp )              , intent(out) :: x4( f % xdef, f % ydef )


      ! Local variables
      !
      integer( i4b ) :: r


!      r = f % vars(n) % shift + f % vars(n) % nz * (t-1) + l 
      r = ( f % vars( f % nvars ) % shift + f % vars( f % nvars ) % nz ) &
           * ( t - 1 ) &
           + f % vars( n ) % shift + l
!      print *, "t=",t, " l=", l, " r=", r
      read(unit=f % un, rec=r) x4


    end subroutine gi_read_data_4byte

    !**************************************************************************
    ! subroutine gi_read_data_8byte
    !
    ! f       : GrADs file attributes
    ! n       : Index for variables
    ! t       : Index for time
    ! l       : Index for vertical levels
    ! x8      : 2 dimensional array for variable ( 8 byte variable )
    !**************************************************************************

    subroutine gi_read_data_8byte( f, n, t, l, x8 )


      type( gi_file_att_type ), intent(in ) :: f
      integer( i4b )          , intent(in ) :: n, t, l
      real( dp )              , intent(out) :: x8( f % xdef, f % ydef )


      ! Local variables
      !
      real( sp ) :: x4( f % xdef, f % ydef )


      call gi_read_data_4byte( f, n, t, l, x4 )

      x8( :, : ) = dble( x4( :, : ) )


    end subroutine gi_read_data_8byte

    !**************************************************************************
    ! subroutine gi_write_data_4byte
    !
    ! f       : GrADs file attributes
    ! n       : Index for variables
    ! t       : Index for time
    ! l       : Index for vertical levels
    ! x4      : 2 dimensional array for variable ( 4 byte variable )
    !**************************************************************************

    subroutine gi_write_data_4byte( f, n, t, l, x4 )

      implicit none

      type( gi_file_att_type ), intent(in) :: f
      real( sp )              , intent(in) :: x4( f % xdef, f % ydef )
      integer( i4b )          , intent(in) :: n, t, l


      ! Local variables
      !
      integer( i4b ) :: r


!      r = f % vars(n) % shift + f % vars(n) % nz * (t-1) + l 
      r = ( f % vars( f % nvars ) % shift + f % vars( f % nvars ) % nz ) &
           * ( t - 1 ) &
           + f % vars( n ) % shift + l
      write(unit=f % un, rec=r) x4


    end subroutine gi_write_data_4byte

    !**************************************************************************
    ! subroutine gi_write_data_8byte
    !
    ! f       : GrADs file attributes
    ! n       : Index for variables
    ! t       : Index for time
    ! l       : Index for vertical levels
    ! x8      : 2 dimensional array for variable ( 8 byte variable )
    !**************************************************************************

    subroutine gi_write_data_8byte( f, n, t, l, x8 )

      implicit none

      type( gi_file_att_type ), intent(in) :: f
      real( dp )              , intent(in) :: x8( f % xdef, f % ydef )
      integer( i4b )          , intent(in) :: n, t, l


      ! Local variables
      !
      real( kind=sp ) :: x4( f % xdef, f % ydef )


      x4( :, : ) = real( x8( :, : ) )

      call gi_write_data_4byte( f, n, t, l, x4 )


    end subroutine gi_write_data_8byte

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

    subroutine gi_file_att_init( gfa )


      type( gi_file_att_type ), intent(out) :: gfa


      ! Local variables
      !


      gfa % nvars           = 0

      if( associated( gfa % ylevs ) ) deallocate( gfa % ylevs )
      if( associated( gfa % zlevs ) ) deallocate( gfa % zlevs )
      if( associated( gfa % vars  ) ) deallocate( gfa % vars  )


    end subroutine gi_file_att_init

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

    subroutine gi_setgrdfn( gfa, gfn )


      type( gi_file_att_type ), intent(inout) :: gfa
      character(len=*)        , intent(in   ) :: gfn


      ! Local variables
      !


      gfa % grd             = gfn


    end subroutine gi_setgrdfn

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

    subroutine gi_setundef( gfa, undef )

      type( gi_file_att_type ), intent(inout) :: gfa
      real(dp)                , intent(in   ) :: undef


      ! Local variables
      !


      gfa % undef           = real( undef )


    end subroutine gi_setundef

    !**************************************************************************
    ! subroutine gi_setdim_levels
    !
    ! gfa      : GrADs file attributes
    ! dimname  : Dimension name ( "x", "y", "z", "t" )
    ! dimtype  : Dimension type ( "LEVELS", "LINEAR" )
    ! dimlen   : Dimension length
    ! dimlevs8 : Dimension levels ( 8 byte array )
    ! levord   : Order of levels 
    !          : ( .true. for ..., .false. for inverse order )
    !**************************************************************************

    subroutine gi_setdim_levels( gfa, dimname, dimtype, &
         dimlen, dimlevs8, levord )


      type( gi_file_att_type ), intent(inout)           :: gfa
      character(len=*)        , intent(in   )           :: dimname, dimtype
      integer(i4b)            , intent(in   )           :: dimlen
      real(dp)                , intent(in   )           :: dimlevs8( dimlen )
      logical                 , intent(in   ), optional :: levord


      ! Local variables
      !
      real(dp)     :: tmp_dimlevs8( dimlen )
      integer(i4b) :: i


      if( ( dimtype .ne. "LEVELS" ) .and. ( dimtype .ne. "levels" ) ) then
         stop "dimtype must be 'LEVELS'."
      endif


      if( present( levord ) ) then
         if( levord ) then
            tmp_dimlevs8( : ) = dimlevs8( : )
         else
            do i = 1, dimlen
               tmp_dimlevs8( i ) = dimlevs8( dimlen - ( i - 1 ) )
            enddo
         endif
      else
         tmp_dimlevs8( : ) = dimlevs8( : )
      endif


      if( ( dimname .eq. "x" ) .or. ( dimname .eq. "X" ) ) then

         stop "This is not supported now: x axis with LEVELS."

      else if( ( dimname .eq. "y" ) .or. ( dimname .eq. "Y" ) ) then

         if( associated( gfa % ylevs ) ) deallocate( gfa % ylevs )

         gfa % ydef       = dimlen
         allocate( gfa % ylevs( gfa % ydef ) )
         gfa % ylevs( : ) = real( tmp_dimlevs8( : ) )

      else if( ( dimname .eq. "z" ) .or. ( dimname .eq. "Z" ) ) then

         if( associated( gfa % zlevs ) ) deallocate( gfa % zlevs )

         gfa % zdef       = dimlen
         allocate( gfa % zlevs( gfa % zdef ) )
         gfa % zlevs( : ) = real( tmp_dimlevs8( : ) )

      else if( ( dimname .eq. "t" ) .or. ( dimname .eq. "T" ) ) then

         stop "This is not supported now: t axis with LEVELS."

      endif


    end subroutine gi_setdim_levels

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

    subroutine gi_setdim_linear( gfa, dimname, dimtype, dimlen, dimst, dimint )


      type( gi_file_att_type ), intent(inout) :: gfa
      character(len=*)        , intent(in )   :: dimname, dimtype
      integer(i4b)            , intent(in )   :: dimlen
      real(dp)                , intent(in )   :: dimst, dimint


      ! Local variables
      !

      if( ( dimtype .ne. "LINEAR" ) .and. ( dimtype .ne. "linear" ) ) then
         stop "dimtype must be 'LINEAR'."
      endif


      if( ( dimname .eq. "x" ) .or. ( dimname .eq. "X" ) ) then

         gfa % xdef = dimlen
         gfa % x1   = dimst
         gfa % x2   = dimint

      else if( ( dimname .eq. "y" ) .or. ( dimname .eq. "Y" ) ) then

         stop "This is not supported now: y axis with LINEAR."

      else if( ( dimname .eq. "z" ) .or. ( dimname .eq. "Z" ) ) then

         stop "This is not supported now: z axis with LINEAR."

      else if( ( dimname .eq. "t" ) .or. ( dimname .eq. "T" ) ) then

         gfa % tdef = dimlen
         gfa % date  = "  0000z01jan0001 "
         gfa % tunit = "  01hr"

      endif


    end subroutine gi_setdim_linear

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

    subroutine gi_addvar( gfa, vname, vtitle, unit, nz, varid )


      type( gi_file_att_type ), intent(inout) :: gfa
      character(len=*)        , intent(in   ) :: vname, vtitle, unit
      integer(i4b)            , intent(in   ) :: nz
      integer(i4b)            , intent(out  ) :: varid


      ! Local variables
      !
      type( gi_var_att_type ), allocatable :: gvar( : )
      integer(i4b)                         :: i


      allocate( gvar( gfa % nvars + 1 ) )


      if( gfa % nvars .ge. 1 ) then

         do i = 1, gfa % nvars
            gvar( i ) % var_name  = gfa % vars( i ) % var_name
            gvar( i ) % var_title = gfa % vars( i ) % var_title
            gvar( i ) % var_unit  = gfa % vars( i ) % var_unit
            gvar( i ) % nz        = gfa % vars( i ) % nz
            gvar( i ) % shift     = gfa % vars( i ) % shift
         enddo

      endif


      gvar( gfa % nvars + 1 ) % var_name  = vname
      gvar( gfa % nvars + 1 ) % var_title = vtitle
      gvar( gfa % nvars + 1 ) % var_unit  = unit
      gvar( gfa % nvars + 1 ) % nz        = nz
      if( gfa % nvars .ge. 1 ) then
         gvar( gfa % nvars + 1 ) % shift &
              = gvar( gfa % nvars ) % shift + gvar( gfa % nvars ) % nz
      else
         gvar( gfa % nvars + 1 ) % shift = 0
              endif


      gfa % nvars = gfa % nvars + 1
      call gi_set_vars( gfa, gvar )


      deallocate( gvar )


      varid = gfa % nvars


    end subroutine gi_addvar

    !**************************************************************************
    ! subroutine gi_write_3ddata_4byte
    !
    ! gfa      : GrADs file attributes
    ! varid    : Variable ID
    ! arr3d4   : 3-dimensional data array ( 4 byte array )
    ! t        : Time level
    ! levord   : Order of levels 
    !          : ( .true. for ..., .false. for inverse order )
    !**************************************************************************

    subroutine gi_write_3ddata_4byte( gfa, arr3d4, varid, t, levord )


      type( gi_file_att_type ), intent(in)           :: gfa
      integer(i4b)            , intent(in)           :: varid
      real(sp)                , intent(in)           :: &
           arr3d4( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )
      integer(i4b)            , intent(in)           :: t
      logical                 , intent(in), optional :: levord


      ! Local variables
      !
      real(sp)     :: &
           tmp_arr3d4( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )
      real(sp)     :: arr2d4( gfa % xdef, gfa % ydef )
      integer(i4b) :: l, n


      n = varid


      if( present( levord ) ) then
         if( levord ) then
            tmp_arr3d4( :, :, : ) = arr3d4( :, :, : )
         else
            do l = 1, gfa % vars( n ) % nz
               tmp_arr3d4( :, :, l ) &
                    = arr3d4( :, :, gfa % vars( n ) % nz - ( l - 1 ))
            enddo
         endif
      else
         tmp_arr3d4( :, :, : ) = arr3d4( :, :, : )
      endif


      do l = 1, gfa % vars( n ) % nz
         arr2d4( :, : ) = tmp_arr3d4( :, :, l )
         call gi_write_data( gfa, n, t, l, arr2d4 )
      enddo


    end subroutine gi_write_3ddata_4byte

    !**************************************************************************
    ! subroutine gi_write_3ddata_8byte
    !
    ! gfa      : GrADs file attributes
    ! varid    : Variable ID
    ! arr3d8   : 3-dimensional data array ( 8 byte array )
    ! t        : Time level
    ! levord   : Order of levels ( .true. for ..., .false. for inverse order )
    !**************************************************************************

    subroutine gi_write_3ddata_8byte( gfa, arr3d8, varid, t, levord )


      type( gi_file_att_type ), intent(in)           :: gfa
      integer(i4b)            , intent(in)           :: varid
      real(dp)                , intent(in)           :: &
           arr3d8( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )
      integer(i4b)            , intent(in)           :: t
      logical                 , intent(in), optional :: levord


      ! Local variables
      !
      real(sp) :: arr3d4( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )


      arr3d4( :, :, : ) = real( arr3d8( :, :, : ) )

      call gi_write_3ddata_4byte( gfa, arr3d4, varid, t, levord )


    end subroutine gi_write_3ddata_8byte

    !**************************************************************************
    ! subroutine gi_read_3ddata_4byte
    !
    ! gfa      : GrADs file attributes
    ! varid    : Variable ID
    ! arr3d4   : 3-dimensional data array ( 4 byte array )
    ! t        : Time level
    ! levord   : Order of levels 
    !          : ( .true. for ..., .false. for inverse order )
    !**************************************************************************

    subroutine gi_read_3ddata_4byte( gfa, arr3d4, varid, t, levord )


      type( gi_file_att_type ), intent(in )           :: gfa
      integer(i4b)            , intent(in )           :: varid
      real(sp)                , intent(out)           :: &
           arr3d4( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )
      integer(i4b)            , intent(in )           :: t
      logical                 , intent(in ), optional :: levord


      ! Local variables
      !
      real(sp)     :: &
           tmp_arr3d4( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )
      real(sp)     :: arr2d4( gfa % xdef, gfa % ydef )
      integer(i4b) :: l, n


      n = varid


      do l = 1, gfa % vars( n ) % nz
         call gi_read_data( gfa, n, t, l, arr2d4 )
         tmp_arr3d4( :, :, l ) = arr2d4( :, : )
      enddo


      if( present( levord ) ) then
         if( levord ) then
            arr3d4( :, :, : ) = tmp_arr3d4( :, :, : )
         else
            do l = 1, gfa % vars( n ) % nz
               arr3d4( :, :, l ) &
                    = tmp_arr3d4( :, :, gfa % vars( n ) % nz - ( l - 1 ))
            enddo
         endif
      else
         arr3d4( :, :, : ) = tmp_arr3d4( :, :, : )
      endif


    end subroutine gi_read_3ddata_4byte

    !**************************************************************************
    ! subroutine gi_read_3ddata_8byte
    !
    ! gfa      : GrADs file attributes
    ! varid    : Variable ID
    ! arr3d8   : 3-dimensional data array ( 8 byte array )
    ! t        : Time level
    ! levord   : Order of levels ( .true. for ..., .false. for inverse order )
    !**************************************************************************

    subroutine gi_read_3ddata_8byte( gfa, arr3d8, varid, t, levord )


      type( gi_file_att_type ), intent(in )           :: gfa
      integer(i4b)            , intent(in )           :: varid
      real(dp)                , intent(out)           :: &
           arr3d8( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )
      integer(i4b)            , intent(in )           :: t
      logical                 , intent(in ), optional :: levord


      ! Local variables
      !
      real(sp) :: arr3d4( gfa % xdef, gfa % ydef, gfa % vars( varid ) % nz )


      call gi_read_3ddata_4byte( gfa, arr3d4, varid, t, levord )

      arr3d8( :, :, : ) = dble( arr3d4( :, :, : ) )


    end subroutine gi_read_3ddata_8byte

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

  end module gi_module
