1 !WRF:MEDIATION_LAYER:IO
4 ! This obs-nudging FDDA module (RTFDDA) is developed by the
5 ! NCAR/RAL/NSAP (National Security Application Programs), under the
6 ! sponsorship of ATEC (Army Test and Evaluation Commands). ATEC is
7 ! acknowledged for releasing this capability for WRF community
8 ! research applications.
10 ! The NCAR/RAL RTFDDA module was adapted, and significantly modified
11 ! from the obs-nudging module in the standard MM5V3.1 which was originally
12 ! developed by PSU (Stauffer and Seaman, 1994).
14 ! Yubao Liu (NCAR/RAL): lead developer of the RTFDDA module
15 ! Al Bourgeois (NCAR/RAL): lead engineer implementing RTFDDA into WRF-ARW
20 ! Liu, Y., A. Bourgeois, T. Warner, S. Swerdlin and J. Hacker, 2005: An
21 ! implementation of obs-nudging-based FDDA into WRF for supporting
22 ! ATEC test operations. 2005 WRF user workshop. Paper 10.7.
24 ! Liu, Y., A. Bourgeois, T. Warner, S. Swerdlin and W. Yu, 2006: An update
25 ! on "obs-nudging"-based FDDA for WRF-ARW: Verification using OSSE
26 ! and performance of real-time forecasts. 2006 WRF user workshop. Paper 4.7.
29 ! Stauffer, D.R., and N.L. Seaman, 1994: Multi-scale four-dimensional data
30 ! assimilation. J. Appl. Meteor., 33, 416-434.
32 ! http://www.rap.ucar.edu/projects/armyrange/references.html
35 SUBROUTINE wrf_fddaobs_in (grid ,config_flags)
39 USE module_model_constants !rovg
43 TYPE(grid_config_rec_type), INTENT(IN) :: config_flags
47 integer :: ktau ! timestep index corresponding to xtime
48 integer :: krest ! restart timestep
49 integer :: inest ! nest level
50 integer :: infreq ! input frequency
51 integer :: nstlev ! nest level
52 real :: dtmin ! dt in minutes
53 real :: xtime ! forecast time in minutes
54 logical :: iprt_in4dob ! print flag
56 INTEGER ids , ide , jds , jde , kds , kde , &
57 ims , ime , jms , jme , kms , kme , &
58 ips , ipe , jps , jpe , kps , kpe
59 INTEGER ij, its, ite, jts, jte
61 ! Modified to also call in4dob intially, since subr in4dob is no
62 ! longer called from subr fddaobs_init. Note that itimestep is now
63 ! the model step BEFORE the model integration step, because this
64 ! routine is now called by med_before_solve_io.
65 ktau = grid%itimestep ! ktau corresponds to xtime
66 krest = grid%fdob%ktaur
68 nstlev = grid%fdob%levidn(inest)
69 infreq = grid%obs_ionf*(grid%parent_grid_ratio**nstlev)
70 iprt_in4dob = grid%obs_ipf_in4dob
72 IF( (ktau.GT.krest.AND.MOD(ktau,infreq).EQ.0) &
73 .OR.(ktau.EQ.krest) ) then
74 ! Calculate forecast time.
78 CALL get_ijk_from_grid ( grid , &
79 ids, ide, jds, jde, kds, kde, &
80 ims, ime, jms, jme, kms, kme, &
81 ips, ipe, jps, jpe, kps, kpe )
86 DO ij = 1 , grid%num_tiles
87 its = grid%i_start(ij)
88 ite = min(grid%i_end(ij),ide-1)
89 jts = grid%j_start(ij)
90 jte = min(grid%j_end(ij),jde-1)
92 CALL in4dob(inest, xtime, ktau, krest, dtmin, &
93 grid%julyr, grid%julday, grid%gmt, & !obsnypatch
94 grid%obs_nudge_opt, grid%obs_nudge_wind, grid%obs_nudge_temp, &
95 grid%obs_nudge_mois, grid%obs_nudge_pstr, grid%obs_coef_wind, &
96 grid%obs_coef_temp, grid%obs_coef_mois, grid%obs_coef_pstr, &
97 grid%obs_rinxy, grid%obs_rinsig, grid%fdob%window, &
98 grid%obs_npfi, grid%obs_ionf, &
99 grid%obs_prt_max, grid%obs_prt_freq, &
101 grid%obs_dtramp, grid%fdob, grid%fdob%varobs, &
102 grid%fdob%timeob, grid%fdob%nlevs_ob, grid%fdob%lev_in_ob, &
103 grid%fdob%plfo, grid%fdob%elevob, grid%fdob%rio, &
104 grid%fdob%rjo, grid%fdob%rko, &
105 grid%xlat, grid%xlong, &
106 config_flags%cen_lat, &
107 config_flags%cen_lon, &
108 config_flags%stand_lon, &
109 config_flags%truelat1, config_flags%truelat2, &
110 grid%fdob%known_lat, grid%fdob%known_lon, &
111 config_flags%dx, config_flags%dy, rovg, t0, &
113 grid%fdob%latprt, grid%fdob%lonprt, &
114 grid%fdob%mlatprt, grid%fdob%mlonprt, &
116 ims, ime, jms, jme, &
117 its, ite, jts, jte, &
118 config_flags%map_proj, &
119 model_config_rec%parent_grid_ratio, &
120 model_config_rec%i_parent_start(inest), &
121 model_config_rec%j_parent_start(inest), &
122 model_config_rec%max_dom, grid%fdob%refprt, &
123 model_config_rec%nobs_ndg_vars, grid%max_obs, iprt_in4dob)
125 !$OMP END PARALLEL DO
131 END SUBROUTINE wrf_fddaobs_in
133 !------------------------------------------------------------------------------
134 ! Begin subroutine in4dob and its subroutines
135 !------------------------------------------------------------------------------
136 SUBROUTINE in4dob(inest, xtime, ktau, ktaur, dtmin, &
137 myear, julday, gmt, & !obsnypatch
138 nudge_opt, iswind, istemp, &
139 ismois, ispstr, giv, &
141 rinxy, rinsig, twindo, &
142 npfi, ionf, prt_max, prt_freq, idynin, &
143 dtramp, fdob, varobs, &
144 timeob, nlevs_ob, lev_in_ob, &
151 true_lat1, true_lat2, &
152 known_lat, known_lon, &
153 dxm, dym, rovg, t0, &
156 mlat_prt, mlon_prt, &
158 ims, ime, jms, jme, &
159 its, ite, jts, jte, &
168 USE module_model_constants, ONLY : rcp
169 USE module_date_time , ONLY : geth_idts
174 ! THIS IS SUBROUTINE READS AN OBSERVATION DATA FILE AND
175 ! SELECTS ONLY THOSE VALUES OBSERVED AT TIMES THAT FALL
176 ! WITHIN A TIME WINDOW (TWINDO) CENTERED ABOUT THE CURRENT
177 ! FORECAST TIME (XTIME). THE INCOMING OBS FILES MUST BE
178 ! IN CHRONOLOGICAL ORDER.
180 ! NOTE: This routine was originally designed for MM5, which uses
181 ! a nonstandard (I,J) coordinate system. For WRF, I is the
182 ! east-west running coordinate, and J is the south-north
183 ! running coordinate. So "J-slab" here is west-east in
184 ! extent, not south-north as for MM5. RIO and RJO have
185 ! the opposite orientation here as for MM5. -ajb 06/10/2004
187 ! NOTE - IN4DOB IS CALLED ONLY FOR THE COARSE MESH TIMES
188 ! - VAROBS(IVAR,N) HOLDS THE OBSERVATIONS.
193 ! IVAR=5 OBS Pressure
196 INTEGER, intent(in) :: niobf ! maximum number of observations
197 INTEGER, intent(in) :: nndgv ! number of nudge variables
198 INTEGER, intent(in) :: INEST ! nest level
199 REAL, intent(in) :: xtime ! model time in minutes
200 INTEGER, intent(in) :: KTAU ! current timestep
201 INTEGER, intent(in) :: KTAUR ! restart timestep
202 REAL, intent(in) :: dtmin ! dt in minutes
203 INTEGER, intent(in) :: myear ! model year !obsnypatch
204 INTEGER, intent(in) :: julday ! Julian day
205 REAL, intent(in) :: gmt ! Model GMT at start of run
206 INTEGER, intent(in) :: nudge_opt ! obs-nudge flag for this nest
207 INTEGER, intent(in) :: iswind ! nudge flag for wind
208 INTEGER, intent(in) :: istemp ! nudge flag for temperature
209 INTEGER, intent(in) :: ismois ! nudge flag for moisture
210 INTEGER, intent(in) :: ispstr ! nudge flag for pressure (obsolete)
211 REAL, intent(in) :: giv ! coefficient for wind
212 REAL, intent(in) :: git ! coefficient for temperature
213 REAL, intent(in) :: giq ! coefficient for moisture
214 REAL, intent(in) :: gip ! coefficient for pressure
215 REAL, intent(in) :: rinxy ! horizontal radius of influence (km)
216 REAL, intent(in) :: rinsig ! vertical radius of influence (on sigma)
217 REAL, intent(inout) :: twindo ! (time window)/2 (min) for nudging
218 INTEGER, intent(in) :: npfi ! coarse-grid time-step frequency for diagnostics
219 INTEGER, intent(in) :: ionf ! coarse-grid time-step frequency for obs-nudging calcs
220 INTEGER, intent(in) :: prt_max ! max number of entries of obs for diagnostic printout
221 INTEGER, intent(in) :: prt_freq ! frequency (in obs index) for diagnostic printout.
222 INTEGER, intent(in) :: idynin ! for dynamic initialization using a ramp-down function
223 REAL, intent(in) :: dtramp ! time period in minutes for ramping
224 TYPE(fdob_type), intent(inout) :: fdob ! derived data type for obs data
225 REAL, intent(inout) :: varobs(nndgv,niobf) ! observational values in each variable
226 REAL, intent(inout) :: timeob(niobf) ! model times for each observation (hours)
227 REAL, intent(inout) :: nlevs_ob(niobf) ! numbers of levels in sounding obs
228 REAL, intent(inout) :: lev_in_ob(niobf) ! level in sounding-type obs
229 REAL, intent(inout) :: plfo(niobf) ! index for type of obs-platform
230 REAL, intent(inout) :: elevob(niobf) ! elevations of observations (meters)
231 REAL, intent(inout) :: rio(niobf) ! west-east grid coordinate (non-staggered grid)
232 REAL, intent(inout) :: rjo(niobf) ! south-north grid coordinate (non-staggered grid)
233 REAL, intent(inout) :: rko(niobf) ! vertical grid coordinate
234 REAL, DIMENSION( ims:ime, jms:jme ), &
235 INTENT(IN ) :: xlat, xlong ! lat/lon on mass-pt grid (for diagnostics only)
236 REAL, intent(in) :: cen_lat ! center latitude for map projection
237 REAL, intent(in) :: cen_lon ! center longitude for map projection
238 REAL, intent(in) :: stand_lon ! standard longitude for map projection
239 REAL, intent(in) :: true_lat1 ! truelat1 for map projection
240 REAL, intent(in) :: true_lat2 ! truelat2 for map projection
241 REAL, intent(in) :: known_lat ! latitude of domain origin point (i,j)=(1,1)
242 REAL, intent(in) :: known_lon ! longigude of domain origin point (i,j)=(1,1)
243 REAL, intent(in) :: dxm ! grid size in x (meters)
244 REAL, intent(in) :: dym ! grid size in y (meters)
245 REAL, intent(in) :: rovg ! constant rho over g
246 REAL, intent(in) :: t0 ! background temperature
247 INTEGER, intent(inout) :: obs_prt(prt_max) ! For printout of obs index
248 REAL, intent(inout) :: lat_prt(prt_max) ! For printout of obs latitude
249 REAL, intent(inout) :: lon_prt(prt_max) ! For printout of obs longitude
250 REAL, intent(inout) :: mlat_prt(prt_max) ! For printout of model lat at obs (ri,rj)
251 REAL, intent(inout) :: mlon_prt(prt_max) ! For printout of model lon at obs (ri,rj)
252 INTEGER, intent(in) :: e_we ! max grid index in south-north coordinate
253 INTEGER, intent(in) :: e_sn ! max grid index in west-east coordinate
254 INTEGER, intent(in) :: ims ! grid memory start index (west-east dim)
255 INTEGER, intent(in) :: ime ! grid memory end index (west-east dim)
256 INTEGER, intent(in) :: jms ! grid memory start index (south-north dim)
257 INTEGER, intent(in) :: jme ! grid memory end index (south-north dim)
258 INTEGER, intent(in) :: its ! grid tile start index (west-east dim)
259 INTEGER, intent(in) :: ite ! grid tile end index (west-east dim)
260 INTEGER, intent(in) :: jts ! grid tile start index (south-north dim)
261 INTEGER, intent(in) :: jte ! grid tile end index (south-north dim)
262 INTEGER, intent(in) :: map_proj ! map projection index
263 INTEGER, intent(in) :: parent_grid_ratio ! parent to nest grid ration
264 INTEGER, intent(in) :: i_parent_start ! starting i coordinate in parent domain
265 INTEGER, intent(in) :: j_parent_start ! starting j coordinate in parent domain
266 INTEGER, intent(in) :: maxdom ! maximum number of domains
267 INTEGER, intent(inout) :: refprt(maxdom) ! reference obs index for printout
268 LOGICAL, intent(in) :: iprt ! print flag
270 !*** DECLARATIONS FOR IMPLICIT NONE
271 integer :: n, ndum, nopen, nvol, idate, imm, iss
272 integer :: nlast ! the (very) last obs in the list
273 integer :: nsta ! number of stations held in timeobs array
274 integer :: nstaw ! # stations within the actual time window
275 integer :: nprev=0 ! previous n in obs read loop (for printout only)
276 integer :: meas_count, imc, njend, njc, njcc, julob, kn
277 real :: hourob, rjulob
278 real :: xhour, tback, tforwd, rjdate1, timanl1, rtimob
279 real :: rj, ri, elevation, pressure_data
280 real :: pressure_qc, height_data, height_qc, temperature_data
281 real :: temperature_qc, u_met_data, u_met_qc, v_met_data
282 real :: v_met_qc, rh_data, rh_qc, r_data, slp_data, slp_qc
283 real :: ref_pres_data, ref_pres_qc, psfc_data, psfc_qc
284 real :: precip_data, precip_qc, tbar, twdop
286 INTEGER, EXTERNAL :: nvals_le_limit ! for finding #obs with timeobs <= tforwd
289 TYPE (PROJ_INFO) :: obs_proj ! Structure for obs projection information.
290 character*14 date_char
291 character*19 obs_date !obsnypatch
292 integer idts !obsnypatch
293 character*40 platform,source,id,namef
295 character(len=200) :: msg ! Argument to wrf_message
296 real latitude,longitude
297 real :: plat_prt=0.0 ! Previous obs latitude in read loop (used for printout)
298 real :: plon_prt=0.0 ! Previous obs longitude in read loop (used for printout)
299 logical :: newpass ! New pass flag (used for printout)
300 logical is_sound,bogus
302 integer :: ieof(5),ifon(5)
305 integer :: nmove, nvola
306 integer :: iyear, itimob !obsnypatch
307 ! external :: fcst_hours !obsnypatch
308 ! real :: fcst_hours !obsnypatch
309 DATA NMOVE/0/,NVOLA/61/
311 ! if(ieof(inest).eq.2.and.fdob%nstat.eq.0)then
312 ! IF (iprt) print *,'returning from in4dob'
315 ! IF (iprt) print *,'start in4dob ',inest,xtime
316 IF(nudge_opt.NE.1)RETURN
318 ! Initialize obs for printout
322 ! if start time, or restart time, set obs array to missing value
323 IF(KTAU.EQ.0.OR.KTAU.EQ.KTAUR) THEN
327 fdob%xtime_at_rest = xtime !yliu 20080127
329 ! set number of obs=0 if at start or restart
330 IF(KTAU.EQ.KTAUR)fdob%NSTAT=0
334 XHOUR=AMAX1(XHOUR,0.0)
338 ! DEFINE THE MAX LIMITS OF THE WINDOW
343 write(msg,fmt='(2(a,f8.3),a,i2)') &
344 'OBS NUDGING: Reading new obs for time window TBACK = ', &
345 tback,' TFORWD = ',tforwd,' for grid = ',inest
346 call wrf_message(msg)
349 ! For obs that have become invalid because they are too old for the current time
350 ! window, mark with 99999 to set up for removal from the rolling valid-obs list.
354 t_window : DO N=1,NSTA+1
355 IF((TIMEOB(N)-TBACK).LT.0) THEN
358 IF(TIMEOB(N).LT.9.E4) EXIT t_window
362 IF (iprt .and. ndum>0) THEN
363 write(msg,'(a,i5,2a)') 'OBS NUDGING: ',ndum,' previously read obs ', &
364 'are now too old for the current window and have been removed.'
365 call wrf_message(msg)
368 ! REMOVE OLD OBS DENOTED BY 99999. AT THE FRONT OF TIMEOB ARRAY
369 ! IF (iprt) print *,'ndum at 20=',ndum
372 IF(NMOVE.GT.0 .AND. NDUM.NE.0 ) THEN
375 VAROBS(KN,N)=VAROBS(KN,N+NDUM)
377 ! RIO is the west-east coordinate. RJO is south-north. (ajb)
381 TIMEOB(N)=TIMEOB(N+NDUM)
382 nlevs_ob(n)=nlevs_ob(n+ndum)
383 lev_in_ob(n)=lev_in_ob(n+ndum)
385 elevob(n)=elevob(n+ndum)
389 IF(NOPEN.LE.NIOBF) THEN
406 ! Compute map projection info.
407 call set_projection(obs_proj, map_proj, cen_lat, cen_lon, &
408 true_lat1, true_lat2, stand_lon, &
409 known_lat, known_lon, &
410 e_we, e_sn, dxm, dym )
412 ! FIND THE LAST OBS IN THE LIST
414 last_ob : DO N=1,NIOBF
415 ! print *,'nlast,n,timeob(n)=',nlast,n,timeob(n)
416 IF(TIMEOB(N).GT.9.E4) EXIT last_ob
420 ! print *,'in4dob, after 90 ',nlast,ktau,ktaur,nsta
421 ! open file if at beginning or restart
422 IF(KTAU.EQ.0.OR.KTAU.EQ.KTAUR) THEN
424 INQUIRE (NVOLA+INEST-1,OPENED=OPENED)
425 IF (.NOT. OPENED) THEN
427 write(fonc(1:2),'(i2)')ifon(inest)
428 if(fonc(1:1).eq.' ')fonc(1:1)='0'
429 INQUIRE (file='OBS_DOMAIN'//CHAR(INEST+ICHAR('0'))//fonc(1:2) &
433 write(msg,*) 'opening first fdda obs file, fonc=', &
435 call wrf_message(msg)
436 write(msg,*) 'ifon=',ifon(inest)
437 call wrf_message(msg)
439 OPEN(NVOLA+INEST-1, &
440 FILE='OBS_DOMAIN'//CHAR(INEST+ICHAR('0'))//fonc(1:2), &
441 FORM='FORMATTED',STATUS='OLD')
443 ! no first file to open
444 IF (iprt) call wrf_message("there are no fdda obs files to open")
449 ENDIF !end if(KTAU.EQ.0.OR.KTAU.EQ.KTAUR)
450 ! print *,'at jc check1'
452 !**********************************************************************
453 ! -------------- BIG 100 LOOP OVER N --------------
454 !**********************************************************************
455 ! NOW CHECK TO SEE IF EXTRA DATA MUST BE READ IN FROM THE
456 ! DATA FILE. CONTINUE READING UNTIL THE REACHING THE EOF
457 ! (DATA TIME IS NEGATIVE) OR FIRST TIME PAST TFORWD. THE
458 ! LAST OBS CURRENTLY AVAILABLE IS IN N=NMOVE.
465 ! ieof=2 means no more files
466 ! print *,'after 1001,n,timeob(n)=',n,timeob(n)
468 IF(IEOF(inest).GT.1) then
473 !ajb 20070116 bugfix for zero array index. N=0 if first obs is not in the domain.
475 IF(TIMEOB(N).GT.TFORWD.and.timeob(n).lt.99999.) THEN
480 ! OBSFILE: no more data in the obsfile
481 if(ieof(inest).eq.1 )then
486 !**********************************************************************
487 ! -------------- 110 SUBLOOP OVER N --------------
488 !**********************************************************************
489 ! THE TIME OF THE MOST RECENTLY ACQUIRED OBS IS .LE. TFORWD,
490 ! SO CONTINUE READING
493 IF(N.GT.NIOBF-1)GOTO 120
494 ! REPLACE NVOLA WITH LUN 70, AND USE NVOLA AS A FILE COUNTER
496 IF(fdob%IEODI.EQ.1)GOTO 111
497 read(nvol,101,end=111,err=111)date_char
502 ! Convert the form of the observation date for geth_idts.
503 call fmt_date(date_char, obs_date)
505 ! Compute the time period in seconds from the model reference
506 ! date (fdob%sdate) until the observation date.
508 call geth_idts(obs_date, fdob%sdate(1:19), idts)
510 ! Convert time in seconds to hours.
511 ! In case of restart, correct for new sdate.
512 idts = idts + nint(fdob%xtime_at_rest*60.) ! yliu 20080127
514 rtimob =float(idts)/3600.
517 ! print *,'read in ob ',n,timeob(n),rtimob
518 IF(IDYNIN.EQ.1.AND.TIMEOB(N)*60..GT.fdob%DATEND) THEN
520 write(msg,*) ' IN4DOB: FOR INEST = ',INEST,' AT XTIME = ',XTIME, &
521 ' TIMEOB = ',TIMEOB(N)*60.,' AND DATEND = ',fdob%DATEND,' :'
522 call wrf_message(msg)
523 write(msg,*) ' END-OF-DATA FLAG SET FOR OBS-NUDGING', &
524 ' DYNAMIC INITIALIZATION'
525 call wrf_message(msg)
531 read(nvol,102)latitude,longitude
532 102 FORMAT(2x,2(f9.4,1x))
534 ! save obs and model latitude and longitude for printout
535 CALL collect_obs_info(newpass,inest,n,latitude,longitude,nprev,niobf, &
536 refprt(inest),rio,rjo,prt_max,prt_freq,xlat,xlong, &
537 obs_prt,lat_prt,lon_prt,mlat_prt,mlon_prt, &
538 plat_prt,plon_prt, e_we,e_sn, &
539 ims,ime,jms,jme,its,ite,jts,jte)
541 ! if(ifon.eq.4)print *,'ifon=4',latitude,longitude
542 ! this works only for lc projection
543 ! yliu: add llxy for all 3 projection
545 !ajb Arguments ri and rj have been switched from MM5 orientation.
547 CALL latlon_to_ij(obs_proj, latitude, longitude, ri, rj)
549 !ajb ri and rj are referenced to the non-staggered grid (not mass-pt!).
550 ! (For MM5, they were referenced to the dot grid.)
552 ri = ri + .5 !ajb Adjust from mass-pt to non-staggered grid.
553 rj = rj + .5 !ajb Adjust from mass-pt to non-staggered grid.
558 read(nvol,1021)id,namef
559 1021 FORMAT(2x,2(a40,3x))
560 read(nvol,103)platform,source,elevation,is_sound,bogus,meas_count
561 103 FORMAT( 2x,2(a16,2x),f8.0,2x,2(l4,2x),i5)
563 ! write(6,*) '----- OBS description ----- '
564 ! write(6,*) 'platform,source,elevation,is_sound,bogus,meas_count:'
565 ! write(6,*) platform,source,elevation,is_sound,bogus,meas_count
570 ! change platform from synop to profiler when needed
571 if(namef(2:9).eq.'PROFILER')platform(7:14)='PROFILER'
573 if(namef(2:6).eq.'ACARS')platform(7:11)='ACARS'
574 if(namef(1:7).eq.'SATWNDS') platform(1:11)='SATWNDS '
575 if(namef(1:8).eq.'CLASS DA')platform(7:10)='TEMP'
580 ! if((platform(7:11).eq.'METAR').or.(platform(7:11).eq.'SPECI').or.
581 ! 1 (platform(7:10).eq.'SHIP').or.(platform(7:11).eq.'SYNOP').or.
582 ! 1 (platform(1:4).eq.'SAMS'))
584 if(.NOT. is_sound) rko(n)=1.0
587 ! plfo is inFORMATion on what platform. May use this later in adjusting weights
589 if(platform(7:11).eq.'METAR')plfo(n)=1.
590 if(platform(7:11).eq.'SPECI')plfo(n)=2.
591 if(platform(7:10).eq.'SHIP')plfo(n)=3.
592 if(platform(7:11).eq.'SYNOP')plfo(n)=4.
593 if(platform(7:10).eq.'TEMP')plfo(n)=5.
594 if(platform(7:11).eq.'PILOT')plfo(n)=6.
595 if(platform(1:7).eq.'SATWNDS')plfo(n)=7.
596 if(platform(1:4).eq.'SAMS')plfo(n)=8.
597 if(platform(7:14).eq.'PROFILER')plfo(n)=9.
598 ! yliu: ACARS->SATWINDS
599 if(platform(7:11).eq.'ACARS')plfo(n)=7.
601 if(plfo(n).eq.99.) then
603 write(msg,*) 'n=',n,' unknown ob of type ',platform
604 call wrf_message(msg)
608 !======================================================================
609 !======================================================================
610 ! THIS PART READS SOUNDING INFO
612 nlevs_ob(n)=real(meas_count)
615 ! write(6,*) '0 inest = ',inest,' n = ',n
616 ! the sounding has one header, many levels. This part puts it into
617 ! "individual" observations. There's no other way for nudob to deal
619 if(imc.gt.1)then ! sub-loop over N
621 if(n.gt.niobf)goto 120
622 nlevs_ob(n)=real(meas_count)
623 lev_in_ob(n)=real(imc)
628 plfo(n)=plfo(n-imc+1)
632 read(nvol,104)pressure_data,pressure_qc, &
633 height_data,height_qc, &
634 temperature_data,temperature_qc, &
635 u_met_data,u_met_qc, &
636 v_met_data,v_met_qc, &
638 104 FORMAT( 1x,6(f11.3,1x,f11.3,1x))
640 ! yliu: Ensemble - add disturbance to upr obs
641 ! if(plfo(n).eq.5.or.plfo(n).eq.6.or.plfo(n).eq.9) then FORE07E08
642 ! if(imc.eq.1) then FORE07E08
644 ! t_rand =- (rand(2)-0.5)*6
645 ! call srand(n+100000)
646 ! u_rand =- (rand(2)-0.5)*6
647 ! call srand(n+200000)
648 ! v_rand =- (rand(2)-0.5)*6
650 ! if(temperature_qc.ge.0..and.temperature_qc.lt.30000..and.
651 ! & temperature_data .gt. -88880.0 )
652 ! & temperature_data = temperature_data + t_rand
653 ! if((u_met_qc.ge.0..and.u_met_qc.lt.30000.).and.
654 ! & (v_met_qc.ge.0..and.v_met_qc.lt.30000.).and.
655 ! make sure at least 1 of the components is .ne.0
656 ! & (u_met_data.ne.0..or.v_met_data.ne.0.) .and.
657 ! & (u_met_data.gt.-88880.0 .and. v_met_data.gt.-88880.0) )then
658 ! u_met_data = u_met_data + u_rand
659 ! v_met_data = v_met_data + v_rand
662 ! yliu: Ens test - end
666 ! hardwire to switch -777777. qc to 0. here temporarily
667 ! -777777. is a sounding level that no qc was done on.
669 if(temperature_qc.eq.-777777.)temperature_qc=0.
670 if(pressure_qc.eq.-777777.)pressure_qc=0.
671 if(height_qc.eq.-777777.)height_qc=0.
672 if(u_met_qc.eq.-777777.)u_met_qc=0.
673 if(v_met_qc.eq.-777777.)v_met_qc=0.
674 if(rh_qc.eq.-777777.)rh_qc=0.
675 if(temperature_data.eq.-888888.)temperature_qc=-888888.
676 if(pressure_data.eq.-888888.)pressure_qc=-888888.
677 if(height_data.eq.-888888.)height_qc=-888888.
678 if(u_met_data.eq.-888888.)u_met_qc=-888888.
679 if(v_met_data.eq.-888888.)v_met_qc=-888888.
680 if(rh_data.eq.-888888.)rh_qc=-888888.
683 ! Hardwire so that only use winds in pilot obs (no winds from temp) and
684 ! only use temperatures and rh in temp obs (no temps from pilot obs)
685 ! Do this because temp and pilot are treated as 2 platforms, but pilot
686 ! has most of the winds, and temp has most of the temps. If use both,
687 ! the second will smooth the effect of the first. Usually temps come in after
688 ! pilots. pilots usually don't have any temps, but temp obs do have some
690 ! plfo=5 is TEMP ob, range sounding is an exception
691 !yliu start -- comment out to test with merged PILOT and TEMP and
692 ! do not use obs interpolated by little_r
693 ! if(plfo(n).eq.5. .and. namef(1:8).ne.'CLASS DA')then
694 ! u_met_data=-888888.
695 ! v_met_data=-888888.
699 if(plfo(n).eq.5..and.(u_met_qc.eq.256..or.v_met_qc.eq.256.))then
707 if(plfo(n).eq.6.)then
708 temperature_data=-888888.
710 temperature_qc=-888888.
714 !ajb Store temperature for WRF
715 ! NOTE: The conversion to potential temperature, performed later in subroutine
716 ! errob, requires good pressure data, either directly or via good height data.
717 ! So here, in addition to checking for good temperature data, we must also
718 ! do a check for good pressure or height.
719 if(temperature_qc.ge.0..and.temperature_qc.lt.30000.)then
721 if( (pressure_qc.ge.0..and.pressure_qc.lt.30000.) .or. &
722 (height_qc .ge.0..and.height_qc .lt.30000.) ) then
724 varobs(3,n) = temperature_data
733 !ajb Store obs height
734 if(height_qc.ge.0..and.height_qc.lt.30000.)then
735 varobs(6,n)=height_data
740 if(pressure_qc.ge.0..and.pressure_qc.lt.30000.)then
741 ! if(pressure_qc.ge.0.)then
742 varobs(5,n)=pressure_data
746 if(varobs(6,n).eq.-888888.000) then
747 call wrf_message("********** PROBLEM *************")
748 write(msg,*) 'sounding, p and ht undefined',latitude,longitude
749 call wrf_message(msg)
753 if(varobs(5,n).ge.0.)varobs(5,n)=varobs(5,n)*1.e-3
754 ! don't use data above 80 mb
755 if((varobs(5,n).gt.0.).and.(varobs(5,n).le.8.))then
760 temperature_data=-888888.
761 temperature_qc=-888888.
767 ! Store horizontal wind components for WRF
768 if((u_met_qc.ge.0..and.u_met_qc.lt.30000.).and. &
769 (v_met_qc.ge.0..and.v_met_qc.lt.30000.).and. &
770 ! make sure at least 1 of the components is .ne.0
771 (u_met_data.ne.0..or.v_met_data.ne.0.))then
773 ! If Earth-relative wind vector, need to rotate it to grid-relative coords
774 if(u_met_qc.eq.129. .and. v_met_qc.eq.129.) then
775 CALL rotate_vector(longitude,u_met_data,v_met_data, &
778 varobs(1,n)=u_met_data
779 varobs(2,n)=v_met_data
787 if(rh_qc.ge.0..and.rh_qc.lt.30000.)then
788 if((pressure_qc.ge.0.).and.(temperature_qc.ge.0.).and. &
789 (pressure_qc.lt.30000.).and.(temperature_qc.lt.30000.))then
790 call rh2r(rh_data,temperature_data,pressure_data*.01, &
791 r_data,0) ! yliu, change last arg from 1 to 0
793 ! print *,'rh, but no t or p to convert',temperature_qc, &
799 enddo ! end do imc=1,meas_count
800 ! print *,'--- sdng n=',n,nlevs_ob(n),lev_in_ob(n),timeob(n)
801 ! read in non-sounding obs
803 ELSEIF(.NOT.is_sound)THEN
806 read(nvol,105)slp_data,slp_qc, &
807 ref_pres_data,ref_pres_qc, &
808 height_data,height_qc, &
809 temperature_data,temperature_qc, &
810 u_met_data,u_met_qc, &
811 v_met_data,v_met_qc, &
814 precip_data,precip_qc
815 105 FORMAT( 1x,9(f11.3,1x,f11.3,1x))
817 ! Ensemble: add disturbance to sfc obs
819 ! t_rand =+ (rand(2)-0.5)*5
820 ! call srand(n+100000)
821 ! u_rand =+ (rand(2)-0.5)*5
822 ! call srand(n+200000)
823 ! v_rand =+ (rand(2)-0.5)*5
824 ! if(temperature_qc.ge.0..and.temperature_qc.lt.30000. .and.
825 ! & temperature_data .gt. -88880.0 )
826 ! & temperature_data = temperature_data + t_rand
827 ! if((u_met_qc.ge.0..and.u_met_qc.lt.30000.).and.
828 ! & (v_met_qc.ge.0..and.v_met_qc.lt.30000.).and.
829 ! make sure at least 1 of the components is .ne.0
830 ! & (u_met_data.ne.0..or.v_met_data.ne.0.) .and.
831 ! & (u_met_data.gt.-88880.0 .and. v_met_data.gt.-88880.0) )then
832 ! u_met_data = u_met_data + u_rand
833 ! v_met_data = v_met_data + v_rand
835 ! yliu: Ens test - end
839 ! calculate psfc if slp is there
840 if((psfc_qc.lt.0.).and.(slp_qc.ge.0..and.slp_qc.lt.30000.).and. &
841 (temperature_qc.ge.0..and.temperature_qc.lt.30000.).and. &
842 (slp_data.gt.90000.))then
843 tbar=temperature_data+0.5*elevation*.0065
844 psfc_data=slp_data*exp(-elevation/(rovg*tbar))
845 varobs(5,n)=psfc_data
849 !c *No* **Very rough** estimate of psfc from sfc elevation if UUtah ob and elev>1000m
850 ! estimate psfc from temp and elevation
851 ! Do not know sfc pressure in model at this point.
852 ! if((psfc_qc.lt.0.).and.(elevation.gt.1000.).and.
853 ! 1 (temperature_qc.ge.0..and.temperature_qc.lt.30000.)
854 ! 1 .and.(platform(7:16).eq.'SYNOP PRET'))then
855 if((psfc_qc.lt.0.).and. &
856 (temperature_qc.ge.0..and.temperature_qc.lt.30000.))then
857 tbar=temperature_data+0.5*elevation*.0065
858 psfc_data=100000.*exp(-elevation/(rovg*tbar))
859 varobs(5,n)=psfc_data
863 if((psfc_qc.ge.0..and.psfc_qc.lt.30000.).and.(psfc_data.gt.70000. &
864 .and.psfc_data.lt.105000.))then
865 varobs(5,n)=psfc_data
870 if(varobs(5,n).ge.0.)varobs(5,n)=varobs(5,n)*1.e-3
873 !ajb Store temperature for WRF
874 if(temperature_qc.ge.0..and.temperature_qc.lt.30000.)then
876 if((psfc_qc.ge.0..and.psfc_qc.lt.30000.).and. &
877 (psfc_data.gt.70000. .and.psfc_data.lt.105000.))then
879 varobs(3,n) = temperature_data
887 ! Store horizontal wind components for WRF
888 if((u_met_qc.ge.0..and.u_met_qc.lt.30000.).and. &
889 (v_met_qc.ge.0..and.v_met_qc.lt.30000.).and. &
890 ! make sure at least 1 of the components is .ne.0
891 (u_met_data.ne.0..or.v_met_data.ne.0.))then
893 ! If Earth-relative wind vector, need to rotate it to grid-relative coords
894 if(u_met_qc.eq.129. .and. v_met_qc.eq.129.) then
895 CALL rotate_vector(longitude,u_met_data,v_met_data, &
898 varobs(1,n)=u_met_data
899 varobs(2,n)=v_met_data
906 ! if a ship ob has rh<70%, then throw out
908 if(plfo(n).eq.3..and.rh_qc.ge.0..and.rh_data.lt.70.)then
914 if(rh_qc.ge.0..and.rh_qc.lt.30000.)then
915 if((psfc_qc.ge.0..and.psfc_qc.lt.30000.) &
916 .and.(temperature_qc.ge.0..and.temperature_qc.lt.30000.))then
917 ! rh_data=amin1(rh_data,96.) ! yliu: do not allow surface to be saturated
918 call rh2r(rh_data,temperature_data,psfc_data*.01, &
919 r_data,0) ! yliu, change last arg from 1 to 0
921 ! print *,'rh, but no t or p',temperature_data,
929 call wrf_message(" ====== ")
930 call wrf_message(" NO Data Found ")
932 ENDIF !end if(is_sound)
933 ! END OF SFC OBS INPUT SECTION
934 !======================================================================
935 !======================================================================
936 ! check if ob time is too early (only applies to beginning)
937 IF(RTIMOB.LT.TBACK-TWINDO)then
938 IF (iprt) call wrf_message("ob too early")
943 ! check if this ob is a duplicate
944 ! this check has to be before other checks
946 if(is_sound)njend=n-meas_count
948 ! Check that time, lat, lon, and platform all match exactly.
949 ! Platforms 1-4 (surface obs) can match with each other. Otherwise,
950 ! platforms have to match exactly.
951 if( (timeob(n).eq.timeob(njc)) .and. &
952 (rio(n).eq.rio(njc)) .and. &
953 (rjo(n).eq.rjo(njc)) .and. &
954 (plfo(njc).ne.99.) ) then
955 !yliu: if two sfc obs are departed less than 1km, consider they are redundant
956 ! (abs(rio(n)-rio(njc))*dscg.gt.1000.) &
957 ! .or. (abs(rjo(n)-rjo(njc))*dscg.gt.1000.) &
958 ! .or. (plfo(njc).eq.99.) )goto 801
960 ! If platforms different, and either > 4, jump out
961 if( ( (plfo(n).le.4.).and.(plfo(njc).le.4.) ) .or. &
962 (plfo(n).eq.plfo(njc)) ) then
964 ! if not a sounding, and levels are the same then replace first occurrence
965 if((.not.is_sound).and.(rko(njc).eq.rko(n))) then
966 ! print *,'dup single ob-replace ',n,inest,
968 ! this is the sfc ob replacement part
970 VAROBS(KN,njc)=VAROBS(KN,n)
972 ! don't need to switch these because they're the same
976 ! TIMEOB(njc)=TIMEOB(n)
977 ! nlevs_ob(njc)=nlevs_ob(n)
978 ! lev_in_ob(njc)=lev_in_ob(n)
980 ! end sfc ob replacement part
984 ! It's harder to fix the soundings, since the number of levels may be different
985 ! The easiest thing to do is to just set the first occurrence to all missing, and
986 ! keep the second occurrence, or vice versa.
987 ! For temp or profiler keep the second, for pilot keep the one with more levs
988 ! This is for a temp or prof sounding, equal to same
989 ! also if a pilot, but second one has more obs
990 elseif( (is_sound).and.(plfo(njc).eq.plfo(n)) .and. &
991 ( (plfo(njc).eq.5.).or.(plfo(njc).eq.9.).or. &
992 ( (plfo(njc).eq.6.).and. &
993 (nlevs_ob(n).ge.nlevs_ob(njc)) ) ) )then
995 write(msg,*) 'duplicate sounding - eliminate first occurrence', &
996 n,inest,meas_count,nlevs_ob(njc), &
997 latitude,longitude,plfo(njc)
998 call wrf_message(msg)
1000 if(lev_in_ob(njc).ne.1.) then
1002 write(msg,*) 'problem ******* - dup sndg ', &
1003 lev_in_ob(njc),nlevs_ob(njc)
1004 call wrf_message(msg)
1008 ! set the first sounding ob to missing
1009 do njcc=njc,njc+nint(nlevs_ob(njc))-1
1011 VAROBS(KN,njcc)=-888888.
1016 ! if a pilot, but first one has more obs
1017 elseif( (is_sound).and.(plfo(njc).eq.plfo(n)) .and. &
1018 (plfo(njc).eq.6.).and. &
1019 (nlevs_ob(n).lt.nlevs_ob(njc)) )then
1022 'duplicate pilot sounding - eliminate second occurrence', &
1023 n,inest,meas_count,nlevs_ob(njc), &
1024 latitude,longitude,plfo(njc)
1025 call wrf_message(msg)
1027 if(lev_in_ob(njc).ne.1.) then
1029 write(msg,*) 'problem ******* - dup sndg ', &
1030 lev_in_ob(njc),nlevs_ob(njc)
1031 call wrf_message(msg)
1036 !ajb Reset timeob for discarded indices.
1037 do imc = n+1, n+meas_count
1038 timeob(imc) = 99999.
1041 ! This is for a single-level satellite upper air ob - replace first
1042 elseif( (is_sound).and. &
1043 (nlevs_ob(njc).eq.1.).and. &
1044 (nlevs_ob(n).eq.1.).and. &
1045 (varobs(5,njc).eq.varobs(5,n)).and. &
1046 (plfo(njc).eq.7.).and.(plfo(n).eq.7.) ) then
1049 'duplicate single lev sat-wind ob - replace first',n, &
1050 inest,meas_count,varobs(5,n)
1051 call wrf_message(msg)
1053 ! this is the single ua ob replacement part
1055 VAROBS(KN,njc)=VAROBS(KN,n)
1057 ! don't need to switch these because they're the same
1061 ! TIMEOB(njc)=TIMEOB(n)
1062 ! nlevs_ob(njc)=nlevs_ob(n)
1063 ! lev_in_ob(njc)=lev_in_ob(n)
1065 ! end single ua ob replacement part
1070 write(msg,*) 'duplicate location, but no match otherwise',n,njc, &
1071 plfo(n),varobs(5,n),nlevs_ob(n),lev_in_ob(n), &
1072 plfo(njc),varobs(5,njc),nlevs_ob(njc),lev_in_ob(njc)
1073 call wrf_message(msg)
1078 ! end of njc do loop
1081 ! check if ob is a sams ob that came in via UUtah - discard
1082 if( plfo(n).eq.4..and.(platform(7:16).eq.'SYNOP PRET').and. &
1083 (id(7:15).eq.'METNET= 3') )then
1084 ! print *,'elim metnet=3',latitude,longitude,rtimob
1090 !! check if ob is in coarse mesh domain (061404 switched sn/we)
1091 ! if( (ri.lt.2.).or.(ri.gt.real(we_maxcg-1)).or.(rj.lt.2.).or. &
1092 ! (rj.gt.real(sn_maxcg-1)) ) then
1094 ! check if ob is in the domain
1095 if( (ri.lt.2.).or.(ri.gt.real(e_we-1)).or.(rj.lt.2.).or. &
1096 (rj.gt.real(e_sn-1)) ) then
1099 !ajb Reset timeob for discarded indices.
1100 do imc = n+1, n+meas_count
1101 timeob(imc) = 99999.
1106 ! check if an upper air ob is too high
1107 ! the ptop here is hardwired
1108 ! this check has to come after other checks - usually the last few
1109 ! upper air obs are too high
1112 ! do jcj=meas_count,1,-1
1113 ! 6. is 60 mb - hardwired
1114 ! if((varobs(5,n).lt.6.).and.(varobs(5,n).gt.0.))then
1115 ! print *,'obs too high - eliminate,n,p=',n,varobs(5,n)
1118 ! if(varobs(5,n).gt.0.)goto 100
1123 IF(TIMEOB(N).LT.fdob%RTLAST) THEN
1125 call wrf_message("2 OBS ARE NOT IN CHRONOLOGICAL ORDER")
1126 call wrf_message("NEW YEAR?")
1127 write(msg,*) 'timeob,rtlast,n=',timeob(n),fdob%rtlast,n
1128 call wrf_message(msg)
1130 call wrf_error_fatal ( 'wrf_fddaobs_in: in4dob STOP 111' )
1132 fdob%RTLAST=TIMEOB(N)
1136 !**********************************************************************
1137 ! -------------- END BIG 100 LOOP OVER N --------------
1138 !**********************************************************************
1141 write(msg,5403) NVOL,XTIME
1142 call wrf_message(msg)
1146 close(NVOLA+INEST-1)
1148 write(msg,*) 'closed fdda file for inest=',inest,nsta
1149 call wrf_message(msg)
1152 ! if(nsta.eq.1.and.timeob(1).gt.9.e4)nsta=0
1155 ! THE OBSERVATION ARRAYS ARE FULL AND THE MOST RECENTLY
1156 ! ACQUIRED OBS STILL HAS TIMEOB .LE. TFORWD. SO START
1157 ! DECREASING THE SIZE OF THE WINDOW
1158 ! get here if too many obs
1161 write(msg,121) N,NIOBF
1162 call wrf_message(msg)
1164 call wrf_message(msg)
1166 call wrf_error_fatal ( 'wrf_fddaobs_in: in4dob STOP 122' )
1167 TWINDO=TWINDO-0.1*TWINDO
1168 IF(TWINDO.LT.0) call wrf_error_fatal ( 'wrf_fddaobs_in: in4dob STOP 120' )
1169 ! IF THE WINDOW BECOMES NEGATIVE, THE INCOMING DATA IS
1170 ! PROBABLY GARBLED. STOP.
1173 ! READ CYCLE IS COMPLETED. DETERMINE THE NUMBER OF OBS IN
1174 ! THE CURRENT WINDOW
1176 !CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
1177 ! BUT FIRST, WHEN KTAU.EQ.0 (OR IN GENERAL, KTAUR), DISCARD THE
1178 ! "OLD" OBS FIRST...
1181 ! Get here if at end of file, or if obs time is beyond what we need right now.
1182 ! On startup, we report the index of the last obs read.
1183 ! For restarts, we need to remove any old obs and then repack obs list.
1184 IF(KTAU.EQ.KTAUR)THEN
1186 keep_obs : DO N=1,NIOBF
1187 ! try to keep all obs, but just don't use yet
1188 ! (don't want to throw away last obs read in - especially if
1189 ! its a sounding, in which case it looks like many obs)
1190 IF(TIMEOB(N).GT.9.e4) EXIT keep_obs
1191 if(timeob(n).gt.tforwd) then
1193 write(msg,950) inest
1194 call wrf_message(msg)
1195 write(msg,951) n,timeob(n),tforwd
1196 call wrf_message(msg)
1198 950 FORMAT('Saving index of first ob after end of current time window ', &
1199 'for nest = ', i3,':')
1200 951 FORMAT(' ob index = ',i8,', time of ob = ',f8.4, &
1201 ' hrs, end of time window = ',f8.4,' hrs')
1207 ! make time=99999. if ob is too old
1208 ! print *,'tback,nsta=',tback,nsta
1209 old_obs : DO N=1,NSTA+1
1210 IF((TIMEOB(N)-TBACK).LT.0)THEN
1213 ! print *,'n,ndum,timeob=',n,ndum,timeob(n)
1214 IF(TIMEOB(N).LT.9.E4) EXIT old_obs
1218 ! REMOVE OLD OBS DENOTED BY 99999. AT THE FRONT OF TIMEOB ARRAY
1219 IF (iprt .and. ktaur > 0) THEN
1220 write(msg,fmt='(a,i5,a)') 'OBS NUDGING: Upon restart, skipped over ',ndum, &
1221 ' obs that are now too old for the current obs window.'
1222 call wrf_message(msg)
1227 IF( NMOVE.GT.0 .AND. NDUM.NE.0) THEN
1230 VAROBS(KN,N)=VAROBS(KN,N+NDUM)
1235 TIMEOB(N)=TIMEOB(N+NDUM)
1236 nlevs_ob(n)=nlevs_ob(n+ndum)
1237 lev_in_ob(n)=lev_in_ob(n+ndum)
1238 plfo(n)=plfo(n+ndum)
1241 ! moved obs up. now fill remaining space with 99999.
1243 IF(NOPEN.LE.NIOBF) THEN
1255 !CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
1257 ! print *,'nsta at restart setting is ',nsta
1258 ! recalculate nsta after moving things around
1259 recalc : DO N=1,NIOBF
1260 ! try to save all obs - don't throw away latest read in
1261 IF(TIMEOB(N).GT.9.e4) EXIT recalc
1263 ! nsta=n-1 ! yliu test
1266 ! Find the number of stations that are actually within the time window.
1267 nstaw = nvals_le_limit(nsta, timeob, tforwd)
1269 CALL finish_obs_info(iprt,nstaw,niobf,rio,rjo,prt_max,prt_freq, &
1270 xlat,xlong,e_we,e_sn,refprt(inest),obs_prt, &
1271 lat_prt,lon_prt,mlat_prt,mlon_prt, &
1272 plat_prt,plon_prt,xtime, &
1273 ims,ime,jms,jme,its,ite,jts,jte)
1276 write(msg,160) KTAU,XTIME,NSTAW
1277 call wrf_message(msg)
1279 IF(KTAU.EQ.KTAUR)THEN
1280 IF(nudge_opt.EQ.1)THEN
1283 write(msg,1449) INEST,RINXY,RINSIG,TWDOP
1284 call wrf_message(msg)
1285 IF(ISWIND.EQ.1) then
1287 call wrf_message(msg)
1290 call wrf_message(msg)
1292 IF(ISTEMP.EQ.1) then
1294 call wrf_message(msg)
1297 call wrf_message(msg)
1299 IF(ISMOIS.EQ.1) then
1301 call wrf_message(msg)
1304 call wrf_message(msg)
1309 IF(KTAU.EQ.KTAUR)THEN
1310 IF(fdob%IWTSIG.NE.1)THEN
1313 call wrf_message(msg)
1314 write(msg,556) fdob%RINFMN*RINXY,fdob%RINFMX*RINXY,fdob%PFREE*10.
1315 call wrf_message(msg)
1317 IF(fdob%RINFMN.GT.fdob%RINFMX) then
1318 call wrf_error_fatal ( 'wrf_fddaobs_in: in4dob STOP 556' )
1320 ! IS MINIMUM GREATER THAN MAXIMUM?
1323 write(msg,557) fdob%DPSMX*10.,fdob%DCON
1324 call wrf_message(msg)
1326 IF(fdob%DPSMX.GT.10.) then
1327 call wrf_error_fatal ( 'wrf_fddaobs_in: in4dob STOP 557' )
1332 IF(KTAU.EQ.KTAUR)THEN
1334 write(msg,601) INEST,IONF
1335 call wrf_message(msg)
1336 call wrf_message("")
1342 555 FORMAT(1X,' ABOVE THE SURFACE LAYER, OBS NUDGING IS PERFORMED', &
1343 ' ON PRESSURE LEVELS,')
1344 556 FORMAT(1X,' WHERE RINXY VARIES LINEARLY FROM ',E11.3,' KM AT', &
1345 ' THE SURFACE TO ',E11.3,' KM AT ',F7.2,' MB AND ABOVE')
1346 557 FORMAT(1X,' IN THE SURFACE LAYER, WXY IS A FUNCTION OF ', &
1347 'DPSMX = ',F7.2,' MB WITH DCON = ',E11.3, &
1348 ' - SEE SUBROUTINE NUDOB')
1349 601 FORMAT('FOR EFFICIENCY, THE OBS NUDGING FREQUENCY ', &
1350 'FOR MESH #',I2,' IS ',1I2,' CGM TIMESTEPS ')
1351 121 FORMAT(' WARNING: NOBS = ',I4,' IS GREATER THAN NIOBF = ', &
1352 I4,': INCREASE PARAMETER NIOBF')
1353 5403 FORMAT(1H0,'-------------EOF REACHED FOR NVOL = ',I3, &
1354 ' AND XTIME = ',F10.2,'-------------------')
1355 122 FORMAT(1X,' ...OR THE CODE WILL REDUCE THE TIME WINDOW')
1356 160 FORMAT('****** CALL IN4DOB AT KTAU = ',I5,' AND XTIME = ', &
1357 F10.2,': NSTA = ',I7,' ******')
1358 1449 FORMAT('*****NUDGING INDIVIDUAL OBS ON MESH #',I2, &
1360 E11.3,' KM, RINSIG = ',E11.3,' AND TWINDO (HALF-PERIOD) = ', &
1362 1450 FORMAT(1X,'NUDGING IND. OBS WINDS WITH GIV = ',E11.3)
1363 1451 FORMAT(1X,'NUDGING IND. OBS TEMPERATURE WITH GIT = ',E11.3)
1364 1452 FORMAT(1X,'NUDGING IND. OBS MOISTURE WITH GIQ = ',E11.3)
1365 1455 FORMAT(1X,'*** OBS WIND NUDGING TURNED OFF FOR THIS MESH!!')
1366 1456 FORMAT(1X,'*** OBS TEMPERATURE NUDGING IS TURNED OFF FOR THIS MESH!!')
1367 1457 FORMAT(1X,'*** OBS MOISTURE NUDGING IS TURNED OFF FOR THIS MESH!!')
1370 END SUBROUTINE in4dob
1372 SUBROUTINE julgmt(mdate,julgmtn,timanl,julday,gmt,ind)
1373 ! CONVERT MDATE YYMMDDHH TO JULGMT (JULIAN DAY * 100. +GMT)
1374 ! AND TO TIMANL (TIME IN MINUTES WITH RESPECT TO MODEL TIME)
1375 ! IF IND=0 INPUT MDATE, OUTPUT JULGMTN AND TIMANL
1376 ! IF IND=1 INPUT TIMANL, OUTPUT JULGMTN
1377 ! IF IND=2 INPUT JULGMTN, OUTPUT TIMANL
1378 INTEGER, intent(in) :: MDATE
1379 REAL, intent(out) :: JULGMTN
1380 REAL, intent(out) :: TIMANL
1381 INTEGER, intent(in) :: JULDAY
1382 REAL, intent(in) :: GMT
1383 INTEGER, intent(in) :: IND
1385 !*** DECLARATIONS FOR IMPLICIT NONE
1386 real :: MO(12), rjulanl, houranl, rhr
1388 integer :: iyr, idate1, imo, idy, ihr, my1, my2, my3, ileap
1389 integer :: juldayn, juldanl, idymax, mm
1392 IF(IND.EQ.2)GOTO 150
1393 IYR=INT(MDATE/1000000.+0.001)
1394 IDATE1=MDATE-IYR*1000000
1395 IMO=INT(IDATE1/10000.+0.001)
1396 IDY=INT((IDATE1-IMO*10000.)/100.+0.001)
1397 IHR=IDATE1-IMO*10000-IDY*100
1400 ! IS THE YEAR A LEAP YEAR? (IN THIS CENTURY)
1407 ! IF(MY1.EQ.0.AND.MY2.NE.0.OR.MY3.EQ.0)THEN
1412 IF(IND.EQ.1)GOTO 200
1425 JULDAYN=JULDAYN+MO(MM)
1432 JULGMTN=(JULDAYN+IDY)*100.+IHR
1433 ! CONVERT JULGMT TO TIMANL WRT MODEL TIME IN MINUTES (XTIME)
1435 JULDANL=INT(JULGMTN/100.+0.000001)
1436 RJULANL=FLOAT(JULDANL)*100.
1437 HOURANL=JULGMTN-RJULANL
1438 TIMANL=(FLOAT(JULDANL-JULDAY)*24.-GMT+HOURANL)*60.
1441 RHR=GMT+TIMANL/60.+0.000001
1444 300 IF(RHR.GE.24.0)THEN
1449 IF(IDY.GT.IDYMAX)IDY=IDY-IDYMAX
1450 JULGMTN=FLOAT(IDY)*100.+RHR
1452 END SUBROUTINE julgmt
1454 SUBROUTINE rh2r(rh,t,p,r,iice)
1457 ! if iice=1, use saturation with respect to ice
1463 REAL, intent(in) :: rh
1464 REAL, intent(in) :: t
1465 REAL, intent(in) :: p
1466 REAL, intent(out) :: r
1467 INTEGER, intent(in) :: iice
1469 !*** DECLARATIONS FOR IMPLICIT NONE
1470 real eps, e0, eslcon1, eslcon2, esicon1, esicon2, t0, rh1
1481 ! print *,'rh2r input=',rh,t,p
1484 if(iice.eq.1.and.t.le.t0)then
1485 esat=e0*exp(esicon1*(t-273.16)/(t-esicon2))
1487 esat=e0*exp(eslcon1*(t-273.16)/(t-eslcon2))
1489 rsat=eps*esat/(p-esat)
1490 ! print *,'rsat,esat=',rsat,esat
1493 ! print *,'rh2r rh,t,p,r=',rh1,t,p,r
1498 SUBROUTINE rh2rb(rh,t,p,r,iice)
1501 ! if iice=1, use daturation with respect to ice
1507 REAL, intent(in) :: rh
1508 REAL, intent(in) :: t
1509 REAL, intent(in) :: p
1510 REAL, intent(out) :: r
1511 INTEGER, intent(in) :: iice
1513 !*** DECLARATIONS FOR IMPLICIT NONE
1514 real eps, e0, eslcon1, eslcon2, esicon1, esicon2, t0, rh1
1516 character(len=200) :: msg ! Argument to wrf_message
1526 write(msg,*) 'rh2r input=',rh,t,p
1527 call wrf_message(msg)
1530 if(iice.eq.1.and.t.le.t0)then
1531 esat=e0*exp(esicon1-esicon2/t)
1533 esat=e0*exp(eslcon1*(t-t0)/(t-eslcon2))
1535 rsat=eps*esat/(p-esat)
1536 ! print *,'rsat,esat=',rsat,esat
1537 r=rh1*eps*rsat/(eps+rsat*(1.-rh1))
1539 write(msg,*) 'rh2r rh,t,p,r=',rh1,t,p,r
1540 call wrf_message(msg)
1544 END SUBROUTINE rh2rb
1546 SUBROUTINE set_projection (obs_proj, map_proj, cen_lat, cen_lon, &
1547 true_lat1, true_lat2, stand_lon, &
1548 known_lat, known_lon, &
1549 e_we, e_sn, dxm, dym )
1553 !*************************************************************************
1554 ! Purpose: Set map projection information which will be used to map the
1555 ! observation (lat,lon) location to its corresponding (x,y)
1556 ! location on the WRF (coarse) grid. using the selected map
1557 ! projection (e.g., Lambert, Mercator, Polar Stereo, etc).
1558 !*************************************************************************
1562 TYPE(PROJ_INFO), intent(out) :: obs_proj ! structure for obs projection info.
1563 INTEGER, intent(in) :: map_proj ! map projection index
1564 REAL, intent(in) :: cen_lat ! center latitude for map projection
1565 REAL, intent(in) :: cen_lon ! center longiture for map projection
1566 REAL, intent(in) :: true_lat1 ! truelat1 for map projection
1567 REAL, intent(in) :: true_lat2 ! truelat2 for map projection
1568 REAL, intent(in) :: stand_lon ! standard longitude for map projection
1569 INTEGER, intent(in) :: e_we ! max grid index in south-north coordinate
1570 INTEGER, intent(in) :: e_sn ! max grid index in west-east coordinate
1571 REAL, intent(in) :: known_lat ! latitude of domain origin point (i,j)=(1,1)
1572 REAL, intent(in) :: known_lon ! longigude of domain origin point (i,j)=(1,1)
1573 REAL, intent(in) :: dxm ! grid size in x (meters)
1574 REAL, intent(in) :: dym ! grid size in y (meters)
1576 ! Set up map transformation structure
1577 CALL map_init(obs_proj)
1580 IF (map_proj == PROJ_MERC) THEN
1581 CALL map_set(PROJ_MERC, obs_proj, &
1582 truelat1 = true_lat1, &
1590 ELSE IF (map_proj == PROJ_LC) THEN
1591 CALL map_set(PROJ_LC, obs_proj, &
1592 truelat1 = true_lat1, &
1593 truelat2 = true_lat2, &
1594 stdlon = stand_lon, &
1601 ! Polar stereographic
1602 ELSE IF (map_proj == PROJ_PS) THEN
1603 CALL map_set(PROJ_PS, obs_proj, &
1604 truelat1 = true_lat1, &
1605 stdlon = stand_lon, &
1611 ! Cassini (global ARW)
1612 ELSE IF (map_proj == PROJ_CASSINI) THEN
1613 CALL map_set(PROJ_CASSINI, obs_proj, &
1614 latinc = dym*360.0/(2.0*EARTH_RADIUS_M*PI), &
1615 loninc = dxm*360.0/(2.0*EARTH_RADIUS_M*PI), &
1618 ! We still need to get POLE_LAT and POLE_LON metadata variables before
1619 ! this will work for rotated poles.
1626 ! Rotated latitude-longitude
1627 ELSE IF (map_proj == PROJ_ROTLL) THEN
1628 CALL map_set(PROJ_ROTLL, obs_proj, &
1629 ! I have no idea how this should work for NMM nested domains
1632 phi = real(e_sn-2)*dym/2.0, &
1633 lambda = real(e_we-2)*dxm, &
1642 ! write(6,*) 'ajb init: map_proj = ',map_proj
1643 ! write(6,*) 'ajb: after setting map:'
1644 ! write(6,*) 'truelat1 = ',obs_proj%truelat1
1645 ! write(6,*) 'truelat2 = ',obs_proj%truelat2
1646 ! write(6,*) 'stdlon = ',obs_proj%stdlon
1647 ! write(6,*) 'lat1 = ',obs_proj%lat1
1648 ! write(6,*) 'lon1 = ',obs_proj%lon1
1649 ! write(6,*) 'knowni = ',obs_proj%knowni
1650 ! write(6,*) 'knownj = ',obs_proj%knownj
1651 ! write(6,*) 'dx = ',obs_proj%dx
1653 END SUBROUTINE set_projection
1655 SUBROUTINE fmt_date(idate,odate) !obsnypatch
1657 !*************************************************************************
1658 ! Purpose: Re-format a character date string from YYYYMMDDHHmmss form
1659 ! to YYYY-MM-DD_HH:mm:ss form.
1661 ! IDATE - Date string as YYYYMMDDHHmmss
1663 ! ODATE - Date string as YYYY-MM-DD_HH:mm:ss
1664 !*************************************************************************
1668 CHARACTER*14, intent(in) :: idate ! input date string
1669 CHARACTER*19, intent(out) :: odate ! output date string
1671 odate(1:19) = "0000-00-00_00:00:00"
1672 odate(1:4) = idate(1:4) ! Year
1673 odate(6:7) = idate(5:6) ! Month
1674 odate(9:10) = idate(7:8) ! Day
1675 odate(12:13) = idate(9:10) ! Hours
1676 odate(15:16) = idate(11:12) ! Minutes
1677 odate(18:19) = idate(13:14) ! Seconds
1680 END SUBROUTINE fmt_date
1682 INTEGER FUNCTION nvals_le_limit(isize, values, limit)
1683 !------------------------------------------------------------------------------
1684 ! PURPOSE: Return the number of values in a (real) non-decreasing array that
1685 ! are less than or equal to the specified upper limit.
1686 ! NOTE: It is important that the array is non-decreasing!
1688 !------------------------------------------------------------------------------
1691 INTEGER, INTENT(IN) :: isize ! Size of input array
1692 REAL, INTENT(IN) :: values(isize) ! Input array of reals
1693 REAL, INTENT(IN) :: limit ! Upper limit
1698 ! Search the array from largest to smallest values.
1699 find_nvals: DO n = isize, 1, -1
1700 if(values(n).le.limit) EXIT find_nvals
1705 END FUNCTION nvals_le_limit
1707 SUBROUTINE collect_obs_info(newpass,inest,n,latitude,longitude,nprev,niobf, &
1708 iref,rio,rjo,prt_max,prt_freq,xlat,xlong,obs, &
1709 lat,lon, mlat,mlon,plat,plon,e_we,e_sn, &
1710 ims,ime,jms,jme,its,ite,jts,jte)
1711 !*************************************************************************
1712 ! Purpose: Collect obs and model latitude and longitude values for print
1713 ! diagnostics. This is tricky because (1) THIS SUBROUTINE CALL IS
1714 ! WITHIN THE LOOP FOR READING THE OBServations, (2) observations
1715 ! can be rejected, and (3) soundings may contain multiple obs
1716 ! levels (i.e., n jumps by the number of levels in the sounding.
1717 ! Here, "prev" means the previous iteration of the read obs loop.
1718 ! NOTE: The lat/lon info for an obs is not actually stored until
1719 ! the read of the subsequent obs, because the number of
1720 ! levels in the obs, or even whether or not the obs has
1721 ! been accepted, is not known until the subsequent read.
1722 !*************************************************************************
1726 LOGICAL, intent(inout) :: newpass ! New pass flag
1727 INTEGER, intent(in) :: inest ! nest index
1728 INTEGER, intent(in) :: n ! Observation index
1729 REAL, intent(in) :: latitude ! Latitude of obs
1730 REAL, intent(in) :: longitude ! Latitude of obs
1731 INTEGER, intent(inout) :: nprev ! Previous observation index
1732 INTEGER, intent(in) :: niobf ! Maximum number of observations
1733 INTEGER, intent(inout) :: iref ! Reference obs index
1734 REAL, intent(in) :: rio(niobf) ! West-east coord (non-stagger)
1735 REAL, intent(in) :: rjo(niobf) ! South-north coord (non-stagger)
1736 INTEGER, intent(in) :: prt_max ! Max no. of obs for diagnostic printout
1737 INTEGER, intent(in) :: prt_freq ! Frequency for diagnostic printout
1738 REAL, DIMENSION( ims:ime, jms:jme ), &
1739 intent(in ) :: xlat, xlong ! Lat/lon on mass-pt grid
1740 INTEGER, intent(inout) :: obs(prt_max) ! Obs index for printout
1741 REAL, intent(inout) :: lat(prt_max) ! Obs latitude for printout
1742 REAL, intent(inout) :: lon(prt_max) ! Obs longitude for printout
1743 REAL, intent(inout) :: mlat(prt_max) ! Model latitude at (rio,rjo) for printout
1744 REAL, intent(inout) :: mlon(prt_max) ! Model longitude at (rio,rjo) for printout
1745 REAL, intent(inout) :: plat ! Previous latitude read
1746 REAL, intent(inout) :: plon ! Previous longitude read
1747 INTEGER, intent(in) :: e_we ! Max grid index in south-north
1748 INTEGER, intent(in) :: e_sn ! Max grid index in west-east
1749 INTEGER, intent(in) :: ims ! Grid mem start (west-east)
1750 INTEGER, intent(in) :: ime ! Grid mem end (west-east)
1751 INTEGER, intent(in) :: jms ! Grid mem start (south-north)
1752 INTEGER, intent(in) :: jme ! Grid mem end (south-north)
1753 INTEGER, intent(in) :: its ! Grid tile start (west-east)
1754 INTEGER, intent(in) :: ite ! Grid tile end (west-east)
1755 INTEGER, intent(in) :: jts ! Grid tile start (south-north)
1756 INTEGER, intent(in) :: jte ! Grid tile end (south-north)
1758 integer nn ! Loop counter over obs index
1759 integer ndx ! Index into printout arrays
1760 real :: ri, rj ! Mass-pt coord of obs
1761 integer :: ril, rjl ! Mass-pt integer coord immed sw of obs
1762 integer :: iend, jend ! Upper i, j index for interpolation
1763 real :: dxob, dyob ! Grid fractions for interp
1764 logical :: llsave ! Save lat/lon values if true
1765 character(len=200) :: msg ! Argument to wrf_message
1767 if(newpass) then ! No action on first pass
1771 ! Start iteration only if we have not yet stored the 100th obs for printing.
1772 ! Note: The loop below could represent multiple levels in a sounding, so we
1773 ! go ahead and start the loop if the beginning index (ndx) is 100 or
1774 ! less, and then exit the loop if ndx exceeds 100.
1775 if(prt_freq.gt.0) then
1776 ndx = (nprev-1-iref)/prt_freq + 1
1778 write(msg,*) 'STOP! OBS NAMELIST INPUT obs_prt_freq MUST BE GREATER THAN ZERO.'
1779 call wrf_message(msg)
1780 write(msg,*) 'THE NAMELIST VALUE IS',prt_freq,' FOR NEST ',inest
1781 call wrf_message(msg)
1782 call wrf_error_fatal ( 'wrf_fddaobs_in: in4dob STOP' )
1785 if(ndx .le. prt_max) then
1787 MODCHK : do nn = nprev, n-1
1789 if( mod(nn-1,prt_freq) .eq. 0 ) then
1790 ndx = (nn-1-iref)/prt_freq + 1
1791 if(ndx.gt.prt_max) EXIT MODCHK ! Limit printout to prt_max entries
1796 ! Collect obs index and latitude and longitude.
1801 ! Compute and collect the model latitude and longitude at the obs point.
1802 CALL get_model_latlon(nn,niobf,rio,rjo,xlat,xlong,e_we,e_sn, &
1803 ims,ime,jms,jme,its,ite,jts,jte, &
1804 mlat(ndx),mlon(ndx))
1806 endif !end if(llsave)
1809 endif !end if(nprev/prt_freq+1) .le. prt_max)
1811 endif ! end if(.not. newpass)
1813 ! Save index of previous obs in read loop.
1818 END SUBROUTINE collect_obs_info
1820 SUBROUTINE finish_obs_info(iprt,nstaw,niobf,rio,rjo,prt_max,prt_freq, &
1821 xlat,xlong,e_we,e_sn,iref,obs,lat,lon, &
1822 mlat,mlon,plat,plon,xtime, &
1823 ims,ime,jms,jme,its,ite,jts,jte)
1824 !*************************************************************************
1825 ! Purpose: Finish collecting obs information, according to prt_freq, but
1826 ! limit to prt_max obs. Because latitude and longitude informa-
1827 ! tion was collected (with subroutine collect_obs_info) within
1828 ! the OBS READ LOOP, where lat&lon for obs N is collected on
1829 ! pass N+1, there could be one more obs whose lat&lon info needs
1830 ! to be collected (for printing). Note that in subroutine
1831 ! collect_obs_info, the obs lat&lon values for the currently read
1832 ! obs was stored into plat&plon for just this purpose.
1833 !*************************************************************************
1837 LOGICAL, intent(in) :: iprt ! Print flag
1838 INTEGER, intent(in) :: nstaw ! Number of observations
1839 INTEGER, intent(in) :: niobf ! Maximum number of observations
1840 REAL, intent(in) :: rio(niobf) ! West-east coord (non-stagger)
1841 REAL, intent(in) :: rjo(niobf) ! South-north coord (non-stagger)
1842 INTEGER, intent(in) :: prt_max ! Max no. of obs for diagnostic printout
1843 INTEGER, intent(in) :: prt_freq ! Frequency for diagnostic printout
1844 REAL, DIMENSION( ims:ime, jms:jme ), &
1845 intent(in) :: xlat, xlong ! Lat/lon on mass-pt grid
1846 INTEGER, intent(in) :: e_we ! Max grid index in south-north
1847 INTEGER, intent(in) :: e_sn ! Max grid index in west-east
1848 INTEGER, intent(in) :: iref ! Reference obs index
1849 INTEGER, intent(inout) :: obs(prt_max) ! Saved obs indices to print
1850 REAL, intent(inout) :: lat(prt_max) ! Saved latitudes
1851 REAL, intent(inout) :: lon(prt_max) ! Saved longitudes
1852 REAL, intent(inout) :: mlat(prt_max) ! Saved model latitudes
1853 REAL, intent(inout) :: mlon(prt_max) ! Saved longitudes
1854 REAL, intent(in) :: plat ! Previous latitude read
1855 REAL, intent(in) :: plon ! Previous longitude read
1856 REAL, intent(in) :: xtime ! Model time in minutes
1857 INTEGER, intent(in) :: ims ! Grid mem start (west-east)
1858 INTEGER, intent(in) :: ime ! Grid mem end (west-east)
1859 INTEGER, intent(in) :: jms ! Grid mem start (south-north)
1860 INTEGER, intent(in) :: jme ! Grid mem end (south-north)
1861 INTEGER, intent(in) :: its ! Grid tile start (west-east)
1862 INTEGER, intent(in) :: ite ! Grid tile end (west-east)
1863 INTEGER, intent(in) :: jts ! Grid tile start (south-north)
1864 INTEGER, intent(in) :: jte ! Grid tile end (south-north)
1867 integer :: n ! Loop counter over obs
1868 integer :: lastobs ! Last obs collected for printout
1869 integer :: lastn ! Value of n for last obs(n) entry
1870 integer :: igap ! Gap between last obs read and last saved
1871 integer :: ndx ! Index into printout arrays
1872 integer :: iobs ! Obs gap indices between lastobs and nstaw
1874 ! Find the last obs index whose lat&lon info was collected.
1878 if(obs(n).ne.-999) then
1884 ! There could be more obs to collect.
1886 if(lastn.gt.0) then ! Do only if at least 1 obs was collected.
1887 igap = (nstaw-lastobs)/prt_freq
1892 iobs = lastobs + n*prt_freq
1893 if(ndx.le.prt_max) then
1895 ! Compute and collect model lat&lon at obs point.
1896 CALL get_model_latlon(iobs,niobf,rio,rjo,xlat,xlong,e_we,e_sn, &
1897 ims,ime,jms,jme,its,ite,jts,jte, &
1898 mlat(ndx),mlon(ndx))
1900 ! Collect obs index and lat&lon.
1907 END SUBROUTINE finish_obs_info
1909 SUBROUTINE get_model_latlon(n,niobf,rio,rjo,xlat,xlong,e_we,e_sn, &
1910 ims,ime,jms,jme,its,ite,jts,jte, &
1912 !*************************************************************************
1913 ! Purpose: Use bilinear interpolation to compute the model latitude and
1914 ! longitude at the observation point.
1915 !*************************************************************************
1919 INTEGER, intent(in) :: n ! Observation index
1920 INTEGER, intent(in) :: niobf ! Maximum number of observations
1921 REAL, intent(in) :: rio(niobf) ! West-east coord (non-stagger)
1922 REAL, intent(in) :: rjo(niobf) ! South-north coord (non-stagger)
1923 REAL, DIMENSION( ims:ime, jms:jme ), &
1924 intent(in ) :: xlat, xlong ! Lat/lon on mass-pt grid
1925 INTEGER, intent(in) :: e_we ! Max grid index in south-north
1926 INTEGER, intent(in) :: e_sn ! Max grid index in west-east
1927 INTEGER, intent(in) :: ims ! Grid mem start (west-east)
1928 INTEGER, intent(in) :: ime ! Grid mem end (west-east)
1929 INTEGER, intent(in) :: jms ! Grid mem start (south-north)
1930 INTEGER, intent(in) :: jme ! Grid mem end (south-north)
1931 INTEGER, intent(in) :: its ! Grid tile start (west-east)
1932 INTEGER, intent(in) :: ite ! Grid tile end (west-east)
1933 INTEGER, intent(in) :: jts ! Grid tile start (south-north)
1934 INTEGER, intent(in) :: jte ! Grid tile end (south-north)
1935 REAL, intent(out) :: mlat ! Model latitude at obs point
1936 REAL, intent(out) :: mlon ! Model longitude at obs point
1939 integer ndx ! Index into save arrays
1940 real :: ri, rj ! Mass-pt coord of obs
1941 integer :: ril, rjl ! Mass-pt integer coord immed sw of obs
1942 integer :: iend, jend ! Upper i, j index for interpolation
1943 real :: dxob, dyob ! Grid fractions for interp
1945 ! Compute model latitude and longitude if point on tile.
1946 ri = rio(n) - .5 ! mass-pt west-east obs grid coord
1947 rj = rjo(n) - .5 ! mass-pt south-north obs grid coord
1950 dxob = ri - float(ril)
1951 dyob = rj - float(rjl)
1952 iend = min(ite+1,e_we-2)
1953 jend = min(jte+1,e_sn-2)
1957 if(ri.ge.its .and. ri.lt.iend .and. rj.ge.jts .and. rj.lt.jend) then
1959 ! bilinear interpolation
1960 mlat = ((1.-dyob)*((1.-dxob)*xlat(ril,rjl)+ &
1961 dxob*xlat(ril+1,rjl) &
1962 )+dyob*((1.-dxob)*xlat(ril,rjl+1)+ &
1963 dxob*xlat(ril+1,rjl+1)))
1965 mlon = ((1.-dyob)*((1.-dxob)*xlong(ril,rjl)+ &
1966 dxob*xlong(ril+1,rjl) &
1967 )+dyob*((1.-dxob)*xlong(ril,rjl+1)+ &
1968 dxob*xlong(ril+1,rjl+1)))
1971 END SUBROUTINE get_model_latlon
1973 SUBROUTINE rotate_vector(lon,u,v,obs_proj,map_proj)
1977 !*************************************************************************
1978 ! Purpose: Rotate a single Earth-relative wind vector to a grid-relative
1980 !*************************************************************************
1984 REAL, intent(in) :: lon ! Longitude (deg)
1985 REAL, intent(inout) :: u ! U-component of wind vector
1986 REAL, intent(inout) :: v ! V-component of wind vector
1987 TYPE(PROJ_INFO),intent(in) :: obs_proj ! Structure for obs projection
1988 INTEGER, intent(in) :: map_proj ! Map projection index
1992 double precision udbl, vdbl
1994 ! Only rotate winds for Lambert conformal or polar stereographic
1995 if (map_proj == PROJ_LC .or. map_proj == PROJ_PS) then
1997 diff = obs_proj%stdlon - lon
1998 if (diff > 180.) then
2000 else if (diff < -180.) then
2004 ! Calculate the rotation angle, alpha, in radians
2005 if (map_proj == PROJ_LC) then
2006 alpha = diff * obs_proj%cone * rad_per_deg * obs_proj%hemi
2008 alpha = diff * rad_per_deg * obs_proj%hemi
2011 udbl = v*sin(alpha) + u*cos(alpha)
2012 vdbl = v*cos(alpha) - u*sin(alpha)
2017 END SUBROUTINE rotate_vector
2020 !-----------------------------------------------------------------------
2021 ! End subroutines for in4dob
2022 !-----------------------------------------------------------------------