1module obsSpaceDiag_mod
2 ! MODULE obsSpaceDiag_mod (prefix='osd' category='1. High-level functionality')
3 !
4 !:Purpose: Some experimental procedures for computing various diagnostics in
5 ! observation space.
6 !
7 use codePrecision_mod
8 use midasMpi_mod
9 use bufr_mod
10 use codtyp_mod
11 use MathPhysConstants_mod
12 use horizontalCoord_mod
13 use timeCoord_mod
14 use controlVector_mod
15 use obsSpaceData_mod
16 use columnData_mod
17 use verticalCoord_mod
18 use gridStateVector_mod
19 use bMatrix_mod
20 use bMatrixHi_mod
21 use bMatrixEnsemble_mod
22 use bCovarSetupChem_mod
23 use varNameList_mod
24 use stateToColumn_mod
25 use randomNumber_mod
26 use obsOperators_mod
27 use utilities_mod
28 use physicsfunctions_mod
29 use obsSubSpaceData_mod
30 use obsfiles_mod
31 use obsOperatorsChem_mod
32 use obsFamilyList_mod
33
34 implicit none
35 save
36 private
37
38 ! public procedures
39 !------------------
40 public :: osd_ObsSpaceDiag
41
42 type :: struct_osd_diagn
43
44 ! Structure for storing observation-space diagnostic arrays
45 !
46 ! Real arrays indexed by (lat,lon,lev,stat), where stat=1 for RMS and stat=2 for mean for all except Jo_stats.
47 ! For Jo_stats, stat=1 is Jo for x=x_analysis and stat=2 is Jo for x=x_background.
48 ! The array counts is indexed by (lat,lon,lev).
49 ! The array status_count is indexed (lat,lon,lev,status), where the status number ranges from 0 to 2.
50 !
51 ! Variable Description
52 ! -------- -----------
53 ! OmP_stats obs - background statistics
54 ! OmA_stats obs - analysis statistics
55 ! obs_stats observation statistics
56 ! Jo_stats cost function statistics
57 ! - First four elements (of last dimension) apply only prescribed obs error variances
58 ! as normalizing-scaling denomicators.
59 ! - Fifth element (of last dimension) applies the sum of the prescribed obs error variances
60 ! and the diag(HPHT) as normalizing-scaling denominator. This does not include consideration
61 ! of spatial correlations of (O-P) between obs points
62 ! associated to HPHT in the normalizing denominator.
63 ! Jpa_stats statistics (P-A) in observation space.
64 ! - Not exactly equivalent to Jb of the cost function
65 ! - Applies the prescribed diag(HPHT) as normalizing-scaling denomiator.
66 ! - Does not include consideration of spatial correlations of (P-A) between obs points
67 ! associated to HPHT in normalizing denominator
68 ! - Vert. coordinate binning included but not currently output.
69 ! diagR_stats Elements for the calc of mean{[(O-P)-mean(O-P)][(O-A)-mean(O-A)]}
70 ! (with each O-P and O-A difference divided by sigma_obs)
71 ! for scaling factor adjustments of obs std. dev via the Desroziers approach.
72 ! diagHPHT_stats Elements for the calc of mean{[(O-P)-mean(O-P)][[(O-P)-mean(O-P)]-[(O-A)-mean(O-A)]]}
73 ! = mean{[(O-P)-mean(O-P)][(A-P)-mean(A-P)]}(with each O-P and O-A difference divided by sqrtHPHT)
74 ! for scaling factor adjustments of background std. dev. in obs space via the Desroziers approach.
75 ! counts number of observations in a (lat,lon,lev) bin
76 ! nlat number of latitude levels
77 ! nlon number of longitude levels
78 ! nlev number of vertical levels
79 ! nbin total number of (lat,lon,lev) bins
80 ! nstat number of different statistics types
81 ! nstatus indicates the number of observations with a certain status,
82 ! with status values denoting:
83 ! 0 - observation has been rejected and not included for diagnostics
84 ! 1 - observation has been assimilated
85 ! 2 - observation has been used for diagnostics only (not assimilated)
86 ! deltaLat latitude bin size (deg)
87 ! deltaLon longitude bin size (deg)
88 ! deltaLogPressure vertical bin size in log(pressure)
89 ! allow_print_summary indicates fs printing of summary diagnostics is allowed
90 ! assim_mode indicates if assimilation was performed for this dataset
91
92 real(8), allocatable :: OmP_stats(:,:,:,:),OmA_stats(:,:,:,:),obs_stats(:,:,:,:),Jo_stats(:,:,:,:)
93 real(8), allocatable :: diagR_stats(:,:,:,:),diagHPHT_stats(:,:,:,:), Jpa_stats(:,:,:)
94 integer, allocatable :: counts(:,:,:),nstatus(:,:,:,:)
95
96 integer :: nlev,nlat,nlon,nbin,nstat
97 real(8) :: deltaLat
98 real(8) :: deltaLon
99 real(8) :: deltaLogPressure
100 logical :: allow_print_summary,assim_mode
101
102 end type struct_osd_diagn
103
104 ! Parameters identifying obs sets and related actions for diagnostic calcs of each family
105 !
106 ! diagn_num Prescribed (starting) number of (stnid, bufr, nlev) for the diagnostics calc
107 !
108 ! diagn_stnid Prescribed (starting) list of stnid (with *s as needed) for the diagnostics calc
109 ! with '*' denoting wild cards
110 !
111 ! diagn_varno Prescribed (starting) list of bufr elements for the diagnostics calc
112 !
113 ! diagn_unilev Prescribed (starting) list of logicals indicating uni-level obs for the diagnostics calc
114 !
115 ! diagn_pressmin Bottom of top layer for diagnostics (in Pa).
116 !
117 ! diagn_save Logical indicating gridded diagnostics are to be saved
118 ! in an ascii file in addition to overall diagnostics.
119 !
120 ! diagn_nset Integer indicating grouping of diagnostics with
121 ! 1: group by stnid
122 ! 2: group by (stnid,bufr)
123 ! 3: group by (stnid,bufr,nlev)
124 !
125 ! diagn_all Logical indicating if all combinations specified by diagn_nset are to be
126 ! used in diagnostics or only those specified by the diagn_* arrays
127 !
128 ! obsspace_diagn_filename
129 ! File name for file containing obs space diagnostics related to chemical constituents.
130
131 character(len=100) :: obsspace_diagn_filename(ofl_numFamily)
132 integer, parameter :: max_cfg_size=100
133
134 ! Namelist variables
135 real(8) :: deltaLat ! Size of latitude bins for diagnostics (degrees)
136 real(8) :: deltaLon ! Size of longitude bins for diagnostics (degrees)
137 real(8) :: deltaPressure ! Size of vertical bins for diagnostics (Pascal)
138 real(8) :: deltaHeight ! Size of vertical bins for diagnostics (meters)
139 integer :: numFamily ! MUST NOT BE INCLUDED IN NAMELIST!
140 character(len=2) :: familyList(ofl_numFamily) ! List of family names to consider
141 integer :: numElement ! MUST NOT BE INCLUDED IN NAMELIST!
142 integer :: elementList(ofl_numFamily) ! List of bufr element ids to consider
143 integer :: nrandseed ! Random seed value
144 logical :: lrandom ! Perform diagnostics from random perturbations
145 integer :: diagn_num(ofl_numFamily) ! Prescribed (starting) number of (stnid, bufr, nlev) for diag calc
146 integer :: diagn_nset(ofl_numFamily) ! Choose to group by 1: stnid, 2: stnid,bufr, 3: stnid,bufr,nlev
147 integer :: diagn_varno(ofl_numFamily,max_cfg_size) ! List of bufr element ids for diag calc
148 character(len=9) :: diagn_stnid(ofl_numFamily,max_cfg_size) ! List of stnid for diag calc
149 logical :: diagn_save(ofl_numFamily) ! Choose to save gridded info in ascii file
150 logical :: diagn_all(ofl_numFamily) ! Choose to use all combinations specified by diagn_nset
151 logical :: diagn_unilev(ofl_numFamily,max_cfg_size) ! List indicating if uni-lev obs for diag calc
152 real(8) :: diagn_pressmin(ofl_numFamily) ! Bottom of top layer for diagnostics (in Pa)
153
154contains
155
156 !--------------------------------------------------------------------------
157 ! osd_ObsSpaceDiag
158 !--------------------------------------------------------------------------
159 subroutine osd_ObsSpaceDiag( obsSpaceData, columnTrlOnAnlIncLev, hco_anl, analysisMode_opt )
160 !
161 !:Purpose: Calls routines to perform observation-space diagnostic tasks
162 !
163 !:Arguments:
164 ! :obsSpaceData: Obs space data structure
165 ! :columnTrlOnAnlIncLev: Structure of vertical columns at obs locations.
166 ! Expected to be for analysis vertical levels if to be used.
167 ! :analysisMode: logical indicating if following analysis mode or not (optional)
168 ! Assumed .true. if not present.
169 !
170 implicit none
171
172 ! Arguments:
173 type(struct_obs) , intent(inout) :: obsSpaceData
174 type(struct_columnData), intent(inout) :: columnTrlOnAnlIncLev
175 type(struct_hco), pointer, intent(in) :: hco_anl
176 logical, optional, intent(in) :: analysisMode_opt
177
178 ! Locals:
179 logical :: nmlExists,anlm_mod
180 integer :: ierr
181 integer :: dateprnt,timeprnt,newdate
182
183 if (present(analysisMode_opt)) then
184 anlm_mod = analysisMode_opt
185 else
186 anlm_mod = .true.
187 end if
188
189 call osd_setup(nmlExists)
190 ierr = newdate(tim_getDatestamp(),dateprnt,timeprnt,-3)
191 dateprnt=dateprnt*100+timeprnt/1000000
192
193 ! Perform diagnostics based on OmP (and OmA if available)
194
195 call osd_obsPostProc(obsSpaceData,columnTrlOnAnlIncLev,deltaLat,deltaLon,deltaPressure,anlm_mod)
196
197 if ((.not. anlm_mod) .or. (.not.lrandom) .or. (.not.nmlExists)) return
198
199 ! Perform diagnostics from random perturbations
200
201 call osd_calcInflation(obsSpaceData,columnTrlOnAnlIncLev,hco_anl,dateprnt)
202
203 end subroutine osd_ObsSpaceDiag
204
205 !--------------------------------------------------------------------------
206 ! osd_calcInflation
207 !--------------------------------------------------------------------------
208 subroutine osd_calcInflation( obsSpaceData, columnTrlOnAnlIncLev, hco_anl, dateprnt )
209 !
210 !:Purpose: Calculates observation-space diagnostics from random perturbations
211 !
212
213 implicit none
214
215 ! Arguments:
216 type(struct_obs) , intent(inout) :: obsSpaceData
217 type(struct_columnData) , intent(inout) :: columnTrlOnAnlIncLev
218 type(struct_hco), pointer, intent(in) :: hco_anl
219 integer , intent(in) :: dateprnt
220
221 ! Locals:
222 type(struct_gsv) :: statevector
223 type(struct_columnData) :: column
224 type(struct_vco), pointer :: vco_anl
225 real(8), allocatable,target :: controlVector(:)
226 integer :: familyIndex,elementIndex,bodyIndex,headerIndex,latIndex,lonIndex,verticalIndex
227 integer :: maxLat,maxLon,maxVertical
228 real(8), allocatable :: innovStd(:,:,:),bmatHiStd(:,:,:),bmatEnStd(:,:,:)
229 integer, allocatable :: counts(:,:,:)
230 real(8), allocatable :: my_innovStd(:,:,:),my_bmatHiStd(:,:,:),my_bmatEnStd(:,:,:)
231 integer, allocatable :: my_counts(:,:,:)
232 integer :: ierr,nulinnov,nulBmatHi,nulBmatEn,nulcount,fnom,fclos,ivco,ivco_recv,iseed,jj,jlev,jvar
233 integer :: ivar_count,nlev_max
234 logical :: lpert_static, lpert_ens
235 real(8), pointer :: cvBhi(:), cvBen(:), cvBchm(:)
236 real(pre_incrReal), pointer :: field(:,:,:,:)
237 real(8), allocatable :: HxBhi(:), HxBen(:)
238 real(8), allocatable :: scaleFactor(:),scaleFactorChm(:,:)
239 character(len=128) :: innovFileName,bmatHiFileName,bmatEnFileName,countFileName
240 character(len=6) :: elementStr
241 character(len=10) :: dateStr
242
243 write(*,*) 'osd_calcInflation: Starting'
244
245 if( nrandseed == 999 ) nrandseed=dateprnt ! if seed not set by namelist, use valid date/time
246 write(*,*) 'osd_calcInflation: random seed set to ',nrandseed
247
248 maxLat = nint(180.0d0/deltaLat)
249 maxLon = nint(360.0d0/deltaLon)
250 maxVertical = max(1+nint(110000.0d0/deltaPressure),1+nint(80000.0d0/deltaHeight),200)
251
252 write(*,*) 'osd_calcInflation: Compute random realization of background error'
253
254 ! allocate vectors to store Hx for the static and ensemble-based covariance matrices
255 allocate(HxBhi(obs_numbody(obsSpaceData)))
256 allocate(HxBen(obs_numbody(obsSpaceData)))
257
258 ! initialize columnData object for increment
259 call col_setVco(column,col_getVco(columnTrlOnAnlIncLev))
260 call col_allocate(column,col_getNumCol(columnTrlOnAnlIncLev),mpiLocal_opt=.true.)
261
262 ! initialize gridStateVector object for increment
263 vco_anl => col_getVco(columnTrlOnAnlIncLev)
264 call gsv_allocate(statevector, tim_nstepobsinc, hco_anl, vco_anl, &
265 dataKind_opt=pre_incrReal, mpi_local_opt=.true.)
266
267 nlev_max=max(col_getNumLev(columnTrlOnAnlIncLev,'MM'),col_getNumLev(columnTrlOnAnlIncLev,'TH'))
268
269 allocate(controlVector(cvm_nvadim))
270 allocate(scaleFactor(nlev_max))
271 allocate(scaleFactorChm(100,nlev_max))
272
273 ! COMPUTE BMATRIX PERTURBATION FOR THE STATIC COVARIANCES CASE; from Bhi and or BChm
274
275 if (all(familyList(1:numFamily) == 'CH').or.(.not.cvm_subVectorExists('B_HI'))) then
276 cvBhi => null()
277 else
278 cvBhi => cvm_getSubVector(controlVector,'B_HI')
279 end if
280 if (any(familyList(1:numFamily) == 'CH').and.obs_famExist(obsSpaceData,'CH').and.cvm_subVectorExists('B_CHM')) then
281 cvBChm => cvm_getSubVector(controlVector,'B_CHM')
282 else
283 cvBChm => null()
284 end if
285
286 HxBhi(:) = 0.0d0
287
288 iseed = abs(nrandseed)
289 call rng_setup(iseed)
290
291 if (cvm_subVectorExists('B_HI').or.cvm_subVectorExists('B_CHM')) then
292
293 ! compute random control vector
294
295 controlVector(:) = 0.0d0
296
297 if (cvm_subVectorExists('B_HI')) then
298 do jj = 1,size(cvBhi)
299 cvBhi(jj)=rng_gaussian()
300 enddo
301 ! initialize vector of scaleFactors
302 call bhi_getScaleFactor(scaleFactor)
303 else
304 scaleFactor(:)=1.0
305 end if
306
307 if (cvm_subVectorExists('B_CHM')) then
308 do jj = 1,size(cvBChm)
309 cvBChm(jj)=rng_gaussian()
310 enddo
311 ! initialize vector of scaleFactors
312 call bcsc_getScaleFactor(scaleFactorChm)
313 else
314 scaleFactorChm(:,:)=1.0
315 end if
316
317 ! multiply vector by B^1/2
318 call bmat_sqrtB(controlVector,cvm_nvadim,statevector)
319
320 ! undo the scaleFactor (THIS IS NOT CORRECT FOR 2D VARIABLES!!! (P0 and TG) )
321
322 ivar_count=0
323 do jvar=1,vnl_numvarmax
324 if(gsv_varExist(statevector,vnl_varNameList(jvar))) then
325
326 call gsv_getField(statevector,field,vnl_varNameList(jvar))
327
328 if (vnl_varKindFromVarname(vnl_varNameList(jvar)) == 'MT') then
329 do jlev = 1, gsv_getNumLev(statevector,vnl_varLevelFromVarname(vnl_varNameList(jvar)))
330 if(scaleFactor(jlev) > 0.0d0) field(:,:,jlev,:)=field(:,:,jlev,:)/scaleFactor(jlev)
331 enddo
332 else if (vnl_varKindFromVarname(vnl_varNameList(jvar)) == 'CH') then
333 ivar_count=ivar_count+1
334 do jlev = 1, gsv_getNumLev(statevector,vnl_varLevelFromVarname(vnl_varNameList(jvar)))
335 if(scaleFactorChm(ivar_count,jlev) > 0.0d0) field(:,:,jlev,:)=field(:,:,jlev,:) &
336 /scaleFactorChm(ivar_count,jlev)
337 end do
338 end if
339 endif
340 enddo
341
342 ! multiply by H
343 call s2c_tl(statevector,column,columnTrlOnAnlIncLev,obsSpaceData) ! put in column H_horiz dx
344 call oop_Htl(column,columnTrlOnAnlIncLev,obsSpaceData,1) ! Save as OBS_WORK: H_vert H_horiz dx = Hdx
345 do bodyIndex=1,obs_numBody(obsSpaceData)
346 HxBhi(bodyIndex) = obs_bodyElem_r(obsSpaceData,OBS_WORK,bodyIndex)
347 enddo
348
349 end if
350
351 ! COMPUTE BMATRIX PERTURBATION FOR THE ENSEMBLE COVARIANCES CASE; from Ben
352
353 if (cvm_subVectorExists('B_ENS')) then
354
355 cvBen => cvm_getSubVector(controlVector,'B_ENS')
356 HxBen(:) = 0.0d0
357
358 ! compute random control vector
359
360 controlVector(:) = 0.0d0
361
362 do jj = 1,size(cvBen)
363 cvBen(jj)=rng_gaussian()
364 enddo
365
366 ! initialize vector of scaleFactors
367 call ben_getScaleFactor(scaleFactor)
368
369 scaleFactorChm(:,:)=1.0
370
371 ! multiply vector by B^1/2
372 call bmat_sqrtB(controlVector,cvm_nvadim,statevector)
373
374 ! undo the scaleFactor
375
376 ivar_count=0
377 do jvar=1,vnl_numvarmax
378 if(gsv_varExist(statevector,vnl_varNameList(jvar))) then
379
380 call gsv_getField(statevector,field,vnl_varNameList(jvar))
381
382 if (vnl_varKindFromVarname(vnl_varNameList(jvar)) == 'MT') then
383 do jlev = 1, gsv_getNumLev(statevector,vnl_varLevelFromVarname(vnl_varNameList(jvar)))
384 if(scaleFactor(jlev) > 0.0d0) field(:,:,jlev,:)=field(:,:,jlev,:)/scaleFactor(jlev)
385 enddo
386 else if (vnl_varKindFromVarname(vnl_varNameList(jvar)) == 'CH') then
387 ivar_count=ivar_count+1
388 do jlev = 1, gsv_getNumLev(statevector,vnl_varLevelFromVarname(vnl_varNameList(jvar)))
389 if(scaleFactorChm(ivar_count,jlev) > 0.0d0) field(:,:,jlev,:)=field(:,:,jlev,:) &
390 /scaleFactorChm(ivar_count,jlev)
391 end do
392 end if
393 endif
394 enddo
395
396 ! multiply vector by H
397 call s2c_tl(statevector,column,columnTrlOnAnlIncLev,obsSpaceData) ! put in column H_horiz dx
398 call oop_Htl(column,columnTrlOnAnlIncLev,obsSpaceData,1) ! Save as OBS_WORK: H_vert H_horiz dx = Hdx
399 do bodyIndex=1,obs_numBody(obsSpaceData)
400 HxBen(bodyIndex) = obs_bodyElem_r(obsSpaceData,OBS_WORK,bodyIndex)
401 enddo
402 else
403 cvBen => null()
404 HxBen(:) = 0.0d0
405 end if
406
407 deallocate(scaleFactor)
408 deallocate(scaleFactorChm)
409 call col_deallocate(column)
410 call gsv_deallocate(statevector)
411
412 allocate(my_innovStd(maxLat,maxLon,maxVertical))
413 allocate(my_bmatHiStd(maxLat,maxLon,maxVertical))
414 allocate(my_bmatEnStd(maxLat,maxLon,maxVertical))
415 allocate(my_counts(maxLat,maxLon,maxVertical))
416
417 allocate(innovStd(maxLat,maxLon,maxVertical))
418 allocate(bmatHiStd(maxLat,maxLon,maxVertical))
419 allocate(bmatEnStd(maxLat,maxLon,maxVertical))
420 allocate(counts(maxLat,maxLon,maxVertical))
421
422 FAMILY: do familyIndex = 1, numFamily
423
424 ELEMENT: do elementIndex = 1, numElement
425
426 ! Initialize logicals for calc of perturbation diagnostics.
427
428 lpert_static=.false.
429 lpert_ens=.false.
430
431 if (familyList(familyIndex) /= 'CH') then
432 if (cvm_subVectorExists('B_HI')) lpert_static=.true.
433 else
434 if (cvm_subVectorExists('B_CHM')) lpert_static=.true.
435 end if
436 if (cvm_subVectorExists('B_ENS')) lpert_ens=.true.
437
438 ivco = -999
439 my_innovStd(:,:,:) = 0.0d0
440 my_bmatHiStd(:,:,:) = 0.0d0
441 my_bmatEnStd(:,:,:) = 0.0d0
442 my_counts(:,:,:) = 0
443
444 call obs_set_current_body_list(obsSpaceData,familyList(familyIndex))
445 BODY: do
446 bodyIndex = obs_getBodyIndex(obsSpaceData)
447 if (bodyIndex < 0) exit BODY
448
449 if(obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex) == elementList(elementIndex) .and. &
450 obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) == obs_assimilated) then
451
452 call osd_getIndices(obsSpaceData,bodyIndex,latIndex,lonIndex,verticalIndex)
453
454 if(verticalIndex == -1) then
455 ! skip this obs for whatever reason
456 cycle BODY
457 else if(latIndex > maxLat .or. lonIndex > maxLon .or. verticalIndex > maxVertical) then
458 write(*,*) 'osd_calcInflation: index too big: lat,lon,vertical=',latIndex,lonIndex,verticalIndex, &
459 ' lat_max,lon_max,vertical_max=',maxlat,maxlon,maxvertical
460 call utl_abort('osd_calcInflation')
461 endif
462
463 ivco = obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex)
464 my_counts(latIndex,lonIndex,verticalIndex) = my_counts(latIndex,lonIndex,verticalIndex) + 1
465 my_innovStd(latIndex,lonIndex,verticalIndex) = my_innovStd(latIndex,lonIndex,verticalIndex) + &
466 obs_bodyElem_r(obsSpaceData,OBS_OMP,bodyIndex)* &
467 obs_bodyElem_r(obsSpaceData,OBS_OMP,bodyIndex)
468 if (lpert_static) my_bmatHiStd(latIndex,lonIndex,verticalIndex) = my_bmatHiStd(latIndex,lonIndex,verticalIndex) + &
469 HxBhi(bodyIndex)*HxBhi(bodyIndex)
470 if (lpert_ens) my_bmatEnStd(latIndex,lonIndex,verticalIndex) = my_bmatEnStd(latIndex,lonIndex,verticalIndex) + &
471 HxBen(bodyIndex)*HxBen(bodyIndex)
472
473 headerIndex = obs_bodyElem_i(obsSpaceData,OBS_HIND,bodyIndex)
474
475 endif
476 enddo BODY
477
478 call rpn_comm_allreduce(ivco,ivco_recv,1,"MPI_INTEGER","MPI_MAX","GRID",ierr)
479 ivco = ivco_recv
480
481 call rpn_comm_allreduce(my_counts,counts,maxLat*maxLon*maxVertical,"MPI_INTEGER","MPI_SUM","GRID",ierr)
482 call rpn_comm_allreduce(my_innovStd,innovStd,maxLat*maxLon*maxVertical,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
483 if (lpert_static) call rpn_comm_allreduce(my_bmatHiStd,bmatHiStd,maxLat*maxLon*maxVertical,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
484 if (lpert_ens) call rpn_comm_allreduce(my_bmatEnStd,bmatEnStd,maxLat*maxLon*maxVertical,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
485
486 where (counts > 0) innovStd = sqrt(innovStd/counts)
487 if (lpert_static) then
488 where (counts > 0) bmatHiStd = sqrt(bmatHiStd/counts)
489 end if
490 if (lpert_ens) then
491 where (counts > 0) bmatEnStd = sqrt(bmatEnStd/counts)
492 end if
493
494 if(mmpi_myid == 0 .and. sum(counts(:,:,:)) > 0) then
495
496 ! determine file names
497 write(dateStr,'(i10.10)') dateprnt
498 write(elementStr,'(i6.6)') elementList(elementIndex)
499 innovFileName = 'innov' // dateStr // '_' // trim(familyList(familyIndex)) // '_' // trim(elementStr) // '.dat'
500 if (lpert_static) bmatHiFileName = 'bmathi' // dateStr // '_' // trim(familyList(familyIndex)) // '_' // trim(elementStr) // '.dat'
501 if (lpert_ens) bmatEnFileName = 'bmaten' // dateStr // '_' // trim(familyList(familyIndex)) // '_' // trim(elementStr) // '.dat'
502 countFileName = 'count' // dateStr // '_' // trim(familyList(familyIndex)) // '_' // trim(elementStr) // '.dat'
503
504 ! open files
505 nulinnov=0
506 nulBmatHi =0
507 nulBmatEn =0
508 nulcount=0
509 ierr = fnom(nulinnov,innovFileName,'FMT+R/W',0)
510 if (lpert_static) ierr = fnom(nulBmatHi ,bmatHiFileName ,'FMT+R/W',0)
511 if (lpert_ens) ierr = fnom(nulBmatEn ,bmatEnFileName ,'FMT+R/W',0)
512 ierr = fnom(nulcount,countFileName,'FMT+R/W',0)
513
514 ! write data for this family/element
515 write(nulinnov,*) '***maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight='
516 write(nulinnov,*) maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight
517
518 if (lpert_static) then
519 write(nulBmatHi,*) '***maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight='
520 write(nulBmatHi,*) maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight
521 end if
522 if (lpert_ens) then
523 write(nulBmatEn,*) '***maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight='
524 write(nulBmatEn,*) maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight
525 end if
526
527 write(nulcount,*) '***maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight='
528 write(nulcount,*) maxLon,maxLat,deltaLon,deltaLat,deltaPressure,deltaHeight
529 do verticalIndex = 1,maxVertical
530 if(sum(counts(:,:,verticalIndex)) > 0) then
531 write(nulinnov,*) '***verticalIndex,vco='
532 write(nulinnov,*) verticalIndex,ivco
533
534 if (lpert_static) then
535 write(nulBmatHi,*) '***verticalIndex,vco='
536 write(nulBmatHi,*) verticalIndex,ivco
537 end if
538 if (lpert_ens) then
539 write(nulBmatEn,*) '***verticalIndex,vco='
540 write(nulBmatEn,*) verticalIndex,ivco
541 end if
542
543 write(nulcount,*) '***verticalIndex,vco='
544 write(nulcount,*) verticalIndex,ivco
545 do latIndex = 1,maxLat
546 write(nulinnov,*) innovStd(latIndex,:,verticalIndex)
547 write(nulcount,*) counts(latIndex,:,verticalIndex)
548 enddo
549
550 if (lpert_static) then
551 do latIndex = 1,maxLat
552 write(nulBmatHi ,*) bmatHiStd(latIndex,:,verticalIndex)
553 enddo
554 end if
555 if (lpert_ens) then
556 do latIndex = 1,maxLat
557 write(nulBmatEn ,*) bmatEnStd(latIndex,:,verticalIndex)
558 enddo
559 end if
560
561 endif
562 enddo
563
564 ! close files
565 ierr = fclos(nulinnov)
566 if (lpert_static) ierr = fclos(nulBmatHi)
567 if (lpert_ens) ierr = fclos(nulBmatEn)
568 ierr = fclos(nulcount)
569 endif
570 enddo ELEMENT
571
572 enddo FAMILY
573
574 deallocate(my_counts)
575 deallocate(my_innovStd)
576 deallocate(my_bmatHiStd)
577 deallocate(my_bmatEnStd)
578
579 deallocate(innovStd)
580 deallocate(bmatHiStd)
581 deallocate(bmatEnStd)
582 deallocate(counts)
583
584 deallocate(HxBhi)
585 deallocate(HxBen)
586
587 write(*,*) 'osd_calcInflation: Finished'
588
589 end subroutine osd_calcInflation
590
591 !--------------------------------------------------------------------------
592 ! osd_getIndices
593 !--------------------------------------------------------------------------
594 subroutine osd_getIndices( obsSpaceData, bodyIndex, latIndex, lonIndex, verticalIndex )
595 !
596 implicit none
597
598 ! Arguments:
599 type(struct_obs), intent(in) :: obsSpaceData
600 integer , intent(in) :: bodyIndex
601 integer , intent(out) :: latIndex
602 integer , intent(out) :: lonIndex
603 integer , intent(out) :: verticalIndex
604
605 ! Locals:
606 real(8), parameter :: epsilon=0.001
607 integer :: headerIndex, bodyElem_i
608
609 ! codtypes for tovs: 164(AMSUA) 168 180 181 182 183 185 186 192 193
610
611 ! epsilon is added below to handle case where lon/dlon~1 or lat/dlat~1
612 headerIndex = obs_bodyElem_i(obsSpaceData,OBS_HIND,bodyIndex)
613 latIndex = 1 + floor((90.0d0 + obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)*MPC_DEGREES_PER_RADIAN_R8)/deltaLat - epsilon)
614 if (latIndex == 0) latIndex=1
615 lonIndex = 1 + floor(obs_headElem_r(obsSpaceData,OBS_LON,headerIndex)*MPC_DEGREES_PER_RADIAN_R8/deltaLon - epsilon)
616 if (lonIndex == 0) lonIndex=1
617
618 select case(obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex))
619 case(1)
620 ! height coordinate
621 verticalIndex = 1 + nint(obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)/deltaHeight)
622 case(2)
623 ! pressure coordinate
624 verticalIndex = 1 + nint(obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)/deltaPressure)
625 case(3)
626 ! channel number
627 verticalIndex = nint(obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex))
628 if(obs_headElem_i(obsSpaceData,OBS_ITY,headerIndex) == codtyp_get_codtyp("AMSUA")) then
629 ! amsu-a
630 verticalIndex = verticalIndex - 27
631 else
632 ! ignore other types of TOVS for now
633 verticalIndex = -1
634 endif
635 case(4,5)
636 ! Integrated column or surface value - assign to first level
637 verticalIndex = 1
638 case default
639 ! unknown vertical coordinate
640 bodyElem_i = obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex)
641 write(*,*) 'osd_getIndices: Unknown VCO! ', bodyElem_i
642 verticalIndex = -1
643 end select
644
645 end subroutine osd_getIndices
646
647 !--------------------------------------------------------------------------
648 ! osd_setup
649 !--------------------------------------------------------------------------
650 subroutine osd_setup(nmlExists)
651 !
652 implicit none
653
654 ! Arguments:
655 logical, intent(out) :: nmlExists
656
657 ! Locals:
658 integer :: nulnam,ierr,fnom,fclos
659 integer :: familyIndex, elementIndex
660
661 namelist /namosd/nrandseed,deltaLat,deltaLon,deltaPressure,deltaHeight, &
662 numFamily,familyList,numElement,elementList,lrandom, &
663 diagn_save,diagn_all,diagn_num,diagn_stnid,diagn_varno,diagn_unilev, &
664 diagn_nset,diagn_pressmin
665
666 ! set default values for namelist variables
667 nrandseed = 999
668 lrandom=.true.
669 deltaLat = 10.0d0
670 deltaLon = 10.0d0
671 deltaPressure = 10000.0d0
672 deltaHeight = 5000.0d0
673
674 !numFamily = ofl_numFamily
675 !familyList(:) = ofl_familyList(:)
676 numFamily = MPC_missingValue_INT
677 familyList(:) = ' '
678
679 numElement = MPC_missingValue_INT
680 elementList(:) = -1
681
682 diagn_save(:)=.false.
683 diagn_all(:)=.true.
684 diagn_pressmin(:)=10. ! 0.1 hPa
685 diagn_nset(:)=2
686 diagn_num(:)=0
687 diagn_stnid(:,:)='*********'
688 diagn_varno(:,:)=0
689 diagn_unilev(:,:)=.false.
690
691 nulnam = 0
692 ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
693 read(nulnam,nml=namosd,iostat=ierr)
694 if(ierr /= 0) then
695 write(*,*) 'osd_setup: No valid namelist NAMOSD found, skipping some diagnostics'
696 nmlExists = .false.
697 ierr = fclos(nulnam)
698 return
699 else
700 nmlExists = .true.
701 endif
702 if(mmpi_myid == 0) write(*,nml=namosd)
703 ierr = fclos(nulnam)
704 if (numFamily /= MPC_missingValue_INT) then
705 call utl_abort('osd_setup: check NAMOSD namelist section: numFamily should be removed')
706 end if
707 numFamily = 0
708 do familyIndex = 1, ofl_numFamily
709 if (familyList(familyIndex) == ' ') exit
710 numFamily = numFamily + 1
711 end do
712 if (numElement /= MPC_missingValue_INT) then
713 call utl_abort('osd_setup: check NAMOSD namelist section: numElement should be removed')
714 end if
715 numElement = 0
716 do elementIndex = 1, ofl_numFamily
717 if (elementList(elementIndex) == -1) exit
718 numElement = numElement + 1
719 end do
720 do familyIndex = 1, numFamily
721 if ( .not. ofl_isFamilyTypeInList(familyList(familyIndex)) ) &
722 call utl_abort('osd_setup: Specified family '//familyList(familyIndex)//' was not recognized')
723 obsspace_diagn_filename(familyIndex) ='obsspace_diag_'//familyList(familyIndex)//'_'
724 if (diagn_num(familyIndex) > max_cfg_size) call utl_abort('osd_setup: Number exceeds allowed size of max_cfg_size')
725 end do
726
727 end subroutine osd_setup
728
729 !--------------------------------------------------------------------------
730 ! osd_update_obsfile
731 !--------------------------------------------------------------------------
732 subroutine osd_update_obsfile( obsSpaceData )
733 !
734 !:Purpose: Update of obs file(s) for content other
735 ! than OBS,OMA,OMP,OER,FGE,MRK in obsSpaceData
736 ! Content can be augmented as needed.
737 !
738 implicit none
739
740 ! Arguments:
741 type (struct_obs), intent(inout) :: obsSpaceData
742
743 ! If needed, add effective temperature values in CH family obs file
744 ! for total column measurements
745
746 if (obs_famExist(obsSpaceData,'CH')) call oopc_addEfftempObsfile()
747
748 end subroutine osd_update_obsfile
749
750 !-----------------------------------------------------------------------------------------
751 !------------------- Observation-space diagnostic functions and routines -----------------
752
753 !--------------------------------------------------------------------------
754 ! osd_obsPostProc
755 !--------------------------------------------------------------------------
756 subroutine osd_obsPostProc( obsSpaceData, columnTrlOnAnlIncLev, deltaLat, deltaLon, deltaPressure, anlm_mode )
757 !
758 !:Purpose: Interface for observation-space post-processing procedures.
759 !
760 !:Arguments:
761 ! :obsSpaceData: Obs space data structure
762 ! :obsfam: Target obs family (e.g. CH)
763 ! :codtyplist: Code type list asscoiated to obsfam.
764 ! :columnTrlOnAnlIncLev: Columns from analysis vertical coordinate in obs space (at obs location)
765 ! :date: YYYYMMDDHH
766 ! :deltaLat: Size of latitude bins for diagnostics (degrees)
767 ! :deltaLon: Size of longitude bins for diagnostics (degrees)
768 ! :deltaPressure: Size of vertical bins for diagnostics (Pascal)
769 ! :anlm_mode: Logical indicating if OmA (and Jo) diagnostics to be generated.
770 !
771 implicit none
772
773 ! Arguments:
774 type(struct_obs), intent(inout) :: obsSpaceData
775 type(struct_columnData), intent(in) :: columnTrlOnAnlIncLev
776 real(8), intent(in) :: deltaLat
777 real(8), intent(in) :: deltaLon
778 real(8), intent(in) :: deltaPressure
779 logical, intent(in) :: anlm_mode
780
781 ! Locals:
782 integer, allocatable :: codtyplist(:)
783 integer :: jelm,ifam
784
785 if (mmpi_myid == 0) then
786 write(*,*)
787 write(*,*) "Enter osd_obsPostProc: Observation-space post-processing tasks for chemical constituents"
788 write(*,*)
789 end if
790
791 ! Generate and output cost function, OmP, and OmA diagnostics
792
793 if (obs_famExist(obsSpaceData,'CH')) then
794
795 ifam=0
796 do jelm=1,numFamily
797 if (familyList(jelm) == 'CH') then
798 ifam=jelm
799 exit
800 end if
801 end do
802 if (ifam == 0) then
803
804 write(*,*) 'osd_obsPostProc: Warning - No post-processing requested for CH family.'
805
806 else
807
808 ! Initialize oss_comboIDlist and add (stnid,varno) pairs from the namelist
809 ! Sets list of identifiers for observations to be processed in osd_obsDiagnostics within the CH family
810
811 call oss_comboIdlist(initialize_opt=.true., nset_opt=diagn_nset(ifam), all_combos_opt=diagn_all(ifam))
812 do jelm=1,diagn_num(ifam)
813 call oss_comboIdlist(stnid_add_opt=diagn_stnid(ifam,jelm), varno_add_opt=diagn_varno(ifam,jelm), unilev_add_opt=diagn_unilev(ifam,jelm))
814 end do
815
816 ! Diagnostics for retrievd chemical constituents (CH family)
817
818 allocate(codtyplist(2))
819
820 codtyplist(1)=codtyp_get_codtyp('CHEMREMOTE')
821 codtyplist(2)=codtyp_get_codtyp('CHEMINSITU')
822
823 call osd_obsDiagnostics(obsSpaceData,columnTrlOnAnlIncLev,'CH',codtyplist,trim(obsspace_diagn_filename(ifam)), &
824 diagn_save(ifam),deltaLat,deltaLon,deltaPressure,diagn_pressmin(ifam),anlm_mode)
825
826 deallocate(codtyplist)
827
828 end if
829
830 end if
831
832 ! Generate and output cost function, OmP, and OmA diagnostics for
833 ! channels/instruments of the TO family (when processed with accompanying
834 ! CH obs).
835
836 ! call osd_TO_obsDiagnostics(obsSpaceData,columnTrlOnAnlIncLev,date,deltaLat,deltaLon,deltaPressure,anlm_mode)
837
838 ! Apply any required obs file update
839
840 call osd_update_obsfile(obsSpaceData)
841
842 if (mmpi_myid == 0) then
843 write(*,*)
844 write(*,*) "Exit osd_obsPostProc"
845 write(*,*)
846 end if
847
848 end subroutine osd_obsPostProc
849
850 !--------------------------------------------------------------------------
851 ! osd_obsDiagnostics
852 !--------------------------------------------------------------------------
853 subroutine osd_obsDiagnostics( obsSpaceData, columnTrlOnAnlIncLev, obsfam, codtyplist, filename, save_diagn, &
854 deltaLat, deltaLon, deltaPressure, pressmin, anlm_mode )
855 !
856 !:Purpose: Calculates and prints observation-space diagnostics for chemical constituents
857 !
858 !:Arguments:
859 ! :obsSpaceData: Obs space data structure
860 ! :columnTrlOnAnlIncLev: Columns from analysis vertical coordinate in obs space (at obs location)
861 ! :obsfam: Obs family (e.g. 'CH'
862 ! :codtypelist: Code type list
863 ! :filename: Output file name
864 ! :save_diagn: Logical indicating gridded diagnostics are to be save
865 ! :date: YYYYMMDDHH
866 ! :deltaLat: Size of latitude bins for diagnostics (degrees)
867 ! :deltaLon: Size of longitude bins for diagnostics (degrees)
868 ! :deltaPressure: Size of vertical bins for diagnostics (Pascal)
869 ! :pressmin: bottom of top layer for diagnostics (in Pa).
870 ! :anlm_mode: Logical indicating if OmA diagnostics are to be generated.
871 !
872 !:Output: Content of ascii file with obs space diagnostics
873 !
874 !:Comments:
875 !
876 ! - Although Jo_analysis is already calculated in OBS_JOBS obsSpaceData and can be passed
877 ! to osd_obsspace_diagn_add, it is recalculated in osd_obsspace_diagn_add since OBS_JOBS
878 ! will be set to zero for diagnostic-only observations.
879 !
880 implicit none
881
882 ! Arguments:
883 type(struct_obs) , intent(inout) :: obsSpaceData
884 type(struct_columnData), intent(in) :: columnTrlOnAnlIncLev
885 character(len=*) , intent(in) :: obsfam
886 character(len=*) , intent(in) :: filename
887 integer, intent(in) :: codtyplist(:)
888 real(8), intent(in) :: deltaLat
889 real(8), intent(in) :: deltaLon
890 real(8), intent(in) :: deltaPressure
891 real(8), intent(in) :: pressmin
892 logical, intent(in) :: anlm_mode
893 logical, intent(in) :: save_diagn
894
895 ! Locals:
896 type(struct_osd_diagn) :: obs_diagn
897 integer :: headerIndex,bodyIndex,vco,nlev_obs,ilev_obs,nlev_mod,ilev_mod
898 integer, parameter :: nmax=100
899 integer :: varno,varno_elemID(nmax)
900 integer :: elemID,num_elemID,nset,iass
901 character(len=9) :: stnid_elemID(nmax)
902 logical :: unilev_elemID(nmax),unilevel,diagn_only,assim_obs,status_hpht
903 character(len=256) :: label
904 real(8) :: lat,lon
905 character(len=12) :: stnid
906 real(8), allocatable :: lev(:), omp(:), oma(:), obs(:)
907 real(8), allocatable :: pres_mod(:), sigma_obs(:), sqrtHPHT(:)
908 logical, allocatable :: success(:)
909 integer, allocatable :: status(:)
910 real(8), pointer :: height_mod(:)
911
912 ! Get combination lists to group diagnostics by
913 call oss_get_comboIdlist(obsSpaceData,stnid_elemID,varno_elemID,unilev_elemID,num_elemID,nset)
914
915 if (num_elemID == 0) return
916
917 if (mmpi_myid == 0) then
918 write(*,*)
919 write(*,*) "osd_obsDiagnostics: Observation-space diagnostics for chemical constituents"
920 write(*,*)
921 end if
922
923 ! Read forecast error std. dev. at obs locations from obs files (saved in OBS_HPHT in obsSpaceData)
924 !status_hpht=.false.
925 if (anlm_mode) call osd_ReadSqrtHPHT(obsSpaceData,obsfam,codtyplist,status_hpht)
926
927 ! Allocate memory for diagnostic arrays in obs_diagn
928 call osd_obsspace_diagn_alloc(obs_diagn,deltaLat,deltaLon,deltaPressure,pressmin)
929
930 ! Loop over all pairs in *_elemID lists
931
932 do elemID=1,num_elemID
933
934 ! Initialize the diagnostic arrays
935 call osd_obsspace_diagn_init(obs_diagn)
936
937 call obs_set_current_header_list(obsSpaceData,obsfam)
938 HEADER: do
939
940 headerIndex = obs_getHeaderIndex(obsSpaceData)
941 if (headerIndex < 0) exit HEADER
942
943 ! Body info that we only need for first point in the profile
944 bodyIndex = obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
945 vco = obs_bodyElem_i(obsSpaceData,OBS_VCO,bodyIndex)
946
947 if (vco /= 1 .and. vco /= 2 .and. vco /= 4 .and. vco /= 5) then
948 ! Vertical coordinate not handled
949 write(*,*) 'osd_obsDiagnostics: Currently unaccounted VCO = ',vco
950 cycle HEADER
951 end if
952
953 varno = obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
954 nlev_obs = obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex)
955 stnid = obs_elem_c(obsSpaceData,'STID',headerIndex)
956
957 ! Identify max number of profile points in the profile (exclude BUFR_SCALE_EXPONENT elements)
958 call obs_set_current_body_list(obsSpaceData,headerIndex)
959 do
960 bodyIndex = obs_getBodyIndex(obsSpaceData)
961 if (bodyIndex < 0) exit
962 if (obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex) == BUFR_SCALE_EXPONENT) then
963 nlev_obs = nlev_obs-1
964 else
965 varno=obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
966 end if
967 end do
968
969 ! Determine if this observation should be added to this group (as specified by nset)
970 if (.not.utl_stnid_equal(stnid_elemID(elemID),stnid)) cycle HEADER
971 if (nset >= 2.and.varno /= varno_elemID(elemID)) cycle HEADER
972 if (nset >= 3.and..not.(( nlev_obs == 1 .and. vco == 4 ).eqv.unilev_elemID(elemID))) cycle HEADER
973
974 ! Accumulate for this combo
975
976 lat = obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex)*MPC_DEGREES_PER_RADIAN_R8
977 lon = obs_headElem_r(obsSpaceData,OBS_LON,headerIndex)*MPC_DEGREES_PER_RADIAN_R8
978
979 allocate(lev(nlev_obs), omp(nlev_obs), oma(nlev_obs), obs(nlev_obs))
980 allocate(sigma_obs(nlev_obs), success(nlev_obs), status(nlev_obs))
981 if (anlm_mode) allocate(sqrtHPHT(nlev_obs))
982
983 lev(:) = 0.0d0
984 omp(:) = 0.0d0
985 oma(:) = 0.0d0
986 obs(:) = 0.0d0
987 sigma_obs(:) = 0.0d0
988 if (anlm_mode) sqrtHPHT(:) = 0.0d0
989 status(:) = 0
990 assim_obs = .false.
991 ilev_obs = 0
992
993 call obs_set_current_body_list(obsSpaceData,headerIndex)
994 BODY: do
995
996 bodyIndex = obs_getBodyIndex(obsSpaceData)
997 if (bodyIndex < 0) exit BODY
998
999 if (obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex) /= varno) cycle BODY
1000
1001 ilev_obs = ilev_obs+1
1002
1003 iass = obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex)
1004
1005 ! Indicates if diagnostics are to be calculated but observation not assimilated
1006 diagn_only = oopc_diagnOnly(obsfam,stnid,varno,nlev_obs,obs_bodyElem_i(obsSpaceData,OBS_FLG,bodyIndex))
1007
1008 assim_obs = ((.not.diagn_only).and.anlm_mode) .or. assim_obs
1009
1010 if (iass == obs_assimilated) then
1011 status(ilev_obs) = 1
1012 else if (diagn_only) then
1013 status(ilev_obs) = 2
1014 end if
1015
1016 lev(ilev_obs) = obs_bodyElem_r(obsSpaceData,OBS_PPP,bodyIndex)
1017
1018 ! Include in the sum assimilated data and diagnostic only data
1019 if (status(ilev_obs) > 0) then
1020
1021 omp(ilev_obs) = obs_bodyElem_r(obsSpaceData,OBS_OMP,bodyIndex)
1022 obs(ilev_obs) = obs_bodyElem_r(obsSpaceData,OBS_VAR,bodyIndex)
1023 sigma_obs(ilev_obs) = obs_bodyElem_r(obsSpaceData,OBS_OER,bodyIndex)
1024 if (anlm_mode) then
1025 oma(ilev_obs) = obs_bodyElem_r(obsSpaceData,OBS_OMA,bodyIndex)
1026 if (status_hpht) then
1027 sqrtHPHT(ilev_obs) = obs_bodyElem_r(obsSpaceData,OBS_HPHT,bodyIndex)
1028 if (sqrtHPHT(ilev_obs) < 1.D-30) then
1029 write(*,*) 'osd_obsDiagnostics: WARNING. sqrtHPHT not found for all obs'
1030 write(*,*) 'Will not be used in Desroziers-based diagnostics.'
1031 status_hpht=.false.
1032 end if
1033 end if
1034 end if
1035
1036 end if
1037
1038 end do BODY
1039
1040 ! Convert to pressure if needed and identify unilevel observations
1041 unilevel = .false.
1042 select case(vco)
1043 case(1)
1044 ! Height coordinate
1045
1046 nlev_mod = col_getNumLev(columnTrlOnAnlIncLev,'TH') ! number of model levels
1047 height_mod => col_getColumn(columnTrlOnAnlIncLev,headerIndex,'Z_T') ! geopotential
1048
1049 allocate(pres_mod(nlev_mod))
1050
1051 do ilev_mod=1,nlev_mod
1052 pres_mod(ilev_mod) = col_getPressure(columnTrlOnAnlIncLev,ilev_mod,headerIndex,'TH') ! model pressure
1053 end do
1054
1055 ! Convert altidudes to pressure
1056 success = status > 0
1057 lev = phf_convert_z_to_pressure(lev,height_mod,pres_mod,nlev_obs,nlev_mod,lat/MPC_DEGREES_PER_RADIAN_R8,success)
1058
1059 deallocate(pres_mod)
1060
1061 case(4,5)
1062 ! Uni-level observations
1063 unilevel = .true.
1064 end select
1065
1066 ! Add observation to diagnostic arrays
1067 if (anlm_mode) then
1068 if (status_hpht) then
1069 call osd_obsspace_diagn_add(obs_diagn, lat, lon, lev, &
1070 pressmin, omp, obs, sigma_obs, &
1071 nlev_obs, unilevel, assim_obs, status, &
1072 oma_opt=oma, sqrtHPHT_opt=sqrtHPHT)
1073 else
1074 call osd_obsspace_diagn_add(obs_diagn, lat, lon, lev, &
1075 pressmin, omp, obs, sigma_obs, &
1076 nlev_obs, unilevel, assim_obs, status,oma_opt=oma)
1077 end if
1078 else
1079 call osd_obsspace_diagn_add(obs_diagn, lat, lon, lev, &
1080 pressmin, omp, obs, sigma_obs, &
1081 nlev_obs, unilevel, assim_obs, status)
1082 end if
1083
1084 deallocate(lev,omp,oma,obs,sigma_obs,success,status)
1085 if (anlm_mode) deallocate(sqrtHPHT)
1086
1087 end do HEADER
1088
1089 ! Prepare output identification
1090 select case(nset)
1091 case(1)
1092 write(label,*) ">>> Statistics for STNID ",stnid_elemID(elemID)
1093 case(2)
1094 write(label,*) ">>> Statistics for STNID ",stnid_elemID(elemID)," and BUFR # ",varno_elemID(elemID)
1095 case(3)
1096 write(label,*) ">>> Statistics for STNID ",stnid_elemID(elemID)," and BUFR # ",varno_elemID(elemID)," and UNILEV = ",unilev_elemID(elemID)
1097 end select
1098
1099 ! Sum over different processors
1100 call osd_obsspace_diagn_MPIreduce(obs_diagn)
1101
1102 ! Output, and deallocate diagnostic arrays
1103 if (mmpi_myid == 0) call osd_obsspace_diagn_print(obs_diagn,filename, save_diagn, &
1104 'stats', pressmin, status_hpht, label_opt=label, openfile_opt=.true.)
1105
1106 end do
1107
1108 ! Output diagnostics summary (over all CH observations)
1109 if (mmpi_myid == 0) call osd_obsspace_diagn_print(obs_diagn,filename, save_diagn, &
1110 'summary', pressmin, status_hpht, openfile_opt=.true.)
1111
1112 ! Deallocate arrays in obs_diagn
1113 call osd_obsspace_diagn_dealloc(obs_diagn)
1114
1115 if (mmpi_myid == 0) then
1116 write(*,*)
1117 write(*,*) "End osd_obsDiagnostics"
1118 write(*,*)
1119 end if
1120
1121 end subroutine osd_obsDiagnostics
1122
1123 !--------------------------------------------------------------------------
1124 ! osd_ReadSqrtHPHT
1125 !--------------------------------------------------------------------------
1126 subroutine osd_ReadSqrtHPHT( obsSpaceData, obsfam, codtyplist, status_hpht )
1127 !
1128 !:Purpose: Read background error std. dev. at obs locations from the obs files and store
1129 ! under OBS_HPHT in obsSpaceData
1130 !
1131 !:Arguments:
1132 ! :obsSpaceData: Observation space data
1133 ! :obsfam: Obs family. e.g. 'CH'
1134 ! :codtyplist: Code type list associated to obsfam
1135 ! :status_hpht: logical indicating if successfully retrieved sqrtHPHT from obs file
1136 !
1137 implicit none
1138
1139 ! Arguments:
1140 logical, intent(out) :: status_hpht
1141 type(struct_obs), intent(inout) :: obsSpaceData
1142 character(len=*), intent(in) :: obsfam
1143 integer, intent(in) :: codtyplist(:)
1144
1145 ! Locals:
1146 integer :: bodyIndex,headerIndex,rln,nlv,kk
1147 integer :: stat,varno,icodtyp
1148 integer, parameter :: max_nlev=500
1149 integer, parameter :: ndim=1
1150 real(8) :: array(max_nlev)
1151 character(len=12), parameter :: stnid='************'
1152 type(struct_oss_obsdata) :: SqrtHPHT_struct
1153
1154 write(*,*) 'osd_ReadSqrtHPHT: STARTING'
1155
1156 ! Retrieve data from FGE blocks, i.e. sqrt(HPHT)
1157 ! (with ndim=1, bkstp=15 and block_type='DATA')
1158
1159 status_hpht=.true.
1160 SqrtHPHT_struct = obsf_obsSub_read(obsfam,stnid,-1,max_nlev,ndim,bkstp_opt=15,block_opt='DATA', &
1161 match_nlev_opt=.false.,codtyp_opt=codtyplist)
1162
1163 if (SqrtHPHT_struct%nrep == 0) then
1164 write(*,*) 'osd_ReadSqrtHPHT: WARNING. sqrtHPHT not found in obs file(s).'
1165 write(*,*) 'Will not be used in Desroziers-based diagnostics.'
1166 status_hpht=.false.
1167 call oss_obsdata_dealloc(SqrtHPHT_struct)
1168 return
1169 end if
1170
1171 ! Save in OBS_HPHT of obsSpaceData
1172
1173 call obs_set_current_header_list(obsSpaceData,obsfam)
1174 HEADER: do
1175
1176 headerIndex = obs_getHeaderIndex(obsSpaceData)
1177 if (headerIndex < 0) exit HEADER
1178
1179 icodtyp = obs_headElem_i(obsSpaceData,OBS_ITY,headerIndex)
1180 if (all(icodtyp /= codtyplist)) cycle HEADER
1181
1182 ! Search for corresponding HPHT profile/element
1183
1184 array(:)=0.0D0
1185 array=oss_obsdata_get_data1d(SqrtHPHT_struct, &
1186 obs_headElem_r(obsSpaceData,OBS_LON,headerIndex),obs_headElem_r(obsSpaceData,OBS_LAT,headerIndex), &
1187 obs_headElem_i(obsSpaceData,OBS_DAT,headerIndex),obs_headElem_i(obsSpaceData,OBS_ETM,headerIndex), &
1188 obs_elem_c(obsSpaceData,'STID',headerIndex),stat_opt=stat)
1189
1190 if (stat == 0) then
1191
1192 ! Store OBS_HPHT profile
1193
1194 rln=obs_headElem_i(obsSpaceData,OBS_RLN,headerIndex)
1195 nlv=obs_headElem_i(obsSpaceData,OBS_NLV,headerIndex)
1196 kk=0
1197 do bodyIndex = rln, nlv + rln -1
1198 varno=obs_bodyElem_i(obsSpaceData,OBS_VNM,bodyIndex)
1199 if ( varno > 10000 .and. varno < 16000 ) then
1200 kk=kk+1
1201 call obs_bodySet_r(obsSpaceData,OBS_HPHT,bodyIndex,array(kk))
1202 else
1203 call obs_bodySet_r(obsSpaceData,OBS_HPHT,bodyIndex,0.0D0)
1204 end if
1205 end do
1206 end if
1207
1208 enddo HEADER
1209
1210 call oss_obsdata_dealloc(SqrtHPHT_struct)
1211
1212 write(*,*) 'osd_ReadSqrtHPHT: DONE'
1213
1214 end subroutine osd_ReadSqrtHPHT
1215
1216 !--------------------------------------------------------------------------
1217 ! osd_obsspace_diagn_alloc
1218 !--------------------------------------------------------------------------
1219 subroutine osd_obsspace_diagn_alloc( obs_diagn, deltaLat, deltaLon, deltaPressure, pressmin )
1220 !
1221 !:Purpose: Allocates diagnostic arrays in obs_diagn.
1222 !
1223 !:Arguments:
1224 ! :deltaLat: latitude bin size in degrees
1225 ! :deltaLon: longitutde in degrees
1226 ! :deltaPressure: pressures bin size in Pa (approximate)
1227 ! :pressmin: bottom of top layer for diagnostics (in Pa).
1228 !
1229 implicit none
1230
1231 ! Arguments:
1232 type(struct_osd_diagn), intent(inout) :: obs_diagn
1233 real(8) , intent(in) :: deltaLat
1234 real(8) , intent(in) :: deltaLon
1235 real(8) , intent(in) :: deltaPressure
1236 real(8) , intent(in) :: pressmin
1237
1238 ! Locals:
1239 integer :: nlev,nlat,nlon,nbin,nstat
1240
1241 obs_diagn%deltaLat = deltaLat
1242 obs_diagn%deltaLon = deltaLon
1243 obs_diagn%deltaLogPressure = deltaPressure/1.0d5 ! set constant delta ln(P) bin
1244
1245 nlat = floor(180.0d0/deltaLat)
1246 nlon = floor(360.0d0/deltaLon)
1247
1248 ! Add a last unequal size bin if remainder is larger than one degree
1249 if (180.0d0-nlat*deltaLat > 1.) nlat = nlat+1
1250 if (360.0d0-nlon*deltaLon > 1.) nlon = nlon+1
1251
1252 ! Set number of levels for a pressure coordinate in hPa to cover the range
1253 ! of 0.01*pressmin (in hPa) to 1000 hPa for layers 2 to nlev-1.
1254 ! Layer 1 covers all pressure levels <= 0.01*pressmin hPa = top of layer 2,
1255 ! The bottom of layer nlev-1 is provided by press_bins(nlev).
1256 nlev = 2 + nint(log(1.0d5/pressmin)/obs_diagn%deltaLogPressure) ! last index is for unilevel observations
1257 nbin = nlev*nlat*nlon
1258
1259 ! Two different statistics held, stat=1 for RMS and stat=2 for mean
1260 nstat = 2
1261
1262 obs_diagn%nlat = nlat
1263 obs_diagn%nlon = nlon
1264 obs_diagn%nlev = nlev
1265 obs_diagn%nbin = nbin
1266 obs_diagn%nstat = nstat
1267
1268 if (.not. allocated(obs_diagn%OmP_stats)) allocate(obs_diagn%OmP_stats(nlat,nlon,nlev,nstat))
1269 if (.not. allocated(obs_diagn%OmA_stats)) allocate(obs_diagn%OmA_stats(nlat,nlon,nlev,nstat))
1270 if (.not. allocated(obs_diagn%obs_stats)) allocate(obs_diagn%obs_stats(nlat,nlon,nlev,nstat))
1271 if (.not. allocated(obs_diagn%Jo_stats)) allocate(obs_diagn%Jo_stats(nlat,nlon,nlev,nstat*2+1))
1272 if (.not. allocated(obs_diagn%Jpa_stats)) allocate(obs_diagn%Jpa_stats(nlat,nlon,nlev))
1273 if (.not. allocated(obs_diagn%counts)) allocate(obs_diagn%counts(nlat,nlon,nlev))
1274 if (.not. allocated(obs_diagn%nstatus)) allocate(obs_diagn%nstatus(nlat,nlon,nlev,0:2))
1275 if (.not. allocated(obs_diagn%diagR_stats)) allocate(obs_diagn%diagR_stats(nlat,nlon,nlev,3))
1276 if (.not. allocated(obs_diagn%diagHPHT_stats)) allocate(obs_diagn%diagHPHT_stats(nlat,nlon,nlev,3))
1277
1278 end subroutine osd_obsspace_diagn_alloc
1279
1280 !--------------------------------------------------------------------------
1281 ! osd_obsspace_diagn_init
1282 !--------------------------------------------------------------------------
1283 subroutine osd_obsspace_diagn_init(obs_diagn)
1284 !
1285 !:Purpose: Initializes diagnostic arrays in obs_diagn.
1286 !
1287 implicit none
1288
1289 ! Arguments:
1290 type(struct_osd_diagn), intent(inout) :: obs_diagn
1291
1292 obs_diagn%OmP_stats(:,:,:,:) = 0.0d0
1293 obs_diagn%OmA_stats(:,:,:,:) = 0.0d0
1294 obs_diagn%obs_stats(:,:,:,:) = 0.0d0
1295 obs_diagn%Jo_stats(:,:,:,:) = 0.0d0
1296 obs_diagn%Jpa_stats(:,:,:) = 0.0d0
1297 obs_diagn%counts(:,:,:) = 0
1298 obs_diagn%nstatus(:,:,:,:) = 0
1299 obs_diagn%diagR_stats(:,:,:,:) = 0.0d0
1300 obs_diagn%diagHPHT_stats(:,:,:,:) = 0.0d0
1301
1302 obs_diagn%allow_print_summary = .false.
1303 obs_diagn%assim_mode = .false.
1304
1305 end subroutine osd_obsspace_diagn_init
1306
1307 !--------------------------------------------------------------------------
1308 ! osd_obsspace_diagn_dealloc
1309 !--------------------------------------------------------------------------
1310 subroutine osd_obsspace_diagn_dealloc(obs_diagn)
1311 !
1312 !:Purpose: Deallocates diagnostic arrays in obs_diagn.
1313 !
1314 implicit none
1315
1316 ! Arguments:
1317 type(struct_osd_diagn), intent(inout) :: obs_diagn
1318
1319 deallocate(obs_diagn%OmP_stats,obs_diagn%OmA_stats,obs_diagn%obs_stats)
1320 deallocate(obs_diagn%Jo_stats,obs_diagn%Jpa_stats,obs_diagn%counts,obs_diagn%nstatus)
1321 deallocate(obs_diagn%diagR_stats,obs_diagn%diagHPHT_stats)
1322
1323 end subroutine osd_obsspace_diagn_dealloc
1324
1325 !--------------------------------------------------------------------------
1326 ! osd_obsspace_diagn_add
1327 !--------------------------------------------------------------------------
1328 subroutine osd_obsspace_diagn_add( obs_diagn, lat, lon, pressure, pressmin, OmP, obs, sigma_obs, nlev_obs, &
1329 unilevel, assim_obs, status, OmA_opt, sqrtHPHT_opt)
1330 !
1331 !:Purpose: Adds an observation to the diagnostic arrays in obs_diagn.
1332 !
1333 !:Arguments:
1334 ! :lat: latitude in degrees
1335 ! :lon: longitutde in degrees
1336 ! :pressure: pressures of the profile (Pa)
1337 ! :pressmin: bottom of top layer for diagnostics (in Pa).
1338 ! :OmP: obs - background
1339 ! :OmA_opt: obs - analysis
1340 ! :obs: observations
1341 ! :Jo: cost function
1342 ! :sigma_obs: observation error standard deviation
1343 ! :sqrtHPHT_opt: forecast error standard deviation in obs space
1344 ! :assim_obs: indicates if the profile belongs to an assimilated data set
1345 ! :status: indicates status of the observations, with values denoting:
1346 !
1347 ! - 0 - observation has been rejected and not included in diagnostics
1348 ! - 1 - observation has been assimilated
1349 ! - 2 - observation has been used for diagnostics only (not assimilated)
1350 ! only observations with status=1,2 will be added to the statistic arrays
1351 ! :nlev_obs: number of observations in the profile
1352 ! :unilevel: if the observation does not have a defined height coordinate
1353 !
1354 implicit none
1355
1356 ! Arguments:
1357 type(struct_osd_diagn), intent(inout) :: obs_diagn
1358 real(8) , intent(in) :: lat
1359 real(8) , intent(in) :: lon
1360 integer , intent(in) :: nlev_obs
1361 integer , intent(in) :: status(nlev_obs)
1362 real(8) , intent(in) :: pressure(nlev_obs)
1363 real(8) , intent(in) :: OmP(nlev_obs)
1364 real(8) , intent(in) :: obs(nlev_obs)
1365 real(8) , intent(in) :: sigma_obs(nlev_obs)
1366 real(8) , intent(in) :: pressmin
1367 logical , intent(in) :: unilevel
1368 logical , intent(in) :: assim_obs
1369 real(8) , optional, intent(in) :: OmA_opt(nlev_obs)
1370 real(8) , optional, intent(in) :: sqrtHPHT_opt(nlev_obs)
1371
1372 ! Locals:
1373 integer :: ilat,ilon,ilev,ilev_obs
1374
1375 if (assim_obs) obs_diagn%assim_mode = .true.
1376
1377 ! Put in first/list bin if lat,lon,level lower/higher than diagnostic range
1378
1379 ilat = max(min(1 + floor((90.0d0+lat)/obs_diagn%deltaLat), obs_diagn%nlat), 1)
1380 ilon = max(min(1 + floor(lon/obs_diagn%deltaLon), obs_diagn%nlon), 1)
1381
1382 LEVELS: do ilev_obs=1,nlev_obs
1383
1384 if (unilevel) then
1385 ilev = obs_diagn%nlev ! put unilevel data in last level index
1386 else
1387 ilev = max(min(2 + floor(log(pressure(ilev_obs)/pressmin)/obs_diagn%deltaLogPressure), obs_diagn%nlev-1), 1)
1388 end if
1389
1390 obs_diagn%nstatus(ilat,ilon,ilev,status(ilev_obs)) = obs_diagn%nstatus(ilat,ilon,ilev,status(ilev_obs)) + 1
1391
1392 if (status(ilev_obs) == 0) cycle LEVELS ! skip adding of stats if the observation was rejected
1393
1394 obs_diagn%counts(ilat,ilon,ilev) = obs_diagn%counts(ilat,ilon,ilev) + 1
1395
1396 obs_diagn%OmP_stats(ilat,ilon,ilev,1) = obs_diagn%OmP_stats(ilat,ilon,ilev,1) + OmP(ilev_obs)**2
1397 obs_diagn%OmP_stats(ilat,ilon,ilev,2) = obs_diagn%OmP_stats(ilat,ilon,ilev,2) + OmP(ilev_obs)
1398
1399 obs_diagn%obs_stats(ilat,ilon,ilev,1) = obs_diagn%obs_stats(ilat,ilon,ilev,1) + obs(ilev_obs)**2
1400 obs_diagn%obs_stats(ilat,ilon,ilev,2) = obs_diagn%obs_stats(ilat,ilon,ilev,2) + obs(ilev_obs)
1401
1402 obs_diagn%Jo_stats(ilat,ilon,ilev,2) = obs_diagn%Jo_stats(ilat,ilon,ilev,2) + 0.5 * OmP(ilev_obs)**2 / sigma_obs(ilev_obs)**2
1403 if (status(ilev_obs) == 1) obs_diagn%Jo_stats(ilat,ilon,ilev,4) = obs_diagn%Jo_stats(ilat,ilon,ilev,4) + 0.5 * OmP(ilev_obs)**2 / sigma_obs(ilev_obs)**2
1404
1405 if (present(OmA_opt)) then
1406
1407 obs_diagn%OmA_stats(ilat,ilon,ilev,1) = obs_diagn%OmA_stats(ilat,ilon,ilev,1) + OmA_opt(ilev_obs)**2
1408 obs_diagn%OmA_stats(ilat,ilon,ilev,2) = obs_diagn%OmA_stats(ilat,ilon,ilev,2) + OmA_opt(ilev_obs)
1409 obs_diagn%Jo_stats(ilat,ilon,ilev,1) = obs_diagn%Jo_stats(ilat,ilon,ilev,1) + 0.5 * OmA_opt(ilev_obs)**2 / sigma_obs(ilev_obs)**2
1410
1411 if (status(ilev_obs) == 1) then
1412
1413 obs_diagn%diagR_stats(ilat,ilon,ilev,1) = obs_diagn%diagR_stats(ilat,ilon,ilev,1) &
1414 + OmP(ilev_obs)*OmA_opt(ilev_obs)/sigma_obs(ilev_obs)**2
1415 obs_diagn%diagR_stats(ilat,ilon,ilev,2) = obs_diagn%diagR_stats(ilat,ilon,ilev,2) &
1416 + OmP(ilev_obs)/sigma_obs(ilev_obs)
1417 obs_diagn%diagR_stats(ilat,ilon,ilev,3) = obs_diagn%diagR_stats(ilat,ilon,ilev,3) &
1418 + OmA_opt(ilev_obs)/sigma_obs(ilev_obs)
1419
1420 if (present(sqrtHPHT_opt)) then
1421 if (sqrtHPHT_opt(ilev_obs) > 0.0) then
1422 obs_diagn%diagHPHT_stats(ilat,ilon,ilev,1) = obs_diagn%diagHPHT_stats(ilat,ilon,ilev,1) &
1423 + OmP(ilev_obs)*(OmP(ilev_obs)-OmA_opt(ilev_obs))/sqrtHPHT_opt(ilev_obs)**2
1424 obs_diagn%diagHPHT_stats(ilat,ilon,ilev,2) = obs_diagn%diagHPHT_stats(ilat,ilon,ilev,2) &
1425 + OmP(ilev_obs)/sqrtHPHT_opt(ilev_obs)
1426 obs_diagn%diagHPHT_stats(ilat,ilon,ilev,3) = obs_diagn%diagHPHT_stats(ilat,ilon,ilev,3) &
1427 + (OmP(ilev_obs)-OmA_opt(ilev_obs))/sqrtHPHT_opt(ilev_obs)
1428 ! Following two diagnostics do not account for spatial correlations (spatial correlations of HPHT)!
1429 ! As a consequence, Jb likely overestimated.
1430 obs_diagn%Jo_stats(ilat,ilon,ilev,5) = obs_diagn%Jo_stats(ilat,ilon,ilev,5) + 0.5 * (OmP(ilev_obs))**2 &
1431 / (sigma_obs(ilev_obs)**2 + sqrtHPHT_opt(ilev_obs)**2)
1432 obs_diagn%Jpa_stats(ilat,ilon,ilev) = obs_diagn%Jpa_stats(ilat,ilon,ilev) + 0.5 * (OmA_opt(ilev_obs)-OmP(ilev_obs))**2 &
1433 / sqrtHPHT_opt(ilev_obs)**2
1434 end if
1435 else
1436
1437 ! No division by sqrtHPHT
1438
1439 obs_diagn%diagHPHT_stats(ilat,ilon,ilev,1) = obs_diagn%diagHPHT_stats(ilat,ilon,ilev,1) + OmP(ilev_obs)*(OmP(ilev_obs)-OmA_opt(ilev_obs))
1440 obs_diagn%diagHPHT_stats(ilat,ilon,ilev,2) = obs_diagn%diagHPHT_stats(ilat,ilon,ilev,2) + OmP(ilev_obs)
1441 obs_diagn%diagHPHT_stats(ilat,ilon,ilev,3) = obs_diagn%diagHPHT_stats(ilat,ilon,ilev,3) + OmP(ilev_obs)-OmA_opt(ilev_obs)
1442 end if
1443
1444 end if
1445
1446 end if
1447
1448 end do LEVELS
1449
1450 end subroutine osd_obsspace_diagn_add
1451
1452 !--------------------------------------------------------------------------
1453 ! osd_obsspace_diagn_MPIreduce
1454 !--------------------------------------------------------------------------
1455 subroutine osd_obsspace_diagn_MPIreduce(obs_diagn)
1456 !
1457 !:Purpose: Performs a MPI allreduce on diagnostic arrays in obs_diagn.
1458 !
1459 implicit none
1460
1461 ! Arguments:
1462 type(struct_osd_diagn), intent(inout) :: obs_diagn
1463
1464 ! Locals:
1465 real(8), allocatable :: OmP_global(:,:,:,:), OmA_global(:,:,:,:), obs_global(:,:,:,:), Jo_global(:,:,:,:)
1466 real(8), allocatable :: Jpa_global(:,:,:), diagR_global(:,:,:,:), diagHPHT_global(:,:,:,:)
1467 integer, allocatable :: counts_global(:,:,:),nstatus_global(:,:,:,:)
1468 logical :: assim_global
1469 integer :: nlat,nlon,nlev,nstat,nbin,ierr
1470
1471 nlat = obs_diagn%nlat
1472 nlon = obs_diagn%nlon
1473 nlev = obs_diagn%nlev
1474 nstat = obs_diagn%nstat
1475 nbin = obs_diagn%nbin
1476
1477 ! Allocate memory for mpi global arrays
1478 allocate(OmP_global(nlat,nlon,nlev,nstat))
1479 allocate(OmA_global(nlat,nlon,nlev,nstat))
1480 allocate(obs_global(nlat,nlon,nlev,nstat))
1481 allocate(Jo_global(nlat,nlon,nlev,nstat*2+1))
1482 allocate(Jpa_global(nlat,nlon,nlev))
1483 allocate(diagR_global(nlat,nlon,nlev,3))
1484 allocate(diagHPHT_global(nlat,nlon,nlev,3))
1485 allocate(counts_global(nlat,nlon,nlev))
1486 allocate(nstatus_global(nlat,nlon,nlev,0:2))
1487
1488 ! Reduce from all mpi processes
1489 call rpn_comm_allreduce(obs_diagn%OmP_stats,OmP_global,nbin*nstat,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1490 call rpn_comm_allreduce(obs_diagn%OmA_stats,OmA_global,nbin*nstat,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1491 call rpn_comm_allreduce(obs_diagn%obs_stats,obs_global,nbin*nstat,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1492 call rpn_comm_allreduce(obs_diagn%Jo_stats,Jo_global,nbin*(nstat*2+1),"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1493 call rpn_comm_allreduce(obs_diagn%Jpa_stats,Jpa_global,nbin,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1494 call rpn_comm_allreduce(obs_diagn%diagR_stats,diagR_global,nbin*3,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1495 call rpn_comm_allreduce(obs_diagn%diagHPHT_stats,diagHPHT_global,nbin*3,"MPI_DOUBLE_PRECISION","MPI_SUM","GRID",ierr)
1496 call rpn_comm_allreduce(obs_diagn%counts,counts_global,nbin,"MPI_INTEGER","MPI_SUM","GRID",ierr)
1497 call rpn_comm_allreduce(obs_diagn%nstatus,nstatus_global,nbin*3,"MPI_INTEGER","MPI_SUM","GRID",ierr)
1498 call rpn_comm_allreduce(obs_diagn%assim_mode,assim_global,1,"MPI_LOGICAL","MPI_LOR","GRID",ierr)
1499
1500 ! save in struct_osd_diagn
1501 obs_diagn%OmP_stats = OmP_global
1502 obs_diagn%OmA_stats = OmA_global
1503 obs_diagn%obs_stats = obs_global
1504 obs_diagn%Jo_stats = Jo_global
1505 obs_diagn%Jpa_stats = Jpa_global
1506 obs_diagn%diagR_stats = diagR_global
1507 obs_diagn%diagHPHT_stats = diagHPHT_global
1508 obs_diagn%counts = counts_global
1509 obs_diagn%nstatus = nstatus_global
1510 obs_diagn%assim_mode = assim_global
1511
1512 deallocate(OmP_global,OmA_global,obs_global,Jo_global,Jpa_global,diagR_global,diagHPHT_global, &
1513 counts_global,nstatus_global)
1514
1515 end subroutine osd_obsspace_diagn_MPIreduce
1516
1517 !--------------------------------------------------------------------------
1518 ! osd_obsspace_diagn_print
1519 !--------------------------------------------------------------------------
1520 subroutine osd_obsspace_diagn_print(obs_diagn, filename, save_diagn, print_type, pressmin, status_hpht, label_opt, openfile_opt )
1521 !
1522 !:Purpose: Prints observation space diagnostics. If called with print_type = 'stats', the
1523 ! printed statistics will be added to the total diagnostic arrays.
1524 !
1525 !:Arguments:
1526 ! :filename: output file name
1527 ! :save_diagn: Logical indicating gridded diagnostics are to be save
1528 ! :print_type: Specifies which statistics to print, with possible values:
1529 !
1530 ! - 'stats' - prints statistics for the the arrays within obs_diagn
1531 ! - 'summary'- prints total statistics held in the saved variables
1532 ! within this subrouine
1533 ! :pressmin: min pressure level for output
1534 ! :label_opt: label to print (only relevant if print_type = 'stats')
1535 ! :openfile_opt: logical indicating if file filename is to be opened.
1536 ! :status_hpht: logical indicating if sqrtHPHT were available.
1537 !
1538 implicit none
1539
1540 ! Arguments:
1541 type(struct_osd_diagn) , intent(inout) :: obs_diagn
1542 character(len=*) , intent(in) :: print_type
1543 character(len=*) , intent(in) :: filename
1544 real(8) , intent(in) :: pressmin
1545 logical , intent(in) :: status_hpht
1546 logical , intent(in) :: save_diagn
1547 logical , optional, intent(in) :: openfile_opt
1548 character(len=256), optional, intent(in) :: label_opt
1549
1550 ! Locals:
1551 integer, external :: fnom, fclos
1552 real(8) :: Jo_a,Jo_b,Jpa_assim, Jo_a_assim,Jo_b_assim, Jo_p_assim
1553 real(8), save :: Jo_a_total=0.0d0, Jo_b_total=0.0d0, Jpa_total_assim=0.0d0
1554 real(8), save :: Jo_a_total_assim=0.0d0, Jo_b_total_assim=0.0d0, Jo_p_total_assim=0.0d0
1555 integer, save :: counts_total=0, counts_total_assim=0
1556 integer :: ierr,unit,icount,nlat,nlon,nlev,ilev,ilat,ilon,icount_assim
1557 integer, allocatable :: ncounts(:), ncounts_assim(:)
1558 real(8), allocatable :: press_bins(:)
1559 logical :: fileout_exist,multilevel,unilevel
1560
1561 select case(trim(print_type))
1562 case('stats','STATS')
1563
1564 ! Print observation-space statistics to listing file or to output file obspace_diag_filename.
1565
1566 ! Open and append to output file if requested
1567
1568 if (present(openfile_opt)) then
1569 if (openfile_opt) then
1570 inquire(file=filename, exist=fileout_exist)
1571 call utl_open_asciifile(filename,unit)
1572 else
1573 unit=6
1574 end if
1575 else
1576 unit=6
1577 end if
1578
1579 if (present(label_opt)) then
1580 write(unit,*)
1581 write(unit,*) trim(label_opt)
1582 end if
1583
1584 if (any(obs_diagn%counts > 0)) then
1585
1586 nlat = obs_diagn%nlat
1587 nlon = obs_diagn%nlon
1588 nlev = obs_diagn%nlev
1589
1590 allocate(ncounts(nlev), ncounts_assim(nlev), press_bins(nlev+1))
1591
1592 ! Pressure boundaries for each bin starting from a top layer with lower boundary of 0.01*pressmin (i=2) in hPa
1593 ! and extending to the surface.
1594
1595 press_bins(2:nlev-1) = (/ (0.01*pressmin*exp((ilev-2)*obs_diagn%deltaLogPressure), ilev=2,nlev-1) /)
1596 press_bins(1) = 0.0d0
1597 press_bins(nlev) = 1200.0d0 ! set to pressure larger than the largest expected surface pressure.
1598 press_bins(nlev+1) = 0.0d0 ! set to zero for unilevel
1599
1600 ! Total counts for each level
1601 ncounts = sum(sum(obs_diagn%counts,dim=1),dim=1)
1602 counts_total = counts_total + sum(ncounts)
1603 ncounts_assim = sum(sum(obs_diagn%nstatus(:,:,:,1),dim=1),dim=1)
1604 counts_total_assim = counts_total_assim + sum(ncounts_assim)
1605
1606 ! Indicates if any multilevel or unilevel observations exist
1607 multilevel = any(obs_diagn%nstatus(:,:,1:nlev-1,:) > 0)
1608 unilevel = any(obs_diagn%nstatus(:,:,nlev,:) > 0)
1609
1610 if (obs_diagn%assim_mode) then
1611 write(unit,*)
1612 write(unit,'(A)') " Elements for calc of obs and background error standard deviation scaling factors via"
1613 write(unit,'(A)') " the Desroziers approach can be found in the third and fourth blocks of statistics."
1614 write(unit,*)
1615 end if
1616
1617 write(unit,*)
1618 write(unit,'(A)') " Global statistics"
1619 write(unit,'(A)') " -----------------"
1620
1621 ! Multi-level data
1622
1623 if (multilevel) then
1624
1625 icount = sum(ncounts(1:nlev-1))
1626 icount_assim = sum(ncounts_assim(1:nlev-1))
1627
1628 if (icount > 0) then
1629 Jo_a = sum(obs_diagn%Jo_stats(:,:,1:nlev-1,1))
1630 Jo_b = sum(obs_diagn%Jo_stats(:,:,1:nlev-1,2))
1631 Jo_a_assim = sum(obs_diagn%Jo_stats(:,:,1:nlev-1,3))
1632 Jo_b_assim = sum(obs_diagn%Jo_stats(:,:,1:nlev-1,4))
1633 Jo_p_assim = sum(obs_diagn%Jo_stats(:,:,1:nlev-1,5))
1634 Jpa_assim = sum(obs_diagn%Jpa_stats(:,:,1:nlev-1))
1635 Jo_a_total = Jo_a_total + Jo_a
1636 Jo_b_total = Jo_b_total + Jo_b
1637 Jo_a_total_assim = Jo_a_total_assim + Jo_a_assim
1638 Jo_b_total_assim = Jo_b_total_assim + Jo_b_assim
1639 Jo_p_total_assim = Jo_p_total_assim + Jo_p_assim
1640 Jpa_total_assim = Jpa_total_assim + Jpa_assim
1641 else
1642 Jo_a = 0.0d0
1643 Jo_a_assim = 0.0d0
1644 Jo_b_assim = 0.0d0
1645 Jo_p_assim = 0.0d0
1646 Jpa_assim = 0.0d0
1647 end if
1648
1649 obs_diagn%allow_print_summary = .true.
1650
1651 call print_J(unit,"Multi-level data:",Jo_a,Jo_b,icount,Jo_a_assim,Jo_b_assim,Jpa_assim,Jo_p_assim,icount_assim)
1652
1653 call print_stats(unit,obs_diagn,press_bins,1,nlat,1,nlon,1,nlev-1)
1654 if (obs_diagn%assim_mode) call print_Desroziers(unit,obs_diagn,press_bins,1,nlat,1,nlon,1,nlev-1,status_hpht)
1655
1656 end if
1657
1658 ! Uni-level data
1659
1660 if (unilevel) then
1661
1662 if (ncounts(nlev) > 0) then
1663 Jo_a = sum(obs_diagn%Jo_stats(:,:,nlev,1))
1664 Jo_b = sum(obs_diagn%Jo_stats(:,:,nlev,2))
1665 Jo_a_assim = sum(obs_diagn%Jo_stats(:,:,nlev,3))
1666 Jo_b_assim = sum(obs_diagn%Jo_stats(:,:,nlev,4))
1667 Jo_p_assim = sum(obs_diagn%Jo_stats(:,:,nlev,5))
1668 Jpa_assim = sum(obs_diagn%Jpa_stats(:,:,nlev))
1669 Jo_a_total = Jo_a_total + Jo_a
1670 Jo_b_total = Jo_b_total + Jo_b
1671 Jo_a_total_assim = Jo_a_total_assim + Jo_a_assim
1672 Jo_b_total_assim = Jo_b_total_assim + Jo_b_assim
1673 Jo_p_total_assim = Jo_p_total_assim + Jo_p_assim
1674 Jpa_total_assim = Jpa_total_assim + Jpa_assim
1675 else
1676 Jo_a = 0.0d0
1677 Jo_b = 0.0d0
1678 Jo_a_assim = 0.0d0
1679 Jo_b_assim = 0.0d0
1680 Jo_p_assim = 0.0d0
1681 Jpa_assim = 0.0d0
1682 end if
1683
1684 obs_diagn%allow_print_summary = .true.
1685
1686 call print_J(unit,"Uni-level data:",Jo_a,Jo_b,ncounts(nlev),Jo_a_assim,Jo_b_assim,Jpa_assim,Jo_p_assim,ncounts_assim(nlev))
1687
1688 call print_stats(unit,obs_diagn,press_bins,1,nlat,1,nlon,nlev,nlev)
1689 if (obs_diagn%assim_mode) call print_Desroziers(unit,obs_diagn,press_bins,1,nlat,1,nlon,nlev,nlev,status_hpht)
1690
1691 end if
1692
1693 deallocate(ncounts,ncounts_assim)
1694
1695 ! Output lat,lon dependent averages to file if obsspace_diagn_filename is provided
1696 if (present(openfile_opt)) then
1697 if (openfile_opt .and. save_diagn .and. (nlat > 1 .or. nlon > 1)) then
1698
1699 write(unit,*)
1700 write(unit,'(A)') " Lat-lon gridded statistics"
1701 write(unit,'(A)') " --------------------------"
1702 write(unit,*)
1703 write(unit,'(2X,3(A,I4))') "nlat = ",nlat," , nlon = ",nlon," , nlev = ",nlev
1704 write(unit,*)
1705
1706 do ilat=1,nlat
1707 do ilon=1,nlon
1708 write(unit,'(2X,2(A,I6),3X,2(F8.1,A,F8.1))') "ilat = ",ilat," , ilon = ",ilon, &
1709 (ilat-1.)*obs_diagn%deltaLat-90.," < lat < ",ilat*obs_diagn%deltaLat-90., &
1710 (ilon-1.)*obs_diagn%deltaLon," < lon < ",ilon*obs_diagn%deltaLon
1711 if (any(obs_diagn%nstatus(ilat,ilon,1:nlev-1,:) > 0)) then
1712 write(unit,*)
1713 write(unit,'(A)') " Multi-level data:"
1714 write(unit,*)
1715 call print_stats(unit,obs_diagn,press_bins,ilat,ilat,ilon,ilon,1,nlev-1)
1716 if (obs_diagn%assim_mode) call print_Desroziers(unit,obs_diagn,press_bins,ilat,ilat,ilon,ilon,1,nlev-1,status_hpht)
1717 else if (multilevel) then
1718 write(unit,*)
1719 write(unit,'(A)') " No multi-level data."
1720 write(unit,*)
1721 end if
1722 if (any(obs_diagn%nstatus(ilat,ilon,nlev,:) > 0)) then
1723 write(unit,*)
1724 write(unit,'(A)') " Uni-level data:"
1725 write(unit,*)
1726 call print_stats(unit,obs_diagn,press_bins,ilat,ilat,ilon,ilon,nlev,nlev)
1727 if (obs_diagn%assim_mode) call print_Desroziers(unit,obs_diagn,press_bins,ilat,ilat,ilon,ilon,nlev,nlev,status_hpht)
1728 else if (unilevel) then
1729 write(unit,*)
1730 write(unit,'(A)') " No uni-level data."
1731 write(unit,*)
1732 end if
1733 end do
1734 end do
1735
1736 end if
1737 end if
1738
1739 deallocate(press_bins)
1740
1741 else
1742 write(unit,*)
1743 write(unit,*) "No data found for this combination."
1744 write(unit,*)
1745 end if
1746
1747 if (present(openfile_opt)) then
1748 if (openfile_opt) ierr=fclos(unit)
1749 end if
1750
1751 case('summary','SUMMARY')
1752
1753 if (.not.obs_diagn%allow_print_summary) then
1754 write(*,*) "osd_obsspace_diagn_print: allow_print_summary is set to false, no summary will be printed."
1755 return
1756 end if
1757
1758 if (present(openfile_opt)) then
1759 if (openfile_opt) then
1760 inquire(file=filename, exist=fileout_exist)
1761 call utl_open_asciifile(filename,unit)
1762 else
1763 unit=6
1764 end if
1765 else
1766 unit=6
1767 end if
1768
1769 call print_J(unit,"Total cost function for CH observations:",Jo_a_total,Jo_b_total,counts_total, &
1770 Jo_a_total_assim,Jo_b_total_assim,Jpa_total_assim,Jo_a_total_assim,counts_total_assim)
1771
1772 if (present(openfile_opt)) then
1773 if (openfile_opt) ierr=fclos(unit)
1774 end if
1775
1776 case default
1777 call utl_abort("osd_obsspace_diagn_print: Invalid print_type select of " // trim(print_type) )
1778 end select
1779
1780
1781 contains
1782
1783 !--------------------------------------------------------------------------
1784 ! print_J
1785 !--------------------------------------------------------------------------
1786 subroutine print_J( unit, title, Jo_analysis, Jo_backgrnd, nobs, Jo_anal_assim, Jo_bgck_assim, Jpa_assim, Jo_p_assim, nobs_assim )
1787 !
1788 implicit none
1789
1790 ! Arguments:
1791 integer , intent(in) :: unit
1792 integer , intent(in) :: nobs
1793 integer , intent(in) :: nobs_assim
1794 character(len=*), intent(in) :: title
1795 real(8) , intent(in) :: Jo_analysis
1796 real(8) , intent(in) :: Jo_backgrnd
1797 real(8) , intent(in) :: Jo_anal_assim
1798 real(8) , intent(in) :: Jo_bgck_assim
1799 real(8) , intent(in) :: Jpa_assim
1800 real(8) , intent(in) :: Jo_p_assim
1801
1802 ! Locals:
1803 real(8) :: Jo_analysis_norm,Jo_backgrnd_norm,Jt_assim
1804 real(8) :: Jpa_norm_assim,Jt_norm_assim,Jo_p_norm_assim
1805 character(len=100) :: fmt
1806
1807 if (Jo_analysis < 1.0d6 .and. Jo_backgrnd < 1.0d6) then
1808 fmt = '(A,F24.8,A,F24.8)'
1809 else
1810 fmt = '(A,ES24.8,A,ES24.8)'
1811 end if
1812
1813 if (nobs > 0) then
1814 Jo_analysis_norm = 2.*Jo_analysis/nobs
1815 Jo_backgrnd_norm = 2.*Jo_backgrnd/nobs
1816 else
1817 Jo_analysis_norm = 0.0d0
1818 Jo_backgrnd_norm = 0.0d0
1819 end if
1820
1821 if ((nobs > 0 .and. nobs_assim > 0) .or. nobs_assim == 0) then
1822 write(unit,*)
1823 write(unit,'(A)') " " // title // " totals "
1824 write(unit,*)
1825 write(unit,fmt) " Jo(x_analysis) = ",Jo_analysis," , 2*Jo(x_analysis)/N = ",Jo_analysis_norm
1826 write(unit,fmt) " Jo(x_backgrnd) = ",Jo_backgrnd," , 2*Jo(x_backgrnd)/N = ",Jo_backgrnd_norm
1827 write(unit,'(A,I24)') " N = ",nobs
1828 write(unit,*)
1829 end if
1830
1831 if (nobs_assim > 0) then
1832 Jo_analysis_norm = 2.*Jo_anal_assim/nobs_assim
1833 Jo_backgrnd_norm = 2.*Jo_bgck_assim/nobs_assim
1834 Jpa_norm_assim = 2.*Jpa_assim/nobs_assim
1835 Jt_norm_assim = Jpa_norm_assim+Jo_analysis_norm
1836 Jo_p_norm_assim = 2.*Jo_p_assim/nobs_assim
1837
1838 write(unit,*)
1839 write(unit,'(A)') " " // title // " assimilated data "
1840 write(unit,*)
1841 write(unit,fmt) " J(A-P)+Jo(x_a) = ",Jt_assim, " , 2*[J(A-P)+Jo(x_a)]/N = ",Jt_norm_assim
1842 write(unit,fmt) " J(A-P) = ",Jpa_assim, " , 2*J(A-P)/N = ",Jpa_norm_assim
1843 write(unit,fmt) " Jo(x_analysis) = ",Jo_anal_assim," , 2*Jo(x_analysis)/N = ",Jo_analysis_norm
1844 write(unit,fmt) " Jo(x_backgrnd) = ",Jo_bgck_assim," , 2*Jo(x_backgrnd)/N = ",Jo_backgrnd_norm
1845 write(unit,fmt) " J(O-P) = ",Jo_p_assim, " , 2*J(O-P)/N = ",Jo_p_norm_assim
1846 write(unit,'(A,I24)') " N = ",nobs_assim
1847 write(unit,*)
1848 end if
1849
1850 end subroutine print_J
1851
1852 !--------------------------------------------------------------------------
1853 ! print_stats
1854 !--------------------------------------------------------------------------
1855 subroutine print_stats( unit, obs_diagn, pressure, ilat_start, ilat_end, ilon_start, ilon_end, ilev_start, ilev_end )
1856 implicit none
1857
1858 ! Arguments:
1859 type(struct_osd_diagn), intent(in) :: obs_diagn
1860 real(8) , intent(in) :: pressure(obs_diagn%nlev+1)
1861 integer , intent(in) :: unit
1862 integer , intent(in) :: ilat_start
1863 integer , intent(in) :: ilat_end
1864 integer , intent(in) :: ilon_start
1865 integer , intent(in) :: ilon_end
1866 integer , intent(in) :: ilev_start
1867 integer , intent(in) :: ilev_end
1868
1869 ! Locals:
1870 integer :: ilev,level,counts(obs_diagn%nlev),N_assim,N_diagn,N_rej
1871 real(8) :: pres1,pres2,jo_a,jo_b,jo_a_norm,jo_b_norm,obs_sum,obs_mean,obs_std,OmP_mean,OmP_rms,OmA_mean,OmA_rms
1872 logical :: skip(obs_diagn%nlev)
1873
1874 skip(:) = .false.
1875
1876 write(unit,'(A)') " Layer Pressure (hPa) Counts (N) N_assim N_diagn N_rej Jo(O-A) 2*Jo(O-A)/N Jo(O-P) 2*Jo(O-P)/N"
1877 write(unit,'(A)') " ----- -------------- ---------- ------- ------- ----- ------- ----------- ------- -----------"
1878
1879 do ilev = ilev_start, ilev_end
1880
1881 counts(ilev) = sum(obs_diagn%counts(ilat_start:ilat_end,ilon_start:ilon_end,ilev))
1882
1883 N_rej = sum(obs_diagn%nstatus(ilat_start:ilat_end,ilon_start:ilon_end,ilev,0))
1884 N_assim = sum(obs_diagn%nstatus(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1))
1885 N_diagn = sum(obs_diagn%nstatus(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2))
1886
1887 skip(ilev) = counts(ilev) == 0 .and. N_rej == 0
1888
1889 if (skip(ilev)) cycle
1890
1891 if (ilev < nlev) then
1892 pres1 = pressure(ilev)
1893 pres2 = pressure(ilev+1)
1894 level = ilev
1895 else
1896 pres1 = 0.0d0
1897 pres2 = 0.0d0
1898 level = 0
1899 end if
1900
1901 jo_a = sum(obs_diagn%Jo_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1))
1902 jo_b = sum(obs_diagn%Jo_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2))
1903
1904 if ( counts(ilev) == 0 ) then
1905 jo_a_norm = 0.0d0
1906 jo_b_norm = 0.0d0
1907 else
1908 jo_a_norm = 2.*jo_a/counts(ilev)
1909 jo_b_norm = 2.*jo_b/counts(ilev)
1910 end if
1911
1912 write(unit,'(2X, I3, 2(2X, F11.4), 4(2X,I9), 4(2X, ES11.4))') &
1913 level,pres1,pres2,counts(ilev),N_assim,N_diagn,N_rej,jo_a,jo_a_norm,jo_b,jo_b_norm
1914
1915 end do
1916
1917 write(unit,*)
1918 ! Division by <O> removed to prevent unlikely division by zero (or <= zero)
1919 ! write(unit,'(A)') " Layer Pressure (hPa) Counts obs (mean) obs (std) rms(O-P)/<O> <O-P>/<O> rms(O-A)/<O> <O-A>/<O>"
1920 ! write(unit,'(A)') " ----- -------------- ------ ---------- --------- ------------ --------- ------------ ---------"
1921 write(unit,'(A)') " Layer Pressure (hPa) Counts obs (mean) obs (std) rms(O-P) <O-P> rms(O-A) <O-A>"
1922 write(unit,'(A)') " ----- -------------- ------ ---------- --------- -------- ----- -------- -----"
1923
1924
1925 do ilev=ilev_start,ilev_end
1926
1927 if (skip(ilev)) cycle
1928
1929 if (ilev < nlev) then
1930 pres1 = pressure(ilev)
1931 pres2 = pressure(ilev+1)
1932 level = ilev
1933 else
1934 pres1 = 0.0d0
1935 pres2 = 0.0d0
1936 level = 0
1937 end if
1938
1939 if (counts(ilev) == 0) then
1940 obs_mean = 0.0d0
1941 obs_std = 0.0d0
1942 OmP_rms = 0.0d0
1943 OmP_mean = 0.0d0
1944 OmA_rms = 0.0d0
1945 OmA_mean = 0.0d0
1946
1947 write(unit,'(2X, I3, 2(2X, F11.4), 2X, I6, 6(2X, ES11.4))') &
1948 level,pres1,pres2,counts(ilev),obs_mean,obs_std,OmP_rms,OmP_mean,OmA_rms,OmA_mean
1949 else
1950 obs_sum = sum(obs_diagn%obs_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2))
1951 obs_mean = obs_sum / counts(ilev)
1952 obs_std = sqrt(max(0.0D0, sum(obs_diagn%obs_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1))/counts(ilev) - (obs_sum/counts(ilev))**2 ))
1953
1954 OmP_rms = sqrt(max(0.0D0, sum(obs_diagn%OmP_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1)) / counts(ilev) ))
1955 OmP_mean = sum(obs_diagn%OmP_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2)) / obs_sum
1956
1957 OmA_rms = sqrt(max(0.0D0, sum(obs_diagn%OmA_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1)) / counts(ilev) ))
1958 OmA_mean = sum(obs_diagn%OmA_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2)) / counts(ilev)
1959
1960 ! write(unit,'(2X, I3, 2(2X, F11.4), 6(2X, ES11.4))') &
1961 ! level,pres1,pres2,obs_mean,obs_std,OmP_rms/obs_mean,OmP_mean/obs_sum,OmA_rms/obs_mean,OmA_mean/obs_sum
1962
1963 write(unit,'(2X, I3, 2(2X, F11.4), 2X, I6, 6(2X, ES11.4))') &
1964 level,pres1,pres2,counts(ilev),obs_mean,obs_std,OmP_rms,OmP_mean,OmA_rms,OmA_mean
1965
1966 end if
1967
1968 end do
1969
1970 write(unit,*)
1971
1972 end subroutine print_stats
1973
1974 !--------------------------------------------------------------------------
1975 ! print_Desroziers
1976 !--------------------------------------------------------------------------
1977 subroutine print_Desroziers( unit, obs_diagn, pressure, ilat_start, ilat_end, ilon_start, ilon_end, ilev_start, ilev_end, status_hpht )
1978 !
1979 !:Purpose: Prints elements contributing to the calc of scaling factors for observation and background
1980 ! error std. dev. based on the Desroziers approach.
1981 !
1982 implicit none
1983
1984 ! Arguments:
1985 type(struct_osd_diagn), intent(in) :: obs_diagn
1986 real(8) , intent(in) :: pressure(obs_diagn%nlev+1)
1987 integer , intent(in) :: unit
1988 integer , intent(in) :: ilat_start
1989 integer , intent(in) :: ilat_end
1990 integer , intent(in) :: ilon_start
1991 integer , intent(in) :: ilon_end
1992 integer , intent(in) :: ilev_start
1993 integer , intent(in) :: ilev_end
1994 logical , intent(in) :: status_hpht
1995
1996 ! Locals:
1997 integer :: ilev,level,N_assim(obs_diagn%nlev)
1998 real(8) :: pres1,pres2,sum_prod,sum_OmP,sum_OmA,sum_AmP,scaling
1999
2000 N_assim(:) = 0
2001
2002 write(unit,'(A)') " Layer Pressure (hPa) N_assim sum[(O-P)(O-A)/var(O)] sum[(O-P)/sig(O)] sum[(O-A)/sig(O)] mean[(O-P)(O-A)/var(O)]-mean[(O-P)/sig(O)]*mean[(O-A)/sig(O)]"
2003 write(unit,'(A)') " ----- -------------- ------- ---------------------- ----------------- ----------------- -------------------------------------------------------------"
2004
2005 do ilev=ilev_start,ilev_end
2006
2007 N_assim(ilev) = sum(obs_diagn%nstatus(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1))
2008
2009 if (N_assim(ilev) == 0) cycle
2010
2011 if (ilev < nlev) then
2012 pres1 = pressure(ilev)
2013 pres2 = pressure(ilev+1)
2014 level = ilev
2015 else
2016 pres1 = 0.0d0
2017 pres2 = 0.0d0
2018 level = 0
2019 end if
2020
2021 sum_prod = sum(obs_diagn%diagR_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1))
2022 sum_OmP = sum(obs_diagn%diagR_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2))
2023 sum_OmA = sum(obs_diagn%diagR_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,3))
2024
2025 scaling = sum_prod/N_assim(ilev) - sum_OmP*sum_OmA/N_assim(ilev)**2
2026
2027 write(unit,'(2X, I3, 2(2X, F11.4), 2X, I9, 11X, ES11.4, 2(8X,ES11.4), 10X, ES11.4)') level,pres1,pres2,N_assim(ilev),sum_prod,sum_OmP,sum_OmA,scaling
2028
2029 end do
2030
2031 write(unit,*)
2032 if (status_hpht) then
2033 write(unit,'(A)') " Layer Pressure (hPa) N_assim sum[(O-P)(A-P)/var(P)] sum[(O-P)/sig(P)] sum[(A-P)/sig(P)] mean[(O-P)(A-P)/var(P)]-mean[(O-P)/sig(P)]*mean[(A-P)/sig(P)]"
2034 else
2035 write(unit,'(A)') " Layer Pressure (hPa) N_assim sum[(O-P)(A-P)] sum[O-P] sum[A-P] mean[(O-P)(A-P)]-mean[O-P]*mean[A-P]"
2036 end if
2037 write(unit,'(A)') " ----- -------------- ------- ---------------------- ----------------- ----------------- -------------------------------------------------------------"
2038
2039 do ilev=ilev_start,ilev_end
2040
2041 if (N_assim(ilev) == 0) cycle
2042
2043 if (ilev < nlev) then
2044 pres1 = pressure(ilev)
2045 pres2 = pressure(ilev+1)
2046 level = ilev
2047 else
2048 pres1 = 0.0d0
2049 pres2 = 0.0d0
2050 level = 0
2051 end if
2052
2053 sum_prod = sum(obs_diagn%diagHPHT_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,1))
2054 sum_OmP = sum(obs_diagn%diagHPHT_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,2))
2055 sum_AmP = sum(obs_diagn%diagHPHT_stats(ilat_start:ilat_end,ilon_start:ilon_end,ilev,3))
2056
2057 scaling = sum_prod/N_assim(ilev) - sum_OmP*sum_AmP/N_assim(ilev)**2
2058
2059 write(unit,'(2X, I3, 2(2X, F11.4), 2X, I9, 11X, ES11.4, 2(8X,ES11.4), 10X, ES11.4)') level,pres1,pres2,N_assim(ilev),sum_prod,sum_OmP,sum_AmP,scaling
2060
2061 end do
2062
2063 write(unit,*)
2064
2065 end subroutine print_Desroziers
2066
2067 end subroutine osd_obsspace_diagn_print
2068
2069end module obsSpaceDiag_mod