1MODULE ensembleObservations_mod
2 ! MODULE ensembleObservations_mod (prefix='eob' category='6. High-level data objects')
3 !
4 !:Purpose: Store and manipulate ensemble of quanitites in observation space.
5 ! This module uses the kdtree2 module for efficiently finding the
6 ! nearest observations within the local volume.
7 !
8 use kdTree2_mod
9 use columnData_mod
10 use tovsNL_mod
11 use rttov_types, only: rttov_transmission, rttov_profile
12 use parkind1, only: jpim, jprb
13 use midasMpi_mod
14 use oceanMask_mod
15 use obsSpaceData_mod
16 use randomNumber_mod
17 use mathPhysConstants_mod
18 use utilities_mod
19 use earthConstants_mod
20 use bufr_mod
21 use codePrecision_mod
22 use codtyp_mod
23 use obsfamilylist_mod
24 use varnamelist_mod
25 implicit none
26 save
27 private
28
29 ! public types
30 public :: struct_eob
31
32 ! public procedures
33 public :: eob_init, eob_allocate, eob_deallocate, eob_allGather, eob_getLocalBodyIndices
34 public :: eob_setYb, eob_setYa, eob_setDeterYb, eob_setLatLonObs, eob_setObsErrInv
35 public :: eob_setHPHT, eob_calcAndRemoveMeanYb, eob_setVertLocation, eob_setAssFlag, eob_copy, eob_zero
36 public :: eob_calcRandPert, eob_setSigiSigo, eob_setTypeVertCoord, eob_setSimObsVal
37 public :: eob_backgroundCheck, eob_huberNorm, eob_rejectRadNearSfc, eob_setMeanOMP
38 public :: eob_removeObsNearLand, eob_readFromFiles, eob_writeToFiles
39
40 ! public variables
41 public :: eob_simObsAssim
42
43 integer, parameter :: maxNumLocalObsSearch = 500000
44 integer, external :: get_max_rss
45 logical :: eob_simObsAssim, psvObsAssim
46 integer :: numSimObsFam
47 integer :: numPsvObsFam
48 integer :: numSimCodTyp(ofl_numFamily), numPsvCodTyp(ofl_numFamily)
49 integer :: numSimVarNum(vnl_numvarmax), numPsvVarNum(vnl_numvarmax)
50 integer, allocatable :: simCodTyp(:,:), psvCodTyp(:,:)
51 integer, allocatable :: simVarNum(:,:), psvVarNum(:,:)
52
53 type struct_eob
54 logical :: allocated = .false.
55 logical :: meanRemoved = .false.
56 integer :: numMembers ! number of ensemble members
57 integer :: numObs ! number of observations
58 integer :: fileMemberIndex1 = 1 ! first member number in ensemble set
59 character(len=20) :: typeVertCoord = 'undefined' ! 'logPressure' or 'depth'
60 type(struct_obs), pointer :: obsSpaceData ! pointer to obsSpaceData object
61 real(8), allocatable :: lat(:), lon(:) ! lat/lon of observation
62 real(8), allocatable :: vertLocation(:) ! in ln(pres) or meters, used for localization
63 real(8), allocatable :: obsErrInv(:) ! inverse of obs error variances
64 real(8), allocatable :: obsErrInv_sim(:) ! like obsErrInv, used when simulating observations
65 real(4), allocatable :: Yb_r4(:,:) ! background ensemble perturbation in obs space
66 real(4), allocatable :: Ya_r4(:,:) ! analysis ensemble perturbation in obs space
67 real(4), allocatable :: randPert_r4(:,:) ! unbiased random perturbations with covariance equal to R
68 real(8), allocatable :: meanYb(:) ! ensemble mean background state in obs space
69 real(8), allocatable :: deterYb(:) ! deterministic background state in obs space
70 real(8), allocatable :: obsValue(:) ! the observed value
71 integer, allocatable :: assFlag(:) ! assimilation flag
72 end type struct_eob
73
74 type(kdtree2), pointer :: tree => null()
75
76 ! namelist variables
77 character(len=2) :: simObsFamily(ofl_numFamily) ! observation families for simulation
78 character(len=2) :: psvObsFamily(ofl_numFamily) ! observation families for passive assimilation
79 character(len=codtyp_name_length) :: simCodTypName(ofl_numFamily,codtyp_maxNumber) ! codtyp names for sim. obs families
80 character(len=codtyp_name_length) :: psvCodTypName(ofl_numFamily,codtyp_maxNumber) ! codtyp names for psv. obs families
81 character(len=4) :: simVarName(ofl_numFamily,vnl_numvarmax) ! varName(s) for sim. obs families
82 character(len=4) :: psvVarName(ofl_numFamily,vnl_numvarmax) ! varName(s) for psv. obs families
83 namelist /NAMENSOBS/simObsFamily, psvObsFamily, simCodTypName, psvCodTypName, simVarName, psvVarName
84
85
86CONTAINS
87
88 !--------------------------------------------------------------------------
89 ! eob_init
90 !--------------------------------------------------------------------------
91 subroutine eob_init()
92 !
93 !: Purpose: This subroutine reads the namelist section NAMENSOBS for this module.
94 !
95 implicit none
96
97 ! Locals:
98 integer :: nulnam, ierr, obsfamIndex, codtypIndex, varnumIndex
99 integer, external :: fnom, fclos
100 logical, save :: eob_initialized = .false.
101
102 if (eob_initialized) return
103 write(*,*) 'eob_init: starting'
104 eob_initialized = .true.
105
106 ! default values for namelist variables
107 simObsFamily(:) = ''
108 simCodTypName(:,:) = ''
109 simVarName(:,:) = ''
110 psvObsFamily(:) = ''
111 psvCodTypName(:,:) = ''
112 psvVarName(:,:) = ''
113
114 ! for tracking the number of non-empty chars in namelist variable arrays;
115 ! these are used in loops in various subroutines
116 numSimObsFam = 0
117 numPsvObsFam = 0
118 numSimCodTyp(:) = 0
119 numPsvCodTyp(:) = 0
120 numSimVarNum(:) = 0
121 numPsvVarNum(:) = 0
122
123 ! read namelist
124 if (utl_isNamelistPresent('namensobs','./flnml')) then
125 nulnam=0
126 ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
127 read(nulnam,nml=namensobs,iostat=ierr)
128 if (ierr /= 0) call utl_abort('eob_init: Error reading namelist namensobs')
129 ierr=fclos(nulnam)
130 do obsfamIndex = 1, ofl_numFamily
131 if (trim(simObsFamily(obsfamIndex)) /= '') then
132 numSimObsFam = numSimObsFam + 1
133 end if
134 if (trim(psvObsFamily(obsfamIndex)) /= '') then
135 numPsvObsFam = numPsvObsFam + 1
136 end if
137 end do
138
139 do obsfamIndex = 1, ofl_numFamily
140 ! Simulation functionality section
141 if (trim(simObsFamily(obsfamIndex)) /= '') then
142 ! simulated observation family specified for current
143 ! obsfamIndex; check to see if any codtyp names specified
144 do codtypIndex = 1, codtyp_maxNumber
145 if (trim(simCodTypName(obsfamIndex,codtypIndex)) /= '') then
146 numSimCodTyp(obsfamIndex) = numSimCodTyp(obsfamIndex) + 1
147 if (.not. allocated(simCodTyp)) then
148 allocate(simCodTyp(numSimObsFam,codtyp_maxNumber))
149 simCodTyp(:,:) = -999
150 end if
151 ! store CodTyp for simulated obs family
152 simCodTyp(obsfamIndex,codtypIndex) = codtyp_get_codtyp(simCodTypName(obsfamIndex,codtypIndex))
153 end if
154 end do ! codtypIndex
155 ! also check to see if any varnames specified
156 do varnumIndex = 1, vnl_numvarmax
157 if (trim(simVarName(obsfamIndex,varnumIndex)) /= '') then
158 numSimVarNum(obsfamIndex) = numSimVarNum(obsfamIndex) + 1
159 if (.not. allocated(simVarNum)) then
160 allocate(simVarNum(numSimObsFam,vnl_numvarmax))
161 simVarNum(:,:) = -999
162 end if
163 ! store VarNum for simulated obs family
164 simVarNum(obsfamIndex,varnumIndex) = vnl_varnumFromVarName(simVarName(obsfamIndex,varnumIndex))
165 end if
166 end do ! varnumIndex
167 end if ! simObsFamily
168
169 ! Passive functionality section
170 if (trim(psvObsFamily(obsfamIndex)) /= '') then
171 ! passive observation family specified for current
172 ! obsfamIndex; check to see if any codtyp names specified
173 do codtypIndex = 1, codtyp_maxNumber
174 if (trim(psvCodTypName(obsfamIndex,codtypIndex)) /= '') then
175 numPsvCodTyp(obsfamIndex) = numPsvCodTyp(obsfamIndex) + 1
176 if (.not. allocated(psvCodTyp)) then
177 allocate(psvCodTyp(numPsvObsFam,codtyp_maxNumber))
178 psvCodTyp(:,:) = -999
179 end if
180 ! store CodTyp for passive obs family
181 psvCodTyp(obsfamIndex,codtypIndex) = codtyp_get_codtyp(psvCodTypName(obsfamIndex,codtypIndex))
182 end if
183 end do ! codtypIndex
184 ! also check to see if any varnames specified
185 do varnumIndex = 1, vnl_numvarmax
186 if (trim(psvVarName(obsfamIndex,varnumIndex)) /= '') then
187 numPsvVarNum(obsfamIndex) = numPsvVarNum(obsfamIndex) + 1
188 if (.not. allocated(PsvVarNum)) then
189 allocate(psvVarNum(numPsvObsFam,vnl_numvarmax))
190 psvVarNum(:,:) = -999
191 end if
192 ! store VarNum for passive obs family
193 psvVarNum(obsfamIndex,varnumIndex) = vnl_varnumFromVarName(psvVarName(obsfamIndex,varnumIndex))
194 end if
195 end do ! varnumIndex
196 end if ! psvObsFamily
197
198 end do ! obsFamIndex
199 else
200 write(*,*)
201 write(*,*) 'eob_init: namensobs is missing in the namelist. The default value will be taken.'
202 end if
203
204 eob_simObsAssim = numSimObsFam > 0
205 psvObsAssim = numPsvObsFam > 0
206
207 write(*,*) 'eob_init: eob_simObsAssim = ', eob_simObsAssim
208 write(*,*) 'eob_init: psvObsAssim = ', psvObsAssim
209
210 end subroutine eob_init
211
212 !--------------------------------------------------------------------------
213 ! eob_allocate
214 !--------------------------------------------------------------------------
215 subroutine eob_allocate(ensObs, numMembers, numObs, obsSpaceData, &
216 fileMemberIndex1_opt)
217 !
218 !:Purpose: Allocate an ensObs object
219 !
220 implicit none
221
222 ! Arguments:
223 type(struct_eob) , intent(inout) :: ensObs
224 integer , intent(in) :: numMembers
225 integer , intent(in) :: numObs
226 type(struct_obs), target, intent(in) :: obsSpaceData
227 integer, optional , intent(in) :: fileMemberIndex1_opt
228
229 if (ensObs%allocated) then
230 write(*,*) 'eob_allocate: this object is already allocated, deallocating first.'
231 call eob_deallocate(ensObs)
232 end if
233
234 if (present(fileMemberIndex1_opt)) ensObs%fileMemberIndex1 = fileMemberIndex1_opt
235
236 call eob_init()
237
238 ensObs%obsSpaceData => obsSpaceData
239 ensObs%numMembers = numMembers
240 ensObs%numObs = numObs
241
242 allocate(ensObs%lat(ensObs%numObs))
243 allocate(ensObs%lon(ensObs%numObs))
244 allocate(ensObs%vertLocation(ensObs%numObs))
245 allocate(ensObs%obsValue(ensObs%numObs))
246 allocate(ensObs%obsErrInv(ensObs%numObs))
247 if (eob_simObsAssim) allocate(ensObs%obsErrInv_sim(ensObs%numObs))
248 allocate(ensObs%Yb_r4(ensObs%numMembers,ensObs%numObs))
249 allocate(ensObs%meanYb(ensObs%numObs))
250 allocate(ensObs%deterYb(ensObs%numObs))
251 allocate(ensObs%assFlag(ensObs%numObs))
252
253 ensObs%allocated = .true.
254
255 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
256
257 end subroutine eob_allocate
258
259 !--------------------------------------------------------------------------
260 ! eob_deallocate
261 !--------------------------------------------------------------------------
262 subroutine eob_deallocate(ensObs)
263 implicit none
264
265 ! Arguments:
266 type(struct_eob), intent(inout) :: ensObs
267
268 if (.not. ensObs%allocated) return
269
270 deallocate(ensObs%lat)
271 deallocate(ensObs%lon)
272 deallocate(ensObs%vertLocation)
273 deallocate(ensObs%obsValue)
274 deallocate(ensObs%obsErrInv)
275 if (allocated(ensObs%obsErrInv_sim)) deallocate(ensObs%obsErrInv_sim)
276 deallocate(ensObs%Yb_r4)
277 if (allocated(ensObs%Ya_r4)) deallocate(ensObs%Ya_r4)
278 if (allocated(ensObs%randPert_r4)) deallocate(ensObs%randPert_r4)
279 deallocate(ensObs%meanYb)
280 deallocate(ensObs%deterYb)
281 deallocate(ensObs%assFlag)
282
283 ensObs%allocated = .false.
284
285 end subroutine eob_deallocate
286
287 !--------------------------------------------------------------------------
288 ! eob_zero
289 !--------------------------------------------------------------------------
290 subroutine eob_zero(ensObs)
291 !
292 !:Purpose: Initialize an ensObs object to zero
293 !
294 implicit none
295
296 ! Arguments:
297 type(struct_eob), intent(inout) :: ensObs
298
299 if ( .not.ensObs%allocated ) then
300 call utl_abort('eob_zero: this object is not allocated')
301 end if
302
303 ensObs%lat(:) = 0.0d0
304 ensObs%lon(:) = 0.0d0
305 ensObs%vertLocation(:) = 0.0d0
306 ensObs%obsValue(:) = 0.0d0
307 ensObs%obsErrInv(:) = 0.0d0
308 if (allocated(ensObs%obsErrInv_sim)) ensObs%obsErrInv_sim(:) = 0.0
309 ensObs%Yb_r4(:,:) = 0.0
310 if (allocated(ensObs%Ya_r4)) ensObs%Ya_r4(:,:) = 0.0
311 if (allocated(ensObs%randPert_r4)) ensObs%randPert_r4(:,:) = 0.0
312 ensObs%meanYb(:) = 0.0d0
313 ensObs%deterYb(:) = 0.0d0
314 ensObs%assFlag(:) = 0
315
316 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
317
318 end subroutine eob_zero
319
320 !--------------------------------------------------------------------------
321 ! eob_setTypeVertCoord
322 !--------------------------------------------------------------------------
323 subroutine eob_setTypeVertCoord(ensObs, typeVertCoord)
324 !
325 !:Purpose: Set the type of vertical coordinate ('logPressure' or 'depth').
326 !
327 implicit none
328
329 ! Arguments:
330 type(struct_eob), intent(inout) :: ensObs
331 character(len=*), intent(in) :: typeVertCoord
332
333 if ( trim(typeVertCoord) /= 'logPressure' .and. &
334 trim(typeVertCoord) /= 'depth' ) then
335 write(*,*) 'eob_setTypeVertCoord: typeVertCoord = ', trim(typeVertCoord)
336 call utl_abort('eob_setTypeVertCoord: Unknown type of vertical coordinate')
337 end if
338
339 ensObs%typeVertCoord = typeVertCoord
340
341 end subroutine eob_setTypeVertCoord
342
343 !--------------------------------------------------------------------------
344 ! eob_clean (private routine)
345 !--------------------------------------------------------------------------
346 subroutine eob_clean(ensObs,ensObsClean)
347 !
348 !:Purpose: Remove all obs from the ensObs object that are not
349 ! flagged for assimilation. Put the cleaned result in the
350 ! locally created output object.
351 !
352 implicit none
353
354 ! Arguments:
355 type(struct_eob), intent(inout) :: ensObs
356 type(struct_eob), intent(out) :: ensObsClean
357
358 ! Locals:
359 integer :: obsIndex, obsCleanIndex, numObsClean
360
361 call eob_setAssFlag(ensObs)
362
363 numObsClean = 0
364 do obsIndex = 1, ensObs%numObs
365 if (ensObs%assFlag(obsIndex) == 1) numObsClean = numObsClean + 1
366 end do
367
368 write(*,*) 'eob_clean: reducing numObs from ', ensObs%numObs, ' to ', numObsClean
369 call eob_allocate(ensObsClean, ensObs%numMembers, numObsClean, ensObs%obsSpaceData)
370 if (allocated(ensObs%Ya_r4)) then
371 allocate(ensObsClean%Ya_r4(ensObs%numMembers,numObsClean))
372 end if
373 if (allocated(ensObs%randPert_r4)) then
374 allocate(ensObsClean%randPert_r4(ensObs%numMembers,numObsClean))
375 end if
376
377 obsCleanIndex = 0
378 do obsIndex = 1, ensObs%numObs
379 if (ensObs%assFlag(obsIndex) == 1) then
380 obsCleanIndex = obsCleanIndex + 1
381 ensObsClean%lat(obsCleanIndex) = ensObs%lat(obsIndex)
382 ensObsClean%lon(obsCleanIndex) = ensObs%lon(obsIndex)
383 ensObsClean%vertLocation(obsCleanIndex) = ensObs%vertLocation(obsIndex)
384 ensObsClean%obsErrInv(obsCleanIndex) = ensObs%obsErrInv(obsIndex)
385 if (allocated(ensObs%obsErrInv_sim)) then
386 ensObsClean%obsErrInv_sim(obsCleanIndex) = ensObs%obsErrInv_sim(obsIndex)
387 end if
388 ensObsClean%Yb_r4(:,obsCleanIndex) = ensObs%Yb_r4(:,obsIndex)
389 if (allocated(ensObs%Ya_r4)) then
390 ensObsClean%Ya_r4(:,obsCleanIndex) = ensObs%Ya_r4(:,obsIndex)
391 end if
392 if (allocated(ensObs%randPert_r4)) then
393 ensObsClean%randPert_r4(:,obsCleanIndex) = ensObs%randPert_r4(:,obsIndex)
394 end if
395 ensObsClean%meanYb(obsCleanIndex) = ensObs%meanYb(obsIndex)
396 ensObsClean%deterYb(obsCleanIndex) = ensObs%deterYb(obsIndex)
397 ensObsClean%obsValue(obsCleanIndex) = ensObs%obsValue(obsIndex)
398 ensObsClean%assFlag(obsCleanIndex) = ensObs%assFlag(obsIndex)
399 end if
400 end do
401
402 end subroutine eob_clean
403
404 !--------------------------------------------------------------------------
405 ! eob_copy
406 !--------------------------------------------------------------------------
407 subroutine eob_copy(ensObsIn,ensObsOut)
408 implicit none
409
410 ! Arguments:
411 type(struct_eob), intent(in) :: ensObsIn
412 type(struct_eob), intent(inout) :: ensObsOut
413
414 ensObsOut%lat(:) = ensObsIn%lat(:)
415 ensObsOut%lon(:) = ensObsIn%lon(:)
416 ensObsOut%vertLocation(:) = ensObsIn%vertLocation(:)
417 ensObsOut%obsErrInv(:) = ensObsIn%obsErrInv(:)
418 if (allocated(ensObsIn%obsErrInv_sim)) then
419 ensObsOut%obsErrInv(:) = ensObsIn%obsErrInv_sim(:)
420 end if
421 ensObsOut%Yb_r4(:,:) = ensObsIn%Yb_r4(:,:)
422 if (allocated(ensObsIn%Ya_r4)) then
423 allocate( ensObsOut%Ya_r4(ensObsIn%numMembers,ensObsIn%numObs))
424 ensObsOut%Ya_r4(:,:) = ensObsIn%Ya_r4(:,:)
425 end if
426 if (allocated(ensObsIn%randPert_r4)) then
427 allocate(ensObsOut%randPert_r4(ensObsIn%numMembers,ensObsIn%numObs))
428 ensObsOut%randPert_r4(:,:) = ensObsIn%randPert_r4(:,:)
429 end if
430 ensObsOut%meanYb(:) = ensObsIn%meanYb(:)
431 ensObsOut%deterYb(:) = ensObsIn%deterYb(:)
432 ensObsOut%obsValue(:) = ensObsIn%obsValue(:)
433 ensObsOut%assFlag(:) = ensObsIn%assFlag(:)
434 ensObsOut%typeVertCoord = ensObsIn%typeVertCoord
435
436 end subroutine eob_copy
437
438 !--------------------------------------------------------------------------
439 ! eob_allGather
440 !--------------------------------------------------------------------------
441 subroutine eob_allGather(ensObs,ensObs_mpiglobal)
442 !
443 !:Purpose: Collect obs information distributed over all mpi tasks and
444 ! make it available on all mpi tasks. The output ensObs object
445 ! will be allocated within this subroutine.
446 !
447 implicit none
448
449 ! Arguments:
450 type(struct_eob), intent(inout) :: ensObs
451 type(struct_eob), intent(out) :: ensObs_mpiglobal
452
453 ! Locals:
454 type(struct_eob) :: ensObsClean
455 integer :: ierr, procIndex, memberIndex, numObs_mpiglobal
456 integer :: allNumObs(mmpi_nprocs), displs(mmpi_nprocs)
457
458 write(*,*) 'eob_allGather: starting'
459 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
460
461 call utl_tmg_start(10,'--Observations')
462 call utl_tmg_start(24,'----Eob_AllGather')
463
464 ! refresh assimilation flag and then clean ensObs before communicating and writing
465 call eob_setAssFlag(ensObs)
466 call eob_clean(ensObs,ensObsClean)
467
468 call rpn_comm_allgather(ensObsClean%numObs, 1, 'mpi_integer', &
469 allNumObs, 1, 'mpi_integer', &
470 'GRID', ierr)
471 numObs_mpiglobal = sum(allNumObs(:))
472
473 if (ensObs_mpiglobal%allocated) then
474 call utl_abort('eob_allGather: output ensObs object must not be already allocated')
475 end if
476 call eob_allocate(ensObs_mpiglobal, ensObsClean%numMembers, numObs_mpiglobal, ensObsClean%obsSpaceData, &
477 fileMemberIndex1_opt=ensObs%fileMemberIndex1)
478 if (allocated(ensObsClean%Ya_r4)) then
479 allocate(ensObs_mpiglobal%Ya_r4(ensObsClean%numMembers,numObs_mpiglobal))
480 end if
481 if (allocated(ensObsClean%randPert_r4)) then
482 allocate(ensObs_mpiglobal%randPert_r4(ensObsClean%numMembers,numObs_mpiglobal))
483 end if
484 ensObs_mpiglobal%typeVertCoord = ensObsClean%typeVertCoord
485
486 if (mmpi_myid == 0) then
487 displs(1) = 0
488 do procIndex = 2, mmpi_nprocs
489 displs(procIndex) = displs(procIndex-1) + allNumObs(procIndex-1)
490 end do
491 else
492 displs(:) = 0
493 end if
494
495 call rpn_comm_gatherv(ensObsClean%lat, ensObsClean%numObs, 'mpi_real8', &
496 ensObs_mpiglobal%lat, allNumObs, displs, 'mpi_real8', &
497 0, 'GRID', ierr)
498 call rpn_comm_gatherv(ensObsClean%lon, ensObsClean%numObs, 'mpi_real8', &
499 ensObs_mpiglobal%lon, allNumObs, displs, 'mpi_real8', &
500 0, 'GRID', ierr)
501 call rpn_comm_gatherv(ensObsClean%vertLocation, ensObsClean%numObs, 'mpi_real8', &
502 ensObs_mpiglobal%vertLocation, allNumObs, displs, 'mpi_real8', &
503 0, 'GRID', ierr)
504 call rpn_comm_gatherv(ensObsClean%obsValue, ensObsClean%numObs, 'mpi_real8', &
505 ensObs_mpiglobal%obsValue, allNumObs, displs, 'mpi_real8', &
506 0, 'GRID', ierr)
507 call rpn_comm_gatherv(ensObsClean%obsErrInv, ensObsClean%numObs, 'mpi_real8', &
508 ensObs_mpiglobal%obsErrInv, allNumObs, displs, 'mpi_real8', &
509 0, 'GRID', ierr)
510 call rpn_comm_gatherv(ensObsClean%meanYb, ensObsClean%numObs, 'mpi_real8', &
511 ensObs_mpiglobal%meanYb, allNumObs, displs, 'mpi_real8', &
512 0, 'GRID', ierr)
513 call rpn_comm_gatherv(ensObsClean%deterYb, ensObsClean%numObs, 'mpi_real8', &
514 ensObs_mpiglobal%deterYb, allNumObs, displs, 'mpi_real8', &
515 0, 'GRID', ierr)
516 call rpn_comm_gatherv(ensObsClean%assFlag, ensObsClean%numObs, 'mpi_integer', &
517 ensObs_mpiglobal%assFlag, allNumObs, displs, 'mpi_integer', &
518 0, 'GRID', ierr)
519 if (allocated(ensObsClean%obsErrInv_sim)) then
520 call rpn_comm_gatherv(ensObsClean%obsErrInv_sim, ensObsClean%numObs, 'mpi_real8', &
521 ensObs_mpiglobal%obsErrInv_sim, allNumObs, displs, 'mpi_real8', &
522 0, 'GRID', ierr)
523 end if
524 do memberIndex = 1, ensObsClean%numMembers
525 call rpn_comm_gatherv(ensObsClean%Yb_r4(memberIndex,:), ensObsClean%numObs, 'mpi_real4', &
526 ensObs_mpiglobal%Yb_r4(memberIndex,:), allNumObs, displs, 'mpi_real4', &
527 0, 'GRID', ierr)
528 if (allocated(ensObsClean%Ya_r4)) then
529 call rpn_comm_gatherv(ensObsClean%Ya_r4(memberIndex,:), ensObsClean%numObs, 'mpi_real4', &
530 ensObs_mpiglobal%Ya_r4(memberIndex,:), allNumObs, displs, 'mpi_real4', &
531 0, 'GRID', ierr)
532 end if
533 if (allocated(ensObsClean%randPert_r4)) then
534 call rpn_comm_gatherv(ensObsClean%randPert_r4(memberIndex,:), ensObsClean%numObs, 'mpi_real4', &
535 ensObs_mpiglobal%randPert_r4(memberIndex,:), allNumObs, displs, 'mpi_real4', &
536 0, 'GRID', ierr)
537 end if
538 end do
539
540 call rpn_comm_bcast(ensObs_mpiglobal%lat, ensObs_mpiglobal%numObs, 'mpi_real8', &
541 0, 'GRID', ierr)
542 call rpn_comm_bcast(ensObs_mpiglobal%lon, ensObs_mpiglobal%numObs, 'mpi_real8', &
543 0, 'GRID', ierr)
544 call rpn_comm_bcast(ensObs_mpiglobal%vertLocation, ensObs_mpiglobal%numObs, 'mpi_real8', &
545 0, 'GRID', ierr)
546 call rpn_comm_bcast(ensObs_mpiglobal%obsValue, ensObs_mpiglobal%numObs, 'mpi_real8', &
547 0, 'GRID', ierr)
548 call rpn_comm_bcast(ensObs_mpiglobal%obsErrInv, ensObs_mpiglobal%numObs, 'mpi_real8', &
549 0, 'GRID', ierr)
550 if (allocated(ensObs_mpiglobal%obsErrInv_sim)) then
551 call rpn_comm_bcast(ensObs_mpiglobal%obsErrInv_sim, ensObs_mpiglobal%numObs, 'mpi_real8', &
552 0, 'GRID', ierr)
553 end if
554 call rpn_comm_bcast(ensObs_mpiglobal%meanYb, ensObs_mpiglobal%numObs, 'mpi_real8', &
555 0, 'GRID', ierr)
556 call rpn_comm_bcast(ensObs_mpiglobal%deterYb, ensObs_mpiglobal%numObs, 'mpi_real8', &
557 0, 'GRID', ierr)
558 call rpn_comm_bcast(ensObs_mpiglobal%assFlag, ensObs_mpiglobal%numObs, 'mpi_integer', &
559 0, 'GRID', ierr)
560 do memberIndex = 1, ensObsClean%numMembers
561 call rpn_comm_bcast(ensObs_mpiglobal%Yb_r4(memberIndex,:), ensObs_mpiglobal%numObs, 'mpi_real4', &
562 0, 'GRID', ierr)
563 if (allocated(ensObs_mpiglobal%Ya_r4)) then
564 call rpn_comm_bcast(ensObs_mpiglobal%Ya_r4(memberIndex,:), ensObs_mpiglobal%numObs, 'mpi_real4', &
565 0, 'GRID', ierr)
566 end if
567 if (allocated(ensObs_mpiglobal%randPert_r4)) then
568 call rpn_comm_bcast(ensObs_mpiglobal%randPert_r4(memberIndex,:), ensObs_mpiglobal%numObs, 'mpi_real4', &
569 0, 'GRID', ierr)
570 end if
571 end do
572
573 call eob_deallocate(ensObsClean)
574
575 write(*,*) 'eob_allGather: total number of obs to be assimilated =', sum(ensObs_mpiglobal%assFlag(:))
576
577 call utl_tmg_stop(24)
578 call utl_tmg_stop(10)
579
580 write(*,*) 'eob_allGather: finished'
581 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
582
583 end subroutine eob_allGather
584
585 !--------------------------------------------------------------------------
586 ! eob_writeToFiles
587 !--------------------------------------------------------------------------
588 subroutine eob_writeToFiles(ensObs, outputFilenamePrefix, writeObsInfo, &
589 numGroupsToDivideMembers_opt, &
590 maxNumMembersPerGroup_opt)
591 !
592 !:Purpose: Write the contents of an ensObs mpi local object to files
593 !
594 implicit none
595
596 ! Arguments:
597 type(struct_eob), intent(in) :: ensObs
598 character(len=*), intent(in) :: outputFilenamePrefix
599 logical, intent(in) :: writeObsInfo
600 integer, optional, intent(in) :: numGroupsToDivideMembers_opt
601 integer, optional, intent(in) :: maxNumMembersPerGroup_opt
602
603 ! Locals:
604 integer :: unitNum, ierr, obsIndex, memberIndex
605 integer :: obsVcoCode(ensObs%numObs), obsAssFlag(ensObs%numObs)
606 integer :: obsFlag(ensObs%numObs)
607 integer, allocatable :: memberIndexArray(:)
608 character(len=40) :: fileName
609 character(len=4) :: myidxStr, myidyStr
610 character(len=30) :: fileNameExtention
611 integer :: fnom, fclos
612 logical :: fileExists
613
614 if (.not. ensObs%allocated) then
615 call utl_abort('eob_writeToFiles: this object is not allocated')
616 end if
617
618 call obs_extractObsIntBodyColumn(obsVcoCode, ensObs%obsSpaceData, OBS_VCO)
619 call obs_extractObsIntBodyColumn(obsAssFlag, ensObs%obsSpaceData, OBS_ASS)
620 call obs_extractObsIntBodyColumn(obsFlag, ensObs%obsSpaceData, OBS_FLG)
621
622 write(myidxStr,'(I4.4)') (mmpi_myidx + 1)
623 write(myidyStr,'(I4.4)') (mmpi_myidy + 1)
624 fileNameExtention = trim(myidxStr) // '_' // trim(myidyStr)
625
626 ! write observation info to a file
627 if (writeObsInfo) then
628 fileName = 'eob_obsInfo_' // trim(fileNameExtention)
629 write(*,*) 'eob_writeToFiles: writing ',trim(filename)
630 inquire(file=trim(fileName),exist=fileExists)
631 if ( fileExists ) then
632 call utl_abort('eob_writeToFiles: file should not exist')
633 end if
634
635 unitNum = 0
636 ierr = fnom(unitNum, fileName, 'FTN+SEQ+UNF+R/W', 0)
637 write(unitNum) ensObs%numMembers, ensObs%numObs
638 write(unitNum) (ensObs%lat(obsIndex), obsIndex = 1, ensObs%numObs)
639 write(unitNum) (ensObs%lon(obsIndex), obsIndex = 1, ensObs%numObs)
640 write(unitNum) (obsVcoCode(obsIndex), obsIndex = 1, ensObs%numObs)
641 write(unitNum) (ensObs%obsValue(obsIndex), obsIndex = 1, ensObs%numObs)
642 write(unitNum) (obsAssFlag(obsIndex), obsIndex = 1, ensObs%numObs)
643 write(unitNum) (obsFlag(obsIndex), obsIndex = 1, ensObs%numObs)
644 ierr = fclos(unitNum)
645 end if
646
647 ! get memberIndex in the full ensemble set
648 allocate(memberIndexArray(ensObs%numMembers))
649 call getMemberIndexInFullEnsSet(ensObs, memberIndexArray, &
650 numGroupsToDivideMembers_opt=numGroupsToDivideMembers_opt, &
651 maxNumMembersPerGroup_opt=maxNumMembersPerGroup_opt)
652
653 ! Open file and write ensObs%Yb for all the members to one file
654 fileName = trim(outputFilenamePrefix) // '_' // trim(fileNameExtention)
655 write(*,*) 'eob_writeToFiles: writing ',trim(filename)
656 inquire(file=trim(fileName),exist=fileExists)
657 if (fileExists) then
658 call utl_abort('eob_writeToFiles: file should not exist')
659 end if
660
661 unitNum = 0
662 ierr = fnom(unitNum, fileName, 'FTN+SEQ+UNF+R/W', 0)
663 write(unitNum) ensObs%numMembers
664 write(unitNum) (memberIndexArray(memberIndex), memberIndex = 1, ensObs%numMembers)
665 do memberIndex = 1, ensObs%numMembers
666 if (mmpi_myid == 0) then
667 write(*,*) 'eob_writeToFiles: fileMemberIndex1=', ensObs%fileMemberIndex1, &
668 ', memberIndex=', memberIndex, &
669 ', memberIndex in full ensemble set=', memberIndexArray(memberIndex)
670 end if
671 write(unitNum) (ensObs%Yb_r4(memberIndex,obsIndex), obsIndex = 1, ensObs%numObs)
672 end do
673 ierr = fclos(unitNum)
674
675 deallocate(memberIndexArray)
676
677 end subroutine eob_writeToFiles
678
679 !--------------------------------------------------------------------------
680 ! eob_readFromFiles
681 !--------------------------------------------------------------------------
682 subroutine eob_readFromFiles(ensObs, numMembersToRead, inputFilenamePrefix, &
683 readObsInfo)
684 !
685 !:Purpose: Read mpi local ensObs%Yb object from file. Several files in separate subdirectories
686 ! can be read. Some examples of path+filename are:
687 ! ensObs_0001/eob_HX_0001_0001
688 ! ensObs_0002/eob_HX_0001_0001
689 !
690 implicit none
691
692 ! Arguments:
693 type(struct_eob), intent(inout) :: ensObs
694 integer , intent(in) :: numMembersToRead
695 character(len=*), intent(in) :: inputFilenamePrefix
696 logical, intent(in) :: readObsInfo
697
698 ! Locals:
699 real(8) :: latFromFile(ensObs%numObs), lonFromFile(ensObs%numObs)
700 real(8) :: obsValueFromFile(ensObs%numObs)
701 integer :: obsVcoCode(ensObs%numObs), obsVcoCodeFromFile(ensObs%numObs)
702 integer :: obsFlag(ensObs%numObs), assFlagFrom1File(ensObs%numObs)
703 integer :: assFlagFromAllFiles(ensObs%numObs)
704 integer :: unitNum, ierr, memberIndex, obsIndex, numObsFromFile
705 integer :: numMembersFromFile, fnom, fclos
706 integer :: fileIndex, numMembersAlreadyRead
707 integer, allocatable :: memberIndexFromFile(:)
708 logical :: fileExists
709 character(len=256) :: fileName
710 character(len=100) :: fileBaseName
711 character(len=4) :: myidxStr, myidyStr
712 character(len=3) :: fileIndexStr
713 character(len=30) :: fileNameExtention
714
715 if ( .not. ensObs%allocated ) then
716 call utl_abort('eob_readFromFiles: this object is not allocated')
717 end if
718
719 call obs_extractObsIntBodyColumn(obsVcoCode, ensObs%obsSpaceData, OBS_VCO)
720
721 write(myidxStr,'(I4.4)') (mmpi_myidx + 1)
722 write(myidyStr,'(I4.4)') (mmpi_myidy + 1)
723 fileNameExtention = trim(myidxStr) // '_' // trim(myidyStr)
724
725 if (readObsInfo) then
726 ! loop on all file from different directories, read obsInfo and check they match ensObs
727 fileBaseName = 'eob_obsInfo_' // trim(fileNameExtention)
728
729 fileIndex = 0
730 numMembersAlreadyRead = 0
731 do while (numMembersAlreadyRead < numMembersToRead)
732 fileIndex = fileIndex + 1
733 write(fileIndexStr,'(i3.3)') fileIndex
734 fileName = './ensObs_' // fileIndexStr // '/' // fileBaseName
735
736 write(*,*) 'eob_readFromFiles: reading ',trim(fileName)
737 inquire(file=trim(fileName),exist=fileExists)
738 if (.not. fileExists) then
739 write(*,*) 'fileName=', fileName
740 call utl_abort('eob_readFromFiles: file does not exist')
741 end if
742
743 unitNum = 0
744 ierr = fnom(unitNum,trim(fileName),'FTN+SEQ+UNF',0)
745 read(unitNum) numMembersFromFile, numObsFromFile
746 if (ensObs%numObs /= numObsFromFile) then
747 call utl_abort('eob_readFromFiles: ensObs%numObs does not match with that of file')
748 end if
749
750 read(unitNum) (latFromFile(obsIndex), obsIndex = 1, ensObs%numObs)
751 read(unitNum) (lonFromFile(obsIndex), obsIndex = 1, ensObs%numObs)
752 read(unitNum) (obsVcoCodeFromFile(obsIndex), obsIndex = 1, ensObs%numObs)
753 read(unitNum) (obsValueFromFile(obsIndex), obsIndex = 1, ensObs%numObs)
754
755 if (maxval(abs(latFromFile(:) - ensObs%lat(:))) > 1.0d-5 .or. &
756 maxval(abs(lonFromFile(:) - ensObs%lon(:))) > 1.0d-5 .or. &
757 maxval(abs(obsValueFromFile(:) - ensObs%obsValue(:))) > 1.0d-7 .or. &
758 .not. all(obsVcoCodeFromFile(:) == obsVcoCode(:))) then
759
760 call utl_abort('eob_readFromFiles: obsInfo file do not match ensObs')
761 end if
762
763 ! Read assimilation flag for of all files and apply a "logical or" to get the value
764 ! to put in obsSpaceData. Read obs flag only on the first file.
765 read(unitNum) (assFlagFrom1File(obsIndex), obsIndex = 1, ensObs%numObs)
766 if (numMembersAlreadyRead == 0) then
767 read(unitNum) (obsFlag(obsIndex), obsIndex = 1, ensObs%numObs)
768 end if
769
770 if (numMembersAlreadyRead == 0) assFlagFromAllFiles(:) = assFlagFrom1File(:)
771 where (assFlagFrom1File(:) == obs_notAssimilated .and. numMembersAlreadyRead > 0)
772 assFlagFromAllFiles(:) = obs_notAssimilated
773 end where
774
775 ierr = fclos(unitNum)
776
777 numMembersAlreadyRead = numMembersAlreadyRead + numMembersFromFile
778 end do
779
780 ! update assimilation flag in obsSpaceData
781 do obsIndex = 1, ensObs%numObs
782 ! skip this obs it is already set to be assimilated
783 if (assFlagFromAllFiles(obsIndex) == obs_assimilated) cycle
784
785 call obs_bodySet_i(ensObs%obsSpaceData, OBS_ASS, obsIndex, obs_notAssimilated)
786 call obs_bodySet_i(ensObs%obsSpaceData, OBS_FLG, obsIndex, obsFlag(obsIndex))
787 end do
788 end if
789
790 ! loop on all files from different directories to read ensObs%Yb for all members
791 fileBaseName = trim(inputFilenamePrefix) // '_' // trim(fileNameExtention)
792
793 fileIndex = 0
794 numMembersAlreadyRead = 0
795 do while (numMembersAlreadyRead < numMembersToRead)
796 fileIndex = fileIndex + 1
797 write(fileIndexStr,'(i3.3)') fileIndex
798 fileName = './ensObs_' // fileIndexStr // '/' // fileBaseName
799
800 write(*,*) 'eob_readFromFiles: reading ',trim(fileName)
801 inquire(file=trim(fileName),exist=fileExists)
802 if (.not. fileExists) then
803 write(*,*) 'fileName=', fileName
804 call utl_abort('eob_readFromFiles: file does not exist')
805 end if
806
807 unitNum = 0
808 ierr = fnom(unitNum,trim(fileName),'FTN+SEQ+UNF',0)
809 read(unitNum) numMembersFromFile
810 allocate(memberIndexFromFile(numMembersFromFile))
811 read(unitNum) (memberIndexFromFile(memberIndex), memberIndex = 1, numMembersFromFile)
812 do memberIndex = 1, numMembersFromFile
813 read(unitNum) (ensObs%Yb_r4(memberIndexFromFile(memberIndex),obsIndex), obsIndex = 1, ensObs%numObs)
814 end do
815 ierr = fclos(unitNum)
816
817 deallocate(memberIndexFromFile)
818
819 numMembersAlreadyRead = numMembersAlreadyRead + numMembersFromFile
820 end do
821
822 end subroutine eob_readFromFiles
823
824 !--------------------------------------------------------------------------
825 ! eob_getLocalBodyIndices
826 !--------------------------------------------------------------------------
827 function eob_getLocalBodyIndices(ensObs,localBodyIndices,distances,lat,lon,vertLocation, &
828 hLocalize,vLocalize,numLocalObsFound) result(numLocalObs)
829 !
830 !:Purpose: Return a list of values of bodyIndex for all observations within
831 ! the local volume around the specified lat/lon used for assimilation
832 ! (as defined by h/vLocalize). The kdtree2 module is used to efficiently
833 ! perform this task. The kdtree itself is constructed on the first call.
834 !
835 implicit none
836
837 ! Arguments:
838 type(struct_eob), intent(in) :: ensObs
839 integer , intent(out) :: localBodyIndices(:)
840 real(8) , intent(out) :: distances(:)
841 real(8) , intent(in) :: lat, lon, vertLocation, hLocalize, vLocalize
842 integer , intent(out) :: numLocalObsFound
843 ! Result:
844 integer :: numLocalObs ! function output
845
846 ! Locals:
847 integer :: bodyIndex, numLocalObsFoundSearch, maxNumLocalObs, localObsIndex
848 real(8) :: distance
849 real(kdkind), allocatable :: positionArray(:,:)
850 type(kdtree2_result), allocatable :: searchResults(:)
851 real(kdkind) :: maxRadius
852 real(kdkind) :: refPosition(3)
853
854 ! create the kdtree on the first call
855 if (.not. associated(tree)) then
856 write(*,*) 'eob_getLocalBodyIndices: start creating kdtree'
857 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
858 allocate(positionArray(3,ensObs%numObs))
859 do bodyIndex = 1, ensObs%numObs
860 positionArray(1,bodyIndex) = ec_ra * sin(ensObs%lon(bodyIndex)) * cos(ensObs%lat(bodyIndex))
861 positionArray(2,bodyIndex) = ec_ra * cos(ensObs%lon(bodyIndex)) * cos(ensObs%lat(bodyIndex))
862 positionArray(3,bodyIndex) = ec_ra * sin(ensObs%lat(bodyIndex))
863 end do
864 tree => kdtree2_create(positionArray, sort=.true., rearrange=.true.)
865 write(*,*) 'eob_getLocalBodyIndices: done creating kdtree'
866 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
867 end if
868
869 ! do the search
870 maxNumLocalObs = size(localBodyIndices)
871 maxRadius = hLocalize**2
872 refPosition(1) = ec_ra * sin(lon) * cos(lat)
873 refPosition(2) = ec_ra * cos(lon) * cos(lat)
874 refPosition(3) = ec_ra * sin(lat)
875 allocate(searchResults(maxNumLocalObsSearch))
876 call kdtree2_r_nearest(tp=tree, qv=refPosition, r2=maxRadius, nfound=numLocalObsFoundSearch,&
877 nalloc=maxNumLocalObsSearch, results=searchResults)
878 if (numLocalObsFoundSearch > maxNumLocalObsSearch) then
879 call utl_abort('eob_getLocalBodyIndices: the parameter maxNumLocalObsSearch must be increased')
880 end if
881
882 if ( vLocalize > 0.0d0 .and. vertLocation /= MPC_missingValue_R8 ) then
883 ! copy search results to output vectors, only those within vertical localization distance
884 numLocalObsFound = 0
885 numLocalObs = 0
886 do localObsIndex=1, numLocalObsFoundSearch
887 distance = abs( vertLocation - ensObs%vertLocation(searchResults(localObsIndex)%idx) )
888 if (distance <= vLocalize .and. ensObs%assFlag(searchResults(localObsIndex)%idx)==1) then
889 numLocalObsFound = numLocalObsFound + 1
890 if (numLocalObs < maxNumLocalObs) then
891 numLocalObs = numLocalObs + 1
892 localBodyIndices(numLocalObs) = searchResults(localObsIndex)%idx
893 distances(numLocalObs) = sqrt(searchResults(localObsIndex)%dis)
894 end if
895 end if
896 end do
897 else
898 ! no vertical location, so just copy results
899 numLocalObsFound = 0
900 numLocalObs = 0
901 do localObsIndex=1, numLocalObsFoundSearch
902 if (ensObs%assFlag(searchResults(localObsIndex)%idx)==1) then
903 numLocalObsFound = numLocalObsFound + 1
904 if (numLocalObs < maxNumLocalObs) then
905 numLocalObs = numLocalObs + 1
906 localBodyIndices(numLocalObs) = searchResults(localObsIndex)%idx
907 distances(numLocalObs) = sqrt(searchResults(localObsIndex)%dis)
908 end if
909 end if
910 end do
911 end if
912 deallocate(searchResults)
913
914 end function eob_getLocalBodyIndices
915
916 !--------------------------------------------------------------------------
917 ! eob_setLatLonObs
918 !--------------------------------------------------------------------------
919 subroutine eob_setLatLonObs(ensObs)
920 implicit none
921
922 ! Arguments:
923 type(struct_eob), intent(inout) :: ensObs
924
925 call obs_extractObsRealHeaderColumn(ensObs%lat, ensObs%obsSpaceData, OBS_LAT)
926 call obs_extractObsRealHeaderColumn(ensObs%lon, ensObs%obsSpaceData, OBS_LON)
927 call obs_extractObsRealBodyColumn(ensObs%obsValue, ensObs%obsSpaceData, OBS_VAR)
928
929 end subroutine eob_setLatLonObs
930
931 !--------------------------------------------------------------------------
932 ! eob_setobsErrInv
933 !--------------------------------------------------------------------------
934 subroutine eob_setobsErrInv(ensObs)
935 implicit none
936
937 ! Arguments:
938 type(struct_eob), intent(inout) :: ensObs
939
940 ! Locals:
941 integer :: obsIndex
942
943 call obs_extractObsRealBodyColumn(ensObs%obsErrInv, ensObs%obsSpaceData, OBS_OER)
944 do obsIndex = 1, ensObs%numObs
945 if(ensObs%obsErrInv(obsIndex) > 0.0d0) then
946 ensObs%obsErrInv(obsIndex) = 1.0d0/(ensObs%obsErrInv(obsIndex)**2)
947 else
948 ensObs%obsErrInv(obsIndex) = 0.0d0
949 end if
950 end do
951
952 ! read namelist if necessary and calculate obs error inverse for
953 ! passive and simulated observations
954 call eob_init()
955 if (psvObsAssim) call eob_setPsvObsErrInv(ensObs)
956 if (eob_simObsAssim) call eob_setSimObsErrInv(ensObs)
957
958 end subroutine eob_setobsErrInv
959
960 !--------------------------------------------------------------------------
961 ! eob_setPsvObsErrInv
962 !--------------------------------------------------------------------------
963 subroutine eob_setPsvObsErrInv(ensObs)
964 !
965 !:Purpose: Updates the inverse of the observation error variance
966 ! for passive osbervations and stores this in ensObs%obsErrInv.
967 ! This is done assuming that ensObs%obsErrInv was already set.
968 !
969 implicit none
970
971 ! Arguments:
972 type(struct_eob), intent(inout) :: ensObs
973
974 ! Locals:
975 integer :: obsIndex, headerIndex
976 integer :: codtyp, varnum, obsfamIndex
977 character(2) :: obsfamCurrent
978 logical :: psvFlag
979
980 do obsIndex = 1, ensObs%numObs
981 psvFlag = .false.
982 headerIndex = obs_bodyElem_i(ensObs%obsSpaceData, OBS_HIND, obsIndex)
983 obsfamCurrent = obs_getFamily(ensObs%obsSpaceData, headerIndex_opt=headerIndex)
984 ! update obs error inverse to 0 if current observation is passive
985 if (ANY(psvObsFamily == obsfamCurrent)) then
986 obsfamIndex = utl_findloc(psvObsFamily(:), obsfamCurrent)
987 if ((numPsvCodTyp(obsfamIndex) > 0) .and. (numPsvVarNum(obsfamIndex) > 0)) then
988 ! at least 1 codtyp AND varnum specified for current obs family so
989 ! see if current observation matches any of those codtypes AND
990 ! any of those varnums
991 codtyp = obs_headElem_i(ensObs%obsSpaceData, OBS_ITY, headerIndex)
992 varnum = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, obsIndex)
993 if (ANY(psvCodTyp(obsfamIndex,:) == codtyp) .and. ANY(psvVarNum(obsfamIndex,:) == varnum)) then
994 ensObs%obsErrInv(obsIndex) = 0.0d0
995 psvFlag = .true.
996 end if
997 else if (numPsvCodTyp(obsfamIndex) > 0) then
998 ! at least 1 codtype is specified for current obs family so
999 ! see if current observation matches any of those codtypes
1000 codtyp = obs_headElem_i(ensObs%obsSpaceData, OBS_ITY, headerIndex)
1001 if (ANY(psvCodTyp(obsfamIndex,:) == codtyp)) then
1002 ensObs%obsErrInv(obsIndex) = 0.0d0
1003 psvFlag = .true.
1004 end if
1005 else if (numPsvVarNum(obsfamIndex) > 0) then
1006 ! at least 1 varnum is specified for current obs family so
1007 ! see if current observation matches any of those varnums
1008 varnum = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, obsIndex)
1009 if (ANY(psvVarNum(obsfamIndex,:) == varnum)) then
1010 ensObs%obsErrInv(obsIndex) = 0.0d0
1011 psvFlag = .true.
1012 end if
1013 else
1014 ! passive observation family doesn't include any codtype or
1015 ! any varnum, so set error inverse to 0 irrespective of current
1016 ! observation codtype and varnum
1017 ensObs%obsErrInv(obsIndex) = 0.0d0
1018 psvFlag = .true.
1019 end if
1020 ! set OBS_FLG to indicate passive observation
1021 if (psvFlag) call obs_bodySet_i(ensObs%obsSpaceData,OBS_FLG,obsIndex, &
1022 ibset(obs_bodyElem_i(ensObs%obsSpaceData,OBS_FLG,obsIndex),25))
1023 end if
1024 end do
1025
1026 end subroutine eob_setPsvObsErrInv
1027
1028 !--------------------------------------------------------------------------
1029 ! eob_setSimObsErrInv
1030 !--------------------------------------------------------------------------
1031 subroutine eob_setSimObsErrInv(ensObs)
1032 !
1033 !:Purpose: Computes the inverse of the observation error variance if
1034 ! simulating any observations. Stores this in
1035 ! ensObs%obsErrInv_sim.
1036 !
1037 implicit none
1038
1039 ! Arguments:
1040 type(struct_eob), intent(inout) :: ensObs
1041
1042 ! Locals:
1043 integer :: obsIndex, headerIndex
1044 integer :: codtyp, varnum, obsfamIndex
1045 character(2) :: obsfamCurrent
1046 logical :: simFlag
1047
1048 write(*,*) 'eob_setSimObsErrInv: starting'
1049
1050 ! set to copy of regular obs error inverse
1051 ensObs%obsErrInv_sim(:) = ensObs%obsErrInv(:)
1052
1053 ! loop through all observations, and update the obs error inverse
1054 ! to a value of 0 for simulated observations
1055 do obsIndex = 1, ensObs%numObs
1056 simFlag = .false.
1057 headerIndex = obs_bodyElem_i(ensObs%obsSpaceData, OBS_HIND, obsIndex)
1058 obsfamCurrent = obs_getFamily(ensObs%obsSpaceData, headerIndex_opt=headerIndex)
1059 ! update obs error inverse for mean update to 0 if current observation is simulated
1060 if (ANY(simObsFamily == obsfamCurrent)) then
1061 obsfamIndex = utl_findloc(simObsFamily(:), obsfamCurrent)
1062 if ((numSimCodTyp(obsfamIndex) > 0) .and. (numSimVarNum(obsfamIndex) > 0)) then
1063 ! at least 1 codtyp AND varnum specified for current obs family so
1064 ! see if current observation matches any of those codtypes AND
1065 ! any of those varnums
1066 codtyp = obs_headElem_i(ensObs%obsSpaceData, OBS_ITY, headerIndex)
1067 varnum = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, obsIndex)
1068 if (ANY(simCodTyp(obsfamIndex,:) == codtyp) .and. ANY(simVarNum(obsfamIndex,:) == varnum)) then
1069 ensObs%obsErrInv_sim(obsIndex) = 0.0d0
1070 simFlag = .true.
1071 end if
1072 else if (numSimCodTyp(obsfamIndex) > 0) then
1073 ! at least 1 codtype is specified for current obs family so
1074 ! see if current observation matches any of those codtypes
1075 codtyp = obs_headElem_i(ensObs%obsSpaceData, OBS_ITY, headerIndex)
1076 if (ANY(simCodTyp(obsfamIndex,:) == codtyp)) then
1077 ensObs%obsErrInv_sim(obsIndex) = 0.0d0
1078 simFlag = .true.
1079 end if
1080 else if (numSimVarNum(obsfamIndex) > 0) then
1081 ! at least 1 varnum is specified for current obs family so
1082 ! see if current observation matches any of those varnums
1083 varnum = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, obsIndex)
1084 if (ANY(simVarNum(obsfamIndex,:) == varnum)) then
1085 ensObs%obsErrInv(obsIndex) = 0.0d0
1086 simFlag = .true.
1087 end if
1088 else
1089 ! simulated observation family doesn't include any codtype or
1090 ! any varnum, so set error inverse to 0 irrespective of current
1091 ! observation codtype and varnum
1092 ensObs%obsErrInv_sim(obsIndex) = 0.0d0
1093 simFlag = .true.
1094 end if
1095 ! set OBS_FLG to indicate simulated observation
1096 if (simFlag) call obs_bodySet_i(ensObs%obsSpaceData,OBS_FLG,obsIndex, &
1097 ibset(obs_bodyElem_i(ensObs%obsSpaceData,OBS_FLG,obsIndex),24))
1098 end if
1099 end do
1100
1101 end subroutine eob_setSimObsErrInv
1102
1103 !--------------------------------------------------------------------------
1104 ! eob_setVertLocation
1105 !--------------------------------------------------------------------------
1106 subroutine eob_setVertLocation(ensObs, columnMeanTrl)
1107 !
1108 !:Purpose: Set the vertical location value for each observation that
1109 ! will be used when doing vertical localization. For
1110 ! radiance observations, the level of the maximum value
1111 ! of the derivative of transmission is used. This value
1112 ! is also written in obsSpaceData in the OBS_ZHA column.
1113 !
1114 implicit none
1115
1116 ! Arguments:
1117 type(struct_eob) , intent(inout) :: ensObs
1118 type(struct_columnData), intent(in) :: columnMeanTrl
1119
1120 ! Locals:
1121 integer :: obsIndex, headerIndex, channelIndex, tovsIndex, numTovsLevels, nosensor
1122 integer :: levIndex, levIndexBelow, levIndexAbove, nLev_M
1123 integer :: varNumber(ensObs%numObs), obsVcoCode(ensObs%numObs), codType(ensObs%numObs)
1124 real(8) :: obsHeight, interpFactor, obsPPP(ensObs%numObs)
1125 real(8), pointer :: sfcPres_ptr(:,:), presM_ptr(:,:), heightM_ptr(:,:)
1126 type(rttov_profile), pointer :: profiles(:)
1127 logical :: verbose = .false.
1128
1129 call eob_setAssFlag(ensObs)
1130
1131 call obs_extractObsRealBodyColumn(obsPPP, ensObs%obsSpaceData, OBS_PPP)
1132 call obs_extractObsIntBodyColumn(varNumber, ensObs%obsSpaceData, OBS_VNM)
1133 call obs_extractObsIntBodyColumn(obsVcoCode, ensObs%obsSpaceData, OBS_VCO)
1134 call obs_extractObsIntHeaderColumn(codType, ensObs%obsSpaceData, OBS_ITY)
1135
1136 if (ensObs%typeVertCoord == 'logPressure') then
1137
1138 presM_ptr => col_getAllColumns(columnMeanTrl,'P_M')
1139 heightM_ptr => col_getAllColumns(columnMeanTrl,'Z_M')
1140 sfcPres_ptr => col_getAllColumns(columnMeanTrl,'P0')
1141 nLev_M = col_getNumLev(columnMeanTrl,'MM')
1142
1143 call tvs_getProfile(profiles,'nl')
1144
1145 end if
1146
1147 OBS_LOOP: do obsIndex = 1, ensObs%numObs
1148 headerIndex = obs_bodyElem_i(ensObs%obsSpaceData,OBS_HIND,obsIndex)
1149
1150 if( varNumber(obsIndex) == BUFR_NETS .or. varNumber(obsIndex) == BUFR_NEPS .or. &
1151 varNumber(obsIndex) == BUFR_NEUS .or. varNumber(obsIndex) == BUFR_NEVS .or. &
1152 varNumber(obsIndex) == BUFR_NESS .or. varNumber(obsIndex) == BUFR_NEPN .or. &
1153 varNumber(obsIndex) == BUFR_VIS .or. varNumber(obsIndex) == BUFR_LOGVIS .or. &
1154 varNumber(obsIndex) == BUFR_GUST .or. &
1155 varNumber(obsIndex) == BUFR_radarPrecip .or. varNumber(obsIndex) == BUFR_logRadarPrecip ) then
1156
1157 ! all surface observations
1158 if (ensObs%typeVertCoord == 'logPressure') then
1159 ensObs%vertLocation(obsIndex) = log(sfcPres_ptr(1,headerIndex))
1160 else if (ensObs%typeVertCoord == 'depth') then
1161 ensObs%vertLocation(obsIndex) = 0.0D0
1162 else
1163 call utl_abort('eob_setVertLocation: unknown typeVertCoord:' // trim(ensObs%typeVertCoord))
1164 end if
1165
1166 else if (varNumber(obsIndex) == BUFR_NEZD) then
1167
1168 ! ZTD observation, try 0.7*Psfc (i.e. ~700hPa when Psfc=1000hPa)
1169 if (ensObs%typeVertCoord == 'logPressure') then
1170 ensObs%vertLocation(obsIndex) = log(0.7D0 * sfcPres_ptr(1,headerIndex))
1171 else
1172 call utl_abort('eob_setVertLocation: ZTD obs only compatible with logPressure coordinate')
1173 end if
1174
1175 else if (obsPPP(obsIndex) > 0.0d0 .and. obsVcoCode(obsIndex)==2) then
1176
1177 ! all pressure level observations
1178 if (ensObs%typeVertCoord == 'logPressure') then
1179 ensObs%vertLocation(obsIndex) = log(obsPPP(obsIndex))
1180 else
1181 call utl_abort('eob_setVertLocation: pressure obs only compatible with logPressure coordinate')
1182 end if
1183
1184 else if(obsVcoCode(obsIndex) == 1 .and. .not.bufr_isOceanObs(varNumber(obsIndex))) then
1185
1186 if (ensObs%typeVertCoord /= 'logPressure') then
1187 ! skip this obs if it will not be assimilated
1188 if (ensObs%assFlag(obsIndex) == 0) cycle OBS_LOOP
1189 ! otherwise, abort
1190 write(*,*) 'eob_setVertLocation: varNum = ', varNumber(obsIndex)
1191 call utl_abort('eob_setVertLocation: height level obs only compatible with logPressure coordinate')
1192 end if
1193
1194 ! all height level observations (not including surface obs)
1195 obsHeight = obsPPP(obsIndex)
1196
1197 ! find level just below the observation
1198 levIndexBelow = 0
1199 LEV_LOOP: do levIndex = 1, nLev_M
1200 if (obsHeight > heightM_ptr(levIndex,headerIndex)) then
1201 levIndexBelow = levIndex
1202 exit LEV_LOOP
1203 end if
1204 end do LEV_LOOP
1205
1206 ! set the log pressure for observation
1207 if (levIndexBelow == 1) then
1208 ! above top level, use top level pressure
1209 ensObs%vertLocation(obsIndex) = log(presM_ptr(1,headerIndex))
1210 else if (levIndexBelow == 0) then
1211 ! below bottom level, use surface pressure
1212 ensObs%vertLocation(obsIndex) = log(sfcPres_ptr(1,headerIndex))
1213 else
1214 ! interpolate
1215 levIndexAbove = levIndexBelow - 1
1216 interpFactor = ( obsHeight - heightM_ptr(levIndexBelow,headerIndex) ) / &
1217 ( heightM_ptr(levIndexAbove,headerIndex) - heightM_ptr(levIndexBelow,headerIndex) )
1218 ensObs%vertLocation(obsIndex) = interpFactor * log(presM_ptr(levIndexAbove,headerIndex)) + &
1219 (1.0D0 - interpFactor) * log(presM_ptr(levIndexBelow,headerIndex))
1220 end if
1221
1222 else if(tvs_isIdBurpTovs(codType(obsIndex))) then
1223
1224 if (ensObs%typeVertCoord /= 'logPressure') then
1225 call utl_abort('eob_setVertLocation: radiance obs only compatible with logPressure coordinate')
1226 end if
1227
1228 tovsIndex = tvs_tovsIndex(headerIndex)
1229 nosensor = tvs_lsensor(tovsIndex)
1230 numTovsLevels = size(tvs_transmission(tovsIndex)%tau_levels,1)
1231 channelIndex = nint(obsPPP(obsIndex))
1232 channelIndex = max(0,min(channelIndex,tvs_maxChannelNumber+1))
1233 channelIndex = channelIndex - tvs_channelOffset(nosensor)
1234 channelIndex = utl_findloc(tvs_ichan(:,nosensor), channelIndex)
1235 if (channelIndex > 0 .and. ensObs%assFlag(obsIndex)==1) then
1236 call max_transmission(tvs_transmission(tovsIndex), numTovsLevels, &
1237 channelIndex, profiles(tovsIndex)%p, ensObs%vertLocation(obsIndex))
1238 if(mmpi_myid == 0 .and. verbose) then
1239 write(*,*) 'eob_setVertLocation for tovs: ', codType(obsIndex), &
1240 obsPPP(obsIndex), 0.01*exp(ensObs%vertLocation(obsIndex))
1241 end if
1242 else
1243 ensObs%vertLocation(obsIndex) = log(500.0D2)
1244 end if
1245
1246 else if (varNumber(obsIndex) == BUFR_SST) then
1247
1248 if (ensObs%typeVertCoord /= 'depth') then
1249 call utl_abort('eob_setVertLocation: SST obs only compatible with ocean depth coordinate')
1250 end if
1251
1252 ! SST observations
1253 ensObs%vertLocation(obsIndex) = minval(columnMeanTrl%vco%depths(:))
1254
1255 else if(ensObs%assFlag(obsIndex)==1) then
1256
1257 write(*,*) 'eob_setLatLonPresObs: ERROR! cannot compute pressure for this observation: ', &
1258 obsPPP(obsIndex), varNumber(obsIndex), obsVcoCode(obsIndex)
1259 call utl_abort('eob_setVertLocation')
1260
1261 end if
1262
1263 ! write the value into obsSpaceData for later diagnostics
1264 if (ensObs%assFlag(obsIndex)==1) then
1265 call obs_bodySet_r(ensObs%obsSpaceData, OBS_ZHA, obsIndex, &
1266 ensObs%vertLocation(obsIndex))
1267 end if
1268
1269 end do OBS_LOOP
1270
1271 nullify(profiles)
1272
1273 end subroutine eob_setVertLocation
1274
1275 !--------------------------------------------------------------------------
1276 ! eob_setAssFlag
1277 !--------------------------------------------------------------------------
1278 subroutine eob_setAssFlag(ensObs)
1279 implicit none
1280
1281 ! Arguments:
1282 type(struct_eob), intent(inout) :: ensObs
1283
1284 call obs_extractObsIntBodyColumn(ensObs%assFlag, ensObs%obsSpaceData, OBS_ASS)
1285
1286 end subroutine eob_setAssFlag
1287
1288 !--------------------------------------------------------------------------
1289 ! eob_setYb
1290 !--------------------------------------------------------------------------
1291 subroutine eob_setYb(ensObs, memberIndex)
1292 implicit none
1293
1294 ! Arguments:
1295 type(struct_eob), intent(inout) :: ensObs
1296 integer , intent(in) :: memberIndex
1297
1298 ! get the Y-HX value from obsSpaceData
1299 call obs_extractObsRealBodyColumn_r4(ensObs%Yb_r4(memberIndex,:), ensObs%obsSpaceData, OBS_OMP)
1300
1301 ! now compute HX = Y - (Y-HX)
1302 ensObs%Yb_r4(memberIndex,:) = ensObs%obsValue(:) - ensObs%Yb_r4(memberIndex,:)
1303
1304 end subroutine eob_setYb
1305
1306 !--------------------------------------------------------------------------
1307 ! eob_setYa (like eob_setYb but for the analysis)
1308 !--------------------------------------------------------------------------
1309 subroutine eob_setYa(ensObs, memberIndex, obsColumnName)
1310 implicit none
1311
1312 ! Arguments:
1313 type(struct_eob), intent(inout) :: ensObs
1314 integer , intent(in) :: memberIndex
1315 integer , intent(in) :: obsColumnName
1316
1317 if ( .not. allocated(ensObs%Ya_r4) ) then
1318 call utl_abort('eob_setYa: ensObs%Ya_r4 must be allocated and it is not')
1319 end if
1320
1321 ! get the Y-HX value from obsSpaceData
1322 call obs_extractObsRealBodyColumn_r4(ensObs%Ya_r4(memberIndex,:), ensObs%obsSpaceData, obsColumnName)
1323
1324 ! now compute HX = Y - (Y-HX)
1325 ensObs%Ya_r4(memberIndex,:) = ensObs%obsValue(:) - ensObs%Ya_r4(memberIndex,:)
1326
1327 end subroutine eob_setYa
1328
1329 !--------------------------------------------------------------------------
1330 ! eob_setSimObsVal
1331 !--------------------------------------------------------------------------
1332 subroutine eob_setSimObsVal(ensObs)
1333 !
1334 !:Purpose: Set the observed value for simulated observations to
1335 ! the background ensemble mean in observation space.
1336 !
1337 implicit none
1338
1339 ! Arguments:
1340 type(struct_eob) , intent(inout) :: ensObs
1341
1342 ! Locals:
1343 integer :: obsIndex, headerIndex
1344 integer :: codtyp, varnum, obsfamIndex
1345 character(2) :: obsfamCurrent
1346
1347 if (.not. eob_simObsAssim) return
1348 ! Loop through observations and set y to mean(H(x)) if y is in obs family of interest
1349 do obsIndex = 1, ensObs%numObs
1350 headerIndex = obs_bodyElem_i(ensObs%obsSpaceData, OBS_HIND, obsIndex)
1351 obsfamCurrent = obs_getFamily(ensObs%obsSpaceData, headerIndex_opt=headerIndex)
1352 if (ANY(simObsFamily == obsfamCurrent)) then
1353 obsfamIndex = utl_findloc(simObsFamily(:), obsfamCurrent)
1354 if ((numSimCodTyp(obsfamIndex) > 0) .and. (numSimVarNum(obsfamIndex) > 0)) then
1355 ! at least 1 codtyp AND varnum specified for current obs family so
1356 ! see if current observation matches any of those codtypes AND
1357 ! any of those varnums
1358 codtyp = obs_headElem_i(ensObs%obsSpaceData, OBS_ITY, headerIndex)
1359 varnum = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, obsIndex)
1360 if (ANY(simCodTyp(obsfamIndex,:) == codtyp) .and. ANY(simVarNum(obsfamIndex,:) == varnum)) then
1361 ensObs%obsvalue(obsIndex) = ensObs%meanYb(obsIndex)
1362 end if
1363 else if (numSimCodTyp(obsfamIndex) > 0) then
1364 ! at least 1 codtype is specified for current obs family so
1365 ! see if current observation matches any of those codtypes
1366 codtyp = obs_headElem_i(ensObs%obsSpaceData, OBS_ITY, headerIndex)
1367 if (ANY(simCodTyp(obsfamIndex,:) == codtyp)) then
1368 ensObs%obsvalue(obsIndex) = ensObs%meanYb(obsIndex)
1369 end if
1370 else if (numSimVarNum(obsfamIndex) > 0) then
1371 ! at least 1 varnum is specified for current obs family so
1372 ! see if current observation matches any of those varnums
1373 varnum = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, obsIndex)
1374 if (ANY(simVarNum(obsfamIndex,:) == varnum)) then
1375 ensObs%obsvalue(obsIndex) = ensObs%meanYb(obsIndex)
1376 end if
1377 else
1378 ! simulated observation family doesn't include any codtype
1379 ! so set irrespective of current observation's codtype
1380 ensObs%obsvalue(obsIndex) = ensObs%meanYb(obsIndex)
1381 end if
1382 end if
1383 end do
1384
1385 end subroutine eob_setSimObsVal
1386
1387 !--------------------------------------------------------------------------
1388 ! eob_setDeterYb
1389 !--------------------------------------------------------------------------
1390 subroutine eob_setDeterYb(ensObs)
1391 implicit none
1392
1393 ! Arguments:
1394 type(struct_eob), intent(inout) :: ensObs
1395
1396 ! get the Y-HX value from obsSpaceData
1397 call obs_extractObsRealBodyColumn(ensObs%DeterYb(:), ensObs%obsSpaceData, OBS_OMP)
1398
1399 ! now compute HX = Y - (Y-HX)
1400 ensObs%DeterYb(:) = ensObs%obsValue(:) - ensObs%DeterYb(:)
1401
1402 end subroutine eob_setDeterYb
1403
1404 !--------------------------------------------------------------------------
1405 ! eob_calcAndRemoveMeanYb
1406 !--------------------------------------------------------------------------
1407 subroutine eob_calcAndRemoveMeanYb(ensObs)
1408 implicit none
1409
1410 ! Arguments:
1411 type(struct_eob), intent(inout) :: ensObs
1412
1413 ! Locals:
1414 integer :: obsIndex
1415
1416 do obsIndex = 1, ensObs%numObs
1417 ensObs%meanYb(obsIndex) = sum(ensObs%Yb_r4(:,obsIndex)) / ensObs%numMembers
1418 ensObs%Yb_r4(:,obsIndex) = ensObs%Yb_r4(:,obsIndex) - ensObs%meanYb(obsIndex)
1419 end do
1420
1421 ensObs%meanRemoved = .true.
1422
1423 end subroutine eob_calcAndRemoveMeanYb
1424
1425 !--------------------------------------------------------------------------
1426 ! eob_calcRandPert
1427 !--------------------------------------------------------------------------
1428 subroutine eob_calcRandPert(ensObs, randomSeed)
1429 implicit none
1430
1431 ! Arguments:
1432 type(struct_eob), intent(inout) :: ensObs
1433 integer , intent(in) :: randomSeed
1434
1435 ! Locals:
1436 integer :: obsIndex, memberIndex
1437 real(4) :: meanRandPert, sigObs
1438
1439 call rng_setup(abs(randomSeed))
1440 if ( allocated(ensObs%randPert_r4) ) then
1441 call utl_abort('eob_calcRandPert: ensObs%randPert_r4 must not be already allocated')
1442 end if
1443
1444 allocate(ensObs%randPert_r4(ensObs%numMembers,ensObs%numObs))
1445
1446 do obsIndex = 1, ensObs%numObs
1447 sigObs = obs_bodyElem_r(ensObs%obsSpaceData, OBS_OER, obsIndex)
1448 do memberIndex = 1, ensObs%numMembers
1449 ensObs%randPert_r4(memberIndex,obsIndex) = sigObs * rng_gaussian()
1450 end do
1451
1452 meanRandPert = sum(ensObs%randPert_r4(:,obsIndex)) / real(ensObs%numMembers,4)
1453 ensObs%randPert_r4(:,obsIndex) = ensObs%randPert_r4(:,obsIndex) - meanRandPert
1454 end do
1455
1456 end subroutine eob_calcRandPert
1457
1458 !--------------------------------------------------------------------------
1459 ! eob_setMeanOMP
1460 !--------------------------------------------------------------------------
1461 subroutine eob_setMeanOMP(ensObs)
1462 implicit none
1463
1464 ! Arguments:
1465 type(struct_eob), intent(inout) :: ensObs
1466
1467 ! Locals:
1468 integer :: obsIndex
1469
1470 do obsIndex = 1, ensObs%numObs
1471 if (obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, obsIndex) == obs_notAssimilated) cycle
1472 call obs_bodySet_r(ensObs%obsSpaceData, OBS_OMP, obsIndex, &
1473 ensObs%obsValue(obsIndex)-ensObs%meanYb(obsIndex))
1474 end do
1475
1476 end subroutine eob_setMeanOMP
1477
1478 !--------------------------------------------------------------------------
1479 ! eob_setHPHT
1480 !--------------------------------------------------------------------------
1481 subroutine eob_setHPHT(ensObs)
1482 implicit none
1483
1484 ! Arguments:
1485 type(struct_eob), intent(inout) :: ensObs
1486
1487 ! Locals:
1488 integer :: obsIndex, memberIndex
1489 real(8) :: hpht
1490
1491 do obsIndex = 1, ensObs%numObs
1492 if (obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, obsIndex) == obs_notAssimilated) cycle
1493 hpht = 0.0d0
1494 do memberIndex = 1, ensObs%numMembers
1495 hpht = hpht + ensObs%Yb_r4(memberIndex,obsIndex)**2 / ensObs%numMembers
1496 end do
1497 if (hpht > 0.0D0) then
1498 hpht = sqrt(hpht)
1499 else
1500 hpht = 0.0D0
1501 end if
1502 call obs_bodySet_r(ensObs%obsSpaceData, OBS_HPHT, obsIndex, hpht)
1503
1504 end do
1505
1506 end subroutine eob_setHPHT
1507
1508 !--------------------------------------------------------------------------
1509 ! eob_backgroundCheck
1510 !--------------------------------------------------------------------------
1511 subroutine eob_backgroundCheck(ensObs)
1512 !
1513 !:Purpose: Apply additional background using the ensemble spread.
1514 !
1515 implicit none
1516
1517 ! Arguments::
1518 type(struct_eob), intent(inout) :: ensObs
1519
1520 ! Locals:
1521 integer :: bodyIndexBeg, bodyIndexEnd, headerIndex, ivar, bodyIndex
1522 integer :: numRejected, numRejectedMpiGlobal, ierr, windCount
1523 real :: sigo, sigb, omp, sig, reject_limit
1524 logical :: reject_wind
1525
1526 numRejected = 0
1527 reject_limit = 5.0
1528 do headerIndex = 1, obs_numheader(ensObs%obsSpaceData)
1529 bodyIndexBeg = obs_headElem_i(ensObs%obsSpaceData, OBS_RLN, headerIndex)
1530 bodyIndexEnd = obs_headElem_i(ensObs%obsSpaceData, OBS_NLV, headerIndex) + bodyIndexBeg - 1
1531 reject_wind = .false.
1532
1533 do bodyIndex = bodyIndexBeg, bodyIndexEnd
1534 if (obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex) == obs_notAssimilated) cycle
1535 sigo = obs_bodyElem_r(ensObs%obsSpaceData, OBS_OER, bodyIndex)
1536 sigb = obs_bodyElem_r(ensObs%obsSpaceData, OBS_HPHT, bodyIndex)
1537 ! cut off at reject_limit standard deviations
1538 sig = reject_limit*(sigo**2 + sigb**2)**0.5
1539 omp = abs(obs_bodyElem_r(ensObs%obsSpaceData, OBS_OMP, bodyIndex))
1540 if (omp > sig) then
1541 call obs_bodySet_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
1542 call obs_bodySet_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex, &
1543 IBSET(obs_bodyElem_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex),9))
1544 ivar = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex)
1545 reject_wind = bufr_isWindComponent(ivar)
1546 write(*,*) 'eob_backgroundCheck: headerIndex, omp, sig, ivar, reject_wind', &
1547 headerIndex, omp, sig, ivar, reject_wind
1548 numRejected = numRejected + 1
1549 end if
1550 end do
1551
1552 ! take the same reject decision for both components of the
1553 ! wind vector (and any other winds for that station).
1554 ! N.B.: This seems to assume only one level per header, this is generally
1555 ! the case for wind observations currently, since radiosondes are 4D
1556 if (reject_wind) then
1557 ! first count how many wind observations we have for this station
1558 windCount = 0
1559 do bodyIndex = bodyIndexBeg, bodyIndexEnd
1560 if (bufr_isWindComponent(obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex))) then
1561 windCount = windCount + 1
1562 end if
1563 end do
1564 if (windCount > 2) then
1565 write(*,*) 'eob_backgroundCheck: WARNING'
1566 write(*,*) 'Station ',headerIndex,' has ',windCount,' wind observations '
1567 write(*,*) 'Perhaps old radiosonde format - std dev not changed for other wind component'
1568 else
1569 do bodyIndex = bodyIndexBeg, bodyIndexEnd
1570 if (obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex) == obs_notassimilated) cycle
1571 ivar = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex)
1572 if (bufr_isWindComponent(ivar) .and. &
1573 obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex) == obs_assimilated) then
1574 call obs_bodySet_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
1575 call obs_bodySet_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex, &
1576 IBSET(obs_bodyElem_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex),9))
1577 write(*,*) 'eob_backgroundCheck: other wind component headerIndex, ivar', &
1578 headerIndex, ivar
1579 numRejected = numRejected + 1
1580 end if
1581 end do
1582 end if
1583 end if
1584 end do
1585
1586 call rpn_comm_allreduce(numRejected,numRejectedMpiGlobal,1,'mpi_integer','mpi_sum','GRID',ierr)
1587 write(*,*)
1588 write(*,*) 'eob_backgroundCheck: number of observations rejected (local) =', numRejected
1589 write(*,*) 'eob_backgroundCheck: number of observations rejected (global)=', numRejectedMpiGlobal
1590
1591 end subroutine eob_backgroundCheck
1592
1593 !--------------------------------------------------------------------------
1594 ! eob_removeObsNearLand
1595 !--------------------------------------------------------------------------
1596 subroutine eob_removeObsNearLand(ensObs, oceanMask, minDistanceToLand)
1597 !
1598 !:Purpose: Reject observations that are close to land as determined by
1599 ! the argument "minDistanceToLand". In the case of a depth-
1600 ! varying land mask, the first level (should be the surface) is
1601 ! used.
1602 !
1603 implicit none
1604
1605 ! Arguments:
1606 type(struct_eob), intent(inout) :: ensObs
1607 type(struct_ocm), intent(in) :: oceanMask
1608 real(8), intent(in) :: minDistanceToLand
1609
1610 ! Locals:
1611 integer :: headerIndex, bodyIndex, bodyIndexBeg, bodyIndexEnd, levIndex
1612 integer :: numRejected, numRejectedMpiGlobal, ierr
1613 real(8) :: obsLon, obsLat
1614
1615 write(*,*) 'eob_removeObsNearLand: starting'
1616
1617 numRejected = 0
1618
1619 HEADER_LOOP: do headerIndex = 1, obs_numheader(ensObs%obsSpaceData)
1620 bodyIndexBeg = obs_headElem_i(ensObs%obsSpaceData, OBS_RLN, headerIndex)
1621 bodyIndexEnd = obs_headElem_i(ensObs%obsSpaceData, OBS_NLV, headerIndex) + bodyIndexBeg - 1
1622
1623 levIndex = 1
1624 obsLat = obs_headElem_r(ensObs%obsSpaceData, OBS_LAT, headerIndex)
1625 obsLon = obs_headElem_r(ensObs%obsSpaceData, OBS_LON, headerIndex)
1626
1627 ! skip this obs if it is far from land
1628 if (ocm_farFromLand(oceanMask, levIndex, obsLon, obsLat, minDistanceToLand)) then
1629 cycle HEADER_LOOP
1630 end if
1631
1632 ! otherwise it is rejected
1633 BODY_LOOP: do bodyIndex = bodyIndexBeg, bodyIndexEnd
1634 if (obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex) == obs_notAssimilated) cycle BODY_LOOP
1635
1636 call obs_bodySet_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
1637 call obs_bodySet_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex, &
1638 IBSET(obs_bodyElem_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex),9))
1639 numRejected = numRejected + 1
1640 end do BODY_LOOP
1641 end do HEADER_LOOP
1642
1643 call rpn_comm_allreduce(numRejected,numRejectedMpiGlobal,1,'mpi_integer','mpi_sum','GRID',ierr)
1644 write(*,*)
1645 write(*,*) 'eob_removeObsNearLand: number of observations rejected (local) =', numRejected
1646 write(*,*) 'eob_removeObsNearLand: number of observations rejected (global)=', numRejectedMpiGlobal
1647
1648 write(*,*) 'eob_removeObsNearLand: finished'
1649
1650 end subroutine eob_removeObsNearLand
1651
1652 !--------------------------------------------------------------------------
1653 ! eob_setSigiSigo
1654 !--------------------------------------------------------------------------
1655 subroutine eob_setSigiSigo(ensObs)
1656 !
1657 !:Purpose: Apply huber norm quality control procedure. This modifies
1658 ! the OBS_OER value, but before that its value is copied into
1659 ! OBS_SIGO and also OBS_SIGI computed
1660 !
1661 implicit none
1662
1663 ! Arguments:
1664 type(struct_eob), intent(inout) :: ensObs
1665
1666 ! Locals:
1667 integer :: bodyIndex
1668 real(pre_obsReal) :: sigo, sigb, sigi
1669
1670 ! Set 'sigi' and 'sigo' before oer is modified by Huber norm
1671 do bodyIndex = 1, obs_numbody(ensObs%obsSpaceData)
1672 sigb = obs_bodyElem_r(ensObs%obsSpaceData, OBS_HPHT, bodyIndex)
1673 sigo = obs_bodyElem_r(ensObs%obsSpaceData, OBS_OER, bodyIndex)
1674 if (obs_bodyElem_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex) == obs_assimilated) then
1675 sigi = (sigo**2 + sigb**2)**0.5
1676 call obs_bodySet_r(ensObs%obsSpaceData, OBS_SIGI, bodyIndex, sigi)
1677 call obs_bodySet_r(ensObs%obsSpaceData, OBS_SIGO, bodyIndex, sigo)
1678 end if
1679 end do
1680
1681 end subroutine eob_setSigiSigo
1682
1683 !--------------------------------------------------------------------------
1684 ! eob_huberNorm
1685 !--------------------------------------------------------------------------
1686 subroutine eob_huberNorm(ensObs)
1687 !
1688 !:Purpose: Apply huber norm quality control procedure. This modifies
1689 ! the OBS_OER value.
1690 !
1691 implicit none
1692
1693 ! Arguments:
1694 type(struct_eob), intent(inout) :: ensObs
1695
1696 ! Locals:
1697 integer :: huberCount, huberCountMpiGlobal, ivar, windCount, ierr
1698 integer :: bodyIndex, bodyIndexBeg, bodyIndexEnd, headerIndex
1699 real(pre_obsReal) :: c_limit, sig, sigo, sigb, omp, sigo_hub, sigo_hub_wind
1700 logical :: reject_wind
1701
1702 c_limit = 2.0
1703 huberCount = 0
1704 do headerIndex = 1, obs_numheader(ensObs%obsSpaceData)
1705 bodyIndexBeg = obs_headElem_i(ensObs%obsSpaceData, OBS_RLN, headerIndex)
1706 bodyIndexEnd = obs_headElem_i(ensObs%obsSpaceData, OBS_NLV, headerIndex) + bodyIndexBeg - 1
1707 reject_wind = .false.
1708 sigo_hub_wind = 0.0
1709 do bodyIndex = bodyIndexBeg, bodyIndexEnd
1710 sigo = obs_bodyElem_r(ensObs%obsSpaceData, OBS_OER, bodyIndex)
1711 sigb = obs_bodyElem_r(ensObs%obsSpaceData, OBS_HPHT, bodyIndex)
1712 ! cut off at reject_limit standard deviations
1713 sig = c_limit*(sigo**2 + sigb**2)**0.5
1714 omp = abs(obs_bodyElem_r(ensObs%obsSpaceData, OBS_OMP, bodyIndex))
1715 if (omp > sig) then
1716 ! redefining the observational error such that the innovation
1717 ! is at exactly c_limit standard deviations.
1718 huberCount = huberCount + 1
1719 sigo_hub = ((omp/c_limit)**2-sigb**2)**0.5
1720 ivar = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex)
1721 if (bufr_isWindComponent(ivar)) then
1722 ! the other wind components will be changed at the same time (next if)
1723 reject_wind = .true.
1724 call obs_bodySet_r(ensObs%obsSpaceData, OBS_OER, bodyIndex, sigo_hub)
1725 ! this is for the special case both components of the wind innovation
1726 ! suggest using the Huber norm.
1727 if (sigo_hub > sigo_hub_wind) then
1728 sigo_hub_wind = sigo_hub
1729 end if
1730 else
1731 call obs_bodySet_r(ensObs%obsSpaceData, OBS_OER, bodyIndex, sigo_hub)
1732 end if
1733 end if
1734 end do
1735
1736 ! Give the same inflated observation error to both wind components.
1737 ! this part assumes that modern sondes are used (at most two
1738 ! wind observations per station.
1739 if (reject_wind) then
1740 ! first count how many wind observations we have for this station
1741 windCount = 0
1742 do bodyIndex = bodyIndexBeg, bodyIndexEnd
1743 if (bufr_isWindComponent(obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex))) then
1744 windCount = windCount + 1
1745 end if
1746 end do
1747 if (windCount > 2) then
1748 write(*,*) 'Warning Hubernorm'
1749 write(*,*) 'Station ',headerIndex,' has ',windCount,' wind observations '
1750 write(*,*) 'Perhaps old radiosonde format - std dev not changed for other wind component'
1751 else
1752 huberCount = huberCount + 1
1753 do bodyIndex = bodyIndexBeg, bodyIndexEnd
1754 if (bufr_isWindComponent(obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex))) then
1755 call obs_bodySet_r(ensObs%obsSpaceData, OBS_OER, bodyIndex, sigo_hub_wind)
1756 end if
1757 end do
1758 end if
1759 end if
1760 end do
1761
1762 call rpn_comm_allreduce(huberCount, huberCountMpiGlobal, 1, 'mpi_integer', 'mpi_sum', 'GRID', ierr)
1763 write(*,*)
1764 write(*,*) 'eob_huberNorm: number of obs with increased error stddev (local) = ', huberCount
1765 write(*,*) 'eob_huberNorm: number of obs with increased error stddev (global)= ', huberCountMpiGlobal
1766
1767 end subroutine eob_huberNorm
1768
1769 !--------------------------------------------------------------------------
1770 ! eob_rejectRadNearSfc
1771 !--------------------------------------------------------------------------
1772 subroutine eob_rejectRadNearSfc(ensObs)
1773 !
1774 !:Purpose: Reject all radiance observations with peak sensitivity
1775 ! too close to the surface.
1776 !
1777 implicit none
1778
1779 ! Arguments:
1780 type(struct_eob), intent(inout) :: ensObs
1781
1782 ! Locals:
1783 integer :: acceptCount, rejectCount, acceptCountMpiGlobal, rejectCountMpiGlobal
1784 integer :: varNumber, bodyIndex, ierr
1785 ! reject lower than 975 hPa
1786 real, parameter :: logPresRadianceLimit = 11.4876E0
1787
1788 acceptCount = 0
1789 rejectCount = 0
1790 do bodyIndex = 1, obs_numbody(ensObs%obsSpaceData)
1791 varNumber = obs_bodyElem_i(ensObs%obsSpaceData, OBS_VNM, bodyIndex)
1792 if (varNumber == bufr_nbt1 .or. &
1793 varNumber == bufr_nbt2 .or. &
1794 varNumber == bufr_nbt3) then
1795 if (ensObs%vertLocation(bodyIndex) < logPresRadianceLimit) then
1796 acceptCount = acceptCount + 1
1797 else
1798 rejectCount = rejectCount + 1
1799 call obs_bodySet_i(ensObs%obsSpaceData, OBS_ASS, bodyIndex, obs_notAssimilated)
1800 call obs_bodySet_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex, &
1801 IBSET(obs_bodyElem_i(ensObs%obsSpaceData, OBS_FLG, bodyIndex),9))
1802 end if
1803 end if
1804 end do
1805
1806 call rpn_comm_allreduce(acceptCount, acceptCountMpiGlobal, 1, 'mpi_integer', 'mpi_sum', 'GRID', ierr)
1807 call rpn_comm_allreduce(rejectCount, rejectCountMpiGlobal, 1, 'mpi_integer', 'mpi_sum', 'GRID', ierr)
1808 write(*,*)
1809 write(*,*) 'eob_rejectRadNearSfc: Number of accepted, rejected observations (local) : ', &
1810 acceptCount, rejectCount
1811 write(*,*) 'eob_rejectRadNearSfc: Number of accepted, rejected observations (global): ', &
1812 acceptCountMpiGlobal, rejectCountMpiGlobal
1813
1814 end subroutine eob_rejectRadNearSfc
1815
1816 !--------------------------------------------------------------------------
1817 ! getMemberIndexInFullEnsSet (private routine)
1818 !--------------------------------------------------------------------------
1819 subroutine getMemberIndexInFullEnsSet(ensObs, memberIndexArray, &
1820 numGroupsToDivideMembers_opt, &
1821 maxNumMembersPerGroup_opt)
1822 !
1823 !:Purpose: get memberIndex array corresponding to the full ensemble set. This
1824 ! is useful when ensObs is a subset of full ensemble members.
1825 ! If first member in ensObs is member 6, to get the full ensemble set equivalent of ensObs members:
1826 ! a) When members are not grouped (numGroupsToDivideMembers=1), all members are offset by
1827 ! memberIndexOffset (e.g. 6, 7, ..., 6+ensObs%numMermbers)
1828 ! b) When members are grouped (numGroupsToDivideMembers/=1), members within each group
1829 ! are offset by memberIndexOffset but there is increment of maxNumMembersPerGroup_opt
1830 ! to jump to the next group (e.g. if maxNumMembersPerGroup_opt=10, for first group 6, 7, 8, 9,
1831 ! for second group 6+10, 7+10, 8+10, 9+10, and so on)
1832 !
1833 implicit none
1834
1835 ! Arguments:
1836 type(struct_eob), intent(in) :: ensObs
1837 integer, intent(inout) :: memberIndexArray(:)
1838 integer, optional, intent(in) :: numGroupsToDivideMembers_opt
1839 integer, optional, intent(in) :: maxNumMembersPerGroup_opt
1840
1841 ! Locals:
1842 integer :: memberIndex, groupIndex, memberIndexOffset, memberIndexInGroup
1843 integer :: numGroupsToDivideMembers, numMembersPerGroup
1844
1845 if (present(numGroupsToDivideMembers_opt)) then
1846 numGroupsToDivideMembers = numGroupsToDivideMembers_opt
1847 else
1848 numGroupsToDivideMembers = 1
1849 end if
1850
1851 memberIndexOffset = ensObs%fileMemberIndex1
1852
1853 if (numGroupsToDivideMembers == 1) then
1854 do memberIndex = 1, ensObs%numMembers
1855 memberIndexArray(memberIndex) = memberIndex + memberIndexOffset - 1
1856 end do
1857 else
1858 if (.not. present(maxNumMembersPerGroup_opt)) then
1859 call utl_abort('getMemberIndexInFullEnsSet: maxNumMembersPerGroup_opt input argument missing')
1860 end if
1861
1862 ! divide members into groups
1863 numMembersPerGroup = ensObs%numMembers / numGroupsToDivideMembers
1864 if (numMembersPerGroup > maxNumMembersPerGroup_opt) then
1865 call utl_abort('getMemberIndexInFullEnsSet: numMembersPerGroup > maxNumMembersPerGroup_opt')
1866 end if
1867
1868 memberIndex = 0
1869 do groupIndex = 1, numGroupsToDivideMembers
1870 do memberIndexInGroup = 1, numMembersPerGroup
1871 memberIndex = memberIndex + 1
1872 memberIndexArray(memberIndex) = (groupIndex - 1) * maxNumMembersPerGroup_opt + &
1873 memberIndexInGroup + memberIndexOffset - 1
1874 end do
1875 end do
1876
1877 end if
1878
1879 end subroutine getMemberIndexInFullEnsSet
1880
1881 !--------------------------------------------------------------------------
1882 ! max_transmission (private routine)
1883 !--------------------------------------------------------------------------
1884 subroutine max_transmission(transmission, numLevels, transIndex, rttovPres, maxLnP)
1885 !
1886 !:Purpose: Determine the height in log pressure where we find the maximum
1887 ! value of the first derivative of transmission with respect to
1888 ! log pressure
1889 !
1890 implicit none
1891
1892 ! Arguments:
1893 type(rttov_transmission), intent(in) :: transmission ! transmission (rttov type)
1894 integer(kind=jpim) , intent(in) :: numLevels ! number of RTTOV levels
1895 integer , intent(in) :: transIndex ! index of transmission%tau_levels
1896 real(kind=jprb), pointer, intent(in) :: rttovPres(:) ! pressure of RTTOV levels
1897 real(8) , intent(out) :: maxLnP ! log pressure of maximum
1898
1899 ! Locals:
1900 integer :: levIndex
1901 real(8) :: lnPres(numLevels), avgPres(numLevels-1)
1902 real(8) :: diffTau, derivTau(numLevels), maxDeriv
1903 integer :: nAvgLev, maxIndex
1904
1905 nAvgLev = numLevels - 1
1906 lnPres(:) = log(rttovPres(:)*MPC_PA_PER_MBAR_R8)
1907 ! calculate the first derivative of transmission with respect to log pressure
1908 ! and find the level index for its maximum
1909 maxDeriv = -0.1d0
1910 derivTau(1) = 0.0d0
1911 maxIndex = numLevels
1912 do levIndex = 2, numLevels
1913 avgPres(levIndex-1) = 0.5d0*(lnPres(levIndex)+lnPres(levIndex-1))
1914 diffTau = transmission%tau_levels(levIndex-1,transIndex) - transmission%tau_levels(levIndex,transIndex)
1915 derivTau(levIndex) = diffTau / (lnPres(levIndex)-lnPres(levIndex-1))
1916 if (derivTau(levIndex)>maxDeriv) then
1917 maxDeriv = derivTau(levIndex)
1918 maxIndex = levIndex
1919 end if
1920 end do
1921
1922 ! get the height in log pressure for the level index (maxIndex) found above
1923 if (maxIndex==1) maxIndex = maxIndex + 1
1924 if ((maxIndex==2).or.(maxIndex==numLevels)) then
1925 maxLnP = avgPres(maxIndex-1)
1926 else
1927 call get_peak(maxIndex,nAvgLev,avgPres,derivTau,maxLnP)
1928 end if
1929
1930 end subroutine max_transmission
1931
1932 !--------------------------------------------------------------------------
1933 ! get_peak (private routine)
1934 !--------------------------------------------------------------------------
1935 subroutine get_peak(maxIndex,nlev,lnp,deriv,maxLnP)
1936 !
1937 !:Purpose: Do quadratic interpolation to find pressure of peak transmission.
1938 !
1939 implicit none
1940
1941 ! Arguments:
1942 integer, intent(in) :: maxIndex, nlev
1943 real(8), intent(in) :: lnp(nlev), deriv(nlev+1)
1944 real(8), intent(inout) :: maxLnP
1945
1946 ! Locals:
1947 external :: dgesv
1948 integer, parameter :: N=3
1949 integer :: info
1950 integer, parameter :: lda=N, ldb=N, nrhs=1
1951 integer :: ipiv(N)
1952 real(8) :: A(lda,N),B(ldb,nrhs)
1953 integer :: index1, index2
1954
1955 index2 = 0
1956 do index1=maxIndex-1,maxIndex+1
1957 index2 = index2 + 1
1958 A(index2,1) = lnp(index1-1)*lnp(index1-1)
1959 A(index2,2) = lnp(index1-1)
1960 A(index2,3) = 1.0d0
1961 B(index2,1) = deriv(index1)
1962 end do
1963
1964 call dgesv(N,nrhs,A,lda,ipiv,B,ldb,info)
1965
1966 if (info==0) then
1967 maxLnP = -0.5*(B(2,1)/B(1,1))
1968 else
1969 maxLnP = lnp(maxIndex-1)
1970 end if
1971
1972 end subroutine get_peak
1973
1974end module ensembleObservations_mod