Browse Source

Numerous changes to make DataArray implementation pass all unit tests. Properly uses setuptools to make namespace.

main
Bill Ladwig 9 years ago
parent
commit
07349af907
  1. 0
      wrf_open/var/ncl_reference/etaconv.py
  2. 1
      wrf_open/var/setup.py
  3. 3
      wrf_open/var/src/python/wrf/var/__init__.py
  4. 22
      wrf_open/var/src/python/wrf/var/cape.py
  5. 2
      wrf_open/var/src/python/wrf/var/config.py
  6. 2
      wrf_open/var/src/python/wrf/var/constants.py
  7. 20
      wrf_open/var/src/python/wrf/var/ctt.py
  8. 20
      wrf_open/var/src/python/wrf/var/dbz.py
  9. 485
      wrf_open/var/src/python/wrf/var/decorators.py
  10. 34
      wrf_open/var/src/python/wrf/var/destag.py
  11. 12
      wrf_open/var/src/python/wrf/var/dewpoint.py
  12. 107
      wrf_open/var/src/python/wrf/var/extension.py
  13. 12
      wrf_open/var/src/python/wrf/var/geoht.py
  14. 19
      wrf_open/var/src/python/wrf/var/helicity.py
  15. 306
      wrf_open/var/src/python/wrf/var/interp.py
  16. 182
      wrf_open/var/src/python/wrf/var/interputils.py
  17. 27
      wrf_open/var/src/python/wrf/var/latlon.py
  18. 695
      wrf_open/var/src/python/wrf/var/metadecorators.py
  19. 7
      wrf_open/var/src/python/wrf/var/omega.py
  20. 3
      wrf_open/var/src/python/wrf/var/precip.py
  21. 9
      wrf_open/var/src/python/wrf/var/pressure.py
  22. 2
      wrf_open/var/src/python/wrf/var/projection.py
  23. 3
      wrf_open/var/src/python/wrf/var/psadlookup.py
  24. 9
      wrf_open/var/src/python/wrf/var/pw.py
  25. 10
      wrf_open/var/src/python/wrf/var/rh.py
  26. 13
      wrf_open/var/src/python/wrf/var/slp.py
  27. 20
      wrf_open/var/src/python/wrf/var/temp.py
  28. 7
      wrf_open/var/src/python/wrf/var/terrain.py
  29. 2
      wrf_open/var/src/python/wrf/var/times.py
  30. 2
      wrf_open/var/src/python/wrf/var/units.py
  31. 312
      wrf_open/var/src/python/wrf/var/util.py
  32. 90
      wrf_open/var/src/python/wrf/var/uvdecorator.py
  33. 75
      wrf_open/var/src/python/wrf/var/uvmet.py
  34. 19
      wrf_open/var/src/python/wrf/var/vorticity.py
  35. 73
      wrf_open/var/src/python/wrf/var/wind.py
  36. 2
      wrf_open/var/src/python/wrf/var/wrfext.f90
  37. 48
      wrf_open/var/test/utests.py

0
wrf_open/var/src/python/wrf/var/etaconv.py → wrf_open/var/ncl_reference/etaconv.py

1
wrf_open/var/setup.py

