1program midas_letkf
2 !
3 !:Purpose: Main program for the local ensemble transform Kalman filter
4 ! (LETKF). Several different variations of the LETKF algorithm have
5 ! been implemented. Note that the actual calculation of the analyses
6 ! is in the subroutine ``enkf_LETKFanalyses``. Many aspects of this
7 ! program are controlled through the namelist block NAMLETKF.
8 !
9 ! ---
10 !
11 !:Algorithm: The ``letkf`` programs implements several variations of the LETKF
12 ! ensemble data assimilation algorithm. The following variations
13 ! can be chosen through the namelist:
14 !
15 ! - **LETKF**: standard LETKF
16 !
17 ! - **CVLETKF**: LETKF with deterministic approach to cross validation using
18 ! the "gain form" (currently operational)
19 !
20 ! - **CVLETKF-PERTOBS**: LETKF with stochastic approach to cross validation
21 !
22 ! - **LETKF-Gain**: standard LETKF, but using the "gain form"
23 !
24 ! - **LETKF-Gain-ME**: standard LETKF, but using the "modulated ensemble" and "gain
25 ! form" to account for vertical localization
26 !
27 ! - **CVLETKF-ME**: LETKF with deterministic approach to cross validation using
28 ! the "modulated ensemble" and "gain form" to account for
29 ! vertical localization
30 !
31 ! More detail on the LETKF algorithms with and without cross
32 ! validation (deterministic and stochastic) can be found in the
33 ! paper:
34 ! `LETKF with cross validation <https://journals.ametsoc.org/view/journals/mwre/148/6/MWR-D-19-0402.1.xml>`_
35 !
36 ! --
37 !
38 !============================================== ==============================================================
39 ! Input and Output Files (NWP applicaton) Description of file
40 !============================================== ==============================================================
41 ! ``flnml`` In - Main namelist file with parameters user may modify
42 ! ``flnml_static`` In - The "static" namelist that should not be modified
43 ! ``ensemble/$YYYYMMDDHH_006_$NNNN`` In - Background ensemble member files
44 ! ``obsfiles_$FAM/obs$FAM_$NNNN_$NNNN`` In - Observation file for each "family" and MPI task
45 ! ``obserr`` In - Observation error statistics
46 ! ``$YYYYMMDDHH_000_$NNNN`` Out - Analysis ensemble member files
47 ! ``obsfiles_$FAM.updated/obs$FAM_$NNNN_$NNNN`` Out - Updated obs file for each "family" and MPI task
48 ! Remainder are files related to radiance obs:
49 ! ``stats_$SENSOR_assim`` In - Satellite radiance observation errors of difference sensors
50 ! ``stats_tovs`` In - Satellite radiance observation errors
51 ! ``stats_tovs_symmetricObsErr`` In - User-defined symmetric TOVS errors for all sky
52 ! ``Cmat_$PLATFORM_$SENSOR.dat`` In - Inter-channel observation-error correlations
53 ! ``ceres_global.std`` In - High-res surface type and water fraction for radiance obs
54 ! ``rtcoef_$PLATFORM_$SENSOR.dat`` In - RTTOV coefficient files
55 ! ``rttov_h2o_limits.dat`` In - Min/max humidity limits applied to analysis
56 ! ``ozoneclim98`` In - Ozone climatology
57 !============================================== ==============================================================
58 !
59 ! --
60 !
61 !:Synopsis: Below is a summary of the ``letkf`` program calling sequence:
62 !
63 ! - **Initial setups:**
64 !
65 ! - Read the NAMLETKF namelist and check/modify some values.
66 !
67 ! - Various modules are setup: ``obsFiles_mod``,
68 ! ``gridStateVector_mod``, ``timeCoord_mod`` (and set up dates
69 ! and ``dateStampList`` variables for both trials and
70 ! increments/analyses).
71 !
72 ! - Setup horizontal and vertical grid objects from first
73 ! ensemble member file and determine if this is an NWP or
74 ! ocean application.
75 !
76 ! - Setup ``obsSpaceData`` object and read observations from
77 ! files: ``inn_setupObs``.
78 !
79 ! - Setup the observation error statistics in ``obsSpaceData``
80 ! object: ``oer_setObsErrors``.
81 !
82 ! - Allocate and some setup of objects for
83 ! ``ensembleObservations_mod``.
84 !
85 ! - Allocate objects for ``column_mod`` and
86 ! ``gridStateVector_mod``.
87 !
88 ! - Allocate ensemble object and read trial ensemble:
89 ! ``ens_readEnsemble``.
90 !
91 ! - Optionally, read a deterministic state for recentering the
92 ! trial ensemble before the analysis (for special applications
93 ! using LETKF for deterministic analysis).
94 !
95 ! - Compute ensemble mean: ``ens_computeMean``.
96 !
97 ! - **LETKF computations:**
98 !
99 ! - Loop over trial members, computing innovation for each, with
100 ! resulting :math:`H(xb)` being stored in ``ensObs`` objects both for
101 ! original ensemble and, optionally, for the modulated
102 ! ensemble members.
103 !
104 ! - Compute some additional quantities in ``ensObs`` objects based
105 ! on :math:`H(xb)` values.
106 !
107 ! - Compute :math:`y-H(xb)` for trial ensemble mean.
108 !
109 ! - Additional observation quality control procedures based on
110 ! quantities computed from trial ensemble.
111 !
112 ! - Gather ``ensObs`` quantities from all MPI tasks onto all MPI
113 ! tasks.
114 !
115 ! - Allocate and prepare objects for ``ensembleStateVector_mod``
116 ! used to store the trial and analysis ensembles with
117 ! temporal resolution of analysis (can be same as temporal
118 ! resolution for innovation calculation or only a single time
119 ! step).
120 !
121 ! - Setup information for interpolating weights from coarse grid
122 ! to the full model grid: ``enkf_setupInterpInfo``.
123 !
124 ! - Perform LETKF analysis to compute the analysis ensemble:
125 ! ``enkf_LETKFanalyses``
126 !
127 ! - **Final steps:**
128 !
129 ! - Optionally, compute :math:`H(xa)` for each member and put values in
130 ! ``ensObs`` object for later output to diag SQLite files.
131 !
132 ! - Write out (update) the observation files: ``obsf_writeFiles``.
133 !
134 ! - Compute :math:`y-H(xa)` for ensemble mean analysis, stored in
135 ! ``obsSpaceData``.
136 !
137 ! - Optionally, do post-processing of the analysis ensemble (same
138 ! functionality as the ``ensPostProcess`` program), or just
139 ! write out the raw analysis ensemble (for later processing by
140 ! ``ensPostProcess``).
141 !
142 ! --
143 !
144 !:Options: `List of namelist blocks <../namelists_in_each_program.html#letkf>`_
145 ! that can affect the ``letkf`` program.
146 !
147 ! * Some of the other relevant namelist blocks used to configure the
148 ! letkf analysis are listed in the following table:
149 !
150 !======================== ============== ==============================================================
151 ! Program/Module Namelist Description of what is controlled
152 !======================== ============== ==============================================================
153 ! ``midas_letkf`` ``NAMLETKF`` LETKF algorithm, number of ensemble members and
154 ! additional parameters for controlling the LETKF analysis
155 ! ``timeCoord_mod`` ``NAMTIME`` assimilation time window length, temporal resolution of
156 ! the background state and the analysis
157 !======================== ============== ==============================================================
158 !
159 use version_mod
160 use midasMpi_mod
161 use mathPhysConstants_mod
162 use fileNames_mod
163 use ensembleObservations_mod
164 use ensembleStateVector_mod
165 use gridStateVector_mod
166 use gridStateVectorFileIO_mod
167 use columnData_mod
168 use tovsNL_mod
169 use verticalCoord_mod
170 use horizontalCoord_mod
171 use oceanMask_mod
172 use timeCoord_mod
173 use obsTimeInterp_mod
174 use utilities_mod
175 use ramDisk_mod
176 use stateToColumn_mod
177 use obsFiles_mod
178 use obsSpaceData_mod
179 use obsErrors_mod
180 use obsFilter_mod
181 use innovation_mod
182 use enkf_mod
183 use ensPostProcess_mod
184 implicit none
185
186 type(struct_obs), target :: obsSpaceData
187 type(struct_ens), target :: ensembleTrl4D
188 type(struct_ens), pointer :: ensembleTrl
189 type(struct_ens) :: ensembleAnl
190 type(struct_gsv) :: stateVectorMeanTrl4D
191 type(struct_gsv) :: stateVectorMemberAnl
192 type(struct_gsv) :: stateVectorMeanAnl
193 type(struct_gsv) :: stateVector4D
194 type(struct_gsv) :: stateVector4Dmod
195 type(struct_gsv) :: stateVectorWithZandP4D
196 type(struct_gsv) :: stateVectorHeightSfc
197 type(struct_gsv) :: stateVectorCtrlTrl
198 type(struct_gsv) :: stateVectorRecenter
199 type(struct_columnData) :: column
200
201 type(struct_eob), target :: ensObs, ensObs_mpiglobal
202 type(struct_eob), pointer :: ensObsGain, ensObsGain_mpiglobal
203
204 type(struct_vco), pointer :: vco_ens => null()
205 type(struct_hco), pointer :: hco_ens => null()
206 type(struct_ocm) :: oceanMask
207
208 integer :: memberIndex, middleStepIndex, stepIndex, randomSeedObs
209 integer :: nulnam, dateStampFromObs, ierr
210 integer :: get_max_rss, fclos, fnom, fstopc
211 integer :: nEnsGain, eigenVectorIndex, memberIndexInEnsObs
212 integer, allocatable :: dateStampList(:), dateStampListInc(:)
213
214 character(len=256) :: ensFileName, ctrlFileName, recenterFileName
215 character(len=9) :: obsColumnMode
216 character(len=48) :: obsMpiStrategy
217 character(len=48) :: midasMode
218
219 logical :: nwpFields ! indicates if fields are on momentum and thermo levels
220 logical :: oceanFields ! indicates if fields are on depth levels
221 logical :: useModulatedEns ! using modulated ensembles is requested by setting numRetainedEigen
222
223 ! interpolation information for weights (in enkf_mod)
224 type(struct_enkfInterpInfo) :: wInterpInfo
225
226 real(4), pointer :: field_Psfc(:,:,:,:)
227
228 ! namelist variables
229 character(len=20) :: algorithm ! name of the chosen LETKF algorithm: 'LETKF', 'CVLETKF'
230 logical :: ensPostProcessing ! do all post-processing of analysis ensemble
231 logical :: recenterInputEns ! read a deterministic state to recenter ensemble
232 integer :: numSubEns ! number of sub-ensembles to split the full ensemble
233 character(len=256) :: ensPathName ! absolute or relative path to ensemble directory
234 integer :: nEns ! ensemble size
235 logical :: randomShuffleSubEns ! choose to randomly shuffle members into subensembles
236 logical :: writeLocalEnsObsToFile ! Controls writing the ensObs to file.
237 integer :: maxNumLocalObs ! maximum number of obs in each local volume to assimilate
238 integer :: weightLatLonStep ! separation of lat-lon grid points for weight calculation
239 integer :: numRetainedEigen ! number of retained eigenValues/Vectors of vertical localization matrix
240 logical :: modifyAmsubObsError ! reduce AMSU-B obs error stddev in tropics
241 logical :: backgroundCheck ! apply additional background check using ensemble spread
242 logical :: huberize ! apply huber norm quality control procedure
243 logical :: rejectHighLatIR ! reject all IR observations at high latitudes
244 logical :: rejectRadNearSfc ! reject radiance observations near the surface
245 logical :: ignoreEnsDate ! when reading ensemble, ignore the date
246 logical :: outputOnlyEnsMean ! when writing ensemble, can choose to only write member zero
247 logical :: outputEnsObs ! to write trial and analysis ensemble members in observation space to sqlite
248 logical :: debug ! debug option to print values to the listings.
249 logical :: readEnsObsFromFile ! instead of computing innovations, read ensObs%Yb from file.
250 real(8) :: hLocalize(4) ! horizontal localization radius (in km)
251 real(8) :: hLocalizePressure(3) ! pressures where horizontal localization changes (in hPa)
252 real(8) :: vLocalize ! vertical localization radius (units: ln(Pressure in Pa) or meters)
253 real(8) :: minDistanceToLand ! for ice/ocean DA: minimum distance to land for assimilating obs
254 character(len=20) :: obsTimeInterpType ! type of time interpolation to obs time
255 character(len=20) :: mpiDistribution ! type of mpiDistribution for weight calculation ('ROUNDROBIN' or 'TILES')
256 character(len=12) :: etiket_anl ! etiket for output files
257
258 NAMELIST /NAMLETKF/algorithm, ensPostProcessing, recenterInputEns, nEns, numSubEns, &
259 ensPathName, randomShuffleSubEns, &
260 hLocalize, hLocalizePressure, vLocalize, minDistanceToLand, &
261 maxNumLocalObs, weightLatLonStep, &
262 modifyAmsubObsError, backgroundCheck, huberize, rejectHighLatIR, rejectRadNearSfc, &
263 ignoreEnsDate, outputOnlyEnsMean, outputEnsObs, &
264 obsTimeInterpType, mpiDistribution, etiket_anl, &
265 readEnsObsFromFile, writeLocalEnsObsToFile, &
266 numRetainedEigen, debug
267
268 ! Some high-level configuration settings
269 midasMode = 'analysis'
270 obsColumnMode = 'ENKFMIDAS'
271 obsMpiStrategy = 'LIKESPLITFILES'
272
273 call ver_printNameAndVersion('letkf','Program for Local Ensemble Transform Kalman Filter')
274
275 !
276 !- 0. MPI, TMG and misc. initialization
277 !
278 call mmpi_initialize
279 call tmg_init(mmpi_myid, 'TMG_INFO')
280
281 call utl_tmg_start(0,'Main')
282
283 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
284
285 ! Avoid printing lots of stuff to listing for std file I/O
286 ierr = fstopc('MSGLVL','ERRORS',0)
287
288 ! Setup the ramdisk directory (if supplied)
289 call ram_setup
290
291 !
292 !- 1. Set/Read values for the namelist NAMLETKF
293 !
294
295 !- 1.1 Setting default namelist variable values
296 algorithm = 'LETKF'
297 ensPostProcessing = .false.
298 recenterInputEns = .false.
299 ensPathName = 'ensemble'
300 nEns = 10
301 numSubEns = 2
302 randomShuffleSubEns = .false.
303 maxNumLocalObs = 1000
304 weightLatLonStep = 1
305 modifyAmsubObsError = .false.
306 backgroundCheck = .false.
307 huberize = .false.
308 rejectHighLatIR = .false.
309 rejectRadNearSfc = .false.
310 ignoreEnsDate = .false.
311 outputOnlyEnsMean = .false.
312 outputEnsObs = .false.
313 hLocalize(:) = -1.0D0
314 hLocalizePressure = (/14.0D0, 140.0D0, 400.0D0/)
315 vLocalize = -1.0D0
316 minDistanceToLand = -1.0D0
317 obsTimeInterpType = 'LINEAR'
318 mpiDistribution = 'ROUNDROBIN'
319 etiket_anl = 'ENS_ANL'
320 readEnsObsFromFile = .false.
321 writeLocalEnsObsToFile = .false.
322 numRetainedEigen = 0
323 debug = .false.
324
325 !- 1.2 Read the namelist
326 nulnam = 0
327 ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
328 read(nulnam, nml=namletkf, iostat=ierr)
329 if ( ierr /= 0) call utl_abort('midas-letkf: Error reading namelist')
330 if ( mmpi_myid == 0 ) write(*,nml=namletkf)
331 ierr = fclos(nulnam)
332
333 !- 1.3 Some minor modifications of namelist values
334 if (hLocalize(1) > 0.0D0 .and. hLocalize(2) < 0.0D0) then
335 ! if only 1 value given for hLocalize, use it for entire column
336 hLocalize(2:4) = hLocalize(1)
337 if ( mmpi_myid == 0 ) write(*,*) 'midas-letkf: hLocalize(2:4) are modified after reading namelist. ' // &
338 'hLocalize(2:4)=', hLocalize(1)
339 else if ( hLocalize(1) < 0.0D0 ) then
340 call utl_abort('midas-letkf: hLocalize(1) < 0.0D0')
341 end if
342 hLocalize(:) = hLocalize(:) * 1000.0D0 ! convert from km to m
343 hLocalizePressure(:) = log(hLocalizePressure(:) * MPC_PA_PER_MBAR_R8)
344
345 if (minDistanceToLand > 0.0D0) then
346 minDistanceToLand = minDistanceToLand * 1000.0D0 ! convert from km to m
347 end if
348
349 if ( trim(algorithm) /= 'LETKF' .and. &
350 trim(algorithm) /= 'CVLETKF' .and. &
351 trim(algorithm) /= 'CVLETKF-PERTOBS' .and. &
352 trim(algorithm) /= 'LETKF-Gain' .and. &
353 trim(algorithm) /= 'LETKF-Gain-ME' .and. &
354 trim(algorithm) /= 'CVLETKF-ME' ) then
355 call utl_abort('midas-letkf: unknown LETKF algorithm: ' // trim(algorithm))
356 end if
357
358 if ( numRetainedEigen < 0 ) call utl_abort('midas-letkf: numRetainedEigen should be ' // &
359 'equal or greater than zero')
360
361 useModulatedEns = (numRetainedEigen > 0)
362
363 if ( trim(algorithm) == 'LETKF-Gain-ME' .or. trim(algorithm) == 'CVLETKF-ME' ) then
364 if ( .not. useModulatedEns ) call utl_abort('midas-letkf: numRetainedEigen should be ' // &
365 'equal or greater than one for LETKF algorithm: ' // &
366 trim(algorithm))
367 else
368 if ( useModulatedEns ) call utl_abort('midas-letkf: numRetainedEigen should be ' // &
369 'equal to zero for LETKF algorithm: ' // &
370 trim(algorithm))
371 end if
372
373 ! check for NO varying horizontal localization lengthscale in letkf with modulated ensembles.
374 if ( .not. all(hLocalize(2:4) == hLocalize(1)) .and. useModulatedEns ) then
375 call utl_abort('midas-letkf: Varying horizontal localization lengthscales is NOT allowed in ' // &
376 'letkf with modulated ensembles')
377 end if
378
379 !
380 !- 2. Initialization
381 !
382
383 !- 2.1 Setup the observation file names and get dateStamp from obs
384 call obsf_setup(dateStampFromObs, midasMode)
385
386 !- 2.2 Initialize date/time-related info
387
388 ! Setup timeCoord module, set dateStamp from env variable
389 call tim_setup()
390 if (tim_getDateStamp() == 0) then
391 if (dateStampFromObs > 0) then
392 ! use dateStamp from obs if not already set by env variable
393 call tim_setDateStamp(dateStampFromObs)
394 else
395 call utl_abort('midas-letkf: DateStamp was not set')
396 end if
397 end if
398 if (tim_nstepobsinc /= 1 .and. tim_nstepobsinc /= tim_nstepobs) then
399 call utl_abort('midas-letkf: invalid value for namelist variable DSTEPOBSINC. ' // &
400 'Increments can be either 3D or have same number of time steps as trials')
401 end if
402 allocate(dateStampList(tim_nstepobs))
403 call tim_getstamplist(dateStampList,tim_nstepobs,tim_getDatestamp())
404 allocate(dateStampListInc(tim_nstepobsinc))
405 call tim_getstamplist(dateStampListInc,tim_nstepobsinc,tim_getDatestamp())
406
407 write(*,*) 'midas-letkf: analysis dateStamp = ',tim_getDatestamp()
408
409 !- 2.3 Initialize variables of the model states
410 call gsv_setup
411
412 !- 2.4 Initialize the Ensemble grid
413 if (mmpi_myid == 0) write(*,*) ''
414 if (mmpi_myid == 0) write(*,*) 'midas-letkf: Set hco and vco parameters for ensemble grid'
415 call fln_ensFileName( ensFileName, ensPathName, memberIndex_opt=1, &
416 copyToRamDisk_opt=.false. )
417 call hco_SetupFromFile( hco_ens, ensFileName, ' ', 'ENSFILEGRID')
418 call vco_setupFromFile( vco_ens, ensFileName )
419 if (vco_getNumLev(vco_ens, 'MM') /= vco_getNumLev(vco_ens, 'TH')) then
420 call utl_abort('midas-letkf: nLev_M /= nLev_T - currently not supported')
421 end if
422 nwpFields = (vco_getNumLev(vco_ens,'TH') > 0 .or. vco_getNumLev(vco_ens,'MM') > 0)
423 oceanFields = (vco_getNumLev(vco_ens,'DP') > 0)
424 if (.not.nwpFields .and. .not.oceanFields) then
425 call utl_abort('midas-letkf: vertical coordinate does not contain nwp nor ocean fields')
426 end if
427
428 write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
429
430 !- 2.5 Read in the observations and other obs-related set up
431
432 ! Read the observations
433 call inn_setupObs( obsSpaceData, hco_ens, obsColumnMode, obsMpiStrategy, midasMode, &
434 obsClean_opt = .false. )
435
436 ! Initialize obs error covariances and set flag using 'util' column of stats_tovs
437 call oer_setObsErrors(obsSpaceData, midasMode, useTovsUtil_opt=.true.) ! IN
438
439 ! Call suprep again to filter out channels according to 'util' column of stats_tovs
440 call filt_suprep(obsSpaceData)
441
442 ! Allocate vectors for storing HX values
443 call eob_allocate(ensObs, nEns, obs_numBody(obsSpaceData), obsSpaceData)
444 if ( outputEnsObs ) allocate(ensObs%Ya_r4(ensObs%numMembers,ensObs%numObs))
445 call eob_zero(ensObs)
446 if ( useModulatedEns ) then
447 nEnsGain = nEns * numRetainedEigen
448 allocate(ensObsGain)
449 call eob_allocate(ensObsGain, nEnsGain, obs_numBody(obsSpaceData), obsSpaceData)
450 call eob_zero(ensObsGain)
451 else
452 ensObsGain => ensObs
453 end if
454
455 ! Set lat, lon, obs values in ensObs
456 call eob_setLatLonObs(ensObs)
457 if ( useModulatedEns ) call eob_setLatLonObs(ensObsGain)
458
459 !- 2.6 Initialize a single columnData object
460 write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
461 call col_setup
462 call col_setVco(column, vco_ens)
463 call col_allocate(column, obs_numheader(obsSpaceData), &
464 mpiLocal_opt=.true., setToZero_opt=.true.)
465 write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
466
467 !- 2.7 Read the sfc height from ensemble member 1 - only if we are doing NWP
468 if ( nwpFields ) then
469 call gsv_allocate( stateVectorHeightSfc, 1, hco_ens, vco_ens, dateStamp_opt=tim_getDateStamp(), &
470 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
471 dataKind_opt=4, allocHeightSfc_opt=.true., varNames_opt=(/'P0','TT'/) )
472 call gio_readFromFile( stateVectorHeightSfc, ensFileName, ' ', ' ', &
473 containsFullField_opt=.true., readHeightSfc_opt=.true. )
474 end if
475
476 !- 2.8 Allocate statevector related to ensemble mean
477 call gsv_allocate( stateVectorMeanTrl4D, tim_nstepobs, hco_ens, vco_ens, &
478 dateStamp_opt=tim_getDateStamp(), &
479 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
480 dataKind_opt=4, allocHeightSfc_opt=.true., &
481 allocHeight_opt=.false., allocPressure_opt=.false. )
482 call gsv_zero(stateVectorMeanTrl4D)
483 call gsv_allocate( stateVectorMeanAnl, tim_nstepobsinc, hco_ens, vco_ens, &
484 dateStamp_opt=tim_getDateStamp(), &
485 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
486 dataKind_opt=4, allocHeightSfc_opt=.true., &
487 allocHeight_opt=.false., allocPressure_opt=.false. )
488 call gsv_zero(stateVectorMeanAnl)
489 call gsv_allocate( stateVector4D, tim_nstepobs, hco_ens, vco_ens, &
490 dateStamp_opt=tim_getDateStamp(), &
491 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
492 dataKind_opt=4, allocHeightSfc_opt=.true., &
493 allocHeight_opt=.false., allocPressure_opt=.false. )
494 call gsv_zero(stateVector4D)
495 if ( useModulatedEns ) then
496 ! same as stateVector4D
497 call gsv_allocate( stateVector4Dmod, tim_nstepobs, hco_ens, vco_ens, &
498 dateStamp_opt=tim_getDateStamp(), &
499 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
500 dataKind_opt=4, allocHeightSfc_opt=.true., &
501 allocHeight_opt=.false., allocPressure_opt=.false. )
502 call gsv_zero(stateVector4Dmod)
503 end if
504
505 !- 2.9 Allocate statevector related to an analysis ensemble member
506 call gsv_allocate( stateVectorMemberAnl, tim_nstepobsinc, hco_ens, vco_ens, &
507 dateStamp_opt=tim_getDateStamp(), &
508 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
509 dataKind_opt=4, allocHeightSfc_opt=.true. )
510 call gsv_zero(stateVectorMemberAnl)
511
512 !- 2.10 Allocate statevector for storing state with heights and pressures allocated (for s2c_nl)
513 call gsv_allocate( stateVectorWithZandP4D, tim_nstepobs, hco_ens, vco_ens, &
514 dateStamp_opt=tim_getDateStamp(), &
515 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
516 dataKind_opt=4, allocHeightSfc_opt=.true. )
517 call gsv_zero(stateVectorWithZandP4D)
518
519 !- 2.11 Allocate ensembles, read the Trl ensemble
520 call utl_tmg_start(2,'--ReadEnsemble')
521 call ens_allocate(ensembleTrl4D, nEns, tim_nstepobs, hco_ens, vco_ens, dateStampList)
522 call ens_readEnsemble(ensembleTrl4D, ensPathName, biPeriodic=.false., &
523 ignoreDate_opt=ignoreEnsDate)
524 call utl_tmg_stop(2)
525
526 !- 2.12 If desired, read a deterministic state for recentering the ensemble
527 if (recenterInputEns) then
528 call gsv_allocate( stateVectorRecenter, tim_nstepobs, hco_ens, vco_ens, &
529 dateStamp_opt=tim_getDateStamp(), &
530 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
531 dataKind_opt=4, allocHeightSfc_opt=.false., &
532 allocHeight_opt=.false., allocPressure_opt=.false. )
533 call gsv_zero(stateVectorRecenter)
534 call fln_ensTrlFileName( recenterFileName, './', tim_getDateStamp() )
535 do stepIndex = 1, tim_nstepobs
536 call gio_readFromFile( stateVectorRecenter, recenterFileName, ' ', ' ', &
537 stepIndex_opt=stepIndex, containsFullField_opt=.true., &
538 readHeightSfc_opt=.false. )
539 end do
540 call ens_recenter( ensembleTrl4D, stateVectorRecenter, recenteringCoeffScalar_opt=1.0d0 )
541 call gsv_deallocate( stateVectorRecenter )
542 end if
543
544 !- 2.13 Compute ensemble mean and copy to meanTrl and meanAnl stateVectors
545 call ens_computeMean(ensembleTrl4D)
546 call ens_copyEnsMean(ensembleTrl4D, stateVectorMeanTrl4D)
547 if (tim_nstepobsinc < tim_nstepobs) then
548 call gsv_copy4Dto3D(stateVectorMeanTrl4D, stateVectorMeanAnl)
549 else
550 call gsv_copy(stateVectorMeanTrl4D, stateVectorMeanAnl)
551 end if
552
553 !
554 !- 3. Compute HX values with results in ensObs/ensObsGain
555 !
556
557 !- 3.1 Loop over all members and compute HX for each
558 if ( readEnsObsFromFile ) then
559 call eob_readFromFiles(ensObs, nEns, inputFilenamePrefix='eob_HX', readObsInfo=.true.)
560 if ( useModulatedEns ) then
561 call enkf_setupModulationFactor(stateVectorMeanTrl4D%vco, numRetainedEigen, nEns, vLocalize, &
562 beSilent=.true.)
563
564 ! refresh assimilation flag before reading the files
565 call eob_setAssFlag(ensObsGain)
566 call eob_readFromFiles(ensObsGain, nEnsGain, inputFilenamePrefix='eobGain_HX', readObsInfo=.false.)
567 end if
568 else
569 do memberIndex = 1, nEns
570
571 write(*,*) ''
572 write(*,*) 'midas-letkf: apply nonlinear H to ensemble member ', memberIndex
573 write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
574
575 ! copy 1 member to a stateVector
576 call ens_copyMember(ensembleTrl4D, stateVector4D, memberIndex)
577 call gsv_copy(stateVector4D, stateVectorWithZandP4D, allowVarMismatch_opt=.true., &
578 beSilent_opt=.true.)
579
580 ! copy the surface height field
581 if (nwpFields) then
582 call gsv_copyHeightSfc(stateVectorHeightSfc, stateVectorWithZandP4D)
583 end if
584
585 ! Compute and set Yb in ensObs
586 call s2c_nl( stateVectorWithZandP4D, obsSpaceData, column, hco_ens, &
587 timeInterpType=obsTimeInterpType, dealloc_opt=.false., &
588 beSilent_opt=.true. )
589
590 ! Compute Y-H(X) in OBS_OMP
591 call inn_computeInnovation(column, obsSpaceData, beSilent_opt=.true.)
592
593 ! Copy to ensObs: Y-HX for this member
594 call eob_setYb(ensObs, memberIndex)
595
596 ! Compute and set Yb in ensObsGain
597 do eigenVectorIndex = 1, numRetainedEigen
598 if ( mmpi_myid == 0 .and. debug ) then
599 write(*,*) 'midas-letkf: apply nonlinear H to modulated member ', &
600 eigenVectorIndex, '/', numRetainedEigen
601 end if
602
603 ! modulate the member with eigenvectors of vertical localization matrix
604 call enkf_getModulatedState( stateVector4D, stateVectorMeanTrl4D, &
605 vLocalize, numRetainedEigen, nEns, &
606 eigenVectorIndex, stateVector4Dmod, &
607 beSilent=.true. )
608 if ( debug ) then
609 call gsv_getField(stateVector4Dmod,field_Psfc,'P0')
610 write(*,*) 'midas-letkf: max(Psfc)=', maxval(field_Psfc), &
611 ', min(Psfc)=', minval(field_Psfc)
612 end if
613
614 call gsv_copy(stateVector4Dmod, stateVectorWithZandP4D, allowVarMismatch_opt=.true., &
615 beSilent_opt=.true.)
616 if (nwpFields) then
617 call gsv_copyHeightSfc(stateVectorHeightSfc, stateVectorWithZandP4D)
618 end if
619 call s2c_nl( stateVectorWithZandP4D, obsSpaceData, column, hco_ens, &
620 timeInterpType=obsTimeInterpType, dealloc_opt=.false., &
621 beSilent_opt=.true. )
622
623 ! Compute Y-H(X) in OBS_OMP
624 call inn_computeInnovation( column, obsSpaceData, filterObsAndInitOer_opt=.false., &
625 beSilent_opt=.true. )
626
627 ! Copy to ensObsGain: Y-HX for this member
628 memberIndexInEnsObs = (eigenVectorIndex - 1) * nEns + memberIndex
629 call eob_setYb(ensObsGain, memberIndexInEnsObs)
630 end do ! eigenVectorIndex
631
632 end do ! memberIndex
633 end if
634 if ( gsv_isAllocated(stateVector4Dmod) ) call gsv_deallocate(stateVector4Dmod)
635
636 ! write local ensObs to file
637 if (writeLocalEnsObsToFile) then
638 call eob_writeToFiles(ensObs, outputFilenamePrefix='eob_HX', writeObsInfo=.true.)
639 if (useModulatedEns) then
640 call eob_writeToFiles(ensObsGain, outputFilenamePrefix='eobGain_HX', writeObsInfo=.false., &
641 numGroupsToDivideMembers_opt=numRetainedEigen, &
642 maxNumMembersPerGroup_opt=nEns)
643 end if
644 end if
645
646 !- 3.2 Set some additional information in ensObs/ensObsGain and additional quality
647 ! control before finally communicating ensObs/ensObsGain globally
648
649 ! Compute and remove the mean of Yb
650 call eob_calcAndRemoveMeanYb(ensObs)
651 if ( useModulatedEns ) call eob_calcAndRemoveMeanYb(ensObsGain)
652
653 ! Put HPHT in OBS_HPHT, for writing to obs files
654 call eob_setHPHT(ensObs)
655 if ( useModulatedEns ) call eob_setHPHT(ensObsGain)
656
657 ! Compute random observation perturbations
658 if (trim(algorithm) == 'CVLETKF-PERTOBS') then
659 randomSeedObs = 1 + mmpi_myid
660 call eob_calcRandPert(ensObs, randomSeedObs)
661 end if
662
663 ! Apply obs operators to ensemble mean background for several purposes
664 write(*,*) ''
665 write(*,*) 'midas-letkf: apply nonlinear H to ensemble mean background'
666 write(*,*) ''
667 call gsv_copy(stateVectorMeanTrl4D, stateVectorWithZandP4D, allowVarMismatch_opt=.true.)
668 if (nwpFields) then
669 call gsv_copyHeightSfc(stateVectorHeightSfc, stateVectorWithZandP4D)
670 end if
671 call s2c_nl( stateVectorWithZandP4D, obsSpaceData, column, hco_ens, &
672 timeInterpType=obsTimeInterpType, dealloc_opt=.false. )
673 call tvs_allocTransmission(col_getNumLev(column,'TH')) ! this will cause radiative transmission profiles to be stored for use in eob_setVertLocation
674 call inn_computeInnovation(column, obsSpaceData, beSilent_opt=.false.)
675
676 ! Put y-mean(H(X)) in OBS_OMP for writing to obs files (overwrites y-H(mean(X)))
677 call eob_setMeanOMP(ensObs)
678 if ( useModulatedEns ) call eob_setMeanOMP(ensObsGain)
679
680 ! Set y for family of interest to mean(H(x)) if doing simulated observations
681 call eob_setSimObsVal(ensObs)
682
683 ! Set vertical location for all obs for vertical localization (based on ensemble mean pressure and height)
684 if (vLocalize > 0.0d0) then
685 if (nwpFields) then
686 call eob_setTypeVertCoord(ensObs,'logPressure')
687 if ( useModulatedEns ) call eob_setTypeVertCoord(ensObsGain,'logPressure')
688 else if (oceanFields) then
689 call eob_setTypeVertCoord(ensObs,'depth')
690 if ( useModulatedEns ) call eob_setTypeVertCoord(ensObsGain,'depth')
691 end if
692 call eob_setVertLocation(ensObs, column)
693 if ( useModulatedEns ) call eob_setVertLocation(ensObsGain, column)
694 end if
695
696 ! Modify the obs error stddev for AMSUB in the tropics
697 if (modifyAmsubObsError) call enkf_modifyAmsubObsError(obsSpaceData)
698
699 ! Apply a background check (reject limit is set in the routine)
700 if (backgroundCheck) call eob_backgroundCheck(ensObs)
701
702 ! For ice/ocean DA: remove obs that are too close to land
703 if (minDistanceToLand > 0.0D0) then
704 call ens_getMask(ensembleTrl4D,oceanMask)
705 call eob_removeObsNearLand(ensObs, oceanMask, minDistanceToLand)
706 end if
707
708 ! Set values of obs_sigi and obs_sigo before hubernorm modifies obs_oer
709 call eob_setSigiSigo(ensObs)
710
711 ! Apply huber norm quality control procedure (modifies obs_oer)
712 if (huberize) call eob_huberNorm(ensObs)
713
714 !- Reject all IR radiance observation in arctic and antarctic (.i.e |lat|>60. )
715 if (rejectHighLatIR) call enkf_rejectHighLatIR(obsSpaceData)
716
717 ! Reject radiance observations too close to the surface
718 if (rejectRadNearSfc) call eob_rejectRadNearSfc(ensObs)
719
720 ! Compute inverse of obs error variance (done here to use dynamic GPS-RO, GB-GPS based on mean O-P)
721 call eob_setObsErrInv(ensObs)
722 if ( useModulatedEns ) call eob_setObsErrInv(ensObsGain)
723
724 call utl_tmg_start(141,'----Barr')
725 call rpn_comm_barrier('GRID',ierr)
726 call utl_tmg_stop(141)
727
728 ! Clean and globally communicate obs-related data to all mpi tasks
729 call eob_allGather(ensObs,ensObs_mpiglobal)
730 if ( useModulatedEns ) then
731 allocate(ensObsGain_mpiglobal)
732 call eob_allGather(ensObsGain,ensObsGain_mpiglobal)
733 else
734 ensObsGain_mpiglobal => ensObs_mpiglobal
735 end if
736
737 ! Print number of assimilated obs per family to the listing
738 write(*,*) 'oti_timeBinning: After extra filtering done in midas-letkf'
739 call oti_timeBinning(obsSpaceData,tim_nstepobs)
740
741 !
742 !- 4. Final preparations for computing analyses
743 !
744
745 !- 4.1 Copy trial ensemble to nstepobsinc time steps
746 if (tim_nstepobsinc < tim_nstepobs) then
747 allocate(ensembleTrl)
748 call ens_allocate(ensembleTrl, nEns, tim_nstepobsinc, hco_ens, vco_ens, dateStampListInc)
749 call ens_copy4Dto3D(ensembleTrl4D,ensembleTrl)
750 call ens_deallocate(ensembleTrl4D)
751 else
752 ! number of timesteps is the same, so just point to it
753 ensembleTrl => ensembleTrl4D
754 end if
755
756 !- 4.2 Copy trl ensemble to anl ensemble
757 call ens_allocate(ensembleAnl, nEns, tim_nstepobsinc, hco_ens, vco_ens, dateStampListInc)
758 call ens_copy(ensembleTrl,ensembleAnl)
759
760 !- 4.3 Setup for interpolating weights from coarse to full resolution
761 call enkf_setupInterpInfo(wInterpInfo, stateVectorMeanAnl%hco, weightLatLonStep, &
762 stateVectorMeanAnl%myLonBeg, stateVectorMeanAnl%myLonEnd, &
763 stateVectorMeanAnl%myLatBeg, stateVectorMeanAnl%myLatEnd)
764
765 !
766 !- 5. Main calculation of ensemble analyses
767 !
768
769 !- 5.1 Call to perform LETKF
770 call enkf_LETKFanalyses(algorithm, numSubEns, randomShuffleSubEns, &
771 ensembleAnl, ensembleTrl, &
772 ensObs_mpiglobal, ensObsGain_mpiglobal, &
773 stateVectorMeanAnl, &
774 wInterpInfo, maxNumLocalObs, &
775 hLocalize, hLocalizePressure, vLocalize, &
776 mpiDistribution, numRetainedEigen)
777
778 !- 5.2 Loop over all analysis members and compute H(Xa_member) (if output is desired)
779 if ( outputEnsObs ) then
780
781 do memberIndex = 1, nEns
782
783 write(*,*) ''
784 write(*,*) 'midas-letkf: apply nonlinear H to analysis ensemble member ', memberIndex
785 write(*,*) ''
786
787 ! copy 1 member to a stateVector
788 call ens_copyMember(ensembleAnl, stateVectorMemberAnl, memberIndex)
789
790 if (tim_nstepobsinc < tim_nstepobs) then
791 ! ensembleAnl is only 3D, so need to make 4D for s2c_nl
792 middleStepIndex = (tim_nstepobs + 1) / 2
793 call gsv_copy(stateVectorMemberAnl, stateVectorWithZandP4D, allowVarMismatch_opt=.true., stepIndexOut_opt=middleStepIndex)
794 call gsv_3dto4d(stateVectorWithZandP4D)
795 else
796 call gsv_copy(stateVectorMemberAnl, stateVectorWithZandP4D, allowVarMismatch_opt=.true.)
797 end if
798
799 ! copy the surface height field
800 if (nwpFields) then
801 call gsv_copyHeightSfc(stateVectorHeightSfc, stateVectorWithZandP4D)
802 end if
803
804 call s2c_nl( stateVectorWithZandP4D, obsSpaceData, column, hco_ens, &
805 timeInterpType=obsTimeInterpType, dealloc_opt=.false. )
806
807 ! Compute Y-H(Xa) in OBS_OMAM (used instead of OBS_OMA so that obsSpaceData isn't unintentionally modified)
808 call inn_computeInnovation(column, obsSpaceData, destObsColumn_opt=OBS_OMAM, beSilent_opt=.true., &
809 callFiltTopo_opt=.false., callSetErrGpsgb_opt=.false., analysisMode_opt=.false.)
810
811 ! Copy to ensObs: Y-H(Xa_member) for this member
812 call eob_setYa(ensObs, memberIndex, OBS_OMAM)
813
814 end do
815
816 end if
817
818 !- 6. Output obs files with mean OMP and (unrecentered) OMA
819
820 ! Compute Y-H(Xa_mean) in OBS_OMA
821 write(*,*) ''
822 write(*,*) 'midas-letkf: apply nonlinear H to ensemble mean analysis'
823 write(*,*) ''
824 if (tim_nstepobsinc < tim_nstepobs) then
825 ! meanAnl is only 3D, so need to make 4D for s2c_nl
826 middleStepIndex = (tim_nstepobs + 1) / 2
827 call gsv_copy(stateVectorMeanAnl, stateVectorWithZandP4D, allowVarMismatch_opt=.true., stepIndexOut_opt=middleStepIndex)
828 call gsv_3dto4d(stateVectorWithZandP4D)
829 else
830 call gsv_copy(stateVectorMeanAnl, stateVectorWithZandP4D, allowVarMismatch_opt=.true.)
831 end if
832 if (nwpFields) then
833 call gsv_copyHeightSfc(stateVectorHeightSfc, stateVectorWithZandP4D)
834 end if
835 call s2c_nl( stateVectorWithZandP4D, obsSpaceData, column, hco_ens, &
836 timeInterpType=obsTimeInterpType )
837 call inn_computeInnovation(column, obsSpaceData, destObsColumn_opt=OBS_OMA, beSilent_opt=.false.)
838
839 ! Write (update) observation files.
840 if (outputEnsObs) then
841 call obsf_writeFiles( obsSpaceData, ensObs_opt=ensObs)
842 else
843 call obsf_writeFiles( obsSpaceData)
844 end if
845
846 !- 7. Post processing of the analysis results (if desired) and write everything to files
847 if (ensPostProcessing) then
848 !- Allocate and read the Trl control member (used to compute control member increment for IAU)
849 call gsv_allocate( stateVectorCtrlTrl, tim_nstepobsinc, hco_ens, vco_ens, &
850 dateStamp_opt=tim_getDateStamp(), &
851 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
852 dataKind_opt=4, allocHeightSfc_opt=.true., &
853 allocHeight_opt=.false., allocPressure_opt=.false. )
854 if (recenterInputEns) then
855 !- Use the deterministic trial, if we are recentering the input ensemble
856 ctrlFileName = trim(recenterFileName)
857 else
858 !- Otherwise, use member 0000
859 call fln_ensFileName(ctrlFileName, ensPathName, memberIndex_opt=0, &
860 copyToRamDisk_opt=.false.)
861 end if
862 do stepIndex = 1, tim_nstepobsinc
863 call gio_readFromFile( stateVectorCtrlTrl, ctrlFileName, ' ', ' ', &
864 stepIndex_opt=stepIndex, containsFullField_opt=.true., &
865 readHeightSfc_opt=.false. )
866 end do
867
868 call epp_postProcess(ensembleTrl, ensembleAnl, stateVectorHeightSfc, stateVectorCtrlTrl, &
869 writeTrlEnsemble=.false., outputOnlyEnsMean_opt=outputOnlyEnsMean)
870 else
871 !- Just write the raw analysis ensemble to files
872 if (mmpi_myid == 0) then
873 write(*,*) 'midas-letkf: No ensemble post-processing requested, so just write the raw analysis ensemble'
874 end if
875 call utl_tmg_start(3,'--WriteEnsemble')
876 if (.not. outputOnlyEnsMean) then
877 call ens_writeEnsemble(ensembleAnl, '.', '', etiket_anl, 'A', &
878 numBits_opt=16, etiketAppendMemberNumber_opt=.true., &
879 containsFullField_opt=.true.)
880 end if
881 call utl_tmg_stop(3)
882
883 end if
884
885 !
886 !- 8. MPI, tmg finalize
887 !
888 write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
889 call utl_tmg_stop(0)
890
891 call tmg_terminate(mmpi_myid, 'TMG_INFO')
892 call rpn_comm_finalize(ierr)
893
894 write(*,*) 'Memory Used: ', get_max_rss()/1024, 'Mb'
895
896 if ( mmpi_myid == 0 ) write(*,*) ' --------------------------------'
897 if ( mmpi_myid == 0 ) write(*,*) ' MIDAS-LETKF ENDS'
898 if ( mmpi_myid == 0 ) write(*,*) ' --------------------------------'
899
900end program midas_letkf