!|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

 module forcing_ap

!BOP
! !MODULE: forcing_ap
!
! !DESCRIPTION:
!  Contains routines and variables used for determining the
!  surface atmopheric pressure for using atmospheric pressure
!  forcing at the surface.
!
! !REVISION HISTORY:
!  SVN:$Id$
!
! !USES:

   use kinds_mod
   use domain
   use constants
   use broadcast
   use io
   use forcing_tools
   use time_management
   use grid
   use exit_mod

   implicit none
   private
   save

! !PUBLIC MEMBER FUNCTIONS:

   public:: init_ap,     &
            set_ap

! !PUBLIC DATA MEMBERS:

   real (r8), public :: & ! public for use in restart
      ap_interp_last      ! time when last interpolation was done

   character (char_len), public :: &! needed in barotropic
      ap_data_type     !  keyword for the period of the forcing data

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  module private variables
!
!-----------------------------------------------------------------------

   real (r8), dimension(:,:,:,:,:), allocatable :: &
      ATM_PRESS_DATA !  surface atm pressure at multiple times that
                     !  may be interpolated in time to get ATM_PRESS

   real (r8), dimension(12) :: &
      ap_data_time  !  time (in hours) corresponding to pressure
                    !  in ATM_PRESS_DATA

   real (r8), dimension(20) :: &
      ap_data_renorm   ! scale factors for changing input units
                       ! to model units

   real (r8) ::       &
      ap_data_inc,    &! time increment between values of forcing data
      ap_data_next,   &! time to be used for next forcing data
      ap_data_update, &! time when new forcing value added to interp set
      ap_interp_inc,  &! time increment between interpolation
      ap_interp_next   ! time when next interpolation will be done

   integer (int_kind) ::   &
      ap_interp_order,     &! order of temporal interpolation
      ap_data_time_min_loc  ! index of 3rd dimension of ATM_PRESS_DATA,
                            !   containing the minimum forcing time

   character (char_len) :: &
      ap_filename,    &!  name of file conainting forcing data
      ap_file_fmt,    &!  data format of forcing file (bin or nc)
      ap_interp_freq, &!  keyword for period of temporal interpolation
      ap_interp_type, &
      ap_data_label,  &
      ap_formulation

   character (char_len), dimension(:), allocatable :: &
      ap_data_names    ! field names for getting ap data

   integer (int_kind), dimension(:), allocatable :: &
      ap_bndy_loc,    &! location and field type for ghost cell
      ap_bndy_type     !   updates

   type (datafile) :: &
      ap_data_file    ! io file type for ap data file

   type (io_field_desc) :: &
      ap_data_in      ! io field type for reading ap data

!EOC
!***********************************************************************

 contains

!***********************************************************************
!BOP
! !IROUTINE: init_ap
! !INTERFACE:

 subroutine init_ap(ATM_PRESS)

! !DESCRIPTION:
!  Initializes atm pressure forcing by either calculating or reading
!  in the atm pressure.  Also does initial book-keeping concerning
!  when new data is needed for the temporal interpolation and
!  when the forcing will need to be updated.
!
! !REVISION HISTORY:
!  same as module

! !INPUT/OUTPUT PARAMETERS:

   real (r8), dimension(nx_block,ny_block,max_blocks_clinic), &
      intent(inout) :: &
      ATM_PRESS        !  surface momentum fluxes at current timestep

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

   integer (int_kind) ::     &
      n, iblock, nu,         &! dummy loop index and unit number
      nml_error               ! namelist i/o error flag

   character (char_len) ::   &
      forcing_filename        ! name of file containing forcing data

   type (block) :: &
      this_block    ! block information for current block

   type (io_dim) :: &
      i_dim, j_dim, &! dimension descriptor for horiz dims
      month_dim      ! dimension descriptor for monthly fields

   real (r8), dimension(nx_block,ny_block) :: &
      WORK

   real (r8), dimension(:,:,:,:), target, allocatable :: &
      TEMP_DATA   ! temp array for reading monthly data

   namelist /forcing_ap_nml/ ap_data_type,   ap_data_inc,    &
                             ap_interp_type, ap_interp_freq, &
                             ap_interp_inc,  ap_filename,    &
                             ap_data_renorm, ap_file_fmt

