| 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 | |
|---|