Bill Ladwig 6 years ago
parent
commit
f3068c2b69
  1. 5
      src/wrf/geobnds.py
  2. 50
      src/wrf/interp.py
  3. 8
      src/wrf/interputils.py
  4. 23
      src/wrf/latlonutils.py
  5. 99
      src/wrf/metadecorators.py
  6. 74
      src/wrf/projection.py
  7. 1
      src/wrf/projutils.py
  8. 4
      src/wrf/py3compat.py
  9. 9
      src/wrf/routines.py
  10. 15
      src/wrf/specialdec.py
  11. 14
      src/wrf/units.py
  12. 77
      src/wrf/util.py
  13. 1
      src/wrf/version.py

5
src/wrf/geobnds.py

@ -2,6 +2,7 @@ from __future__ import (absolute_import, division, print_function)
from .coordpair import CoordPair from .coordpair import CoordPair
class GeoBounds(object): class GeoBounds(object):
"""A class that stores the geographic boundaries. """A class that stores the geographic boundaries.
@ -84,7 +85,3 @@ class NullGeoBounds(GeoBounds):
def __repr__(self): def __repr__(self):
return "{}()".format(self.__class__.__name__) return "{}()".format(self.__class__.__name__)

50
src/wrf/interp.py

@ -175,9 +175,9 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
timeidx (:obj:`int`, optional): The timeidx (:obj:`int`, optional): The
desired time index when obtaining map boundary information desired time index when obtaining map boundary information
from moving nests. This value can be a positive or negative integer. from moving nests. This value can be a positive or negative
Only required when *wrfin* is specified and the nest is moving. integer. Only required when *wrfin* is specified and the nest is
Currently, :data:`wrf.ALL_TIMES` is not supported. moving. Currently, :data:`wrf.ALL_TIMES` is not supported.
Default is 0. Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs stagger (:obj:`str`): If using latitude, longitude coordinate pairs
@ -299,12 +299,12 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
# domain could move outside of the line, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving: if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving "
"is not supported when using lat/lon " "nest is not supported when using "
"cross sections because the domain could " "lat/lon cross sections because the "
"move outside of the cross section. " "domain could move outside of the "
"You must request each time " "cross section. You must request "
"individually.") "each time individually.")
else: else:
# Domain not moving, just use 0 # Domain not moving, just use 0
_timeidx = 0 _timeidx = 0
@ -392,9 +392,9 @@ def interpline(field2d, wrfin=None, timeidx=0, stagger=None, projection=None,
timeidx (:obj:`int`, optional): The timeidx (:obj:`int`, optional): The
desired time index when obtaining map boundary information desired time index when obtaining map boundary information
from moving nests. This value can be a positive or negative integer. from moving nests. This value can be a positive or negative
Only required when *wrfin* is specified and the nest is moving. integer. Only required when *wrfin* is specified and the nest is
Currently, :data:`wrf.ALL_TIMES` is not supported. moving. Currently, :data:`wrf.ALL_TIMES` is not supported.
Default is 0. Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs stagger (:obj:`str`): If using latitude, longitude coordinate pairs
@ -509,10 +509,10 @@ def interpline(field2d, wrfin=None, timeidx=0, stagger=None, projection=None,
# domain could move outside of the line, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving: if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving "
"is not supported when using a lat/lon " "nest is not supported when using a "
"line because the domain could " "lat/lon line because the domain "
"move outside of line. " "could move outside of line. "
"You must request each time " "You must request each time "
"individually.") "individually.")
else: else:
@ -585,8 +585,8 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
* 'ght_msl': grid point height msl [km] * 'ght_msl': grid point height msl [km]
* 'ght_agl': grid point height agl [km] * 'ght_agl': grid point height agl [km]
* 'theta', 'th': potential temperature [K] * 'theta', 'th': potential temperature [K]
* 'theta-e', 'thetae', 'eth': equivalent potential temperature \ * 'theta-e', 'thetae', 'eth': equivalent potential \
[K] temperature [K]
interp_levels (sequence): A 1D sequence of vertical levels to interp_levels (sequence): A 1D sequence of vertical levels to
interpolate to. Values must be in the same units as specified interpolate to. Values must be in the same units as specified
@ -699,7 +699,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
"pressure_hpa": 1.0/ConversionFactors.PA_TO_HPA, "pressure_hpa": 1.0/ConversionFactors.PA_TO_HPA,
"z_km": 1.0/ConversionFactors.M_TO_KM, "z_km": 1.0/ConversionFactors.M_TO_KM,
"ght_km": 1.0/ConversionFactors.M_TO_KM, "ght_km": 1.0/ConversionFactors.M_TO_KM,
} }
out_unitmap = {"p_hpa": ConversionFactors.PA_TO_HPA, out_unitmap = {"p_hpa": ConversionFactors.PA_TO_HPA,
@ -707,7 +706,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
"pressure_hpa": ConversionFactors.PA_TO_HPA, "pressure_hpa": ConversionFactors.PA_TO_HPA,
"z_km": ConversionFactors.M_TO_KM, "z_km": ConversionFactors.M_TO_KM,
"ght_km": ConversionFactors.M_TO_KM, "ght_km": ConversionFactors.M_TO_KM,
} }
# These constants match what's in the fortran code. # These constants match what's in the fortran code.
@ -728,12 +726,12 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
# Check for valid coord # Check for valid coord
if vert_coord not in valid_coords: if vert_coord not in valid_coords:
raise ValueError("'%s' is not a valid vertical " raise ValueError("'{}' is not a valid vertical "
"coordinate type" % vert_coord) "coordinate type".format(vert_coord))
# Check for valid field type # Check for valid field type
if field_type not in valid_field_types: if field_type not in valid_field_types:
raise ValueError("'%s' is not a valid field type" % field_type) raise ValueError("'{}' is not a valid field type".format(field_type))
log_p_int = 1 if log_p else 0 log_p_int = 1 if log_p else 0
@ -861,9 +859,3 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
res_ = res res_ = res
return ma.masked_values(res_, missing) return ma.masked_values(res_, missing)

8
src/wrf/interputils.py

@ -85,7 +85,6 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
if (angle > 315.0 or angle < 45.0 if (angle > 315.0 or angle < 45.0
or ((angle > 135.0) and (angle < 225.0))): or ((angle > 135.0) and (angle < 225.0))):
#x = y*slope + intercept
slope = -(360.-angle)/45. slope = -(360.-angle)/45.
if(angle < 45.): if(angle < 45.):
slope = angle/45. slope = angle/45.
@ -121,7 +120,7 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
slope = (270.-angle)/45. slope = (270.-angle)/45.
intercept = yp - xp*slope intercept = yp - xp*slope
#find intersections with domain boundaries # Find intersections with domain boundaries
x0 = 0. x0 = 0.
y0 = x0*slope + intercept y0 = x0*slope + intercept
@ -323,6 +322,7 @@ def get_xy(var, pivot_point=None, angle=None,
return xy return xy
def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None, def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
ll_point=None): ll_point=None):
"""Return the coordinate pairs in grid space. """Return the coordinate pairs in grid space.
@ -389,7 +389,8 @@ def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
if wrfin is not None: if wrfin is not None:
xy_vals = _ll_to_xy(lat, lon, wrfin=wrfin, timeidx=timeidx, xy_vals = _ll_to_xy(lat, lon, wrfin=wrfin, timeidx=timeidx,
squeeze=True, meta=False, stagger=stagger, as_int=True) squeeze=True, meta=False, stagger=stagger,
as_int=True)
else: else:
map_proj = projection.map_proj map_proj = projection.map_proj
@ -427,7 +428,6 @@ def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
xy_vals = xy_vals.squeeze() xy_vals = xy_vals.squeeze()
if xy_vals.ndim == 1: if xy_vals.ndim == 1:
return CoordPair(x=xy_vals[0], y=xy_vals[1]) return CoordPair(x=xy_vals[0], y=xy_vals[1])
else: else:

