module coefficients

   integer :: n_levels
   real, allocatable, dimension(:) :: a, b

   contains

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   ! Name: read_coeffs
   !
   ! Notes: Obtain table of coefficients for input by this routine from
   !        http://www.ecmwf.int/products/data/technical/model_levels/index.html
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   subroutine read_coeffs()
      
      implicit none

      integer :: i, nlvl, istatus

      open(21,file='ecmwf_coeffs',form='formatted',status='old',iostat=istatus)
  
      n_levels = 0

      if (istatus /= 0) then
         write(6,*) 'ERROR: Error opening ecmwf_coeffs' 
         return
      end if

      read(21,*,iostat=istatus) nlvl
      do while (istatus == 0)
         n_levels = n_levels + 1
         read(21,*,iostat=istatus) nlvl
      end do
      
      rewind(21)

      n_levels = n_levels - 1

      allocate(a(0:n_levels))
      allocate(b(0:n_levels))

      write(6,*) ' '
      write(6,*) 'Coefficients for each level:',n_levels
      do i=0,n_levels
         read(21,*,iostat=istatus) nlvl, a(i), b(i)
         write(6,'(i5,5x,f12.6,2x,f12.10)') nlvl, a(i), b(i)
      end do
      write(6,*) ' '

      close(21)
      
   end subroutine read_coeffs


   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   ! Name: cleanup_coeffs
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   subroutine cleanup_coeffs()

      implicit none

      n_levels = 0
      if (allocated(a)) deallocate(a)
      if (allocated(b)) deallocate(b)

   end subroutine cleanup_coeffs

