!BOP ===========================================================================
!
! !MODULE: ice_pio -- reads and writes driver files
!
! !DESCRIPTION:
!  Writes netcdf files
!
! !REMARKS:
!
! !REVISION HISTORY:
!    Created by Mariana Vertenstein, June 2009
!
! !INTERFACE: ------------------------------------------------------------------


module ice_pio 3,14

  ! !USES:

  use shr_kind_mod, only: r8 => shr_kind_r8, in=>shr_kind_in
  use shr_kind_mod, only: cl => shr_kind_cl
  use shr_sys_mod , only: shr_sys_flush
  use ice_kinds_mod
  use ice_blocks
  use ice_broadcast
  use ice_communicate
  use ice_domain, only : nblocks, blocks_ice
  use ice_domain_size
  use ice_fileunits  
  use ice_exit
  use pio

  implicit none
  private
  save

  ! !PUBLIC TYPES:

  ! none

  !PUBLIC MEMBER FUNCTIONS:


  interface ice_pio_initdecomp 10
     module procedure ice_pio_initdecomp_2d
     module procedure ice_pio_initdecomp_3d
     module procedure ice_pio_initdecomp_3d_inner
  end interface

  public ice_pio_init
  public ice_pio_finalize
  public ice_pio_initdecomp

  ! !PUBLIC DATA MEMBERS

  integer, public :: ice_pio_stride, ice_num_iotasks, ice_pio_root, ice_pio_type

  !EOP

  !----------------------------------------------------------------------------
  ! Local data
  !----------------------------------------------------------------------------

  type(iosystem_desc_t) :: ice_pio_subsystem

!===============================================================================

contains

!===============================================================================
!BOP
!
! !IROUTINE: ice_pio_finalize - finalize io for input or output
!
! !INTERFACE: 

   subroutine ice_pio_finalize
   
     integer  :: ierr

     call pio_finalize(ice_pio_subsystem,ierr)

   end subroutine ice_pio_finalize
!===============================================================================
!BOP
!
! !IROUTINE: ice_pio_init - initialize io for input or output
!
! !INTERFACE: 

   subroutine ice_pio_init(mode, filename, File, clobber, cdf64) 3,11
!
! !DESCRIPTION:
!    Read the pio_inparm namelist and initialize the io subsystem
!
! !REVISION HISTORY:
!    2009-Feb-17 - J. Edwards - initial version
!
! !INPUT/OUTPUT PARAMETERS:
!
   implicit none
   character(len=*)     , intent(in),    optional :: mode
   character(len=*)     , intent(in),    optional :: filename
   type(file_desc_t)    , intent(inout), optional :: File
   logical              , intent(in),    optional :: clobber
   logical              , intent(in),    optional :: cdf64
