fsoi_mod sourceΒΆ

   1module fsoi_mod
   2  ! MODULE fsoi_mod (prefix='fso' category='1. High-level functionality')
   3  !
   4  !:Purpose: Observation impact (FSOI) library.
   5  !
   6  use midasMpi_mod
   7  use codePrecision_mod
   8  use horizontalCoord_mod
   9  use verticalCoord_mod
  10  use obsSpaceData_mod
  11  use gridStateVector_mod
  12  use gridStateVectorFileIO_mod
  13  use columnData_mod
  14  use controlVector_mod
  15  use bmatrix_mod
  16  use bmatrixensemble_mod
  17  use stateToColumn_mod
  18  use obsOperators_mod
  19  use rMatrix_mod
  20  use MathPhysConstants_mod
  21  use quasinewton_mod
  22  use costFunction_mod
  23  use tovsNL_mod
  24  use timeCoord_mod
  25  use utilities_mod
  26  use calcHeightAndPressure_mod
  27  use rttov_const, only: inst_name, platform_name
  28  implicit none
  29  save
  30  private
  31
  32  ! public subroutines and functions
  33  public :: fso_setup, fso_ensemble
  34
  35  ! module private variables
  36  type(struct_obs),        pointer :: obsSpaceData_ptr
  37  type(struct_columnData), pointer :: columnTrlOnAnlIncLev_ptr
  38  type(struct_columnData), pointer :: column_ptr
  39  real(8),allocatable :: vhat(:)
  40  integer,external    :: get_max_rss
  41  integer             :: fso_nsim, nvadim_mpilocal
  42
  43  type(struct_hco), pointer :: hco_anl => null()
  44
  45  ! namelist variables
  46  integer             :: nvamaj        ! number of vector pairs to store in memory for Hessian approximation
  47  integer             :: nitermax      ! maximum number of minimization iterations
  48  integer             :: nsimmax       ! maximum number of cost function evaluations during minimization
  49  real(8)             :: leadTime      ! lead time of forecast (in hours)
  50  real(8)             :: repsg         ! relative gradient amplitude used as stopping criteria
  51  real(8)             :: rdf1fac       ! factor applied to initial cost function value to approximate final value
  52  real(8)             :: latMinNorm    ! minimum latitude for area included in forecast error norm (in degrees)
  53  real(8)             :: latMaxNorm    ! maximum latitude for area included in forecast error norm (in degrees)
  54  real(8)             :: lonMinNorm    ! minimum longitude for area included in forecast error norm (in degrees)
  55  real(8)             :: lonMaxNorm    ! maximum longitude for area included in forecast error norm (in degrees)
  56  logical             :: includeUVnorm ! choose to include winds in forecast error norm
  57  logical             :: includeTTnorm ! choose to include temperature in forecast error norm
  58  logical             :: includeP0norm ! choose to include surface pressure in forecast error norm
  59  logical             :: includeHUnorm ! choose to include humidity in forecast error norm
  60  logical             :: includeTGnorm ! choose to include surface skin temperature in forecast error norm
  61  character(len=256)  :: forecastPath  ! relative path where forecast files are stored
  62  character(len=4)    :: fsoMode       ! type of FSOI algorithm: can be 'HFSO' or 'EFSO'
  63  logical             :: StratoNorm    ! choose for forecast error norm from 100hPa to 1hPa,default from surface to 100hPa 
  64
  65  contains
  66
  67  !--------------------------------------------------------------------------
  68  ! fso_setup
  69  !--------------------------------------------------------------------------
  70  subroutine fso_setup(hco_anl_in)
  71    !
  72    !:Purpose: Initialise the FSOI module: read the namelist and initialise
  73    !           global variables and structure
  74    !
  75    implicit none
  76
  77    ! Arguments:
  78    type(struct_hco), pointer, intent(in) :: hco_anl_in
  79
  80    ! Locals:
  81    integer :: ierr,nulnam
  82    integer :: fnom,fclos
  83
  84    NAMELIST /NAMFSO/leadTime, nvamaj, nitermax, nsimmax
  85    NAMELIST /NAMFSO/repsg, rdf1fac, forecastPath, fsoMode
  86    NAMELIST /NAMFSO/latMinNorm, latMaxNorm, lonMinNorm, lonMaxNorm
  87    NAMELIST /NAMFSO/includeUVnorm, includeTTnorm, includeP0norm, includeHUnorm, includeTGnorm
  88    NAMELIST /NAMFSO/StratoNorm
  89
  90    ! set default values for namelist variables
  91    leadtime = 12.0d0
  92    nvamaj = 6
  93    nitermax = 100
  94    nsimmax  = 120
  95    repsg    = 1d-5
  96    rdf1fac  = 0.25d0
  97    latMinNorm = -95.0d0
  98    latMaxNorm = 95.0d0
  99    lonMinNorm = -185.0d0
 100    lonMaxNorm = 365.0d0
 101    includeUVnorm=.true.
 102    includeTTnorm=.true.
 103    includeP0norm=.true.
 104    includeHUnorm=.false.
 105    includeTGnorm=.false.
 106    forecastPath = './forecasts'
 107    fsoMode  = 'HFSO'
 108    StratoNorm = .false.
 109
 110    ! read in the namelist NAMFSO
 111    nulnam = 0
 112    ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
 113    read(nulnam,nml=namfso,iostat=ierr)
 114    if(ierr /= 0) call utl_abort('fso_setup: Error reading namelist')
 115    write(*,nml=namfso)
 116    ierr = fclos(nulnam)
 117
 118    if (trim(fsoMode) /= 'HFSO' .and. trim(fsoMode) /= 'EFSO') then
 119      call utl_abort('fso_setup: Invalid value of fsoMode. Must be HFSO or EFSO')
 120    end if
 121
 122    ! convert Latmin,max and Lonmin,max from degres into RAD
 123    latMinNorm = latMinNorm*MPC_RADIANS_PER_DEGREE_R8
 124    latMaxNorm = latMaxNorm*MPC_RADIANS_PER_DEGREE_R8
 125    lonMinNorm = lonMinNorm*MPC_RADIANS_PER_DEGREE_R8
 126    lonMaxNorm = lonMaxNorm*MPC_RADIANS_PER_DEGREE_R8
 127
 128    call ben_setFsoLeadTime(leadTime)
 129    fso_nsim = 0
 130
 131    hco_anl => hco_anl_in
 132
 133  end subroutine fso_setup
 134
 135  !--------------------------------------------------------------------------
 136  ! fso_ensemble
 137  !--------------------------------------------------------------------------
 138  subroutine fso_ensemble(columnTrlOnAnlIncLev,obsSpaceData)
 139    !
 140    !:Purpose: Perform forecast sensitivity to observation calculation using
 141    !           ensemble approach
 142    !
 143    implicit none
 144
 145    ! Arguments:
 146    type(struct_columnData), target, intent(inout)  :: columnTrlOnAnlIncLev
 147    type(struct_obs),        target, intent(inout)  :: obsSpaceData
 148
 149    ! Locals:
 150    type(struct_columnData),target  :: column
 151    type(struct_gsv)                :: statevector_FcstErr, statevector_fso, statevector_HUreference
 152    type(struct_vco), pointer       :: vco_anl
 153    real(8),allocatable             :: ahat(:), zhat(:)
 154    integer                         :: dateStamp_fcst, dateStamp
 155    integer                         :: headerIndex, bodyIndexBeg, bodyIndexEnd, bodyIndex
 156    real(8)                         :: fso_ori, fso_fin
 157
 158    if (mmpi_myid == 0) write(*,*) 'fso_ensemble: starting'
 159
 160    vco_anl => col_getVco(columnTrlOnAnlIncLev)
 161
 162    nvadim_mpilocal = cvm_nvadim
 163
 164    ! initialize column object for storing "increment"
 165    call col_setVco(column,col_getVco(columnTrlOnAnlIncLev))
 166    call col_allocate(column,col_getNumCol(columnTrlOnAnlIncLev))
 167
 168    ! compute dateStamp_fcst
 169    call incdatr(dateStamp_fcst, tim_getDatestamp(), leadTime)
 170    dateStamp = tim_getDatestamp()
 171    write(*,*) 'fso_ensemble: analysis datestamp = ',dateStamp
 172    write(*,*) 'fso_ensemble: forecast datestamp = ',dateStamp_fcst
 173
 174    ! allocate control vector related arrays (these are all mpilocal)
 175    allocate(ahat(nvadim_mpilocal))
 176    allocate(vhat(nvadim_mpilocal))
 177    allocate(zhat(nvadim_mpilocal))
 178
 179    ! initialize control vector related arrays to zero
 180    ahat(:) = 0.0d0
 181    vhat(:) = 0.0d0
 182
 183    ! for statevector_FcstErr
 184    call gsv_allocate(statevector_FcstErr, 1, hco_anl, vco_anl, &
 185                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 186                      allocHeight_opt=.false., allocPressure_opt=.false.)
 187
 188    ! for statevector_fso
 189    call gsv_allocate(statevector_fso, tim_nstepobsinc, hco_anl, vco_anl, &
 190                      dataKind_opt=pre_incrReal, &
 191                      datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true.)
 192
 193    ! for statevector_HUreference (verifying analysis)
 194    call gsv_allocate(statevector_HUreference, 1, hco_anl, vco_anl, &
 195                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 196                      allocHeight_opt=.false., allocPressure_opt=.false.)
 197
 198    ! compute forecast error = C * (error_t^fa + error_t^fb)
 199    call calcFcstError(columnTrlOnAnlIncLev,statevector_FcstErr,statevector_HUreference)
 200
 201
 202    ! compute vhat = B_t^T/2 * C * (error_t^fa + error_t^fb)
 203    call bmat_sqrtBT(vhat, nvadim_mpilocal, statevector_FcstErr, useFSOFcst_opt = .true., &
 204    stateVectorRef_opt=statevector_HUreference)
 205
 206    if (mmpi_myid == 0) write(*,*) 'fso: B_t^T/2 * C * (error_t^fa + error_t^fb) max,min:', &
 207        maxval(vhat),minval(vhat)
 208
 209    if( trim(fsoMode) == 'HFSO' ) then
 210      call minimize(nvadim_mpilocal, zhat, column, columnTrlOnAnlIncLev, obsSpaceData)
 211      ahat = zhat + vhat
 212      call bmat_sqrtB(ahat, nvadim_mpilocal, statevector_fso)
 213    elseif( trim(fsoMode) == 'EFSO' ) then
 214      call bmat_sqrtB(vhat, nvadim_mpilocal, statevector_fso)
 215    end if
 216
 217    ! Compute yhat = [R^-1 H B^1/2 ahat], and put in OBS_FSO
 218    call s2c_tl(statevector_fso,column,columnTrlOnAnlIncLev,obsSpaceData)  ! put in column H_horiz B^1/2 ahat
 219    call oop_Htl(column,columnTrlOnAnlIncLev,obsSpaceData,1)          ! Save as OBS_WORK: H_vert H_horiz B^1/2 vhat = H B^1/2 ahat
 220    call rmat_RsqrtInverseAllObs(obsSpaceData,OBS_FSO,OBS_WORK) ! Save as OBS_FSO : R**-1/2 H B^1/2 ahat
 221    call rmat_RsqrtInverseAllObs(obsSpaceData,OBS_FSO,OBS_FSO)  ! Save as OBS_FSO : R**-1 H B^1/2 ahat\
 222
 223    do headerIndex = 1, obs_numHeader(obsSpaceData)
 224
 225      bodyIndexBeg = obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
 226      bodyIndexEnd = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex) + bodyIndexBeg - 1
 227
 228      do bodyIndex = bodyIndexBeg, bodyIndexEnd
 229        if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated ) then
 230          fso_ori = obs_bodyElem_r(obsSpaceData,OBS_FSO,bodyIndex)
 231          fso_fin = fso_ori * obs_bodyElem_r(obsSpaceData,OBS_OMP,bodyIndex)
 232          call obs_bodySet_r(obsSpaceData,OBS_FSO,bodyIndex, fso_fin)
 233        end if
 234      end do
 235
 236    end do
 237
 238    ! print out the information of total FSO for each family
 239    call sumFSO(obsSpaceData)
 240
 241    ! deallocate the control vector related arrays
 242    deallocate(ahat)
 243    deallocate(vhat)
 244    deallocate(zhat)
 245    call col_deallocate(column)
 246
 247    if (mmpi_myid == 0) write(*,*) 'fso_ensemble: Finished'
 248
 249  end subroutine fso_ensemble
 250
 251  !--------------------------------------------------------------------------
 252  ! calcFcstError
 253  !--------------------------------------------------------------------------
 254  subroutine calcFcstError(columnTrlOnAnlIncLev,statevector_out,statevector_verifAnalysis)
 255    !
 256    !:Purpose: Reads the forecast from background and analysis, the verifying
 257    !           analysis based on these inputs, calculates the Forecast error
 258    !
 259    implicit none
 260
 261    ! Arguments:
 262    type(struct_columnData), target, intent(in)    :: columnTrlOnAnlIncLev
 263    type(struct_gsv)       , target, intent(inout) :: statevector_out
 264    type(struct_gsv)       , target, intent(inout) :: statevector_verifAnalysis
 265
 266    ! Locals:
 267    type(struct_gsv)                :: statevector_fa, statevector_fb, statevector_a
 268    character(len=256)              :: fileName_fa, fileName_fb, fileName_a
 269    logical                         :: faExists
 270    type(struct_gsv)                :: statevector_tempfa, statevector_tempfb
 271    type(struct_vco), pointer       :: vco_anl
 272    integer                         :: dateStamp_fcst, dateStamp
 273
 274    vco_anl => col_getVco(columnTrlOnAnlIncLev)
 275
 276    ! compute dateStamp_fcst
 277    call incdatr(dateStamp_fcst, tim_getDatestamp(), leadTime)
 278    dateStamp = tim_getDatestamp()
 279    write(*,*) 'fso_ensemble: analysis datestamp = ',dateStamp
 280    write(*,*) 'fso_ensemble: forecast datestamp = ',dateStamp_fcst
 281
 282    ! read forecasts from the analysis and background state
 283    fileName_fa = trim(forecastPath) // '/forecast_a'
 284    inquire(file=trim(fileName_fa),exist=faExists)
 285    write(*,*) 'faExists', faExists
 286    call gsv_allocate(statevector_fa, 1, hco_anl, vco_anl, &
 287                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 288                      hInterpolateDegree_opt='LINEAR', &
 289                      allocHeight_opt=.false., allocPressure_opt=.false.)
 290    call gio_readFromFile(statevector_fa, fileName_fa, ' ', 'P', containsFullField_opt=.true.)
 291
 292    !for statevecotr_tempfa
 293    call gsv_allocate(statevector_tempfa, 1, hco_anl, vco_anl, &
 294                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 295                      allocHeight_opt=.false., allocPressure_opt=.false.)
 296
 297    !for statevector_fb
 298    fileName_fb = trim(forecastPath) // '/forecast_b'
 299    call gsv_allocate(statevector_fb, 1, hco_anl, vco_anl, &
 300                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 301                      hInterpolateDegree_opt='LINEAR', &
 302                      allocHeight_opt=.false., allocPressure_opt=.false.)
 303    call gio_readFromFile(statevector_fb, fileName_fb, ' ', 'P', containsFullField_opt=.true.)
 304
 305    !for statevecotr_tempfb
 306    call gsv_allocate(statevector_tempfb, 1, hco_anl, vco_anl, &
 307                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 308                      allocHeight_opt=.false., allocPressure_opt=.false.)
 309
 310    ! read verifying analysis
 311    fileName_a = trim(forecastPath) // '/analysis'
 312    call gsv_allocate(statevector_a, 1,hco_anl, vco_anl, &
 313                      datestamp_opt=datestamp_fcst, mpi_local_opt=.true., &
 314                      hInterpolateDegree_opt='LINEAR', &
 315                      allocHeight_opt=.false., allocPressure_opt=.false.)
 316    call gio_readFromFile(statevector_a, fileName_a, ' ', 'A', containsFullField_opt=.true.)
 317
 318    ! compute error of both forecasts (overwrite forecasts with error)
 319    call gsv_add(statevector_a, statevector_fa, -1.0d0)
 320    call gsv_add(statevector_a, statevector_fb, -1.0d0)
 321
 322    call gsv_copy(statevector_fa,statevector_tempfa)
 323    call gsv_copy(statevector_fb,statevector_tempfb)
 324    call multEnergyNorm(statevector_tempfa, statevector_a,  &
 325                            latMinNorm, latMaxNorm, lonMinNorm, lonMaxNorm, &
 326                            includeUVnorm, includeTTnorm, includeP0norm,  &
 327                            includeHUnorm, includeTGnorm, StratoNorm) ! use analysis as reference state
 328    call multEnergyNorm(statevector_tempfb, statevector_a,  &
 329                            latMinNorm, latMaxNorm, lonMinNorm, lonMaxNorm, &
 330                            includeUVnorm, includeTTnorm, includeP0norm,  &
 331                            includeHUnorm, includeTGnorm, StratoNorm) ! use analysis as reference state
 332
 333    ! compute error Norm =  C * (error_t^fa + error_t^fb)
 334    call gsv_add(statevector_fa, statevector_fb, 1.0d0)
 335    call multEnergyNorm(statevector_fb, statevector_a,  &
 336                            latMinNorm, latMaxNorm, lonMinNorm, lonMaxNorm, &
 337                            includeUVnorm, includeTTnorm, includeP0norm,  &
 338                            includeHUnorm, includeTGnorm, StratoNorm) ! use analysis as reference state
 339    call gsv_copy(statevector_fb,statevector_out)
 340    call gsv_copy(statevector_a,statevector_verifAnalysis)
 341
 342  end subroutine calcFcstError
 343
 344  !--------------------------------------------------------------------------
 345  ! minimize
 346  !--------------------------------------------------------------------------
 347  subroutine minimize(nvadim,zhat,column,columnTrlOnAnlIncLev,obsSpaceData)
 348    !
 349    !:Purpose: Performs HFSO quasi-Newton minimization
 350    !
 351    implicit none
 352
 353    ! Arguments:
 354    integer,                         intent(in)  :: nvadim
 355    real(8), dimension(nvadim),      intent(out) :: zhat
 356    type(struct_columnData), target, intent(in)  :: column
 357    type(struct_columnData), target, intent(in)  :: columnTrlOnAnlIncLev
 358    type(struct_obs),        target, intent(in)  :: obsSpaceData
 359
 360    ! Locals:
 361    integer                         :: nulout = 6
 362    integer                         :: imode, itermax, isimmax, indic, nmtra
 363    integer                         :: impres, iztrl(10), intunused(1)
 364    real                            :: rspunused(1)
 365    real(8)                         :: zjsp, zxmin, zdf1, zeps, dlgnorm, dlxnorm,zspunused(1)
 366    real(8),allocatable             :: gradJ(:), vatra(:)
 367
 368    call utl_tmg_start(90,'--Minimization')
 369
 370    if (mmpi_myid == 0) write(*,*) 'minimize: starting'
 371
 372    nmtra = (4 + 2*nvamaj)*nvadim
 373    write(*,'(4X,"NVAMAJ = ",I3,/5X,"NMTRA =",I14)') nvamaj,nmtra
 374
 375    columnTrlOnAnlIncLev_ptr => columnTrlOnAnlIncLev
 376    column_ptr  => column
 377    obsSpaceData_ptr => obsSpaceData
 378
 379    allocate(gradJ(nvadim))
 380    allocate(vatra(nmtra))
 381
 382    gradJ(:) = 0.0d0
 383    vatra(:) = 0.0d0
 384    zhat(:)  = 0.0d0
 385
 386    ! Compute zhat by performing variational minimization
 387    ! Set-up for the minimization
 388    if (mmpi_myid == 0) then
 389      impres = 5
 390    else
 391      impres = 0
 392    end if
 393
 394    imode = 0
 395    zeps = repsg
 396    itermax = nitermax
 397    isimmax = nsimmax
 398    zxmin = epsilon(zxmin)
 399    ! initial gradient calculation
 400    indic = 2
 401    call utl_tmg_start(91,'----QuasiNewton')
 402    call simvar(indic,nvadim,zhat,zjsp,gradJ)
 403    call utl_tmg_stop(91)
 404    zdf1 =  rdf1fac * abs(zjsp)
 405
 406    ! print amplitude of initial gradient and cost function value
 407    call prscal(nvadim,gradJ,gradJ,dlgnorm)
 408    dlgnorm = dsqrt(dlgnorm)
 409    call prscal(nvadim,zhat,zhat,dlxnorm)
 410    dlxnorm = dsqrt(dlxnorm)
 411    write(*,*)' |X| = ', dlxnorm
 412    write(*,'(/4X,"J(X) = ",G23.16,4X,"|Grad J(X)| = ",G23.16)') zjsp, dlgnorm
 413    write(*,'(//,10X," Minimization QNA_N1QN3 starts ...",/  &
 414             10x,"DXMIN =",G23.16,2X,"DF1 =",G23.16,2X,"EPSG =",G23.16  &
 415             /,10X,"IMPRES =",I3,2X,"NITER = ",I3,2X,"NSIM = ",I3)') zxmin,zdf1,zeps,impres,itermax,isimmax
 416
 417    ! Do the minimization
 418    call utl_tmg_start(91,'----QuasiNewton')
 419    call qna_n1qn3(simvar, prscal, dcanonb, dcanab, nvadim, zhat,  &
 420                   zjsp, gradJ, zxmin, zdf1, zeps, impres, nulout, imode,   &
 421                   itermax,isimmax, iztrl, vatra, nmtra, intunused, rspunused,  &
 422                   zspunused)
 423    call utl_tmg_stop(91)
 424    call fool_optimizer(obsSpaceData)
 425
 426    write(*,'(//,20X,20("*"),2X    &
 427        ,/,20X,"              Minimization ended with MODE:",I4  &
 428        ,/,20X,"                Total number of iterations:",I4  &
 429        ,/,20X,"               Total number of simulations:",I4)' ) imode,itermax,isimmax
 430
 431    if (mmpi_myid == 0) write(*,*) 'minimize: Finished'
 432
 433    deallocate(vatra)
 434    deallocate(gradJ)
 435
 436    call utl_tmg_stop(90)
 437
 438  end subroutine minimize
 439
 440  !--------------------------------------------------------------------------
 441  ! sumFSO
 442  !--------------------------------------------------------------------------
 443  subroutine sumFSO(obsSpaceData)
 444    !
 445    !:Purpose: Print out the information of total FSO for each family
 446    !
 447    implicit none
 448
 449    ! Arguments:
 450    type(struct_obs), intent(in)  :: obsSpaceData
 451
 452    ! Locals:
 453    real(8)            :: pfso_1
 454    integer            :: bodyIndex,itvs,isens,headerIndex
 455    integer            :: bodyIndexBeg, bodyIndexEnd
 456    integer, parameter :: numFamily = 10
 457    character(len=2), parameter :: familyList(numFamily) = (/'UA','AI','SF','SC','TO','SW','PR','RO','GP','CH'/)
 458    real(8)            :: tfso(numFamily), tfsotov_sensors(tvs_nsensors),totFSO
 459    integer            :: numAss_local(numFamily), numAss_global(numFamily)
 460    integer            :: numAss_sensors_loc(tvs_nsensors), numAss_sensors_glb(tvs_nsensors)
 461    integer            :: ierr, familyIndex
 462
 463    if (mmpi_myid == 0) write(*,*) 'sumFSO: Starting'
 464
 465    ! initialize
 466    do familyIndex = 1, numFamily
 467      tfso(familyIndex) = 0.d0
 468      numAss_local(familyIndex) = 0
 469      numAss_global(familyIndex) = 0
 470    end do
 471
 472    tfsotov_sensors(:) = 0.d0
 473    numAss_sensors_loc(:) = 0
 474    numAss_sensors_glb(:) = 0
 475    totFSO = 0.d0
 476
 477    do familyIndex = 1, numFamily
 478      do bodyIndex = 1, obs_numbody(obsSpaceData)
 479
 480        pfso_1 = obs_bodyElem_r(obsSpaceData,OBS_FSO,bodyIndex)
 481        if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == 1 ) then
 482          ! FSO for each family
 483          if (obs_getFamily(obsSpaceData,bodyIndex_opt=bodyIndex) == familyList(familyIndex) ) then
 484            tfso(familyIndex) = tfso(familyIndex) + pfso_1
 485            numAss_local(familyIndex) = numAss_local(familyIndex) + 1
 486          end if
 487        end if
 488
 489      end do
 490    end do
 491
 492    do itvs = 1, tvs_nobtov
 493      headerIndex  = tvs_headerIndex(itvs)
 494      if (headerIndex > 0 ) then
 495        bodyIndexBeg = obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
 496        bodyIndexEnd = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex) + bodyIndexBeg - 1
 497        do bodyIndex = bodyIndexBeg, bodyIndexEnd
 498          pfso_1 = obs_bodyElem_r(obsSpaceData,OBS_FSO,bodyIndex)
 499          if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == 1 ) then
 500            isens = tvs_lsensor (itvs)
 501            tfsotov_sensors(isens) =  tfsotov_sensors(isens) + pfso_1
 502            numAss_sensors_loc(isens) = numAss_sensors_loc(isens) + 1
 503          end if
 504        end do
 505      end if
 506    end do
 507
 508    do familyIndex = 1, numFamily
 509      call mmpi_allreduce_sumreal8scalar(tfso(familyIndex),'GRID')
 510      totFSO = totFSO + tfso(familyIndex)
 511      call rpn_comm_allreduce(numAss_local(familyIndex), numAss_global(familyIndex) ,1,'MPI_INTEGER','MPI_SUM','GRID',ierr)
 512    end do
 513
 514    do isens = 1, tvs_nsensors
 515      call mmpi_allreduce_sumreal8scalar(tfsotov_sensors(isens),'GRID')
 516      call rpn_comm_allreduce(numAss_sensors_loc(isens), numAss_sensors_glb(isens) ,1,'MPI_INTEGER','MPI_SUM','GRID',ierr)
 517    end do
 518
 519    if (mmpi_myid == 0) then
 520
 521      write(*,*) ' '
 522      write(*,'(a15,f15.8)') 'Total FSO=', totFSO
 523      write(*,*) ' '
 524
 525      do familyIndex = 1, numFamily
 526        write(*,'(a4,a2,a2,f15.8,a16,i10)') 'FSO-', familyList(familyIndex), '=', tfso(familyIndex),'  Count Number=', numAss_global(familyIndex)
 527      end do
 528      write(*,*) ' '
 529
 530      if (tvs_nsensors > 0) then
 531        write(*,'(1x,a)') 'For TOVS decomposition by sensor:'
 532        write(*,'(1x,a)') '#  plt sat ins    FSO'
 533        do isens = 1, tvs_nsensors
 534          write(*,'(i2,1x,a,1x,a,1x,i2,1x,f15.8,i10)') isens,inst_name(tvs_instruments(isens)), &
 535                platform_name(tvs_platforms(isens)),tvs_satellites(isens),tfsotov_sensors(isens), numAss_sensors_glb(isens)
 536        end do
 537        write(*,*) ' '
 538      end if
 539
 540    end if
 541
 542  end subroutine sumFSO
 543
 544  !--------------------------------------------------------------------------
 545  ! simvar
 546  !--------------------------------------------------------------------------
 547  subroutine simvar(indic,nvadim,zhat,Jtotal,gradJ)
 548    !
 549    !:Purpose: Implement the Variational solver as described in
 550    !           Courtier, 1997, Dual formulation of four-dimentional variational
 551    !           assimilation, Q.J.R., pp2449-2461.
 552    !
 553    !:Arguments:
 554    !   :indic:   Value of indic
 555    !             Note: 1 and 4 are reserved values for call back from m1qn3.
 556    !             For direct calls use other value than 1 and 4.
 557    !             =1 No action taken; =4 Both J(u) and its gradient are computed.
 558    !             =2 Same as 4 (compute J and gradJ) but do not interrupt timer
 559    !             of the minimizer.
 560    !             =3 Compute Jo and gradJo only.
 561    !
 562    !   :nvadim:  Dimension of the control vector in forecast error covariances space
 563    !
 564    !   :zhat:    Control variable in forecast error covariances space
 565    !
 566    !   :Jtotal:  Cost function of the Variational algorithm
 567    !
 568    !   :gradJ:   Gradient of the Variational Cost funtion
 569    !
 570    implicit none
 571
 572    ! Arguments:
 573    integer,                    intent(in)    :: indic
 574    integer,                    intent(in)    :: nvadim
 575    real(8), dimension(nvadim), intent(inout) :: zhat
 576    real(8),                    intent(out)   :: Jtotal
 577    real(8), dimension(nvadim), intent(out)   :: gradJ
 578
 579    ! Locals:
 580    real(8) :: ahat_vhat(nvadim)
 581    real(8) :: Jb, Jobs
 582    type(struct_gsv)          :: statevector
 583    type(struct_vco), pointer :: vco_anl
 584
 585    call utl_tmg_stop(91)
 586    call utl_tmg_stop(90)
 587
 588    if (indic /= 1) then ! No action taken if indic == 1
 589      fso_nsim = fso_nsim + 1
 590
 591      if (mmpi_myid == 0) then
 592        write(*,*) 'simvar: entering for simulation ',fso_nsim
 593        write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 594        call flush(6)
 595      end if
 596
 597      ! note: vhat = B_t^T/2 hat(del x_t)
 598      ahat_vhat(1:nvadim_mpilocal) = zhat(1:nvadim_mpilocal) + vhat(1:nvadim_mpilocal)
 599
 600      ! Computation of background term of cost function:
 601      Jb = dot_product(zhat(1:nvadim_mpilocal),zhat(1:nvadim_mpilocal))/2.d0
 602      call mmpi_allreduce_sumreal8scalar(Jb,'GRID')
 603
 604      vco_anl => col_getVco(columnTrlOnAnlIncLev_ptr)
 605      call gsv_allocate(statevector,tim_nstepobsinc, hco_anl, vco_anl, &
 606                        dataKind_opt=pre_incrReal, mpi_local_opt=.true.)
 607
 608      call bmat_sqrtB(ahat_vhat,nvadim_mpilocal,statevector)
 609
 610      call s2c_tl(statevector,column_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr)  ! put in column H_horiz dx
 611      call utl_tmg_start(10,'--Observations')
 612      call utl_tmg_start(18,'----ObsOper_TL')
 613      call oop_Htl(column_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr,fso_nsim)  ! Save as OBS_WORK: H_vert H_horiz dx = Hdx
 614
 615      call rmat_RsqrtInverseAllObs(obsSpaceData_ptr,OBS_WORK,OBS_WORK)  ! Save as OBS_WORK : R**-1/2 (Hdx)
 616      call utl_tmg_stop(18)
 617      call utl_tmg_stop(10)
 618
 619      call cfn_calcJo(obsSpaceData_ptr)  ! Store J-obs in OBS_JOBS : 1/2 * R**-1 (Hdx)**2
 620
 621      Jobs = 0.d0
 622      call utl_tmg_start(90,'--Minimization')
 623      call utl_tmg_start(92,'----SumCostFunction')
 624      call cfn_sumJo(obsSpaceData_ptr,Jobs)
 625      Jtotal = Jb + Jobs
 626      if (indic == 3) then
 627        Jtotal = Jobs
 628        if (mmpi_myid == 0) write(*,FMT='(6X,"SIMVAR:  JO = ",G23.16,6X)') Jobs
 629      else
 630        Jtotal = Jb + Jobs
 631        if (mmpi_myid == 0) write(*,FMT='(6X,"SIMVAR:  Jb = ",G23.16,6X,"JO = ",G23.16,6X,"Jt = ",G23.16)') Jb,Jobs,Jtotal
 632      end if
 633      call utl_tmg_stop(92)
 634      call utl_tmg_stop(90)
 635
 636      call utl_tmg_start(10,'--Observations')
 637      call rmat_RsqrtInverseAllObs(obsSpaceData_ptr,OBS_WORK,OBS_WORK)  ! Modify OBS_WORK : R**-1 (Hdx)
 638      call utl_tmg_stop(10)
 639
 640      call col_zero(column_ptr)
 641
 642      call utl_tmg_start(10,'--Observations')
 643      call utl_tmg_start(19,'----ObsOper_AD')
 644      call oop_Had(column_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr)   ! Put in column : H_vert**T R**-1 (Hdx)
 645      call utl_tmg_stop(19)
 646      call utl_tmg_stop(10)
 647
 648      call s2c_ad(statevector,column_ptr,columnTrlOnAnlIncLev_ptr,obsSpaceData_ptr)  ! Put in statevector H_horiz**T H_vert**T R**-1 (Hdx)
 649
 650      gradJ(:) = 0.d0
 651      call bmat_sqrtBT(gradJ,nvadim_mpilocal,statevector)
 652      call gsv_deallocate(statevector)
 653
 654      if (indic /= 3) then
 655        gradJ(1:nvadim_mpilocal) = zhat(1:nvadim_mpilocal) + gradJ(1:nvadim_mpilocal)
 656      end if
 657    end if
 658
 659    call utl_tmg_start(90,'--Minimization')
 660    call utl_tmg_start(91,'----QuasiNewton')
 661
 662    if (mmpi_myid == 0) write(*,*) 'simvar: Finished'
 663
 664  end subroutine simvar
 665
 666  !--------------------------------------------------------------------------
 667  ! prscal
 668  !--------------------------------------------------------------------------
 669  subroutine prscal(kdim,px,py,ddsc)
 670    !
 671    !:Purpose: evaluation of the inner product in canonical space used in the minimization
 672    !
 673    implicit none
 674
 675    ! Arguments:
 676    integer, intent(in)  :: kdim     ! dimension of the vectors
 677    real(8), intent(in)  :: px(kdim) ! vector components for which <px,py> is being calculated
 678    real(8), intent(in)  :: py(kdim) ! vector components for which <px,py> is being calculated
 679    real(8), intent(out) :: ddsc     ! result of the inner product
 680
 681    ! Locals:
 682    integer ::  cvIndex
 683
 684    ddsc = 0.d0
 685
 686    do cvIndex=1,nvadim_mpilocal
 687      ddsc = ddsc + px(cvIndex)*py(cvIndex)
 688    end do
 689
 690    call mmpi_allreduce_sumreal8scalar(ddsc,'GRID')
 691
 692  end subroutine prscal
 693
 694  !--------------------------------------------------------------------------
 695  ! dcanab
 696  !--------------------------------------------------------------------------
 697  subroutine dcanab(kdim,py,px)
 698    !
 699    !:Purpose: Change of variable associated with the canonical inner product
 700    !           to compute PX = L^-1 * Py with L related to the inner product
 701    !           <PX,PY> = PX^t  L^t  L PY
 702    !           (see the modulopt documentation aboutn DTCAB)
 703    !           Double precision version based on single precision CTCAB.
 704    !           Refered to  as dummy argument DTCAB by N1QN3 minimization
 705    !           package.
 706    !
 707    implicit none
 708
 709    ! Arguments:
 710    integer, intent(in)     :: kdim     ! dimension of the vectors
 711    real(8), intent(inout)  :: px(kdim)
 712    real(8), intent(in)     :: py(kdim)
 713
 714    ! Locals:
 715    integer :: cvIndex
 716
 717    do cvIndex = 1, kdim
 718      px(cvIndex) = py(cvIndex)
 719    end do
 720
 721  end subroutine dcanab
 722
 723  !--------------------------------------------------------------------------
 724  ! dcanonb
 725  !--------------------------------------------------------------------------
 726  subroutine dcanonb(kdim,px,py)
 727    !
 728    !:Purpose: Change of variable associated with the canonical inner product
 729    !           to compute PY = L * PX with L related to the inner product
 730    !           <PX,PY> = PX^t  L^t  L PY
 731    !           (see the modulopt documentation about DTONB)
 732    !           Double precision version based on single precision CTCAB.
 733    !           Refered to  as dummy argument DTCAB by N1QN3 minimization
 734    !           package.
 735    !
 736    implicit none
 737
 738    ! Arguments:
 739    integer, intent(in)     :: kdim     ! dimension of the vectors
 740    real(8), intent(in)     :: px(kdim)
 741    real(8), intent(inout)  :: py(kdim)
 742
 743    ! Locals:
 744    integer :: cvIndex
 745
 746    do cvIndex = 1, kdim
 747      py(cvIndex) = px(cvIndex)
 748    end do
 749
 750  end subroutine dcanonb
 751
 752  !--------------------------------------------------------------------------
 753  ! multEnergyNorm
 754  !--------------------------------------------------------------------------
 755  subroutine multEnergyNorm(statevector_inout, statevector_ref,  &
 756                                latMin, latMax, lonMin, lonMax,      &
 757                                uvNorm,ttNorm,p0Norm,huNorm,tgNorm, straNorm)
 758    !
 759    !:Purpose: Computes energy norms
 760    !           For some positive definite symmetric matrix defining the energy,
 761    !             total energy = x^T * C * x
 762    !             statevector_inout = C * statevector_inout
 763    !           (Buehner, Du and Bedard, 2018)
 764    !
 765    implicit none
 766
 767    ! Arguments:
 768    type(struct_gsv), intent(inout) :: statevector_inout
 769    type(struct_gsv), intent(in)    :: statevector_ref
 770    real(8),          intent(in)    :: latMin
 771    real(8),          intent(in)    :: latMax
 772    real(8),          intent(in)    :: lonMin
 773    real(8),          intent(in)    :: lonMax
 774    logical,          intent(in)    :: uvNorm
 775    logical,          intent(in)    :: ttNorm
 776    logical,          intent(in)    :: p0Norm
 777    logical,          intent(in)    :: huNorm
 778    logical,          intent(in)    :: tgNorm
 779    logical,          intent(in)    :: straNorm
 780
 781    ! Locals:
 782    integer              :: stepIndex, lonIndex, levIndex, latIndex, lonIndex2, latIndex2, nLev_M, nLev_T
 783    real(8)              :: scaleFactor, scaleFactorConst, scaleFactorLat, scaleFactorLon, scaleFactorLev
 784    real(8)              :: pfac, tfac, qfac
 785    real(8)              :: sumScale , sumeu, sumev, sumep, sumet, sumeq
 786    real(8), pointer     :: field_UU(:,:,:,:), field_VV(:,:,:,:), field_T(:,:,:,:), field_LQ(:,:,:,:)
 787    real(8), pointer     :: field_Psfc(:,:,:,:), field_TG(:,:,:,:),Psfc_ptr(:,:,:)
 788    real(8), pointer     :: Press_T(:,:,:)
 789    real(8), pointer     :: Press_M(:,:,:)
 790    real(8), allocatable :: Psfc_ref(:,:)
 791    real(8), parameter   :: T_r = 280.0D0
 792    real(8), parameter   :: Psfc_r = 100000.0D0 ! unit Pa
 793    real(8), parameter   :: sigma = 0.3 ! weight factor for humidity
 794
 795    if (mmpi_myid == 0) write(*,*) 'multEnergyNorm: START'
 796    nullify(Press_T,Press_M)
 797
 798    ! the factors for TT, HU and Ps (for wind is 1)
 799    tfac = mpc_cp_dry_air_r8/T_r                                 ! temperature factor (c_p/T_r)
 800    qfac = sigma*mpc_heat_condens_water_r8**2/(mpc_cp_dry_air_r8*T_r)  ! humidity factor ( (l_p*l_p)/(c_p*T_r) )
 801    pfac = mpc_rgas_dry_air_r8*T_r/(Psfc_r**2)                   ! surface pressure factor (R*T_r/Psfc_r^2)
 802
 803    if (.not. gsv_isAllocated(statevector_inout)) then
 804      call utl_abort('multEnergyNorm: gridStateVector_inout not yet allocated')
 805    end if
 806
 807    nLev_M = gsv_getNumLev(statevector_inout,'MM')
 808    nLev_T = gsv_getNumLev(statevector_inout,'TH')
 809
 810    ! compute 3D log pressure fields
 811    call gsv_getField(statevector_ref,Psfc_ptr,'P0')
 812    allocate(Psfc_ref(statevector_inout%lonPerPEmax,statevector_inout%latPerPEmax))
 813    Psfc_ref(:,:) =  &
 814                  Psfc_ptr(statevector_inout%myLonBeg:statevector_inout%myLonEnd,  &
 815                  statevector_inout%myLatBeg:statevector_inout%myLatEnd, 1)
 816    call czp_fetch3DLevels(statevector_inout%vco, Psfc_ref, &
 817                           fldM_opt=Press_M, fldT_opt=Press_T)
 818    ! dlat * dlon
 819    scaleFactorConst = statevector_inout%hco%dlat*statevector_inout%hco%dlon
 820
 821    ! for wind components if to include in Norm calculation
 822    call gsv_getField(statevector_inout,field_UU,'UU')
 823    call gsv_getField(statevector_inout,field_VV,'VV')
 824
 825    sumeu = 0.0D0
 826    sumev = 0.0D0
 827    sumScale = 0.0D0
 828    if (uvNorm) then
 829      do levIndex = 1, nLev_M
 830        do stepIndex = 1, statevector_inout%numStep
 831          do latIndex = statevector_inout%myLatBeg, statevector_inout%myLatEnd
 832            latIndex2 = latIndex - statevector_inout%myLatBeg + 1
 833            ! IF lat is out of the domain where we want to compute the NRJ norm, we put scaleFactorLat = 0.
 834            if (statevector_inout%hco%lat(latIndex) >= latMin .and. statevector_inout%hco%lat(latIndex) <= latMax) then
 835              scaleFactorLat = cos(statevector_inout%hco%lat(latIndex))
 836            else
 837              scaleFactorLat = 0.0D0
 838            end if
 839            do lonIndex = statevector_inout%myLonBeg, statevector_inout%myLonEnd
 840              lonIndex2 = lonIndex - statevector_inout%myLonBeg + 1
 841              ! Similarly, if lon is out of the domain where we want to compute the NRJ norm, we put scaleFactorLon = 0.
 842              if (statevector_inout%hco%lon(lonIndex) >= lonMin .and. statevector_inout%hco%lon(lonIndex) <= lonMax) then
 843                scaleFactorLon = 1.0D0
 844              else
 845                scaleFactorLon = 0.0D0
 846              end if
 847              ! do all thermo levels for which there is a momentum level above and below
 848              if (straNorm) then !for strato Norm 
 849                if (levIndex == nLev_M .or. levIndex == 1 .or. & 
 850                    Press_T(lonIndex2, latIndex2, levIndex) < 100.0D0 .or. &
 851                    Press_T(lonIndex2, latIndex2, levIndex) > 10000.0D0) then
 852                  scaleFactorLev = 0.0D0
 853                else
 854                  scaleFactorLev = Press_T(lonIndex2, latIndex2, levIndex+1) - Press_T(lonIndex2, latIndex2, levIndex)
 855                end if
 856
 857              else 
 858                if ( levIndex == nLev_M) then
 859                  scaleFactorLev = Press_M(lonIndex2, latIndex2, nLev_M)-Press_T(lonIndex2, latIndex2, nLev_T-1)
 860                else if ( Press_T(lonIndex2, latIndex2, levIndex) < 10000.0D0) then
 861                  scaleFactorLev = 0.0D0
 862                else
 863                  scaleFactorLev = Press_T(lonIndex2, latIndex2, levIndex+1) - Press_T(lonIndex2, latIndex2, levIndex)
 864                end if
 865              end if
 866
 867              scaleFactor = scaleFactorConst * scaleFactorLat* scaleFactorLon * scaleFactorLev
 868              sumScale = sumScale + scaleFactor
 869
 870              sumeu = sumeu + &
 871                      0.5 * field_UU(lonIndex,latIndex,levIndex,stepIndex) * field_UU(lonIndex,latIndex,levIndex,stepIndex) * scaleFactor
 872              sumev = sumev + &
 873                      0.5 * field_VV(lonIndex,latIndex,levIndex,stepIndex) * field_VV(lonIndex,latIndex,levIndex,stepIndex) * scaleFactor
 874
 875              field_UU(lonIndex,latIndex,levIndex,stepIndex) = &
 876                   field_UU(lonIndex,latIndex,levIndex,stepIndex) * 0.5 * scaleFactor
 877              field_VV(lonIndex,latIndex,levIndex,stepIndex) = &
 878                   field_VV(lonIndex,latIndex,levIndex,stepIndex) * 0.5 * scaleFactor
 879            end do !lonIndex
 880          end do !latIndex
 881        end do ! stepIndex
 882      end do ! levIndex
 883      call mmpi_allreduce_sumreal8scalar(sumeu,'grid')
 884      call mmpi_allreduce_sumreal8scalar(sumev,'grid')
 885      call mmpi_allreduce_sumreal8scalar(sumScale,'grid')
 886      sumeu = sumeu/sumScale
 887      sumev = sumev/sumScale
 888
 889      field_UU(:,:,:,:) = field_UU(:,:,:,:)/sumScale
 890      field_VV(:,:,:,:) = field_VV(:,:,:,:)/sumScale
 891    else
 892      field_UU(:,:,:,:) = field_UU(:,:,:,:)*0.0D0
 893      field_VV(:,:,:,:) = field_VV(:,:,:,:)*0.0D0
 894    end if ! if uvNorm
 895
 896    if (mmpi_myid == 0)  write(*,*) 'energy for UU=', sumeu
 897    if (mmpi_myid == 0)  write(*,*) 'energy for VV=', sumev
 898
 899    ! for Temperature
 900    call gsv_getField(statevector_inout,field_T,'TT')
 901    sumScale = 0.0D0
 902    sumet = 0.0D0
 903    if (ttNorm) then
 904      do levIndex = 1, nLev_T
 905        do stepIndex = 1, statevector_inout%numStep
 906          do latIndex = statevector_inout%myLatBeg, statevector_inout%myLatEnd
 907            latIndex2 = latIndex - statevector_inout%myLatBeg + 1
 908            ! IF lat is out of the domain where we want to compute the NRJ norm, we put scaleFactorLat = 0.
 909            if (statevector_inout%hco%lat(latIndex) >= latMin .and. statevector_inout%hco%lat(latIndex) <= latMax) then
 910              scaleFactorLat = cos(statevector_inout%hco%lat(latIndex))
 911            else
 912              scaleFactorLat = 0.0D0
 913            end if
 914            do lonIndex = statevector_inout%myLonBeg, statevector_inout%myLonEnd
 915              lonIndex2 = lonIndex - statevector_inout%myLonBeg + 1
 916              ! Similarly, if lon is out of the domain where we want to compute the NRJ norm, we put scaleFactorLon = 0.
 917              if (statevector_inout%hco%lon(lonIndex) >= lonMin .and. statevector_inout%hco%lon(lonIndex) <= lonMax) then
 918                scaleFactorLon = 1.0D0
 919              else
 920                scaleFactorLon = 0.0D0
 921              end if
 922              ! do all thermo levels for which there is a momentum level above and below
 923              if (straNorm) then !for strato norm
 924                if (levIndex == nLev_T .or. levIndex == 1) then
 925                  scaleFactorLev = 0.0D0
 926                else if (Press_M(lonIndex2, latIndex2, levIndex-1) < 100.0D0 .or. &
 927                         Press_M(lonIndex2, latIndex2, levIndex-1) > 10000.0D0 ) then
 928                  scaleFactorLev = 0.0D0
 929                else
 930                  scaleFactorLev = Press_M(lonIndex2, latIndex2, levIndex ) - Press_M(lonIndex2, latIndex2, levIndex-1)
 931                end if
 932
 933              else
 934                if (levIndex == nLev_T) then  !surface
 935                  scaleFactorLev =  Press_T(lonIndex2, latIndex2, nLev_T)-Press_T(lonIndex2, latIndex2, nLev_T-1)
 936                else if (levIndex == 1)  then  ! top
 937                  scaleFactorLev = 0.0D0
 938                else if ( Press_M(lonIndex2, latIndex2, levIndex-1) < 10000.0D0) then
 939                  scaleFactorLev = 0.0D0
 940                else
 941                  scaleFactorLev = Press_M(lonIndex2, latIndex2, levIndex ) - Press_M(lonIndex2, latIndex2, levIndex-1)
 942                end if
 943              end if
 944
 945              scaleFactor = scaleFactorConst * scaleFactorLat * scaleFactorLon * scaleFactorLev
 946              sumet = sumet + &
 947                   0.5 * tfac * field_T(lonIndex,latIndex,levIndex,stepIndex) * field_T(lonIndex,latIndex,levIndex,stepIndex) * scaleFactor
 948              sumScale = sumScale + scaleFactor
 949              field_T(lonIndex,latIndex,levIndex,stepIndex) = &
 950                           field_T(lonIndex,latIndex,levIndex,stepIndex) * 0.5 * tfac * scaleFactor
 951            end do
 952          end do
 953        end do ! stepIndex
 954      end do ! levIndex
 955      call mmpi_allreduce_sumreal8scalar(sumet,'grid')
 956      call mmpi_allreduce_sumreal8scalar(sumScale,'grid')
 957      sumet = sumet/sumScale
 958      field_T(:,:,:,:) = field_T(:,:,:,:)/sumScale
 959    else
 960      field_T(:,:,:,:) = field_T(:,:,:,:)*0.0D0
 961    end if ! if ttNorm
 962
 963    if (mmpi_myid == 0)  write(*,*) 'energy for TT=', sumet
 964
 965    ! humidity (set to zero, for now)
 966    call gsv_getField(statevector_inout,field_LQ,'HU')
 967    sumScale = 0.0D0
 968    sumeq = 0.0D0
 969    if (huNorm) then
 970      do levIndex = 1, nLev_T
 971        do stepIndex = 1, statevector_inout%numStep
 972          do latIndex = statevector_inout%myLatBeg, statevector_inout%myLatEnd
 973            latIndex2 = latIndex - statevector_inout%myLatBeg + 1
 974            ! IF lat is out of the domain where we want to compute the NRJ norm, we put scaleFactorLat = 0.
 975            if (statevector_inout%hco%lat(latIndex) >= latMin .and. statevector_inout%hco%lat(latIndex) <= latMax) then
 976              scaleFactorLat = cos(statevector_inout%hco%lat(latIndex))
 977            else
 978              scaleFactorLat = 0.0D0
 979            end if
 980            do lonIndex = statevector_inout%myLonBeg, statevector_inout%myLonEnd
 981              lonIndex2 = lonIndex - statevector_inout%myLonBeg + 1
 982              ! Similarly, if lon is out of the domain where we want to compute the NRJ norm, we put scaleFactorLon = 0.
 983              if (statevector_inout%hco%lon(lonIndex) >= lonMin .and. statevector_inout%hco%lon(lonIndex) <= lonMax) then
 984                scaleFactorLon = 1.0D0
 985              else
 986                scaleFactorLon = 0.0D0
 987              end if
 988              ! do all thermo levels for which there is a momentum level above and below
 989              if (straNorm) then !for strato norm
 990                if (levIndex == nLev_T .or. levIndex == 1) then
 991                  scaleFactorLev = 0.0D0
 992                else if(Press_M(lonIndex2, latIndex2, levIndex-1) < 100.0D0 .or. &
 993                        Press_M(lonIndex2, latIndex2, levIndex-1) > 10000.0D0 ) then    
 994                  scaleFactorLev = 0.0D0
 995                else
 996                  scaleFactorLev = Press_M(lonIndex2, latIndex2, levIndex ) - Press_M(lonIndex2, latIndex2, levIndex-1)
 997                end if
 998
 999              else
