1 | """ |
---|
2 | repository of wrf utilities |
---|
3 | """ |
---|
4 | import os |
---|
5 | import sys |
---|
6 | import numpy as n |
---|
7 | import pylab as p |
---|
8 | from matplotlib.numerix import ma |
---|
9 | from matplotlib.toolkits.basemap import Basemap |
---|
10 | import pywrf.viz.utils as vu |
---|
11 | |
---|
12 | |
---|
13 | # the syntax to import nio depends on its version. We try all options from the |
---|
14 | # newest to the oldest |
---|
15 | try: |
---|
16 | import Nio as nio |
---|
17 | except: |
---|
18 | try: |
---|
19 | import PyNGL.Nio as nio |
---|
20 | except: |
---|
21 | import PyNGL_numpy.Nio as nio |
---|
22 | |
---|
23 | # The following is only needed for hn3.its.monash.edu.au |
---|
24 | # Unfortunately we must get rid of spurious entries in the sys.path that can |
---|
25 | # lead to the loading of conflicting packages |
---|
26 | for dir_idx in range(len(sys.path)-1,-1,-1): |
---|
27 | if 'python2.4' in sys.path[dir_idx]: |
---|
28 | sys.path.pop(dir_idx) |
---|
29 | |
---|
30 | a_small_number = 1e-8 |
---|
31 | |
---|
32 | def colons_to_underscores(test=False): |
---|
33 | files = os.listdir('.') |
---|
34 | print 'I plan to do the following:' |
---|
35 | for file in files: |
---|
36 | command = 'mv ' + file + ' ' + file[:-6] + '_00_00' |
---|
37 | print command |
---|
38 | print 'Happy? [y/n]' |
---|
39 | answer = raw_input() |
---|
40 | if answer == 'y': |
---|
41 | for file in files: |
---|
42 | command = 'mv ' + file + ' ' + file[:-6] + '_00_00' |
---|
43 | os.system(command) |
---|
44 | else: |
---|
45 | print 'OK, maybe next time ;]' |
---|
46 | return |
---|
47 | |
---|
48 | def wrf_to_pressure(var, pressure, press_lvl, fill_value=1e35, positive_only=False): |
---|
49 | """ |
---|
50 | this functions vertically interpolates to pressure levels WRF fields |
---|
51 | usage |
---|
52 | >>> interpolated_var(var, pressure, press_lvl, fill_value=1e35, |
---|
53 | ... positive_only=False) |
---|
54 | where |
---|
55 | var -> field to be interpolated |
---|
56 | pressure -> total pressure field (p + pb) |
---|
57 | press_lvl -> list or numpy array of desired levels |
---|
58 | fill_value -> this is assigned to a cell if the requested pressure level |
---|
59 | lies below or above the column of values supplied for that cell in |
---|
60 | the pressure array. |
---|
61 | positive_only -> set this flag to true to prevent the interpolation to |
---|
62 | generate negative values for fields for which this does not make |
---|
63 | sense. |
---|
64 | interpolated_var -> the interpolated field which will have shape |
---|
65 | (len(press_lvl), var.shape[1], var.shape[2]) |
---|
66 | NB in this current implementation of the function arrays need to be |
---|
67 | destaggerred before they operated upon. Furthermore, it is assumed the |
---|
68 | function is invoked to process one frame at at time i.e. it works on 3D |
---|
69 | arrays. As usual COARDS ordering of dimensions i.e. (time,lvl,lat,lon) |
---|
70 | is assumed. |
---|
71 | """ |
---|
72 | # these are the pressure levels we want as output |
---|
73 | k_max, j_max, i_max = pressure.shape |
---|
74 | output = n.zeros((len(press_lvl), j_max, i_max)) |
---|
75 | for lvl_idx in range(len(press_lvl)): |
---|
76 | for j in range(j_max): |
---|
77 | for i in range(i_max): |
---|
78 | # make it ascending for searchsorted |
---|
79 | pressure_column = pressure[::-1,j,i] |
---|
80 | if press_lvl[lvl_idx] > pressure_column.max() \ |
---|
81 | or press_lvl[lvl_idx] < pressure_column.min(): |
---|
82 | output[lvl_idx,j,i] = fill_value |
---|
83 | else: |
---|
84 | var_column = var[::-1,j,i] |
---|
85 | press_idx = pressure_column.searchsorted(press_lvl[lvl_idx]) |
---|
86 | xa = pressure_column[press_idx] |
---|
87 | xb = pressure_column[press_idx - 1] |
---|
88 | ya = var_column[press_idx] |
---|
89 | yb = var_column[press_idx - 1] |
---|
90 | x = press_lvl[lvl_idx] |
---|
91 | y = vu.lin_interp(xa, xb, ya, yb, x) |
---|
92 | if positive_only and y < 0.: |
---|
93 | y = 0. |
---|
94 | output[lvl_idx,j,i] = y |
---|
95 | return output |
---|
96 | |
---|
97 | def wrf2latlon(projection, |
---|
98 | lat_0, lon_0, |
---|
99 | lat_1, |
---|
100 | lat_2, |
---|
101 | grid_centre_lon, |
---|
102 | grid_centre_lat, |
---|
103 | nx, |
---|
104 | ny, |
---|
105 | delta, |
---|
106 | staggered = False, |
---|
107 | return_extra = False |
---|
108 | ): |
---|
109 | from pyproj import Proj |
---|
110 | proj = Proj(proj=projection, lat_0=lat_0, lon_0=lon_0, lat_1=lat_1, lat_2=lat_2) |
---|
111 | grid_centre_x, grid_centre_y = proj(grid_centre_lon, grid_centre_lat) |
---|
112 | grid_x_extent = nx * delta |
---|
113 | grid_y_extent = ny * delta |
---|
114 | min_x = grid_centre_x - grid_x_extent/2. |
---|
115 | min_y = grid_centre_y - grid_y_extent/2. |
---|
116 | max_x = min_x + grid_x_extent |
---|
117 | max_y = min_y + grid_y_extent |
---|
118 | x = n.arange(min_x, max_x + a_small_number, delta) |
---|
119 | y = n.arange(min_y, max_y + a_small_number, delta) |
---|
120 | X, Y = p.meshgrid(x,y) |
---|
121 | lon, lat = proj(X, Y, inverse=True) |
---|
122 | |
---|
123 | if staggered: |
---|
124 | x_u = n.arange(min_x, max_x + delta + a_small_number, delta) |
---|
125 | x_u -= (delta /2.) |
---|
126 | X_u, Y_u = p.meshgrid(x_u,y) |
---|
127 | y_v = n.arange(min_y, max_y + delta + a_small_number, delta) |
---|
128 | y_v -= (delta /2.) |
---|
129 | X_v, Y_v = p.meshgrid(x,y_v) |
---|
130 | lon_u, lat_u = proj(X_u, Y_u, inverse=True) |
---|
131 | lon_v, lat_v = proj(X_v, Y_v, inverse=True) |
---|
132 | |
---|
133 | llcrnrlon, llcrnrlat = proj(min_x, min_y, inverse=True) |
---|
134 | urcrnrlon, urcrnrlat = proj(max_x, max_y, inverse=True) |
---|
135 | map = Basemap( |
---|
136 | projection = projection, |
---|
137 | lon_0 = lon_0, |
---|
138 | lat_0 = lat_0, |
---|
139 | lat_1 = lat_1, |
---|
140 | lat_2 = lat_2, |
---|
141 | llcrnrlon = llcrnrlon, |
---|
142 | llcrnrlat = llcrnrlat, |
---|
143 | urcrnrlon = urcrnrlon, |
---|
144 | urcrnrlat = urcrnrlat |
---|
145 | ) |
---|
146 | |
---|
147 | if return_extra: |
---|
148 | # it seems that the basemap automatically sets the origin the native |
---|
149 | # coordinate system at its llcrnr (or close to it...) |
---|
150 | offset = map(lon_0, lat_0) |
---|
151 | if staggered: |
---|
152 | X += offset[0] |
---|
153 | Y += offset[1] |
---|
154 | X_u += offset[0] |
---|
155 | Y_u += offset[1] |
---|
156 | X_v += offset[0] |
---|
157 | Y_v += offset[1] |
---|
158 | return lon, lat, lon_u, lat_u, lon_v, lat_v, \ |
---|
159 | X, Y, X_u, Y_u, X_v, Y_v, map |
---|
160 | else: |
---|
161 | X += offset[0] |
---|
162 | Y += offset[1] |
---|
163 | return lon, lat, X, Y, map |
---|
164 | else: |
---|
165 | if staggered: |
---|
166 | return lon, lat, lon_u, lat_u, lon_v, lat_v |
---|
167 | else: |
---|
168 | return lon, lat |
---|
169 | |
---|
170 | def wrf_grid( |
---|
171 | # WPS -> map_proj |
---|
172 | projection, |
---|
173 | # WPS -> truelat1 |
---|
174 | lat_1, |
---|
175 | # WPS -> truelat2 |
---|
176 | lat_2, |
---|
177 | # WPS -> stand_lon |
---|
178 | lon_0, |
---|
179 | # WPS -> ref_lat |
---|
180 | grid_centre_lat, |
---|
181 | # WPS -> ref_lon |
---|
182 | grid_centre_lon, |
---|
183 | delta_x, |
---|
184 | delta_y, |
---|
185 | # WPS -> e_we |
---|
186 | nx, |
---|
187 | # WPS -> e_sn |
---|
188 | ny, |
---|
189 | show_mass_grid = False, |
---|
190 | show_stag_grids = False, |
---|
191 | ): |
---|
192 | if lon_0 != grid_centre_lon: |
---|
193 | print 'not implemented yet -> see the source' |
---|
194 | print "\tbut let's try it anyways..." |
---|
195 | #return |
---|
196 | |
---|
197 | width = nx * delta_x |
---|
198 | height = ny * delta_y |
---|
199 | frame_x = 10 * delta_x |
---|
200 | frame_y = 10 * delta_y |
---|
201 | m = Basemap( |
---|
202 | lat_0 = grid_centre_lat, |
---|
203 | # this could be a bad assumption... because lon_0 and grid_centre_lon |
---|
204 | # need not be aligned, but at the same time I need to give this to |
---|
205 | # basemap for the grid to be centred... I could probably fix it |
---|
206 | # assigning lon_0 and then imposing a grid shift in native coordinates |
---|
207 | # if ref_lon and lon_0 were not the same |
---|
208 | lon_0 = lon_0, |
---|
209 | lat_1 = lat_1, |
---|
210 | lat_2 = lat_2, |
---|
211 | width = width + 2*frame_x, |
---|
212 | height = height + 2*frame_y, |
---|
213 | resolution = 'l', |
---|
214 | area_thresh=1000. |
---|
215 | ) |
---|
216 | grid_centre_x, grid_centre_y = m(grid_centre_lon, grid_centre_lat) |
---|
217 | min_x = grid_centre_x - width/2. |
---|
218 | min_y = grid_centre_y - height/2. |
---|
219 | max_x = min_x + width |
---|
220 | max_y = min_y + height |
---|
221 | x = n.arange(min_x, max_x + a_small_number, delta_x) |
---|
222 | y = n.arange(min_y, max_y + a_small_number, delta_y) |
---|
223 | x = x[1:-1] |
---|
224 | y = y[1:-1] |
---|
225 | x_u = n.arange(min_x, max_x + delta_x + a_small_number, delta_x) |
---|
226 | x_u -= delta_x/2. |
---|
227 | x_u = x_u[1:-1] |
---|
228 | y_v = n.arange(min_y, max_y + delta_y + a_small_number, delta_y) |
---|
229 | y_v -= delta_y/2. |
---|
230 | y_v = y_v[1:-1] |
---|
231 | X, Y = p.meshgrid(x,y) |
---|
232 | lon, lat = m(X, Y, inverse=True) |
---|
233 | X_u, Y_u = p.meshgrid(x_u,y) |
---|
234 | lon_u, lat_u = m(X_u, Y_u, inverse=True) |
---|
235 | X_v, Y_v = p.meshgrid(x,y_v) |
---|
236 | lon_v, lat_v = m(X_v, Y_v, inverse=True) |
---|
237 | if show_mass_grid: |
---|
238 | m.plot(X, Y, 'b+') |
---|
239 | m.plot([grid_centre_x], [grid_centre_y], 'r+') |
---|
240 | if show_stag_grids: |
---|
241 | m.plot(X_u, Y_u, 'g+') |
---|
242 | m.plot(X_v, Y_v, 'r+') |
---|
243 | m.drawcoastlines() |
---|
244 | p.show() |
---|
245 | output = { |
---|
246 | 'map' : m, |
---|
247 | 'mass_stag': { |
---|
248 | 'lon_2d' : lon, |
---|
249 | 'lat_2d' : lat, |
---|
250 | 'x' : x, |
---|
251 | 'y' : y, |
---|
252 | 'x_2d' : X, |
---|
253 | 'y_2d' : Y, |
---|
254 | }, |
---|
255 | 'u_stag': { |
---|
256 | 'lon_2d' : lon_u, |
---|
257 | 'lat_2d' : lat_u, |
---|
258 | 'x' : x_u, |
---|
259 | 'y' : y, |
---|
260 | 'x_2d' : X_u, |
---|
261 | 'y_2d' : Y_u, |
---|
262 | }, |
---|
263 | 'v_stag': { |
---|
264 | 'lon_2d' : lon_v, |
---|
265 | 'lat_2d' : lat_v, |
---|
266 | 'x' : x, |
---|
267 | 'y' : y_v, |
---|
268 | 'x_2d' : X_v, |
---|
269 | 'y_2d' : Y_v, |
---|
270 | } |
---|
271 | } |
---|
272 | |
---|
273 | return output |
---|
274 | |
---|
275 | |
---|
276 | def find_parent_ij(outer_grid, inner_grid): |
---|
277 | projection = outer_grid[0] |
---|
278 | lat_0 = outer_grid[1] |
---|
279 | lon_0 = outer_grid[2] |
---|
280 | lat_1 = outer_grid[3] |
---|
281 | lat_2 = outer_grid[4] |
---|
282 | grid_centre_lon = outer_grid[5] |
---|
283 | grid_centre_lat = outer_grid[6] |
---|
284 | nx = outer_grid[7] |
---|
285 | ny = outer_grid[8] |
---|
286 | delta = outer_grid[9] |
---|
287 | |
---|
288 | from pyproj import Proj |
---|
289 | proj = Proj(proj=projection, lat_0=lat_0, lon_0=lon_0, lat_1=lat_1, lat_2=lat_2) |
---|
290 | grid_centre_x, grid_centre_y = proj(grid_centre_lon, grid_centre_lat) |
---|
291 | grid_x_extent = nx * delta |
---|
292 | grid_y_extent = ny * delta |
---|
293 | min_x = grid_centre_x - grid_x_extent/2. |
---|
294 | min_y = grid_centre_y - grid_y_extent/2. |
---|
295 | max_x = min_x + grid_x_extent |
---|
296 | max_y = min_y + grid_y_extent |
---|
297 | outer_x = n.arange(min_x, max_x + a_small_number, delta) |
---|
298 | outer_y = n.arange(min_y, max_y + a_small_number, delta) |
---|
299 | |
---|
300 | projection = inner_grid[0] |
---|
301 | lat_0 = inner_grid[1] |
---|
302 | lon_0 = inner_grid[2] |
---|
303 | lat_1 = inner_grid[3] |
---|
304 | lat_2 = inner_grid[4] |
---|
305 | grid_centre_lon = inner_grid[5] |
---|
306 | grid_centre_lat = inner_grid[6] |
---|
307 | nx = inner_grid[7] |
---|
308 | ny = inner_grid[8] |
---|
309 | delta = inner_grid[9] |
---|
310 | |
---|
311 | grid_centre_x, grid_centre_y = proj(grid_centre_lon, grid_centre_lat) |
---|
312 | grid_x_extent = nx * delta |
---|
313 | grid_y_extent = ny * delta |
---|
314 | min_x = grid_centre_x - grid_x_extent/2. |
---|
315 | min_y = grid_centre_y - grid_y_extent/2. |
---|
316 | max_x = min_x + grid_x_extent |
---|
317 | max_y = min_y + grid_y_extent |
---|
318 | inner_x = n.arange(min_x, max_x + a_small_number, delta) |
---|
319 | inner_y = n.arange(min_y, max_y + a_small_number, delta) |
---|
320 | |
---|
321 | return outer_x.searchsorted(inner_x[0]), outer_y.searchsorted(inner_y[0]) |
---|
322 | |
---|
323 | def write_namelist(namelist_dict,outfile='outfile'): |
---|
324 | |
---|
325 | out_string='' |
---|
326 | for group in namelist_dict.keys(): |
---|
327 | out_string += group + '\n' |
---|
328 | for variable in namelist_dict[group].keys(): |
---|
329 | out_string += variable + ' = ' |
---|
330 | for element in namelist_dict[group][variable]: |
---|
331 | out_string += repr(element).strip("'")+', ' |
---|
332 | out_string+='\n' |
---|
333 | out_string+= '/\n\n' |
---|
334 | |
---|
335 | fid=open(outfile,'w') |
---|
336 | fid.write(out_string) |
---|
337 | |
---|
338 | return None |
---|
339 | |
---|
340 | |
---|
341 | |
---|
342 | def read_namelist(namelist_file): |
---|
343 | """read contents of namelist file and return dictionary containing all options |
---|
344 | |
---|
345 | Created 20/01/08 by Thom Chubb. |
---|
346 | Modified 20/01/08 by Thom Chubb and Valerio Bisignesi |
---|
347 | |
---|
348 | TODO: mod_levs have a slightly different format in the namelist file, but as |
---|
349 | they come last in namelist.wps I have conveniently dropped them (with a warning |
---|
350 | of course =) ). Whoever needs them first can come up with a fix for this. |
---|
351 | Untested as yet with the namelist.input file. It should work fine and may be useful |
---|
352 | as a consistency check between the two files. This has been buggine me for a while. |
---|
353 | """ |
---|
354 | |
---|
355 | fid=open(namelist_file) |
---|
356 | |
---|
357 | out_dict={} |
---|
358 | data = fid.readlines() |
---|
359 | num_lines = len(data) |
---|
360 | |
---|
361 | for line in data: |
---|
362 | if '&' in line: |
---|
363 | # Then this line is a namelist title |
---|
364 | is_comment=False |
---|
365 | current_label = line.rstrip('\n').lstrip(' ') |
---|
366 | out_dict[current_label] ={} |
---|
367 | elif '/' in line: |
---|
368 | # Then lines following this are comments until the next '&' |
---|
369 | is_comment=True |
---|
370 | elif '=' in line: |
---|
371 | # Then this line contains variable information to be stored |
---|
372 | if not is_comment: |
---|
373 | variable,values = line.split('=') |
---|
374 | values = values.rstrip('\n').rstrip(',') |
---|
375 | try: |
---|
376 | values=[int(element) for element in values.split(',')] |
---|
377 | except ValueError: |
---|
378 | try: |
---|
379 | values=[float(element) for element in values.split(',')] |
---|
380 | except ValueError: |
---|
381 | values=[value.strip() for value in values.split(',')] |
---|
382 | |
---|
383 | out_dict[current_label][variable.strip()]=values |
---|
384 | |
---|
385 | return out_dict |
---|
386 | |
---|
387 | def check_namelist_consistency(): |
---|
388 | # TODO |
---|
389 | # run time vs. run date |
---|
390 | # strict consistency between dates in namelist.wps and namelist.input not |
---|
391 | # necessary as long as dates in namelist.input are a subset of those in namelist.wps |
---|
392 | # and the interval_seconds is correct |
---|
393 | pass |
---|
394 | |
---|
395 | #def read_namelist_old(namelist_file): |
---|
396 | # """read contents of namelist file and return dictionary containing all options |
---|
397 | # |
---|
398 | # Created 20/01/08 by Thom Chubb. |
---|
399 | # |
---|
400 | # TODO: mod_levs have a slightly different format in the namelist file, but as |
---|
401 | # they come last in namelist.wps I have conveniently dropped them (with a warning |
---|
402 | # of course =) ). Whoever needs them first can come up with a fix for this. |
---|
403 | # Untested as yet with the namelist.input file. It should work fine and may be useful |
---|
404 | # as a consistency check between the two files. This has been buggine me for a while. |
---|
405 | # """ |
---|
406 | # |
---|
407 | # fid=open(namelist_file) |
---|
408 | # |
---|
409 | # out_dict={} |
---|
410 | # data = fid.readlines() |
---|
411 | # num_lines = len(data) |
---|
412 | # |
---|
413 | # # for k in range(0,num_lines): |
---|
414 | # for line in data: |
---|
415 | # # str = data[k].rstrip('\n').rstrip(',').split() |
---|
416 | # str = line.rstrip('\n').rstrip(',').split() |
---|
417 | # |
---|
418 | # if str == []: |
---|
419 | # pass |
---|
420 | # elif str[0] == '': |
---|
421 | # pass |
---|
422 | # elif str[0][0] == '': |
---|
423 | # pass |
---|
424 | # elif str[0][0] == '/' : |
---|
425 | # is_comment=True |
---|
426 | # elif str[0][0] == '&': |
---|
427 | # # Then this line is a namelist title |
---|
428 | # is_comment=False |
---|
429 | # label = str[0] |
---|
430 | # |
---|
431 | # if label == '&mod_levs': |
---|
432 | # print ">> WARNING: mod levels don't work yet" |
---|
433 | # break |
---|
434 | # |
---|
435 | # out_dict[label] ={} |
---|
436 | # |
---|
437 | # else: |
---|
438 | # if not is_comment: |
---|
439 | # field = str[0] |
---|
440 | # out_dict[label][field] = [] |
---|
441 | # |
---|
442 | # |
---|
443 | # for k in range(2,str.__len__()): |
---|
444 | # dat = str[k].rstrip(',') |
---|
445 | # # dat = str[k].split(',') |
---|
446 | # print str, dat |
---|
447 | # try: |
---|
448 | # dat=float(dat) |
---|
449 | # except ValueError: |
---|
450 | # pass |
---|
451 | # except TypeError: |
---|
452 | # pass |
---|
453 | # |
---|
454 | # out_dict[label][field].extend(dat) |
---|
455 | # |
---|
456 | # # out_dict[label][field] = [] |
---|
457 | # # out_dict[label][field].append(str[2:]) |
---|
458 | # |
---|
459 | # return out_dict |
---|
460 | |
---|
461 | |
---|
462 | def wrf_grid_wrapper(namelist_file='namelist.wps',nest_level=0): |
---|
463 | """ |
---|
464 | wrf_grid_wrapper(namelist_file='namelist.wps',nest_level=0): |
---|
465 | Basic wrapper to easily visualise grids specified in namelist.wps |
---|
466 | |
---|
467 | Uses wrf.utils.read_namelist() to determine the read the appropriate variables |
---|
468 | in a specified namelist file and then calls wrf.utils.wrf_grid() to define |
---|
469 | the Basemap projection and show the grid over a map. |
---|
470 | |
---|
471 | Created 20/01/08 by Thom Chubb. |
---|
472 | Modified 27/01/08 - implemented viz.utils.plot_grid() to handle plotting and |
---|
473 | capability for viewing as many grids as desired. |
---|
474 | |
---|
475 | TODO: Could use some more error checking, i think it will all fail if nest_levels |
---|
476 | are not consecutive!! Interpolation implemented is awkward but seems to work. |
---|
477 | |
---|
478 | """ |
---|
479 | |
---|
480 | # Create namelist dictionary |
---|
481 | nd = read_namelist(namelist_file) |
---|
482 | |
---|
483 | # Field editing to make python happy |
---|
484 | if nd['&geogrid']['map_proj'][0]=="'lambert'": |
---|
485 | print 'debug: modify input field lambert -> lcc' |
---|
486 | nd['&geogrid']['map_proj'][0]='lcc' |
---|
487 | |
---|
488 | grid = [] |
---|
489 | |
---|
490 | outer_grid = wrf2latlon(nd['&geogrid']['map_proj'][0], |
---|
491 | nd['&geogrid']['ref_lat'][0], |
---|
492 | nd['&geogrid']['ref_lon'][0], |
---|
493 | nd['&geogrid']['truelat1'][0], |
---|
494 | nd['&geogrid']['truelat2'][0], |
---|
495 | nd['&geogrid']['ref_lon'][0], |
---|
496 | nd['&geogrid']['ref_lat'][0], |
---|
497 | # nd['&geogrid']['e_we'][nest_level[0]], |
---|
498 | # nd['&geogrid']['e_sn'][nest_level[0]], |
---|
499 | nd['&geogrid']['e_we'][0], |
---|
500 | nd['&geogrid']['e_sn'][0], |
---|
501 | nd['&geogrid']['dx'][0], |
---|
502 | staggered = False, |
---|
503 | return_extra = True |
---|
504 | ) |
---|
505 | # print "outer_grid.shape =", outer_grid[0].shape |
---|
506 | |
---|
507 | grid.append(outer_grid) |
---|
508 | nest_level.sort() |
---|
509 | # nest_level=p.sort(nest_level) |
---|
510 | |
---|
511 | # for k in nest_level[1:]: |
---|
512 | for k in range(1,max(nest_level)+1): |
---|
513 | this_grid = [] |
---|
514 | try: |
---|
515 | e_we = nd['&geogrid']['e_we'][k] |
---|
516 | except IndexError: |
---|
517 | print "Out of range. Not enough grids specified within namelist file" |
---|
518 | return 1 |
---|
519 | e_sn = nd['&geogrid']['e_sn'][k] |
---|
520 | pgr = nd['&geogrid']['parent_grid_ratio'][k] |
---|
521 | ips = nd['&geogrid']['i_parent_start'][k] |
---|
522 | jps = nd['&geogrid']['j_parent_start'][k] |
---|
523 | print 'processing grid: ',k |
---|
524 | # print e_we,e_sn,pgr,ips,jps |
---|
525 | # print ips,':',(ips+(e_we/pgr)),',', jps,':',(jps+(e_sn/pgr)) |
---|
526 | |
---|
527 | # Interpolate in grid space to estimate inner gridpoints - |
---|
528 | # care to find a more elegant approach??? |
---|
529 | x1=grid[-1][2][jps, ips:ips+e_we/pgr] |
---|
530 | y1=grid[-1][3][jps:jps+e_sn/pgr, ips] |
---|
531 | |
---|
532 | a1=n.arange(0,x1.__len__(),1) |
---|
533 | a2=n.arange(0,x1.__len__(),1./pgr) |
---|
534 | b1=n.arange(0,y1.__len__(),1) |
---|
535 | b2=n.arange(0,y1.__len__(),1./pgr) |
---|
536 | |
---|
537 | x2=n.interp(a2,a1,x1) |
---|
538 | y2=n.interp(b2,b1,y1) |
---|
539 | |
---|
540 | [X,Y]=n.meshgrid(x2,y2) |
---|
541 | |
---|
542 | # convert back to navigational coordinates |
---|
543 | lon,lat=grid[-1][4](X,Y,nd['&geogrid']['map_proj']) |
---|
544 | |
---|
545 | for j in [lon,lat,X,Y,grid[-1][4]]: |
---|
546 | this_grid.append(j) |
---|
547 | if (k in nest_level): |
---|
548 | map=vu.plot_grid(this_grid[0],this_grid[1],skip=10,same_figure=True,return_map=True) |
---|
549 | grid.append(this_grid) |
---|
550 | # print grid[-1][0].shape |
---|
551 | |
---|
552 | |
---|
553 | if 0 in nest_level: |
---|
554 | map=vu.plot_grid(outer_grid[0],outer_grid[1],skip=10,same_figure=True,return_map=True) |
---|
555 | map.drawmeridians(n.arange(130,180,15),labels=[1,0,0,1]) |
---|
556 | map.drawparallels(n.arange(0,-90,-15),labels=[1,0,0,1]) |
---|
557 | map.drawcoastlines() |
---|
558 | map.drawrivers() |
---|
559 | plot_custom_points(map) |
---|
560 | p.show() |
---|
561 | |
---|
562 | return grid, map |
---|
563 | |
---|
564 | def calculate_mslp(p,pb,ph,phb,t,qvapor): |
---|
565 | ''' |
---|
566 | calculate sea level pressure starting from 'raw' wrf output fields |
---|
567 | usage: |
---|
568 | >>> calculate_mslp(p,pb,ph,phb,t,qvapor) |
---|
569 | where the arguments names correspond to the variable names in the |
---|
570 | wrfout files e.g. p(lvl,lat,lon) or p(time,lvl,lat,lon) |
---|
571 | ''' |
---|
572 | import from_wrf_to_grads as fw2g |
---|
573 | cs = fw2g.from_wrf_to_grads.compute_seaprs |
---|
574 | |
---|
575 | if len(p.shape) == 3: |
---|
576 | # recover the full pressure field by adding perturbation and base |
---|
577 | p = p + pb |
---|
578 | p_t = p.transpose() |
---|
579 | # same geopotential height |
---|
580 | ph = (ph + phb) / 9.81 |
---|
581 | ph_t = ph.transpose() |
---|
582 | qvapor_t = qvapor.transpose() |
---|
583 | # do not add the wrf specified 300 factor as the wrapped fortran code |
---|
584 | # does that for us |
---|
585 | t_t = t.transpose() |
---|
586 | nz = ph_t.shape[2] |
---|
587 | # populate the geopotential_height at mid_levels array with |
---|
588 | # averages between layers below and above |
---|
589 | z = (ph_t[:,:,:nz-1] + ph_t[:,:,1:nz]) / 2.0 |
---|
590 | # finally "in one fell sweep" |
---|
591 | # the zero is for debug purposes |
---|
592 | return cs(z,t_t,p_t,qvapor_t,0).transpose() |
---|
593 | elif len(p.shape) == 4: |
---|
594 | mslp_shape = (p.shape[0], p.shape[2], p.shape[3]) |
---|
595 | mslp = n.zeros(mslp_shape) |
---|
596 | for time_idx in range(p.shape[0]): |
---|
597 | # recover the full pressure field by adding perturbation and base |
---|
598 | dummy_p = p[time_idx] + pb[time_idx] |
---|
599 | dummy_p_t = dummy_p.transpose() |
---|
600 | # same geopotential height |
---|
601 | dummy_ph = (ph[time_idx] + phb[time_idx]) / 9.81 |
---|
602 | dummy_ph_t = dummy_ph.transpose() |
---|
603 | dummy_qvapor_t = qvapor[time_idx].transpose() |
---|
604 | # do not add the wrf specified 300 factor as the wrapped fortran code |
---|
605 | # does that for us |
---|
606 | dummy_t_t = t[time_idx].transpose() |
---|
607 | nz = dummy_ph_t.shape[2] |
---|
608 | # populate the geopotential_height at mid_levels array with |
---|
609 | # averages between layers below and above |
---|
610 | z = (dummy_ph_t[:,:,:nz-1] + dummy_ph_t[:,:,1:nz]) / 2.0 |
---|
611 | # finally "in one fell sweep" |
---|
612 | # the zero is for debug purposes |
---|
613 | mslp[time_idx] = cs(z,dummy_t_t,dummy_p_t,dummy_qvapor_t,0).transpose() |
---|
614 | return mslp |
---|
615 | else: |
---|
616 | print 'Wrong shape of the array' |
---|
617 | return |
---|
618 | |
---|
619 | def calculate_mslp_wrapper(vars_dict, time_idx): |
---|
620 | """Utility function to |
---|
621 | pull out the necessary variables from the wrfout file and call |
---|
622 | calculate_mslp to generate the mslp field. |
---|
623 | """ |
---|
624 | # accessing the times in the nc_file one at a time and |
---|
625 | # using the .copy() method reduce the memory footprint |
---|
626 | perturbation_pressure = vars_dict['P'].get_value()[time_idx].copy() |
---|
627 | base_pressure = vars_dict['PB'].get_value()[time_idx].copy() |
---|
628 | perturbation_geopotential = vars_dict['PH'].get_value()[time_idx].copy() |
---|
629 | base_geopotential = vars_dict['PHB'].get_value()[time_idx].copy() |
---|
630 | temperature = vars_dict['T'].get_value()[time_idx].copy() |
---|
631 | mixing_ratio = vars_dict['QVAPOR'].get_value()[time_idx].copy() |
---|
632 | mslp = calculate_mslp( |
---|
633 | perturbation_pressure, |
---|
634 | base_pressure, |
---|
635 | perturbation_geopotential, |
---|
636 | base_geopotential, |
---|
637 | temperature, |
---|
638 | mixing_ratio) |
---|
639 | #del perturbation_pressure, base_pressure |
---|
640 | #del perturbation_geopotential, base_geopotential |
---|
641 | #del temperature, mixing_ratio |
---|
642 | return mslp |
---|
643 | |
---|
644 | def plot_custom_points(map): |
---|
645 | """back by popular demand""" |
---|
646 | |
---|
647 | # canberra_lon = [149 + 8./60] |
---|
648 | # canberra_lat = [-35 - 17./60] |
---|
649 | # map.plot(canberra_lon,canberra_lat, 'gs') |
---|
650 | |
---|
651 | blue_calf_lon = [148.3944] |
---|
652 | blue_calf_lat = [-36.3869] |
---|
653 | map.plot(blue_calf_lon,blue_calf_lat, 'gs') |
---|
654 | return |
---|
655 | |
---|
656 | def time_string_to_datetime(times): |
---|
657 | ''' |
---|
658 | This function takes as input a numpy array of numpy string-arrays and |
---|
659 | returns a list of corresponding datetime objects. |
---|
660 | The array will be typically generated by a pynio get_value() call for |
---|
661 | the 'Times' variable of a wrfout file. |
---|
662 | |
---|
663 | Usage: |
---|
664 | >>> import wrf.utils as wu |
---|
665 | >>> import viz.utils as vu |
---|
666 | >>> f,fv = vu.peek('some_wrfout_file' + '.nc', return_pointers=True) |
---|
667 | >>> times = fv['Times'].get_value() |
---|
668 | >>> times = wu.time_string_to_datetime(times) |
---|
669 | ''' |
---|
670 | from datetime import datetime |
---|
671 | # VB we know that using pynio to access the 'Times' variable in a wrfout |
---|
672 | # files we are returned a numpy array of string arrays hence |
---|
673 | result = [] |
---|
674 | for time in times: |
---|
675 | time_string = time.tostring() |
---|
676 | year = int(time_string[:4]) |
---|
677 | month = int(time_string[5:7]) |
---|
678 | day = int(time_string[8:10]) |
---|
679 | hour = int(time_string[11:13]) |
---|
680 | minute = int(time_string[14:16]) |
---|
681 | second = int(time_string[17:]) |
---|
682 | # VB We assume the time is UTC so we will not bother |
---|
683 | # specifying a time zone |
---|
684 | result.append(datetime(year,month,day,hour,minute,second)) |
---|
685 | return result |
---|
686 | |
---|