23
src/wrf/latlonutils.py

@ -7,8 +7,8 @@ import numpy as np
from .constants import Constants, ProjectionTypes from .constants import Constants, ProjectionTypes
from .extension import _lltoxy, _xytoll from .extension import _lltoxy, _xytoll
from .util import (extract_vars, extract_global_attrs, from .util import (extract_vars, extract_global_attrs,
either, is_moving_domain, is_multi_time_req, either, is_moving_domain, iter_left_indexes,
iter_left_indexes, is_mapping, is_multi_file) is_mapping, is_multi_file)
from .py3compat import viewkeys, viewitems from .py3compat import viewkeys, viewitems
from .projutils import dict_keys_to_upper from .projutils import dict_keys_to_upper
@ -46,6 +46,7 @@ def _lat_varname(wrfin, stagger):
return varname return varname
def _lon_varname(wrfin, stagger): def _lon_varname(wrfin, stagger):
"""Return the longitude variable name for the specified stagger type. """Return the longitude variable name for the specified stagger type.
@ -79,6 +80,7 @@ def _lon_varname(wrfin, stagger):
return varname return varname
def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key): def _get_proj_params(wrfin, timeidx, stagger, method, squeeze, cache, _key):
"""Return the map projection parameters. """Return the map projection parameters.
@ -391,7 +393,6 @@ def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0,
result = np.empty(outdim, np.float64) result = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
#left_and_slice_idxs = left_idxs + (slice(None), )
# Left indexes is a misnomer, since these will be on the right # Left indexes is a misnomer, since these will be on the right
x_idxs = (0,) + left_idxs x_idxs = (0,) + left_idxs
y_idxs = (1,) + left_idxs y_idxs = (1,) + left_idxs
@ -426,7 +427,6 @@ def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0,
result[0] = fort_out[1] result[0] = fort_out[1]
result[1] = fort_out[0] result[1] = fort_out[0]
# Make indexes 0-based # Make indexes 0-based
result = result - 1 result = result - 1
@ -435,6 +435,7 @@ def _ll_to_xy(latitude, longitude, wrfin=None, timeidx=0,
return result return result
# X and Y should be 0-based # X and Y should be 0-based
def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None, def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None,
method="cat", squeeze=True, cache=None, _key=None, method="cat", squeeze=True, cache=None, _key=None,
@ -528,7 +529,6 @@ def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc) = _kwarg_proj_params(**projparams) loninc) = _kwarg_proj_params(**projparams)
if isinstance(x, Iterable): if isinstance(x, Iterable):
x_arr = np.asarray(x) x_arr = np.asarray(x)
y_arr = np.asarray(y) y_arr = np.asarray(y)
@ -555,7 +555,6 @@ def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None,
result = np.empty(outdim, np.float64) result = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
#left_and_slice_idxs = left_idxs + (slice(None), )
lat_idxs = (0,) + left_idxs lat_idxs = (0,) + left_idxs
lon_idxs = (1,) + left_idxs lon_idxs = (1,) + left_idxs
@ -573,7 +572,6 @@ def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None,
ref_lon_val, pole_lat, pole_lon, known_x, known_y, ref_lon_val, pole_lat, pole_lon, known_x, known_y,
dx, dy, latinc, loninc, x_val, y_val) dx, dy, latinc, loninc, x_val, y_val)
#result[left_and_slice_idxs] = ll[:]
result[lat_idxs] = ll[0] result[lat_idxs] = ll[0]
result[lon_idxs] = ll[1] result[lon_idxs] = ll[1]
@ -582,13 +580,8 @@ def _xy_to_ll(x, y, wrfin=None, timeidx=0, stagger=None,
x_val = x + 1 x_val = x + 1
y_val = y + 1 y_val = y + 1
result = _xytoll(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, result = _xytoll(map_proj, truelat1, truelat2, stdlon, ref_lat,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, ref_lon, pole_lat, pole_lon, known_x, known_y,
loninc, x_val, y_val) dx, dy, latinc, loninc, x_val, y_val)
return result return result

99
src/wrf/metadecorators.py

@ -112,7 +112,6 @@ def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=None,
var_to_copy = None if cache is None else cache.get(_copy_varname, var_to_copy = None if cache is None else cache.get(_copy_varname,
None) None)
if var_to_copy is None: if var_to_copy is None:
var_to_copy = extract_vars(wrfin, timeidx, (_copy_varname,), var_to_copy = extract_vars(wrfin, timeidx, (_copy_varname,),
method, squeeze, cache, method, squeeze, cache,
@ -155,7 +154,6 @@ def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=None,
except KeyError: except KeyError:
pass pass
if name is not None: if name is not None:
outname = name outname = name
@ -277,10 +275,8 @@ def set_wind_metadata(copy_varname, name, description,
outattrs = OrderedDict() outattrs = OrderedDict()
outdimnames = list(copy_var.dims) outdimnames = list(copy_var.dims)
#outcoords.update(copy_var.coords)
outattrs.update(copy_var.attrs) outattrs.update(copy_var.attrs)
if wind_ncvar: if wind_ncvar:
outcoords.update(copy_var.coords) outcoords.update(copy_var.coords)
elif not wspd_wdir: elif not wspd_wdir:
@ -414,7 +410,6 @@ def set_cape_metadata(is2d):
outattrs["_FillValue"] = missing outattrs["_FillValue"] = missing
outattrs["missing_value"] = missing outattrs["missing_value"] = missing
# xarray doesn't line up coordinate dimensions based on # xarray doesn't line up coordinate dimensions based on
# names, it just remembers the index it originally mapped to. # names, it just remembers the index it originally mapped to.
# So, need to rebuild the XLAT, XLONG, coordinates again since the # So, need to rebuild the XLAT, XLONG, coordinates again since the
@ -433,7 +428,6 @@ def set_cape_metadata(is2d):
else: else:
outcoords["cape_cin"] = ["cape", "cin"] outcoords["cape_cin"] = ["cape", "cin"]
return DataArray(result, name=outname, coords=outcoords, return DataArray(result, name=outname, coords=outcoords,
dims=outdimnames, attrs=outattrs) dims=outdimnames, attrs=outattrs)
@ -632,8 +626,8 @@ def set_latlon_metadata(xy=False):
for x in zip(arr1, arr2)]) for x in zip(arr1, arr2)])
coords[dimnames[0]] = ["lat", "lon"] coords[dimnames[0]] = ["lat", "lon"]
else: else:
coords["latlon_coord"] = (dimnames[-1], [CoordPair(lat=x[0], coords["latlon_coord"] = (dimnames[-1],
lon=x[1]) [CoordPair(lat=x[0], lon=x[1])
for x in zip(arr1, arr2)]) for x in zip(arr1, arr2)])
coords[dimnames[0]] = ["x", "y"] coords[dimnames[0]] = ["x", "y"]
@ -750,11 +744,12 @@ def set_height_metadata(geopt=False, stag=False):
"(mass grid)".format(height_type)) "(mass grid)".format(height_type))
else: else:
outattrs["description"] = ("model height - [{}] (vertically " outattrs["description"] = ("model height - [{}] (vertically "
"staggered grid)".format(height_type)) "staggered grid)".format(
height_type))
return DataArray(result, name=outname, dims=outdimnames,
coords=outcoords, attrs=outattrs)
return DataArray(result, name=outname,
dims=outdimnames, coords=outcoords, attrs=outattrs)
return func_wrapper return func_wrapper
@ -819,7 +814,6 @@ def _set_horiz_meta(wrapped, instance, args, kwargs):
if isinstance(z, DataArray): if isinstance(z, DataArray):
vert_units = z.attrs.get("units", None) vert_units = z.attrs.get("units", None)
if isinstance(field3d, DataArray): if isinstance(field3d, DataArray):
outcoords = OrderedDict() outcoords = OrderedDict()
outdimnames = list(field3d.dims) outdimnames = list(field3d.dims)
@ -993,7 +987,8 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
end_point_xy = (end_point.x, end_point.y) end_point_xy = (end_point.x, end_point.y)
xy, var2dz, z_var2d = get_xy_z_params(to_np(z), pivot_point_xy, angle, xy, var2dz, z_var2d = get_xy_z_params(to_np(z), pivot_point_xy, angle,
start_point_xy, end_point_xy, levels, autolevels) start_point_xy, end_point_xy,
levels, autolevels)
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache
if cache is not None: if cache is not None:
@ -1039,7 +1034,6 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
except KeyError: except KeyError:
pass # Xarray 0.9 pass # Xarray 0.9
# Delete any lat,lon coords # Delete any lat,lon coords
delkeys = [key for key in viewkeys(outcoords) if is_coordvar(key)] delkeys = [key for key in viewkeys(outcoords) if is_coordvar(key)]
for key in delkeys: for key in delkeys:
@ -1069,12 +1063,10 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
lats = _interpline(latcoord, xy) lats = _interpline(latcoord, xy)
lons = _interpline(loncoord, xy) lons = _interpline(loncoord, xy)
outcoords["xy_loc"] = ("cross_line_idx", outcoords["xy_loc"] = ("cross_line_idx", np.asarray(tuple(
np.asarray(tuple(
CoordPair(x=xy[i, 0], y=xy[i, 1], CoordPair(x=xy[i, 0], y=xy[i, 1],
lat=lats[i], lon=lons[i]) lat=lats[i], lon=lons[i])
for i in py3range(xy.shape[-2]))) for i in py3range(xy.shape[-2]))))
)
# Moving domain # Moving domain
else: else:
extra_dims = latcoord.shape[0:-2] extra_dims = latcoord.shape[0:-2]
@ -1086,12 +1078,11 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
lats = _interpline(latcoord[idxs], xy) lats = _interpline(latcoord[idxs], xy)
lons = _interpline(loncoord[idxs], xy) lons = _interpline(loncoord[idxs], xy)
latlon_loc[idxs] = np.asarray(tuple( latlon_loc[idxs] = np.asarray(
CoordPair(x=xy[i,0], y=xy[i,1], tuple(CoordPair(
x=xy[i, 0], y=xy[i, 1],
lat=lats[i], lon=lons[i]) lat=lats[i], lon=lons[i])
for i in py3range(xy.shape[-2])) for i in py3range(xy.shape[-2])))[:]
)[:]
extra_dimnames = latcoord.dims[0:-2] extra_dimnames = latcoord.dims[0:-2]
loc_dimnames = extra_dimnames + ("cross_line_idx",) loc_dimnames = extra_dimnames + ("cross_line_idx",)
@ -1100,12 +1091,14 @@ def _set_cross_meta(wrapped, instance, args, kwargs):
else: else:
warnings.warn("'latlon' is set to True, but 'field3d' " warnings.warn("'latlon' is set to True, but 'field3d' "
"contains no coordinate information") "contains no coordinate information")
outcoords["xy_loc"] = ("cross_line_idx", np.asarray(tuple( outcoords["xy_loc"] = ("cross_line_idx",
np.asarray(tuple(
CoordPair(xy[i, 0], xy[i, 1]) CoordPair(xy[i, 0], xy[i, 1])
for i in py3range(xy.shape[-2])))) for i in py3range(xy.shape[-2]))))
else: else:
outcoords["xy_loc"] = ("cross_line_idx", np.asarray(tuple( outcoords["xy_loc"] = ("cross_line_idx",
np.asarray(tuple(
CoordPair(xy[i, 0], xy[i, 1]) CoordPair(xy[i, 0], xy[i, 1])
for i in py3range(xy.shape[-2])))) for i in py3range(xy.shape[-2]))))
@ -1160,11 +1153,10 @@ def _set_line_meta(wrapped, instance, args, kwargs):
:mod:`wrapt` :mod:`wrapt`
""" """
argvars = from_args(wrapped, ("field2d", argvars = from_args(wrapped, ("field2d", "wrfin", "timeidx", "stagger",
"wrfin", "timeidx", "stagger", "projection", "projection", "ll_point", "pivot_point",
"ll_point", "pivot_point", "angle", "angle", "start_point", "end_point",
"start_point", "end_point", "latlon", "latlon", "cache"),
"cache"),
*args, **kwargs) *args, **kwargs)
field2d = argvars["field2d"] field2d = argvars["field2d"]
@ -1223,7 +1215,6 @@ def _set_line_meta(wrapped, instance, args, kwargs):
# to avoid problems downstream # to avoid problems downstream
_timeidx = 0 _timeidx = 0
if pivot_point is not None: if pivot_point is not None:
if pivot_point.lat is not None and pivot_point.lon is not None: if pivot_point.lat is not None and pivot_point.lon is not None:
xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx, xy_coords = to_xy_coords(pivot_point, wrfin, _timeidx,
@ -1232,7 +1223,6 @@ def _set_line_meta(wrapped, instance, args, kwargs):
else: else:
pivot_point_xy = (pivot_point.x, pivot_point.y) pivot_point_xy = (pivot_point.x, pivot_point.y)
if start_point is not None and end_point is not None: if start_point is not None and end_point is not None:
if start_point.lat is not None and start_point.lon is not None: if start_point.lat is not None and start_point.lon is not None:
xy_coords = to_xy_coords(start_point, wrfin, _timeidx, xy_coords = to_xy_coords(start_point, wrfin, _timeidx,
@ -1248,7 +1238,6 @@ def _set_line_meta(wrapped, instance, args, kwargs):
else: else:
end_point_xy = (end_point.x, end_point.y) end_point_xy = (end_point.x, end_point.y)
xy = get_xy(field2d, pivot_point_xy, angle, start_point_xy, end_point_xy) xy = get_xy(field2d, pivot_point_xy, angle, start_point_xy, end_point_xy)
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache
@ -1276,8 +1265,7 @@ def _set_line_meta(wrapped, instance, args, kwargs):
cross_str = "({0}, {1}) to ({2}, {3})".format(st_x, st_y, ed_x, ed_y) cross_str = "({0}, {1}) to ({2}, {3})".format(st_x, st_y, ed_x, ed_y)
if angle is not None: if angle is not None:
cross_str += " ; center={0} ; angle={1}".format(pivot_point, cross_str += " ; center={0} ; angle={1}".format(pivot_point, angle)
angle)
if isinstance(field2d, DataArray): if isinstance(field2d, DataArray):
outcoords = OrderedDict() outcoords = OrderedDict()
@ -1318,12 +1306,10 @@ def _set_line_meta(wrapped, instance, args, kwargs):
lats = _interpline(latcoord, xy) lats = _interpline(latcoord, xy)
lons = _interpline(loncoord, xy) lons = _interpline(loncoord, xy)
outcoords["xy_loc"] = ("line_idx", outcoords["xy_loc"] = ("line_idx", np.asarray(tuple(
np.asarray(tuple(
CoordPair(x=xy[i, 0], y=xy[i, 1], CoordPair(x=xy[i, 0], y=xy[i, 1],
lat=lats[i], lon=lons[i]) lat=lats[i], lon=lons[i])
for i in py3range(xy.shape[-2]))) for i in py3range(xy.shape[-2]))))
)
# Moving domain # Moving domain
else: else:
@ -1339,9 +1325,7 @@ def _set_line_meta(wrapped, instance, args, kwargs):
latlon_loc[idxs] = np.asarray(tuple( latlon_loc[idxs] = np.asarray(tuple(
CoordPair(x=xy[i, 0], y=xy[i, 1], CoordPair(x=xy[i, 0], y=xy[i, 1],
lat=lats[i], lon=lons[i]) lat=lats[i], lon=lons[i])
for i in py3range(xy.shape[-2])) for i in py3range(xy.shape[-2])))[:]
)[:]
extra_dimnames = latcoord.dims[0:-2] extra_dimnames = latcoord.dims[0:-2]
loc_dimnames = extra_dimnames + ("line_idx",) loc_dimnames = extra_dimnames + ("line_idx",)
@ -1350,15 +1334,13 @@ def _set_line_meta(wrapped, instance, args, kwargs):
else: else:
warnings.warn("'latlon' is set to True, but 'field2d' " warnings.warn("'latlon' is set to True, but 'field2d' "
"contains no coordinate information") "contains no coordinate information")
outcoords["xy_loc"] = ("line_idx", np.asarray(tuple( outcoords["xy_loc"] = ("line_idx", np.asarray(
CoordPair(xy[i,0], xy[i,1]) tuple(CoordPair(xy[i, 0], xy[i, 1])
for i in py3range(xy.shape[-2])))) for i in py3range(xy.shape[-2]))))
else: else:
outcoords["xy_loc"] = ("line_idx", np.asarray(tuple( outcoords["xy_loc"] = ("line_idx", np.asarray(
CoordPair(xy[i,0], xy[i,1]) tuple(CoordPair(xy[i, 0], xy[i, 1])
for i in py3range(xy.shape[-2])))) for i in py3range(xy.shape[-2]))))
else: else:
if inc_latlon: if inc_latlon:
warnings.warn("'latlon' is set to True, but 'field2d' is " warnings.warn("'latlon' is set to True, but 'field2d' is "
@ -1427,7 +1409,6 @@ def _set_vinterp_meta(wrapped, instance, args, kwargs):
outcoords = None outcoords = None
outattrs = OrderedDict() outattrs = OrderedDict()
if isinstance(field, DataArray): if isinstance(field, DataArray):
outcoords = OrderedDict() outcoords = OrderedDict()
outdimnames = list(field.dims) outdimnames = list(field.dims)
@ -1443,7 +1424,6 @@ def _set_vinterp_meta(wrapped, instance, args, kwargs):
outcoords["interp_level"] = interp_levels outcoords["interp_level"] = interp_levels
outattrs.update(field.attrs) outattrs.update(field.attrs)
outname = field.name outname = field.name
else: else:
@ -1503,8 +1483,7 @@ def _set_2dxy_meta(wrapped, instance, args, kwargs):
ed_x = xy[-1, 0] ed_x = xy[-1, 0]
ed_y = xy[-1, 1] ed_y = xy[-1, 1]
cross_str = "({0},{1}) to ({2},{3})".format(st_x, st_y, cross_str = "({0},{1}) to ({2},{3})".format(st_x, st_y, ed_x, ed_y)
ed_x, ed_y)
outname = None outname = None
outdimnames = None outdimnames = None
@ -1532,7 +1511,6 @@ def _set_2dxy_meta(wrapped, instance, args, kwargs):
del outcoords[key] del outcoords[key]
outdimnames.append("line_idx") outdimnames.append("line_idx")
#outattrs.update(field3d.attrs)
desc = field3d.attrs.get("description", None) desc = field3d.attrs.get("description", None)
if desc is not None: if desc is not None:
@ -1544,7 +1522,8 @@ def _set_2dxy_meta(wrapped, instance, args, kwargs):
outname = "{0}_2dxy".format(field3d.name) outname = "{0}_2dxy".format(field3d.name)
outcoords["xy_loc"] = ("line_idx", [CoordPair(xy[i,0], xy[i,1]) outcoords["xy_loc"] = ("line_idx",
[CoordPair(xy[i, 0], xy[i, 1])
for i in py3range(xy.shape[-2])]) for i in py3range(xy.shape[-2])])
for key in ("MemoryOrder",): for key in ("MemoryOrder",):
@ -1824,7 +1803,6 @@ def set_alg_metadata(alg_ndims, refvarname,
if not xarray_enabled() or not do_meta: if not xarray_enabled() or not do_meta:
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
result = wrapped(*args, **kwargs) result = wrapped(*args, **kwargs)
outname = wrapped.__name__ outname = wrapped.__name__
@ -1858,7 +1836,6 @@ def set_alg_metadata(alg_ndims, refvarname,
if _units is not None: if _units is not None:
outattrs["units"] = _units outattrs["units"] = _units
if description is not None: if description is not None:
if isinstance(description, from_var): if isinstance(description, from_var):
desc = description(wrapped, *args, **kwargs) desc = description(wrapped, *args, **kwargs)
@ -1867,7 +1844,6 @@ def set_alg_metadata(alg_ndims, refvarname,
else: else:
outattrs["description"] = description outattrs["description"] = description
# Copy the dimnames from the reference variable, otherwise, use # Copy the dimnames from the reference variable, otherwise, use
# the supplied dimnames # the supplied dimnames
if refvarname is not None: if refvarname is not None:
@ -2094,7 +2070,6 @@ def set_cape_alg_metadata(is2d, copyarg="pres_hpa"):
outattrs = OrderedDict() outattrs = OrderedDict()
if is2d: if is2d:
if is1d: if is1d:
outname = "cape_2d" outname = "cape_2d"
@ -2114,7 +2089,6 @@ def set_cape_alg_metadata(is2d, copyarg="pres_hpa"):
outattrs["description"] = "cape; cin" outattrs["description"] = "cape; cin"
outattrs["units"] = "J kg-1 ; J kg-1" outattrs["units"] = "J kg-1 ; J kg-1"
if isinstance(p, DataArray): if isinstance(p, DataArray):
if is2d: if is2d:
if not is1d: if not is1d:
@ -2131,7 +2105,6 @@ def set_cape_alg_metadata(is2d, copyarg="pres_hpa"):
else: else:
outdims[1] = p.dims[0] outdims[1] = p.dims[0]
outcoords = {} outcoords = {}
# Left-most is always cape_cin or cape_cin_lcl_lfc # Left-most is always cape_cin or cape_cin_lcl_lfc
if is2d: if is2d:
@ -2214,7 +2187,6 @@ def set_cloudfrac_alg_metadata(copyarg="vert"):
# Left dims # Left dims
outdims[1:-2] = cp.dims[0:-3] outdims[1:-2] = cp.dims[0:-3]
outcoords = {} outcoords = {}
# Left-most is always low_mid_high # Left-most is always low_mid_high
outdims[0] = "low_mid_high" outdims[0] = "low_mid_high"
@ -2294,8 +2266,3 @@ def set_destag_metadata():
return out return out
return func_wrapper return func_wrapper

74
src/wrf/projection.py

@ -61,7 +61,8 @@ if cartopy_enabled():
super(crs.Mercator, self).__init__(proj4_params, globe=globe) super(crs.Mercator, self).__init__(proj4_params, globe=globe)
# Calculate limits. # Calculate limits.
limits = self.transform_points(crs.Geodetic(), limits = self.transform_points(
crs.Geodetic(),
np.array([-180, 180]) + central_longitude, np.array([-180, 180]) + central_longitude,
np.array([min_latitude, max_latitude])) np.array([min_latitude, max_latitude]))
@ -195,7 +196,6 @@ class WrfProj(object):
if self.stand_lon is None: if self.stand_lon is None:
self.stand_lon = self._cen_lon self.stand_lon = self._cen_lon
@staticmethod @staticmethod
def _context_equal(x, y, ctx): def _context_equal(x, y, ctx):
"""Return True if both objects are equal based on the provided context. """Return True if both objects are equal based on the provided context.
@ -230,7 +230,6 @@ class WrfProj(object):
return True return True
def __eq__(self, other): def __eq__(self, other):
"""Return True if this projection object is the same as *other*. """Return True if this projection object is the same as *other*.
@ -262,7 +261,6 @@ class WrfProj(object):
WrfProj._context_equal(self.dx, other.dx, ctx) and WrfProj._context_equal(self.dx, other.dx, ctx) and
WrfProj._context_equal(self.dy, other.dy, ctx)) WrfProj._context_equal(self.dy, other.dy, ctx))
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
return None return None
@ -275,17 +273,15 @@ class WrfProj(object):
def _calc_extents(self, geobounds): def _calc_extents(self, geobounds):
# Need to modify the extents for the new projection # Need to modify the extents for the new projection
pc = crs.PlateCarree() pc = crs.PlateCarree()
xs, ys, _ = self._cartopy().transform_points(pc, xs, ys, _ = self._cartopy().transform_points(
np.array([geobounds.bottom_left.lon, pc,
geobounds.top_right.lon]), np.array([geobounds.bottom_left.lon, geobounds.top_right.lon]),
np.array([geobounds.bottom_left.lat, np.array([geobounds.bottom_left.lat, geobounds.top_right.lat])).T
geobounds.top_right.lat])).T
_xlimits = xs.tolist() _xlimits = xs.tolist()
_ylimits = ys.tolist() _ylimits = ys.tolist()
return (_xlimits, _ylimits) return (_xlimits, _ylimits)
def _cart_extents(self, geobounds): def _cart_extents(self, geobounds):
try: try:
_ = len(geobounds) _ = len(geobounds)
@ -529,10 +525,9 @@ class LambertConformal(WrfProj):
if self.truelat2 is not None: if self.truelat2 is not None:
self._std_parallels.append(self.truelat2) self._std_parallels.append(self.truelat2)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
_cf_params["grid_mapping_name"] = "lambert_conformal_conic"; _cf_params["grid_mapping_name"] = "lambert_conformal_conic"
_cf_params["standard_parallel"] = self._std_parallels _cf_params["standard_parallel"] = self._std_parallels
_cf_params["longitude_of_central_meridian"] = self.stand_lon _cf_params["longitude_of_central_meridian"] = self.stand_lon
_cf_params["latitude_of_projection_origin"] = self.moad_cen_lat _cf_params["latitude_of_projection_origin"] = self.moad_cen_lat
@ -540,13 +535,11 @@ class LambertConformal(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
truelat2 = (self.truelat1 truelat2 = (self.truelat1 if _ismissing(self.truelat2)
if _ismissing(self.truelat2)
else self.truelat2) else self.truelat2)
_pyngl = Resources() _pyngl = Resources()
@ -567,7 +560,6 @@ class LambertConformal(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
@ -650,16 +642,14 @@ class Mercator(WrfProj):
""" """
super(Mercator, self).__init__(**proj_params) super(Mercator, self).__init__(**proj_params)
self._lat_ts = (None self._lat_ts = (
if self.truelat1 == 0. or _ismissing(self.truelat1) None if self.truelat1 == 0. or _ismissing(self.truelat1)
else self.truelat1) else self.truelat1)
self._stand_lon = (0. if _ismissing(self.stand_lon, islat=False) self._stand_lon = (0. if _ismissing(self.stand_lon, islat=False)
else self.stand_lon) else self.stand_lon)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
_cf_params["grid_mapping_name"] = "mercator" _cf_params["grid_mapping_name"] = "mercator"
_cf_params["longitude_of_projection_origin"] = self.stand_lon _cf_params["longitude_of_projection_origin"] = self.stand_lon
@ -667,7 +657,6 @@ class Mercator(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -689,7 +678,6 @@ class Mercator(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
@ -710,25 +698,20 @@ class Mercator(WrfProj):
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
if self._lat_ts == 0.0: if self._lat_ts == 0.0:
_cartopy = crs.Mercator( _cartopy = crs.Mercator(central_longitude=self._stand_lon,
central_longitude = self._stand_lon,
globe=self._globe()) globe=self._globe())
else: else:
_cartopy = MercatorWithLatTS( _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())
return _cartopy return _cartopy
def _proj4(self): def _proj4(self):
_proj4 = ("+proj=merc +units=meters +a={} +b={} " _proj4 = ("+proj=merc +units=meters +a={} +b={} "
@ -740,6 +723,7 @@ class Mercator(WrfProj):
return _proj4 return _proj4
class PolarStereographic(WrfProj): class PolarStereographic(WrfProj):
"""A :class:`wrf.WrfProj` subclass for Polar Stereographic projections. """A :class:`wrf.WrfProj` subclass for Polar Stereographic projections.
@ -750,7 +734,6 @@ class PolarStereographic(WrfProj):
:class:`Mercator`, :class:`LambertConformal` :class:`Mercator`, :class:`LambertConformal`
""" """
def __init__(self, **proj_params): def __init__(self, **proj_params):
"""Initialize a :class:`wrf.PolarStereographic` object. """Initialize a :class:`wrf.PolarStereographic` object.
@ -770,10 +753,7 @@ class PolarStereographic(WrfProj):
""" """
super(PolarStereographic, self).__init__(**proj_params) super(PolarStereographic, self).__init__(**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 if _ismissing(self.truelat1) else self.truelat1)
if _ismissing(self.truelat1)
else self.truelat1)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
@ -785,7 +765,6 @@ class PolarStereographic(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -811,7 +790,6 @@ class PolarStereographic(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
@ -832,7 +810,6 @@ class PolarStereographic(WrfProj):
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
@ -843,7 +820,6 @@ class PolarStereographic(WrfProj):
globe=self._globe()) globe=self._globe())
return _cartopy return _cartopy
def _proj4(self): def _proj4(self):
_proj4 = ("+proj=stere +units=meters +a={} +b={} " _proj4 = ("+proj=stere +units=meters +a={} +b={} "
"+lat0={} +lon_0={} +lat_ts={} +nadgrids=@null".format( "+lat0={} +lon_0={} +lat_ts={} +nadgrids=@null".format(
@ -856,7 +832,6 @@ class PolarStereographic(WrfProj):
return _proj4 return _proj4
class LatLon(WrfProj): class LatLon(WrfProj):
"""A :class:`wrf.WrfProj` subclass for Lat Lon projections. """A :class:`wrf.WrfProj` subclass for Lat Lon projections.
@ -886,13 +861,11 @@ class LatLon(WrfProj):
""" """
super(LatLon, self).__init__(**proj_params) super(LatLon, self).__init__(**proj_params)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
_cf_params["grid_mapping_name"] = "latitude_longitude" _cf_params["grid_mapping_name"] = "latitude_longitude"
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -914,7 +887,6 @@ class LatLon(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
@ -934,7 +906,6 @@ class LatLon(WrfProj):
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
@ -944,19 +915,19 @@ class LatLon(WrfProj):
return _cartopy return _cartopy
def _cart_extents(self, geobounds): def _cart_extents(self, geobounds):
return ([geobounds.bottom_left.lon, geobounds.top_right.lon], return ([geobounds.bottom_left.lon, geobounds.top_right.lon],
[geobounds.bottom_left.lat, geobounds.top_right.lat]) [geobounds.bottom_left.lat, geobounds.top_right.lat])
def _proj4(self): def _proj4(self):
_proj4 = ("+proj=eqc +units=meters +a={} +b={} " _proj4 = ("+proj=eqc +units=meters +a={} +b={} "
"+lon_0={} +nadgrids=@null".format(Constants.WRF_EARTH_RADIUS, "+lon_0={} +nadgrids=@null".format(
Constants.WRF_EARTH_RADIUS,
Constants.WRF_EARTH_RADIUS, Constants.WRF_EARTH_RADIUS,
self.stand_lon)) self.stand_lon))
return _proj4 return _proj4
# Notes (may not be correct since this projection confuses me): # Notes (may not be correct since this projection confuses me):
# Each projection system handles this differently. # Each projection system handles this differently.
# 1) In WRF, if following the WPS instructions, POLE_LON is mainly used to # 1) In WRF, if following the WPS instructions, POLE_LON is mainly used to
@ -1059,7 +1030,6 @@ class RotatedLatLon(WrfProj):
self._cart_pole_lon = (-self.stand_lon - 180.0 if self._north self._cart_pole_lon = (-self.stand_lon - 180.0 if self._north
else -self.stand_lon) else -self.stand_lon)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
# Assuming this follows the same guidelines as cartopy # Assuming this follows the same guidelines as cartopy
@ -1070,7 +1040,6 @@ class RotatedLatLon(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -1092,7 +1061,6 @@ class RotatedLatLon(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
@ -1113,13 +1081,11 @@ class RotatedLatLon(WrfProj):
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
_cartopy = crs.RotatedPole( _cartopy = crs.RotatedPole(pole_longitude=self._cart_pole_lon,
pole_longitude=self._cart_pole_lon,
pole_latitude=self._bm_cart_pole_lat, pole_latitude=self._bm_cart_pole_lat,
central_rotated_longitude=( central_rotated_longitude=(
180.0 - self.pole_lon), # Probably 180.0 - self.pole_lon), # Probably
@ -1138,6 +1104,7 @@ class RotatedLatLon(WrfProj):
return _proj4 return _proj4
def getproj(**proj_params): def getproj(**proj_params):
"""Return a :class:`wrf.WrfProj` subclass. """Return a :class:`wrf.WrfProj` subclass.
@ -1164,7 +1131,6 @@ def getproj(**proj_params):
specified map projection parameters. specified map projection parameters.
""" """
up_proj_params = dict_keys_to_upper(proj_params) up_proj_params = dict_keys_to_upper(proj_params)
proj_type = up_proj_params.get("MAP_PROJ", 0) proj_type = up_proj_params.get("MAP_PROJ", 0)
@ -1184,5 +1150,3 @@ def getproj(**proj_params):
else: else:
# Unknown projection # Unknown projection
return WrfProj(**proj_params) return WrfProj(**proj_params)

1
src/wrf/projutils.py

@ -2,6 +2,7 @@ from __future__ import (absolute_import, division, print_function)
from .py3compat import viewitems from .py3compat import viewitems
def dict_keys_to_upper(d): def dict_keys_to_upper(d):
"""Return a dictionary with the keys changed to uppercase. """Return a dictionary with the keys changed to uppercase.

4
src/wrf/py3compat.py

@ -3,6 +3,7 @@ from __future__ import (absolute_import, division, print_function)
from sys import version_info from sys import version_info
from math import floor, copysign from math import floor, copysign
# Dictionary python 2-3 compatibility stuff # Dictionary python 2-3 compatibility stuff
def viewitems(d): def viewitems(d):
"""Return either the items or viewitems method for a dictionary. """Return either the items or viewitems method for a dictionary.
@ -57,6 +58,7 @@ def viewvalues(d):
func = d.values func = d.values
return func() return func()
def isstr(s): def isstr(s):
"""Return True if the object is a string type. """Return True if the object is a string type.
@ -165,5 +167,3 @@ def ucode(*args, **kwargs):
return str(*args, **kwargs) return str(*args, **kwargs)
return unicode(*args, **kwargs) return unicode(*args, **kwargs)

9
src/wrf/routines.py

@ -16,7 +16,8 @@ from .g_pressure import get_pressure, get_pressure_hpa
from .g_pw import get_pw from .g_pw import get_pw
from .g_rh import get_rh, get_rh_2m from .g_rh import get_rh, get_rh_2m
from .g_slp import get_slp from .g_slp import get_slp
from .g_temp import get_tc, get_eth, get_temp, get_theta, get_tk, get_tv, get_tw from .g_temp import (get_tc, get_eth, get_temp, get_theta, get_tk, get_tv,
get_tw)
from .g_terrain import get_terrain from .g_terrain import get_terrain
from .g_uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir, from .g_uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir,
get_uvmet_wspd_wdir, get_uvmet_wspd, get_uvmet_wdir, get_uvmet_wspd_wdir, get_uvmet_wspd, get_uvmet_wdir,
@ -194,6 +195,7 @@ _ALIASES = {"cape_2d" : "cape2d",
"wdir_uvmet10": "uvmet10_wdir", "wdir_uvmet10": "uvmet10_wdir",
} }
class ArgumentError(Exception): class ArgumentError(Exception):
def __init__(self, msg): def __init__(self, msg):
self.msg = msg self.msg = msg
@ -201,6 +203,7 @@ class ArgumentError(Exception):
def __str__(self): def __str__(self):
return self.msg return self.msg
def _undo_alias(alias): def _undo_alias(alias):
actual = _ALIASES.get(alias, None) actual = _ALIASES.get(alias, None)
if actual is None: if actual is None:
@ -208,6 +211,7 @@ def _undo_alias(alias):
else: else:
return actual return actual
def _check_kargs(var, kargs): def _check_kargs(var, kargs):
for arg in viewkeys(kargs): for arg in viewkeys(kargs):
if arg not in _VALID_KARGS[var]: if arg not in _VALID_KARGS[var]:
@ -340,10 +344,9 @@ def getvar(wrfin, varname, timeidx=0,
actual_var = _undo_alias(varname) actual_var = _undo_alias(varname)
if actual_var not in _VALID_KARGS: if actual_var not in _VALID_KARGS:
raise ValueError("'%s' is not a valid variable name" % (varname)) raise ValueError("'{}' is not a valid variable name".format(varname))
_check_kargs(actual_var, kwargs) _check_kargs(actual_var, kwargs)
return _FUNC_MAP[actual_var](wrfin, timeidx, method, squeeze, cache, return _FUNC_MAP[actual_var](wrfin, timeidx, method, squeeze, cache,
meta, _key, **kwargs) meta, _key, **kwargs)

15
src/wrf/specialdec.py

@ -99,8 +99,6 @@ def uvmet_left_iter(alg_dtype=np.float64):
if (u.shape[-1] == lat.shape[-1] or u.shape[-2] == lat.shape[-2]): if (u.shape[-1] == lat.shape[-1] or u.shape[-2] == lat.shape[-2]):
raise ValueError("v is staggered but u is not") raise ValueError("v is staggered but u is not")
# No special left side iteration, return the function result # No special left side iteration, return the function result
if (num_left_dims_u == 0): if (num_left_dims_u == 0):
return wrapped(u, v, lat, lon, cen_long, cone, isstag=is_stag, return wrapped(u, v, lat, lon, cen_long, cone, isstag=is_stag,
@ -132,15 +130,14 @@ def uvmet_left_iter(alg_dtype=np.float64):
lat_left_and_slice = left_and_slice_idxs lat_left_and_slice = left_and_slice_idxs
elif mode == 2: elif mode == 2:
# Only need the left-most # Only need the left-most
lat_left_and_slice = tuple(left_idx lat_left_and_slice = tuple(
for left_idx in left_idxs[0:num_left_dims_lat]) left_idx for left_idx in left_idxs[0:num_left_dims_lat])
u_output_idxs = (0,) + left_idxs + (slice(None),) u_output_idxs = (0,) + left_idxs + (slice(None),)
v_output_idxs = (1,) + left_idxs + (slice(None),) v_output_idxs = (1,) + left_idxs + (slice(None),)
u_view_idxs = left_idxs + (0, slice(None)) u_view_idxs = left_idxs + (0, slice(None))
v_view_idxs = left_idxs + (1, slice(None)) v_view_idxs = left_idxs + (1, slice(None))
new_u = u[left_and_slice_idxs] new_u = u[left_and_slice_idxs]
new_v = v[left_and_slice_idxs] new_v = v[left_and_slice_idxs]
new_lat = lat[lat_left_and_slice] new_lat = lat[lat_left_and_slice]
@ -186,7 +183,6 @@ def uvmet_left_iter(alg_dtype=np.float64):
return func_wrapper return func_wrapper
def cape_left_iter(alg_dtype=np.float64): def cape_left_iter(alg_dtype=np.float64):
"""A decorator to handle iterating over the leftmost dimensions for the """A decorator to handle iterating over the leftmost dimensions for the
cape diagnostic. cape diagnostic.
@ -374,7 +370,6 @@ def cape_left_iter(alg_dtype=np.float64):
capeview.__array_interface__["data"][0]): capeview.__array_interface__["data"][0]):
raise RuntimeError("output array was copied") raise RuntimeError("output array was copied")
if flip and not is2d: if flip and not is2d:
output[cape_output_idxs] = ( output[cape_output_idxs] = (
outview_array[view_cape_reverse_idxs].astype(orig_dtype)) outview_array[view_cape_reverse_idxs].astype(orig_dtype))
@ -441,8 +436,8 @@ def cloudfrac_left_iter(alg_dtype=np.float64):
return output return output
# Initial output is ...,low_mid_high,nz,ny,nx to create contiguous
# Initial output is ...,low_mid_high,nz,ny,nx to create contiguous views # views
outdims = vert.shape[0:num_left_dims] outdims = vert.shape[0:num_left_dims]
extra_dims = tuple(outdims) # Copy the left-most dims for iteration extra_dims = tuple(outdims) # Copy the left-most dims for iteration
@ -579,7 +574,6 @@ def interplevel_left_iter(is2dlev, alg_dtype=np.float64):
else: else:
z_slice_idxs = left_idxs + (slice(None),) z_slice_idxs = left_idxs + (slice(None),)
new_args[0] = field3d[field_out_slice_idxs] new_args[0] = field3d[field_out_slice_idxs]
new_args[1] = z[z_slice_idxs] new_args[1] = z[z_slice_idxs]
@ -695,4 +689,3 @@ def check_interplevel_args(is2dlev):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return func_wrapper return func_wrapper

14
src/wrf/units.py

@ -152,7 +152,6 @@ _UNIT_ALIASES = {"mps" : "m s-1",
"fps": "ft s-1", "fps": "ft s-1",
"fs-1": "ft s-1", "fs-1": "ft s-1",
"feet_per_second": "ft s-1", "feet_per_second": "ft s-1",
"pascal": "pa", "pascal": "pa",
"pascals": "pa", "pascals": "pa",
"hecto_pascal": "hpa", "hecto_pascal": "hpa",
@ -160,7 +159,6 @@ _UNIT_ALIASES = {"mps" : "m s-1",
"millibar": "mb", "millibar": "mb",
"millibars": "mb", "millibars": "mb",
"mbar": "mb", "mbar": "mb",
"kelvin": "k", "kelvin": "k",
"degree_kelvin": "k", "degree_kelvin": "k",
"degrees_kelvin": "k", "degrees_kelvin": "k",
@ -174,7 +172,6 @@ _UNIT_ALIASES = {"mps" : "m s-1",
"degs_k": "k", "degs_k": "k",
"deg k": "k", "deg k": "k",
"degs k": "k", "degs k": "k",
"celsius": "c", "celsius": "c",
"degree_celsius": "c", "degree_celsius": "c",
"degrees_celsius": "c", "degrees_celsius": "c",
@ -188,7 +185,6 @@ _UNIT_ALIASES = {"mps" : "m s-1",
"degs_c": "c", "degs_c": "c",
"deg c": "c", "deg c": "c",
"degs c": "c", "degs c": "c",
"fahrenheit": "f", "fahrenheit": "f",
"degree_fahrenheit": "f", "degree_fahrenheit": "f",
"degrees_fahrenheit": "f", "degrees_fahrenheit": "f",
@ -202,7 +198,6 @@ _UNIT_ALIASES = {"mps" : "m s-1",
"degs_f": "f", "degs_f": "f",
"deg f": "f", "deg f": "f",
"degs f": "f", "degs f": "f",
"meter": "m", "meter": "m",
"meters": "m", "meters": "m",
"metre": "m", "metre": "m",
@ -223,7 +218,6 @@ _UNIT_ALIASES = {"mps" : "m s-1",
"foot": "ft", "foot": "ft",
"mile": "mi", "mile": "mi",
"miles": "mi" "miles": "mi"
} }
# A mapping of unit types to the avaible units # A mapping of unit types to the avaible units
@ -298,6 +292,7 @@ _TEMP_CONV_METHODS = {"c" : _k_to_c,
"f": _k_to_f "f": _k_to_f
} }
def dealias_and_clean_unit(unit): def dealias_and_clean_unit(unit):
"""Return the properly cleaned and dealiased unit name. """Return the properly cleaned and dealiased unit name.
@ -336,7 +331,7 @@ def check_units(unit, unit_type):
""" """
u_cleaned = dealias_and_clean_unit(unit) u_cleaned = dealias_and_clean_unit(unit)
if u_cleaned not in _VALID_UNITS[unit_type]: if u_cleaned not in _VALID_UNITS[unit_type]:
raise ValueError("invalid unit type '%s'" % unit) raise ValueError("invalid unit type '{}'".format(unit))
def do_conversion(var, vartype, var_unit, dest_unit): def do_conversion(var, vartype, var_unit, dest_unit):
@ -365,8 +360,3 @@ def do_conversion(var, vartype, var_unit, dest_unit):
return _apply_conv_fact(var, vartype, var_unit.lower(), u_cleaned) return _apply_conv_fact(var, vartype, var_unit.lower(), u_cleaned)
else: else:
return _apply_temp_conv(var, var_unit.lower(), u_cleaned) return _apply_temp_conv(var, var_unit.lower(), u_cleaned)

77
src/wrf/util.py

@ -214,7 +214,7 @@ def _generator_copy(gen):
res = module.get(funcname)(**argd) res = module.get(funcname)(**argd)
except AttributeError: except AttributeError:
res = getattr(module, funcname)(**argd) res = getattr(module, funcname)(**argd)
except: except Exception:
# This is the old way it used to work, but it looks like this was # This is the old way it used to work, but it looks like this was
# fixed by Python. # fixed by Python.
try: try:
@ -228,7 +228,7 @@ def _generator_copy(gen):
try: try:
argd = {key: argvals.locals[key] for key in argvals.args} argd = {key: argvals.locals[key] for key in argvals.args}
res = getattr(__main__, funcname)(**argd) res = getattr(__main__, funcname)(**argd)
except: except Exception:
# This was the old way it used to work, but appears to have # This was the old way it used to work, but appears to have
# been fixed by Python. # been fixed by Python.
res = getattr(__main__, funcname)(**argvals.locals) res = getattr(__main__, funcname)(**argvals.locals)
@ -669,10 +669,10 @@ def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar):
end_idxs[-3] = i end_idxs[-3] = i
end_idxs = tuple(end_idxs) end_idxs = tuple(end_idxs)
if (first_ll_corner[0] != lats[start_idxs] or if (first_ll_corner[0] != lats[start_idxs]
first_ll_corner[1] != lons[start_idxs] or or first_ll_corner[1] != lons[start_idxs]
first_ur_corner[0] != lats[end_idxs] or or first_ur_corner[0] != lats[end_idxs]
first_ur_corner[1] != lons[end_idxs]): or first_ur_corner[1] != lons[end_idxs]):
return True return True
return False return False
@ -913,9 +913,10 @@ def extract_dim(wrfin, dim):
# figure it out # figure it out
try: try:
s = wrfin.variables["P"].shape s = wrfin.variables["P"].shape
return s[-4] except Exception:
except:
raise ValueError("unsupported NetCDF reader") raise ValueError("unsupported NetCDF reader")
else:
return s[-4]
return d # PyNIO return d # PyNIO
@ -1044,12 +1045,10 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta, _key):
# make it so that key_0 is leftmost # make it so that key_0 is leftmost
outdims = key_coordnames + list(first_array.dims[existing_cnt:]) outdims = key_coordnames + list(first_array.dims[existing_cnt:])
# Create the new 'key_n', value pairs # Create the new 'key_n', value pairs
for coordname, coordval in zip(key_coordnames, coord_vals): for coordname, coordval in zip(key_coordnames, coord_vals):
outcoords[coordname] = coordval outcoords[coordname] = coordval
outattrs = OrderedDict(first_array.attrs) outattrs = OrderedDict(first_array.attrs)
outarr = DataArray(outdata, name=outname, coords=outcoords, outarr = DataArray(outdata, name=outname, coords=outcoords,
@ -1292,7 +1291,6 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile,
except IndexError: except IndexError:
pass pass
coords = OrderedDict() coords = OrderedDict()
# Handle lat/lon coordinates and projection information if available # Handle lat/lon coordinates and projection information if available
@ -1618,8 +1616,6 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
return _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta, return _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta,
_key) _key)
#time_idx_or_slice = timeidx if not multitime else slice(None)
# If all times are requested, need to build a new array and cat together # If all times are requested, need to build a new array and cat together
# all of the arrays in the sequence # all of the arrays in the sequence
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
@ -1665,7 +1661,8 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
outxtimes = get_cached_item(_key, timekey) outxtimes = get_cached_item(_key, timekey)
if outxtimes is None: if outxtimes is None:
outxtimes = np.empty(outdims[0]) outxtimes = np.empty(outdims[0])
outxtimes[startidx:endidx] = to_np(first_var.coords[timename][:]) outxtimes[startidx:endidx] = to_np(
first_var.coords[timename][:])
else: else:
timecached = True timecached = True
@ -1677,7 +1674,8 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
outlats = get_cached_item(_key, latkey) outlats = get_cached_item(_key, latkey)
if outlats is None: if outlats is None:
outlats = np.empty(outcoorddims, first_var.dtype) outlats = np.empty(outcoorddims, first_var.dtype)
outlats[startidx:endidx, :] = to_np(first_var.coords[latname][:]) outlats[startidx:endidx, :] = to_np(
first_var.coords[latname][:])
else: else:
latcached = True latcached = True
@ -1685,11 +1683,11 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
outlons = get_cached_item(_key, lonkey) outlons = get_cached_item(_key, lonkey)
if outlons is None: if outlons is None:
outlons = np.empty(outcoorddims, first_var.dtype) outlons = np.empty(outcoorddims, first_var.dtype)
outlons[startidx:endidx, :] = to_np(first_var.coords[lonname][:]) outlons[startidx:endidx, :] = to_np(
first_var.coords[lonname][:])
else: else:
loncached = True loncached = True
startidx = endidx startidx = endidx
while True: while True:
try: try:
@ -1964,8 +1962,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key):
# For join, the times are a function of fileidx # For join, the times are a function of fileidx
file_times = extract_times(wrfnc, ALL_TIMES, meta=False, file_times = extract_times(wrfnc, ALL_TIMES, meta=False,
do_xtime=False) do_xtime=False)
time_coord[file_idx, 0:numtimes] = np.asarray(file_times, time_coord[file_idx, 0:numtimes] = np.asarray(
"datetime64[ns]")[:] file_times, "datetime64[ns]")[:]
if timename is not None and not timecached: if timename is not None and not timecached:
xtimedata = wrfnc.variables[timename][:] xtimedata = wrfnc.variables[timename][:]
@ -2514,10 +2512,8 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None,
outname = "XTIME" outname = "XTIME"
outarr = DataArray(time_arr, name=outname, coords=outcoords, outarr = DataArray(time_arr, name=outname, coords=outcoords,
dims=outdimnames, attrs=outattrs) dims=outdimnames, attrs=outattrs)
else: else:
outarr = time_arr outarr = time_arr
@ -2670,7 +2666,7 @@ def get_right_slices(var, right_ndims, fixed_val=0):
[slice(None)]*right_ndims) [slice(None)]*right_ndims)
def get_proj_params(wrfin):#, timeidx=0, varname=None): def get_proj_params(wrfin):
"""Return a tuple of latitude, longitude, and projection parameters from """Return a tuple of latitude, longitude, and projection parameters from
a WRF output file object or a sequence of WRF output file objects. a WRF output file object or a sequence of WRF output file objects.
@ -2697,7 +2693,8 @@ def get_proj_params(wrfin):#, timeidx=0, varname=None):
longitude coordinate, and global projection attributes. longitude coordinate, and global projection attributes.
""" """
proj_params = extract_global_attrs(wrfin, attrs=("MAP_PROJ", proj_params = extract_global_attrs(wrfin,
attrs=("MAP_PROJ",
"CEN_LAT", "CEN_LON", "CEN_LAT", "CEN_LON",
"TRUELAT1", "TRUELAT2", "TRUELAT1", "TRUELAT2",
"MOAD_CEN_LAT", "STAND_LON", "MOAD_CEN_LAT", "STAND_LON",
@ -2710,7 +2707,7 @@ def get_proj_params(wrfin):#, timeidx=0, varname=None):
def from_args(func, argnames, *args, **kwargs): def from_args(func, argnames, *args, **kwargs):
"""Return a mapping of argument name to value for the called function. """Return a mapping of argument name to value for the called function.
This function parses the function \*args and \*\*kwargs to obtain the \ This function parses the function args and kwargs to obtain the
desired argument value. If the argument has not been passed in, the value desired argument value. If the argument has not been passed in, the value
is taken from the default keyword argument value. is taken from the default keyword argument value.
@ -2719,7 +2716,7 @@ def from_args(func, argnames, *args, **kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
\*args or \*\*kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -2763,7 +2760,7 @@ def _args_to_list2(func, args, kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
*args or **kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -2850,7 +2847,7 @@ def _args_to_list3(func, args, kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
*args or **kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -2885,7 +2882,7 @@ def args_to_list(func, args, kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
\*args or \*\*kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -3039,13 +3036,27 @@ def psafilepath():
def get_filepath(obj): def get_filepath(obj):
"""Return the file path for the specified object.
This is used to return the file path for a netcdf object. If the
particular object does not have the appropriate file path information,
then one is created based on the timestep in the file.
Args:
obj: An object.
Returns:
:obj:`str`: A string for a file path.
"""
try: try:
path = obj.filepath() path = obj.filepath()
except AttributeError: except AttributeError:
try: try:
path = obj.file.path path = obj.file.path
except: except AttributeError:
# Let's make up a filename from the first file time # Let's make up a filename from the first file time
found = False found = False
times = extract_times(obj, None, meta=False, do_xtime=False) times = extract_times(obj, None, meta=False, do_xtime=False)
@ -3059,6 +3070,7 @@ def get_filepath(obj):
return path return path
def get_id(obj, prefix=''): def get_id(obj, prefix=''):
"""Return the cache id. """Return the cache id.
@ -3234,6 +3246,7 @@ def geo_bounds(var=None, wrfin=None, varname=None, timeidx=0, method="cat",
# Non-moving domains # Non-moving domains
return GeoBounds(lats=lats, lons=lons) return GeoBounds(lats=lats, lons=lons)
def _get_wrf_proj_geobnds(var, wrfin, varname, timeidx, method, squeeze, def _get_wrf_proj_geobnds(var, wrfin, varname, timeidx, method, squeeze,
cache): cache):
"""Return the :class:`wrf.WrfProj` subclass and :class:`wrf.GeoBounds`. """Return the :class:`wrf.WrfProj` subclass and :class:`wrf.GeoBounds`.
@ -3737,7 +3750,8 @@ def cartopy_xlim(var=None, geobounds=None, wrfin=None, varname=None, timeidx=0,
""" """
wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname, wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname,
timeidx, method, squeeze, cache) timeidx, method, squeeze,
cache)
if geobounds is not None: if geobounds is not None:
return wrf_proj.cartopy_xlim(geobounds) return wrf_proj.cartopy_xlim(geobounds)
@ -3823,7 +3837,8 @@ def cartopy_ylim(var=None, geobounds=None, wrfin=None, varname=None, timeidx=0,
""" """
wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname, wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname,
timeidx, method, squeeze, cache) timeidx, method, squeeze,
cache)
if geobounds is not None: if geobounds is not None:
return wrf_proj.cartopy_ylim(geobounds) return wrf_proj.cartopy_ylim(geobounds)

1
src/wrf/version.py

@ -1,2 +1 @@
__version__ = "1.3.1" __version__ = "1.3.1"

Loading…
Cancel
Save