program gen_be_etkf

!---------------------------------------------------------------------- 
!  Purpose : Perform an Ensemble Transform Kalman Filter (ETKF) rescaling
!  of WRF ensemble forecast data.
!
!  Dale Barker (NCAR/MMM)     January 2006    WRF/ETKF interface. 
!  Xuguang Wang (NOAA)        January 2006    ETKF algorithm.
!  Arthur P. Mizzi (NCAR/MMM) February 2011:  Modified for ETKF obs filtering, ETKF obs error filtering, 
!                                             and for LETKF data.  Also, the ETKF interface uses the 
!                                             extended ob.etkf files (including additional metadata for
!                                             filtering and LETKF formulation).  Also, for hybrid cycling
!                                             algorithm, this procedure modified to output ensemble 
!                                             perturbations.      
!
!----------------------------------------------------------------------

#ifdef crayx1
#define iargc ipxfargc
#endif

   use da_control, only : trace_use, stdout,filename_len
!   use da_gen_be
   use da_etkf, only : da_solve_etkf, da_solve_letkf, rand_filter
   use da_reporting, only : da_error, message

   implicit none

#include "netcdf.inc"

   integer, parameter    :: max_num_vars = 50         ! Maximum number of variables.
   integer, parameter    :: max_num_dims = 20         ! Maximum number of dimensions.
   integer, parameter    :: unit = 100                ! Unit number.

   character (len=filename_len)   :: input_file                ! Input file. 
   character (len=filename_len)   :: output_file               ! Output file. 
   character (len=3)     :: ce                        ! Member index -> character.

   integer               :: num_members               ! Ensemble size.
   integer               :: nv                        ! Number of variables.
   integer               :: num_obs                   ! Number of observations.
   integer               :: naccumt1                  ! Number of previous cycles.
   integer               :: naccumt2                  ! Number of previous cycles.
   integer               :: nstartaccum1              ! Cycle from which naccumt1 cycle starts.
   integer               :: nstartaccum2              ! Cycle from which naccumt2 cycle starts.
   integer               :: nout                      ! Output record for inn. vec./ob. error var.
   integer               :: length                    ! Filename length.
   integer               :: rcode                     ! NETCDF return code.
   integer               :: cdfid                     ! NETCDF file IDs.
   integer               :: member, v, o, i, j, k, ijkv ! Loop counters.
   integer               :: ivtype                    ! 4=integer, 5=real, 6=d.p.
   integer               :: natts                     ! Number of field attributes.

   integer               :: index                     ! Array index.
   integer               :: nijkv                     ! Array counter.
   integer               :: iend                      ! End of array 
   real                  :: num_members_inv           ! 1 / ensemble size.
   real                  :: tainflatinput             ! Pre-specified inflation, if not using adaptive inflation.
   real                  :: rhoinput                  ! Pre-specified inflation, if not using adaptive rho factor.
   real                  :: ds                        ! Grid resolution (m).

   character(len=10)     :: cv(1:max_num_vars)        ! Default array of variable names.
   integer               :: id_var(1:max_num_vars)    ! NETCDF variable ID.
   integer               :: ndims(1:max_num_vars)     ! Number of field dimensions.
   integer               :: istart(1:max_num_vars)    ! Start index.
   integer               :: dimids(1:max_num_dims)    ! Array of dimension IDs.
   integer               :: one(1:max_num_dims)       ! Array of dimension starts.
   integer               :: dims(1:max_num_vars,1:max_num_dims)      ! Array of dimensions.
   integer               :: dim_prod(1:max_num_dims)  ! Product of array dimensions.

   real (kind=4), allocatable     :: data_r_3d(:,:,:)               ! 3-D Data array.
   real (kind=4), allocatable     :: data_r_4d(:,:,:,:)             ! 4-D Data array.

   real, pointer         :: xf(:,:)                   ! Ensemble perturbations.
   real, pointer         :: xf_mean(:)                ! Ensemble perturbation mean.
   real, pointer         :: xf_vari(:)                ! Ensemble perturbation variance.
   real, pointer         :: y(:,:)                    ! H(xf).
   real, pointer         :: sigma_o2(:)               ! Ob error variance.
   real, pointer         :: yo(:)                     ! Observation.
   real, pointer         :: ens_mean(:)               ! Variable ensemble mean.
   real, pointer         :: ens_stdv_pert_prior(:)    ! Variable prior perturbation std. dev.
   real, pointer         :: ens_stdv_pert_poste(:)    ! Variable posterior perturbation std. dev.

   character(len=150)    :: infl_let_file             ! Inflation factor data filename and path.
   character(len=150)    :: infl_fac_file             ! Inflation factor data filename and path.
   character(len=150)    :: eigen_val_file            ! Eigen value filename and path.
   character(len=150)    :: inno2_val_file            ! Innovation value filename and path.
   character(len=150)    :: proj2_val_file            ! Projection value filename and path.
   real                  :: num_members_m1_inv        ! 1 / (ensemble size-1).
   
   real, pointer                 :: apm_yo(:,:)
   real, pointer                 :: apm_y(:,:)
   real, pointer                 :: apm_sigma_o2(:,:)
   character(len=10), pointer    :: apm_obs_type(:,:)
   real, pointer                 :: apm_type(:,:)
   real, pointer                 :: apm_subtype(:,:)
   real, pointer                 :: apm_obslat(:,:)
   real, pointer                 :: apm_obslon(:,:)
   real, pointer                 :: apm_obsprs(:,:)
   real, pointer                 :: apm_obstim(:,:)
   integer, pointer              :: apm_irec(:,:)
   character(len=10), pointer    :: yo_obs_typ(:)
   real, pointer                 :: yo_typ(:)
   real, pointer                 :: yo_subtyp(:)
   real, pointer                 :: yo_lat(:)
   real, pointer                 :: yo_lon(:)
   real, pointer                 :: yo_prs(:)
   real, pointer                 :: yo_tim(:)
   real, pointer                 :: xf_lon(:)
   real, pointer                 :: xf_lat(:)
   real (kind=4), allocatable    :: ens_lon_T(:,:),ens_lon_u(:,:),ens_lon_v(:,:)
   real (kind=4), allocatable    :: ens_lat_T(:,:),ens_lat_u(:,:),ens_lat_v(:,:)
   real (kind=4), allocatable    :: ens_prs_T(:,:,:),ens_pbs_T(:,:,:)
   integer                       :: ityp,idim
   integer                       :: id_lat_T,id_lat_u,id_lat_v
   integer                       :: id_lon_T,id_lon_u,id_lon_v
   integer                       :: id_prs_T,id_pbs_T
   integer                       :: ndim_lat_T,ndim_lat_u,ndim_lat_v
   integer                       :: ndim_lon_T,ndim_lon_u,ndim_lon_v
   integer                       :: ndim_prs_T,ndim_pbs_T
   integer, pointer              :: dim_lat_T(:),dim_lat_u(:),dim_lat_v(:)
   integer, pointer              :: dim_lon_T(:),dim_lon_u(:),dim_lon_v(:)
   integer, pointer              :: dim_prs_T(:),dim_pbs_T(:)

   integer                       :: apm_num_obs
   integer                       :: apm_icnt
   integer                       :: apm_reject
   integer                       :: apm_imem
   logical                       :: infl_fac_TRNK,infl_fac_WG03,infl_fac_WG07,letkf_flg
   logical                       :: infl_fac_BOWL, rand_filt 
   integer, allocatable          :: ijk_idx(:,:,:,:)
   integer, allocatable          :: apl_idx(:,:)
   integer, allocatable          :: apl_ndim(:)
   integer                       :: napl1, napl2, nxs, nys, nzs, ixy, idx, apm_unit
   character(len=150),dimension(:),allocatable    :: filt_ob_etkf              ! Filtered ob.etkf file.
   integer                       :: rnd_seed, rnd_nobs, nobs_flt, nseed

   logical                       :: etkf_erro_flg, etkf_inno_flg, etkf_wrfda  
   real                          :: etkf_erro_max, etkf_erro_min
   real                          :: etkf_inno_max, etkf_inno_min

   namelist / gen_be_etkf_nl / num_members, nv, cv, &
                               naccumt1, naccumt2, nstartaccum1, nstartaccum2, &
                               nout, tainflatinput, rhoinput, infl_fac_file, &
                               eigen_val_file, inno2_val_file, proj2_val_file, &
                               infl_fac_TRNK, infl_fac_WG03, infl_fac_WG07, &
                               infl_fac_BOWL, letkf_flg, rand_filt, rnd_seed, &
                               rnd_nobs, infl_let_file, etkf_erro_max, etkf_erro_min, &
                               etkf_inno_max, etkf_inno_min, etkf_erro_flg, etkf_inno_flg, &
                               etkf_wrfda
   
