innovation_mod sourceΒΆ

  1module innovation_mod
  2  ! MODULE innovation_mod (prefix='inn' category='1. High-level functionality')
  3  !
  4  !:Purpose:  Several high-level subroutines used to compute the innovations:
  5  !           that is, the observation-minus-background values. This includes
  6  !           the subroutine that reads in the gridded high-res background state
  7  !           from standard files.
  8  !
  9  use midasMpi_mod
 10  use obsSpaceData_mod
 11  use columnData_mod
 12  use timeCoord_mod
 13  use obsTimeInterp_mod
 14  use obsOperators_mod
 15  use mathPhysConstants_mod
 16  use horizontalCoord_mod
 17  use varNameList_mod
 18  use verticalCoord_mod
 19  use gridStateVector_mod
 20  use utilities_mod
 21  use message_mod
 22  use obsFilter_mod  
 23  use gps_mod
 24  use tovsNL_mod
 25  use multiIRbgck_mod
 26  use obsFiles_mod
 27  use randomNumber_mod
 28  use obsErrors_mod
 29  use bufr_mod
 30  use statetocolumn_mod
 31  use biascorrectionSat_mod
 32  use rmatrix_mod
 33  use costFunction_mod
 34  use varqc_mod
 35  use humidityLimits_mod
 36  use interpolation_mod
 37  implicit none
 38  save
 39  private
 40
 41  ! public procedures
 42  public :: inn_setupObs, inn_computeInnovation
 43  public :: inn_perturbObs, inn_setupColumnsOnTrlLev, inn_setupColumnsOnAnlIncLev
 44  public :: inn_getHcoVcoFromTrlmFile
 45
 46  character(len=48) :: innovationMode
 47
 48contains
 49
 50  !--------------------------------------------------------------------------
 51  ! inn_setupObs
 52  !--------------------------------------------------------------------------
 53  subroutine inn_setupobs(obsSpaceData, hco_anl, obsColumnMode, obsMpiStrategy, &
 54       innovationMode_in, obsClean_opt )
 55    !
 56    !:Purpose: To initialize the observation parameters and constants
 57    !
 58    implicit none
 59
 60    ! Arguments:
 61    type(struct_obs),           intent(out) :: obsSpaceData
 62    type(struct_hco), pointer,  intent(in)  :: hco_anl
 63    character(len=*),           intent(in)  :: obsMpiStrategy
 64    character(len=*),           intent(in)  :: obsColumnMode
 65    character(len=*),           intent(in)  :: innovationMode_in
 66    logical,          optional, intent(in)  :: obsClean_opt
 67
 68    ! Locals:
 69    character(len=20) :: nameDimFile
 70    integer :: get_max_rss, ierr, fnom, fclos, unitDimFile, mxstn, mxobs
 71    logical :: obsDimFileExists
 72
 73    write(*,FMT=9000)
 749000 FORMAT(/,1x,' INN_SETUPOBS - Initialisation of observations',/,1x,3('- -----------'))
 75
 76    call utl_tmg_start(10,'--Observations')
 77
 78    !
 79    !- Setup de the mode
 80    !
 81    innovationMode = innovationMode_in
 82
 83    !
 84    !- Specify the active observation-array columns
 85    !
 86    call obs_class_initialize(obsColumnMode)
 87    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
 88    !
 89    !- Allocate memory for observation arrays
 90    !
 91    nameDimFile = './obs/cmadim'
 92    inquire( file=trim(nameDimFile), exist=obsDimFileExists )
 93    if ( obsDimFileExists ) then
 94      unitDimFile = 0
 95      ierr = fnom( unitDimFile, trim(nameDimFile), 'FTN+SEQ+R/O', 0 )
 96      read(unitDimFile,*) mxstn
 97      read(unitDimFile,*) mxobs
 98      ierr=fclos(unitDimFile)  
 99      call obs_initialize(obsSpaceData, numHeader_max_opt=mxstn, numBody_max_opt=mxobs, &
100                          mpi_local_opt=obsf_filesSplit())
101    else
102      call obs_initialize(obsSpaceData, mpi_local_opt=obsf_filesSplit())
103    end if
104    write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
105
106    !
107    !- Set up the list of elements to be assimilated and flags for rejection
108    !
109    call filt_setup(innovationMode) ! IN
110
111    !
112    !- Initialize TOVS processing
113    !
114    call tvs_setup
115
116    !
117    !- Read the observations from files
118    !
119    call utl_tmg_start(11,'----ReadObsFiles')
120    call obsf_readFiles( obsSpaceData )
121    call utl_tmg_stop(11)
122
123    !
124    !- Initialize GPS processing
125    !
126    if (obs_famExist(obsSpaceData,'RO')) call gps_setupro
127    if (obs_famExist(obsSpaceData,'GP')) call gps_setupgb
128
129    !
130    !- Filter out data from the obs data base
131    !
132    call filt_suprep(obsSpaceData)
133
134    ! Additional filtering for bias correction if requested 
135    call bcs_setup()
136    call bcs_filterObs(obsSpaceData)
137
138    if ( present(obsClean_opt) ) then
139      if ( obsClean_opt ) then
140        write(*,*) ''
141        write(*,*) 'inn_setupObs: !!WARNING!! Performing a cleanup of obsSpaceData'
142        write(*,*) '              This may make it impossible to update burp files'
143        write(*,*) ''
144        call obs_clean2(obsSpaceData)
145      end if
146    end if
147
148    !
149    !- Set (OBS_IPC and OBS_IPT) or OBS_IP columns according to the chosen strategy
150    !
151    write(*,*)
152    write(*,*) 'inn_setupObs - Using obsMpiStrategy = ', trim(obsMpiStrategy)
153    call setObsMpiStrategy(obsSpaceData,hco_anl,obsMpiStrategy)
154
155    !
156    !- Check if burp files already split
157    !
158    if ( obsf_filesSplit() ) then 
159      ! local observations files, so just do reallocation to reduce memory used
160      call obs_squeeze(obsSpaceData)
161      if ( obs_columnActive_IH(obsSpaceData,OBS_IPC) ) then
162        call obs_MpiRedistribute(obsSpaceData,OBS_IPC)
163      end if
164      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
165    else
166      ! complete set of obs on each MPI process, only keep subset according to OBS_IP
167      call obs_reduceToMpiLocal(obsSpaceData)
168    end if
169
170    !
171    !- Initialization and memory allocation for TOVS processing
172    !
173    if (obs_famExist(obsSpaceData,'TO') .and. (trim(innovationMode) /= 'thinning')) then
174      call tvs_setupAlloc(obsSpaceData)
175      if (trim(innovationMode) == 'bgck' ) call irbg_setup()
176      ! Initialize non diagonal observation error matrices
177      if ( trim(innovationMode) == 'analysis' .or. trim(innovationMode) == 'FSO') call oer_setInterchanCorr()
178    end if
179
180    call utl_tmg_stop(10)
181
182  end subroutine inn_setupobs
183
184  !--------------------------------------------------------------------------
185  ! inn_setupColumnsOnTrlLev
186  !--------------------------------------------------------------------------
187  subroutine inn_setupColumnsOnTrlLev( columnTrlOnTrlLev, obsSpaceData, hco_core, &
188                                       stateVectorUpdateHighRes, &
189                                       deallocInterpInfoNL_opt )
190    !
191    !:Purpose: To compute vertical (and potentially slanted) columns of trial data interpolated to obs location
192    !
193    implicit none
194
195    ! Arguments:
196    type(struct_columnData),   intent(out)   :: columnTrlOnTrlLev
197    type(struct_obs),          intent(inout) :: obsSpaceData
198    type(struct_hco), pointer, intent(in)    :: hco_core
199    type(struct_gsv),          intent(inout) :: stateVectorUpdateHighRes
200    logical        , optional, intent(in)    :: deallocInterpInfoNL_opt
201
202    ! Locals:
203    type(struct_vco), pointer :: vco_trl => null()
204    integer                   :: ierr, nulnam, fnom, fclos
205    logical                   :: deallocInterpInfoNL
206    real(8), pointer          :: onecolumn(:)
207    character(len=20) :: timeInterpType_nl  ! 'NEAREST' or 'LINEAR'
208    integer           :: numObsBatches      ! number of batches for calling interp setup
209
210    NAMELIST /NAMINN/timeInterpType_nl, numObsBatches
211
212    write(*,*)
213    write(*,*) 'inn_setupColumnsOnTrlLev: START'
214
215    timeInterpType_nl = 'NEAREST'
216    numObsBatches     = 20
217
218    if (utl_isNamelistPresent('naminn','./flnml')) then
219      nulnam = 0
220      ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
221      if (ierr /= 0) call utl_abort('inn_setupColumnsOnTrlLev: Error opening file flnml')
222      read(nulnam,nml=naminn,iostat=ierr)
223      if (ierr /= 0) call utl_abort('inn_setupColumnsOnTrlLev: Error reading namelist')
224      if (mmpi_myid == 0) write(*,nml=naminn)
225      ierr = fclos(nulnam)
226    else
227      write(*,*)
228      write(*,*) 'inn_setupColumnsOnTrlLev: Namelist block NAMINN is missing in the namelist.'
229      write(*,*) '                            The default values will be taken.'
230      if (mmpi_myid == 0) write(*,nml=naminn)
231    end if
232
233    if ( present(deallocInterpInfoNL_opt) ) then
234      deallocInterpInfoNL = deallocInterpInfoNL_opt
235    else
236      deallocInterpInfoNL = .true.
237    end if
238
239    nullify(vco_trl)
240    vco_trl => gsv_getVco(stateVectorUpdateHighRes)
241
242    call col_setVco(columnTrlOnTrlLev,vco_trl)
243    call col_allocate(columnTrlOnTrlLev,obs_numHeader(obsSpaceData),mpiLocal_opt=.true.)
244
245    ! copy latitude from obsSpaceData
246    if ( obs_numHeader(obsSpaceData) > 0 ) then
247      call obs_extractObsRealHeaderColumn(columnTrlOnTrlLev%lat(:), obsSpaceData, OBS_LAT)
248    end if
249
250    call s2c_nl( stateVectorUpdateHighRes, obsSpaceData, columnTrlOnTrlLev, hco_core, &
251                 timeInterpType=timeInterpType_nl, &
252                 moveObsAtPole_opt=.true., numObsBatches_opt=numObsBatches, &
253                 dealloc_opt=deallocInterpInfoNL )
254
255    if ( col_getNumCol(columnTrlOnTrlLev) > 0 .and. col_varExist(columnTrlOnTrlLev,'Z_T ') ) then
256      write(*,*) 'inn_setupBackgroundColumns, statevector->Column 1:'
257      write(*,*) 'Z_T:'
258      onecolumn => col_getColumn(columnTrlOnTrlLev,1,'Z_T ')
259      write(*,*) onecolumn(:)
260      write(*,*) 'Z_M:'
261      onecolumn => col_getColumn(columnTrlOnTrlLev,1,'Z_M ')
262      write(*,*) onecolumn(:)
263
264      nullify(onecolumn)
265    end if
266    if ( col_getNumCol(columnTrlOnTrlLev) > 0 .and. col_varExist(columnTrlOnTrlLev,'P_T ') ) then
267      write(*,*) 'inn_setupBackgroundColumns, statevector->Column 1:'
268      write(*,*) 'P_T:'
269      onecolumn => col_getColumn(columnTrlOnTrlLev,1,'P_T ')
270      write(*,*) onecolumn(:)
271      write(*,*) 'P_M:'
272      onecolumn => col_getColumn(columnTrlOnTrlLev,1,'P_M ')
273      write(*,*) onecolumn(:)
274
275      nullify(onecolumn)
276    end if
277
278    write(*,*) 'inn_setupColumnsOnTrlLev: END'
279
280  end subroutine inn_setupColumnsOnTrlLev
281
282  !--------------------------------------------------------------------------
283  ! inn_setupColumnsOnAnlIncLev
284  !--------------------------------------------------------------------------
285  subroutine inn_setupColumnsOnAnlIncLev(columnTrlOnTrlLev,columnTrlOnAnlIncLev)
286    !
287    !:Purpose: To create trial data columns on analysis increment levels
288    !
289    implicit none
290
291    ! Arguments:
292    type(struct_columnData), intent(inout) :: columnTrlOnAnlIncLev
293    type(struct_columnData), intent(in)    :: columnTrlOnTrlLev
294
295    ! Locals:
296    integer :: jvar, jlev, columnIndex
297    real(8), pointer :: columnTrlOnAnlIncLev_ptr(:), columnTrlOnTrlLev_ptr(:)
298
299    if (col_getNumCol(columnTrlOnAnlIncLev) == 0) return
300
301    call msg('inn_setupColumnsOnAnlIncLev','START',verb_opt=2)
302
303    !
304    !- Data copying from columnh to columnTrlOnAnlIncLev
305    !
306    
307    ! copy latitude
308    columnTrlOnAnlIncLev%lat(:) = columnTrlOnTrlLev%lat(:)
309
310    ! copy 2D surface variables
311    do jvar = 1, vnl_numvarmax2D
312      if ( .not. col_varExist(columnTrlOnAnlIncLev,vnl_varNameList2D(jvar)) ) cycle
313      do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
314        columnTrlOnAnlIncLev_ptr  => col_getColumn(columnTrlOnAnlIncLev , columnIndex, vnl_varNameList2D(jvar))
315        columnTrlOnTrlLev_ptr => col_getColumn(columnTrlOnTrlLev, columnIndex, vnl_varNameList2D(jvar))
316        columnTrlOnAnlIncLev_ptr(:) = columnTrlOnTrlLev_ptr(:)
317      end do
318    end do
319
320    !
321    !- Vertical interpolation of 3D variables from trials levels to analysis increment levels
322    !
323    do jvar = 1, vnl_numvarmax3D
324
325      if ( .not. col_varExist(columnTrlOnAnlIncLev,vnl_varNameList3D(jvar)) ) cycle
326      
327      call int_vInterp_col( columnTrlOnTrlLev, columnTrlOnAnlIncLev, &
328                            vnl_varNameList3D(jvar), useColumnPressure_opt=.false.)
329
330      if ( vnl_varNameList3D(jvar) == 'HU  ') then
331        ! Imposing a minimum value for HU
332        do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
333          columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,columnIndex,'HU')
334          do jlev = 1, col_getNumLev(columnTrlOnAnlIncLev,'TH')
335            columnTrlOnAnlIncLev_ptr(jlev) = max(columnTrlOnAnlIncLev_ptr(jlev),col_rhumin)
336          end do
337        end do
338
339      ! Imposing minimum/maximum value for ALL cloud variables at all levels
340      else if (vnl_isCloudVar(vnl_varNameList3D(jvar))) then
341        do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
342          columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,columnIndex,vnl_varNameList3D(jvar))
343          do jlev = 1, col_getNumLev(columnTrlOnAnlIncLev,'TH')
344            columnTrlOnAnlIncLev_ptr(jlev) = max(columnTrlOnAnlIncLev_ptr(jlev), &
345                                                 qlim_getMinValueCloud(vnl_varNameList3D(jvar)))
346            columnTrlOnAnlIncLev_ptr(jlev) = min(columnTrlOnAnlIncLev_ptr(jlev), &
347                                                 qlim_getMaxValueCloud(vnl_varNameList3D(jvar)))
348          end do
349        end do
350
351      else if (trim(vnl_varKindFromVarname(vnl_varNameList3D(jvar))) == 'CH') then
352        ! Imposing boundary values for CH kind variables. This is to prevent
353        ! undesired values usually from vertical extrapolation.
354        do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
355          columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,columnIndex,trim(vnl_varNameList3D(jvar)))
356          if ( col_minValVarKindCH(vnl_varListIndex(vnl_varNameList3D(jvar))) > 1.01*MPC_missingValue_R8 ) then
357            do jlev=1,col_getNumLev(columnTrlOnAnlIncLev,'TH')
358              columnTrlOnAnlIncLev_ptr(jlev) = max(columnTrlOnAnlIncLev_ptr(jlev),col_minValVarKindCH(vnl_varListIndex(vnl_varNameList3D(jvar))))
359            end do
360          end if
361        end do
362      end if
363
364    end do
365
366    ! Print pressure on thermo levels for the first column
367    if (col_varExist(columnTrlOnAnlIncLev,'P_T')) then
368      columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,1,'P_T')
369      call msg('inn_setupColumnsOnAnlIncLev', ' after vintprof:'&
370           //new_line('')//'P_T (columnTrlOnAnlIncLev(1)) = '//str(columnTrlOnAnlIncLev_ptr(:)), mpiAll_opt=.false.)
371    end if
372
373    !
374    !- Height adjustments
375    !
376
377    ! Set surface height
378    do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
379      columnTrlOnAnlIncLev%heightSfc(columnIndex) = columnTrlOnTrlLev%heightSfc(columnIndex)
380    end do
381
382    ! Remove the height offset for the diagnostic levels for backward compatibility only
383    if ( col_varExist(columnTrlOnAnlIncLev,'Z_T') .and. .not.columnTrlOnAnlIncLev%addHeightSfcOffset ) then 
384      do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
385        columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,columnIndex,'Z_T')
386        columnTrlOnAnlIncLev_ptr(col_getNumLev(columnTrlOnAnlIncLev,'TH')) = columnTrlOnAnlIncLev%heightSfc(columnIndex)
387      end do
388    end if
389    if ( col_varExist(columnTrlOnAnlIncLev,'Z_M') .and. .not.columnTrlOnAnlIncLev%addHeightSfcOffset ) then
390      do columnIndex = 1, col_getNumCol(columnTrlOnAnlIncLev)
391        columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,columnIndex,'Z_M')
392        columnTrlOnAnlIncLev_ptr(col_getNumLev(columnTrlOnAnlIncLev,'MM')) = columnTrlOnAnlIncLev%heightSfc(columnIndex)
393      end do
394    end if
395
396    ! Print height info of the first original and interpolated columns
397    if ( col_getNumLev(columnTrlOnAnlIncLev,'TH') > 0 .and. col_varExist(columnTrlOnAnlIncLev,'Z_T') ) then
398      columnTrlOnTrlLev_ptr => col_getColumn(columnTrlOnTrlLev,1,'Z_T')
399      columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,1,'Z_T')
400      call msg('inn_setupColumnsOnAnlIncLev','vIntProf output:' &
401            //new_line('')//'Z_T (columnTrlOnTrlLev) = '//str(columnTrlOnTrlLev_ptr(:)) &
402            //new_line('')//'Z_T (columnTrlOnAnlIncLev) = '//str(columnTrlOnAnlIncLev_ptr(:)))
403    end if
404    
405    if ( col_getNumLev(columnTrlOnAnlIncLev,'MM') > 0 .and. col_varExist(columnTrlOnAnlIncLev,'Z_M') ) then
406      columnTrlOnTrlLev_ptr => col_getColumn(columnTrlOnTrlLev,1,'Z_M')
407      columnTrlOnAnlIncLev_ptr => col_getColumn(columnTrlOnAnlIncLev,1,'Z_M')
408      call msg('inn_setupColumnsOnAnlIncLev','vIntProf output:'&
409            //new_line('')//'Z_M (columnTrlOnTrlLev) = '//str(columnTrlOnTrlLev_ptr(:)) &
410            //new_line('')//'Z_M (columnTrlOnAnlIncLev) = '//str(columnTrlOnAnlIncLev_ptr(:)))
411    end if
412
413    call msg('inn_setupColumnsOnAnlIncLev','HeightSfc:'//str(columnTrlOnAnlIncLev%heightSfc(1)))
414
415    call msg('inn_setupColumnsOnAnlIncLev','END',verb_opt=2)
416
417  end subroutine inn_setupColumnsOnAnlIncLev
418
419  !--------------------------------------------------------------------------
420  ! inn_computeInnovation
421  !--------------------------------------------------------------------------
422  subroutine inn_computeInnovation( columnTrlOnTrlLev, obsSpaceData, filterObsAndInitOer_opt, &
423                                    applyVarqcOnNlJo_opt, destObsColumn_opt, &
424                                    beSilent_opt, callFiltTopo_opt, callSetErrGpsgb_opt, &
425                                    analysisMode_opt )
426    !
427    !:Purpose: To initialize observation innovations using the nonlinear H
428    !
429    implicit none
430
431    ! Arguments:
432    type(struct_columnData), intent(in)    :: columnTrlOnTrlLev
433    type(struct_obs)       , intent(inout) :: obsSpaceData
434    logical, optional      , intent(in)    :: filterObsAndInitOer_opt
435    logical, optional      , intent(in)    :: applyVarqcOnNlJo_opt
436    integer, optional      , intent(in)    :: destObsColumn_opt ! column where result stored, default is OBS_OMP
437    logical, optional      , intent(in)    :: beSilent_opt
438    logical, optional      , intent(in)    :: callFiltTopo_opt ! whether to make call to FiltTopo
439    logical, optional      , intent(in)    :: callSetErrGpsgb_opt ! whether to make call to oer_SETERRGPSGB
440    logical, optional      , intent(in)    :: analysisMode_opt ! analysisMode argument for oer_SETERRGPSGB and oop_gpsgb_nl
441    
442    ! Locals:
443    real(8) :: Jo
444    integer :: destObsColumn, get_max_rss
445    logical :: applyVarqcOnNlJo, filterObsAndInitOer, beSilent, callFiltTopo, callSetErrGpsgb, analysisMode 
446    logical, save :: lgpdata = .false.
447
448    call utl_tmg_start(10,'--Observations')
449
450    if ( present(beSilent_opt) ) then
451      beSilent = beSilent_opt
452    else
453      beSilent = .false.
454    end if
455
456    if ( .not. beSilent ) then
457      write(*,*)
458      write(*,*) '--Starting subroutine inn_computeInnovation--'
459      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
460    end if
461
462    if ( present(filterObsAndInitOer_opt) ) then
463      filterObsAndInitOer = filterObsAndInitOer_opt
464    else
465      filterObsAndInitOer = .true.
466    end if
467
468    if ( present(applyVarqcOnNlJo_opt) ) then
469      applyVarqcOnNlJo = applyVarqcOnNlJo_opt
470    else
471      applyVarqcOnNlJo = .false.
472    end if
473
474    if ( present(destObsColumn_opt) ) then
475      destObsColumn = destObsColumn_opt
476    else
477      destObsColumn = obs_omp
478    end if
479
480    if ( present(callFiltTopo_opt) ) then
481      callFiltTopo = callFiltTopo_opt
482    else
483      callFiltTopo = .true.
484    end if   
485
486    if ( present(callSetErrGpsgb_opt ) ) then
487      callSetErrGpsgb = callSetErrGpsgb_opt
488    else
489      callSetErrGpsgb = .true.   
490    end if    
491
492    if ( present(analysisMode_opt ) ) then
493      analysisMode = analysisMode_opt
494    else
495      analysisMode = .true.
496    end if
497
498    if ( .not.beSilent ) write(*,*) 'oti_timeBinning: Before filtering done in inn_computeInnovation'
499    if ( .not.beSilent ) call oti_timeBinning(obsSpaceData,tim_nstepobs)
500
501    ! Reject observed elements too far below the surface. Pressure values
502    ! for elements slightly below the surface are replaced by the surface
503    ! pressure values of the trial field.
504    !
505    ! GB-GPS (met and ZTD) observations are processed in s/r filt_topoSFC (in obsFilter_mod.ftn90)
506    !
507    if ( filterObsAndInitOer .and. callFiltTopo ) then
508      call filt_topo(columnTrlOnTrlLev,obsSpaceData,beSilent)
509    else
510      if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip filt_topo'
511    end if
512   
513    ! Remove surface station wind observations
514    if ( trim(innovationMode) == 'analysis' .or. trim(innovationMode) == 'FSO' ) then
515      if ( filterObsAndInitOer ) then
516        call filt_surfaceWind(obsSpaceData, beSilent)
517      else
518        if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip filt_surfaceWind'
519      end if
520    end if
521
522    ! Find interpolation layer in model profiles
523    if ( col_getNumLev(columnTrlOnTrlLev,'MM') > 1 ) call oop_vobslyrs(columnTrlOnTrlLev, obsSpaceData, beSilent)
524
525    !
526    !- Calculate the innovations [Y - H(Xb)] and place the result in obsSpaceData in destObsColumn column
527    !
528    call utl_tmg_start(17,'----ObsOper_NL')
529    
530    ! Radiosondes
531    call oop_ppp_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'UA', destObsColumn)
532
533    ! Aircrafts
534    call oop_ppp_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'AI', destObsColumn)
535
536    ! SatWinds
537    if ( filterObsAndInitOer ) then
538      call oer_sw(columnTrlOnTrlLev,obsSpaceData)
539    else
540      if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip oer_sw'
541    end if
542    
543    call oop_ppp_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'SW', destObsColumn)
544
545    ! Surface (SF, UA, SC, GP and RA families)
546    call oop_sfc_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'SF', destObsColumn)
547    call oop_sfc_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'UA', destObsColumn)
548    call oop_sfc_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'SC', destObsColumn)
549    call oop_sfc_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'GP', destObsColumn)
550    call oop_sfc_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'RA', destObsColumn)
551
552    ! RADAR Doppler velocity
553    if ( filterObsAndInitOer ) then
554     ! Filter Radar for Doppler velocity
555      call filt_radvel(columnTrlOnTrlLev, obsSpaceData, beSilent)
556    else
557      if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip filt_radvel'
558    end if
559
560    call oop_raDvel_nl(columnTrlOnTrlLev,obsSpaceData, beSilent, 'RA', destObsColumn)  
561
562    ! Sea surface temperature
563    call oop_sst_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'TM', destObsColumn)
564
565    ! Sea ice concentration
566    if ( filterObsAndInitOer ) then
567      call filt_iceConcentration(obsSpaceData, beSilent)
568      call filt_backScatAnisIce(obsSpaceData, beSilent)
569      call oer_setErrBackScatAnisIce(obsSpaceData, beSilent, columnTrlOnTrlLev_opt=columnTrlOnTrlLev)
570    else
571      if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip filt_iceConcentration, filt_backScatAnisIce, and oer_setErrBackScatAnisIce'
572    end if
573
574    call oop_ice_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'GL', destObsColumn)
575
576    ! Hydrology
577    call oop_hydro_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'HY', destObsColumn)
578
579    ! TOVS / Radiances
580    if (trim(innovationMode) == 'bgck'  ) then
581      call oop_tovs_nl(columnTrlOnTrlLev, obsSpaceData, tim_getDatestamp(),  &
582                       beSilent, bgckMode_opt=.true., destObs_opt=destObsColumn)
583    else
584      call oop_tovs_nl(columnTrlOnTrlLev, obsSpaceData, tim_getDatestamp(),  &
585                       beSilent, bgckMode_opt=.false., destObs_opt=destObsColumn)
586    end if
587
588    ! Profilers
589    call oop_zzz_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'PR', destObsColumn)
590
591    ! Aladin winds
592    call oop_zzz_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, 'AL', destObsColumn)
593
594    ! GPS radio occultation
595    if (obs_famExist(obsSpaceData,'RO', localMPI_opt = .true. )) then
596      if ( filterObsAndInitOer ) then
597        call filt_gpsro(columnTrlOnTrlLev, obsSpaceData, beSilent)
598        call oer_SETERRGPSRO(columnTrlOnTrlLev, obsSpaceData, beSilent)
599      else
600        if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip filt_gpsro, and oer_SETERRGPSRO'
601      end if
602      call oop_gpsro_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, destObsColumn)
603    end if
604
605    ! Chemical constituents
606    call oop_chm_nl(columnTrlOnTrlLev, obsSpaceData, destObsColumn)
607
608    ! GPS ground-based zenith delay
609    if (obs_famExist(obsSpaceData,'GP', localMPI_opt = .true. )) then
610      if ( CallSetErrGpsgb ) then
611        call oer_SETERRGPSGB(columnTrlOnTrlLev, obsSpaceData, beSilent, lgpdata, analysisMode)
612      else
613        if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,*) 'inn_computeInnovation: skip oer_SETERRGPSGB'
614      end if
615      if (lgpdata) call oop_gpsgb_nl(columnTrlOnTrlLev, obsSpaceData, beSilent, &
616                                     destObsColumn, analysisMode_opt=analysisMode)   
617    end if
618
619    call utl_tmg_stop(17)
620
621    ! Save as OBS_WORK : R**-1/2 (d)
622    call rmat_RsqrtInverseAllObs(obsSpaceData,OBS_WORK,destObsColumn)
623
624    ! Store J-obs in OBS_JOBS : 1/2 * R**-1 (d)**2
625    call cfn_calcJo(obsSpaceData)
626
627    ! applying varqc, if asked for
628    if ( applyVarqcOnNlJo ) call vqc_NlTl(obsSpaceData)
629
630    ! Compute Jo components and print
631    call cfn_sumJo(obsSpaceData, Jo, beSilent_opt=beSilent)
632    if ( mmpi_myid == 0 .and. .not. beSilent ) write(*,'(a15,f25.17)') 'Total Jo = ',Jo
633
634    if ( .not.beSilent ) write(*,*) 'oti_timeBinning: After filtering done in inn_computeInnovation'
635    if ( .not.beSilent ) call oti_timeBinning(obsSpaceData,tim_nstepobs)
636
637    if ( .not. beSilent ) then
638      write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
639      write(*,*) '--Done subroutine inn_computeInnovation--'
640    end if
641
642    call utl_tmg_stop(10)
643
644  end subroutine inn_computeInnovation
645
646  !--------------------------------------------------------------------------
647  ! setObsMpiStrategy
648  !--------------------------------------------------------------------------
649  subroutine setObsMpiStrategy(obsSpaceData, hco_anl, mpiStrategy)
650    !
651    !:Purpose: To distribute header indices following the chosen strategy,
652    !          current options: "LIKESPLITFILES", "ROUNDROBIN", "LATLONTILES".
653    !
654    implicit none
655
656    ! Arguments:
657    type(struct_obs),          intent(inout) :: obsSpaceData
658    type(struct_hco), pointer, intent(in)    :: hco_anl
659    character(len=*),          intent(in)    :: mpiStrategy
660
661    ! Locals:
662    real(8) :: lat_r8, lon_r8
663    real    :: lat_r4, lon_r4
664    real    :: xpos_r4, ypos_r4
665    integer :: numHeaderFile, headerIndex, latIndex, lonIndex, ierr
666    integer :: IP, IP_x, IP_y
667    integer :: gdxyfll
668
669    !
670    !- Determine obs_ipc (column) and obs_ipt (tile) according to distribution strategy
671    !
672    write(*,*)
673    numHeaderFile = obs_numheader(obsSpaceData)
674    write(*,*) 'setObsMpiStrategy: numHeader after reading files = ',numHeaderFile
675    write(*,*) 'setObsMpiStrategy: strategy = ',trim(mpiStrategy)
676
677    ! make sure old PE index is not used (set to -1)
678    do headerIndex = 1, numHeaderFile
679       call obs_headSet_i(obsSpaceData,OBS_IP,headerIndex,-1)
680    end do
681
682    ! set PE index for the file (where reading and updating was/will be done)
683    if ( obs_columnActive_IH(obsSpaceData,OBS_IPF) ) then
684      do headerIndex = 1, numHeaderFile
685        call obs_headSet_i(obsSpaceData,OBS_IPF,headerIndex,mmpi_myid)
686      end do
687    end if
688
689    select case (trim(mpiStrategy))
690    case ('LIKESPLITFILES')
691       !- Keep distribution exactly as it is in the split files:
692       do headerIndex = 1, numHeaderFile
693          if ( obs_columnActive_IH(obsSpaceData,OBS_IPC) ) &
694            call obs_headSet_i(obsSpaceData,OBS_IPC,headerIndex, mmpi_myid)
695          if ( obs_columnActive_IH(obsSpaceData,OBS_IPT) ) &
696            call obs_headSet_i(obsSpaceData,OBS_IPT,headerIndex, mmpi_myid)
697          if ( obs_columnActive_IH(obsSpaceData,OBS_IP) ) &
698            call obs_headSet_i(obsSpaceData,OBS_IP,headerIndex, mmpi_myid)
699       end do
700    case ('ROUNDROBIN')
701       !- Distribute by a round-robin strategy for both obs_ipc and obs_ipt:
702       !  (Only use if files already not split by round robin)
703       do headerIndex = 1, numHeaderFile
704          IP = mod((headerIndex-1),mmpi_nprocs)
705          if ( obs_columnActive_IH(obsSpaceData,OBS_IPC) ) &
706            call obs_headSet_i(obsSpaceData,OBS_IPC,headerIndex, IP)
707          if ( obs_columnActive_IH(obsSpaceData,OBS_IPT) ) &
708            call obs_headSet_i(obsSpaceData,OBS_IPT,headerIndex, IP)
709          if ( obs_columnActive_IH(obsSpaceData,OBS_IP) ) &
710            call obs_headSet_i(obsSpaceData,OBS_IP ,headerIndex, IP)
711       end do
712    case ('LATLONTILES')
713       !- Distribute by latitude/longitude tiles for both obs_ipc and obs_ipt:
714       do headerIndex = 1, numHeaderFile
715          ! compute grid index for each observation header
716          lat_r8 = obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)
717          lon_r8 = obs_headElem_r(obsSpaceData,OBS_LON,headerIndex)
718          lat_r4 = real(lat_r8) * MPC_DEGREES_PER_RADIAN_R4
719          lon_r4 = real(lon_r8) * MPC_DEGREES_PER_RADIAN_R4
720          ierr = gdxyfll( hco_anl%EZscintID,   & ! IN 
721               xpos_r4, ypos_r4,    & ! OUT
722               lat_r4, lon_r4, 1 )    ! IN
723
724          ! compute correponding mpi task id for each observation
725          latIndex = floor(ypos_r4)
726          lonIndex = floor(xpos_r4)
727          IP_y = mmpi_myidYfromLat(latIndex, hco_anl%nj)
728          IP_x = mmpi_myidXfromLon(lonIndex, hco_anl%ni)
729          IP = IP_x + IP_y*mmpi_npex
730
731          call obs_headSet_i(obsSpaceData,OBS_IPC,headerIndex, IP)
732          call obs_headSet_i(obsSpaceData,OBS_IPT,headerIndex, IP)
733       end do
734    case ('LATLONTILESBALANCED')
735       !- Distribute by latitude/longitude tiles, but with simple & cheap balancing for obs_ipc (1 send or recv):
736       call utl_abort('setObsMpiStrategy: Sorry, LATLONTILESBALANCED no longer available')
737    case default
738       write(*,*)
739       write(*,*) 'ERROR unknown mpiStrategy: ', trim(mpiStrategy)
740       call utl_abort('setObsMpiStrategy')
741    end select
742
743  end subroutine setObsMpiStrategy
744
745  !--------------------------------------------------------------------------
746  ! inn_perturbObs
747  !--------------------------------------------------------------------------
748  subroutine inn_perturbObs(obsSpaceData,numAnalyses,indexAnalysis, &
749                            indexBatch,obs_column_index_src,obs_column_index_dest)
750    !
751    !:Purpose: To perturb the innovation vector to simulate effect of
752    !          observation uncertainty
753    !
754    !.. WARNING:: perturbations are not the same when MPI topology changes!!!
755    !
756    implicit none
757
758    ! Arguments:
759    type(struct_obs), intent(inout) :: obsSpaceData
760    integer,          intent(in)    :: numAnalyses
761    integer,          intent(in)    :: indexAnalysis
762    integer,          intent(in)    :: indexBatch
763    integer,          intent(in)    :: obs_column_index_src
764    integer,          intent(in)    :: obs_column_index_dest
765
766    ! Locals:
767    integer :: numPerturbations
768    integer :: nrandseed,iseed,indexAnalysis2,indexBody,indexFamily
769    integer, parameter :: numFamily=10
770    real*8  :: zmean,originalOmp
771    real*8  :: scaleFactor(numFamily)
772    character(len=2) :: familyList(numFamily)
773    real*8, save, pointer :: obsPerturbations(:,:) => NULL()
774    logical, save :: firstTime = .true.
775
776    familyList(1)='UA' ; scaleFactor(1)=1.00d0
777    familyList(2)='AI' ; scaleFactor(2)=1.00d0
778    familyList(3)='SF' ; scaleFactor(3)=1.00d0
779    familyList(4)='TO' ; scaleFactor(4)=1.00d0
780    familyList(5)='SW' ; scaleFactor(5)=1.00d0
781    familyList(6)='SC' ; scaleFactor(6)=1.00d0
782    familyList(7)='PR' ; scaleFactor(7)=1.00d0
783    familyList(8)='RO' ; scaleFactor(8)=1.00d0
784    familyList(9)='GP' ; scaleFactor(9)=1.00d0
785    familyList(10)='RA'; scaleFactor(10)=1.00d0    
786
787    numPerturbations = numAnalyses
788
789    if (firstTime) then
790
791       if (.not.associated(obsPerturbations)) then
792          write(*,*) 'perturbObs: allocating space for all perturbations'
793          allocate(obsPerturbations(obs_numBody(obsSpaceData),numPerturbations))
794       end if
795
796       write(*,*) 'perturbObs: computing random numbers'
797
798       ! compute random perturbations
799       do indexAnalysis2 = 1,numPerturbations
800          nrandseed   = indexAnalysis2 + (indexBatch-1)*numAnalyses
801          iseed     = ABS(nrandseed)
802          write(*,*) 'perturbobs: indexAnalysis, iseed=',indexAnalysis2, iseed
803          call rng_setup(iseed) ! JFC: should be called only once, no???
804          do indexBody = 1,obs_numBody(obsSpaceData)
805             obsPerturbations(indexBody,indexAnalysis2)=rng_gaussian()*obs_bodyElem_r(obsSpaceData,OBS_OER,indexBody)
806          end do
807       end do
808
809       ! apply scale factor
810       do indexFamily = 1,numFamily
811          do indexBody = 1,obs_numBody(obsSpaceData)      
812             if (obs_getFamily(obsSpaceData,bodyIndex_opt=indexBody).eq.familyList(indexFamily)) then
813                do indexAnalysis2 = 1,numPerturbations
814                   obsPerturbations(indexBody,indexAnalysis2)=obsPerturbations(indexBody,indexAnalysis2)*scaleFactor(indexFamily)
815                end do
816             end if
817          end do
818       end do
819
820       ! remove ensemble mean
821       do indexBody = 1,obs_numBody(obsSpaceData)      
822          zmean=0.0d0
823          do indexAnalysis2 = 1,numPerturbations
824             zmean=zmean+obsPerturbations(indexBody,indexAnalysis2)
825          end do
826          zmean=zmean/numPerturbations
827          do indexAnalysis2 = 1,numPerturbations
828             obsPerturbations(indexBody,indexAnalysis2)=obsPerturbations(indexBody,indexAnalysis2)-zmean
829          end do
830       end do
831
832       firstTime=.false.
833
834    end if
835
836    ! apply perturbation for current analysis
837    write(*,*) 'perturbObs: applying perturbation for analysis number: ',indexAnalysis
838    do indexBody = 1,obs_numBody(obsSpaceData)      
839       if (obs_bodyElem_i(obsSpaceData,OBS_ASS,indexBody) == obs_assimilated) then
840          originalOmp = obs_bodyElem_r(obsSpaceData,obs_column_index_src,indexBody)
841          call obs_bodySet_r(obsSpaceData,obs_column_index_dest,indexBody,originalOmp+obsPerturbations(indexBody,indexAnalysis))
842       end if
843    end do
844
845  end subroutine inn_perturbObs
846
847  !--------------------------------------------------------------------------
848  ! inn_getHcoVcoFromTrlmFile
849  !--------------------------------------------------------------------------
850  subroutine inn_getHcoVcoFromTrlmFile( hco_trl, vco_trl )
851    !
852    !:Purpose: Get hco/vco of the trials
853    !
854    implicit none
855
856    ! Arguments:
857    type(struct_hco), pointer, intent(inout) :: hco_trl
858    type(struct_vco), pointer, intent(inout) :: vco_trl
859
860    ! Locals:
861    character(len=4), pointer :: anlVar(:)
862
863    write(*,*) 'inn_getHcoVcoFromTrlmFile: START'
864    nullify(hco_trl,vco_trl)
865
866    ! check if gsv is initialized.
867    if ( .not. gsv_isInitialized() ) then
868      write(*,*)
869      write(*,*) 'inn_getHcoVcoFromTrlmFile: gsv_setup must be called first. Call it now'
870      call gsv_setup
871    end if
872
873    nullify(anlVar)
874    call gsv_varNamesList(anlVar)
875    call hco_SetupFromFile(hco_trl, './trlm_01', ' ', 'Trial', varName_opt=anlVar(1))
876
877    call vco_SetupFromFile(vco_trl, './trlm_01')
878
879    write(*,*) 'inn_getHcoVcoFromTrlmFile: END'
880
881  end subroutine inn_getHcoVcoFromTrlmFile
882
883end module innovation_mod