!-----------------------------------------------------------------------
!
!  read atmospheric pressure namelist input after setting
!  default values.
!
!-----------------------------------------------------------------------

   ap_data_type   = 'none'
   ap_data_inc    = 1.e20_r8
   ap_interp_type = 'nearest'
   ap_interp_freq = 'never'
   ap_interp_inc  = 1.e20_r8
   ap_filename    = 'unknown-ap'
   ap_file_fmt    = 'bin'
   ap_data_renorm = c1
!  ap_data_renorm = c10  ! convert from Pa to dynes/cm**2

   if (my_task == master_task) then
      open (nml_in, file=nml_filename, status='old', iostat=nml_error)
      if (nml_error /= 0) then
         nml_error = -1
      else
         nml_error =  1
      endif
      !*** keep reading until find right namelist
      do while (nml_error > 0)
         read(nml_in, nml=forcing_ap_nml,iostat=nml_error)
      end do
      if (nml_error == 0) close(nml_in)
   end if

   call broadcast_scalar(nml_error, master_task)
   if (nml_error /= 0) then
      call exit_POP(sigAbort,'ERROR reading forcing_ap namelist')
   endif

   call broadcast_scalar(ap_data_type,   master_task)
   call broadcast_scalar(ap_data_inc,    master_task)
   call broadcast_scalar(ap_interp_type, master_task)
   call broadcast_scalar(ap_interp_freq, master_task)
   call broadcast_scalar(ap_interp_inc,  master_task)
   call broadcast_scalar(ap_filename,    master_task)
   call broadcast_scalar(ap_file_fmt,    master_task)
   call broadcast_array (ap_data_renorm, master_task)

   ap_formulation = char_blank

!-----------------------------------------------------------------------
!
!  convert data_type to 'monthly-calendar' if input is 'monthly'
!
!-----------------------------------------------------------------------

   if (ap_data_type == 'monthly') ap_data_type = 'monthly-calendar'

!-----------------------------------------------------------------------
!
!  convert interp_type to corresponding integer value.
!
!-----------------------------------------------------------------------

   select case (ap_interp_type)

   case ('nearest')
      ap_interp_order = 1

   case ('linear')
      ap_interp_order = 2

   case ('4point')
      ap_interp_order = 4

   case default
      call exit_POP(sigAbort, &
                    'init_ap: Unknown value for ap_interp_type')

   end select

