bCovarSetupChem_mod sourceΒΆ

   1module bCovarSetupChem_mod 
   2  ! MODULE bCovarSetupChem_mod (prefix='bcsc' category='6. High-level data objects')
   3  !
   4  !:Purpose:  Contains routines for the reading and preparation of
   5  !           background-error covariance elements. Correlation matrices 
   6  !           based on horizontally homogeneous/isotropic correlations.  
   7  !
   8  !:Comments:
   9  !
  10  !   1. Covariances uncoupled from weather variable.
  11  !
  12  !   2. Handles univariate and multivariate covariances.
  13  !      See routines bcsc_readcorns2 and bcsc_sucorns2. 
  14  ! 
  15  !   3. For multiple univariate variables (or univarite blocks of one to
  16  !      multiple variables), one can alternatively have multiple sets of
  17  !      covariance matrices within this module instead of a single covariance
  18  !      matrix setup (similarly to what was done for corvert*).
  19  !
  20
  21  ! Public Subroutines:
  22  !
  23  !    bcsc_setupCH:    Must be called first. Sets of background covariance
  24  !                     matrix (and balance operators if any are eventually
  25  !                     added)
  26  !    bcsc_getCovarCH: Provides covariances and related
  27  !    bcsc_getScaleFactor : Provides std dev scaling factor
  28  !                     elements for background check and obs operators. 
  29  !    bcsc_finalize    Deallocate internal module arrays.
  30  !    bcsc_StatsExistForVarName: Determine is Covar available for specified variable
  31  !    bcsc_getBgStddev: Interpolation background error std dev to obs location
  32  !    bcsc_retrieveBgStddev: Retrieve previously saved background stddev
  33  !                      profiles in bgStddev from the header index.
  34  !    bcsc_addBgStddev: Add background stddev profiles (and inverse) to 
  35  !                      bgStddev which can be retrieved later using a header index.
  36
  37  use midasMpi_mod
  38  use MathPhysConstants_mod
  39  use earthConstants_mod
  40  use obsSubSpaceData_mod
  41  use gridStateVector_mod
  42  use globalSpectralTransform_mod
  43  use horizontalCoord_mod
  44  use verticalCoord_mod
  45  use varNameList_mod
  46  use utilities_mod
  47  use calcHeightAndPressure_mod
  48
  49  implicit none
  50  save
  51  private
  52
  53  ! public procedures
  54  ! ------------------
  55    
  56  public :: bcsc_setupCH,bcsc_finalize
  57  public :: bcsc_getCovarCH
  58  public :: bcsc_getScaleFactor
  59  public :: bcsc_StatsExistForVarName
  60  public :: bcsc_retrieveBgStddev,bcsc_addBgStddev,bcsc_getBgStddev
  61  
  62  ! public types
  63  ! ------------
  64    
  65  public :: struct_bcsc_bgStats
  66
  67  ! module shared variables
  68  ! ----------------------- 
  69  
  70  integer             :: nlev_M,nlev_T
  71  integer             :: gstID         
  72  real(8), allocatable   :: rlatr(:),rlongr(:)     
  73  type(struct_vco),pointer :: vco_anl
  74  
  75  character(len=15) :: bcsc_mode
  76                            
  77  integer, external   :: get_max_rss
  78  integer             :: nulbgst=0
  79
  80  ! Background error covariance matrix elements.
  81  ! One could add an additional dimension to corns  
  82  ! for separate block-univariate correlation matrices.
  83  ! This would also permit merging of bmatrixhi_mod and bmatrixchem_mod
  84  ! into one module.
  85
  86  real(8),allocatable :: stddev(:,:,:)
  87  real(8),allocatable :: rstddev(:,:)
  88
  89  ! Parameters of the NAMBCHM namelist
  90  integer             :: ntrunc               ! spectral truncation
  91  real(8)             :: rpor(vnl_numvarmax)  ! horizontal localization distance (Gaussian)
  92  real(8)             :: rvloc(vnl_numvarmax) ! vertical localization distance (GC)
  93  real(8)             :: scaleFactor(vnl_numvarmax,vco_maxNumLevels) ! variable and level dependent scale factor applied to variances
  94  integer             :: numModeZero          ! number of eigenmodes to set to zero
  95  logical             :: ReadWrite_sqrt       ! choose to read and/or write sqrt of correlations
  96  logical             :: getPhysSpaceStats    ! choose to calculate/save physical space cov (stddev, corverti)
  97  logical             :: getPhysSpaceHCorrel  ! calculate correlation lengths from spectral cov (needed for some CH obs operator settings)
  98  logical             :: WritePhysSpaceStats  ! choose to output physical space stats in 'bCovarSetupChem_out.fst'
  99  character(len=4)    :: stddevMode           ! can be 'GD2D', 'GD3D' or 'SP2D'
 100  character(len=4)    :: IncludeAnlVarKindCH(vnl_numvarmax) ! list of CH variable names to consider
 101  character(len=4)    :: CrossCornsVarKindCH(vnl_numvarmax) ! not sure what this is for...
 102  character(len=20)   :: TransformVarKindCH                 ! name of variable transform to apply to chemistry variables
 103
 104  ! Square root of scaleFactor   
 105  real(8) :: scaleFactor_stddev(vnl_numvarmax,vco_maxNumLevels) 
 106      
 107  real(8), parameter :: zps = 101000.D0 ! Reference surface pressure
 108
 109  ! module structures
 110  ! -----------------
 111  
 112  type :: struct_bcsc_bgStats
 113
 114     !  Structure storing background error stats 
 115     !  and related elements
 116
 117     ! logical indicating if stats are available     
 118     logical              :: initialized=.false.
 119     
 120     integer              :: numvar3d   ! number of 3D fields
 121     integer              :: numvar2d   ! number of 2D fields
 122     integer              :: ni         ! number of latitudes
 123     integer              :: nj         ! number of longitudes
 124     integer              :: nlev       ! number of vertical levels
 125     
 126     ! Total number of elements over all verticallevels and 
 127     ! variables (varNameList)
 128     integer              :: nkgdim
 129     
 130     integer              :: ntrunc     ! spectral dimension
 131     character(len=4), allocatable :: varNameList(:) ! list of variable names
 132                      
 133     integer, allocatable :: nsposit(:)  ! start positions of fields
 134     real(8), allocatable :: lat(:)      ! grid lat in radians
 135     real(8), allocatable :: lon(:)      ! grid lon in radians
 136     real(8), allocatable :: vlev(:)     ! vertical levels
 137     real(8), allocatable :: corns(:,:,:)    ! spectral space correlation matrix
 138
 139     ! Phys. Space vertical correlation matrix
 140     real(8), allocatable :: corvert(:,:,:)
 141     
 142     real(8), allocatable :: corverti(:,:,:) ! Inverse of 'corvert'
 143     
 144     real(8), allocatable :: stddev(:,:,:)   ! error std dev
 145     real(8), allocatable :: hcorrlen(:,:)   ! horizontal correlation lengths
 146  
 147  end type struct_bcsc_bgStats
 148
 149  ! Assigned type variables
 150  ! -----------------------
 151    
 152  type(struct_bcsc_bgStats)  :: bgStats  
 153  type(struct_oss_obsdata)   :: bgStddev ! Arrays for background error 
 154                                           ! std dev in obs space
 155
 156  !*************************************************************************
 157    
 158  contains
 159
 160  !--------------------------------------------------------------------------
 161  ! bcsc_setupCH
 162  !--------------------------------------------------------------------------
 163  subroutine bcsc_setupCH(hco_in,vco_in,covarNeeded,mode)
 164    !
 165    !:Purpose: Set up for constituents static background error covariances.
 166    !
 167    implicit none
 168
 169    ! Arguments:
 170    type(struct_hco), pointer, intent(in)  :: hco_in
 171    type(struct_vco), pointer, intent(in)  :: vco_in
 172    logical,                   intent(out) :: covarNeeded
 173    character(len=*),          intent(in)  :: mode ! 'Analysis' or 'BackgroundCheck'
 174
 175    ! Locals:
 176    integer :: nulnam, ierr, fnom, fclos
 177    integer :: varIndex,nChmVars,varIndex2
 178    character(len=4) :: BchmVars(vnl_numvarmax)
 179    real(8), pointer    :: pressureProfile_T(:)
 180        
 181    NAMELIST /NAMBCHM/ntrunc,rpor,rvloc,scaleFactor,numModeZero,ReadWrite_sqrt, &
 182                      stddevMode,IncludeAnlVarKindCH,getPhysSpaceHCorrel, &
 183		      CrossCornsVarKindCH,WritePhysSpaceStats, &
 184                      TransformVarKindCH,getPhysSpaceStats
 185
 186    write(*,*) 'Started bcsc_setupCH'  
 187     
 188    ! First check if there are any CH fields 
 189    
 190    covarNeeded = .true.
 191    varIndex2=0
 192    do varIndex = 1, vnl_numvarmax
 193      if (gsv_varExist(varName = vnl_varNameList(varIndex))) then
 194        if (vnl_varKindFromVarname(vnl_varNameList(varIndex)) == 'CH') then
 195          varIndex2 = 1
 196          exit
 197        end if 
 198      end if      
 199    end do
 200    if (varIndex2 == 0) then
 201      ! Assume there is no need for Bchm
 202      covarNeeded = .false.
 203      return
 204    end if
 205
 206    bgStats%numvar3d = 0
 207    bgStats%numvar2d = 0
 208 
 209    allocate(bgStats%varNameList(vnl_numvarmax))
 210    bgStats%varNameList(:) = ''
 211    allocate(bgStats%nsposit(vnl_numvarmax+1))
 212    bgStats%nsposit(1) = 1
 213
 214    if ( trim(mode) == 'Analysis' .or. trim(mode) == 'BackgroundCheck') then
 215      bcsc_mode = trim(mode)
 216      if(mmpi_myid == 0) write(*,*)
 217      if(mmpi_myid == 0) write(*,*) 'bcsc_setupCH: Mode activated = ', &
 218        trim(bcsc_mode)
 219    else
 220      write(*,*)
 221      write(*,*) 'mode = ', trim(mode)
 222      call utl_abort('bcsc_setupCH: unknown mode')
 223    end if
 224
 225    ! Initialization of namelist NAMBCHM parameters
 226    
 227    ntrunc=108
 228    rpor(:)=3000.D3
 229    rvloc(:)=4.0D0
 230    scaleFactor(:,:) = 0.0d0
 231    numModeZero = 0
 232    ReadWrite_sqrt = .false.
 233    WritePhysSpaceStats = .false.
 234    getPhysSpaceHCorrel = .false.
 235    getPhysSpaceStats = .false.
 236    stddevMode = 'GD3D'    
 237    IncludeAnlVarKindCH(:) = ''
 238    CrossCornsVarKindCH(:) = ''
 239    TransformVarKindCH = ''
 240            
 241    ! Read namelist input
 242    
 243    nulnam = 0
 244    ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 245    read(nulnam,nml=NAMBCHM,iostat=ierr)
 246    if(ierr /= 0) call utl_abort('bcsc_setupCH: Error reading namelist')
 247    if(mmpi_myid == 0) write(*,nml=NAMBCHM)
 248    ierr = fclos(nulnam)
 249
 250    ! Set BchmVars
 251    nChmVars=0
 252    BChmVars(:)=''
 253    if (trim(IncludeAnlVarKindCH(1)) == '') then
 254      do varIndex = 1, vnl_numvarmax
 255        if (.not. gsv_varExist(varName = vnl_varNameList(varIndex))) cycle
 256        if (vnl_varKindFromVarname(vnl_varNameList(varIndex)) /= 'CH') cycle
 257        nChmVars = nChmVars+1
 258        BchmVars(nChmVars) = trim(vnl_varNameList(varIndex))
 259      end do
 260    else
 261      do varIndex = 1, vnl_numvarmax
 262        if (.not. gsv_varExist(varName = vnl_varNameList(varIndex))) cycle
 263        if (vnl_varKindFromVarname(vnl_varNameList(varIndex)) /= 'CH') cycle
 264        do varIndex2 = 1, vnl_numvarmax
 265          if (trim(IncludeAnlVarKindCH(varIndex2)) == '') exit
 266          if (trim(vnl_varNameList(varIndex)) == &
 267	    trim(IncludeAnlVarKindCH(varIndex2))) then
 268            nChmVars = nChmVars+1
 269            BchmVars(nChmVars)= trim(vnl_varNameList(varIndex))
 270            exit
 271          end if
 272        end do
 273      end do
 274    end if
 275
 276    if (nChmVars == 0) then 
 277      if(mmpi_myid == 0) then
 278        write(*,*) 'Size of BchmVars is zero. B matrix for CH family ', &
 279	           'not produced.'
 280        write(*,*) 'No chemical assimilation to be performed.'
 281        write(*,*) 'Completed bcsc_setupCH'
 282      end if
 283      covarNeeded = .false.
 284      return
 285    end if
 286    
 287    ! Set vertical dimensions
 288
 289    vco_anl => vco_in
 290    nLev_M = vco_anl%nlev_M
 291    nLev_T = vco_anl%nlev_T
 292
 293    ! Find the 3D variables (within NAMSTATE namelist)
 294
 295    do varIndex = 1, vnl_numvarmax3D    
 296      if (gsv_varExist(varName=vnl_varNameList3D(varIndex)) .and. &
 297          any(trim(vnl_varNameList3D(varIndex))==BchmVars(1:nChmVars)) ) then
 298
 299        if (vnl_varKindFromVarname(vnl_varNameList3D(varIndex)) /= 'CH') cycle
 300	  
 301        bgStats%numvar3d = bgStats%numvar3d + 1
 302        bgStats%nsposit(bgStats%numvar3d+1)= &
 303	  bgStats%nsposit(bgStats%numvar3d)+nLev_T
 304        bgStats%varNameList(bgStats%numvar3d)= &
 305	  vnl_varNameList3D(varIndex)
 306      end if
 307    end do
 308 
 309    ! Find the 2D variables (within NAMSTATE namelist)
 310
 311    do varIndex = 1, vnl_numvarmax2D
 312      if (gsv_varExist(varName=vnl_varNameList2D(varIndex)) .and. &
 313          any(trim(vnl_varNameList2D(varIndex)) == BchmVars(1:nChmVars)) ) then
 314
 315        if (vnl_varKindFromVarname(vnl_varNameList2D(varIndex)) /= 'CH') cycle
 316        bgStats%numvar2d = bgStats%numvar2d + 1
 317        bgStats%nsposit(bgStats%numvar3d+bgStats%numvar2d+1) = &
 318	  bgStats%nsposit(bgStats%numvar3d+bgStats%numvar2d)+1
 319        bgStats%varNameList(bgStats%numvar2d) = vnl_varNameList2D(varIndex)
 320      end if       
 321    end do
 322
 323    if (bgStats%numvar3d+bgStats%numvar2d == 0) then    
 324      if (mmpi_myid == 0) then
 325        write(*,*) 'B matrix for CH family not produced.'
 326        write(*,*) 'No chemical assimilation to be performed.'
 327        write(*,*) 'Completed bcsc_setupCH'
 328      end if
 329      covarNeeded = .false.
 330      return
 331    else if (mmpi_myid == 0) then
 332      if (bgStats%numvar3d > 0) then
 333        write(*,*) 'bcsc_setupCH: Number of 3D variables', &
 334	  bgStats%numvar3d,bgStats%varNameList(1:bgStats%numvar3d)
 335      end if
 336      if (bgStats%numvar2d > 0) then
 337        write(*,*) 'bcsc_setupCH: Number of 2D variables', &
 338	  bgStats%numvar2d,bgStats%varNameList(bgStats%numvar3d+1: &
 339	    bgStats%numvar3d+bgStats%numvar2d)
 340      end if
 341    end if
 342    
 343    bgStats%nkgdim = &
 344      bgStats%nsposit(bgStats%numvar3d+bgStats%numvar2d+1)-1
 345
 346    ! Scalefactors must be > 0 until ensembles for constituents can be used.
 347    
 348    if (bgStats%numvar3d > 0) then    
 349      if (any(scaleFactor(1:bgStats%numvar3d,1:nLev_T) <= 0.0D0)) then
 350        write(*,*) 'Scalefactors: ',scaleFactor(1:bgStats%numvar3d,1:nLev_T) 
 351        call utl_abort('bcsc_setupCH: Scalefactors values must be > 0 for now.')
 352      end if
 353    end if
 354    if (bgStats%numvar2d > 0) then     
 355      if (any(scaleFactor(1:bgStats%numvar2d,1) <= 0.0D0)) then 
 356        write(*,*) 'Scalefactors: ',scaleFactor(1:bgStats%numvar3d,1:nLev_T) 
 357        call utl_abort('bcsc_setupCH: Scalefactors values must be > 0 for now.') 
 358      end if
 359    end if
 360    
 361    ! Set scalefactor_stddev
 362
 363    scaleFactor_stddev(1:bgStats%numvar3d+bgStats%numvar2d, &
 364      1:max(nLev_M,nLev_T))=0.0d0
 365      
 366    where (scaleFactor(1:bgStats%numvar3d+bgStats%numvar2d, &
 367                       1:max(nLev_M,nLev_T)) > 0.0d0) 
 368      
 369      scaleFactor_stddev(1:bgStats%numvar3d+bgStats%numvar2d, &
 370        1:max(nLev_M,nLev_T)) = sqrt(scaleFactor(1:bgStats%numvar3d &
 371	                             + bgStats%numvar2d,1:max(nLev_M,nLev_T)))
 372	  
 373    end where
 374    
 375    bgStats%ni = hco_in%ni
 376    bgStats%nj = hco_in%nj
 377    bgStats%ntrunc = ntrunc
 378    gstID  = gst_setup(bgStats%ni,bgStats%nj,bgStats%ntrunc, &
 379      bgStats%nkgdim)
 380
 381    if (allocated(bgStats%lat)) deallocate(bgStats%lat)
 382    if (allocated(bgStats%lon)) deallocate(bgStats%lon)    
 383    allocate(bgStats%lat(bgStats%nj))  
 384    allocate(bgStats%lon(bgStats%ni+1))
 385    
 386    bgStats%lat(1:bgStats%nj) = hco_in%lat(1:bgStats%nj)
 387    bgStats%lon(1:bgStats%ni) = hco_in%lon(1:bgStats%ni)
 388    bgStats%lon(bgStats%ni+1) = 2*MPC_PI_R8
 389    
 390    ! Assign sizes and transfer some fields
 391    
 392    if ( bgStats%numvar3d > 0 ) then
 393      bgStats%nlev = nlev_T
 394    else if (bgStats%numvar2d > 0 ) then
 395      bgStats%nlev = 1
 396    else
 397      bgStats%nlev = 0
 398    end if
 399    
 400    allocate(stddev(bgStats%ni+1, bgStats%nj, bgStats%nkgdim))
 401    allocate(rstddev(bgStats%nkgdim, 0:bgStats%ntrunc))    
 402      
 403    allocate(bgStats%corns(bgStats%nkgdim,bgStats%nkgdim, &
 404      0:bgStats%ntrunc))  
 405    allocate(bgStats%stddev(bgStats%ni+1,bgStats%nj, &
 406      bgStats%nkgdim ))
 407    if ( bgStats%nlev > 1 ) then 
 408      allocate(bgStats%vlev(bgStats%nlev))
 409      if (getPhysSpaceStats .or. trim(bcsc_mode) == 'BackgroundCheck') then
 410        allocate(bgStats%corvert(bgStats%nlev,bgStats%nlev, &
 411          bgStats%numvar3d))
 412      end if
 413      if (getPhysSpaceStats) then
 414        allocate(bgStats%corverti(bgStats%nlev,bgStats%nlev,&
 415          bgStats%numvar3d))
 416      end if
 417    end if
 418    
 419    ! Get vertical levels
 420    
 421    if (bgStats%nlev > 1) then
 422      call czp_fetch1DLevels(vco_anl, zps, profT_opt=pressureProfile_T)
 423      bgStats%vlev(1:bgStats%nlev) = pressureProfile_T(1:bgStats%nlev) 
 424    else if (bgStats%nlev == 1) then
 425      bgStats%vlev(1)=zps     
 426    end if
 427      
 428    ! Read covar stats, scale standard deviations,  and apply localization 
 429    ! to vertical correlation matrices in horizontal spectral space
 430    
 431    call bcsc_rdstats
 432    bgStats%stddev(:,:,:)=stddev(:,:,:)
 433    if (allocated(stddev)) deallocate(stddev)
 434    
 435    ! Generate or read correlation matrix square roots
 436    
 437    call bcsc_sucorns2
 438    
 439    if(mmpi_myid == 0) write(*,*) 'Completed bcsc_setupCH'
 440    
 441    bgStats%initialized = .true.
 442    if (allocated(rstddev)) deallocate(rstddev)
 443
 444  end subroutine bcsc_setupCH
 445
 446  !--------------------------------------------------------------------------
 447  ! bcsc_getScaleFactor
 448  !--------------------------------------------------------------------------
 449  subroutine bcsc_getScaleFactor(scaleFactorOut)
 450    !
 451    !:Purpose: To set scaling factors for background error std. dev.
 452    !
 453    implicit none
 454
 455    ! Arguments:
 456    real(8), intent(out) :: scaleFactorOut(:,:) ! Error std. dev. scale factor
 457
 458    ! Locals:
 459    integer :: levelIndex,varIndex
 460
 461    do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
 462    do levelIndex = 1, bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
 463      scaleFactorOut(varIndex,levelIndex) = scaleFactor_stddev(varIndex,levelIndex)
 464    end do
 465    end do
 466
 467  end subroutine bcsc_getScaleFactor
 468
 469  !--------------------------------------------------------------------------
 470  ! bcsc_rdstats
 471  !--------------------------------------------------------------------------
 472  subroutine bcsc_rdstats
 473    !
 474    !:Purpose: To read chemical constituents background stats file.
 475    !
 476    implicit none
 477
 478    ! Locals:
 479    integer :: ierr, fnom, fstouv, fstfrm, fclos
 480    logical :: lExists
 481    character(len=12) :: bFileName = './bgchemcov'
 482    type(struct_vco),pointer :: vco_file => null()
 483
 484    inquire(file=bFileName,exist=lExists)
 485    if ( lexists ) then
 486      ierr = fnom(nulbgst,bFileName,'RND+OLD+R/O',0)
 487      if ( ierr == 0 ) then
 488        ierr =  fstouv(nulbgst,'RND+OLD')
 489      else
 490        call utl_abort('bcsc_rdstats: Problem in opening the background ' // &
 491	               'chemical constituent stat file')
 492      end if
 493    else
 494      call utl_abort('bcsc_rdstats: Background chemical constituent stat ' // &
 495                     'file is missing')
 496    end if
 497
 498    ! check if analysisgrid and covariance file have the same vertical levels
 499    call vco_SetupFromFile( vco_file,  & ! OUT
 500                            bFileName )  ! IN
 501    if (.not. vco_equal(vco_anl,vco_file)) then
 502      call utl_abort('bcsc_rstats: vco from analysisgrid and chem cov file ' // &
 503                     'do not match')
 504    end if
 505
 506    ! Read spectral space correlations
 507    
 508    call bcsc_readcorns2
 509    
 510    ! Read error standard deviations
 511    
 512    call bcsc_rdstddev 
 513
 514    ! Scale error standard deviations (and save to file if requested)
 515    
 516    call bcsc_scalestd
 517    
 518    ierr = fstfrm(nulbgst)
 519    ierr = fclos(nulbgst)
 520
 521  end subroutine bcsc_rdstats
 522
 523  !--------------------------------------------------------------------------
 524  ! bcsc_scalestd
 525  !--------------------------------------------------------------------------
 526  subroutine bcsc_scalestd
 527    !
 528    !:Purpose: To scale error standard-deviation values.
 529    !
 530    implicit none
 531
 532    ! Locals:
 533    integer :: lonIndex, latIndex, varIndex, levelIndex, nlev, nulsig
 534    integer :: ierr, fnom, fstouv, fstfrm, fclos
 535  
 536    if (WritePhysSpaceStats .and. mmpi_myid == 0) then
 537      nulsig = 0
 538      ierr = fnom(nulsig,'bCovarSetupChem_out.fst','STD+RND',0)
 539      ierr = fstouv(nulsig,'RND')
 540      ierr = utl_fstecr(bgStats%vlev,-32,nulsig,0,0,0,1,1,bgStats%nlev, &
 541                        0,0,0,'X','PX','Pressure','X',0,0,0,0,5,.true.)
 542      ierr = utl_fstecr(bgStats%lat,-32,nulsig,0,0,0,1,bgStats%nj,1, &
 543                        0,0,0,'X','^^','latitude','X',0,0,0,0,5,.true.)
 544      ierr = utl_fstecr(bgStats%lon,-32,nulsig,0,0,0,bgStats%ni+1,1,1, &
 545                        0,0,0,'X','>>','longitude','X',0,0,0,0,5,.true.)
 546    end if
 547    
 548    do varIndex = 1,bgStats%numvar3d+bgStats%numvar2d
 549      nlev=bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
 550      do lonIndex = 1, bgStats%ni+1
 551      do latIndex = 1, bgStats%nj
 552        stddev(lonIndex,latIndex,bgStats%nsposit(varIndex): &
 553	       bgStats%nsposit(varIndex+1)-1) = &
 554               scaleFactor_stddev(varIndex,1:nlev)* &
 555               stddev(lonIndex,latIndex,bgStats%nsposit(varIndex): &
 556	       bgStats%nsposit(varIndex+1)-1)
 557      end do
 558      end do
 559
 560      if (WritePhysSpaceStats .and. mmpi_myid == 0) then
 561        do levelIndex=1,nlev
 562          ierr = utl_fstecr(stddev(1:bgStats%ni+1,1:bgStats%nj, &
 563	         bgStats%nsposit(varIndex)-1+levelIndex),-32,nulsig, &
 564		 0,0,0,bgStats%ni+1,bgStats%nj,1,levelIndex,0,nlev, &
 565                 'X',bgStats%varNameList(varIndex),'STDDEV','X',0,0,0,0, &
 566		 5,.true.)
 567        end do
 568      end if
 569
 570    end do
 571    
 572    if (WritePhysSpaceStats .and. mmpi_myid == 0) then
 573      ierr = fstfrm(nulsig)  
 574      ierr = fclos(nulsig)
 575    end if
 576
 577  end subroutine bcsc_scalestd
 578  
 579  !--------------------------------------------------------------------------
 580  ! bcsc_readCorns2
 581  !--------------------------------------------------------------------------
 582  subroutine bcsc_readCorns2
 583    !
 584    !:Purpose: To read correlation information and to form the correlation
 585    !          matrix.
 586    !
 587    !:Notes: Can read distinct block diagonal matrices with or without
 588    !        cross-correlations.
 589    !
 590
 591    ! Based on bhi_readcorns2.
 592    implicit none
 593
 594    ! Locals:
 595    integer :: jn,ierr,varIndex,varIndex2
 596    integer :: jcol,jrow,jstart,jnum,jstart2,jnum2
 597    real(8), allocatable, dimension(:) :: zstdsrc
 598    real(8), allocatable, dimension(:,:) :: zcornssrc
 599
 600    ! Standard file variables
 601    integer :: ini,inj,ink
 602    integer :: ip1,ip2,ip3,idateo
 603    character(len=2)  :: cltypvar
 604    character(len=4)  :: clnomvar
 605    character(len=4), allocatable :: clnomvarCrosscorns(:)
 606    character(len=12) :: cletiket
 607    integer :: fstinf
 608
 609    rstddev(:,:) = 0.0d0
 610    bgStats%corns(:,:,:) = 0.0d0
 611    if (any(CrossCornsVarKindCH(:) /= '')) then
 612      allocate(clnomvarCrosscorns(bgStats%numvar3d+bgStats%numvar2d))
 613      clnomvarCrosscorns(:)=''
 614    end if
 615    
 616    ! Read auto-correlations matrices
 617    
 618    do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
 619   
 620      clnomvar = bgStats%varNameList(varIndex)
 621      jstart = bgStats%nsposit(varIndex)
 622      jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
 623      allocate(zcornssrc(jnum,jnum))
 624      allocate(zstdsrc(jnum))
 625
 626      do jn = 0, bgStats%ntrunc
 627
 628        ! Looking for FST record parameters.
 629      
 630        idateo = -1
 631        cletiket = 'RSTDDEV'
 632        ip1 = -1
 633        ip2 = jn
 634        ip3 = -1
 635        cltypvar = 'X'
 636          
 637        ierr = utl_fstlir(ZSTDSRC,nulbgst,INI,INJ,INK,idateo,cletiket,ip1, &
 638	                  ip2,ip3,cltypvar,clnomvar)
 639          
 640        if (ierr < 0 .and. ip2 < 10 .and. all(CrossCornsVarKindCH(:) == '')) then
 641          write(*,*) 'bcsc_readcorns2: RSTDDEV ',ip2,jnum,clnomvar
 642          call utl_abort('bcsc_readcorns2: Problem with constituents ' // &
 643	                 'background stat file')
 644        else if (ierr < 0 .and. ip2 == 0 .and. any(CrossCornsVarKindCH(:) /= '')) then
 645          write(*,*) 'bcsc_readcorns2: Assumes content from cross-corrns ' // &
 646	             'input for ',clnomvar
 647          clnomvarCrosscorns(varIndex)=clnomvar
 648          exit
 649        end if
 650        if (ini /= jnum)  then
 651	  call utl_abort('bcsc_readcorns2: Constituents ' // &
 652	                 'background stat levels inconsistencies')
 653        end if
 654
 655        ! Looking for FST record parameters..
 656
 657        if (ierr >= 0) then
 658          idateo = -1
 659          cletiket = 'CORRNS'
 660          ip1 = -1
 661          ip2 = jn
 662          ip3 = -1
 663          cltypvar = 'X'
 664          ierr = utl_fstlir(ZCORNSSRC,nulbgst,INI,INJ,INK,idateo,cletiket, &
 665	                    ip1,ip2,ip3,cltypvar,clnomvar)
 666
 667          if (ierr < 0) then
 668            write(*,*) 'bcsc_readcorns2: CORRNS ',ip2,jnum,clnomvar
 669            call utl_abort('bcsc_readcorns2: Problem with constituents ' // &
 670	                   'background stat file')
 671          end if
 672          if (ini /= jnum .and. inj /= jnum) then
 673	    call utl_abort('bcsc_readcorns2: Constituents BG stat levels ' // &
 674	                   'inconsistencies')
 675	  end if
 676        else
 677          write(*,*) 'WARNING from bcsc_readcorns2: Setting RSDTDDEV to ' // &
 678	             '1.D-15 for NOMVAR and JN: ',clnomvar,' ',jn
 679          zstdsrc(1:jnum) = 1.D-15
 680          zcornssrc(1:jnum,1:jnum) = 0.0D0
 681          do jrow = 1, jnum
 682            zcornssrc(jrow,jrow) = 1.0D0
 683          end do
 684        end if
 685          
 686        rstddev(jstart:jstart+jnum-1,jn) = zstdsrc(1:jnum)
 687        bgStats%corns(jstart:jstart+jnum-1,jstart:jstart+jnum-1,jn)= &
 688	  zcornssrc(1:jnum,1:jnum)
 689          
 690      end do
 691       
 692      deallocate(zcornssrc)
 693      deallocate(zstdsrc)
 694
 695    end do
 696
 697    ! Read cross-correlation matrices
 698    
 699    if (any(CrossCornsVarKindCH(:) /= '')) then  
 700     
 701      do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
 702   
 703        if (all(CrossCornsVarKindCH(:) /= bgStats%varNameList(varIndex))) cycle
 704
 705        clnomvar = bgStats%varNameList(varIndex)
 706        jstart = bgStats%nsposit(varIndex)
 707        jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
 708
 709        if (clnomvarCrosscorns(varIndex) == clnomvar) then 
 710	  clnomvarCrosscorns(varIndex)=''
 711	end if
 712        
 713        do varIndex2 = 1, bgStats%numvar3d+bgStats%numvar2d
 714        
 715          if (varIndex == varIndex2) cycle
 716          
 717          cletiket='CORRNS '//bgStats%varNameList(varIndex2)
 718          ierr = fstinf(nulbgst,INI,INJ,INK,-1,cletiket,-1,-1,-1,'X',clnomvar)
 719          if (ierr < 0 ) cycle
 720          
 721          jstart2 = bgStats%nsposit(varIndex2)
 722          jnum2 =  bgStats%nsposit(varIndex2+1)-bgStats%nsposit(varIndex2)
 723          allocate(zcornssrc(jnum,jnum2))
 724          
 725          if (clnomvarCrosscorns(varIndex2) == bgStats%varNameList(varIndex2)) then
 726	    clnomvarCrosscorns(varIndex2)=''
 727	  end if
 728          
 729          do jn = 0, bgStats%ntrunc
 730 
 731            ierr = utl_fstlir(ZCORNSSRC,nulbgst,INI,INJ,INK,-1,cletiket,-1, &
 732	                      jn,-1,'X',clnomvar)
 733
 734            if (ierr < 0) then
 735              if (jn < 10) then
 736                write(*,*) 'bcsc_readcorns2: CORRNS ',ip2,jnum,clnomvar, &
 737                bgStats%varNameList(varIndex2)
 738                call utl_abort('bcsc_readcorns2: Problem with constituents ' // &
 739                               'background stat file')
 740              else
 741                exit
 742              end if
 743            end if
 744            if (ini /= jnum .and. inj /= jnum2) then
 745	      call utl_abort('bcsc_readcorns2: Constituents BG2 stat ' // &
 746	                     'levels inconsistencies')
 747            end if
 748	     
 749            bgStats%corns(jstart:jstart+jnum-1,jstart2:jstart2+jnum2-1,jn)= &
 750	      zcornssrc(1:jnum,1:jnum2)
 751            bgStats%corns(jstart2:jstart2+jnum2-1,jstart:jstart+jnum-1,jn)= &
 752	      transpose(zcornssrc(1:jnum,1:jnum2))
 753          
 754          end do
 755          deallocate(zcornssrc)
 756        end do
 757      end do   
 758    end if
 759    
 760    if (any(CrossCornsVarKindCH(:) /= '')) then
 761      if (any(clnomvarCrosscorns(:) /= '')) then
 762        write(*,*) 'bcsc_readcorns2: Missing matrix for ', &
 763	           clnomvarCrosscorns(1:bgStats%numvar3d+bgStats%numvar2d)
 764        call utl_abort('bcsc_readcorns2: Missing correlations matrix')      
 765      end if
 766      deallocate(clnomvarCrosscorns)
 767    end if
 768     
 769    ! Apply convolution to RSTDDEV correlation
 770
 771    call bcsc_convol
 772    
 773    do jn = 0, bgStats%ntrunc
 774
 775      ! Re-build correlation matrix: factorization of corns with convoluted RSTDDEV
 776      do jcol = 1, bgStats%nkgdim
 777        bgStats%corns(1:bgStats%nkgdim,jcol,jn) = &
 778	  rstddev(1:bgStats%nkgdim,jn) * &
 779	  bgStats%corns(1:bgStats%nkgdim,jcol,jn) * rstddev(jcol,jn)
 780      end do
 781
 782    end do
 783
 784  end subroutine bcsc_readCorns2
 785
 786  !--------------------------------------------------------------------------
 787  ! bcsc_convol
 788  !--------------------------------------------------------------------------
 789  subroutine bcsc_convol
 790    implicit none
 791
 792    ! Locals:
 793    real(8) :: dlfact2,dlc,dsummed
 794    real(8) :: dtlen,zr,dlfact
 795    integer :: jn,latIndex,jk,varIndex,levelIndex,nsize,ierr
 796    real(8) :: zleg(0:bgStats%ntrunc,bgStats%nj)
 797    real(8) :: zsp(0:bgStats%ntrunc,bgStats%nkgdim)
 798    real(8) :: zgr(bgStats%nj,bgStats%nkgdim)
 799    real(8) :: zrmu(bgStats%nj)
 800    integer :: nlev_MT,ini,inj,ink,nulcorns
 801    real(8), allocatable :: wtemp(:,:,:)    
 802    real(8), allocatable :: hcorrel(:,:,:),hdist(:)
 803    logical :: lfound
 804    integer :: fnom, fstouv, fstfrm, fclos
 805
 806    do latIndex = 1, bgStats%nj
 807      zrmu(latIndex)  = gst_getrmu(latIndex,gstID)
 808    end do
 809
 810    ! CONVERT THE CORRELATIONS IN SPECTRAL SPACE INTO SPECTRAL
 811    ! COEFFICIENTS OF THE CORRELATION FUNCTION AND FUNCTION TO BE
 812    ! SELF-CONVOLVED
 813
 814    do jn = 0, bgStats%ntrunc
 815      dlfact = ((2.0d0*jn+1.0d0)/2.0d0)**(0.25d0)
 816      dlfact2 = ((2.0d0*jn +1.0d0)/2.0d0)**(0.25d0)
 817      do jk = 1, bgStats%nkgdim
 818        zsp(jn,jk) = rstddev(jk,jn)*dlfact*dlfact2
 819      end do
 820    end do
 821
 822    ! Transform to physical space
 823    call gst_zleginv(gstID,zgr,zsp,bgStats%nkgdim)
 824    
 825    ! Truncate in horizontal extent with Gaussian window
 826    
 827    varIndex=1
 828    do jk = 1, bgStats%nkgdim
 829      if (jk == bgStats%nsposit(varIndex)) then
 830        dtlen = rpor(varIndex)
 831        varIndex=varIndex+1 
 832      end if
 833      if (dtlen > 0.0d0) then
 834        dlc = 1.d0/dble(dtlen)
 835        dlc = 0.5d0*dlc*dlc
 836        do latIndex = 1, bgStats%nj
 837          zr = ec_ra * acos(zrmu(latIndex))
 838          dlfact = dexp(-(zr**2)*dlc)
 839          zgr(latIndex,jk) = dlfact*zgr(latIndex,jk)
 840        end do
 841      end if
 842
 843      !write(*,*) 'zeroing length (km)=',jk,dtlen/1000.0
 844    end do
 845
 846    ! Transform back to spectral space
 847    call gst_zlegdir(gstID,zgr,zsp,bgStats%nkgdim)
 848
 849    ! Convert back to correlations
 850    do jk = 1, bgStats%nkgdim
 851      do jn = 0, bgStats%ntrunc
 852        zsp(jn,jk) = zsp(jn,jk)*(2.0d0/(2.0d0*jn+1.0d0))**(0.25d0)
 853      end do
 854    end do
 855
 856    ! PUT BACK INTO RSTDDEV
 857    do jn = 0, bgStats%ntrunc
 858      do jk = 1, bgStats%nkgdim
 859        rstddev(jk,jn) = zsp(jn,jk)
 860      end do
 861    end do
 862
 863    ! Re-normalize to ensure correlations
 864    do jk = 1, bgStats%nkgdim
 865      dsummed = 0.d0
 866      do jn = 0, bgStats%ntrunc
 867        dsummed = dsummed+ dble(rstddev(jk,jn)**2)*sqrt(((2.d0*jn)+1.d0)/2.d0)
 868      end do
 869      dsummed = sqrt(dsummed)
 870      do jn = 0, bgStats%ntrunc
 871        if(dsummed > 1.d-30) rstddev(jk,jn) = rstddev(jk,jn)/dsummed
 872      end do
 873    end do
 874
 875    ! CONVERT THE SPECTRAL COEFFICIENTS OF THE CORRELATION FUNCTION
 876    ! BACK INTO CORRELATIONS OF SPECTRAL COMPONENTS
 877    do jn = 0, bgStats%ntrunc
 878      dlfact = sqrt(0.5d0)*(1.0d0/((2.0d0*jn+1)/2.0d0))**0.25d0
 879      do jk = 1, bgStats%nkgdim
 880        rstddev(jk,jn) = rstddev(jk,jn)*dlfact
 881      end do
 882    end do
 883
 884    if ( .not.getPhysSpaceHCorrel .or. .not.getPhysSpaceStats ) return
 885
 886    ! Compute resultant physical space horizontal correlations and
 887    ! 1/e correlation length from correlation array if not available
 888    
 889    if (allocated(hcorrel)) deallocate(hcorrel)
 890    allocate(hcorrel(bgStats%nj,bgStats%nlev, &
 891      bgStats%numvar3d+bgStats%numvar2d))
 892    if (allocated(wtemp)) deallocate(wtemp)
 893    allocate(wtemp(0:bgStats%ntrunc,bgStats%nj,1))
 894    if (allocated(bgStats%hcorrlen)) deallocate(bgStats%hcorrlen)
 895    allocate(bgStats%hcorrlen(bgStats%nlev,  &
 896      bgStats%numvar3d+bgStats%numvar2d))
 897    if (allocated(hdist)) deallocate(hdist)
 898    allocate(hdist(bgStats%nj))
 899
 900    lfound=.true.
 901    do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
 902      nlev_MT = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
 903      do levelIndex = 1, nlev_MT
 904        ierr = utl_fstlir(hcorrel(:,levelIndex,varIndex),nulbgst,INI,INJ, &
 905	                  INK,-1,'HCORREL',levelIndex,-1,-1,'X', &
 906	                  bgStats%varNameList(varIndex))
 907        if (ierr < 0 ) then
 908          lfound=.false.
 909          exit
 910        end if
 911      end do
 912      ierr = utl_fstlir(bgStats%hcorrlen(1:nlev_MT,varIndex),nulbgst,INI, &
 913             INJ,INK,-1,'HCORRLEN',-1,-1,-1,'X',bgStats%varNameList(varIndex))
 914      if (ierr < 0 ) then
 915        lfound=.false.
 916        exit
 917      end if
 918    end do 
 919    
 920    if (lfound) return
 921
 922    do latIndex = 1, bgStats%nj
 923      hdist(latIndex)=ec_ra*acos(zrmu(latIndex))
 924    end do
 925
 926    zleg(:,:)=0.0d0
 927    wtemp(:,:,:)=0.0d0
 928    hcorrel(:,:,:)=0.0d0
 929    bgStats%hcorrlen(:,:)=0.0
 930    
 931    do latIndex = mmpi_myid+1, bgStats%nJ, mmpi_nprocs
 932      do jn = 0, bgStats%ntrunc
 933        wtemp(jn,latIndex,1) = gst_getzleg(jn,latIndex,gstID)
 934      end do
 935    end do
 936    
 937    nsize=bgStats%nJ*(bgStats%ntrunc+1)    
 938    call rpn_comm_allreduce(wtemp(0:bgStats%ntrunc,1:bgStats%nJ,1), &
 939         zleg(0:bgStats%ntrunc,1:bgStats%nJ),nsize,"mpi_double_precision", &
 940         "mpi_sum","GRID",ierr)
 941
 942    deallocate(wtemp)
 943    allocate(wtemp(bgStats%nj, bgStats%nlev, &
 944      bgStats%numvar3d+bgStats%numvar2d))
 945    wtemp(:,:,:)=0.0
 946    
 947    varIndex = 1
 948    levelIndex = 1
 949    do jk = 1, bgStats%nkgdim
 950      if (jk == bgStats%nsposit(varIndex+1)) then
 951        varIndex = varIndex+1 
 952        levelIndex = 1
 953      end if
 954
 955      do latIndex = mmpi_myid+1, bgStats%nj, mmpi_nprocs
 956        do jn = 0, bgStats%ntrunc
 957          wtemp(latIndex,levelIndex,varIndex) = wtemp(latIndex,levelIndex, &
 958	        varIndex)+rstddev(jk,jn)*rstddev(jk,jn)*  &
 959                sqrt(2.0)*sqrt(2.0*jn+1.0)*zleg(jn,latIndex)
 960        end do       
 961      end do
 962      levelIndex = levelIndex+1
 963    end do
 964    
 965    nsize=bgStats%nj*bgStats%nkgdim   
 966    call rpn_comm_allreduce(wtemp,hcorrel,nsize,"mpi_double_precision", &
 967                            "mpi_sum","GRID",ierr)
 968    deallocate(wtemp)
 969
 970    varIndex = 1
 971    levelIndex = 1
 972    do jk = 1, bgStats%nkgdim
 973      if (jk == bgStats%nsposit(varIndex+1)) then
 974        varIndex = varIndex+1 
 975        levelIndex = 1
 976      end if
 977      do latIndex=bgStats%nj-1,2,-1
 978        if (hcorrel(latIndex,levelIndex,varIndex) <= 0.368) then  ! 1/e ~ 0.368
 979          bgStats%hcorrlen(levelIndex,varIndex) = (hdist(latIndex)* &
 980	    (hcorrel(latIndex+1,levelIndex,varIndex)-0.368) &
 981            + hdist(latIndex+1)*(0.368-hcorrel(latIndex,    &
 982	    levelIndex,varIndex))) &
 983            /(hcorrel(latIndex+1,levelIndex,varIndex)- &
 984	    hcorrel(latIndex,levelIndex,varIndex))
 985          exit
 986        end if
 987      end do
 988      levelIndex = levelIndex+1
 989    end do  
 990
 991    
 992    if ( mmpi_myid == 0 ) then
 993    
 994      if (WritePhysSpaceStats) then 
 995        nulcorns = 0
 996        ierr = fnom(nulcorns,'bCovarSetupChem_out.fst','STD+RND',0)
 997        ierr = fstouv(nulcorns,'RND')
 998
 999        do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
