1 MODULE module_duplicate
3 !-----------------------------------------------------------------------------!
4 ! Sort observations by location and time,
5 ! Merge space duplicate stations (same type, same location, same time),
6 ! Remove time duplicate stations in time (same type & location, different
12 ! F. VANDENBERGHE, March 2001
14 ! 01/13/2003 - Updated for Profiler obs. S. R. H. Rizvi
16 ! 02/04/2003 - Updated for Buoy obs. S. R. H. Rizvi
18 ! 02/11/2003 - Reviewed and modified for Profiler
19 ! and Buoy obs. Y.-R. Guo
21 ! 08/31/2004 - Corrected check_duplicate_time for retaining obs
22 ! closer to analysis time S. R. H. Rizvi
23 ! 09/02/2004 - Reviewed the above correction, and add the
24 ! modifications for GPS Ref. Y.-R. Guo
25 ! 06/30/2006 - Updated for AIRS retrievals Syed RH Rizvi
27 ! 11/09/2006 - add the modifications for GPS Excess Phase Y.-R. Guo
28 !------------------------------------------------------------------------------
35 !-----------------------------------------------------------------------------!
36 ! SUBROUTINE check_duplicate_loc (obs,index,num_obs,total_dups,time_analysis,
38 ! SUBROUTINE check_duplicate_time (obs,index,num_obs,total_dups,time_analysis,
41 ! -------------------------------------------------------------------------
43 SUBROUTINE check_duplicate_loc(obs, index, num_obs, total_dups, time_analysis,&
46 ! Checks array of reports (obs), which has a sorted index to the reports,
47 ! to determine if any reports are for the same time/location. If so,
48 ! and the data is duplicated exactly in all fields, one is discarded. If
49 ! they are from same time/location and data is not identical, data from
50 ! two reports is merged: 'missing' is replaced by known values; data at
51 ! different levels is merged into one linked list.
59 TYPE ( report ) , INTENT ( INOUT ) , DIMENSION ( : ) :: obs
60 INTEGER , INTENT ( IN ) , DIMENSION ( : ) :: index
61 INTEGER , INTENT ( IN ) :: num_obs
63 INTEGER :: current , &
67 INTEGER , INTENT ( OUT ) :: total_dups
68 ! INTEGER , INTENT ( IN ) :: date , &
70 LOGICAL, INTENT (IN) :: print_duplicate
71 CHARACTER (LEN = 19) :: time_analysis
72 INTEGER :: total_valid
74 INTEGER :: century_year, month, day
75 INTEGER :: hour, minute, seconds
77 INTEGER :: iunit, io_error
79 CHARACTER (LEN = 80):: filename
80 CHARACTER (LEN = 80):: proc_name = "check_duplicate_ob"
81 CHARACTER (LEN = 160):: error_message
82 LOGICAL :: fatal, connected
85 CHARACTER (LEN = 40) :: platforma, platformb
86 INTEGER :: nsynopb, nmetarb, nshipsb, &
87 nsoundb, npilotb, nairepb, &
88 nsatemb, nsatobb, ngpspwb, &
89 nssmt1b, nssmt2b, nssmib, &
90 ntovsb, notherb, namdarb, &
91 nqscatb, nproflb, ngpsepb, nbuoysb, &
92 ngpszdb, ngpsrfb, nbogusb, &
94 INTEGER :: nsynopa, nmetara, nshipsa, &
95 nsounda, npilota, nairepa, &
96 nsatema, nsatoba, ngpspwa, &
97 nssmt1a, nssmt2a, nssmia, &
98 ntovsa, nothera, namdara, &
99 nqscata, nprofla, ngpsepa, nbuoysa, &
100 ngpszda, ngpsrfa, nbogusa, &
103 INCLUDE 'platform_interface.inc'
105 !------------------------------------------------------------------------------!
108 '------------------------------------------------------------------------------'
109 WRITE ( UNIT = 0, FMT = '(A,/)') 'REMOVE DUPLICATE STATIONS BY LOCATION:'
111 ! Open diagnostic file
113 IF (print_duplicate) THEN
115 filename = 'obs_duplicate_loc.diag'
118 INQUIRE ( UNIT = iunit, OPENED = connected )
120 IF (connected) CLOSE (iunit)
122 OPEN (UNIT = iunit , FILE = filename , FORM = 'FORMATTED' , &
123 ACTION = 'WRITE' , STATUS = 'REPLACE', IOSTAT = io_error )
125 IF (io_error .NE. 0) THEN
126 CALL error_handler (proc_name, &
127 "Unable to open output diagnostic file. ", filename, .TRUE.)
129 WRITE (UNIT = 0, FMT = '(A,A,/)') &
130 "Diagnostics in file ", TRIM (filename)
137 nsynopb = 0; nmetarb = 0; nshipsb = 0;
138 nsoundb = 0; npilotb = 0; nairepb = 0;
139 nsatemb = 0; nsatobb = 0; ngpspwb = 0;
140 nssmt1b = 0; nssmt2b = 0; nssmib = 0;
141 ntovsb = 0; notherb = 0; namdarb = 0;
142 nqscatb = 0; nproflb = 0; nbuoysb = 0;
143 nqscatb = 0; nproflb = 0; nbuoysb = 0;
144 nairsb =0; nairsa = 0
145 nsynopa = 0; nmetara = 0; nshipsa = 0;
146 nsounda = 0; npilota = 0; nairepa = 0;
147 nsatema = 0; nsatoba = 0; ngpspwa = 0;
148 nssmt1a = 0; nssmt2a = 0; nssmia = 0;
149 ntovsa = 0; nothera = 0; namdara = 0;
150 nqscata = 0; nprofla = 0; nbuoysa = 0;
151 ngpszda = 0; ngpszdb = 0; nbogusa = 0;
152 ngpsrfa = 0; ngpsrfb = 0; nbogusb = 0;
153 ngpsepa = 0; ngpsepb = 0
154 ntamdara= 0; ntamdarb= 0
155 ! Count obs per type before merging
158 DO current = 1 , num_obs
160 first = index(current)
162 ! If this obs has been merged with another obs or discarded, skip it.
164 IF ( obs(first)%info%discard ) THEN
168 ! Count obs present per type before merging
170 READ (obs(first) % info % platform (4:6), '(I3)') fmb
172 CALL fm_decoder (fmb, platformb, &
173 synop=nsynopb, ship =nshipsb, metar=nmetarb,&
174 pilot=npilotb, sound=nsoundb, satem=nsatemb,&
175 satob=nsatobb, airep=nairepb, gpspw=ngpspwb,&
176 gpszd=ngpszdb, gpsrf=ngpsrfb, gpsep=ngpsepb,&
178 ssmt1=nssmt1b, ssmt2=nssmt2b, ssmi =nssmib, &
179 tovs =ntovsb, other=notherb, amdar=namdarb,&
180 qscat=nqscatb, profl=nproflb, buoy = nbuoysb,&
181 airs=nairsb,tamdar=ntamdarb)
185 ! Break analysis time into ccyymmdd and hhmnss
187 CALL split_date_char (time_analysis, &
188 century_year, month, day, hour, minute, seconds )
190 date = century_year * 10000 + month * 100 + day
191 time = hour * 10000 + minute * 100 + seconds
193 ! Count the total number of duplicate reports.
201 DO current = 1 , num_obs - 1
203 first = index(current)
205 ! If this obs has been merged with another obs or discarded, skip it.
207 IF ( obs(first)%info%discard ) THEN
211 total_valid = total_valid + 1
213 ! Get second obs to compare with first; compare first obs to second obs
214 ! until next obs does not match.
216 compare: DO next = current + 1 , num_obs
220 ! Sorted by location, so if locations NE, then no chance of any
221 ! more matches with first.
224 ! IF (.NOT. (obs(first)%location .EQ. obs(second)%location )) THEN
225 IF (.NOT. loc_eq (obs(first), obs(second))) THEN
229 ! If this obs has been merged with another obs or discarded, skip it.
231 IF (obs(second)%info%discard) THEN
235 ! If time fields are not completely identical, go to next observation.
236 ! Sort is by location ONLY, not by time; so next+1 may be identical
237 ! even though next has different time.
239 ! This statements modifies the obs date and time
240 ! IF (.NOT. time_eq (obs(first)%valid_time, obs(second)%valid_time, &
243 IF (.NOT. time_eq_old (obs(first)%valid_time, obs(second)%valid_time))&
246 IF (print_duplicate) THEN
248 error_message = ' Found multiple times for ' &
249 // TRIM ( obs(first)%location%id ) // ' ' &
250 // TRIM ( obs(first)%location%name ) // ', ' &
251 // TRIM ( obs(first)%valid_time%date_char ) // ' and ' &
252 // TRIM ( obs(second)%valid_time%date_char ) // '.'
254 WRITE (UNIT = iunit, FMT = '(A)') TRIM (error_message)
257 ! CALL error_handler (proc_name, error_message , "", fatal)
265 ! Observations are from same location and time, so merge them.
267 CALL merge_obs ( obs(first) , obs(second), print_duplicate, iunit)
269 ! Mark second of pair as discarded; data is put in 'first'.
270 ! Note that a duplicate has been found by incrementing the counter.
272 obs(second)%info%discard = .true.
273 obs(first)%info%num_dups = obs(first)%info%num_dups + 1
274 total_dups = total_dups + 1
276 ! Free up the space for the observation report that is discarded.
277 ! Unfortunately, OR NOT!
280 ! CALL dealloc_meas ( obs(second)%surface )
281 NULLIFY ( obs(second)%surface )
283 ! obs (second)%info%discard = .TRUE.
289 total_valid = total_valid + 1
291 ! Count obs per type after merging
294 DO current = 1 , num_obs
296 first = index(current)
298 ! If this obs has been merged with another obs or discarded, skip it.
300 IF ( obs(first)%info%discard ) THEN
304 ! Count obs present per type before merging
306 READ (obs(first) % info % platform (4:6), '(I3)') fma
308 CALL fm_decoder (fma, platforma, &
309 synop=nsynopa, ship =nshipsa, metar=nmetara,&
310 pilot=npilota, sound=nsounda, satem=nsatema,&
311 satob=nsatoba, airep=nairepa, gpspw=ngpspwa,&
312 gpszd=ngpszda, gpsrf=ngpsrfa, gpsep=ngpsepa,&
314 ssmt1=nssmt1a, ssmt2=nssmt2a, ssmi =nssmia, &
315 tovs =ntovsa, other=nothera, amdar=namdara,&
316 qscat=nqscata, profl=nprofla, buoy = nbuoysa, &
317 airs=nairsa, tamdar=ntamdara)
321 nsynops (icor) = nsynopb - nsynopa
322 nmetars (icor) = nmetarb - nmetara
323 nshipss (icor) = nshipsb - nshipsa
324 nsounds (icor) = nsoundb - nsounda
325 namdars (icor) = namdarb - namdara
326 npilots (icor) = npilotb - npilota
327 naireps (icor) = nairepb - nairepa
328 ntamdar (icor) = ntamdarb- ntamdara
329 nsatems (icor) = nsatemb - nsatema
330 nsatobs (icor) = nsatobb - nsatoba
331 ngpspws (icor) = ngpspwb - ngpspwa
332 ngpsztd (icor) = ngpszdb - ngpszda
333 ngpsref (icor) = ngpsrfb - ngpsrfa
334 ngpseph (icor) = ngpsepb - ngpsepa
335 nssmt1s (icor) = nssmt1b - nssmt1a
336 nssmt2s (icor) = nssmt2b - nssmt2a
337 nssmis (icor) = nssmib - nssmia
338 ntovss (icor) = ntovsb - ntovsa
339 nqscats (icor) = nqscatb - nqscata
340 nprofls (icor) = nproflb - nprofla
341 nbuoyss (icor) = nbuoysb - nbuoysa
342 nboguss (icor) = nbogusb - nbogusa
343 nairss (icor) = nairsb - nairsa
344 nothers (icor) = notherb - nothera
346 WRITE (UNIT = 0 , FMT = '(A,I7,A,/)' ) &
347 "Found ",total_dups," location duplicate stations that have been merged."
349 IF (print_duplicate) CLOSE (iunit)
351 END SUBROUTINE check_duplicate_loc
354 ! -----------------------------------------------------------------------
356 SUBROUTINE check_duplicate_time (obs, index, num_obs, total_dups, time_analysis,print_duplicate)
358 ! Checks array of reports (obs), which has a sorted index to the reports,
359 ! to determine if any reports are for the same location but different time.
360 ! -If both observations are soundings, then the data closest to the analysis
362 ! -If time differences are equal (obs before and after the analysis time),
363 ! then the obs valid after the analysis time is kept.
364 ! -If one is a sounding and the other a surface observation, then the sounding
365 ! is kept whatever the time differences are.
372 TYPE (report), INTENT (INOUT), DIMENSION (:) :: obs
373 INTEGER, INTENT (IN), DIMENSION (:) :: index
374 INTEGER, INTENT (IN) :: num_obs
375 CHARACTER (LEN = 19), INTENT (INOUT) :: time_analysis
376 INTEGER, INTENT (OUT) :: total_dups
377 LOGICAL, INTENT (IN) :: print_duplicate
378 INTEGER :: total_valid
380 INTEGER :: current, next, first, second
381 CHARACTER (LEN = 19) :: time_first, time_second
382 INTEGER :: itfirst, itsecond
383 LOGICAL :: llfirst, llsecond
385 TYPE (report) :: obs_tmp
386 TYPE (measurement), POINTER :: current_tmp
387 LOGICAL :: remove_duplicate = .TRUE.
389 CHARACTER (LEN = 80) :: filename
390 CHARACTER (LEN = 32 ), PARAMETER :: proc_name = 'check_duplicate_time '
392 INTEGER :: iunit, io_error
394 INCLUDE 'platform_interface.inc'
395 !------------------------------------------------------------------------------!
398 '------------------------------------------------------------------------------'
399 WRITE ( UNIT = 0, FMT = '(A,/)') 'REMOVE DUPLICATE STATIONS BY TIME:'
401 ! Open diagnostic file
403 IF (print_duplicate) THEN
405 filename = 'obs_duplicate_time.diag_'//time_analysis
408 INQUIRE ( UNIT = iunit, OPENED = connected )
410 IF (connected) CLOSE (iunit)
412 OPEN (UNIT = iunit , FILE = filename , FORM = 'FORMATTED' , &
413 ACTION = 'WRITE' , STATUS = 'REPLACE', IOSTAT = io_error )
415 IF (io_error .NE. 0) THEN
416 CALL error_handler (proc_name, &
417 "Unable to open output diagnostic file. ", filename, .TRUE.)
419 WRITE (UNIT = 0, FMT = '(A,A,/)') &
420 "Diagnostics in file ", TRIM (filename)
425 ! Count the total number of duplicate reports.
430 obsloop: DO current = 1 , num_obs - 1
432 first = index(current)
434 ! If this obs has been merged with another obs or discarded, skip it.
436 IF ( obs(first)%info%discard ) THEN
440 total_valid = total_valid + 1
442 ! Get second obs to compare with first; compare first obs to second obs
443 ! until next obs does not match.
445 compare: DO next = current + 1 , num_obs
449 ! Sorted by location, so if locations NE, then no chance of any
450 ! more matches with first.
452 IF ( .NOT. loc_eq ( obs(first) , obs(second) ) ) THEN
456 ! If this obs has been merged with another obs or discarded, skip it.
458 IF ( obs(second)%info%discard ) THEN
462 ! If time fields are not completely identical, they are duplicated
465 IF (.NOT. time_eq_old (obs(first)%valid_time, obs(second)%valid_time))&
468 total_dups = total_dups + 1
472 IF (print_duplicate) THEN
474 WRITE (UNIT = iunit, FMT = '(/,A)') 'Found duplicated stations:'
476 WRITE (UNIT = iunit , FMT = '(A,2x,A,A5,A,A23,2F9.3,A,L10)') &
477 'Station 1 name and ID = ' , &
478 TRIM (obs(first)%info%platform), &
479 TRIM (obs(first)%location%id ) , ' ' , &
480 TRIM (obs(first)%location%name ) , &
481 obs(first)%location%latitude , &
482 obs(first)%location%longitude, ' ',&
483 obs (first)%info%is_sound
485 WRITE (UNIT = iunit , FMT = '(A,2x,A,A5,A,A23,2F9.3,A,L10)') &
486 'Station 2 name and ID = ' , &
487 TRIM (obs(second)%info%platform), &
488 TRIM (obs(second)%location%id ) , ' ' , &
489 TRIM (obs(second)%location%name ) , &
490 obs(second)%location%latitude , &
491 obs(second)%location%longitude,' ',&
492 obs(second)%info%is_sound
496 ! First we check the nature of the observation: sounding or surface
498 is_sound:IF ( obs (first) % info % is_sound .AND. &
499 .NOT. obs (second) % info % is_sound) THEN
504 ELSE IF (.NOT. obs (first) % info % is_sound .AND. &
505 obs (second) % info % is_sound) THEN
512 ! Second we test the time difference between the analysis time and
513 ! the observations time
514 ! (negative for before analysis time, positive for after analysis time)
516 WRITE (time_first, FMT='(A4,"-",A2,"-",A2,"_",A2,":",A2,":",A2)') &
517 obs (first) % valid_time % date_char ( 1: 4), &
518 obs (first) % valid_time % date_char ( 5: 6), &
519 obs (first) % valid_time % date_char ( 7: 8), &
520 obs (first) % valid_time % date_char ( 9:10), &
521 obs (first) % valid_time % date_char (11:12), &
522 obs (first) % valid_time % date_char (13:14)
524 WRITE (time_second, FMT='(A4,"-",A2,"-",A2,"_",A2,":",A2,":",A2)') &
525 obs (second) % valid_time % date_char ( 1: 4), &
526 obs (second) % valid_time % date_char ( 5: 6), &
527 obs (second) % valid_time % date_char ( 7: 8), &
528 obs (second) % valid_time % date_char ( 9:10), &
529 obs (second) % valid_time % date_char (11:12), &
530 obs (second) % valid_time % date_char (13:14)
532 CALL GETH_IDTS (time_first, time_analysis, itfirst)
533 CALL GETH_IDTS (time_second, time_analysis, itsecond)
535 IF (print_duplicate) THEN
537 WRITE (UNIT = iunit, FMT = '(2A)') 'Analysis time = ',time_analysis
539 IF (itfirst .GE. 0) THEN
540 WRITE (UNIT = iunit, FMT = '(3A,I6,A)') &
541 'Station 1 time = ',time_first, &
542 ' = ta + ',itfirst,'s'
544 WRITE (UNIT = iunit, FMT = '(3A,I6,A)') &
545 'Station 1 time = ',time_first, &
546 ' = ta - ',ABS (itfirst),'s'
549 IF (itsecond .GE. 0) THEN
550 WRITE (UNIT = iunit, FMT = '(3A,I6,A)') &
551 'Station 2 time = ',time_second,&
552 ' = ta + ',itsecond,'s'
554 WRITE (UNIT = iunit, FMT = '(3A,I6,A)') &
555 'Station 2 time = ',time_second,&
556 ' = ta - ',ABS (itsecond),'s'
561 ! Time difference must be different
563 time_equal: IF (itfirst .EQ. itsecond) THEN
564 WRITE (0,'(A)') ' Internal error:'
565 WRITE (0,'(2A)') ' first_time = ',time_first
566 WRITE (0,'(2A)') ' second_time = ',time_second
567 STOP ' in check_duplicate_time.F'
570 time_different: IF (abs(itfirst) .LT. abs(itsecond)) THEN
571 ! first obs is close to analysis time and so retain first
574 ELSE IF (abs(itfirst) .GT. abs(itsecond)) THEN
575 ! second obs is close to analysis time and so retain second
578 ELSE IF (abs(itfirst) .EQ. abs(itsecond)) THEN
579 ! Two obervations are at exactly time from analysis time but on opposit side
580 ! Retain the one which is after analysis time
581 IF ( itfirst >= 0.) THEN
592 ! Remove duplicate sounding
594 IF (remove_duplicate) THEN
598 IF (print_duplicate) THEN
599 WRITE (UNIT = iunit, FMT = '(A)') &
600 'Keep station 1 and reject station 2.'
603 READ (obs(second) % info % platform (4:6), '(I3)') fm
605 CALL fm_decoder (fm, platform, &
606 synop=nsynops (icor), ship =nshipss (icor), &
607 metar=nmetars (icor), pilot=npilots (icor), &
608 sound=nsounds (icor), satem=nsatems (icor), &
609 satob=nsatobs (icor), airep=naireps (icor), &
610 gpspw=ngpspws (icor), gpszd=ngpsztd (icor), &
611 gpsrf=ngpsref (icor), gpsep=ngpseph (icor), &
612 ssmt1=nssmt1s (icor), bogus=nboguss (icor), &
613 ssmt2=nssmt2s (icor), ssmi =nssmis (icor), &
614 tovs =ntovss (icor), other=nothers (icor), &
615 amdar=namdars (icor), qscat=nqscats (icor), &
616 profl=nprofls (icor), buoy =nbuoyss (icor), &
617 airs =nairss (icor) , tamdar=ntamdar(icor) )
619 obs (second)%info%discard = .true.
620 obs (first)%info%num_dups = obs (first)%info%num_dups + 1
622 NULLIFY (obs(second)%surface )
627 ELSE IF (llsecond) THEN
629 IF (print_duplicate) THEN
630 Write (UNIT = iunit, FMT = '(A)') &
631 'Keep station 2 and reject station 1.'
635 READ (obs(first) % info % platform (4:6), '(I3)') fm
637 CALL fm_decoder (fm, platform, &
638 synop=nsynops (icor), ship =nshipss (icor), &
639 metar=nmetars (icor), pilot=npilots (icor), &
640 sound=nsounds (icor), satem=nsatems (icor), &
641 satob=nsatobs (icor), airep=naireps (icor), &
642 gpspw=ngpspws (icor), gpszd=ngpsztd (icor), &
643 gpsrf=ngpsref (icor), gpsep=ngpseph (icor), &
644 ssmt1=nssmt1s (icor), bogus=nboguss (icor), &
645 ssmt2=nssmt2s (icor), ssmi =nssmis (icor), &
646 tovs =ntovss (icor), other=nothers (icor), &
647 amdar=namdars (icor), qscat=nqscats (icor), &
648 profl=nprofls (icor), buoy =nbuoyss (icor), &
649 airs =nairss (icor), tamdar=ntamdar(icor) )
651 obs (first)%info%discard = .true.
652 obs (second)%info%num_dups = obs (second)%info%num_dups + 1
654 NULLIFY ( obs(first)%surface )
662 ! Order duplicate soundings by time incresing
666 ! If first observation is before second, do nothing
668 ELSE IF (llsecond) THEN
670 ! If second observation is before first, swap
672 obs_tmp = obs (second)
673 obs (second) = obs (first)
674 obs (first) = obs_tmp
680 ENDIF time_difference
682 ! Free up the space for the observation report that is discarded.
683 ! Unfortunately, OR NOT!
685 ! CALL dealloc_meas ( obs(second)%surface )
686 ! NULLIFY ( obs(second)%surface )
693 IF (print_duplicate) CLOSE (iunit)
695 total_valid = total_valid + 1
697 WRITE (UNIT = 0 , FMT = '(A,I7,A,/)' ) &
698 "Found ",total_dups," time duplicate stations that have been removed."
700 END SUBROUTINE check_duplicate_time
702 END MODULE module_duplicate