!---------------------------------------------------------------------------------------------
   write(stdout,'(/a)')' [1] Initialize information.'
!-----------------------------------------------------------------------------------------

   num_members = 56
   nv = 1
   cv = "U"
   naccumt1 = 0
   naccumt2 = 0
   nstartaccum1 = 0
   nstartaccum2 = 0
   nout = 1 
   tainflatinput = 0.0
   rhoinput = 0.0
   infl_fac_file  = "inflation_factor.dat"
   infl_let_file  = "inflation_letkf.dat"
   eigen_val_file = "eigen_value.dat"
   inno2_val_file = "innovation_value.dat"
   proj2_val_file = "projection_value.dat"

   open(unit=unit, file='gen_be_etkf_nl.nl', &
        form='formatted', status='old', action='read')
   read(unit, gen_be_etkf_nl)
   close(unit)

   write(stdout,'(a,i4)')'   Number of ensemble members = ', num_members
   write(stdout,'(a,i4)')'   Number of prognostic variables = ', nv
   write(stdout,'(50a)')'    List of prognostic variables = ', cv(1:nv)
   write(stdout,'(a,i4)')'   naccumt1 = ', naccumt1
   write(stdout,'(a,i4)')'   naccumt2 = ', naccumt2
   write(stdout,'(a,i4)')'   nstartaccum1 = ', nstartaccum1
   write(stdout,'(a,i4)')'   nstartaccum2 = ', nstartaccum2
   write(stdout,'(a,i4)')'   nout = ', nout
   write(stdout,'(a,f15.5)')'   tainflatinput = ', tainflatinput
   write(stdout,'(a,f15.5)')'   rhoinput = ', rhoinput
   write(stdout,'(150a)')'   infl_fac_file = ',infl_fac_file
   write(stdout,'(150a)')'   infl_let_file = ',infl_let_file
   write(stdout,'(150a)')'   eigen_val_file = ',eigen_val_file
   write(stdout,'(150a)')'   inno2_val_file = ',inno2_val_file
   write(stdout,'(150a)')'   proj2_val_file = ',proj2_val_file
   write(stdout,'(a,l10)')'   infl_fac_TRNK = ',infl_fac_TRNK
   write(stdout,'(a,l10)')'   infl_fac_WG03 = ',infl_fac_WG03
   write(stdout,'(a,l10)')'   infl_fac_WG07 = ',infl_fac_WG07
   write(stdout,'(a,l10)')'   infl_fac_BOWL = ',infl_fac_BOWL
   write(stdout,'(a,l10)')'   letkf_flag = ',letkf_flg
   write(stdout,'(a,i20)')'   rnd_seed = ',rnd_seed
   write(stdout,'(a,i20)')'   rnd_nobs = ',rnd_nobs
   write(stdout,'(a,f15.5)')' etkf_erro_max = ',etkf_erro_max
   write(stdout,'(a,f15.5)')' etkf_erro_min = ',etkf_erro_min
   write(stdout,'(a,f15.5)')' etkf_inno_max = ',etkf_inno_max
   write(stdout,'(a,f15.5)')' etkf_inno_min = ',etkf_inno_min
   write(stdout,'(a,l10)')'   etkf_erro_flg = ',etkf_erro_flg
   write(stdout,'(a,l10)')'   etkf_inno_flg = ',etkf_inno_flg
   write(stdout,'(a,l10)')'   etkf_wrfda = ',etkf_wrfda

   num_members_inv = 1.0 / real(num_members)
   num_members_m1_inv = 1.0 / real(num_members)

   allocate( ens_mean(1:nv) )
   allocate( ens_stdv_pert_prior(1:nv) )
   allocate( ens_stdv_pert_poste(1:nv) )