1000                if ( levIndex == nLev_T) then !surface
1001                  scaleFactorLev =  Press_T(lonIndex2, latIndex2, nLev_T) - Press_T(lonIndex2, latIndex2, nLev_T-1)
1002                else if (levIndex == 1)  then  ! top
1003                  scaleFactorLev = 0.0D0
1004                else if ( Press_M(lonIndex2, latIndex2, levIndex-1) < 10000.0D0) then
1005                  scaleFactorLev = 0.0D0
1006                else
1007                  scaleFactorLev = Press_M(lonIndex2, latIndex2, levIndex ) - Press_M(lonIndex2, latIndex2, levIndex-1)
1008                end if
1009              end if
1010
1011              scaleFactor = scaleFactorConst * scaleFactorLat * scaleFactorLon * scaleFactorLev
1012              sumScale = sumScale + scaleFactor
1013
1014              sumeq = sumeq + 0.5 * qfac * &
1015                    field_LQ(lonIndex,latIndex,levIndex,stepIndex) * field_LQ(lonIndex,latIndex,levIndex,stepIndex) * scaleFactor
1016
1017              field_LQ(lonIndex,latIndex,levIndex,stepIndex) = &
1018                       field_LQ(lonIndex,latIndex,levIndex,stepIndex) * 0.5 * scaleFactor * qfac
1019
1020            end do
1021          end do
1022        end do ! stepIndex
1023      end do ! latIndex
1024      call mmpi_allreduce_sumreal8scalar(sumScale,'grid')
1025      call mmpi_allreduce_sumreal8scalar(sumeq,'grid')
1026      sumeq = sumeq/sumScale
1027      field_LQ(:,:,:,:) = field_LQ(:,:,:,:)/sumScale
1028    else
1029      field_LQ(:,:,:,:) = field_LQ(:,:,:,:)*0.0D0
1030    end if ! if huNorm
1031
1032    if (mmpi_myid == 0)  write(*,*) 'energy for HU=', sumeq
1033
1034    ! surface pressure
1035    call gsv_getField(statevector_inout,field_Psfc,'P0')
1036    sumScale = 0.0D0
1037    sumep = 0.0
1038    if (p0Norm .and. .not.straNorm) then
1039      do stepIndex = 1, statevector_inout%numStep
1040        do latIndex = statevector_inout%myLatBeg, statevector_inout%myLatEnd
1041            ! IF lat is out of the domain where we want to compute the NRJ norm, we put scaleFactorLat = 0.
1042            if (statevector_inout%hco%lat(latIndex) >= latMin .and. statevector_inout%hco%lat(latIndex) <= latMax) then
1043              scaleFactorLat = cos(statevector_inout%hco%lat(latIndex))
1044            else
1045              scaleFactorLat = 0.0D0
1046            end if
1047            do lonIndex = statevector_inout%myLonBeg, statevector_inout%myLonEnd
1048              ! Similarly, if lon is out of the domain where we want to compute the NRJ norm, we put scaleFactorLon = 0.
1049              if (statevector_inout%hco%lon(lonIndex) >= lonMin .and. statevector_inout%hco%lon(lonIndex) <= lonMax) then
1050                scaleFactorLon = 1.0D0
1051              else
1052                scaleFactorLon = 0.0D0
1053              end if
1054              scaleFactor = scaleFactorConst * scaleFactorLat * scaleFactorLon
1055              sumScale = sumScale + scaleFactor
1056              sumep = sumep + 0.5 * pfac * &
1057                  field_Psfc(lonIndex,latIndex,1,stepIndex) * field_Psfc(lonIndex,latIndex,1,stepIndex) * scaleFactor
1058              field_Psfc(lonIndex,latIndex,1,stepIndex) = &
1059              field_Psfc(lonIndex,latIndex,1,stepIndex) * 0.5 * scaleFactor * pfac
1060          end do
1061        end do ! latIndex
1062      end do ! stepIndex
1063
1064      call mmpi_allreduce_sumreal8scalar(sumep,'grid')
1065      call mmpi_allreduce_sumreal8scalar(sumScale,'grid')
1066      sumep = sumep/sumScale
1067      field_Psfc(:,:,:,:) =  field_Psfc(:,:,:,:)/sumScale
1068    else
1069      field_Psfc(:,:,:,:) =  field_Psfc(:,:,:,:)*0.0D0
1070    end if ! if p0Norm
1071
1072    if (mmpi_myid == 0)  write(*,*) 'energy for Ps=', sumep
1073
1074    ! skin temperature (set to zero for now)
1075    call gsv_getField(statevector_inout,field_TG,'TG')
1076    sumScale = 0.0D0
1077    if (tgNorm .and. .not.straNorm) then
1078      do stepIndex = 1, statevector_inout%numStep
1079        do latIndex = statevector_inout%myLatBeg, statevector_inout%myLatEnd
1080            ! IF lat is out of the domain where we want to compute the NRJ norm, we put scaleFactorLat = 0.
1081            if (statevector_inout%hco%lat(latIndex) >= latMin .and. statevector_inout%hco%lat(latIndex) <= latMax) then
1082              scaleFactorLat = cos(statevector_inout%hco%lat(latIndex))
1083            else
1084              scaleFactorLat = 0.0D0
1085            end if
1086            do lonIndex = statevector_inout%myLonBeg, statevector_inout%myLonEnd
1087              ! Similarly, if lon is out of the domain where we want to compute the NRJ norm, we put scaleFactorLon = 0.
1088              if (statevector_inout%hco%lon(lonIndex) >= lonMin .and. statevector_inout%hco%lon(lonIndex) <= lonMax) then
1089                scaleFactorLon = 1.0D0
1090              else
1091                scaleFactorLon = 0.0D0
1092              end if
1093              scaleFactor = scaleFactorConst * scaleFactorLat * scaleFactorLon
1094              sumScale = sumScale + scaleFactor
1095              field_TG(lonIndex,latIndex,1,stepIndex) = &
1096              field_TG(lonIndex,latIndex,1,stepIndex) * 0.5 * scaleFactor * 0.0
1097          end do
1098        end do ! latIndex
1099      end do ! stepIndex
1100      call mmpi_allreduce_sumreal8scalar(sumScale,'grid')
1101      field_TG(:,:,:,:) = field_TG(:,:,:,:)/sumScale
1102    else
1103      field_TG(:,:,:,:) = field_TG(:,:,:,:)*0.0D0
1104    end if ! if tgNorm
1105
1106    if (mmpi_myid == 0) write(*,*) 'energy for total=', sumeu + sumev + sumet + sumep + sumeq
1107    deallocate(Press_T,Press_M)
1108    deallocate(Psfc_ref)
1109
1110    if (mmpi_myid == 0) write(*,*) 'multEnergyNorm: END'
1111
1112  end subroutine multEnergyNorm
1113
1114end module fsoi_mod