4 real, allocatable, dimension(:) :: a, b
8 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
11 ! Notes: Obtain table of coefficients for input by this routine from
12 ! http://www.ecmwf.int/products/data/technical/model_levels/index.html
14 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
15 subroutine read_coeffs()
19 integer :: i, nlvl, istatus
21 open(21,file='ecmwf_coeffs',form='formatted',status='old',iostat=istatus)
25 if (istatus /= 0) then
26 write(6,*) 'ERROR: Error opening ecmwf_coeffs'
30 read(21,*,iostat=istatus) nlvl
31 do while (istatus == 0)
32 n_levels = n_levels + 1
33 read(21,*,iostat=istatus) nlvl
38 n_levels = n_levels - 1
40 allocate(a(0:n_levels))
41 allocate(b(0:n_levels))
44 write(6,*) 'Coefficients for each level:',n_levels
46 read(21,*,iostat=istatus) nlvl, a(i), b(i)
47 write(6,'(i5,5x,f12.6,2x,f12.10)') nlvl, a(i), b(i)
53 end subroutine read_coeffs
56 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
57 ! Name: cleanup_coeffs
58 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
59 subroutine cleanup_coeffs()
64 if (allocated(a)) deallocate(a)
65 if (allocated(b)) deallocate(b)
67 end subroutine cleanup_coeffs
69 end module coefficients
72 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
75 ! The purpose of this program is to compute a 3d pressure field for ECMWF
76 ! model-level data sets; the code works in the WPS intermediate file format,
77 ! reading a PSFC field from intermediate files, the A and B coefficients
78 ! from a text file, ecmwf_coeffs, and writes the pressure data to an
82 ! Note: This program now also computes height for each level, this is needed by real
83 ! modified by: Daniel van Dijke, MeteoConsult B.V., The Netherlands
84 ! Chiel van Heerwaarden, Wageningen University, The Netherlands
86 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
92 use misc_definitions_module
101 integer :: i, idiff, n_times, t, istatus, fg_idx, counter
102 real :: a_full, b_full
103 character (len=19) :: valid_date, temp_date
104 character (len=128) :: input_name
105 real, allocatable, dimension(:,:) :: psfc, hgtsfc, hgtprev, pstart, pend
106 real, allocatable, dimension(:,:,:) :: tt, qv, hgt_3ddata
108 type (met_data) :: ecmwf_data, p_data, rh_data, hgt_data
112 ! Setup (read namelist and check on time range)
114 call get_namelist_params()
116 call set_debug_level(WARN)
118 ! Compute number of times that we will process
119 call geth_idts(end_date(1), start_date(1), idiff)
120 call mprintf((idiff < 0),ERROR,'Ending date is earlier than starting date in namelist for domain %i.', i1=1)
122 n_times = idiff / interval_seconds
124 ! Check that the interval evenly divides the range of times to process
125 call mprintf((mod(idiff, interval_seconds) /= 0),WARN, &
126 'In namelist, interval_seconds does not evenly divide '// &
127 '(end_date - start_date) for domain %i. Only %i time periods '// &
128 'will be processed.', i1=1, i2=n_times)
132 input_name = fg_name(fg_idx)
135 ! Get coefficients for model level pressures
141 ! Loop over all prefixes listed in namelist for fg_name
143 do while (input_name /= '*')
146 ! Loop over all times to be processed for this domain
150 ! Get the date string for the current time
151 call geth_newdate(valid_date, trim(start_date(1)), t*interval_seconds)
153 write(temp_date,'(a19)') valid_date(1:10)//'_'//valid_date(12:19)
155 ! Initialize the module for reading in the met fields
156 call read_met_init(trim(input_name), .false., temp_date(1:13), istatus)
160 if (istatus == 0) then
161 call mprintf(.true.,STDOUT,'Reading from %s at time %s', s1=input_name, s2=temp_date(1:13))
163 ! Process all fields and levels from the current file; read_next_met_field()
164 ! will return a non-zero status when there are no more fields to be read.
165 do while (istatus == 0)
167 call read_next_met_field(ecmwf_data, istatus)
169 if (istatus == 0) then
171 ! Have we found either PSFC or LOGSFP?
172 if ((trim(ecmwf_data%field) == 'PSFC' .and. ecmwf_data%xlvl == 200100.) &
173 .or. trim(ecmwf_data%field) == 'LOGSFP') then
175 p_data%field = 'PRESSURE '
176 p_data%desc = 'Pressure'
179 rh_data%field = 'RH '
181 rh_data%desc = 'Relative humidity'
183 if (.not. allocated(psfc)) then
184 allocate(psfc(ecmwf_data%nx,ecmwf_data%ny))
186 call mprintf(.true.,STDOUT,'Found %s field in %s:%s', &
187 s1=ecmwf_data%field, s2=input_name, s3=temp_date(1:13))
190 if (trim(ecmwf_data%field) == 'LOGSFP') then
191 psfc(:,:) = exp(ecmwf_data%slab(:,:))
193 psfc(:,:) = ecmwf_data%slab(:,:)
196 !CvH_DvD: - Store geopotential height
197 else if (trim(ecmwf_data%field) == 'SOILHGT' .and. ecmwf_data%xlvl == 200100.) then
198 hgt_data = ecmwf_data
199 hgt_data%field = 'GHT '
201 hgt_data%desc = 'Height'
202 if (.not. allocated(hgtsfc)) then
203 allocate(hgtsfc(ecmwf_data%nx,ecmwf_data%ny))
205 call mprintf(.true.,STDOUT,'Found %s field in %s:%s', &
206 s1=ecmwf_data%field, s2=input_name, s3=temp_date(1:13))
208 hgtsfc(:,:) = ecmwf_data%slab(:,:)
210 ! Have we found surface geopotential?
211 else if (trim(ecmwf_data%field) == 'SOILGEO' .and. ecmwf_data%xlvl == 1.) then
212 hgt_data = ecmwf_data
213 hgt_data%field = 'GHT '
214 hgt_data%units = 'm' ! units on output after conversion to height
215 hgt_data%desc = 'Height'
216 if (.not. allocated(hgtsfc)) then
217 allocate(hgtsfc(ecmwf_data%nx,ecmwf_data%ny))
219 call mprintf(.true.,STDOUT,'Found %s field in %s:%s', &
220 s1=ecmwf_data%field, s2=input_name, s3=temp_date(1:13))
222 hgtsfc(:,:) = ecmwf_data%slab(:,:)/9.81
225 ! Have we found temperature?
226 else if (trim(ecmwf_data%field) == 'TT') then
228 if (.not. allocated(tt)) then
229 allocate(tt(ecmwf_data%nx,ecmwf_data%ny,n_levels+1)) ! Extra level is for surface
232 if (nint(ecmwf_data%xlvl) >= 1 .and. &
233 nint(ecmwf_data%xlvl) <= n_levels) then
234 tt(:,:,nint(ecmwf_data%xlvl)) = ecmwf_data%slab
235 else if (nint(ecmwf_data%xlvl) == 200100) then
236 tt(:,:,n_levels+1) = ecmwf_data%slab
239 ! Have we found specific humidity?
240 else if (trim(ecmwf_data%field) == 'SPECHUMD') then
242 if (.not. allocated(qv)) then
243 allocate(qv(ecmwf_data%nx,ecmwf_data%ny,n_levels+1)) ! Extra level is for surface
246 if (nint(ecmwf_data%xlvl) >= 1 .and. &
247 nint(ecmwf_data%xlvl) <= n_levels) then
248 qv(:,:,nint(ecmwf_data%xlvl)) = ecmwf_data%slab
249 else if (nint(ecmwf_data%xlvl) == 200100) then
250 qv(:,:,n_levels+1) = ecmwf_data%slab
255 if (associated(ecmwf_data%slab)) deallocate(ecmwf_data%slab)
261 call read_met_close()
264 ! Now write out, for each level, the pressure field
267 allocate(p_data%slab(p_data%nx,p_data%ny))
268 allocate(rh_data%slab(rh_data%nx,rh_data%ny))
269 !CvH_DvD: add HGT variable
270 allocate(hgt_data%slab(hgt_data%nx,hgt_data%ny))
272 call write_met_init(trim(get_path(input_name))//'PRES', .false., temp_date(1:13), istatus)
274 if (allocated(tt) .and. allocated(qv)) then
275 p_data%xlvl = 200100.
277 ! Surface RH should be computed from surface DEWPT by ungrib
278 ! rh_data%xlvl = 200100.
279 ! call calc_rh(tt(:,:,n_levels+1), qv(:,:,n_levels+1), psfc, rh_data%slab, rh_data%nx, rh_data%ny)
280 ! call write_next_met_field(rh_data, istatus)
281 call write_next_met_field(p_data, istatus)
283 call mprintf(.true.,WARN,'Either TT or SPECHUMD not found. No RH will be computed.')
288 !CvH_DvD: if tt, qv and hgtsfc are available compute hgt
289 if (allocated(tt) .and. allocated(qv) .and. allocated(hgtsfc)) then
291 if (.not. allocated(hgtprev)) then
292 allocate(hgtprev(ecmwf_data%nx,ecmwf_data%ny))
294 if (.not. allocated(pstart)) then
295 allocate(pstart(ecmwf_data%nx,ecmwf_data%ny))
297 if (.not. allocated(pend)) then
298 allocate(pend(ecmwf_data%nx,ecmwf_data%ny))
300 if (.not. allocated(hgt_3ddata)) then
301 allocate(hgt_3ddata(ecmwf_data%nx,ecmwf_data%ny, n_levels))
304 ! CvH_DvD: interpolate Q and T if they are not available at all levels
305 do i = n_levels, 1, -1
306 if (tt(1,1,i) .eq. 0) then ! CvH_DvD: If TT is zero, level is unknown, so moisture is zero too
307 if (i .eq. n_levels) then
308 write(6,*) 'WARNING First level is missing!' ! CvH_DvD: First level should be there!
309 else if (i .eq. 1) then
310 ! DvD: If TT is zero at the top level, so missing --> use previous level,
311 ! mod level will remove it anyway
312 tt(:,:,i)=tt(:,:,i+1)
313 qv(:,:,i)=qv(:,:,i+1)
316 ! CvH_DvD: Find first available level
317 do while ((i-counter .gt. 1) .and. (tt(1,1,i-counter) .eq. 0))
320 if (tt(1,1,i-counter) .gt. 0) then
321 ! DvD: Interpolate tt and qv from next available level
322 tt(:,:,i)=tt(:,:,i+1)+(tt(:,:,i-counter)-tt(:,:,i+1))/(counter+1)
323 qv(:,:,i)=qv(:,:,i+1)+(qv(:,:,i-counter)-qv(:,:,i+1))/(counter+1)
325 ! DvD: No available level found --> should not happen.
326 write(6,*) 'WARNING No available level found near level ',i,'!'
332 ! CvH_DvD: first previous hgt is surface hgt/soilgeo
334 ! CvH_DvD: - Loop from surface to top, note: 1=top & n_levels=surface
335 do i = n_levels, 1, -1
336 ! CvH_DvD: compute half level pressure at current half-level
337 pend = 0.5 * (a(i-1) + a(i)) + psfc * 0.5 * (b(i-1) + b(i))
338 if (i .eq. n_levels) then
339 ! CvH_DvD: use Tv not T, use Psfc and T_halflevel_1=AveT lowest level
340 hgt_3ddata(:,:,i) = hgtprev + 287.05 * tt(:,:,i) * (1. + 0.61 * qv(:,:,i)) * log(psfc/pend) / 9.81
342 ! CvH_DvD: compute half level pressure beneath current half-level
343 pstart = 0.5 * (a(i+1) + a(i)) + psfc * 0.5 * (b(i+1) + b(i))
344 ! CvH_DvD: use Tv not T, create T at full leve beneath current half level
345 hgt_3ddata(:,:,i) = hgtprev + 287.05 * (tt(:,:,i) + tt(:,:,i+1))/2. * &
346 (1. + 0.61 * (qv(:,:,i)+qv(:,:,i+1))/2.) * log(pstart/pend) / 9.81
348 hgtprev = hgt_3ddata(:,:,i)
356 a_full = 0.5 * (a(i-1) + a(i)) ! A and B are dimensioned (0:n_levels)
357 b_full = 0.5 * (b(i-1) + b(i))
359 p_data%xlvl = real(i)
360 p_data%slab = a_full + psfc * b_full
362 if (allocated(tt) .and. allocated(qv)) then
363 rh_data%xlvl = real(i)
364 call calc_rh(tt(:,:,i), qv(:,:,i), p_data%slab, rh_data%slab, rh_data%nx, rh_data%ny)
365 call write_next_met_field(rh_data, istatus)
367 if (allocated(hgtsfc)) then
368 ! CvH_DvD: put hgt_3ddata into hgt_data object
369 hgt_data%xlvl = real(i)
370 hgt_data%slab = hgt_3ddata(:,:,i)
371 call write_next_met_field(hgt_data, istatus)
373 call mprintf(.true., WARN, &
374 'Either SOILHGT or SOILGEO are required to create 3-d GHT field, which is required '// &
375 'for a correct vertical interpolation in real.')
379 call write_next_met_field(p_data, istatus)
383 call write_met_close()
385 deallocate(p_data%slab)
386 deallocate(rh_data%slab)
387 ! CvH_DvD: deallocate stuff
388 deallocate(hgt_data%slab)
392 if (allocated(tt)) deallocate(tt)
393 if (allocated(qv)) deallocate(qv)
394 if (allocated(psfc)) deallocate(psfc)
395 ! CvH_DvD: deallocate stuff
396 if (allocated(hgt_3ddata)) deallocate(hgt_3ddata)
397 if (allocated(pstart)) deallocate(pstart)
398 if (allocated(pend)) deallocate(pend)
399 if (allocated(hgtprev)) deallocate(hgtprev)
403 call mprintf(.true.,ERROR,'Problem opening %s at time %s', s1=input_name, s2=temp_date(1:13))
407 ! CvH_DvD: only now deallocate hgtsfc,
408 ! because oper EC does not have hgtsfc @ fps
409 if (allocated(hgtsfc)) deallocate(hgtsfc)
411 input_name = fg_name(fg_idx)
415 call cleanup_coeffs()
419 end program calc_ecmwf_p
422 subroutine calc_rh(t, qv, p, rh, nx, ny)
427 integer, intent(in) :: nx, ny
428 real, dimension(nx, ny), intent(in) :: t, qv, p
429 real, dimension(nx, ny), intent(out) :: rh
432 real, parameter :: svp1=0.6112
433 real, parameter :: svp2=17.67
434 real, parameter :: svp3=29.65
435 real, parameter :: svpt0=273.15
436 real, parameter :: eps = 0.622
444 es=svp1*10.*exp(svp2*(t(i,j)-svpt0)/(t(i,j)-svp3))
445 e=qv(i,j)*p(i,j)/100./(eps+qv(i,j)*(1.-eps)) ! qv is specific humidity
446 rh(i,j) = 100.0 * e/es
450 end subroutine calc_rh