!-----------------------------------------------------------------------
!
!  set values of atm pressure arrays (ATM_PRESS or ATM_PRESS_DATA)
!  depending on the type of the atm pressure data.
!
!-----------------------------------------------------------------------

   select case (ap_data_type)

   case ('none')

      !***  no atm pressure, therefore no interpolation in time needed
      !***  (ap_interp_freq = 'none'), nor are there any new values
      !***  to be used (ap_data_next = ap_data_update = never).

      ATM_PRESS = c0
      ap_data_next = never
      ap_data_update = never
      ap_interp_freq = 'never'

   case ('analytic')

      !*** simple analytic atm pressure that is constant in time,
      !*** therefore no interpolation in time is needed
      !*** (ap_interp_freq = 'none'), nor are there any new values
      !*** to be used (ap_data_next = ap_data_update = never).

      !$OMP PARALLEL DO PRIVATE(iblock,this_block,WORK)

      do iblock=1,nblocks_clinic
         this_block = get_block(blocks_clinic(iblock),iblock)

         WORK = (ULAT(:,:,iblock)*radian -  25.0_r8)**2 + &
                (ULON(:,:,iblock)*radian - 180.0_r8)**2

         where (CALCT(:,:,iblock) .and. WORK < 100.0_r8**2)
            ATM_PRESS(:,:,iblock) = grav*exp(-WORK*p5/15.0_r8**2)
            ATM_PRESS(:,:,iblock) = WORK - &
                   sum(WORK*TAREA(:,:,iblock),mask=CALCT(:,:,iblock))/ &
                   sum(     TAREA(:,:,iblock),mask=CALCT(:,:,iblock))
         elsewhere
            ATM_PRESS(:,:,iblock) = c0
         end where

         if (ap_data_renorm(1) /= c1) ATM_PRESS(:,:,iblock) = &
                                      ATM_PRESS(:,:,iblock)*  &
                                      ap_data_renorm(1)

      end do
      !$OMP END PARALLEL DO

      ap_data_next = never
      ap_data_update = never
      ap_interp_freq = 'never'

   case ('annual')

      !*** annual mean climatological atm pressure (read in from a file)
      !*** constant in time, therefore no interpolation in time needed
      !*** (ap_interp_freq = 'none'), nor are there any new values
      !***  to be used (ap_data_next = ap_data_update = never).

      allocate(ap_data_names(1), &
               ap_bndy_loc  (1), &
               ap_bndy_type (1))
      ap_data_names(1) = 'ATMOSPHERIC PRESSURE'
      ap_bndy_loc  (1) = field_loc_center
      ap_bndy_type (1) = field_type_scalar

      ap_data_file = construct_file(ap_file_fmt,                 &
                                    full_name=trim(ap_filename), &
                                    record_length=rec_type_dbl,  &
                                    recl_words=nx_global*ny_global)

      call data_set(ap_data_file, 'open_read')

      i_dim = construct_io_dim('i',nx_global)
      j_dim = construct_io_dim('j',ny_global)

      ap_data_in = construct_io_field(trim(ap_data_names(1)),  &
                     dim1=i_dim, dim2=j_dim,                   &
                     field_loc = ap_bndy_loc(1),               &
                     field_type = ap_bndy_type(1),             &
                     d2d_array = ATM_PRESS)

      call data_set (ap_data_file, 'define', ap_data_in)
      call data_set (ap_data_file, 'read',   ap_data_in)
      call data_set (ap_data_file, 'close')
      call destroy_io_field(ap_data_in)
      call destroy_file(ap_data_file)

      if (ap_data_renorm(1) /= c1) ATM_PRESS = &
                                   ATM_PRESS*ap_data_renorm(1)

      ap_data_next = never
      ap_data_update = never
      ap_interp_freq = 'never'

      if (my_task == master_task) then
         write(stdout,blank_fmt)
         write(stdout,'(a22,a)') ' AP Annual file read: ', &
                                 trim(forcing_filename)
      endif

   case ('monthly-equal','monthly-calendar')

      !*** monthly mean climatological atm pressure. all 12 months
      !*** are read in from a file. interpolation order
      !*** (ap_interp_order) may be specified with namelist input.

      allocate(ATM_PRESS_DATA(nx_block,ny_block,max_blocks_clinic, &
                                                          1,0:12), &
               TEMP_DATA(nx_block,ny_block,12,max_blocks_clinic))

      allocate(ap_data_names(1), &
               ap_bndy_loc  (1), &
               ap_bndy_type (1))

      ATM_PRESS_DATA = c0
      ap_data_names(1) = 'ATMOSPHERIC PRESSURE'
      ap_bndy_loc  (1) = field_loc_center
      ap_bndy_type (1) = field_type_scalar

      call find_forcing_times(ap_data_time, ap_data_inc,            &
                              ap_interp_type, ap_data_next,         &
                              ap_data_time_min_loc, ap_data_update, &
                              ap_data_type)

      ap_data_file = construct_file(ap_file_fmt,                 &
                                    full_name=trim(ap_filename), &
                                    record_length=rec_type_dbl,  &
                                    recl_words=nx_global*ny_global)

      call data_set(ap_data_file, 'open_read')

      i_dim = construct_io_dim('i',nx_global)
      j_dim = construct_io_dim('j',ny_global)
      month_dim = construct_io_dim('month',12)

      ap_data_in = construct_io_field(trim(ap_data_names(1)),  &
                     dim1=i_dim, dim2=j_dim, dim3=month_dim,   &
                     field_loc = ap_bndy_loc(1),               &
                     field_type = ap_bndy_type(1),             &
                     d3d_array = TEMP_DATA)

      call data_set (ap_data_file, 'define', ap_data_in)
      call data_set (ap_data_file, 'read',   ap_data_in)
      call destroy_io_field(ap_data_in)

      call data_set (ap_data_file, 'close')
      call destroy_file(ap_data_file)

      !$OMP PARALLEL DO PRIVATE(iblock,n)
      do iblock=1,nblocks_clinic
      do n=1,12
         ATM_PRESS_DATA(:,:,iblock,1,n) = TEMP_DATA(:,:,n,iblock)

         if (ap_data_renorm(1) /= c1) &
            ATM_PRESS_DATA(:,:,iblock,1,n) = &
            ATM_PRESS_DATA(:,:,iblock,1,n)*ap_data_renorm(1)
      end do
      end do
      !$OMP END PARALLEL DO

      deallocate(TEMP_DATA)

      if (my_task == master_task) then
         write(stdout,blank_fmt)
         write(stdout,'(a23,a)') ' Monthly AP file read: ', &
                                 trim(forcing_filename)
      endif

   case ('n-hour')

      !*** atm pressure specified every n-hours, where the n-hour
      !*** increment should be specified with namelist input
      !*** (ap_data_inc).  Only as many times as are necessary based
      !*** on the order of the temporal interpolation scheme
      !*** (ap_interp_order) reside in memory at any given time.

      allocate (ATM_PRESS_DATA(nx_block,ny_block,max_blocks_clinic, &
                                              1,0:ap_interp_order))
      allocate(ap_data_names(1), &
               ap_bndy_loc  (1), &
               ap_bndy_type (1))

      ATM_PRESS_DATA = c0
      ap_data_names(1) = 'ATMOSPHERIC PRESSURE'
      ap_bndy_loc  (1) = field_loc_center
      ap_bndy_type (1) = field_type_scalar

      call find_forcing_times(ap_data_time,         ap_data_inc,    &
                              ap_interp_type,       ap_data_next,   &
                              ap_data_time_min_loc, ap_data_update, &
                              ap_data_type)

      do n = 1, ap_interp_order
         call get_forcing_filename(forcing_filename, ap_filename, &
                                   ap_data_time(n), ap_data_inc)

         ap_data_file = construct_file(ap_file_fmt,                 &
                                  full_name=trim(forcing_filename), &
                                  record_length=rec_type_dbl,       &
                                  recl_words=nx_global*ny_global)

         call data_set(ap_data_file, 'open_read')

         i_dim = construct_io_dim('i',nx_global)
         j_dim = construct_io_dim('j',ny_global)

         ap_data_in = construct_io_field(trim(ap_data_names(1)),  &
                        dim1=i_dim, dim2=j_dim,                   &
                        field_loc = ap_bndy_loc(1),               &
                        field_type = ap_bndy_type(1),             &
                        d2d_array = ATM_PRESS_DATA(:,:,:,1,n))

         call data_set (ap_data_file, 'define', ap_data_in)
         call data_set (ap_data_file, 'read',   ap_data_in)
         call data_set (ap_data_file, 'close')
         call destroy_io_field(ap_data_in)
         call destroy_file(ap_data_file)

         if (my_task == master_task) then
            write(stdout,blank_fmt)
            write(stdout,*) ' AP n-hour file read: ', forcing_filename
         endif
      enddo

      if (ap_data_renorm(1) /= c1) ATM_PRESS_DATA = &
                                   ATM_PRESS_DATA*ap_data_renorm(1)

   case default

      call exit_POP(sigAbort,'init_ap: Unknown value for ap_data_type')

   end select

