Browse Source

Bug fixes for demo

main demo_4_21_2016
Bill Ladwig 9 years ago
parent
commit
27f7201841
  1. 214
      wrf_open/var/src/python/wrf/var/__init__.py
  2. 2
      wrf_open/var/src/python/wrf/var/decorators.py
  3. 3
      wrf_open/var/src/python/wrf/var/interp.py
  4. 209
      wrf_open/var/src/python/wrf/var/latlon.py
  5. 227
      wrf_open/var/src/python/wrf/var/latlonutils.py
  6. 57
      wrf_open/var/src/python/wrf/var/metadecorators.py
  7. 38
      wrf_open/var/src/python/wrf/var/projection.py
  8. 166
      wrf_open/var/src/python/wrf/var/routines.py
  9. 316
      wrf_open/var/src/python/wrf/var/util.py
  10. 22
      wrf_open/var/src/python/wrf/var/uvmet.py
  11. 2
      wrf_open/var/test/utests.py

214
wrf_open/var/src/python/wrf/var/__init__.py

@ -5,228 +5,24 @@ import warnings
from . import config from . import config
from .config import * from .config import *
from . import extension from . import routines
from .extension import * from .routines import *
from . import util from . import util
from .util import * from .util import *
from . import cape
from .cape import *
from . import constants
from .constants import *
from . import ctt
from .ctt import *
from . import dbz
from .dbz import *
from . import destag
from .destag import *
from . import dewpoint
from .dewpoint import *
from . import geoht
from .geoht import *
from . import helicity
from .helicity import *
from . import interp from . import interp
from .interp import * from .interp import *
from . import latlon
from .latlon import *
from . import omega
from .omega import *
from . import precip
from .precip import *
from . import pressure
from .pressure import *
from . import psadlookup
from .psadlookup import *
from . import pw
from .pw import *
from . import rh
from .rh import *
from . import slp
from .slp import *
from . import temp
from .temp import *
from . import terrain
from .terrain import *
from . import uvmet
from .uvmet import *
from . import vorticity
from .vorticity import *
from . import wind
from .wind import *
from . import times
from .times import *
from . import units
from .units import *
from . import projection from . import projection
from .projection import * from .projection import *
__all__ = ["getvar"] __all__ = []
__all__.extend(routines.__all__)
__all__.extend(interp.__all__)
__all__.extend(config.__all__) __all__.extend(config.__all__)
__all__.extend( extension.__all__)
__all__.extend(util.__all__) __all__.extend(util.__all__)
__all__.extend(cape.__all__)
__all__.extend(constants.__all__)
__all__.extend(ctt.__all__)
__all__.extend(dbz.__all__)
__all__.extend(destag.__all__)
__all__.extend(dewpoint.__all__)
__all__.extend(geoht.__all__)
__all__.extend(helicity.__all__)
__all__.extend(interp.__all__)
__all__.extend(latlon.__all__)
__all__.extend(omega.__all__)
__all__.extend(precip.__all__)
__all__.extend(psadlookup.__all__)
__all__.extend(pw.__all__)
__all__.extend(rh.__all__)
__all__.extend(slp.__all__)
__all__.extend(temp.__all__)
__all__.extend(terrain.__all__)
__all__.extend(uvmet.__all__)
__all__.extend(vorticity.__all__)
__all__.extend(wind.__all__)
__all__.extend(times.__all__)
__all__.extend(pressure.__all__)
__all__.extend(units.__all__)
__all__.extend(projection.__all__) __all__.extend(projection.__all__)
# func is the function to call. kargs are required arguments that should
# not be altered by the user
_FUNC_MAP = {"cape2d" : get_2dcape,
"cape3d" : get_3dcape,
"dbz" : get_dbz,
"maxdbz" : get_max_dbz,
"dp" : get_dp,
"dp2m" : get_dp_2m,
"height" : get_height,
"geopt" : get_geopt,
"srh" : get_srh,
"uhel" : get_uh,
"omega" : get_omega,
"pw" : get_pw,
"rh" : get_rh,
"rh2m" : get_rh_2m,
"slp" : get_slp,
"theta" : get_theta,
"temp" : get_temp,
"tk" : get_tk,
"tc" : get_tc,
"theta_e" : get_eth,
"tv" : get_tv,
"twb" : get_tw,
"terrain" : get_terrain,
"times" : get_times,
"uvmet" : get_uvmet,
"uvmet10" : get_uvmet10,
"avo" : get_avo,
"pvo" : get_pvo,
"ua" : get_u_destag,
"va" : get_v_destag,
"wa" : get_w_destag,
"lat" : get_lat,
"lon" : get_lon,
"pressure" : get_pressure_hpa,
"pres" : get_pressure,
"wspd_wdir" : get_destag_wspd_wdir,
"wspd_wdir10" : get_destag_wspd_wdir10,
"wspd_wdir_uvmet" : get_uvmet_wspd_wdir,
"wspd_wdir_uvmet10" : get_uvmet10_wspd_wdir,
"ctt" : get_ctt
}
_VALID_KARGS = {"cape2d" : ["missing"],
"cape3d" : ["missing"],
"dbz" : ["do_variant", "do_liqskin"],
"maxdbz" : ["do_variant", "do_liqskin"],
"dp" : ["units"],
"dp2m" : ["units"],
"height" : ["msl", "units"],
"geopt" : [],
"srh" : ["top"],
"uhel" : ["bottom", "top"],
"omega" : [],
"pw" : [],
"rh" : [],
"rh2m" : [],
"slp" : ["units"],
"temp" : ["units"],
"tk" : [],
"tc" : [],
"theta" : ["units"],
"theta_e" : ["units"],
"tv" : ["units"],
"twb" : ["units"],
"terrain" : ["units"],
"times" : [],
"uvmet" : ["units"],
"uvmet10" : ["units"],
"avo" : [],
"pvo" : [],
"ua" : ["units"],
"va" : ["units"],
"wa" : ["units"],
"lat" : [],
"lon" : [],
"pres" : ["units"],
"pressure" : ["units"],
"wspddir" : ["units"],
"wspddir_uvmet" : ["units"],
"wspddir_uvmet10" : ["units"],
"ctt" : [],
"default" : []
}
_ALIASES = {"cape_2d" : "cape2d",
"cape_3d" : "cape3d",
"eth" : "theta_e",
"mdbz" : "maxdbz",
"geopotential" : "geopt",
"helicity" : "srh",
"latitude" : "lat",
"longitude" : "lon",
"omg" : "omega",
"p" : "pres",
"rh2" : "rh2m",
"z": "height",
"ter" : "terrain",
"updraft_helicity" : "uhel",
"td" : "dp",
"td2" : "dp2m"
}
class ArgumentError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
def _undo_alias(alias):
actual = _ALIASES.get(alias, None)
if actual is None:
return alias
else:
return actual
def _check_kargs(var, kargs):
for arg in kargs.iterkeys():
if arg not in _VALID_KARGS[var]:
raise ArgumentError("'%s' is an invalid keyword "
"argument for '%s'" % (arg, var))
def getvar(wrfnc, var, timeidx=0,
method="cat", squeeze=True, cache=None,
**kargs):
if is_standard_wrf_var(wrfnc, var):
return extract_vars(wrfnc, timeidx, var, method, squeeze, cache)[var]
actual_var = _undo_alias(var)
if actual_var not in _VALID_KARGS:
raise ArgumentError("'%s' is not a valid variable name" % (var))
_check_kargs(actual_var, kargs)
return _FUNC_MAP[actual_var](wrfnc,timeidx,**kargs)

2
wrf_open/var/src/python/wrf/var/decorators.py