!-----------------------------------------------------------------------------------------
   write(stdout,'(/a)')' [2] Read observation information.'
!-----------------------------------------------------------------------------------------
!
! LOOP OVER ENSEMBLE MEMBERS AND READ OB.ETKF.exxx DATA
   do member = 1, num_members
      write(unit=ce,FMT='(i3.3)') member
      input_file = 'ob.etkf.e'//ce  
      open(unit, file = input_file, status='old')
      read(unit,*) apm_num_obs
      print *, ' Number of unfiltered observations = ',apm_num_obs,member
      if ( member == 1 ) then
         allocate( apm_yo(1:apm_num_obs,1:num_members) )
         allocate( apm_y(1:apm_num_obs,1:num_members) )
         allocate( apm_sigma_o2(1:apm_num_obs,1:num_members) )
         allocate( apm_obs_type(1:apm_num_obs,1:num_members) )
         allocate( apm_type(1:apm_num_obs,1:num_members) )
         allocate( apm_subtype(1:apm_num_obs,1:num_members) )
         allocate( apm_obslat(1:apm_num_obs,1:num_members) )
         allocate( apm_obslon(1:apm_num_obs,1:num_members) )
         allocate( apm_obsprs(1:apm_num_obs,1:num_members) )
         allocate( apm_obstim(1:apm_num_obs,1:num_members) )
         allocate( apm_irec(1:apm_num_obs,1:num_members) )
      end if
!
! READ OB.ETKF DATA
      if(etkf_wrfda) then
         do o = 1, apm_num_obs
            read(unit,'(3f17.7)') apm_yo(o,member),apm_y(o,member),apm_sigma_o2(o,member)
         end do
         close(unit)
      else
         do o = 1, apm_num_obs
            read(unit,'(3f17.7,2x,a10,2x,2(f6.0,2x),4(f8.2,2x),i10)') &
            apm_yo(o,member),apm_y(o,member),apm_sigma_o2(o,member),apm_obs_type(o,member), &
            apm_type(o,member),apm_subtype(o,member),apm_obslat(o,member), &
            apm_obslon(o,member),apm_obsprs(o,member),apm_obstim(o,member),apm_irec(o,member)
         end do
      end if
   end do    
!
   apm_icnt=0
!
! FILTER OBS BASED ON TYPE 
! (SELECT ONLY SOUNDING OBS)
   if(etkf_wrfda) then
      write(stdout,'(a)') 'etkf_wrfda is true: NO ETKF OBS FILTERING '
      apm_icnt = apm_num_obs
   else
      do o = 1, apm_num_obs
         if((apm_type(o,1).ne.220.) .and. (apm_type(o,1).ne.120.)) then
            apm_reject=1
            do member=1,num_members
               apm_irec(o,member)=-999        
            end do 
         else
            apm_reject=0
            do member=1, num_members
!
! OBS ERROR TEST
               if((etkf_erro_flg .and. &
                  (apm_sigma_o2(o,member)/apm_yo(o,member).le.etkf_erro_min) .or. &
                  (apm_sigma_o2(o,member)/apm_yo(o,member).ge.etkf_erro_max)) .or. &
!
! INNOVATION TEST
                  (etkf_inno_flg .and. & 
                  (abs(apm_y(o,member))/apm_yo(o,member).le.etkf_inno_min) .or. &
                  (abs(apm_y(o,member))/apm_yo(o,member).ge.etkf_inno_max))) then  
                  apm_reject=1
               endif            
            end do
            if(apm_reject.eq.1) then 
               do member=1,num_members
                  apm_irec(o,member)=-999        
               end do 
            else         
               apm_icnt=apm_icnt+1
            end if
         end if
      end do
   end if     