1000          nlev_MT = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1001          do levelIndex = 1, nlev_MT
1002            ierr = utl_fstecr(hcorrel(:,levelIndex,varIndex),-32,nulcorns,0,0,0, &
1003	                      1,bgStats%nj,1,levelIndex,0,nlev_MT,'X', &
1004		              bgStats%varNameList(varIndex), &
1005                              'HCORREL','X',0,0,0,0,5,.true.)
1006          end do
1007          ierr = utl_fstecr(bgStats%hcorrlen(1:nlev_MT,varIndex),-32,nulcorns, &
1008	         0,0,0,1,1,nlev_MT,0,0,0,'X',bgStats%varNameList(varIndex), &
1009                 'HCORRLEN','X',0,0,0,0,5,.true.)
1010          ierr = utl_fstecr(hdist(1:bgStats%nj),-32,nulcorns,0,0,0,1, &
1011	         bgStats%nj,1,0,0,0,'X',bgStats%varNameList(varIndex), &
1012                 'HDIST','X',0,0,0,0,5,.true.)
1013        end do
1014      
1015        ierr = fstfrm(nulcorns)  
1016        ierr = fclos(nulcorns)
1017	
1018      end if
1019      
1020      write(*,*)
1021      write(*,*) 'bcsc_convol: Horizontal correlations'
1022      write(*,*)
1023      write(*,*) 'Separation distances (km)'
1024      write(*,*) bgStats%nj-bgStats%nj*4/5+1
1025      write(*,*) hdist(bgStats%nj*4/5:bgStats%nj)/1000.00
1026      write(*,*)
1027      do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
1028        if (varIndex <= bgStats%numvar3d) then
1029          write(*,*) bgStats%varNameList(varIndex), bgStats%nlev
1030          do levelIndex = 1, bgStats%nlev
1031            write(*,'(i3,f10.2,3000f6.2)') levelIndex, &
1032	                  bgStats%hcorrlen(levelIndex,varIndex)/1000.00, &
1033	                  hcorrel(bgStats%nj*4/5:bgStats%nj,levelIndex,varIndex)
1034          end do
1035        else
1036          write(*,*) bgStats%varNameList(varIndex), 1
1037          write(*,'(i3,f10.2,3000f6.2)') 1, &
1038	                           bgStats%hcorrlen(1,varIndex)/1000.00, &
1039	                           hcorrel(bgStats%nj*4/5:bgStats%nj,1,varIndex)
1040        end if
1041        write(*,*)
1042      end do
1043    end if
1044
1045    if (allocated(hcorrel)) deallocate(hcorrel)
1046    if (allocated(hdist)) deallocate(hdist)
1047
1048  end subroutine bcsc_convol
1049
1050  !--------------------------------------------------------------------------
1051  ! bcsc_rdstddev
1052  !--------------------------------------------------------------------------
1053  subroutine bcsc_rdstddev
1054    !
1055    !:Purpose: To read stddev and to set as 3D fields.
1056    !
1057    implicit none
1058
1059    ! Locals:
1060    integer :: ikey
1061    real(8) :: rcoord(10000)
1062    ! standard file variables
1063    integer :: ini,inj,ink
1064    integer :: ip1,ip2,ip3
1065    integer :: idate(100)
1066
1067    ! Reading the data
1068
1069    idate(1) = -1
1070    ip2      = -1
1071    ip3      = -1
1072
1073    ! Get latitudes and longitudes if available
1074
1075    ip1=-1
1076    ikey = utl_fstlir(rcoord,nulbgst,ini,inj,ink, &
1077                      idate(1),' ',ip1,ip2,ip3,' ','^^')
1078                         
1079    if (ikey >= 0) then
1080      if (allocated(rlatr)) deallocate(rlatr)
1081      allocate(rlatr(inj))
1082      rlatr(1:inj) = rcoord(1:inj) 
1083      rlatr(1:inj) = rlatr(1:inj)*MPC_RADIANS_PER_DEGREE_R8
1084    else 
1085      ! Assume same as bgStats%lat
1086      if (allocated(rlatr)) deallocate(rlatr)
1087      allocate(rlatr(bgStats%nj))
1088      inj = bgStats%nj
1089      rlatr(1:inj) = bgStats%lat(1:inj) 
1090    end if    
1091
1092    ikey = utl_fstlir(rcoord,nulbgst,ini,inj,ink, &
1093                      idate(1),' ',ip1,ip2,ip3,' ','>>')
1094                         
1095    if (ikey >= 0) then
1096      if (allocated(rlongr)) deallocate(rlongr)
1097      allocate(rlongr(ini+1))
1098      rlongr(1:ini) = rcoord(1:ini) 
1099      rlongr(1:ini) = rlongr(1:ini)*MPC_RADIANS_PER_DEGREE_R8
1100    else if (stddevMode /= 'SP2D') then
1101      ! Assume same as bgStats%lon
1102      if (allocated(rlongr)) deallocate(rlongr)
1103      allocate(rlongr(bgStats%ni+1))
1104      ini = bgStats%ni
1105      rlongr(1:ini) = bgStats%lon(1:ini) 
1106    end if 
1107    rlongr(ini+1) = 360.*MPC_RADIANS_PER_DEGREE_R8
1108     
1109    ! Read specified input type for error std. dev.
1110    
1111    if(stddevMode == 'GD3D') then
1112      call bcsc_rdstd3D
1113    elseif(stddevMode == 'GD2D') then
1114      call bcsc_rdstd
1115    elseif(stddevMode == 'SP2D') then
1116      call bcsc_rdspstd
1117    else
1118      call utl_abort('bcsc_rdstddev: unknown stddevMode')
1119    end if
1120    
1121  end subroutine bcsc_rdstddev
1122
1123  !--------------------------------------------------------------------------
1124  ! bcsc_rdspstd
1125  !--------------------------------------------------------------------------
1126  subroutine bcsc_rdspstd
1127    implicit none
1128
1129    ! Locals:
1130    integer :: varIndex,jn,inix,injx,inkx
1131    integer :: ikey, levelIndexo, firstn,lastn
1132    real(8) :: zsp(0:bgStats%ntrunc,max(nlev_M,nlev_T))
1133    real(8) :: zspbuf(max(nlev_M,nlev_T))
1134    real(8) :: zgr(bgStats%nj,max(nlev_M,nlev_T))
1135    ! standard file variables
1136    integer :: ini,inj,ink
1137    integer :: ip1,ip2,ip3
1138    integer :: idate(100),nlev_MT
1139    character(len=2)  :: cltypvar
1140    character(len=4)  :: clnomvar
1141    character(len=12) :: cletiket
1142    integer :: fstinf
1143
1144    stddev(:,:,:) = 0.0d0
1145    
1146    ! Reading the Legendre poly representation of the 2D background 
1147    ! error std. dev. field
1148
1149    idate(1) = -1
1150    ip1      = -1
1151    ip2      = -1
1152    ip3      = -1
1153
1154    cletiket = 'SPSTDDEV'
1155    cltypvar = 'X'
1156    
1157    do varIndex = 1,bgStats%numvar3d+bgStats%numvar2d
1158      clnomvar = bgStats%varNameList(varIndex)
1159      nlev_MT = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1160      
1161      firstn = -1
1162      do jn = 0, bgStats%ntrunc
1163        ip2 = jn
1164        ikey = fstinf(nulbgst,inix,injx,inkx,idate(1),cletiket,ip1,ip2,ip3, &
1165	       cltypvar,clnomvar)
1166
1167        if(ikey >= 0 ) then
1168          ikey = utl_fstlir(zspbuf(1:nlev_MT),nulbgst,ini,inj,ink,idate(1), &
1169	                    cletiket,ip1,ip2,ip3,cltypvar,clnomvar)
1170        else
1171          if(firstn == -1) firstn = jn
1172          lastn = jn
1173          zspbuf(:) = 0.0d0
1174        end if
1175
1176        if (ini /= nlev_MT) then
1177          if (ini == nlev_MT-1) then
1178            zspbuf(nlev_MT) = zspbuf(ini)
1179            write(*,*) 'WARNING in CHM_RDSPSTD: ini and nlev_MT not ', &
1180	               'same size - ',ini,nlev_MT
1181          else
1182            write(*,*) 'JN, INI, nlev_MT, NOMVAR: ',jn,ini,nlev_MT,' ',clnomvar
1183            call utl_abort('CHM_RDSPSTD: Constituents background stats ' // &
1184	                   'levels inconsistency')
1185          end if    
1186        end if
1187     
1188        do levelIndexo = 1, nlev_MT
1189          zsp(jn,levelIndexo) = zspbuf(levelIndexo)
1190        end do
1191      end do
1192      
1193      if (mmpi_myid == 0 .and. firstn /= -1) then
1194        write(*,*) 'WARNING: CANNOT FIND SPSTD FOR ',clnomvar, &
1195                   ' AT N BETWEEN ',firstn,' AND ',lastn,', SETTING TO ZERO!!!'
1196      end if
1197
1198      call gst_zleginv(gstID,zgr(:,1:nlev_MT),zsp(:,1:nlev_MT),nlev_MT)
1199
1200      do jn = 1, bgStats%ni+1
1201        stddev(jn,1:bgStats%nj, &
1202	  bgStats%nsposit(varIndex):bgStats%nsposit(varIndex+1)-1) = &
1203	  zgr(1:bgStats%nj,1:nlev_MT)
1204      end do
1205
1206    end do 
1207
1208  end subroutine bcsc_rdspstd
1209
1210  !--------------------------------------------------------------------------
1211  ! bcsc_rdspstd_newfmt
1212  !--------------------------------------------------------------------------
1213  subroutine bcsc_rdspstd_newfmt
1214    implicit none
1215
1216    ! Locals:
1217    integer :: varIndex,jn,inix,injx,inkx,ntrunc_file
1218    integer :: ikey,levelIndexo
1219    real(8) :: zsp(0:bgStats%ntrunc,max(nlev_M,nlev_T))
1220    real(8), allocatable :: zspbuf(:)
1221    real(8) :: zgr(bgStats%nj,max(nlev_M,nlev_T))
1222    ! standard file variables
1223    integer :: ini,inj,ink
1224    integer :: ip1,ip2,ip3
1225    integer :: idate(100),nlev_MT
1226    character(len=2)  :: cltypvar
1227    character(len=4)  :: clnomvar
1228    character(len=12) :: cletiket
1229    integer :: fstinf
1230
1231    stddev(:,:,:) = 0.0d0
1232
1233    ! Reading the data
1234
1235    idate(1) = -1
1236    ip2      = -1
1237    ip3      = -1
1238
1239    cletiket = 'SPSTDDEV'
1240    cltypvar = 'X'
1241
1242    ! Check if file is old format
1243    
1244    ip1 = -1
1245    clnomvar = bgStats%varNameList(1) 
1246    ikey = fstinf(nulbgst,inix,injx,inkx,idate(1),cletiket,ip1,ip2,ip3, &
1247                  cltypvar,clnomvar)
1248    write(*,*) 'ini,inj,ink=',inix,injx,inkx
1249    if(inix > 1) then
1250      write(*,*) 'bcsc_rdspstd_newfmt: ini>1, SPSTDDEV is in old format, ', &
1251                 ' calling bcsc_RDSPSTD...'
1252      call bcsc_rdspstd
1253      return
1254    end if
1255
1256    ! write(*,*) 'Reading for 3D and 2D variables'
1257    
1258    do varIndex = 1,bgStats%numvar3d+bgStats%numvar2d
1259      clnomvar = bgStats%varNameList(varIndex)
1260      nlev_MT = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1261
1262      !write(*,*) 'Reading ',clnomvar
1263
1264      do levelIndexo = 1, nlev_MT
1265        if (nlev_MT == 1) then
1266           ip1 = -1
1267        else if(vnl_varLevelFromVarName(clnomvar) == 'MM') then
1268          ip1 = vco_anl%ip1_M(levelIndexo)
1269        else
1270          ip1 = vco_anl%ip1_T(levelIndexo)
1271        end if
1272        
1273        ikey = fstinf(nulbgst,inix,ntrunc_file,inkx,idate(1),cletiket, &
1274	       ip1,ip2,ip3,cltypvar,clnomvar)
1275        ntrunc_file = ntrunc_file-1
1276
1277        allocate(zspbuf(0:ntrunc_file))
1278        
1279        if(ikey >= 0 ) then
1280          ikey = utl_fstlir(zspbuf(0:ntrunc_file),nulbgst,ini,inj,ink, &
1281	                    idate(1),cletiket,ip1,ip2,ip3,cltypvar,clnomvar)
1282        else
1283          write(*,*) 'bcsc_rdspstd_newfmt: ',varIndex,clnomvar,nlev_MT, &
1284	             levelIndexo,ikey,bgStats%ntrunc,ntrunc_file
1285          call utl_abort('bcsc_rdspstd_newfmt: SPSTDDEV record not found')
1286        end if
1287
1288        zsp(:,levelIndexo) = 0.0d0
1289        do jn = 0, min(bgStats%ntrunc,ntrunc_file)
1290          zsp(jn,levelIndexo) = zspbuf(jn)
1291        end do
1292        deallocate(zspbuf)
1293      end do
1294
1295      call gst_zleginv(gstID,zgr(:,1:nlev_MT),zsp(:,1:nlev_MT),nlev_MT)
1296
1297      do jn = 1, bgStats%ni+1
1298        stddev(jn,1:bgStats%nj,bgStats%nsposit(varIndex): &
1299	  bgStats%nsposit(varIndex+1)-1) = zgr(1:bgStats%nj,1:nlev_MT)
1300      end do
1301
1302    end do
1303
1304  end subroutine bcsc_rdspstd_newfmt
1305
1306  !--------------------------------------------------------------------------
1307  ! bcsc_rdstd
1308  !--------------------------------------------------------------------------
1309  subroutine bcsc_rdstd
1310    !
1311    !:Purpose: To read 2D stddev and to store as 3D
1312    !
1313    implicit none
1314
1315    ! Locals:
1316    integer :: varIndex,in
1317    integer :: ikey
1318    real(8), allocatable :: stddev3d(:,:,:)
1319    ! standard file variables
1320    integer :: ini,inj,ink
1321    integer :: ip1,ip2,ip3
1322    integer :: idate(100),nlev_MT
1323    character(len=2)  :: cltypvar
1324    character(len=4)  :: clnomvar
1325    character(len=12) :: cletiket
1326    integer :: fstinf
1327    real(8), allocatable  :: vlev(:),vlevout(:)
1328
1329    stddev(:,:,:) = 0.0d0
1330
1331    ! Reading the data
1332
1333    idate(1) = -1
1334    ip1      = -1
1335    ip2      = -1
1336    ip3      = -1
1337    
1338    cletiket = 'STDDEV'
1339    cltypvar=' '
1340
1341    ! Reading for 3D and 2D variables
1342    
1343    do varIndex = 1,bgStats%numvar3d+bgStats%numvar2d
1344      clnomvar = bgStats%varNameList(varIndex)
1345      nlev_MT = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1346 
1347      ikey = fstinf(nulbgst,ini,inj,ink,idate(1),cletiket,ip1,ip2,ip3, &
1348                    cltypvar,clnomvar)
1349      if (ikey < 0 .or. ini > 1 .or. ink /= nlev_MT) then
1350        write(*,*) 'bcsc_rdstd: ',varIndex,clnomvar,ikey,ini,ink,nlev_MT
1351        call utl_abort(': bcsc_rdstd record not found or incorrect')          
1352      end if
1353      
1354      allocate(stddev3d(1,inj,ink))
1355      stddev3d(1,:,:) = 0.0D0
1356      allocate(vlev(ink),vlevout(nlev_MT))
1357      vlev(:) = 1.0D0
1358      vlevout(:) = 1.0D0
1359      
1360      ikey = utl_fstlir(stddev3d(1,:,:), nulbgst, ini, inj, ink, &
1361                        idate(1), cletiket, ip1, ip2, ip3, cltypvar, clnomvar)
1362
1363      if (ikey < 0) then
1364        write(*,*) 'bcsc_rdstd: ',varIndex,clnomvar,nlev_MT,ikey
1365        call utl_abort(': bcsc_rdstd record not found')
1366      end if
1367        
1368      ! Extend to 3D
1369      if (inj == bgStats%nj) then
1370        do in = 1, bgStats%ni+1
1371          stddev(in,:,bgStats%nsposit(varIndex): &
1372	    bgStats%nsposit(varIndex+1)-1) = stddev3d(1,:,:) 
1373        end do
1374      else
1375        ! Interpolate in lat
1376        call gsv_field3d_hbilin(stddev3d, 1, inj, ink, rlongr, rlatr, vlev, &
1377             stddev(:,:,bgStats%nsposit(varIndex): &
1378	     bgStats%nsposit(varIndex+1)-1), bgStats%ni+1, bgStats%ni, &
1379	     nlev_MT, bgStats%lon, bgStats%lat, vlevout)
1380      end if
1381      deallocate(stddev3d, vlev, vlevout)
1382       
1383    end do
1384
1385  end subroutine bcsc_rdstd
1386
1387  !--------------------------------------------------------------------------
1388  ! bcsc_rdstd3d
1389  !--------------------------------------------------------------------------
1390  subroutine bcsc_rdstd3d
1391    !
1392    !:Purpose: To read 3D stddev.
1393    !
1394    !           Originally based on bcsc_rdspstd_newfmt
1395    !
1396    implicit none
1397
1398    ! Locals:
1399    integer :: varIndex
1400    integer :: ikey,levelIndexo
1401    real(8), allocatable :: stddev3d(:,:,:)
1402    ! standard file variables
1403    integer :: ini,inj,ink
1404    integer :: ip1,ip2,ip3
1405    integer :: idate(100),nlev_MT
1406    character(len=2)  :: cltypvar
1407    character(len=4)  :: clnomvar
1408    character(len=12) :: cletiket
1409    integer :: fstinf
1410    real(8) :: vlev(1),vlevout(1)
1411
1412    stddev(:,:,:) = 0.0d0
1413    vlev(:) = 1.0D0
1414    vlevout(:) = 1.0D0
1415
1416    ! Reading the data
1417
1418    idate(1) = -1
1419    ip2      = -1
1420    ip3      = -1
1421    
1422    cletiket = 'STDDEV3D'
1423    cltypvar=' '
1424
1425    ! Reading for 3D and 2D variables
1426    
1427    do varIndex = 1,bgStats%numvar3d+bgStats%numvar2d
1428      clnomvar = bgStats%varNameList(varIndex)
1429      nlev_MT = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1430
1431      !write(*,*) 'Reading ',clnomvar
1432
1433      do levelIndexo = 1, nlev_MT
1434        if (nlev_MT == 1) then
1435          ip1 = -1
1436        else if(vnl_varLevelFromVarName(clnomvar) == 'MM') then
1437          ip1 = vco_anl%ip1_M(levelIndexo)
1438        else
1439          ip1 = vco_anl%ip1_T(levelIndexo)
1440        end if
1441        
1442        ikey = fstinf(nulbgst,ini,inj,ink,idate(1),cletiket,ip1,ip2,ip3, &
1443	       cltypvar,clnomvar)
1444        if (ikey < 0) then
1445            write(*,*) 'bcsc_rdstd3d: ',varIndex,clnomvar,ikey,levelIndexo
1446            call utl_abort(': bcsc_RDSTD record not foun0d')          
1447        end if
1448      
1449        allocate(stddev3d(ini+1, inj, 1))
1450        stddev3d(:,:,1) = 0.0D0
1451        
1452        ikey = utl_fstlir(stddev3d(1:ini,:,1), nulbgst, ini, inj, ink, &
1453                          idate(1), cletiket, ip1, ip2, ip3, cltypvar, clnomvar)
1454        if (ikey < 0) then
1455          write(*,*) 'bcsc_rdstd3d: ',varIndex,clnomvar,nlev_MT,levelIndexo, &
1456	              ikey,ip1
1457          call utl_abort(': bcsc_RDSTD3D record not found')
1458        end if
1459        
1460        ! Move to stddev
1461        if (inj == bgStats%nj .and. ini == bgStats%ni) then
1462          stddev(1:bgStats%ni,:,bgStats%nsposit(varIndex)+(levelIndexo-1)) = &
1463	    stddev3d(1:bgStats%ni,:,1) 
1464          stddev(bgStats%ni+1,:,bgStats%nsposit(varIndex)+(levelIndexo-1)) = &
1465	    stddev3d(1,:,1)
1466        else
1467          ! Interpolate in lat and long
1468          stddev3d(ini+1,:,1) = stddev3d(1,:,1)
1469          call gsv_field3d_hbilin(stddev3d, ini+1, inj, 1, rlongr, rlatr, vlev, &
1470               stddev(:,:,bgStats%nsposit(varIndex)+(levelIndexo-1)), &
1471	       bgStats%ni+1, bgStats%nj, 1, &
1472               bgStats%lon, bgStats%lat, vlevout)
1473       end if
1474       deallocate(stddev3d)
1475
1476      end do
1477    end do
1478
1479  end subroutine bcsc_rdstd3d
1480
1481  !--------------------------------------------------------------------------
1482  ! bcsc_sucorns2
1483  !--------------------------------------------------------------------------
1484  subroutine bcsc_sucorns2
1485    implicit none
1486
1487    ! Locals:
1488    real(8) :: eigenval(bgStats%nkgdim)
1489    real(8) :: eigenvalsqrt(bgStats%nkgdim)
1490    real(8), allocatable :: eigenvec(:,:),result(:,:)
1491    integer :: jn,jk1,jk2,varIndex,ierr
1492    integer :: ilwork,info,jnum,jstart,nsize
1493    real(8) :: zwork(2*4*bgStats%nkgdim)
1494    real(8) :: ztlen,zcorr,zr,zpres1,zpres2,eigenvalmax
1495    real(8), allocatable :: corns_temp(:,:,:)
1496    logical, allocatable :: lfound_sqrt(:)
1497    
1498    ! Apply vertical localization to correlations of 3D fields.
1499    ! Currently assumes no-cross correlations for variables (block diagonal matrix)
1500  
1501    if ( bgStats%numvar3d > 0 ) then   
1502      do varIndex = 1, bgStats%numvar3d
1503        ztlen = rvloc(varIndex) ! specify length scale (in units of ln(Pressure))
1504        
1505        jstart = bgStats%nsposit(varIndex)
1506        jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1507       
1508        if(ztlen > 0.0d0) then
1509          ! calculate 5'th order function (from Gaspari and Cohn)
1510          do jk1 = 1, jnum
1511            zpres1 = log(bgStats%vlev(jk1))
1512            do jk2 = 1, jnum
1513              zpres2 = log(bgStats%vlev(jk2))
1514              zr = abs(zpres2 - zpres1)
1515              zcorr = gasparicohn(ztlen,zr)
1516              bgStats%corns(jstart-1+jk1,jstart-1+jk2,0:bgStats%ntrunc) = &
1517                bgStats%corns(jstart-1+jk1,jstart-1+jk2, &
1518	        0:bgStats%ntrunc)*zcorr  
1519            end do
1520          end do
1521        end if
1522      end do
1523    end if
1524    
1525    ! Compute total vertical correlations and its inverse 
1526    ! (currently for each block matrix).
1527    
1528    if (getPhysSpaceStats .or. trim(bcsc_mode) == 'BackgroundCheck') then
1529      call bcsc_corvertSetup
1530    end if
1531    
1532    if (trim(bcsc_mode) == 'BackgroundCheck') return
1533
1534    allocate(lfound_sqrt(bgStats%numvar3d+bgStats%numvar2d))
1535    if (ReadWrite_sqrt) then
1536      ! if desired, read precomputed sqrt of corns
1537      call readcorns(lfound_sqrt,bgStats%ntrunc,'CORNS_SQRT')
1538    else
1539      lfound_sqrt(:)=.false.
1540    end if
1541
1542    do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
1543
1544      if (any(CrossCornsVarKindCH(:) /= '')) then
1545        jstart=1
1546        jnum=bgStats%nkgdim
1547      else
1548        jstart = bgStats%nsposit(varIndex)
1549        jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1550      end if
1551      
1552      if (.not.lfound_sqrt(varIndex)) then
1553        
1554        ! compute square-root of corns for each total wavenumber
1555    
1556        allocate(corns_temp(jnum,jnum,0:bgStats%ntrunc))
1557        allocate(eigenvec(jnum,jnum),result(jnum,jnum))
1558        corns_temp(:,:,:) = 0.0d0
1559        do jn = mmpi_myid, bgStats%ntrunc, mmpi_nprocs
1560
1561          eigenvec(1:jnum,1:jnum) = bgStats%corns(jstart:jstart-1+jnum, &
1562	                            jstart:jstart-1+jnum,jn)
1563
1564          ! CALCULATE EIGENVALUES AND EIGENVECTORS.
1565          ilwork = 4*jnum*2
1566          call dsyev('V','U',jnum,eigenvec,jnum,eigenval,zwork,ilwork,info)
1567          if(info /= 0) then
1568            write(*,*) 'bcsc_sucorns2: non-zero value of info =',info, &
1569	               ' returned by dsyev for wavenumber ',jn,varIndex
1570            call utl_abort('bcsc_sucorns2')
1571          end if
1572     
1573          ! set selected number of eigenmodes to zero
1574          if(numModeZero > 0) then
1575            do jk1 = 1, numModeZero
1576              eigenval(jk1) = 0.0d0
1577            end do
1578           end if
1579
1580           eigenvalmax = maxval(eigenval(1:jnum))
1581           do jk1 = 1, jnum
1582             ! if(eigenval(jk1) < 1.0d-15) then
1583             if(eigenval(jk1) < 1.0d-8*eigenvalmax) then
1584               eigenvalsqrt(jk1) = 0.0d0
1585             else
1586               eigenvalsqrt(jk1) = sqrt(eigenval(jk1))
1587             end if
1588           end do
1589
1590           ! E * lambda^1/2
1591           do jk1 = 1, jnum
1592             result(1:jnum,jk1) = eigenvec(1:jnum,jk1)*eigenvalsqrt(jk1)
1593           end do
1594  
1595           ! (E * lambda^1/2) * E^T
1596           do jk1 = 1, jnum
1597           do jk2 = 1, jnum
1598             corns_temp(1:jnum,jk1,jn) = corns_temp(1:jnum,jk1,jn) &
1599	                               + result(1:jnum,jk2)*eigenvec(jk1,jk2)
1600           end do
1601           end do
1602
1603         end do ! jn
1604  
1605         nsize = jnum*jnum*(bgStats%ntrunc+1)
1606         call rpn_comm_allreduce(corns_temp, &
1607	   bgStats%corns(jstart:jstart+jnum-1,jstart:jstart+jnum-1,:), &
1608	   nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
1609         deallocate(corns_temp,eigenvec,result)
1610
1611      end if
1612      
1613      if (any(CrossCornsVarKindCH(:) /= '')) exit
1614
1615    end do
1616   
1617    deallocate(lfound_sqrt)
1618    if(ReadWrite_sqrt) then
1619      ! Write computed sqrt to a separate file.
1620      call writecorns(bgStats%ntrunc,'CORNS_SQRT',-1)
1621    end if
1622    
1623  end subroutine bcsc_sucorns2
1624
1625  !--------------------------------------------------------------------------
1626  ! bcsc_corvertSetup
1627  !--------------------------------------------------------------------------
1628  subroutine bcsc_corvertSetup
1629    !
1630    !:Purpose: To compute total vertical correlations (bcsc_corvert) and its
1631    !           inverse (bgStats%corverti; currently for each block matrix).
1632    !
1633    !
1634    !:Note: Currently assumes no (or neglects) cross-correlations 
1635    !
1636    implicit none
1637
1638    ! Locals:
1639    real(8) :: eigenval(bgStats%nkgdim)
1640    real(8), allocatable :: eigenvec(:,:),result(:,:)
1641    integer :: jn,jk1,jk2,varIndex,numvartot,jstart
1642    integer :: ilwork,info,jnum,nvlev,ierr
1643    real(8) :: zwork(2*4*bgStats%nkgdim)
1644    real(8) :: eigenvalmax
1645    integer iulcorvert
1646    ! Standard file variables
1647    character(len=4)  :: clnomvar
1648    character(len=12) :: cletiket
1649    integer :: fnom,fstouv,fstfrm,fclos
1650    logical, allocatable :: lfound(:)
1651   
1652    ! Compute total vertical correlations and its inverse 
1653    ! (currently for each block matrix).
1654
1655    if (mmpi_myid == 0 .and. WritePhysSpaceStats) then
1656      iulcorvert = 0
1657      ierr = fnom(iulcorvert,'bCovarSetupChem_out.fst','STD+RND',0)
1658      ierr = fstouv(iulcorvert,'RND')
1659    end if 
1660    
1661    nvlev=-1
1662    numvartot=bgStats%numvar3d
1663    do varIndex = 1, numvartot
1664   
1665      jstart = bgStats%nsposit(varIndex)
1666      jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1667       
1668      bgStats%corvert(1:jnum,1:jnum,varIndex) = 0.0d0
1669      do jn = 0, bgStats%ntrunc
1670        bgStats%corvert(1:jnum,1:jnum,varIndex) = &
1671	  bgStats%corvert(1:jnum,1:jnum,varIndex)+ ((2*jn+1)* &
1672          bgStats%corns(bgStats%nsposit(varIndex): &
1673	  bgStats%nsposit(varIndex+1)-1,&
1674	  bgStats%nsposit(varIndex):bgStats%nsposit(varIndex+1)-1,jn))
1675      end do
1676       
1677      ! Inverse (and possible vertical interpolation to model levels) 
1678      ! not needed if not in analysis mode
1679      
1680      if (trim(bcsc_mode) == 'BackgroundCheck') cycle
1681
1682      if (varIndex == 1) then
1683        allocate(lfound(numvartot))
1684        call readcorns(lfound,0,'CORVERTI') 
1685      end if
1686      
1687      if (.not.lfound(varIndex)) then
1688      
1689        write(*,*) 'bcsc_corvertSetup: Calculating CORVERTI ', &
1690	           'for varIndex =',varIndex
1691
1692        allocate(eigenvec(jnum,jnum),result(jnum,jnum))
1693        eigenvec(1:jnum,1:jnum)=bgStats%corvert(1:jnum,1:jnum,varIndex)       
1694
1695        ! CALCULATE EIGENVALUES AND EIGENVECTORS.
1696        ilwork = 4*jnum*2
1697        call dsyev('V','U',jnum,eigenvec,jnum,eigenval,zwork,ilwork,info)
1698        if (info /= 0) then
1699          write(*,*) 'bcsc_corvertSetup: non-zero value of info =',info, &
1700	             ' returned by dsyev for wavenumber ',jn
1701          call utl_abort('bcsc_corvertSetup')
1702        end if
1703
1704        ! Set selected number of eigenmodes to zero
1705        if (numModeZero > 0) then
1706          do jk1 = 1, numModeZero
1707            eigenval(jk1) = 0.0d0
1708          end do
1709          write(*,*) 'bcsc_corvertSetup: modified eigenvalues=',eigenval(:)
1710        end if
1711
1712        ! E * lambda^{-1}
1713        eigenvalmax=maxval(eigenval(1:jnum))
1714        do jk1 = 1, jnum
1715          !! if (eigenval(jk1) > 1.0d-8*eigenvalmax) then
1716          ! Threshold increased below to reduce numerical error effects
1717	  ! relevant when using corverti in applications
1718	  ! (Threshold factor should not be smaller than 1.0D-4.)
1719	  ! As consequence C*C^-1 will differ from the identity matrix
1720	  ! while being nearly diagonal.
1721          if (eigenval(jk1) > 1.0D-4*eigenvalmax) then
1722            result(1:jnum,jk1) = eigenvec(1:jnum,jk1)/eigenval(jk1)
1723          else
1724            result(1:jnum,jk1) = 0.0D0
1725          end if
1726        end do
1727
1728        ! E * lambda^{-1} * E^T
1729        bgStats%corverti(1:jnum,1:jnum,varIndex)=0.0D0
1730        do jk1 = 1, jnum
1731          do jk2 = 1, jnum
1732            bgStats%corverti(1:jnum,jk1,varIndex) = &
1733	      bgStats%corverti(1:jnum,jk1,varIndex) + result(1:jnum,jk2)* &
1734	      eigenvec(jk1,jk2)
1735          end do
1736        end do
1737
1738        ! zr=maxval(abs(bgStats%corverti(1:jnum,1:jnum,varIndex)))
1739        ! where (abs(bgStats%corverti(1:jnum,1:jnum,varIndex)) < 1.E-5*zr) &
1740        !   bgStats%corverti(1:jnum,1:jnum,varIndex)=0.0D0
1741
1742        ! Check inverse (for output when mmpi_myid is 0)
1743        result(1:jnum,1:jnum)=0.0D0
1744        do jk1 = 1, jnum
1745          do jk2 = 1, jnum
1746            result(1:jnum,jk1) = result(1:jnum,jk1) + &
1747              bgStats%corvert(1:jnum,jk2,varIndex)* &
1748	      bgStats%corverti(jk2,jk1,varIndex)
1749          end do
1750        end do
1751
1752        cletiket = 'C*C^-1'
1753        clnomvar = bgStats%varNameList(varIndex)
1754
1755        if (mmpi_myid == 0 .and. writePhysSpaceStats) then          
1756          ierr = utl_fstecr(result(1:jnum,1:jnum),-32,iulcorvert,0,0,0,jnum,jnum, &
1757                 1,0,0,bgStats%ntrunc,'X',clnomvar,cletiket,'X',0,0,0,0,5,.true.)
1758        end if
1759	 
1760        deallocate(eigenvec,result)
1761      end if
1762      
1763    end do
1764    
1765    if (mmpi_myid /= 0 .or. .not.WritePhysSpaceStats) return
1766    
1767    ierr = fstfrm(iulcorvert)  
1768    ierr = fclos(iulcorvert)
1769
1770    jnum=nvlev
1771    call writecorns(0,'CORVERT',nvlev)
1772    if ( allocated(lfound) ) then
1773      if (any(.not.lfound(1:numvartot))) call writecorns(0,'CORVERTI',-1)
1774      deallocate(lfound)
1775    end if
1776
1777  end subroutine bcsc_corvertSetup
1778
1779  !--------------------------------------------------------------------------
1780  ! writecorns
1781  !--------------------------------------------------------------------------
1782  subroutine writecorns(nmat,cletiket,nlev)
1783  
1784    implicit none
1785
1786    ! Arguments:
1787    character(len=*), intent(in) :: cletiket
1788    integer,          intent(in) :: nmat
1789    integer,          intent(in) :: nlev
1790
1791    ! Locals:
1792    integer :: jn, nulcorns,ierr,varIndex,jstart,jnum,numvartot
1793    ! standard file variables
1794    integer :: ip1,ip2,ip3
1795    integer :: idateo, ipak, idatyp
1796    character(len=4)  :: clnomvar
1797    integer :: fnom, fstouv, fstfrm, fclos
1798    
1799    if(mmpi_myid==0) then
1800
1801      write(*,*) 'WRITECORNS: ', trim(cletiket), ' being written to file ', &
1802                 'bCovaarSetupChem_out.fst for number of matrices - 1 =', nmat
1803
1804      nulcorns = 0
1805      ierr = fnom(nulcorns,'bCovarSetupChem_out.fst','STD+RND',0)
1806      ierr = fstouv(nulcorns,'RND')
1807
1808      ipak = -32
1809      idatyp = 5
1810      ip1 = 0
1811      ip2 = 0
1812      ip3 = bgStats%ntrunc
1813      idateo = 0
1814
1815      if (nmat == 0) then
1816        numvartot=bgStats%numvar3d
1817      else
1818        numvartot=bgStats%numvar3d+bgStats%numvar2d
1819      end if
1820      
1821      do varIndex = 1, numvartot
1822   
1823        if (any(CrossCornsVarKindCH(:) /= '') .and. nmat > 0) then
1824          jstart=1
1825          jnum=bgStats%nkgdim
1826          clnomvar = 'ZZ'
1827        else
1828          jstart = bgStats%nsposit(varIndex)
1829          jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex)
1830          if (nlev > 0) jnum=nlev
1831          clnomvar = bgStats%varNameList(varIndex)
1832        end if
1833        
1834        if (nmat > 0 ) then
1835          do jn = 0, nmat
1836            ip2 = jn
1837            ierr = utl_fstecr(bgStats%corns(jstart:jstart+jnum-1, &
1838	           jstart:jstart+jnum-1,jn),ipak,nulcorns,idateo,0,0,jnum,  &
1839                   jnum,1,ip1,ip2,ip3,'X',clnomvar,cletiket,'X',0,0,0,0, &
1840		   idatyp,.true.)
1841          end do
1842        else
1843          if (trim(cletiket) == 'CORVERT') then
1844            ierr = utl_fstecr(bgStats%corvert(1:jnum,1:jnum,varIndex), &
1845	           ipak,nulcorns,idateo,0,0,jnum,jnum,1,  &
1846                   ip1,ip2,ip3,'X',clnomvar,cletiket,'X',0,0,0,0,idatyp,.true.)
1847          else
1848            ierr = utl_fstecr(bgStats%corverti(1:jnum,1:jnum,varIndex), &
1849	           ipak,nulcorns,idateo,0,0,jnum,jnum,1,  &
1850                   ip1,ip2,ip3,'X',clnomvar,cletiket,'X',0,0,0,0,idatyp,.true.)
1851          end if
1852        end if
1853        
1854        if (any(CrossCornsVarKindCH(:) /= '') .and. nmat > 0) exit
1855        
1856      end do
1857      
1858      ierr = fstfrm(nulcorns)  
1859      ierr = fclos(nulcorns)
1860    end if
1861
1862  end subroutine writecorns
1863
1864  !--------------------------------------------------------------------------
1865  ! readcorns
1866  !--------------------------------------------------------------------------
1867  subroutine readcorns(lfound,nmat,cletiket)
1868
1869    implicit none
1870
1871    ! Arguments:
1872    logical,          intent(out) :: lfound(:)
1873    integer,          intent(in)  :: nmat
1874    character(len=*), intent(in)  :: cletiket
1875
1876    ! Locals:
1877    integer :: jn, icornskey,varIndex,jnum,jstart,numvartot
1878    real(8), allocatable :: zcornssrc(:,:)
1879    ! standard file variables
1880    integer :: ini,inj,ink
1881    integer :: ip1,ip2,ip3
1882    integer :: idateo
1883    character(len=2)  :: cltypvar
1884    character(len=4)  :: clnomvar
1885
1886    if (nmat == 0) then
1887      numvartot=bgStats%numvar3d
1888      if (trim(cletiket) == 'CORVERT') then
1889        bgStats%corvert(:,:,:)=0.0d0
1890      else if (trim(cletiket) == 'CORVERTI') then
1891        bgStats%corverti(:,:,:)=0.0d0
1892      end if
1893    else if (nmat == bgStats%ntrunc) then
1894      numvartot=bgStats%numvar3d+bgStats%numvar2d
1895    end if
1896    
1897    write(*,*) 'readcorns: ', trim(cletiket), &
1898               ' being searched for number of matrices -1 =',nmat
1899
1900    idateo = -1
1901    ip1 = -1
1902    ip3 = bgStats%ntrunc
1903    cltypvar = 'X'
1904
1905    lfound(:)=.false.    
1906    VARCYCLE: do varIndex = 1, numvartot
1907   
1908      if (any(CrossCornsVarKindCH(:) /= '') .and. nmat > 0) then
1909        jstart=1
1910        jnum=bgStats%nkgdim
1911        clnomvar = 'ZZ'
1912      else
1913        jstart = bgStats%nsposit(varIndex)
1914        jnum = bgStats%nsposit(varIndex+1)-bgStats%nsposit(varIndex) 
1915        clnomvar = bgStats%varNameList(varIndex)
1916      end if
1917      allocate(zcornssrc(jnum,jnum))
1918
1919      do jn = 0, nmat
1920        ip2 = jn
1921
1922        ! Looking for FST record parameters..
1923  
1924        icornskey = utl_fstlir(ZCORNSSRC,nulbgst,INI,INJ,INK,idateo,cletiket, &
1925	                       ip1,ip2,ip3,cltypvar,clnomvar)
1926
1927        if(icornskey  < 0 ) then
1928          write(*,*) 'readcorns: matrix not found in stats file for variable ', &
1929	              clnomvar
1930          deallocate(zcornssrc)
1931          cycle VARCYCLE
1932        end if
1933
1934        if (ini /= jnum .or. inj /= jnum) then
1935	  call utl_abort('readcorns: BG stat levels inconsitencies')
1936	end if
1937
1938        if (nmat > 0) then
1939          if (jn == 0) then
1940	    bgStats%corns(jstart:jstart+jnum-1,jstart:jstart+jnum-1,:)=0.0d0
1941	  end if
1942          bgStats%corns(jstart:jstart+jnum-1,jstart:jstart+jnum-1,jn)= &
1943	    zcornssrc(1:jnum,1:jnum)
1944        else if (trim(cletiket) == 'CORVERT') then
1945          bgStats%corvert(1:jnum,1:jnum,varIndex)=zcornssrc(1:jnum,1:jnum)
1946        else
1947          bgStats%corverti(1:jnum,1:jnum,varIndex)=zcornssrc(1:jnum,1:jnum)        
1948        end if
1949      end do
1950      lfound(varIndex)=.true.
1951
1952      deallocate(zcornssrc)
1953
1954      if (any(CrossCornsVarKindCH(:) /= '') .and. nmat > 0) exit
1955
1956    end do VARCYCLE
1957    
1958  end subroutine readcorns
1959
1960  !--------------------------------------------------------------------------
1961  ! gaspariCohn
1962  !--------------------------------------------------------------------------
1963  function gaspariCohn(ztlen,zr)
1964    implicit none
1965
1966    ! Arguments:
1967    real(8), intent(in) :: ztlen,zr
1968    ! Result:
1969    real(8) :: gasparicohn
1970
1971    ! Locals:
1972    real(8)  :: zlc
1973
1974    zlc = ztlen/2.0d0
1975    if(zr <= zlc) then
1976      gasparicohn = -0.250d0*(zr/zlc)**5+0.5d0*(zr/zlc)**4             &
1977                  +0.625d0*(zr/zlc)**3-(5.0d0/3.0d0)*(zr/zlc)**2+1.0d0
1978    elseif(zr <= (2.0d0*zlc)) then
1979      gasparicohn = (1.0d0/12.0d0)*(zr/zlc)**5-0.5d0*(zr/zlc)**4         &
1980                  +0.625d0*(zr/zlc)**3+(5.0d0/3.0d0)*(zr/zlc)**2       &
1981                  -5.0d0*(zr/zlc)+4.0d0-(2.0d0/3.0d0)*(zlc/zr)
1982    else
1983      gasparicohn = 0.0d0
1984    end if
1985    if(gasparicohn < 0.0d0) gasparicohn = 0.0d0
1986
1987  end function gaspariCohn
1988
1989  !--------------------------------------------------------------------------
1990  ! bcsc_finalize
1991  !--------------------------------------------------------------------------
1992  subroutine bcsc_finalize()
1993    implicit none
1994
1995    if(bgStats%initialized) then
1996      if (allocated(bgStats%nsposit))  deallocate(bgStats%nsposit)
1997      if (allocated(bgStats%vlev))     deallocate(bgStats%vlev)
1998      if (allocated(bgStats%lat))      deallocate(bgStats%lat)
1999      if (allocated(bgStats%lon))      deallocate(bgStats%lon)       
2000      if (allocated(bgStats%stddev))   deallocate(bgStats%stddev)
2001      if (allocated(bgStats%corns))    deallocate(bgStats%corns)
2002      if (allocated(bgStats%hcorrlen)) deallocate(bgStats%hcorrlen)
2003      if (allocated(bgStats%corvert))  deallocate(bgStats%corvert)
2004      if (allocated(bgStats%corverti)) deallocate(bgStats%corverti)
2005    end if
2006
2007  end subroutine bcsc_finalize
2008
2009  !--------------------------------------------------------------------------
2010  ! bcsc_getCovarCH
2011  !--------------------------------------------------------------------------
2012  subroutine bcsc_getCovarCH(bgStatsOut,transformVarKind_opt)
2013    !
2014    !:Purpose: Pass on covariances in bgStats
2015    !
2016    implicit none
2017
2018    ! Arguments:
2019    type(struct_bcsc_bgStats),  intent(out) :: bgStatsOut           ! Structure with covariance elements
2020    character(len=*), optional, intent(out) :: TransformVarKind_opt ! Name of variable transform to apply to chemistry variables
2021			 
2022    if (present(TransformVarKind_opt)) TransformVarKind_opt=TransformVarKindCH
2023
2024    if(.not.bgStats%initialized) then
2025      bgStatsOut%initialized=.false.
2026      call utl_abort('bcsc_getCovarCH: Covariances not set up.')
2027    end if
2028        
2029    bgStatsOut=bgStats
2030    
2031  end subroutine bcsc_getCovarCH
2032
2033  !--------------------------------------------------------------------------
2034  ! bcsc_resetCorvert
2035  !--------------------------------------------------------------------------
2036  subroutine bcsc_resetCorvert(nlev_T,vlev_T)
2037    !
2038    !:Purpose: Vertically interpolate error correlation matrix fields to 
2039    !           generate approximate matrices/vectors in trial field vertical
2040    !           levels via interpolation. No need to make matrix positive  
2041    !           definite for this approximation.
2042    !
2043    implicit none
2044    
2045    ! Arguments:
2046    integer,          intent(in) :: nlev_T    ! Number of vertical levels for trial fields
2047    real(8), pointer, intent(in) :: vlev_T(:) ! Trial field vertical levels
2048
2049    ! Locals:
2050    integer :: ilev1,ilev2,j,d1,d2
2051    real(8) :: dz
2052    real(8), allocatable :: wtemp(:,:,:)
2053        
2054    d2=0 
2055    d1=nlev_T-bgStats%nlev
2056    if (d1 < 0) then
2057      d1=0
2058      d2=d1
2059    end if
2060    
2061    allocate(wtemp(nlev_T, nlev_T,bgStats%numvar3d))
2062    wtemp(:,:,:)=0.0d0
2063    
2064    ! Apply interpolation
2065
2066    do ilev1 = 1, nlev_T
2067      if (bgStats%vlev(1) >= vlev_T(ilev1)) then
2068        wtemp(ilev1,1:bgStats%nlev+d2,1:bgStats%numvar3d)= &
2069	      bgStats%corvert(1,1:bgStats%nlev+d2,1:bgStats%numvar3d)
2070        wtemp(1:bgStats%nlev+d2,ilev1,1:bgStats%numvar3d)= &
2071	      bgStats%corvert(1:bgStats%nlev+d2,1,1:bgStats%numvar3d)          
2072      else if (bgStats%vlev(bgStats%nlev) <= vlev_T(ilev1)) then
2073        wtemp(ilev1,1+ilev1-bgStats%nlev-d2:ilev1,1:bgStats%numvar3d)= &
2074	      bgStats%corvert(bgStats%nlev,1-d2:bgStats%nlev, &
2075	                      1:bgStats%numvar3d)
2076        wtemp(1+ilev1-bgStats%nlev-d2:ilev1,ilev1,1:bgStats%numvar3d)= &
2077	      bgStats%corvert(1-d2:bgStats%nlev,bgStats%nlev, &
2078	                      1:bgStats%numvar3d)          
2079      else 
2080        do ilev2=1,bgStats%nlev-1
2081          if (vlev_T(ilev1) >= bgStats%vlev(ilev2) .and. &
2082              vlev_T(ilev1) < bgStats%vlev(ilev2+1)) exit
2083        end do
2084
2085        dz=log(vlev_T(ilev1)/bgStats%vlev(ilev2)) &
2086            /log(bgStats%vlev(ilev2+1)/bgStats%vlev(ilev2)) 
2087                  
2088        do j=1-max(ilev1-d1,1),nlev_T-min(d1+ilev1,nlev_T)
2089          wtemp(ilev1,ilev1+j,1:bgStats%numvar3d)= &
2090	        (bgStats%corvert(ilev2,ilev2+j,1:bgStats%numvar3d) &
2091                *(1.0-dz) + bgStats%corvert(ilev2+1,ilev2+1+j, &
2092		                            1:bgStats%numvar3d)*dz)
2093          wtemp(ilev1+j,ilev1,1:bgStats%numvar3d)= &
2094	    wtemp(ilev1,ilev1+j,1:bgStats%numvar3d)
2095        end do               
2096      end if
2097    end do
2098    
2099    bgStats%corvert(1:nlev_T,1:nlev_T,:) = wtemp(:,:,:)
2100    
2101    deallocate(wtemp)
2102    
2103  end subroutine bcsc_resetCorvert
2104
2105  !--------------------------------------------------------------------------
2106  ! bcsc_resetHcorrlen
2107  !--------------------------------------------------------------------------
2108  subroutine bcsc_resetHcorrlen(nlev_T,vlev_T)
2109    !
2110    !:Purpose: To interpolate horizontal correlation length 
2111    !           to trial field vertical levels.
2112    !
2113    implicit none
2114
2115    ! Arguments:
2116    integer, intent(in)  :: nlev_T      ! Number of target vertical levels
2117    real(8), intent(in)  :: vlev_T(:)   ! Target vertical levels
2118    
2119    ! Locals:
2120    real(8) :: hcorrlen(nlev_T,bgStats%numvar3d) ! correlation lengths
2121    integer :: nlev,ilev1,ilev2
2122    real(8) :: dz
2123
2124    nlev = bgStats%nlev
2125    
2126    ! Apply interpolation
2127
2128    do ilev1=1, nlev_T
2129      if (bgStats%vlev(1) >= vlev_T(ilev1)) then
2130        hcorrlen(ilev1,:)=bgStats%hcorrlen(1,1:bgStats%numvar3d)
2131      else if (bgStats%vlev(nlev) <= vlev_T(ilev1)) then
2132        hcorrlen(ilev1,:)=bgStats%hcorrlen(nlev,1:bgStats%numvar3d)
2133      else  
2134        do ilev2=1,nlev-1
2135          if (vlev_T(ilev1) >= bgStats%vlev(ilev2) .and. &
2136              vlev_T(ilev1) <  bgStats%vlev(ilev2+1)) exit
2137        end do
2138        dz=log(vlev_T(ilev1)/ bgStats%vlev(ilev2)) &
2139          /log( bgStats%vlev(ilev2+1)/ bgStats%vlev(ilev2))
2140        hcorrlen(ilev1,:)=bgStats%hcorrlen(ilev2,1:bgStats%numvar3d)* &
2141	                  (1.0-dz)+bgStats%hcorrlen(ilev2+1, &
2142			  1:bgStats%numvar3d)*dz
2143      end if
2144    end do
2145    
2146    bgStats%hcorrlen(1:nlev_T,1:bgStats%numvar3d) = hcorrlen(:,:)
2147    
2148  end subroutine bcsc_resetHcorrlen
2149
2150  !--------------------------------------------------------------------------
2151  ! bcsc_StatsExistForVarName
2152  !--------------------------------------------------------------------------
2153  logical function bcsc_StatsExistForVarName(varName)
2154    !
2155    !:Purpose: To check whether covariances have been made available for the
2156    !           specified variable
2157    !
2158    implicit none
2159
2160    ! Arguments:
2161    character(len=*), intent(in) :: varName
2162
2163    if (allocated(bgStats%varNameList)) then
2164      if (any(bgStats%varNameList(:) == varName)) then
2165        bcsc_StatsExistForVarName = .true.
2166      else
2167        bcsc_StatsExistForVarName = .false.
2168      end if
2169    else
2170      bcsc_StatsExistForVarName = .false.
2171    end if
2172    
2173  end function bcsc_StatsExistForVarName
2174
2175  !--------------------------------------------------------------------------
2176  ! bcsc_getBgStddev
2177  !--------------------------------------------------------------------------
2178  subroutine bcsc_getBgStddev(varName,maxsize,xlat,xlong,stddevOut,vlev_opt)
2179    !
2180    !:Purpose: To interpolate error std. dev. to obs location.
2181    !
2182    implicit none
2183
2184    ! Arguments:
2185    character(len=*),  intent(in)  :: varName ! Variable name
2186    integer,           intent(in)  :: maxsize  ! Max array size
2187    real(8),           intent(in)  :: xlat     ! Target latitude
2188    real(8),           intent(in)  :: xlong    ! Target longitude
2189    real(8),           intent(out) :: stddevOut(:) ! Error std. dev.
2190    real(8), optional, intent(in)  :: vlev_opt(:) ! Target vertical levels
2191    
2192    ! Locals:
2193    integer :: varIndex,latIndex,lonIndex,levIndex,nlev,startPosition
2194    real(8) :: work(maxsize,2),zc1,zc2,rlat1,rlat2,rlong1,rlong2,zd1,zd2
2195    real(8) :: stddev_max
2196    integer :: ilev1,ilev2
2197    real(8) :: dz
2198    integer, parameter :: itype=0
2199
2200    if (.not.bgStats%initialized) return
2201    
2202    ! Determine location and size of background error std. dev.
2203
2204    do varIndex = 1, bgStats%numvar3d+bgStats%numvar2d
2205      if (trim(varName) == trim(bgStats%varNameList(varIndex))) then
2206        if (varIndex <= bgStats%numvar3d ) then
2207          nlev = bgStats%nlev
2208        else
2209          nlev = 1
2210        end if
2211        startPosition = bgStats%nsposit(varIndex)	
2212        exit
2213      end if
2214    end do
2215    if  (varIndex > bgStats%numvar3d+bgStats%numvar2d) &
2216      call utl_abort('bcsc_getbgStddev: Variable not found')
2217    
2218    if (.not.present(vlev_opt) .and. nlev /= maxsize ) then
2219      write(*,*) 'nlev, maxsize: ',nlev,maxsize
2220      call utl_abort('bcsc_getbgStddev: Inconsistent size')
2221    end if
2222 
2223    ! Determine reference longitude index 
2224
2225    lonIndex = 2    
2226    do while (xlong > bgStats%lon(lonIndex) .and. lonIndex < bgStats%ni+1) 
2227      lonIndex = lonIndex+1
2228    end do
2229
2230    ! Set interpolation weights
2231
2232    rlong2 = bgStats%lon(lonIndex)
2233    rlong1 = bgStats%lon(lonIndex-1)
2234    
2235    zd2 = (xlong-rlong1)/(rlong2-rlong1)
2236    zd1 = 1.0-zd2
2237     
2238    ! Determine reference latitude index
2239
2240    latIndex = 2 
2241    do while (xlat > bgStats%lat(latIndex) .and. latIndex < bgStats%nj) 
2242      latIndex = latIndex+1
2243    end do
2244
2245    ! Set interpolation weights
2246
2247    rlat2 = bgStats%lat(latIndex)
2248    rlat1 = bgStats%lat(latIndex-1)
2249    
2250    zc2 = (xlat-rlat1)/(rlat2-rlat1)
2251    zc1 = 1.0-zc2
2252
2253    if (xlat <= rlat1) then
2254      zc1 = 1.0
2255      zc2 = 0.0
2256    else if (xlat >= rlat2) then
2257      zc1 = 0.0
2258      zc2 = 1.0
2259    end if
2260        
2261    ! Apply interpolation
2262
2263    if (itype == 0) then
2264    
2265      ! Interpolation of variances and take square root
2266
2267      work(1:nlev,1) = zc1*bgStats%stddev(lonIndex-1,latIndex-1, &
2268                                     startPosition:startPosition-1+nlev)**2 + &
2269                       zc2*bgStats%stddev(lonIndex-1,latIndex, &
2270		                     startPosition:startPosition-1+nlev)**2
2271    
2272      work(1:nlev,2) = zc1*bgStats%stddev(lonIndex,latIndex-1, &
2273                                     startPosition:startPosition-1+nlev)**2 + &
2274                      zc2*bgStats%stddev(lonIndex,latIndex, &
2275                                    startPosition:startPosition-1+nlev)**2 
2276       
2277      work(1:nlev,1) = zd1*work(1:nlev,1) + zd2*work(1:nlev,2)
2278      
2279      if (nlev /= maxsize) then
2280        do ilev1=1, maxsize
2281          if (bgStats%vlev(1) >= vlev_opt(ilev1)) then
2282            stddevOut(ilev1)=sqrt(work(1,1))
2283          else if (bgStats%vlev(nlev) <= vlev_opt(ilev1)) then
2284            stddevOut(ilev1)=sqrt(work(nlev,1))
2285          else  
2286            do ilev2=1,nlev-1
2287              if (vlev_opt(ilev1) >= bgStats%vlev(ilev2) .and. &
2288                  vlev_opt(ilev1) <  bgStats%vlev(ilev2+1)) exit
2289            end do
2290            dz=log(vlev_opt(ilev1)/ bgStats%vlev(ilev2)) &
2291               /log( bgStats%vlev(ilev2+1)/ bgStats%vlev(ilev2))
2292            stddevOut(ilev1)=sqrt(work(ilev2,1)*(1.0-dz)+work(ilev2+1,1)*dz)
2293          end if
2294        end do
2295      else
2296        stddevOut(1:nlev) = sqrt(work(1:nlev,1))
2297      end if 
2298    
2299    else 
2300
2301      ! Interpolation of std. dev. (to reduce execution time)
2302
2303      work(1:nlev,1) = zc1*bgStats%stddev(lonIndex-1,latIndex-1, &
2304                                     startPosition:startPosition-1+nlev)**2 + &      
2305                       zc2*bgStats%stddev(lonIndex-1,latIndex, &
2306                                     startPosition:startPosition-1+nlev)**2 
2307
2308      work(1:nlev,2) = zc1*bgStats%stddev(lonIndex,latIndex-1, &
2309                                     startPosition:startPosition-1+nlev)**2 + &
2310                       zc2*bgStats%stddev(lonIndex,latIndex, &
2311                                     startPosition:startPosition-1+nlev)**2
2312
2313      work(1:nlev,1) = zd1*work(1:nlev,1) + zd2*work(1:nlev,2)
2314    
2315      if (nlev /= maxsize) then
2316        do ilev1=1, maxsize
2317          if (bgStats%vlev(1) >= vlev_opt(ilev1)) then
2318            stddevOut(ilev1)=work(1,1)
2319          else if (bgStats%vlev(nlev) <= vlev_opt(ilev1)) then
2320            stddevOut(ilev1)=work(nlev,1)
2321          else  
2322            do ilev2=1,nlev-1
2323              if (vlev_opt(ilev1) >= bgStats%vlev(ilev2) .and. &
2324                  vlev_opt(ilev1) < bgStats%vlev(ilev2+1)) exit
2325            end do
2326            dz=log(vlev_opt(ilev1)/bgStats%vlev(ilev2)) &
2327               /log(bgStats%vlev(ilev2+1)/bgStats%vlev(ilev2))
2328            stddevOut(ilev1)=work(ilev2,1)*(1.0-dz)+work(ilev2+1,1)*dz
2329          end if
2330        end do
2331      else
2332        stddevOut(1:nlev) = work(1:nlev,1)
2333      end if 
2334      
2335    end if
2336    
2337    stddev_max = maxval(bgStats%stddev(lonIndex-1:lonIndex, &
2338                        latIndex-1:latIndex,  &                 
2339			startPosition:startPosition-1+nlev)) 
2340    do levIndex = 1, nlev
2341      if (stddevOut(levIndex) < 0.0 .or. stddevOut(levIndex) > 1.1*stddev_max) then
2342        write(*,*) 'bcsc_getbgStddev: Interpolated std dev incorrect:'
2343        write(*,*) 'bcsc_getbgStddev:   zc1,zc2,zd1,zd2 = ',zc1,zc2,zd1,zd2
2344        write(*,*) 'bcsc_getbgStddev:   stddevOut,stddev_max = ', &
2345	           stddevOut(levIndex),stddev_max
2346        write(*,*) 'bcsc_getbgStddev:   lonIndex,latIndex,j,xlong,xlat = ', &
2347	           lonIndex,latIndex,levIndex,xlong,xlat
2348        write(*,*) 'bcsc_getbgStddev:   rlong2,rlong1,rlat1,rlat2 = ', &
2349	           rlong2,rlong1,rlat1,rlat2
2350        call utl_abort('bcsc_getbgStddev')
2351      end if
2352    end do
2353
2354  end subroutine bcsc_getBgStddev
2355
2356  !--------------------------------------------------------------------------
2357  ! bcsc_addBgStddev
2358  !--------------------------------------------------------------------------
2359  subroutine bcsc_addBgStddev(headerIndex,stddevIn,obsdataMaxsize)
2360    !
2361    !:Purpose: To add background stddev profiles (and inverse) to bgStddev
2362    !          which can be retrieved later using a header index.
2363    !
2364    implicit none 
2365
2366    ! Arguments:
2367    integer, intent(in) :: headerIndex
2368    real(8), intent(in) :: stddevIn(:,:)
2369    integer, intent(in) :: obsdataMaxsize
2370     
2371    if (.not.associated(bgStddev%data2d)) then
2372      call oss_obsdata_alloc(bgStddev, obsdataMaxsize, dim1= &
2373           size(stddevIn,dim=1), dim2_opt=size(stddevIn,dim=2))
2374      bgStddev%nrep = 0
2375    end if
2376
2377    ! In this case nrep will count the number of filled reps in the data arrays
2378    bgStddev%nrep = bgStddev%nrep+1 
2379
2380    if (bgStddev%nrep > obsdataMaxsize) then
2381      call utl_abort('bcsc_addbgStddev: Reached max size of array ' // &
2382	             trim(utl_str(obsdataMaxsize)) )
2383    end if
2384    
2385    ! Use the header number as the unique code for this obs data
2386    write(bgStddev%code(bgStddev%nrep),'(I22)') headerIndex
2387
2388    bgStddev%data2d(:,1,bgStddev%nrep) = stddevIn(:,1)
2389
2390    where (stddevIn(:,1) > 0.0D0)
2391      bgStddev%data2d(:,2,bgStddev%nrep) = 1.0D0/stddevIn(:,1)
2392    elsewhere
2393      bgStddev%data2d(:,2,bgStddev%nrep) = 0.0D0
2394    end where
2395
2396  end subroutine bcsc_addBgStddev
2397
2398  !--------------------------------------------------------------------------
2399  ! bcsc_retrievebgStddev
2400  !--------------------------------------------------------------------------
2401  function bcsc_retrieveBgStddev(dim1,dim2,headerIndex) result(stddevOut)
2402    !
2403    !:Purpose: To retrieve previously saved background stddev profiles
2404    !           in bgStddev from the header index of the observation.
2405    !
2406    implicit none
2407
2408    ! Arguments:
2409    integer, intent(in) :: dim1  ! Dimensions of output array
2410    integer, intent(in) :: dim2  ! Dimensions of output array
2411    integer, intent(in) :: headerIndex ! Index of observation
2412    ! Result:
2413    real(8) :: stddevOut(dim1,dim2)
2414
2415    ! Locals:
2416    character(len=22) :: code
2417    
2418    if (bgStddev%dim1 /= dim1 .or. bgStddev%dim2 /= dim2) then
2419      call utl_abort("bcsc_retrievebgStddev: Inconsitent dimensions")
2420    end if
2421
2422    write(code,'(I22)') headerIndex
2423	  
2424    stddevOut = oss_obsdata_get_array2d(bgStddev,code)
2425    
2426  end function bcsc_retrieveBgStddev
2427
2428end module bCovarSetupChem_mod