end module coefficients


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! calc_ecmwf_p
!
! The purpose of this program is to compute a 3d pressure field for ECMWF 
!    model-level data sets; the code works in the WPS intermediate file format, 
!    reading a PSFC field from intermediate files, the A and B coefficients 
!    from a text file, ecmwf_coeffs, and writes the pressure data to an 
!    intermediate file.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
program calc_ecmwf_p

   use coefficients
   use date_pack
   use gridinfo_module
   use misc_definitions_module
   use module_debug
   use read_met_module
   use stringutil
   use write_met_module

   implicit none

   ! Local variables
   integer :: i, idiff, n_times, t, istatus, fg_idx
   real :: a_full, b_full
   character (len=19) :: valid_date, temp_date
   character (len=128) :: input_name
   real, allocatable, dimension(:,:) :: psfc
   real, allocatable, dimension(:,:,:) :: tt, qv
   logical :: is_psfc
   type (met_data) :: ecmwf_data, p_data, rh_data


   !
   ! Setup (read namelist and check on time range)
   !
   call get_namelist_params()

   call set_debug_level(WARN)

   ! Compute number of times that we will process
   call geth_idts(end_date(1), start_date(1), idiff)
   call mprintf((idiff < 0),ERROR,'Ending date is earlier than starting date in namelist for domain %i.', i1=1)

   n_times = idiff / interval_seconds

   ! Check that the interval evenly divides the range of times to process
   call mprintf((mod(idiff, interval_seconds) /= 0),WARN, &
                'In namelist, interval_seconds does not evenly divide '// &
                '(end_date - start_date) for domain %i. Only %i time periods '// &
                'will be processed.', i1=1, i2=n_times)

   fg_idx = 1

   input_name = fg_name(fg_idx)

   !
   ! Get coefficients for model level pressures
   !
   call read_coeffs()


   !
   ! Loop over all prefixes listed in namelist for fg_name
   !
   do while (input_name /= '*')

      !
      ! Loop over all times to be processed for this domain
      !
      do t=0,n_times

         ! Get the date string for the current time
         call geth_newdate(valid_date, trim(start_date(1)), t*interval_seconds)
         temp_date = ' '
         write(temp_date,'(a19)') valid_date(1:10)//'_'//valid_date(12:19)

         ! Initialize the module for reading in the met fields
         call read_met_init(trim(input_name), .false., temp_date(1:13), istatus)

         is_psfc = .false.

         if (istatus == 0) then
            call mprintf(.true.,STDOUT,'Reading from %s at time %s', s1=input_name, s2=temp_date(1:13))

            ! Process all fields and levels from the current file; read_next_met_field()
            !   will return a non-zero status when there are no more fields to be read.
            do while (istatus == 0)

               call read_next_met_field(ecmwf_data, istatus)

               if (istatus == 0) then

                  ! Have we found either PSFC or LOGSFP?
                  if ((trim(ecmwf_data%field) == 'PSFC' .and. ecmwf_data%xlvl == 200100.) &
                      .or. trim(ecmwf_data%field) == 'LOGSFP') then
                     p_data = ecmwf_data
                     p_data%field = 'PRESSURE '
                     p_data%desc  = 'Pressure'

                     rh_data = ecmwf_data
                     rh_data%field = 'RH       '
                     rh_data%units = '%'
                     rh_data%desc  = 'Relative humidity'

                     if (.not. allocated(psfc)) then
                        allocate(psfc(ecmwf_data%nx,ecmwf_data%ny))
                     end if
                     call mprintf(.true.,STDOUT,'Found %s field in %s:%s', &
                                  s1=ecmwf_data%field, s2=input_name, s3=temp_date(1:13))

                     is_psfc   = .true.
                     if (trim(ecmwf_data%field) == 'LOGSFP') then
                        psfc(:,:) = exp(ecmwf_data%slab(:,:))
                     else
                        psfc(:,:) = ecmwf_data%slab(:,:)
                     end if

                  ! Have we found temperature?
                  else if (trim(ecmwf_data%field) == 'TT') then

                     if (.not. allocated(tt)) then
                        allocate(tt(ecmwf_data%nx,ecmwf_data%ny,n_levels+1))  ! Extra level is for surface
                     end if

                     if (nint(ecmwf_data%xlvl) >= 1 .and. &
                         nint(ecmwf_data%xlvl) <= n_levels) then 
                        tt(:,:,nint(ecmwf_data%xlvl)) = ecmwf_data%slab
                     else if (nint(ecmwf_data%xlvl) == 200100) then
                        tt(:,:,n_levels+1) = ecmwf_data%slab
                     end if

                  ! Have we found specific humidity?
                  else if (trim(ecmwf_data%field) == 'SPECHUMD') then

                     if (.not. allocated(qv)) then
                        allocate(qv(ecmwf_data%nx,ecmwf_data%ny,n_levels+1))  ! Extra level is for surface
                     end if

                     if (nint(ecmwf_data%xlvl) >= 1 .and. &
                         nint(ecmwf_data%xlvl) <= n_levels) then 
                        qv(:,:,nint(ecmwf_data%xlvl)) = ecmwf_data%slab
                     else if (nint(ecmwf_data%xlvl) == 200100) then
                        qv(:,:,n_levels+1) = ecmwf_data%slab
                     end if

                  end if
                  
                  if (associated(ecmwf_data%slab)) deallocate(ecmwf_data%slab)
   
               end if
   
            end do
   
            call read_met_close()


            ! Now write out, for each level, the pressure field
            if (is_psfc) then

               allocate(p_data%slab(p_data%nx,p_data%ny))
               allocate(rh_data%slab(rh_data%nx,rh_data%ny))

               call write_met_init(trim(get_path(input_name))//'PRES', .false., temp_date(1:13), istatus)

               if (allocated(tt) .and. allocated(qv)) then
                  p_data%xlvl  = 200100.
                  p_data%slab  = psfc
! Surface RH should be computed from surface DEWPT by ungrib
!                  rh_data%xlvl = 200100.
!                  call calc_rh(tt(:,:,n_levels+1), qv(:,:,n_levels+1), psfc, rh_data%slab, rh_data%nx, rh_data%ny)
!                  call write_next_met_field(rh_data, istatus) 
                  call write_next_met_field(p_data, istatus) 
               else
                  call mprintf(.true.,WARN,'Either TT or SPECHUMD not found. No RH will be computed.')
               end if

               do i = 1, n_levels

                  a_full = 0.5 * (a(i-1) + a(i))   ! A and B are dimensioned (0:n_levels)
                  b_full = 0.5 * (b(i-1) + b(i))

                  p_data%xlvl = real(i)
                  p_data%slab = a_full + psfc * b_full

                  if (allocated(tt) .and. allocated(qv)) then
                     rh_data%xlvl = real(i)
                     call calc_rh(tt(:,:,i), qv(:,:,i), p_data%slab, rh_data%slab, rh_data%nx, rh_data%ny)
                     call write_next_met_field(rh_data, istatus) 
                  end if

                  call write_next_met_field(p_data, istatus) 

               end do

               call write_met_close()
  
               deallocate(p_data%slab)
               deallocate(rh_data%slab)

            end if

            if (allocated(tt))   deallocate(tt)
            if (allocated(qv))   deallocate(qv)
            if (allocated(psfc)) deallocate(psfc)

         else
            call mprintf(.true.,ERROR,'Problem opening %s at time %s', s1=input_name, s2=temp_date(1:13))
         end if

      end do 

      fg_idx = fg_idx + 1
      input_name = fg_name(fg_idx)

   end do 

   call cleanup_coeffs()

   stop

end program calc_ecmwf_p


subroutine calc_rh(t, qv, p, rh, nx, ny)

   implicit none

   ! Arguments
   integer, intent(in) :: nx, ny
   real, dimension(nx, ny), intent(in)  :: t, qv, p
   real, dimension(nx, ny), intent(out) :: rh

   ! Constants
   real, parameter :: svp1=0.6112
   real, parameter :: svp2=17.67
   real, parameter :: svp3=29.65
   real, parameter :: svpt0=273.15
   real, parameter :: eps = 0.622

   ! Local variables
   integer :: i, j
   real :: es, e

   do j=1,ny
   do i=1,nx
      es=svp1*10.*exp(svp2*(t(i,j)-svpt0)/(t(i,j)-svp3))
      e=qv(i,j)*p(i,j)/100./(eps+qv(i,j)*(1.-eps)) ! qv is specific humidity
      rh(i,j) = 100.0 * e/es
   end do
   end do

end subroutine calc_rh