!
! ASSIGN FILTERED OBS TO ORIGINAL ARRAYS 
   write(stdout,'(a,i10)')'   Number of filtered observations = ', apm_icnt
   nobs_flt = apm_icnt
!
! CONDUCT RANDOM FILTERING
   if(etkf_wrfda) then
      num_obs=nobs_flt
      allocate( y(1:num_obs,1:num_members) )
      allocate( sigma_o2(1:num_obs) )
      allocate( yo(1:num_obs) )
      apm_icnt=0
      do o=1,num_obs
         yo(o)=apm_yo(o,1)
         sigma_o2(o)=apm_sigma_o2(o,1)
         do member=1,num_members
            y(o,member)=apm_y(o,member)
         end do
      end do 
   else
      if(rand_filt) then
         nseed=rnd_seed
         num_obs=rnd_nobs
         allocate( y(1:num_obs,1:num_members) )
         allocate( sigma_o2(1:num_obs) )
         allocate( yo(1:num_obs) )
         allocate( yo_obs_typ(1:num_obs) )
         allocate( yo_typ(1:num_obs) )
         allocate( yo_subtyp(1:num_obs) )
         allocate( yo_lat(1:num_obs) )
         allocate( yo_lon(1:num_obs) )
         allocate( yo_prs(1:num_obs) )
         allocate( yo_tim(1:num_obs) )
         call rand_filter(apm_y,apm_sigma_o2,apm_yo,apm_obs_type,apm_type,apm_subtype, &
                         apm_obslat,apm_obslon,apm_obsprs,apm_obstim,y,sigma_o2,yo,yo_obs_typ, &
                         yo_typ,yo_subtyp,yo_lat,yo_lon,yo_prs,yo_tim,apm_irec,apm_num_obs, &
                         num_obs,nobs_flt,num_members,nseed)
      else   
         num_obs=nobs_flt
         allocate( y(1:num_obs,1:num_members) )
         allocate( sigma_o2(1:num_obs) )
         allocate( yo(1:num_obs) )
         allocate( yo_obs_typ(1:num_obs) )
         allocate( yo_typ(1:num_obs) )
         allocate( yo_subtyp(1:num_obs) )
         allocate( yo_lat(1:num_obs) )
         allocate( yo_lon(1:num_obs) )
         allocate( yo_prs(1:num_obs) )
         allocate( yo_tim(1:num_obs) )
         apm_icnt=0
         do o=1,apm_num_obs
            if(apm_irec(o,1).ne.-999) then
               apm_icnt=apm_icnt+1
               yo(apm_icnt)=apm_yo(o,1)
               yo_obs_typ(apm_icnt)=apm_obs_type(o,1)
               yo_typ(apm_icnt)=apm_type(o,1)
               yo_subtyp(apm_icnt)=apm_subtype(o,1)
               yo_lat(apm_icnt)=apm_obslat(o,1)
               yo_lon(apm_icnt)=apm_obslon(o,1)
               yo_prs(apm_icnt)=apm_obsprs(o,1)
               yo_tim(apm_icnt)=apm_obstim(o,1)
               sigma_o2(apm_icnt)=apm_sigma_o2(o,1)
               do member=1,num_members
                  y(apm_icnt,member)=apm_y(o,member)
               end do
!           print *, apm_icnt,yo(apm_icnt),y(apm_icnt,1),sigma_o2(apm_icnt), &
!           y(apm_icnt,2),y(apm_icnt,3),yo_lat(apm_icnt),yo_lon(apm_icnt), &
!           yo_prs(apm_icnt),yo_tim(apm_icnt) 
            endif
         enddo
      endif
!
! OPEN FILTERED OB.ETKF FILES 
      print *, "begin setup of ob.etkf.filt "
      allocate(filt_ob_etkf(num_members))
      do member=1,num_members
         write(unit=ce,FMT='(i3.3)') member
         filt_ob_etkf(member) = 'ob.etkf.filt.e'//ce 
         apm_unit=200+member 
         open(apm_unit, file = filt_ob_etkf(member), status='unknown')
         write(apm_unit,'(i17)') num_obs
      end do
      print *, "complete setup of ob.etkf.filt "
!
! SAVE FILTERED OB.ETKF FILES 
      do member=1,num_members
         apm_icnt=0
         do o=1, num_obs
               apm_icnt=apm_icnt+1
               apm_unit=200+member
               write(apm_unit,'(3f17.7,2x,a10,2x,2(f6.0,2x),4(f8.2,2x),i10)') &
               yo(o),y(o,member),sigma_o2(o),yo_obs_typ(o),yo_typ(o),yo_subtyp(o), &
               yo_lat(o),yo_lon(o),yo_prs(o),yo_tim(o),apm_icnt
         end do
      end do
      do member=1,num_members
         apm_unit=200+member
         close(apm_unit)
      enddo
      deallocate(filt_ob_etkf)
      deallocate(apm_yo,apm_y,apm_sigma_o2,apm_obs_type,apm_type,apm_subtype, &
      apm_obslat,apm_obslon,apm_obsprs,apm_obstim,apm_irec)  
   end if      