!
!EOP
!
   integer (int_kind) :: &
      nml_error          ! namelist read error flag

   character(len=16)  :: ice_pio_type_name

   logical :: exists
   logical :: lclobber
   logical :: lcdf64
   integer :: status
   integer :: nmode
   character(*),parameter :: subName = '(ice_pio_wopen) '
   logical, save :: first_call = .true.

   !  Input namelist
    
   namelist /ice_pio_nml/    &
        ice_num_iotasks, &
        ice_pio_stride,  &
        ice_pio_type_name 

   ice_pio_root      =  1  ! defaulted to 1

   ice_num_iotasks   = -1  ! set based on io_stride value when initialized < 0
   ice_pio_stride    = -1  ! set based on num_iotasks value when initialized < 0
   ice_pio_type_name = 'netcdf'

   if (my_task == master_task) then
      call get_fileunit(nu_nml)
      open (nu_nml, file=nml_filename, status='old',iostat=nml_error)
      if (nml_error /= 0) then
         nml_error = -1
      else
         nml_error =  1
      endif
      do while (nml_error > 0)
         read(nu_nml, nml=ice_pio_nml,iostat=nml_error)
	 if (nml_error > 0) read(nu_nml,*)  ! for Nagware compiler
      end do
      if (nml_error == 0) close(nu_nml)
      call ice_pio_set_params(ice_pio_type_name)
      call release_fileunit(nu_nml)

      write(nu_diag,*) 'CICE PIO parameter settings...'
      write(nu_diag,*) '  ice_pio_stride    = ',ice_pio_stride
      write(nu_diag,*) '  ice_num_iotasks   = ',ice_num_iotasks
      write(nu_diag,*) '  ice pio_type_name = ',ice_pio_type_name
      call shr_sys_flush(nu_diag) 
   endif

   call broadcast_scalar(nml_error, master_task)
   if (nml_error /= 0) then
      call abort_ice('ice: error reading pio_nml')
   endif

   call broadcast_scalar(ice_num_iotasks, master_task)
   call broadcast_scalar(ice_pio_root,    master_task)
   call broadcast_scalar(ice_pio_stride,  master_task)
   call broadcast_scalar(ice_pio_type,    master_task)

   if (first_call) then	
      call pio_init(my_task, MPI_COMM_ICE, ice_num_iotasks, &
           ice_pio_root, ice_pio_stride, PIO_REARR_BOX, ice_pio_subsystem)
      first_call = .false.
   end if

   if (present(mode) .and. present(filename) .and. present(File)) then

      if (trim(mode) == 'write') then
         lclobber = .false.
         if (present(clobber)) lclobber=clobber
         
         lcdf64 = .false.
         if (present(cdf64)) lcdf64=cdf64
         
         if (File%fh<0) then
            ! filename not open
            inquire(file=trim(filename),exist=exists)
            if (exists) then
               if (lclobber) then
                  nmode = pio_clobber
                  if (lcdf64) nmode = ior(nmode,PIO_64BIT_OFFSET)
                  status = pio_createfile(ice_pio_subsystem, File, ice_pio_type, trim(filename), nmode)
                  if (my_task == master_task) then
                     write(nu_diag,*) subname,' create file ',trim(filename)
                  end if
               else
                  status = pio_openfile(ice_pio_subsystem, File, ice_pio_type, trim(filename), pio_write)
                  if (my_task == master_task) then
                     write(nu_diag,*) subname,' open file ',trim(filename)
                  end if
               endif
            else
               nmode = pio_noclobber
               if (lcdf64) nmode = ior(nmode,PIO_64BIT_OFFSET)
               status = pio_createfile(ice_pio_subsystem, File, ice_pio_type, trim(filename), nmode)
               if (my_task == master_task) then
                  write(nu_diag,*) subname,' create file ',trim(filename)
               end if
            endif
         else
            ! filename is already open, just return
         endif
      end if
      
      if (trim(mode) == 'read') then
         inquire(file=trim(filename),exist=exists)
         if (exists) then
            status = pio_openfile(ice_pio_subsystem, File, ice_pio_type, trim(filename), pio_nowrite)
         else
            if(my_task==master_task) then
               write(nu_diag,*) 'ice_pio_ropen ERROR: file invalid ',trim(filename)
            end if
            call abort_ice('aborting in ice-pio_ropen with invalid file')
         endif
      end if

   end if

 end subroutine ice_pio_init

!===============================================================================
!BOP
!
! !IROUTINE: ice_pio_set_params - set pio parameters
!
! !INTERFACE: 

    subroutine ice_pio_set_params(ice_pio_type_name) 1,3
!
! !DESCRIPTION:
!    Set the pio parameters for the subsystem
!
! !USES:
!
   use shr_string_mod,only: shr_string_toUpper
!
! !INPUT/OUTPUT PARAMETERS:
!
   implicit none
   character(len=*), intent(in) :: ice_pio_type_name
!
!EOP
!
   character(len=16) :: tmpname
   integer (kind=int_kind) :: npes

   tmpname = shr_string_toupper(ice_pio_type_name)

   if (trim(tmpname) == 'NETCDF') then
      ice_pio_type = iotype_netcdf
   else if (trim(tmpname) == 'PNETCDF') then
      ice_pio_type = iotype_pnetcdf
   else
      if (my_task == master_task) then
         write(nu_diag,*)' Bad io_type argument - using iotype_netcdf'
      end if
      ice_pio_type = iotype_netcdf
   end if

   npes = get_num_procs()
   if      (ice_pio_stride>0 .and. ice_num_iotasks<0) then
      ice_num_iotasks = npes/ice_pio_stride
   else if (ice_num_iotasks>0 .and. ice_pio_stride<0) then
      ice_pio_stride = npes/ice_num_iotasks
   else if (ice_num_iotasks<0 .and. ice_pio_stride<0) then
      ice_pio_stride = max(min(npes,4),npes/8)
      ice_num_iotasks = npes/ice_pio_stride
   end if

   if (ice_pio_root<0) then
      ice_pio_root = 1
   endif
   ice_pio_root = min(ice_pio_root,npes-1)
   
    if(ice_pio_root + (ice_pio_stride)*(ice_num_iotasks-1) >= npes .or. &
       ice_pio_stride<=0 .or. ice_num_iotasks<=0 .or. ice_pio_root < 0 .or. &
       ice_pio_root > npes-1) then
       if (my_task == master_task) then
          write(nu_diag,*)&
               'ice_pio_stride or ice_num_iotasks out of bounds, resetting to defaults ',&
               ice_pio_stride, ice_num_iotasks, ice_pio_root
       end if
       ice_pio_stride = max(1,npes/4)
       ice_num_iotasks = npes/ice_pio_stride
       ice_pio_root = min(1,npes-1)
    end if
    if (my_task == master_task) then
       write(nu_diag,*)'Using io_type=',tmpname,' stride=',ice_pio_stride,&
            ' iotasks=',ice_num_iotasks,' root=',ice_pio_root
    end if

   end subroutine ice_pio_set_params