!-----------------------------------------------------------------------
!
!  now check interpolation period (ap_interp_freq) to set the
!    time for the next temporal interpolation (ap_interp_next).
!
!  if no interpolation is to be done, set next interpolation time
!    to a large number so the atm pressure update test in routine
!    set_surface_forcing will always be false.
!
!  if interpolation is to be done every n-hours, find the first
!    interpolation time greater than the current time.
!
!  if interpolation is to be done every timestep, set next interpolation
!    time to a large negative number so the atm pressure update test in
!    routine set_surface_forcing will always be true.
!
!-----------------------------------------------------------------------

   select case (ap_interp_freq)

   case ('never')

      ap_interp_next = never
      ap_interp_last = never
      ap_interp_inc  = c0

   case ('n-hour')

      call find_interp_time(ap_interp_inc, ap_interp_next)

   case ('every-timestep')

      ap_interp_next = always
      ap_interp_inc  = c0

   case default

      call exit_POP(sigAbort, &
                    'init_ap: Unknown value for ap_interp_freq')

   end select

   if (nsteps_total == 0) ap_interp_last = thour00

!-----------------------------------------------------------------------
!
!  echo forcing options to stdout.
!
!-----------------------------------------------------------------------

   ap_data_label = 'Surface Atmospheric Pressure'
   call echo_forcing_options(ap_data_type,   ap_formulation,  &
                             ap_data_inc,    ap_interp_freq,  &
                             ap_interp_type, ap_interp_inc,   &
                             ap_data_label)