!
! MANIPULATE FILTERED OB.ETKF DATA FOR USE IN REMAINDER OF CODE
   do o = 1, num_obs
!
! CONVERT OBS ERROR STANDARD DEVIATION TO VARIANCE
      sigma_o2(o) = sigma_o2(o) * sigma_o2(o)
!
! CONVERT yo-H(xb) TO H(xb):
      do member = 1, num_members
         y(o,member) = yo(o) - y(o,member)
      end do
   end do

!-----------------------------------------------------------------------------------------
   write(stdout,'(/a)')' [3] Set up arrays using input ensemble mean forecast'
!-----------------------------------------------------------------------------------------
!
! OPEN THE ENSEMBLE MEAN DATA FILE
      input_file = 'etkf_input'
      length = len_trim(input_file)
      rcode = nf_open( input_file(1:length), NF_NOWRITE, cdfid )
!
! LOOP OVER VARIABLES (v) TO GET DIMENSIONS
      do v = 1, nv
!
! GET VARIABLE IDENTIFIER        
          rcode = nf_inq_varid ( cdfid, cv(v), id_var(v) )
         if ( rcode /= 0 ) then
            write(UNIT=message(1),FMT='(A,A)') &
            cv(v), ' variable is not in input file'
            call da_error(__FILE__,__LINE__,message(1:1)) 
         end if 
!
! CHECK IF VARIABLE IS TYPE REAL
         dimids = 0
         rcode = nf_inq_var( cdfid, id_var(v), cv(v), ivtype, ndims(v), dimids, natts )
         if ( ivtype /= 5 ) then
            write(UNIT=message(1),FMT='(A,A)') cv(v), ' variable is not real type'
            call da_error(__FILE__,__LINE__,message(1:1))
         end if
!
! GET DIMENSIONS FOR THIS VARIABLE
         dims(v,:) = 0
         do i = 1, ndims(v)
            rcode = nf_inq_dimlen( cdfid, dimids(i), dims(v,i) )
         end do
      end do
!
! SET COLUMN VECTOR INDEXING ARRAY
      one = 1
      istart(1) = 1
      do v = 2, nv
         istart(v) = istart(v-1) + product(dims(v-1,1:ndims(v-1)-1))
      end do
!
! CALCULATE SIZE OF COLUMN VECTOR AND ALLOCATE ARRAY
      nijkv = istart(nv) + product(dims(nv,1:ndims(nv)-1)) - 1
      allocate( xf_mean(1:nijkv) )
!   
! GET THE ENSEMBLE MEAN DATA
      do v = 1, nv
         index = istart(v)
         if(ndims(v) == 3) then
            allocate( data_r_3d(dims(v,1),dims(v,2),dims(v,3)))
            rcode = nf_get_vara_real( cdfid, id_var(v), one, dims(v,:), data_r_3d)
            do k = 1, dims(v,3)
               do j = 1, dims(v,2)
                  do i = 1, dims(v,1)
                     xf_mean(index) = data_r_3d(i,j,k)
                     index = index + 1
                  end do
               end do
            end do
            deallocate( data_r_3d )
         endif
         if(ndims(v) == 4) then
            allocate( data_r_4d(dims(v,1),dims(v,2),dims(v,3),dims(v,4)))
            rcode = nf_get_vara_real( cdfid, id_var(v), one, dims(v,:), data_r_4d)
            do k = 1, dims(v,3)
               do j = 1, dims(v,2)
                  do i = 1, dims(v,1)
                     xf_mean(index) = data_r_4d(i,j,k,1)
                     index = index + 1
                  end do
               end do
            end do
            deallocate( data_r_4d )
         endif
      enddo
!
! GET VAR IDs
      rcode=nf_inq_varid(cdfid,'XLONG',id_lon_T)
      rcode=nf_inq_varid(cdfid,'XLONG_U',id_lon_u)
      rcode=nf_inq_varid(cdfid,'XLONG_V',id_lon_v)
      rcode=nf_inq_varid(cdfid,'XLAT',id_lat_T)
      rcode=nf_inq_varid(cdfid,'XLAT_U',id_lat_u)
      rcode=nf_inq_varid(cdfid,'XLAT_V',id_lat_v)
      rcode=nf_inq_varid(cdfid,'P',id_prs_T)
      rcode=nf_inq_varid(cdfid,'PB',id_pbs_T)
!      print *,id_lon_T
!      print *,id_lon_u
!      print *,id_lon_v
!      print *,id_lat_T
!      print *,id_lat_u
!      print *,id_lat_v
!      print *,id_prs_T
!      print *,id_pbs_T
!
! GET DIMENSIONS
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_lon_T,'XLONG',ityp,ndim_lon_T,dimids,natts)
      allocate(dim_lon_T(ndim_lon_T))
      do idim=1,ndim_lon_T
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_lon_T(idim))
      enddo
!      print *,"dim_lon_T ",dim_lon_T
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_lon_u,'XLONG_U',ityp,ndim_lon_u,dimids,natts)
      allocate(dim_lon_u(ndim_lon_u))
      do idim=1,ndim_lon_u
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_lon_u(idim))
      enddo
!      print *,"dim_lon_u ",dim_lon_u
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_lon_v,'XLONG_V',ityp,ndim_lon_v,dimids,natts)
      allocate(dim_lon_v(ndim_lon_v))
      do idim=1,ndim_lon_v
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_lon_v(idim))
      enddo
