1 subroutine da_read_obs_bufratms (obstype,iv, infile)
3 !---------------------------------------------------------------------------
4 ! Purpose: read in NCEP bufr atms 1b data to innovation structure
6 ! METHOD: use F90 sequential data structure to avoid reading file twice
7 ! so that da_scan_bufrtovs is not necessary any more.
8 ! 1. read file radiance data in sequential data structure
10 ! 3. assign sequential data structure to innovation structure
11 ! and deallocate sequential data structure
12 ! Peiming Dong Added NPP atms, 2012/4/17
13 ! Peiming Dong seperated the atms from da_read_obs_bufrtovs.inc in that
14 ! all the data needs to be read in together first to make
15 ! the spatial average, 2013/1/10
16 !---------------------------------------------------------------------------
20 character(5) , intent (in) :: obstype
21 character(20) , intent (in) :: infile
22 type (iv_type) , intent (inout) :: iv
27 integer(i_kind), allocatable :: nread(:)
28 !Dongpm for the spatial average
29 integer(i_kind) ,parameter :: Num_Obs = 800000
30 integer(i_kind) ,parameter :: NChanl = 22
31 integer(i_kind) ,allocatable :: Fov_save(:)
32 real(r_kind) ,allocatable :: Time_save(:)
33 real(r_kind) ,allocatable :: BT_InOut_save(:,:)
34 integer(i_kind) ,allocatable :: Scanline_save(:)
35 integer(i_kind) :: Error_Status
36 integer(i_kind) :: nnum, nn
37 real(r_kind) ,allocatable :: lat_save(:)
38 real(r_kind) ,allocatable :: lon_save(:)
39 real(r_kind) ,allocatable :: obs_time_save(:)
40 real(r_kind) ,allocatable :: satzen_save(:)
41 real(r_kind) ,allocatable :: satazi_save(:)
42 real(r_kind) ,allocatable :: solzen_save(:)
43 real(r_kind) ,allocatable :: solazi_save(:)
44 real(r_kind) ,allocatable :: srf_height_save(:)
45 integer(i_kind) ,allocatable :: landsea_mask_save(:)
46 integer(i_kind) ,allocatable :: satid_save(:)
47 character(len=19),allocatable :: date_char_save(:)
49 integer(i_kind),parameter:: n1bhdr=15
50 integer(i_kind),parameter:: n2bhdr=2
51 !Dongpm integer(i_kind),parameter:: n1bhdr=13
52 integer(i_kind),parameter:: maxinfo=12
53 integer(i_kind),parameter:: maxchanl=100
56 logical outside, outside_all, iuse
60 character(8) subset,subfgn
64 integer(i_kind) ihh,i,j,k,ifov,idd,ireadmg,ireadsb
65 integer(i_kind) iret,idate,im,iy,nchan
66 integer :: num_bufr(7), numbufr, ibufr
67 character(20) :: filename
70 integer(i_kind) itt,itx,iobs,iout
71 real(r_kind) terrain,timedif,crit,dist
72 real(r_kind) dlon_earth,dlat_earth
74 real(r_kind) tbmin,tbmax, tbbad
75 real(r_kind) panglr,rato
77 real(r_kind) step,start
79 real(r_double),dimension(maxinfo+maxchanl):: data1b8
80 real(r_double),dimension(n1bhdr):: bfr1bhdr
82 real(r_double),dimension(n2bhdr):: bfr2bhdr
84 ! Instrument triplet, follow the convension of RTTOV
85 integer :: platform_id, satellite_id, sensor_id
88 integer :: year,month,day,hour,minute,second ! observation time
90 ! real :: rlat, rlon ! lat/lon in degrees for Anfovs
91 real :: satzen, satazi, solzen ,solazi ! scan angles for Anfovs
92 integer :: landsea_mask
94 ! channels' bright temperature
95 real , allocatable :: tb_inv(:) ! bright temperatures
96 ! end type bright_temperature
98 type (datalink_type), pointer :: head, p, current, prev
101 type(info_type) :: info
102 type(model_loc_type) :: loc
105 data hdr1b /'SAID FOVN YEAR MNTH DAYS HOUR MINU SECO CLAT CLON SAZA SOZA HMSL LSQL SOLAZI'/
106 data hdr2b /'CLATH CLONH'/
107 ! data hdr1b /'FOVN YEAR MNTH DAYS HOUR MINU SECO CLAT CLON SAZA SOZA HOLS LSQL SLNM BEARAZ'/
109 data tbmin,tbmax,tbbad / 50.0_r_kind, 550.0_r_kind, -9.99e11_r_kind /
110 integer :: num_tovs_local, num_tovs_file, num_tovs_global, num_tovs_selected
111 integer :: num_tovs_thinned, num_tovs_used, num_tovs_used_tmp
114 integer(i_kind), allocatable :: ptotal(:)
115 real , allocatable :: in(:), out(:)
116 logical :: found, head_found
118 if (trace_use) call da_trace_entry("da_read_obs_bufratms")
120 ! Initialize variables
125 allocate(nread(1:rtminit_nsensor))
126 allocate(ptotal(0:num_fgat_time))
127 nread(1:rtminit_nsensor) = 0
128 ptotal(0:num_fgat_time) = 0
130 ! Set various variables depending on type of data to be read
132 ! platform_id = 1 !! for NOAA series
133 ! platform_id = 10 !! for METOP series
135 atms= obstype == 'atms '
140 start = -52.725_r_kind
145 allocate (tb_inv(nchan))
147 num_tovs_file = 0 ! number of obs in file
148 num_tovs_global = 0 ! number of obs within whole domain
149 num_tovs_local = 0 ! number of obs within tile
150 num_tovs_thinned = 0 ! number of obs rejected by thinning
151 num_tovs_used = 0 ! number of obs entered into innovation computation
152 num_tovs_selected = 0 ! number of obs limited for debuging
153 iobs = 0 ! for thinning, argument is inout
155 ! 0.0 Open unit to satellite bufr file and read file header
156 !--------------------------------------------------------------
157 allocate(Fov_save(1:Num_obs))
158 allocate(Time_save(1:Num_Obs))
159 allocate(BT_InOut_save(1:NChanl,1:Num_Obs))
160 allocate(Scanline_save(1:Num_Obs))
161 allocate(lat_save(1:Num_Obs))
162 allocate(lon_save(1:Num_Obs))
163 allocate(satid_save(1:Num_Obs))
164 allocate(obs_time_save(1:Num_Obs))
165 allocate(satzen_save(1:Num_Obs))
166 allocate(satazi_save(1:Num_Obs))
167 allocate(solzen_save(1:Num_Obs))
168 allocate(solazi_save(1:Num_Obs))
169 allocate(srf_height_save(1:Num_Obs))
170 allocate(landsea_mask_save(1:Num_Obs))
171 allocate(date_char_save(1:Num_Obs))
176 if (num_fgat_time>1) then
178 call da_get_unit(lnbufr)
179 write(filename,fmt='(A,2I1,A)') trim(infile),0,i,'.bufr'
180 open(unit = lnbufr, FILE = trim(filename),iostat = iost, form = 'unformatted', STATUS = 'OLD')
187 call da_free_unit(lnbufr)
193 if (numbufr==0) numbufr=1
195 bufrfile: do ibufr=1,numbufr
196 if (num_fgat_time==1) then
197 filename=trim(infile)//'.bufr'
199 if ((numbufr ==1) .and. (num_bufr(ibufr) == 0)) then
200 filename=trim(infile)//'.bufr'
202 write(filename,fmt='(A,2I1,A)') trim(infile),0,num_bufr(ibufr),'.bufr'
206 ! We want to use specific unit number for bufr data, so we can control the endian format in environment.
209 open(unit=lnbufr,file=trim(filename),form='unformatted', &
210 iostat = iost, status = 'old')
212 call da_warning(__FILE__,__LINE__, &
213 (/"Cannot open file "//infile/))
214 if (trace_use) call da_trace_exit("da_read_obs_bufratms")
218 call openbf(lnbufr,'IN',lnbufr)
220 call readmg(lnbufr,subset,idate,iret)
221 if (subset /= subfgn) then
224 message(1)='The file title does not match the data subset'
225 write(unit=message(2),fmt=*) &
226 'infile=', lnbufr, infile,' subset=', subset, ' subfgn=',subfgn
227 call da_error(__FILE__,__LINE__,message(1:2))
234 write(unit=date,fmt='( i10)') idate
235 read(unit=date,fmt='(i4,3i2)') iy,im,idd,ihh
236 write(unit=stdout,fmt=*) &
237 'Bufr file date is ',iy,im,idd,ihh,infile
239 ! Loop to read bufr file and assign information to a sequential structure
240 !-------------------------------------------------------------------------
242 ! if ( ibufr == 1 ) then
244 ! ! allocate ( head % tb_inv (1:nchan) )
245 ! nullify ( head % next )
249 if (tovs_start > 1) then
250 write (unit=stdout,fmt='(A,I6)') " Skipping tovs obs before", tovs_start
252 bufrobs: do while (ireadmg(lnbufr,subset,idate)==0 .and. subset==subfgn .and. nnum < Num_Obs)
253 do while (ireadsb(lnbufr)==0 .and. nnum < Num_Obs)
255 ! 1.0 Read header record and data record
257 call ufbint(lnbufr,bfr1bhdr,n1bhdr,1,iret,hdr1b)
258 call ufbint(lnbufr,bfr2bhdr,n2bhdr,1,iret,hdr2b)
259 ! Dongpm call ufbrep(lnbufr,data1b8,1,nchan,iret,'TMBR')
260 call ufbrep(lnbufr,data1b8,1,nchan,iret,'TMANT')
261 ! call ufbrep(lnbufr,data1b8,1,1,iret,'BEARAZ')
263 ! check if observation outside range
264 ! 1.5 To save the data
266 if(abs(bfr2bhdr(1)) <= 90. .and. abs(bfr2bhdr(2)) <= 360.)then
267 lat_save(nnum) = bfr2bhdr(1)
268 lon_save(nnum) = bfr2bhdr(2)
269 elseif(abs(bfr1bhdr(9)) <= 90. .and. abs(bfr1bhdr(10)) <= 360.)then
270 lat_save(nnum) = bfr1bhdr(9)
271 lon_save(nnum) = bfr1bhdr(10)
273 ifov = nint(bfr1bhdr(bufr_ifov))
274 Fov_save(nnum) = ifov
275 satid_save(nnum)=nint(bfr1bhdr(bufr_satellite_id))
276 year = bfr1bhdr(bufr_year)
277 month = bfr1bhdr(bufr_month)
278 day = bfr1bhdr(bufr_day)
279 hour = bfr1bhdr(bufr_hour)
280 minute = bfr1bhdr(bufr_minute)
281 second = bfr1bhdr(bufr_second)
283 write(unit=date_char_save(nnum), fmt='(i4,a,i2.2,a,i2.2,a,i2.2,a,i2.2,a,i2.2)') &
284 year, '-', month, '-', day, '_', hour, ':', minute, ':', second
286 ! QC3: time consistency check with the background date
296 call da_get_julian_time(year,month,day,hour,minute,obs_time)
297 obs_time_save(nnum)=obs_time
298 Time_save(nnum)=obs_time_save(nnum)*60.0+second
300 panglr=(start+float(ifov-1)*step)*deg2rad
301 satzen_save(nnum) = bfr1bhdr(bufr_satzen) !*deg2rad ! local zenith angle
302 satazi_save(nnum) = panglr*rad2deg ! look angle
303 solzen_save(nnum) = bfr1bhdr(bufr_solzen) ! solar zenith angle
304 solazi_save(nnum) = bfr1bhdr(bufr_solazi) !RTTOV9_3
305 srf_height_save(nnum) = bfr1bhdr(bufr_station_height) ! station height
306 landsea_mask_save(nnum) = nint(bfr1bhdr(bufr_landsea_mask)) ! 0:land ; 1:sea (same as RTTOV)
307 BT_InOut_save(1:nchan,nnum)= data1b8(1:nchan)
310 num_tovs_file = num_tovs_file + 1
323 call da_warning(__FILE__,__LINE__,(/"No ATMS data were read in"/))
324 if (trace_use) call da_trace_exit("da_read_obs_bufratms")
327 write(unit=message(1),fmt='(a,i10)') 'The number of observations is:',nnum-1
328 call da_message(message(1:1))
329 call ATMS_Spatial_Average(nnum, NChanl, Fov_save(1:nnum), Time_save(1:nnum), BT_InOut_save(1:NChanl,1:nnum), &
330 Scanline_save, Error_Status)
331 if(Error_Status==1) then
332 WRITE(UNIT=message(1), FMT='(A)')"Error reading ATMS data"
333 call da_error(__FILE__,__LINE__,message(1:1))
339 ! ! allocate ( head % tb_inv (1:nchan) )
340 nullify ( head % next )
344 ! 2.0 Extract observation location and other required information
345 ! QC1: judge if data is in the domain, read next record if not
346 !------------------------------------------------------------------------
347 ! rlat = bfr1bhdr(bufr_lat)
348 ! rlon = bfr1bhdr(bufr_lat)
349 ! if (rlon < 0.0) rlon = rlon+360.0
350 info%lat = lat_save(nn)
351 info%lon = lon_save(nn)
353 call da_llxy (info, loc, outside, outside_all)
355 if (outside_all) cycle
357 ! 3.0 Extract other information
358 !------------------------------------------------------
359 ! 3.1 Extract satellite id and scan position.
361 if ( satid_save(nn) == 224 ) then ! npp
367 ! QC2: limb pixel rejected (not implemented)
369 ! 3.2 Extract date information.
371 info%date_char=date_char_save(nn)
372 obs_time=obs_time_save(nn)
374 if (obs_time < time_slots(0) .or. &
375 obs_time >= time_slots(num_fgat_time)) cycle
377 ! 3.2.1 determine FGAT index ifgat
379 do ifgat=1,num_fgat_time
380 if (obs_time >= time_slots(ifgat-1) .and. &
381 obs_time < time_slots(ifgat)) exit
384 ! 3.3 Find wrfvar instrument index from RTTOV instrument triplet
385 ! go to next data if id is not in the lists
388 do i = 1, rtminit_nsensor
389 if (platform_id == rtminit_platform(i) &
390 .and. satellite_id == rtminit_satid(i) &
391 .and. sensor_id == rtminit_sensor(i)) then
398 ! 3.4 extract satellite and solar angle
400 satzen = satzen_save(nn)
402 ! if (amsua .and. ifov .le. 15) satzen=-satzen
403 ! if (amsub .and. ifov .le. 45) satzen=-satzen
404 ! if (hirs3 .and. ifov .le. 28) satzen=-satzen
405 if ( satzen > 65.0 ) cycle ! CRTM has a satzen > 65.0 check
406 satazi = satazi_save(nn) ! look angle
407 ! if (satazi<0.0) satazi = satazi+360.0
408 solzen = solzen_save(nn) ! solar zenith angle
409 solazi = solazi_save(nn) !RTTOV9_3
411 num_tovs_global = num_tovs_global + 1
412 ptotal(ifgat) = ptotal(ifgat) + 1
414 if (num_tovs_global < tovs_start) then
418 if (num_tovs_global > tovs_end) then
419 write (unit=stdout,fmt='(A,I6)') " Skipping radiance obs after", tovs_end
423 num_tovs_selected = num_tovs_selected + 1
425 if (num_tovs_selected > max_tovs_input) then
426 write(unit=message(1),fmt='(A,I10,A)') &
427 "Max number of tovs",max_tovs_input," reached"
428 call da_warning(__FILE__,__LINE__,message(1:1))
429 num_tovs_selected = num_tovs_selected-1
430 num_tovs_global = num_tovs_global-1
431 ptotal(ifgat) = ptotal(ifgat) - 1
435 if (outside) cycle ! No good for this PE
436 num_tovs_local = num_tovs_local + 1
439 ! Map obs to thinning grid
440 !-------------------------------------------------------------------
442 dlat_earth = info%lat
443 dlon_earth = info%lon
444 if (dlon_earth<zero) dlon_earth = dlon_earth+r360
445 if (dlon_earth>=r360) dlon_earth = dlon_earth-r360
446 dlat_earth = dlat_earth*deg2rad
447 dlon_earth = dlon_earth*deg2rad
448 timedif = 0.0 !2.0_r_kind*abs(tdiff) ! range: 0 to 6
449 terrain = 0.01_r_kind*abs(bfr1bhdr(13))
450 crit = 1.0 !0.01_r_kind+terrain + timedif !+ 10.0_r_kind*float(iskip)
451 call map2grids(inst,ifgat,dlat_earth,dlon_earth,crit,iobs,itx,1,itt,iout,iuse)
453 num_tovs_thinned=num_tovs_thinned+1
459 num_tovs_used = num_tovs_used + 1
460 nread(inst) = nread(inst) + 1
462 ! 3.5 extract surface information
463 srf_height = srf_height_save(nn) ! station height
464 if (srf_height < 8888.0 .AND. srf_height > -416.0) then
469 landsea_mask = landsea_mask_save(nn) ! 0:land ; 1:sea (same as RTTOV)
470 !Dongpm There is no landsea-mask in atms bufr data
471 if (landsea_mask <= 1 .AND. landsea_mask >= 0) then
477 ! if (nint(bfr1bhdr(bufr_landsea_mask))==1) rmask=0.0_r_kind ! reverse the land/sea mask in bufr
478 ! landsea_mask = rmask+.001_r_kind ! land sea mask
480 info%elv = srf_height
482 ! 3.6 extract channel bright temperature
484 tb_inv(1:nchan) = BT_InOut_save(1:nchan,nn)
486 if ( tb_inv(k) < tbmin .or. tb_inv(k) > tbmax) &
487 tb_inv(k) = missing_r
489 if ( all(tb_inv<0.0) ) then
490 num_tovs_local = num_tovs_local -1
491 num_tovs_used = num_tovs_used - 1
492 nread(inst) = nread(inst) - 1
495 ! 4.0 assign information to sequential radiance structure
496 !--------------------------------------------------------------------------
497 allocate (p % tb_inv (1:nchan))
500 p%landsea_mask = landsea_mask
505 p%tb_inv(1:nchan) = tb_inv(1:nchan)
506 p%sensor_index = inst
511 allocate (p%next) ! add next data
518 ! call closbf(lnbufr)
523 if (thinning .and. num_tovs_global > 0 ) then
527 ! Get minimum crit and associated processor index.
529 do ifgat = 1, num_fgat_time
530 do n = 1, iv%num_inst
531 j = j + thinning_grid(n,ifgat)%itxmax
539 do ifgat = 1, num_fgat_time
540 do n = 1, iv%num_inst
541 do i = 1, thinning_grid(n,ifgat)%itxmax
543 in(j) = thinning_grid(n,ifgat)%score_crit(i)
547 call mpi_reduce(in, out, j, true_mpi_real, mpi_min, root, comm, ierr)
549 call wrf_dm_bcast_real (out, j)
552 do ifgat = 1, num_fgat_time
553 do n = 1, iv%num_inst
554 do i = 1, thinning_grid(n,ifgat)%itxmax
556 if ( ABS(out(j)-thinning_grid(n,ifgat)%score_crit(i)) > 1.0E-10 ) thinning_grid(n,ifgat)%ibest_obs(i) = 0
566 ! Delete the nodes which being thinning out
570 num_tovs_used_tmp = num_tovs_used
571 do j = 1, num_tovs_used_tmp
576 do i = 1, thinning_grid(n,ifgat)%itxmax
577 if ( thinning_grid(n,ifgat)%ibest_obs(i) == j .and. thinning_grid(n,ifgat)%score_crit(i) < 9.99e6_r_kind ) then
584 if ( .not. found ) then
587 if ( head_found ) then
593 deallocate ( current % tb_inv )
594 deallocate ( current )
595 num_tovs_thinned = num_tovs_thinned + 1
596 num_tovs_used = num_tovs_used - 1
597 nread(n) = nread(n) - 1
601 if ( found .and. head_found ) then
607 if ( found .and. .not. head_found ) then
616 endif ! End of thinning
618 iv%total_rad_pixel = iv%total_rad_pixel + num_tovs_used
619 iv%total_rad_channel = iv%total_rad_channel + num_tovs_used*nchan
621 iv%info(radiance)%nlocal = iv%info(radiance)%nlocal + num_tovs_used
622 iv%info(radiance)%ntotal = iv%info(radiance)%ntotal + num_tovs_global
624 do i = 1, num_fgat_time
625 ptotal(i) = ptotal(i) + ptotal(i-1)
626 iv%info(radiance)%ptotal(i) = iv%info(radiance)%ptotal(i) + ptotal(i)
628 if ( iv%info(radiance)%ptotal(num_fgat_time) /= iv%info(radiance)%ntotal ) then
629 write(unit=message(1),fmt='(A,I10,A,I10)') &
630 "Number of ntotal:",iv%info(radiance)%ntotal," is different from the sum of ptotal:", iv%info(radiance)%ptotal(num_fgat_time)
631 call da_warning(__FILE__,__LINE__,message(1:1))
634 write(unit=stdout,fmt='(a)') 'num_tovs_file num_tovs_global num_tovs_local num_tovs_used num_tovs_thinned'
635 write(unit=stdout,fmt='(5i10)') num_tovs_file,num_tovs_global, num_tovs_local,num_tovs_used,num_tovs_thinned
639 ! 5.0 allocate innovation radiance structure
640 !----------------------------------------------------------------
642 do i = 1, iv%num_inst
643 if (nread(i) < 1) cycle
644 iv%instid(i)%num_rad = nread(i)
645 iv%instid(i)%info%nlocal = nread(i)
646 write(UNIT=stdout,FMT='(a,i3,2x,a,3x,i10)') &
647 'Allocating space for radiance innov structure', &
648 i, iv%instid(i)%rttovid_string, iv%instid(i)%num_rad
650 call da_allocate_rad_iv(i,nchan,iv)
654 ! 6.0 assign sequential structure to innovation structure
655 !-------------------------------------------------------------
656 nread(1:rtminit_nsensor) = 0
658 ! do while ( associated(p) )
660 do n = 1, num_tovs_used
662 nread(i) = nread(i) + 1
664 call da_initialize_rad_iv (i, nread(i), iv, p)
670 deallocate ( current % tb_inv )
671 deallocate ( current )
673 ! deallocate the save data
675 deallocate(Time_save)
676 deallocate(BT_InOut_save)
677 deallocate(Scanline_save)
680 deallocate(satid_save)
681 deallocate(obs_time_save)
682 deallocate(satzen_save)
683 deallocate(satazi_save)
684 deallocate(solzen_save)
685 deallocate(solazi_save)
686 deallocate(srf_height_save)
687 deallocate(landsea_mask_save)
688 deallocate(date_char_save)
694 ! check if sequential structure has been freed
697 ! do i = 1, num_rad_selected
698 ! write (unit=stdout,fmt=*) i, p%tb_inv(1:nchan)
702 if (trace_use) call da_trace_exit("da_read_obs_bufratms")
704 call da_error(__FILE__,__LINE__,(/"Needs to be compiled with a BUFR library"/))
708 end subroutine da_read_obs_bufratms