@ -19,5 +19,6 @@ numpy.distutils.core.setup(
packages = setuptools.find_packages("src/python"), packages = setuptools.find_packages("src/python"),
ext_modules = [ext1,ext2], ext_modules = [ext1,ext2],
package_dir={"":"src/python"}, package_dir={"":"src/python"},
namespace_packages=["wrf"],
scripts=[], scripts=[],
) )

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

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import warnings import warnings
from . import config from . import config

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

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as n import numpy as n
import numpy.ma as ma import numpy.ma as ma
@ -5,22 +8,24 @@ from .extension import computetk,computecape
from .destag import destagger from .destag import destagger
from .constants import Constants, ConversionFactors from .constants import Constants, ConversionFactors
from .util import extract_vars, combine_with from .util import extract_vars, combine_with
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_2dcape", "get_3dcape"] __all__ = ["get_2dcape", "get_3dcape"]
@copy_and_set_metadata(copy_varname="T", @copy_and_set_metadata(copy_varname="T",
name="cape_2d", name="cape_2d",
dimnames=combine_with("T", remove_dims=("bottom_top",), dimnames=combine_with("T", remove_dims=("bottom_top",),
insert_before="south_north", insert_before="south_north",
new_dimnames=("mcape_mcin_lcl_lfc",)), new_dimnames=["mcape_mcin_lcl_lfc"]),
description="mcape ; mcin ; lcl ; lfc", description="mcape ; mcin ; lcl ; lfc",
units="J/kg ; J/kg ; m ; m", units="J/kg ; J/kg ; m ; m",
MemoryOrder = "XY") MemoryOrder="XY")
def get_2dcape(wrfnc, timeidx=0, missing=Constants.DEFAULT_FILL, def get_2dcape(wrfnc, timeidx=0, missing=Constants.DEFAULT_FILL,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
"""Return the 2d fields of cape, cin, lcl, and lfc""" """Return the 2d fields of cape, cin, lcl, and lfc"""
varnames = ("T", "P", "PB", "QVAPOR", "PH","PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH","PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -69,18 +74,20 @@ def get_2dcape(wrfnc, timeidx=0, missing=Constants.DEFAULT_FILL,
return ma.masked_values(res, missing) return ma.masked_values(res, missing)
@copy_and_set_metadata(copy_varname="T", name="cape_3d", @copy_and_set_metadata(copy_varname="T", name="cape_3d",
dimnames=combine_with("T", dimnames=combine_with("T",
insert_before="bottom_top", insert_before="bottom_top",
new_dimnames=("cape_cin",)), new_dimnames=["cape_cin"]),
description="cape ; cin", description="cape ; cin",
units="J kg-1 ; J kg-1", units="J kg-1 ; J kg-1",
MemoryOrder = "XY") MemoryOrder="XY")
def get_3dcape(wrfnc, timeidx=0, missing=Constants.DEFAULT_FILL, def get_3dcape(wrfnc, timeidx=0, missing=Constants.DEFAULT_FILL,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
"""Return the 3d fields of cape and cin""" """Return the 3d fields of cape and cin"""
varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -119,7 +126,6 @@ def get_3dcape(wrfnc, timeidx=0, missing=Constants.DEFAULT_FILL,
res[...,0,:,:,:] = cape[:] res[...,0,:,:,:] = cape[:]
res[...,1,:,:,:] = cin[:] res[...,1,:,:,:] = cin[:]
#return res
return ma.masked_values(res, missing) return ma.masked_values(res, missing)

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

@ -1,3 +1,5 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
try: try:
from xarray import DataArray from xarray import DataArray

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

@ -1,3 +1,5 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
__all__ = ["Constants", "ConversionFactors"] __all__ = ["Constants", "ConversionFactors"]

20
wrf_open/var/src/python/wrf/var/ctt.py

@ -1,16 +1,21 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as n import numpy as n
from .extension import computectt, computetk from .extension import computectt, computetk
from .constants import Constants, ConversionFactors from .constants import Constants, ConversionFactors
from .destag import destagger from .destag import destagger
from .decorators import convert_units, copy_and_set_metadata from .decorators import convert_units
from .metadecorators import copy_and_set_metadata
from .util import extract_vars from .util import extract_vars
__all__ = ["get_ctt"] __all__ = ["get_ctt"]
@copy_and_set_metadata(copy_varname="T", name="ctt", @copy_and_set_metadata(copy_varname="T", name="ctt",
description="cloud top temperature") remove_dims=("bottom_top",),
description="cloud top temperature",
MemoryOrder="XY")
@convert_units("temp", "c") @convert_units("temp", "c")
def get_ctt(wrfnc, timeidx=0, units="c", method="cat", squeeze=True, def get_ctt(wrfnc, timeidx=0, units="c", method="cat", squeeze=True,
cache=None): cache=None):
@ -18,7 +23,8 @@ def get_ctt(wrfnc, timeidx=0, units="c", method="cat", squeeze=True,
""" """
varnames = ("T", "P", "PB", "PH", "PHB", "HGT", "QVAPOR") varnames = ("T", "P", "PB", "PH", "PHB", "HGT", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -29,7 +35,8 @@ def get_ctt(wrfnc, timeidx=0, units="c", method="cat", squeeze=True,
haveqci = 1 haveqci = 1
try: try:
icevars = extract_vars(wrfnc, timeidx, "QICE", method, squeeze, cache) icevars = extract_vars(wrfnc, timeidx, "QICE",
method, squeeze, cache)
except KeyError: except KeyError:
qice = n.zeros(qv.shape, qv.dtype) qice = n.zeros(qv.shape, qv.dtype)
haveqci = 0 haveqci = 0
@ -37,7 +44,8 @@ def get_ctt(wrfnc, timeidx=0, units="c", method="cat", squeeze=True,
qice = icevars["QICE"] * 1000.0 #g/kg qice = icevars["QICE"] * 1000.0 #g/kg
try: try:
cldvars = extract_vars(wrfnc, timeidx, "QCLOUD", method, squeeze, cache) cldvars = extract_vars(wrfnc, timeidx, "QCLOUD",
method, squeeze, cache)
except KeyError: except KeyError:
raise RuntimeError("'QCLOUD' not found in NetCDF file") raise RuntimeError("'QCLOUD' not found in NetCDF file")
else: else:
@ -52,6 +60,6 @@ def get_ctt(wrfnc, timeidx=0, units="c", method="cat", squeeze=True,
geopt_unstag = destagger(geopt, -3) geopt_unstag = destagger(geopt, -3)
ght = geopt_unstag / Constants.G ght = geopt_unstag / Constants.G
ctt = computectt(p_hpa,tk,qice,qcld,qv,ght,ter,haveqci) ctt = computectt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci)
return ctt return ctt

20
wrf_open/var/src/python/wrf/var/dbz.py

@ -1,9 +1,12 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as n import numpy as n
from .extension import computedbz,computetk from .extension import computedbz,computetk
from .constants import Constants from .constants import Constants
from .util import extract_vars, combine_with from .util import extract_vars
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_dbz", "get_max_dbz"] __all__ = ["get_dbz", "get_max_dbz"]
@ -23,7 +26,8 @@ def get_dbz(wrfnc, timeidx=0, do_varint=False, do_liqskin=False,
""" """
varnames = ("T", "P", "PB", "QVAPOR", "QRAIN") varnames = ("T", "P", "PB", "QVAPOR", "QRAIN")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -65,13 +69,15 @@ def get_dbz(wrfnc, timeidx=0, do_varint=False, do_liqskin=False,
return computedbz(full_p,tk,qv,qr,qs,qg,sn0,ivarint,iliqskin) return computedbz(full_p,tk,qv,qr,qs,qg,sn0,ivarint,iliqskin)
@copy_and_set_metadata(copy_varname="T", name="dbz",
dimnames=combine_with("T", remove_dims=("bottom_top",)), @copy_and_set_metadata(copy_varname="T", name="max_dbz",
remove_dims=("bottom_top",),
description="maximum radar reflectivity", description="maximum radar reflectivity",
units="dBz") units="dBz",
MemoryOrder="XY")
def get_max_dbz(wrfnc, timeidx=0, do_varint=False, do_liqskin=False, def get_max_dbz(wrfnc, timeidx=0, do_varint=False, do_liqskin=False,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
return n.amax(get_dbz(wrfnc, do_varint, do_liqskin, timeidx, method, return n.amax(get_dbz(wrfnc, timeidx, do_varint, do_liqskin,
method, squeeze, cache), method, squeeze, cache),
axis=-3) axis=-3)

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

@ -1,79 +1,21 @@
#from functools import wraps from __future__ import (absolute_import, division, print_function,
import wrapt unicode_literals)
from inspect import getargspec
from collections import OrderedDict from collections import Iterable
import wrapt
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
from .units import do_conversion, check_units from .units import do_conversion, check_units
from .destag import destagger from .util import (iter_left_indexes, viewitems, from_args, npvalues)
from .util import (iter_left_indexes, viewitems, extract_vars,
combine_with, either)
from .config import xarray_enabled from .config import xarray_enabled
if xarray_enabled(): if xarray_enabled():
from xarray import DataArray from xarray import DataArray
__all__ = ["convert_units", "handle_left_iter", "uvmet_left_iter", __all__ = ["convert_units", "handle_left_iter", "uvmet_left_iter",
"handle_casting" "copy_and_set_metadata", "set_wind_metadata", "handle_casting", "handle_extract_transpose"]
"set_latlon_metadata", "set_height_metadata"]
# TODO: In python 3.x, getargspec is deprecated
def from_args(func, argnames, *args, **kwargs):
"""Parses the function args and kargs looking for the desired argument
value. Otherwise, the value is taken from the default keyword argument
using the arg spec.
"""
if isinstance(argnames, str):
arglist = [argnames]
else:
arglist = argnames
result = {}
for argname in arglist:
arg_loc = arg_location(func, argname, args, kwargs)
if arg_loc is not None:
result[argname] = arg_loc[0][arg_loc[1]]
else:
result[argname] = None
return result
def arg_location(func, argname, args, kwargs):
"""Parses the function args, kargs and signature looking for the desired
argument location (either in args, kargs, or argspec.defaults),
and returns a tuple of argument_sequence, location.
"""
argspec = getargspec(func)
print argspec
if argname not in argspec.args and argname not in kwargs:
return None
try:
result_idx = argspec.args.index(argname)
except ValueError:
result_idx = None
result = None
if (argname in kwargs):
result = kwargs, argname
elif (len(args) > result_idx and result_idx is not None):
result = args, result_idx
else:
arg_idx_from_right = (len(argspec.args) -
argspec.args.index(argname))
result = list(argspec.defaults), -arg_idx_from_right
return result
def convert_units(unit_type, alg_unit): def convert_units(unit_type, alg_unit):
"""A decorator that applies unit conversion to a function's output array. """A decorator that applies unit conversion to a function's output array.
@ -84,7 +26,6 @@ def convert_units(unit_type, alg_unit):
- alg_unit - the units that the function returns by default - alg_unit - the units that the function returns by default
""" """
# def convert_decorator(func):
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
@ -94,9 +35,8 @@ def convert_units(unit_type, alg_unit):
# Unit conversion done here # Unit conversion done here
return do_conversion(wrapped(*args, **kwargs), unit_type, return do_conversion(wrapped(*args, **kwargs), unit_type,
alg_unit, desired_units) alg_unit, desired_units)
return func_wrapper
# return convert_decorator return func_wrapper
def _calc_out_dims(outvar, left_dims): def _calc_out_dims(outvar, left_dims):
@ -115,7 +55,7 @@ def handle_left_iter(ref_var_expected_dims, ref_var_idx=-1,
algorithm is expecting for the reference variable algorithm is expecting for the reference variable
- ref_var_idx - the index in args used as the reference variable for - ref_var_idx - the index in args used as the reference variable for
calculating leftmost dimensions calculating leftmost dimensions
- ref_var_name - the keyword argument name for kargs used as the - ref_var_name - the keyword argument name for kwargs used as the
reference varible for calculating leftmost dimensions reference varible for calculating leftmost dimensions
- alg_out_fixed_dims - additional fixed dimension sizes for the - alg_out_fixed_dims - additional fixed dimension sizes for the
numerical algorithm (e.g. uvmet has a fixed left dimsize of 2) numerical algorithm (e.g. uvmet has a fixed left dimsize of 2)
@ -128,11 +68,10 @@ def handle_left_iter(ref_var_expected_dims, ref_var_idx=-1,
the output dimensions (e.g. avo) the output dimensions (e.g. avo)
""" """
# def indexing_decorator(func):
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
ignore_args = ignore_args if ignore_args is not None else () _ignore_args = ignore_args if ignore_args is not None else ()
ignore_kargs = ignore_kargs if ignore_kargs is not None else () _ignore_kargs = ignore_kargs if ignore_kargs is not None else ()
if ref_var_idx >= 0: if ref_var_idx >= 0:
ref_var = args[ref_var_idx] ref_var = args[ref_var_idx]
@ -154,16 +93,16 @@ def handle_left_iter(ref_var_expected_dims, ref_var_idx=-1,
# Make the left indexes plus a single slice object # Make the left indexes plus a single slice object
# The single slice will handle all the dimensions to # The single slice will handle all the dimensions to
# the right (e.g. [1,1,:]) # the right (e.g. [1,1,:])
left_and_slice_idxs = ([x for x in left_idxs] + left_and_slice_idxs = tuple([x for x in left_idxs] + [slice(None)])
[slice(None, None, None)])
# Slice the args if applicable # Slice the args if applicable
new_args = [arg[left_and_slice_idxs] new_args = [arg[left_and_slice_idxs]
if i not in ignore_args else arg if i not in _ignore_args else arg
for i,arg in enumerate(args)] for i,arg in enumerate(args)]
# Slice the kargs if applicable # Slice the kwargs if applicable
new_kargs = {key:(val[left_and_slice_idxs] new_kargs = {key:(val[left_and_slice_idxs]
if key not in ignore_kargs else val) if key not in _ignore_kargs else val)
for key,val in viewitems(kwargs)} for key,val in viewitems(kwargs)}
# Call the numerical routine # Call the numerical routine
@ -220,109 +159,26 @@ def handle_left_iter(ref_var_expected_dims, ref_var_idx=-1,
return func_wrapper return func_wrapper
# return indexing_decorator
def uvmet_left_iter():
"""Decorator to handle iterating over leftmost dimensions when using
multiple files and/or multiple times with the uvmet product.
"""
#def indexing_decorator(func):
@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 = ([x for x in left_idxs] +
[slice(None, None, 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
# return indexing_decorator
def handle_casting(ref_idx=0, arg_idxs=None, karg_names=None, dtype=np.float64): def handle_casting(ref_idx=0, arg_idxs=None, karg_names=None, dtype=np.float64):
"""Decorator to handle casting to/from required dtype used in """Decorator to handle casting to/from required dtype used in
numerical routine. numerical routine.
""" """
# def cast_decorator(func):
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
arg_idxs = arg_idxs if arg_idxs is not None else () _arg_idxs = arg_idxs if arg_idxs is not None else ()
karg_names = karg_names if karg_names is not None else () _karg_names = karg_names if karg_names is not None else ()
orig_type = args[ref_idx].dtype orig_type = args[ref_idx].dtype
new_args = [arg.astype(dtype) new_args = [arg.astype(dtype)
if i in arg_idxs else arg if i in _arg_idxs else arg
for i,arg in enumerate(args)] for i,arg in enumerate(args)]
new_kargs = {key:(val.astype(dtype) new_kargs = {key:(val.astype(dtype)
if key in karg_names else val) if key in _karg_names else val)
for key,val in viewitems()} for key,val in viewitems(kwargs)}
res = wrapped(*new_args, **new_kargs) res = wrapped(*new_args, **new_kargs)
@ -337,29 +193,29 @@ def handle_casting(ref_idx=0, arg_idxs=None, karg_names=None, dtype=np.float64):
return func_wrapper return func_wrapper
# return cast_decorator
def _extract_and_transpose(arg): def _extract_and_transpose(arg, do_transpose):
if xarray_enabled(): if xarray_enabled():
if isinstance(arg, DataArray): if isinstance(arg, DataArray):
arg = arg.values arg = npvalues(arg)
if do_transpose:
if isinstance(arg, np.ndarray): if isinstance(arg, np.ndarray):
if not arg.flags.F_CONTIGUOUS: if not arg.flags.f_contiguous and arg.ndim > 1:
return arg.T return arg.T
return arg return arg
def handle_extract_transpose():
def handle_extract_transpose(do_transpose=True):
"""Decorator to extract the data array from a DataArray and also """Decorator to extract the data array from a DataArray and also
transposes if the data is not fortran contiguous. transposes the view of the data if the data is not fortran contiguous.
""" """
# def extract_transpose_decorator(func):
@wrapt.decorator @wrapt.decorator
def func_wrapper(wrapped, instance, args, kwargs): def func_wrapper(wrapped, instance, args, kwargs):
new_args = [_extract_and_transpose(arg) for arg in args] new_args = [_extract_and_transpose(arg, do_transpose) for arg in args]
new_kargs = {key:_extract_and_transpose(val) new_kargs = {key:_extract_and_transpose(val)
for key,val in viewitems(kwargs)} for key,val in viewitems(kwargs)}
@ -367,291 +223,16 @@ def handle_extract_transpose():
res = wrapped(*new_args, **new_kargs) res = wrapped(*new_args, **new_kargs)
if isinstance(res, np.ndarray): if isinstance(res, np.ndarray):
if res.flags.F_CONTIGUOUS: if res.flags.f_contiguous and res.ndim > 1:
return res.T return res.T
else: elif isinstance(res, Iterable):
return tuple(x.T if x.flags.F_CONTIGUOUS else x for x in res) return tuple(x.T if x.flags.f_contiguous and x.ndim > 1 else x
for x in res)
return res return res
return func_wrapper return func_wrapper
# return extract_transpose_decorator
def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=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 = {}
if (callable(copy_varname)):
copy_varname = copy_varname(wrfnc)
# 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)[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 = list(args)
new_kargs = dict(kwargs)
cache_argseq, cache_argloc = arg_location(wrapped, "cache",
new_args, new_kargs)
cache_argseq[cache_argloc] = new_cache
result = wrapped(*new_args, **new_kargs)
outname = ""
outdimnames = list()
outcoords = OrderedDict()
outattrs = OrderedDict()
if copy_varname is not None:
outname = str(var_to_copy.name)
if dimnames is not None:
if isinstance(combine_with):
outdimnames, outcoords = dimnames(copy_varname)
else:
outdimnames = dimnames
outcoords = coords
else:
outdimnames += var_to_copy.dims
outcoords.update(var_to_copy.coords)
outattrs.update(var_to_copy.attrs)
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:
del outattrs[attr]
if isinstance(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(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 = {}
lat_var = either("XLAT", "XLAT_M")(wrfnc)
xlat_var = extract_vars(wrfnc, timeidx, lat_var,
method, squeeze, cache)
lat = xlat_var[lat_var]
lon_var = either("XLONG", "XLONG_M")
xlon_var = extract_vars(wrfnc, timeidx, lon_var,
method, squeeze, cache)
lon = xlon_var[lon_var]
pres_var = either("P", "PRES")
p_var = extract_vars(wrfnc, timeidx, lon_var, method, squeeze, cache)
pres = p_var[pres_var]
# Make a copy so we don't modify a user supplied cache
new_cache = dict(cache)
new_cache[lat_var] = lat
new_cache[lon_var] = lon
new_cache[pres_var] = pres
# Don't modify the original args/kargs. The args need to be a list
# so it can be modified.
new_args = list(args)
new_kargs = dict(kwargs)
cache_argseq, cache_argloc = arg_location(wrapped, "cache",
new_args, new_kargs)
cache_argseq[cache_argloc] = new_cache
result = wrapped(*new_args, **new_kargs)
outcoords = OrderedDict()
outattrs = OrderedDict()
outname = "uvmet" if not ten_m else "uvmet10"
outdimnames = list(pres.dims)
outcoords.update(pres.coords)
outattrs.update(pres.attrs)
if not wspd_wdir:
outattrs["description"] = ("earth rotated u,v" if not ten_m else
"10m earth rotated u,v")
if not ten_m:
outdimnames.insert(-3, "u_v")
else:
outdimnames.insert(-2, "u_v")
else:
outattrs["description"] = ("earth rotated wspd,wdir" if not ten_m
else "10m earth rotated wspd,wdir")
if not ten_m:
outdimnames.insert(-3, "wspd_wdir")
else:
outdimnames.insert(-2, "wspd_wdir")
if units is not None:
outattrs["units"] = units
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)
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_var] = ht_metadata_var
# Don't modify the original args/kargs. The args need to be a list
# so it can be modified.
new_args = list(args)
new_kargs = dict(kwargs)
cache_argseq, cache_argloc = arg_location(wrapped, "cache",
new_args, new_kargs)
cache_argseq[cache_argloc] = new_cache
result = wrapped(*new_args, **new_kargs)
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)

34
wrf_open/var/src/python/wrf/var/destag.py

@ -1,9 +1,11 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .decorators import handle_extract_transpose
from .util import extract_vars __all__ = ["destagger"]
__all__ = ["destagger", "destagger_windcomp", "destagger_winds"]
@handle_extract_transpose(do_transpose=False)
def destagger(var, stagger_dim): def destagger(var, stagger_dim):
""" De-stagger the variable. """ De-stagger the variable.
@ -21,10 +23,11 @@ def destagger(var, stagger_dim):
# Dynamically building the range slices to create the appropriate # Dynamically building the range slices to create the appropriate
# number of ':'s in the array accessor lists. # number of ':'s in the array accessor lists.
# For example, for a 3D array, the calculation would be # For example, for a 3D array, the calculation would be
# result = .5 * (var[:,:,0:stagger_dim_size-2] + var[:,:,1:stagger_dim_size-1]) # result = .5 * (var[:,:,0:stagger_dim_size-2]
# + var[:,:,1:stagger_dim_size-1])
# for stagger_dim=2. So, full slices would be used for dims 0 and 1, but # for stagger_dim=2. So, full slices would be used for dims 0 and 1, but
# dim 2 needs the special slice. # dim 2 needs the special slice.
full_slice = slice(None, None, None) full_slice = slice(None)
slice1 = slice(0, stagger_dim_size - 1, 1) slice1 = slice(0, stagger_dim_size - 1, 1)
slice2 = slice(1, stagger_dim_size, 1) slice2 = slice(1, stagger_dim_size, 1)
@ -40,25 +43,4 @@ def destagger(var, stagger_dim):
return result return result
# def destagger_windcomp(wrfnc, comp, timeidx=0,
# method="cat", squeeze=True, cache=None):
# if comp.lower() == "u":
# wrfvar = "U"
# stagdim = -1
# elif comp.lower() == "v":
# wrfvar = "V"
# stagdim = -2
# elif comp.lower() == "w":
# wrfvar = "W"
# stagdim = -3
#
# ncvars = extract_vars(wrfnc, timeidx, wrfvar, method, squeeze, cache)
# wind_data = ncvars[wrfvar]
#
# return destagger(wind_data, stagdim)
#
# def destagger_winds(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
# return (destagger_windcomp(wrfnc, "u", timeidx, method, squeeze, cache),
# destagger_windcomp(wrfnc, "v", timeidx, method, squeeze, cache),
# destagger_windcomp(wrfnc, "w", timeidx, method, squeeze, cache))

12
wrf_open/var/src/python/wrf/var/dewpoint.py

@ -1,5 +1,9 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .extension import computetd from .extension import computetd
from .decorators import convert_units, copy_and_set_metadata from .decorators import convert_units
from .metadecorators import copy_and_set_metadata
from .util import extract_vars from .util import extract_vars
__all__ = ["get_dp", "get_dp_2m"] __all__ = ["get_dp", "get_dp_2m"]
@ -11,7 +15,8 @@ def get_dp(wrfnc, timeidx=0, units="c",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varnames=("P", "PB", "QVAPOR") varnames=("P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -30,7 +35,8 @@ def get_dp(wrfnc, timeidx=0, units="c",
def get_dp_2m(wrfnc, timeidx=0, units="c", def get_dp_2m(wrfnc, timeidx=0, units="c",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varnames=("PSFC", "Q2") varnames=("PSFC", "Q2")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
# Algorithm requires hPa # Algorithm requires hPa
psfc = .01*(ncvars["PSFC"]) psfc = .01*(ncvars["PSFC"])

107
wrf_open/var/src/python/wrf/var/extension.py

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as np import numpy as np
from .constants import Constants from .constants import Constants
@ -11,37 +14,42 @@ from ._wrfext import (f_interpz3d, f_interp2dxy,f_interp1d,
f_lltoij, f_ijtoll, f_converteta, f_computectt, f_lltoij, f_ijtoll, f_converteta, f_computectt,
f_monotonic, f_filter2d, f_vintrp) f_monotonic, f_filter2d, f_vintrp)
from ._wrfcape import f_computecape from ._wrfcape import f_computecape
from .decorators import (handle_left_iter, uvmet_left_iter, from .decorators import (handle_left_iter, handle_casting,
handle_casting, handle_extract_transpose) handle_extract_transpose)
from .uvdecorator import uvmet_left_iter
__all__ = ["FortranException", "computeslp", "computetk", "computetd", __all__ = ["FortranException", "computeslp", "computetk", "computetd",
"computerh", "computeavo", "computepvo", "computeeth", "computerh", "computeavo", "computepvo", "computeeth",
"computeuvmet","computeomega", "computetv", "computesrh", "computeuvmet","computeomega", "computetv", "computesrh",
"computeuh", "computepw","computedbz","computecape", "computeuh", "computepw","computedbz","computecape",
"computeij", "computell", "computeeta", "computectt"] "computeij", "computell", "computeeta", "computectt",
"interp2dxy", "interpz3d", "interp1d", "computeinterpline",
"computevertcross"]
class FortranException(Exception): class FortranException(Exception):
def __call__(self, message): def __call__(self, message):
raise self.__class__(message) raise self.__class__(message)
@handle_left_iter(3,0, ignore_args=(2,3)) @handle_left_iter(3,0, ignore_args=(2,3))
@handle_casting(arg_idxs=(0,1)) @handle_casting(arg_idxs=(0,1))
@handle_extract_transpose() @handle_extract_transpose()
def interpz3d(data3d, zdata, desiredloc, missingval): def interpz3d(field3d, z, desiredloc, missingval):
res = f_interpz3d(data3d, res = f_interpz3d(field3d,
zdata, z,
desiredloc, desiredloc,
missingval) missingval)
return res return res
@handle_left_iter(3,0) @handle_left_iter(3,0, ignore_args=(1,))
@handle_casting(arg_idxs=(0,1)) @handle_casting(arg_idxs=(0,1))
@handle_extract_transpose() @handle_extract_transpose()
def interp2dxy(data3d,xy): def interp2dxy(field3d, xy):
res = f_interp2dxy(data3d, res = f_interp2dxy(field3d,
xy) xy)
return res return res
@handle_left_iter(1, 0, ignore_args=(2,3))
@handle_casting(arg_idxs=(0,1,2)) @handle_casting(arg_idxs=(0,1,2))
@handle_extract_transpose() @handle_extract_transpose()
def interp1d(v_in, z_in, z_out, missingval): def interp1d(v_in, z_in, z_out, missingval):
@ -52,19 +60,45 @@ def interp1d(v_in, z_in, z_out, missingval):
return res return res
@handle_left_iter(3, 0, ignore_args=(1,4,3))
@handle_casting(arg_idxs=(0,))
@handle_extract_transpose(do_transpose=False)
def computevertcross(field3d, xy, var2dz, z_var2d, missingval):
var2d = np.empty((z_var2d.size, xy.shape[0]), dtype=var2dz.dtype)
var2dtmp = interp2dxy(field3d, xy)
for i in xrange(xy.shape[0]):
var2d[:,i] = interp1d(var2dtmp[:,i], var2dz[:,i], z_var2d, missingval)
return var2d
@handle_left_iter(2, 0, ignore_args=(1,))
@handle_casting(arg_idxs=(0,))
@handle_extract_transpose(do_transpose=False)
def computeinterpline(field2d, xy):
tmp_shape = [1] + [x for x in field2d.shape]
var2dtmp = np.empty(tmp_shape, field2d.dtype)
var2dtmp[0,:,:] = field2d[:,:]
var1dtmp = interp2dxy(var2dtmp, xy)
return var1dtmp[0,:]
@handle_left_iter(3,0) @handle_left_iter(3,0)
@handle_casting(arg_idxs=(0,1,2,3)) @handle_casting(arg_idxs=(0,1,2,3))
@handle_extract_transpose() @handle_extract_transpose()
def computeslp(z, t, p, q): def computeslp(z, t, p, q):
t_surf = np.zeros((z.shape[-2], z.shape[-1]), np.float64, order="F")
t_sea_level = np.zeros((z.shape[-2], z.shape[-1]), np.float64, order="F") t_surf = np.zeros(z.shape[0:2], np.float64, order="F")
level = np.zeros((z.shape[-2], z.shape[-1]), np.int32, order="F") t_sea_level = np.zeros(z.shape[0:2], np.float64, order="F")
level = np.zeros(z.shape[0:2], np.int32, order="F")
res = f_computeslp(z, res = f_computeslp(z,
t, t,
p, p,
q, q,
t_sea_level, # Should come in with fortran ordering t_sea_level,
t_surf, t_surf,
level, level,
FortranException()) FortranException())
@ -79,7 +113,8 @@ def computetk(pres, theta):
shape = pres.shape shape = pres.shape
res = f_computetk(pres.ravel(order="A"), res = f_computetk(pres.ravel(order="A"),
theta.ravel(order="A")) theta.ravel(order="A"))
res = np.reshape(res, shape) res = np.reshape(res, shape, order="F")
return res return res
# Note: No left iteration decorator needed with 1D arrays # Note: No left iteration decorator needed with 1D arrays
@ -89,7 +124,7 @@ def computetd(pressure, qv_in):
shape = pressure.shape shape = pressure.shape
res = f_computetd(pressure.ravel(order="A"), res = f_computetd(pressure.ravel(order="A"),
qv_in.ravel(order="A")) qv_in.ravel(order="A"))
res = np.reshape(res, shape) res = np.reshape(res, shape, order="F")
return res return res
# Note: No decorator needed with 1D arrays # Note: No decorator needed with 1D arrays
@ -100,7 +135,7 @@ def computerh(qv, q, t):
res = f_computerh(qv.ravel(order="A"), res = f_computerh(qv.ravel(order="A"),
q.ravel(order="A"), q.ravel(order="A"),
t.ravel(order="A")) t.ravel(order="A"))
res = np.reshape(res, shape) res = np.reshape(res, shape, order="F")
return res return res
@handle_left_iter(3,0, ignore_args=(6,7)) @handle_left_iter(3,0, ignore_args=(6,7))
@ -151,15 +186,15 @@ def computeeth(qv, tk, p):
@handle_casting(arg_idxs=(0,1,2,3)) @handle_casting(arg_idxs=(0,1,2,3))
@handle_extract_transpose() @handle_extract_transpose()
def computeuvmet(u, v, lat, lon, cen_long, cone): def computeuvmet(u, v, lat, lon, cen_long, cone):
longca = np.zeros((lat.shape[-2], lat.shape[-1]), np.float64, order="F") longca = np.zeros((lat.shape[0], lat.shape[1]), np.float64, order="F")
longcb = np.zeros((lon.shape[-2], lon.shape[-1]), np.float64, order="F") longcb = np.zeros((lon.shape[0], lon.shape[1]), np.float64, order="F")
rpd = Constants.PI/180. rpd = Constants.PI/180.
# Make the 2D array a 3D array with 1 dimension # Make the 2D array a 3D array with 1 dimension
if u.ndim < 3: if u.ndim < 3:
u = u.reshape((u.shape[-2], u.shape[-1], 1), order="F") u = u.reshape((u.shape[0], u.shape[1], 1), order="F")
v = v.reshape((v.shape[-2], v.shape[-1], 1), order="F") v = v.reshape((v.shape[0], v.shape[1], 1), order="F")
# istag will always be false since winds are destaggered already # istag will always be false since winds are destaggered already
# Missing values don't appear to be used, so setting to 0 # Missing values don't appear to be used, so setting to 0
@ -238,9 +273,9 @@ def computesrh(u, v, z, ter, top):
@handle_extract_transpose() @handle_extract_transpose()
def computeuh(zp, mapfct, u, v, wstag, dx, dy, bottom, top): def computeuh(zp, mapfct, u, v, wstag, dx, dy, bottom, top):
tem1 = np.zeros((u.shape[-3],u.shape[-2],u.shape[-1]), np.float64, tem1 = np.zeros((u.shape[0], u.shape[1], u.shape[2]), np.float64,
order="F") order="F")
tem2 = np.zeros((u.shape[-3],u.shape[-2],u.shape[-1]), np.float64, tem2 = np.zeros((u.shape[0], u.shape[1], u.shape[2]), np.float64,
order="F") order="F")
res = f_computeuh(zp, res = f_computeuh(zp,
@ -261,8 +296,8 @@ def computeuh(zp, mapfct, u, v, wstag, dx, dy, bottom, top):
@handle_casting(arg_idxs=(0,1,2,3)) @handle_casting(arg_idxs=(0,1,2,3))
@handle_extract_transpose() @handle_extract_transpose()
def computepw(p, tv, qv, ht): def computepw(p, tv, qv, ht):
# Note, dim -3 is height, we only want y and x
zdiff = np.zeros((p.shape[-2], p.shape[-1]), np.float64, order="F") zdiff = np.zeros((p.shape[0], p.shape[1]), np.float64, order="F")
res = f_computepw(p, res = f_computepw(p,
tv, tv,
qv, qv,
@ -292,19 +327,22 @@ def computedbz(p, tk, qv, qr, qs, qg, sn0, ivarint, iliqskin):
@handle_casting(arg_idxs=(0,1,2,3,4,5)) @handle_casting(arg_idxs=(0,1,2,3,4,5))
@handle_extract_transpose() @handle_extract_transpose()
def computecape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow): def computecape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow):
flip_cape = np.zeros((p_hpa.shape[-3],p_hpa.shape[-2],p_hpa.shape[-1]), flip_cape = np.zeros((p_hpa.shape[0], p_hpa.shape[1], p_hpa.shape[2]),
np.float64, order="F") np.float64, order="F")
flip_cin = np.zeros((p_hpa.shape[-3],p_hpa.shape[-2],p_hpa.shape[-1]), flip_cin = np.zeros((p_hpa.shape[0], p_hpa.shape[1], p_hpa.shape[2]),
np.float64, order="F") np.float64, order="F")
PSADITHTE, PSADIPRS, PSADITMK = get_lookup_tables() PSADITHTE, PSADIPRS, PSADITMK = get_lookup_tables()
PSADITMK = PSADITMK.T PSADITMK = PSADITMK.T
# The fortran routine needs pressure to be ascending in z-direction, # The fortran routine needs pressure to be ascending in z-direction,
# along with tk,qv,and ht. # along with tk,qv,and ht.
flip_p = p_hpa[::-1,:,:] # The extra mumbo-jumbo is so that the view created by numpy is fortran
flip_tk = tk[::-1,:,:] # contiguous. 'ascontiguousarray' only works in C ordering, hence the extra
flip_qv = qv[::-1,:,:] # transposes.
flip_ht = ht[::-1,:,:] flip_p = np.ascontiguousarray(p_hpa[:,:,::-1].T).T
flip_tk = np.ascontiguousarray(tk[:,:,::-1].T).T
flip_qv = np.ascontiguousarray(qv[:,:,::-1].T).T
flip_ht = np.ascontiguousarray(ht[:,:,::-1].T).T
f_computecape(flip_p, f_computecape(flip_p,
flip_tk, flip_tk,
@ -322,10 +360,9 @@ def computecape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow):
ter_follow, ter_follow,
FortranException()) FortranException())
# Don't need to transpose since we only passed a view to fortran # Need to flip the vertical back to decending pressure with height.
# Remember to flip cape and cin back to descending p coordinates cape = np.ascontiguousarray(flip_cape[:,:,::-1].T).T
cape = flip_cape[::-1,:,:] cin = np.ascontiguousarray(flip_cin[:,:,::-1].T).T
cin = flip_cin[::-1,:,:]
return (cape, cin) return (cape, cin)
@ -396,7 +433,7 @@ def smooth2d(field, passes):
else: else:
missing = Constants.DEFAULT_FILL missing = Constants.DEFAULT_FILL
field_copy = field.copy() field_copy = field.copy(order="A")
field_tmp = np.zeros(field_copy.shape, field_copy.dtype, order="F") field_tmp = np.zeros(field_copy.shape, field_copy.dtype, order="F")
f_filter2d(field_copy, f_filter2d(field_copy,

12
wrf_open/var/src/python/wrf/var/geoht.py

@ -1,6 +1,10 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .constants import Constants from .constants import Constants
from .destag import destagger from .destag import destagger
from .decorators import convert_units, set_height_metadata from .decorators import convert_units
from .metadecorators import set_height_metadata
from .util import extract_vars, either from .util import extract_vars, either
__all__ = ["get_geopt", "get_height"] __all__ = ["get_geopt", "get_height"]
@ -16,14 +20,16 @@ def _get_geoht(wrfnc, timeidx, height=True, msl=True,
varname = either("PH", "GHT")(wrfnc) varname = either("PH", "GHT")(wrfnc)
if varname == "PH": if varname == "PH":
ph_vars = extract_vars(wrfnc, timeidx, varnames=("PH", "PHB", "HGT")) ph_vars = extract_vars(wrfnc, timeidx, ("PH", "PHB", "HGT"),
method, squeeze, cache, nometa=True)
ph = ph_vars["PH"] ph = ph_vars["PH"]
phb = ph_vars["PHB"] phb = ph_vars["PHB"]
hgt = ph_vars["HGT"] hgt = ph_vars["HGT"]
geopt = ph + phb geopt = ph + phb
geopt_unstag = destagger(geopt, -3) geopt_unstag = destagger(geopt, -3)
else: else:
ght_vars = extract_vars(wrfnc, timeidx, varnames=("GHT", "HGT_M")) ght_vars = extract_vars(wrfnc, timeidx, ("GHT", "HGT_M"),
method, squeeze, cache, nometa=True)
geopt_unstag = ght_vars["GHT"] * Constants.G geopt_unstag = ght_vars["GHT"] * Constants.G
hgt = ght_vars["HGT_M"] hgt = ght_vars["HGT_M"]

19
wrf_open/var/src/python/wrf/var/helicity.py

@ -1,9 +1,12 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .constants import Constants from .constants import Constants
from .extension import computesrh, computeuh from .extension import computesrh, computeuh
from .destag import destagger from .destag import destagger
from .util import extract_vars, extract_global_attrs, either from .util import extract_vars, extract_global_attrs, either
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_srh", "get_uh"] __all__ = ["get_srh", "get_uh"]
@ -15,7 +18,7 @@ def get_srh(wrfnc, timeidx=0, top=3000.0,
# Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh) # Top can either be 3000 or 1000 (for 0-1 srh or 0-3 srh)
ncvars = extract_vars(wrfnc, timeidx, ("HGT", "PH", "PHB"), ncvars = extract_vars(wrfnc, timeidx, ("HGT", "PH", "PHB"),
method, squeeze, cache) method, squeeze, cache, nometa=True)
ter = ncvars["HGT"] ter = ncvars["HGT"]
ph = ncvars["PH"] ph = ncvars["PH"]
@ -23,11 +26,13 @@ def get_srh(wrfnc, timeidx=0, top=3000.0,
# As coded in NCL, but not sure this is possible # As coded in NCL, but not sure this is possible
varname = either("U", "UU")(wrfnc) varname = either("U", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = either("V", "VV")(wrfnc) varname = either("V", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
geopt = ph + phb geopt = ph + phb
@ -64,11 +69,13 @@ def get_uh(wrfnc, timeidx=0, bottom=2000.0, top=5000.0,
# As coded in NCL, but not sure this is possible # As coded in NCL, but not sure this is possible
varname = either("U", "UU")(wrfnc) varname = either("U", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = either("V", "VV")(wrfnc) varname = either("V", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
zp = ph + phb zp = ph + phb

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

@ -1,12 +1,17 @@
from math import floor, ceil from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as n import numpy as np
import numpy.ma as ma import numpy.ma as ma
from .extension import (interpz3d,interp2dxy,interp1d, from .extension import (interpz3d, interp2dxy, interp1d,
smooth2d,monotonic,vintrp) smooth2d, monotonic, vintrp, computevertcross,
from .decorators import handle_left_iter, handle_casting computeinterpline)
from .decorators import (handle_left_iter, handle_casting,
handle_extract_transpose)
from .metadecorators import set_interp_metadata
from .util import extract_vars, is_staggered from .util import extract_vars, is_staggered
from .interputils import get_xy, get_xy_z_params
from .constants import Constants, ConversionFactors from .constants import Constants, ConversionFactors
from .terrain import get_terrain from .terrain import get_terrain
from .geoht import get_height from .geoht import get_height
@ -16,148 +21,32 @@ from .pressure import get_pressure
__all__ = ["interplevel", "vertcross", "interpline", "vinterp"] __all__ = ["interplevel", "vertcross", "interpline", "vinterp"]
# Note: Extension decorator is good enough to handle left dims # Note: Extension decorator is good enough to handle left dims
def interplevel(data3d, zdata, desiredloc, missingval=Constants.DEFAULT_FILL): @set_interp_metadata("horiz")
def interplevel(field3d, z, desiredloc, missingval=Constants.DEFAULT_FILL):
"""Return the horizontally interpolated data at the provided level """Return the horizontally interpolated data at the provided level
data3d - the 3D field to interpolate field3d - the 3D field to interpolate
zdata - the vertical values (height or pressure) z - the vertical values (height or pressure)
desiredloc - the vertical level to interpolate at (must be same units as desiredloc - the vertical level to interpolate at (must be same units as
zdata) zdata)
missingval - the missing data value (which will be masked on return) missingval - the missing data value (which will be masked on return)
""" """
r1 = interpz3d(data3d, zdata, desiredloc, missingval) r1 = interpz3d(field3d, z, desiredloc, missingval)
masked_r1 = ma.masked_values (r1, missingval) masked_r1 = ma.masked_values (r1, missingval)
return masked_r1 return masked_r1
def _to_positive_idxs(shape, coord): @set_interp_metadata("cross")
if (coord[-2] >= 0 and coord[-1] >= 0): def vertcross(field3d, z, missingval=Constants.DEFAULT_FILL,
return coord
return [x if (x >= 0) else shape[i]+x for (i,x) in enumerate(coord) ]
def _get_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 combination of None arguments")
dx = x1 - x0
dy = y1 - y0
distance = (dx*dx + dy*dy)**0.5
npts = int(distance)
dxy = distance/npts
xy = n.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
@handle_left_iter(3, 0, ignore_args=(2,3,4,5,6),
ignore_kargs=("missingval", "pivot_point", "angle",
"start_point", "end_point"))
@handle_casting(arg_idxs=(0,1))
def vertcross(data3d, z, missingval=Constants.DEFAULT_FILL,
pivot_point=None, angle=None, pivot_point=None, angle=None,
start_point=None, end_point=None): start_point=None, end_point=None,
cache=None):
"""Return the vertical cross section for a 3D field, interpolated """Return the vertical cross section for a 3D field, interpolated
to a verical plane defined by a horizontal line. to a verical plane defined by a horizontal line.
Arguments: Arguments:
data3d - a 3D data field field3d - a 3D data field
z - 3D height field z - 3D height field
pivot_point - a pivot point of (south_north,west_east) pivot_point - a pivot point of (south_north,west_east)
(must be used with angle) (must be used with angle)
@ -167,70 +56,27 @@ def vertcross(data3d, z, missingval=Constants.DEFAULT_FILL,
""" """
if pivot_point is not None: try:
pos_pivot = _to_positive_idxs(z.shape[-2:], pivot_point) xy = cache["xy"]
else: var2dz = cache["var2dz"]
pos_pivot = pivot_point z_var2d = cache["z_var2d"]
except (KeyError, TypeError):
xy, var2dz, z_var2d = get_xy_z_params(z, pivot_point, angle,
start_point, end_point)
if start_point is not None: res = computevertcross(field3d, xy, var2dz, z_var2d, missingval)
pos_start = _to_positive_idxs(z.shape[-2:], start_point)
else:
pos_start = start_point
if end_point is not None:
pos_end = _to_positive_idxs(z.shape[-2:], end_point)
else:
pos_end = start_point
xdim = z.shape[-1] return ma.masked_values(res, missingval)
ydim = z.shape[-2]
xy = _get_xy(xdim, ydim, pos_pivot, angle, pos_start, pos_end)
# Interp z @set_interp_metadata("line")
var2dz = interp2dxy(z, xy) def interpline(field2d, pivot_point=None,
# interp to constant z grid
if(var2dz[0,0] > var2dz[-1,0]): # monotonically decreasing coordinate
z_max = floor(n.amax(z) / 10) * 10 # bottom value
z_min = ceil(n.amin(z) / 10) * 10 # top value
dz = 10
nlevels = int((z_max-z_min) / dz)
z_var2d = n.zeros((nlevels), dtype=z.dtype)
z_var2d[0] = z_max
dz = -dz
else:
z_max = n.amax(z)
z_min = 0.
dz = 0.01 * z_max
nlevels = int(z_max / dz)
z_var2d = n.zeros((nlevels), dtype=z.dtype)
z_var2d[0] = z_min
for i in xrange(1,nlevels):
z_var2d[i] = z_var2d[0] + i*dz
#interp the variable
var2d = n.zeros((nlevels, xy.shape[0]),dtype=var2dz.dtype)
var2dtmp = interp2dxy(data3d, xy)
for i in xrange(xy.shape[0]):
var2d[:,i] = interp1d(var2dtmp[:,i], var2dz[:,i], z_var2d, missingval)
return ma.masked_values(var2d, missingval)
@handle_left_iter(2, 0, ignore_args=(1,2,3,4),
ignore_kargs=("pivot_point", "angle",
"start_point", "end_point"))
@handle_casting(arg_idxs=(0,))
def interpline(data2d, pivot_point=None,
angle=None, start_point=None, angle=None, start_point=None,
end_point=None): end_point=None, cache=None):
"""Return the 2D field interpolated along a line. """Return the 2D field interpolated along a line.
Arguments: Arguments:
var2d - a 2D data field field2d - a 2D data field
pivot_point - a pivot point of (south_north,west_east) pivot_point - a pivot point of (south_north,west_east)
angle - the angle through the pivot point in degrees angle - the angle through the pivot point in degrees
start_point - a start_point tuple of (south_north1,west_east1) start_point - a start_point tuple of (south_north1,west_east1)
@ -238,38 +84,18 @@ def interpline(data2d, pivot_point=None,
""" """
if pivot_point is not None: try:
pos_pivot = _to_positive_idxs(data2d.shape[-2:], pivot_point) xy = cache["xy"]
else: except (KeyError, TypeError):
pos_pivot = pivot_point xy = get_xy(field2d, pivot_point, angle, start_point, end_point)
if start_point is not None:
pos_start = _to_positive_idxs(data2d.shape[-2:], start_point)
else:
pos_start = start_point
if end_point is not None:
pos_end = _to_positive_idxs(data2d.shape[-2:], end_point)
else:
pos_end = start_point
tmp_shape = [1] + [x for x in data2d.shape] return computeinterpline(field2d, xy)
var2dtmp = n.zeros(tmp_shape, data2d.dtype)
xdim = data2d.shape[-1]
ydim = data2d.shape[-2]
xy = _get_xy(xdim, ydim, pos_pivot, angle, pos_start, pos_end)
var2dtmp[0,:,:] = data2d[:,:]
var1dtmp = interp2dxy(var2dtmp, xy)
return var1dtmp[0,:]
@set_interp_metadata("vinterp")
def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False, def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
field_type=None, log_p=False): field_type=None, log_p=False, timeidx=-1, method="cat",
squeeze=True, cache=None):
# Remove case sensitivity # Remove case sensitivity
field_type = field_type.lower() if field_type is not None else "none" field_type = field_type.lower() if field_type is not None else "none"
vert_coord = vert_coord.lower() if vert_coord is not None else "none" vert_coord = vert_coord.lower() if vert_coord is not None else "none"
@ -301,8 +127,8 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
sclht = rgas*256./9.81 sclht = rgas*256./9.81
# interp_levels might be a list or tuple, make a numpy array # interp_levels might be a list or tuple, make a numpy array
if not isinstance(interp_levels, n.ndarray): if not isinstance(interp_levels, np.ndarray):
interp_levels = n.array(interp_levels, "float64") interp_levels = np.asarray(interp_levels, np.float64)
# TODO: Check if field is staggered # TODO: Check if field is staggered
if is_staggered(field, wrfnc): if is_staggered(field, wrfnc):
@ -327,19 +153,26 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
icase = icase_lookup[field_type] icase = icase_lookup[field_type]
# Extract vriables # Extract vriables
timeidx = -1 # Should this be an argument? #timeidx = -1 # Should this be an argument?
ncvars = extract_vars(wrfnc, timeidx, varnames=("PSFC", "QVAPOR", "F")) ncvars = extract_vars(wrfnc, timeidx, ("PSFC", "QVAPOR", "F"),
method, squeeze, cache, nometa=True)
sfp = ncvars["PSFC"] * ConversionFactors.PA_TO_HPA sfp = ncvars["PSFC"] * ConversionFactors.PA_TO_HPA
qv = ncvars["QVAPOR"] qv = ncvars["QVAPOR"]
coriolis = ncvars["F"] coriolis = ncvars["F"]
terht = get_terrain(wrfnc, timeidx) terht = get_terrain(wrfnc, timeidx, units="m",
t = get_theta(wrfnc, timeidx) method=method, squeeze=squeeze, cache=cache)
tk = get_temp(wrfnc, timeidx, units="k") t = get_theta(wrfnc, timeidx, units="k",
p = get_pressure(wrfnc, timeidx, units="pa") method=method, squeeze=squeeze, cache=cache)
ght = get_height(wrfnc, timeidx, msl=True) tk = get_temp(wrfnc, timeidx, units="k",
ht_agl = get_height(wrfnc, timeidx, msl=False) method=method, squeeze=squeeze, cache=cache)
p = get_pressure(wrfnc, timeidx, units="pa",
method=method, squeeze=squeeze, cache=cache)
ght = get_height(wrfnc, timeidx, msl=True, units="m",
method=method, squeeze=squeeze, cache=cache)
ht_agl = get_height(wrfnc, timeidx, msl=False, units="m",
method=method, squeeze=squeeze, cache=cache)
smsfp = smooth2d(sfp, 3) smsfp = smooth2d(sfp, 3)
@ -352,11 +185,11 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
elif vert_coord == "ght_msl": elif vert_coord == "ght_msl":
vcor = 2 vcor = 2
vcord_array = n.exp(-ght/sclht) vcord_array = np.exp(-ght/sclht)
elif vert_coord == "ght_agl": elif vert_coord == "ght_agl":
vcor = 3 vcor = 3
vcord_array = n.exp(-ht_agl/sclht) vcord_array = np.exp(-ht_agl/sclht)
elif vert_coord in ("theta", "th"): elif vert_coord in ("theta", "th"):
vcor = 4 vcor = 4
@ -389,7 +222,7 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
icase = 0 icase = 0
# Set the missing value # Set the missing value
if isinstance(field, n.ma.MaskedArray): if isinstance(field, ma.MaskedArray):
missing = field.fill_value missing = field.fill_value
else: else:
missing = Constants.DEFAULT_FILL missing = Constants.DEFAULT_FILL
@ -400,6 +233,23 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
return ma.masked_values(res, missing) return ma.masked_values(res, missing)
# Thin wrappers around fortran calls which allow for metadata
# Move to the new routines module
# TODO: Rename after the extensions are renamed
@set_interp_metadata("horiz")
def wrap_interpz3d(field3d, z, desiredloc, missingval):
return interpz3d(field3d, z, desiredloc, missingval)
@set_interp_metadata("2dxy")
def wrap_interp2dxy(field3d, xy):
return interp2dxy(field3d, xy)
@set_interp_metadata("1d")
def wrap_interp1d(v_in, z_in, z_out, missingval):
return interp1d(v_in, z_in, z_out, missingval)

182
wrf_open/var/src/python/wrf/var/interputils.py

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

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

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from collections import Iterable from collections import Iterable
import numpy as np import numpy as np
@ -6,18 +9,18 @@ from .config import xarray_enabled
from .constants import Constants from .constants import Constants
from .extension import computeij, computell from .extension import computeij, computell
from .util import (extract_vars, extract_global_attrs, from .util import (extract_vars, extract_global_attrs,
either, _is_moving_domain, _is_multi_time, either, _is_moving_domain, _is_multi_time_req,
iter_left_indexes) iter_left_indexes)
from .decorators import set_latlon_metadata from .metadecorators import set_latlon_metadata
if xarray_enabled(): if xarray_enabled():
from xarray import DataArray from xarray import DataArray
__all__ = ["get_lat", "get_lon", "get_ij", "get_ll"] __all__ = ["get_lat", "get_lon", "get_ij", "get_ll"]
def _lat_varname(stagger): def _lat_varname(wrfnc, stagger):
if stagger is None or stagger.lower() == "m": if stagger is None or stagger.lower() == "m":
varname = either("XLAT", "XLAT_M") varname = either("XLAT", "XLAT_M")(wrfnc)
elif stagger.lower() == "u" or stagger.lower() == "v": elif stagger.lower() == "u" or stagger.lower() == "v":
varname = "XLAT_{}".format(stagger.upper()) varname = "XLAT_{}".format(stagger.upper())
else: else:
@ -25,9 +28,9 @@ def _lat_varname(stagger):
return varname return varname
def _lon_varname(stagger): def _lon_varname(wrfnc, stagger):
if stagger is None or stagger.lower() == "m": if stagger is None or stagger.lower() == "m":
varname = either("XLONG", "XLONG_M") varname = either("XLONG", "XLONG_M")(wrfnc)
elif stagger.lower() == "u" or stagger.lower() == "v": elif stagger.lower() == "u" or stagger.lower() == "v":
varname = "XLONG_{}".format(stagger.upper()) varname = "XLONG_{}".format(stagger.upper())
else: else:
@ -38,16 +41,18 @@ def _lon_varname(stagger):
def get_lat(wrfnc, timeidx=0, stagger=None, def get_lat(wrfnc, timeidx=0, stagger=None,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varname = _lat_varname(stagger) varname = _lat_varname(wrfnc, stagger)
lat_var = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) lat_var = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=False)
return lat_var[varname] return lat_var[varname]
def get_lon(wrfnc, timeidx=0, stagger=None, def get_lon(wrfnc, timeidx=0, stagger=None,
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varname = _lon_varname(stagger) varname = _lon_varname(wrfnc, stagger)
lon_var = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) lon_var = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=False)
return lon_var[varname] return lon_var[varname]
@ -83,7 +88,7 @@ def _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache):
lat_timeidx = timeidx lat_timeidx = timeidx
# Only need all the lats/lons if it's a moving domain file/files # Only need all the lats/lons if it's a moving domain file/files
if _is_multi_time(timeidx): if _is_multi_time_req(timeidx):
if not _is_moving_domain(wrfnc, latvar=latvar, lonvar=lonvar): if not _is_moving_domain(wrfnc, latvar=latvar, lonvar=lonvar):
lat_timeidx = 0 lat_timeidx = 0

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

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

7
wrf_open/var/src/python/wrf/var/omega.py

@ -1,9 +1,11 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .constants import Constants from .constants import Constants
from .destag import destagger from .destag import destagger
from .extension import computeomega,computetk from .extension import computeomega,computetk
from .util import extract_vars from .util import extract_vars
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_omega"] __all__ = ["get_omega"]
@ -12,7 +14,8 @@ __all__ = ["get_omega"]
units="Pa/s") units="Pa/s")
def get_omega(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None): def get_omega(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
varnames=("T", "P", "W", "PB", "QVAPOR") varnames=("T", "P", "W", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
w = ncvars["W"] w = ncvars["W"]

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

@ -1,4 +1,5 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .util import extract_vars from .util import extract_vars

9
wrf_open/var/src/python/wrf/var/pressure.py

@ -1,5 +1,8 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .decorators import convert_units, copy_and_set_metadata from .decorators import convert_units
from .metadecorators import copy_and_set_metadata
from .util import extract_vars, either from .util import extract_vars, either
__all__ = ["get_pressure", "get_pressure_hpa"] __all__ = ["get_pressure", "get_pressure_hpa"]
@ -13,13 +16,13 @@ def get_pressure(wrfnc, timeidx=0, units="pa",
varname = either("P", "PRES")(wrfnc) varname = either("P", "PRES")(wrfnc)
if varname == "P": if varname == "P":
p_vars = extract_vars(wrfnc, timeidx, ("P", "PB"), p_vars = extract_vars(wrfnc, timeidx, ("P", "PB"),
method, squeeze, cache) method, squeeze, cache, nometa=True)
p = p_vars["P"] p = p_vars["P"]
pb = p_vars["PB"] pb = p_vars["PB"]
pres = p + pb pres = p + pb
else: else:
pres = extract_vars(wrfnc, timeidx, "PRES", pres = extract_vars(wrfnc, timeidx, "PRES",
method, squeeze, cache)["PRES"] method, squeeze, cache, nometa=True)["PRES"]
return pres return pres

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

@ -1,3 +1,5 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as np import numpy as np
import math import math

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

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as n import numpy as n
__all__ = ["get_lookup_tables"] __all__ = ["get_lookup_tables"]

9
wrf_open/var/src/python/wrf/var/pw.py

@ -1,17 +1,22 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .extension import computepw,computetv,computetk from .extension import computepw,computetv,computetk
from .constants import Constants from .constants import Constants
from .util import extract_vars from .util import extract_vars
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_pw"] __all__ = ["get_pw"]
@copy_and_set_metadata(copy_varname="T", name="pw", @copy_and_set_metadata(copy_varname="T", name="pw",
remove_dims=("bottom_top",),
description="precipitable water", description="precipitable water",
MemoryOrder="XY",
units="kg m-2") units="kg m-2")
def get_pw(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None): def get_pw(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
varnames=("T", "P", "PB", "PH", "PHB", "QVAPOR") varnames=("T", "P", "PB", "PH", "PHB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]

10
wrf_open/var/src/python/wrf/var/rh.py

@ -1,8 +1,10 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .constants import Constants from .constants import Constants
from .extension import computerh, computetk from .extension import computerh, computetk
from .util import extract_vars from .util import extract_vars
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_rh", "get_rh_2m"] __all__ = ["get_rh", "get_rh_2m"]
@ -11,7 +13,8 @@ __all__ = ["get_rh", "get_rh_2m"]
delete_attrs=("units",)) delete_attrs=("units",))
def get_rh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None): def get_rh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
varnames=("T", "P", "PB", "QVAPOR") varnames=("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -30,7 +33,8 @@ def get_rh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
delete_attrs=("units",)) delete_attrs=("units",))
def get_rh_2m(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None): def get_rh_2m(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
varnames=("T2", "PSFC", "Q2") varnames=("T2", "PSFC", "Q2")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t2 = ncvars["T2"] t2 = ncvars["T2"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
q2 = ncvars["Q2"] q2 = ncvars["Q2"]

13
wrf_open/var/src/python/wrf/var/slp.py

@ -1,19 +1,25 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .extension import computeslp, computetk from .extension import computeslp, computetk
from .constants import Constants from .constants import Constants
from .destag import destagger from .destag import destagger
from .decorators import convert_units, copy_and_set_metadata from .decorators import convert_units
from .metadecorators import copy_and_set_metadata
from .util import extract_vars from .util import extract_vars
__all__ = ["get_slp"] __all__ = ["get_slp"]
@copy_and_set_metadata(copy_varname="T", name="slp", @copy_and_set_metadata(copy_varname="T", name="slp",
remove_dims=("bottom_top",), remove_dims=("bottom_top",),
description="sea level pressure") description="sea level pressure",
MemoryOrder="XY")
@convert_units("pressure", "hpa") @convert_units("pressure", "hpa")
def get_slp(wrfnc, timeidx=0, units="hpa", def get_slp(wrfnc, timeidx=0, units="hpa",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varnames=("T", "P", "PB", "QVAPOR", "PH", "PHB") varnames=("T", "P", "PB", "QVAPOR", "PH", "PHB")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -25,6 +31,7 @@ def get_slp(wrfnc, timeidx=0, units="hpa",
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
full_p = p + pb full_p = p + pb
qvapor[qvapor < 0] = 0. qvapor[qvapor < 0] = 0.
full_ph = (ph + phb) / Constants.G full_ph = (ph + phb) / Constants.G
destag_ph = destagger(full_ph, -3) destag_ph = destagger(full_ph, -3)

20
wrf_open/var/src/python/wrf/var/temp.py

@ -1,7 +1,10 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .constants import Constants from .constants import Constants
from .extension import computetk, computeeth, computetv, computewetbulb from .extension import computetk, computeeth, computetv, computewetbulb
from .decorators import convert_units, copy_and_set_metadata from .decorators import convert_units
from .metadecorators import copy_and_set_metadata
from .util import extract_vars from .util import extract_vars
__all__ = ["get_theta", "get_temp", "get_eth", "get_tv", "get_tw", __all__ = ["get_theta", "get_temp", "get_eth", "get_tv", "get_tw",
@ -14,7 +17,8 @@ def get_theta(wrfnc, timeidx=0, units="k",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varnames = ("T",) varnames = ("T",)
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
@ -28,7 +32,8 @@ def get_temp(wrfnc, timeidx=0, units="k", method="cat", squeeze=True,
"""Return the temperature in Kelvin or Celsius""" """Return the temperature in Kelvin or Celsius"""
varnames=("T", "P", "PB") varnames=("T", "P", "PB")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -47,7 +52,8 @@ def get_eth(wrfnc, timeidx=0, units="k", method="cat", squeeze=True,
"Return equivalent potential temperature (Theta-e) in Kelvin" "Return equivalent potential temperature (Theta-e) in Kelvin"
varnames=("T", "P", "PB", "QVAPOR") varnames=("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -69,7 +75,8 @@ def get_tv(wrfnc, timeidx=0, units="k", method="cat", squeeze=True,
"Return the virtual temperature (tv) in Kelvin or Celsius" "Return the virtual temperature (tv) in Kelvin or Celsius"
varnames=("T", "P", "PB", "QVAPOR") varnames=("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -92,7 +99,8 @@ def get_tw(wrfnc, timeidx=0, units="k", method="cat", squeeze=True,
"Return the wetbulb temperature (tw)" "Return the wetbulb temperature (tw)"
varnames=("T", "P", "PB", "QVAPOR") varnames=("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache) ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
nometa=True)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]

7
wrf_open/var/src/python/wrf/var/terrain.py

@ -1,5 +1,8 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .decorators import convert_units, copy_and_set_metadata from .decorators import convert_units
from .metadecorators import copy_and_set_metadata
from .util import extract_vars, either from .util import extract_vars, either
__all__ = ["get_terrain"] __all__ = ["get_terrain"]
@ -12,7 +15,7 @@ def get_terrain(wrfnc, timeidx=0, units="m", method="cat", squeeze=True,
cache=None): cache=None):
varname = either("HGT", "HGT_M")(wrfnc) varname = either("HGT", "HGT_M")(wrfnc)
return extract_vars(wrfnc, timeidx, varname, return extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache)[varname] method, squeeze, cache, nometa=True)[varname]

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

@ -1,3 +1,5 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .util import extract_times from .util import extract_times

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

@ -1,3 +1,5 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .constants import Constants, ConversionFactors from .constants import Constants, ConversionFactors

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

@ -1,12 +1,18 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from collections import Iterable, Mapping, OrderedDict from collections import Iterable, Mapping, OrderedDict
from itertools import product from itertools import product
from inspect import getargspec
import datetime as dt import datetime as dt
import numpy as np import numpy as np
import numpy.ma as ma
from .config import xarray_enabled from .config import xarray_enabled
from .projection import getproj from .projection import getproj
if xarray_enabled(): if xarray_enabled():
from xarray import DataArray from xarray import DataArray
@ -14,33 +20,61 @@ __all__ = ["extract_vars", "extract_global_attrs", "extract_dim",
"combine_files", "is_standard_wrf_var", "extract_times", "combine_files", "is_standard_wrf_var", "extract_times",
"iter_left_indexes", "get_left_indexes", "get_right_slices", "iter_left_indexes", "get_left_indexes", "get_right_slices",
"is_staggered", "get_proj_params", "viewitems", "viewkeys", "is_staggered", "get_proj_params", "viewitems", "viewkeys",
"viewvalues"] "viewvalues", "combine_with", "from_args", "arg_location",
"args_to_list", "npvalues", "XYCoord"]
_COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"),
"XLONG" : ("XLAT", "XLONG"),
"XLAT_M" : ("XLAT_M", "XLONG_M"),
"XLONG_M" : ("XLAT_M", "XLONG_M"),
"XLAT_U" : ("XLAT_U", "XLONG_U"),
"XLONG_U" : ("XLAT_U", "XLONG_U"),
"XLAT_V" : ("XLAT_V", "XLONG_V"),
"XLONG_V" : ("XLAT_V", "XLONG_V"),
"CLAT" : ("CLAT", "CLONG"),
"CLONG" : ("CLAT", "CLONG")}
_COORD_VARS = ("XLAT", "XLONG", "XLAT_M", "XLONG_M", "XLAT_U", "XLONG_U",
"XLAT_V", "XLONG_V", "CLAT", "CLONG")
def _is_coord_var(varname):
return varname in _COORD_VARS
def _get_coord_pairs(varname):
return _COORD_PAIR_MAP[varname]
def _is_multi_time_req(timeidx):
return timeidx == -1
def _is_multi_time(timeidx):
if timeidx == -1:
return True
return False
def _is_multi_file(wrfnc): def _is_multi_file(wrfnc):
if isinstance(wrfnc, Iterable) and not isinstance(wrfnc, str): return (isinstance(wrfnc, Iterable) and not isstr(wrfnc))
return True
return False
def _is_mapping(wrfnc): def _is_mapping(wrfnc):
if isinstance(wrfnc, Mapping): return isinstance(wrfnc, Mapping)
return True
return False
def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar): def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar):
lats = wrfnc.variables[latvar] lats = wrfnc.variables[latvar]
lons = wrfnc.variables[lonvar] lons = wrfnc.variables[lonvar]
# Need to check all times # Need to check all times
for i in xrange(lats.shape[-3]): for i in xrange(lats.shape[-3]):
start_idxs = [0]*lats.ndim start_idxs = [0]*len(lats.shape) # PyNIO does not support ndim
start_idxs[-3] = i start_idxs[-3] = i
start_idxs = tuple(start_idxs)
end_idxs = [-1]*lats.ndim end_idxs = [-1]*len(lats.shape)
end_idxs[-3] = i end_idxs[-3] = i
end_idxs = tuple(end_idxs)
if (first_ll_corner[0] != lats[start_idxs] or if (first_ll_corner[0] != lats[start_idxs] or
first_ll_corner[1] != lons[start_idxs] or first_ll_corner[1] != lons[start_idxs] or
@ -62,8 +96,15 @@ def _is_moving_domain(wrfseq, varname=None, latvar="XLAT", lonvar="XLONG"):
first_wrfnc = next(wrf_iter) first_wrfnc = next(wrf_iter)
if varname is not None: if varname is not None:
try:
coord_names = getattr(first_wrfnc.variables[varname], coord_names = getattr(first_wrfnc.variables[varname],
"coordinates").split() "coordinates").split()
except AttributeError:
# Variable doesn't have a coordinates attribute, use the
# arguments
lon_coord = lonvar
lat_coord = latvar
else:
lon_coord = coord_names[0] lon_coord = coord_names[0]
lat_coord = coord_names[1] lat_coord = coord_names[1]
else: else:
@ -73,10 +114,14 @@ def _is_moving_domain(wrfseq, varname=None, latvar="XLAT", lonvar="XLONG"):
lats = first_wrfnc.variables[lat_coord] lats = first_wrfnc.variables[lat_coord]
lons = first_wrfnc.variables[lon_coord] lons = first_wrfnc.variables[lon_coord]
zero_idxs = [0] * first_wrfnc.variables[lat_coord].ndim
zero_idxs = [0]*len(lats.shape) # PyNIO doesn't have ndim
last_idxs = list(zero_idxs) last_idxs = list(zero_idxs)
last_idxs[-2:] = [-1]*2 last_idxs[-2:] = [-1]*2
zero_idxs = tuple(zero_idxs)
last_idxs = tuple(last_idxs)
lat0 = lats[zero_idxs] lat0 = lats[zero_idxs]
lat1 = lats[last_idxs] lat1 = lats[last_idxs]
lon0 = lons[zero_idxs] lon0 = lons[zero_idxs]
@ -107,7 +152,7 @@ def _get_global_attr(wrfnc, attr):
return val return val
def extract_global_attrs(wrfnc, attrs): def extract_global_attrs(wrfnc, attrs):
if isinstance(attrs, str): if isstr(attrs):
attrlist = [attrs] attrlist = [attrs]
else: else:
attrlist = attrs attrlist = attrs
@ -134,7 +179,7 @@ def extract_dim(wrfnc, dim):
return len(d) #netCDF4 return len(d) #netCDF4
return d # PyNIO return d # PyNIO
def _combine_dict(wrfdict, varname, timeidx, method): def _combine_dict(wrfdict, varname, timeidx, method, nometa):
"""Dictionary combination creates a new left index for each key, then """Dictionary combination creates a new left index for each key, then
does a cat or join for the list of files for that key""" does a cat or join for the list of files for that key"""
keynames = [] keynames = []
@ -145,7 +190,8 @@ def _combine_dict(wrfdict, varname, timeidx, method):
keynames.append(first_key) keynames.append(first_key)
first_array = _extract_var(wrfdict[first_key], varname, first_array = _extract_var(wrfdict[first_key], varname,
timeidx, method, squeeze=False) timeidx, method, squeeze=False,
nometa=nometa)
# Create the output data numpy array based on the first array # Create the output data numpy array based on the first array
@ -171,10 +217,8 @@ def _combine_dict(wrfdict, varname, timeidx, method):
outdata[idx,:] = vardata.values[:] outdata[idx,:] = vardata.values[:]
idx += 1 idx += 1
if not xarray_enabled(): if xarray_enabled() and not nometa:
outarr = outdata outname = unicode(first_array.name)
else:
outname = str(first_array.name)
# Note: assumes that all entries in dict have same coords # Note: assumes that all entries in dict have same coords
outcoords = OrderedDict(first_array.coords) outcoords = OrderedDict(first_array.coords)
outdims = ["key"] + list(first_array.dims) outdims = ["key"] + list(first_array.dims)
@ -183,25 +227,31 @@ def _combine_dict(wrfdict, varname, timeidx, method):
outarr = DataArray(outdata, name=outname, coords=outcoords, outarr = DataArray(outdata, name=outname, coords=outcoords,
dims=outdims, attrs=outattrs) dims=outdims, attrs=outattrs)
else:
outarr = outdata
return outarr return outarr
# TODO: implement in C # TODO: implement in C
def _cat_files(wrfseq, varname, timeidx): def _cat_files(wrfseq, varname, timeidx, squeeze, nometa):
is_moving = _is_moving_domain(wrfseq, varname) is_moving = _is_moving_domain(wrfseq, varname)
file_times = extract_times(wrfseq, timeidx) file_times = extract_times(wrfseq, timeidx)
multitime = _is_multi_time(timeidx) multitime = _is_multi_time_req(timeidx)
time_idx_or_slice = timeidx if not multitime else slice(None, None, None) time_idx_or_slice = timeidx if not multitime else slice(None)
# wrfseq might be a generator # wrfseq might be a generator
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
first_var = (_build_data_array(next(wrf_iter), varname, timeidx, is_moving) if xarray_enabled() and not nometa:
if xarray_enabled() else first_var = _build_data_array(next(wrf_iter), varname,
wrfseq[0].variables[varname][time_idx_or_slice, :]) timeidx, is_moving)
else:
first_var = (next(wrf_iter)).variables[varname][time_idx_or_slice, :]
if not multitime:
first_var = first_var[np.newaxis,:]
outdims = [len(file_times)] outdims = [len(file_times)]
@ -236,13 +286,16 @@ def _cat_files(wrfseq, varname, timeidx):
startidx = endidx startidx = endidx
if xarray_enabled(): if xarray_enabled() and not nometa:
# FIXME: If it's a moving nest, then the coord arrays need to have same # FIXME: If it's a moving nest, then the coord arrays need to have same
# time indexes as the whole data set # time indexes as the whole data set
outname = str(first_var.name) outname = unicode(first_var.name)
outattrs = OrderedDict(first_var.attrs) outattrs = OrderedDict(first_var.attrs)
outcoords = OrderedDict(first_var.coords) outcoords = OrderedDict(first_var.coords)
outdimnames = list(first_var.dims) outdimnames = list(first_var.dims)
if "Time" not in outdimnames:
outdimnames.insert(0, "Time")
outcoords[outdimnames[0]] = file_times # New time dimension values outcoords[outdimnames[0]] = file_times # New time dimension values
outarr = DataArray(outdata, name=outname, coords=outcoords, outarr = DataArray(outdata, name=outname, coords=outcoords,
@ -254,24 +307,23 @@ def _cat_files(wrfseq, varname, timeidx):
return outarr return outarr
# TODO: implement in C # TODO: implement in C
def _join_files(wrfseq, varname, timeidx): def _join_files(wrfseq, varname, timeidx, nometa):
is_moving = _is_moving_domain(wrfseq, varname) is_moving = _is_moving_domain(wrfseq, varname)
multitime = _is_multi_time(timeidx) multitime = _is_multi_time_req(timeidx)
numfiles = len(wrfseq) numfiles = len(wrfseq)
if not multitime: time_idx_or_slice = timeidx if not multitime else slice(None)
time_idx_or_slice = timeidx
else:
time_idx_or_slice = slice(None, None, None)
# wrfseq might be a generator # wrfseq might be a generator
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
if xarray_enabled(): if xarray_enabled() and not nometa:
first_var = _build_data_array(next(wrf_iter), varname, first_var = _build_data_array(next(wrf_iter), varname,
timeidx, is_moving) timeidx, is_moving)
else: else:
first_var = (next(wrf_iter)).variables[varname][time_idx_or_slice, :] first_var = (next(wrf_iter)).variables[varname][time_idx_or_slice, :]
if not multitime:
first_var = first_var[np.newaxis, :]
# Create the output data numpy array based on the first array # Create the output data numpy array based on the first array
outdims = [numfiles] outdims = [numfiles]
@ -287,11 +339,14 @@ def _join_files(wrfseq, varname, timeidx):
except StopIteration: except StopIteration:
break break
else: else:
outdata[idx,:] = wrfnc.variables[varname][time_idx_or_slice, :] outvar = wrfnc.variables[varname][time_idx_or_slice, :]
if not multitime:
outvar = outvar[np.newaxis, :]
outdata[idx,:] = outvar[:]
idx += 1 idx += 1
if xarray_enabled(): if xarray_enabled() and not nometa:
outname = str(first_var.name) outname = unicode(first_var.name)
outcoords = OrderedDict(first_var.coords) outcoords = OrderedDict(first_var.coords)
outattrs = OrderedDict(first_var.attrs) outattrs = OrderedDict(first_var.attrs)
# New dimensions # New dimensions
@ -306,37 +361,48 @@ def _join_files(wrfseq, varname, timeidx):
return outarr return outarr
def combine_files(wrfseq, varname, timeidx, method="cat", squeeze=True): def combine_files(wrfseq, varname, timeidx, method="cat", squeeze=True,
nometa=False):
# Dictionary is unique # Dictionary is unique
if _is_mapping(wrfseq): if _is_mapping(wrfseq):
outarr = _combine_dict(wrfseq, varname, timeidx, method) outarr = _combine_dict(wrfseq, varname, timeidx, method, nometa)
elif method.lower() == "cat": elif method.lower() == "cat":
outarr = _cat_files(wrfseq, varname, timeidx) outarr = _cat_files(wrfseq, varname, timeidx, squeeze, nometa)
elif method.lower() == "join": elif method.lower() == "join":
outarr = _join_files(wrfseq, varname, timeidx) outarr = _join_files(wrfseq, varname, timeidx, nometa)
else: else:
raise ValueError("method must be 'cat' or 'join'") raise ValueError("method must be 'cat' or 'join'")
return outarr.squeeze() if squeeze else outarr return outarr.squeeze() if squeeze else outarr
# Note, always returns the full data set with the time dimension included
def _build_data_array(wrfnc, varname, timeidx, is_moving_domain): def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
multitime = _is_multi_time(timeidx) multitime = _is_multi_time_req(timeidx)
time_idx_or_slice = timeidx if not multitime else slice(None)
var = wrfnc.variables[varname] var = wrfnc.variables[varname]
data = var[:] data = var[time_idx_or_slice, :]
# Want to preserve the time dimension
if not multitime:
data = data[np.newaxis, :]
attrs = OrderedDict(var.__dict__) attrs = OrderedDict(var.__dict__)
dimnames = var.dimensions dimnames = var.dimensions[-data.ndim:]
# WRF variables will have a coordinates attribute. MET_EM files have # WRF variables will have a coordinates attribute. MET_EM files have
# a stagger attribute which indicates the coordinate variable. # a stagger attribute which indicates the coordinate variable.
try: try:
# WRF files # WRF files
coord_attr = getattr(var, "coordinates") coord_attr = getattr(var, "coordinates")
except KeyError: except AttributeError:
if _is_coord_var(varname):
# Coordinate variable (most likely XLAT or XLONG)
lat_coord, lon_coord = _get_coord_pairs(varname)
else:
try: try:
# met_em files # met_em files
stag_attr = getattr(var, "stagger") stag_attr = getattr(var, "stagger")
except KeyError: except AttributeError:
lon_coord = None lon_coord = None
lat_coord = None lat_coord = None
else: else:
@ -393,54 +459,66 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
if dimnames[0] == "Time": if dimnames[0] == "Time":
coords[dimnames[0]] = extract_times(wrfnc, timeidx) coords[dimnames[0]] = extract_times(wrfnc, timeidx)
data_array = DataArray(data, name=varname, dims=dimnames, coords=coords, data_array = DataArray(data, name=varname, dims=dimnames, coords=coords,
attrs=attrs) attrs=attrs)
return data_array return data_array
# Cache is a dictionary of already extracted variables # Cache is a dictionary of already extracted variables
def _extract_var(wrfnc, varname, timeidx, method, squeeze, cache): def _extract_var(wrfnc, varname, timeidx, method, squeeze, cache, nometa):
# Mainly used internally so variables don't get extracted multiple times, # Mainly used internally so variables don't get extracted multiple times,
# particularly to copy metadata. This can be slow. # particularly to copy metadata. This can be slow.
if cache is not None: if cache is not None:
try: try:
return cache[varname] cache_var = cache[varname]
except KeyError: except KeyError:
pass pass
else:
if nometa:
if isinstance(cache_var, DataArray):
return cache_var.values
return cache_var
is_moving = _is_moving_domain(wrfnc, varname) is_moving = _is_moving_domain(wrfnc, varname)
multitime = _is_multi_time(timeidx) multitime = _is_multi_time_req(timeidx)
multifile = _is_multi_file(wrfnc) multifile = _is_multi_file(wrfnc)
if not multifile: if not multifile:
if xarray_enabled(): if xarray_enabled() and not nometa:
result = _build_data_array(wrfnc, varname, timeidx, is_moving) result = _build_data_array(wrfnc, varname, timeidx, is_moving)
else: else:
if not multitime: if not multitime:
result = wrfnc.variables[varname][timeidx,:] result = wrfnc.variables[varname][timeidx,:]
result = result[np.newaxis, :] # So that no squeeze works
else: else:
result = wrfnc.variables[varname][:] result = wrfnc.variables[varname][:]
else: else:
# Squeeze handled in this routine, so just return it # Squeeze handled in this routine, so just return it
return combine_files(wrfnc, varname, timeidx, method, squeeze) return combine_files(wrfnc, varname, timeidx, method, squeeze, nometa)
return result.squeeze() if squeeze else result return result.squeeze() if squeeze else result
def extract_vars(wrfnc, timeidx, varnames, method="cat", squeeze=True, def extract_vars(wrfnc, timeidx, varnames, method="cat", squeeze=True,
cache=None): cache=None, nometa=False):
if isinstance(varnames, str): if isstr(varnames):
varlist = [varnames] varlist = [varnames]
else: else:
varlist = varnames varlist = varnames
return {var:_extract_var(wrfnc, var, timeidx, method, squeeze, cache) return {var:_extract_var(wrfnc, var, timeidx,
method, squeeze, cache, nometa)
for var in varlist} for var in varlist}
def _make_time(timearr): def _make_time(timearr):
return dt.datetime.strptime("".join(timearr[:]), "%Y-%m-%d_%H:%M:%S") return dt.datetime.strptime("".join(timearr[:]), "%Y-%m-%d_%H:%M:%S")
def _file_times(wrfnc, timeidx): def _file_times(wrfnc, timeidx):
multitime = _is_multi_time(timeidx) multitime = _is_multi_time_req(timeidx)
if multitime: if multitime:
times = wrfnc.variables["Times"][:,:] times = wrfnc.variables["Times"][:,:]
for i in xrange(times.shape[0]): for i in xrange(times.shape[0]):
@ -468,6 +546,7 @@ def is_standard_wrf_var(wrfnc, var):
wrfnc = wrfnc[0] wrfnc = wrfnc[0]
return var in wrfnc.variables return var in wrfnc.variables
def is_staggered(var, wrfnc): def is_staggered(var, wrfnc):
we = extract_dim(wrfnc, "west_east") we = extract_dim(wrfnc, "west_east")
sn = extract_dim(wrfnc, "south_north") sn = extract_dim(wrfnc, "south_north")
@ -478,6 +557,8 @@ def is_staggered(var, wrfnc):
return False return False
def get_left_indexes(ref_var, expected_dims): def get_left_indexes(ref_var, expected_dims):
"""Returns the extra left side dimensions for a variable with an expected """Returns the extra left side dimensions for a variable with an expected
shape. shape.
@ -491,7 +572,7 @@ def get_left_indexes(ref_var, expected_dims):
if (extra_dim_num == 0): if (extra_dim_num == 0):
return [] return []
return [ref_var.shape[x] for x in xrange(extra_dim_num)] return tuple([ref_var.shape[x] for x in xrange(extra_dim_num)])
def iter_left_indexes(dims): def iter_left_indexes(dims):
"""A generator which yields the iteration tuples for a sequence of """A generator which yields the iteration tuples for a sequence of
@ -513,9 +594,10 @@ def iter_left_indexes(dims):
def get_right_slices(var, right_ndims, fixed_val=0): def get_right_slices(var, right_ndims, fixed_val=0):
extra_dim_num = var.ndim - right_ndims extra_dim_num = var.ndim - right_ndims
if extra_dim_num == 0: if extra_dim_num == 0:
return [slice(None,None,None)] * right_ndims return [slice(None)] * right_ndims
return [fixed_val]*extra_dim_num + [slice(None,None,None)]*right_ndims return tuple([fixed_val]*extra_dim_num +
[slice(None)]*right_ndims)
def get_proj_params(wrfnc, timeidx=0, varname=None): def get_proj_params(wrfnc, timeidx=0, varname=None):
proj_params = extract_global_attrs(wrfnc, attrs=("MAP_PROJ", proj_params = extract_global_attrs(wrfnc, attrs=("MAP_PROJ",
@ -523,16 +605,20 @@ def get_proj_params(wrfnc, timeidx=0, varname=None):
"TRUELAT1", "TRUELAT2", "TRUELAT1", "TRUELAT2",
"MOAD_CEN_LAT", "STAND_LON", "MOAD_CEN_LAT", "STAND_LON",
"POLE_LAT", "POLE_LON")) "POLE_LAT", "POLE_LON"))
multitime = _is_multi_time(timeidx) multitime = _is_multi_time_req(timeidx)
if not multitime: if not multitime:
time_idx_or_slice = timeidx time_idx_or_slice = timeidx
else: else:
time_idx_or_slice = slice(None, None, None) time_idx_or_slice = slice(None)
if varname is not None: if varname is not None:
coord_names = getattr(wrfnc.variables[varname], "coordinates").split() if not _is_coord_var(varname):
coord_names = getattr(wrfnc.variables[varname],
"coordinates").split()
lon_coord = coord_names[0] lon_coord = coord_names[0]
lat_coord = coord_names[1] lat_coord = coord_names[1]
else:
lat_coord, lon_coord = _get_coord_pairs(varname)
else: else:
lat_coord = "XLAT" lat_coord = "XLAT"
lon_coord = "XLONG" lon_coord = "XLONG"
@ -562,6 +648,28 @@ def viewvalues(d):
func = d.values func = d.values
return func() return func()
def isstr(s):
try:
return isinstance(s, basestring)
except NameError:
return isinstance(s, str)
# Helper to extract masked arrays from DataArrays that convert to NaN
def npvalues(da):
if not isinstance(da, DataArray):
result = da
else:
try:
fill_value = da.attrs["_FillValue"]
except KeyError:
result = da.values
else:
result = ma.masked_invalid(da.values, copy=False)
result.set_fill_value(fill_value)
return result
# Helper utilities for metadata # Helper utilities for metadata
class either(object): class either(object):
def __init__(self, *varnames): def __init__(self, *varnames):
@ -572,7 +680,7 @@ class either(object):
wrfnc = next(iter(wrfnc)) wrfnc = next(iter(wrfnc))
for varname in self.varnames: for varname in self.varnames:
if varname in wrfnc: if varname in wrfnc.variables:
return varname return varname
raise ValueError("{} are not valid variable names".format( raise ValueError("{} are not valid variable names".format(
@ -585,8 +693,9 @@ class combine_with:
self.varname = varname self.varname = varname
self.remove_dims = remove_dims self.remove_dims = remove_dims
self.insert_before = insert_before self.insert_before = insert_before
self.new_dimnames = new_dimnames self.new_dimnames = new_dimnames if new_dimnames is not None else []
self.new_coords = new_coords self.new_coords = (new_coords if new_coords is not None
else OrderedDict())
def __call__(self, var): def __call__(self, var):
new_dims = list(var.dims) new_dims = list(var.dims)
@ -609,6 +718,75 @@ class combine_with:
return new_dims, new_coords return new_dims, new_coords
class XYCoord(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return "{}({}, {})".format(self.__class__.__name__, self.x, self.y)
def from_args(func, argnames, *args, **kwargs):
"""Parses the function args and kargs looking for the desired argument
value. Otherwise, the value is taken from the default keyword argument
using the arg spec.
"""
if isstr(argnames):
arglist = [argnames]
else:
arglist = argnames
result = {}
for argname in arglist:
arg_loc = arg_location(func, argname, args, kwargs)
if arg_loc is not None:
result[argname] = arg_loc[0][arg_loc[1]]
else:
result[argname] = None
return result
def args_to_list(func, args, kwargs):
"""Converts the mixed args/kwargs to a single list of args"""
argspec = getargspec(func)
# Build the full tuple with defaults filled in
outargs = [None]*len(argspec.args)
for i,default in enumerate(argspec.defaults[::-1], 1):
outargs[-i] = default
# Add the supplied args
for i,arg in enumerate(args):
outargs[i] = arg
# Fill in the supplied kargs
for argname,val in viewitems(kwargs):
argidx = argspec.args.index(argname)
outargs[argidx] = val
return outargs
def arg_location(func, argname, args, kwargs):
"""Parses the function args, kargs and signature looking for the desired
argument location (either in args, kargs, or argspec.defaults),
and returns a list containing representing all arguments in the
correct order with defaults filled in.
"""
argspec = getargspec(func)
list_args = args_to_list(func, args, kwargs)
# Return the new sequence and location
if argname not in argspec.args and argname not in kwargs:
return None
result_idx = argspec.args.index(argname)
return list_args, result_idx

90
wrf_open/var/src/python/wrf/var/uvdecorator.py

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

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

@ -1,37 +1,46 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from math import fabs, log, tan, sin, cos from math import fabs, log, tan, sin, cos
from .extension import computeuvmet from .extension import computeuvmet
from .destag import destagger from .destag import destagger
from .constants import Constants from .constants import Constants
from .wind import _calc_wspd_wdir from .wind import _calc_wspd_wdir
from .decorators import convert_units, set_wind_metadata from .decorators import convert_units
from .metadecorators import set_wind_metadata
from .util import extract_vars, extract_global_attrs, either from .util import extract_vars, extract_global_attrs, either
__all__=["get_uvmet", "get_uvmet10", "get_uvmet_wspd_wdir", __all__=["get_uvmet", "get_uvmet10", "get_uvmet_wspd_wdir",
"get_uvmet10_wspd_wdir"] "get_uvmet10_wspd_wdir"]
@set_wind_metadata(wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_uvmet(wrfnc, timeidx=0, ten_m=False, units ="mps", def _get_uvmet(wrfnc, timeidx=0, ten_m=False, units ="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
""" Return a tuple of u,v with the winds rotated in to earth space""" """ Return a tuple of u,v with the winds rotated in to earth space"""
if not ten_m: if not ten_m:
varname = either("U", "UU")(wrfnc) varname = either("U", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = either("V", "VV")(wrfnc) varname = either("V", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
else: else:
varname = either("U10", "UU") varname = either("U10", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = (u_vars[varname] if varname == "U10" else u = (u_vars[varname] if varname == "U10" else
destagger(u_vars[varname][...,0,:,:], -1)) destagger(u_vars[varname][...,0,:,:], -1))
varname = either("V10", "VV") varname = either("V10", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = (v_vars[varname] if varname == "V10" else v = (v_vars[varname] if varname == "V10" else
destagger(v_vars[varname][...,0,:,:], -2)) destagger(v_vars[varname][...,0,:,:], -2))
@ -71,12 +80,12 @@ def get_uvmet(wrfnc, timeidx=0, ten_m=False, units ="mps",
varname = either("XLAT_M", "XLAT")(wrfnc) varname = either("XLAT_M", "XLAT")(wrfnc)
xlat_var = extract_vars(wrfnc, timeidx, varname, xlat_var = extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache) method, squeeze, cache, nometa=True)
lat = xlat_var[varname] lat = xlat_var[varname]
varname = either("XLONG_M", "XLONG") varname = either("XLONG_M", "XLONG")(wrfnc)
xlon_var = extract_vars(wrfnc, timeidx, varname, xlon_var = extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache) method, squeeze, cache, nometa=True)
lon = xlon_var[varname] lon = xlon_var[varname]
if map_proj == 1: if map_proj == 1:
@ -92,31 +101,57 @@ def get_uvmet(wrfnc, timeidx=0, ten_m=False, units ="mps",
else: else:
cone = 1 cone = 1
res = computeuvmet(u,v,lat,lon,cen_lon,cone) res = computeuvmet(u, v ,lat, lon, cen_lon, cone)
return res return res
@set_wind_metadata(copy_varname=either("P", "PRES"),
name="uvmet",
description="earth rotated u,v",
two_d=False,
wspd_wdir=False)
def get_uvmet(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None):
return _get_uvmet(wrfnc, timeidx, False, units, method, squeeze, cache)
@set_wind_metadata(copy_varname=either("PSFC", "F"),
name="uvmet10",
description="10m earth rotated u,v",
two_d=True,
wspd_wdir=False)
def get_uvmet10(wrfnc, timeidx=0, units="mps", def get_uvmet10(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
return get_uvmet(wrfnc, timeidx, True, units)
@set_wind_metadata(wspd_wdir=True) return _get_uvmet(wrfnc, timeidx, True, units, method, squeeze, cache)
@set_wind_metadata(copy_varname=either("P", "PRES"),
name="uvmet_wspd_wdir",
description="earth rotated wspd,wdir",
two_d=False,
wspd_wdir=True)
def get_uvmet_wspd_wdir(wrfnc, timeidx=0, units="mps", def get_uvmet_wspd_wdir(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
uvmet = get_uvmet(wrfnc, timeidx, False, units, method, squeeze, cache) uvmet = _get_uvmet(wrfnc, timeidx, False, units, method, squeeze, cache)
return _calc_wspd_wdir(uvmet[0,:], uvmet[1,:], False, units) return _calc_wspd_wdir(uvmet[...,0,:,:,:], uvmet[...,1,:,:,:], False, units)
@set_wind_metadata(wspd_wdir=True) @set_wind_metadata(copy_varname=either("PSFC", "F"),
name="uvmet10_wspd_wdir",
description="10m earth rotated wspd,wdir",
two_d=True,
wspd_wdir=True)
def get_uvmet10_wspd_wdir(wrfnc, timeidx=0, units="mps", def get_uvmet10_wspd_wdir(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
uvmet10 = get_uvmet10(wrfnc, timeidx, units="mps", method, squeeze, cache) uvmet10 = _get_uvmet(wrfnc, timeidx, True, units, method, squeeze, cache)
return _calc_wspd_wdir(uvmet10[0,:], uvmet10[1,:], True, units) return _calc_wspd_wdir(uvmet10[...,0,:,:], uvmet10[...,1,:,:], True, units)

19
wrf_open/var/src/python/wrf/var/vorticity.py

@ -1,17 +1,20 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .extension import computeavo, computepvo from .extension import computeavo, computepvo
from .util import extract_vars, extract_global_attrs from .util import extract_vars, extract_global_attrs
from .decorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
__all__ = ["get_avo", "get_pvo"] __all__ = ["get_avo", "get_pvo"]
@copy_and_set_metadata(copy_varname="F", name="avo", @copy_and_set_metadata(copy_varname="T", name="avo",
description="absolute vorticity", description="absolute vorticity",
units="10-5 s-1") units="10-5 s-1")
def get_avo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None): def get_avo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
ncvars = extract_vars(wrfnc, timeidx, varnames=("U", "V", "MAPFAC_U", ncvars = extract_vars(wrfnc, timeidx, ("U", "V", "MAPFAC_U",
"MAPFAC_V", "MAPFAC_M", "MAPFAC_V", "MAPFAC_M",
"F"), "F"),
method, squeeze, cache) method, squeeze, cache, nometa=True)
attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
@ -24,18 +27,18 @@ def get_avo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
dx = attrs["DX"] dx = attrs["DX"]
dy = attrs["DY"] dy = attrs["DY"]
return computeavo(u,v,msfu,msfv,msfm,cor,dx,dy) return computeavo(u, v, msfu, msfv, msfm, cor, dx, dy)
@copy_and_set_metadata(copy_varname="T", name="pvo", @copy_and_set_metadata(copy_varname="T", name="pvo",
description="potential vorticity", description="potential vorticity",
units="PVU") units="PVU")
def get_pvo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None): def get_pvo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
ncvars = extract_vars(wrfnc, timeidx, varnames=("U", "V", "T", "P", ncvars = extract_vars(wrfnc, timeidx, ("U", "V", "T", "P",
"PB", "MAPFAC_U", "PB", "MAPFAC_U",
"MAPFAC_V", "MAPFAC_M", "MAPFAC_V", "MAPFAC_M",
"F"), "F"),
method, squeeze, cache) method, squeeze, cache, nometa=True)
attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
@ -54,5 +57,5 @@ def get_pvo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None):
full_t = t + 300 full_t = t + 300
full_p = p + pb full_p = p + pb
return computepvo(u,v,full_t,full_p,msfu,msfv,msfm,cor,dx,dy) return computepvo(u, v, full_t, full_p, msfu, msfv, msfm, cor, dx, dy)

73
wrf_open/var/src/python/wrf/var/wind.py

@ -1,10 +1,13 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import numpy as np import numpy as np
from .constants import Constants from .constants import Constants
from .destag import destagger from .destag import destagger
from .util import extract_vars, either from .util import extract_vars, either
from .decorators import convert_units, set_wind_metadata from .decorators import convert_units
from .metadecorators import set_wind_metadata
__all__ = ["get_u_destag", "get_v_destag", "get_w_destag", __all__ = ["get_u_destag", "get_v_destag", "get_w_destag",
"get_destag_wspd_wdir", "get_destag_wspd_wdir10"] "get_destag_wspd_wdir", "get_destag_wspd_wdir10"]
@ -24,63 +27,103 @@ def _calc_wspd_wdir(u, v, two_d, units):
idx_end = -2 if two_d else -3 idx_end = -2 if two_d else -3
outdims = list(wspd.shape[0:idx_end]) + [2] + list(wspd.shape[idx_end:]) outdims = list(wspd.shape[0:idx_end]) + [2] + list(wspd.shape[idx_end:])
res = np.zeros(outdims, wspd.dtype) res = np.zeros(outdims, wspd.dtype)
res[...,0,:] = wspd[:]
res[...,1,:] = wdir[:] idxs0 = ((Ellipsis, 0, slice(None), slice(None), slice(None))
if not two_d else
(Ellipsis, 0, slice(None), slice(None)))
idxs1 = ((Ellipsis, 1, slice(None), slice(None), slice(None))
if not two_d else
(Ellipsis, 1, slice(None), slice(None)))
res[idxs0] = wspd[:]
res[idxs1] = wdir[:]
return res return res
@set_wind_metadata(wspd_wdir=False) @set_wind_metadata(copy_varname=either("P", "PRES"),
name="ua",
description="destaggered u-wind component",
wind_ncvar=True,
two_d=False,
wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_u_destag(wrfnc, timeidx=0, units="mps", def get_u_destag(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varname = either("U", "UU")(wrfnc) varname = either("U", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
return u return u
@set_wind_metadata(wspd_wdir=False) @set_wind_metadata(copy_varname=either("P", "PRES"),
name="va",
description="destaggered v-wind component",
two_d=False,
wind_ncvar=True,
wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_v_destag(wrfnc, timeidx=0, units="mps", def get_v_destag(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varname = either("V", "VV")(wrfnc) varname = either("V", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
return v return v
@set_wind_metadata(wspd_wdir=False) @set_wind_metadata(copy_varname=either("P", "PRES"),
name="wa",
description="destaggered w-wind component",
two_d=False,
wind_ncvar=True,
wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_w_destag(wrfnc, timeidx=0, units="mps", def get_w_destag(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
w_vars = extract_vars(wrfnc, timeidx, "W", method, squeeze, cache) w_vars = extract_vars(wrfnc, timeidx, "W", method, squeeze, cache,
nometa=True)
w = destagger(w_vars["W"], -3) w = destagger(w_vars["W"], -3)
return w return w
@set_wind_metadata(wspd_wdir=True) @set_wind_metadata(copy_varname=either("P", "PRES"),
name="wspd_wdir",
description="wspd,wdir in projection space",
two_d=False,
wspd_wdir=True)
def get_destag_wspd_wdir(wrfnc, timeidx=0, units="mps", def get_destag_wspd_wdir(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varname = either("U", "UU")(wrfnc) varname = either("U", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
varname = either("V", "VV")(wrfnc) varname = either("V", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
return _calc_wspd_wdir(u, v, False, units) return _calc_wspd_wdir(u, v, False, units)
@set_wind_metadata(wspd_wdir=True) @set_wind_metadata(copy_varname=either("PSFC", "F"),
name="wspd_wdir10",
description="10m wspd,wdir in projection space",
two_d=False,
wspd_wdir=True)
def get_destag_wspd_wdir10(wrfnc, timeidx=0, units="mps", def get_destag_wspd_wdir10(wrfnc, timeidx=0, units="mps",
method="cat", squeeze=True, cache=None): method="cat", squeeze=True, cache=None):
varname = either("U10", "UU")(wrfnc) varname = either("U10", "UU")(wrfnc)
u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) u_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
u = (u_vars[varname] if varname == "U10" else u = (u_vars[varname] if varname == "U10" else
destagger(u_vars[varname][...,0,:,:], -1)) destagger(u_vars[varname][...,0,:,:], -1))
varname = either("V10", "VV")(wrfnc) varname = either("V10", "VV")(wrfnc)
v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache) v_vars = extract_vars(wrfnc, timeidx, varname, method, squeeze, cache,
nometa=True)
v = (v_vars[varname] if varname == "V10" else v = (v_vars[varname] if varname == "V10" else
destagger(v_vars[varname][...,0,:,:], -2)) destagger(v_vars[varname][...,0,:,:], -2))

2
wrf_open/var/src/python/wrf/var/wrfext.f90

@ -85,7 +85,7 @@ SUBROUTINE f_interp1d(v_in,z_in,z_out,vmsg,v_out,nz_in,nz_out)
IMPLICIT NONE IMPLICIT NONE
INTEGER,INTENT(IN) :: NZ_IN,NZ_OUT INTEGER,INTENT(IN) :: nz_in, nz_out
REAL(KIND=8),DIMENSION(nz_in),INTENT(IN) :: v_in,z_in REAL(KIND=8),DIMENSION(nz_in),INTENT(IN) :: v_in,z_in
REAL(KIND=8),DIMENSION(nz_out),INTENT(IN) :: z_out REAL(KIND=8),DIMENSION(nz_out),INTENT(IN) :: z_out
REAL(KIND=8),DIMENSION(nz_out),INTENT(OUT) :: v_out REAL(KIND=8),DIMENSION(nz_out),INTENT(OUT) :: v_out

48
wrf_open/var/test/utests.py

@ -5,12 +5,14 @@ import numpy.ma as ma
import os, sys import os, sys
import subprocess import subprocess
from wrf.var import getvar, interplevel, interpline, vertcross, vinterp from wrf.var import (getvar, interplevel, interpline, vertcross, vinterp,
disable_xarray, xarray_enabled, npvalues)
NCL_EXE = "/Users/ladwig/nclbuild/6.3.0/bin/ncl" NCL_EXE = "/Users/ladwig/nclbuild/6.3.0/bin/ncl"
TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00" TEST_FILE = "/Users/ladwig/Documents/wrf_files/wrfout_d01_2010-06-13_21:00:00"
OUT_NC_FILE = "/tmp/wrftest.nc" OUT_NC_FILE = "/tmp/wrftest.nc"
def setUpModule(): def setUpModule():
ncarg_root = os.environ.get("NCARG_ROOT", None) ncarg_root = os.environ.get("NCARG_ROOT", None)
if ncarg_root is None: if ncarg_root is None:
@ -101,28 +103,28 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False):
my_vals = getvar(in_wrfnc, "temp", units="c") my_vals = getvar(in_wrfnc, "temp", units="c")
tol = 0 tol = 0
atol = .1 # Note: NCL uses 273.16 as conversion for some reason atol = .1 # Note: NCL uses 273.16 as conversion for some reason
nt.assert_allclose(my_vals, ref_vals, tol, atol) nt.assert_allclose(npvalues(my_vals), ref_vals, tol, atol)
elif (varname == "pw"): elif (varname == "pw"):
my_vals = getvar(in_wrfnc, "pw") my_vals = getvar(in_wrfnc, "pw")
tol = .5/100.0 tol = .5/100.0
atol = 0 # NCL uses different constants and doesn't use same atol = 0 # NCL uses different constants and doesn't use same
# handrolled virtual temp in method # handrolled virtual temp in method
nt.assert_allclose(my_vals, ref_vals, tol, atol) nt.assert_allclose(npvalues(my_vals), ref_vals, tol, atol)
elif (varname == "cape_2d"): elif (varname == "cape_2d"):
cape_2d = getvar(in_wrfnc, varname) cape_2d = getvar(in_wrfnc, varname)
tol = 0/100. # Not sure why different, F77 vs F90? tol = 0/100. # Not sure why different, F77 vs F90?
atol = .1 atol = .2
nt.assert_allclose(cape_2d, ref_vals, tol, atol) nt.assert_allclose(npvalues(cape_2d), ref_vals, tol, atol)
elif (varname == "cape_3d"): elif (varname == "cape_3d"):
cape_3d = getvar(in_wrfnc, varname) cape_3d = getvar(in_wrfnc, varname)
tol = 0/100. # Not sure why different, F77 vs F90? tol = 0/100. # Not sure why different, F77 vs F90?
atol = .01 atol = .01
nt.assert_allclose(cape_3d, ref_vals, tol, atol) nt.assert_allclose(npvalues(cape_3d), ref_vals, tol, atol)
else: else:
my_vals = getvar(in_wrfnc, varname) my_vals = getvar(in_wrfnc, varname)
tol = 0/100. tol = 0/100.
atol = 0.0001 atol = 0.0001
nt.assert_allclose(my_vals, ref_vals, tol, atol) nt.assert_allclose(npvalues(my_vals), ref_vals, tol, atol)
return test return test
@ -199,7 +201,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
p = getvar(in_wrfnc, "pressure") p = getvar(in_wrfnc, "pressure")
hts_500 = interplevel(hts, p, 500) hts_500 = interplevel(hts, p, 500)
nt.assert_allclose(hts_500, ref_ht_500) nt.assert_allclose(npvalues(hts_500), ref_ht_500)
elif (varname == "vertcross"): elif (varname == "vertcross"):
ref_ht_cross = _get_refvals(referent, "ht_cross", repeat, multi) ref_ht_cross = _get_refvals(referent, "ht_cross", repeat, multi)
@ -209,14 +211,14 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
p = getvar(in_wrfnc, "pressure") p = getvar(in_wrfnc, "pressure")
pivot_point = (hts.shape[-2] / 2, hts.shape[-1] / 2) pivot_point = (hts.shape[-2] / 2, hts.shape[-1] / 2)
ht_cross = vertcross(hts, p, pivot_point=pivot_point,angle=90.) ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.)
nt.assert_allclose(ht_cross, ref_ht_cross, rtol=.01) nt.assert_allclose(npvalues(ht_cross), ref_ht_cross, rtol=.01)
# Test opposite # Test opposite
p_cross1 = vertcross(p,hts,pivot_point=pivot_point, angle=90.0) p_cross1 = vertcross(p,hts,pivot_point=pivot_point, angle=90.0)
nt.assert_allclose(p_cross1, nt.assert_allclose(npvalues(p_cross1),
ref_p_cross, ref_p_cross,
rtol=.01) rtol=.01)
# Test point to point # Test point to point
@ -226,9 +228,11 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
p_cross2 = vertcross(p,hts,start_point=start_point, p_cross2 = vertcross(p,hts,start_point=start_point,
end_point=end_point) end_point=end_point)
nt.assert_allclose(p_cross1, p_cross2) nt.assert_allclose(npvalues(p_cross1),
npvalues(p_cross2))
elif (varname == "interpline"): elif (varname == "interpline"):
ref_t2_line = _get_refvals(referent, "t2_line", repeat, multi) ref_t2_line = _get_refvals(referent, "t2_line", repeat, multi)
t2 = getvar(in_wrfnc, "T2") t2 = getvar(in_wrfnc, "T2")
@ -236,7 +240,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0) t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0)
nt.assert_allclose(t2_line1, ref_t2_line) nt.assert_allclose(npvalues(t2_line1), ref_t2_line)
# Test point to point # Test point to point
start_point = (t2.shape[-2]/2, 0) start_point = (t2.shape[-2]/2, 0)
@ -245,7 +249,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
t2_line2 = interpline(t2, start_point=start_point, t2_line2 = interpline(t2, start_point=start_point,
end_point=end_point) end_point=end_point)
nt.assert_allclose(t2_line1, t2_line2) nt.assert_allclose(npvalues(t2_line1), npvalues(t2_line2))
elif (varname == "vinterp"): elif (varname == "vinterp"):
# Tk to theta # Tk to theta
fld_tk_theta = _get_refvals(referent, "fld_tk_theta", repeat, multi) fld_tk_theta = _get_refvals(referent, "fld_tk_theta", repeat, multi)
@ -267,7 +271,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
atol = 0.0001 atol = 0.0001
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_tk_theta, tol, atol) nt.assert_allclose(npvalues(field), fld_tk_theta, tol, atol)
# Tk to theta-e # Tk to theta-e
fld_tk_theta_e = _get_refvals(referent, "fld_tk_theta_e", repeat, multi) fld_tk_theta_e = _get_refvals(referent, "fld_tk_theta_e", repeat, multi)
@ -287,7 +291,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
atol = 0.0001 atol = 0.0001
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_tk_theta_e, tol, atol) nt.assert_allclose(npvalues(field), fld_tk_theta_e, tol, atol)
# Tk to pressure # Tk to pressure
fld_tk_pres = _get_refvals(referent, "fld_tk_pres", repeat, multi) fld_tk_pres = _get_refvals(referent, "fld_tk_pres", repeat, multi)
@ -304,7 +308,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
log_p=True) log_p=True)
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_tk_pres, tol, atol) nt.assert_allclose(npvalues(field), fld_tk_pres, tol, atol)
# Tk to geoht_msl # Tk to geoht_msl
fld_tk_ght_msl = _get_refvals(referent, "fld_tk_ght_msl", repeat, multi) fld_tk_ght_msl = _get_refvals(referent, "fld_tk_ght_msl", repeat, multi)
@ -320,7 +324,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
log_p=True) log_p=True)
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_tk_ght_msl, tol, atol) nt.assert_allclose(npvalues(field), fld_tk_ght_msl, tol, atol)
# Tk to geoht_agl # Tk to geoht_agl
fld_tk_ght_agl = _get_refvals(referent, "fld_tk_ght_agl", repeat, multi) fld_tk_ght_agl = _get_refvals(referent, "fld_tk_ght_agl", repeat, multi)
@ -336,7 +340,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
log_p=True) log_p=True)
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_tk_ght_agl, tol, atol) nt.assert_allclose(npvalues(field), fld_tk_ght_agl, tol, atol)
# Hgt to pressure # Hgt to pressure
fld_ht_pres = _get_refvals(referent, "fld_ht_pres", repeat, multi) fld_ht_pres = _get_refvals(referent, "fld_ht_pres", repeat, multi)
@ -353,7 +357,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
log_p=True) log_p=True)
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_ht_pres, tol, atol) nt.assert_allclose(npvalues(field), fld_ht_pres, tol, atol)
# Pressure to theta # Pressure to theta
fld_pres_theta = _get_refvals(referent, "fld_pres_theta", repeat, multi) fld_pres_theta = _get_refvals(referent, "fld_pres_theta", repeat, multi)
@ -370,7 +374,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
log_p=True) log_p=True)
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_pres_theta, tol, atol) nt.assert_allclose(npvalues(field), fld_pres_theta, tol, atol)
# Theta-e to pres # Theta-e to pres
fld_thetae_pres = _get_refvals(referent, "fld_thetae_pres", repeat, multi) fld_thetae_pres = _get_refvals(referent, "fld_thetae_pres", repeat, multi)
@ -387,7 +391,7 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
log_p=True) log_p=True)
field = n.squeeze(field) field = n.squeeze(field)
nt.assert_allclose(field, fld_thetae_pres, tol, atol) nt.assert_allclose(npvalues(field), fld_thetae_pres, tol, atol)
return test return test

Loading…
Cancel
Save