!-----------------------------------------------------------------------
!EOC

 end subroutine init_ap

!***********************************************************************
!BOP
! !IROUTINE: set_ap
! !INTERFACE:

 subroutine set_ap(ATM_PRESS)

! !DESCRIPTION:
!  Updates the current value of the atm pressure array (ATM\_PRESS) by
!  interpolating to the current time.  It may also be necessary to
!  use (read) new data values in the interpolation.
!
! !REVISION HISTORY:
!  same as module

! !INPUT/OUTPUT PARAMETERS:

   real (r8), dimension(nx_block,ny_block,max_blocks_clinic), &
      intent(inout) :: &
      ATM_PRESS        !  atmospheric pressure at current timestep

!EOP
!BOC
!-----------------------------------------------------------------------
!
!  local variables
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!  check if new data is necessary for interpolation.  if yes, then
!  shuffle indices in ATM_PRESS_DATA and ap_data_time arrays.
!  and read in new data if necessary ('n-hour' case).  also
!  increment values of ap_data_time_min_loc, ap_data_next and
!  ap_data_update.  then do the temporal interpolation.
!
!-----------------------------------------------------------------------

   if (thour00 >= ap_interp_next .or. nsteps_run == 0) then

   select case(ap_data_type)

   case ('monthly-equal','monthly-calendar')

      ap_data_label = 'AP Monthly'
      if (thour00 >= ap_data_update)                                  &
         call update_forcing_data(ap_data_time, ap_data_time_min_loc, &
                                  ap_interp_type, ap_data_next,       &
                                  ap_data_update, ap_data_type,       &
                                  ap_data_inc,                        &
                                  ATM_PRESS_DATA(:,:,:,:,1:12),       &
                                  ap_data_renorm,                     &
                                  ap_data_label, ap_data_names,       &
                                  ap_bndy_loc, ap_bndy_type,          &
                                  ap_filename, ap_file_fmt)

      call interpolate_forcing(ATM_PRESS_DATA(:,:,:,:,0),          &
                               ATM_PRESS_DATA(:,:,:,:,1:12),       &
                               ap_data_time, ap_interp_type,       &
                               ap_data_time_min_loc,               &
                               ap_interp_freq, ap_interp_inc,      &
                               ap_interp_next, ap_interp_last,     &
                               nsteps_run)

      ATM_PRESS = ATM_PRESS_DATA(:,:,:,1,0)

   case ('n-hour')

      ap_data_label = 'AP n-hour'
      if (thour00 >= ap_data_update)                                  &
         call update_forcing_data(ap_data_time, ap_data_time_min_loc, &
                                  ap_interp_type, ap_data_next,       &
                                  ap_data_update, ap_data_type,       &
                                  ap_data_inc,                        &
                           ATM_PRESS_DATA(:,:,:,:,1:ap_interp_order), &
                                  ap_data_renorm,                     &
                                  ap_data_label, ap_data_names,       &
                                  ap_bndy_loc, ap_bndy_type,          &
                                  ap_filename, ap_file_fmt)


      call interpolate_forcing(ATM_PRESS_DATA(:,:,:,:,0),             &
                           ATM_PRESS_DATA(:,:,:,:,1:ap_interp_order), &
                               ap_data_time, ap_interp_type,          &
                               ap_data_time_min_loc,                  &
                               ap_interp_freq, ap_interp_inc,         &
                               ap_interp_next, ap_interp_last,        &
                               nsteps_run)

      ATM_PRESS = ATM_PRESS_DATA(:,:,:,1,0)

   end select

   if (nsteps_run /= 0) ap_interp_next = ap_interp_next + ap_interp_inc

   endif ! thour00 > ap_interp_next

!-----------------------------------------------------------------------
!EOC

 end subroutine set_ap

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

 end module forcing_ap

!|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