@ -93,7 +93,7 @@ def handle_left_iter(ref_var_expected_dims, ref_var_idx=-1,
# Make the left indexes plus a single slice object # Make the left indexes plus a single slice object
# The single slice will handle all the dimensions to # The single slice will handle all the dimensions to
# the right (e.g. [1,1,:]) # the right (e.g. [1,1,:])
left_and_slice_idxs = tuple([x for x in left_idxs] + [slice(None)]) left_and_slice_idxs = left_idxs + (slice(None), )
# Slice the args if applicable # Slice the args if applicable
new_args = [arg[left_and_slice_idxs] new_args = [arg[left_and_slice_idxs]

3
wrf_open/var/src/python/wrf/var/interp.py

@ -7,8 +7,7 @@ import numpy.ma as ma
from .extension import (interpz3d, interp2dxy, interp1d, from .extension import (interpz3d, interp2dxy, interp1d,
smooth2d, monotonic, vintrp, computevertcross, smooth2d, monotonic, vintrp, computevertcross,
computeinterpline) computeinterpline)
from .decorators import (handle_left_iter, handle_casting,
handle_extract_transpose)
from .metadecorators import set_interp_metadata from .metadecorators import set_interp_metadata
from .util import extract_vars, is_staggered from .util import extract_vars, is_staggered
from .interputils import get_xy, get_xy_z_params from .interputils import get_xy, get_xy_z_params

209
wrf_open/var/src/python/wrf/var/latlon.py

@ -1,42 +1,13 @@
from __future__ import (absolute_import, division, print_function, from __future__ import (absolute_import, division, print_function,
unicode_literals) unicode_literals)
from collections import Iterable from .util import (extract_vars)
from .latlonutils import (_lat_varname, _lon_varname, ll_to_ij, ij_to_ll)
import numpy as np
from .config import xarray_enabled
from .constants import Constants
from .extension import computeij, computell
from .util import (extract_vars, extract_global_attrs,
either, _is_moving_domain, _is_multi_time_req,
iter_left_indexes)
from .metadecorators import set_latlon_metadata from .metadecorators import set_latlon_metadata
if xarray_enabled():
from xarray import DataArray
__all__ = ["get_lat", "get_lon", "get_ij", "get_ll"] __all__ = ["get_lat", "get_lon", "get_ij", "get_ll"]
def _lat_varname(wrfnc, stagger):
if stagger is None or stagger.lower() == "m":
varname = either("XLAT", "XLAT_M")(wrfnc)
elif stagger.lower() == "u" or stagger.lower() == "v":
varname = "XLAT_{}".format(stagger.upper())
else:
raise ValueError("invalid 'stagger' value")
return varname
def _lon_varname(wrfnc, stagger):
if stagger is None or stagger.lower() == "m":
varname = either("XLONG", "XLONG_M")(wrfnc)
elif stagger.lower() == "u" or stagger.lower() == "v":
varname = "XLONG_{}".format(stagger.upper())
else:
raise ValueError("invalid 'stagger' value")
return varname
def get_lat(wrfnc, timeidx=0, stagger=None, def get_lat(wrfnc, timeidx=0, stagger=None,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
@ -47,6 +18,7 @@ def get_lat(wrfnc, timeidx=0, stagger=None,
return lat_var[varname] return lat_var[varname]
def get_lon(wrfnc, timeidx=0, stagger=None, def get_lon(wrfnc, timeidx=0, stagger=None,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
@ -56,182 +28,19 @@ def get_lon(wrfnc, timeidx=0, stagger=None,
return lon_var[varname] return lon_var[varname]
def _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache):
if timeidx < 0:
raise ValueError("'timeidx' must be greater than 0")
attrs = extract_global_attrs(wrfnc, attrs=("MAP_PROJ", "TRUELAT1",
"TRUELAT2", "STAND_LON",
"DX", "DY"))
map_proj = attrs["MAP_PROJ"]
truelat1 = attrs["TRUELAT1"]
truelat2 = attrs["TRUELAT2"]
stdlon = attrs["STAND_LON"]
dx = attrs["DX"]
dy = attrs["DY"]
if map_proj == 6:
pole_attrs = extract_global_attrs(wrfnc, attrs=("POLE_LAT",
"POLE_LON"))
pole_lat = pole_attrs["POLE_LAT"]
pole_lon = pole_attrs["POLE_LON"]
latinc = (dy*360.0)/2.0 / Constants.PI/Constants.WRF_EARTH_RADIUS
loninc = (dx*360.0)/2.0 / Constants.PI/Constants.WRF_EARTH_RADIUS
else:
pole_lat = 90.0
pole_lon = 0.0
latinc = 0.0
loninc = 0.0
latvar = _lat_varname(stagger)
lonvar = _lon_varname(stagger)
lat_timeidx = timeidx
# Only need all the lats/lons if it's a moving domain file/files
if _is_multi_time_req(timeidx):
if not _is_moving_domain(wrfnc, latvar=latvar, lonvar=lonvar):
lat_timeidx = 0
xlat = get_lat(wrfnc, lat_timeidx, stagger, method, squeeze, cache)
xlon = get_lon(wrfnc, lat_timeidx, stagger, method, squeeze, cache)
ref_lat = np.ravel(xlat[...,0,0])
ref_lon = np.ravel(xlon[...,0,0])
# Note: fortran index
known_i = 1.0
known_j = 1.0
return (map_proj,truelat1,truelat2,stdlon,ref_lat,ref_lon,
pole_lat,pole_lon,known_i,known_j,dx,latinc,
loninc)
@set_latlon_metadata(ij=True) @set_latlon_metadata(ij=True)
def get_ij(wrfnc, latitude, longitude, timeidx=0, def get_ij(wrfnc, latitude, longitude, timeidx=0,
stagger=None, method="cat", squeeze=True, cache=None): stagger=None, method="cat", squeeze=True, cache=None):
return ll_to_ij(wrfnc, latitude, longitude, timeidx, stagger,
(map_proj,truelat1,truelat2,stdlon,ref_lat,ref_lon, method, squeeze, cache)
pole_lat,pole_lon,known_i,known_j,dx,latinc,
loninc) = _get_proj_params(wrfnc, timeidx)
if isinstance(wrfnc, Iterable) and not isinstance(wrfnc, str):
lats = np.asarray(latitude)
lons = np.asarray(longitude)
if lats.ndim > 1:
lats = lats.ravel()
if lons.ndim > 1:
lons = lons.ravel()
if (lats.size != lons.size):
raise ValueError("'latitude' and 'longitude' "
"must be the same length")
if ref_lat.size == 1:
outdim = [lats.size, 2]
extra_dims = outdim[0]
else:
# Moving domain will have moving ref_lats/ref_lons
outdim = [lats.size, ref_lat.size, 2]
extra_dims = outdim[0:2]
res = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + [slice(None, None, None)]
if ref_lat.size == 1:
ref_lat_val = ref_lat[0]
ref_lon_val = ref_lon[0]
else:
ref_lat_val = ref_lat[left_idxs[-1]]
ref_lon_val = ref_lon[left_idxs[-1]]
lat = lats[left_idxs[0]]
lon = lons[left_idxs[0]]
ij = computeij(map_proj, truelat1, truelat2, stdlon,
ref_lat_val, ref_lon_val, pole_lat, pole_lon,
known_i, known_j, dx, latinc, loninc,
lat, lon)
res[left_and_slice_idxs] = ij[:]
else:
res = computeij(map_proj, truelat1, truelat2, stdlon,
ref_lat, ref_lon, pole_lat, pole_lon,
known_i, known_j, dx, latinc, loninc,
latitude, longitude)
return res
@set_latlon_metadata(ij=False) @set_latlon_metadata(ij=False)
def get_ll(wrfnc, i, j, timeidx=0): def get_ll(wrfnc, i, j, timeidx=0,
if isinstance(wrfnc, Iterable) and not isinstance(wrfnc, str): stagger=None, method="cat", squeeze=True, cache=None):
raise TypeError("'get_ll' is only applicabe for single files") return ij_to_ll(wrfnc, i, j, timeidx, stagger,
method, squeeze, cache)
(map_proj,truelat1,truelat2,stdlon,ref_lat,ref_lon,
pole_lat,pole_lon,known_i,known_j,dx,latinc,
loninc) = _get_proj_params(wrfnc, timeidx)
if isinstance(wrfnc, Iterable) and not isinstance(wrfnc, str):
i_arr = np.asarray(i)
j_arr = np.asarray(j)
if i_arr.ndim > 1:
i_arr = i_arr.ravel()
if j_arr.ndim > 1:
j_arr = j_arr.ravel()
if (i_arr.size != j_arr.size):
raise ValueError("'i' and 'j' "
"must be the same length")
if ref_lat.size == 1:
outdim = [i_arr.size, 2]
extra_dims = outdim[0]
else:
# Moving domain will have moving ref_lats/ref_lons
outdim = [i_arr.size, ref_lat.size, 2]
extra_dims = outdim[0:2]
res = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + [slice(None, None, None)]
if ref_lat.size == 1:
ref_lat_val = ref_lat[0]
ref_lon_val = ref_lon[0]
else:
ref_lat_val = ref_lat[left_idxs[-1]]
ref_lon_val = ref_lon[left_idxs[-1]]
i_val = i_arr[left_idxs[0]]
j_val = j_arr[left_idxs[0]]
ll = computell(map_proj, truelat1, truelat2,
stdlon, ref_lat_val, ref_lon_val,
pole_lat, pole_lon, known_i, known_j,
dx, latinc, loninc,
i_val, j_val)
res[left_and_slice_idxs] = ll[:]
else:
res = computell(map_proj, truelat1, truelat2,
stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_i, known_j,
dx, latinc, loninc,
i_val, j_val)
return res

227
wrf_open/var/src/python/wrf/var/latlonutils.py

@ -0,0 +1,227 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from collections import Iterable
import numpy as np
from .constants import Constants
from .extension import computeij, computell
from .util import (extract_vars, extract_global_attrs,
either, _is_moving_domain, _is_multi_time_req,
iter_left_indexes, _is_mapping, _is_multi_file,
viewkeys)
def _lat_varname(wrfnc, stagger):
if stagger is None or stagger.lower() == "m":
varname = either("XLAT", "XLAT_M")(wrfnc)
elif stagger.lower() == "u" or stagger.lower() == "v":
varname = "XLAT_{}".format(stagger.upper())
else:
raise ValueError("invalid 'stagger' value")
return varname
def _lon_varname(wrfnc, stagger):
if stagger is None or stagger.lower() == "m":
varname = either("XLONG", "XLONG_M")(wrfnc)
elif stagger.lower() == "u" or stagger.lower() == "v":
varname = "XLONG_{}".format(stagger.upper())
else:
raise ValueError("invalid 'stagger' value")
return varname
def _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache):
if timeidx < 0:
raise ValueError("'timeidx' must be greater than 0")
attrs = extract_global_attrs(wrfnc, attrs=("MAP_PROJ", "TRUELAT1",
"TRUELAT2", "STAND_LON",
"DX", "DY"))
map_proj = attrs["MAP_PROJ"]
truelat1 = attrs["TRUELAT1"]
truelat2 = attrs["TRUELAT2"]
stdlon = attrs["STAND_LON"]
dx = attrs["DX"]
dy = attrs["DY"]
if map_proj == 6:
pole_attrs = extract_global_attrs(wrfnc, attrs=("POLE_LAT",
"POLE_LON"))
pole_lat = pole_attrs["POLE_LAT"]
pole_lon = pole_attrs["POLE_LON"]
latinc = (dy*360.0)/2.0 / Constants.PI/Constants.WRF_EARTH_RADIUS
loninc = (dx*360.0)/2.0 / Constants.PI/Constants.WRF_EARTH_RADIUS
else:
pole_lat = 90.0
pole_lon = 0.0
latinc = 0.0
loninc = 0.0
latvar = _lat_varname(wrfnc, stagger)
lonvar = _lon_varname(wrfnc, stagger)
lat_timeidx = timeidx
is_moving = _is_moving_domain(wrfnc, latvar=latvar, lonvar=lonvar)
# Only need one file and one time if the domain is not moving
if not is_moving:
if _is_multi_time_req(timeidx):
lat_timeidx = 0
if _is_multi_file(wrfnc):
if not _is_mapping(wrfnc):
wrfnc = next(iter(wrfnc)) # only need one file
else:
wrfnc = wrfnc[next(iter(viewkeys(wrfnc)))]
return _get_proj_params(wrfnc, timeidx, stagger,
method, squeeze, cache)
xlat = extract_vars(wrfnc, lat_timeidx, (latvar,), method, squeeze, cache,
nometa=True)[latvar]
xlon = extract_vars(wrfnc, lat_timeidx, (lonvar,), method, squeeze, cache,
nometa=True)[lonvar]
ref_lat = np.ravel(xlat[...,0,0])
ref_lon = np.ravel(xlon[...,0,0])
# Note: fortran index
known_i = 1.0
known_j = 1.0
return (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_i, known_j, dx, latinc, loninc)
def ll_to_ij(wrfnc, latitude, longitude, timeidx=0,
stagger=None, method="cat", squeeze=True, cache=None):
(map_proj,truelat1,truelat2,stdlon,ref_lat,ref_lon,
pole_lat,pole_lon,known_i,known_j,dx,latinc,
loninc) = _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache)
if isinstance(latitude, Iterable):
lats = np.asarray(latitude)
lons = np.asarray(longitude)
if lats.ndim > 1:
lats = lats.ravel()
if lons.ndim > 1:
lons = lons.ravel()
if (lats.size != lons.size):
raise ValueError("'latitude' and 'longitude' "
"must be the same length")
if ref_lat.size == 1:
outdim = [lats.size, 2]
extra_dims = [outdim[0]]
else:
# Moving domain will have moving ref_lats/ref_lons
outdim = [lats.size, ref_lat.size, 2]
extra_dims = outdim[0:2]
res = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + (slice(None), )
if ref_lat.size == 1:
ref_lat_val = ref_lat[0]
ref_lon_val = ref_lon[0]
else:
ref_lat_val = ref_lat[left_idxs[-1]]
ref_lon_val = ref_lon[left_idxs[-1]]
lat = lats[left_idxs[0]]
lon = lons[left_idxs[0]]
ij = computeij(map_proj, truelat1, truelat2, stdlon,
ref_lat_val, ref_lon_val, pole_lat, pole_lon,
known_i, known_j, dx, latinc, loninc,
lat, lon)
res[left_and_slice_idxs] = ij[:]
else:
res = computeij(map_proj, truelat1, truelat2, stdlon,
ref_lat, ref_lon, pole_lat, pole_lon,
known_i, known_j, dx, latinc, loninc,
latitude, longitude)
if squeeze:
res = res.squeeze()
return res
def ij_to_ll(wrfnc, i, j, timeidx=0,
stagger=None, method="cat", squeeze=True, cache=None):
(map_proj,truelat1,truelat2,stdlon,ref_lat,ref_lon,
pole_lat,pole_lon,known_i,known_j,dx,latinc,
loninc) = _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache)
if isinstance(i, Iterable):
i_arr = np.asarray(i)
j_arr = np.asarray(j)
if i_arr.ndim > 1:
i_arr = i_arr.ravel()
if j_arr.ndim > 1:
j_arr = j_arr.ravel()
if (i_arr.size != j_arr.size):
raise ValueError("'i' and 'j' "
"must be the same length")
if ref_lat.size == 1:
outdim = [i_arr.size, 2]
extra_dims = [outdim[0]]
else:
# Moving domain will have moving ref_lats/ref_lons
outdim = [i_arr.size, ref_lat.size, 2]
extra_dims = outdim[0:2]
res = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + (slice(None), )
if ref_lat.size == 1:
ref_lat_val = ref_lat[0]
ref_lon_val = ref_lon[0]
else:
ref_lat_val = ref_lat[left_idxs[-1]]
ref_lon_val = ref_lon[left_idxs[-1]]
i_val = i_arr[left_idxs[0]]
j_val = j_arr[left_idxs[0]]
ll = computell(map_proj, truelat1, truelat2,
stdlon, ref_lat_val, ref_lon_val,
pole_lat, pole_lon, known_i, known_j,
dx, latinc, loninc,
i_val, j_val)
res[left_and_slice_idxs] = ll[:]
else:
i_val = i
j_val = j
res = computell(map_proj, truelat1, truelat2,
stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_i, known_j,
dx, latinc, loninc,
i_val, j_val)
if squeeze:
res = res.squeeze()
return res

57
wrf_open/var/src/python/wrf/var/metadecorators.py

@ -9,8 +9,9 @@ import numpy.ma as ma
from .util import (viewkeys, viewitems, extract_vars, from .util import (viewkeys, viewitems, extract_vars,
combine_with, either, from_args, arg_location, combine_with, either, from_args, arg_location,
_is_coord_var, XYCoord, npvalues) _is_coord_var, CoordPair, npvalues)
from .interputils import get_xy_z_params, get_xy from .interputils import get_xy_z_params, get_xy
from .latlonutils import ij_to_ll, ll_to_ij
from .config import xarray_enabled from .config import xarray_enabled
if xarray_enabled(): if xarray_enabled():
@ -211,34 +212,52 @@ def set_wind_metadata(copy_varname, name, description,
def set_latlon_metadata(ij=False): def set_latlon_metadata(ij=False):
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
if not xarray_enabled(): if not xarray_enabled():
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
res = wrapped(*args, **kwargs) res = wrapped(*args, **kwargs)
argnames = ("latitude", "longitude") if not ij else ("i", "j") # Want to preserve the input coordinate pair in metadata
if res.ndim == 1:
res = res[np.newaxis, :]
argnames = ["i", "j"] if not ij else ["latitude", "longitude"]
argnames.append("squeeze")
outname = "latlon" if not ij else "ij" outname = "latlon" if not ij else "ij"
if res.ndim <= 2: if res.ndim == 2:
dimnames = (["lat_lon", "i_j"] if not ij dimnames = (["ij", "lat_lon"] if not ij
else ["i_j", "lat_lon"]) else ["latlon", "i_j"])
else: else:
dimnames = (["lat_lon", "domain", "i_j"] if not ij dimnames = (["ij", "domain", "lat_lon"] if not ij
else ["i_j", "domain", "lat_lon"]) else ["latlon", "domain", "i_j"])
argvars = from_args(wrapped, argnames, *args, **kwargs) argvars = from_args(wrapped, argnames, *args, **kwargs)
var1 = argvars[argnames[0]] var1 = argvars[argnames[0]]
var2 = argvars[argnames[1]] var2 = argvars[argnames[1]]
squeeze = argvars["squeeze"]
arr1 = np.asarray(var1).ravel() arr1 = np.asarray(var1).ravel()
arr2 = np.asarray(var2).ravel() arr2 = np.asarray(var2).ravel()
coords = {} coords = {}
coords[dimnames[0]] = [x for x in zip(arr1, arr2)] if not ij:
coords["coord_pair"] = (dimnames[0], [CoordPair(i=x[0], j=x[1])
for x in zip(arr1, arr2)])
coords[dimnames[-1]] = ["lat", "lon"]
else:
coords["coord_pair"] = (dimnames[0], [CoordPair(lat=x[0], lon=x[1])
for x in zip(arr1, arr2)])
coords[dimnames[-1]] = ["i", "j"]
return DataArray(res, name=outname, dims=dimnames, coords=coords) da = DataArray(res, name=outname, dims=dimnames, coords=coords)
if squeeze:
da = da.squeeze()
return da
return func_wrapper return func_wrapper
@ -329,6 +348,10 @@ def _set_horiz_meta(wrapped, instance, args, kwargs):
if vert_units is not None if vert_units is not None
else "{0}".format(desiredloc)) else "{0}".format(desiredloc))
name_levelstr = ("{0}_{1}".format(desiredloc, vert_units)
if vert_units is not None
else "{0}".format(desiredloc))
if isinstance(field3d, DataArray): if isinstance(field3d, DataArray):
outcoords = OrderedDict() outcoords = OrderedDict()
outattrs = OrderedDict() outattrs = OrderedDict()
@ -337,7 +360,7 @@ def _set_horiz_meta(wrapped, instance, args, kwargs):
outdimnames.remove(field3d.dims[-3]) outdimnames.remove(field3d.dims[-3])
del outcoords[field3d.dims[-3]] del outcoords[field3d.dims[-3]]
outattrs.update(field3d.attrs) outattrs.update(field3d.attrs)
outname = "{0}_{1}".format(field3d.name, levelstr) outname = "{0}_{1}".format(field3d.name, name_levelstr)
else: else:
outname = "field3d_{0}".format(levelstr) outname = "field3d_{0}".format(levelstr)
@ -436,8 +459,8 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
except KeyError: except KeyError:
pass pass
outcoords["xy"] = [XYCoord(xy[i,0], xy[i,1]) outcoords["xy_loc"] = ("xy", [CoordPair(xy[i,0], xy[i,1])
for i in xrange(xy.shape[-2])] for i in xrange(xy.shape[-2])])
outcoords["vertical"] = z_var2d[:] outcoords["vertical"] = z_var2d[:]
@ -525,8 +548,8 @@ def _set_line_meta(wrapped, instance, args, kwargs):
except KeyError: except KeyError:
pass pass
outcoords["xy"] = [XYCoord(xy[i,0], xy[i,1]) outcoords["xy_loc"] = ("xy", [CoordPair(xy[i,0], xy[i,1])
for i in xrange(xy.shape[-2])] for i in xrange(xy.shape[-2])])
else: else:
outname = "field2d_line" outname = "field2d_line"
@ -617,8 +640,8 @@ def _set_2dxy_meta(wrapped, instance, args, kwargs):
outname = "{0}_xy".format(field3d.name) outname = "{0}_xy".format(field3d.name)
outcoords["xy"] = [XYCoord(xy[i,0], xy[i,1]) outcoords["xy_loc"] = ("xy", [CoordPair(xy[i,0], xy[i,1])
for i in xrange(xy.shape[-2])] for i in xrange(xy.shape[-2])])
for key in ("MemoryOrder",): for key in ("MemoryOrder",):
try: try:

38
wrf_open/var/src/python/wrf/var/projection.py

@ -15,13 +15,13 @@ if basemap_enabled():
if pyngl_enabled(): if pyngl_enabled():
from Ngl import Resources from Ngl import Resources
__all__ = ["WrfProj", "LambertConformalProj", "MercatorProj", __all__ = ["WrfProj", "LambertConformal", "Mercator",
"PolarStereographicProj", "LatLonProj", "RotLatLonProj", "PolarStereographic", "LatLon", "RotatedLatLon",
"getproj"] "getproj"]
if cartopy_enabled(): if cartopy_enabled():
class MercatorWithLatTsProj(crs.Mercator): class MercatorWithLatTS(crs.Mercator):
def __init__(self, central_longitude=0.0, def __init__(self, central_longitude=0.0,
latitude_true_scale=0.0, latitude_true_scale=0.0,
min_latitude=-80.0, min_latitude=-80.0,
@ -181,10 +181,10 @@ class WrfProj(object):
return self._cf_params return self._cf_params
class LambertConformalProj(WrfProj): class LambertConformal(WrfProj):
def __init__(self, bottom_left=None, top_right=None, def __init__(self, bottom_left=None, top_right=None,
lats=None, lons=None, **proj_params): lats=None, lons=None, **proj_params):
super(LambertConformalProj, self).__init__(bottom_left, super(LambertConformal, self).__init__(bottom_left,
top_right, lats, lons, **proj_params) top_right, lats, lons, **proj_params)
self._std_parallels = [self.truelat1] self._std_parallels = [self.truelat1]
@ -287,10 +287,10 @@ class LambertConformalProj(WrfProj):
self.stand_lon)) self.stand_lon))
return _proj4 return _proj4
class MercatorProj(WrfProj): class Mercator(WrfProj):
def __init__(self, bottom_left=None, top_right=None, def __init__(self, bottom_left=None, top_right=None,
lats=None, lons=None, **proj_params): lats=None, lons=None, **proj_params):
super(MercatorProj, self).__init__(bottom_left, top_right, super(Mercator, self).__init__(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
self._lat_ts = (None self._lat_ts = (None
@ -354,7 +354,7 @@ class MercatorProj(WrfProj):
globe = self._globe) globe = self._globe)
else: else:
_cartopy = MercatorWithLatTsProj( _cartopy = MercatorWithLatTS(
central_longitude = self.stand_lon, central_longitude = self.stand_lon,
latitude_true_scale = self._lat_ts, latitude_true_scale = self._lat_ts,
globe = self._globe) globe = self._globe)
@ -387,10 +387,10 @@ class MercatorProj(WrfProj):
return _proj4 return _proj4
class PolarStereographicProj(WrfProj): class PolarStereographic(WrfProj):
def __init__(self, bottom_left=None, top_right=None, def __init__(self, bottom_left=None, top_right=None,
lats=None, lons=None, **proj_params): lats=None, lons=None, **proj_params):
super(PolarStereographicProj, self).__init__(bottom_left, super(PolarStereographic, self).__init__(bottom_left,
top_right, lats, lons, **proj_params) top_right, lats, lons, **proj_params)
self._hemi = -90. if self.truelat1 < 0 else 90. self._hemi = -90. if self.truelat1 < 0 else 90.
self._lat_ts = (None self._lat_ts = (None
@ -486,10 +486,10 @@ class PolarStereographicProj(WrfProj):
class LatLonProj(WrfProj): class LatLon(WrfProj):
def __init__(self, bottom_left=None, top_right=None, def __init__(self, bottom_left=None, top_right=None,
lats=None, lons=None, **proj_params): lats=None, lons=None, **proj_params):
super(LatLonProj, self).__init__(bottom_left, top_right, super(LatLon, self).__init__(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
@property @property
@ -588,10 +588,10 @@ class LatLonProj(WrfProj):
# this reason, the proj4 string for this class will use cartopy's values # this reason, the proj4 string for this class will use cartopy's values
# to keep things in the -180 to 180, -90 to 90 range. # to keep things in the -180 to 180, -90 to 90 range.
# 12) This projection makes me sad. # 12) This projection makes me sad.
class RotLatLonProj(WrfProj): class RotatedLatLon(WrfProj):
def __init__(self, bottom_left=None, top_right=None, def __init__(self, bottom_left=None, top_right=None,
lats=None, lons=None, **proj_params): lats=None, lons=None, **proj_params):
super(RotLatLonProj, self).__init__(bottom_left, top_right, super(RotatedLatLon, self).__init__(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
# Need to determine hemisphere, typically pole_lon is 0 for southern # Need to determine hemisphere, typically pole_lon is 0 for southern
@ -726,21 +726,21 @@ def getproj(bottom_left=None, top_right=None,
proj_type = proj_params.get("MAP_PROJ", 0) proj_type = proj_params.get("MAP_PROJ", 0)
if proj_type == 1: if proj_type == 1:
return LambertConformalProj(bottom_left, top_right, return LambertConformal(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
elif proj_type == 2: elif proj_type == 2:
return PolarStereographicProj(bottom_left, top_right, return PolarStereographic(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
elif proj_type == 3: elif proj_type == 3:
return MercatorProj(bottom_left, top_right, return Mercator(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
elif proj_type == 0 or proj_type == 6: elif proj_type == 0 or proj_type == 6:
if (proj_params.get("POLE_LAT", None) == 90. if (proj_params.get("POLE_LAT", None) == 90.
and proj_params.get("POLE_LON", None) == 0.): and proj_params.get("POLE_LON", None) == 0.):
return LatLonProj(bottom_left, top_right, return LatLon(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
else: else:
return RotLatLonProj(bottom_left, top_right, return RotatedLatLon(bottom_left, top_right,
lats, lons, **proj_params) lats, lons, **proj_params)
else: else:
# Unknown projection # Unknown projection

166
wrf_open/var/src/python/wrf/var/routines.py

@ -0,0 +1,166 @@
from .util import _unpack_sequence, is_standard_wrf_var, extract_vars
from .cape import get_2dcape, get_3dcape
from .ctt import get_ctt
from .dbz import get_dbz, get_max_dbz
from .dewpoint import get_dp, get_dp_2m
from .geoht import get_geopt, get_height
from .helicity import get_srh, get_uh
from .latlon import get_lat, get_lon
from .omega import get_omega
from .pressure import get_pressure, get_pressure_hpa
from .pw import get_pw
from .rh import get_rh, get_rh_2m
from .slp import get_slp
from .temp import get_tc, get_eth, get_temp, get_theta, get_tk, get_tv, get_tw
from .terrain import get_terrain
from .uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir,
get_uvmet_wspd_wdir)
from .vorticity import get_avo, get_pvo
from .wind import (get_destag_wspd_wdir, get_destag_wspd_wdir10,
get_u_destag, get_v_destag, get_w_destag)
from .times import get_times
__all__ = ["getvar"]
# func is the function to call. kargs are required arguments that should
# not be altered by the user
_FUNC_MAP = {"cape2d" : get_2dcape,
"cape3d" : get_3dcape,
"dbz" : get_dbz,
"maxdbz" : get_max_dbz,
"dp" : get_dp,
"dp2m" : get_dp_2m,
"height" : get_height,
"geopt" : get_geopt,
"srh" : get_srh,
"uhel" : get_uh,
"omega" : get_omega,
"pw" : get_pw,
"rh" : get_rh,
"rh2m" : get_rh_2m,
"slp" : get_slp,
"theta" : get_theta,
"temp" : get_temp,
"tk" : get_tk,
"tc" : get_tc,
"theta_e" : get_eth,
"tv" : get_tv,
"twb" : get_tw,
"terrain" : get_terrain,
"times" : get_times,
"uvmet" : get_uvmet,
"uvmet10" : get_uvmet10,
"avo" : get_avo,
"pvo" : get_pvo,
"ua" : get_u_destag,
"va" : get_v_destag,
"wa" : get_w_destag,
"lat" : get_lat,
"lon" : get_lon,
"pressure" : get_pressure_hpa,
"pres" : get_pressure,
"wspd_wdir" : get_destag_wspd_wdir,
"wspd_wdir10" : get_destag_wspd_wdir10,
"wspd_wdir_uvmet" : get_uvmet_wspd_wdir,
"wspd_wdir_uvmet10" : get_uvmet10_wspd_wdir,
"ctt" : get_ctt
}
_VALID_KARGS = {"cape2d" : ["missing"],
"cape3d" : ["missing"],
"dbz" : ["do_variant", "do_liqskin"],
"maxdbz" : ["do_variant", "do_liqskin"],
"dp" : ["units"],
"dp2m" : ["units"],
"height" : ["msl", "units"],
"geopt" : [],
"srh" : ["top"],
"uhel" : ["bottom", "top"],
"omega" : [],
"pw" : [],
"rh" : [],
"rh2m" : [],
"slp" : ["units"],
"temp" : ["units"],
"tk" : [],
"tc" : [],
"theta" : ["units"],
"theta_e" : ["units"],
"tv" : ["units"],
"twb" : ["units"],
"terrain" : ["units"],
"times" : [],
"uvmet" : ["units"],
"uvmet10" : ["units"],
"avo" : [],
"pvo" : [],
"ua" : ["units"],
"va" : ["units"],
"wa" : ["units"],
"lat" : [],
"lon" : [],
"pres" : ["units"],
"pressure" : ["units"],
"wspddir" : ["units"],
"wspddir_uvmet" : ["units"],
"wspddir_uvmet10" : ["units"],
"ctt" : [],
"default" : []
}
_ALIASES = {"cape_2d" : "cape2d",
"cape_3d" : "cape3d",
"eth" : "theta_e",
"mdbz" : "maxdbz",
"geopotential" : "geopt",
"helicity" : "srh",
"latitude" : "lat",
"longitude" : "lon",
"omg" : "omega",
"p" : "pres",
"rh2" : "rh2m",
"z": "height",
"ter" : "terrain",
"updraft_helicity" : "uhel",
"td" : "dp",
"td2" : "dp2m"
}
class ArgumentError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
def _undo_alias(alias):
actual = _ALIASES.get(alias, None)
if actual is None:
return alias
else:
return actual
def _check_kargs(var, kargs):
for arg in kargs.iterkeys():
if arg not in _VALID_KARGS[var]:
raise ArgumentError("'%s' is an invalid keyword "
"argument for '%s'" % (arg, var))
def getvar(wrfnc, var, timeidx=0,
method="cat", squeeze=True, cache=None,
**kargs):
wrfnc = _unpack_sequence(wrfnc)
if is_standard_wrf_var(wrfnc, var):
return extract_vars(wrfnc, timeidx, var, method, squeeze, cache)[var]
actual_var = _undo_alias(var)
if actual_var not in _VALID_KARGS:
raise ArgumentError("'%s' is not a valid variable name" % (var))
_check_kargs(actual_var, kargs)
return _FUNC_MAP[actual_var](wrfnc,timeidx,**kargs)

316
wrf_open/var/src/python/wrf/var/util.py

@ -20,8 +20,8 @@ __all__ = ["extract_vars", "extract_global_attrs", "extract_dim",
"combine_files", "is_standard_wrf_var", "extract_times", "combine_files", "is_standard_wrf_var", "extract_times",
"iter_left_indexes", "get_left_indexes", "get_right_slices", "iter_left_indexes", "get_left_indexes", "get_right_slices",
"is_staggered", "get_proj_params", "viewitems", "viewkeys", "is_staggered", "get_proj_params", "viewitems", "viewkeys",
"viewvalues", "combine_with", "from_args", "arg_location", "viewvalues", "combine_with", "either", "from_args", "arg_location",
"args_to_list", "npvalues", "XYCoord"] "args_to_list", "npvalues", "CoordPair"]
_COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"), _COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"),
@ -39,9 +39,6 @@ _COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"),
_COORD_VARS = ("XLAT", "XLONG", "XLAT_M", "XLONG_M", "XLAT_U", "XLONG_U", _COORD_VARS = ("XLAT", "XLONG", "XLAT_M", "XLONG_M", "XLAT_U", "XLONG_U",
"XLAT_V", "XLONG_V", "CLAT", "CLONG") "XLAT_V", "XLONG_V", "CLAT", "CLONG")
def _is_coord_var(varname): def _is_coord_var(varname):
return varname in _COORD_VARS return varname in _COORD_VARS
@ -61,6 +58,123 @@ def _is_multi_file(wrfnc):
def _is_mapping(wrfnc): def _is_mapping(wrfnc):
return isinstance(wrfnc, Mapping) return isinstance(wrfnc, Mapping)
def _unpack_sequence(wrfseq):
"""Unpacks generators in to lists or dictionaries if applicable, otherwise
returns the original object.
This is apparently the easiest and
fastest way of being able to re-iterate through generators when used
more than once.
"""
if not _is_multi_file(wrfseq):
return wrfseq
else:
if not _is_mapping(wrfseq):
if isinstance(wrfseq, (list, tuple)):
return wrfseq
else:
return list(wrfseq) # generator/custom iterable class
else:
if isinstance(wrfseq, dict):
return wrfseq
else:
return dict(wrfseq) # generator/custom iterable class
# Dictionary python 2-3 compatibility stuff
def viewitems(d):
func = getattr(d, "viewitems", None)
if func is None:
func = d.items
return func()
def viewkeys(d):
func = getattr(d, "viewkeys", None)
if func is None:
func = d.keys
return func()
def viewvalues(d):
func = getattr(d, "viewvalues", None)
if func is None:
func = d.values
return func()
def isstr(s):
try:
return isinstance(s, basestring)
except NameError:
return isinstance(s, str)
# Helper to extract masked arrays from DataArrays that convert to NaN
def npvalues(da):
if not isinstance(da, DataArray):
result = da
else:
try:
fill_value = da.attrs["_FillValue"]
except KeyError:
result = da.values
else:
result = ma.masked_invalid(da.values, copy=False)
result.set_fill_value(fill_value)
return result
# Helper utilities for metadata
class either(object):
def __init__(self, *varnames):
self.varnames = varnames
def __call__(self, wrfnc):
if _is_multi_file(wrfnc):
if not _is_mapping(wrfnc):
wrfnc = next(iter(wrfnc))
else:
entry = wrfnc[next(iter(viewkeys(wrfnc)))]
return self(entry)
for varname in self.varnames:
if varname in wrfnc.variables:
return varname
raise ValueError("{} are not valid variable names".format(
self.varnames))
class combine_with:
# Remove remove_idx first, then insert_idx is applied to removed set
def __init__(self, varname, remove_dims=None, insert_before=None,
new_dimnames=None, new_coords=None):
self.varname = varname
self.remove_dims = remove_dims
self.insert_before = insert_before
self.new_dimnames = new_dimnames if new_dimnames is not None else []
self.new_coords = (new_coords if new_coords is not None
else OrderedDict())
def __call__(self, var):
new_dims = list(var.dims)
new_coords = OrderedDict(var.coords)
if self.remove_dims is not None:
for dim in self.remove_dims:
new_dims.remove(dim)
del new_coords[dim]
if self.insert_before is not None:
insert_idx = new_dims.index(self.insert_before)
new_dims = (new_dims[0:insert_idx] + self.new_dimnames +
new_dims[insert_idx:])
elif self.new_dimnames is not None:
new_dims = self.new_dimnames
if self.new_coords is not None:
new_coords.update(self.new_coords)
return new_dims, new_coords
def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar): def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar):
lats = wrfnc.variables[latvar] lats = wrfnc.variables[latvar]
@ -84,16 +198,27 @@ def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar):
return False return False
def _is_moving_domain(wrfseq, varname=None, latvar="XLAT", lonvar="XLONG"): def _is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
lonvar=either("XLONG", "XLONG_M")):
if isinstance(latvar, either):
latvar = latvar(wrfseq)
if isinstance(lonvar, either):
lonvar = lonvar(wrfseq)
# In case it's just a single file # In case it's just a single file
if not _is_multi_file(wrfseq): if not _is_multi_file(wrfseq):
wrfseq = [wrfseq] wrfseq = [wrfseq]
# Slow, but safe. Compare the corner points to the first item and see # Slow, but safe. Compare the corner points to the first item and see
# any move. User iterator protocol in case wrfseq is not a list/tuple. # any move. User iterator protocol in case wrfseq is not a list/tuple.
if not _is_mapping(wrfseq):
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
first_wrfnc = next(wrf_iter) first_wrfnc = next(wrf_iter)
else:
entry = wrfseq[next(iter(viewkeys(wrfseq)))]
return _is_moving_domain(entry, varname, latvar, lonvar)
if varname is not None: if varname is not None:
try: try:
@ -146,7 +271,7 @@ def _get_global_attr(wrfnc, attr):
val = getattr(wrfnc, attr, None) val = getattr(wrfnc, attr, None)
# PyNIO puts single values in to an array # PyNIO puts single values in to an array
if isinstance(val,np.ndarray): if isinstance(val, np.ndarray):
if len(val) == 1: if len(val) == 1:
return val[0] return val[0]
return val return val
@ -161,18 +286,20 @@ def extract_global_attrs(wrfnc, attrs):
if multifile: if multifile:
if not _is_mapping(wrfnc): if not _is_mapping(wrfnc):
wrfnc = wrfnc[0] wrfnc = next(iter(wrfnc))
else: else:
wrfnc = wrfnc[next(wrfnc.iterkeys())] entry = wrfnc[next(iter(viewkeys(wrfnc)))]
return extract_global_attrs(entry, attrs)
return {attr:_get_global_attr(wrfnc, attr) for attr in attrlist} return {attr:_get_global_attr(wrfnc, attr) for attr in attrlist}
def extract_dim(wrfnc, dim): def extract_dim(wrfnc, dim):
if _is_multi_file(wrfnc): if _is_multi_file(wrfnc):
if not _is_mapping(wrfnc): if not _is_mapping(wrfnc):
wrfnc = wrfnc[0] wrfnc = next(iter(wrfnc))
else: else:
wrfnc = wrfnc[next(wrfnc.iterkeys())] entry = wrfnc[next(iter(viewkeys(wrfnc)))]
return extract_dim(entry, dim)
d = wrfnc.dimensions[dim] d = wrfnc.dimensions[dim]
if not isinstance(d, int): if not isinstance(d, int):
@ -189,9 +316,11 @@ def _combine_dict(wrfdict, varname, timeidx, method, nometa):
first_key = next(key_iter) first_key = next(key_iter)
keynames.append(first_key) keynames.append(first_key)
is_moving = _is_moving_domain(wrfdict, varname)
first_array = _extract_var(wrfdict[first_key], varname, first_array = _extract_var(wrfdict[first_key], varname,
timeidx, method, squeeze=False, timeidx, is_moving=is_moving, method=method,
nometa=nometa) squeeze=False, cache=None, nometa=nometa)
# Create the output data numpy array based on the first array # Create the output data numpy array based on the first array
@ -209,12 +338,13 @@ def _combine_dict(wrfdict, varname, timeidx, method, nometa):
else: else:
keynames.append(key) keynames.append(key)
vardata = _extract_var(wrfdict[key], varname, timeidx, vardata = _extract_var(wrfdict[key], varname, timeidx,
method, squeeze=False) is_moving=is_moving, method=method,
squeeze=False, cache=None, nometa=nometa)
if outdata.shape[1:] != vardata.shape: if outdata.shape[1:] != vardata.shape:
raise ValueError("data sequences must have the " raise ValueError("data sequences must have the "
"same size for all dictionary keys") "same size for all dictionary keys")
outdata[idx,:] = vardata.values[:] outdata[idx,:] = npvalues(vardata)[:]
idx += 1 idx += 1
if xarray_enabled() and not nometa: if xarray_enabled() and not nometa:
@ -233,7 +363,9 @@ def _combine_dict(wrfdict, varname, timeidx, method, nometa):
return outarr return outarr
# TODO: implement in C # TODO: implement in C
def _cat_files(wrfseq, varname, timeidx, squeeze, nometa): # Note: is moving argument needed for dictionary combination
def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, nometa):
if is_moving is None:
is_moving = _is_moving_domain(wrfseq, varname) is_moving = _is_moving_domain(wrfseq, varname)
file_times = extract_times(wrfseq, timeidx) file_times = extract_times(wrfseq, timeidx)
@ -307,7 +439,8 @@ def _cat_files(wrfseq, varname, timeidx, squeeze, nometa):
return outarr return outarr
# TODO: implement in C # TODO: implement in C
def _join_files(wrfseq, varname, timeidx, nometa): def _join_files(wrfseq, varname, timeidx, is_moving, nometa):
if is_moving is None:
is_moving = _is_moving_domain(wrfseq, varname) is_moving = _is_moving_domain(wrfseq, varname)
multitime = _is_multi_time_req(timeidx) multitime = _is_multi_time_req(timeidx)
numfiles = len(wrfseq) numfiles = len(wrfseq)
@ -361,15 +494,20 @@ def _join_files(wrfseq, varname, timeidx, nometa):
return outarr return outarr
def combine_files(wrfseq, varname, timeidx, method="cat", squeeze=True, def combine_files(wrfseq, varname, timeidx, is_moving=None,
nometa=False): method="cat", squeeze=True, nometa=False):
# Handles generators, single files, lists, tuples, custom classes
wrfseq = _unpack_sequence(wrfseq)
# Dictionary is unique # Dictionary is unique
if _is_mapping(wrfseq): if _is_mapping(wrfseq):
outarr = _combine_dict(wrfseq, varname, timeidx, method, nometa) outarr = _combine_dict(wrfseq, varname, timeidx, method, nometa)
elif method.lower() == "cat": elif method.lower() == "cat":
outarr = _cat_files(wrfseq, varname, timeidx, squeeze, nometa) outarr = _cat_files(wrfseq, varname, timeidx, is_moving,
squeeze, nometa)
elif method.lower() == "join": elif method.lower() == "join":
outarr = _join_files(wrfseq, varname, timeidx, nometa) outarr = _join_files(wrfseq, varname, timeidx, is_moving, nometa)
else: else:
raise ValueError("method must be 'cat' or 'join'") raise ValueError("method must be 'cat' or 'join'")
@ -467,7 +605,8 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
return data_array return data_array
# Cache is a dictionary of already extracted variables # Cache is a dictionary of already extracted variables
def _extract_var(wrfnc, varname, timeidx, method, squeeze, cache, nometa): def _extract_var(wrfnc, varname, timeidx, is_moving,
method, squeeze, cache, nometa):
# Mainly used internally so variables don't get extracted multiple times, # Mainly used internally so variables don't get extracted multiple times,
# particularly to copy metadata. This can be slow. # particularly to copy metadata. This can be slow.
if cache is not None: if cache is not None:
@ -482,27 +621,28 @@ def _extract_var(wrfnc, varname, timeidx, method, squeeze, cache, nometa):
return cache_var return cache_var
is_moving = _is_moving_domain(wrfnc, varname)
multitime = _is_multi_time_req(timeidx) multitime = _is_multi_time_req(timeidx)
multifile = _is_multi_file(wrfnc) multifile = _is_multi_file(wrfnc)
if not multifile: if not multifile:
if xarray_enabled() and not nometa: if xarray_enabled() and not nometa:
if is_moving is None:
is_moving = _is_moving_domain(wrfnc, varname)
result = _build_data_array(wrfnc, varname, timeidx, is_moving) result = _build_data_array(wrfnc, varname, timeidx, is_moving)
else: else:
if not multitime: if not multitime:
result = wrfnc.variables[varname][timeidx,:] result = wrfnc.variables[varname][timeidx,:]
result = result[np.newaxis, :] # So that no squeeze works result = result[np.newaxis, :] # So that no squeeze works
else: else:
result = wrfnc.variables[varname][:] result = wrfnc.variables[varname][:]
else: else:
# Squeeze handled in this routine, so just return it # Squeeze handled in this routine, so just return it
return combine_files(wrfnc, varname, timeidx, method, squeeze, nometa) return combine_files(wrfnc, varname, timeidx, is_moving,
method, squeeze, nometa)
return result.squeeze() if squeeze else result return result.squeeze() if squeeze else result
def extract_vars(wrfnc, timeidx, varnames, method="cat", squeeze=True, def extract_vars(wrfnc, timeidx, varnames, method="cat", squeeze=True,
cache=None, nometa=False): cache=None, nometa=False):
if isstr(varnames): if isstr(varnames):
@ -510,7 +650,7 @@ def extract_vars(wrfnc, timeidx, varnames, method="cat", squeeze=True,
else: else:
varlist = varnames varlist = varnames
return {var:_extract_var(wrfnc, var, timeidx, return {var:_extract_var(wrfnc, var, timeidx, None,
method, squeeze, cache, nometa) method, squeeze, cache, nometa)
for var in varlist} for var in varlist}
@ -543,7 +683,12 @@ def extract_times(wrfnc, timeidx):
def is_standard_wrf_var(wrfnc, var): def is_standard_wrf_var(wrfnc, var):
multifile = _is_multi_file(wrfnc) multifile = _is_multi_file(wrfnc)
if multifile: if multifile:
wrfnc = wrfnc[0] if not _is_mapping(wrfnc):
wrfnc = next(iter(wrfnc))
else:
entry = wrfnc[next(iter(viewkeys(wrfnc)))]
return is_standard_wrf_var(entry, var)
return var in wrfnc.variables return var in wrfnc.variables
@ -627,104 +772,37 @@ def get_proj_params(wrfnc, timeidx=0, varname=None):
wrfnc.variables[lon_coord][time_idx_or_slice,:], wrfnc.variables[lon_coord][time_idx_or_slice,:],
proj_params) proj_params)
# Dictionary python 2-3 compatibility stuff
def viewitems(d):
func = getattr(d, "viewitems", None)
if func is None:
func = d.items
return func()
def viewkeys(d):
func = getattr(d, "viewkeys", None)
if func is None:
func = d.keys
return func()
def viewvalues(d):
func = getattr(d, "viewvalues", None)
if func is None:
func = d.values
return func()
def isstr(s):
try:
return isinstance(s, basestring)
except NameError:
return isinstance(s, str)
# Helper to extract masked arrays from DataArrays that convert to NaN
def npvalues(da):
if not isinstance(da, DataArray):
result = da
else:
try:
fill_value = da.attrs["_FillValue"]
except KeyError:
result = da.values
else:
result = ma.masked_invalid(da.values, copy=False)
result.set_fill_value(fill_value)
return result
# Helper utilities for metadata
class either(object):
def __init__(self, *varnames):
self.varnames = varnames
def __call__(self, wrfnc):
if _is_multi_file(wrfnc):
wrfnc = next(iter(wrfnc))
for varname in self.varnames: class CoordPair(object):
if varname in wrfnc.variables: def __init__(self, x=None, y=None, i=None, j=None, lat=None, lon=None):
return varname self.x = x
self.y = y
raise ValueError("{} are not valid variable names".format( self.i = i
self.varnames)) self.j = j
self.lat = lat
class combine_with: self.lon = lon
# Remove remove_idx first, then insert_idx is applied to removed set
def __init__(self, varname, remove_dims=None, insert_before=None,
new_dimnames=None, new_coords=None):
self.varname = varname
self.remove_dims = remove_dims
self.insert_before = insert_before
self.new_dimnames = new_dimnames if new_dimnames is not None else []
self.new_coords = (new_coords if new_coords is not None
else OrderedDict())
def __call__(self, var): def __repr__(self):
new_dims = list(var.dims) args = []
new_coords = OrderedDict(var.coords) if self.x is not None:
args.append("x={}".format(self.x))
args.append("y={}".format(self.y))
if self.remove_dims is not None: if self.i is not None:
for dim in self.remove_dims: args.append("i={}".format(self.i))
new_dims.remove(dim) args.append("j={}".format(self.j))
del new_coords[dim]
if self.insert_before is not None: if self.lat is not None:
insert_idx = new_dims.index(self.insert_before) args.append("lat={}".format(self.lat))
new_dims = (new_dims[0:insert_idx] + self.new_dimnames + args.append("lon={}".format(self.lon))
new_dims[insert_idx:])
elif self.new_dimnames is not None:
new_dims = self.new_dimnames
if self.new_coords is not None: argstr = ", ".join(args)
new_coords.update(self.new_coords)
return new_dims, new_coords return "{}({})".format(self.__class__.__name__, argstr)
class XYCoord(object): def __str__(self):
def __init__(self, x, y): return self.__repr__()
self.x = x
self.y = y
def __repr__(self):
return "{}({}, {})".format(self.__class__.__name__, self.x, self.y)
def from_args(func, argnames, *args, **kwargs): def from_args(func, argnames, *args, **kwargs):
"""Parses the function args and kargs looking for the desired argument """Parses the function args and kargs looking for the desired argument

22
wrf_open/var/src/python/wrf/var/uvmet.py

@ -3,6 +3,8 @@ from __future__ import (absolute_import, division, print_function,
from math import fabs, log, tan, sin, cos from math import fabs, log, tan, sin, cos
import numpy as np
from .extension import computeuvmet from .extension import computeuvmet
from .destag import destagger from .destag import destagger
from .constants import Constants from .constants import Constants
@ -55,8 +57,24 @@ def _get_uvmet(wrfnc, timeidx=0, ten_m=False, units ="mps",
# don't appear to be supported any longer # don't appear to be supported any longer
if map_proj in (0,3,6): if map_proj in (0,3,6):
# No rotation needed for Mercator and Lat/Lon # No rotation needed for Mercator and Lat/Lon, but still need
return u,v # u,v aggregated in to one array
end_idx = -3 if not ten_m else -2
resdim = list(u.shape[0:end_idx]) + [2] + list(u.shape[end_idx:])
# Make a new output array for the result
res = np.empty(resdim, u.dtype)
# For 2D array, this makes (...,0,:,:) and (...,1,:,:)
# For 3D array, this makes (...,0,:,:,:) and (...,1,:,:,:)
idx0 = tuple([Ellipsis] + [0] + [slice(None)]*(-end_idx))
idx1 = tuple([Ellipsis] + [1] + [slice(None)]*(-end_idx))
res[idx0] = u[:]
res[idx1] = v[:]
return res
elif map_proj in (1,2): elif map_proj in (1,2):
lat_attrs = extract_global_attrs(wrfnc, attrs=("TRUELAT1", lat_attrs = extract_global_attrs(wrfnc, attrs=("TRUELAT1",
"TRUELAT2")) "TRUELAT2"))

2
wrf_open/var/test/utests.py

@ -408,7 +408,7 @@ if __name__ == "__main__":
"geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure",
"pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc",
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "ctt", "cape_2d", "cape_3d"] "wa", "uvmet10", "uvmet", "z", "ctt"]
interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"]
try: try:

Loading…
Cancel
Save