1MODULE biasCorrectionConv_mod
2 ! MODULE biasCorrectionConv_mod (prefix='bcc' category='1. High-level functionality')
3 !
4 !:Purpose: Performs bias correction for conventional observations.
5 !
6 use utilities_mod
7 use obsSpaceData_mod
8 use MathPhysConstants_mod
9 use midasMpi_mod
10 use bufr_mod
11 use codtyp_mod
12 use timeCoord_mod
13
14 implicit none
15 save
16 private
17 public :: bcc_applyAIBcor, bcc_applyGPBcor, bcc_applyUABcor
18 public :: bcc_biasActive
19
20 ! This variable is set to .true. when bcc_readConfig() [the routine that reads &NAMBIASCONV namelist]
21 ! is called for the first time to read/initialize the bias correction namelist variables.
22 logical :: initialized = .false.
23
24 integer, parameter :: nPhases=3, nLevels=5, nAircraftMax=100000
25 integer, parameter :: nStationMaxGP = 10000
26 integer, parameter :: phaseLevel = 3
27 integer, parameter :: phaseAscent = 5
28 integer, parameter :: phaseDescent = 6
29 integer, parameter :: phaseLevelIndex = 1
30 integer, parameter :: phaseAscentIndex = 2
31 integer, parameter :: phaseDescentIndex = 3
32
33 integer, parameter :: nSondesMax = 18, nMandLevs = 16
34 integer, parameter :: nStationMaxUA = 1000
35 integer, parameter :: Index500mb = 5
36
37 ! Missing values in the input bias correction files for AI, GP and UA families
38 real(8), parameter :: aiMissingValue = 99.d0
39 real(8), parameter :: gpMissingValue = -999.00d0
40 real(8), parameter :: uaMissingValue = -99.0d0
41
42 integer :: nbAircrafts, nbGpStations
43 real(8), allocatable :: AIttCorrections(:,:,:)
44 real(8), allocatable :: ztdCorrections(:)
45 real(8), allocatable :: ttCorrections(:,:,:,:), tdCorrections(:,:,:,:)
46 real(8), allocatable :: ttCorrectionsStn(:,:,:,:), tdCorrectionsStn(:,:,:,:)
47
48 character(len=9), allocatable :: aircraftIds(:), gpsStations(:), uaStations(:)
49 character(len=8) :: sondeTypes(nSondesMax)
50
51 real(8) :: mandLevs(nMandLevs), tolPress(nMandLevs)
52
53 logical :: bcc_aiBiasActive, bcc_gpBiasActive, bcc_uaBiasActive
54 logical, allocatable :: biasCorrPresentStype(:,:,:), biasCorrPresentStn(:,:,:)
55
56 ! Bias correction files (must be in program working directory)
57 character(len=8), parameter :: aiBcFile = "ai_bcors", gpBcFile = "gp_bcors"
58 character(len=14), parameter :: uaBcFileStype = "ua_bcors_stype", uaBcFileStn = "ua_bcors_stn"
59
60 integer, external :: fnom, fclos, newdate
61
62 ! Namelist variables
63 logical :: aiBiasActive ! Control if bias correction is applied to aircraft data
64 logical :: gpBiasActive ! Control if bias correction is applied to ground-based GPS data
65 logical :: uaBiasActive ! Control if bias correction is applied to radiosonde data
66 logical :: aiRevOnly ! Don't apply new correction but simply reverse any old corrections for AI
67 logical :: gpRevOnly ! Don't apply new correction but simply reverse any old corrections for GP
68 logical :: uaRevOnly ! Don't apply new correction but simply reverse any old corrections for UA
69 logical :: uaRejUnBcor ! Set DATA QC flag bit 11 on to exclude uncorrected UA observations from assimilation
70 integer :: uaNbiasCat ! Number of bias profile categories in UA bcor files, e.g. 1, or 2 for "asc" and "desc" phase categories
71 integer :: uaNlatBands ! Number of latitude bands in ua_bcors_stype bcor file (= 5 or 1). Set to 1 if there are no latitude bands in file
72 integer :: uaNprofsMin ! Min number of bias profiles required for a station/stype/time-of-day to use biases 'ua_bcors_stn' as corrections
73 character(len=8) :: nlSondeTypes(nSondesMax) ! List of radiosonde type names
74 integer :: nlSondeCodes(nSondesMax,20) ! List of radiosonde type codes
75 integer :: nlNbSondes ! Number of radiosonde types in lists
76
77 ! Structure to hold dictionary containing the BUFR sonde type codes associated with each sonde type
78 type sondeType
79 character(len=8) :: name
80 integer :: codes(20)
81 end type sondeType
82
83 type(sondeType), allocatable :: rsTypes(:)
84
85 namelist /nambiasconv/ aiBiasActive,gpBiasActive,aiRevOnly,gpRevOnly,uaBiasActive,uaRevOnly,uaNprofsMin,uaRejUnBcor,uaNbiasCat,uaNlatBands
86 namelist /namsondetypes/ nlNbSondes, nlSondeTypes, nlSondeCodes
87
88 ! 16 mandatory pressure levels (mb) on which radiosonde bias profiles are defined
89 data mandLevs /1000.d0,925.d0,850.d0,700.d0,500.d0,400.d0,300.d0,250.d0,200.d0,150.d0,100.d0,70.d0,50.d0,30.d0,20.d0,10.d0/
90 ! +/- tolerance (mb) for matching a radiosonde observation pressure to one of the mandatory levels
91 data tolPress / 10.d0, 10.d0, 10.d0, 10.d0, 10.d0, 10.d0, 10.d0, 5.d0, 5.d0, 5.d0, 5.d0, 2.d0, 2.d0, 2.d0, 1.d0, 1.d0/
92
93CONTAINS
94
95 !-----------------------------------------------------------------------
96 ! bcc_readConfig
97 !-----------------------------------------------------------------------
98 subroutine bcc_readConfig()
99 !
100 !:Purpose: Read NAMBIASCONV namelist section and NAMSONDETYPES section if uaBiasActive=true
101 !
102 implicit none
103
104 ! Locals:
105 integer :: ierr, nulnam, sondeIndex
106
107 if ( initialized ) then
108 write(*,*) "bcc_readConfig has already been called. Returning..."
109 return
110 end if
111
112 ! set default values for namelist variables
113 aiBiasActive = .false. ! bias correct AI data (TT)
114 gpBiasActive = .false. ! bias correct GP data (ZTD)
115 uaBiasActive = .false. ! bias correct UA data (TT,ES)
116 aiRevOnly = .false. ! AI: don't apply new correction but simply reverse any old corrections
117 gpRevOnly = .false. ! GP: don't apply new correction but simply reverse any old corrections
118 uaRevOnly = .false. ! UA: don't apply new correction but simply reverse any old corrections
119 uaRejUnBcor = .false. ! UA: Set DATA QC flag bit 11 on to exclude uncorrected UA observations from assimilation
120 uaNprofsMin = 100 ! UA: If Nprofs for a given stn/stype < uaNprofsMin, flag the bcors as unusable
121 uaNbiasCat = 1 ! UA: Number of bias profile categories in UA bcor files: 1, or 2 for "asc" and "desc" categories
122 uaNlatBands = 1 ! UA: Number of latitude bands in ua_bcors_stype file (1 or 5): 1 = no bands (global biases).
123 ! read in the namelist NAMBIASCONV
124 if ( utl_isNamelistPresent('nambiasconv','./flnml') ) then
125 nulnam = 0
126 ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
127 read(nulnam,nml=nambiasconv,iostat=ierr)
128 if ( ierr /= 0 ) call utl_abort('bcc_readConfig: Error reading namelist section NAMBIASCONV')
129 if ( mmpi_myid == 0 ) write(*,nml=nambiasconv)
130 ierr = fclos(nulnam)
131 else
132 write(*,*)
133 write(*,*) 'bcc_readconfig: NAMBIASCONV section is missing in the namelist. The default values will be used.'
134 end if
135
136 bcc_aiBiasActive = aiBiasActive
137 bcc_gpBiasActive = gpBiasActive
138 bcc_uaBiasActive = uaBiasActive
139
140 ! read in the namelist NAMSONDETYPES
141 if ( uaBiasActive .and. .not.uaRevOnly ) then
142 if ( utl_isNamelistPresent('namsondetypes','./flnml') ) then
143 nlNbSondes = MPC_missingValue_INT
144 nlSondeTypes(:) = 'empty'
145 nlSondeCodes(:,:) = MPC_missingValue_INT
146 nulnam = 0
147 ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
148 read(nulnam,nml=namsondetypes,iostat=ierr)
149 if ( ierr /= 0 ) call utl_abort('bcc_readConfig: Error reading namelist section NAMSONDETYPES')
150 if (nlNbSondes /= MPC_missingValue_INT) then
151 call utl_abort('bcc_readConfig: check namsondetypes namelist section, you should remove nlNbSondes')
152 end if
153 nlNbSondes = 0
154 do sondeIndex = 1, nSondesMax
155 if (trim(nlSondeTypes(sondeIndex)) == 'empty') exit
156 nlNbSondes = nlNbSondes + 1
157 end do
158 if ( mmpi_myid == 0 ) write(*,nml=namsondetypes)
159 ierr = fclos(nulnam)
160 else
161 write(*,*)
162 call utl_abort('bcc_readconfig: NAMSONDETYPES section is missing in the namelist!')
163 end if
164 if ( nlNbSondes > nSondesMax ) call utl_abort('bcc_readconfig: Number of sonde types in namelist exceeds nSondesMax!')
165 if ( nlNbSondes <= 0 ) call utl_abort('bcc_readconfig: Number of sonde types in namelist <= 0!')
166 allocate( rsTypes(nlNbSondes) )
167 do sondeIndex = 1, nlNbSondes
168 rsTypes(sondeIndex)%name = nlSondeTypes(sondeIndex)
169 rsTypes(sondeIndex)%codes(:) = nlSondeCodes(sondeIndex,:)
170 end do
171 end if
172
173 initialized = .true.
174
175 end subroutine bcc_readConfig
176
177 !-----------------------------------------------------------------------
178 ! bcc_UACorrection
179 !-----------------------------------------------------------------------
180 function bcc_UACorrection(timeOfDayX,corrNight,corrDay) result(uaCorrection)
181 !
182 !:Purpose: Returns a UA bias correction from day and night corrections
183 !
184 implicit none
185
186 ! Arguments:
187 real(8), intent(in) :: timeOfDayX ! 0.0 (night) <= timeOfDayX <= 1.0 (day), depends on solar_elev
188 real(8), intent(in) :: corrNight ! night bias correction
189 real(8), intent(in) :: corrDay ! day bias correction
190 ! Result:
191 real(8) :: uaCorrection
192
193 uaCorrection = MPC_missingValue_R8
194
195 if ( timeOfDayX == 0.0d0 ) then
196 uaCorrection = corrNight
197 else if ( timeOfDayX == 1.0d0 ) then
198 uaCorrection = corrDay
199 else
200 if ( corrNight /= MPC_missingValue_R8 .and. corrDay /= MPC_missingValue_R8 ) then
201 uaCorrection = timeOfDayX*corrDay + (1.0d0-timeOfDayX)*corrNight
202 end if
203 end if
204
205 end function bcc_UACorrection
206
207 !-----------------------------------------------------------------------
208 ! bcc_GetUACorrection
209 !-----------------------------------------------------------------------
210 subroutine bcc_GetUACorrection(varName,stnIndex,sondeTypeIndex,sondeType,biasProfileCategory,timeOfDayX,latband,obsPressure,corr,sourceCorr)
211 !
212 !:Purpose: Return a TT or TD bias correction (corr)
213
214 !
215 ! varName = (str) 'TT' or 'TD'
216 ! stnIndex = (int) station index (in uaStations) = -1 if station was not found in bcor file
217 ! sondeTypeIndex = (int) sonde type index (in rsTypes) = 0 if sonde-type code is not associated with any type
218 ! sondeType = (str) sonde-type from rsTypes
219 ! biasProfileCategory = (int) bias profile category = 1 (ascent/none) or 3 (descent)
220 ! timeOfDayX = (float) 0.0 (night) <= TimeOfDayX <= 1.0 (day), depends on solar_elev
221 ! latband = (int) 1-5
222 ! obsPressure = (float) pressure level (mb) of observation
223 !
224 ! Also returns info regarding the source of the correction returned:
225 ! sourceCorr = (str) 'none', 'stn', or 'stype'
226 !
227 ! Requires TT and TD correction profiles read from UA bcor file ua_bcors_stn and stored in
228 ! ttCorrectionsStn(stnIndex,sondeTypeIndex,j,MandLev)
229 ! tdCorrectionsStn(stnIndex,sondeTypeIndex,j,MandLev)
230 ! with backup corrections by sonde-type read from UA bcor file ua_bcors_stype and stored in
231 ! ttCorrections(sondeTypeIndex,latband,j,MandLev)
232 ! tdCorrections(sondeTypeIndex,latband,j,MandLev)
233 ! where MandLev = 1 (1000 mb) to 16 (10 mb)
234 ! If uaNbiasCat = 1 (biasProfileCategory = 1)
235 ! j = 1 (night)
236 ! 2 (day)
237 ! If uaNbiasCat = 2 (biasProfileCategory = 1 [ascent] or 3 [descent])
238 ! j = 1 (night-ascent)
239 ! 2 (day-ascent)
240 ! 3 (night-descent)
241 ! 4 (day-descent)
242 !
243 ! Interpolation of the correction profiles on mandatory levels is used to get the
244 ! correction at the observation level (obsPressure).
245 ! Persistence is applied for observations outside the range of the mandatory levels.
246 !
247 implicit none
248
249 ! Arguments:
250 integer, intent(in) :: stnIndex
251 integer, intent(in) :: sondeTypeIndex
252 integer, intent(in) :: biasProfileCategory
253 integer, intent(in) :: latband
254 real(8), intent(in) :: timeOfDayX
255 real(8), intent(in) :: obsPressure
256 character(len=*), intent(in) :: varName
257 character(len=*), intent(in) :: sondeType
258 real(8), intent(out) :: corr
259 character(len=*), intent(out) :: sourceCorr
260
261 ! Locals:
262 real(8) :: corrProfileStnDay(16), corrProfileStnNight(16), corrProfileStypeDay(16), corrProfileStypeNight(16)
263 real(8) :: corrDay, corrNight
264 real(8) :: weight1, weight2, deltaPressure, pressureAbove, pressureBelow, corrAbove, corrBelow
265 real(8) :: corrAboveNight, corrBelowNight, corrAboveDay, corrBelowDay
266 integer :: level, levelIndex
267 logical :: profileExistsStn, profileExistsStype, doInterp
268
269 sourceCorr = "none"
270
271 ! Bias correction by station and sonde-type
272 if ( stnIndex == -1 ) then
273 profileExistsStn = .false.
274 else
275 if ( sondeTypeIndex > 0 ) then
276 ! Check that correction profile for this station, sonde-type and TimeOfDay is available for use
277 profileExistsStn = biasCorrPresentStn(stnIndex,sondeTypeIndex,biasProfileCategory)
278 else ! There are no bias profiles for this station/stype combination
279 profileExistsStn = .false.
280 end if
281 end if
282
283 ! BACKUP: Bias correction by sonde-type
284 profileExistsStype = .false.
285 if ( trim(sondeType) /= 'unknown' .and. trim(sondeType) /= 'Others' .and. trim(sondeType) /= 'None' ) then
286 ! Check if correction profile for this sonde-type,latband,and TimeOfDay is available for use
287 profileExistsStype = biasCorrPresentStype(sondeTypeIndex,latband,biasProfileCategory)
288 end if
289
290 if ( .not.profileExistsStn .and. .not.profileExistsStype ) then
291 corr = MPC_missingValue_R8
292 return
293 end if
294
295 ! Fill the night and day bias correction profiles
296 if ( varName == 'TT' ) then
297 if ( profileExistsStn ) then
298 corrProfileStnNight(:) = ttCorrectionsStn(stnIndex,sondeTypeIndex,biasProfileCategory,:)
299 corrProfileStnDay(:) = ttCorrectionsStn(stnIndex,sondeTypeIndex,biasProfileCategory+1,:)
300 end if
301 if ( profileExistsStype ) then
302 corrProfileStypeNight(:) = ttCorrections(sondeTypeIndex,latband,biasProfileCategory,:)
303 corrProfileStypeDay(:) = ttCorrections(sondeTypeIndex,latband,biasProfileCategory+1,:)
304 end if
305 else if ( varName == 'TD' ) then
306 if ( profileExistsStn ) then
307 corrProfileStnNight(:) = tdCorrectionsStn(stnIndex,sondeTypeIndex,biasProfileCategory,:)
308 corrProfileStnDay(:) = tdCorrectionsStn(stnIndex,sondeTypeIndex,biasProfileCategory+1,:)
309 end if
310 if ( profileExistsStype ) then
311 corrProfileStypeNight(:) = tdCorrections(sondeTypeIndex,latband,biasProfileCategory,:)
312 corrProfileStypeDay(:) = tdCorrections(sondeTypeIndex,latband,biasProfileCategory+1,:)
313 end if
314 else
315 call utl_abort('bcc_GetUACorrection: Unsupported varName '//varName)
316 end if
317
318 corr = MPC_missingValue_R8
319 doInterp = .true.
320
321 !--------------------------------------------------------------------------------------
322 ! Get the correction at the observation level (obsPressure)
323 !--------------------------------------------------------------------------------------
324
325 ! Check if obsPressure is outside range of levels (no interpolation possible)
326 if ( obsPressure >= mandLevs(1) ) then
327 levelIndex = 1
328 else if ( obsPressure <= mandLevs(nMandLevs) ) then
329 levelIndex = nMandLevs
330 else
331 levelIndex = 0
332 end if
333
334 ! Check if obsPressure is close to one of the 16 mandatory levels (no interpolation needed)
335 if ( levelIndex == 0 ) then
336 do level = 1, nMandLevs
337 if ( abs(obsPressure-mandLevs(level)) <= tolPress(level) ) then
338 levelIndex = level
339 exit
340 end if
341 end do
342 end if
343
344 ! If obsPressure is close to one of the 16 mandatory levels get the correction for the mandatory level:
345 ! Use correction by station with correction by stype as backup
346 if ( levelIndex > 0 ) then
347 if ( profileExistsStn ) then
348 corrNight = corrProfileStnNight(levelIndex)
349 corrDay = corrProfileStnDay(levelIndex)
350 sourceCorr = "stn"
351 corr = bcc_UACorrection(timeOfDayX,corrNight,corrDay)
352 if ( corr == MPC_missingValue_R8 ) then
353 sourceCorr = "none"
354 if ( profileExistsStype ) then
355 corrNight = corrProfileStypeNight(levelIndex)
356 corrDay = corrProfileStypeDay(levelIndex)
357 sourceCorr = "stype"
358 corr = bcc_UACorrection(timeOfDayX,corrNight,corrDay)
359 if ( corr == MPC_missingValue_R8 ) sourceCorr = "none"
360 end if
361 end if
362 else
363 if ( profileExistsStype ) then
364 corrNight = corrProfileStypeNight(levelIndex)
365 corrDay = corrProfileStypeDay(levelIndex)
366 sourceCorr = "stype"
367 corr = bcc_UACorrection(timeOfDayX,corrNight,corrDay)
368 if ( corr == MPC_missingValue_R8 ) sourceCorr = "none"
369 end if
370 end if
371 doInterp = .false.
372 end if
373
374 ! or interpolate to get correction for observation level obsPressure:
375 ! Use corrections by station with corrections by stype as backup
376 if ( doInterp ) then
377 corrAbove = MPC_missingValue_R8
378 corrBelow = MPC_missingValue_R8
379
380 if ( profileExistsStn ) then
381 do level = 1, nMandLevs-1
382 if ( obsPressure <= mandLevs(level) .and. obsPressure > mandLevs(level+1) ) then
383 pressureBelow = mandLevs(level)
384 pressureAbove = mandLevs(level+1)
385 corrBelowNight = corrProfileStnNight(level)
386 corrAboveNight = corrProfileStnNight(level+1)
387 corrBelowDay = corrProfileStnDay(level)
388 corrAboveDay = corrProfileStnDay(level+1)
389 exit
390 end if
391 end do
392 sourceCorr = "stn"
393 if ( timeOfDayX == 0.0d0 ) then
394 corrBelow = corrBelowNight
395 corrAbove = corrAboveNight
396 else if ( timeOfDayX == 1.0d0 ) then
397 corrBelow = corrBelowDay
398 corrAbove = corrAboveDay
399 else
400 if ( corrBelowNight /= MPC_missingValue_R8 .and. corrBelowDay /= MPC_missingValue_R8 ) then
401 corrBelow = timeOfDayX*corrBelowDay + (1.0d0-timeOfDayX)*corrBelowNight
402 end if
403 if ( corrAboveNight /= MPC_missingValue_R8 .and. corrAboveDay /= MPC_missingValue_R8 ) then
404 corrAbove = timeOfDayX*corrAboveDay + (1.0d0-timeOfDayX)*corrAboveNight
405 end if
406 end if
407 end if
408
409 if ( corrAbove == MPC_missingValue_R8 .or. corrBelow == MPC_missingValue_R8 ) then
410 if ( profileExistsStype ) then
411 do level = 1, nMandLevs-1
412 if ( obsPressure <= mandLevs(level) .and. obsPressure > mandLevs(level+1) ) then
413 pressureBelow = mandLevs(level)
414 pressureAbove = mandLevs(level+1)
415 corrBelowNight = corrProfileStypeNight(level)
416 corrAboveNight = corrProfileStypeNight(level+1)
417 corrBelowDay = corrProfileStypeDay(level)
418 corrAboveDay = corrProfileStypeDay(level+1)
419 exit
420 end if
421 end do
422 sourceCorr = "stype"
423 if ( timeOfDayX == 0.0d0 ) then
424 corrBelow = corrBelowNight
425 corrAbove = corrAboveNight
426 else if ( timeOfDayX == 1.0d0 ) then
427 corrBelow = corrBelowDay
428 corrAbove = corrAboveDay
429 else
430 if ( corrBelowNight /= MPC_missingValue_R8 .and. corrBelowDay /= MPC_missingValue_R8 ) then
431 corrBelow = timeOfDayX*corrBelowDay + (1.0d0-timeOfDayX)*corrBelowNight
432 end if
433 if ( corrAboveNight /= MPC_missingValue_R8 .and. corrAboveDay /= MPC_missingValue_R8 ) then
434 corrAbove = timeOfDayX*corrAboveDay + (1.0d0-timeOfDayX)*corrAboveNight
435 end if
436 end if
437 end if
438 end if
439 deltaPressure = log10(pressureBelow)-log10(pressureAbove)
440 weight1 = (log10(pressureBelow) - log10(obsPressure)) / deltaPressure
441 weight2 = (log10(obsPressure) - log10(pressureAbove)) / deltaPressure
442 if ( corrAbove /= MPC_missingValue_R8 .and. corrBelow /= MPC_missingValue_R8 ) then
443 corr = weight1*corrAbove + weight2*corrBelow
444 else if ( corrAbove /= MPC_missingValue_R8 .and. max(weight1,weight2) == weight1 ) then
445 corr = corrAbove
446 else if ( corrBelow /= MPC_missingValue_R8 .and. max(weight1,weight2) == weight2 ) then
447 corr = corrBelow
448 else
449 corr = MPC_missingValue_R8
450 sourceCorr = "none"
451 end if
452 end if
453
454 end subroutine bcc_GetUACorrection
455
456 !-----------------------------------------------------------------------
457 ! bcc_StationIndex
458 !-----------------------------------------------------------------------
459 function bcc_StationIndex(station) result(stationIndexOut)
460 !
461 !:Purpose: Return the station index (order in array uaStations) corresponding to station
462 ! Returns -1 if station is not found in uaStations.
463 !
464 implicit none
465
466 ! Arguments:
467 character(len=*), intent(in) :: station
468 ! Result:
469 integer :: stationIndexOut
470
471 ! Locals:
472 integer :: stationIndex
473
474 if ( allocated(uaStations) ) then
475 stationIndexOut = -1
476 do stationIndex = 1, nStationMaxUA
477 if ( trim(uaStations(stationIndex)) == trim(station) ) then
478 stationIndexOut = stationIndex
479 exit
480 end if
481 end do
482 else
483 call utl_abort('bcc_StationIndex: array uaStations not allocated!')
484 end if
485
486 end function bcc_StationIndex
487
488 !-----------------------------------------------------------------------
489 ! bcc_SondeIndex
490 !-----------------------------------------------------------------------
491 function bcc_SondeIndex(sondeType) result(sondeIndex)
492 !
493 !:Purpose: Return the Sonde Type index (order in array rsTypes) corresponding to the SondeType
494 ! Requires array of sondeType structures (rsTypes) to be allocated and filled.
495 ! Returns -1 if SondeType is not found in rsTypes.
496 !
497 implicit none
498
499 ! Arguments:
500 character(len=*), intent(in) :: sondeType
501 ! Result:
502 integer :: sondeIndex
503
504 ! Locals:
505 integer :: typeIndex, ntypes
506
507 if ( allocated(rsTypes) ) then
508 ntypes = nlNbSondes
509 else
510 call utl_abort('bcc_SondeIndex: array rsTypes not allocated!')
511 end if
512
513 SondeIndex = -1
514 do typeIndex = 1, ntypes
515 if ( trim(rsTypes(typeIndex)%name) == trim(sondeType) ) then
516 sondeIndex = typeIndex
517 exit
518 end if
519 end do
520
521 end function bcc_SondeIndex
522
523 !-----------------------------------------------------------------------
524 ! bcc_GetSondeType
525 !-----------------------------------------------------------------------
526 subroutine bcc_GetSondeType(sondeTypeCode,sondeType,sondeTypeIndex)
527 !
528 !:Purpose: Returns the sonde type and index given a BUFR table sondeTypeCode (BUFR elem 002011)
529 ! Returns sondeType='unknown', sondeTypeIndex=0 if sondeTypeCode is not found.
530 !
531 !
532 ! Requires array of sonde_type structures (rsTypes) to be allocated and filled with the
533 ! sonde type codes associated with each sonde-type (read from namelist).
534 !
535 implicit none
536
537 ! Arguments:
538 integer, intent(in) :: sondeTypeCode
539 character(len=*), intent(out) :: sondeType
540 integer, intent(out) :: sondeTypeIndex
541
542 ! Locals:
543 integer :: typeIndex, ntypes, sondeCode
544
545 if ( allocated(rsTypes) ) then
546 ntypes = nlNbSondes
547 else
548 call utl_abort('bcc_GetSondeType: array rsTypes not allocated!')
549 end if
550
551 if ( sondeTypeCode == 190 .or. sondeTypeCode == 192 ) then ! NCAR dropsonde (BUFR only)
552 sondeCode = 13 ! RS92 code
553 else if ( sondeTypeCode == 191 .or. sondeTypeCode == 193 ) then ! NCAR dropsonde (BUFR only)
554 sondeCode = 41 ! RS41 code
555 else if ( sondeTypeCode >=100 .and. sondeTypeCode < 200 ) then
556 sondeCode = sondeTypeCode - 100
557 else
558 sondeCode = sondeTypeCode
559 end if
560
561 sondeType = 'unknown'
562 sondeTypeIndex = 0
563 do typeIndex = 1, ntypes
564 if ( ANY(rsTypes(typeIndex)%codes == sondeCode) ) then
565 sondeType = rsTypes(typeIndex)%name
566 sondeTypeIndex = typeIndex
567 exit
568 end if
569 end do
570
571 end subroutine bcc_GetSondeType
572
573 !-----------------------------------------------------------------------
574 ! bcc_GetSolarElevation
575 !-----------------------------------------------------------------------
576 subroutine bcc_GetSolarElevation(lat,lon,date,time,solarElev)
577 !
578 !:Purpose: Returns the solar elevation angle (degrees) given lat,lon,date(yyyymmdd),time(hhmm)
579 !
580
581 ! lat, lon = obsSpaceData header column OBS_LAT, OBS_LON (radians)
582 ! date, time = obsSpaceData header column OBS_DAT (yyyymmdd), OBS_ETM (hhmm) -or-
583 ! datestamp = tim_getDatestamp() -- date stamp for central (analysis) time
584 ! ier = newdate(datestamp,date,time,-3)
585 ! time = time/10000
586 !
587 implicit none
588
589 ! Arguments:
590 integer, intent(in) :: date ! yyyymmdd
591 integer, intent(in) :: time ! hhmm
592 real(8), intent(in) :: lat ! radians
593 real(8), intent(in) :: lon ! radians
594 real(8), intent(out) :: solarElev ! degrees
595
596 ! Locals:
597 integer :: days(13) = (/0,31,28,31,30,31,30,31,31,30,31,30,31/)
598 integer :: leap_years(7) = (/2016,2020,2024,2028,2032,2036,2040/)
599 integer :: yy, mmdd, mm, dd, hh, nn, doy
600 real(8) :: timeUTC, timeLCL, solarDec, hourAngle, csz, sza
601
602 yy = date/10000
603 mmdd = date - yy*10000
604 mm = mmdd/100
605 dd = mmdd - mm*100
606 hh = time/100
607 nn = time - hh*100
608
609 if ( ANY(leap_years == yy) ) days(3) = 29
610 doy = SUM(days(1:mm)) + dd
611 timeUTC = float(hh) + float(nn)/60.0d0
612 timeLCL = timeUTC + (lon*MPC_DEGREES_PER_RADIAN_R8)/15.0d0
613 if ( timeLCL < 0.0d0 ) timeLCL = 24.0d0 + timeLCL
614 if ( timeLCL > 24.0d0 ) timeLCL = timeLCL - 24.0d0
615 solarDec = 0.4093d0 * sin(((2.0d0*MPC_PI_R8)/365.0d0)*(284.0d0 + float(doy)))
616 hourAngle = 15.0d0*(timeLCL-12.0d0) * MPC_RADIANS_PER_DEGREE_R8
617 csz = sin(solarDec)*sin(lat) + cos(solarDec)*cos(lat)*cos(hourAngle)
618 sza = MPC_DEGREES_PER_RADIAN_R8 * acos(csz)
619 solarElev = 90.0d0 - sza
620
621 end subroutine bcc_GetSolarElevation
622
623 !-----------------------------------------------------------------------
624 ! bcc_GetTimeOfDay
625 !-----------------------------------------------------------------------
626 subroutine bcc_GetTimeOfDay(solarElev,timeOfDayX)
627 !
628 !:Purpose: Returns the time-of-day x value (0.0(night) <= x <= 1.0(day))
629 !
630 implicit none
631
632 ! Arguments:
633 real(8), intent(in) :: solarElev ! degrees
634 real(8), intent(out) :: timeOfDayX
635
636 if (solarElev < -7.5d0) then
637 timeOfDayX = 0.0d0
638 else if (solarElev < 22.5d0) then
639 timeOfDayX = (solarElev+7.5d0)/(22.5d0+7.5d0)
640 else
641 timeOfDayX = 1.0d0
642 end if
643
644 end subroutine bcc_GetTimeOfDay
645
646 !----------------------------------------------------------------------------------------
647 ! bcc_uaPhase
648 !----------------------------------------------------------------------------------------
649 function bcc_uaPhase(codeType) result(uaPhase)
650 !
651 !:Purpose: Returns the radiosonde phase (1=ascent, 2=descent) given a header code type
652 !
653 implicit none
654
655 ! Arguments:
656 integer, intent(in) :: codeType
657 ! Result:
658 integer :: uaPhase
659
660 if (codeType == codtyp_get_codtyp('tempdrop')) then
661 uaPhase = 2
662 else
663 uaPhase = 1
664 end if
665
666 end function bcc_uaPhase
667
668 !-----------------------------------------------------------------------
669 ! bcc_LatBand
670 !-----------------------------------------------------------------------
671 function bcc_LatBand(latInRadians) result(latBand)
672 !
673 !:Purpose: Returns latitude band number given latitude (radians)
674 !
675 implicit none
676
677 ! Arguments:
678 real(8), intent(in) :: latInRadians
679 ! Result:
680 integer :: latBand
681
682 ! Locals:
683 real(8) :: latInDegrees
684
685 if ( uaNlatBands /= 5 ) then
686 latBand = -1
687 else
688 latInDegrees = MPC_DEGREES_PER_RADIAN_R8 * latInRadians
689 if (latInDegrees < -90.0d0 ) latBand = 1 ! Should never be the case!
690 if (latInDegrees >= -90.0d0 .and. latInDegrees < -60.0d0) then
691 latBand = 1
692 else if (latInDegrees >= -60.0d0 .and. latInDegrees < -20.0d0) then
693 latBand = 2
694 else if (latInDegrees >= -20.0d0 .and. latInDegrees < 20.0d0) then
695 latBand = 3
696 else if (latInDegrees >= 20.0d0 .and. latInDegrees < 60.0d0) then
697 latBand = 4
698 else
699 latBand = 5
700 end if
701 end if
702
703 end function bcc_LatBand
704
705 !-----------------------------------------------------------------------
706 ! bcc_readAIBiases
707 !-----------------------------------------------------------------------
708 subroutine bcc_readAIBiases(biasEstimateFile)
709 !
710 !:Purpose: Read aircraft (AI) TT bias estimates from bias file and fill bias correction array ttCorrections.
711 ! The first line of the file is the number of aircraft plus one.
712 ! The rest of the file gives 15 values of Mean O-A for each aircraft, with each (AC,value) line written in format "a9,1x,f6.2".
713 ! The order is the same as what is written by genbiascorr script genbc.aircraft_bcor.py.
714 ! The first "aircraft" (AC name = BULKBCORS) values are the bulk biases by layer for All-AC (first 5 values), AIREP/ADS
715 ! (second 5 values) and AMDAR/BUFR (last 5 values).
716 ! Missing value = 99.0.
717 !
718 implicit none
719
720 ! Arguments:
721 character(len=*), intent(in) :: biasEstimateFile
722
723 ! Locals:
724 integer :: ierr, nulcoeff
725 integer :: stationIndex, phaseIndex, levelIndex
726 real(8) :: biasEstimate, correctionValue
727 character(len=9) :: stationId
728
729 if (.not.initialized) call bcc_readConfig()
730
731 if ( aiRevOnly ) return
732
733 nulcoeff = 0
734 ierr = fnom(nulcoeff, biasEstimateFile, 'FMT+R/O', 0)
735 if ( ierr /= 0 ) then
736 call utl_abort('bcc_readAIBiases: unable to open airplanes bias correction file ' // biasEstimateFile )
737 end if
738 read (nulcoeff, '(i5)', iostat=ierr ) nbAircrafts
739 if ( ierr /= 0 ) then
740 call utl_abort('bcc_readAIBiases: error 1 while reading airplanes bias correction file ' // biasEstimateFile )
741 end if
742
743 allocate( AIttCorrections(nAircraftMax,nPhases,nLevels) )
744 allocate( aircraftIds(nAircraftMax) )
745
746 AIttCorrections(:,:,:) = MPC_missingValue_R8
747
748 do stationIndex=1,nbAircrafts
749 do phaseIndex=1,3
750 do levelIndex=1,5
751 read (nulcoeff, *, iostat=ierr) stationId, biasEstimate
752 if ( ierr /= 0 ) then
753 call utl_abort('bcc_readAIBiases: error 2 while reading airplanes bias correction file ' // biasEstimateFile )
754 end if
755 if ( biasEstimate == aiMissingValue ) then
756 correctionValue = MPC_missingValue_R8
757 else
758 correctionValue = -1.0D0*biasEstimate
759 end if
760 AIttCorrections(stationIndex,phaseIndex,levelIndex) = correctionValue
761 aircraftIds(stationIndex) = stationId
762 !print*, stationIndex, phaseIndex, levelIndex, aircraftIds(stationIndex), AIttCorrections(stationIndex,phaseIndex,levelIndex)
763 end do
764 end do
765 end do
766 ierr = fclos(nulcoeff)
767
768 ! Check for bulk bias corrections at start of file
769 if ( aircraftIds(1) /= "BULKBCORS" ) then
770 call utl_abort('bcc_readAIBiases: Bulk bias corrections are missing in bias correction file!' )
771 end if
772
773 end subroutine bcc_readAIBiases
774
775 !-----------------------------------------------------------------------
776 ! bcc_applyAIBcor
777 !-----------------------------------------------------------------------
778 subroutine bcc_applyAIBcor(obsSpaceData)
779 !
780 !:Purpose: to apply aircraft (AI) bias corrections to observations in ObsSpaceData
781 !
782 implicit none
783
784 ! Arguments:
785 type(struct_obs), intent(inout) :: obsSpaceData
786
787 ! Locals:
788 integer :: headerIndex, bodyIndex, codtyp
789 integer :: flag, phase, bufrCode
790 integer :: phaseIndex, levelIndex, stationIndex, stationNumber
791 integer :: countTailCorrections, countBulkCorrections
792 integer :: headerFlag
793 real(8) :: corr, tt, oldCorr, pressure
794 character(len=9) :: stnid, stnId1, stnId2
795
796 if (.not.initialized) call bcc_readConfig()
797
798 if ( .not. aiBiasActive ) return
799
800 write(*,*) "bcc_applyAIBcor: start"
801
802 if ( .not.aiRevOnly ) call bcc_readAIBiases(aiBcFile)
803
804 countTailCorrections = 0
805 countBulkCorrections = 0
806
807 call obs_set_current_header_list(obsSpaceData,'AI')
808 HEADER: do
809 headerIndex = obs_getHeaderIndex(obsSpaceData)
810 if ( headerIndex < 0 ) exit HEADER
811
812 headerFlag = obs_headElem_i(obsSpaceData, OBS_ST1, headerIndex )
813
814 call obs_set_current_body_list(obsSpaceData, headerIndex)
815
816 BODY: do
817
818 bodyIndex = obs_getBodyIndex(obsSpaceData)
819 if ( bodyIndex < 0 ) exit BODY
820
821 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) /= obs_assimilated ) cycle BODY
822
823 bufrCode = obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex )
824
825 if ( bufrCode == BUFR_NETT ) then
826 tt = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex )
827 flag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex )
828 oldCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex )
829 corr = MPC_missingValue_R8
830
831 if ( tt /= MPC_missingValue_R8 ) then
832
833 if ( btest(flag, 6) .and. oldCorr /= MPC_missingValue_R8 ) then
834 if ( btest(headerFlag, 15) ) then
835 tt = tt - oldCorr
836 else
837 tt = tt + oldCorr
838 end if
839 flag = ibclr(flag, 6)
840 end if
841 if ( aiRevOnly ) corr = 0.0D0
842
843 if ( .not.aiRevOnly ) then
844
845 pressure = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex ) * MPC_MBAR_PER_PA_R8
846
847 ! Get level index and current (mar 2020) static bulk corrections applied to AI TT data at derivate stage
848 if ( (pressure <= 1100.d0) .and. (pressure > 700.d0) ) then
849 corr = 0.0D0
850 levelIndex = 5
851 else if ( (pressure <= 700.d0) .and. (pressure > 500.d0) ) then
852 corr = -0.1D0
853 levelIndex = 4
854 else if ( (pressure <= 500.d0) .and. (pressure > 400.d0) ) then
855 corr = -0.2D0
856 levelIndex = 3
857 else if ( (pressure <= 400.d0) .and. (pressure > 300.d0) ) then
858 corr = -0.3D0
859 levelIndex = 2
860 else if ( (pressure <= 300.d0) .and. (pressure > 100.d0) ) then
861 corr = -0.5D0
862 levelIndex = 1
863 else
864 levelIndex = 0
865 corr = 0.0D0
866 end if
867
868 codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex)
869
870 ! Default bulk corrections read from bcor file (applied if dynamic corrections are not availble for the aircraft)
871 select case( trim( codtyp_get_name(codtyp) ) )
872 case('airep','ads')
873 phaseIndex = phaseAscentIndex
874 case('amdar','acars')
875 phaseIndex = phaseDescentIndex
876 case default
877 write(*,*) 'bcc_applyAIBcor: codtyp=', codtyp
878 call utl_abort('bcc_applyAIBcor: unknown codtyp')
879 end select
880
881 if ( levelIndex /= 0 ) then
882 if ( AIttCorrections(1,phaseIndex,levelIndex) /= MPC_missingValue_R8 ) corr = AIttCorrections(1,phaseIndex,levelIndex)
883 countBulkCorrections = countBulkCorrections + 1
884 end if
885
886 headerIndex = obs_bodyElem_i(obsSpaceData, OBS_HIND, bodyIndex )
887 stnid = trim( obs_elem_c(obsSpaceData,'STID',headerIndex) )
888
889 ! on verifie si la station est dans le dictionnaire du fichier de correction de biais
890 !---------------------------------------------------------------------------------
891 stationNumber = 0
892 stnId2 = trim(stnid)
893 do stationIndex = 1, nbAircrafts
894 stnId1 = trim(aircraftIds(stationIndex))
895 if ( stnId2(2:9) == stnId1(1:8) ) stationNumber = stationIndex
896 end do
897
898 phase = obs_headElem_i(obsSpaceData, OBS_PHAS, headerIndex )
899
900 ! If the aircraft is in the bias correction file, get the new correction
901 ! AIttCorrections(stationNumber,phaseIndex,levelIndex) where
902 ! stationNumber = index for this AC in bias correction file (0 if not found)
903 ! phaseIndex = index for the 3 phases of flight (level, asc, desc)
904 ! levelIndex = index for the 5 layers (100-300, 300-400,400-500,500-700,700-1100)
905 ! and use it instead of bulk value (if it is not missing value).
906 if ( stationNumber /= 0 ) then
907 phaseIndex = 0
908 if ( phase == phaseLevel ) phaseIndex = phaseLevelIndex
909 if ( phase == phaseAscent ) phaseIndex = phaseAscentIndex
910 if ( phase == phaseDescent ) phaseIndex = phaseDescentIndex
911 if ( levelIndex /= 0 .and. phaseIndex /= 0 ) then
912 if ( AIttCorrections(stationNumber,phaseIndex,levelIndex) /= MPC_missingValue_R8 ) then
913 corr = AIttCorrections(stationNumber,phaseIndex,levelIndex)
914 countTailCorrections = countTailCorrections + 1
915 countBulkCorrections = countBulkCorrections - 1
916 end if
917 end if
918 end if
919
920 ! Apply the bias correction (bulk or new) and set the "bias corrected" bit in TT data flag ON
921 tt = tt + corr
922 flag = ibset(flag, 6)
923
924 end if
925
926 end if
927
928 call obs_bodySet_r( obsSpaceData, OBS_BCOR, bodyIndex, corr )
929 call obs_bodySet_r( obsSpaceData, OBS_VAR , bodyIndex, tt )
930 call obs_bodySet_i( obsSpaceData, OBS_FLG , bodyIndex, flag )
931
932 end if
933
934 end do BODY
935
936 ! Set header flag bit 15 on to indicate that bias correction has been ADDED to raw value.
937 ! In older versions of this routine, such as the version used in IC-3/GDPS v8.0, the correction was SUBTRACTED.
938 headerFlag = ibset(headerFlag, 15)
939 call obs_headSet_i( obsSpaceData, OBS_ST1, headerIndex, headerFlag )
940
941 end do HEADER
942
943 if ( countBulkCorrections + countTailCorrections /= 0 ) then
944 write (*, '(a58, i10)' ) "bcc_applyAIBcor: Number of obs with TT bulk correction = ", countBulkCorrections
945 write (*, '(a58, i10)' ) "bcc_applyAIBcor: Number of obs with TT tail correction = ", countTailCorrections
946 else
947 write(*,*) "bcc_applyAIBcor: No AI data found"
948 end if
949
950 if ( allocated(AIttCorrections) ) deallocate(AIttCorrections)
951 if ( allocated(aircraftIds) ) deallocate(aircraftIds)
952
953 write(*,*) "bcc_applyAIBcor: end"
954
955 end subroutine bcc_applyAIBcor
956
957 !-----------------------------------------------------------------------
958 ! bcc_readGPBiases
959 !-----------------------------------------------------------------------
960 subroutine bcc_readGPBiases(biasEstimateFile)
961 !
962 !:Purpose: Read GB-GPS bias estimates (mean ZTD O-A [mm] by station) and fill bias correction array ztdCorrections.
963 ! Missing value = -999.00
964 !
965 implicit none
966
967 ! Arguments:
968 character(len=*), intent(in) :: biasEstimateFile
969
970 ! Locals:
971 integer :: ierr, nulcoeff
972 integer :: stationIndex
973 real(8) :: biasEstimate
974 character(len=9) :: stationId
975
976 if (.not.initialized) call bcc_readConfig()
977
978 if ( gpRevOnly ) return
979
980 nulcoeff = 0
981 ierr = fnom(nulcoeff, biasEstimateFile, 'FMT+R/O', 0)
982 if ( ierr /= 0 ) then
983 call utl_abort('bcc_readGPBiases: unable to open GB-GPS bias correction file ' // biasEstimateFile )
984 end if
985 read (nulcoeff, '(i5)', iostat=ierr ) nbGpStations
986 if ( ierr /= 0 ) then
987 call utl_abort('bcc_readGPBiases: error 1 while reading GB-GPS bias correction file ' // biasEstimateFile )
988 end if
989
990 allocate( ztdCorrections(nStationMaxGP) )
991 allocate( gpsStations(nStationMaxGP) )
992
993 ztdCorrections(:) = MPC_missingValue_R8
994
995 do stationIndex=1,nbGpStations
996 read (nulcoeff, *, iostat=ierr) stationId, biasEstimate
997 if ( ierr /= 0 ) then
998 call utl_abort('bcc_readGPBiases: error 2 while reading GB-GPS bias correction file ' // biasEstimateFile )
999 end if
1000 if ( biasEstimate /= gpMissingValue ) ztdCorrections(stationIndex) = -1.0D0*(biasEstimate/1000.0D0) ! mm to m
1001 gpsStations(stationIndex) = stationId
1002 end do
1003 ierr = fclos(nulcoeff)
1004
1005 end subroutine bcc_readGPBiases
1006
1007 !-----------------------------------------------------------------------
1008 ! bcc_applyGPBcor
1009 !-----------------------------------------------------------------------
1010 subroutine bcc_applyGPBcor(obsSpaceData)
1011 !
1012 !:Purpose: to apply GB-GPS (GP) ZTD bias corrections to ZTD observations in ObsSpaceData
1013 !
1014 implicit none
1015
1016 ! Arguments:
1017 type(struct_obs), intent(inout) :: obsSpaceData
1018
1019 ! Locals:
1020 integer :: headerIndex, bodyIndex
1021 integer :: flag, bufrCode
1022 integer :: stationIndex, stationNumber
1023 integer :: nbCorrected
1024 real(8) :: corr, ztd, oldCorr
1025 character(len=9) :: stnid, stnId1, stnId2
1026
1027 if (.not.initialized) call bcc_readConfig()
1028
1029 if ( .not. gpBiasActive ) return
1030
1031 write(*,*) "bcc_applyGPBcor: start"
1032
1033 if ( .not.gpRevOnly ) call bcc_readGPBiases(gpBcFile)
1034
1035 nbCorrected = 0
1036
1037 call obs_set_current_header_list(obsSpaceData,'GP')
1038
1039 HEADER: do
1040 headerIndex = obs_getHeaderIndex(obsSpaceData)
1041 if ( headerIndex < 0 ) exit HEADER
1042
1043 call obs_set_current_body_list(obsSpaceData, headerIndex)
1044
1045 BODY: do
1046
1047 bodyIndex = obs_getBodyIndex(obsSpaceData)
1048 if ( bodyIndex < 0 ) exit BODY
1049
1050 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) /= obs_assimilated ) cycle BODY
1051
1052 bufrCode = obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex )
1053
1054 if ( bufrCode == BUFR_NEZD ) then
1055
1056 ztd = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex )
1057 oldCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex )
1058 flag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex )
1059
1060 corr = MPC_missingValue_R8
1061
1062 if ( ztd /= MPC_missingValue_R8 ) then
1063
1064 ! Remove any previous bias correction
1065 if ( btest(flag, 6) .and. oldCorr /= MPC_missingValue_R8 ) then
1066 ztd = ztd - oldCorr
1067 flag = ibclr(flag, 6)
1068 end if
1069
1070 if ( .not. gpRevOnly ) then
1071 headerIndex = obs_bodyElem_i(obsSpaceData, OBS_HIND, bodyIndex )
1072 stnid = trim( obs_elem_c(obsSpaceData,'STID',headerIndex) )
1073
1074 ! on verifie si la station est dans le dictionnaire du fichier de correction de biais
1075 ! ---------------------------------------------------------------------------------
1076 stationNumber = 0
1077 stnId2 = trim(stnid)
1078 do stationIndex = 1, nbGpStations
1079 stnId1 = trim(gpsStations(stationIndex))
1080 if ( stnId2 == stnId1 ) stationNumber = stationIndex
1081 end do
1082
1083 if (stationNumber /= 0) then
1084 corr = ztdCorrections(stationNumber)
1085 end if
1086
1087 ! Apply the bias correction and set the "bias corrected" bit in ZTD data flag ON
1088 if ( corr /= MPC_missingValue_R8 ) then
1089 ztd = ztd + corr
1090 nbCorrected = nbCorrected + 1
1091 flag = ibset(flag, 6)
1092 else
1093 corr = 0.0D0
1094 end if
1095 else
1096 corr = 0.0D0
1097 end if
1098
1099 end if
1100
1101 call obs_bodySet_r( obsSpaceData, OBS_BCOR, bodyIndex, corr )
1102 call obs_bodySet_r( obsSpaceData, OBS_VAR , bodyIndex, ztd )
1103 call obs_bodySet_i( obsSpaceData, OBS_FLG , bodyIndex, flag )
1104
1105 end if
1106
1107 end do BODY
1108 end do HEADER
1109
1110 if ( nbCorrected /= 0 ) then
1111 write (*, '(a50, i10)' ) "bcc_applyGPBcor: Number of ZTD observations corrected = ", nbCorrected
1112 else
1113 write(*,*) "bcc_applyGPBcor: No GP data bias corrections made"
1114 end if
1115
1116 if ( allocated(ztdCorrections) ) deallocate( ztdCorrections )
1117 if ( allocated(gpsStations) ) deallocate( gpsStations )
1118
1119 write(*,*) "bcc_applyGPBcor: end"
1120
1121 end subroutine bcc_applyGPBcor
1122
1123
1124 !-----------------------------------------------------------------------
1125 ! bcc_readUABcorStype
1126 !-----------------------------------------------------------------------
1127 subroutine bcc_readUABcorStype(biasCorrectionFileName,nGroups)
1128 !
1129 !:Purpose: Read night and day TT, TD biases by SONDE TYPE and latitude band on 16 mandatory levels for UA family.
1130
1131 ! The first line is the sonde-type (and latitude band if uaNlatBands=5). Sonde type = 'END' for end of file.
1132 ! nGroups groups of of 16 lines follow, e.g., nGroups = 2: one group for ascending sonde observations and
1133 ! one for descending sonde observations
1134 ! -- 16 lines are 16 mandatory levels from 100000 Pa to 1000 Pa
1135 ! -- Lines contain four values: TT night-bias, TT day-bias, TD night-bias, TD day-bias
1136 ! Missing value = -99.0.
1137 !
1138 implicit none
1139
1140 ! Arguments:
1141 character(len=*), intent(in) :: biasCorrectionFileName
1142 integer, intent(in) :: nGroups
1143
1144 ! Locals:
1145 integer :: ierr, nulcoeff
1146 integer :: sondeTypeIndex, group, latBand, levelIndex, groupIndex, maxGroups
1147 real(8) :: ttBiasNight, ttBiasDay, tdBiasNight, tdBiasDay
1148 character(len=8) :: sondeType
1149
1150 if ( uaRevOnly ) return
1151
1152 nulcoeff = 0
1153 ierr = fnom(nulcoeff, biasCorrectionFileName, 'FMT+R/O', 0)
1154 if ( ierr /= 0 ) then
1155 call utl_abort('bcc_readUABcorStype: unable to open radiosonde bias correction file ' // biasCorrectionFileName )
1156 end if
1157
1158 maxGroups = nGroups*2
1159
1160 allocate( ttCorrections(nSondesMax,uaNlatBands,maxGroups,nMandLevs) )
1161 allocate( tdCorrections(nSondesMax,uaNlatBands,maxGroups,nMandLevs) )
1162 allocate( biasCorrPresentStype(nSondesMax,uaNlatBands,maxGroups) )
1163
1164 ttCorrections(:,:,:,:) = MPC_missingValue_R8
1165 tdCorrections(:,:,:,:) = MPC_missingValue_R8
1166 sondeTypes(:) = 'empty'
1167
1168 biasCorrPresentStype(:,:,:) = .false.
1169
1170 main_loop: do
1171
1172 if ( uaNlatBands == 1 ) then
1173 read (nulcoeff, *, iostat=ierr) sondeType
1174 latBand = 1
1175 else
1176 read (nulcoeff, *, iostat=ierr) sondeType, latBand
1177 end if
1178 if ( ierr /= 0 ) then
1179 call utl_abort('bcc_readUABcorStype: error reading sondeType, latBand in radiosonde bias correction file ' // biasCorrectionFileName )
1180 end if
1181 if ( trim(sondeType) == 'END' ) exit main_loop
1182 sondeTypeIndex = bcc_SondeIndex(sondeType)
1183 if ( sondeTypeIndex < 0 ) call utl_abort('bcc_readUABcorStype: Unknown sonde type '//sondeType)
1184 if ( latBand < 0 .or. latBand > uaNlatBands ) then
1185 write(*,*) 'sondeType, latband = ', sondeType, latBand
1186 call utl_abort('bcc_readUABcorStype: Latitude band out of range 0-5!')
1187 end if
1188 ! Skip sondeType/latband 0 (globe) if present
1189 if ( latBand == 0 ) then
1190 do group = 1, nGroups
1191 do levelIndex = 1, nMandLevs
1192 read (nulcoeff, *, iostat=ierr) ttBiasNight, ttBiasDay, tdBiasNight, tdBiasDay
1193 end do
1194 end do
1195 cycle main_loop
1196 end if
1197 sondeTypes(sondeTypeIndex) = sondeType
1198 do group = 1, nGroups
1199 groupIndex = (group-1)*2 + 1
1200 do levelIndex = 1, nMandLevs
1201 read (nulcoeff, *, iostat=ierr) ttBiasNight, ttBiasDay, tdBiasNight, tdBiasDay
1202 if ( ierr /= 0 ) then
1203 call utl_abort('bcc_readUABcorStype: error reading corrections in radiosonde bias correction file ' // biasCorrectionFileName )
1204 end if
1205 if ( ttBiasNight == uaMissingValue ) ttBiasNight = MPC_missingValue_R8
1206 if ( ttBiasDay == uaMissingValue ) ttBiasDay = MPC_missingValue_R8
1207 if ( tdBiasNight == uaMissingValue ) tdBiasNight = MPC_missingValue_R8
1208 if ( tdBiasDay == uaMissingValue ) tdBiasDay = MPC_missingValue_R8
1209 if ( ttBiasNight /= MPC_missingValue_R8 ) ttCorrections(sondeTypeIndex,latBand,groupIndex,levelIndex) = -1.0d0*ttBiasNight
1210 if ( ttBiasDay /= MPC_missingValue_R8 ) ttCorrections(sondeTypeIndex,latBand,groupIndex+1,levelIndex) = -1.0d0*ttBiasDay
1211 if ( tdBiasNight /= MPC_missingValue_R8 ) tdCorrections(sondeTypeIndex,latBand,groupIndex,levelIndex) = -1.0d0*tdBiasNight
1212 if ( tdBiasDay /= MPC_missingValue_R8 ) tdCorrections(sondeTypeIndex,latBand,groupIndex+1,levelIndex) = -1.0d0*tdBiasDay
1213 end do
1214 if ( ttCorrections(sondeTypeIndex,latBand,groupIndex,Index500mb) /= MPC_missingValue_R8 ) biasCorrPresentStype(sondeTypeIndex,latBand,groupIndex) = .true.
1215 if ( ttCorrections(sondeTypeIndex,latBand,groupIndex+1,Index500mb) /= MPC_missingValue_R8 ) biasCorrPresentStype(sondeTypeIndex,latBand,groupIndex+1) = .true.
1216 end do
1217
1218 end do main_loop
1219
1220 ierr = fclos(nulcoeff)
1221
1222 end subroutine bcc_readUABcorStype
1223
1224 !-----------------------------------------------------------------------
1225 ! bcc_readUABcorStn
1226 !-----------------------------------------------------------------------
1227 subroutine bcc_readUABcorStn(biasCorrectionFileName,nProfsMin,nGroups)
1228 !
1229 !:Purpose: Read TT, TD biases by STATION/sonde-type on 16 mandatory levels for UA family.
1230 !
1231 ! The first line is the station and sonde-type followed by number of profiles.
1232 !
1233 ! Station = 'END' for end of file.
1234 !
1235 ! nGroups groups of of 16 lines follow, e.g., nGroups = 2: one group for ascending sonde observations and
1236 ! one for descending sonde observations
1237 !
1238 ! -- 16 lines are 16 mandatory levels from 100000 Pa to 1000 Pa
1239 ! -- Lines contain 4 values: TT night-bias, TT day-bias, TD night-bias, TD day-bias
1240 !
1241 ! Missing value (file) = -99.0.
1242 !
1243 ! Sonde type in file is "None" if type was unknown/missing for the reports from a station.
1244 !
1245 ! biasCorrPresentStn(iStn,iType,TOD) = .true. for all TOD if total Nprofs >= nProfsMin (e.g. 100)
1246 !
1247 ! ttCorrectionsStn(iStn,iType,TOD,level) = MPC_missingValue_R8 if TTcorrectionValue == -99.0
1248 !
1249 implicit none
1250
1251 ! Arguments:
1252 character(len=*), intent(in) :: biasCorrectionFileName
1253 integer, intent(in) :: nProfsMin
1254 integer, intent(in) :: nGroups
1255
1256 ! Locals:
1257 integer :: ierr, nulcoeff, stationIndex, typeIndex
1258 integer :: group, levelIndex, nProfs, groupIndex, maxGroups
1259 real(8) :: ttBiasNight, ttBiasDay, tdBiasNight, tdBiasDay
1260 character(len=8) :: sondeType
1261 character(len=9) :: station, stnPrev
1262
1263 if ( uaRevOnly ) return
1264
1265 nulcoeff = 0
1266 ierr = fnom(nulcoeff, biasCorrectionFileName, 'FMT+R/O', 0)
1267 if ( ierr /= 0 ) then
1268 call utl_abort('bcc_readUABcorStn: unable to open radiosonde bias correction file ' // biasCorrectionFileName )
1269 end if
1270
1271 maxGroups = nGroups*2
1272
1273 allocate( ttCorrectionsStn(nStationMaxUA,nSondesMax,maxGroups,nMandLevs) )
1274 allocate( tdCorrectionsStn(nStationMaxUA,nSondesMax,maxGroups,nMandLevs) )
1275 allocate( uaStations(nStationMaxUA) )
1276 allocate( biasCorrPresentStn(nStationMaxUA,nSondesMax,maxGroups) )
1277
1278 ttCorrectionsStn(:,:,:,:) = MPC_missingValue_R8
1279 tdCorrectionsStn(:,:,:,:) = MPC_missingValue_R8
1280 uaStations(:) = 'empty'
1281 biasCorrPresentStn(:,:,:) = .false.
1282
1283 stationIndex = 0
1284 stnPrev = "toto"
1285 main_loop: do
1286
1287 read (nulcoeff, *, iostat=ierr) station, sondeType, nProfs
1288 if ( ierr /= 0 ) then
1289 call utl_abort('bcc_readUABcorStn: error reading station line in radiosonde bias correction file ' // biasCorrectionFileName )
1290 end if
1291 if ( trim(station) == 'END' ) exit main_loop
1292 typeIndex = bcc_SondeIndex(sondeType)
1293 if ( typeIndex < 0 ) call utl_abort('bcc_readUABcorStn: Unknown sonde type '//sondeType//' not in rsTypes defined in namelist')
1294
1295 ! If new station, add station to uaStations array.
1296 ! Assumes entries for the same station and different types are consecutive in bcor file
1297 if ( trim(station) /= trim(stnPrev) ) then
1298 stationIndex = stationIndex + 1
1299 uaStations(stationIndex) = station
1300 stnPrev = station
1301 end if
1302
1303 ! Determine which station/sondeType/time-of-day have enough profiles to use the bias corrections
1304 ! and set logical biasCorrPresentStn accordingly.
1305
1306 do group = 1, maxGroups
1307 if ( nProfs >= nProfsMin ) biasCorrPresentStn(stationIndex,typeIndex,group) = .true.
1308 end do
1309
1310 ! Read the bias correction profiles for this station/sondeType.
1311 do group = 1, nGroups
1312 groupIndex = (group-1)*2 + 1
1313 do levelIndex = 1, nMandLevs
1314 read (nulcoeff, *, iostat=ierr) ttBiasNight, ttBiasDay, tdBiasNight, tdBiasDay
1315 if ( ierr /= 0 ) then
1316 call utl_abort('bcc_readUABcorStn: error reading corrections in radiosonde bias correction file ' // biasCorrectionFileName )
1317 end if
1318 if ( ttBiasNight == uaMissingValue ) ttBiasNight = MPC_missingValue_R8
1319 if ( ttBiasDay == uaMissingValue ) ttBiasDay = MPC_missingValue_R8
1320 if ( tdBiasNight == uaMissingValue ) tdBiasNight = MPC_missingValue_R8
1321 if ( tdBiasDay == uaMissingValue ) tdBiasDay = MPC_missingValue_R8
1322 if ( ttBiasNight /= MPC_missingValue_R8 ) ttCorrectionsStn(stationIndex,typeIndex,groupIndex,levelIndex) = -1.0d0*ttBiasNight
1323 if ( ttBiasDay /= MPC_missingValue_R8 ) ttCorrectionsStn(stationIndex,typeIndex,groupIndex+1,levelIndex) = -1.0d0*ttBiasDay
1324 if ( tdBiasNight /= MPC_missingValue_R8 ) tdCorrectionsStn(stationIndex,typeIndex,groupIndex,levelIndex) = -1.0d0*tdBiasNight
1325 if ( tdBiasDay /= MPC_missingValue_R8 ) tdCorrectionsStn(stationIndex,typeIndex,groupIndex+1,levelIndex) = -1.0d0*tdBiasDay
1326 end do
1327 end do
1328
1329 end do main_loop
1330
1331 ierr = fclos(nulcoeff)
1332
1333 end subroutine bcc_readUABcorStn
1334
1335 !-----------------------------------------------------------------------
1336 ! bcc_applyUABcor
1337 !-----------------------------------------------------------------------
1338 subroutine bcc_applyUABcor(obsSpaceData)
1339 !
1340 !:Purpose: To apply bias corrections to radiosonde TT and ES observationa in obsSpaceData
1341 ! This public routine is called by external routines.
1342
1343 ! NOTE: We ADD the correction (negative of the bias) to the raw observation.
1344 ! We first SUBTRACT the old correction (if any) from the observation to remove (reverse) the old correction!
1345 ! Bias correction is put in obsSpaceData OBS_BCOR column.
1346 !
1347 ! NOTE: Bias correction is NOT applied if the sonde type is RS41. Observations from this sonde type are
1348 ! assumed to be unbiased.
1349 !
1350 ! Routine does nothing if uaBiasActive = .false. (just returns to calling routine)
1351 !
1352 implicit none
1353
1354 ! Arguments:
1355 type(struct_obs), intent(inout) :: obsSpaceData
1356
1357 ! Locals:
1358 integer :: headerIndex, bodyIndex, codtyp
1359 integer :: flag, bufrCode, sondeTypeCode, sondeTypeIndex, stnIndex
1360 integer :: date, time, latBand, groupIndex, phase
1361 integer :: countTTCorrections, countESCorrections, countUnknownStype, countMissingStype
1362 integer :: countUnknownStation, countTTCorrByStation, countTTCorrByStype, countTTObs, countRS41
1363 integer :: countCat1, countCat2, countNight, countDay, countDawnDusk
1364 integer :: ttBodyIndex, esBodyIndex
1365 real(8) :: tt, oldCorr, pressure, lat, lon, ttOriginal, es, esOriginal, td
1366 real(8) :: solarElev, corr, p1, p2, p3, p4
1367 real(8) :: timeOfDayX
1368 character(len=9) :: stnid, stnidPrev
1369 character(len=8) :: sondeType
1370 character(len=5) :: sourceCorr
1371 logical :: newStation, debug, stationFound, realRS41
1372
1373 if ( .not. uaBiasActive ) return
1374
1375 write(*,*) "bcc_applyUABcor: start"
1376
1377 debug = .false.
1378
1379 debug = debug .and. ( mmpi_myid == 0 )
1380
1381 ! Read the ascii files containing TT and TD bis profiles by sonde-type and by station
1382 if ( .not.uaRevOnly ) then
1383 call bcc_readUABcorStype(uaBcFileStype, uaNbiasCat)
1384 call bcc_readUABcorStn(uaBcFileStn, uaNprofsMin, uaNbiasCat)
1385 end if
1386
1387 countTTCorrections = 0
1388 countESCorrections = 0
1389 countUnknownStype = 0
1390 countMissingStype = 0
1391 countUnknownStation = 0
1392 countTTCorrByStation = 0
1393 countTTCorrByStype = 0
1394 countTTObs = 0
1395 countRS41 = 0
1396 countCat1 = 0
1397 countCat2 = 0
1398 countNight = 0
1399 countDay = 0
1400 countDawnDusk = 0
1401
1402 call obs_set_current_header_list(obsSpaceData,'UA')
1403
1404 stnidPrev = 'toto'
1405 HEADER: do ! Loop over UA headers (one header per pressure level)
1406 headerIndex = obs_getHeaderIndex(obsSpaceData)
1407 if ( headerIndex < 0 ) exit HEADER
1408
1409 call obs_set_current_body_list(obsSpaceData, headerIndex)
1410
1411 stnid = trim(obs_elem_c(obsSpaceData,'STID',headerIndex))
1412 if ( trim(stnid) /= trim(stnidPrev) ) then
1413 newStation = .true.
1414 stnidPrev = stnid
1415 else
1416 newStation = .false.
1417 end if
1418
1419 ! Get the information needed to apply the bias corrections from the first header for this station
1420 if ( newStation .and. .not.uaRevOnly ) then
1421
1422 ! Station index in list of stations (uaStations) from ua_bcors_stn file
1423 stationFound = .false.
1424 stnIndex = bcc_StationIndex(stnid)
1425 if (debug) write(*,*) 'stnid, index = ', stnid, stnIndex
1426 if ( stnIndex > 0 ) then
1427 stationFound = .true.
1428 else
1429 countUnknownStation = countUnknownStation + 1
1430 if (debug) write(*,*) 'Unknown station (not in ua_bcors_stn file) '//stnid
1431 end if
1432
1433 ! Date and lat,lon
1434 codtyp = obs_headElem_i(obsSpaceData, OBS_ITY, headerIndex) ! CODE TYPE
1435 date = obs_headElem_i(obsSpaceData, OBS_DAT, headerIndex) ! YYYYMMDD
1436 time = obs_headElem_i(obsSpaceData, OBS_ETM, headerIndex) ! HHMM
1437 lat = obs_headElem_r(obsSpaceData, OBS_LAT, headerIndex) ! radians!
1438 lon = obs_headElem_r(obsSpaceData, OBS_LON, headerIndex) ! radians!
1439
1440 ! Sonde phase: 1=asc, 2=desc
1441 phase = bcc_uaPhase(codtyp)
1442
1443 if (uaNbiasCat == 2) then
1444 groupIndex = (phase-1)*2 + 1
1445 else
1446 groupIndex = 1
1447 end if
1448
1449 if ( groupIndex == 1 ) then
1450 countCat1 = countCat1 + 1
1451 else
1452 countCat2 = countCat2 + 1
1453 end if
1454
1455 ! Get the sonde type index in rsTypes read from namelist
1456 sondeTypeCode = obs_headElem_i(obsSpaceData, OBS_RTP, headerIndex) ! sonde type BUFR code (WMO table)
1457 if (debug) then
1458 write(*,*) 'stnid, sondeTypeCode, date, time, lat'
1459 write(*,*) stnid, sondeTypeCode, date, time, lat*MPC_DEGREES_PER_RADIAN_R8
1460 end if
1461 if ( sondeTypeCode == -999 ) then
1462 sondeTypeCode = 0
1463 countMissingStype = countMissingStype + 1
1464 write (*,'(a60)') "bcc_applyUABcor: Missing sonde type at stn "//stnid
1465 end if
1466 call bcc_GetsondeType(sondeTypeCode,sondeType,sondeTypeIndex)
1467 if ( sondeTypeIndex == 0 ) then
1468 countUnknownStype = countUnknownStype + 1
1469 write (*,'(a70,i4)') "bcc_applyUABcor: Unknown sonde-type code at stn "//stnid//". Code = ", sondeTypeCode
1470 end if
1471 if (debug) write(*,*) 'sondeType, sondeTypeIndex = ', sondeType, sondeTypeIndex
1472
1473 ! We assume that a sonde type of "RS41" reported by Chinese UA stations is not correct
1474 realRS41 = .false.
1475 if ( trim(sondeType) == "RS41" ) then
1476 if ( stnid(1:1) == "5" ) then
1477 realRS41 = .false.
1478 else
1479 realRS41 = .true.
1480 end if
1481 end if
1482
1483 ! Time-of-day x-value
1484 call bcc_GetSolarElevation(lat,lon,date,time,solarElev)
1485 call bcc_GetTimeOfDay(solarElev,timeOfDayX)
1486 if ( timeOfDayX == 0.0 ) then
1487 countNight = countNight+1
1488 else if ( timeOfDayX == 1.0 ) then
1489 countDay = countDay+1
1490 else
1491 countDawnDusk = countDawnDusk+1
1492 end if
1493
1494 ! Latitude band
1495 if ( uaNlatBands == 1 ) then
1496 latBand = 1
1497 else
1498 latBand = bcc_LatBand(lat)
1499 if ( latBand == -1 ) then
1500 write(*,*) "uaNlatBands = ", uaNlatBands
1501 call utl_abort("bcc_applyUABcor: uaNlatBands must equal 1 or 5")
1502 end if
1503 end if
1504
1505 if (debug) write(*,*) 'solarElev, timeOfDayX, latBand = ',solarElev, timeOfDayX, latBand
1506
1507 end if
1508
1509 ttBodyIndex = -1
1510 esBodyIndex = -1
1511
1512 BODY: do ! Find the body indices for the TT and ES observations for this header (pressure level)
1513
1514 bodyIndex = obs_getBodyIndex(obsSpaceData)
1515 if ( bodyIndex < 0 ) exit BODY
1516
1517 if ( obs_bodyElem_i(obsSpaceData,OBS_ASS,bodyIndex) /= obs_assimilated ) cycle BODY
1518
1519 bufrCode = obs_bodyElem_i(obsSpaceData, OBS_VNM, bodyIndex )
1520
1521 ! Assumes that normal (non high-precision) codes are used for TT and ES observations in obsSpaceData
1522 if ( bufrCode == BUFR_NETT ) then
1523 ttBodyIndex = bodyIndex
1524 else if ( bufrCode == BUFR_NEES ) then
1525 esBodyIndex = bodyIndex
1526 end if
1527
1528 end do BODY
1529
1530 ! Reverse any old corrections and apply new corrections (if uaRevOnly=.false.)
1531
1532 ! TT bias correction
1533 if ( ttBodyIndex >= 0 ) then
1534 bodyIndex = ttBodyIndex
1535 ttOriginal = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex )
1536 tt = ttOriginal
1537 pressure = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex ) * MPC_MBAR_PER_PA_R8
1538 flag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex )
1539 oldCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex )
1540 corr = MPC_missingValue_R8
1541 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'TTin',ttBodyIndex,pressure,tt
1542 if ( tt /= MPC_missingValue_R8 ) then
1543 if ( btest(flag, 6) .and. oldCorr /= MPC_missingValue_R8 ) then
1544 tt = tt - oldCorr
1545 flag = ibclr(flag, 6)
1546 end if
1547 if ( uaRevOnly ) corr = 0.0d0
1548 if ( .not.uaRevOnly ) then
1549 ! Get the TT correction for this station, sonde type, time-of-day and level
1550 ! stnIndex = -1 if station not found in bcor file
1551 ! sondeTypeIndex = 0 if sonde-type code is not associated with any of the types in
1552 ! namelist (rsTypes) including stypes "Others" and "None" [code=0]
1553 countTTObs = countTTObs + 1
1554 if ( realRS41 ) then
1555 corr = MPC_missingValue_R8
1556 countRS41 = countRS41 + 1
1557 else
1558 call bcc_GetUACorrection('TT',stnIndex,sondeTypeIndex,sondeType,groupIndex,timeOfDayX,latBand,pressure,corr,sourceCorr)
1559 if ( sourceCorr == 'stn' ) then
1560 countTTCorrByStation = countTTCorrByStation + 1
1561 else if ( sourceCorr == 'stype' ) then
1562 countTTCorrByStype = countTTCorrByStype + 1
1563 end if
1564 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'corrTT, source, groupIndex = ',corr, sourceCorr, groupIndex
1565 end if
1566 if ( corr /= MPC_missingValue_R8 ) then
1567 tt = tt + corr
1568 flag = ibset(flag, 6)
1569 countTTCorrections = countTTCorrections + 1
1570 else
1571 if ( .not.realRS41 .and. uaRejUnBcor ) flag = ibset(flag, 11)
1572 end if
1573 end if
1574 end if
1575 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'TTout, corr, flag = ', tt, corr, flag
1576 call obs_bodySet_r( obsSpaceData, OBS_BCOR, bodyIndex, corr )
1577 call obs_bodySet_r( obsSpaceData, OBS_VAR , bodyIndex, tt )
1578 call obs_bodySet_i( obsSpaceData, OBS_FLG , bodyIndex, flag )
1579 end if
1580
1581 ! ES bias correction
1582 if ( esBodyIndex >= 0 .and. ttBodyIndex >= 0 ) then
1583 bodyIndex = esBodyIndex
1584 esOriginal = obs_bodyElem_r(obsSpaceData, OBS_VAR, bodyIndex )
1585 es = esOriginal
1586 pressure = obs_bodyElem_r(obsSpaceData, OBS_PPP, bodyIndex ) * MPC_MBAR_PER_PA_R8
1587 flag = obs_bodyElem_i(obsSpaceData, OBS_FLG, bodyIndex )
1588 oldCorr = obs_bodyElem_r(obsSpaceData, OBS_BCOR, bodyIndex )
1589 corr = MPC_missingValue_R8
1590 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'ESin',esBodyIndex,pressure,es
1591 if ( es /= MPC_missingValue_R8 ) then
1592 if ( btest(flag, 6) .and. oldCorr /= MPC_missingValue_R8 ) then
1593 es = es - oldCorr
1594 flag = ibclr(flag, 6)
1595 end if
1596 if ( uaRevOnly ) corr = 0.0d0
1597 if ( .not.uaRevOnly ) then
1598 if ( realRS41 ) then
1599 corr = MPC_missingValue_R8
1600 else
1601 call bcc_GetUACorrection('TD',stnIndex,sondeTypeIndex,sondeType,groupIndex,timeOfDayX,latBand,pressure,corr,sourceCorr)
1602 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'corrTD, source, groupIndex = ',corr, sourceCorr, groupIndex
1603 end if
1604 if ( corr /= MPC_missingValue_R8 ) then
1605 td = (ttOriginal - es) + corr
1606 es = tt - td
1607 if ( es < 0.0 ) es = 0.0d0
1608 if ( es > 30.0) es = 30.0d0
1609 corr = es - esOriginal
1610 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'ES corr =',corr
1611 flag = ibset(flag, 6)
1612 countESCorrections = countESCorrections + 1
1613 else
1614 if ( .not.realRS41 .and. uaRejUnBcor ) flag = ibset(flag, 11)
1615 end if
1616 end if
1617 end if
1618 if ( debug .and. pressure == 500.0d0 ) write(*,*) 'ESout, corr, flag = ', es, corr, flag
1619 call obs_bodySet_r( obsSpaceData, OBS_BCOR, bodyIndex, corr )
1620 call obs_bodySet_r( obsSpaceData, OBS_VAR , bodyIndex, es )
1621 call obs_bodySet_i( obsSpaceData, OBS_FLG , bodyIndex, flag )
1622 end if
1623
1624 end do HEADER
1625
1626 if ( .not.uaRevOnly ) then
1627 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of TT obs corrected = ", countTTCorrections
1628 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of ES obs corrected = ", countESCorrections
1629 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number obs with unknown station = ", countUnknownStation
1630 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number obs with missing sonde type = ", countMissingStype
1631 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number obs with unknown sonde code = ", countUnknownStype
1632 write (*, '(a40)' ) "----------------------------------------"
1633 write (*, '(a60, i10)' ) "bcc_applyUABcor: Total number of TT observations = ", countTTObs
1634 if ( countTTObs > 0 ) then
1635 p1 = (float(countTTCorrByStation)/float(countTTObs))*100.d0
1636 p2 = (float(countTTCorrByStype)/float(countTTObs))*100.d0
1637 p3 = 100.d0 - (p1+p2)
1638 p4 = (float(countRS41)/float(countTTObs))*100.
1639 write (*, '(a60, f7.2)' ) "bcc_applyUABcor: Percentage corrected by STATION = ", p1
1640 write (*, '(a60, f7.2)' ) "bcc_applyUABcor: Percentage corrected by SONDE-TYPE = ", p2
1641 write (*, '(a60, f7.2)' ) "bcc_applyUABcor: Percentage uncorrected = ", p3
1642 write (*, '(a60, f7.2)' ) "bcc_applyUABcor: Percentage RS41 = ", p4
1643 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of night profiles = ", countNight
1644 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of day profiles = ", countDay
1645 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of dawn-dusk profiles = ", countDawnDusk
1646 if (uaNbiasCat == 2) then
1647 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of ascent profiles = ", countCat1
1648 write (*, '(a60, i10)' ) "bcc_applyUABcor: Number of descent profiles = ", countCat2
1649 end if
1650 end if
1651 end if
1652
1653 if ( allocated(ttCorrections) ) deallocate(ttCorrections)
1654 if ( allocated(tdCorrections) ) deallocate(tdCorrections)
1655 if ( allocated(ttCorrectionsStn) ) deallocate(ttCorrectionsStn)
1656 if ( allocated(tdCorrectionsStn) ) deallocate(tdCorrectionsStn)
1657 if ( allocated(biasCorrPresentStype) ) deallocate(biasCorrPresentStype)
1658 if ( allocated(biasCorrPresentStn) ) deallocate(biasCorrPresentStn)
1659 if ( allocated(uaStations) ) deallocate(uaStations)
1660
1661 if ( allocated(rsTypes) ) deallocate(rsTypes)
1662
1663 write(*,*) "bcc_applyUABcor: end"
1664
1665 end subroutine bcc_applyUABcor
1666
1667
1668 !-----------------------------------------------------------------------
1669 ! bcc_biasActive
1670 !-----------------------------------------------------------------------
1671 function bcc_biasActive(obsFam) result(biasActive)
1672 !
1673 !:Purpose: returns True if bias correction is active for the given conventional observation family
1674 !
1675 implicit none
1676
1677 ! Arguments:
1678 character(len=*),intent(in) :: obsFam
1679 ! Result:
1680 logical :: biasActive
1681
1682 if (.not.initialized) call bcc_readConfig()
1683
1684 select case(trim(obsFam))
1685 case('GP')
1686 biasActive = bcc_gpBiasActive
1687 case('UA')
1688 biasActive = bcc_uaBiasActive
1689 case('AI')
1690 biasActive = bcc_aiBiasActive
1691 case default
1692 biasActive = .false.
1693 end select
1694
1695 end function bcc_biasActive
1696
1697end MODULE biasCorrectionConv_mod