!================================================================================


   subroutine ice_pio_initdecomp_2d(iodesc) 1,1

      type(io_desc_t), intent(out) :: iodesc

      integer (kind=int_kind) :: &
          iblk,ilo,ihi,jlo,jhi,lon,lat,i,j,n,k

      type(block) :: this_block 

      integer(kind=int_kind), pointer :: dof2d(:)

      allocate(dof2d(nx_block*ny_block*nblocks))

      n=0
      do iblk = 1, nblocks
         this_block = get_block(blocks_ice(iblk),iblk)         
         ilo = this_block%ilo
         ihi = this_block%ihi
         jlo = this_block%jlo
         jhi = this_block%jhi
         
         do j=1,ny_block
         do i=1,nx_block
            n = n+1
            if (j < jlo .or. j>jhi) then
               dof2d(n) = 0
            else if (i < ilo .or. i > ihi) then
               dof2d(n) = 0
            else
               lon = this_block%i_glob(i)
               lat = this_block%j_glob(j)
               dof2d(n) = (lat-1)*nx_global + lon
            endif
         enddo !i
         enddo !j
      end do

      call pio_initdecomp(ice_pio_subsystem, pio_double, (/nx_global,ny_global/), &
           dof2d, iodesc)

      deallocate(dof2d)
 
    end subroutine ice_pio_initdecomp_2d

!================================================================================


   subroutine ice_pio_initdecomp_3d (ndim3, iodesc) 1,1

      integer(kind=int_kind), intent(in) :: ndim3
      type(io_desc_t), intent(out) :: iodesc

      integer (kind=int_kind) :: &
          iblk,ilo,ihi,jlo,jhi,lon,lat,i,j,n,k 

      type(block) :: this_block 

      integer(kind=int_kind), pointer :: dof3d(:)

      allocate(dof3d(nx_block*ny_block*nblocks*ndim3))

      n=0
      do iblk = 1, nblocks
         this_block = get_block(blocks_ice(iblk),iblk)         
         ilo = this_block%ilo
         ihi = this_block%ihi
         jlo = this_block%jlo
         jhi = this_block%jhi
         
         do k=1,ndim3
         do j=1,ny_block
         do i=1,nx_block
            n = n+1
            if (j < jlo .or. j>jhi) then
               dof3d(n)=0
            else if (i < ilo .or. i > ihi) then
               dof3d(n) = 0
            else
               lon = this_block%i_glob(i)
               lat = this_block%j_glob(j)
               dof3d(n) = ((lat-1)*nx_global + lon) + (k-1)*nx_global*ny_global 
            endif
         enddo !i
         enddo !j
         enddo !ndim3
      end do

      call pio_initdecomp(ice_pio_subsystem, pio_double, (/nx_global,ny_global,ndim3/), &
           dof3d, iodesc)

      deallocate(dof3d)

    end subroutine ice_pio_initdecomp_3d

!================================================================================


    subroutine ice_pio_initdecomp_3d_inner(ndim3, inner_dim, iodesc) 1,1

      integer(kind=int_kind), intent(in) :: ndim3
      logical, intent(in) :: inner_dim
      type(io_desc_t), intent(out) :: iodesc

      integer (kind=int_kind) :: &
          iblk,ilo,ihi,jlo,jhi,lon,lat,i,j,n,k 

      type(block) :: this_block 

      integer(kind=int_kind), pointer :: dof3d(:)

      allocate(dof3d(nx_block*ny_block*nblocks*ndim3))

      n=0
      do iblk = 1, nblocks
         this_block = get_block(blocks_ice(iblk),iblk)         
         ilo = this_block%ilo
         ihi = this_block%ihi
         jlo = this_block%jlo
         jhi = this_block%jhi
         
         do j=1,ny_block
         do i=1,nx_block
         do k=1,ndim3
            n = n+1
            if (j < jlo .or. j>jhi) then
               dof3d(n) = 0
            else if (i < ilo .or. i > ihi) then
               dof3d(n) = 0
            else
               lon = this_block%i_glob(i)
               lat = this_block%j_glob(j)
               dof3d(n) = k + ((lon-1) + (lat-1)*nx_global)*ndim3
            endif
         end do !ndim3
         enddo  !i
         enddo  !j
      end do    !iblk

      call pio_initdecomp(ice_pio_subsystem, pio_double, (/ndim3,nx_global,ny_global/), &
           dof3d, iodesc)

      deallocate(dof3d)

    end subroutine ice_pio_initdecomp_3d_inner


end module ice_pio