calcStatsLam_mod sourceΒΆ

   1module calcStatsLam_mod
   2  ! MODULE calcStatsLam_mod (prefix='csl' category='1. High-level functionality')
   3  !
   4  !:Purpose:  To compute homogeneous and isotropic background error covariances 
   5  !           from forecast error estimate in model variable space (limited-area
   6  !           version).
   7  !
   8  use mathPhysConstants_mod
   9  use earthConstants_mod
  10  use gridStateVector_mod
  11  use gridStateVectorFileIO_mod
  12  use lamSpectralTransform_mod
  13  use lamAnalysisGridTransforms_mod
  14  use horizontalCoord_mod
  15  use verticalCoord_mod
  16  use localizationFunction_mod
  17  use utilities_mod
  18  use menetrierDiag_mod
  19  use ensemblestatevector_mod
  20  use gridVariableTransforms_mod
  21  use varNameList_mod
  22  use gridBinning_mod
  23  use timeCoord_mod
  24  use midasMpi_mod
  25  use calcHeightAndPressure_mod
  26  implicit none
  27  save
  28  private
  29
  30  ! Public Subroutines
  31  public :: csl_setup, csl_computeBhi
  32  public :: csl_toolbox
  33
  34  type(struct_hco), pointer :: hco_ens => null() ! Ensemble horizontal grid parameters
  35  type(struct_hco), pointer :: hco_bhi => null() ! B matrix horizontal grid parameters
  36  type(struct_vco), pointer :: vco_bhi => null() ! B matrix vertical grid parameters
  37
  38  integer,external   :: get_max_rss
  39
  40  integer, parameter :: cv_model = 1
  41  integer, parameter :: cv_bhi   = 2
  42  integer, parameter :: nMaxControlVar = 10
  43  
  44  type  :: struct_cv
  45    character(len=4)     :: NomVar(2)
  46    integer              :: varLevIndexStart
  47    integer              :: varLevIndexEnd
  48    integer              :: nlev
  49    character(len=2)     :: GridType
  50    integer, allocatable :: ip1(:)
  51  end type struct_cv
  52
  53  type  :: struct_bhi
  54    type(struct_cv)  :: controlVariable(nMaxControlVar)
  55    character(len=4) :: momentumControlVar(2)
  56    integer          :: nControlVariable
  57    integer          :: nVarLev
  58  end type struct_bhi
  59
  60  type(struct_bhi)  :: bhi
  61
  62  integer :: nEns
  63  integer :: ip2_ens
  64
  65  logical :: initialized = .false.
  66  logical :: vertLoc, horizLoc, stdDevScaling
  67  logical :: ensContainsFullField
  68
  69  character(len=2)  :: ctrlVarHumidity
  70
  71  type(struct_ens)  :: ensPerts
  72
  73  real(8), pointer  :: pressureProfile_M(:), pressureProfile_T(:)
  74
  75
  76  real(8),allocatable :: scaleFactor_M(:), scaleFactor_T(:)
  77  real(8)             :: scaleFactor_SF
  78
  79  ! Namelist variables
  80  integer :: nTrunc
  81  character(len=12) :: WindTransform
  82  logical :: NormByStdDev
  83  logical :: SetTGtoZero
  84  logical :: writeEnsPert
  85  character(len=12) :: SpectralWeights
  86  real(8) :: vLocalize_wind     ! vertical length scale (in units of ln(Pressure))
  87  real(8) :: vlocalize_mass     ! vertical length scale (in units of ln(Pressure))
  88  real(8) :: vlocalize_humidity ! vertical length scale (in units of ln(Pressure))
  89  real(8) :: vlocalize_other    ! vertical length scale (in units of ln(Pressure))
  90  real(8) :: hlocalize_wind     ! horizontal length scale (in km)
  91  real(8) :: hlocalize_mass     ! horizontal length scale (in km)
  92  real(8) :: hlocalize_humidity ! horizontal length scale (in km)
  93  real(8) :: hlocalize_other    ! horizontal length scale (in km)
  94  character(len=4)  :: correlatedVariables(vnl_numvarmax)
  95
  96contains
  97  
  98  !--------------------------------------------------------------------------
  99  ! csl_setup
 100  !--------------------------------------------------------------------------
 101  subroutine csl_setup(nEns_in, hco_ens_in, vco_ens_in, ip2_in)
 102    !
 103    !:Purpose: To initialize this module
 104    !
 105    implicit none
 106
 107    ! Arguments:
 108    integer,                   intent(in)   :: nEns_in
 109    type(struct_vco), pointer, intent(in)   :: vco_ens_in
 110    type(struct_hco), pointer, intent(in)   :: hco_ens_in
 111    integer,                   intent(in)   :: ip2_in
 112
 113    ! Locals:
 114    integer :: nulnam, ier
 115    integer :: fclos, fnom
 116    integer :: varIndex, levIndex, k
 117    integer :: numStep
 118    integer, allocatable :: dateStampList(:)
 119    real(8) :: SurfacePressure
 120    character(len=256)  :: enspathname
 121    logical :: makeBiPeriodic
 122    character(len=4), pointer :: controlVarNames(:)
 123
 124    ! Namelist variables (local)
 125    real(8) :: scaleFactor(vco_maxNumLevels)
 126    integer :: grd_ext_x
 127    integer :: grd_ext_y
 128
 129    NAMELIST /NAMCALCSTATS_LAM/nTrunc,grd_ext_x,grd_ext_y,WindTransform,NormByStdDev, &
 130                               SetTGtoZero,writeEnsPert,SpectralWeights,              &
 131                               vLocalize_wind,vlocalize_mass,vlocalize_humidity,      &
 132                               hLocalize_wind,hlocalize_mass,hlocalize_humidity,      &
 133                               hLocalize_other,vlocalize_other,                       &
 134                               correlatedVariables,scaleFactor
 135
 136    write(*,*)
 137    write(*,*) 'csl_setup: Starting...'
 138    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 139
 140    !
 141    !- 1. Initialized the info on the ensemble
 142    !
 143    nEns=nEns_in
 144
 145    hco_ens => hco_ens_in
 146    vco_bhi => vco_ens_in
 147
 148    if ( vco_bhi%nlev_T == 1 .and. vco_bhi%nlev_T == 1 ) then
 149      write(*,*)
 150      write(*,*) 'Spatial dimensions = 2D'
 151    else
 152      write(*,*)
 153      write(*,*) 'Spatial dimensions = 3D'
 154    end if
 155
 156    ip2_ens = ip2_in
 157
 158    !
 159    !- 2. Read namelist NAMCALCSTATS_LAM
 160    !
 161    nTrunc        = 75       ! Default value
 162    grd_ext_x     = 10       ! Default value
 163    grd_ext_y     = 10       ! Default value
 164    WindTransform = 'PsiChi' ! Default value
 165    NormByStdDev  = .true.   ! Default value
 166    SetTGtoZero   = .false.  ! Default value
 167    writeEnsPert  = .false.  ! Default value
 168    correlatedVariables = '    '
 169    SpectralWeights = 'lst'  ! Default value
 170    vLocalize_wind      = -1.0d0 ! Default value (no vloc)
 171    vLocalize_mass      = -1.0d0 ! Default value (no vloc)
 172    vLocalize_humidity  = -1.0d0 ! Default value (no vloc)
 173    vLocalize_other     = -1.0d0 ! Default value (no vloc)
 174    hLocalize_wind      = -1.0d0 ! Default value (no hloc)
 175    hLocalize_mass      = -1.0d0 ! Default value (no hloc)
 176    hLocalize_humidity  = -1.0d0 ! Default value (no hloc)
 177    hLocalize_other     = -1.0d0 ! Default value (no hloc)
 178    scaleFactor(:)      =  1.0d0 ! Default value (no scaling)
 179    
 180    nulnam = 0
 181
 182    ier=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 183    read (nulnam,nml=namcalcstats_lam)
 184    write(*     ,nml=namcalcstats_lam)
 185    ier=fclos(nulnam)
 186
 187    write(*,*)
 188    write(*,*) 'Truncation = ', nTrunc
 189
 190    select case(trim(SpectralWeights))
 191    case ('lst')
 192      write(*,*)
 193      write(*,*) 'Using spectral weights from the lamSpectralTransform module'
 194    case ('legacy')
 195      write(*,*)
 196      write(*,*) 'Using spectral weights from the OLD VAR code in LAM mode'
 197    case default
 198      write(*,*)
 199      write(*,*) 'Unknown spectral weights TYPE : ', trim(SpectralWeights)
 200      call utl_abort('csl_setup')
 201    end select
 202
 203    !
 204    !- 3. Initialized the extended (bi-periodic) grid
 205    !
 206
 207    !- 3.1 Create the extended and non-extended grid prototype file
 208    if (mmpi_myid == 0) then
 209      call lgt_createLamTemplateGrids('./analysisgrid', hco_ens, vco_bhi, & ! IN
 210                                      grd_ext_x, grd_ext_y)                 ! IN
 211    end if
 212    call rpn_comm_barrier("GRID",ier)
 213
 214    !- 3.2 Setup the Extended B_HI grid
 215    call hco_setupFromFile( hco_bhi,'./analysisgrid', 'ANALYSIS', 'BHI' ) ! IN
 216
 217    !- 3.3 Setup the LAM analysis grid metrics
 218    call lgt_setupFromHCO( hco_bhi, hco_ens ) ! IN
 219
 220    !
 221    !- 4. Read the ensemble
 222    !
 223    numStep = 1
 224    allocate(dateStampList(numStep))
 225    dateStampList(:)  = -1
 226    call ens_allocate(ensPerts, nEns, numStep, hco_bhi, vco_bhi, dateStampList, &
 227                      hco_core_opt=hco_ens)
 228
 229    ensContainsFullField = .false.
 230    ctrlVarHumidity = 'LQ'
 231    ensPathName    = './ensemble'
 232    makeBiPeriodic = .true.
 233    call ens_readEnsemble(ensPerts, ensPathName, makeBiPeriodic, &
 234                          containsFullField_opt=ensContainsFullField)
 235
 236    if ( ctrlVarHumidity == 'LQ' .and. ens_varExist(ensPerts,'HU') .and. &
 237         ensContainsFullField ) then
 238      call gvt_transform(ensPerts,'HUtoLQ')
 239    end if
 240
 241    !
 242    !- 5.  Setup the control variables (model space and B_hi space)
 243    !
 244    nullify(controlVarNames)
 245    call ens_varNamesList(controlVarNames,ensPerts)
 246
 247    bhi%nControlVariable = size(controlVarNames) !count(mask) 
 248    write(*,*)
 249    write(*,*) 'Number of Control Variables = ', bhi%nControlVariable
 250
 251    !- 5.1 Set ControlVariable structure
 252    bhi%momentumControlVar(:) = 'NULL' 
 253    do varIndex = 1, bhi%nControlVariable
 254
 255      !- Set variable name
 256      bhi%controlVariable(varIndex)%nomvar(cv_model)  = trim(controlVarNames(varIndex))
 257      if ( bhi%controlVariable(varIndex)%nomvar(cv_model) == 'UU' ) then
 258        if ( trim(WindTransform) == 'PsiChi') then
 259          write(*,*)
 260          write(*,*) '--- Momentum Control Variables = Psi-Chi ---'
 261          bhi%momentumControlVar(1) = 'PP'
 262        else if ( trim(WindTransform) == 'VortDiv') then
 263          write(*,*)
 264          write(*,*) '--- Momentum Control Variables = Vort-Div ---'
 265          bhi%momentumControlVar(1) = 'QR'
 266          call utl_abort('Momentum Control Variables = Vort-Div not yest availble')
 267        else if ( trim(WindTransform) == 'UV') then
 268          write(*,*)
 269          write(*,*) '--- Momentum Control Variables = U-V ---'
 270          bhi%momentumControlVar(1) = 'UU'
 271        else
 272          write(*,*)
 273          write(*,*) 'Wind Transform not available ', trim((WindTransform))
 274          call utl_abort('csl_setup')
 275        end if
 276        bhi%controlVariable(varIndex)%nomvar(cv_bhi) = trim(bhi%momentumControlVar(1))
 277      else if ( bhi%controlVariable(varIndex)%nomvar(cv_model) == 'VV' ) then
 278        if ( trim(WindTransform) == 'PsiChi') then
 279          bhi%momentumControlVar(2) = 'CC'
 280        else if ( trim(WindTransform) == 'VortDiv') then
 281          bhi%momentumControlVar(2) = 'DD'
 282        else if ( trim(WindTransform) == 'UV') then
 283          bhi%momentumControlVar(2) = 'VV'
 284        else
 285          write(*,*)
 286          write(*,*) 'Wind Transform not available ', trim((WindTransform))
 287          call utl_abort('csl_setup')
 288        end if
 289        bhi%controlVariable(varIndex)%nomvar(cv_bhi) = bhi%momentumControlVar(2)
 290      else
 291        bhi%controlVariable(varIndex)%nomvar(cv_bhi) = bhi%controlVariable(varIndex)%nomvar(cv_model)
 292      end if
 293
 294      !- 5.2 Set Level info
 295      bhi%controlVariable(varIndex)%GridType =  vnl_varLevelFromVarname(bhi%controlVariable(varIndex)%nomvar(cv_model))
 296      bhi%controlVariable(varIndex)%nlev     =  ens_getNumLev(ensPerts,bhi%controlVariable(varIndex)%GridType)
 297
 298      write(*,*)
 299      write(*,*) 'Control Variable Name ', bhi%controlVariable(varIndex)%nomvar(cv_model)
 300      write(*,*) '   Number of Levels = ', bhi%controlVariable(varIndex)%nlev
 301      write(*,*) '   Type   of Levels = ', bhi%controlVariable(varIndex)%GridType
 302
 303      allocate( bhi%controlVariable(varIndex)%ip1 (bhi%controlVariable(varIndex)%nlev) )
 304
 305      if (bhi%controlVariable(varIndex)%GridType == 'TH') then
 306        bhi%controlVariable(varIndex)%ip1(:) = vco_bhi%ip1_T(:)
 307      else if (bhi%controlVariable(varIndex)%GridType == 'MM') then
 308        bhi%controlVariable(varIndex)%ip1(:) = vco_bhi%ip1_M(:)
 309      else if (bhi%controlVariable(varIndex)%GridType == 'SF') then
 310        bhi%controlVariable(varIndex)%ip1(:) = 0
 311      end if
 312
 313      bhi%controlVariable(varIndex)%varLevIndexStart =  &
 314           ens_getOffsetFromVarName(ensPerts,bhi%controlVariable(varIndex)%nomvar(cv_model)) + 1 
 315      bhi%controlVariable(varIndex)%varLevIndexEnd   =  &
 316           ens_getOffsetFromVarName(ensPerts,bhi%controlVariable(varIndex)%nomvar(cv_model)) + &
 317           bhi%controlVariable(varIndex)%nlev
 318
 319    end do
 320
 321    bhi%nVarLev = ens_getNumK(ensPerts)
 322
 323    !
 324    !- 6.  Transform u-wind and v-wind to control variables 
 325    !
 326    if (writeEnsPert) then
 327      call ens_writeEnsemble(ensPerts, './', 'MODELVAR_', 'MODELVAR', 'E', &
 328                             containsFullField_opt = ensContainsFullField)
 329    end if
 330
 331    if      ( bhi%momentumControlVar(1) == 'PP' .and. bhi%momentumControlVar(2) == 'CC' ) then
 332      call gvt_transform(ensPerts,'UVtoPsiChi')
 333      call ens_modifyVarName(ensPerts, 'UU', 'PP')
 334      call ens_modifyVarName(ensPerts, 'VV', 'CC')
 335    else if ( bhi%momentumControlVar(1) == 'QR' .and. bhi%momentumControlVar(2) == 'DD' ) then
 336      call gvt_transform(ensPerts,'UVtoVortDiv')
 337      call ens_modifyVarName(ensPerts, 'UU', 'QR')
 338      call ens_modifyVarName(ensPerts, 'VV', 'DD')
 339    end if
 340
 341    if (writeEnsPert) then
 342      call ens_writeEnsemble(ensPerts, './', 'CTRLVAR', 'CTRLVAR', 'E', &
 343                             containsFullField_opt = ensContainsFullField)
 344    end if
 345
 346    !
 347    !- 7.  Compute and remove the ensemble mean; compute the stdDev
 348    !
 349    call ens_computeMean(ensPerts)
 350    call ens_removeMean (ensPerts)
 351    call ens_computeStdDev(ensPerts)
 352
 353    !
 354    !- 8.  Setup the localization 
 355    !
 356
 357    !- 8.1 Setup horizontal localization
 358    if ( hLocalize_wind     < 0.d0 .and. hLocalize_mass  < 0.d0 .and. &
 359         hLocalize_humidity < 0.d0 .and. hLocalize_other < 0.d0) then
 360      write(*,*) 
 361      write(*,*) 'csl_setup: NO horizontal correlation localization will be performed'
 362      horizLoc=.false.
 363    else
 364      write(*,*) 
 365      write(*,*) 'csl_setup: horizontal correlation localization WILL BE performed'
 366      horizLoc=.true.
 367    end if
 368
 369    !- 8.2 Setup vertical localization
 370    if ( vLocalize_wind     < 0.d0 .and. vLocalize_mass  < 0.d0 .and. &
 371         vLocalize_humidity < 0.d0 .and. vLocalize_other < 0.d0 ) then
 372      write(*,*) 
 373      write(*,*) 'csl_setup: NO vertical correlation localization will be performed'
 374      vertLoc=.false.
 375    else
 376      write(*,*) 
 377      write(*,*) 'csl_setup: vertical correlation localization WILL BE performed'
 378      vertLoc=.true.
 379    end if
 380
 381    !
 382    !- 9.  Setup pressure profile for vertical localization
 383    !
 384    if (vco_bhi%vgridPresent) then
 385      SurfacePressure = 101000.D0
 386      call czp_fetch1DLevels(vco_bhi, SurfacePressure, &
 387                             profM_opt=pressureProfile_M, profT_opt=pressureProfile_T)
 388
 389      write(*,*)
 390      write(*,*) 'Pressure profile...'
 391      do k = 1, vco_bhi%nlev_M
 392        write(*,*) k, pressureProfile_M(k) / 100.d0, ' hPa'
 393      end do
 394      
 395      write(*,*)
 396      write(*,*) 'Pressure profile...'
 397      do k = 1, vco_bhi%nlev_T
 398        write(*,*) k, pressureProfile_T(k) / 100.d0, ' hPa'
 399      end do
 400
 401    end if
 402
 403    !
 404    !- 10.  Setup the scaling
 405    !
 406    if ( all(scaleFactor(:) == 1.d0) ) then
 407      write(*,*) 
 408      write(*,*) 'csl_setup: NO scaling of the StdDev will be performed'
 409      stdDevScaling=.false.
 410
 411    else
 412      write(*,*) 
 413      write(*,*) 'csl_setup: scaling of the StdDev WILL BE performed'
 414      stdDevScaling=.true.
 415
 416      allocate(scaleFactor_M(vco_bhi%nlev_M))
 417      allocate(scaleFactor_T(vco_bhi%nlev_T))
 418      do levIndex = 1, vco_bhi%nlev_T
 419        if (scaleFactor(levIndex) > 0.0d0) then 
 420          scaleFactor_T(levIndex) = sqrt(scaleFactor(levIndex))
 421        else
 422          scaleFactor_T(levIndex) = 0.0d0
 423        end if
 424      end do
 425      scaleFactor_M(1:vco_bhi%nlev_M) = scaleFactor_T(1:vco_bhi%nlev_M)
 426      scaleFactor_SF = scaleFactor_T(vco_bhi%nlev_T)
 427
 428    end if
 429
 430    !
 431    !- 11.  Ending
 432    !
 433    initialized = .true.
 434
 435    write(*,*)
 436    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 437    write(*,*) 'csl_setup: Done!'
 438
 439  end subroutine csl_setup
 440
 441  !--------------------------------------------------------------------------
 442  ! csl_computeBhi
 443  !--------------------------------------------------------------------------
 444  subroutine csl_computeBhi
 445    !
 446    !:Purpose: To compute an homogeneous and isotopic B matrix
 447    !
 448    implicit none
 449
 450    ! Locals:
 451    real(8), allocatable :: SpVertCorrel(:,:,:)
 452    real(8), allocatable :: TotVertCorrel(:,:)
 453    real(8), allocatable :: NormB(:,:,:)
 454    real(8), allocatable :: NormBsqrt(:,:,:)
 455    real(8), allocatable :: PowerSpectrum(:,:)
 456    real(8), allocatable :: HorizScale(:)
 457    character(len=4), pointer :: varNamesList(:)
 458    type(struct_gbi) :: gbi_horizontalMean
 459    type(struct_gsv) :: statevector_stdDev
 460    type(struct_gsv) :: statevector_mean, statevector_stdDevGridPoint
 461    integer :: ier
 462
 463    write(*,*)
 464    write(*,*) 'csl_computeBhi: Starting...'
 465    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 466
 467    allocate(SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc))
 468    allocate(TotVertCorrel(bhi%nVarLev,bhi%nVarLev))
 469    allocate(PowerSpectrum(bhi%nVarLev,0:nTrunc))
 470    allocate(NormB(bhi%nVarLev,bhi%nVarLev,0:nTrunc))
 471    allocate(NormBsqrt(bhi%nVarLev,bhi%nVarLev,0:nTrunc))
 472    allocate(HorizScale(bhi%nVarLev))
 473
 474    !
 475    !- 1.  Calculate the gridded binned Std. Dev. to be used in the analysis step
 476    !
 477    nullify(varNamesList)
 478    call ens_varNamesList(varNamesList,ensPerts) 
 479    call gsv_allocate(statevector_stdDev, ens_getNumStep(ensPerts),                            &
 480                      ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
 481                      datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true.,      &
 482                      dataKind_opt=8 )
 483
 484    call gbi_setup(gbi_horizontalMean, 'HorizontalMean', statevector_stdDev, hco_ens)
 485
 486    call gbi_stdDev(gbi_horizontalMean, ensPerts, & ! IN
 487                    statevector_stdDev)             ! OUT
 488
 489    !
 490    !- 2.  Normalization of the ensemble perturbations
 491    !
 492    if ( NormByStdDev ) then
 493      call ens_normalize(ensPerts)
 494    end if
 495
 496    !
 497    !- 3.  Covariance statistics in Spectral Space
 498    !
 499
 500    !- 3.1 Vertical correlations and Power Spectra
 501    call calcSpectralStats(ensPerts,                      & ! IN
 502                           SpVertCorrel, PowerSpectrum,   & ! OUT
 503                           NormB)                           ! OUT
 504
 505     if (mmpi_myid == 0) then
 506       !- 3.2 Calculate the horiontal correlation lenght scales
 507       call calcHorizScale(HorizScale, & ! OUT
 508                           NormB)        ! IN
 509
 510       !- 3.3 Calculate the total vertical correlation matrix
 511       call calcTotVertCorrel(TotVertCorrel,             & ! OUT
 512                              SpVertCorrel, PowerSpectrum) ! IN
 513       
 514       !- 3.4 Set cross-correlations
 515       call setSpVertCorrel(NormB) ! INOUT
 516
 517       !- 3.5 Calculate the square-root of the correlation-based B matrix
 518       call calcBsqrt(NormBsqrt, & ! OUT
 519                      NormB   )    ! IN
 520     end if
 521
 522     !- 3.6 Apply scaling
 523     if (stdDevScaling) then
 524       call scaleStdDev(statevector_stdDev) ! INOUT
 525     end if
 526     
 527     call rpn_comm_barrier("GRID",ier)
 528
 529    !
 530    !- 4.  Writing statistics to files
 531    !
 532
 533    !- 4.1 Statistics needed by VAR in analysis mode
 534    call writeVarStats(NormBsqrt, statevector_stdDev) ! IN
 535
 536    !- 4.2 Diagnostics fields
 537    call ens_copyEnsMean(ensPerts, statevector_mean)
 538    call ens_copyEnsStdDev(ensPerts, statevector_stdDevGridPoint)
 539
 540    call writeDiagStats(NormB, SpVertCorrel, TotVertCorrel, statevector_mean, & ! IN 
 541                        statevector_stdDevGridPoint, PowerSpectrum, HorizScale) ! IN
 542
 543    !
 544    !- 5.  Ending
 545    !
 546    call ens_deallocate(ensPerts)
 547    call gsv_deallocate(statevector_mean)
 548    call gsv_deallocate(statevector_stdDev)
 549    call gsv_deallocate(statevector_stdDevGridPoint)
 550
 551    deallocate(SpVertCorrel)
 552    deallocate(TotVertCorrel)
 553    deallocate(PowerSpectrum)
 554    deallocate(NormB)
 555    deallocate(NormBsqrt)
 556    deallocate(HorizScale)
 557
 558    write(*,*)
 559    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 560    write(*,*) 'csl_computeBhi: Done!'
 561
 562  end subroutine csl_computeBhi
 563
 564  !--------------------------------------------------------------------------
 565  ! csl_toolbox
 566  !--------------------------------------------------------------------------
 567  subroutine csl_toolbox
 568    !
 569    !:Purpose: High-level control of various diagnostic tools
 570    !
 571    implicit none
 572
 573    ! Locals:
 574    real(8), allocatable :: SpVertCorrel(:,:,:)
 575    real(8), allocatable :: NormB(:,:,:)
 576    real(8), allocatable :: PowerSpectrum(:,:)
 577    integer :: nulnam, ier, fnom, fclos
 578    type(struct_gsv) :: statevector_stdDev
 579    type(struct_gsv) :: statevector_template
 580    character(len=4), pointer :: varNamesList(:)
 581    character(len=60) :: tool
 582
 583    NAMELIST /NAMTOOLBOX/tool
 584
 585    write(*,*)
 586    write(*,*) 'csl_toolbox: Starting...'
 587    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 588
 589    !
 590    !- 1.  Tool selection
 591    !
 592    nulnam = 0
 593    ier = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 594    read(nulnam,nml=NAMTOOLBOX)
 595    write(*,nml=NAMTOOLBOX)
 596    ier = fclos(nulnam)
 597
 598    select case(trim(tool))
 599
 600    case ('VERTCORREL_GRIDPOINT')
 601       write(*,*)
 602       write(*,*) 'Computing vertical correlation in grid point space'
 603
 604       call ens_normalize(ensPerts)
 605
 606       call calcVertCorrel(ensPerts)
 607
 608    case ('HORIZCORREL_FUNCTION')
 609       write(*,*)
 610       write(*,*) 'Computing horizontal correlation functions'
 611
 612       allocate(SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc))
 613       allocate(PowerSpectrum(bhi%nVarLev,0:nTrunc))
 614       allocate(NormB(bhi%nVarLev,bhi%nVarLev,0:nTrunc))
 615
 616       call ens_normalize(ensPerts)
 617
 618       call calcSpectralStats(ensPerts,                      & ! IN
 619                              SpVertCorrel, PowerSpectrum,   & ! OUT
 620                              NormB)                           ! OUT
 621
 622       if (mmpi_myid == 0) then
 623         call horizCorrelFunction(NormB) ! IN
 624       end if
 625
 626       deallocate(NormB)
 627       deallocate(PowerSpectrum)
 628       deallocate(SpVertCorrel)
 629
 630    case ('STDDEV')
 631       write(*,*)
 632       write(*,*) 'Computing Standard-Deviations'
 633       
 634       nullify(varNamesList)
 635       call ens_varNamesList(varNamesList,ensPerts) 
 636       call gsv_allocate(statevector_stdDev, ens_getNumStep(ensPerts),                            &
 637                         ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
 638                         datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true.,      &
 639                         dataKind_opt=8 )
 640
 641       call ens_copyEnsStdDev(ensPerts, statevector_stdDev)
 642
 643       call gio_writeToFile(statevector_stdDev, './stddev.fst', 'STDDEV_GRIDP', &
 644                            typvar_opt = 'E', numBits_opt = 32)
 645
 646       call gsv_deallocate(statevector_stdDev)
 647
 648    case ('HVCORREL_LOCAL')
 649       write(*,*)
 650       write(*,*) 'Computing Local Correlation'
 651       call ens_normalize(ensPerts)
 652       call calcLocalCorrelations(ensPerts)
 653
 654     case ('LOCALIZATIONRADII')
 655       write(6,*)
 656       write(6,*) 'Estimating the optimal covariance localization radii'
 657
 658       call ens_copyEnsStdDev(ensPerts, statevector_template)
 659
 660       call bmd_setup(statevector_template, hco_ens, nEns, pressureProfile_M, pressureProfile_T, 1)
 661
 662       call bmd_localizationRadii(ensPerts, waveBandIndex_opt=1) ! IN
 663
 664    case default
 665       call utl_abort('csl_toolbox: Unknown TOOL '// trim(tool))
 666    end select
 667
 668    !
 669    !- 2.  Write the estimated pressure profiles
 670    !
 671    if (mmpi_myid == 0 .and. vco_bhi%vgridPresent) then
 672      call writePressureProfiles
 673    end if
 674
 675    !
 676    !- 3.  Ending
 677    !
 678    call ens_deallocate(ensPerts)
 679
 680    write(*,*)
 681    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 682    write(*,*) 'csl_toolbox: Done!'
 683
 684  end subroutine csl_toolbox
 685
 686  !--------------------------------------------------------------------------
 687  ! calcSpectralStats
 688  !--------------------------------------------------------------------------
 689  subroutine calcSpectralStats(ensPerts,SpVertCorrel,PowerSpectrum, &
 690                               NormB)
 691    !
 692    !:Purpose: To compute background-error covariances in spectral space
 693    !           from an ensemble of gridded data
 694    !
 695    implicit none
 696
 697    ! Arguments:
 698    type(struct_ens), intent(inout) :: ensPerts
 699    real(8),          intent(out)   :: SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
 700    real(8),          intent(out)   :: PowerSpectrum(bhi%nVarLev,0:nTrunc)
 701    real(8),          intent(out)   :: NormB(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
 702
 703    ! Locals:
 704    real(8), allocatable    :: NormPowerSpectrum(:,:)
 705    real(8), allocatable    :: SpectralStateVar(:,:,:)
 706    real(8), allocatable    :: SpVertCorrel_local(:,:,:)
 707    real(8), allocatable    :: GridState(:,:,:)
 708    real(8), allocatable    :: SumWeight(:)
 709    real(8), allocatable    :: SumWeight_local(:)
 710    type(struct_lst)  :: lst_bhi ! Spectral transform Parameters
 711    real(4), pointer  :: ptr4d_r4(:,:,:,:)
 712    real(8)           :: weight
 713    integer           :: k1, k2, ens, e, ila, p, k, totwvnb
 714    integer           :: myLonBeg, myLonEnd, myLatBeg, myLatEnd
 715    integer           :: nSize, ier
 716    character(len=24) :: kind
 717
 718    write(*,*)
 719    write(*,*) 'CalcSpectralStats: Starting...'
 720    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 721
 722    !
 723    !- 1. Setup the (LAM) spectral transform
 724    !
 725
 726    call lst_setup(lst_bhi,                             & ! OUT
 727                   hco_bhi%ni, hco_bhi%nj,              & ! IN
 728                   hco_bhi%dlon, nTrunc,                & ! IN
 729                   'LatLonMN', maxlevels_opt=bhi%nVarLev) ! IN
 730
 731    !
 732    !- 2.  Calculate the Vertical Covariances in Spectral Space
 733    !
 734    call ens_getLatLonBounds(ensPerts, myLonBeg, myLonEnd, myLatBeg, myLatEnd)
 735
 736    allocate( SpVertCorrel_local(bhi%nVarLev, bhi%nVarLev, 0:nTrunc) )
 737    SpVertCorrel_local(:,:,:) = 0.d0
 738
 739    allocate( SpectralStateVar(lst_bhi%nla,lst_bhi%nphase,bhi%nVarLev) )
 740    allocate( GridState(myLonBeg:myLonEnd, myLatBeg:myLatEnd, bhi%nVarLev) )
 741
 742    allocate(SumWeight_local(0:nTrunc))
 743    SumWeight_local(:) = 0.d0
 744
 745    do ens = 1, nEns
 746
 747      write(6,*) 'ens = ', ens
 748      flush(6)
 749
 750      !- 2.1 Extract fields from ensPerturbations
 751      do k = 1, bhi%nVarLev
 752        ptr4d_r4 => ens_getOneLev_r4(ensPerts,k)
 753        GridState(:,:,k) = real(ptr4d_r4(ens,1,:,:),8)
 754      end do
 755
 756      !- 2.2 Grid Point Space -> Spectral Space
 757      kind = 'GridPointToSpectral'
 758      call lst_VarTransform(lst_bhi,               & ! IN
 759                            SpectralStateVar,      & ! OUT
 760                            GridState,             & ! IN
 761                            kind, bhi%nVarLev)       ! IN
 762
 763      !- 2.3 Compute the covariances
 764      !$OMP PARALLEL DO PRIVATE(totwvnb,weight,e,ila,p,k2,k1)
 765      do totwvnb = 0, nTrunc
 766        do e = 1, lst_bhi%nePerK(totwvnb)
 767          ila = lst_bhi%ilaFromEK(e,totwvnb)
 768          do p = 1, lst_bhi%nphase
 769            if (trim(SpectralWeights) == 'lst') then
 770              weight = lst_bhi%Weight(ila)
 771              SumWeight_local(totwvnb) = SumWeight_local(totwvnb) + weight
 772            else
 773              weight = 2.0d0
 774            end if
 775            do k2 = 1, bhi%nVarLev
 776              do k1 = 1, bhi%nVarLev
 777                SpVertCorrel_local(k1,k2,totwvnb) = SpVertCorrel_local(k1,k2,totwvnb) &
 778                     + weight * (SpectralStateVar(ila,p,k1) * SpectralStateVar(ila,p,k2))
 779              end do
 780            end do
 781          end do
 782        end do
 783      end do
 784      !$OMP END PARALLEL DO
 785
 786    end do ! Loop in Ensemble
 787
 788    deallocate(SpectralStateVar)
 789    deallocate(GridState)
 790
 791    ! Gather the all the info in processor 0
 792    SpVertCorrel(:,:,:) = 0.d0
 793    nSize = bhi%nVarLev * bhi%nVarLev * (nTrunc + 1)
 794    call rpn_comm_reduce(SpVertCorrel_local,SpVertCorrel,nsize,"mpi_double_precision","mpi_sum",0,"GRID",ier)
 795
 796    allocate(SumWeight(0:nTrunc))
 797    SumWeight(:) = 0.d0
 798    nSize = nTrunc + 1
 799    call rpn_comm_reduce(SumWeight_local,   SumWeight,   nsize,"mpi_double_precision","mpi_sum",0,"GRID",ier)
 800
 801    deallocate(SumWeight_local)
 802    deallocate(SpVertCorrel_local)
 803
 804    if (mmpi_myid == 0) then 
 805
 806      !- 2.4 Compute the weighted COVARIANCES for each total wavenumber
 807      do totwvnb = 0, nTrunc
 808        if (trim(SpectralWeights) == 'legacy') then
 809          if (totwvnb /= 0 ) then
 810            SumWeight(totwvnb) = 2.d0 * real(lst_bhi%nphase * lst_bhi%nePerKglobal(totwvnb),8) - 2.d0
 811          else
 812            SumWeight(totwvnb) = 1.d0
 813          end if
 814          SumWeight(totwvnb) = SumWeight(totwvnb)*nEns
 815        end if
 816        
 817        if ( SumWeight(totwvnb) /= 0.d0 ) then 
 818          SpVertCorrel(:,:,totwvnb) = SpVertCorrel(:,:,totwvnb) / SumWeight(totwvnb)
 819        else
 820          SpVertCorrel(:,:,totwvnb) = 0.d0
 821        end if
 822        
 823      end do
 824
 825      deallocate(SumWeight)
 826      
 827      !- 2.5 Extract the power spectrum (the variances on the diagonal elements)
 828      do k = 1, bhi%nVarLev
 829        PowerSpectrum(k,:) = SpVertCorrel(k,k,:)
 830      end do
 831
 832      !
 833      !- 3.  Calculate the Vertical Correlations in Spectral Space
 834      !
 835      !$OMP PARALLEL DO PRIVATE (totwvnb,k2,k1)
 836      do totwvnb = 0, nTrunc
 837        do k2 = 1, bhi%nVarLev
 838          do k1 = 1, bhi%nVarLev 
 839            if ( PowerSpectrum(k1,totwvnb) /= 0.d0 .and. &
 840                 PowerSpectrum(k2,totwvnb) /= 0.d0 ) then
 841              SpVertCorrel(k1,k2,totwvnb) = SpVertCorrel(k1,k2,totwvnb) / &
 842                   sqrt( PowerSpectrum(k1,totwvnb) * PowerSpectrum(k2,totwvnb) )
 843            else
 844              SpVertCorrel(k1,k2,totwvnb) = 0.d0
 845            end if
 846          end do
 847        end do
 848      end do
 849      !$OMP END PARALLEL DO
 850      
 851      ! Apply vertical localization (if wanted)
 852      if (vertLoc) then                                                                 
 853        call applyVertLoc(SpVertCorrel) ! INOUT                                        
 854      end if
 855      
 856      !
 857      !- 4.  Normalize the power spectrum (i.e. build normalised spectral densities of the variance)
 858      !
 859      allocate(NormPowerSpectrum(bhi%nVarLev,0:nTrunc))
 860      
 861      call NormalizePowerSpectrum(PowerSpectrum,     & ! IN
 862           NormPowerSpectrum)   ! OUT
 863      
 864      ! Apply horizontal localization (if wanted)
 865      if (horizLoc) then                                                                 
 866        call applyHorizLoc(NormPowerSpectrum) ! INOUT                                        
 867      end if
 868      
 869      !
 870      !- 5.  Normalize the spectral vertical correlation matrix to ensure correlations in horizontal
 871      !
 872      
 873      !$OMP PARALLEL DO PRIVATE (totwvnb,k2,k1)
 874      do totwvnb = 0, nTrunc
 875        do k2 = 1, bhi%nVarLev
 876          do k1 = 1, bhi%nVarLev
 877            NormB(k1,k2,totwvnb) = SpVertCorrel(k1,k2,totwvnb) * &
 878                 sqrt( NormPowerSpectrum(k1,totwvnb) * NormPowerSpectrum(k2,totwvnb) )
 879          end do
 880        end do
 881      end do
 882      !$OMP END PARALLEL DO
 883      
 884      deallocate(NormPowerSpectrum)
 885
 886    end if ! mmpi_myid == 0
 887
 888    write(*,*)
 889    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 890    write(*,*) 'CalcSpectralStats: Done!'
 891
 892  end subroutine calcSpectralStats
 893
 894  !--------------------------------------------------------------------------
 895  ! normalizePowerSpectrum
 896  !--------------------------------------------------------------------------
 897  subroutine normalizePowerSpectrum(PowerSpectrum, NormPowerSpectrum)
 898    !
 899    !:Purpose: To convert spectral variances into spectral correlations 
 900    !
 901    implicit none
 902
 903    ! Arguments:
 904    real(8), intent(in)    :: PowerSpectrum(bhi%nVarLev,0:nTrunc)
 905    real(8), intent(out)   :: NormPowerSpectrum(bhi%nVarLev,0:nTrunc)
 906
 907    ! Locals:
 908    real(8), allocatable   :: SpectralStateVar(:,:,:)
 909    real(8), allocatable   :: GridState(:,:,:)
 910    real(8)           :: sum
 911    integer           :: e, ila, p, k, totwvnb
 912    character(len=24) :: kind
 913    type(struct_lst)  :: lst_norm ! Spectral transform Parameters
 914
 915    write(*,*)
 916    write(*,*) 'NormalizePowerSpectrum: Starting...'
 917    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 918
 919    !
 920    !- 1. Setup the (LAM) spectral transform
 921    !
 922    call lst_setup(lst_norm,                            & ! OUT
 923                   hco_bhi%ni, hco_bhi%nj,              & ! IN
 924                   hco_bhi%dlon, nTrunc,                & ! IN
 925                   'NoMpi')
 926
 927    !
 928    !- 1.  Normalize the power spectrum (i.e. build normalised spectral densities of the variance)
 929    !
 930
 931    !- 1.1 Part 1
 932
 933    !$OMP PARALLEL DO PRIVATE (totwvnb,k,sum)
 934    do k = 1, bhi%nVarLev
 935      sum = 0.0d0
 936      do totwvnb = 0, nTrunc
 937        sum = sum + real(totwvnb,8) * PowerSpectrum(k,totwvnb)
 938      end do
 939      do totwvnb = 0, nTrunc
 940        if ( sum /= 0.0d0 ) then
 941          NormPowerSpectrum(k,totwvnb) = PowerSpectrum(k,totwvnb) / sum
 942        else
 943          NormPowerSpectrum(k,totwvnb) = 0.d0
 944        end if
 945      end do
 946    end do
 947    !$OMP END PARALLEL DO
 948
 949    !- 1.2 Part 2
 950    allocate( SpectralStateVar(lst_norm%nla,lst_norm%nphase,bhi%nVarLev) )
 951    allocate( GridState(hco_bhi%ni, hco_bhi%nj, bhi%nVarLev) )
 952
 953    !- 1.2.1 Spectral transform of a delta function (at the center of the domain)
 954    GridState(:,:,:) = 0.d0
 955    GridState(hco_bhi%ni/2,hco_bhi%nj/2,:) = 1.d0
 956
 957    kind = 'GridPointToSpectral'
 958    call lst_VarTransform(lst_norm,           & ! IN
 959                          SpectralStateVar,   & ! OUT
 960                          GridState,          & ! IN
 961                          kind, bhi%nVarLev)    ! IN
 962
 963    !- 1.2.2 Apply the horizontal correlation function
 964    !$OMP PARALLEL DO PRIVATE (totwvnb,e,ila,p,k)
 965    do totwvnb = 0, nTrunc
 966      do e = 1, lst_norm%nePerK(totwvnb)
 967        ila = lst_norm%ilaFromEK(e,totwvnb)
 968        do p = 1, lst_norm%nphase
 969          do k = 1, bhi%nVarLev
 970            SpectralStateVar(ila,p,k) = SpectralStateVar(ila,p,k) * NormPowerSpectrum(k,totwvnb) * &
 971                 lst_norm%NormFactor(ila,p) * lst_norm%NormFactorAd(ila,p)
 972          end do
 973        end do
 974      end do
 975    end do
 976    !$OMP END PARALLEL DO
 977
 978    !- 1.2.3 Move back to physical space
 979    kind = 'SpectralToGridPoint'
 980    call lst_VarTransform(lst_norm,          & ! IN
 981                          SpectralStateVar,  & ! IN
 982                          GridState,         & ! OUT
 983                          kind, bhi%nVarLev)   ! IN
 984
 985    !- 1.2.4 Normalize to 1
 986    do k = 1, bhi%nVarLev
 987      if ( GridState(hco_bhi%ni/2,hco_bhi%nj/2,k) < 0.d0 ) then
 988        write(*,*) 'NormalizePowerSpectrum: Problem in normalization ', k, GridState(hco_bhi%ni/2,hco_bhi%nj/2,k)
 989        call utl_abort('aborting in NormalizePowerSpectrum')
 990      end if
 991
 992      if ( GridState(hco_bhi%ni/2,hco_bhi%nj/2,k) /= 0.d0 ) then
 993        write(*,*) 'Normalization factor = ', k, GridState(hco_bhi%ni/2,hco_bhi%nj/2,k), 1.d0 / GridState(hco_bhi%ni/2,hco_bhi%nj/2,k)
 994        NormPowerSpectrum(k,:) = NormPowerSpectrum(k,:) / GridState(hco_bhi%ni/2,hco_bhi%nj/2,k)
 995      else
 996        write(*,*) 'Setting NormPowerSpectrum to zero = ', k
 997        NormPowerSpectrum(k,:) = 0.d0
 998      end if
 999    end do
1000
1001    deallocate(SpectralStateVar)
1002    deallocate(GridState)
1003
1004    write(*,*)
1005    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1006    write(*,*) 'NormalizePowerSpectrum: Done!'
1007
1008  end subroutine normalizePowerSpectrum
1009
1010  !--------------------------------------------------------------------------
1011  ! calcHorizScale
1012  !--------------------------------------------------------------------------
1013  subroutine calcHorizScale(HorizScale,SpCovariance)
1014    !
1015    !:Purpose: To compute horizontal lenght scales based on the power spectra
1016    !
1017    implicit none
1018
1019    ! Arguments:
1020    real(8), intent(out) :: HorizScale(bhi%nVarLev)
1021    real(8), intent(in)  :: SpCovariance(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1022
1023    ! Locals:
1024    real(8) :: a, b, beta, dx, dist
1025    integer :: totwvnb, k, var
1026
1027    write(*,*)
1028    write(*,*) 'CalcHorizScale: Starting...'
1029
1030    !
1031    !- Computing distance-related variables
1032    !
1033
1034    ! Grid spacing in meters
1035    dx = hco_bhi%dlon * ec_ra
1036    write(*,*)
1037    write(*,*) 'grid spacing (m) =', dx
1038
1039    dist = max(hco_bhi%ni, hco_bhi %nj) * dx
1040    beta = (dist/(2.d0*MPC_PI_R8))**2
1041
1042    !
1043    !- Estimate horizontal correlation scales based on the power spectra
1044    !
1045    do k = 1, bhi%nVarLev
1046      a = 0.d0
1047      b = 0.d0
1048      do totwvnb = 0, nTrunc
1049        a = a + SpCovariance(k,k,totwvnb) * totwvnb
1050        b = b + SpCovariance(k,k,totwvnb) * totwvnb**3
1051      end do
1052      if (b <= 0.d0) then 
1053        HorizScale(k) = 0.d0
1054      else
1055        HorizScale(k) = sqrt(2.d0*a*beta/b)
1056      end if
1057    end do
1058
1059    do var = 1, bhi%nControlVariable
1060      write(*,*)
1061      write(*,*) bhi%controlVariable(var)%nomvar(cv_bhi)
1062      do k = bhi%controlVariable(var)%varLevIndexStart, bhi%controlVariable(var)%varLevIndexEnd
1063        write(*,'(i3,2X,f9.2,2X,a2)') k, HorizScale(k)/1000.d0, 'km'
1064      end do
1065    end do
1066
1067    write(*,*)
1068    write(*,*) 'calcHorizScale: Done!'
1069
1070  end subroutine calcHorizScale
1071
1072  !--------------------------------------------------------------------------
1073  ! calcTotVertCorrel
1074  !--------------------------------------------------------------------------
1075  subroutine calcTotVertCorrel(TotVertCorrel, SpVertCorrel, PowerSpectrum)
1076    !
1077    !:Purpose: To compute the total vertical correlations (i.e. gridpoint equivalent)
1078    !
1079    implicit none
1080
1081    ! Arguments:
1082    real(8), intent(out)    :: TotVertCorrel(bhi%nVarLev,bhi%nVarLev)
1083    real(8), intent(in)     :: SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1084    real(8), intent(in)     :: PowerSpectrum(bhi%nVarLev,0:nTrunc)
1085
1086    ! Locals:
1087    real(8), allocatable    :: TotVertCov(:,:)
1088    integer           :: k1, k2, totwvnb
1089
1090    write(*,*)
1091    write(*,*) 'CalcTotVertCorrel: Starting...'
1092    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1093
1094    !
1095    !- 1.  Calculate the total Normalized Covariance Matrix
1096    !
1097    allocate(TotVertCov(bhi%nVarLev,bhi%nVarLev))
1098    TotVertCov(:,:) = 0.d0
1099
1100    do k2 = 1, bhi%nVarLev
1101      do k1 = 1, bhi%nVarLev
1102        do totwvnb = 0, nTrunc
1103          TotVertCov(k1,k2) = TotVertCov(k1,k2) + &
1104               real(totwvnb,8) * SpVertCorrel(k1,k2,totwvnb) *    &
1105               sqrt(PowerSpectrum(k1,totwvnb) * PowerSpectrum(k2,totwvnb))
1106        end do
1107      end do
1108    end do
1109
1110    !
1111    !- 2.  Transform into correlations
1112    !
1113    do k2 = 1, bhi%nVarLev
1114      do k1 = 1, bhi%nVarLev
1115        if ( TotVertCov(k1,k1) /= 0.d0 .and. &
1116             TotVertCov(k2,k2) /= 0.d0 ) then
1117          TotVertCorrel(k1,k2) = TotVertCov(k1,k2) / &
1118               ( sqrt(TotVertCov(k1,k1)) * sqrt(TotVertCov(k2,k2)) )
1119        else
1120          TotVertCorrel(k1,k2) = 0.d0
1121        end if
1122      end do
1123    end do
1124
1125    deallocate(TotVertCov)
1126
1127    write(*,*)
1128    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1129    write(*,*) 'CalcTotVertCorrel: Done!'
1130
1131  end subroutine calcTotVertCorrel
1132
1133  !--------------------------------------------------------------------------
1134  ! calcBsqrt
1135  !--------------------------------------------------------------------------
1136  subroutine calcBsqrt(Bsqrt,B)
1137    !
1138    !:Purpose: To compute the sqare-root of B
1139    !
1140    implicit none
1141
1142    ! Arguments:
1143    real(8), intent(out)   :: Bsqrt(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1144    real(8), intent(in)    :: B    (bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1145
1146    ! Locals:
1147    integer :: totwvnb
1148
1149    !
1150    !-  Calculate B^0.5 for each total wave number
1151    !
1152    Bsqrt(:,:,:) = B(:,:,:)
1153
1154    do totwvnb = 0, nTrunc
1155      call utl_matSqrt(Bsqrt(:,:,totwvnb),bhi%nVarLev,1.0d0,.true.)
1156    end do
1157
1158  end subroutine calcBsqrt
1159
1160  !--------------------------------------------------------------------------
1161  ! setSpVertCorrel
1162  !--------------------------------------------------------------------------
1163  subroutine setSpVertCorrel(SpVertCorrel)
1164    !
1165    !:Purpose: To discard some user-defined cross-correlations
1166    !
1167    implicit none
1168
1169    ! Arguments:
1170    real(8), intent(inout) :: SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1171
1172    ! Locals:
1173    real(8), allocatable :: KeepOrDiscard(:,:)
1174    integer :: totwvnb, var1, var2, k1, k2 
1175
1176    write(*,*)
1177    write(*,*) 'SetSpVertCorrel: Starting...'
1178
1179    !
1180    !-  Determine which bloc of the correlation matrix to keep/discard
1181    !
1182    allocate(KeepOrDiscard(bhi%nControlVariable,bhi%nControlVariable))
1183
1184    !- Calculate upper half
1185    do var2 = 1, bhi%nControlVariable
1186      do var1 = 1, var2
1187        if (var1 == var2) then
1188          KeepOrDiscard(var1,var2) = 1.d0 ! Keep the Auto-Correlations
1189        elseif( any(correlatedVariables == bhi%controlVariable(var1)%nomvar(cv_bhi)) .and. &
1190                any(correlatedVariables == bhi%controlVariable(var2)%nomvar(cv_bhi)) ) then
1191          KeepOrDiscard(var1,var2) = 1.d0 ! Keep these Cross-Correlations
1192        else
1193          KeepOrDiscard(var1,var2) = 0.d0 ! Discard these Cross-Correlations
1194        end if
1195        write(*,*) var1, var2, bhi%controlVariable(var1)%nomvar(cv_bhi), bhi%controlVariable(var2)%nomvar(cv_bhi), KeepOrDiscard(var1,var2) 
1196      end do
1197    end do
1198
1199    ! Symmetrize
1200    do var1 = 2, bhi%nControlVariable
1201      do var2 = 1, var1-1
1202        KeepOrDiscard(var1,var2) = KeepOrDiscard(var2,var1)
1203      end do
1204    end do
1205
1206    !
1207    !- Modify the Vertical Correlation Matrix
1208    !
1209    do totwvnb = 0, nTrunc
1210
1211      do var2 = 1, bhi%nControlVariable
1212        do var1 = 1, bhi%nControlVariable
1213
1214          do k2 = bhi%controlVariable(var2)%varLevIndexStart, bhi%controlVariable(var2)%varLevIndexEnd
1215            do k1 = bhi%controlVariable(var1)%varLevIndexStart, bhi%controlVariable(var1)%varLevIndexEnd
1216              SpVertCorrel(k1,k2,totwvnb) = KeepOrDiscard(var1,var2) * &
1217                   SpVertCorrel(k1,k2,totwvnb)
1218            end do
1219          end do
1220
1221        end do
1222      end do
1223
1224    end do  ! total wave number
1225
1226    deallocate(KeepOrDiscard)
1227
1228    write(*,*)
1229    write(*,*) 'SetSpVertCorrel: Done!'
1230
1231  end subroutine setSpVertCorrel
1232
1233  !--------------------------------------------------------------------------
1234  ! calcVertCorrel
1235  !--------------------------------------------------------------------------
1236  subroutine calcVertCorrel(ensPerts)
1237    !
1238    !:Purpose: To compute vertical correlations from an ensemble of gridded data
1239    !
1240    implicit none
1241
1242    ! Arguments:
1243    type(struct_ens), intent(inout) :: ensPerts
1244
1245    ! Locals:
1246    real(8), allocatable :: vertCorrel(:,:)
1247    real(4), pointer  :: ptr4d_k1_r4(:,:,:,:)
1248    real(4), pointer  :: ptr4d_k2_r4(:,:,:,:)
1249    real(8), allocatable :: vertCorrel_local(:,:)
1250    integer :: lonIndex, latIndex, k1, k2, memberIndex
1251    integer :: myLonBeg, myLonEnd, myLatBeg, myLatEnd, nSize, ier
1252    integer :: fstouv, fnom, fstfrm, fclos, iunstats
1253
1254    write(*,*)
1255    write(*,*) 'CalcVertCorrel: Starting...'
1256    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1257
1258    allocate(vertCorrel(bhi%nVarLev,bhi%nVarLev))
1259    vertCorrel(:,:) = 0.d0
1260
1261    allocate(vertCorrel_local(bhi%nVarLev,bhi%nVarLev))
1262    vertCorrel_local(:,:) = 0.d0
1263
1264    !
1265    !- Calculate the Vertical Correlation in GridPoint Space
1266    !  ... we assume that the ensemble grid point mean was removed and that
1267    !      the ensemble values were divided by the grid point std dev.
1268
1269    call ens_getLatLonBounds(ensPerts, myLonBeg, myLonEnd, myLatBeg, myLatEnd)
1270
1271    do memberIndex = 1, nEns
1272
1273      !$OMP PARALLEL DO PRIVATE (k1,k2,ptr4d_k1_r4,ptr4d_k2_r4,latIndex,lonIndex)
1274      do k2 = 1, bhi%nVarLev
1275        do k1 = 1, bhi%nVarLev
1276          
1277          ptr4d_k1_r4 => ens_getOneLev_r4(ensPerts,k1)
1278          ptr4d_k2_r4 => ens_getOneLev_r4(ensPerts,k2)
1279
1280          do latIndex = myLatBeg, myLatEnd
1281            do lonIndex = myLonBeg, myLonEnd
1282
1283              if (lonIndex > hco_ens%ni .or. latIndex > hco_ens%nj) cycle ! do not use data in the extension zone
1284
1285              VertCorrel_local(k1,k2) = VertCorrel_local(k1,k2)            &
1286                   + real(ptr4d_k1_r4(memberIndex,1,lonIndex,latIndex),8)  &
1287                   * real(ptr4d_k2_r4(memberIndex,1,lonIndex,latIndex),8)
1288            end do
1289          end do
1290
1291        end do
1292      end do
1293      !$OMP END PARALLEL DO
1294
1295    end do ! Loop in Ensemble
1296
1297    !- Communication
1298    nSize = bhi%nVarLev * bhi%nVarLev
1299    call rpn_comm_reduce(vertCorrel_local,vertCorrel,nsize,"mpi_double_precision","mpi_sum",0,"GRID",ier)
1300
1301    deallocate(vertCorrel_local)
1302
1303    !- Conversion to correlation
1304    if (mmpi_myid == 0) then
1305      vertCorrel(:,:) = vertCorrel(:,:) / real((nEns-1)*hco_ens%nj*hco_ens%ni,8)
1306    end if
1307
1308    !- Output
1309    if (mmpi_myid == 0) then
1310      iunstats = 0
1311      ier    = fnom(iunstats,'./vertCorrel.fst','RND',0)
1312      ier    = fstouv(iunstats,'RND')
1313      call WriteTotVertCorrel(VertCorrel,iunstats,'ZT','GPVCOR_CORE') ! IN
1314      ier =  fstfrm(iunstats)
1315      ier =  fclos (iunstats)
1316    end if
1317
1318    deallocate(VertCorrel)
1319
1320    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1321    write(*,*) 'CalcVertCorrel: Done!'
1322
1323  end subroutine calcVertCorrel
1324
1325  !--------------------------------------------------------------------------
1326  ! horizCorrelFunction
1327  !--------------------------------------------------------------------------
1328  subroutine horizCorrelFunction(NormB)
1329    !
1330    !:Purpose: To compute and write the horizontal correlation function of
1331    !           every variables and levels in the correlation formulation of
1332    !           the B matrix (i.e. C matrix)
1333    !
1334    implicit none
1335
1336    ! Arguments:
1337    real(8), intent(in)    :: NormB(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1338
1339    ! Locals:
1340    real(8), allocatable   :: SpectralStateVar(:,:,:)
1341    real(8), allocatable   :: GridState(:,:,:)
1342    type(struct_gsv) :: statevector
1343    real(8), pointer :: ptr3d_r8(:,:,:)
1344    character(len=4), pointer :: varNamesList(:)
1345    integer   :: e, ila, p, k, totwvnb
1346    type(struct_lst)  :: lst_cor ! Spectral transform Parameters
1347    character(len=24) :: kind
1348
1349    nullify(varNamesList)
1350    call ens_varNamesList(varNamesList,ensPerts) 
1351    call gsv_allocate(statevector, ens_getNumStep(ensPerts),                    &
1352                      hco_bhi, ens_getVco(ensPerts), varNames_opt=varNamesList, &
1353                      datestamp_opt=tim_getDatestamp(), mpi_local_opt=.false.,  &
1354                      dataKind_opt=8 )
1355
1356    !
1357    !- 1. Setup the (LAM) spectral transform
1358    !
1359    call lst_setup(lst_cor,                             & ! OUT
1360                   hco_bhi%ni, hco_bhi%nj,              & ! IN
1361                   hco_bhi%dlon, nTrunc,                & ! IN
1362                   'NoMpi')
1363
1364    allocate( SpectralStateVar(lst_cor%nla,lst_cor%nphase,bhi%nVarLev) )
1365    allocate( GridState(hco_bhi%ni, hco_bhi%nj, bhi%nVarLev) )
1366
1367    !- 3.2.1 Spectral transform of a delta function (at the center of the domain)
1368    GridState(:,:,:) = 0.d0
1369    GridState(hco_bhi%ni/2,hco_bhi%nj/2,:) = 1.d0
1370
1371    kind = 'GridPointToSpectral'
1372    call lst_VarTransform(lst_cor,            & ! IN
1373                          SpectralStateVar,   & ! OUT
1374                          GridState,          & ! IN
1375                          kind, bhi%nVarLev)    ! IN
1376
1377    !- 3.2.2 Apply the horizontal correlation function
1378    !$OMP PARALLEL DO PRIVATE (totwvnb,e,ila,p,k)
1379    do totwvnb = 0, nTrunc
1380      do e = 1, lst_cor%nePerK(totwvnb)
1381        ila = lst_cor%ilaFromEK(e,totwvnb)
1382        do p = 1, lst_cor%nphase
1383          do k = 1, bhi%nVarLev
1384            SpectralStateVar(ila,p,k) = SpectralStateVar(ila,p,k) * NormB(k,k,totwvnb) * &
1385                 lst_cor%NormFactor(ila,p) * lst_cor%NormFactorAd(ila,p)
1386          end do
1387        end do
1388      end do
1389    end do
1390    !$OMP END PARALLEL DO
1391
1392    !- 3.2.3 Move back to physical space
1393    kind = 'SpectralToGridPoint'
1394    call lst_VarTransform(lst_cor,          & ! IN
1395                          SpectralStateVar, & ! IN
1396                          GridState,        & ! OUT
1397                          kind, bhi%nVarLev)  ! IN
1398
1399    !
1400    !- 4.  Write to file
1401    !
1402    call gsv_getField(statevector,ptr3d_r8)
1403    ptr3d_r8(:,:,:) = GridState(:,:,:)
1404    call gio_writeToFile(statevector, './horizCorrel.fst', 'CORRELFUNC')
1405
1406    deallocate(SpectralStateVar)
1407    deallocate(GridState)
1408    call gsv_deallocate(statevector)
1409
1410  end subroutine horizCorrelFunction
1411
1412  !--------------------------------------------------------------------------
1413  ! applyHorizLoc
1414  !--------------------------------------------------------------------------
1415  subroutine applyHorizLoc(NormPowerSpectrum)
1416    !
1417    !:Purpose: To apply horizontal localization to the  spectral correlations
1418    !
1419    implicit none
1420
1421    ! Arguments:
1422    real(8), intent(inout) :: NormPowerSpectrum(bhi%nVarLev,0:nTrunc)
1423
1424    ! Locals:
1425    real(8), allocatable   :: SpectralStateVar(:,:,:)
1426    real(8), allocatable   :: GridState(:,:,:)
1427    real(8), allocatable   :: GridStateLoc(:,:,:)
1428    real(8), allocatable   :: PowerSpectrum(:,:)
1429    real(8), allocatable   :: SumWeight(:)
1430    real(8), allocatable   :: local_length(:)
1431    type(struct_lst)  :: lst_hloc ! Spectral transform Parameters
1432    integer :: totwvnb, var, k, e, ila, p
1433    real(8)  :: hlocalize
1434    character(len=24) :: kind
1435
1436    write(*,*)
1437    write(*,*) 'applyHorizLoc: Starting...'
1438
1439    !
1440    !- 1. Setup the (LAM) spectral transform
1441    !
1442    call lst_setup(lst_hloc,                            & ! OUT
1443                   hco_bhi%ni, hco_bhi%nj,              & ! IN
1444                   hco_bhi%dlon, nTrunc,                & ! IN
1445                   'NoMpi')
1446
1447    !
1448    !- 1. Get the original gridpoint horizontal correlations
1449    !
1450
1451    allocate( SpectralStateVar(lst_hloc%nla,lst_hloc%nphase,bhi%nVarLev) )
1452    allocate( GridState(hco_bhi%ni, hco_bhi%nj, bhi%nVarLev) )
1453
1454    !- 1.1 Spectral transform of a delta function (at the lower-left of the domain)
1455    GridState(:,:,:) = 0.d0
1456    GridState(1,1,:) = 1.d0
1457
1458    kind = 'GridPointToSpectral'
1459    call lst_VarTransform(lst_hloc,            & ! IN
1460                          SpectralStateVar,    & ! OUT
1461                          GridState,           & ! IN
1462                          kind, bhi%nVarLev)     ! IN
1463
1464    !- 1.2 Apply the horizontal correlation function
1465    !$OMP PARALLEL DO PRIVATE (totwvnb,e,ila,p,k)
1466    do totwvnb = 0, nTrunc
1467      do e = 1, lst_hloc%nePerK(totwvnb)
1468        ila = lst_hloc%ilaFromEK(e,totwvnb)
1469        do p = 1, lst_hloc%nphase
1470          do k = 1, bhi%nVarLev
1471            SpectralStateVar(ila,p,k) = SpectralStateVar(ila,p,k) * NormPowerSpectrum(k,totwvnb) * &
1472                 lst_hloc%NormFactor(ila,p) * lst_hloc%NormFactorAd(ila,p)
1473          end do
1474        end do
1475      end do
1476    end do
1477    !$OMP END PARALLEL DO
1478
1479    !- 1.3 Move back to physical space
1480    kind = 'SpectralToGridPoint'
1481    call lst_VarTransform(lst_hloc,          & ! IN
1482                          SpectralStateVar,  & ! IN
1483                          GridState,         & ! OUT
1484                          kind, bhi%nVarLev)   ! IN
1485
1486    !
1487    !- 2.  Create and apply the localization function in gridpoint space
1488    !
1489    allocate(local_length(bhi%nVarLev))
1490    allocate(GridStateLoc(hco_bhi%ni, hco_bhi%nj, bhi%nVarLev) )
1491
1492    do var = 1, bhi%nControlVariable
1493      if      ( bhi%controlVariable(var)%nomvar(cv_bhi) == 'CC' .or. &
1494           bhi%controlVariable(var)%nomvar(cv_bhi) == 'PP' .or. &
1495           bhi%controlVariable(var)%nomvar(cv_bhi) == 'QR' .or. &
1496           bhi%controlVariable(var)%nomvar(cv_bhi) == 'DD' ) then
1497        hLocalize = hLocalize_wind
1498      else if ( bhi%controlVariable(var)%nomvar(cv_bhi) == 'TT' .or. &
1499           bhi%controlVariable(var)%nomvar(cv_bhi) == 'TG' .or. &
1500           bhi%controlVariable(var)%nomvar(cv_bhi) == 'P0' ) then
1501        hLocalize = hLocalize_mass
1502      else if ( bhi%controlVariable(var)%nomvar(cv_bhi) == 'LQ' ) then
1503        hLocalize = hLocalize_humidity
1504      else
1505        hLocalize = hLocalize_other
1506      end if
1507
1508      do k = bhi%controlVariable(var)%varLevIndexStart, bhi%controlVariable(var)%varLevIndexEnd
1509        local_length(k) = hLocalize
1510      end do
1511
1512    end do
1513
1514    call lfn_setup('FifthOrder') ! IN
1515    call lfn_CreateBiPerFunction( GridStateLoc,                  & ! OUT
1516         local_length, hco_bhi%dlon,    & ! IN
1517         hco_bhi%ni, hco_bhi%nj, bhi%nVarLev)  ! IN
1518
1519    GridState(:,:,:) = GridStateLoc(:,:,:) * GridState(:,:,:)
1520
1521    deallocate(GridStateLoc)
1522    deallocate(local_length)
1523
1524    !
1525    !- 3. Create the localized normalized power spectrum
1526    !    
1527    allocate(PowerSpectrum(bhi%nVarLev,0:nTrunc))
1528
1529    !- 3.1 Transform to spectral space
1530    kind = 'GridPointToSpectral'
1531    call lst_VarTransform(lst_hloc,          & ! IN
1532                          SpectralStateVar,  & ! OUT
1533                          GridState,         & ! IN
1534                          kind, bhi%nVarLev)   ! IN
1535
1536    !- 3.2 Compute band mean
1537    allocate(SumWeight(0:nTrunc))
1538    SumWeight(:) = 0.d0
1539
1540    PowerSpectrum(:,:) = 0.d0
1541    do totwvnb = 0, nTrunc
1542      do e = 1, lst_hloc%nePerK(totwvnb)
1543        ila = lst_hloc%ilaFromEK(e,totwvnb)
1544        do p = 1, lst_hloc%nphase
1545          SumWeight(totwvnb) = SumWeight(totwvnb) + lst_hloc%Weight(ila)
1546          do k = 1, bhi%nVarLev
1547            PowerSpectrum(k,totwvnb) = PowerSpectrum(k,totwvnb) + &
1548                 lst_hloc%Weight(ila) * abs(SpectralStateVar(ila,p,k))
1549          end do
1550        end do
1551      end do
1552    end do
1553
1554    do totwvnb = 0, nTrunc
1555      if (SumWeight(totwvnb) /= 0.d0) then
1556        PowerSpectrum(:,totwvnb) = PowerSpectrum(:,totwvnb) / SumWeight(totwvnb)
1557      else
1558        PowerSpectrum(:,totwvnb) = 0.d0
1559      endif
1560    end do
1561
1562    deallocate(SumWeight)
1563
1564    !- 3.3 Normalize
1565    call normalizePowerSpectrum(PowerSpectrum,     & ! IN
1566                                NormPowerSpectrum)   ! OUT
1567
1568    deallocate(SpectralStateVar)
1569    deallocate(GridState)
1570    deallocate(PowerSpectrum)
1571
1572    write(*,*)
1573    write(*,*) 'applyHorizLoc: Done!'
1574
1575  end subroutine applyHorizLoc
1576
1577  !--------------------------------------------------------------------------
1578  ! applyVertLoc
1579  !--------------------------------------------------------------------------
1580  subroutine applyVertLoc(SpVertCorrel)
1581    !
1582    !:Purpose: To apply vertical localization to the spectral correlations
1583    !
1584    implicit none
1585
1586    ! Arguments:
1587    real(8), intent(inout) :: SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1588
1589    ! Locals:
1590    integer :: totwvnb, var1, var2, k1, k2, lev1, lev2
1591    real(8)  :: dist, fact, vLocalize, pres1, pres2, vLocalize1, vLocalize2
1592
1593    write(*,*)
1594    write(*,*) 'applyVertLoc: Starting...'
1595
1596    !
1597    !- 1.  Select the localization function
1598    !
1599    call lfn_setup('FifthOrder') ! IN 
1600
1601    !
1602    !- 2.  Apply localization to the spectral vertical correlations
1603    !
1604
1605    !- 2.1 Loop on control variables
1606    do var2 = 1, bhi%nControlVariable
1607      do var1 = 1, bhi%nControlVariable
1608
1609        !-  2.2 Set the vertical length scale
1610
1611        !-  2.2.1 Select the length scale for control variable 1
1612        if      ( bhi%controlVariable(var1)%nomvar(cv_bhi) == 'CC' .or. &
1613             bhi%controlVariable(var1)%nomvar(cv_bhi) == 'PP' .or. &
1614             bhi%controlVariable(var1)%nomvar(cv_bhi) == 'QR' .or. &
1615             bhi%controlVariable(var1)%nomvar(cv_bhi) == 'DD') then
1616          vLocalize1 = vLocalize_wind
1617        else if ( bhi%controlVariable(var1)%nomvar(cv_bhi) == 'TT' .or. &
1618             bhi%controlVariable(var1)%nomvar(cv_bhi) == 'TG' .or. &
1619             bhi%controlVariable(var1)%nomvar(cv_bhi) == 'P0' ) then
1620          vLocalize1 = vLocalize_mass
1621        else if ( bhi%controlVariable(var1)%nomvar(cv_bhi) == 'LQ' ) then
1622          vLocalize1 = vLocalize_humidity
1623        else
1624          vLocalize1 = vLocalize_other
1625        end if
1626
1627        !-  2.2.2 Select the length scale for control variable 2
1628        if      ( bhi%controlVariable(var2)%nomvar(cv_bhi) == 'CC' .or. &
1629             bhi%controlVariable(var2)%nomvar(cv_bhi) == 'PP' .or. &
1630             bhi%controlVariable(var2)%nomvar(cv_bhi) == 'QR' .or. &
1631             bhi%controlVariable(var2)%nomvar(cv_bhi) == 'DD') then
1632          vLocalize2 = vLocalize_wind
1633        else if ( bhi%controlVariable(var2)%nomvar(cv_bhi) == 'TT' .or. &
1634             bhi%controlVariable(var2)%nomvar(cv_bhi) == 'TG' .or. &
1635             bhi%controlVariable(var2)%nomvar(cv_bhi) == 'P0' ) then
1636          vLocalize2 = vLocalize_mass
1637        else if ( bhi%controlVariable(var2)%nomvar(cv_bhi) == 'LQ' ) then
1638          vLocalize2 = vLocalize_humidity
1639        else
1640          vLocalize2 = vLocalize_other
1641        end if
1642
1643        !- 2.2.3 Length scale to be use for var1-var2 correlation
1644        vLocalize = (vLocalize1+vLocalize2)/2.d0
1645
1646        !- 2.3 Loop on vertical levels
1647        do k2 = bhi%controlVariable(var2)%varLevIndexStart, bhi%controlVariable(var2)%varLevIndexEnd
1648          do k1 = bhi%controlVariable(var1)%varLevIndexStart, bhi%controlVariable(var1)%varLevIndexEnd
1649
1650            !- 2.4 Set the pressure values
1651
1652            !- 2.4.1 Pressure for control-variable-1 level
1653            if (bhi%controlVariable(var1)%nlev /= 1) then ! variable 3D
1654              lev1 = k1 - bhi%controlVariable(var1)%varLevIndexStart + 1
1655              if (bhi%controlVariable(var1)%GridType == 'TH') then
1656                pres1 = pressureProfile_T(lev1)
1657              else
1658                pres1 = pressureProfile_M(lev1)
1659              end if
1660            else
1661              pres1 = pressureProfile_M(vco_bhi%nlev_M) ! variable 2D
1662            end if
1663
1664            !- 2.4.2 Pressure for control-variable-2 level
1665            if (bhi%controlVariable(var2)%nlev /= 1) then ! variable 3D
1666              lev2 = k2 - bhi%controlVariable(var2)%varLevIndexStart + 1
1667              if (bhi%controlVariable(var2)%GridType == 'TH') then
1668                pres2 = pressureProfile_T(lev2)
1669              else
1670                pres2 = pressureProfile_M(lev2)
1671              end if
1672            else
1673              pres2 = pressureProfile_M(vco_bhi%nlev_M) ! variable 2D
1674            end if
1675
1676            !- 2.5 Compute the localization factor
1677            dist = abs(log(pres2) - log(pres1))
1678            fact = lfn_response(dist,vLocalize)
1679
1680            !- 2.6 Localize each total wavenumber (not scale-dependent!)
1681            do totwvnb = 0, nTrunc
1682              SpVertCorrel(k1,k2,totwvnb) = fact * SpVertCorrel(k1,k2,totwvnb)
1683            end do
1684
1685          end do
1686        end do
1687
1688      end do
1689    end do
1690
1691    deallocate(pressureProfile_M)
1692    deallocate(pressureProfile_T)
1693
1694    write(*,*)
1695    write(*,*) 'applyVertLoc: Done!'
1696
1697  end subroutine applyVertLoc
1698
1699  !--------------------------------------------------------------------------
1700  ! scaleStdDev
1701  !--------------------------------------------------------------------------
1702  subroutine scaleStdDev(statevector_stdDev)
1703    !
1704    !:Purpose: To scale the gridpoint background-error standard deviations
1705    !
1706    implicit none
1707    
1708    ! Arguments:
1709    type(struct_gsv), intent(inout) :: statevector_stdDev
1710
1711    ! Locals:
1712    real(8), pointer :: ptr3d_r8(:,:,:)
1713    real(8) :: multFactor
1714    integer :: nVarLev, varLevIndex, levIndex
1715    character(len=4) :: varName
1716
1717    write(*,*)
1718    write(*,*) 'scaleStdDev: Starting...'
1719    
1720    nVarLev = gsv_getNumK(statevector_stdDev)
1721 
1722    call gsv_getField(statevector_stdDev,ptr3d_r8)
1723
1724    do varLevIndex = 1, nVarLev
1725      varName = gsv_getVarNameFromK(statevector_stdDev,varLevIndex)
1726      levIndex = gsv_getLevFromK(statevector_stdDev,varLevIndex)
1727
1728      if ( vnl_varLevelFromVarname(varName) == 'MM' ) then
1729        multFactor = scaleFactor_M(levIndex)
1730      else if ( vnl_varLevelFromVarname(varName) == 'TH' ) then
1731        multFactor = scaleFactor_T(levIndex)
1732      else ! SF
1733        multFactor = scaleFactor_SF
1734      end if
1735
1736      ptr3d_r8(:,:,varLevIndex) = multFactor * ptr3d_r8(:,:,varLevIndex)
1737    end do
1738
1739    write(*,*)
1740    write(*,*) 'scaleStdDev: Done...'
1741
1742  end subroutine scaleStdDev
1743
1744  !--------------------------------------------------------------------------
1745  ! writeVarStats
1746  !--------------------------------------------------------------------------
1747  subroutine writeVarStats(Bsqrt,statevector_stdDev)
1748    !
1749    !:Purpose: To write data needed for VAR applications, i.e C^1/2 and stdDev
1750    !
1751    implicit none
1752
1753    ! Arguments:
1754    real(8),          intent(in) :: Bsqrt(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1755    type(struct_gsv), intent(in) :: statevector_stdDev
1756
1757    ! Locals:
1758    integer   :: ier, fstouv, fnom, fstfrm, fclos
1759    integer   :: iunstats
1760    character(len=24) :: fileName = './bgcov.fst'
1761
1762    write(*,*)
1763    write(*,*) 'Writing covariance statistics for VAR'
1764
1765    !
1766    !- 1. Add the gridded std dev (and the Tic-Tac-Toc)
1767    !
1768    call gio_writeToFile(statevector_stdDev, trim(fileName), 'STDDEV', &
1769                         typvar_opt = 'E', numBits_opt = 32)
1770
1771    !
1772    !- 2. Add C^1/2
1773    !
1774    if (mmpi_myid == 0) then
1775      !- Opening Output file
1776      iunstats = 0
1777      ier    = fnom(iunstats,trim(fileName),'RND',0)
1778      ier    = fstouv(iunstats,'RND')
1779      
1780      !- Add Control Variable Info
1781      call WriteControlVarInfo(iunstats)
1782      
1783      !- Bsqrt
1784      call WriteSpVertCorrel(Bsqrt,iunstats,'ZN','B_SQUAREROOT') ! IN
1785      
1786      !- Closing output file
1787      ier =  fstfrm(iunstats)
1788      ier =  fclos (iunstats)
1789    end if
1790
1791  end subroutine writeVarStats
1792
1793  !--------------------------------------------------------------------------
1794  ! writeDiagStats
1795  !--------------------------------------------------------------------------
1796  subroutine writeDiagStats(NormB,SpVertCorrel,TotVertCorrel,statevector_mean, &
1797                            statevector_stdDevGridPoint,PowerSpectrum,HorizScale)
1798    !
1799    !:Purpose: To write other relevant data computed during the
1800    !           calculation of Bhi that are not needed for VAR applications
1801    !
1802    implicit none
1803
1804    ! Arguments:
1805    real(8),          intent(in) :: NormB(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1806    real(8),          intent(in) :: SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1807    real(8),          intent(in) :: TotVertCorrel(bhi%nVarLev,bhi%nVarLev)
1808    type(struct_gsv), intent(in) :: statevector_mean
1809    type(struct_gsv), intent(in) :: statevector_stdDevGridPoint
1810    real(8),          intent(in) :: PowerSpectrum(bhi%nVarLev,0:nTrunc)
1811    real(8),          intent(in) :: HorizScale(bhi%nVarLev)
1812
1813    ! Locals:
1814    integer   :: ier, fstouv, fnom, fstfrm, fclos
1815    integer   :: iunstats
1816    character(len=24) :: fileName = './bgcov_diag.fst'
1817
1818    write(*,*)
1819    write(*,*) 'Writing Diagnostics'
1820
1821    !
1822    !- 1. Add the gridded mean and std dev (and the Tic-Tac-Toc)
1823    !
1824    call gio_writeToFile(statevector_mean, trim(fileName), 'ENSMEAN', &
1825                         typvar_opt = 'E', numBits_opt = 32)
1826    call gio_writeToFile(statevector_stdDevGridPoint, trim(fileName), 'STDDEV_GRIDP', &
1827                         typvar_opt = 'E', numBits_opt = 32)
1828
1829    !
1830    !- 2. Add stats in spectral space
1831    !
1832    if (mmpi_myid == 0) then
1833      !- Opening Output file
1834      iunstats = 0
1835      ier    = fnom(iunstats,trim(fileName),'RND',0)
1836      ier    = fstouv(iunstats,'RND')
1837      
1838      !- Spectral Vertical Correlations
1839      call writeSpVertCorrel(SpVertCorrel,iunstats,'ZZ','SPVERTCORREL') ! IN
1840      
1841      !- Total Vertical Correlations
1842      call writeTotVertCorrel(TotVertCorrel,iunstats,'ZT','TTVERTCORREL') ! IN
1843      
1844      !- Normalized Vertical Correlations
1845      call writeSpVertCorrel(NormB,iunstats,'ZN','NRVERTCORREL') ! IN
1846      
1847      !- Power Spectrum
1848      call writePowerSpectrum(PowerSpectrum,iunstats,'POWERSPECT',cv_bhi) ! IN
1849      
1850      !- Horizontal Correlation Length scale
1851      call writeHorizScale(HorizScale,iunstats,'HORIZSCALE',cv_bhi) ! IN
1852      
1853      !- Closing output file
1854      ier =  fstfrm(iunstats)
1855      ier =  fclos (iunstats)
1856    end if
1857
1858  end subroutine writeDiagStats
1859
1860  !--------------------------------------------------------------------------
1861  ! writeSpVertCorrel
1862  !--------------------------------------------------------------------------
1863  subroutine writeSpVertCorrel(SpVertCorrel,iun,nomvar_in,etiket_in)
1864    !
1865    !:Purpose: To write vertical correlations in spectral space
1866    !
1867    implicit none
1868
1869    ! Arguments:
1870    real(8),          intent(in) :: SpVertCorrel(bhi%nVarLev,bhi%nVarLev,0:nTrunc)
1871    integer,          intent(in) :: iun
1872    character(len=*), intent(in) :: nomvar_in
1873    character(len=*), intent(in) :: etiket_in
1874
1875    ! Locals:
1876    real(4), allocatable :: work2d(:,:)
1877    real(4) :: work
1878    integer   :: ier, fstecr, totwvnb
1879    integer :: dateo, npak, ni, nj, nk
1880    integer :: ip1, ip2, ip3, deet, npas, datyp
1881    integer :: ig1 ,ig2 ,ig3 ,ig4
1882    character(len=1)  :: grtyp
1883    character(len=4)  :: nomvar
1884    character(len=2)  :: typvar
1885    character(len=12) :: etiket
1886
1887    allocate(work2d(bhi%nVarLev, bhi%nVarLev))
1888
1889    !- Loop over Total Wavenumbers
1890    do totwvnb = 0, nTrunc
1891
1892      npak   = -32
1893      dateo  = 0
1894      deet   = 0
1895      npas   = 0
1896      ni     = bhi%nVarLev
1897      nj     = bhi%nVarLev
1898      nk     = 1
1899      ip1    = 0
1900      ip2    = totwvnb
1901      ip3    = nEns
1902      typvar = 'XX'
1903      nomvar = nomvar_in
1904      etiket = etiket_in
1905      grtyp  = 'X'
1906      ig1    = 0
1907      ig2    = 0
1908      ig3    = 0
1909      ig4    = 0
1910      datyp  = 5
1911
1912      !- Extract from full Matrix
1913      work2d(:,:) = real(SpVertCorrel(:,:,totwvnb),4)
1914
1915      !- Writing 
1916      ier = fstecr(work2d, work, npak, iun, dateo, deet, npas, ni, nj, &
1917           nk, ip1, ip2, ip3, typvar, nomvar, etiket, grtyp,        &
1918           ig1, ig2, ig3, ig4, datyp, .true.)
1919
1920    end do ! Total Wavenumbers
1921
1922    deallocate(work2d)
1923
1924  end subroutine writeSpVertCorrel
1925
1926  !--------------------------------------------------------------------------
1927  ! writeTotVertCorrel
1928  !--------------------------------------------------------------------------
1929  subroutine writeTotVertCorrel(TotVertCorrel,iun,nomvar_in,etiket_in)
1930    !
1931    !:Purpose: To write the total vertical correlations
1932    !           (i.e. gridpoint equivalent)
1933    !
1934    implicit none
1935
1936    ! Arguments:
1937    real(8),          intent(in) :: TotVertCorrel(bhi%nVarLev,bhi%nVarLev)
1938    integer,          intent(in) :: iun
1939    character(len=*), intent(in) :: nomvar_in
1940    character(len=*), intent(in) :: etiket_in
1941
1942    ! Locals:
1943    real(4), allocatable :: workecr(:,:)
1944    real(4)   :: work
1945    integer   :: ier, fstecr
1946    integer :: dateo, npak, ni, nj, nk
1947    integer :: ip1, ip2, ip3, deet, npas, datyp
1948    integer :: ig1 ,ig2 ,ig3 ,ig4
1949    character(len=1)  :: grtyp
1950    character(len=4)  :: nomvar
1951    character(len=2)  :: typvar
1952    character(len=12) :: etiket
1953
1954    allocate(workecr(bhi%nVarLev, bhi%nVarLev))
1955
1956    npak   = -32
1957    dateo  = 0
1958    deet   = 0
1959    npas   = 0
1960    ni     = bhi%nVarLev
1961    nj     = bhi%nVarLev
1962    nk     = 1
1963    ip1    = 0
1964    ip2    = 0
1965    ip3    = nEns
1966    typvar = 'XX'
1967    nomvar = nomvar_in
1968    etiket = etiket_in
1969    grtyp  = 'X'
1970    ig1    = 0
1971    ig2    = 0
1972    ig3    = 0
1973    ig4    = 0
1974    datyp  = 5
1975
1976    !- Covert to real 4
1977    workecr(:,:) = real(TotVertCorrel(:,:),4)
1978
1979    !- Writing 
1980    ier = fstecr(workecr, work, npak, iun, dateo, deet, npas, ni, nj, &
1981         nk, ip1, ip2, ip3, typvar, nomvar, etiket, grtyp,        &
1982         ig1, ig2, ig3, ig4, datyp, .true.)
1983
1984    deallocate(workecr)
1985
1986  end subroutine writeTotVertCorrel
1987
1988  !--------------------------------------------------------------------------
1989  ! writePowerSpectrum
1990  !--------------------------------------------------------------------------
1991  subroutine writePowerSpectrum(PowerSpectrum,iun,etiket_in,cv_type)
1992    !
1993    !:Purpose: To write the power spectrum 
1994    !
1995    implicit none
1996
1997    ! Arguments:
1998    real(8),          intent(in) :: PowerSpectrum(bhi%nVarLev,0:nTrunc)
1999    integer,          intent(in) :: iun
2000    integer,          intent(in) :: cv_type
2001    character(len=*), intent(in) :: Etiket_in
2002
2003    ! Locals:
2004    real(4), allocatable :: workecr(:,:)
2005    real(4)   :: work
2006    integer   :: ier, fstecr
2007    integer   :: var, k, kgdim
2008    integer :: dateo, npak, ni, nj, nk
2009    integer :: ip1, ip2, ip3, deet, npas, datyp
2010    integer :: ig1 ,ig2 ,ig3 ,ig4
2011    character(len=1)  :: grtyp
2012    character(len=4)  :: nomvar
2013    character(len=2)  :: typvar
2014    character(len=12) :: etiket
2015
2016    allocate(workecr(nTrunc+1, 1))
2017
2018    !- Loop over Control Variables
2019    do var = 1, bhi%nControlVariable
2020
2021      !- Loop over vertical Levels
2022      do k = 1, bhi%controlVariable(var)%nlev
2023
2024        npak   = -32
2025        dateo  = 0
2026        deet   = 0
2027        npas   = 0
2028        ni     = nTrunc + 1
2029        nj     = 1
2030        nk     = 1
2031        ip1    = bhi%controlVariable(var)%ip1(k)
2032        ip2    = 0
2033        ip3    = 0
2034        typvar = 'E'
2035        nomvar = trim(bhi%controlVariable(var)%nomvar(cv_type))
2036        etiket = trim(Etiket_in)
2037        grtyp  = 'X'
2038        ig1    = 0
2039        ig2    = 0
2040        ig3    = 0 
2041        ig4    = 0
2042        datyp  = 1
2043
2044        !- Extract
2045        kgdim = bhi%controlVariable(var)%varLevIndexStart + k - 1
2046        workecr(:,1) = real(PowerSpectrum(kgdim,:),4)
2047
2048        !- Writing 
2049        ier = fstecr(workecr, work, npak, iun, dateo, deet, npas, ni, nj, &
2050             nk, ip1, ip2, ip3, typvar, nomvar, etiket, grtyp,   &
2051             ig1, ig2, ig3, ig4, datyp, .true.)
2052
2053      end do ! Vertical Levels
2054
2055    end do ! Variables
2056
2057    deallocate(workecr)
2058
2059  end subroutine writePowerSpectrum
2060
2061  !--------------------------------------------------------------------------
2062  ! writeHorizScale
2063  !--------------------------------------------------------------------------
2064  subroutine writeHorizScale(HorizScale,iun,etiket_in,cv_type)
2065    !
2066    !:Purpose: To write the horizontal lenght scales
2067    !
2068    implicit none
2069
2070    ! Arguments:
2071    real(8),          intent(in) :: HorizScale(bhi%nVarLev)
2072    integer,          intent(in) :: iun
2073    integer,          intent(in) :: cv_type
2074    character(len=*), intent(in) :: Etiket_in
2075
2076    ! Locals:
2077    real(4), allocatable :: workecr(:,:,:)
2078    real(4)   :: work
2079    integer   :: ier, fstecr, var
2080    integer :: dateo, npak, ni, nj, nk
2081    integer :: ip1, ip2, ip3, deet, npas, datyp
2082    integer :: ig1 ,ig2 ,ig3 ,ig4
2083    character(len=1)  :: grtyp
2084    character(len=4)  :: nomvar
2085    character(len=2)  :: typvar
2086    character(len=12) :: etiket
2087
2088    !- Loop over Control Variables
2089    do var = 1, bhi%nControlVariable
2090
2091      allocate(workecr(1,1,bhi%controlVariable(var)%nlev))
2092
2093      npak   = -32
2094      dateo  = 0
2095      deet   = 0
2096      npas   = 0
2097      ni     = 1
2098      nj     = 1
2099      nk     = bhi%controlVariable(var)%nlev
2100      ip1    = 0
2101      ip2    = 0
2102      ip3    = 0
2103      typvar = 'E'
2104      nomvar = trim(bhi%controlVariable(var)%nomvar(cv_type))
2105      etiket = trim(Etiket_in)
2106      grtyp  = 'X'
2107      ig1    = 0
2108      ig2    = 0
2109      ig3    = 0
2110      ig4    = 0
2111      datyp  = 1
2112
2113      !- Extract
2114      workecr(1,1,:) = real(HorizScale(bhi%controlVariable(var)%varLevIndexStart:bhi%controlVariable(var)%varLevIndexEnd),4)
2115
2116      !- Writing 
2117      ier = fstecr(workecr, work, npak, iun, dateo, deet, npas, ni, nj, &
2118           nk, ip1, ip2, ip3, typvar, nomvar, etiket, grtyp,   &
2119           ig1, ig2, ig3, ig4, datyp, .true.)
2120
2121      deallocate(workecr)
2122
2123    end do ! Variables
2124
2125  end subroutine writeHorizScale
2126
2127  !--------------------------------------------------------------------------
2128  ! writeControlVarInfo
2129  !--------------------------------------------------------------------------
2130  subroutine writeControlVarInfo(iun)
2131    !
2132    !:Purpose: To write the control variable related info
2133    !
2134    implicit none
2135
2136    ! Arguments:
2137    integer, intent(in) :: iun
2138
2139    ! Locals:
2140    integer :: ier, fstecr, fstecr_s
2141    real(8) :: work
2142    integer :: npak, var, dateo, ni, nj
2143    integer :: ip1,ip2,ip3,deet,npas,datyp,ig1,ig2,ig3,ig4
2144    character(len=1)  :: grtyp
2145    character(len=2)  :: typvar
2146    character(len=4)  :: nomvar
2147    character(len=4)  :: ControlModelVarnameList(bhi%nControlVariable)
2148    character(len=4)  :: ControlBhiVarnameList  (bhi%nControlVariable)
2149    character(len=2)  :: ControlVarGridTypeList (bhi%nControlVariable)
2150    integer           :: ControlVarNlevList     (bhi%nControlVariable)
2151
2152    !
2153    !- 1. Gathering the info
2154    !
2155
2156    !- Loop over Control Variables
2157    do var = 1, bhi%nControlVariable
2158      ControlModelVarnameList(var) = trim(bhi%controlVariable(var)%nomvar(cv_model))
2159      ControlBhiVarnameList(var)   = trim(bhi%controlVariable(var)%nomvar(cv_bhi))
2160      ControlVarNlevList(var)      = bhi%controlVariable(var)%nlev
2161      ControlVarGridTypeList(var)  = bhi%controlVariable(var)%GridType
2162    end do
2163
2164    write(*,*)
2165    write(*,*) 'ControlModelVarnameList = ',ControlModelVarnameList(:)
2166    write(*,*) 'ControlBhiVarnameList   = ',ControlBhiVarnameList(:)
2167    write(*,*) 'ControlVarNlevList      = ',ControlVarNlevList(:)
2168    write(*,*) 'ControlVarGridTypeList  = ',ControlVarGridTypeList(:)
2169
2170    !
2171    !- 2.  Writing the list of control variables and number of vertical levels
2172    !
2173    npak     = -32
2174    dateo    =  0
2175    deet     =  0
2176    ip1      =  0
2177    ip2      =  0
2178    ip3      =  0
2179    npas     =  0
2180    grtyp    = 'X'
2181    typvar   = 'X'
2182    ig1      =  0
2183    ig2      =  0
2184    ig3      =  0
2185    ig4      =  0
2186
2187    nomvar   = 'CVN'
2188    ni       =  4  ! 4 Characters
2189    nj       =  bhi%nControlVariable
2190    datyp    =  7 ! Character
2191
2192    ier = fstecr_s(ControlModelVarnameList, work, npak, &
2193         iun, dateo, deet, npas, ni, nj, 1, ip1,    &
2194         ip2, ip3, typvar, nomvar, 'MODEL', grtyp, ig1, &
2195         ig2, ig3, ig4, datyp, .true.)
2196
2197    ier = fstecr_s(ControlBhiVarnameList, work, npak, &
2198         iun, dateo, deet, npas, ni, nj, 1, ip1,    &
2199         ip2, ip3, typvar, nomvar, 'B_HI', grtyp, ig1, &
2200         ig2, ig3, ig4, datyp, .true.)
2201
2202    nomvar   = 'CVL'
2203    ni       =  2  ! 2 Characters
2204    ier = fstecr_s(ControlVarGridTypeList, work, npak, &
2205         iun, dateo, deet, npas, ni, nj, 1, ip1,    &
2206         ip2, ip3, typvar, nomvar, 'LEVTYPE', grtyp, ig1, &
2207         ig2, ig3, ig4, datyp, .true.)
2208
2209    datyp    =  2 ! Integer
2210    ni       =  bhi%nControlVariable
2211    nj       =  1 
2212    ier = fstecr(ControlVarNlevList, work, npak, &
2213         iun, dateo, deet, npas, ni, nj, 1, ip1,    &
2214         ip2, ip3, typvar, nomvar, 'NLEV', grtyp, ig1, &
2215         ig2, ig3, ig4, datyp, .true.)
2216
2217  end subroutine writeControlVarInfo
2218
2219  !--------------------------------------------------------------------------
2220  ! writePressureProfiles
2221  !--------------------------------------------------------------------------
2222  subroutine writePressureProfiles
2223    !
2224    !:Purpose: To write the MM and TH pressure profiles used for vertical localization
2225    !
2226    implicit none
2227
2228    ! Locals:
2229    character(len=128) :: outfilename
2230    integer :: jk
2231
2232    outfilename = "./pressureProfile_M.txt"
2233    open (unit=99,file=outfilename,action="write",status="new")
2234    do jk = 1, vco_bhi%nlev_M
2235      write(99,'(I3,2X,F6.1)') jk, pressureProfile_M(jk)/100.d0
2236    end do
2237    close(unit=99)
2238
2239    outfilename = "./pressureProfile_T.txt"
2240    open (unit=99,file=outfilename,action="write",status="new")
2241    do jk = 1, vco_bhi%nlev_T
2242      write(99,'(I3,2X,F6.1)') jk, pressureProfile_T(jk)/100.d0
2243    end do
2244    close(unit=99)
2245
2246    write(6,*) 'finished writing pressure profiles...'
2247    call flush(6)
2248
2249  end subroutine writePressureProfiles
2250
2251  !--------------------------------------------------------------------------
2252  ! calcLocalCorrelations
2253  !--------------------------------------------------------------------------
2254  subroutine calcLocalCorrelations(ensPerts)
2255    !
2256    !:Purpose: To compute the local horizontal correlation for some 'reference' grid points
2257    !
2258    implicit none
2259
2260    ! Arguments:
2261    type(struct_ens), intent(inout) :: ensPerts
2262
2263    ! Locals:
2264    type(struct_gsv) :: statevector_locHorizCor
2265    type(struct_gsv) :: statevector_oneMember
2266    type(struct_gsv) :: statevector_oneMemberTiles
2267    real(8), pointer :: ptr3d_r8(:,:,:)
2268    real(8), pointer :: ptr3d_r8_oneMember(:,:,:)
2269    real(8) :: dnEns
2270    integer :: i, j, k, ens
2271    integer :: blocklength_x, blocklength_y, blockpadding, nirefpoint, njrefpoint
2272    integer :: iref_id, jref_id, iref, jref
2273    integer :: imin, imax, jmin, jmax
2274    character(len=4), pointer :: varNamesList(:)
2275    integer :: ier, fclos, fnom, nulnam
2276
2277    NAMELIST /NAMHVCORREL_LOCAL/nirefpoint, njrefpoint, blockpadding
2278
2279    !
2280    ! To compute the local horizontal correlation for some 'reference' grid point
2281    ! ... we assume that the ensemble grid point mean was removed and that
2282    !     the ensemble values were divided by the grid point std dev.
2283    !
2284
2285    nirefpoint = 4 ! Number of reference grid point in x
2286    njrefpoint = 2 ! Number of reference grid point in y
2287    blockpadding = 4  ! Number of grid point padding between blocks (to set correlation to 0 between each block)
2288
2289    nulnam = 0
2290    ier = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
2291    read(nulnam,nml=NAMHVCORREL_LOCAL)
2292    write(*,nml=NAMHVCORREL_LOCAL)
2293    ier = fclos(nulnam)
2294
2295    blocklength_x = hco_ens%ni / nirefpoint ! Horizontal correlation will be compute blocklength x blocklength gridpoint
2296    ! around each reference point
2297    blocklength_y = hco_ens%nj / njrefpoint ! Horizontal correlation will be compute blocklength x blocklength gridpoint
2298    ! around each reference point
2299
2300    nullify(varNamesList)
2301    call ens_varNamesList(varNamesList,ensPerts) 
2302
2303    call gsv_allocate(statevector_locHorizCor, ens_getNumStep(ensPerts),                     &
2304                      ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
2305                      datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true.,                &
2306                      mpi_distribution_opt='VarsLevs', dataKind_opt=8 )
2307
2308    call gsv_allocate(statevector_oneMemberTiles, ens_getNumStep(ensPerts),                  &
2309                      ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
2310                      datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true.,                &
2311                      mpi_distribution_opt='Tiles', dataKind_opt=8 )
2312
2313    call gsv_allocate(statevector_oneMember, ens_getNumStep(ensPerts),                       &
2314                      ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
2315                      datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true.,                &
2316                      mpi_distribution_opt='VarsLevs', dataKind_opt=8 )
2317
2318    call gsv_zero(statevector_locHorizCor)
2319
2320    dnEns = 1.0d0/dble(nEns-1)
2321
2322    call gsv_getField(statevector_locHorizCor,ptr3d_r8)
2323
2324    do ens = 1, nEns
2325      call ens_copyMember(ensPerts, statevector_oneMemberTiles, ens)
2326      call gsv_transposeTilesToVarsLevs(statevector_oneMemberTiles, statevector_oneMember)
2327      call gsv_getField(statevector_oneMember,ptr3d_r8_oneMember)
2328
2329      do k = statevector_locHorizCor%mykBeg, statevector_locHorizCor%mykEnd
2330        do jref_id = 1, njrefpoint
2331          do iref_id = 1, nirefpoint
2332            iref = (2*iref_id-1)*blocklength_x/2
2333            jref = (2*jref_id-1)*blocklength_y/2
2334            jmin = max(jref-(blocklength_y-blockpadding)/2,1)
2335            jmax = min(jref+(blocklength_y-blockpadding)/2,hco_ens%nj)
2336            imin = max(iref-(blocklength_x-blockpadding)/2,1)
2337            imax = min(iref+(blocklength_x-blockpadding)/2,hco_ens%ni)
2338            do j = jmin, jmax
2339              do i = imin, imax
2340                ptr3d_r8(i,j,k) = ptr3d_r8(i,j,k) + &
2341                     ptr3d_r8_oneMember(i,j,k)*ptr3d_r8_oneMember(iref,jref,k)
2342              end do
2343            end do
2344          end do
2345        end do
2346      end do
2347
2348    end do
2349
2350    call gsv_scale(statevector_locHorizCor,dnEns)
2351
2352    write(*,*) 'finished computing the local horizontal correlations...'
2353
2354    !
2355    !- 4.  Write to file
2356    !
2357    call gio_writeToFile(statevector_locHorizCor, './horizCorrelLocal.fst', 'HCORREL_LOC', &
2358                         typvar_opt = 'E', numBits_opt = 32)
2359
2360    call gsv_deallocate(statevector_locHorizCor)
2361    call gsv_deallocate(statevector_oneMember)
2362    call gsv_deallocate(statevector_oneMemberTiles)
2363
2364  end subroutine calcLocalCorrelations
2365
2366end module calcStatsLam_mod