1module enkf_mod
2 ! MODULE enkf_mod (prefix='enkf' category='1. High-level functionality')
3 !
4 !:Purpose: Various routines that are useful for implementing
5 ! an EnKF in MIDAS, including the LETKF.
6 !
7 use mpi, only : mpi_statuses_ignore ! this is the mpi library module
8 use midasMpi_mod
9 use utilities_mod
10 use mathPhysConstants_mod
11 use timeCoord_mod
12 use verticalCoord_mod
13 use horizontalCoord_mod
14 use ensembleStateVector_mod
15 use gridStateVector_mod
16 use obsSpaceData_mod
17 use tovsNL_mod
18 use ensembleObservations_mod
19 use gridVariableTransforms_mod
20 use localizationFunction_mod
21 use varNameList_mod
22 use codePrecision_mod
23 use codTyp_mod
24 use calcHeightAndPressure_mod
25 implicit none
26 save
27 private
28
29 ! public types
30 public :: struct_enkfInterpInfo
31
32 ! public procedures
33 public :: enkf_setupInterpInfo, enkf_LETKFanalyses, enkf_modifyAMSUBobsError
34 public :: enkf_rejectHighLatIR, enkf_getModulatedState, enkf_setupModulationFactor
35
36 ! for weight interpolation
37 type struct_enkfInterpInfo
38 integer :: latLonStep
39 integer, allocatable :: numIndexes(:,:)
40 integer, allocatable :: lonIndexes(:,:,:)
41 integer, allocatable :: latIndexes(:,:,:)
42 real(8), allocatable :: interpWeights(:,:,:)
43 integer :: myLonBegHalo
44 integer :: myLonEndHalo
45 integer :: myLatBegHalo
46 integer :: myLatEndHalo
47 integer :: myLonBeg
48 integer :: myLonEnd
49 integer :: myLatBeg
50 integer :: myLatEnd
51 end type struct_enkfInterpInfo
52
53 integer, external :: get_max_rss
54
55contains
56
57 !----------------------------------------------------------------------
58 ! enkf_LETKFanalyses
59 !----------------------------------------------------------------------
60 subroutine enkf_LETKFanalyses(algorithm, numSubEns, randomShuffleSubEns, &
61 ensembleAnl, ensembleTrl, &
62 ensObs_mpiglobal, ensObsGain_mpiglobal, &
63 stateVectorMeanAnl, &
64 wInterpInfo, maxNumLocalObs, &
65 hLocalize, hLocalizePressure, vLocalize, &
66 mpiDistribution, numRetainedEigen)
67 !
68 !:Purpose: Local subroutine containing the code for computing
69 ! the LETKF analyses for all ensemble members, ensemble
70 ! mean.
71 !
72 implicit none
73
74 ! Arguments:
75 character(len=*), intent(in) :: algorithm
76 integer , intent(in) :: numSubEns
77 logical , intent(in) :: randomShuffleSubEns
78 type(struct_ens), pointer, intent(inout) :: ensembleTrl
79 type(struct_ens), intent(inout) :: ensembleAnl
80 type(struct_eob), target, intent(in) :: ensObs_mpiglobal
81 type(struct_eob), intent(in) :: ensObsGain_mpiglobal
82 type(struct_gsv), intent(in) :: stateVectorMeanAnl
83 type(struct_enkfInterpInfo), intent(in) :: wInterpInfo
84 integer, intent(in) :: maxNumLocalObs
85 real(8), intent(in) :: hLocalize(:)
86 real(8), intent(in) :: hLocalizePressure(:)
87 real(8), intent(in) :: vLocalize
88 character(len=*), intent(in) :: mpiDistribution
89 integer, intent(in) :: numRetainedEigen
90
91 ! Locals:
92 integer :: nEns, nEnsPerSubEns, nEnsPerSubEns_mod, nEnsIndependentPerSubEns
93 integer :: nLev_M, nLev_depth, nLev_weights
94 integer :: memberIndex, memberIndex1, memberIndex2, ierr, matrixRank
95 integer :: memberIndexCV, memberIndexCV1, memberIndexCV2
96 integer :: procIndex, procIndexSend, hLocIndex
97 integer :: latIndex, lonIndex, stepIndex, varLevIndex, levIndex, levIndex2
98 integer :: bodyIndex, localObsIndex, numLocalObs, numLocalObsFound
99 integer :: countMaxExceeded, maxCountMaxExceeded, numGridPointWeights
100 integer :: myNumLatLonRecv, myNumLatLonSend
101 integer :: latLonIndex, subEnsIndex, subEnsIndex2
102 integer :: sendTag, recvTag, nsize, numRecv, numSend
103 integer :: myLonBeg, myLonEnd, myLatBeg, myLatEnd, numVarLev
104 integer :: myLonBegHalo, myLonEndHalo, myLatBegHalo, myLatEndHalo
105 integer :: imode, dateStamp, timePrint, datePrint, randomSeed, newDate
106 integer :: nEnsGain, eigenVectorColumnIndex
107 integer :: memberIndexInModEns
108 real(8) :: anlLat, anlLon, anlVertLocation
109 real(8) :: distance, tolerance, localization
110 real(4) :: modulationFactor_r4
111
112 integer, allocatable :: localBodyIndices(:), levFromK(:)
113 integer, allocatable :: myLatIndexesRecv(:), myLonIndexesRecv(:)
114 integer, allocatable :: myLatIndexesSend(:), myLonIndexesSend(:)
115 integer, allocatable :: myNumProcIndexesSend(:)
116 integer, allocatable :: myProcIndexesRecv(:), myProcIndexesSend(:,:)
117 integer, allocatable :: requestIdRecv(:), requestIdSend(:)
118 integer, allocatable :: memberIndexSubEns(:,:), memberIndexSubEns_mod(:,:)
119 integer, allocatable :: memberIndexSubEnsComp(:,:)
120 integer, allocatable :: randomMemberIndexArray(:), latLonTagMpiGlobal(:,:)
121
122 real(8), pointer :: PaInv_mean(:,:), Pa_mean(:,:)
123 real(8), pointer :: YbTinvR_mean(:,:), YbTinvRCopy_mean(:,:), YbTinvRYb_mean(:,:)
124 real(8), pointer :: eigenValues_mean(:), eigenVectors_mean(:,:)
125
126 real(8), allocatable, target :: PaInv_pert(:,:), Pa_pert(:,:)
127 real(8), allocatable, target :: YbTinvR_pert(:,:),YbTinvRCopy_pert(:,:), YbTinvRYb_pert(:,:)
128 real(8), allocatable, target :: eigenValues_pert(:), eigenVectors_pert(:,:)
129
130 real(8), allocatable :: distances(:), PaSqrt_pert(:,:)
131 real(8), allocatable :: YbTinvRYb_CV(:,:), YbTinvRYb_mod(:,:)
132 real(8), allocatable :: eigenValues_CV(:), eigenVectors_CV(:,:)
133 real(8), allocatable :: weightsTemp(:), weightsTemp2(:)
134 real(8), allocatable :: weightsMembers(:,:,:,:), weightsMembersLatLon(:,:,:)
135 real(8), allocatable :: weightsMean(:,:,:,:), weightsMeanLatLon(:,:,:)
136 real(8), allocatable :: memberAnlPert(:)
137 real(4), allocatable :: vertLocation_r4(:,:,:), YbCopy_r4(:,:), YbGainCopy_r4(:,:)
138
139 real(4), pointer :: meanTrl_ptr_r4(:,:,:,:), meanAnl_ptr_r4(:,:,:,:), meanInc_ptr_r4(:,:,:,:)
140 real(4), pointer :: memberTrl_ptr_r4(:,:,:,:), memberAnl_ptr_r4(:,:,:,:)
141 real(4) :: pert_r4
142
143 character(len=4) :: varName
144 character(len=2), allocatable :: varKindFromK(:)
145 character(len=4), allocatable :: varLevelFromK(:)
146
147 type(struct_hco), pointer :: hco_ens
148 type(struct_vco), pointer :: vco_ens
149 type(struct_gsv) :: stateVectorMeanInc
150 type(struct_gsv) :: stateVectorMeanTrl
151
152 logical :: hLocalizeIsConstant, useModulatedEns, firstTime = .true.
153
154 call utl_tmg_start(131,'--LETKFanalysis')
155
156 write(*,*) 'enkf_LETKFanalyses: starting'
157 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
158
159 !
160 ! Set things up for the redistribution of work across mpi tasks
161 !
162 call enkf_LETKFsetupMpiDistribution(myNumLatLonRecv, myNumLatLonSend, &
163 myLatIndexesRecv, myLonIndexesRecv, &
164 myLatIndexesSend, myLonIndexesSend, &
165 myProcIndexesRecv, myProcIndexesSend, &
166 myNumProcIndexesSend, mpiDistribution, wInterpInfo)
167 allocate(requestIdSend(3*myNumLatLonSend*maxval(myNumProcIndexesSend)))
168 allocate(requestIdRecv(3*myNumLatLonRecv))
169
170 nEns = ens_getNumMembers(ensembleAnl)
171 useModulatedEns = (numRetainedEigen > 0)
172 if ( useModulatedEns ) then
173 nEnsGain = nEns * numRetainedEigen
174 else
175 nEnsGain = nEns
176 end if
177 nLev_M = ens_getNumLev(ensembleAnl, 'MM')
178 nLev_depth = ens_getNumLev(ensembleAnl, 'DP')
179 nLev_weights = max(nLev_M,nLev_depth)
180 if ( useModulatedEns ) nLev_weights = 1
181 hco_ens => ens_getHco(ensembleAnl)
182 vco_ens => ens_getVco(ensembleAnl)
183 myLonBeg = stateVectorMeanAnl%myLonBeg
184 myLonEnd = stateVectorMeanAnl%myLonEnd
185 myLatBeg = stateVectorMeanAnl%myLatBeg
186 myLatEnd = stateVectorMeanAnl%myLatEnd
187 numVarLev = stateVectorMeanAnl%nk
188 myLonBegHalo = wInterpInfo%myLonBegHalo
189 myLonEndHalo = wInterpInfo%myLonEndHalo
190 myLatBegHalo = wInterpInfo%myLatBegHalo
191 myLatEndHalo = wInterpInfo%myLatEndHalo
192
193 !
194 ! Compute gridded 3D ensemble weights
195 !
196 allocate(localBodyIndices(maxNumLocalObs))
197 allocate(distances(maxNumLocalObs))
198 allocate(YbTinvR_pert(nEnsGain,maxNumLocalObs))
199 allocate(YbTinvRCopy_pert(maxNumLocalObs,nEnsGain))
200 allocate(YbGainCopy_r4(maxNumLocalObs,nEnsGain))
201 allocate(YbTinvRYb_pert(nEnsGain,nEnsGain))
202 if ( trim(algorithm) == 'CVLETKF-ME' .or. &
203 trim(algorithm) == 'LETKF-Gain-ME' ) then
204 allocate(YbTinvRYb_mod(nEnsGain,nEns))
205 allocate(YbCopy_r4(maxNumLocalObs,nEns))
206 end if
207 allocate(eigenValues_pert(nEnsGain))
208 allocate(eigenVectors_pert(nEnsGain,nEnsGain))
209 allocate(PaInv_pert(nEnsGain,nEnsGain))
210 allocate(PaSqrt_pert(nEnsGain,nEnsGain))
211 allocate(Pa_pert(nEnsGain,nEnsGain))
212
213 if (eob_simObsAssim) then
214 allocate(YbTinvR_mean(nEnsGain,maxNumLocalObs))
215 allocate(YbTinvRCopy_mean(maxNumLocalObs,nEnsGain))
216 allocate(YbTinvRYb_mean(nEnsGain,nEnsGain))
217 allocate(eigenValues_mean(nEnsGain))
218 allocate(eigenVectors_mean(nEnsGain,nEnsGain))
219 allocate(PaInv_mean(nEnsGain,nEnsGain))
220 allocate(Pa_mean(nEnsGain,nEnsGain))
221 else
222 YbTinvR_mean => YbTinvR_pert
223 YbTinvRCopy_mean => YbTinvRCopy_pert
224 YbTinvRYb_mean => YbTinvRYb_pert
225 eigenValues_mean => eigenValues_pert
226 eigenVectors_mean => eigenVectors_pert
227 PaInv_mean => PaInv_pert
228 Pa_mean => Pa_pert
229 end if
230
231 allocate(memberAnlPert(nEns))
232 allocate(weightsTemp(nEnsGain))
233 allocate(weightsTemp2(nEnsGain))
234 weightsTemp(:) = 0.0d0
235 weightsTemp2(:) = 0.0d0
236 ! Weights for mean analysis
237 allocate(weightsMean(nEnsGain,1,myLonBegHalo:myLonEndHalo,myLatBegHalo:myLatEndHalo))
238 weightsMean(:,:,:,:) = 0.0d0
239 allocate(weightsMeanLatLon(nEnsGain,1,myNumLatLonSend))
240 weightsMeanLatLon(:,:,:) = 0.0d0
241 ! Weights for member analyses
242 allocate(weightsMembers(nEnsGain,nEns,myLonBegHalo:myLonEndHalo,myLatBegHalo:myLatEndHalo))
243 weightsMembers(:,:,:,:) = 0.0d0
244 allocate(weightsMembersLatLon(nEnsGain,nEns,myNumLatLonSend))
245 weightsMembersLatLon(:,:,:) = 0.0d0
246
247 call gsv_allocate( stateVectorMeanTrl, tim_nstepobsinc, hco_ens, vco_ens, dateStamp_opt=tim_getDateStamp(), &
248 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
249 dataKind_opt=4, allocHeightSfc_opt=.true., &
250 allocHeight_opt=.false., allocPressure_opt=.false. )
251 call gsv_zero(stateVectorMeanTrl)
252 call gsv_allocate( stateVectorMeanInc, tim_nstepobsinc, hco_ens, vco_ens, dateStamp_opt=tim_getDateStamp(), &
253 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
254 dataKind_opt=4, allocHeightSfc_opt=.true., &
255 allocHeight_opt=.false., allocPressure_opt=.false. )
256 call gsv_zero(stateVectorMeanInc)
257
258 call ens_computeMean(ensembleTrl)
259 call ens_copyEnsMean(ensembleTrl, stateVectorMeanTrl)
260
261 ! Quantities needed for CVLETKF and CVLETKF-PERTOBS and CVLETKF-ME
262 if ( trim(algorithm) == 'CVLETKF' .or. trim(algorithm) == 'CVLETKF-PERTOBS' .or. &
263 trim(algorithm) == 'CVLETKF-ME' ) then
264 nEnsPerSubEns = nEns / numSubEns
265 if ( (nEnsPerSubEns * numSubEns) /= nEns ) then
266 call utl_abort('enkf_LETKFanalyses: ensemble size not divisible by numSubEnsembles')
267 end if
268 if (numSubEns <= 1) then
269 call utl_abort('enkf_LETKFanalyses: for CVLETKF(-PERTOBS)(-ME) algorithm, numSubEns must be greater than 1')
270 end if
271 if ( .not. useModulatedEns ) then
272 nEnsIndependentPerSubEns = nEns - nEnsPerSubEns
273 else
274 nEnsPerSubEns_mod = nEnsPerSubEns * numRetainedEigen
275 nEnsIndependentPerSubEns = nEnsGain - nEnsPerSubEns_mod
276 end if
277 allocate(YbTinvRYb_CV(nEnsIndependentPerSubEns,nEnsIndependentPerSubEns))
278 allocate(eigenValues_CV(nEnsIndependentPerSubEns))
279 allocate(eigenVectors_CV(nEnsIndependentPerSubEns,nEnsIndependentPerSubEns))
280 allocate(memberIndexSubEns(nEnsPerSubEns,numSubEns))
281 allocate(memberIndexSubEnsComp(nEnsIndependentPerSubEns,numSubEns))
282 if ( useModulatedEns ) allocate(memberIndexSubEns_mod(nEnsPerSubEns_mod,numSubEns))
283 if (.not.randomShuffleSubEns) then
284 ! form subensembles with contiguous sequential groups of members
285 do subEnsIndex = 1, numSubEns
286 do memberIndex = 1, nEnsPerSubEns
287 memberIndexSubEns(memberIndex,subEnsIndex) = &
288 (subEnsIndex-1)*nEnsPerSubEns + memberIndex
289 end do
290 end do
291 if ( useModulatedEns ) then
292 do subEnsIndex = 1, numSubEns
293 memberIndex2 = 0
294 do memberIndex = 1, nEnsPerSubEns
295 do eigenVectorColumnIndex = 1, numRetainedEigen
296 memberIndex2 = memberIndex2 + 1
297 memberIndexInModEns = (eigenVectorColumnIndex - 1) * nEns + &
298 memberIndex
299 memberIndexSubEns_mod(memberIndex2,subEnsIndex) = &
300 (subEnsIndex-1)*nEnsPerSubEns + memberIndexInModEns
301 end do
302 end do
303 end do
304 end if
305 else
306 ! compute random seed from the date for randomly forming subensembles
307 imode = -3 ! stamp to printable date and time: YYYYMMDD, HHMMSShh
308 dateStamp = tim_getDateStamp()
309 ierr = newdate(dateStamp, datePrint, timePrint, imode)
310 timePrint = timePrint/1000000
311 datePrint = datePrint*100 + timePrint
312 ! Remove the century, keeping 2 digits of the year
313 randomSeed = datePrint - 100000000*(datePrint/100000000)
314 allocate(randomMemberIndexArray(nEns))
315 do memberIndex = 1, nEns
316 randomMemberIndexArray(memberIndex) = memberIndex
317 end do
318 call utl_randomOrderInt(randomMemberIndexArray,randomSeed)
319 write(*,*) 'enkf_LETKFanalyses: seed for random shuffle of sub ens = ', randomSeed
320 write(*,*) 'enkf_LETKFanalyses: randomOrder = ', randomMemberIndexArray(:)
321 do subEnsIndex = 1, numSubEns
322 do memberIndex = 1, nEnsPerSubEns
323 memberIndexSubEns(memberIndex,subEnsIndex) = &
324 randomMemberIndexArray((subEnsIndex-1)*nEnsPerSubEns + memberIndex)
325 end do
326 end do
327 if ( useModulatedEns ) then
328 do subEnsIndex = 1, numSubEns
329 memberIndex2 = 0
330 do memberIndex = 1, nEnsPerSubEns
331 do eigenVectorColumnIndex = 1, numRetainedEigen
332 memberIndex2 = memberIndex2 + 1
333 memberIndexSubEns_mod(memberIndex2,subEnsIndex) = &
334 randomMemberIndexArray((subEnsIndex-1)*nEnsPerSubEns + memberIndex) + &
335 (eigenVectorColumnIndex - 1) * nEns
336 end do
337 end do
338 end do
339 end if
340 end if
341
342 do subEnsIndex = 1, numSubEns
343 memberIndex = 1
344 do subEnsIndex2 = 1, numSubEns
345 if (subEnsIndex2 == subEnsIndex) cycle
346
347 if ( .not. useModulatedEns ) then
348 memberIndexSubEnsComp(memberIndex:memberIndex+nEnsPerSubEns-1,subEnsIndex) = &
349 memberIndexSubEns(:,subEnsIndex2)
350 memberIndex = memberIndex + nEnsPerSubEns
351 else
352 memberIndexSubEnsComp(memberIndex:memberIndex+nEnsPerSubEns_mod-1,subEnsIndex) = &
353 memberIndexSubEns_mod(:,subEnsIndex2)
354 memberIndex = memberIndex + nEnsPerSubEns_mod
355 end if
356 end do
357 end do
358
359 if ( mmpi_myid == 0 ) then
360 write(*,*) 'nEns, numSubEns, nEnsPerSubEns, nEnsIndependentPerSubEns = ', &
361 nEns, numSubEns, nEnsPerSubEns, nEnsIndependentPerSubEns
362 do subEnsIndex = 1, numSubEns
363 write(*,*) 'memberIndexSubEns = '
364 write(*,*) memberIndexSubEns(:,subEnsIndex)
365 if ( useModulatedEns ) then
366 write(*,*) 'memberIndexSubEns_mod = '
367 write(*,*) memberIndexSubEns_mod(:,subEnsIndex)
368 end if
369 write(*,*) 'memberIndexSubEnsComp = '
370 write(*,*) memberIndexSubEnsComp(:,subEnsIndex)
371 end do
372 end if
373
374 end if ! if CVLETKF(-PERTOBS)(-ME) algorithm
375
376 call lfn_Setup(LocFunctionWanted='FifthOrder')
377
378 ! compute 3D field of vertical location needed for localization
379 if (vLocalize > 0.0d0) then
380 call enkf_computeVertLocation(vertLocation_r4,stateVectorMeanTrl)
381 end if
382
383 allocate(varLevelFromK(numVarLev))
384 allocate(levFromK(numVarLev))
385 allocate(varKindFromK(numVarLev))
386 do varLevIndex = 1, numVarLev
387 varName = gsv_getVarNameFromK(stateVectorMeanInc,varLevIndex)
388 varLevelFromK(varLevIndex) = vnl_varLevelFromVarname(varName)
389 levFromK(varLevIndex) = gsv_getLevFromK(stateVectorMeanInc,varLevIndex)
390 varKindFromK(varLevIndex) = vnl_varKindFromVarname(varName)
391 end do
392
393 call utl_tmg_start(141,'----Barr')
394 call rpn_comm_barrier('GRID',ierr)
395 call utl_tmg_stop(141)
396
397 ! get mpi global list of tags used for mpi send/recv
398 call utl_tmg_start(142, '----GetGlobalTags')
399 allocate(latLonTagMpiGlobal(stateVectorMeanAnl%ni,stateVectorMeanAnl%nj))
400 call enkf_LETKFgetMpiGlobalTags(latLonTagMpiGlobal,myLatIndexesRecv,myLonIndexesRecv)
401 call utl_tmg_stop(142)
402
403 ! Compute the weights for ensemble mean and members
404 countMaxExceeded = 0
405 maxCountMaxExceeded = 0
406 numGridPointWeights = 0
407 LEV_LOOP: do levIndex = 1, nLev_weights
408 write(*,*) 'computing ensemble updates for vertical level = ', levIndex
409
410 !
411 ! First post all recv instructions for communication of weights
412 !
413 call utl_tmg_start(132,'----CommWeights')
414 numSend = 0
415 numRecv = 0
416 do latLonIndex = 1, myNumLatLonRecv
417 latIndex = myLatIndexesRecv(latLonIndex)
418 lonIndex = myLonIndexesRecv(latLonIndex)
419 procIndex = myProcIndexesRecv(latLonIndex)
420 recvTag = latLonTagMpiGlobal(lonIndex,latIndex)
421
422 nsize = nEnsGain
423 numRecv = numRecv + 1
424 call mpi_irecv( weightsMean(:,1,lonIndex,latIndex), &
425 nsize, mmpi_datyp_real8, procIndex-1, recvTag, &
426 mmpi_comm_grid, requestIdRecv(numRecv), ierr )
427 nsize = nEnsGain * nEns
428 numRecv = numRecv + 1
429 recvTag = recvTag + maxval(latLonTagMpiGlobal(:,:))
430 call mpi_irecv( weightsMembers(:,:,lonIndex,latIndex), &
431 nsize, mmpi_datyp_real8, procIndex-1, recvTag, &
432 mmpi_comm_grid, requestIdRecv(numRecv), ierr )
433 end do
434 call utl_tmg_stop(132)
435
436 LATLON_LOOP: do latLonIndex = 1, myNumLatLonSend
437 latIndex = myLatIndexesSend(latLonIndex)
438 lonIndex = myLonIndexesSend(latLonIndex)
439
440 numGridPointWeights = numGridPointWeights + 1
441
442 ! lat-lon of the grid point for which we are doing the analysis
443 anlLat = hco_ens%lat2d_4(lonIndex,latIndex)
444 anlLon = hco_ens%lon2d_4(lonIndex,latIndex)
445 hLocalizeIsConstant = all(hLocalize(:) == hLocalize(1))
446 if (vLocalize > 0.0d0 .or. .not.hLocalizeIsConstant) then
447 anlVertLocation = real(vertLocation_r4(lonIndex,latIndex,levIndex),8)
448 end if
449
450 ! Find which horizontal localization value to use for this analysis level
451 if (hLocalizeIsConstant) then
452 hLocIndex = 1
453 else
454 hLocIndex = 1 + count(anlVertLocation > hLocalizePressure(:))
455 end if
456
457 ! Get list of nearby observations and distances to gridpoint. With modulated-ensembles,
458 ! we get observations in entire column.
459 call utl_tmg_start(133,'----GetLocalBodyIndices')
460 if ( useModulatedEns ) anlVertLocation = MPC_missingValue_R8
461 numLocalObs = eob_getLocalBodyIndices(ensObs_mpiglobal, localBodyIndices, &
462 distances, anlLat, anlLon, anlVertLocation, &
463 hLocalize(hLocIndex), vLocalize, numLocalObsFound)
464 if (numLocalObsFound > maxNumLocalObs) then
465 countMaxExceeded = countMaxExceeded + 1
466 maxCountMaxExceeded = max(maxCountMaxExceeded, numLocalObsFound)
467 end if
468 call utl_tmg_stop(133)
469
470 call utl_tmg_start(134,'----CalculateWeights')
471
472 ! Extract initial quantities YbTinvR and first term of PaInv (YbTinvR*Yb)
473 do localObsIndex = 1, numLocalObs
474 bodyIndex = localBodyIndices(localObsIndex)
475
476 ! Compute value of localization function
477 ! Horizontal
478 localization = lfn_Response(distances(localObsIndex),hLocalize(hLocIndex))
479 ! Vertical when NOT using modulated ensembles - use pressures at the grid point (not obs) location
480 if (vLocalize > 0.0d0 .and. .not. useModulatedEns) then
481 distance = abs( anlVertLocation - ensObs_mpiglobal%vertLocation(bodyIndex) )
482 localization = localization * lfn_Response(distance,vLocalize)
483 end if
484 do memberIndex = 1, nEnsGain
485 ! YbTinvR for updating ensemble perturbations
486 YbTinvR_pert(memberIndex,localObsIndex) = &
487 ensObsGain_mpiglobal%Yb_r4(memberIndex, bodyIndex) * &
488 localization * ensObsGain_mpiglobal%obsErrInv(bodyIndex)
489 end do
490 if (eob_simObsAssim) then
491 do memberIndex = 1, nEnsGain
492 ! YbTinvR for the ensemble mean update for EDA observation simulation experiment
493 YbTinvR_mean(memberIndex,localObsIndex) = &
494 ensObsGain_mpiglobal%Yb_r4(memberIndex, bodyIndex) * &
495 localization * ensObsGain_mpiglobal%obsErrInv_sim(bodyIndex)
496 end do
497 end if
498 end do ! localObsIndex
499
500 call utl_tmg_start(136,'------CalcYbTinvRYb')
501
502 ! make copy of YbTinvR, and ensObsGain_mpiglobal%Yb_r4
503 call utl_tmg_start(137,'--------YbArraysCopy')
504 !$OMP PARALLEL DO PRIVATE (localObsIndex, bodyIndex, memberIndex2)
505 do localObsIndex = 1, numLocalObs
506 bodyIndex = localBodyIndices(localObsIndex)
507 do memberIndex2 = 1, nEnsGain
508 YbGainCopy_r4(localObsIndex,memberIndex2) = ensObsGain_mpiglobal%Yb_r4(memberIndex2,bodyIndex)
509 YbTinvRCopy_pert(localObsIndex,memberIndex2) = YbTinvR_pert(memberIndex2,localObsIndex)
510 end do
511 end do
512 !$OMP END PARALLEL DO
513 if (eob_simObsAssim) then
514 !$OMP PARALLEL DO PRIVATE (localObsIndex, bodyIndex, memberIndex2)
515 do localObsIndex = 1, numLocalObs
516 bodyIndex = localBodyIndices(localObsIndex)
517 do memberIndex2 = 1, nEnsGain
518 YbTinvRCopy_mean(localObsIndex,memberIndex2) = YbTinvR_mean(memberIndex2,localObsIndex)
519 end do
520 end do
521 !$OMP END PARALLEL DO
522 end if
523 call utl_tmg_stop(137)
524
525 call utl_tmg_start(138,'--------YbTinvRYb1')
526
527 YbTinvRYb_pert(:,:) = 0.0D0
528 !$OMP PARALLEL DO PRIVATE (memberIndex1, memberIndex2)
529 do memberIndex2 = 1, nEnsGain
530 do memberIndex1 = 1, memberIndex2 ! compute only upper triangle
531 YbTinvRYb_pert(memberIndex1,memberIndex2) = &
532 YbTinvRYb_pert(memberIndex1,memberIndex2) + &
533 sum(YbTinvRCopy_pert(1:numLocalObs,memberIndex1) * YbGainCopy_r4(1:numLocalObs,memberIndex2))
534 end do
535 end do
536 !$OMP END PARALLEL DO
537 ! copy upper triangle to lower triangle (symmetric matrix)
538 do memberIndex2 = 1, nEnsGain
539 do memberIndex1 = memberIndex2+1, nEnsGain
540 YbTinvRYb_pert(memberIndex1,memberIndex2) = &
541 YbTinvRYb_pert(memberIndex2,memberIndex1)
542 end do
543 end do
544 if (eob_simObsAssim) then
545 YbTinvRYb_mean(:,:) = 0.0D0
546 !$OMP PARALLEL DO PRIVATE (memberIndex1, memberIndex2)
547 do memberIndex2 = 1, nEnsGain
548 do memberIndex1 = 1, memberIndex2 ! compute only upper triangle
549 YbTinvRYb_mean(memberIndex1,memberIndex2) = &
550 YbTinvRYb_mean(memberIndex1,memberIndex2) + &
551 sum(YbTinvRCopy_mean(1:numLocalObs,memberIndex1) * YbGainCopy_r4(1:numLocalObs,memberIndex2))
552 end do
553 end do
554 !$OMP END PARALLEL DO
555 ! copy upper triangle to lower triangle (symmetric matrix)
556 do memberIndex2 = 1, nEnsGain
557 do memberIndex1 = memberIndex2+1, nEnsGain
558 YbTinvRYb_mean(memberIndex1,memberIndex2) = &
559 YbTinvRYb_mean(memberIndex2,memberIndex1)
560 end do
561 end do
562 end if
563 call utl_tmg_stop(138)
564
565 ! computing YbTinvRYb that uses modulated and original ensembles for perturbation update
566 if ( trim(algorithm) == 'CVLETKF-ME' .or. &
567 trim(algorithm) == 'LETKF-Gain-ME' ) then
568 ! make copy of ensObs_mpiglobal%Yb_r4
569 call utl_tmg_start(137,'--------YbArraysCopy')
570 YbCopy_r4(:,:) = 0.0
571 do localObsIndex = 1, numLocalObs
572 bodyIndex = localBodyIndices(localObsIndex)
573 do memberIndex2 = 1, nEns
574 YbCopy_r4(localObsIndex,memberIndex2) = ensObs_mpiglobal%Yb_r4(memberIndex2,bodyIndex)
575 end do
576 end do
577 call utl_tmg_stop(137)
578
579 YbTinvRYb_mod(:,:) = 0.0D0
580 call utl_tmg_start(139,'--------YbTinvRYb2')
581 !$OMP PARALLEL DO PRIVATE (memberIndex1, memberIndex2)
582 do memberIndex2 = 1, nEns
583 do memberIndex1 = 1, nEnsGain
584 YbTinvRYb_mod(memberIndex1,memberIndex2) = &
585 YbTinvRYb_mod(memberIndex1,memberIndex2) + &
586 sum(YbTinvRCopy_pert(1:numLocalObs,memberIndex1) * YbCopy_r4(1:numLocalObs,memberIndex2))
587 end do
588 end do
589 !$OMP END PARALLEL DO
590 call utl_tmg_stop(139)
591 end if !CVLETKF-ME or LETKF-GAIN-ME
592
593 call utl_tmg_stop(136)
594
595 ! Rest of the computation of local weights for this grid point
596 if (numLocalObs > 0) then
597
598 if (trim(algorithm) == 'LETKF') then
599 !
600 ! Weight calculation for standard LETKF algorithm
601 !
602
603 ! Add second term of PaInv
604 PaInv_pert(:,:) = YbTinvRYb_pert(:,:)
605 do memberIndex = 1, nEns
606 PaInv_pert(memberIndex,memberIndex) = PaInv_pert(memberIndex,memberIndex) + real(nEns - 1,8)
607 end do
608 if (eob_simObsAssim) then
609 PaInv_mean(:,:) = YbTinvRYb_mean(:,:)
610 do memberIndex = 1, nEns
611 PaInv_mean(memberIndex,memberIndex) = PaInv_mean(memberIndex,memberIndex) + real(nEns - 1,8)
612 end do
613 end if
614
615 ! Compute Pa and sqrt(Pa) matrices from PaInv
616 Pa_pert(:,:) = PaInv_pert(:,:)
617 call utl_tmg_start(135,'------EigenDecomp')
618 call utl_matInverse(Pa_pert, nEns, inverseSqrt_opt=PaSqrt_pert)
619 if (eob_simObsAssim) then
620 Pa_mean(:,:) = PaInv_mean(:,:)
621 call utl_matInverse(Pa_mean, nEns)
622 end if
623 call utl_tmg_stop(135)
624
625 ! Compute ensemble mean local weights as Pa * YbTinvR * (obs - meanYb)
626 weightsTemp(:) = 0.0d0
627 do localObsIndex = 1, numLocalObs
628 bodyIndex = localBodyIndices(localObsIndex)
629 do memberIndex = 1, nEns
630 weightsTemp(memberIndex) = weightsTemp(memberIndex) + &
631 YbTinvR_mean(memberIndex,localObsIndex) * &
632 ( ensObs_mpiglobal%obsValue(bodyIndex) - &
633 ensObs_mpiglobal%meanYb(bodyIndex) )
634 end do
635 end do
636
637 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
638 do memberIndex2 = 1, nEns
639 do memberIndex1 = 1, nEns
640 weightsMeanLatLon(memberIndex1,1,latLonIndex) = &
641 weightsMeanLatLon(memberIndex1,1,latLonIndex) + &
642 Pa_mean(memberIndex1,memberIndex2)*weightsTemp(memberIndex2)
643 end do
644 end do
645
646 ! Compute ensemble perturbation weights: [(Nens-1)^1/2*PaSqrt]
647 weightsMembersLatLon(:,:,latLonIndex) = sqrt(real(nEns - 1,8)) * PaSqrt_pert(:,:)
648
649 else if (trim(algorithm) == 'LETKF-Gain') then
650 !
651 ! Weight calculation for standard LETKF algorithm
652 !
653
654 ! Compute eigenValues/Vectors of Yb^T R^-1 Yb = E * Lambda * E^T
655 call utl_tmg_start(135,'------EigenDecomp')
656 tolerance = 1.0D-50
657 call utl_eigenDecomp(YbTinvRYb_pert, eigenValues_pert, eigenVectors_pert, tolerance, matrixRank)
658 if (eob_simObsAssim) then
659 call utl_eigenDecomp(YbTinvRYb_mean, eigenValues_mean, eigenVectors_mean, tolerance, matrixRank)
660 end if
661 call utl_tmg_stop(135)
662
663 ! Compute ensemble mean local weights as E * (Lambda + (Nens-1)*I)^-1 * E^T * YbTinvR * (obs - meanYb)
664 weightsTemp(:) = 0.0d0
665 do localObsIndex = 1, numLocalObs
666 bodyIndex = localBodyIndices(localObsIndex)
667 do memberIndex = 1, nEns
668 weightsTemp(memberIndex) = weightsTemp(memberIndex) + &
669 YbTinvR_mean(memberIndex,localObsIndex) * &
670 ( ensObs_mpiglobal%obsValue(bodyIndex) - &
671 ensObs_mpiglobal%meanYb(bodyIndex) )
672 end do
673 end do
674 weightsTemp2(:) = 0.0d0
675 do memberIndex2 = 1, matrixRank
676 do memberIndex1 = 1, nEns
677 weightsTemp2(memberIndex2) = weightsTemp2(memberIndex2) + &
678 eigenVectors_mean(memberIndex1,memberIndex2) * &
679 weightsTemp(memberIndex1)
680 end do
681 end do
682 do memberIndex = 1, matrixRank
683 weightsTemp2(memberIndex) = weightsTemp2(memberIndex) * &
684 1.0D0/(eigenValues_mean(memberIndex) + real(nEns - 1,8))
685 end do
686 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
687 do memberIndex2 = 1, matrixRank
688 do memberIndex1 = 1, nEns
689 weightsMeanLatLon(memberIndex1,1,latLonIndex) = &
690 weightsMeanLatLon(memberIndex1,1,latLonIndex) + &
691 eigenVectors_mean(memberIndex1,memberIndex2) * &
692 weightsTemp2(memberIndex2)
693 end do
694 end do
695
696 ! Compute ensemble perturbation weights:
697 ! Wa = [ - (Nens-1)^1/2 * E *
698 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} * Lambda^-1 *
699 ! E^T * YbTinvRYb ]
700 ! Loop over members within the current sub-ensemble being updated
701 do memberIndex = 1, nEns
702
703 ! E^T * YbTinvRYb
704 weightsTemp(:) = 0.0d0
705 do memberIndex2 = 1, matrixRank
706 do memberIndex1 = 1, nEns
707 weightsTemp(memberIndex2) = weightsTemp(memberIndex2) + &
708 eigenVectors_pert(memberIndex1,memberIndex2) * &
709 YbTinvRYb_pert(memberIndex1,memberIndex)
710 end do
711 end do
712
713 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} Lambda^-1 * previous_result
714
715 do memberIndex1 = 1, matrixRank
716 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) * &
717 ( 1.0D0/sqrt(real(nEns - 1,8)) - &
718 1.0D0/sqrt(eigenValues_pert(memberIndex1) + &
719 real(nEns - 1,8)) )
720 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) / &
721 eigenValues_pert(memberIndex1)
722 end do
723
724 ! E * previous_result
725 weightsMembersLatLon(:,memberIndex,latLonIndex) = 0.0d0
726 do memberIndex2 = 1, matrixRank
727 do memberIndex1 = 1, nEns
728 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) = &
729 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) + &
730 eigenVectors_pert(memberIndex1,memberIndex2) * &
731 weightsTemp(memberIndex2)
732 end do
733 end do
734
735 ! -1 * (Nens-1)^1/2 * previous_result
736 weightsMembersLatLon(:,memberIndex,latLonIndex) = &
737 -1.0D0 * sqrt(real(nEns - 1,8)) * &
738 weightsMembersLatLon(:,memberIndex,latLonIndex)
739
740 ! I + previous_result
741 weightsMembersLatLon(memberIndex,memberIndex,latLonIndex) = &
742 1.0D0 + weightsMembersLatLon(memberIndex,memberIndex,latLonIndex)
743
744 end do
745
746 ! Remove the weights mean computed over the columns
747 do memberIndex = 1, nEns
748 weightsMembersLatLon(memberIndex,:,latLonIndex) = &
749 weightsMembersLatLon(memberIndex,:,latLonIndex) - &
750 sum(weightsMembersLatLon(memberIndex,:,latLonIndex))/real(nEns,8)
751 end do
752
753 else if (trim(algorithm) == 'LETKF-Gain-ME') then
754 !
755 ! Weight calculation for standard LETKF algorithm with modulated ensemble
756 !
757
758 ! Compute eigenValues/Vectors of Yb^T R^-1 Yb = E * Lambda * E^T
759 call utl_tmg_start(135,'------EigenDecomp')
760 tolerance = 1.0D-50
761 call utl_eigenDecomp(YbTinvRYb_pert, eigenValues_pert, eigenVectors_pert, tolerance, matrixRank)
762 if (eob_simObsAssim) then
763 call utl_eigenDecomp(YbTinvRYb_mean, eigenValues_mean, eigenVectors_mean, tolerance, matrixRank)
764 end if
765 call utl_tmg_stop(135)
766
767 ! Compute ensemble mean local weights as E * (Lambda + (Nens-1)*I)^-1 * E^T * YbTinvR * (obs - meanYb)
768 weightsTemp(:) = 0.0d0
769 do localObsIndex = 1, numLocalObs
770 bodyIndex = localBodyIndices(localObsIndex)
771 do memberIndex = 1, nEnsGain
772 weightsTemp(memberIndex) = weightsTemp(memberIndex) + &
773 YbTinvR_mean(memberIndex,localObsIndex) * &
774 (ensObs_mpiglobal%obsValue(bodyIndex) - &
775 ensObs_mpiglobal%meanYb(bodyIndex))
776 end do
777 end do
778 weightsTemp2(:) = 0.0d0
779 do memberIndex2 = 1, matrixRank
780 do memberIndex1 = 1, nEnsGain
781 weightsTemp2(memberIndex2) = weightsTemp2(memberIndex2) + &
782 eigenVectors_mean(memberIndex1,memberIndex2) * &
783 weightsTemp(memberIndex1)
784 end do
785 end do
786 do memberIndex = 1, matrixRank
787 weightsTemp2(memberIndex) = weightsTemp2(memberIndex) * &
788 1.0D0/(eigenValues_mean(memberIndex) + real(nEnsGain - 1,8))
789 end do
790 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
791 do memberIndex2 = 1, matrixRank
792 do memberIndex1 = 1, nEnsGain
793 weightsMeanLatLon(memberIndex1,1,latLonIndex) = &
794 weightsMeanLatLon(memberIndex1,1,latLonIndex) + &
795 eigenVectors_mean(memberIndex1,memberIndex2) * &
796 weightsTemp2(memberIndex2)
797 end do
798 end do
799
800 ! Compute ensemble perturbation weights:
801 ! Wa = [ - (Nens-1)^1/2 * E *
802 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} * Lambda^-1 *
803 ! E^T * YbTinvRYb_mod ]
804 ! Loop over members within the current sub-ensemble being updated
805 do memberIndex = 1, nEns
806
807 ! E^T * YbTinvRYb_mod
808 weightsTemp(:) = 0.0d0
809 do memberIndex2 = 1, matrixRank
810 do memberIndex1 = 1, nEnsGain
811 weightsTemp(memberIndex2) = weightsTemp(memberIndex2) + &
812 eigenVectors_pert(memberIndex1,memberIndex2) * &
813 YbTinvRYb_mod(memberIndex1,memberIndex)
814 end do
815 end do
816
817 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} Lambda^-1 * previous_result
818
819 do memberIndex1 = 1, matrixRank
820 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) * &
821 ( 1.0D0/sqrt(real(nEnsGain - 1,8)) - &
822 1.0D0/sqrt(eigenValues_pert(memberIndex1) + &
823 real(nEnsGain - 1,8)) )
824 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) / &
825 eigenValues_pert(memberIndex1)
826 end do
827
828 ! E * previous_result
829 weightsMembersLatLon(:,memberIndex,latLonIndex) = 0.0d0
830 do memberIndex2 = 1, matrixRank
831 do memberIndex1 = 1, nEnsGain
832 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) = &
833 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) + &
834 eigenVectors_pert(memberIndex1,memberIndex2) * &
835 weightsTemp(memberIndex2)
836 end do
837 end do
838
839 ! -1 * (Nens-1)^1/2 * previous_result
840 weightsMembersLatLon(:,memberIndex,latLonIndex) = &
841 -1.0D0 * sqrt(real(nEnsGain - 1,8)) * &
842 weightsMembersLatLon(:,memberIndex,latLonIndex)
843
844 end do
845
846 ! Remove the weights mean computed over the columns
847 do memberIndex = 1, nEnsGain
848 weightsMembersLatLon(memberIndex,:,latLonIndex) = &
849 weightsMembersLatLon(memberIndex,:,latLonIndex) - &
850 sum(weightsMembersLatLon(memberIndex,:,latLonIndex))/real(nEns,8)
851 end do
852
853 else if (trim(algorithm) == 'CVLETKF') then
854 !
855 ! Weight calculation for cross-validation LETKF algorithm
856 !
857
858 ! Compute eigenValues/Vectors of Yb^T R^-1 Yb = E * Lambda * E^T
859 call utl_tmg_start(135,'------EigenDecomp')
860 tolerance = 1.0D-50
861 call utl_eigenDecomp(YbTinvRYb_mean, eigenValues_mean, eigenVectors_mean, tolerance, matrixRank)
862 call utl_tmg_stop(135)
863
864 ! Compute ensemble mean local weights as E * (Lambda + (Nens-1)*I)^-1 * E^T * YbTinvR * (obs - meanYb)
865 weightsTemp(:) = 0.0d0
866 do localObsIndex = 1, numLocalObs
867 bodyIndex = localBodyIndices(localObsIndex)
868 do memberIndex = 1, nEns
869 weightsTemp(memberIndex) = weightsTemp(memberIndex) + &
870 YbTinvR_mean(memberIndex,localObsIndex) * &
871 ( ensObs_mpiglobal%obsValue(bodyIndex) - &
872 ensObs_mpiglobal%meanYb(bodyIndex) )
873 end do
874 end do
875 weightsTemp2(:) = 0.0d0
876 do memberIndex2 = 1, matrixRank
877 do memberIndex1 = 1, nEns
878 weightsTemp2(memberIndex2) = weightsTemp2(memberIndex2) + &
879 eigenVectors_mean(memberIndex1,memberIndex2) * &
880 weightsTemp(memberIndex1)
881 end do
882 end do
883 do memberIndex = 1, matrixRank
884 weightsTemp2(memberIndex) = weightsTemp2(memberIndex) * &
885 1.0D0/(eigenValues_mean(memberIndex) + real(nEns - 1,8))
886 end do
887 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
888 do memberIndex2 = 1, matrixRank
889 do memberIndex1 = 1, nEns
890 weightsMeanLatLon(memberIndex1,1,latLonIndex) = &
891 weightsMeanLatLon(memberIndex1,1,latLonIndex) + &
892 eigenVectors_mean(memberIndex1,memberIndex2) * &
893 weightsTemp2(memberIndex2)
894 end do
895 end do
896
897 ! Compute ensemble perturbation weights:
898 ! Wa = [ I - (Nens-1)^1/2 * E *
899 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} * Lambda^-1 *
900 ! E^T * YbTinvRYb ]
901 ! Loop over sub-ensembles
902
903 !$OMP PARALLEL DO PRIVATE(subEnsIndex, memberIndexCV, memberIndexCV1, memberIndexCV2, &
904 !$OMP memberIndex, memberIndex1, memberIndex2, weightsTemp, tolerance, &
905 !$OMP YbTinvRYb_CV, eigenValues_CV, eigenVectors_CV, matrixRank)
906 do subEnsIndex = 1, numSubEns
907
908 ! Use complement (independent) ens to get eigenValues/Vectors of Yb^T R^-1 Yb = E*Lambda*E^T
909 call utl_tmg_start(135,'------EigenDecomp')
910 do memberIndexCV2 = 1, nEnsIndependentPerSubEns
911 memberIndex2 = memberIndexSubEnsComp(memberIndexCV2, subEnsIndex)
912 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
913 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
914 YbTinvRYb_CV(memberIndexCV1,memberIndexCV2) = YbTinvRYb_pert(memberIndex1,memberIndex2)
915 end do
916 end do
917 tolerance = 1.0D-50
918 call utl_eigenDecomp(YbTinvRYb_CV, eigenValues_CV, eigenVectors_CV, tolerance, matrixRank)
919 call utl_tmg_stop(135)
920
921 ! Loop over members within the current sub-ensemble being updated
922 do memberIndexCV = 1, nEnsPerSubEns
923
924 ! This is index of member being updated
925 memberIndex = memberIndexSubEns(memberIndexCV, subEnsIndex)
926
927 ! E^T * YbTinvRYb
928 weightsTemp(:) = 0.0d0
929 do memberIndex2 = 1, matrixRank
930 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
931 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
932 weightsTemp(memberIndex2) = weightsTemp(memberIndex2) + &
933 eigenVectors_CV(memberIndexCV1,memberIndex2) * &
934 YbTinvRYb_pert(memberIndex1,memberIndex)
935 end do
936 end do
937
938 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} Lambda^-1 * previous_result
939
940 do memberIndex1 = 1, matrixRank
941 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) * &
942 ( 1.0D0/sqrt(real(nEnsIndependentPerSubEns - 1,8)) - &
943 1.0D0/sqrt(eigenValues_CV(memberIndex1) + &
944 real(nEnsIndependentPerSubEns - 1,8)) )
945 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) / &
946 eigenValues_CV(memberIndex1)
947 end do
948
949 ! E * previous_result
950 weightsMembersLatLon(:,memberIndex,latLonIndex) = 0.0d0
951 do memberIndex2 = 1, matrixRank
952 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
953 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
954 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) = &
955 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) + &
956 eigenVectors_CV(memberIndexCV1,memberIndex2) * &
957 weightsTemp(memberIndex2)
958 end do
959 end do
960
961 ! -1 * (Nens-1)^1/2 * previous_result
962 weightsMembersLatLon(:,memberIndex,latLonIndex) = &
963 -1.0D0 * sqrt(real(nEnsIndependentPerSubEns - 1,8)) * &
964 weightsMembersLatLon(:,memberIndex,latLonIndex)
965
966 ! I + previous_result
967 weightsMembersLatLon(memberIndex,memberIndex,latLonIndex) = &
968 1.0D0 + weightsMembersLatLon(memberIndex,memberIndex,latLonIndex)
969
970 end do ! memberIndexCV
971 end do ! subEnsIndex
972 !$OMP END PARALLEL DO
973
974 ! Remove the weights mean computed over the columns
975 do memberIndex = 1, nEns
976 weightsMembersLatLon(memberIndex,:,latLonIndex) = &
977 weightsMembersLatLon(memberIndex,:,latLonIndex) - &
978 sum(weightsMembersLatLon(memberIndex,:,latLonIndex))/real(nEns,8)
979 end do
980
981 else if (trim(algorithm) == 'CVLETKF-ME') then
982 !
983 ! Weight calculation for cross-validation LETKF algorithm
984 !
985
986 ! Compute eigenValues/Vectors of Yb^T R^-1 Yb = E * Lambda * E^T
987 call utl_tmg_start(135,'------EigenDecomp')
988 tolerance = 1.0D-50
989 call utl_eigenDecomp(YbTinvRYb_mean, eigenValues_mean, eigenVectors_mean, tolerance, matrixRank)
990 call utl_tmg_stop(135)
991 !if (matrixRank < (nEns-1)) then
992 ! write(*,*) 'YbTinvRYb is rank deficient =', matrixRank, nEns, numLocalObs
993 !end if
994
995 ! Compute ensemble mean local weights as E * (Lambda + (Nens-1)*I)^-1 * E^T * YbTinvR * (obs - meanYb)
996 weightsTemp(:) = 0.0d0
997 do localObsIndex = 1, numLocalObs
998 bodyIndex = localBodyIndices(localObsIndex)
999 do memberIndex = 1, nEnsGain
1000 weightsTemp(memberIndex) = weightsTemp(memberIndex) + &
1001 YbTinvR_mean(memberIndex,localObsIndex) * &
1002 ( ensObs_mpiglobal%obsValue(bodyIndex) - &
1003 ensObs_mpiglobal%meanYb(bodyIndex) )
1004 end do
1005 end do
1006 weightsTemp2(:) = 0.0d0
1007 do memberIndex2 = 1, matrixRank
1008 do memberIndex1 = 1, nEnsGain
1009 weightsTemp2(memberIndex2) = weightsTemp2(memberIndex2) + &
1010 eigenVectors_mean(memberIndex1,memberIndex2) * &
1011 weightsTemp(memberIndex1)
1012 end do
1013 end do
1014 do memberIndex = 1, matrixRank
1015 weightsTemp2(memberIndex) = weightsTemp2(memberIndex) * &
1016 1.0D0/(eigenValues_mean(memberIndex) + real(nEnsGain - 1,8))
1017 end do
1018 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
1019 do memberIndex2 = 1, matrixRank
1020 do memberIndex1 = 1, nEnsGain
1021 weightsMeanLatLon(memberIndex1,1,latLonIndex) = &
1022 weightsMeanLatLon(memberIndex1,1,latLonIndex) + &
1023 eigenVectors_mean(memberIndex1,memberIndex2) * &
1024 weightsTemp2(memberIndex2)
1025 end do
1026 end do
1027
1028 ! Compute ensemble perturbation weights:
1029 ! Wa = [ - (Nens-1)^1/2 * E *
1030 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} * Lambda^-1 *
1031 ! E^T * YbTinvRYb_mod ]
1032 ! Loop over sub-ensembles
1033 !$OMP PARALLEL DO PRIVATE(subEnsIndex, memberIndexCV, memberIndexCV1, memberIndexCV2, &
1034 !$OMP memberIndex, memberIndex1, memberIndex2, weightsTemp, tolerance, &
1035 !$OMP YbTinvRYb_CV, eigenValues_CV, eigenVectors_CV, matrixRank)
1036 do subEnsIndex = 1, numSubEns
1037
1038 ! Use complement (independent) ens to get eigenValues/Vectors of Yb^T R^-1 Yb = E*Lambda*E^T
1039 call utl_tmg_start(135,'------EigenDecomp')
1040 do memberIndexCV2 = 1, nEnsIndependentPerSubEns
1041 memberIndex2 = memberIndexSubEnsComp(memberIndexCV2, subEnsIndex)
1042 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
1043 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
1044 YbTinvRYb_CV(memberIndexCV1,memberIndexCV2) = YbTinvRYb_pert(memberIndex1,memberIndex2)
1045 end do
1046 end do
1047 tolerance = 1.0D-50
1048 call utl_eigenDecomp(YbTinvRYb_CV, eigenValues_CV, eigenVectors_CV, tolerance, matrixRank)
1049 call utl_tmg_stop(135)
1050
1051 ! Loop over members within the current sub-ensemble being updated
1052 do memberIndexCV = 1, nEnsPerSubEns
1053
1054 ! This is index of member being updated
1055 memberIndex = memberIndexSubEns(memberIndexCV, subEnsIndex)
1056
1057 ! E^T * YbTinvRYb
1058 weightsTemp(:) = 0.0d0
1059 do memberIndex2 = 1, matrixRank
1060 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
1061 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
1062 weightsTemp(memberIndex2) = weightsTemp(memberIndex2) + &
1063 eigenVectors_CV(memberIndexCV1,memberIndex2) * &
1064 YbTinvRYb_mod(memberIndex1,memberIndex)
1065 end do
1066 end do
1067
1068 ! {(Nens-1)^-1/2*I - (Lambda + (Nens-1)*I)^-1/2} Lambda^-1 * previous_result
1069
1070 do memberIndex1 = 1, matrixRank
1071 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) * &
1072 ( 1.0D0/sqrt(real(nEnsIndependentPerSubEns - 1,8)) - &
1073 1.0D0/sqrt(eigenValues_CV(memberIndex1) + &
1074 real(nEnsIndependentPerSubEns - 1,8)) )
1075 weightsTemp(memberIndex1) = weightsTemp(memberIndex1) / &
1076 eigenValues_CV(memberIndex1)
1077 end do
1078
1079 ! E * previous_result
1080 weightsMembersLatLon(:,memberIndex,latLonIndex) = 0.0d0
1081 do memberIndex2 = 1, matrixRank
1082 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
1083 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
1084 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) = &
1085 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) + &
1086 eigenVectors_CV(memberIndexCV1,memberIndex2) * &
1087 weightsTemp(memberIndex2)
1088 end do
1089 end do
1090
1091 ! -1 * (Nens-1)^1/2 * previous_result
1092 weightsMembersLatLon(:,memberIndex,latLonIndex) = &
1093 -1.0D0 * sqrt(real(nEnsIndependentPerSubEns - 1,8)) * &
1094 weightsMembersLatLon(:,memberIndex,latLonIndex)
1095
1096 end do ! memberIndexCV
1097 end do ! subEnsIndex
1098 !$OMP END PARALLEL DO
1099
1100 ! Remove the weights mean computed over the columns
1101 do memberIndex = 1, nEnsGain
1102 weightsMembersLatLon(memberIndex,:,latLonIndex) = &
1103 weightsMembersLatLon(memberIndex,:,latLonIndex) - &
1104 sum(weightsMembersLatLon(memberIndex,:,latLonIndex))/real(nEns,8)
1105 end do
1106
1107 else if (trim(algorithm) == 'CVLETKF-PERTOBS') then
1108 !
1109 ! Weight calculation for perturbed-obs cross-validation LETKF algorithm
1110 !
1111
1112 ! Compute eigenValues/Vectors of Yb^T R^-1 Yb = E * Lambda * E^T
1113 call utl_tmg_start(135,'------EigenDecomp')
1114 tolerance = 1.0D-50
1115 call utl_eigenDecomp(YbTinvRYb_mean, eigenValues_mean, eigenVectors_mean, tolerance, matrixRank)
1116 call utl_tmg_stop(135)
1117
1118 ! Compute ensemble mean local weights as E * (Lambda + (Nens-1)*I)^-1 * E^T * YbTinvR * (obs - meanYb)
1119 weightsTemp(:) = 0.0d0
1120 do localObsIndex = 1, numLocalObs
1121 bodyIndex = localBodyIndices(localObsIndex)
1122 do memberIndex = 1, nEns
1123 weightsTemp(memberIndex) = weightsTemp(memberIndex) + &
1124 YbTinvR_mean(memberIndex,localObsIndex) * &
1125 ( ensObs_mpiglobal%obsValue(bodyIndex) - &
1126 ensObs_mpiglobal%meanYb(bodyIndex) )
1127 end do
1128 end do
1129 weightsTemp2(:) = 0.0d0
1130 do memberIndex2 = 1, matrixRank
1131 do memberIndex1 = 1, nEns
1132 weightsTemp2(memberIndex2) = weightsTemp2(memberIndex2) + &
1133 eigenVectors_mean(memberIndex1,memberIndex2) * &
1134 weightsTemp(memberIndex1)
1135 end do
1136 end do
1137 do memberIndex = 1, matrixRank
1138 weightsTemp2(memberIndex) = weightsTemp2(memberIndex) * &
1139 1.0D0/(eigenValues_mean(memberIndex) + real(nEns - 1,8))
1140 end do
1141 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
1142 do memberIndex2 = 1, matrixRank
1143 do memberIndex1 = 1, nEns
1144 weightsMeanLatLon(memberIndex1,1,latLonIndex) = &
1145 weightsMeanLatLon(memberIndex1,1,latLonIndex) + &
1146 eigenVectors_mean(memberIndex1,memberIndex2) * &
1147 weightsTemp2(memberIndex2)
1148 end do
1149 end do
1150
1151 ! Compute ensemble perturbation weights using mean increment weights
1152 ! formula, but with subset of members:
1153 ! wa_i = I_i + E * (Lambda + (Nens-1)*I)^-1 * E^T * YbTinvR * (obs + randpert_i - Yb_i)
1154 ! Wa = wa_i - mean_over_i(wa_i)
1155 !
1156 ! Loop over sub-ensembles
1157 !$OMP PARALLEL DO PRIVATE(subEnsIndex, memberIndexCV, memberIndexCV1, memberIndexCV2, &
1158 !$OMP memberIndex, memberIndex1, memberIndex2, weightsTemp, tolerance, &
1159 !$OMP YbTinvRYb_CV, eigenValues_CV, eigenVectors_CV, matrixRank, &
1160 !$OMP weightsTemp2, localObsIndex, bodyIndex)
1161 do subEnsIndex = 1, numSubEns
1162
1163 ! Use complement (independent) ens to get eigenValues/Vectors of Yb^T R^-1 Yb = E*Lambda*E^T
1164 call utl_tmg_start(135,'------EigenDecomp')
1165 do memberIndexCV2 = 1, nEnsIndependentPerSubEns
1166 memberIndex2 = memberIndexSubEnsComp(memberIndexCV2, subEnsIndex)
1167 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
1168 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
1169 YbTinvRYb_CV(memberIndexCV1,memberIndexCV2) = YbTinvRYb_pert(memberIndex1,memberIndex2)
1170 end do
1171 end do
1172 tolerance = 1.0D-50
1173 call utl_eigenDecomp(YbTinvRYb_CV, eigenValues_CV, eigenVectors_CV, tolerance, matrixRank)
1174 call utl_tmg_stop(135)
1175
1176 ! Loop over members within the current sub-ensemble being updated
1177 do memberIndexCV = 1, nEnsPerSubEns
1178
1179 ! This is index of member being updated (i'th member)
1180 memberIndex = memberIndexSubEns(memberIndexCV, subEnsIndex)
1181
1182 ! YbTinvRYb * (obsValue + randPert_i - Yb_i)
1183 weightsTemp(:) = 0.0d0
1184 do localObsIndex = 1, numLocalObs
1185 bodyIndex = localBodyIndices(localObsIndex)
1186 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
1187 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
1188 weightsTemp(memberIndexCV1) = &
1189 weightsTemp(memberIndexCV1) + &
1190 YbTinvR_pert(memberIndex1,localObsIndex) * &
1191 ( ensObs_mpiglobal%obsValue(bodyIndex) + &
1192 ensObs_mpiglobal%randPert_r4(memberIndex,bodyIndex) - &
1193 ( ensObs_mpiglobal%meanYb(bodyIndex) + &
1194 ensObs_mpiglobal%Yb_r4(memberIndex,bodyIndex) ) )
1195 end do
1196 end do
1197
1198 ! E^T * previous_result
1199 weightsTemp2(:) = 0.0d0
1200 do memberIndex2 = 1, matrixRank
1201 do memberIndex1 = 1, nEnsIndependentPerSubEns
1202 weightsTemp2(memberIndex2) = weightsTemp2(memberIndex2) + &
1203 eigenVectors_CV(memberIndex1,memberIndex2) * &
1204 weightsTemp(memberIndex1)
1205 end do
1206 end do
1207
1208 ! [lambda + (N_indep-1)*I]^-1 * previous_result
1209 do memberIndex1 = 1, matrixRank
1210 weightsTemp2(memberIndex1) = &
1211 weightsTemp2(memberIndex1) * &
1212 1.0D0/(eigenValues_CV(memberIndex1) + real(nEnsIndependentPerSubEns - 1,8))
1213 end do
1214
1215 ! E * previous_result
1216 weightsMembersLatLon(:,memberIndex,latLonIndex) = 0.0d0
1217 do memberIndex2 = 1, matrixRank
1218 do memberIndexCV1 = 1, nEnsIndependentPerSubEns
1219 memberIndex1 = memberIndexSubEnsComp(memberIndexCV1, subEnsIndex)
1220 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) = &
1221 weightsMembersLatLon(memberIndex1,memberIndex,latLonIndex) + &
1222 eigenVectors_CV(memberIndexCV1,memberIndex2) * &
1223 weightsTemp2(memberIndex2)
1224 end do
1225 end do
1226
1227 ! I + previous_result
1228 weightsMembersLatLon(memberIndex,memberIndex,latLonIndex) = &
1229 1.0D0 + weightsMembersLatLon(memberIndex,memberIndex,latLonIndex)
1230
1231 end do ! memberIndexCV
1232 end do ! subEnsIndex
1233 !$OMP END PARALLEL DO
1234
1235 ! Remove the weights mean computed over the columns
1236 do memberIndex = 1, nEns
1237 weightsMembersLatLon(memberIndex,:,latLonIndex) = &
1238 weightsMembersLatLon(memberIndex,:,latLonIndex) - &
1239 sum(weightsMembersLatLon(memberIndex,:,latLonIndex))/real(nEns,8)
1240 end do
1241
1242 else
1243
1244 call utl_abort('UNKNOWN LETKF ALGORITHM')
1245
1246 end if
1247
1248 else
1249
1250 ! no obs near this grid point, mean weights zero, member weights identity
1251 weightsMeanLatLon(:,1,latLonIndex) = 0.0d0
1252 weightsMembersLatLon(:,:,latLonIndex) = 0.0d0
1253 do memberIndex = 1, nEns
1254 if ( useModulatedEns ) then
1255 do eigenVectorColumnIndex = 1, numRetainedEigen
1256 memberIndexInModEns = (eigenVectorColumnIndex - 1) * nEns + memberIndex
1257 weightsMembersLatLon(memberIndexInModEns,memberIndex,latLonIndex) = 1.0d0
1258 end do
1259 else
1260 weightsMembersLatLon(memberIndex,memberIndex,latLonIndex) = 1.0d0
1261 end if
1262 end do
1263
1264 end if ! numLocalObs > 0
1265
1266 call utl_tmg_stop(134)
1267
1268 !
1269 ! Now post all send instructions (each lat-lon may be sent to multiple tasks)
1270 !
1271 call utl_tmg_start(132,'----CommWeights')
1272 latIndex = myLatIndexesSend(latLonIndex)
1273 lonIndex = myLonIndexesSend(latLonIndex)
1274 do procIndex = 1, myNumProcIndexesSend(latLonIndex)
1275 sendTag = latLonTagMpiGlobal(lonIndex,latIndex)
1276 procIndexSend = myProcIndexesSend(latLonIndex, procIndex)
1277
1278 nsize = nEnsGain
1279 numSend = numSend + 1
1280 call mpi_isend( weightsMeanLatLon(:,1,latLonIndex), &
1281 nsize, mmpi_datyp_real8, procIndexSend-1, sendTag, &
1282 mmpi_comm_grid, requestIdSend(numSend), ierr )
1283 nsize = nEnsGain * nEns
1284 numSend = numSend + 1
1285 sendTag = sendTag + maxval(latLonTagMpiGlobal(:,:))
1286 call mpi_isend( weightsMembersLatLon(:,:,latLonIndex), &
1287 nsize, mmpi_datyp_real8, procIndexSend-1, sendTag, &
1288 mmpi_comm_grid, requestIdSend(numSend), ierr )
1289 end do
1290 call utl_tmg_stop(132)
1291
1292 end do LATLON_LOOP
1293
1294 !
1295 ! Wait for communiations to finish before continuing
1296 !
1297 call utl_tmg_start(132,'----CommWeights')
1298 if (firstTime) write(*,*) 'numSend/Recv = ', numSend, numRecv
1299 firstTime = .false.
1300
1301 if ( numRecv > 0 ) then
1302 call mpi_waitAll(numRecv, requestIdRecv(1:numRecv), MPI_STATUSES_IGNORE, ierr)
1303 end if
1304
1305 if ( numSend > 0 ) then
1306 call mpi_waitAll(numSend, requestIdSend(1:numSend), MPI_STATUSES_IGNORE, ierr)
1307 end if
1308
1309 call utl_tmg_stop(132)
1310
1311 !
1312 ! Interpolate weights from coarse to full resolution
1313 !
1314 call utl_tmg_start(140,'----InterpolateWeights')
1315 if (wInterpInfo%latLonStep > 1) then
1316 call enkf_interpWeights(wInterpInfo, weightsMean)
1317 call enkf_interpWeights(wInterpInfo, weightsMembers)
1318 end if
1319 call utl_tmg_stop(140)
1320
1321 call utl_tmg_start(143,'----ApplyWeights')
1322
1323 !
1324 ! Apply the weights to compute the ensemble mean and members
1325 !
1326 call gsv_getField(stateVectorMeanInc,meanInc_ptr_r4)
1327 call gsv_getField(stateVectorMeanTrl,meanTrl_ptr_r4)
1328 call gsv_getField(stateVectorMeanAnl,meanAnl_ptr_r4)
1329
1330 !$OMP PARALLEL DO PRIVATE(latIndex, lonIndex, varLevIndex, levIndex2, memberTrl_ptr_r4, memberAnl_ptr_r4), &
1331 !$OMP PRIVATE(memberAnlPert, stepIndex, memberIndex, memberIndex2, memberIndex1, eigenVectorColumnIndex, pert_r4), &
1332 !$OMP PRIVATE(memberIndexInModEns, modulationFactor_r4)
1333 do latIndex = myLatBeg, myLatEnd
1334 LON_LOOP5: do lonIndex = myLonBeg, myLonEnd
1335
1336 ! skip this grid point if all weights zero (no nearby obs)
1337 if (all(weightsMean(:,1,lonIndex,latIndex) == 0.0d0)) cycle LON_LOOP5
1338
1339 ! Compute the ensemble mean increment and analysis
1340 do varLevIndex = 1, numVarLev
1341 ! Only treat varLevIndex values that correspond with current levIndex
1342 if (varLevelFromK(varLevIndex) == 'SF' .or. varLevelFromK(varLevIndex) == 'SFMM' .or. &
1343 varLevelFromK(varLevIndex) == 'SFTH' .or. varLevelFromK(varLevIndex) == 'SS') then
1344 if (varKindFromK(varLevIndex) == 'OC') then
1345 levIndex2 = 1
1346 else
1347 levIndex2 = max(nLev_M,nLev_depth)
1348 end if
1349 else if (varLevelFromK(varLevIndex) == 'MM' .or. varLevelFromK(varLevIndex) == 'TH' .or. varLevelFromK(varLevIndex) == 'DP') then
1350 levIndex2 = levFromK(varLevIndex)
1351 else if (varLevelFromK(varLevIndex) == 'OT') then
1352 ! Most (all?) variables using the 'other' coordinate are surface
1353 levIndex2 = max(nLev_M,nLev_depth)
1354 else
1355 write(*,*) 'varLevel = ', varLevelFromK(varLevIndex)
1356 call utl_abort('enkf_LETKFanalyses: unknown varLevel')
1357 end if
1358 if (levIndex2 /= levIndex .and. .not. useModulatedEns) cycle
1359 memberTrl_ptr_r4 => ens_getOneLev_r4(ensembleTrl,varLevIndex)
1360 do stepIndex = 1, tim_nstepobsinc
1361 ! mean increment
1362 if ( useModulatedEns ) then
1363 do eigenVectorColumnIndex = 1, numRetainedEigen
1364 call getModulationFactor( stateVectorMeanInc%vco, levIndex2, &
1365 eigenVectorColumnIndex, numRetainedEigen, &
1366 nEns, vLocalize, &
1367 modulationFactor_r4 )
1368
1369 do memberIndex = 1, nEns
1370 pert_r4 = modulationFactor_r4 * ( memberTrl_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex) - &
1371 meanTrl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) )
1372
1373 ! Index of the modulated ensemble member corresponding to original
1374 ! ensemble member index (memberIndex1) and eigenVectorColumnIndex.
1375 memberIndexInModEns = (eigenVectorColumnIndex - 1) * nEns + memberIndex
1376
1377 meanInc_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) = &
1378 meanInc_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) + &
1379 weightsMean(memberIndexInModEns,1,lonIndex,latIndex) * pert_r4
1380 end do
1381 end do
1382 else
1383 do memberIndex = 1, nEns
1384 meanInc_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) = &
1385 meanInc_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) + &
1386 weightsMean(memberIndex,1,lonIndex,latIndex) * &
1387 (memberTrl_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex) - &
1388 meanTrl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex))
1389 end do
1390 end if
1391
1392 ! mean analysis
1393 meanAnl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) = &
1394 meanTrl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) + &
1395 meanInc_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex)
1396 end do ! stepIndex
1397 end do ! varLevIndex
1398
1399 ! Compute the ensemble member analyses
1400 do varLevIndex = 1, numVarLev
1401 ! Only treat varLevIndex values that correspond with current levIndex
1402 if (varLevelFromK(varLevIndex) == 'SF' .or. varLevelFromK(varLevIndex) == 'SFMM' .or. &
1403 varLevelFromK(varLevIndex) == 'SFTH' .or. varLevelFromK(varLevIndex) == 'SS') then
1404 if (varKindFromK(varLevIndex) == 'OC') then
1405 levIndex2 = 1
1406 else
1407 levIndex2 = max(nLev_M,nLev_depth)
1408 end if
1409 else if (varLevelFromK(varLevIndex) == 'MM' .or. varLevelFromK(varLevIndex) == 'TH' .or. varLevelFromK(varLevIndex) == 'DP') then
1410 levIndex2 = levFromK(varLevIndex)
1411 else if (varLevelFromK(varLevIndex) == 'OT') then
1412 ! Most (all?) variables using the 'other' coordinate are surface
1413 levIndex2 = max(nLev_M,nLev_depth)
1414 else
1415 write(*,*) 'varLevel = ', varLevelFromK(varLevIndex)
1416 call utl_abort('enkf_LETKFanalyses: unknown varLevel')
1417 end if
1418 if (levIndex2 /= levIndex .and. .not. useModulatedEns) cycle
1419 memberTrl_ptr_r4 => ens_getOneLev_r4(ensembleTrl,varLevIndex)
1420 memberAnl_ptr_r4 => ens_getOneLev_r4(ensembleAnl,varLevIndex)
1421 do stepIndex = 1, tim_nstepobsinc
1422
1423 call utl_tmg_start(144,'------ApplyWeightsMember')
1424
1425 ! Compute analysis member perturbation
1426 memberAnlPert(:) = 0.0d0
1427
1428 if ( useModulatedEns ) then
1429 do memberIndex2 = 1, nEns
1430 do eigenVectorColumnIndex = 1, numRetainedEigen
1431 call getModulationFactor( stateVectorMeanInc%vco, levIndex2, &
1432 eigenVectorColumnIndex, numRetainedEigen, &
1433 nEns, vLocalize, &
1434 modulationFactor_r4 )
1435
1436 do memberIndex1 = 1, nEns
1437 ! Compute background ensemble perturbations for the modulated ensemble (Xb_Mod)
1438 pert_r4 = modulationFactor_r4 * ( memberTrl_ptr_r4(memberIndex1,stepIndex,lonIndex,latIndex) - &
1439 meanTrl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) )
1440
1441 ! Index of the modulated ensemble member corresponding to original
1442 ! ensemble member index (memberIndex1) and eigenVectorColumnIndex.
1443 memberIndexInModEns = (eigenVectorColumnIndex - 1) * nEns + memberIndex1
1444
1445 ! sum Xb_Mod * Wa over all modulated ensembles to get member perturbations for
1446 ! original ensemble (memberIndex2)
1447 memberAnlPert(memberIndex2) = memberAnlPert(memberIndex2) + &
1448 weightsMembers(memberIndexInModEns,memberIndex2,lonIndex,latIndex) * pert_r4
1449 end do
1450 end do
1451
1452 ! Compute final member perturbations by removing background original ensemble perturbations
1453 memberAnlPert(memberIndex2) = (memberTrl_ptr_r4(memberIndex2,stepIndex,lonIndex,latIndex) - &
1454 meanTrl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex)) + &
1455 memberAnlPert(memberIndex2)
1456
1457 end do ! memberIndex2
1458 else
1459 do memberIndex2 = 1, nEns
1460 do memberIndex1 = 1, nEns
1461 memberAnlPert(memberIndex2) = memberAnlPert(memberIndex2) + &
1462 weightsMembers(memberIndex1,memberIndex2,lonIndex,latIndex) * &
1463 (memberTrl_ptr_r4(memberIndex1,stepIndex,lonIndex,latIndex) - &
1464 meanTrl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex))
1465 end do ! memberIndex1
1466 end do ! memberIndex2
1467 end if
1468
1469 ! Add analysis member perturbation to mean analysis
1470 memberAnl_ptr_r4(:,stepIndex,lonIndex,latIndex) = &
1471 meanAnl_ptr_r4(lonIndex,latIndex,varLevIndex,stepIndex) + memberAnlPert(:)
1472
1473 call utl_tmg_stop(144)
1474
1475 end do ! stepIndex
1476 end do ! varLevIndex
1477
1478 end do LON_LOOP5
1479 end do
1480 !$OMP END PARALLEL DO
1481
1482 call utl_tmg_stop(143)
1483
1484 end do LEV_LOOP
1485
1486 if (countMaxExceeded > 0) then
1487 write(*,*) 'enkf_LETKFanalyses: WARNING: Found more local obs than specified max number at ', &
1488 real(100*countMaxExceeded)/real(numGridPointWeights), '% of grid points.'
1489 write(*,*) ' Maximum number found was ', maxCountMaxExceeded, &
1490 ' which is greater than specified number ', maxNumLocalObs
1491 write(*,*) ' Therefore will keep closest obs only.'
1492 end if
1493
1494 call utl_tmg_start(141,'----Barr')
1495 call rpn_comm_barrier('GRID',ierr)
1496 call utl_tmg_stop(141)
1497
1498 call gsv_deallocate(stateVectorMeanInc)
1499 call gsv_deallocate(stateVectorMeanTrl)
1500
1501 write(*,*) 'enkf_LETKFanalyses: done'
1502 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1503
1504 call utl_tmg_stop(131)
1505
1506 end subroutine enkf_LETKFanalyses
1507
1508 !----------------------------------------------------------------------
1509 ! enkf_computeVertLocation (private subroutine)
1510 !----------------------------------------------------------------------
1511 subroutine enkf_computeVertLocation(vertLocation_r4,stateVectorMeanTrl)
1512 !
1513 !:Purpose: Compute extract global 3D vertical location field from supplied
1514 ! stateVector. Can be either logPressure or depth levels.
1515 !
1516 implicit none
1517
1518 ! Arguments:
1519 real(4), allocatable, intent(inout) :: vertLocation_r4(:,:,:)
1520 type(struct_gsv), intent(inout) :: stateVectorMeanTrl
1521
1522 ! Locals:
1523 integer :: nLev_M, nLev_depth, nLev_vertLocation, levIndex, nsize, ierr
1524 real(4), pointer :: vertLocation_ptr_r4(:,:,:)
1525 type(struct_gsv) :: stateVectorMeanTrlPressure
1526 type(struct_gsv) :: stateVectorMeanTrlPressure_1step
1527
1528 write(*,*) 'enkf_computeVertLocation: starting'
1529
1530 nLev_M = gsv_getNumLev(stateVectorMeanTrl, 'MM')
1531 nLev_depth = gsv_getNumLev(stateVectorMeanTrl, 'DP')
1532 if ( nLev_M > 0 .and. nLev_depth > 0 ) then
1533 call utl_abort('enkf_computeVertLocation: both momentum and depth levels exist.')
1534 else if ( nLev_M == 0 .and. nLev_depth == 0 ) then
1535 call utl_abort('enkf_computeVertLocation: neither momentum nor depth levels exist.')
1536 end if
1537 nLev_vertLocation = max(nLev_M, nLev_depth)
1538
1539 allocate(vertLocation_r4(stateVectorMeanTrl%hco%ni, &
1540 stateVectorMeanTrl%hco%nj, &
1541 nLev_vertLocation))
1542
1543 if ( nLev_M > 0 ) then ! log pressure for NWP fields
1544
1545 ! Compute background ens mean 3D log pressure and make mpiglobal for vertical localization
1546 call gsv_allocate( stateVectorMeanTrlPressure, tim_nstepobsinc, &
1547 stateVectorMeanTrl%hco, stateVectorMeanTrl%vco, dateStamp_opt=tim_getDateStamp(), &
1548 mpi_local_opt=.true., mpi_distribution_opt='Tiles', &
1549 dataKind_opt=4, allocHeightSfc_opt=.true., varNames_opt=(/'P0','P_M','P_T'/) )
1550 call gsv_zero(stateVectorMeanTrlPressure)
1551 call gsv_copy(stateVectorMeanTrl, stateVectorMeanTrlPressure, allowVarMismatch_opt=.true.)
1552 call gvt_transform(stateVectorMeanTrlPressure,'ZandP_nl')
1553 if (mmpi_myid == 0) then
1554 call gsv_allocate( stateVectorMeanTrlPressure_1step, 1, &
1555 stateVectorMeanTrl%hco, stateVectorMeanTrl%vco, dateStamp_opt=tim_getDateStamp(), &
1556 mpi_local_opt=.false., &
1557 dataKind_opt=4, allocHeightSfc_opt=.true., varNames_opt=(/'P0','P_M','P_T'/) )
1558 end if
1559 call gsv_transposeTilesToStep(stateVectorMeanTrlPressure_1step, stateVectorMeanTrlPressure, (tim_nstepobsinc+1)/2)
1560 call gsv_deallocate(stateVectorMeanTrlPressure)
1561 if (mmpi_myid == 0) then
1562 call gsv_getField(stateVectorMeanTrlPressure_1step,vertLocation_ptr_r4,'P_M')
1563 vertLocation_r4(:,:,:) = log(vertLocation_ptr_r4(:,:,:))
1564 end if
1565 nsize = stateVectorMeanTrlPressure%ni * stateVectorMeanTrlPressure%nj * nLev_M
1566 call rpn_comm_bcast(vertLocation_r4, nsize, 'mpi_real4', 0, 'GRID', ierr)
1567
1568 else if ( nLev_depth > 0 ) then ! depth for ocean fields
1569
1570 ! fill in all horizontal grid points with the same profile of depth values
1571 do levIndex = 1, nLev_depth
1572 write(*,*) 'setting vertLocation for levIndex =', levIndex, &
1573 ', depth = ', stateVectorMeanTrl%vco%depths(levIndex)
1574 vertLocation_r4(:,:,levIndex) = stateVectorMeanTrl%vco%depths(levIndex)
1575 end do
1576
1577 end if
1578
1579 write(*,*) 'enkf_computeVertLocation: finished'
1580
1581 end subroutine enkf_computeVertLocation
1582
1583 !----------------------------------------------------------------------
1584 ! enkf_setupMpiDistribution (private subroutine)
1585 !----------------------------------------------------------------------
1586 subroutine enkf_LETKFsetupMpiDistribution(myNumLatLonRecv, myNumLatLonSend, &
1587 myLatIndexesRecv, myLonIndexesRecv, &
1588 myLatIndexesSend, myLonIndexesSend, &
1589 myProcIndexesRecv, myProcIndexesSend, &
1590 myNumProcIndexesSend, mpiDistribution, wInterpInfo)
1591 !
1592 !:Purpose: Setup for distribution of grid points over mpi tasks.
1593 !
1594 implicit none
1595
1596 ! Arguments:
1597 integer, intent(out) :: myNumLatLonRecv
1598 integer, intent(out) :: myNumLatLonSend
1599 integer, allocatable, intent(out) :: myLatIndexesRecv(:)
1600 integer, allocatable, intent(out) :: myLonIndexesRecv(:)
1601 integer, allocatable, intent(out) :: myLatIndexesSend(:)
1602 integer, allocatable, intent(out) :: myLonIndexesSend(:)
1603 integer, allocatable, intent(out) :: myProcIndexesRecv(:)
1604 integer, allocatable, intent(out) :: myProcIndexesSend(:,:)
1605 integer, allocatable, intent(out) :: myNumProcIndexesSend(:)
1606 character(len=*), intent(in) :: mpiDistribution
1607 type(struct_enkfInterpInfo), intent(in) :: wInterpInfo
1608
1609 ! Locals:
1610 integer :: latIndex, lonIndex, procIndex, procIndexSend, latLonIndex
1611 integer :: myLonBegHalo, myLonEndHalo, myLatBegHalo, myLatEndHalo
1612 integer :: numLatLonRecvMax, numLatLonTotalUnique, ierr
1613 integer, allocatable :: allLatIndexesRecv(:,:), allLonIndexesRecv(:,:)
1614 integer, allocatable :: allLatIndexesSend(:,:), allLonIndexesSend(:,:)
1615 integer, allocatable :: allNumLatLonRecv(:), allNumLatLonSend(:)
1616
1617 myLonBegHalo = wInterpInfo%myLonBegHalo
1618 myLonEndHalo = wInterpInfo%myLonEndHalo
1619 myLatBegHalo = wInterpInfo%myLatBegHalo
1620 myLatEndHalo = wInterpInfo%myLatEndHalo
1621
1622 write(*,*) 'enkf_LETKFsetupMpiDistribution: starting'
1623 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1624
1625 if (trim(mpiDistribution) == 'TILES') then
1626
1627 ! First, determine number of grid points needed locally (for recv-ing)
1628 myNumLatLonRecv = 0
1629 do latIndex = myLatBegHalo, myLatEndHalo
1630 LON_LOOP0: do lonIndex = myLonBegHalo, myLonEndHalo
1631 ! If this lat-lon is to be interpolated, then skip calculation
1632 if (wInterpInfo%numIndexes(lonIndex,latIndex) > 0) cycle LON_LOOP0
1633 myNumLatLonRecv = myNumLatLonRecv + 1
1634 end do LON_LOOP0
1635 end do
1636
1637 write(*,*) 'enkf_LETKFsetupMpiDistribution: myNumLatLonRecv =', myNumLatLonRecv
1638
1639 ! Determine list of grid point indexes where weights needed locally (for recv-ing)
1640 allocate(myLatIndexesRecv(myNumLatLonRecv))
1641 allocate(myLonIndexesRecv(myNumLatLonRecv))
1642 allocate(myProcIndexesRecv(myNumLatLonRecv))
1643 myNumLatLonRecv = 0
1644 do latIndex = myLatBegHalo, myLatEndHalo
1645 LON_LOOP1: do lonIndex = myLonBegHalo, myLonEndHalo
1646 ! If this lat-lon is to be interpolated, then skip calculation
1647 if (wInterpInfo%numIndexes(lonIndex,latIndex) > 0) cycle LON_LOOP1
1648 myNumLatLonRecv = myNumLatLonRecv + 1
1649
1650 myLatIndexesRecv(myNumLatLonRecv) = latIndex
1651 myLonIndexesRecv(myNumLatLonRecv) = lonIndex
1652 myProcIndexesRecv(myNumLatLonRecv) = mmpi_myid+1
1653 end do LON_LOOP1
1654 end do
1655
1656 ! No communication, so send info equals recv info
1657 myNumLatLonSend = myNumLatLonRecv
1658 allocate(myLatIndexesSend(myNumLatLonSend))
1659 allocate(myLonIndexesSend(myNumLatLonSend))
1660 allocate(myProcIndexesSend(myNumLatLonSend,1))
1661 allocate(myNumProcIndexesSend(myNumLatLonSend))
1662
1663 myLatIndexesSend(:) = myLatIndexesRecv(:)
1664 myLonIndexesSend(:) = myLonIndexesRecv(:)
1665 myProcIndexesSend(:,1) = myProcIndexesRecv(:)
1666 myNumProcIndexesSend(:) = 1
1667
1668 else if (trim(mpiDistribution) == 'ROUNDROBIN') then
1669
1670 ! First, determine number of grid points needed locally (for recv-ing)
1671 myNumLatLonRecv = 0
1672 do latIndex = myLatBegHalo, myLatEndHalo
1673 LON_LOOP2: do lonIndex = myLonBegHalo, myLonEndHalo
1674 ! If this lat-lon is to be interpolated, then skip calculation
1675 if (wInterpInfo%numIndexes(lonIndex,latIndex) > 0) cycle LON_LOOP2
1676 myNumLatLonRecv = myNumLatLonRecv + 1
1677 end do LON_LOOP2
1678 end do
1679
1680 ! Communicate to all mpi tasks
1681 allocate(allNumLatLonRecv(mmpi_nprocs))
1682 call rpn_comm_allgather(myNumLatLonRecv, 1, "mpi_integer", &
1683 allNumLatLonRecv, 1,"mpi_integer", "GRID", ierr)
1684 numLatLonRecvMax = maxval(allNumLatLonRecv)
1685 write(*,*) 'enkf_LETKFsetupMpiDistribution: allNumLatLonRecv =', allNumLatLonRecv(:)
1686 write(*,*) 'enkf_LETKFsetupMpiDistribution: numLatLonRecvSum =', sum(allNumLatLonRecv)
1687 write(*,*) 'enkf_LETKFsetupMpiDistribution: numLatLonRecvMax =', numLatLonRecvMax
1688
1689 ! Determine list of grid point indexes where weights needed locally (for recv-ing)
1690 allocate(myLatIndexesRecv(numLatLonRecvMax))
1691 allocate(myLonIndexesRecv(numLatLonRecvMax))
1692 allocate(myProcIndexesRecv(numLatLonRecvMax))
1693 myLatIndexesRecv(:) = -1
1694 myLonIndexesRecv(:) = -1
1695 myProcIndexesRecv(:) = -1
1696 myNumLatLonRecv = 0
1697 do latIndex = myLatBegHalo, myLatEndHalo
1698 LON_LOOP3: do lonIndex = myLonBegHalo, myLonEndHalo
1699 ! If this lat-lon is to be interpolated, then skip calculation
1700 if (wInterpInfo%numIndexes(lonIndex,latIndex) > 0) cycle LON_LOOP3
1701 myNumLatLonRecv = myNumLatLonRecv + 1
1702
1703 myLatIndexesRecv(myNumLatLonRecv) = latIndex
1704 myLonIndexesRecv(myNumLatLonRecv) = lonIndex
1705 end do LON_LOOP3
1706 end do
1707
1708 ! Communicate to all mpi tasks this list of grid point lat-lon indexes
1709 allocate(allLatIndexesRecv(numLatLonRecvMax, mmpi_nprocs))
1710 allocate(allLonIndexesRecv(numLatLonRecvMax, mmpi_nprocs))
1711 call rpn_comm_allgather(myLatIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1712 allLatIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1713 "GRID", ierr)
1714 call rpn_comm_allgather(myLonIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1715 allLonIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1716 "GRID", ierr)
1717
1718 ! From these lat-lon lists, create unique master list of all grid points where weights computed
1719 ! and assign to mpi tasks for doing the calculation and for send-ing
1720 allocate(myLatIndexesSend(numLatLonRecvMax))
1721 allocate(myLonIndexesSend(numLatLonRecvMax))
1722 myLatIndexesSend(:) = -1
1723 myLonIndexesSend(:) = -1
1724 numLatLonTotalUnique = 0
1725 myNumLatLonSend = 0
1726 do procIndex = 1, mmpi_nprocs
1727 WEIGHTS1LEV_LOOP: do latLonIndex = 1, allNumLatLonRecv(procIndex)
1728 if (enkf_latLonAlreadyFound(allLatIndexesRecv, allLonIndexesRecv, latLonIndex, procIndex)) &
1729 cycle WEIGHTS1LEV_LOOP
1730 ! Count the total number of weights
1731 numLatLonTotalUnique = numLatLonTotalUnique + 1
1732
1733 ! Round-robin distribution of master list across mpi tasks
1734 procIndexSend = 1 + mod(numLatLonTotalUnique-1, mmpi_nprocs)
1735
1736 ! Store the lat-lon indexes of the weights I am responsible for
1737 if (procIndexSend == (mmpi_myid+1)) then
1738 myNumLatLonSend = myNumLatLonSend + 1
1739 myLatIndexesSend(myNumLatLonSend) = &
1740 allLatIndexesRecv(latLonIndex, procIndex)
1741 myLonIndexesSend(myNumLatLonSend) = &
1742 allLonIndexesRecv(latLonIndex, procIndex)
1743 end if
1744 end do WEIGHTS1LEV_LOOP
1745 end do
1746 write(*,*) 'enkf_LETKFsetupMpiDistribution: number of lat/lon points where weights to be computed =', &
1747 numLatLonTotalUnique
1748
1749 ! Communicate to all mpi tasks this list of grid point lat-lon indexes
1750 allocate(allNumLatLonSend(mmpi_nprocs))
1751 call rpn_comm_allgather(myNumLatLonSend, 1, "mpi_integer", &
1752 allNumLatLonSend, 1,"mpi_integer", "GRID", ierr)
1753 allocate(allLatIndexesSend(numLatLonRecvMax, mmpi_nprocs))
1754 allocate(allLonIndexesSend(numLatLonRecvMax, mmpi_nprocs))
1755 call rpn_comm_allgather(myLatIndexesSend, numLatLonRecvMax, "mpi_integer", &
1756 allLatIndexesSend, numLatLonRecvMax, "mpi_integer", &
1757 "GRID", ierr)
1758 call rpn_comm_allgather(myLonIndexesSend, numLatLonRecvMax, "mpi_integer", &
1759 allLonIndexesSend, numLatLonRecvMax, "mpi_integer", &
1760 "GRID", ierr)
1761
1762 ! Figure out which mpi tasks I will need to send my results to
1763 allocate(myProcIndexesSend(myNumLatLonSend,mmpi_nprocs))
1764 allocate(myNumProcIndexesSend(myNumLatLonSend))
1765 myProcIndexesSend(:,:) = -1
1766 myNumProcIndexesSend(:) = 0
1767 do latLonIndex = 1, myNumLatLonSend
1768 do procIndex = 1, mmpi_nprocs
1769 if ( any( (myLatIndexesSend(latLonIndex) == allLatIndexesRecv(1:allNumLatLonRecv(procIndex), procIndex)) .and. &
1770 (myLonIndexesSend(latLonIndex) == allLonIndexesRecv(1:allNumLatLonRecv(procIndex), procIndex)) ) ) then
1771 myNumProcIndexesSend(latLonIndex) = myNumProcIndexesSend(latLonIndex) + 1
1772 myProcIndexesSend(latLonIndex,myNumProcIndexesSend(latLonIndex)) = procIndex
1773 end if
1774 end do
1775 end do
1776
1777 ! Figure out which mpi tasks I will receive the results from
1778 do latLonIndex = 1, myNumLatLonRecv
1779 do procIndex = 1, mmpi_nprocs
1780 if ( any( (myLatIndexesRecv(latLonIndex) == allLatIndexesSend(1:allNumLatLonSend(procIndex), procIndex)) .and. &
1781 (myLonIndexesRecv(latLonIndex) == allLonIndexesSend(1:allNumLatLonSend(procIndex), procIndex)) ) ) then
1782 myProcIndexesRecv(latLonIndex) = procIndex
1783 end if
1784 end do
1785 end do
1786
1787 else
1788 call utl_abort('enkf_LETKFsetupMpiDistribution: unknown MPI distribution selected')
1789 end if
1790
1791 write(*,*) 'enkf_LETKFsetupMpiDistribution: lat/lon/proc indexes I need to receive:'
1792 do latLonIndex = 1, myNumLatLonRecv
1793 write(*,*) myLatIndexesRecv(latLonIndex), myLonIndexesRecv(latLonIndex), &
1794 myProcIndexesRecv(latLonIndex)
1795 end do
1796
1797 write(*,*) 'enkf_LETKFsetupMpiDistribution: number of lat/lon indexes I am responsible for =', myNumLatLonSend
1798 write(*,*) 'enkf_LETKFsetupMpiDistribution: the lat/lon/proc indexes I am responsible for:'
1799 do latLonIndex = 1, myNumLatLonSend
1800 write(*,*) myLatIndexesSend(latLonIndex), myLonIndexesSend(latLonIndex), &
1801 myProcIndexesSend(latLonIndex,1:myNumProcIndexesSend(latLonIndex))
1802 end do
1803
1804 write(*,*) 'enkf_LETKFsetupMpiDistribution: done'
1805 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1806
1807 end subroutine enkf_LETKFsetupMpiDistribution
1808
1809 !----------------------------------------------------------------------
1810 ! enkf_LETKFgetMpiGlobalTags (private subroutine)
1811 !----------------------------------------------------------------------
1812 subroutine enkf_LETKFgetMpiGlobalTags(latLonTagMpiGlobal,myLatIndexesRecv,myLonIndexesRecv)
1813 implicit none
1814
1815 ! Arguments:
1816 integer, intent(out) :: latLonTagMpiGlobal(:,:)
1817 integer, intent(in) :: myLatIndexesRecv(:)
1818 integer, intent(in) :: myLonIndexesRecv(:)
1819
1820 ! Locals:
1821 integer :: ierr, ni, nj, lonIndex, latIndex
1822 integer :: countTags, myNumLatLonRecv, numLatLonRecvMax
1823 integer, allocatable :: allNumLatLonRecv(:)
1824 integer, allocatable :: allLatIndexesRecv(:,:), allLonIndexesRecv(:,:)
1825
1826 write(*,*) 'enkf_LETKFgetMpiGlobalTags: Starting'
1827
1828 ni = size(latLonTagMpiGlobal,1)
1829 nj = size(latLonTagMpiGlobal,2)
1830
1831 myNumLatLonRecv = size(myLatIndexesRecv)
1832 allocate(allNumLatLonRecv(mmpi_nprocs))
1833 call rpn_comm_allgather(myNumLatLonRecv, 1, "mpi_integer", &
1834 allNumLatLonRecv, 1,"mpi_integer", "GRID", ierr)
1835 numLatLonRecvMax = maxval(allNumLatLonRecv)
1836
1837 ! Communicate to all mpi tasks this list of grid point lat-lon indexes
1838 allocate(allLatIndexesRecv(numLatLonRecvMax, mmpi_nprocs))
1839 allocate(allLonIndexesRecv(numLatLonRecvMax, mmpi_nprocs))
1840 call rpn_comm_allgather(myLatIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1841 allLatIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1842 "GRID", ierr)
1843 call rpn_comm_allgather(myLonIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1844 allLonIndexesRecv, numLatLonRecvMax, "mpi_integer", &
1845 "GRID", ierr)
1846
1847 latLonTagMpiGlobal(:,:) = 0
1848 !$OMP PARALLEL DO PRIVATE(latIndex, lonIndex)
1849 do lonIndex = 1, ni
1850 do latIndex = 1, nj
1851 if (any(lonIndex == allLonIndexesRecv(:,:) .and. latIndex == allLatIndexesRecv(:,:))) then
1852 latLonTagMpiGlobal(lonIndex,latIndex) = 1
1853 end if
1854 end do
1855 end do
1856 !$OMP END PARALLEL DO
1857
1858 countTags = 0
1859 do lonIndex = 1, ni
1860 do latIndex = 1, nj
1861 if (latLonTagMpiGlobal(lonIndex,latIndex) == 1) then
1862 countTags = countTags + 1
1863 latLonTagMpiGlobal(lonIndex,latIndex) = countTags
1864 end if
1865 end do
1866 end do
1867 write(*,*) 'number of Recv grid points found = ', maxval(latLonTagMpiGlobal(:,:))
1868
1869 write(*,*) 'enkf_LETKFgetMpiGlobalTags: Finished'
1870
1871 end subroutine enkf_LETKFgetMpiGlobalTags
1872
1873 !----------------------------------------------------------------------
1874 ! enkf_latLonAlreadyFound (private function)
1875 !----------------------------------------------------------------------
1876 function enkf_latLonAlreadyFound(allLatIndexesRecv, allLonIndexesRecv, latLonIndex, procIndex) result(found)
1877 implicit none
1878
1879 ! Arguments:
1880 integer, intent(in) :: allLatIndexesRecv(:,:)
1881 integer, intent(in) :: allLonIndexesRecv(:,:)
1882 integer, intent(in) :: latLonIndex
1883 integer, intent(in) :: procIndex
1884 ! Result:
1885 logical :: found
1886
1887 ! Locals:
1888 integer :: latLonIndex2, procIndex2, numLatLonRecvMax
1889
1890 numLatLonRecvMax = size(allLatIndexesRecv, 1)
1891
1892 ! check on all previous mpi tasks if this lat/lon has already been encountered
1893 found = .false.
1894 do procIndex2 = 1, procIndex-1
1895 WEIGHTS1LEV_LOOP2: do latLonIndex2 = 1, numLatLonRecvMax
1896 if (allLatIndexesRecv(latLonIndex2, procIndex2) < 0) cycle WEIGHTS1LEV_LOOP2
1897 if ( (allLatIndexesRecv(latLonIndex, procIndex) == &
1898 allLatIndexesRecv(latLonIndex2, procIndex2)) .and. &
1899 (allLonIndexesRecv(latLonIndex, procIndex) == &
1900 allLonIndexesRecv(latLonIndex2, procIndex2)) ) then
1901 found = .true.
1902 exit WEIGHTS1LEV_LOOP2
1903 end if
1904 end do WEIGHTS1LEV_LOOP2
1905 end do
1906
1907 end function enkf_latLonAlreadyFound
1908
1909 !--------------------------------------------------------------------------
1910 ! enkf_setupInterpInfo
1911 !--------------------------------------------------------------------------
1912 subroutine enkf_setupInterpInfo(wInterpInfo, hco, weightLatLonStep, &
1913 myLonBeg,myLonEnd,myLatBeg,myLatEnd)
1914 !
1915 !:Purpose: Setup the weights and lat/lon indices needed to bilinearly
1916 ! interpolate the LETKF weights from a coarse grid to the full
1917 ! resolution grid. The coarseness of the grid is specified by
1918 ! the weightLatLonStep argument.
1919 !
1920 implicit none
1921
1922 ! Arguments:
1923 type(struct_enkfInterpInfo), intent(out) :: wInterpInfo
1924 type(struct_hco), intent(in) :: hco
1925 integer, intent(in) :: weightLatLonStep
1926 integer, intent(in) :: myLonBeg
1927 integer, intent(in) :: myLonEnd
1928 integer, intent(in) :: myLatBeg
1929 integer, intent(in) :: myLatEnd
1930
1931 ! Locals:
1932 integer :: lonIndex, latIndex, ni, nj
1933 integer :: myLonBegHalo, myLonEndHalo, myLatBegHalo, myLatEndHalo
1934 real(8) :: interpWeightLon, interpWeightLat
1935 logical :: includesYinYangBndry
1936
1937 ni = hco%ni
1938 nj = hco%nj
1939
1940 myLonBegHalo = 1 + weightLatLonStep * floor(real(myLonBeg - 1)/real(weightLatLonStep))
1941 myLonEndHalo = min(ni, 1 + weightLatLonStep * ceiling(real(myLonEnd - 1)/real(weightLatLonStep)))
1942 myLatBegHalo = 1 + weightLatLonStep * floor(real(myLatBeg - 1)/real(weightLatLonStep))
1943 myLatEndHalo = min(nj, 1 + weightLatLonStep * ceiling(real(myLatEnd - 1)/real(weightLatLonStep)))
1944 write(*,*) 'enkf_setupInterpInfo: myLonBeg/End, myLatBeg/End (original) = ', &
1945 myLonBeg, myLonEnd, myLatBeg, myLatEnd
1946 write(*,*) 'enkf_setupInterpInfo: myLonBeg/End, myLatBeg/End (with Halo) = ', &
1947 myLonBegHalo, myLonEndHalo, myLatBegHalo, myLatEndHalo
1948 write(*,*) 'enkf_setupInterpInfo: myLonCount, myLatCount (with Halo) = ', &
1949 myLonEndHalo-myLonBegHalo+1, myLatEndHalo-myLatBegHalo+1
1950 write(*,*) 'enkf_setupInterpInfo: number of local gridpts where weights computed = ', &
1951 ( 1 + ceiling(real(myLonEndHalo - myLonBegHalo) / real(weightLatLonStep)) ) * &
1952 ( 1 + ceiling(real(myLatEndHalo - myLatBegHalo) / real(weightLatLonStep)) )
1953 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
1954
1955 wInterpInfo%latLonStep = weightLatLonStep
1956 wInterpInfo%myLonBegHalo = myLonBegHalo
1957 wInterpInfo%myLonEndHalo = myLonEndHalo
1958 wInterpInfo%myLatBegHalo = myLatBegHalo
1959 wInterpInfo%myLatEndHalo = myLatEndHalo
1960 wInterpInfo%myLonBeg = myLonBeg
1961 wInterpInfo%myLonEnd = myLonEnd
1962 wInterpInfo%myLatBeg = myLatBeg
1963 wInterpInfo%myLatEnd = myLatEnd
1964
1965 allocate(wInterpInfo%numIndexes(myLonBegHalo:myLonEndHalo,myLatBegHalo:myLatEndHalo))
1966 if (weightLatLonStep > 1) then
1967 ! Figure out if this tile straddles Yin-Yang boundary
1968 if (hco%grtyp == 'U' .and. myLatBegHalo <= nj/2 .and. myLatEndHalo >= ((nj/2)+1)) then
1969 includesYinYangBndry = .true.
1970 else
1971 includesYinYangBndry = .false.
1972 end if
1973 allocate(wInterpInfo%lonIndexes(4,myLonBegHalo:myLonEndHalo,myLatBegHalo:myLatEndHalo))
1974 allocate(wInterpInfo%latIndexes(4,myLonBegHalo:myLonEndHalo,myLatBegHalo:myLatEndHalo))
1975 allocate(wInterpInfo%interpWeights(4,myLonBegHalo:myLonEndHalo,myLatBegHalo:myLatEndHalo))
1976 wInterpInfo%lonIndexes(:,:,:) = 0
1977 wInterpInfo%latIndexes(:,:,:) = 0
1978 wInterpInfo%interpWeights(:,:,:) = 0.0D0
1979 ! Determine which lat-lon are interpolated (wInterpInfo%numIndexes>0)
1980 wInterpInfo%numIndexes(:,:) = 4
1981 do latIndex = myLatBegHalo, myLatEndHalo, weightLatLonStep
1982 do lonIndex = myLonBegHalo, myLonEndHalo, weightLatLonStep
1983 wInterpInfo%numIndexes(lonIndex,latIndex) = 0
1984 end do
1985 end do
1986 ! Ensure weights are computed along edge of domain
1987 if (myLonEndHalo == ni .and. &
1988 myLatEndHalo == nj) then
1989 wInterpInfo%numIndexes(myLonEndHalo,myLatEndHalo) = 0
1990 end if
1991 if (myLonEndHalo == ni) then
1992 do latIndex = myLatBegHalo, myLatEndHalo, weightLatLonStep
1993 wInterpInfo%numIndexes(myLonEndHalo,latIndex) = 0
1994 end do
1995 ! Ensure weights are computed along both sides of Yin-Yang boundary
1996 if (includesYinYangBndry) then
1997 wInterpInfo%numIndexes(ni,nj/2) = 0
1998 wInterpInfo%numIndexes(ni,(nj/2)+1) = 0
1999 write(*,*) 'enkf_setupInterpInfo: Yin-Yang boundary (lon,lat1,lat2) =', &
2000 ni, nj/2, (nj/2)+1
2001 end if
2002 end if
2003 if (myLatEndHalo == nj) then
2004 do lonIndex = myLonBegHalo, myLonEndHalo, weightLatLonStep
2005 wInterpInfo%numIndexes(lonIndex,myLatEndHalo) = 0
2006 end do
2007 end if
2008 ! Ensure weights are computed along both sides of Yin-Yang boundary
2009 if (includesYinYangBndry) then
2010 do lonIndex = myLonBegHalo, myLonEndHalo, weightLatLonStep
2011 wInterpInfo%numIndexes(lonIndex,nj/2) = 0
2012 wInterpInfo%numIndexes(lonIndex,(nj/2)+1) = 0
2013 write(*,*) 'enkf_setupInterpInfo: Yin-Yang boundary (lon,lat1,lat2) =', &
2014 lonIndex, nj/2, (nj/2)+1
2015 end do
2016 end if
2017 ! For lon-only interpolation
2018 do latIndex = myLatBegHalo, myLatEndHalo
2019 if (wInterpInfo%numIndexes(myLonBegHalo,latIndex) > 0) cycle
2020 do lonIndex = myLonBegHalo, myLonEndHalo
2021 if (wInterpInfo%numIndexes(lonIndex,latIndex) == 0) cycle
2022 ! Find nearest grid point with a value towards left
2023 wInterpInfo%numIndexes(lonIndex,latIndex) = 2
2024 wInterpInfo%lonIndexes(1,lonIndex,latIndex) = myLonBegHalo + &
2025 weightLatLonStep * floor(real(lonIndex - myLonBegHalo)/real(weightLatLonStep))
2026 wInterpInfo%lonIndexes(2,lonIndex,latIndex) = min(ni, &
2027 wInterpInfo%lonIndexes(1,lonIndex,latIndex) + weightLatLonStep)
2028 wInterpInfo%latIndexes(1,lonIndex,latIndex) = latIndex
2029 wInterpInfo%latIndexes(2,lonIndex,latIndex) = latIndex
2030 wInterpInfo%interpWeights(1,lonIndex,latIndex) = &
2031 real(wInterpInfo%lonIndexes(2,lonIndex,latIndex) - lonIndex, 8)/real(weightLatLonStep, 8)
2032 wInterpInfo%interpWeights(2,lonIndex,latIndex) = 1.0D0 - &
2033 wInterpInfo%interpWeights(1,lonIndex,latIndex)
2034 end do
2035 end do
2036 ! For lat-only interpolation
2037 do latIndex = myLatBegHalo, myLatEndHalo
2038 do lonIndex = myLonBegHalo, myLonEndHalo, weightLatLonStep
2039 if (wInterpInfo%numIndexes(lonIndex,myLatBegHalo) > 0) cycle
2040 if (wInterpInfo%numIndexes(lonIndex,latIndex) == 0) cycle
2041 ! Find nearest grid point with a value towards bottom
2042 wInterpInfo%numIndexes(lonIndex,latIndex) = 2
2043 wInterpInfo%lonIndexes(1,lonIndex,latIndex) = lonIndex
2044 wInterpInfo%lonIndexes(2,lonIndex,latIndex) = lonIndex
2045 wInterpInfo%latIndexes(1,lonIndex,latIndex) = myLatBegHalo + &
2046 weightLatLonStep * floor(real(latIndex - myLatBegHalo)/real(weightLatLonStep))
2047 wInterpInfo%latIndexes(2,lonIndex,latIndex) = min(nj, &
2048 wInterpInfo%latIndexes(1,lonIndex,latIndex) + weightLatLonStep)
2049 ! Ensure we do not interpolate values across Yin-Yang boundary
2050 if (includesYinYangBndry) then
2051 if (latIndex <= nj/2) then
2052 wInterpInfo%latIndexes(2,lonIndex,latIndex) = min(nj/2, wInterpInfo%latIndexes(2,lonIndex,latIndex))
2053 else if(latIndex >= (nj/2)+1) then
2054 wInterpInfo%latIndexes(1,lonIndex,latIndex) = max((nj/2)+1, wInterpInfo%latIndexes(1,lonIndex,latIndex))
2055 end if
2056 end if
2057 wInterpInfo%interpWeights(1,lonIndex,latIndex) = &
2058 real(wInterpInfo%latIndexes(2,lonIndex,latIndex) - latIndex, 8)/real(weightLatLonStep, 8)
2059 wInterpInfo%interpWeights(2,lonIndex,latIndex) = 1.0D0 - &
2060 wInterpInfo%interpWeights(1,lonIndex,latIndex)
2061 end do
2062 end do
2063 ! For interior points needing 2D interpolation
2064 do latIndex = myLatBegHalo, myLatEndHalo
2065 do lonIndex = myLonBegHalo, myLonEndHalo
2066 if (wInterpInfo%numIndexes(lonIndex,latIndex) == 0) cycle ! no interpolation
2067 if (wInterpInfo%lonIndexes(1,lonIndex,latIndex) /= 0) cycle ! already set up
2068 wInterpInfo%numIndexes(lonIndex,latIndex) = 4
2069 ! 1. bottom-left indexes
2070 wInterpInfo%lonIndexes(1,lonIndex,latIndex) = myLonBegHalo + &
2071 weightLatLonStep * floor(real(lonIndex - myLonBegHalo)/real(weightLatLonStep))
2072 wInterpInfo%latIndexes(1,lonIndex,latIndex) = myLatBegHalo + &
2073 weightLatLonStep * floor(real(latIndex - myLatBegHalo)/real(weightLatLonStep))
2074 ! 2. bottom-right indexes
2075 wInterpInfo%lonIndexes(2,lonIndex,latIndex) = min(ni, &
2076 wInterpInfo%lonIndexes(1,lonIndex,latIndex) + weightLatLonStep)
2077 wInterpInfo%latIndexes(2,lonIndex,latIndex) = wInterpInfo%latIndexes(1,lonIndex,latIndex)
2078 ! 3. top-left indexes
2079 wInterpInfo%lonIndexes(3,lonIndex,latIndex) = wInterpInfo%lonIndexes(1,lonIndex,latIndex)
2080 wInterpInfo%latIndexes(3,lonIndex,latIndex) = min(nj, &
2081 wInterpInfo%latIndexes(1,lonIndex,latIndex) + weightLatLonStep)
2082 ! 4. top-right indexes
2083 wInterpInfo%lonIndexes(4,lonIndex,latIndex) = wInterpInfo%lonIndexes(2,lonIndex,latIndex)
2084 wInterpInfo%latIndexes(4,lonIndex,latIndex) = wInterpInfo%latIndexes(3,lonIndex,latIndex)
2085 ! Ensure we do not interpolate values across Yin-Yang boundary
2086 if (includesYinYangBndry) then
2087 if (latIndex <= nj/2) then
2088 wInterpInfo%latIndexes(3,lonIndex,latIndex) = min(nj/2, wInterpInfo%latIndexes(3,lonIndex,latIndex))
2089 wInterpInfo%latIndexes(4,lonIndex,latIndex) = min(nj/2, wInterpInfo%latIndexes(4,lonIndex,latIndex))
2090 else if(latIndex >= (nj/2)+1) then
2091 wInterpInfo%latIndexes(1,lonIndex,latIndex) = max((nj/2)+1, wInterpInfo%latIndexes(1,lonIndex,latIndex))
2092 wInterpInfo%latIndexes(2,lonIndex,latIndex) = max((nj/2)+1, wInterpInfo%latIndexes(2,lonIndex,latIndex))
2093 end if
2094 end if
2095 ! one-dimensional weights in lon and lat directions
2096 interpWeightLon = real(wInterpInfo%lonIndexes(4,lonIndex,latIndex) - lonIndex, 8) / &
2097 real(weightLatLonStep, 8)
2098 interpWeightLat = real(wInterpInfo%latIndexes(4,lonIndex,latIndex) - latIndex, 8) / &
2099 real(weightLatLonStep, 8)
2100 ! four interpolation weights
2101 wInterpInfo%interpWeights(1,lonIndex,latIndex) = interpWeightLon * interpWeightLat
2102 wInterpInfo%interpWeights(2,lonIndex,latIndex) = (1.0D0 - interpWeightLon) * interpWeightLat
2103 wInterpInfo%interpWeights(3,lonIndex,latIndex) = interpWeightLon * (1.0D0 - interpWeightLat)
2104 wInterpInfo%interpWeights(4,lonIndex,latIndex) = (1.0D0 - interpWeightLon) * (1.0D0 - interpWeightLat)
2105 end do
2106 end do
2107 else
2108 ! no interpolation, all weights are computed
2109 wInterpInfo%numIndexes(:,:) = 0
2110 end if
2111 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
2112
2113 end subroutine enkf_setupInterpInfo
2114
2115 !--------------------------------------------------------------------------
2116 ! enkf_interpWeights
2117 !--------------------------------------------------------------------------
2118 subroutine enkf_interpWeights(wInterpInfo, weights)
2119 !
2120 !:Purpose: Perform the bilinear interpolation of the weights
2121 ! using the precalculated interpolation info.
2122 !
2123 implicit none
2124
2125 ! Arguments:
2126 type(struct_enkfInterpInfo), intent(in) :: wInterpInfo
2127 real(8), intent(out) :: weights(1:,1:,wInterpInfo%myLonBegHalo:,wInterpInfo%myLatBegHalo:)
2128
2129 ! Locals:
2130 integer :: myLonBegHalo, myLonEndHalo, myLatBegHalo, myLatEndHalo
2131 integer :: lonIndex, latIndex, memberIndex1, memberIndex2, interpIndex
2132 integer :: interpLonIndex, interpLatIndex, numMembers1, numMembers2
2133 integer :: totalCount(mmpi_numthread)
2134 integer, external :: omp_get_thread_num
2135 logical, save :: firstCall = .true.
2136
2137 myLonBegHalo = wInterpInfo%myLonBegHalo
2138 myLonEndHalo = wInterpInfo%myLonEndHalo
2139 myLatBegHalo = wInterpInfo%myLatBegHalo
2140 myLatEndHalo = wInterpInfo%myLatEndHalo
2141 numMembers1 = size(weights,1)
2142 numMembers2 = size(weights,2)
2143 totalCount(:) = 0
2144
2145 !$OMP PARALLEL DO PRIVATE(latIndex, lonIndex, interpIndex, interpLatIndex, interpLonIndex, memberIndex1, memberIndex2)
2146 do latIndex = wInterpInfo%myLatBeg, wInterpInfo%myLatEnd
2147 do lonIndex = wInterpInfo%myLonBeg, wInterpInfo%myLonEnd
2148 if (wInterpInfo%numIndexes(lonIndex,latIndex) <= 0) cycle
2149 weights(:,:,lonIndex,latIndex) = 0.0D0
2150 if (wInterpInfo%lonIndexes(1,lonIndex,latIndex) == 0) cycle
2151
2152 totalCount(omp_get_thread_num()+1) = totalCount(omp_get_thread_num()+1) + wInterpInfo%numIndexes(lonIndex,latIndex)
2153
2154 do interpIndex = 1, wInterpInfo%numIndexes(lonIndex,latIndex)
2155 interpLonIndex = wInterpInfo%lonIndexes(interpIndex,lonIndex,latIndex)
2156 interpLatIndex = wInterpInfo%latIndexes(interpIndex,lonIndex,latIndex)
2157
2158 do memberIndex2 = 1, numMembers2
2159 do memberIndex1 = 1, numMembers1
2160 weights(memberIndex1,memberIndex2,lonIndex,latIndex) = &
2161 weights(memberIndex1,memberIndex2,lonIndex,latIndex) + &
2162 wInterpInfo%interpWeights(interpIndex,lonIndex,latIndex) * &
2163 weights(memberIndex1,memberIndex2,interpLonIndex,interpLatIndex)
2164 end do
2165 end do
2166
2167 end do ! interpIndex
2168
2169 end do ! lonIndex
2170 end do ! latIndex
2171 !$OMP END PARALLEL DO
2172
2173 if (firstCall) write(*,*) 'enkf_interpWeights: totalCount = ', totalCount(:)
2174 firstCall = .false.
2175
2176 end subroutine enkf_interpWeights
2177
2178 !--------------------------------------------------------------------------
2179 ! enkf_modifyAMSUBobsError
2180 !--------------------------------------------------------------------------
2181 subroutine enkf_modifyAMSUBobsError(obsSpaceData)
2182 implicit none
2183
2184 ! Arguments:
2185 type(struct_obs), target, intent(inout) :: obsSpaceData
2186
2187 ! Locals:
2188 real(pre_obsReal), parameter :: AMSUB_trop_oer = 1.0 ! assumed value for AMSU-B obs error in tropics
2189 integer :: headerIndex, bodyIndex, bodyIndexBeg, bodyIndexEnd, codeType
2190 real(pre_obsReal) :: lat_obs
2191
2192 ! for AMSUB observations set the observation error std dev equal to 1.0
2193 ! in the larger tropical area where the spread-skill correlation suggests
2194 ! that the data are accurate (.i.e |lat|<40. ). Otherwise don't reduce the
2195 ! observational error.
2196 do headerIndex = 1, obs_numheader(obsSpaceData)
2197 lat_obs = obs_headElem_r(obsSpaceData, obs_lat, headerIndex)
2198 codeType = obs_headElem_i(obsSpaceData, obs_ity, headerIndex)
2199 lat_obs = lat_obs * MPC_DEGREES_PER_RADIAN_R8
2200 if ( abs(lat_obs) < 40. .and. (codeType == codtyp_get_codtyp('amsub') .or. &
2201 codeType == codtyp_get_codtyp('mhs') .or. &
2202 codeType == codtyp_get_codtyp('mwhs2')) ) then
2203 bodyIndexBeg = obs_headElem_i(obsSpaceData, obs_rln, headerIndex)
2204 bodyIndexEnd = obs_headElem_i(obsSpaceData, obs_nlv, headerIndex) + bodyIndexBeg - 1
2205 do bodyIndex = bodyIndexBeg, bodyIndexEnd
2206 call obs_bodySet_r(obsSpaceData, obs_oer, bodyIndex, AMSUB_trop_oer)
2207 end do
2208 end if
2209 end do
2210
2211 end subroutine enkf_modifyAMSUBobsError
2212
2213 !--------------------------------------------------------------------------
2214 ! enkf_rejectHighLatIR
2215 !--------------------------------------------------------------------------
2216 subroutine enkf_rejectHighLatIR(obsSpaceData)
2217 implicit none
2218
2219 ! Arguments:
2220 type(struct_obs), target, intent(inout) :: obsSpaceData
2221
2222 ! Locals:
2223 integer :: headerIndex, bodyIndex, bodyIndexBeg, bodyIndexEnd, codeType
2224 real(pre_obsReal) :: lat_obs
2225
2226 ! reject all HIR radiance observation in arctic and antarctic (.i.e |lat|>60. )
2227 do headerIndex = 1, obs_numheader(obsSpaceData)
2228 lat_obs = obs_headElem_r(obsSpaceData, obs_lat, headerIndex)
2229 codeType = obs_headElem_i(obsSpaceData, obs_ity, headerIndex)
2230 lat_obs = lat_obs * MPC_DEGREES_PER_RADIAN_R8
2231 if ( abs(lat_obs) > 60. .and. tvs_isIdBurpHyperSpectral(codeType) ) then
2232 write(*,*) 'enkf_rejectHighLatIR: !!!!!!!!--------WARNING--------!!!!!!!!'
2233 write(*,*) 'enkf_rejectHighLatIR: This HIR radiance profile was rejected because |lat|>60.'
2234 write(*,*) 'enkf_rejectHighLatIR: latidude= ', lat_obs, 'codtyp= ', codeType
2235 bodyIndexBeg = obs_headElem_i(obsSpaceData, obs_rln, headerIndex)
2236 bodyIndexEnd = obs_headElem_i(obsSpaceData, obs_nlv, headerIndex) + bodyIndexBeg - 1
2237 do bodyIndex = bodyIndexBeg, bodyIndexEnd
2238 call obs_bodySet_i(obsSpaceData, obs_ass, bodyIndex, obs_notAssimilated)
2239 ! also set the 'rejected by selection process' flag (bit 11)
2240 call obs_bodySet_i( obsSpaceData, obs_flg, bodyIndex, &
2241 ibset( obs_bodyElem_i( obsSpaceData, obs_flg, bodyIndex ), 11) )
2242 end do
2243 end if
2244 end do
2245
2246 end subroutine enkf_rejectHighLatIR
2247
2248 !--------------------------------------------------------------------------
2249 ! enkf_getModulatedState
2250 !--------------------------------------------------------------------------
2251 subroutine enkf_getModulatedState( stateVector_in, stateVectorMeanTrl, &
2252 vLocalizeLengthScale, numRetainedEigen, nEns, &
2253 eigenVectorColumnIndex, stateVector_out, &
2254 beSilent )
2255 !
2256 !:Purpose: Compute vertical localization matrix, and the corresponding
2257 ! eigenvectors/eigenvalues, to obtain modulated stateVector.
2258 !
2259 implicit none
2260
2261 ! Arguments:
2262 type(struct_gsv), intent(in) :: stateVector_in
2263 type(struct_gsv), intent(in) :: stateVectorMeanTrl
2264 real(8), intent(in) :: vLocalizeLengthScale
2265 integer, intent(in) :: numRetainedEigen
2266 integer, intent(in) :: nEns
2267 integer, intent(in) :: eigenVectorColumnIndex
2268 type(struct_gsv), intent(inout) :: stateVector_out
2269 logical, intent(in) :: beSilent
2270
2271 ! Locals:
2272 real(4) :: modulationFactor_r4
2273 real(4), pointer :: field_out_r4(:,:,:,:)
2274 integer :: nLev, nlev_out, levIndex, latIndex, lonIndex
2275 integer :: lon1, lon2, lat1, lat2
2276 integer :: varIndex, stepIndex, eigenVectorLevelIndex
2277 character(len=4) :: varName
2278
2279 call utl_tmg_start(130,'--getModulatedState')
2280
2281 if ( .not. beSilent ) write(*,*) 'enkf_getModulatedState: START'
2282
2283 if ( stateVector_in%dataKind /= 4 ) then
2284 call utl_abort('enkf_getModulatedState: only dataKind=4 is implemented')
2285 end if
2286
2287 nLev = stateVector_in%vco%nLev_M
2288 if ( vLocalizeLengthScale <= 0.0d0 .or. nLev <= 1 ) then
2289 call utl_abort('enkf_getModulatedState: no vertical localization')
2290 end if
2291
2292 ! Compute perturbation by subtracting ensMean
2293 call gsv_copy(stateVector_in, stateVector_out, beSilent_opt=beSilent)
2294 call gsv_add(stateVectorMeanTrl, stateVector_out, scaleFactor_opt=-1.0d0)
2295
2296 lon1 = stateVector_out%myLonBeg
2297 lon2 = stateVector_out%myLonEnd
2298 lat1 = stateVector_out%myLatBeg
2299 lat2 = stateVector_out%myLatEnd
2300
2301 ! Compute modulated member perturbation from original member perturbation:
2302 ! v'_k = (Nens*nLamda/(Nens - 1))^1/2 * Lambda^1/2 * E * x'_k
2303 step_loop: do stepIndex = 1, stateVector_out%numStep
2304 var_loop: do varIndex = 1, vnl_numvarmax
2305 varName = vnl_varNameList(varIndex)
2306 if ( .not. gsv_varExist(stateVector_out,varName) ) cycle var_loop
2307
2308 nlev_out = stateVector_out%varNumLev(varIndex)
2309
2310 call gsv_getField(statevector_out,field_out_r4,varName)
2311
2312 do latIndex = lat1, lat2
2313 do lonIndex = lon1, lon2
2314 do levIndex = 1, nlev_out
2315 if ( nlev_out == 1 ) then
2316 eigenVectorLevelIndex = nLev
2317 else
2318 eigenVectorLevelIndex = levIndex
2319 end if
2320
2321 call getModulationFactor( stateVector_in%vco, eigenVectorLevelIndex, &
2322 eigenVectorColumnIndex, numRetainedEigen, &
2323 nEns, vLocalizeLengthScale, &
2324 modulationFactor_r4, beSilent_opt=beSilent )
2325
2326 field_out_r4(lonIndex,latIndex,levIndex,stepIndex) = &
2327 field_out_r4(lonIndex,latIndex,levIndex,stepIndex) * &
2328 modulationFactor_r4
2329 end do
2330 end do
2331 end do
2332
2333 end do var_loop
2334 end do step_loop
2335
2336 ! Now add to ensMean to get modulated member
2337 ! v_k = v'_k + v_mean
2338 call gsv_add(stateVectorMeanTrl, stateVector_out)
2339
2340 if ( .not. beSilent ) write(*,*) 'enkf_getModulatedState: END'
2341
2342 call utl_tmg_stop(130)
2343
2344 end subroutine enkf_getModulatedState
2345
2346 !--------------------------------------------------------------------------
2347 ! enkf_setupModulationFactor
2348 !--------------------------------------------------------------------------
2349 subroutine enkf_setupModulationFactor(vco, numRetainedEigen, nEns, vLocalizeLengthScale, &
2350 beSilent)
2351 !
2352 !:Purpose: setup modulationFactorArray by calling getModulationFactor for first time.
2353 !
2354 implicit none
2355
2356 ! Arguments:
2357 type(struct_vco), pointer, intent(in) :: vco
2358 integer, intent(in) :: numRetainedEigen
2359 integer, intent(in) :: nEns
2360 real(8), intent(in) :: vLocalizeLengthScale
2361 logical, intent(in) :: beSilent
2362
2363 ! Locals:
2364 integer :: eigenVectorColumnIndex
2365 integer :: eigenVectorLevelIndex
2366 real(4) :: modulationFactor_r4
2367
2368 eigenVectorColumnIndex = 1
2369 eigenVectorLevelIndex = 1
2370 call getModulationFactor(vco, eigenVectorLevelIndex, &
2371 eigenVectorColumnIndex, numRetainedEigen, &
2372 nEns, vLocalizeLengthScale, &
2373 modulationFactor_r4, beSilent_opt=beSilent)
2374
2375 end subroutine enkf_setupModulationFactor
2376
2377 !--------------------------------------------------------------------------
2378 ! getModulationFactor
2379 !--------------------------------------------------------------------------
2380 subroutine getModulationFactor( vco, eigenVectorLevelIndex, &
2381 eigenVectorColumnIndex, numRetainedEigen, &
2382 nEns, vLocalizeLengthScale, &
2383 modulationFactor_r4, beSilent_opt )
2384 !
2385 !:Purpose: compute modulation factor needed to multiply ensemble
2386 ! perturbation to get the modulated perturbation:
2387 ! (Nens*nLambda/(Nens - 1))^1/2 * Lambda^1/2
2388 !
2389 implicit none
2390
2391 ! Arguments:
2392 type(struct_vco), pointer, intent(in) :: vco
2393 integer, intent(in) :: eigenVectorLevelIndex
2394 integer, intent(in) :: eigenVectorColumnIndex
2395 integer, intent(in) :: numRetainedEigen
2396 integer, intent(in) :: nEns
2397 real(8), intent(in) :: vLocalizeLengthScale
2398 real(4), intent(out) :: modulationFactor_r4
2399 logical, optional, intent(in) :: beSilent_opt
2400
2401 ! Locals:
2402 integer :: levIndex1, levIndex2, eigenIndex
2403 integer :: nLev, nLev_M, nLev_depth, matrixRank
2404 real(8) :: zr, zcorr, pSurfRef
2405 real(8) :: tolerance
2406 real(8), pointer :: pressureProfile(:)
2407 real(8), allocatable, save :: eigenValues(:)
2408 real(8), allocatable, save :: eigenVectors(:,:)
2409 real(8), allocatable, save :: verticalLocalizationMat(:,:)
2410 real(8), allocatable, save :: verticalLocalizationMatLowRank(:,:)
2411 real(4), allocatable, save :: modulationFactorArray_r4(:,:)
2412 logical :: beSilent
2413
2414 logical, save :: firstCall = .true.
2415
2416 if ( present(beSilent_opt) ) then
2417 beSilent = beSilent_opt
2418 else
2419 beSilent = .false.
2420 end if
2421
2422 ! Compute vertical localization matrix and its eigenValues/Vectors on first call
2423 if ( firstCall ) then
2424 firstCall = .false.
2425 if ( mmpi_myid == 0 .and. .not. beSilent ) then
2426 write(*,*) 'getModulationFactor: computing eigenValues/Vectors'
2427 end if
2428
2429 nLev_M = vco%nLev_M
2430 nLev_depth = vco%nlev_depth
2431 nLev = max(nLev_M,nLev_depth)
2432
2433 allocate(eigenValues(nLev))
2434 allocate(eigenVectors(nLev,nLev))
2435 allocate(verticalLocalizationMat(nLev,nLev))
2436 allocate(verticalLocalizationMatLowRank(nLev,nLev))
2437 allocate(modulationFactorArray_r4(numRetainedEigen,nLev))
2438 verticalLocalizationMatLowRank(:,:) = 0.0d0
2439
2440 pSurfRef = 101000.D0
2441 call czp_fetch1DLevels(vco, pSurfRef, profM_opt=pressureProfile)
2442
2443 call lfn_Setup(LocFunctionWanted='FifthOrder')
2444
2445 ! Calculate 5'th order function
2446 do levIndex1 = 1, nLev
2447 do levIndex2 = 1, nLev
2448 zr = abs(log(pressureProfile(levIndex2)) - log(pressureProfile(levIndex1)))
2449 zcorr = lfn_response(zr,vLocalizeLengthScale)
2450 verticalLocalizationMat(levIndex1,levIndex2) = zcorr
2451 end do
2452 end do
2453
2454 ! Compute eigenValues/Vectors of vertical localization matrix
2455 tolerance = 1.0D-50
2456 call utl_eigenDecomp(verticalLocalizationMat, eigenValues, eigenVectors, &
2457 tolerance, matrixRank)
2458 if ( matrixRank < numRetainedEigen ) then
2459 write(*,*) 'matrixRank=', matrixRank
2460 call utl_abort('getModulationFactor: verticalLocalizationMat is rank deficient=')
2461 end if
2462
2463 ! Compute low-ranked vertical localization matrix
2464 do levIndex1 = 1, nLev
2465 do levIndex2 = 1, nLev
2466 do eigenIndex = 1, numRetainedEigen
2467 verticalLocalizationMatLowRank(levIndex1,levIndex2) = verticalLocalizationMatLowRank(levIndex1,levIndex2) + &
2468 eigenVectors(levIndex1,eigenIndex) * &
2469 eigenVectors(levIndex2,eigenIndex) * &
2470 eigenValues(eigenIndex)
2471 end do
2472 end do
2473 end do
2474
2475 ! now compute the 2D modulationFactor array
2476 do levIndex1 = 1, nLev
2477 do eigenIndex = 1, numRetainedEigen
2478 modulationFactorArray_r4(eigenIndex,levIndex1) = real( &
2479 1 / sqrt(verticalLocalizationMatLowRank(levIndex1,levIndex1)) * &
2480 eigenVectors(levIndex1,eigenIndex) * &
2481 eigenValues(eigenIndex) ** 0.5 * &
2482 (nEns * numRetainedEigen / (nEns - 1)) ** 0.5,4)
2483 end do
2484 end do
2485
2486 if ( mmpi_myid == 0 .and. .not. beSilent ) then
2487 do levIndex1 = 1, numRetainedEigen
2488 write(*,*) 'getModulationFactor: eigen mode=', levIndex1, ', eigenVectors=', eigenVectors(:,levIndex1)
2489 end do
2490 write(*,*) 'getModulationFactor: eigenValues=', eigenValues(1:numRetainedEigen)
2491
2492 do levIndex1 = 1, nLev
2493 write(*,*) 'getModulationFactor: verticalLocalizationMat for lev ', levIndex1, '=', verticalLocalizationMat(levIndex1,:)
2494 write(*,*) 'getModulationFactor: verticalLocalizationMatLowRank for lev ', levIndex1, '=', verticalLocalizationMatLowRank(levIndex1,:)
2495 end do
2496 end if
2497 end if
2498
2499 modulationFactor_r4 = modulationFactorArray_r4(eigenVectorColumnIndex,eigenVectorLevelIndex)
2500
2501 end subroutine getModulationFactor
2502
2503end module enkf_mod