!      print *,"dim_lon_v ",dim_lon_v
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_lat_T,'XLAT',ityp,ndim_lat_T,dimids,natts)
      allocate(dim_lat_T(ndim_lat_T))
      do idim=1,ndim_lat_T
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_lat_T(idim))
      enddo
!      print *,"dim_lat_T ",dim_lat_T
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_lat_u,'XLAT_U',ityp,ndim_lat_u,dimids,natts)
      allocate(dim_lat_u(ndim_lat_u))
      do idim=1,ndim_lat_u
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_lat_u(idim))
      enddo
!      print *,"dim_lat_u ",dim_lat_u
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_lat_v,'XLAT_V',ityp,ndim_lat_v,dimids,natts)
      allocate(dim_lat_v(ndim_lat_v))
      do idim=1,ndim_lat_v
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_lat_v(idim))
      enddo
!      print *,"dim_lat_v ",dim_lat_v
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_prs_T,'P',ityp,ndim_prs_T,dimids,natts)
      allocate(dim_prs_T(ndim_prs_T))
      do idim=1,ndim_prs_T
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_prs_T(idim))
      enddo
!      print *,"dim_prs_T ",dim_prs_T
      dimids(:)=0
      rcode=nf_inq_var(cdfid,id_pbs_T,'PB',ityp,ndim_pbs_T,dimids,natts)
      allocate(dim_pbs_T(ndim_pbs_T))
      do idim=1,ndim_pbs_T
         rcode=nf_inq_dimlen(cdfid,dimids(idim),dim_pbs_T(idim))
      enddo
!      print *,"dim_pbs_T ",dim_pbs_T
!
! ALLOCATE TEMPORARY ARRAYS
      allocate(ens_lon_T(dim_lon_T(1),dim_lon_T(2)))
      allocate(ens_lon_u(dim_lon_u(1),dim_lon_u(2)))
      allocate(ens_lon_v(dim_lon_v(1),dim_lon_v(2)))
      allocate(ens_lat_T(dim_lat_T(1),dim_lat_T(2)))
      allocate(ens_lat_u(dim_lat_u(1),dim_lat_u(2)))
      allocate(ens_lat_v(dim_lat_v(1),dim_lat_v(2)))
      allocate(ens_prs_T(dim_prs_T(1),dim_prs_T(2),dim_prs_T(3)))
      allocate(ens_pbs_T(dim_pbs_T(1),dim_pbs_T(2),dim_pbs_T(3)))
!
! READ WRF GRID DATA
      one(:)=1
      rcode=nf_get_vara_real(cdfid,id_lon_T,one,dim_lon_T,ens_lon_T)
!      print *,"Complete read id_lon_T",ens_lon_T(1,1),ens_lon_T(22,22),ens_lon_T(44,44)
      rcode=nf_get_vara_real(cdfid,id_lon_u,one,dim_lon_u,ens_lon_u)
!      print *,"Complete read id_lon_u",ens_lon_u(1,1),ens_lon_u(22,22),ens_lon_u(45,44)
      rcode=nf_get_vara_real(cdfid,id_lon_v,one,dim_lon_v,ens_lon_v)
!      print *,"Complete read id_lon_v",ens_lon_v(1,1),ens_lon_v(44,44)
      rcode=nf_get_vara_real(cdfid,id_lat_T,one,dim_lat_T,ens_lat_T)
!      print *,"Complete read id_lat_T",ens_lat_T(1,1),ens_lat_T(44,44)
      rcode=nf_get_vara_real(cdfid,id_lat_u,one,dim_lat_u,ens_lat_u)
!      print *,"Complete read id_lat_u",ens_lat_u(1,1),ens_lat_u(44,44)
      rcode=nf_get_vara_real(cdfid,id_lat_v,one,dim_lat_v,ens_lat_v)
!      print *,"Complete read id_lat_v",ens_lat_v(1,1),ens_lat_v(44,44)
      rcode=nf_get_vara_real(cdfid,id_prs_T,one,dim_prs_T,ens_prs_T)
!      print *,"Complete read id_prs_T",ens_prs_T(1,1,1),ens_prs_T(44,44,27)
      rcode=nf_get_vara_real(cdfid,id_pbs_T,one,dim_pbs_T,ens_pbs_T)
!      print *,"Complete read id_pbs_T",ens_pbs_T(1,1,1),ens_pbs_T(44,44,27)
      rcode = nf_close( cdfid )
!
!-----------------------------------------------------------------------------------------
   write(stdout,'(/a)')' [4] Extract necessary fields from WRF ensemble forecasts.'
!-----------------------------------------------------------------------------------------
!
! ALLOCATE ENSEMBLE MEMBER DATA ARRAYS
      allocate( xf(1:nijkv,1:num_members) )
      allocate( xf_lon(1:nijkv),xf_lat(1:nijkv))
      allocate( xf_vari(1:nijkv) )
!
! LOOP THROUGH ENSEMBLE MEMBERS TO GET DATA 
      do member = 1, num_members
