forked from 3rdparty/wrf-python
37 changed files with 1764 additions and 989 deletions
@ -0,0 +1,182 @@ |
|||||||
|
from __future__ import (absolute_import, division, print_function, |
||||||
|
unicode_literals) |
||||||
|
|
||||||
|
from math import floor, ceil |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
|
||||||
|
from .extension import interp2dxy |
||||||
|
|
||||||
|
__all__ = ["to_positive_idxs", "calc_xy", "get_xy_z_params", "get_xy"] |
||||||
|
|
||||||
|
def to_positive_idxs(shape, coord): |
||||||
|
if (coord[-2] >= 0 and coord[-1] >= 0): |
||||||
|
return coord |
||||||
|
|
||||||
|
return [x if (x >= 0) else shape[i]+x for (i,x) in enumerate(coord) ] |
||||||
|
|
||||||
|
def calc_xy(xdim, ydim, pivot_point=None, angle=None, |
||||||
|
start_point=None, end_point=None): |
||||||
|
"""Returns the x,y points for the horizontal cross section line. |
||||||
|
|
||||||
|
xdim - maximum x-dimension |
||||||
|
ydim - maximum y-dimension |
||||||
|
pivot_point - a pivot point of (south_north, west_east) |
||||||
|
(must be used with angle) |
||||||
|
angle - the angle through the pivot point in degrees |
||||||
|
start_point - a start_point sequence of [south_north1, west_east1] |
||||||
|
end_point - an end point sequence of [south_north2, west_east2] |
||||||
|
|
||||||
|
""" |
||||||
|
|
||||||
|
# Have a pivot point with an angle to find cross section |
||||||
|
if pivot_point is not None and angle is not None: |
||||||
|
xp = pivot_point[-1] |
||||||
|
yp = pivot_point[-2] |
||||||
|
|
||||||
|
if (angle > 315.0 or angle < 45.0 |
||||||
|
or ((angle > 135.0) and (angle < 225.0))): |
||||||
|
|
||||||
|
#x = y*slope + intercept |
||||||
|
slope = -(360.-angle)/45. |
||||||
|
if( angle < 45. ): |
||||||
|
slope = angle/45. |
||||||
|
if( angle > 135.): |
||||||
|
slope = (angle-180.)/45. |
||||||
|
|
||||||
|
intercept = xp - yp*slope |
||||||
|
|
||||||
|
# find intersections with domain boundaries |
||||||
|
y0 = 0. |
||||||
|
x0 = y0*slope + intercept |
||||||
|
|
||||||
|
if( x0 < 0.): # intersect outside of left boundary |
||||||
|
x0 = 0. |
||||||
|
y0 = (x0 - intercept)/slope |
||||||
|
if( x0 > xdim-1): #intersect outside of right boundary |
||||||
|
x0 = xdim-1 |
||||||
|
y0 = (x0 - intercept)/slope |
||||||
|
y1 = ydim-1. #need to make sure this will be a float? |
||||||
|
x1 = y1*slope + intercept |
||||||
|
|
||||||
|
if( x1 < 0.): # intersect outside of left boundary |
||||||
|
x1 = 0. |
||||||
|
y1 = (x1 - intercept)/slope |
||||||
|
|
||||||
|
if( x1 > xdim-1): # intersect outside of right boundary |
||||||
|
x1 = xdim-1 |
||||||
|
y1 = (x1 - intercept)/slope |
||||||
|
else: |
||||||
|
# y = x*slope + intercept |
||||||
|
slope = (90.-angle)/45. |
||||||
|
if( angle > 225. ): |
||||||
|
slope = (270.-angle)/45. |
||||||
|
intercept = yp - xp*slope |
||||||
|
|
||||||
|
#find intersections with domain boundaries |
||||||
|
x0 = 0. |
||||||
|
y0 = x0*slope + intercept |
||||||
|
|
||||||
|
if( y0 < 0.): # intersect outside of bottom boundary |
||||||
|
y0 = 0. |
||||||
|
x0 = (y0 - intercept)/slope |
||||||
|
|
||||||
|
if( y0 > ydim-1): # intersect outside of top boundary |
||||||
|
y0 = ydim-1 |
||||||
|
x0 = (y0 - intercept)/slope |
||||||
|
|
||||||
|
x1 = xdim-1. # need to make sure this will be a float? |
||||||
|
y1 = x1*slope + intercept |
||||||
|
|
||||||
|
if( y1 < 0.): # intersect outside of bottom boundary |
||||||
|
y1 = 0. |
||||||
|
x1 = (y1 - intercept)/slope |
||||||
|
|
||||||
|
if( y1 > ydim-1):# intersect outside of top boundary |
||||||
|
y1 = ydim-1 |
||||||
|
x1 = (y1 - intercept)/slope |
||||||
|
elif start_point is not None and end_point is not None: |
||||||
|
x0 = start_point[-1] |
||||||
|
y0 = start_point[-2] |
||||||
|
x1 = end_point[-1] |
||||||
|
y1 = end_point[-2] |
||||||
|
if ( x1 > xdim-1 ): |
||||||
|
x1 = xdim |
||||||
|
if ( y1 > ydim-1): |
||||||
|
y1 = ydim |
||||||
|
else: |
||||||
|
raise ValueError("invalid start/end or pivot/angle arguments") |
||||||
|
|
||||||
|
dx = x1 - x0 |
||||||
|
dy = y1 - y0 |
||||||
|
distance = (dx*dx + dy*dy)**0.5 |
||||||
|
npts = int(distance) |
||||||
|
dxy = distance/npts |
||||||
|
|
||||||
|
xy = np.zeros((npts,2), "float") |
||||||
|
|
||||||
|
dx = dx/npts |
||||||
|
dy = dy/npts |
||||||
|
|
||||||
|
for i in xrange(npts): |
||||||
|
xy[i,0] = x0 + i*dx |
||||||
|
xy[i,1] = y0 + i*dy |
||||||
|
|
||||||
|
return xy |
||||||
|
|
||||||
|
def get_xy_z_params(z, pivot_point=None, angle=None, |
||||||
|
start_point=None, end_point=None): |
||||||
|
|
||||||
|
xy = get_xy(z, pivot_point, angle, start_point, end_point) |
||||||
|
|
||||||
|
# Interp z |
||||||
|
var2dz = interp2dxy(z, xy) |
||||||
|
|
||||||
|
extra_dim_num = z.ndim - 3 |
||||||
|
idx1 = tuple([0]*extra_dim_num + [0,0]) |
||||||
|
idx2 = tuple([0]*extra_dim_num + [-1,0]) |
||||||
|
|
||||||
|
# interp to constant z grid |
||||||
|
if(var2dz[idx1] > var2dz[idx2]): # monotonically decreasing coordinate |
||||||
|
z_max = floor(np.amax(z) / 10) * 10 # bottom value |
||||||
|
z_min = ceil(np.amin(z) / 10) * 10 # top value |
||||||
|
dz = 10 |
||||||
|
nlevels = int((z_max-z_min) / dz) |
||||||
|
z_var2d = np.zeros((nlevels), dtype=z.dtype) |
||||||
|
z_var2d[0] = z_max |
||||||
|
dz = -dz |
||||||
|
else: |
||||||
|
z_max = np.amax(z) |
||||||
|
z_min = 0. |
||||||
|
dz = 0.01 * z_max |
||||||
|
nlevels = int(z_max / dz) |
||||||
|
z_var2d = np.zeros((nlevels), dtype=z.dtype) |
||||||
|
z_var2d[0] = z_min |
||||||
|
|
||||||
|
for i in xrange(1,nlevels): |
||||||
|
z_var2d[i] = z_var2d[0] + i*dz |
||||||
|
|
||||||
|
return xy, var2dz, z_var2d |
||||||
|
|
||||||
|
def get_xy(var, pivot_point=None, angle=None, start_point=None, end_point=None): |
||||||
|
if pivot_point is not None: |
||||||
|
pos_pivot = to_positive_idxs(var.shape[-2:], pivot_point) |
||||||
|
else: |
||||||
|
pos_pivot = pivot_point |
||||||
|
|
||||||
|
if start_point is not None: |
||||||
|
pos_start = to_positive_idxs(var.shape[-2:], start_point) |
||||||
|
else: |
||||||
|
pos_start = start_point |
||||||
|
|
||||||
|
if end_point is not None: |
||||||
|
pos_end = to_positive_idxs(var.shape[-2:], end_point) |
||||||
|
else: |
||||||
|
pos_end = start_point |
||||||
|
|
||||||
|
xdim = var.shape[-1] |
||||||
|
ydim = var.shape[-2] |
||||||
|
|
||||||
|
xy = calc_xy(xdim, ydim, pos_pivot, angle, pos_start, pos_end) |
||||||
|
|
||||||
|
return xy |
@ -0,0 +1,695 @@ |
|||||||
|
from __future__ import (absolute_import, division, print_function, |
||||||
|
unicode_literals) |
||||||
|
import wrapt |
||||||
|
from collections import OrderedDict |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
import numpy.ma as ma |
||||||
|
|
||||||
|
|
||||||
|
from .util import (viewkeys, viewitems, extract_vars, |
||||||
|
combine_with, either, from_args, arg_location, |
||||||
|
_is_coord_var, XYCoord, npvalues) |
||||||
|
from .interputils import get_xy_z_params, get_xy |
||||||
|
from .config import xarray_enabled |
||||||
|
|
||||||
|
if xarray_enabled(): |
||||||
|
from xarray import DataArray |
||||||
|
|
||||||
|
__all__ = ["copy_and_set_metadata", "set_wind_metadata", |
||||||
|
"set_latlon_metadata", "set_height_metadata", |
||||||
|
"set_interp_metadata"] |
||||||
|
|
||||||
|
def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=None, |
||||||
|
remove_dims=None, dimnames=None, |
||||||
|
coords=None, **fixed_attrs): |
||||||
|
"""Decorator to set the metadata for a WRF method. |
||||||
|
|
||||||
|
A cache is inserted/updated to include the extracted variable that will |
||||||
|
have its metadata copied. This prevents the variable being extracted more |
||||||
|
than once. This extraction can be slow with sequences of large files. |
||||||
|
|
||||||
|
""" |
||||||
|
@wrapt.decorator |
||||||
|
def func_wrapper(wrapped, instance, args, kwargs): |
||||||
|
if not xarray_enabled(): |
||||||
|
return wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", |
||||||
|
"squeeze", "cache", "units"), |
||||||
|
*args, **kwargs) |
||||||
|
wrfnc = argvars["wrfnc"] |
||||||
|
timeidx = argvars["timeidx"] |
||||||
|
units = argvars["units"] |
||||||
|
method = argvars["method"] |
||||||
|
squeeze = argvars["squeeze"] |
||||||
|
cache = argvars["cache"] |
||||||
|
if cache is None: |
||||||
|
cache = {} |
||||||
|
|
||||||
|
# Note: can't modify nonlocal var |
||||||
|
if (callable(copy_varname)): |
||||||
|
_copy_varname = copy_varname(wrfnc) |
||||||
|
else: |
||||||
|
_copy_varname = copy_varname |
||||||
|
|
||||||
|
# Extract the copy_from argument |
||||||
|
var_to_copy = None if cache is None else cache.get(_copy_varname, |
||||||
|
None) |
||||||
|
|
||||||
|
|
||||||
|
if var_to_copy is None: |
||||||
|
var_to_copy = extract_vars(wrfnc, timeidx, (_copy_varname,), |
||||||
|
method, squeeze, cache, |
||||||
|
nometa=False)[_copy_varname] |
||||||
|
|
||||||
|
# Make a copy so we don't modify a user supplied cache |
||||||
|
new_cache = dict(cache) |
||||||
|
new_cache[_copy_varname] = var_to_copy |
||||||
|
|
||||||
|
# Don't modify the original args/kargs. The args need to be a list |
||||||
|
# so it can be modified. |
||||||
|
new_args, cache_argloc = arg_location(wrapped, "cache", args, kwargs) |
||||||
|
new_args[cache_argloc] = new_cache |
||||||
|
|
||||||
|
result = wrapped(*new_args) |
||||||
|
|
||||||
|
outname = "" |
||||||
|
outdimnames = list() |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
|
||||||
|
if copy_varname is not None: |
||||||
|
outname = unicode(var_to_copy.name) |
||||||
|
|
||||||
|
if dimnames is not None: |
||||||
|
if isinstance(dimnames, combine_with): |
||||||
|
outdimnames, outcoords = dimnames(var_to_copy) |
||||||
|
else: |
||||||
|
outdimnames = dimnames |
||||||
|
outcoords = coords |
||||||
|
else: |
||||||
|
outdimnames += var_to_copy.dims |
||||||
|
outcoords.update(var_to_copy.coords) |
||||||
|
|
||||||
|
outattrs.update(var_to_copy.attrs) |
||||||
|
|
||||||
|
if remove_dims is not None: |
||||||
|
for dimname in remove_dims: |
||||||
|
outdimnames.remove(dimname) |
||||||
|
|
||||||
|
try: |
||||||
|
del outcoords[dimname] |
||||||
|
except KeyError: |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
if name is not None: |
||||||
|
outname = name |
||||||
|
|
||||||
|
if units is not None: |
||||||
|
outattrs["units"] = units |
||||||
|
|
||||||
|
for argname, val in viewitems(fixed_attrs): |
||||||
|
outattrs[argname] = val |
||||||
|
|
||||||
|
if delete_attrs is not None: |
||||||
|
for attr in delete_attrs: |
||||||
|
try: |
||||||
|
del outattrs[attr] |
||||||
|
except KeyError: |
||||||
|
pass |
||||||
|
|
||||||
|
if isinstance(result, ma.MaskedArray): |
||||||
|
outattrs["_FillValue"] = result.fill_value |
||||||
|
outattrs["missing_value"] = result.fill_value |
||||||
|
|
||||||
|
return DataArray(result, name=outname, coords=outcoords, |
||||||
|
dims=outdimnames, attrs=outattrs) |
||||||
|
|
||||||
|
return func_wrapper |
||||||
|
|
||||||
|
|
||||||
|
def set_wind_metadata(copy_varname, name, description, |
||||||
|
wind_ncvar=False, |
||||||
|
two_d=False, wspd_wdir=False): |
||||||
|
@wrapt.decorator |
||||||
|
def func_wrapper(wrapped, instance, args, kwargs): |
||||||
|
if not xarray_enabled(): |
||||||
|
return wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
argvars = from_args(wrapped, ("wrfnc", "timeidx", "units", |
||||||
|
"method", "squeeze", "ten_m", "cache"), |
||||||
|
*args, **kwargs) |
||||||
|
wrfnc = argvars["wrfnc"] |
||||||
|
timeidx = argvars["timeidx"] |
||||||
|
units = argvars["units"] |
||||||
|
method = argvars["method"] |
||||||
|
squeeze = argvars["squeeze"] |
||||||
|
ten_m = argvars["ten_m"] |
||||||
|
cache = argvars["cache"] |
||||||
|
if cache is None: |
||||||
|
cache = {} |
||||||
|
|
||||||
|
if isinstance(copy_varname, either): |
||||||
|
_copy_varname = copy_varname(wrfnc) |
||||||
|
else: |
||||||
|
_copy_varname = copy_varname |
||||||
|
|
||||||
|
copy_var = extract_vars(wrfnc, timeidx, _copy_varname, |
||||||
|
method, squeeze, cache, |
||||||
|
nometa=False)[_copy_varname] |
||||||
|
|
||||||
|
# Make a copy so we don't modify a user supplied cache |
||||||
|
new_cache = dict(cache) |
||||||
|
new_cache[_copy_varname] = copy_var |
||||||
|
|
||||||
|
# Don't modify the original args/kargs. The args need to be a list |
||||||
|
# so it can be modified. |
||||||
|
new_args, cache_argloc = arg_location(wrapped, "cache", args, kwargs) |
||||||
|
new_args[cache_argloc] = new_cache |
||||||
|
|
||||||
|
result = wrapped(*new_args) |
||||||
|
|
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
|
||||||
|
outdimnames = list(copy_var.dims) |
||||||
|
outcoords.update(copy_var.coords) |
||||||
|
outattrs.update(copy_var.attrs) |
||||||
|
|
||||||
|
if wind_ncvar: |
||||||
|
pass |
||||||
|
|
||||||
|
elif not wspd_wdir: |
||||||
|
if not two_d: |
||||||
|
outdimnames.insert(-3, "u_v") |
||||||
|
else: |
||||||
|
outdimnames.insert(-2, "u_v") |
||||||
|
outattrs["MemoryOrder"] = "XY" |
||||||
|
outcoords["u_v"] = ["u", "v"] |
||||||
|
else: |
||||||
|
if not two_d: |
||||||
|
outdimnames.insert(-3, "wspd_wdir") |
||||||
|
else: |
||||||
|
outdimnames.insert(-2, "wspd_wdir") |
||||||
|
outattrs["MemoryOrder"] = "XY" |
||||||
|
|
||||||
|
outcoords["wspd_wdir"] = ["wspd", "wdir"] |
||||||
|
|
||||||
|
if units is not None: |
||||||
|
outattrs["units"] = units |
||||||
|
|
||||||
|
outname = name |
||||||
|
outattrs["description"] = description |
||||||
|
|
||||||
|
return DataArray(result, name=outname, coords=outcoords, |
||||||
|
dims=outdimnames, attrs=outattrs) |
||||||
|
|
||||||
|
return func_wrapper |
||||||
|
|
||||||
|
def set_latlon_metadata(ij=False): |
||||||
|
@wrapt.decorator |
||||||
|
def func_wrapper(wrapped, instance, args, kwargs): |
||||||
|
if not xarray_enabled(): |
||||||
|
return wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
res = wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
argnames = ("latitude", "longitude") if not ij else ("i", "j") |
||||||
|
outname = "latlon" if not ij else "ij" |
||||||
|
|
||||||
|
if res.ndim <= 2: |
||||||
|
dimnames = (["lat_lon", "i_j"] if not ij |
||||||
|
else ["i_j", "lat_lon"]) |
||||||
|
else: |
||||||
|
dimnames = (["lat_lon", "domain", "i_j"] if not ij |
||||||
|
else ["i_j", "domain", "lat_lon"]) |
||||||
|
|
||||||
|
|
||||||
|
argvars = from_args(wrapped, argnames, *args, **kwargs) |
||||||
|
|
||||||
|
var1 = argvars[argnames[0]] |
||||||
|
var2 = argvars[argnames[1]] |
||||||
|
|
||||||
|
arr1 = np.asarray(var1).ravel() |
||||||
|
arr2 = np.asarray(var2).ravel() |
||||||
|
|
||||||
|
coords = {} |
||||||
|
coords[dimnames[0]] = [x for x in zip(arr1, arr2)] |
||||||
|
|
||||||
|
return DataArray(res, name=outname, dims=dimnames, coords=coords) |
||||||
|
|
||||||
|
return func_wrapper |
||||||
|
|
||||||
|
def set_height_metadata(geopt=False): |
||||||
|
@wrapt.decorator |
||||||
|
def func_wrapper(wrapped, instance, args, kwargs): |
||||||
|
if not xarray_enabled(): |
||||||
|
return wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", |
||||||
|
"squeeze", "units", "msl", "cache"), |
||||||
|
*args, **kwargs) |
||||||
|
wrfnc = argvars["wrfnc"] |
||||||
|
timeidx = argvars["timeidx"] |
||||||
|
units = argvars["units"] |
||||||
|
method = argvars["method"] |
||||||
|
squeeze = argvars["squeeze"] |
||||||
|
msl = argvars["msl"] |
||||||
|
cache = argvars["cache"] |
||||||
|
|
||||||
|
if cache is None: |
||||||
|
cache = {} |
||||||
|
|
||||||
|
# For height, either copy the met_em GHT variable or copy and modify |
||||||
|
# pressure (which has the same dims as destaggered height) |
||||||
|
ht_metadata_varname = either("P", "GHT")(wrfnc) |
||||||
|
ht_var = extract_vars(wrfnc, timeidx, ht_metadata_varname, |
||||||
|
method, squeeze, cache, nometa=False) |
||||||
|
ht_metadata_var = ht_var[ht_metadata_varname] |
||||||
|
|
||||||
|
# Make a copy so we don't modify a user supplied cache |
||||||
|
new_cache = dict(cache) |
||||||
|
new_cache[ht_metadata_varname] = ht_metadata_var |
||||||
|
|
||||||
|
# Don't modify the original args/kargs. The args need to be a list |
||||||
|
# so it can be modified. |
||||||
|
new_args, cache_argloc = arg_location(wrapped, "cache", args, kwargs) |
||||||
|
new_args[cache_argloc] = new_cache |
||||||
|
|
||||||
|
result = wrapped(*new_args) |
||||||
|
|
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(ht_metadata_var.dims) |
||||||
|
outcoords.update(ht_metadata_var.coords) |
||||||
|
outattrs.update(ht_metadata_var.attrs) |
||||||
|
|
||||||
|
if geopt: |
||||||
|
outname = "geopt" |
||||||
|
outattrs["units"] = "m2 s-2" |
||||||
|
outattrs["description"] = "full model geopotential" |
||||||
|
else: |
||||||
|
outname = "height" if msl else "height_agl" |
||||||
|
outattrs["units"] = units |
||||||
|
height_type = "MSL" if msl else "AGL" |
||||||
|
outattrs["description"] = "model height ({})".format(height_type) |
||||||
|
|
||||||
|
|
||||||
|
return DataArray(result, name=outname, |
||||||
|
dims=outdimnames, coords=outcoords, attrs=outattrs) |
||||||
|
return func_wrapper |
||||||
|
|
||||||
|
def _set_horiz_meta(wrapped, instance, args, kwargs): |
||||||
|
argvars = from_args(wrapped, ("field3d", "z", "desiredloc", |
||||||
|
"missingval"), |
||||||
|
*args, **kwargs) |
||||||
|
|
||||||
|
field3d = argvars["field3d"] |
||||||
|
z = argvars["z"] |
||||||
|
desiredloc = argvars["desiredloc"] |
||||||
|
missingval = argvars["missingval"] |
||||||
|
|
||||||
|
result = wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
# Defaults, in case the data isn't a DataArray |
||||||
|
outname = None |
||||||
|
outdimnames = None |
||||||
|
outcoords = None |
||||||
|
outattrs = None |
||||||
|
|
||||||
|
# Get the vertical level units |
||||||
|
vert_units = None |
||||||
|
if isinstance(z, DataArray): |
||||||
|
vert_units = z.attrs.get("units", None) |
||||||
|
|
||||||
|
# If we have no metadata to start with, only set the level |
||||||
|
levelstr = ("{0} {1}".format(desiredloc, vert_units) |
||||||
|
if vert_units is not None |
||||||
|
else "{0}".format(desiredloc)) |
||||||
|
|
||||||
|
if isinstance(field3d, DataArray): |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(field3d.dims) |
||||||
|
outcoords.update(field3d.coords) |
||||||
|
outdimnames.remove(field3d.dims[-3]) |
||||||
|
del outcoords[field3d.dims[-3]] |
||||||
|
outattrs.update(field3d.attrs) |
||||||
|
outname = "{0}_{1}".format(field3d.name, levelstr) |
||||||
|
|
||||||
|
else: |
||||||
|
outname = "field3d_{0}".format(levelstr) |
||||||
|
outattrs = OrderedDict() |
||||||
|
|
||||||
|
outattrs["PlotLevelID"] = levelstr |
||||||
|
outattrs["missing_value"] = missingval |
||||||
|
outattrs["_FillValue"] = missingval |
||||||
|
|
||||||
|
for key in ("MemoryOrder", "description"): |
||||||
|
try: |
||||||
|
del outattrs[key] |
||||||
|
except KeyError: |
||||||
|
pass |
||||||
|
|
||||||
|
return DataArray(result, name=outname, dims=outdimnames, |
||||||
|
coords=outcoords, attrs=outattrs) |
||||||
|
|
||||||
|
def _set_cross_meta(wrapped, instance, args, kwargs): |
||||||
|
argvars = from_args(wrapped, ("field3d", "z", "missingval", |
||||||
|
"pivot_point", "angle", |
||||||
|
"start_point", "end_point", |
||||||
|
"cache"), |
||||||
|
*args, **kwargs) |
||||||
|
|
||||||
|
field3d = argvars["field3d"] |
||||||
|
z = argvars["z"] |
||||||
|
missingval = argvars["missingval"] |
||||||
|
pivot_point = argvars["pivot_point"] |
||||||
|
angle = argvars["angle"] |
||||||
|
start_point = argvars["start_point"] |
||||||
|
end_point = argvars["end_point"] |
||||||
|
cache = argvars["cache"] |
||||||
|
|
||||||
|
xy, var2dz, z_var2d = get_xy_z_params(npvalues(z), pivot_point, angle, |
||||||
|
start_point, end_point) |
||||||
|
|
||||||
|
# Make a copy so we don't modify a user supplied cache |
||||||
|
if cache is not None: |
||||||
|
new_cache = dict(cache) |
||||||
|
else: |
||||||
|
new_cache = {} |
||||||
|
new_cache["xy"] = xy |
||||||
|
new_cache["var2dz"] = var2dz |
||||||
|
new_cache["z_var2d"] = z_var2d |
||||||
|
|
||||||
|
# Don't modify the original args/kargs. The args need to be a list |
||||||
|
# so it can be modified. |
||||||
|
new_args, cache_argloc = arg_location(wrapped, "cache", args, kwargs) |
||||||
|
new_args[cache_argloc] = new_cache |
||||||
|
|
||||||
|
result = wrapped(*new_args) |
||||||
|
|
||||||
|
# Defaults, in case the data isn't a DataArray |
||||||
|
outname = None |
||||||
|
outdimnames = None |
||||||
|
outcoords = None |
||||||
|
outattrs = None |
||||||
|
|
||||||
|
# Use XY to set the cross-section metadata |
||||||
|
st_x = xy[0,0] |
||||||
|
st_y = xy[0,1] |
||||||
|
ed_x = xy[-1,0] |
||||||
|
ed_y = xy[-1,1] |
||||||
|
|
||||||
|
cross_str = "cross-section: ({0}, {1}) to ({2}, {3})".format(st_x, st_y, |
||||||
|
ed_x, ed_y) |
||||||
|
if angle is not None: |
||||||
|
cross_str += " ; center={0} ; angle={1}".format(pivot_point, |
||||||
|
angle) |
||||||
|
|
||||||
|
if isinstance(field3d, DataArray): |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(field3d.dims) |
||||||
|
outcoords.update(field3d.coords) |
||||||
|
for i in xrange(-3,0,1): |
||||||
|
outdimnames.remove(field3d.dims[i]) |
||||||
|
del outcoords[field3d.dims[i]] |
||||||
|
|
||||||
|
|
||||||
|
# Delete any lat,lon coords |
||||||
|
for key in viewkeys(outcoords): |
||||||
|
if _is_coord_var(key): |
||||||
|
del outcoords[key] |
||||||
|
|
||||||
|
outdimnames.append("vertical") |
||||||
|
outdimnames.append("xy") |
||||||
|
outattrs.update(field3d.attrs) |
||||||
|
|
||||||
|
outname = "{0}_cross".format(field3d.name) |
||||||
|
|
||||||
|
for key in ("MemoryOrder",): |
||||||
|
try: |
||||||
|
del outattrs[key] |
||||||
|
except KeyError: |
||||||
|
pass |
||||||
|
|
||||||
|
outcoords["xy"] = [XYCoord(xy[i,0], xy[i,1]) |
||||||
|
for i in xrange(xy.shape[-2])] |
||||||
|
|
||||||
|
outcoords["vertical"] = z_var2d[:] |
||||||
|
|
||||||
|
else: |
||||||
|
outname = "field3d_cross" |
||||||
|
outattrs = OrderedDict() |
||||||
|
|
||||||
|
outattrs["orientation"] = cross_str |
||||||
|
outattrs["missing_value"] = missingval |
||||||
|
outattrs["_FillValue"] = missingval |
||||||
|
|
||||||
|
return DataArray(result, name=outname, dims=outdimnames, |
||||||
|
coords=outcoords, attrs=outattrs) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _set_line_meta(wrapped, instance, args, kwargs): |
||||||
|
argvars = from_args(wrapped, ("field2d", "pivot_point", "angle", |
||||||
|
"start_point", "end_point", "cache"), |
||||||
|
*args, **kwargs) |
||||||
|
|
||||||
|
field2d = argvars["field2d"] |
||||||
|
pivot_point = argvars["pivot_point"] |
||||||
|
angle = argvars["angle"] |
||||||
|
start_point = argvars["start_point"] |
||||||
|
end_point = argvars["end_point"] |
||||||
|
cache = argvars["cache"] |
||||||
|
|
||||||
|
if cache is None: |
||||||
|
cache = {} |
||||||
|
|
||||||
|
xy = get_xy(field2d, pivot_point, angle, start_point, end_point) |
||||||
|
|
||||||
|
# Make a copy so we don't modify a user supplied cache |
||||||
|
new_cache = dict(cache) |
||||||
|
new_cache["xy"] = xy |
||||||
|
|
||||||
|
# Don't modify the original args/kargs. The args need to be a list |
||||||
|
# so it can be modified. |
||||||
|
new_args, cache_argloc = arg_location(wrapped, "cache", args, kwargs) |
||||||
|
new_args[cache_argloc] = new_cache |
||||||
|
|
||||||
|
result = wrapped(*new_args) |
||||||
|
|
||||||
|
# Defaults, in case the data isn't a DataArray |
||||||
|
outname = None |
||||||
|
outdimnames = None |
||||||
|
outcoords = None |
||||||
|
outattrs = None |
||||||
|
|
||||||
|
# Use XY to set the cross-section metadata |
||||||
|
st_x = xy[0,0] |
||||||
|
st_y = xy[0,1] |
||||||
|
ed_x = xy[-1,0] |
||||||
|
ed_y = xy[-1,1] |
||||||
|
|
||||||
|
cross_str = "({0}, {1}) to ({2}, {3})".format(st_x, st_y, |
||||||
|
ed_x, ed_y) |
||||||
|
if angle is not None: |
||||||
|
cross_str += " ; center={0} ; angle={1}".format(pivot_point, |
||||||
|
angle) |
||||||
|
|
||||||
|
if isinstance(field2d, DataArray): |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(field2d.dims) |
||||||
|
outcoords.update(field2d.coords) |
||||||
|
for i in xrange(-2,0,1): |
||||||
|
outdimnames.remove(field2d.dims[i]) |
||||||
|
del outcoords[field2d.dims[i]] |
||||||
|
|
||||||
|
# Delete any lat,lon coords |
||||||
|
for key in viewkeys(outcoords): |
||||||
|
if _is_coord_var(key): |
||||||
|
del outcoords[key] |
||||||
|
|
||||||
|
outdimnames.append("xy") |
||||||
|
outattrs.update(field2d.attrs) |
||||||
|
|
||||||
|
outname = "{0}_line".format(field2d.name) |
||||||
|
|
||||||
|
for key in ("MemoryOrder",): |
||||||
|
try: |
||||||
|
del outattrs[key] |
||||||
|
except KeyError: |
||||||
|
pass |
||||||
|
|
||||||
|
outcoords["xy"] = [XYCoord(xy[i,0], xy[i,1]) |
||||||
|
for i in xrange(xy.shape[-2])] |
||||||
|
|
||||||
|
else: |
||||||
|
outname = "field2d_line" |
||||||
|
outattrs = OrderedDict() |
||||||
|
|
||||||
|
outattrs["orientation"] = cross_str |
||||||
|
|
||||||
|
return DataArray(result, name=outname, dims=outdimnames, |
||||||
|
coords=outcoords, attrs=outattrs) |
||||||
|
|
||||||
|
|
||||||
|
def _set_vinterp_meta(wrapped, instance, args, kwargs): |
||||||
|
argvars = from_args(wrapped, ("wrfnc", "field", "vert_coord", |
||||||
|
"interp_levels", "extrapolate", |
||||||
|
"field_type", "log_p", |
||||||
|
"timeidx", "method", "squeeze", |
||||||
|
"cache"), |
||||||
|
*args, **kwargs) |
||||||
|
|
||||||
|
field = argvars["field"] |
||||||
|
vert_coord = argvars["vert_coord"] |
||||||
|
interp_levels = argvars["interp_levels"] |
||||||
|
field_type = argvars["field_type"] |
||||||
|
|
||||||
|
result = wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
# Defaults, in case the data isn't a DataArray |
||||||
|
outname = None |
||||||
|
outdimnames = None |
||||||
|
outcoords = None |
||||||
|
outattrs = None |
||||||
|
|
||||||
|
|
||||||
|
if isinstance(field, DataArray): |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(field.dims) |
||||||
|
outcoords.update(field.coords) |
||||||
|
|
||||||
|
outdimnames.remove(field.dims[-3]) |
||||||
|
del outcoords[field.dims[-3]] |
||||||
|
|
||||||
|
outdimnames.insert(-2, "interp_level") |
||||||
|
outcoords["interp_level"] = interp_levels |
||||||
|
outattrs.update(field.attrs) |
||||||
|
outattrs["vert_interp_type"] = vert_coord |
||||||
|
|
||||||
|
outname = field.name |
||||||
|
|
||||||
|
else: |
||||||
|
outname = field_type |
||||||
|
|
||||||
|
|
||||||
|
return DataArray(result, name=outname, dims=outdimnames, |
||||||
|
coords=outcoords, attrs=outattrs) |
||||||
|
|
||||||
|
|
||||||
|
def _set_2dxy_meta(wrapped, instance, args, kwargs): |
||||||
|
argvars = from_args(wrapped, ("field3d", "xy"), |
||||||
|
*args, **kwargs) |
||||||
|
|
||||||
|
field3d = argvars["field3d"] |
||||||
|
xy = argvars["xy"] |
||||||
|
|
||||||
|
result = wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
# Use XY to set the cross-section metadata |
||||||
|
st_x = xy[0,0] |
||||||
|
st_y = xy[0,1] |
||||||
|
ed_x = xy[-1,0] |
||||||
|
ed_y = xy[-1,1] |
||||||
|
|
||||||
|
cross_str = "({0},{1}) to ({2},{3})".format(st_x, st_y, |
||||||
|
ed_x, ed_y) |
||||||
|
|
||||||
|
# Dims are (...,xy,z) |
||||||
|
if isinstance(field3d, DataArray): |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(field3d.dims) |
||||||
|
outcoords.update(field3d.coords) |
||||||
|
for i in xrange(-2,0,1): |
||||||
|
outdimnames.remove(field3d.dims[i]) |
||||||
|
del outcoords[field3d.dims[i]] |
||||||
|
|
||||||
|
outdimnames[-2] = "xy" |
||||||
|
outattrs.update(field3d.attrs) |
||||||
|
|
||||||
|
outname = "{0}_xy".format(field3d.name) |
||||||
|
|
||||||
|
outcoords["xy"] = [XYCoord(xy[i,0], xy[i,1]) |
||||||
|
for i in xrange(xy.shape[-2])] |
||||||
|
|
||||||
|
for key in ("MemoryOrder",): |
||||||
|
try: |
||||||
|
del outattrs[key] |
||||||
|
except KeyError: |
||||||
|
pass |
||||||
|
|
||||||
|
else: |
||||||
|
outname = "field3d_xy" |
||||||
|
|
||||||
|
outattrs["Orientation"] = cross_str |
||||||
|
|
||||||
|
return DataArray(result, name=outname, dims=outdimnames, |
||||||
|
coords=outcoords, attrs=outattrs) |
||||||
|
|
||||||
|
|
||||||
|
def _set_1d_meta(wrapped, instance, args, kwargs): |
||||||
|
argvars = from_args(wrapped, ("v_in", "z_in", "z_out", "missingval"), |
||||||
|
*args, **kwargs) |
||||||
|
|
||||||
|
v_in = argvars["v_in"] |
||||||
|
z_in = argvars["z_in"] |
||||||
|
z_out = argvars["z_out"] |
||||||
|
missingval = argvars["missingval"] |
||||||
|
|
||||||
|
result = wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
# Dims are (...,xy,z) |
||||||
|
if isinstance(v_in, DataArray): |
||||||
|
outcoords = OrderedDict() |
||||||
|
outattrs = OrderedDict() |
||||||
|
outdimnames = list(v_in.dims) |
||||||
|
outcoords.update(v_in.coords) |
||||||
|
|
||||||
|
|
||||||
|
outdimnames.remove(v_in.dims[-1]) |
||||||
|
del outcoords[v_in.dims[-1]] |
||||||
|
outdimnames.append("z") |
||||||
|
outname = "{0}_z".format(v_in.name) |
||||||
|
outcoords["z"] = z_out |
||||||
|
|
||||||
|
outattrs.update(v_in.attrs) |
||||||
|
outattrs["_FillValue"] = missingval |
||||||
|
outattrs["missing_value"] = missingval |
||||||
|
|
||||||
|
else: |
||||||
|
outname = "v_in_z" |
||||||
|
|
||||||
|
|
||||||
|
return DataArray(result, name=outname, dims=outdimnames, |
||||||
|
coords=outcoords, attrs=outattrs) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_interp_metadata(interp_type): |
||||||
|
@wrapt.decorator |
||||||
|
def func_wrapper(wrapped, instance, args, kwargs): |
||||||
|
if not xarray_enabled(): |
||||||
|
return wrapped(*args, **kwargs) |
||||||
|
|
||||||
|
if interp_type == "horiz": |
||||||
|
return _set_horiz_meta(wrapped, instance, args, kwargs) |
||||||
|
elif interp_type == "cross": |
||||||
|
return _set_cross_meta(wrapped, instance, args, kwargs) |
||||||
|
elif interp_type == "line": |
||||||
|
return _set_line_meta(wrapped, instance, args, kwargs) |
||||||
|
elif interp_type == "vinterp": |
||||||
|
return _set_vinterp_meta(wrapped, instance, args, kwargs) |
||||||
|
elif interp_type == "2dxy": |
||||||
|
return _set_2dxy_meta(wrapped, instance, args, kwargs) |
||||||
|
elif interp_type == "1d": |
||||||
|
return _set_1d_meta(wrapped, instance, args, kwargs) |
||||||
|
return func_wrapper |
@ -0,0 +1,90 @@ |
|||||||
|
from __future__ import (absolute_import, division, print_function, |
||||||
|
unicode_literals) |
||||||
|
|
||||||
|
import numpy as np |
||||||
|
|
||||||
|
import wrapt |
||||||
|
|
||||||
|
from .destag import destagger |
||||||
|
from .util import iter_left_indexes |
||||||
|
|
||||||
|
__all__ = ["uvmet_left_iter"] |
||||||
|
|
||||||
|
# Placed in separate module to resolve a circular dependency with destagger |
||||||
|
# module |
||||||
|
|
||||||
|
def uvmet_left_iter(): |
||||||
|
"""Decorator to handle iterating over leftmost dimensions when using |
||||||
|
multiple files and/or multiple times with the uvmet product. |
||||||
|
|
||||||
|
""" |
||||||
|
@wrapt.decorator |
||||||
|
def func_wrapper(wrapped, instance, args, kwargs): |
||||||
|
u = args[0] |
||||||
|
v = args[1] |
||||||
|
lat = args[2] |
||||||
|
lon = args[3] |
||||||
|
cen_long = args[4] |
||||||
|
cone = args[5] |
||||||
|
|
||||||
|
if u.ndim == lat.ndim: |
||||||
|
num_right_dims = 2 |
||||||
|
is_3d = False |
||||||
|
else: |
||||||
|
num_right_dims = 3 |
||||||
|
is_3d = True |
||||||
|
|
||||||
|
is_stag = False |
||||||
|
if ((u.shape[-1] != lat.shape[-1]) or |
||||||
|
(u.shape[-2] != lat.shape[-2])): |
||||||
|
is_stag = True |
||||||
|
|
||||||
|
if is_3d: |
||||||
|
extra_dim_num = u.ndim - 3 |
||||||
|
else: |
||||||
|
extra_dim_num = u.ndim - 2 |
||||||
|
|
||||||
|
if is_stag: |
||||||
|
u = destagger(u,-1) |
||||||
|
v = destagger(v,-2) |
||||||
|
|
||||||
|
# No special left side iteration, return the function result |
||||||
|
if (extra_dim_num == 0): |
||||||
|
return wrapped(u, v, lat, lon, cen_long, cone) |
||||||
|
|
||||||
|
# Start by getting the left-most 'extra' dims |
||||||
|
outdims = [u.shape[x] for x in xrange(extra_dim_num)] |
||||||
|
extra_dims = list(outdims) # Copy the left-most dims for iteration |
||||||
|
|
||||||
|
# Append the right-most dimensions |
||||||
|
outdims += [2] # For u/v components |
||||||
|
|
||||||
|
outdims += [u.shape[x] for x in xrange(-num_right_dims,0,1)] |
||||||
|
|
||||||
|
output = np.empty(outdims, u.dtype) |
||||||
|
|
||||||
|
for left_idxs in iter_left_indexes(extra_dims): |
||||||
|
# Make the left indexes plus a single slice object |
||||||
|
# The single slice will handle all the dimensions to |
||||||
|
# the right (e.g. [1,1,:]) |
||||||
|
left_and_slice_idxs = tuple([x for x in left_idxs] + [slice(None)]) |
||||||
|
|
||||||
|
new_u = u[left_and_slice_idxs] |
||||||
|
new_v = v[left_and_slice_idxs] |
||||||
|
new_lat = lat[left_and_slice_idxs] |
||||||
|
new_lon = lon[left_and_slice_idxs] |
||||||
|
|
||||||
|
# Call the numerical routine |
||||||
|
res = wrapped(new_u, new_v, new_lat, new_lon, cen_long, cone) |
||||||
|
|
||||||
|
# Note: The 2D version will return a 3D array with a 1 length |
||||||
|
# dimension. Numpy is unable to broadcast this without |
||||||
|
# sqeezing first. |
||||||
|
res = np.squeeze(res) |
||||||
|
|
||||||
|
output[left_and_slice_idxs] = res[:] |
||||||
|
|
||||||
|
return output |
||||||
|
|
||||||
|
return func_wrapper |
||||||
|
|
Loading…
Reference in new issue