[183] | 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 | |
---|