bMatrix1DVar_mod sourceΒΆ

   1module bMatrix1DVar_mod
   2  ! MODULE bMatrix1DVar_mod (prefix='bmat1D' category='2. B and R matrices')
   3  !
   4  !:Purpose: contains all 1Dvar B matrices.
   5  !
   6  use mathPhysConstants_mod
   7  use columnData_mod
   8  use columnVariableTransforms_mod
   9  use controlVector_mod
  10  use gridStatevector_mod
  11  use gridstatevectorFileIO_mod
  12  use horizontalCoord_mod
  13  use midasMpi_mod 
  14  use obsSpaceData_mod
  15  use timeCoord_mod
  16  use utilities_mod
  17  use verticalCoord_mod
  18  use tovsNL_mod
  19  use var1D_mod
  20  use filenames_mod
  21  use localizationFunction_mod
  22  use varNameList_mod
  23  use ensembleStateVector_mod
  24  use stateToColumn_mod
  25  use calcHeightAndPressure_mod
  26  implicit none
  27  save
  28  private
  29
  30  ! public procedures
  31  public :: bmat1D_bsetup
  32  public :: bmat1D_sqrtB, bmat1D_sqrtBT
  33  public :: bmat1D_finalize, bmat1D_get1DVarIncrement
  34
  35  ! public variables
  36  public :: bmat1D_includeAnlVar, bmat1D_numIncludeAnlVar
  37
  38  integer                       :: bmat1D_numIncludeAnlVar
  39  character(len=4), allocatable :: bmat1D_includeAnlVar(:)
  40
  41  type(struct_hco), pointer :: hco_yGrid
  42  logical             :: initialized = .false.
  43  integer             :: nkgdim
  44  integer             :: cvDim_mpilocal
  45 
  46  real(8), allocatable :: bSqrtLand(:,:,:), bSqrtSea(:,:,:)
  47  real(8), allocatable :: bSqrtEns(:,:,:)
  48  real(4), allocatable :: latLand(:), lonLand(:), latSea(:), lonSea(:)
  49  integer              :: nLonLatPosLand, nLonLatPosSea
  50  integer, external    :: get_max_rss
  51  integer,          parameter :: numMasterBmat = 2
  52  character(len=4), parameter :: masterBmatTypeList (numMasterBmat) = (/ 'HI', 'ENS' /)
  53  character(len=8), parameter :: masterBmatLabelList(numMasterBmat) = (/'B_HI', 'B_ENS' /)
  54  logical,          parameter :: masterbmatIs3dList (numMasterBmat) = (/.true., .true. /) 
  55  integer            :: numBmat
  56  integer, parameter :: numBmatMax = 10
  57  character(len=4) :: bmatTypeList  (numBmatMax)
  58  character(len=9) :: bmatLabelList (numBmatMax)
  59  logical          :: bmatIs3dList  (numBmatMax)
  60  logical          :: bmatActive    (numBmatMax)
  61  type(struct_columnData), allocatable :: ensColumns(:)
  62  type(struct_columnData) :: meanColumn
  63  type(struct_ens) :: ensembles
  64
  65  ! Namelist variables
  66  integer          :: nEns                             ! ensemble size
  67  real(8)          :: vlocalize                        ! vertical localization length scale
  68  character(len=4) :: includeAnlVar(vnl_numvarmax)     ! list of variable names to include in B matrix
  69  integer :: numIncludeAnlVar                          ! MUST NOT BE INCLUDED IN NAMELIST!
  70  real(8) :: scaleFactorHI(vco_maxNumLevels)           ! scaling factors for HI variances
  71  real(8) :: scaleFactorHIHumidity(vco_maxNumLevels)   ! scaling factors for HI humidity variances
  72  real(8) :: scaleFactorEns(vco_maxNumLevels)          ! scaling factors for Ens variances
  73  real(8) :: scaleFactorEnsHumidity(vco_maxNumLevels)  ! scaling factors for Ens humidity variances
  74  logical :: dumpBmatrixTofile                         ! flag to control output of B matrices to Bmatrix.bin binary file
  75  logical :: doAveraging                               ! flag to control output the average instead of the invidual B matrices
  76  real(8) :: latMin                                    ! minimum latitude of the Bmatrix latitude-longitude output box
  77  real(8) :: latMax                                    ! maximum latitude of the Bmatrix latitude-longitude output box
  78  real(8) :: lonMin                                    ! minimum longitude of the Bmatrix latitude-longitude output box
  79  real(8) :: lonMax                                    ! maximum longitude of the Bmatrix latitude-longitude output box
  80  NAMELIST /NAMBMAT1D/ scaleFactorHI, scaleFactorHIHumidity, scaleFactorENs, scaleFactorEnsHumidity, nEns, &
  81      vLocalize, includeAnlVar, numIncludeAnlVar, dumpBmatrixTofile, latMin, latMax, lonMin, lonMax, &
  82      doAveraging
  83
  84contains
  85
  86  !--------------------------------------------------------------------------
  87  ! bmat1D_bsetup
  88  !--------------------------------------------------------------------------
  89  subroutine bmat1D_bsetup(vco_in, hco_in, obsSpaceData)
  90    !
  91    !:Purpose: To initialize the 1Dvar analysis Background term.
  92    !
  93    implicit none
  94
  95    ! Arguments:
  96    type(struct_vco), pointer, intent(in)    :: vco_in
  97    type(struct_hco), pointer, intent(in)    :: hco_in
  98    type (struct_obs),         intent(inout) :: obsSpaceData
  99
 100    ! Locals:
 101    integer :: cvdim
 102    integer :: masterBmatIndex, bmatIndex
 103    logical :: active
 104    integer :: nulnam, ierr
 105    integer, external ::  fnom, fclos
 106    integer :: varIndex
 107
 108    call utl_tmg_start(50, '--Bmatrix')
 109
 110    ! default values for namelist variables
 111    scaleFactorHI(:) = 0.d0
 112    scaleFactorHIHumidity(:) = 1.d0
 113    scaleFactorEns(:) = 0.d0
 114    scaleFactorEnsHumidity(:) = 1.d0
 115    nEns = -1
 116    vLocalize = -1.d0
 117    includeAnlVar(:)= ''
 118    numIncludeAnlVar = MPC_missingValue_INT
 119    dumpBmatrixTofile = .false.
 120    doAveraging = .false.
 121    latMin = -1000.d0
 122    latMax = 1000.d0
 123    lonMin = -1000.d0
 124    lonMax = 1000.d0
 125    
 126    nulnam = 0
 127    ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
 128    read(nulnam, nml=nambmat1D, iostat=ierr)
 129    if ( ierr /= 0 ) call utl_abort( 'bmat1D_bsetup: Error reading namelist' )
 130    if ( mmpi_myid == 0 ) write( *, nml = nambmat1D )
 131    ierr = fclos( nulnam )
 132    if (numIncludeAnlVar /= MPC_missingValue_INT) then
 133      call utl_abort('bmat1D_bsetup: check NAMBMAT1D namelist section: numIncludeAnlVar should be removed')
 134    end if
 135    numIncludeAnlVar = 0
 136    do varIndex = 1, vnl_numvarmax
 137      if (trim(includeAnlVar(varIndex)) == '') exit
 138      numIncludeAnlVar = numIncludeAnlVar + 1
 139    end do
 140    bmat1D_numIncludeAnlVar = numIncludeAnlVar
 141    allocate( bmat1D_includeAnlVar(bmat1D_numIncludeAnlVar) )
 142    bmat1D_includeAnlVar(1:bmat1D_numIncludeAnlVar) = includeAnlVar(1:numIncludeAnlVar)
 143
 144    !
 145    !- 1.  Setup the B matrices
 146    !
 147    do masterBmatIndex = 1, numMasterBmat
 148
 149      select case( trim(masterBmatTypeList(masterBmatIndex)) )
 150      case ('HI')
 151        !- 1.1 Time-Mean Homogeneous and Isotropic...
 152        write(*,*) 'bmat1D_bsetup: Setting up the modular GLOBAL HI 1D covariances...'
 153        call utl_tmg_start(51, '----B_HI_Setup')
 154        call bmat1D_SetupBHi(vco_in, obsSpaceData, cvdim)
 155        call utl_tmg_stop(51)
 156        write(*,*) ' bmat1D_bsetup: cvdim= ', cvdim
 157      case ('ENS')
 158        !- 1.2 ensemble based
 159        write(*,*) 'bmat1D_bsetup: Setting up the ensemble based 1D matrix.'
 160        call utl_tmg_start(54, '----B_ENS_Setup')
 161        call bmat1D_SetupBEns(vco_in, hco_in, obsSpaceData, cvdim)
 162        call utl_tmg_stop(54)
 163        write(*,*) ' bmat1D_bsetup: cvdim= ', cvdim
 164      case default
 165        call utl_abort('bmat1D_bSetup: requested bmatrix type does not exist ' // trim(masterBmatTypeList(masterBmatIndex)))
 166      end select
 167
 168      !- 1.2 Append the info to the B matrix info arrays and setup the proper control sub-vectors
 169      numBmat = numBmat + 1
 170      bmatLabelList (numBmat) = trim(masterbmatLabelList(masterBmatIndex))
 171      bmatTypeList  (numBmat) = masterBmatTypeList(masterBmatIndex)
 172      bmatIs3dList  (numBmat) = masterbmatIs3dList(masterBmatIndex)
 173      call cvm_setupSubVector(bmatLabelList(numBmat), bmatTypeList(numBmat), cvdim)
 174    end do
 175
 176    !
 177    !- 2. Print a summary and set the active B matrices array
 178    !
 179    write(*,*)
 180    write(*,*) 'bmat1D_bsetup SUMMARY, number of B matrices found = ', numBmat
 181    do bmatIndex = 1, numBmat
 182      write(*,*) '  B matrix #', bmatIndex
 183      active = cvm_subVectorExists(bmatLabelList(bmatIndex))
 184      if (active) then
 185        write(*,*) '   ACTIVE'
 186      else
 187        write(*,*) '   NOT USED'
 188      end if
 189      write(*,*) '     -> label       = ', bmatLabelList (bmatIndex)
 190      write(*,*) '     -> type        = ', bmatTypeList  (bmatIndex)
 191      if (active) then
 192        write(*,*) '     -> is 3D       = ', bmatIs3dList  (bmatIndex)
 193      end if
 194      bmatActive(bmatIndex) = active
 195    end do
 196
 197    call utl_tmg_stop(50)
 198
 199  end subroutine bmat1D_bsetup
 200
 201  !--------------------------------------------------------------------------
 202  !  bmat1D_setupBHi
 203  !--------------------------------------------------------------------------
 204  subroutine bmat1D_setupBHi(vco_in, obsSpaceData, cvDim_out)
 205    !
 206    !:Purpose: to setup bmat1D module
 207    !
 208    implicit none
 209
 210    ! Arguments:
 211    type(struct_vco), pointer, intent(in)  :: vco_in
 212    type (struct_obs)        , intent(in)  :: obsSpaceData
 213    integer                  , intent(out) :: cvDim_out
 214
 215    ! Locals:
 216    integer :: levelIndex, ierr
 217    integer, external ::  fnom, fclos
 218    integer :: Vcode_anl
 219    logical :: fileExists
 220    integer :: nulbgst=0
 221    type(struct_vco), pointer :: vco_file => null()
 222    type(struct_vco), target  :: vco_1Dvar
 223    type(struct_vco), pointer :: vco_anl
 224    character(len=18) :: oneDBmatLand = './Bmatrix_land.bin'
 225    character(len=17) :: oneDBmatSea  = './Bmatrix_sea.bin'
 226    character(len=4), allocatable :: includeAnlVarHi(:)
 227    integer :: extractDate, locationIndex, varIndex, numIncludeAnlVarHi
 228    integer :: shiftLevel, varLevIndex1, varLevIndex2
 229    integer :: varLevIndexBmat
 230    logical, save :: firstCall=.true.
 231    real(8), allocatable :: bMatrix(:,:), multFactor(:)
 232
 233    if (.not. (gsv_varExist(varName='TT') .and.  &
 234               gsv_varExist(varName='UU') .and.  &
 235               gsv_varExist(varName='VV') .and.  &
 236               (gsv_varExist(varName='HU').or.gsv_varExist(varName='LQ')) .and.  &
 237               gsv_varExist(varName='P0')) ) then
 238      call utl_abort('bmat1D_setupBHi: Some or all weather fields are missing. If it is desired to deactivate&
 239           & the weather assimilation, then all entries of the array SCALEFACTORHI in the namelist NAMVAR1D&
 240           & should be set to zero.')
 241    end if
 242
 243    if (.not. gsv_varExist(varName='TG')) then
 244      write(*,*) 'bmat1D_setupBHi: WARNING: The TG field is missing. This must be present when assimilating'
 245      write(*,*) 'radiance observations.'
 246    end if
 247
 248    if (firstCall) then
 249      call var1D_setup(obsSpaceData)
 250      firstCall = .false.
 251    end if
 252
 253    if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBHi: Starting'
 254    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 255
 256    do levelIndex = 1, vco_maxNumLevels
 257      if( scaleFactorHI(levelIndex) > 0.0d0 ) then 
 258        scaleFactorHI(levelIndex) = sqrt( scaleFactorHI(levelIndex))
 259      else
 260        scaleFactorHI(levelIndex) = 0.0d0
 261      end if
 262    end do
 263   
 264    do levelIndex = 1, vco_maxNumLevels
 265      if(scaleFactorHIHumidity(levelIndex) > 0.0d0) then 
 266        scaleFactorHIHumidity(levelIndex) = sqrt(scaleFactorHIHumidity(levelIndex))
 267      else
 268        scaleFactorHIHumidity(levelIndex) = 0.0d0
 269      end if
 270    end do
 271
 272    if ( sum(scaleFactorHI(1:vco_maxNumLevels)) == 0.0d0 ) then
 273      if ( mmpi_myid == 0 ) write(*,*) 'bmat1D_setupBHi: scaleFactorHI=0, skipping rest of setup'
 274      cvDim_out = 0
 275      return
 276    end if
 277
 278    if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBHi: Read 1DVar background statistics'
 279    inquire(file=trim(oneDBmatLand), exist=fileExists)
 280    if ( fileExists ) then
 281      ierr = fnom(nulbgst, trim(oneDBmatLand), 'FTN+SEQ+UNF+OLD+R/O', 0)
 282    else
 283      call utl_abort('bmat1D_setupBHi: No 1DVar BACKGROUND STAT FILE ' // trim(oneDBmatLand))
 284    end if
 285
 286    read(nulbgst) extractDate, vco_1Dvar%nLev_T, vco_1Dvar%nLev_M, vco_1Dvar%Vcode, &
 287         vco_1Dvar%ip1_sfc, vco_1Dvar%ip1_T_2m, vco_1Dvar%ip1_M_10m, numIncludeAnlVarHi, nkgdim, nLonLatPosLand
 288    allocate( vco_1Dvar%ip1_T(vco_1Dvar%nLev_T), vco_1Dvar%ip1_M(vco_1Dvar%nLev_M) )
 289    if (numIncludeAnlVarHi /= bmat1D_numIncludeAnlVar) then
 290      write(*,*) 'numIncludeAnlVarHi, bmat1D_numIncludeAnlVar= ', numIncludeAnlVarHi, bmat1D_numIncludeAnlVar
 291      call utl_abort('bmat1D_setupBHi: incompatible number of 1DVar analyzed variables in ' // trim(oneDBmatLand))
 292    end if
 293
 294    allocate (multFactor(nkgdim))
 295    if(vco_1Dvar%Vcode == 5002) then
 296      shiftLevel = 1
 297    else
 298      shiftLevel = 0
 299    end if
 300    varLevIndexBmat = 0
 301    do varIndex = 1, bmat1D_numIncludeAnlVar
 302      select case(bmat1D_includeAnlVar(varIndex))
 303        case('TT')
 304          do levelIndex = 1, vco_1Dvar%nLev_T
 305            varLevIndexBmat = varLevIndexBmat + 1
 306            multFactor(varLevIndexBmat) = scaleFactorHI(levelIndex)
 307          end do
 308        case('HU')
 309          do levelIndex = 1, vco_1Dvar%nLev_T
 310            varLevIndexBmat = varLevIndexBmat + 1
 311            multFactor(varLevIndexBmat)= scaleFactorHIHumidity(levelIndex) * scaleFactorHI(levelIndex)
 312          end do
 313        case('UU','VV')
 314          do levelIndex = 1, vco_1Dvar%nLev_M
 315            varLevIndexBmat = varLevIndexBmat + 1
 316            multFactor(varLevIndexBmat) = scaleFactorHI(levelIndex+shiftLevel)
 317          end do
 318        case('P0','TG')
 319          varLevIndexBmat = varLevIndexBmat + 1
 320          multFactor(varLevIndexBmat) = scaleFactorHI(max(vco_1Dvar%nLev_T,vco_1Dvar%nLev_M))
 321        case default
 322          call utl_abort('bmat1D_setupBHi: unsupported variable ' // bmat1D_includeAnlVar(varIndex))
 323        end select
 324    end do
 325
 326    allocate( includeAnlVarHi(bmat1D_numIncludeAnlVar) )
 327    allocate( bMatrix(nkgdim,nkgdim) )
 328    allocate( latLand(nLonLatPosLand), lonLand(nLonLatPosLand))       
 329    allocate( bSqrtLand(nLonLatPosLand, nkgdim, nkgdim) )
 330    read(nulbgst) vco_1Dvar%ip1_T(:), vco_1Dvar%ip1_M(:), includeAnlVarHi(:)
 331    if (any(includeAnlVarHi /= bmat1D_includeAnlVar)) then
 332      do varIndex = 1, bmat1D_numIncludeAnlVar
 333        write(*,*) varIndex, includeAnlVarHi(varIndex), bmat1D_includeAnlVar(varIndex)
 334      end do
 335      call utl_abort('bmat1D_setupBHi: incompatible 1DVar analyzed variable list in ' // trim(oneDBmatLand))
 336    end if
 337    deallocate(includeAnlVarHi)
 338
 339    do locationIndex = 1, nLonLatPosLand
 340      read(nulbgst) latLand(locationIndex), lonLand(locationIndex), bMatrix(:,:)
 341      !application of the scaling factor
 342      do varLevIndex2 = 1, nkgdim
 343        do varLevIndex1 = 1, nkgdim
 344          bSqrtLand(locationIndex, varLevIndex1, varLevIndex2) = &
 345               bMatrix(varLevIndex1, varLevIndex2) *  multFactor(varLevIndex1) * multFactor(varLevIndex2)
 346        end do
 347      end do
 348      call utl_matsqrt(bSqrtLand(locationIndex, :, :), nkgdim, 1.d0, printInformation_opt=.false. )
 349    end do
 350    ierr = fclos(nulbgst)
 351
 352    inquire(file=trim(oneDBmatSea), exist=fileExists)
 353    if ( fileExists ) then
 354      ierr = fnom(nulbgst, trim(oneDBmatSea), 'FTN+SEQ+UNF+OLD+R/O', 0)
 355    else
 356      call utl_abort('bmat1D_setupBHi: No 1DVar BACKGROUND STAT FILE ' // trim(oneDBmatSea))
 357    end if
 358    read(nulbgst) extractDate, vco_1Dvar%nLev_T, vco_1Dvar%nLev_M, vco_1Dvar%Vcode, &
 359         vco_1Dvar%ip1_sfc, vco_1Dvar%ip1_T_2m, vco_1Dvar%ip1_M_10m, numIncludeAnlVarHi, nkgdim, nLonLatPosSea
 360    if (numIncludeAnlVarHi /= bmat1D_numIncludeAnlVar) then
 361      write(*,*) 'numIncludeAnlVarHi, bmat1D_numIncludeAnlVar= ', numIncludeAnlVarHi, bmat1D_numIncludeAnlVar
 362      call utl_abort('bmat1D_setupBHi: incompatible number of 1DVar analyzed variables in ' // trim(oneDBmatSea))
 363    end if
 364    allocate( bSqrtSea(nLonLatPosSea, nkgdim, nkgdim) )
 365    allocate( latSea(nLonLatPosSea), lonSea(nLonLatPosSea))
 366    allocate( includeAnlVarHi(bmat1D_numIncludeAnlVar) )
 367    read(nulbgst) vco_1Dvar%ip1_T(:), vco_1Dvar%ip1_M(:), includeAnlVarHi(:)
 368    if (any(includeAnlVarHi /= bmat1D_includeAnlVar)) then
 369      do varIndex = 1, bmat1D_numIncludeAnlVar
 370        write(*,*) varIndex, includeAnlVarHi(varIndex), bmat1D_includeAnlVar(varIndex)
 371      end do
 372      call utl_abort('bmat1D_setupBHi: incompatible 1DVar analyzed variable list in ' // trim(oneDBmatSea))
 373    end if
 374    deallocate(includeAnlVarHi)
 375    do locationIndex = 1, nLonLatPosSea
 376      read(nulbgst) latSea(locationIndex), lonSea(locationIndex), bMatrix(:,:)
 377      !application of the scaling factor
 378      do varLevIndex2 = 1, nkgdim
 379        do varLevIndex1 = 1, nkgdim
 380          bSqrtSea(locationIndex, varLevIndex1, varLevIndex2) = &
 381               bMatrix(varLevIndex1, varLevIndex2) *  multFactor(varLevIndex1) * multFactor(varLevIndex2)
 382        end do
 383      end do
 384      call utl_matsqrt(bSqrtSea(locationIndex,:,:), nkgdim, 1.d0, printInformation_opt=.false. )
 385    end do
 386    ierr = fclos(nulbgst)
 387  
 388    deallocate(bMatrix, multFactor)
 389
 390    vco_1Dvar%initialized = .true.
 391    vco_1Dvar%vGridPresent = .false.
 392    vco_file => vco_1Dvar
 393    vco_anl => vco_in
 394    if (.not. vco_equal(vco_anl,vco_file)) then
 395      call utl_abort('bmat1D_setupBHi: vco from analysisgrid and cov file do not match')
 396    end if
 397    if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBHi: nLev_M, nLev_T=', vco_1Dvar%nLev_M, vco_1Dvar%nLev_T
 398    Vcode_anl = vco_anl%vCode
 399    if(Vcode_anl /= 5002 .and. Vcode_anl /= 5005) then
 400      write(*,*) 'Vcode_anl = ',Vcode_anl
 401      call utl_abort('bmat1D_setupBHi: unknown vertical coordinate type!')
 402    end if
 403    
 404    cvDim_out = nkgdim * var1D_validHeaderCount
 405    cvDim_mpilocal = cvDim_out
 406    initialized = .true.
 407
 408    if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBHi: Exiting'
 409    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 410
 411  end subroutine bmat1D_setupBHi
 412
 413  !--------------------------------------------------------------------------
 414  !  bmat1D_setupBEns
 415  !--------------------------------------------------------------------------
 416  subroutine bmat1D_setupBEns(vco_in, hco_in, obsSpaceData, cvDim_out)
 417    !
 418    !:Purpose: to setup bmat1D module
 419    !
 420    implicit none
 421
 422    ! Arguments
 423    type(struct_vco), pointer, intent(in)    :: vco_in
 424    type(struct_hco), pointer, intent(in)    :: hco_in
 425    type (struct_obs)        , intent(inout) :: obsSpaceData
 426    integer                  , intent(out)   :: cvDim_out
 427
 428    ! Locals:
 429    character(len=256) :: ensPathName = 'ensemble'
 430    character(len=256) :: ensFileName
 431    type(struct_vco), pointer :: vco_file => null()
 432    type(struct_vco), pointer :: vco_ens => null()
 433    type(struct_hco), pointer :: hco_ens => null()
 434    type(struct_gsv)          :: stateVector, stateVectorMean
 435    integer, allocatable :: dateStampList(:)
 436    character(len=12) :: hInterpolationDegree='LINEAR' ! select degree of horizontal interpolation (if needed)
 437    integer :: memberIndex, columnIndex, headerIndex, varIndex, levIndex
 438    integer :: varLevIndex1, varLevIndex2
 439    integer :: varLevIndexBmat, varLevIndexCol
 440    integer :: numStep, levIndexColumn
 441    real(8), allocatable :: scaleFactor_M(:), scaleFactor_T(:)
 442    real(8) :: scaleFactor_SF, zr
 443    logical :: useAnlLevelsOnly, EnsTopMatchesAnlTop
 444    real(8), pointer :: pressureProfileFile_M(:), pressureProfileInc_M(:)
 445    real(8) :: pSurfRef
 446    integer :: nj, latPerPE, latPerPEmax, myLatBeg, myLatEnd
 447    integer :: ni, lonPerPE, lonPerPEmax, myLonBeg, myLonEnd
 448    integer :: nLevEns_M, nLevEns_T
 449    integer :: nLevInc_M, nLevInc_T
 450    real(8) :: logP1, logP2
 451    real(8), pointer :: currentProfile(:), meanProfile(:)
 452    real(8), allocatable :: lineVector(:,:), meanPressureProfile(:), multFactor(:)
 453    integer, allocatable :: varLevColFromVarLevBmat(:)
 454    character(len=4), allocatable :: varNameFromVarLevIndexBmat(:)
 455    character(len=2) :: varLevel    
 456
 457    if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBEns: Starting'
 458    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 459
 460    if (nEns <= 0) then
 461      if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBEns: no Ensemble members, skipping rest of setup'
 462      cvdim_out = 0
 463      return
 464    end if
 465    
 466    !- 1.1 Number of time step bins
 467    numStep = tim_nstepobsinc
 468    if (numStep /= 1 .and. numStep /= 3.and. numStep /= 5 .and. numStep /= 7) then
 469      call utl_abort('bmat1D_setupBEns: Invalid value for numStep (choose 1 or 3 or 5 or 7)!')
 470    end if
 471    allocate(dateStampList(numStep))
 472    call tim_getstamplist(dateStampList,numStep,tim_getDatestamp())
 473    
 474    hco_ens => hco_in
 475
 476    !- 1.2 Horizontal grid
 477    ni = hco_ens%ni
 478    nj = hco_ens%nj
 479    if (hco_ens%global) then
 480      if (mmpi_myid == 0) write(*,*)
 481      if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBEns: GLOBAL mode activated'
 482    else
 483      if (mmpi_myid == 0) write(*,*)
 484      if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBEns: LAM mode activated'
 485    end if
 486
 487    !- 1.3 Vertical levels
 488    call fln_ensfileName(ensFileName, ensPathName, memberIndex_opt=1)
 489    call vco_SetupFromFile(vco_file, ensFileName)
 490
 491    !- Do we need to read all the vertical levels from the ensemble?
 492    useAnlLevelsOnly = vco_subsetOrNot(vco_in, vco_file)
 493    if (useAnlLevelsOnly) then
 494      write(*,*)
 495      write(*,*) 'bmat1D_setupBEns: only the analysis levels will be read in the ensemble '
 496      vco_ens  => vco_in ! the ensemble target grid is the analysis grid
 497      call vco_deallocate(vco_file)
 498      vco_file => vco_in ! only the analysis levels will be read in the ensemble
 499      EnsTopMatchesAnlTop = .true.
 500    else
 501      write(*,*)
 502      write(*,*) 'bmat1D_setupBEns: all the vertical levels will be read in the ensemble '
 503      if ( vco_in%nLev_M > 0 .and. vco_in%vgridPresent ) then
 504        pSurfRef = 101000.D0
 505        call czp_fetch1DLevels(vco_in, pSurfRef, profM_opt=pressureProfileInc_M)
 506        call czp_fetch1DLevels(vco_in, pSurfRef, profM_opt=pressureProfileFile_M)
 507      
 508        EnsTopMatchesAnlTop = abs( log(pressureProfileFile_M(1)) - log(pressureProfileInc_M(1)) ) < 0.1d0
 509        write(*,*) 'bmat1D_setupBEns: EnsTopMatchesAnlTop, presEns, presInc = ', &
 510             EnsTopMatchesAnlTop, pressureProfileFile_M(1), pressureProfileInc_M(1)
 511        deallocate(pressureProfileFile_M)
 512        deallocate(pressureProfileInc_M)
 513      else
 514        ! not sure what this mean when no MM levels
 515        write(*,*) 'bmat1D_setupBEns: nLev_M       = ', vco_in%nLev_M
 516        write(*,*) 'bmat1D_setupBEns: vgridPresent = ', vco_in%vgridPresent
 517        EnsTopMatchesAnlTop = .true.
 518      end if
 519
 520      if ( EnsTopMatchesAnlTop ) then
 521        if ( mmpi_myid == 0 ) write(*,*) 'bmat1D_setupBEns: top level of ensemble member and analysis grid match'
 522        vco_ens => vco_in  ! IMPORTANT: top levels DO match, therefore safe
 523                           ! to force members to be on analysis vertical levels
 524      else
 525        if ( mmpi_myid == 0 ) write(*,*) 'bmat1D_setupBEns: top level of ensemble member and analysis grid are different, therefore'
 526        if ( mmpi_myid == 0 ) write(*,*) '                      assume member is already be on correct levels - NO CHECKING IS DONE'
 527        vco_ens => vco_file ! IMPORTANT: top levels do not match, therefore must
 528                            ! assume file is already on correct vertical levels
 529      end if
 530    end if
 531
 532    if (vco_in%Vcode /= vco_ens%Vcode) then
 533      write(*,*) 'bmat1D_setupBEns: vco_in%Vcode = ', vco_in%Vcode, ', vco_ens%Vcode = ', vco_ens%Vcode
 534      call utl_abort('bmat1D_setupBEns: vertical levels of ensemble not compatible with analysis grid')
 535    end if
 536    nLevEns_M = vco_ens%nLev_M
 537    nLevEns_T = vco_ens%nLev_T
 538    nLevInc_M = vco_in%nLev_M
 539    nLevInc_T = vco_in%nLev_T
 540
 541    if (vco_in%Vcode == 5002) then
 542      if ( (nLevEns_T /= (nLevEns_M+1)) .and. (nLevEns_T /= 1 .or. nLevEns_M /= 1) ) then
 543        write(*,*) 'bmat1D_setubBEns: nLevEns_T, nLevEns_M = ',nLevEns_T,nLevEns_M
 544        call utl_abort('bmat1D_setubBEns: Vcode=5002, nLevEns_T must equal nLevEns_M+1!')
 545      end if
 546    else if (vco_in%Vcode == 5005) then
 547      if ( nLevEns_T /= nLevEns_M .and. &
 548           nLevEns_T /= 0 .and. &
 549           nLevEns_M /= 0 ) then
 550        write(*,*) 'bmat1D_setubBEns: nLevEns_T, nLevEns_M = ',nLevEns_T,nLevEns_M
 551        call utl_abort('bmat1D_setubBEns: Vcode=5005, nLevEns_T must equal nLevEns_M!')
 552      end if
 553    else if (vco_in%Vcode == 0) then
 554      if ( nLevEns_T /= 0 .and. nLevEns_M /= 0 ) then
 555        write(*,*) 'bmat1D_setubBEns: nLevEns_T, nLevEns_M = ',nLevEns_T, nLevEns_M
 556        call utl_abort('bmat1D_setubBEns: surface-only case (Vcode=0), nLevEns_T and nLevEns_M must equal 0!')
 557      end if
 558    else
 559      write(*,*) 'bmat1D_setubBEns: vco_in%Vcode = ',vco_in%Vcode
 560      call utl_abort('bmat1D_setubBEns: unknown vertical coordinate type!')
 561    end if
 562
 563    if (nLevEns_M > nLevInc_M) then
 564      call utl_abort('bmat1D_setubBEns: ensemble has more levels than increment - not allowed!')
 565    end if
 566
 567    if (nLevEns_M < nLevInc_M) then
 568      if (mmpi_myid == 0) write(*,*) 'bmat1D_setubBEns: ensemble has less levels than increment'
 569      if (mmpi_myid == 0) write(*,*) '                  some levels near top will have zero increment'
 570    end if
 571
 572    !- 1.4 Bmatrix Weight
 573    if (vco_in%Vcode == 5002 .or. vco_in%Vcode == 5005) then
 574      allocate(scaleFactor_M(nLevEns_M))
 575      allocate(scaleFactor_T(nLevEns_T))
 576      do levIndex = 1, nLevEns_T
 577        if (scaleFactorEns(levIndex) > 0.0d0) then 
 578          scaleFactorEns(levIndex) = sqrt(scaleFactorEns(levIndex))
 579        else
 580          scaleFactorEns(levIndex) = 0.0d0
 581        end if
 582      end do
 583      scaleFactor_T(1:nLevEns_T) = scaleFactorEns(1:nLevEns_T)
 584      if (vco_in%Vcode == 5002) then
 585        scaleFactor_M(1:nLevEns_M) = scaleFactorEns(2:(nLevEns_M+1))
 586      else
 587        scaleFactor_M(1:nLevEns_M) = scaleFactorEns(1:nLevEns_M)
 588      end if
 589
 590      do levIndex = 1, nLevEns_T
 591        if (scaleFactorEnsHumidity(levIndex) > 0.0d0) then 
 592          scaleFactorEnsHumidity(levIndex) = sqrt(scaleFactorEnsHumidity(levIndex))
 593        else
 594          scaleFactorEnsHumidity(levIndex) = 0.0d0
 595        end if
 596      end do
 597      
 598      scaleFactor_SF = scaleFactor_T(nLevEns_T)
 599
 600    else ! vco_in%Vcode == 0
 601      if (scaleFactorEns(1) > 0.0d0) then 
 602        scaleFactor_SF = sqrt(scaleFactorEns(1))
 603      else
 604        call utl_abort('bmat1D_setubBEns: with vCode == 0, the scale factor should never be equal to 0')
 605      end if
 606    end if
 607
 608    !- 1.5 Domain Partionning
 609    call mmpi_setup_latbands(nj, latPerPE, latPerPEmax, myLatBeg, myLatEnd)
 610    call mmpi_setup_lonbands(ni, lonPerPE, lonPerPEmax, myLonBeg, myLonEnd)
 611
 612    !- 1.6 Localization
 613    if ( vLocalize <= 0.0d0 .and. (nLevInc_M > 1 .or. nLevInc_T > 1) ) then
 614      call utl_abort('bmat1D_setubBEns: Invalid VERTICAL localization length scale')
 615    end if     
 616  
 617    call ens_allocate(ensembles,  &
 618         nEns, numStep, &
 619         hco_ens,  &
 620         vco_ens, dateStampList, &
 621         hco_core_opt = hco_in, &
 622         varNames_opt = bmat1D_includeAnlVar(1:bmat1D_numIncludeAnlVar), &
 623         hInterpolateDegree_opt = hInterpolationDegree)
 624    write(*,*) 'Read ensemble members'
 625    call ens_readEnsemble(ensembles, ensPathName, biPeriodic=.false., &
 626                          varNames_opt = bmat1D_includeAnlVar(1:bmat1D_numIncludeAnlVar))
 627
 628    allocate(ensColumns(nEns))
 629    call gsv_allocate(stateVector, numstep, hco_ens, vco_ens, &
 630                     dateStamp_opt=tim_getDateStamp(),  &
 631                     mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
 632                     dataKind_opt=4, allocHeightSfc_opt=.true.)
 633    
 634    call gsv_allocate(stateVectorMean, numstep, hco_ens, vco_ens, &
 635                     dateStamp_opt=tim_getDateStamp(),  &
 636                     mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
 637                     dataKind_opt=4, allocHeightSfc_opt=.true.)
 638    call gsv_zero(stateVectorMean)
 639    do memberIndex = 1, nEns
 640      write(*,*) 'Copy member ', memberIndex
 641      call gsv_zero(stateVector)
 642      call ens_copyMember(ensembles, stateVector, memberIndex)
 643      write(*,*) 'interpolate member ', memberIndex
 644      call col_setVco(ensColumns(memberIndex), vco_ens)
 645      call col_allocate(ensColumns(memberIndex), obs_numheader(obsSpaceData), &
 646                        mpiLocal_opt=.true., setToZero_opt=.true.)
 647      call s2c_nl(stateVector, obsSpaceData, ensColumns(memberIndex), hco_in, &
 648                  timeInterpType='NEAREST', dealloc_opt=.false. )
 649      call gsv_add(statevector, statevectorMean, scaleFactor_opt=(1.d0/nEns))
 650    end do
 651
 652    call col_setVco(meanColumn, vco_ens)
 653    call col_allocate(meanColumn, obs_numheader(obsSpaceData), &
 654                      mpiLocal_opt=.true., setToZero_opt=.true.)
 655    call s2c_nl(stateVectorMean, obsSpaceData, meanColumn, hco_in, &
 656                timeInterpType='NEAREST' )
 657
 658    call gsv_deallocate(stateVector)
 659    call gsv_deallocate(stateVectorMean)
 660
 661    nkgdim = 0
 662    do varIndex = 1, bmat1D_numIncludeAnlVar
 663      currentProfile => col_getColumn(meanColumn, var1D_validHeaderIndex(1), varName_opt=bmat1D_includeAnlVar(varIndex))
 664      nkgdim = nkgdim + size(currentProfile)
 665    end do
 666    write(*,*) 'bmat1D_setupBEns: nkgdim', nkgdim
 667    cvDim_out = nkgdim * var1D_validHeaderCount
 668
 669    currentProfile => col_getColumn(meanColumn, var1D_validHeaderIndex(1))
 670    allocate (varLevColFromVarLevBmat(nkgdim))
 671    allocate (varNameFromVarLevIndexBmat(nkgdim))
 672    allocate (multFactor(nkgdim))
 673    varLevIndexBmat = 0
 674    do varIndex = 1, bmat1D_numIncludeAnlVar
 675      levIndex = 0
 676      do varLevIndexCol = 1, size(currentProfile)
 677        if ( trim( col_getVarNameFromK(meanColumn,varLevIndexCol) ) == trim( bmat1D_includeAnlVar(varIndex) ) ) then
 678          varLevIndexBmat = varLevIndexBmat + 1
 679          levIndex = levIndex + 1
 680          varLevColFromVarLevBmat(varLevIndexBmat) = varLevIndexCol
 681          varNameFromVarLevIndexBmat(varLevIndexBmat) = trim( bmat1D_includeAnlVar(varIndex) )
 682          varLevel = vnl_varLevelFromVarname(varNameFromVarLevIndexBmat(varLevIndexBmat))
 683          if ( varLevel == 'MM' ) then      ! Momentum
 684            multFactor(varLevIndexBmat) = scaleFactor_M(levIndex)
 685          else if ( varLevel == 'TH' ) then ! Thermo
 686            multFactor(varLevIndexBmat) = scaleFactor_T(levIndex)
 687          else                              ! SF
 688            multFactor(varLevIndexBmat) = scaleFactor_SF
 689          end if
 690          if (varNameFromVarLevIndexBmat(varLevIndexBmat) == 'HU') then
 691            multFactor(varLevIndexBmat) = multFactor(varLevIndexBmat) * scaleFactorEnsHumidity(levIndex)
 692          end if
 693          if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBEns:  bmat1D_includeAnlVar ', bmat1D_includeAnlVar(varIndex), varLevIndexBmat, levIndex
 694        end if
 695      end do
 696    end do
 697    
 698    call ens_deallocate(ensembles)
 699    allocate(bSqrtEns(var1D_validHeaderCount,nkgdim,nkgdim))
 700    bSqrtEns(:,:,:) = 0.d0
 701    allocate(lineVector(1,nkgdim))
 702
 703    !$OMP PARALLEL DO PRIVATE (columnIndex,headerIndex,memberIndex,meanProfile,currentProfile,lineVector)
 704    do columnIndex = 1, var1D_validHeaderCount
 705      headerIndex = var1D_validHeaderIndex(columnIndex)
 706      meanProfile => col_getColumn(meanColumn, headerIndex)
 707      do memberIndex = 1, nEns
 708        currentProfile => col_getColumn(ensColumns(memberIndex), headerIndex)
 709        lineVector(1,:) = currentProfile(varLevColFromVarLevBmat(:)) - meanProfile(varLevColFromVarLevBmat(:))
 710        lineVector(1,:) = lineVector(1,:) * multFactor(:)
 711        bSqrtEns(columnIndex,:,:) = bSqrtEns(columnIndex,:,:) + &
 712            matmul(transpose(lineVector),lineVector)
 713      end do
 714    end do
 715    !$OMP END PARALLEL DO
 716
 717    do varLevIndexBmat =1, nkgdim
 718      write(*,*) 'bmat1D_setupBEns: variance = ', varLevIndexBmat, &
 719                 sum(bSqrtEns(:,varLevIndexBmat,varLevIndexBmat))/real((nEns-1)*var1D_validHeaderCount)
 720    end do
 721
 722    deallocate(lineVector)
 723    deallocate(multFactor)
 724    allocate(meanPressureProfile(nkgdim))
 725    call lfn_Setup('FifthOrder')
 726
 727    !$OMP PARALLEL DO PRIVATE (columnIndex,headerIndex,meanPressureProfile,varLevIndexBmat,varLevIndexCol,varLevIndex1,varLevIndex2,varLevel,levIndexColumn,logP1,logP2,zr)
 728    do columnIndex = 1, var1D_validHeaderCount
 729      headerIndex = var1D_validHeaderIndex(columnIndex)
 730      bSqrtEns(columnIndex,:,:) = bSqrtEns(columnIndex,:,:) / (nEns - 1)
 731      if (vLocalize > 0.0d0) then
 732        do varLevIndexBmat = 1, nkgdim
 733          varLevIndexCol = varLevColFromVarLevBmat(varLevIndexBmat)
 734          varLevel = vnl_varLevelFromVarname(varNameFromVarLevIndexBmat(varLevIndexBmat))
 735          if (varLevel=='SF') then
 736            meanPressureProfile(varLevIndexBmat) = col_getElem(meanColumn, 1, headerIndex, 'P0')
 737          else
 738            levIndexColumn = col_getLevIndexFromVarLevIndex(meanColumn, varLevIndexCol)
 739            meanPressureProfile(varLevIndexBmat) = col_getPressure(meanColumn, levIndexColumn, headerIndex, varLevel)
 740          end if
 741        end do
 742        do varLevIndex1 = 1, nkgdim 
 743          logP1 = log(meanPressureProfile(varLevIndex1))
 744          do varLevIndex2 = 1, nkgdim
 745            !-  do Schurr product with 5'th order function
 746            logP2 = log(meanPressureProfile(varLevIndex2))
 747            zr = abs(logP2 - logP1)
 748            bSqrtEns(columnIndex, varLevIndex2, varLevIndex1) = &
 749                bSqrtEns(columnIndex, varLevIndex2, varLevIndex1) * lfn_response(zr, vLocalize)
 750          end do
 751        end do
 752      end if
 753    end do
 754    !$OMP END PARALLEL DO
 755
 756    do varLevIndexBmat =1, nkgdim
 757      write(*,*) 'bmat1D_setupBEns: variance (after localization) = ', varLevIndexBmat, &
 758          sum(bSqrtEns(:,varLevIndexBmat,varLevIndexBmat))/real(var1D_validHeaderCount)
 759    end do
 760    
 761    if (dumpBmatrixTofile) then
 762      call dumpBmatrices(vco_in, obsSpaceData, dateStampList, bSqrtEns)
 763    end if
 764    
 765    write(*,*) 'bmat1D_setupBEns: computing matrix sqrt'
 766    do columnIndex = 1, var1D_validHeaderCount
 767      call utl_matsqrt(bSqrtEns(columnIndex, :, :), nkgdim, 1.d0, printInformation_opt=.false. )
 768    end do
 769    
 770    deallocate(varLevColFromVarLevBmat) 
 771    deallocate(varNameFromVarLevIndexBmat)
 772    deallocate(meanPressureProfile)
 773    call col_deallocate(meanColumn)
 774    do memberIndex = 1, nEns
 775      call col_deallocate(ensColumns(memberIndex))
 776    end do
 777    cvDim_mpilocal = cvDim_out
 778    initialized = .true.
 779    
 780    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 781    if (mmpi_myid == 0) write(*,*) 'bmat1D_setupBEns: Exiting'
 782    
 783  end subroutine bmat1D_setupBEns
 784  
 785  !--------------------------------------------------------------------------
 786  ! dumpBmatrices
 787  !--------------------------------------------------------------------------
 788  subroutine dumpBmatrices(vco_in, obsSpaceData, dateStampList, bEns)
 789    !
 790    !:Purpose: output B matrices or their average to binary file Bmatrix.bin
 791    !
 792    implicit none
 793    
 794    ! Arguments:
 795    type(struct_vco), pointer, intent(in)    :: vco_in           ! Analysis vertical grid descriptor
 796    type (struct_obs)        , intent(inout) :: obsSpaceData     ! ObsSpaceData structure
 797    integer                  , intent(in)    :: dateStamplist(:) ! List of datestamps
 798    real(8)                  , intent(in)    :: bEns(:,:,:)      ! B matrices (first dimension is location index)
 799
 800    ! Locals:
 801    integer :: nulmat, ierr
 802    integer :: yyyymmdd, hhmm, countDumped, countDumpedMax, countDumpedMpiGlobal
 803    integer :: globalDumpedIndex, countDumpedOut
 804    integer :: columnIndex, headerIndex
 805    integer :: taskIndex, dumpedIndex
 806    integer :: tag, status
 807    integer :: numstep, nkgdim
 808    integer, external ::  fnom, fclos, newdate
 809    integer              :: obsOffset(0:mmpi_nprocs-1)
 810    integer              :: countDumpedAllTasks(mmpi_nprocs)
 811    integer, allocatable :: listColumnDumped(:)
 812    real(8) :: latitude, longitude
 813    real(8), allocatable :: tempoBmatrix(:,:)
 814    real(8), allocatable :: outLats(:),outLons(:), outBmatrix(:,:,:)
 815
 816    if (mmpi_myid == 0) write(*,*) 'dumpBmatrices: Starting'
 817    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 818    
 819    countDumped = 0
 820    do columnIndex = 1, var1D_validHeaderCount 
 821      headerIndex = var1D_validHeaderIndex(columnIndex)
 822      latitude = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) * MPC_DEGREES_PER_RADIAN_R8
 823      longitude = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) * MPC_DEGREES_PER_RADIAN_R8
 824      if (latitude  >= latMin .and. &
 825          latitude  <= latMax .and. &
 826          longitude >= lonMin .and. &
 827          longitude <= lonMax) then
 828        countDumped = countDumped + 1
 829      end if
 830    end do
 831    call rpn_comm_barrier('GRID', ierr)
 832    
 833    call rpn_comm_allgather(countDumped, 1, 'MPI_INTEGER', countDumpedAllTasks, 1,'MPI_INTEGER', 'GRID', ierr)
 834    countDumpedMpiGlobal = sum( countDumpedAllTasks(:) )
 835    countDumpedMax = maxval( countDumpedAllTasks(:) )
 836    obsOffset(0) = 0
 837    do taskIndex = 1, mmpi_nprocs - 1
 838      obsOffset(taskIndex) = obsOffset(taskIndex - 1) + countDumpedAllTasks(taskIndex)
 839    end do
 840    write(*,*) 'dumpBmatrices: obsOffset: ', obsOffset(:)
 841    
 842    if (countDumpedMax > 0) then
 843      allocate(listColumnDumped(countDumpedMax))
 844      listColumnDumped(:) = -1
 845      countDumped = 0
 846      do columnIndex = 1, var1D_validHeaderCount 
 847        headerIndex = var1D_validHeaderIndex(columnIndex)
 848        latitude = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) * MPC_DEGREES_PER_RADIAN_R8
 849        longitude = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) * MPC_DEGREES_PER_RADIAN_R8
 850        if (latitude  >= latMin .and. &
 851            latitude  <= latMax .and. &
 852            longitude >= lonMin .and. &
 853            longitude <= lonMax) then
 854          countDumped = countDumped + 1
 855          listColumnDumped(countDumped) = columnIndex
 856        end if
 857      end do
 858      nkgdim = size(bEns, dim=2)
 859      if (mmpi_myId == 0) then
 860        nulmat = 0
 861        ierr = fnom(nulmat, './Bmatrix.bin', 'FTN+SEQ+UNF', 0)
 862        numstep = size(dateStampList)
 863        ierr = newdate(dateStampList(1 + numstep / 2), yyyymmdd, hhmm, -3)
 864        countDumpedOut = countDumped
 865        if (doAveraging) countDumpedOut = 1
 866        write(nulmat) yyyymmdd * 100 + nint(hhmm/100.), vco_in%nlev_T, vco_in%nlev_M, vco_in%Vcode, &
 867            vco_in%ip1_sfc, vco_in%ip1_T_2m, vco_in%ip1_M_10m, bmat1D_numIncludeAnlVar, nkgdim, countDumpedOut
 868        write(nulmat) vco_in%ip1_T(:), vco_in%ip1_M(:), bmat1D_includeAnlVar(1:bmat1D_numIncludeAnlVar)
 869        allocate(outLats(countDumpedMpiGlobal), outLons(countDumpedMpiGlobal), OutBmatrix(countDumpedMpiGlobal, nkgdim, nkgdim))
 870        allocate(tempoBmatrix(nkgdim, nkgdim))
 871      end if
 872    end if
 873    do dumpedIndex = 1, countDumpedMax
 874      columnIndex = listColumnDumped(dumpedIndex)
 875      if (columnIndex > 0) then
 876        headerIndex = var1D_validHeaderIndex(columnIndex)
 877        latitude = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex)
 878        longitude = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex)
 879      else
 880        latitude = MPC_missingValue_R8
 881        longitude = MPC_missingValue_R8
 882      end if
 883      if (mmpi_myId == 0) then
 884        if (latitude /= MPC_missingValue_R8 .and. longitude /= MPC_missingValue_R8) then 
 885          outBmatrix(dumpedIndex, :, :) = bEns(columnIndex, :, :)
 886          outLats(dumpedIndex) = latitude
 887          outLons(dumpedIndex) = longitude
 888        end if
 889        do taskIndex = 1,  mmpi_nprocs - 1
 890          tag = 3 * taskIndex
 891          call rpn_comm_recv(latitude, 1, 'mpi_real8', taskIndex, tag, 'GRID', status, ierr)
 892          call rpn_comm_recv(longitude, 1, 'mpi_real8', taskIndex, tag+1, 'GRID', status, ierr)
 893          if (latitude /= MPC_missingValue_R8 .and. longitude /= MPC_missingValue_R8) then
 894            call rpn_comm_recv(tempoBmatrix(:,:), nkgdim*nkgdim, 'mpi_real8', taskIndex, tag + 2, 'GRID', status, ierr)
 895            globalDumpedIndex = dumpedIndex + obsOffset(taskIndex)
 896            outBmatrix(globalDumpedIndex, :, :) = tempoBmatrix(:, :)
 897            outLats(globalDumpedIndex) = latitude
 898            outLons(globalDumpedIndex) = longitude
 899          end if
 900        end do
 901      else
 902        tag = 3 * mmpi_myID
 903        call rpn_comm_send(latitude, 1, 'mpi_real8', 0, tag, 'GRID', ierr)
 904        call rpn_comm_send(longitude, 1, 'mpi_real8', 0, tag + 1, 'GRID', ierr)
 905        if (columnIndex > 0) then
 906          call rpn_comm_send(bEns(columnIndex, :, :), nkgdim*nkgdim, 'mpi_real8', 0, tag + 2, 'GRID', ierr)
 907        end if
 908      end if
 909    end do
 910
 911    if (mmpi_myId == 0) then
 912      if (doAveraging) then
 913        write(nulmat) real(sum(outLats(:))/countDumpedMpiGlobal, kind=4), real(sum(outLons(:))/countDumpedMpiGlobal, kind=4), &
 914            sum(outBmatrix(:, :, :),dim=1)/countDumpedMpiGlobal
 915      else
 916        do dumpedIndex = 1, countDumpedMpiGlobal          
 917          write(nulmat) real(outLats(dumpedIndex), kind=4), real(outLons(dumpedIndex), kind=4), outBmatrix(dumpedIndex, :, :)
 918        end do
 919      end if
 920      ierr = fclos(nulmat)
 921      deallocate(outLats)
 922      deallocate(outLons)
 923      deallocate(outBmatrix)
 924      deallocate(tempoBmatrix)
 925    end if
 926    if (allocated(listColumnDumped)) deallocate(listColumnDumped)
 927  end subroutine dumpBmatrices
 928
 929  !--------------------------------------------------------------------------
 930  ! bmat1D_bSqrtHi
 931  !--------------------------------------------------------------------------
 932  subroutine bmat1D_bSqrtHi(controlVector_in, column, obsSpaceData)
 933    !
 934    !:Purpose: HI component of B square root in 1DVar mode
 935    !
 936    implicit none
 937
 938    ! Arguments:
 939    real(8),                 intent(in)    :: controlVector_in(cvDim_mpilocal)
 940    type(struct_columnData), intent(inout) :: column
 941    type(struct_obs),        intent(in)    :: obsSpaceData
 942
 943    ! Locals:
 944    integer :: headerIndex, latitudeBandIndex(1), varIndex, columnIndex
 945    real(8), pointer :: currentColumn(:)
 946    real(8), allocatable ::  oneDProfile(:)
 947    real(8) :: latitude
 948    integer :: surfaceType, offset
 949
 950    if (mmpi_myid == 0) write(*,*) 'bmat1D_bsqrtHi: starting'
 951    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 952
 953    if (.not. initialized) then
 954      if (mmpi_myid == 0) write(*,*) 'bmat1D_bsqrtHi: 1Dvar B matrix not initialized'
 955      return
 956    end if
 957    allocate(oneDProfile(nkgdim))
 958
 959    !$OMP PARALLEL DO PRIVATE (columnIndex,headerIndex,oneDProfile,offset,varIndex,currentColumn,latitude,surfaceType,latitudeBandIndex)
 960    do columnIndex = 1, var1D_validHeaderCount 
 961      headerIndex = var1D_validHeaderIndex(columnIndex)
 962      latitude = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) !radian 
 963      surfaceType = tvs_ChangedStypValue(obsSpaceData, headerIndex)
 964      if (surfaceType == 1) then !Sea
 965        latitudeBandIndex = minloc( abs( latitude - latSea(:)) )
 966        oneDProfile(:) = matmul(bSqrtSea(latitudeBandIndex(1), :, :), controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim))
 967      else ! Land or Sea Ice
 968        latitudeBandIndex = minloc( abs( latitude - latLand(:)) )
 969        oneDProfile(:) = matmul(bSqrtLand(latitudeBandIndex(1), :, :), controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim))
 970      end if
 971      offset = 0
 972      do varIndex = 1, bmat1D_numIncludeAnlVar
 973        currentColumn => col_getColumn(column, headerIndex, varName_opt=bmat1D_includeAnlVar(varIndex))
 974        currentColumn(:) = currentColumn(:) + oneDProfile(offset+1:offset+size(currentColumn))
 975        offset = offset + size(currentColumn)
 976      end do
 977      if (offset /= nkgdim) then
 978        write(*,*) 'bmat1D_bsqrtHi: offset, nkgdim', offset, nkgdim
 979        call utl_abort('bmat1D_bSqrtHi: inconsistency between Bmatrix and statevector size')
 980      end if
 981    end do
 982    !$OMP END PARALLEL DO
 983
 984    deallocate(oneDProfile)
 985    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
 986    if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtHi: done'
 987
 988  end subroutine bmat1D_bSqrtHi
 989
 990  !--------------------------------------------------------------------------
 991  ! bmat1D_bSqrtHiAd
 992  !--------------------------------------------------------------------------
 993  subroutine bmat1D_bSqrtHiAd(controlVector_in, column, obsSpaceData)
 994    !
 995    !:Purpose: HI component of B square root adjoint in 1DVar mode
 996    !
 997    implicit none
 998
 999    ! Arguments:
1000    real(8),                 intent(inout) :: controlVector_in(cvDim_mpilocal)
1001    type(struct_columnData), intent(inout) :: column
1002    type (struct_obs),       intent(in)    :: obsSpaceData
1003
1004    ! Locals:
1005    integer :: headerIndex, latitudeBandIndex(1), varIndex, columnIndex
1006    real(8), pointer :: currentColumn(:)
1007    real(8), allocatable ::  oneDProfile(:)
1008    real(8) :: latitude
1009    integer :: surfaceType, offset
1010
1011    if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtHiAd: starting'
1012    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
1013    if (.not. initialized) then
1014      if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtHiAd: 1dvar Bmatrix not initialized'
1015      return
1016    end if
1017    allocate(oneDProfile(nkgdim))
1018
1019    controlVector_in(:) = 0.d0
1020
1021    !$OMP PARALLEL DO PRIVATE (columnIndex,headerIndex,oneDProfile,offset,varIndex,currentColumn,latitude,surfaceType,latitudeBandIndex)
1022    do columnIndex = 1, var1D_validHeaderCount
1023      headerIndex = var1D_validHeaderIndex(columnIndex)
1024      offset = 0
1025      do varIndex = 1, bmat1D_numIncludeAnlVar
1026        currentColumn => col_getColumn(column, headerIndex, varName_opt=bmat1D_includeAnlVar(varIndex))
1027        oneDProfile(offset+1:offset+size(currentColumn)) = currentColumn(:)
1028        offset = offset + size(currentColumn)
1029      end do
1030      if (offset /= nkgdim) then
1031        write(*,*) 'bmat1D_bSqrtHiAd: offset, nkgdim', offset, nkgdim
1032        call utl_abort('bmat1D_bSqrtHiAd: inconsistency between Bmatrix and statevector size')
1033      end if
1034      latitude = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) !radians
1035      surfaceType =  tvs_ChangedStypValue(obsSpaceData, headerIndex)
1036      if (surfaceType == 1) then !Sea
1037        latitudeBandIndex = minloc( abs( latitude - latSea(:)) )
1038        controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim) =  &
1039             controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim) + &
1040             matmul(bSqrtSea(latitudeBandIndex(1),:,:), oneDProfile)
1041      else ! Land or Sea Ice
1042        latitudeBandIndex = minloc( abs( latitude - latLand(:)) )
1043        controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim) =  &
1044             controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim) + &
1045             matmul(bSqrtLand(latitudeBandIndex(1),:,:), oneDProfile)
1046      end if
1047    end do
1048    !$OMP END PARALLEL DO
1049
1050    deallocate(oneDProfile)
1051    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
1052    if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtHiAd: done'
1053
1054  end subroutine bmat1D_bSqrtHiAd
1055
1056  !--------------------------------------------------------------------------
1057  ! bmat1D_bSqrtEns
1058  !--------------------------------------------------------------------------
1059  subroutine bmat1D_bSqrtEns(controlVector_in, column)
1060    !
1061    !:Purpose: Ensemble component of B square root in 1DVar mode
1062    !
1063    implicit none
1064
1065    ! Arguments:
1066    real(8),                 intent(in)    :: controlVector_in(cvDim_mpilocal)
1067    type(struct_columnData), intent(inout) :: column
1068
1069    ! Locals:
1070    integer :: headerIndex, varIndex, columnIndex
1071    real(8), pointer :: currentColumn(:)
1072    real(8), allocatable ::  oneDProfile(:)
1073    integer :: offset
1074
1075    allocate(oneDProfile(nkgdim))
1076
1077    !$OMP PARALLEL DO PRIVATE (columnIndex,headerIndex,oneDProfile,offset,varIndex,currentColumn)
1078    do columnIndex = 1, var1D_validHeaderCount 
1079      headerIndex = var1D_validHeaderIndex(columnIndex)
1080      oneDProfile(:) = matmul(bSqrtEns(columnIndex, :, :), controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim))
1081      offset = 0
1082      do varIndex = 1, bmat1D_numIncludeAnlVar
1083        currentColumn => col_getColumn(column, headerIndex, varName_opt=bmat1D_includeAnlVar(varIndex))
1084        currentColumn(:) = currentColumn(:) + oneDProfile(offset+1:offset+size(currentColumn))
1085        offset = offset + size(currentColumn)
1086      end do
1087      if (offset /= nkgdim) then
1088        write(*,*) 'bmat1D_bsqrtEns: offset, nkgdim', offset, nkgdim
1089        call utl_abort('bmat1D_bSqrtEns: inconsistency between Bmatrix and statevector size')
1090      end if
1091    end do
1092    !$OMP END PARALLEL DO
1093
1094    deallocate(oneDProfile)
1095
1096  end subroutine bmat1D_bSqrtEns
1097
1098  !--------------------------------------------------------------------------
1099  ! bmat1D_bSqrtEnsAd
1100  !--------------------------------------------------------------------------
1101  subroutine bmat1D_bSqrtEnsAd(controlVector_in, column)
1102    !
1103    !:Purpose: Ensemble component of B square root in 1DVar mode
1104    !
1105    implicit none
1106
1107    ! Arguments:
1108    real(8),                 intent(inout) :: controlVector_in(cvDim_mpilocal)
1109    type(struct_columnData), intent(inout) :: column
1110
1111    ! Locals:
1112    integer :: headerIndex, varIndex, columnIndex
1113    real(8), pointer :: currentColumn(:)
1114    real(8), allocatable ::  oneDProfile(:)
1115    integer :: offset
1116
1117    if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtEnsAd: starting'
1118    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
1119    if (.not. initialized) then
1120      if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtEnsAd: 1dvar Bmatrix not initialized'
1121      return
1122    end if
1123    allocate(oneDProfile(nkgdim))
1124
1125    controlVector_in(:) = 0.d0
1126
1127    !$OMP PARALLEL DO PRIVATE (columnIndex,headerIndex,oneDProfile,offset,varIndex,currentColumn)
1128    do columnIndex = 1, var1D_validHeaderCount
1129      headerIndex = var1D_validHeaderIndex(columnIndex)
1130      offset = 0
1131      do varIndex = 1, bmat1D_numIncludeAnlVar
1132        currentColumn => col_getColumn(column, headerIndex, varName_opt=bmat1D_includeAnlVar(varIndex))
1133        oneDProfile(offset+1:offset+size(currentColumn)) = currentColumn(:)
1134        offset = offset + size(currentColumn)
1135      end do
1136      if (offset /= nkgdim) then
1137        write(*,*) 'bmat1D_bSqrtHiAd: offset, nkgdim', offset, nkgdim
1138        call utl_abort('bmat1D_bSqrtEnsAd: inconsistency between Bmatrix and statevector size')
1139      end if
1140      controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim) =  &
1141             controlVector_in(1+(columnIndex-1)*nkgdim:columnIndex*nkgdim) + &
1142             matmul(bSqrtEns(columnIndex,:,:), oneDProfile)
1143    end do
1144    !$OMP END PARALLEL DO
1145
1146    deallocate(oneDProfile)
1147    if (mmpi_myid == 0) write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
1148    if (mmpi_myid == 0) write(*,*) 'bmat1D_bSqrtEnsAd: done'
1149
1150  end subroutine bmat1D_bSqrtEnsAd
1151
1152  !--------------------------------------------------------------------------
1153  ! bmat1D_sqrtB
1154  !-------------------------------------------------------------------------- 
1155  subroutine bmat1D_sqrtB(controlVector, cvdim, column, obsSpaceData)
1156    !
1157    !:Purpose: To transform model state from control-vector space to grid-point
1158    !          space.    
1159    !
1160    implicit none
1161
1162    ! Arguments:
1163    integer,                 intent(in)    :: cvdim
1164    real(8),                 intent(in)    :: controlVector(cvdim)
1165    type(struct_columnData), intent(inout) :: column
1166    type(struct_obs),        intent(in)    :: obsSpaceData
1167
1168    ! Locals:
1169    integer :: bmatIndex
1170    real(8), pointer :: subVector(:)
1171
1172    !
1173    !- 1.  Compute the analysis increment
1174    !
1175    call col_zero(column)
1176
1177    bmat_loop: do bmatIndex = 1, numBmat
1178      if ( .not. bmatActive(bmatIndex) ) cycle bmat_loop
1179      subVector => cvm_getSubVector( controlVector, bmatLabelList(bmatIndex) )
1180
1181      call utl_tmg_start(50, '--Bmatrix')
1182      select case( trim(bmatTypeList(bmatIndex)) )
1183      case ('HI')
1184        !- 1.1 Time-Mean Homogeneous and Isotropic...
1185        call utl_tmg_start(52, '----B_HI_TL')
1186        call bmat1D_bsqrtHi(subVector,   & ! IN
1187                            column,      & ! OUT
1188                            obsspacedata ) ! IN
1189        call utl_tmg_stop(52)
1190      case ('ENS')
1191        !- 1.2 Ensemble based
1192        call utl_tmg_start(57, '----B_ENS_TL')
1193        call bmat1D_bsqrtEns(subVector, &  ! IN
1194                              column)      ! OUT
1195        call utl_tmg_stop(57)
1196      case default
1197        call utl_abort( 'bmat1D_sqrtB: requested bmatrix type does not exist ' // trim(bmatTypeList(bmatIndex)) )
1198      end select
1199      call utl_tmg_stop(50)
1200
1201    end do bmat_loop
1202
1203  end subroutine bmat1D_sqrtB
1204
1205  !--------------------------------------------------------------------------
1206  ! bmat1D_sqrtBT
1207  !--------------------------------------------------------------------------
1208  subroutine bmat1D_sqrtBT(controlVector, cvdim, column, obsSpaceData)
1209    !
1210    !:Purpose: To transform model state from grid-point space to
1211    !          error-covariance space.
1212    !
1213    implicit none
1214
1215    ! Arguments:
1216    integer,                 intent(in)    :: cvdim
1217    real(8),                 intent(in)    :: controlVector(cvdim)
1218    type(struct_columnData), intent(inout) :: column
1219    type(struct_obs),        intent(in)    :: obsSpaceData
1220
1221    ! Locals:
1222    integer :: bmatIndex
1223    real(8), pointer :: subVector(:)
1224
1225    ! Process components in opposite order as forward calculation
1226    bmat_loop: do bmatIndex = numBmat, 1, -1
1227      if ( .not. bmatActive(bmatIndex) ) cycle bmat_loop
1228      subVector => cvm_getSubVector( controlVector, bmatLabelList(bmatIndex) )
1229
1230      call utl_tmg_start(50, '--Bmatrix')
1231      select case( trim(bmatTypeList(bmatIndex)) )
1232      case ('HI')
1233        !- Time-Mean Homogeneous and Isotropic...
1234        call utl_tmg_start(53, '----B_HI_AD')
1235        call bmat1D_bsqrtHiAd(subvector,  &  ! IN
1236                              column,     &  ! OUT
1237                              obSSpaceData ) ! IN
1238        call utl_tmg_stop(53)
1239      case ('ENS')
1240        !- Ensemble based
1241        call utl_tmg_start(61, '----B_ENS_AD')
1242        call bmat1D_bsqrtEnsAd(subvector, &  ! IN
1243                                column )     ! OUT
1244        call utl_tmg_stop(61)
1245      case default
1246        call utl_abort( 'bmat1D_sqrtBT: requested bmatrix type does not exist ' // trim(bmatTypeList(bmatIndex)) )
1247      end select
1248      call utl_tmg_stop(50)
1249
1250    end do bmat_loop
1251
1252  end subroutine bmat1D_sqrtBT
1253
1254  !--------------------------------------------------------------------------
1255  ! bmat1D_get1DVarIncrement
1256  !--------------------------------------------------------------------------
1257  subroutine bmat1D_get1DVarIncrement(incr_cv, column, columnTrlOnAnlIncLev, &
1258                                     obsSpaceData, nvadim_mpilocal)
1259    !
1260    !:Purpose: to compute 1Dvar increment from control vector
1261    !
1262    implicit none
1263
1264    ! Arguments:
1265    real(8),                 intent(in)    :: incr_cv(:)
1266    type(struct_columnData), intent(inout) :: column
1267    type(struct_columnData), intent(in)    :: columnTrlOnAnlIncLev
1268    type(struct_obs),        intent(in)    :: obsSpaceData
1269    integer,                 intent(in)    :: nvadim_mpilocal
1270
1271    ! compute increment from control vector (multiply by B^1/2)
1272    call bmat1D_sqrtB(incr_cv, nvadim_mpilocal, column, obsSpaceData)
1273    call cvt_transform(column, 'ZandP_tl', columnTrlOnAnlIncLev)
1274
1275  end subroutine bmat1D_get1DVarIncrement
1276
1277  !--------------------------------------------------------------------------
1278  ! bmat1D_Finalize
1279  !--------------------------------------------------------------------------
1280  subroutine bmat1D_Finalize()
1281    !
1282    !:Purpose: to deallocate memory used by internal module structures
1283    !
1284    implicit none
1285
1286    if (initialized) then
1287       if (allocated(bSqrtLand)) then
1288         deallocate( bSqrtLand )
1289         deallocate( bSqrtSea )
1290         deallocate( latLand, lonLand, latSea, lonSea )
1291       end if
1292       if (allocated(bSqrtEns)) deallocate( bSqrtEns )
1293       call var1D_finalize()
1294    end if
1295
1296  end subroutine bmat1D_Finalize
1297
1298end module bMatrix1DVar_mod