!         print *, "Open ob.etkf files ", member
         write(UNIT=ce,FMT='(i3.3)')member
         input_file = 'etkf_input.e'//ce
         length = len_trim(input_file)
         rcode = nf_open( input_file(1:length), NF_NOWRITE, cdfid )
         do v = 1, nv
            index = istart(v)
            if(ndims(v) == 3) then
               allocate( data_r_3d(dims(v,1),dims(v,2),dims(v,3)))
               rcode = nf_get_vara_real( cdfid, id_var(v), one, dims(v,:), data_r_3d)
               do k = 1, dims(v,3)
                  do j = 1, dims(v,2)
                     do i = 1, dims(v,1)
                        xf(index,member) = data_r_3d(i,j,k)
                        index = index + 1
                     end do
                  end do
               end do
               deallocate( data_r_3d )
            endif
            if(ndims(v) == 4) then
               allocate( data_r_4d(dims(v,1),dims(v,2),dims(v,3),dims(v,4)))
               rcode = nf_get_vara_real( cdfid, id_var(v), one, dims(v,:), data_r_4d)
               do k = 1, dims(v,3)
                  do j = 1, dims(v,2)
                     do i = 1, dims(v,1)
                        xf(index,member) = data_r_4d(i,j,k,1)
                        index = index + 1
                     end do
                  end do
               end do
               deallocate( data_r_4d )
            endif
         end do
         rcode = nf_close( cdfid )
      end do
!
! ASSIGN WRF GRID DATA (THE GRID ASSIGNMENT IS FIXED TO CERTAIN DATA)
! (THE VERTICAL COORDINATE IS NOT INCORPORATED YET)
!
! ALSO CREATE LETKF EFFICIENCY ARRAY
      nxs=dims(1,1)
      nys=dims(2,2)
      nzs=dims(3,3)

      napl1=(nxs-1)*(nys-1)
      napl2=2*nv*nzs

      allocate (ijk_idx(1:nv,1:nxs,1:nys,1:nzs))
      allocate (apl_idx(1:napl1,1:napl2))
      allocate (apl_ndim(1:napl1))
      
      ijk_idx=0
      apl_idx=0
      apl_ndim=0

      do v = 1, nv
         index = istart(v)
         if(cv(v) .eq. 'U') then
            do k = 1, dims(v,3)
               do j = 1, dims(v,2)
                  do i = 1, dims(v,1)
                     xf_lat(index) = ens_lat_u(i,j)
                     xf_lon(index) = ens_lon_u(i,j)
                     ijk_idx(v,i,j,k)=index
                     index = index + 1
                  end do
               end do
            end do
         end if
         if(cv(v) .eq. 'V') then
            do k = 1, dims(v,3)
               do j = 1, dims(v,2)
                  do i = 1, dims(v,1)
                     xf_lat(index) = ens_lat_v(i,j)
                     xf_lon(index) = ens_lon_v(i,j)
                     ijk_idx(v,i,j,k)=index
                     index = index + 1
                  end do
               end do
            end do
         end if
         if(cv(v).eq.'W' .or. cv(v).eq.'T' .or. cv(v).eq.'W' .or. &
         cv(v).eq.'PH' .or. cv(v).eq.'QVAPOR') then
            do k = 1, dims(v,3)
               do j = 1, dims(v,2)
                  do i = 1, dims(v,1)
                     xf_lat(index) = ens_lat_T(i,j)
                     xf_lon(index) = ens_lon_T(i,j)
                     ijk_idx(v,i,j,k)=index
                     index = index + 1
                  end do
               end do
            end do
         end if
         if(cv(v) .eq. 'MU') then
            do j = 1, dims(v,2)
               do i = 1, dims(v,1)
                  xf_lat(index) = ens_lat_v(i,j)
                  xf_lon(index) = ens_lon_v(i,j)
                  ijk_idx(v,i,j,1)=index
                  index = index + 1
               end do
            end do
         end if
      end do
!
! SETUP APPLICATION INDEX ARRAY
      ixy=0
      do j=1,nys-1
         do i=1,nxs-1
            ixy=ixy+1
! U   
            idx=0
            do k=1,dims(1,3)
               idx=idx+1
               apl_idx(ixy,idx)=ijk_idx(1,i,j,k)
               if(i.eq.nxs-1) then
                 idx=idx+1
                 apl_idx(ixy,idx)=ijk_idx(1,nxs,j,k)
               endif
            enddo
! V          
            do k=1,dims(2,3)
               idx=idx+1
               apl_idx(ixy,idx)=ijk_idx(2,i,j,k)
               if(j.eq.nys-1) then
                 idx=idx+1
                 apl_idx(ixy,idx)=ijk_idx(2,i,nys,k)
               endif
            enddo
! W          
            do k=1,dims(3,3)
               idx=idx+1
               apl_idx(ixy,idx)=ijk_idx(3,i,j,k)
            enddo
! PH       
            do k=1,dims(4,3)
               idx=idx+1
               apl_idx(ixy,idx)=ijk_idx(4,i,j,k)
            enddo
! T          
            do k=1,dims(5,3)
               idx=idx+1
               apl_idx(ixy,idx)=ijk_idx(5,i,j,k)
            enddo
! Q          
            do k=1,dims(6,3)
               idx=idx+1
               apl_idx(ixy,idx)=ijk_idx(6,i,j,k)
            enddo
! MU       
            idx=idx+1
            apl_idx(ixy,idx)=ijk_idx(7,i,j,1)
            apl_ndim(ixy)=idx
         enddo  
      enddo
