1module humidityLimits_mod
2 ! MODULE humidityLimits_mod (prefix='qlim' category='4. Data Object transformations')
3 !
4 !:Purpose: Various manipulations of humidity-related quantities.
5 !
6 use midasMpi_mod
7 use utilities_mod
8 use mathPhysConstants_mod
9 use varNameList_mod
10 use physicsFunctions_mod
11 use verticalCoord_mod
12 use gridStateVector_mod
13 use ensembleStateVector_mod
14 use calcHeightAndPressure_mod
15 implicit none
16 save
17 private
18
19 ! public procedures
20 public :: qlim_saturationLimit, qlim_rttovLimit, qlim_setMin
21 public :: qlim_getMinValueCloud, qlim_getMaxValueCloud
22
23 real(8), parameter :: mixratio_to_ppmv = 1.60771704d+6
24 real(8) :: qlim_minValueLWCR, qlim_minValueIWCR, qlim_minValueRF, qlim_minValueSF, qlim_minValueCLDR
25 real(8) :: qlim_maxValueLWCR, qlim_maxValueIWCR, qlim_maxValueRF, qlim_maxValueSF, qlim_maxValueCLDR
26
27 ! interface for qlim_saturationLimit
28 interface qlim_saturationLimit
29 module procedure qlim_saturationLimit_gsv
30 module procedure qlim_saturationLimit_ens
31 end interface qlim_saturationLimit
32
33 ! interface for qlim_rttovLimit
34 interface qlim_rttovLimit
35 module procedure qlim_rttovLimit_gsv
36 module procedure qlim_rttovLimit_ens
37 end interface qlim_rttovLimit
38
39 ! interface for qlim_setMin
40 interface qlim_setMin
41 module procedure qlim_setMin_ens
42 end interface qlim_setMin
43
44contains
45
46 !--------------------------------------------------------------------------
47 ! readNameList
48 !--------------------------------------------------------------------------
49 subroutine readNameList
50 !
51 !:Purpose: Reading NAMQLIM namelist by any subroutines in humidityLimits_mod module.
52 !
53 implicit none
54
55 ! Locals:
56 integer :: nulnam, ierr
57 integer, external :: fnom, fclos
58 logical, save :: nmlAlreadyRead = .false.
59
60 ! Namelist variables:
61 real(8) :: minValueLWCR ! minimum LWCR value
62 real(8) :: maxValueLWCR ! maximum LWCR value
63 real(8) :: minValueIWCR ! minimum IWCR value
64 real(8) :: maxValueIWCR ! maximum IWCR value
65 real(8) :: minValueRF ! minimum RF value
66 real(8) :: maxValueRF ! maximum RF value
67 real(8) :: minValueSF ! minimum SF value
68 real(8) :: maxValueSF ! maximum SF value
69 real(8) :: minValueCLDR ! minimum CLDR value
70 real(8) :: maxValueCLDR ! maximum CLDR value
71
72 NAMELIST /NAMQLIM/ minValueLWCR, maxValueLWCR, minValueIWCR, maxValueIWCR, &
73 minValueRF, maxValueRF, minValueSF, maxValueSF, minValueCLDR, maxValueCLDR
74
75 if ( nmlAlreadyRead ) return
76
77 nmlAlreadyRead = .true.
78
79 !- Setting default values
80 minValueLWCR = 1.0d-9
81 maxValueLWCR = 1.0d0
82
83 minValueIWCR = 1.0d-9
84 maxValueIWCR = 1.0d0
85
86 minValueRF = 0.0d0
87 maxValueRF = 1.0d0
88
89 minValueSF = 0.0d0
90 maxValueSF = 1.0d0
91
92 minValueCLDR = 0.0d0
93 maxValueCLDR = 1.0d0
94
95 if ( .not. utl_isNamelistPresent('NAMQLIM','./flnml') ) then
96 if ( mmpi_myid == 0 ) then
97 write(*,*) 'NAMQLIM is missing in the namelist. The default values will be taken.'
98 end if
99
100 else
101 ! Reading the namelist
102 nulnam = 0
103 ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
104 read(nulnam, nml=namqlim, iostat=ierr)
105 if ( ierr /= 0) call utl_abort('humidityLimits_mod: Error reading namelist')
106 ierr = fclos(nulnam)
107
108 end if
109 if ( mmpi_myid == 0 ) write(*,nml=namqlim)
110
111 ! Transfer namelist variables to module variables.
112 qlim_minValueLWCR = minValueLWCR
113 qlim_maxValueLWCR = maxValueLWCR
114
115 qlim_minValueIWCR = minValueIWCR
116 qlim_maxValueIWCR = maxValueIWCR
117
118 qlim_minValueRF = minValueRF
119 qlim_maxValueRF = maxValueRF
120
121 qlim_minValueSF = minValueSF
122 qlim_maxValueSF = maxValueSF
123
124 qlim_minValueCLDR = minValueCLDR
125 qlim_maxValueCLDR = maxValueCLDR
126
127 end subroutine readNameList
128
129 !--------------------------------------------------------------------------
130 ! qlim_saturationLimit_gsv
131 !--------------------------------------------------------------------------
132 subroutine qlim_saturationLimit_gsv(statevector)
133 !
134 !:Purpose: To impose saturation limit on humidity variable of a statevector
135 !
136 implicit none
137
138 ! Arguments:
139 type(struct_gsv), intent(inout) :: statevector
140
141 ! Locals:
142 type(struct_vco), pointer :: vco_ptr
143 real(4), pointer :: hu_ptr_r4(:,:,:,:), tt_ptr_r4(:,:,:,:), psfc_ptr_r4(:,:,:,:), psfcLS_ptr_r4(:,:,:,:)
144 real(8), pointer :: hu_ptr_r8(:,:,:,:), tt_ptr_r8(:,:,:,:), psfc_ptr_r8(:,:,:,:), psfcLS_ptr_r8(:,:,:,:)
145 real(8), pointer :: pressure(:,:,:)
146 real(8) :: hu, husat, hu_modified, tt
147 integer :: lon1, lon2, lat1, lat2, lev1, lev2
148 real(8), allocatable :: psfc(:,:), psfcLS(:,:)
149 integer :: lonIndex, latIndex, levIndex, stepIndex
150
151 if (mmpi_myid == 0) write(*,*) 'qlim_saturationLimit_gsv: STARTING'
152
153 if( .not. gsv_varExist(statevector,'HU') ) then
154 if( mmpi_myid == 0 ) write(*,*) 'qlim_saturationLimit_gsv: statevector does not ' // &
155 'contain humidity ... doing nothing'
156 return
157 end if
158
159 vco_ptr => gsv_getVco(statevector)
160 if (stateVector%dataKind == 8) then
161 call gsv_getField(statevector,hu_ptr_r8,'HU')
162 call gsv_getField(statevector,tt_ptr_r8,'TT')
163 else
164 call gsv_getField(statevector,hu_ptr_r4,'HU')
165 call gsv_getField(statevector,tt_ptr_r4,'TT')
166 end if
167
168 lon1 = statevector%myLonBeg
169 lon2 = statevector%myLonEnd
170 lat1 = statevector%myLatBeg
171 lat2 = statevector%myLatEnd
172 lev1 = 1
173 lev2 = gsv_getNumLev(statevector,'TH')
174
175 allocate(psfc(lon2-lon1+1,lat2-lat1+1))
176 if (vco_ptr%vcode == 5100) allocate(psfcLS(lon2-lon1+1,lat2-lat1+1))
177 do stepIndex = 1, statevector%numStep
178 if (stateVector%dataKind == 8) then
179 call gsv_getField(statevector,psfc_ptr_r8,'P0')
180 psfc(:,:) = psfc_ptr_r8(:,:,1,stepIndex)
181 if (vco_ptr%vcode == 5100) then
182 call gsv_getField(statevector,psfcLS_ptr_r8,'P0LS')
183 psfcLS(:,:) = psfcLS_ptr_r8(:,:,1,stepIndex)
184 end if
185 else
186 call gsv_getField(statevector,psfc_ptr_r4,'P0')
187 psfc(:,:) = psfc_ptr_r4(:,:,1,stepIndex)
188 if (vco_ptr%vcode == 5100) then
189 call gsv_getField(statevector,psfcLS_ptr_r4,'P0LS')
190 psfcLS(:,:) = psfcLS_ptr_r4(:,:,1,stepIndex)
191 end if
192 end if
193 if (vco_ptr%vcode == 5100) then
194 call czp_fetch3DLevels(vco_ptr, psfc, sfcFldLS_opt=psfcLS, fldT_opt=pressure)
195 else
196 call czp_fetch3DLevels(vco_ptr, psfc, fldT_opt=pressure)
197 end if
198 if (stateVector%dataKind == 8) then
199 !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex, hu, tt, husat, hu_modified)
200 do levIndex = lev1, lev2
201 do latIndex = lat1, lat2
202 do lonIndex = lon1, lon2
203 hu = hu_ptr_r8(lonIndex,latIndex,levIndex,stepIndex)
204 tt = tt_ptr_r8(lonIndex,latIndex,levIndex,stepIndex)
205
206 ! get the saturated vapor pressure from HU
207 husat = phf_foqst8(tt, pressure(lonIndex-lon1+1,latIndex-lat1+1,levIndex) )
208
209 ! limit the humidity to the saturated humidity
210 hu_modified = min(husat, hu)
211 hu_ptr_r8(lonIndex,latIndex,levIndex,stepIndex) = hu_modified
212
213 end do ! lonIndex
214 end do ! latIndex
215 end do ! levIndex
216 !$OMP END PARALLEL DO
217 else
218 !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex, hu, tt, husat, hu_modified)
219 do levIndex = lev1, lev2
220 do latIndex = lat1, lat2
221 do lonIndex = lon1, lon2
222 hu = hu_ptr_r4(lonIndex,latIndex,levIndex,stepIndex)
223 tt = tt_ptr_r4(lonIndex,latIndex,levIndex,stepIndex)
224
225 ! get the saturated vapor pressure from HU
226 husat = phf_foqst8(tt, pressure(lonIndex-lon1+1,latIndex-lat1+1,levIndex) )
227
228 ! limit the humidity to the saturated humidity
229 hu_modified = min(husat, hu)
230 hu_ptr_r4(lonIndex,latIndex,levIndex,stepIndex) = hu_modified
231
232 end do ! lonIndex
233 end do ! latIndex
234 end do ! levIndex
235 !$OMP END PARALLEL DO
236 end if
237
238 deallocate(pressure)
239
240 end do ! stepIndex
241
242 deallocate(psfc)
243 if (allocated(psfcLS)) deallocate(psfcLS)
244
245 end subroutine qlim_saturationLimit_gsv
246
247 !--------------------------------------------------------------------------
248 ! qlim_saturationLimit_ens
249 !--------------------------------------------------------------------------
250 subroutine qlim_saturationLimit_ens(ensemble)
251 !
252 !:Purpose: To impose saturation limit on humidity variable of an ensemble
253 !
254 implicit none
255
256 ! Arguments:
257 type(struct_ens), intent(inout) :: ensemble
258
259 ! Locals:
260 type(struct_vco), pointer :: vco_ptr
261 real(4), pointer :: hu_ptr_r4(:,:,:,:), tt_ptr_r4(:,:,:,:), psfc_ptr_r4(:,:,:,:), psfcLS_ptr_r4(:,:,:,:)
262 real(8), pointer :: pressure(:,:,:)
263 real(8) :: hu, husat, hu_modified, tt
264 integer :: lon1, lon2, lat1, lat2, numLev
265 real(8), allocatable :: psfc(:,:),psfcLS(:,:)
266 integer :: lonIndex, latIndex, levIndex, stepIndex, memberIndex, varLevIndex
267
268 if (mmpi_myid == 0) write(*,*) 'qlim_saturationLimit_ens: STARTING'
269
270 if (ens_getDataKind(ensemble) == 8) then
271 call utl_abort('qlim_saturationLimit_ens: Not compatible with dataKind = 8')
272 end if
273
274 if( .not. ens_varExist(ensemble,'HU') ) then
275 if( mmpi_myid == 0 ) write(*,*) 'qlim_saturationLimit_ens: ensemble does not ' // &
276 'contain humidity ... doing nothing'
277 return
278 end if
279
280 vco_ptr => ens_getVco(ensemble)
281 numLev = ens_getNumLev(ensemble,'TH')
282 call ens_getLatLonBounds(ensemble, lon1, lon2, lat1, lat2)
283 allocate(psfc(ens_getNumMembers(ensemble),ens_getNumStep(ensemble)))
284 if (vco_ptr%vcode == 5100) allocate(psfcLS(ens_getNumMembers(ensemble),ens_getNumStep(ensemble)))
285
286 do latIndex = lat1, lat2
287 do lonIndex = lon1, lon2
288
289 ! compute pressure for all members and steps
290 varLevIndex = ens_getKFromLevVarName(ensemble, 1, 'P0')
291 psfc_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
292 psfc(:,:) = psfc_ptr_r4(:,:,lonIndex,latIndex)
293 if (vco_ptr%vcode == 5100) then
294 varLevIndex = ens_getKFromLevVarName(ensemble, 1, 'P0LS')
295 psfcLS_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
296 psfcLS(:,:) = psfcLS_ptr_r4(:,:,lonIndex,latIndex)
297 call czp_fetch3DLevels(vco_ptr, psfc, sfcFldLS_opt=psfcLS, fldT_opt=pressure)
298 else
299 call czp_fetch3DLevels(vco_ptr, psfc, fldT_opt=pressure)
300 end if
301
302 do levIndex = 1, numLev
303 varLevIndex = ens_getKFromLevVarName(ensemble, levIndex, 'HU')
304 hu_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
305 varLevIndex = ens_getKFromLevVarName(ensemble, levIndex, 'TT')
306 tt_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
307
308 !$OMP PARALLEL DO PRIVATE (stepIndex, memberIndex, hu, tt, husat, hu_modified)
309 do stepIndex = 1, ens_getNumStep(ensemble)
310 do memberIndex = 1, ens_getNumMembers(ensemble)
311 hu = hu_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex)
312 tt = tt_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex)
313
314 ! get the saturated vapor pressure from HU
315 husat = phf_foqst8(tt, pressure(memberIndex,stepIndex,levIndex) )
316
317 ! limit the humidity to the saturated humidity
318 hu_modified = min(husat, hu)
319 hu_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex) = hu_modified
320
321 end do ! memberIndex
322 end do ! stepIndex
323 !$OMP END PARALLEL DO
324
325 end do ! levIndex
326 deallocate(pressure)
327
328 end do ! lonIndex
329 end do ! latIndex
330
331 deallocate(psfc)
332 if (allocated(psfcLS)) deallocate(psfcLS)
333
334 end subroutine qlim_saturationLimit_ens
335
336 !--------------------------------------------------------------------------
337 ! qlim_rttovLimit_gsv
338 !--------------------------------------------------------------------------
339 subroutine qlim_rttovLimit_gsv(statevector, varName_opt, applyLimitToCloud_opt)
340 !
341 !:Purpose: To impose RTTOV limits on humidity/cloud
342 !
343 implicit none
344
345 ! Arguments:
346 type(struct_gsv), intent(inout) :: statevector
347 character(len=*), optional, intent(in) :: varName_opt
348 logical, optional, intent(in) :: applyLimitToCloud_opt
349
350 ! Locals:
351 type(struct_vco), pointer :: vco_ptr
352 real(8), allocatable :: press_rttov(:), qmin_rttov(:), qmax_rttov(:)
353 real(8), allocatable :: psfc(:,:),psfcLS(:,:)
354 real(8), allocatable :: qmin3D_rttov(:,:,:), qmax3D_rttov(:,:,:)
355 real(8), pointer :: hu_ptr_r8(:,:,:,:), psfc_ptr_r8(:,:,:,:), psfcLS_ptr_r8(:,:,:,:)
356 real(8), pointer :: cld_ptr_r8(:,:,:,:)
357 real(4), pointer :: hu_ptr_r4(:,:,:,:), psfc_ptr_r4(:,:,:,:), psfcLS_ptr_r4(:,:,:,:)
358 real(4), pointer :: cld_ptr_r4(:,:,:,:)
359 real(8), pointer :: pressure(:,:,:)
360 real(8) :: hu, hu_modified
361 real(8) :: cld, cld_modified
362 real(8) :: minValueCld, maxValueCld
363 integer :: lon1, lon2, lat1, lat2, lev1, lev2, varNameIndex
364 integer :: lonIndex, latIndex, levIndex, stepIndex
365 integer :: ni, nj, numLev, numLev_rttov
366 integer :: fnom, fclos, ierr, nulfile
367 character(len=256) :: fileName
368 character(len=4) :: varName
369 logical, save :: firstTime=.true.
370 logical :: applyLimitToAllVarname
371 logical :: applyLimitToCloud
372
373 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_gsv: STARTING'
374
375 if (present(varName_opt)) then
376 applyLimitToAllVarname = .false.
377 varName = varName_opt
378 else
379 applyLimitToAllVarname = .true.
380 varName = 'XXXX'
381 end if
382
383 ! for cloud, limits are applied to ALL cloud variables.
384 if (present(applyLimitToCloud_opt)) then
385 applyLimitToCloud = applyLimitToCloud_opt
386 if (.not. applyLimitToCloud) call utl_abort('qlim_rttovLimit_gsv: remove applyLimitToCloud_opt argument')
387 else
388 if (vnl_isCloudVar(varName)) then
389 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_gsv: limits are applied to ALL cloud variables'
390 applyLimitToCloud = .true.
391 else
392 applyLimitToCloud = .false.
393 end if
394 end if
395
396 if (applyLimitToCloud) applyLimitToAllVarname = .false.
397
398 if ((applyLimitToAllVarname .or. trim(varName) == 'HU') .and. &
399 gsv_varExist(statevector,'HU')) then
400
401 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_gsv: applying limits to HU.'
402
403 ! Read in RTTOV humidity limits
404 fileName = "rttov_h2o_limits.dat"
405 nulfile = 0
406 ierr = fnom(nulfile, fileName, "FMT+OLD+R/O", 0)
407 if( ierr /= 0 ) then
408 if ( mmpi_myid == 0 ) write(*,*) 'fileName = ', fileName
409 call utl_abort('qlim_rttovLimit_gsv: error opening the humidity limits file')
410 end if
411
412 read(nulfile,*) numLev_rttov
413 if ( mmpi_myid == 0 .and. firstTime ) write(*,*) 'qlim_rttovLimit_gsv: rttov number of levels = ', numLev_rttov
414 allocate(press_rttov(numLev_rttov))
415 allocate(qmin_rttov(numLev_rttov))
416 allocate(qmax_rttov(numLev_rttov))
417 do levIndex = 1, numLev_rttov
418 read(nulfile,*) press_rttov(levIndex), qmax_rttov(levIndex), qmin_rttov(levIndex)
419 end do
420 ierr = fclos(nulfile)
421 press_rttov(:) = press_rttov(:) * mpc_pa_per_mbar_r8
422 qmin_rttov(:) = qmin_rttov(:) / mixratio_to_ppmv
423 qmax_rttov(:) = qmax_rttov(:) / mixratio_to_ppmv
424
425 if (firstTime) then
426 write(*,*) ' '
427 do levIndex = 1, numLev_rttov
428 if ( mmpi_myid == 0 ) write(*,fmt='(" qlim_rttovLimit_gsv: LEVEL = ",I4,", PRES = ",F9.0,", HUMIN = ",E10.2,", HUMAX = ",E10.2)') &
429 levIndex, press_rttov(levIndex), qmin_rttov(levIndex), qmax_rttov(levIndex)
430 end do
431 firstTime = .false.
432 end if
433
434 vco_ptr => gsv_getVco(statevector)
435 if (statevector%dataKind == 8) then
436 call gsv_getField(statevector,hu_ptr_r8,'HU')
437 else
438 call gsv_getField(statevector,hu_ptr_r4,'HU')
439 end if
440
441 lon1 = statevector%myLonBeg
442 lon2 = statevector%myLonEnd
443 lat1 = statevector%myLatBeg
444 lat2 = statevector%myLatEnd
445 lev1 = 1
446 lev2 = gsv_getNumLev(statevector,'TH')
447
448 ni = lon2 - lon1 + 1
449 nj = lat2 - lat1 + 1
450 numLev = lev2 - lev1 + 1
451 allocate( qmin3D_rttov(ni,nj,numLev) )
452 allocate( qmax3D_rttov(ni,nj,numLev) )
453 allocate( psfc(lon2-lon1+1,lat2-lat1+1) )
454 if (vco_ptr%vcode == 5100) allocate( psfcLS(lon2-lon1+1,lat2-lat1+1) )
455
456 do stepIndex = 1, statevector%numStep
457 if (statevector%dataKind == 8) then
458 call gsv_getField(statevector,psfc_ptr_r8,'P0')
459 psfc(:,:) = psfc_ptr_r8(:,:,1,stepIndex)
460 if (vco_ptr%vcode == 5100) then
461 call gsv_getField(statevector,psfcLS_ptr_r8,'P0')
462 psfcLS(:,:) = psfcLS_ptr_r8(:,:,1,stepIndex)
463 end if
464 else
465 call gsv_getField(statevector,psfc_ptr_r4,'P0')
466 psfc(:,:) = real(psfc_ptr_r4(:,:,1,stepIndex),8)
467 if (vco_ptr%vcode == 5100) then
468 call gsv_getField(statevector,psfcLS_ptr_r4,'P0')
469 psfcLS(:,:) = psfcLS_ptr_r4(:,:,1,stepIndex)
470 end if
471 end if
472 if (vco_ptr%vcode == 5100) then
473 call czp_fetch3DLevels(vco_ptr, psfc, sfcFldLS_opt=psfcLS, fldT_opt=pressure)
474 else
475 call czp_fetch3DLevels(vco_ptr, psfc, fldT_opt=pressure)
476 end if
477
478 ! Interpolate RTTOV limits onto model levels
479 call qlim_lintv_minmax(press_rttov, qmin_rttov, qmax_rttov, numLev_rttov, &
480 ni, nj, numLev, pressure, qmin3D_rttov, qmax3D_rttov)
481
482 !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex, hu, hu_modified)
483 do levIndex = lev1, lev2
484 do latIndex = lat1, lat2
485 do lonIndex = lon1, lon2
486 if (statevector%dataKind == 8) then
487 hu = hu_ptr_r8(lonIndex,latIndex,levIndex,stepIndex)
488 else
489 hu = real(hu_ptr_r4(lonIndex,latIndex,levIndex,stepIndex),8)
490 end if
491
492 ! limit the humidity according to the rttov limits
493 hu_modified = max(hu, qmin3D_rttov(lonIndex - lon1 + 1, latIndex - lat1 + 1, levIndex) )
494 hu_modified = min(hu_modified, qmax3D_rttov(lonIndex - lon1 + 1, latIndex - lat1 + 1, levIndex) )
495 if (statevector%dataKind == 8) then
496 hu_ptr_r8(lonIndex,latIndex,levIndex,stepIndex) = hu_modified
497 else
498 hu_ptr_r4(lonIndex,latIndex,levIndex,stepIndex) = real(hu_modified,4)
499 end if
500
501 end do ! lonIndex
502 end do ! latIndex
503 end do ! levIndex
504 !$OMP END PARALLEL DO
505
506 deallocate(pressure)
507 end do ! stepIndex
508
509 deallocate( psfc )
510 if (allocated(psfcLS)) deallocate( psfcLS )
511 deallocate( qmin3D_rttov )
512 deallocate( qmax3D_rttov )
513
514 deallocate(qmax_rttov)
515 deallocate(qmin_rttov)
516 deallocate(press_rttov)
517
518 end if
519
520 ! apply limits to ALL available cloud variables
521 if ((applyLimitToAllVarname .or. applyLimitToCloud) .and. cloudExistInStateVector(statevector)) then
522
523 do varNameIndex = 1, vnl_numvarmaxCloud
524 if (.not. gsv_varExist(statevector, vnl_varNameListCloud(varNameIndex))) cycle
525
526 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_gsv: applying limits to ', &
527 vnl_varNameListCloud(varNameIndex)
528
529 if (statevector%dataKind == 8) then
530 call gsv_getField(statevector, cld_ptr_r8, vnl_varNameListCloud(varNameIndex))
531 else
532 call gsv_getField(statevector, cld_ptr_r4, vnl_varNameListCloud(varNameIndex))
533 end if
534
535 minValueCld = qlim_getMinValueCloud(vnl_varNameListCloud(varNameIndex))
536 maxValueCld = qlim_getMaxValueCloud(vnl_varNameListCloud(varNameIndex))
537
538 lon1 = statevector%myLonBeg
539 lon2 = statevector%myLonEnd
540 lat1 = statevector%myLatBeg
541 lat2 = statevector%myLatEnd
542 lev1 = 1
543 lev2 = gsv_getNumLev(statevector,'TH')
544
545 do stepIndex = 1, statevector%numStep
546
547 !$OMP PARALLEL DO PRIVATE (levIndex, latIndex, lonIndex, cld, cld_modified)
548 do levIndex = lev1, lev2
549 do latIndex = lat1, lat2
550 do lonIndex = lon1, lon2
551 if (statevector%dataKind == 8) then
552 cld = cld_ptr_r8(lonIndex,latIndex,levIndex,stepIndex)
553 else
554 cld = real(cld_ptr_r4(lonIndex,latIndex,levIndex,stepIndex),8)
555 end if
556
557 cld_modified = max(cld,minValueCld)
558 cld_modified = min(cld_modified,maxValueCld)
559 if (statevector%dataKind == 8) then
560 cld_ptr_r8(lonIndex,latIndex,levIndex,stepIndex) = cld_modified
561 else
562 cld_ptr_r4(lonIndex,latIndex,levIndex,stepIndex) = real(cld_modified,4)
563 end if
564
565 end do ! lonIndex
566 end do ! latIndex
567 end do ! levIndex
568 !$OMP END PARALLEL DO
569
570 end do ! stepIndex
571 end do ! varNameIndex
572
573 end if
574
575 end subroutine qlim_rttovLimit_gsv
576
577 !--------------------------------------------------------------------------
578 ! qlim_rttovLimit_ens
579 !--------------------------------------------------------------------------
580 subroutine qlim_rttovLimit_ens(ensemble, varName_opt, applyLimitToCloud_opt)
581 !
582 !:Purpose: To impose RTTOV limits on humidity/cloud
583 !
584 implicit none
585
586 ! Arguments:
587 type(struct_ens), intent(inout) :: ensemble
588 character(len=*), optional, intent(in) :: varName_opt
589 logical, optional, intent(in) :: applyLimitToCloud_opt
590
591 ! Locals:
592 type(struct_vco), pointer :: vco_ptr
593 real(8), allocatable :: press_rttov(:), qmin_rttov(:), qmax_rttov(:)
594 real(8), allocatable :: psfc(:,:),psfcLS(:,:)
595 real(8), allocatable :: qmin3D_rttov(:,:,:), qmax3D_rttov(:,:,:)
596 real(4), pointer :: hu_ptr_r4(:,:,:,:), psfc_ptr_r4(:,:,:,:), psfcLS_ptr_r4(:,:,:,:)
597 real(4), pointer :: cld_ptr_r4(:,:,:,:)
598 real(8), pointer :: pressure(:,:,:)
599 real(8) :: hu, hu_modified
600 real(8) :: cld, cld_modified
601 real(8) :: minValueCld, maxValueCld
602 integer :: lon1, lon2, lat1, lat2, varNameIndex
603 integer :: lonIndex, latIndex, levIndex, stepIndex, varLevIndex, memberIndex
604 integer :: numMember, numStep, numLev, numLev_rttov
605 integer :: fnom, fclos, ierr, nulfile
606 character(len=256) :: fileName
607 character(len=4) :: varName
608 logical, save :: firstTime=.true.
609 logical :: applyLimitToAllVarname
610 logical :: applyLimitToCloud
611
612 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_ens: STARTING'
613
614 if (present(varName_opt)) then
615 applyLimitToAllVarname = .false.
616 varName = varName_opt
617 else
618 applyLimitToAllVarname = .true.
619 varName = 'XXXX'
620 end if
621
622 ! for cloud, limits are applied to ALL cloud variables.
623 if (present(applyLimitToCloud_opt)) then
624 applyLimitToCloud = applyLimitToCloud_opt
625 if (.not. applyLimitToCloud) call utl_abort('qlim_rttovLimit_ens: remove applyLimitToCloud_opt argument')
626 else
627 if (vnl_isCloudVar(varName)) then
628 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_ens: limits are applied to ALL cloud variables'
629 applyLimitToCloud = .true.
630 else
631 applyLimitToCloud = .false.
632 end if
633 end if
634
635 if (applyLimitToCloud) applyLimitToAllVarname = .false.
636
637 if ((applyLimitToAllVarname .or. trim(varName) == 'HU') .and. &
638 ens_varExist(ensemble,'HU')) then
639
640 if ( mmpi_myid == 0 ) write(*,*) 'qlim_rttovLimit_ens: applying limits to HU.'
641
642 if (ens_getDataKind(ensemble) == 8) then
643 call utl_abort('qlim_rttovLimit_ens: Not compatible with dataKind = 8')
644 end if
645
646 ! Read in RTTOV humidity limits
647 fileName = "rttov_h2o_limits.dat"
648 nulfile = 0
649 ierr = fnom(nulfile, fileName, "FMT+OLD+R/O", 0)
650 if( ierr /= 0 ) then
651 if ( mmpi_myid == 0 ) write(*,*) 'fileName = ', fileName
652 call utl_abort('qlim_rttovLimit_ens: error opening the humidity limits file')
653 end if
654
655 read(nulfile,*) numLev_rttov
656 if ( mmpi_myid == 0 .and. firstTime ) write(*,*) 'qlim_rttovLimit_ens: rttov number of levels = ', numLev_rttov
657 allocate(press_rttov(numLev_rttov))
658 allocate(qmin_rttov(numLev_rttov))
659 allocate(qmax_rttov(numLev_rttov))
660 do levIndex = 1, numLev_rttov
661 read(nulfile,*) press_rttov(levIndex), qmax_rttov(levIndex), qmin_rttov(levIndex)
662 end do
663 ierr = fclos(nulfile)
664 press_rttov(:) = press_rttov(:) * mpc_pa_per_mbar_r8
665 qmin_rttov(:) = qmin_rttov(:) / mixratio_to_ppmv
666 qmax_rttov(:) = qmax_rttov(:) / mixratio_to_ppmv
667
668 if (firstTime) then
669 write(*,*) ' '
670 do levIndex = 1, numLev_rttov
671 if ( mmpi_myid == 0 ) write(*,fmt='(" qlim_rttovLimit_ens: LEVEL = ",I4,", PRES = ",F9.0,", HUMIN = ",E10.2,", HUMAX = ",E10.2)') &
672 levIndex, press_rttov(levIndex), qmin_rttov(levIndex), qmax_rttov(levIndex)
673 end do
674 firstTime = .false.
675 end if
676
677 vco_ptr => ens_getVco(ensemble)
678 numLev = ens_getNumLev(ensemble,'TH')
679 numMember = ens_getNumMembers(ensemble)
680 numStep = ens_getNumStep(ensemble)
681 call ens_getLatLonBounds(ensemble, lon1, lon2, lat1, lat2)
682
683 allocate( psfc(numMember,numStep) )
684 if (vco_ptr%vcode == 5100) allocate( psfcLS(numMember,numStep) )
685 allocate( qmin3D_rttov(numMember,numStep,numLev) )
686 allocate( qmax3D_rttov(numMember,numStep,numLev) )
687
688 do latIndex = lat1, lat2
689 do lonIndex = lon1, lon2
690
691 varLevIndex = ens_getKFromLevVarName(ensemble, 1, 'P0')
692 psfc_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
693 psfc(:,:) = real(psfc_ptr_r4(:,:,lonIndex,latIndex),8)
694 if (vco_ptr%vcode == 5100) then
695 varLevIndex = ens_getKFromLevVarName(ensemble, 1, 'P0LS')
696 psfcLS_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
697 psfcLS(:,:) = real(psfcLS_ptr_r4(:,:,lonIndex,latIndex),8)
698 call czp_fetch3DLevels(vco_ptr, psfc, sfcFldLS_opt=psfcLS, fldT_opt=pressure)
699 else
700 call czp_fetch3DLevels(vco_ptr, psfc, fldT_opt=pressure)
701 end if
702
703 ! Interpolate RTTOV limits onto model levels
704 call qlim_lintv_minmax(press_rttov, qmin_rttov, qmax_rttov, numLev_rttov, &
705 numMember, numStep, numLev, pressure, &
706 qmin3D_rttov, qmax3D_rttov)
707
708 do levIndex = 1, numLev
709
710 varLevIndex = ens_getKFromLevVarName(ensemble, levIndex, 'HU')
711 hu_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
712
713 !$OMP PARALLEL DO PRIVATE (stepIndex, memberIndex, hu, hu_modified)
714 do stepIndex = 1, numStep
715 do memberIndex = 1, numMember
716
717 hu = real(hu_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex),8)
718
719 ! limit the humidity according to the rttov limits
720 hu_modified = max(hu, qmin3D_rttov(memberIndex, stepIndex, levIndex) )
721 hu_modified = min(hu_modified, qmax3D_rttov(memberIndex, stepIndex, levIndex) )
722 hu_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex) = real(hu_modified,4)
723
724 end do ! memberIndex
725 end do ! stepIndex
726 !$OMP END PARALLEL DO
727 end do ! levIndex
728
729 deallocate(pressure)
730
731 end do ! lonIndex
732 end do ! latIndex
733
734 deallocate( psfc )
735 if (allocated(psfcLS)) deallocate( psfcLS )
736 deallocate( qmin3D_rttov )
737 deallocate( qmax3D_rttov )
738
739 deallocate(qmax_rttov)
740 deallocate(qmin_rttov)
741 deallocate(press_rttov)
742 end if
743
744 ! apply limits to ALL available cloud variables
745 if ((applyLimitToAllVarname .or. applyLimitToCloud) .and. cloudExistInEnsemble(ensemble)) then
746
747 vco_ptr => ens_getVco(ensemble)
748 numLev = ens_getNumLev(ensemble,'TH')
749 numMember = ens_getNumMembers(ensemble)
750 numStep = ens_getNumStep(ensemble)
751 call ens_getLatLonBounds(ensemble, lon1, lon2, lat1, lat2)
752
753 do varNameIndex = 1, vnl_numvarmaxCloud
754 if (.not. ens_varExist(ensemble, vnl_varNameListCloud(varNameIndex))) cycle
755
756 if (mmpi_myid == 0) write(*,*) 'qlim_rttovLimit_ens: applying limits to ', &
757 vnl_varNameListCloud(varNameIndex)
758
759 minValueCld = qlim_getMinValueCloud(vnl_varNameListCloud(varNameIndex))
760 maxValueCld = qlim_getMaxValueCloud(vnl_varNameListCloud(varNameIndex))
761
762 do latIndex = lat1, lat2
763 do lonIndex = lon1, lon2
764
765 do levIndex = 1, numLev
766 varLevIndex = ens_getKFromLevVarName(ensemble, levIndex, vnl_varNameListCloud(varNameIndex))
767 cld_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
768
769 !$OMP PARALLEL DO PRIVATE (stepIndex, memberIndex, cld, cld_modified)
770 do stepIndex = 1, numStep
771 do memberIndex = 1, numMember
772
773 cld = real(cld_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex),8)
774
775 cld_modified = max(cld,minValueCld)
776 cld_modified = min(cld_modified,maxValueCld)
777 cld_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex) = real(cld_modified,4)
778
779 end do ! memberIndex
780 end do ! stepIndex
781 !$OMP END PARALLEL DO
782
783 end do ! levIndex
784
785 end do ! lonIndex
786 end do ! latIndex
787 end do ! varNameIndex
788
789 end if
790
791 end subroutine qlim_rttovLimit_ens
792
793 !--------------------------------------------------------------------------
794 ! qlim_lintv_minmax
795 !--------------------------------------------------------------------------
796 subroutine qlim_lintv_minmax(press_src, qmin_src, qmax_src, numLev_src, &
797 ni_dest, nj_dest, numLev_dest, press_dest, &
798 qmin_dest, qmax_dest)
799 !
800 !:Purpose: To perform the vertical interpolation in log of pressure and
801 ! and constant value extrapolation of one-dimensional vectors.
802 !
803 implicit none
804
805 ! Arguments:
806 real(8), intent(in) :: press_src(numLev_src) ! Vertical levels, pressure (source)
807 real(8), intent(in) :: qmin_src(numLev_src) ! Vectors to be interpolated (source)
808 real(8), intent(in) :: qmax_src(numLev_src) ! Vectors to be interpolated (source)
809 integer, intent(in) :: numLev_src ! Number of input levels (source)
810 integer, intent(in) :: ni_dest ! Number of profiles
811 integer, intent(in) :: nj_dest ! Number of profiles
812 integer, intent(in) :: numLev_dest ! Number of output levels (destination)
813 real(8), intent(in) :: press_dest(:,:,:) ! Vertical levels, pressure (destination)
814 real(8), intent(out) :: qmin_dest(:,:,:) ! Interpolated profiles (destination)
815 real(8), intent(out) :: qmax_dest(:,:,:) ! Interpolated profiles (destination)
816
817 ! Locals:
818 integer :: ji, jk, jo, ii, jj, ik, iorder
819 real(8) :: zpo(numLev_dest)
820 integer :: il(numLev_dest)
821 real(8) :: zpi(0:numLev_src+1)
822 real(8) :: zqmin_src(0:numLev_src+1), zqmax_src(0:numLev_src+1)
823 real(8) :: zw1, zw2, zp, xi, zrt, zp1, zp2
824
825 zpi(0)=200000.d0
826 zpi(numLev_src+1)=200000.d0
827
828 ! Determine if input pressure levels are in ascending or
829 ! descending order.
830 if ( press_src(1) < press_src(numLev_src) ) then
831 iorder = 1
832 else
833 iorder = -1
834 end if
835
836 ! Source levels
837 do jk = 1, numLev_src
838 zpi(jk) = press_src(jk)
839 zqmin_src(jk) = qmin_src(jk)
840 zqmax_src(jk) = qmax_src(jk)
841 enddo
842 zqmin_src(0) = qmin_src(1)
843 zqmin_src(numLev_src+1) = qmin_src(numLev_src)
844 zqmax_src(0) = qmax_src(1)
845 zqmax_src(numLev_src+1) = qmax_src(numLev_src)
846
847 do jj = 1, nj_dest
848 do ii = 1, ni_dest
849 zpo(:) = press_dest(ii,jj,:)
850
851 ! Interpolate in log of pressure or extrapolate with constant value
852 ! for each destination pressure level
853
854 ! Find the adjacent level below
855 il(:) = 0
856 do ji = 1, numLev_src
857 do jo = 1, numLev_dest
858 zrt = zpo(jo)
859 zp = zpi(ji)
860 xi = sign(1.0d0,iorder*(zrt-zp))
861 il(jo) = il(jo) + max(0.0d0,xi)
862 end do
863 end do
864
865 ! Interpolation/extrapolation
866 do jo = 1, numLev_dest
867 ik = il(jo)
868 zp = zpo(jo)
869 zp1 = zpi(ik)
870 zp2 = zpi(ik+1)
871 zw1 = log(zp/zp2)/log(zp1/zp2)
872 zw2 = 1.d0 - zw1
873 qmin_dest(ii,jj,jo) = zw1*zqmin_src(ik) + zw2*zqmin_src(ik+1)
874 qmax_dest(ii,jj,jo) = zw1*zqmax_src(ik) + zw2*zqmax_src(ik+1)
875 end do
876
877 end do ! ii
878 end do ! jj
879
880 end subroutine qlim_lintv_minmax
881
882 !--------------------------------------------------------------------------
883 ! qlim_setMin_ens
884 !--------------------------------------------------------------------------
885 subroutine qlim_setMin_ens(ensemble,huMinValue)
886 !
887 !:Purpose: To impose lower limit on humidity variable of an ensemble
888 !
889 implicit none
890
891 ! Arguments:
892 type(struct_ens), intent(inout) :: ensemble
893 real(8), intent(in) :: huMinValue
894
895 ! Locals:
896 real(4), pointer :: hu_ptr_r4(:,:,:,:)
897 real(4) :: hu, hu_modified
898 integer :: lon1, lon2, lat1, lat2, numLev
899 integer :: lonIndex, latIndex, levIndex, stepIndex, memberIndex, varLevIndex
900
901 if (mmpi_myid == 0) write(*,*) 'qlim_setMin_ens: STARTING'
902
903 if (ens_getDataKind(ensemble) == 8) then
904 call utl_abort('qlim_setMin_ens: Not compatible with dataKind = 8')
905 end if
906
907 if( .not. ens_varExist(ensemble,'HU') ) then
908 if( mmpi_myid == 0 ) write(*,*) 'qlim_setMin_ens: ensemble does not ' // &
909 'contain humidity ... doing nothing'
910 return
911 end if
912
913 numLev = ens_getNumLev(ensemble,'TH')
914 call ens_getLatLonBounds(ensemble, lon1, lon2, lat1, lat2)
915
916 do latIndex = lat1, lat2
917 do lonIndex = lon1, lon2
918 do levIndex = 1, numLev
919 varLevIndex = ens_getKFromLevVarName(ensemble, levIndex, 'HU')
920 hu_ptr_r4 => ens_getOneLev_r4(ensemble,varLevIndex)
921
922 !$OMP PARALLEL DO PRIVATE (stepIndex, memberIndex, hu, hu_modified)
923 do stepIndex = 1, ens_getNumStep(ensemble)
924 do memberIndex = 1, ens_getNumMembers(ensemble)
925 hu = hu_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex)
926 hu_modified = max(hu, real(huMinValue,4))
927 hu_ptr_r4(memberIndex,stepIndex,lonIndex,latIndex) = hu_modified
928 end do ! memberIndex
929 end do ! stepIndex
930 !$OMP END PARALLEL DO
931
932 end do ! levIndex
933 end do ! lonIndex
934 end do ! latIndex
935
936 end subroutine qlim_setMin_ens
937
938 !-----------------------------------------------------------------------
939 ! qlim_getMinValueCloud
940 !----------------------------------------------------------------------
941 function qlim_getMinValueCloud(varName) result(minValue)
942 !
943 !:Purpose: Return the minValue for the hydrometeor.
944 !
945 implicit none
946
947 ! Arguments:
948 character(len=*), intent(in) :: varName
949 ! Result:
950 real(8) :: minValue
951
952 ! readNameList runs one time during program execution
953 call readNameList
954
955 select case (trim(varName))
956 case ('LWCR')
957 minValue = qlim_minValueLWCR
958 case ('IWCR')
959 minValue = qlim_minValueIWCR
960 case ('RF')
961 minValue = qlim_minValueRF
962 case ('SF')
963 minValue = qlim_minValueSF
964 case ('CLDR')
965 minValue = qlim_minValueCLDR
966 case default
967 write(*,*)
968 write(*,*) 'ERROR unknown varName: ', trim(varName)
969 call utl_abort('qlim_getMinValueCloud')
970 end select
971
972 end function qlim_getMinValueCloud
973
974 !-----------------------------------------------------------------------
975 ! qlim_getMaxValueCloud
976 !----------------------------------------------------------------------
977 function qlim_getMaxValueCloud(varName) result(maxValue)
978 !
979 !:Purpose: Return the maxValue for the hydrometeor.
980 !
981 implicit none
982
983 ! Arguments:
984 character(len=*), intent(in) :: varName
985 ! Result:
986 real(8) :: maxValue
987
988 ! readNameList runs one time during program execution
989 call readNameList
990
991 select case (trim(varName))
992 case ('LWCR')
993 maxValue = qlim_maxValueLWCR
994 case ('IWCR')
995 maxValue = qlim_maxValueIWCR
996 case ('RF')
997 maxValue = qlim_maxValueRF
998 case ('SF')
999 maxValue = qlim_maxValueSF
1000 case ('CLDR')
1001 maxValue = qlim_maxValueCLDR
1002 case default
1003 write(*,*)
1004 write(*,*) 'ERROR unknown varName: ', trim(varName)
1005 call utl_abort('qlim_getMaxValueCloud')
1006 end select
1007
1008 end function qlim_getMaxValueCloud
1009
1010 !-----------------------------------------------------------------------
1011 ! cloudExistInEnsemble
1012 !----------------------------------------------------------------------
1013 function cloudExistInEnsemble(ensemble) result(cloudExist)
1014 !
1015 !:Purpose: determine if any cloud variable exists in the ensemble.
1016 !
1017 implicit none
1018
1019 ! Arguments:
1020 type(struct_ens), intent(in) :: ensemble
1021 ! Result:
1022 logical :: cloudExist
1023
1024 ! Locals:
1025 integer :: varNameIndex
1026
1027 cloudExist = .false.
1028 do varNameIndex = 1, vnl_numvarmaxCloud
1029 if (ens_varExist(ensemble, vnl_varNameListCloud(varNameIndex))) then
1030 cloudExist = .true.
1031 return
1032 end if
1033 end do
1034
1035 end function cloudExistInEnsemble
1036
1037 !-----------------------------------------------------------------------
1038 ! cloudExistInStateVector
1039 !----------------------------------------------------------------------
1040 function cloudExistInStateVector(stateVector) result(cloudExist)
1041 !
1042 !:Purpose: determine if any cloud variable exists in the stateVector.
1043 !
1044 implicit none
1045
1046 ! Arguments:
1047 type(struct_gsv), intent(in) :: stateVector
1048 ! Result:
1049 logical :: cloudExist
1050
1051 ! Locals:
1052 integer :: varNameIndex
1053
1054 cloudExist = .false.
1055 do varNameIndex = 1, vnl_numvarmaxCloud
1056 if (gsv_varExist(stateVector, vnl_varNameListCloud(varNameIndex))) then
1057 cloudExist = .true.
1058 return
1059 end if
1060 end do
1061
1062 end function cloudExistInStateVector
1063
1064end module humidityLimits_mod