!
! CONVERT ENSEMBLE FORECAST DATA TO PERTURBATIONS
      do ijkv = 1, nijkv
         xf(ijkv,1:num_members) = xf(ijkv,1:num_members) - xf_mean(ijkv)
         xf_vari(ijkv) = sum(xf(ijkv,1:num_members)**2) * num_members_m1_inv
      end do
!
! PRINT A PRIORI SPATIALLY AVERAGED ENSEMBLE MEAN AND SQUARE ROOT OF 
! SPATIALLY AVERAGED ENSEMBLE VARIANCE
     do v = 1, nv
        iend = istart(v) + product(dims(v,1:ndims(v)-1)) - 1
        ens_mean(v) = sum(xf_mean(istart(v):iend)) / real(iend - istart(v) + 1)
        ens_stdv_pert_prior(v) =sqrt( sum(xf_vari(istart(v):iend)) / &
                                    real(iend - istart(v) + 1) )
     end do

!-----------------------------------------------------------------------------------------
   write(stdout,'(/a)')' [5] Call ETKF:'
!-----------------------------------------------------------------------------------------
!
! CALL ETKF SOLVER
     if (letkf_flg) then
        call da_solve_letkf( nijkv, num_members, num_obs, xf, y, sigma_o2, yo, nout, &
                       naccumt1, naccumt2, nstartaccum1, nstartaccum2, tainflatinput, &
                       rhoinput, infl_fac_file, eigen_val_file, inno2_val_file, &
                       proj2_val_file, infl_fac_TRNK, infl_fac_WG03, infl_fac_WG07, &
                       infl_fac_BOWL, yo_lat, yo_lon, yo_prs, xf_lat, xf_lon, ijk_idx, &
                       apl_idx, apl_ndim, napl1, napl2, nxs, nys, nzs, nv, infl_let_file)
     else
        call da_solve_etkf( nijkv, num_members, num_obs, xf, y, sigma_o2, yo, nout, &
                       naccumt1, naccumt2, nstartaccum1, nstartaccum2, tainflatinput, &
                       rhoinput, infl_fac_file, eigen_val_file, inno2_val_file, &
                       proj2_val_file, infl_fac_TRNK, infl_fac_WG03, infl_fac_WG07, &
                       infl_fac_BOWL)
     endif
!
! CALCULATE THE POSTERIORI STANDARD DEVIATION
     do ijkv = 1, nijkv
        xf_vari(ijkv) = sum(xf(ijkv,1:num_members)**2) * num_members_m1_inv
     end do
     write(stdout,'(5a)')'   v', ' Variable  ', '    Ensemble Mean', &
                       '  Prior Pert StDv', ' Post. Pert. StDv'
     do v = 1, nv
        iend = istart(v) + product(dims(v,1:ndims(v)-1)) - 1
        ens_stdv_pert_poste(v) =sqrt( sum(xf_vari(istart(v):iend)) / &
                                    real(iend - istart(v) + 1) )
        write(stdout,'(i4,1x,a10,3f17.7)')v, cv(v), &
        ens_mean(v), ens_stdv_pert_prior(v), ens_stdv_pert_poste(v)
     end do

!-----------------------------------------------------------------------------------------
   write(stdout,'(/a)')' [6] Output ETKF analysis ensemble:'
!----------------------------------------------------------------------------------------- 
     do member = 1, num_members
        write(UNIT=ce,FMT='(i3.3)')member
        input_file = 'etkf_output.e'//ce
        rcode = nf_open( trim(input_file), NF_WRITE, cdfid )
        if ( rcode /= 0 ) then
           print *, 'MISSING FILE ',trim(input_file)
           print *, 'ERROR OPENING OUTPUT FILE: ', nf_strerror(rcode)
           stop
        end if
        do v = 1, nv
           index = istart(v)
           if(ndims(v) == 3) then
              allocate( data_r_3d(dims(v,1),dims(v,2),dims(v,3)))
              rcode = nf_get_vara_real( cdfid, id_var(v), one, dims(v,:), data_r_3d)
              do k = 1, dims(v,3)
                 do j = 1, dims(v,2)
                    do i = 1, dims(v,1)
!
! APM: modification to output perturbation only
!                       data_r_3d(i,j,k) = xf_mean(index) + xf(index,member)
                       data_r_3d(i,j,k) = xf(index,member)
                       index = index + 1
                    end do
                 end do
              end do
              call ncvpt( cdfid, id_var(v), one, dims(v,:), data_r_3d, rcode)
              deallocate( data_r_3d )
           endif
           if(ndims(v) == 4) then
              allocate( data_r_4d(dims(v,1),dims(v,2),dims(v,3),dims(v,4)))
              rcode = nf_get_vara_real( cdfid, id_var(v), one, dims(v,:), data_r_4d)
              do k = 1, dims(v,3)
                 do j = 1, dims(v,2)
                    do i = 1, dims(v,1)
!
! APM: modification to output perturbation only
!                       data_r_4d(i,j,k,1) = xf_mean(index) + xf(index,member)
                       data_r_4d(i,j,k,1) = xf(index,member)
                       index = index + 1
                    end do
                 end do
              end do
              call ncvpt( cdfid, id_var(v), one, dims(v,:), data_r_4d, rcode)
              deallocate( data_r_4d )
           endif
        enddo
        rcode = nf_close( cdfid )
     end do
     deallocate( ens_mean )
     deallocate( ens_stdv_pert_prior )
     deallocate( ens_stdv_pert_poste )

end program gen_be_etkf