Browse Source

Added thread local coordinate and is_moving caching so that this is only done once. Modified the latlon/xy routines to put the product on the left like NCL. Updated unit testing to add the latlon routines. Updated ipynb scripts.

main
Bill Ladwig 9 years ago
parent
commit
7e503a145b
  1. 58
      ncl_reference/WRFUserARW.ncl
  2. 5541
      ncl_reference/WRFUsersherrie.ncl
  3. 9
      src/wrf/cache.py
  4. 12
      src/wrf/cape.py
  5. 5
      src/wrf/cloudfrac.py
  6. 93
      src/wrf/config.py
  7. 10
      src/wrf/ctt.py
  8. 14
      src/wrf/dbz.py
  9. 9
      src/wrf/dewpoint.py
  10. 19
      src/wrf/geoht.py
  11. 17
      src/wrf/helicity.py
  12. 26
      src/wrf/interp.py
  13. 27
      src/wrf/latlon.py
  14. 75
      src/wrf/latlonutils.py
  15. 47
      src/wrf/metadecorators.py
  16. 4
      src/wrf/omega.py
  17. 13
      src/wrf/pressure.py
  18. 4587
      src/wrf/psadlookup.py
  19. 4
      src/wrf/pw.py
  20. 8
      src/wrf/rh.py
  21. 15
      src/wrf/routines.py
  22. 4
      src/wrf/slp.py
  23. 30
      src/wrf/temp.py
  24. 5
      src/wrf/terrain.py
  25. 13
      src/wrf/times.py
  26. 547
      src/wrf/util.py
  27. 32
      src/wrf/uvmet.py
  28. 2
      src/wrf/version.py
  29. 8
      src/wrf/vorticity.py
  30. 24
      src/wrf/wind.py
  31. 2
      test/ipynb/WRF_Workshop_Demo.ipynb
  32. 25
      test/ipynb/WRF_python_demo.ipynb
  33. 26
      test/ncl_get_var.ncl
  34. 58
      test/test_multi_cache.py
  35. 120
      test/utests.py

58
ncl_reference/WRFUserARW.ncl

@ -1378,64 +1378,18 @@ begin
end if end if
if( any( variable .eq. "cfrac" )) then if( any( variable .eq. "cfrac" )) then
P = _get_wrf_var(file_handle,"P",time)
print("calculating wrf cloud fraction") PB = _get_wrf_var(file_handle,"PB",time)
if(typeof(file_handle).eq."file") then rh = wrf_user_getvar(file_handle,"rh",time)
ISFILE = True
nc_file = file_handle
else if(typeof(file_handle).eq."list") then
ISFILE = False
nc_file = file_handle[0]
else
print("wrf_user_getvar: error: the first argument must be a file or a list of files opened with addfile or addfiles")
return
end if
end if
time_in = time
if ( time .eq. -1 ) then
if(ISFILE) then
P = nc_file->P
PB = nc_file->PB
rh = wrf_user_getvar(nc_file,"rh",-1)
else
P = file_handle[:]->P
PB = file_handle[:]->PB
rh = wrf_user_getvar(file_handle,"rh",-1)
end if
else
if(ISFILE) then
P = nc_file->P(time_in,:,:,:)
PB = nc_file->PB(time_in,:,:,:)
rh = wrf_user_getvar(file_handle,"rh",time_in)
else
P = file_handle[:]->P(time_in,:,:,:)
PB = file_handle[:]->PB(time_in,:,:,:)
rh = wrf_user_getvar(file_handle,"rh",time_in)
end if
end if
pres = (P + PB) pres = (P + PB)
thedims = dimsizes(pres)
if(time .eq. -1) then
nt = thedims(0)
nz = thedims(1)
ns = thedims(2)
ew = thedims(3)
else
nt = 1
nz = thedims(0)
ns = thedims(1)
ew = thedims(2)
end if
out_array = wrf_cloud_fraction(pres,rh) out_array = wrf_cloud_fraction(pres,rh)
copy_VarAtts_except(rh,out_array,(/"MemoryOrder","description","units"/))
out_array@units = "%" out_array@units = "%"
out_array@description = "Low, Mid, High Clouds" out_array@description = "Low, Mid, High Clouds"
out_array@MemoryOrder = "XY"
return(out_array) return(out_array)
end if ;variable is cfrac end if ;variable is cfrac

5541
ncl_reference/WRFUsersherrie.ncl

File diff suppressed because it is too large Load Diff

9
src/wrf/cache.py

@ -11,6 +11,9 @@ _local_storage = local()
def cache_item(key, product, value): def cache_item(key, product, value):
global _local_storage global _local_storage
if key is None:
return
try: try:
cache = _local_storage.cache cache = _local_storage.cache
except AttributeError: except AttributeError:
@ -30,6 +33,9 @@ def cache_item(key, product, value):
def get_cached_item(key, product): def get_cached_item(key, product):
if key is None:
return None
cache = getattr(_local_storage, "cache", None) cache = getattr(_local_storage, "cache", None)
if cache is None: if cache is None:
@ -40,8 +46,9 @@ def get_cached_item(key, product):
if prod_dict is None: if prod_dict is None:
return None return None
return prod_dict.get(product, None) result = prod_dict.get(product, None)
return result
def _get_cache(): def _get_cache():
return getattr(_local_storage, "cache", None) return getattr(_local_storage, "cache", None)

12
src/wrf/cape.py

@ -21,15 +21,13 @@ from .metadecorators import set_cape_metadata
# units="J/kg ; J/kg ; m ; m", # units="J/kg ; J/kg ; m ; m",
# MemoryOrder="XY") # MemoryOrder="XY")
@set_cape_metadata(is2d=True) @set_cape_metadata(is2d=True)
def get_2dcape(wrfnc, timeidx=0, method="cat", def get_2dcape(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None, missing=Constants.DEFAULT_FILL):
squeeze=True, cache=None, meta=True,
missing=Constants.DEFAULT_FILL):
"""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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -86,11 +84,11 @@ def get_2dcape(wrfnc, timeidx=0, method="cat",
@set_cape_metadata(is2d=False) @set_cape_metadata(is2d=False)
def get_3dcape(wrfnc, timeidx=0, method="cat", def get_3dcape(wrfnc, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, squeeze=True, cache=None, meta=True,
missing=Constants.DEFAULT_FILL): _key=None, missing=Constants.DEFAULT_FILL):
"""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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]

5
src/wrf/cloudfrac.py

@ -8,10 +8,11 @@ from .util import extract_vars
@set_cloudfrac_metadata() @set_cloudfrac_metadata()
def get_cloudfrac(wrfnc, timeidx=0, method="cat", squeeze=True, def get_cloudfrac(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True): cache=None, meta=True, _key=None):
vars = extract_vars(wrfnc, timeidx, ("P", "PB", "QVAPOR", "T"), vars = extract_vars(wrfnc, timeidx, ("P", "PB", "QVAPOR", "T"),
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
p = vars["P"] p = vars["P"]
pb = vars["PB"] pb = vars["PB"]

93
src/wrf/config.py

@ -1,90 +1,103 @@
from __future__ import (absolute_import, division, print_function, from __future__ import (absolute_import, division, print_function,
unicode_literals) unicode_literals)
from threading import local
_local_config = local()
_local_config.xarray_enabled = True
_local_config.cartopy_enabled = True
_local_config.basemap_enabled = True
_local_config.pyngl_enabled = True
_local_config.cache_size = 20
try: try:
from xarray import DataArray from xarray import DataArray
except ImportError: except ImportError:
_xarray_enabled = False _local_config.xarray_enabled = False
else:
_xarray_enabled = True
try: try:
from cartopy import crs from cartopy import crs
except ImportError: except ImportError:
_cartopy_enabled = False _local_config.cartopy_enabled = False
else:
_cartopy_enabled = True
try: try:
from mpl_toolkits.basemap import Basemap from mpl_toolkits.basemap import Basemap
except ImportError: except ImportError:
_basemap_enabled = False _local_config.basemap_enabled = False
else:
_basemap_enabled = True
try: try:
from Ngl import Resources from Ngl import Resources
except ImportError: except ImportError:
_pyngl_enabled = False _local_config.pyngl_enabled = False
else:
_pyngl_enabled = True
_cache_size = 5
def xarray_enabled(): def xarray_enabled():
global _xarray_enabled global _local_config
return _xarray_enabled return _local_config.xarray_enabled
def disable_xarray(): def disable_xarray():
global _xarray_enabled global _local_config
_xarray_enabled = False _local_config.xarray_enabled = False
def enable_xarray(): def enable_xarray():
global _xarray_enabled global _local_config
_xarray_enabled = True _local_config.xarray_enabled = True
def cartopy_enabled(): def cartopy_enabled():
global _cartopy_enabled global _local_config
return _cartopy_enabled return _local_config.cartopy_enabled
def enable_cartopy(): def enable_cartopy():
global _cartopy_enabled global _local_config
_cartopy_enabled = True _local_config.cartopy_enabled = True
def disable_cartopy(): def disable_cartopy():
global _cartopy_enabled global _local_config
_cartopy_enabled = True _local_config.cartopy_enabled = True
def basemap_enabled(): def basemap_enabled():
global _basemap_enabled global _local_config
return _basemap_enabled return _local_config.basemap_enabled
def enable_basemap(): def enable_basemap():
global _basemap_enabled global _local_config
_basemap_enabled = True _local_config.basemap_enabled = True
def disable_basemap(): def disable_basemap():
global _basemap_enabled global _local_config
_basemap_enabled = True _local_config.basemap_enabled = True
def pyngl_enabled(): def pyngl_enabled():
global _pyngl_enabled global _local_config
return _pyngl_enabled return _local_config.pyngl_enabled
def enable_pyngl(): def enable_pyngl():
global _pyngl_enabled global _local_config
_pyngl_enabled = True _local_config.pyngl_enabled = True
def disable_pyngl(): def disable_pyngl():
global _pyngl_enabled global _local_config
_pyngl_enabled = True _local_config.pyngl_enabled = True
def set_cache_size(size): def set_cache_size(size):
global _cache_size global _local_config
_cache_size = size _local_config.cache_size = size
def get_cache_size(): def get_cache_size():
return int(_cache_size) global _local_config
return int(_local_config.cache_size)

10
src/wrf/ctt.py

@ -18,14 +18,14 @@ from .util import extract_vars
MemoryOrder="XY") MemoryOrder="XY")
@convert_units("temp", "c") @convert_units("temp", "c")
def get_ctt(wrfnc, timeidx=0, method="cat", def get_ctt(wrfnc, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, squeeze=True, cache=None, meta=True, _key=None,
units="c"): units="c"):
"""Return the cloud top temperature. """Return the cloud top temperature.
""" """
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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -37,7 +37,8 @@ def get_ctt(wrfnc, timeidx=0, method="cat",
haveqci = 1 haveqci = 1
try: try:
icevars = extract_vars(wrfnc, timeidx, "QICE", icevars = extract_vars(wrfnc, timeidx, "QICE",
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
except KeyError: except KeyError:
qice = n.zeros(qv.shape, qv.dtype) qice = n.zeros(qv.shape, qv.dtype)
haveqci = 0 haveqci = 0
@ -46,7 +47,8 @@ def get_ctt(wrfnc, timeidx=0, method="cat",
try: try:
cldvars = extract_vars(wrfnc, timeidx, "QCLOUD", cldvars = extract_vars(wrfnc, timeidx, "QCLOUD",
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
except KeyError: except KeyError:
raise RuntimeError("'QCLOUD' not found in NetCDF file") raise RuntimeError("'QCLOUD' not found in NetCDF file")
else: else:

14
src/wrf/dbz.py

@ -14,7 +14,7 @@ from .metadecorators import copy_and_set_metadata
description="radar reflectivity", description="radar reflectivity",
units="dBz") units="dBz")
def get_dbz(wrfnc, timeidx=0, method="cat", def get_dbz(wrfnc, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, squeeze=True, cache=None, meta=True, _key=None,
use_varint=False, use_liqskin=False): use_varint=False, use_liqskin=False):
""" Return the dbz """ Return the dbz
@ -28,7 +28,7 @@ def get_dbz(wrfnc, timeidx=0, method="cat",
""" """
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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -37,7 +37,8 @@ def get_dbz(wrfnc, timeidx=0, method="cat",
try: try:
snowvars = extract_vars(wrfnc, timeidx, "QSNOW", snowvars = extract_vars(wrfnc, timeidx, "QSNOW",
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
except KeyError: except KeyError:
qs = np.zeros(qv.shape, qv.dtype) qs = np.zeros(qv.shape, qv.dtype)
else: else:
@ -45,7 +46,8 @@ def get_dbz(wrfnc, timeidx=0, method="cat",
try: try:
graupvars = extract_vars(wrfnc, timeidx, "QGRAUP", graupvars = extract_vars(wrfnc, timeidx, "QGRAUP",
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
except KeyError: except KeyError:
qg = np.zeros(qv.shape, qv.dtype) qg = np.zeros(qv.shape, qv.dtype)
else: else:
@ -69,9 +71,9 @@ def get_dbz(wrfnc, timeidx=0, method="cat",
units="dBz", units="dBz",
MemoryOrder="XY") MemoryOrder="XY")
def get_max_dbz(wrfnc, timeidx=0, method="cat", def get_max_dbz(wrfnc, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, squeeze=True, cache=None, meta=True, _key=None,
do_varint=False, do_liqskin=False): do_varint=False, do_liqskin=False):
return np.amax(get_dbz(wrfnc, timeidx, method, squeeze, cache, meta, return np.amax(get_dbz(wrfnc, timeidx, method, squeeze, cache, meta,
do_varint, do_liqskin), _key, do_varint, do_liqskin),
axis=-3) axis=-3)

9
src/wrf/dewpoint.py

@ -12,11 +12,11 @@ from .util import extract_vars
description="dew point temperature") description="dew point temperature")
@convert_units("temp", "c") @convert_units("temp", "c")
def get_dp(wrfnc, timeidx=0, method="cat", squeeze=True, def get_dp(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, units="c"): cache=None, meta=True, _key=None, units="c"):
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,
meta=False) meta=False, _key=_key)
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -33,11 +33,10 @@ def get_dp(wrfnc, timeidx=0, method="cat", squeeze=True,
description="2m dew point temperature") description="2m dew point temperature")
@convert_units("temp", "c") @convert_units("temp", "c")
def get_dp_2m(wrfnc, timeidx=0, method="cat", squeeze=True, def get_dp_2m(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None, units="c"):
units="c"):
varnames=("PSFC", "Q2") varnames=("PSFC", "Q2")
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
meta=False) meta=False, _key=_key)
# Algorithm requires hPa # Algorithm requires hPa
psfc = .01*(ncvars["PSFC"]) psfc = .01*(ncvars["PSFC"])

19
src/wrf/geoht.py

@ -8,7 +8,7 @@ from .metadecorators import set_height_metadata
from .util import extract_vars, either from .util import extract_vars, either
def _get_geoht(wrfnc, timeidx, method="cat", squeeze=True, def _get_geoht(wrfnc, timeidx, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
height=True, msl=True): height=True, msl=True):
"""Return the geopotential in units of m2 s-2 if height is False, """Return the geopotential in units of m2 s-2 if height is False,
otherwise return the geopotential height in meters. If height is True, otherwise return the geopotential height in meters. If height is True,
@ -20,7 +20,8 @@ def _get_geoht(wrfnc, timeidx, method="cat", squeeze=True,
varname = either("PH", "GHT")(wrfnc) varname = either("PH", "GHT")(wrfnc)
if varname == "PH": if varname == "PH":
ph_vars = extract_vars(wrfnc, timeidx, ("PH", "PHB", "HGT"), ph_vars = extract_vars(wrfnc, timeidx, ("PH", "PHB", "HGT"),
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
ph = ph_vars["PH"] ph = ph_vars["PH"]
phb = ph_vars["PHB"] phb = ph_vars["PHB"]
hgt = ph_vars["HGT"] hgt = ph_vars["HGT"]
@ -28,7 +29,8 @@ def _get_geoht(wrfnc, timeidx, method="cat", squeeze=True,
geopt_unstag = destagger(geopt, -3) geopt_unstag = destagger(geopt, -3)
else: else:
ght_vars = extract_vars(wrfnc, timeidx, ("GHT", "HGT_M"), ght_vars = extract_vars(wrfnc, timeidx, ("GHT", "HGT_M"),
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
geopt_unstag = ght_vars["GHT"] * Constants.G geopt_unstag = ght_vars["GHT"] * Constants.G
hgt = ght_vars["HGT_M"] hgt = ght_vars["HGT_M"]
@ -50,16 +52,17 @@ def _get_geoht(wrfnc, timeidx, method="cat", squeeze=True,
@set_height_metadata(geopt=True) @set_height_metadata(geopt=True)
def get_geopt(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, def get_geopt(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True): meta=True, _key=None):
return _get_geoht(wrfnc, timeidx, method, squeeze, cache, meta, return _get_geoht(wrfnc, timeidx, method, squeeze, cache, meta, _key,
False, True,) False, True)
@set_height_metadata(geopt=False) @set_height_metadata(geopt=False)
@convert_units("height", "m") @convert_units("height", "m")
def get_height(wrfnc, timeidx=0, method="cat", squeeze=True, def get_height(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
msl=True, units="m"): msl=True, units="m"):
return _get_geoht(wrfnc, timeidx, method, squeeze, cache, meta, True, msl) return _get_geoht(wrfnc, timeidx, method, squeeze, cache, meta, _key,
True, msl)

17
src/wrf/helicity.py

@ -13,11 +13,12 @@ from .metadecorators import copy_and_set_metadata
description="storm relative helicity", description="storm relative helicity",
units="m-2/s-2") units="m-2/s-2")
def get_srh(wrfnc, timeidx=0, method="cat", squeeze=True, def get_srh(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, top=3000.0): cache=None, meta=True, _key=None, 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, meta=False) method, squeeze, cache, meta=False,
_key=_key)
ter = ncvars["HGT"] ter = ncvars["HGT"]
ph = ncvars["PH"] ph = ncvars["PH"]
@ -26,12 +27,12 @@ def get_srh(wrfnc, timeidx=0, method="cat", squeeze=True,
# 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,
meta=False) meta=False, _key=_key)
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,
meta=False) meta=False, _key=_key)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
geopt = ph + phb geopt = ph + phb
@ -52,11 +53,11 @@ def get_srh(wrfnc, timeidx=0, method="cat", squeeze=True,
description="updraft helicity", description="updraft helicity",
units="m-2/s-2") units="m-2/s-2")
def get_uh(wrfnc, timeidx=0, method="cat", squeeze=True, def get_uh(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
bottom=2000.0, top=5000.0): bottom=2000.0, top=5000.0):
ncvars = extract_vars(wrfnc, timeidx, ("W", "PH", "PHB", "MAPFAC_M"), ncvars = extract_vars(wrfnc, timeidx, ("W", "PH", "PHB", "MAPFAC_M"),
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False, _key=None)
wstag = ncvars["W"] wstag = ncvars["W"]
ph = ncvars["PH"] ph = ncvars["PH"]
@ -70,12 +71,12 @@ def get_uh(wrfnc, timeidx=0, method="cat", squeeze=True,
# 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,
meta=False) meta=False, _key=_key)
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,
meta=False) meta=False, _key=_key)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
zp = ph + phb zp = ph + phb

26
src/wrf/interp.py

@ -12,7 +12,7 @@ from .extension import (_interpz3d, _interp2dxy, _interp1d, _vertcross,
_interpline, _smooth2d, _monotonic, _vintrp) _interpline, _smooth2d, _monotonic, _vintrp)
from .metadecorators import set_interp_metadata from .metadecorators import set_interp_metadata
from .util import extract_vars, is_staggered from .util import extract_vars, is_staggered, get_id
from .interputils import get_xy, get_xy_z_params 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
@ -284,6 +284,7 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
`numpy.ndarray` object with no metadata. `numpy.ndarray` object with no metadata.
""" """
_key = get_id(wrfnc)
# 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"
@ -344,24 +345,30 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
# Extract vriables # Extract vriables
#timeidx = -1 # Should this be an argument? #timeidx = -1 # Should this be an argument?
ncvars = extract_vars(wrfnc, timeidx, ("PSFC", "QVAPOR", "F"), ncvars = extract_vars(wrfnc, timeidx, ("PSFC", "QVAPOR", "F"),
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False, _key=_key)
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, units="m", terht = get_terrain(wrfnc, timeidx, units="m",
method=method, squeeze=squeeze, cache=cache) method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
t = get_theta(wrfnc, timeidx, units="k", t = get_theta(wrfnc, timeidx, units="k",
method=method, squeeze=squeeze, cache=cache) method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
tk = get_temp(wrfnc, timeidx, units="k", tk = get_temp(wrfnc, timeidx, units="k",
method=method, squeeze=squeeze, cache=cache) method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
p = get_pressure(wrfnc, timeidx, units="pa", p = get_pressure(wrfnc, timeidx, units="pa",
method=method, squeeze=squeeze, cache=cache) method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
ght = get_height(wrfnc, timeidx, msl=True, units="m", ght = get_height(wrfnc, timeidx, msl=True, units="m",
method=method, squeeze=squeeze, cache=cache) method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
ht_agl = get_height(wrfnc, timeidx, msl=False, units="m", ht_agl = get_height(wrfnc, timeidx, msl=False, units="m",
method=method, squeeze=squeeze, cache=cache) method=method, squeeze=squeeze, cache=cache,
meta=False, _key=_key)
smsfp = _smooth2d(sfp, 3) smsfp = _smooth2d(sfp, 3)
@ -400,7 +407,8 @@ def vinterp(wrfnc, field, vert_coord, interp_levels, extrapolate=False,
idir = 1 idir = 1
delta = 0.01 delta = 0.01
eth = get_eth(wrfnc, timeidx) eth = get_eth(wrfnc, timeidx, method=method, squeeze=squeeze,
cache=cache, meta=False, _key=_key)
p_hpa = p * ConversionFactors.PA_TO_HPA p_hpa = p * ConversionFactors.PA_TO_HPA

27
src/wrf/latlon.py

@ -1,58 +1,63 @@
from __future__ import (absolute_import, division, print_function, from __future__ import (absolute_import, division, print_function,
unicode_literals) unicode_literals)
from .util import extract_vars from .util import extract_vars, get_id
from .latlonutils import (_lat_varname, _lon_varname, _ll_to_xy, _xy_to_ll) from .latlonutils import (_lat_varname, _lon_varname, _ll_to_xy, _xy_to_ll)
from .metadecorators import set_latlon_metadata from .metadecorators import set_latlon_metadata
def get_lat(wrfnc, timeidx=0, method="cat", squeeze=True, def get_lat(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
stagger=None): stagger=None):
varname = _lat_varname(wrfnc, 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,
meta) meta, _key)
return lat_var[varname] return lat_var[varname]
def get_lon(wrfnc, timeidx=0, method="cat", squeeze=True, def get_lon(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
stagger=None): stagger=None):
varname = _lon_varname(wrfnc, 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,
meta) meta, _key)
return lon_var[varname] return lon_var[varname]
# TODO: Do we need the user to know about method, squeeze, cache for this?
# Can either use wrfnc as a single file or sequence, or provide # Can either use wrfnc as a single file or sequence, or provide
# projection parameters (which don't allow for moving domains) # projection parameters (which don't allow for moving domains)
@set_latlon_metadata(xy=True) @set_latlon_metadata(xy=True)
def ll_to_xy(wrfnc, latitude, longitude, timeidx=0, stagger=None, method="cat", def ll_to_xy(wrfnc, latitude, longitude, timeidx=0, stagger=None, method="cat",
squeeze=True, cache=None, meta=True): squeeze=True, cache=None, meta=True, as_int=True):
_key = get_id(wrfnc)
return _ll_to_xy(latitude, longitude, wrfnc, timeidx, stagger, method, return _ll_to_xy(latitude, longitude, wrfnc, timeidx, stagger, method,
squeeze, cache, **{}) squeeze, cache, _key, as_int, **{})
@set_latlon_metadata(xy=True) @set_latlon_metadata(xy=True)
def ll_to_xy_proj(latitude, longitude, meta=True, squeeze=True, **projparams): def ll_to_xy_proj(latitude, longitude, meta=True, squeeze=True, as_int=True,
**projparams):
return _ll_to_xy(latitude, longitude, None, 0, squeeze, "cat", True, None, return _ll_to_xy(latitude, longitude, None, 0, squeeze, "cat", True, None,
**projparams) None, as_int, **projparams)
@set_latlon_metadata(xy=False) @set_latlon_metadata(xy=False)
def xy_to_ll(wrfnc, x, y, timeidx=0, stagger=None, method="cat", squeeze=True, def xy_to_ll(wrfnc, x, y, timeidx=0, stagger=None, method="cat", squeeze=True,
cache=None, meta=True): cache=None, meta=True):
_key = get_id(wrfnc)
return _xy_to_ll(x, y, wrfnc, timeidx, stagger, method, squeeze, cache, return _xy_to_ll(x, y, wrfnc, timeidx, stagger, method, squeeze, cache,
**{}) _key, **{})
@set_latlon_metadata(xy=False) @set_latlon_metadata(xy=False)
def xy_to_ll_proj(x, y, meta=True, squeeze=True, **projparams): def xy_to_ll_proj(x, y, meta=True, squeeze=True, **projparams):
return _xy_to_ll(x, y, None, 0, None, "cat", squeeze, None, **projparams) return _xy_to_ll(x, y, None, 0, None, "cat", squeeze, None, None,
**projparams)

75
src/wrf/latlonutils.py

@ -32,7 +32,7 @@ def _lon_varname(wrfnc, stagger):
return varname return varname
def _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache): def _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache, _key):
if timeidx < 0: if timeidx < 0:
raise ValueError("'timeidx' must be greater than 0") raise ValueError("'timeidx' must be greater than 0")
@ -75,14 +75,16 @@ def _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, cache):
if not is_mapping(wrfnc): if not is_mapping(wrfnc):
wrfnc = next(iter(wrfnc)) # only need one file wrfnc = next(iter(wrfnc)) # only need one file
else: else:
wrfnc = wrfnc[next(iter(viewkeys(wrfnc)))] first_entry = next(iter(viewkeys(wrfnc)))
wrfnc = wrfnc[first_entry]
key = _key[first_entry]
return _get_proj_params(wrfnc, timeidx, stagger, return _get_proj_params(wrfnc, timeidx, stagger,
method, squeeze, cache) method, squeeze, cache, key)
xlat = extract_vars(wrfnc, lat_timeidx, (latvar,), method, squeeze, cache, xlat = extract_vars(wrfnc, lat_timeidx, (latvar,), method, squeeze, cache,
meta=False)[latvar] meta=False, _key=_key)[latvar]
xlon = extract_vars(wrfnc, lat_timeidx, (lonvar,), method, squeeze, cache, xlon = extract_vars(wrfnc, lat_timeidx, (lonvar,), method, squeeze, cache,
meta=False)[lonvar] meta=False, _key=_key)[lonvar]
ref_lat = np.ravel(xlat[..., 0, 0]) ref_lat = np.ravel(xlat[..., 0, 0])
ref_lon = np.ravel(xlon[..., 0, 0]) ref_lon = np.ravel(xlon[..., 0, 0])
@ -164,13 +166,13 @@ def _kwarg_proj_params(projparams):
# Will return 0-based indexes # Will return 0-based indexes
def _ll_to_xy(latitude, longitude, wrfnc=None, timeidx=0, def _ll_to_xy(latitude, longitude, wrfnc=None, timeidx=0,
stagger=None, method="cat", squeeze=True, cache=None, stagger=None, method="cat", squeeze=True, cache=None,
**projparms): _key=None, as_int=True, **projparms):
if wrfnc is not None: if wrfnc is not None:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc) = _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, loninc) = _get_proj_params(wrfnc, timeidx, stagger, method, squeeze,
cache) cache, _key)
else: else:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
@ -191,18 +193,22 @@ def _ll_to_xy(latitude, longitude, wrfnc=None, timeidx=0,
"must be the same length") "must be the same length")
if ref_lat.size == 1: if ref_lat.size == 1:
outdim = [lats.size, 2] outdim = [2, lats.size]
extra_dims = [outdim[0]] #outdim = [lats.size, 2]
extra_dims = [outdim[1]]
else: else:
# Moving domain will have moving ref_lats/ref_lons # Moving domain will have moving ref_lats/ref_lons
outdim = [lats.size, ref_lat.size, 2] outdim = [2, ref_lat.size, lats.size]
extra_dims = outdim[0:2] #outdim = [lats.size, ref_lat.size, 2]
extra_dims = outdim[1:]
result = np.empty(outdim, np.float64) result = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + (slice(None), ) #left_and_slice_idxs = left_idxs + (slice(None), )
# Left indexes is a misnomer, since these will be on the right
x_idxs = (0,) + left_idxs
y_idxs = (1,) + left_idxs
if ref_lat.size == 1: if ref_lat.size == 1:
ref_lat_val = ref_lat[0] ref_lat_val = ref_lat[0]
ref_lon_val = ref_lon[0] ref_lon_val = ref_lon[0]
@ -218,29 +224,41 @@ def _ll_to_xy(latitude, longitude, wrfnc=None, timeidx=0,
known_x, known_y, dx, dy, latinc, loninc, known_x, known_y, dx, dy, latinc, loninc,
lat, lon) lat, lon)
result[left_and_slice_idxs] = xy[:] # Note: comes back from fortran as y,x
result[x_idxs] = xy[1]
result[y_idxs] = xy[0]
else: else:
result = np.empty((2,), np.float64)
result = _lltoxy(map_proj, truelat1, truelat2, stdlon, fort_out = _lltoxy(map_proj, truelat1, truelat2, stdlon,
ref_lat, ref_lon, pole_lat, pole_lon, ref_lat, ref_lon, pole_lat, pole_lon,
known_x, known_y, dx, dy, latinc, loninc, known_x, known_y, dx, dy, latinc, loninc,
latitude, longitude) latitude, longitude)
# Note, comes back from fortran as y,x. So, need to swap them.
result[0] = fort_out[1]
result[1] = fort_out[0]
# Make indexes 0-based # Make indexes 0-based
result = result - 1 result = result - 1
if as_int:
result = np.rint(result).astype(int)
return result return result
# X and Y should be 0-based # X and Y should be 0-based
def _xy_to_ll(x, y, wrfnc=None, timeidx=0, stagger=None, def _xy_to_ll(x, y, wrfnc=None, timeidx=0, stagger=None,
method="cat", squeeze=True, cache=None, **projparams): method="cat", squeeze=True, cache=None, _key=None,
**projparams):
if wrfnc is not None: if wrfnc is not None:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
loninc) = _get_proj_params(wrfnc, timeidx, stagger, method, squeeze, loninc) = _get_proj_params(wrfnc, timeidx, stagger, method, squeeze,
cache) cache, _key)
else: else:
(map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon, (map_proj, truelat1, truelat2, stdlon, ref_lat, ref_lon,
pole_lat, pole_lon, known_x, known_y, dx, dy, latinc, pole_lat, pole_lon, known_x, known_y, dx, dy, latinc,
@ -262,21 +280,26 @@ def _xy_to_ll(x, y, wrfnc=None, timeidx=0, stagger=None,
y_arr = y_arr.ravel() y_arr = y_arr.ravel()
if (x_arr.size != y_arr.size): if (x_arr.size != y_arr.size):
raise ValueError("'x' and 'y' " raise ValueError("'x' and 'y' must be the same length")
"must be the same length")
if ref_lat.size == 1: if ref_lat.size == 1:
outdim = [x_arr.size, 2] #outdim = [x_arr.size, 2]
extra_dims = [outdim[0]] #extra_dims = [outdim[0]]
outdim = [2, x_arr.size]
extra_dims = [outdim[1]]
else: else:
# Moving domain will have moving ref_lats/ref_lons # Moving domain will have moving ref_lats/ref_lons
outdim = [x_arr.size, ref_lat.size, 2] #outdim = [x_arr.size, ref_lat.size, 2]
extra_dims = outdim[0:2] #extra_dims = outdim[0:2]
outdim = [2, ref_lat.size, x_arr.size]
extra_dims = outdim[1:]
result = np.empty(outdim, np.float64) result = np.empty(outdim, np.float64)
for left_idxs in iter_left_indexes(extra_dims): for left_idxs in iter_left_indexes(extra_dims):
left_and_slice_idxs = left_idxs + (slice(None), ) #left_and_slice_idxs = left_idxs + (slice(None), )
lat_idxs = (0,) + left_idxs
lon_idxs = (1,) + left_idxs
if ref_lat.size == 1: if ref_lat.size == 1:
ref_lat_val = ref_lat[0] ref_lat_val = ref_lat[0]
@ -292,7 +315,9 @@ def _xy_to_ll(x, y, wrfnc=None, timeidx=0, stagger=None,
ref_lon_val, pole_lat, pole_lon, known_x, known_y, ref_lon_val, pole_lat, pole_lon, known_x, known_y,
dx, dy, latinc, loninc, x_val, y_val) dx, dy, latinc, loninc, x_val, y_val)
result[left_and_slice_idxs] = ll[:] #result[left_and_slice_idxs] = ll[:]
result[lat_idxs] = ll[0]
result[lon_idxs] = ll[1]
else: else:
# Convert 0-based to 1-based for Fortran # Convert 0-based to 1-based for Fortran

47
src/wrf/metadecorators.py

@ -39,7 +39,8 @@ def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=None,
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", argvars = from_args(wrapped, ("wrfnc", "timeidx", "method",
"squeeze", "cache", "units", "meta"), "squeeze", "cache", "units", "meta",
"_key"),
*args, **kwargs) *args, **kwargs)
wrfnc = argvars["wrfnc"] wrfnc = argvars["wrfnc"]
@ -48,6 +49,7 @@ def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=None,
method = argvars["method"] method = argvars["method"]
squeeze = argvars["squeeze"] squeeze = argvars["squeeze"]
cache = argvars["cache"] cache = argvars["cache"]
_key = argvars["_key"]
if cache is None: if cache is None:
cache = {} cache = {}
@ -65,7 +67,7 @@ def copy_and_set_metadata(copy_varname=None, delete_attrs=None, name=None,
if var_to_copy is None: if var_to_copy is None:
var_to_copy = extract_vars(wrfnc, timeidx, (_copy_varname,), var_to_copy = extract_vars(wrfnc, timeidx, (_copy_varname,),
method, squeeze, cache, method, squeeze, cache,
meta=True)[_copy_varname] meta=True, _key=_key)[_copy_varname]
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache
new_cache = dict(cache) new_cache = dict(cache)
@ -148,7 +150,8 @@ def set_wind_metadata(copy_varname, name, description,
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
argvars = from_args(wrapped, ("wrfnc", "timeidx", "units", argvars = from_args(wrapped, ("wrfnc", "timeidx", "units",
"method", "squeeze", "ten_m", "cache"), "method", "squeeze", "ten_m", "cache",
"_key"),
*args, **kwargs) *args, **kwargs)
wrfnc = argvars["wrfnc"] wrfnc = argvars["wrfnc"]
timeidx = argvars["timeidx"] timeidx = argvars["timeidx"]
@ -157,6 +160,7 @@ def set_wind_metadata(copy_varname, name, description,
squeeze = argvars["squeeze"] squeeze = argvars["squeeze"]
ten_m = argvars["ten_m"] ten_m = argvars["ten_m"]
cache = argvars["cache"] cache = argvars["cache"]
_key = argvars["_key"]
if cache is None: if cache is None:
cache = {} cache = {}
@ -167,7 +171,7 @@ def set_wind_metadata(copy_varname, name, description,
copy_var = extract_vars(wrfnc, timeidx, _copy_varname, copy_var = extract_vars(wrfnc, timeidx, _copy_varname,
method, squeeze, cache, method, squeeze, cache,
meta=True)[_copy_varname] meta=True, _key=_key)[_copy_varname]
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache
new_cache = dict(cache) new_cache = dict(cache)
@ -242,7 +246,7 @@ def set_cape_metadata(is2d):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", "squeeze", argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", "squeeze",
"cache", "missing"), "cache", "_key", "missing"),
*args, **kwargs) *args, **kwargs)
wrfnc = argvars["wrfnc"] wrfnc = argvars["wrfnc"]
timeidx = argvars["timeidx"] timeidx = argvars["timeidx"]
@ -250,12 +254,13 @@ def set_cape_metadata(is2d):
squeeze = argvars["squeeze"] squeeze = argvars["squeeze"]
cache = argvars["cache"] cache = argvars["cache"]
missing = argvars["missing"] missing = argvars["missing"]
_key = argvars["_key"]
if cache is None: if cache is None:
cache = {} cache = {}
_copy_varname = "P" _copy_varname = "P"
copy_var = extract_vars(wrfnc, timeidx, _copy_varname, method, squeeze, copy_var = extract_vars(wrfnc, timeidx, _copy_varname, method, squeeze,
cache, meta=True)[_copy_varname] cache, meta=True, _key=_key)[_copy_varname]
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache
new_cache = dict(cache) new_cache = dict(cache)
@ -335,19 +340,20 @@ def set_cloudfrac_metadata():
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", "squeeze", argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", "squeeze",
"cache"), "cache", "_key"),
*args, **kwargs) *args, **kwargs)
wrfnc = argvars["wrfnc"] wrfnc = argvars["wrfnc"]
timeidx = argvars["timeidx"] timeidx = argvars["timeidx"]
method = argvars["method"] method = argvars["method"]
squeeze = argvars["squeeze"] squeeze = argvars["squeeze"]
cache = argvars["cache"] cache = argvars["cache"]
_key = argvars["_key"]
if cache is None: if cache is None:
cache = {} cache = {}
_copy_varname = "P" _copy_varname = "P"
copy_var = extract_vars(wrfnc, timeidx, _copy_varname, method, squeeze, copy_var = extract_vars(wrfnc, timeidx, _copy_varname, method, squeeze,
cache, meta=True)[_copy_varname] cache, meta=True, _key=_key)[_copy_varname]
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache
new_cache = dict(cache) new_cache = dict(cache)
@ -416,18 +422,17 @@ def set_latlon_metadata(xy=False):
# Want to preserve the input coordinate pair in metadata # Want to preserve the input coordinate pair in metadata
if result.ndim == 1: if result.ndim == 1:
result = result[np.newaxis, :] result = result[:, np.newaxis]
argnames = ["x", "y"] if not xy else ["latitude", "longitude"] argnames = ["x", "y"] if not xy else ["latitude", "longitude"]
argnames.append("squeeze") argnames.append("squeeze")
outname = "latlon" if not xy else "xy" outname = "latlon" if not xy else "xy"
if result.ndim == 2: if result.ndim == 2:
dimnames = (["idx", "lat_lon"] if not xy dimnames = (["lat_lon", "idx"] if not xy else ["x_y", "idx"])
else ["idx", "x_y"])
else: else:
dimnames = (["idx", "domain", "lat_lon"] if not xy dimnames = (["lat_lon", "domain_idx", "idx"] if not xy
else ["idx", "domain", "x_y"]) else ["x_y", "domain_idx", "idx"])
argvars = from_args(wrapped, argnames, *args, **kwargs) argvars = from_args(wrapped, argnames, *args, **kwargs)
@ -440,13 +445,14 @@ def set_latlon_metadata(xy=False):
coords = {} coords = {}
if not xy: if not xy:
coords["xy_coord"] = (dimnames[0], [CoordPair(x=x[0], y=x[1]) coords["xy_coord"] = (dimnames[-1], [CoordPair(x=x[0], y=x[1])
for x in zip(arr1, arr2)]) for x in zip(arr1, arr2)])
coords[dimnames[-1]] = ["lat", "lon"] coords[dimnames[0]] = ["lat", "lon"]
else: else:
coords["latlon_coord"] = (dimnames[0], [CoordPair(lat=x[0], lon=x[1]) coords["latlon_coord"] = (dimnames[-1], [CoordPair(lat=x[0],
lon=x[1])
for x in zip(arr1, arr2)]) for x in zip(arr1, arr2)])
coords[dimnames[-1]] = ["x", "y"] coords[dimnames[0]] = ["x", "y"]
da = DataArray(result, name=outname, dims=dimnames, coords=coords) da = DataArray(result, name=outname, dims=dimnames, coords=coords)
@ -469,7 +475,8 @@ def set_height_metadata(geopt=False):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
argvars = from_args(wrapped, ("wrfnc", "timeidx", "method", argvars = from_args(wrapped, ("wrfnc", "timeidx", "method",
"squeeze", "units", "msl", "cache"), "squeeze", "units", "msl", "cache",
"_key"),
*args, **kwargs) *args, **kwargs)
wrfnc = argvars["wrfnc"] wrfnc = argvars["wrfnc"]
timeidx = argvars["timeidx"] timeidx = argvars["timeidx"]
@ -478,6 +485,7 @@ def set_height_metadata(geopt=False):
squeeze = argvars["squeeze"] squeeze = argvars["squeeze"]
msl = argvars["msl"] msl = argvars["msl"]
cache = argvars["cache"] cache = argvars["cache"]
_key = argvars["_key"]
if cache is None: if cache is None:
cache = {} cache = {}
@ -486,7 +494,8 @@ def set_height_metadata(geopt=False):
# pressure (which has the same dims as destaggered height) # pressure (which has the same dims as destaggered height)
ht_metadata_varname = either("P", "GHT")(wrfnc) ht_metadata_varname = either("P", "GHT")(wrfnc)
ht_var = extract_vars(wrfnc, timeidx, ht_metadata_varname, ht_var = extract_vars(wrfnc, timeidx, ht_metadata_varname,
method, squeeze, cache, meta=True) method, squeeze, cache, meta=True,
_key=_key)
ht_metadata_var = ht_var[ht_metadata_varname] ht_metadata_var = ht_var[ht_metadata_varname]
# Make a copy so we don't modify a user supplied cache # Make a copy so we don't modify a user supplied cache

4
src/wrf/omega.py

@ -13,10 +13,10 @@ from .metadecorators import copy_and_set_metadata
description="omega", description="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,
meta=True): meta=True, _key=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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
w = ncvars["W"] w = ncvars["W"]

13
src/wrf/pressure.py

@ -10,26 +10,29 @@ from .util import extract_vars, either
description="pressure") description="pressure")
@convert_units("pressure", "pa") @convert_units("pressure", "pa")
def get_pressure(wrfnc, timeidx=0, method="cat", squeeze=True, def get_pressure(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="pa"): 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, meta=False) method, squeeze, cache, meta=False,
_key=_key)
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, meta=False)["PRES"] method, squeeze, cache, meta=False,
_key=_key)["PRES"]
return pres return pres
def get_pressure_hpa(wrfnc, timeidx=0, method="cat", squeeze=True, def get_pressure_hpa(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="hpa"): units="hpa"):
return get_pressure(wrfnc, timeidx, method, squeeze, cache, meta, units) return get_pressure(wrfnc, timeidx, method, squeeze, cache, meta, _key,
units)

4587
src/wrf/psadlookup.py

File diff suppressed because it is too large Load Diff

4
src/wrf/pw.py

@ -14,10 +14,10 @@ from .metadecorators import copy_and_set_metadata
MemoryOrder="XY", 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,
meta=True): meta=True, _key=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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]

8
src/wrf/rh.py

@ -12,10 +12,10 @@ from .metadecorators import copy_and_set_metadata
description="relative humidity", description="relative humidity",
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,
meta=True): meta=True, _key=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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -33,10 +33,10 @@ def get_rh(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
description="2m relative humidity", description="2m relative humidity",
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,
meta=True): meta=True, _key=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,
meta=False) meta=False, _key=_key)
t2 = ncvars["T2"] t2 = ncvars["T2"]
psfc = ncvars["PSFC"] psfc = ncvars["PSFC"]
q2 = ncvars["Q2"] q2 = ncvars["Q2"]

15
src/wrf/routines.py

@ -1,7 +1,8 @@
from __future__ import (absolute_import, division, print_function, from __future__ import (absolute_import, division, print_function,
unicode_literals) unicode_literals)
from .util import get_iterable, is_standard_wrf_var, extract_vars, viewkeys from .util import (get_iterable, is_standard_wrf_var, extract_vars, viewkeys,
get_id)
from .cape import get_2dcape, get_3dcape from .cape import get_2dcape, get_3dcape
from .ctt import get_ctt from .ctt import get_ctt
from .dbz import get_dbz, get_max_dbz from .dbz import get_dbz, get_max_dbz
@ -284,17 +285,21 @@ def getvar(wrfnc, varname, timeidx=0,
""" """
_key = get_id(wrfnc)
wrfnc = get_iterable(wrfnc) wrfnc = get_iterable(wrfnc)
if is_standard_wrf_var(wrfnc, varname): if is_standard_wrf_var(wrfnc, varname) and varname != "Times":
return extract_vars(wrfnc, timeidx, varname, return extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache, meta)[varname] method, squeeze, cache, meta, _key)[varname]
elif varname == "Times":
varname = "times" # Diverting to the get_times routine
actual_var = _undo_alias(varname) actual_var = _undo_alias(varname)
if actual_var not in _VALID_KARGS: if actual_var not in _VALID_KARGS:
raise ArgumentError("'%s' is not a valid variable name" % (varname)) raise ArgumentError("'%s' is not a valid variable name" % (varname))
_check_kargs(actual_var, kargs) _check_kargs(actual_var, kargs)
return _FUNC_MAP[actual_var](wrfnc, timeidx, return _FUNC_MAP[actual_var](wrfnc, timeidx, method, squeeze, cache,
method, squeeze, cache, meta, **kargs) meta, _key, **kargs)

4
src/wrf/slp.py

@ -16,11 +16,11 @@ from .util import extract_vars
MemoryOrder="XY") MemoryOrder="XY")
@convert_units("pressure", "hpa") @convert_units("pressure", "hpa")
def get_slp(wrfnc, timeidx=0, method="cat", squeeze=True, def get_slp(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="hpa"): units="hpa"):
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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]

30
src/wrf/temp.py

@ -13,12 +13,12 @@ from .util import extract_vars
description="potential temperature") description="potential temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_theta(wrfnc, timeidx=0, method="cat", squeeze=True, def get_theta(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="k"): units="k"):
varnames = ("T",) varnames = ("T",)
ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfnc, timeidx, varnames, method, squeeze, cache,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
full_t = t + Constants.T_BASE full_t = t + Constants.T_BASE
@ -28,13 +28,13 @@ def get_theta(wrfnc, timeidx=0, method="cat", squeeze=True,
description="temperature") description="temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_temp(wrfnc, timeidx=0, method="cat", squeeze=True, def get_temp(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="k"): units="k"):
"""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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -49,13 +49,13 @@ def get_temp(wrfnc, timeidx=0, method="cat", squeeze=True,
description="equivalent potential temperature") description="equivalent potential temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_eth(wrfnc, timeidx=0, method="cat", squeeze=True, def get_eth(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="k"): units="k"):
"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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -73,13 +73,13 @@ def get_eth(wrfnc, timeidx=0, method="cat", squeeze=True,
description="virtual temperature") description="virtual temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_tv(wrfnc, timeidx=0, method="cat", squeeze=True, def get_tv(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="k"): units="k"):
"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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
@ -98,13 +98,13 @@ def get_tv(wrfnc, timeidx=0, method="cat", squeeze=True,
description="wetbulb temperature") description="wetbulb temperature")
@convert_units("temp", "k") @convert_units("temp", "k")
def get_tw(wrfnc, timeidx=0, method="cat", squeeze=True, def get_tw(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="k"): units="k"):
"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,
meta=False) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
p = ncvars["P"] p = ncvars["P"]
pb = ncvars["PB"] pb = ncvars["PB"]
@ -119,12 +119,14 @@ def get_tw(wrfnc, timeidx=0, method="cat", squeeze=True,
return tw return tw
def get_tk(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, def get_tk(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True): meta=True, _key=None):
return get_temp(wrfnc, timeidx, method, squeeze, cache, meta, units="k") return get_temp(wrfnc, timeidx, method, squeeze, cache, meta, _key,
units="k")
def get_tc(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None, def get_tc(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True): meta=True, _key=None):
return get_temp(wrfnc, timeidx, method, squeeze, cache, meta, units="c") return get_temp(wrfnc, timeidx, method, squeeze, cache, meta, _key,
units="c")

5
src/wrf/terrain.py

@ -11,11 +11,12 @@ from .util import extract_vars, either
description="terrain height") description="terrain height")
@convert_units("height", "m") @convert_units("height", "m")
def get_terrain(wrfnc, timeidx=0, method="cat", squeeze=True, def get_terrain(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=False, units="m"): cache=None, meta=False, _key=None, units="m"):
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, meta=False)[varname] method, squeeze, cache, meta=False,
_key=_key)[varname]

13
src/wrf/times.py

@ -4,5 +4,14 @@ from __future__ import (absolute_import, division, print_function,
from .util import extract_times from .util import extract_times
def get_times(wrfnc, timeidx=0): def get_times(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
return extract_times(wrfnc, timeidx) meta=True, _key=None):
return extract_times(wrfnc, timeidx, method, squeeze, cache,
meta=meta, do_xtime=False)
def get_xtimes(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None):
return extract_times(wrfnc, timeidx, method, squeeze, cache,
meta=meta, do_xtime=True)

547
src/wrf/util.py

@ -29,6 +29,7 @@ from .projection import getproj, NullProjection
from .constants import Constants, ALL_TIMES from .constants import Constants, ALL_TIMES
from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round, from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round,
py3range, ucode) py3range, ucode)
from .cache import cache_item, get_cached_item
if xarray_enabled(): if xarray_enabled():
from xarray import DataArray from xarray import DataArray
@ -302,7 +303,7 @@ def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar):
def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"), def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
lonvar=either("XLONG", "XLONG_M")): lonvar=either("XLONG", "XLONG_M"), _key=None):
if isinstance(latvar, either): if isinstance(latvar, either):
latvar = latvar(wrfseq) latvar = latvar(wrfseq)
@ -320,25 +321,14 @@ def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
first_wrfnc = next(wrf_iter) first_wrfnc = next(wrf_iter)
else: else:
entry = wrfseq[next(iter(viewkeys(wrfseq)))] # Currently only checking the first dict entry.
return is_moving_domain(entry, varname, latvar, lonvar) dict_key = next(iter(viewkeys(wrfseq)))
entry = wrfseq[dict_key]
# Quick check on pressure coordinates, bypassing the need to search the key = _key[dict_key] if _key is not None else None
# domain corner points return is_moving_domain(entry, varname, latvar, lonvar, key)
# try:
# coord_names = getattr(first_wrfnc.variables["P"], # The long way of checking all lat/lon corner points. Doesn't appear
# "coordinates").split() # to be a shortcut in the netcdf files.
# except KeyError:
# pass
# else:
# if "XTIME" in coord_names:
# return True
# else:
# return False
# The long way of checking all lat/lon corner points
# There doesn't appear to be a shortcut, so this should probably
# be stored in a cache somewhere
if varname is not None: if varname is not None:
try: try:
coord_names = getattr(first_wrfnc.variables[varname], coord_names = getattr(first_wrfnc.variables[varname],
@ -349,28 +339,19 @@ def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
lon_coord = lonvar lon_coord = lonvar
lat_coord = latvar lat_coord = latvar
else: else:
# If the XTIME variable is found to be a coordinate variable,
# then it's a moving domain file
# try:
# xtime_coord = coord_names[2]
# except IndexError:
# # XTIME is not a coordinate variable, if the variable is in the
# # file, then this is not a moving domain file
# if "XTIME" in first_wrfnc.variables:
# return False
#
# else:
# # XTIME is a coordinate, so this is a moving domain file
# if xtime_coord == "XTIME":
# return True
lon_coord = coord_names[0] lon_coord = coord_names[0]
lat_coord = coord_names[1] lat_coord = coord_names[1]
else: else:
lon_coord = lonvar lon_coord = lonvar
lat_coord = latvar lat_coord = latvar
# Probably a met_em file, need to search all the files # See if there is a cached value
product = "is_moving_{}_{}".format(lat_coord, lon_coord)
moving = get_cached_item(_key, product)
if moving is not None:
return moving
# Need to search all the files
lats = first_wrfnc.variables[lat_coord] lats = first_wrfnc.variables[lat_coord]
lons = first_wrfnc.variables[lon_coord] lons = first_wrfnc.variables[lon_coord]
@ -398,8 +379,12 @@ def is_moving_domain(wrfseq, varname=None, latvar=either("XLAT", "XLAT_M"),
if _corners_moved(wrfnc, ll_corner, ur_corner, if _corners_moved(wrfnc, ll_corner, ur_corner,
lat_coord, lon_coord): lat_coord, lon_coord):
cache_item(_key, product, True)
return True return True
cache_item(_key, product, False)
return False return False
def _get_global_attr(wrfnc, attr): def _get_global_attr(wrfnc, attr):
@ -441,7 +426,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, meta): def _combine_dict(wrfdict, varname, timeidx, method, meta, _key):
"""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 = []
@ -451,11 +436,14 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta):
first_key = next(key_iter) first_key = next(key_iter)
keynames.append(first_key) keynames.append(first_key)
is_moving = is_moving_domain(wrfdict, varname) is_moving = is_moving_domain(wrfdict, varname, _key=_key)
# Not quite sure how to handle coord caching with dictionaries, so
# disabling it for now by setting _key to None.
first_array = _extract_var(wrfdict[first_key], varname, first_array = _extract_var(wrfdict[first_key], varname,
timeidx, is_moving=is_moving, method=method, timeidx, is_moving=is_moving, method=method,
squeeze=False, cache=None, meta=meta) squeeze=False, cache=None, meta=meta,
_key=_key[first_key])
# Create the output data numpy array based on the first array # Create the output data numpy array based on the first array
@ -474,7 +462,8 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta):
keynames.append(key) keynames.append(key)
vardata = _extract_var(wrfdict[key], varname, timeidx, vardata = _extract_var(wrfdict[key], varname, timeidx,
is_moving=is_moving, method=method, is_moving=is_moving, method=method,
squeeze=False, cache=None, meta=meta) squeeze=False, cache=None, meta=meta,
_key=_key[key])
if outdata.shape[1:] != vardata.shape: if outdata.shape[1:] != vardata.shape:
raise ValueError("data sequences must have the " raise ValueError("data sequences must have the "
@ -486,8 +475,41 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta):
outname = str(first_array.name) 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)
outcoords["key"] = keynames # First find and store all the existing key coord names/values
# This is applicable only if there are nested dictionaries.
key_coordnames = []
coord_vals = []
existing_cnt = 0
while True:
key_coord_name = "key_{}".format(existing_cnt)
if key_coord_name not in first_array.dims:
break
key_coordnames.append(key_coord_name)
coord_vals.append(npvalues(first_array.coords[key_coord_name]))
existing_cnt += 1
# Now add the key coord name and values for THIS dictionary.
# Put the new key_n name at the bottom, but the new values will
# be at the top to be associated with key_0 (left most). This
# effectively shifts the existing 'key_n' coordinate values to the
# right one dimension so *this* dicionary's key coordinate values
# are at 'key_0'.
key_coordnames.append(key_coord_name)
coord_vals.insert(0, keynames)
# make it so that key_0 is leftmost
outdims = key_coordnames + list(first_array.dims[existing_cnt:])
# Create the new 'key_n', value pairs
for coordname, coordval in zip(key_coordnames, coord_vals):
outcoords[coordname] = coordval
outattrs = OrderedDict(first_array.attrs) outattrs = OrderedDict(first_array.attrs)
outarr = DataArray(outdata, name=outname, coords=outcoords, outarr = DataArray(outdata, name=outname, coords=outcoords,
@ -497,6 +519,7 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta):
return outarr return outarr
def _find_coord_names(coords): def _find_coord_names(coords):
try: try:
lat_coord = [name for name in _COORD_VARS[0::2] if name in coords][0] lat_coord = [name for name in _COORD_VARS[0::2] if name in coords][0]
@ -532,11 +555,19 @@ def _find_max_time_size(wrfseq):
return max_times return max_times
def _build_data_array(wrfnc, varname, timeidx, is_moving_domain): def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile,
_key):
# Note: wrfnc is always a single netcdf file object
# is_moving_domain and is_multifile are arguments indicating if the
# single file came from a sequence, and if that sequence is has a moving
# domain. Both arguments are used mainly for coordinate extraction and
# caching.
multitime = is_multi_time_req(timeidx) multitime = is_multi_time_req(timeidx)
time_idx_or_slice = timeidx if not multitime else slice(None) time_idx_or_slice = timeidx if not multitime else slice(None)
var = wrfnc.variables[varname] var = wrfnc.variables[varname]
data = var[time_idx_or_slice, :] data = var[time_idx_or_slice, :]
time_coord = None
# Want to preserve the time dimension # Want to preserve the time dimension
if not multitime: if not multitime:
@ -551,14 +582,19 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
# WRF files # WRF files
coord_attr = getattr(var, "coordinates") coord_attr = getattr(var, "coordinates")
except AttributeError: except AttributeError:
if is_coordvar(varname): if is_coordvar(varname):
# Coordinate variable (most likely XLAT or XLONG) # Coordinate variable (most likely XLAT or XLONG)
lat_coord, lon_coord = get_coord_pairs(varname) lat_coord, lon_coord = get_coord_pairs(varname)
time_coord = None time_coord = None
if is_moving_domain and has_time_coord(wrfnc): if has_time_coord(wrfnc):
time_coord = "XTIME" time_coord = "XTIME"
elif is_time_coord_var(varname):
lon_coord = None
lat_coord = None
time_coord = None
else: else:
try: try:
# met_em files # met_em files
@ -584,18 +620,60 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
# Handle lat/lon coordinates and projection information if available # Handle lat/lon coordinates and projection information if available
if lon_coord is not None and lat_coord is not None: if lon_coord is not None and lat_coord is not None:
lon_coord_var = wrfnc.variables[lon_coord] # Using a cache for coordinate variables so the extraction only happens
lat_coord_var = wrfnc.variables[lat_coord] # once.
time_coord_var = (wrfnc.variables[time_coord] lon_coord_dimkey = lon_coord + "_dim"
if time_coord is not None lon_coord_valkey = lon_coord + "_val"
else None) lat_coord_dimkey = lat_coord + "_dim"
lat_coord_valkey = lat_coord + "_val"
lon_coord_dims = get_cached_item(_key, lon_coord_dimkey)
lon_coord_vals = get_cached_item(_key, lon_coord_valkey)
if lon_coord_dims is None or lon_coord_vals is None:
lon_var = wrfnc.variables[lon_coord]
lon_coord_dims = lon_var.dimensions
lon_coord_vals = lon_var[:]
# Only cache here if the domain is not moving, otherwise
# caching is handled by cat/join
if not is_moving_domain:
cache_item(_key, lon_coord_dimkey, lon_coord_dims)
cache_item(_key, lon_coord_valkey, lon_coord_vals)
lat_coord_dims = get_cached_item(_key, lat_coord_dimkey)
lat_coord_vals = get_cached_item(_key, lat_coord_valkey)
if lat_coord_dims is None or lat_coord_vals is None:
lat_var = wrfnc.variables[lat_coord]
lat_coord_dims = lat_var.dimensions
lat_coord_vals = lat_var[:]
# Only cache here if the domain is not moving, otherwise
# caching is done in cat/join
if not is_moving_domain:
cache_item(_key, lat_coord_dimkey, lat_coord_dims)
cache_item(_key, lat_coord_valkey, lat_coord_vals)
time_coord_vals = None
if time_coord is not None:
# If not from a multifile sequence, then cache the time
# coordinate. Otherwise, handled in cat/join/
if not is_multifile:
time_coord_vals = get_cached_item(_key, time_coord)
if time_coord_vals is None:
time_coord_vals = wrfnc.variables[time_coord][:]
if not is_multifile:
cache_item(_key, time_coord, time_coord_vals)
else:
time_coord_vals = wrfnc.variables[time_coord][:]
if multitime: if multitime:
if is_moving_domain: if is_moving_domain:
# Special case with a moving domain in a multi-time file, # Special case with a moving domain in a multi-time file,
# otherwise the projection parameters don't change # otherwise the projection parameters don't change
coords[lon_coord] = lon_coord_var.dimensions, lon_coord_var[:] coords[lon_coord] = lon_coord_dims, lon_coord_vals
coords[lat_coord] = lat_coord_var.dimensions, lat_coord_var[:] coords[lat_coord] = lat_coord_dims, lat_coord_vals
# Returned lats/lons arrays will have a time dimension, so proj # Returned lats/lons arrays will have a time dimension, so proj
# will need to be a list due to moving corner points # will need to be a list due to moving corner points
@ -606,23 +684,29 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
lons=lons[i,:], lons=lons[i,:],
**proj_params) for i in py3range(lats.shape[0])] **proj_params) for i in py3range(lats.shape[0])]
if time_coord is not None:
coords[time_coord] = (lon_coord_var.dimensions[0],
time_coord_var[:])
else: else:
coords[lon_coord] = (lon_coord_var.dimensions[1:], coords[lon_coord] = (lon_coord_dims[1:],
lon_coord_var[0,:]) lon_coord_vals[0,:])
coords[lat_coord] = (lat_coord_var.dimensions[1:], coords[lat_coord] = (lat_coord_dims[1:],
lat_coord_var[0,:]) lat_coord_vals[0,:])
# Domain not moving, so just get the first time # Domain not moving, so just get the first time
lats, lons, proj_params = get_proj_params(wrfnc, 0, varname) lats, lons, proj_params = get_proj_params(wrfnc, 0, varname)
proj = getproj(lats=lats, lons=lons, **proj_params) proj = getproj(lats=lats, lons=lons, **proj_params)
if time_coord is not None:
coords[time_coord] = (lon_coord_dims[0], time_coord_vals)
else: else:
coords[lon_coord] = (lon_coord_var.dimensions[1:], coords[lon_coord] = (lon_coord_dims[1:],
lon_coord_var[timeidx,:]) lon_coord_vals[timeidx,:])
coords[lat_coord] = (lat_coord_var.dimensions[1:], coords[lat_coord] = (lat_coord_dims[1:],
lat_coord_var[timeidx,:]) lat_coord_vals[timeidx,:])
if time_coord is not None:
coords[time_coord] = (lon_coord_dims[0],
[time_coord_vals[timeidx]])
lats, lons, proj_params = get_proj_params(wrfnc, 0, varname) lats, lons, proj_params = get_proj_params(wrfnc, 0, varname)
proj = getproj(lats=lats, lons=lons, **proj_params) proj = getproj(lats=lats, lons=lons, **proj_params)
@ -630,7 +714,10 @@ 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) t = extract_times(wrfnc, timeidx, meta=False, do_xtime=False)
if not multitime:
t = [t]
coords[dimnames[0]] = t
data_array = DataArray(data, name=varname, dims=dimnames, coords=coords, data_array = DataArray(data, name=varname, dims=dimnames, coords=coords,
attrs=attrs) attrs=attrs)
@ -639,7 +726,7 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain):
return data_array return data_array
def _find_forward(wrfseq, varname, timeidx, is_moving, meta): def _find_forward(wrfseq, varname, timeidx, is_moving, meta, _key):
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
comboidx = 0 comboidx = 0
@ -657,18 +744,18 @@ def _find_forward(wrfseq, varname, timeidx, is_moving, meta):
if meta: if meta:
return _build_data_array(wrfnc, varname, filetimeidx, return _build_data_array(wrfnc, varname, filetimeidx,
is_moving) is_moving, True, _key)
else: else:
result = wrfnc.variables[varname][filetimeidx, :] result = wrfnc.variables[varname][filetimeidx, :]
return result[np.newaxis, :] # So that nosqueee works return result[np.newaxis, :] # So that nosqueeze works
else: else:
comboidx += numtimes comboidx += numtimes
raise IndexError("invalid time index") raise IndexError("timeidx {} is out of bounds".format(timeidx))
def _find_reverse(wrfseq, varname, timeidx, is_moving, meta): def _find_reverse(wrfseq, varname, timeidx, is_moving, meta, _key):
try: try:
revwrfseq = reversed(wrfseq) revwrfseq = reversed(wrfseq)
except TypeError: except TypeError:
@ -695,35 +782,36 @@ def _find_reverse(wrfseq, varname, timeidx, is_moving, meta):
if meta: if meta:
return _build_data_array(wrfnc, varname, filetimeidx, return _build_data_array(wrfnc, varname, filetimeidx,
is_moving) is_moving, True, _key)
else: else:
result = wrfnc.variables[varname][filetimeidx, :] result = wrfnc.variables[varname][filetimeidx, :]
return result[np.newaxis, :] # So that nosqueeze works return result[np.newaxis, :] # So that nosqueeze works
else: else:
comboidx += numtimes comboidx += numtimes
raise IndexError("invalid time index") raise IndexError("timeidx {} is out of bounds".format(timeidx))
def _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta): def _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta, _key):
if timeidx >= 0: if timeidx >= 0:
return _find_forward(wrfseq, varname, timeidx, is_moving, meta) return _find_forward(wrfseq, varname, timeidx, is_moving, meta, _key)
else: else:
return _find_reverse(wrfseq, varname, timeidx, is_moving, meta) return _find_reverse(wrfseq, varname, timeidx, is_moving, meta, _key)
# TODO: implement in C # TODO: implement in C
def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta): def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
if is_moving is None: if is_moving is None:
is_moving = is_moving_domain(wrfseq, varname) is_moving = is_moving_domain(wrfseq, varname, _key=_key)
file_times = extract_times(wrfseq, ALL_TIMES) file_times = extract_times(wrfseq, ALL_TIMES, meta=False, do_xtime=False)
multitime = is_multi_time_req(timeidx) multitime = is_multi_time_req(timeidx)
# For single times, just need to find the ncfile and appropriate # For single times, just need to find the ncfile and appropriate
# time index, and return that array # time index, and return that array
if not multitime: if not multitime:
return _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta) return _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta,
_key)
#time_idx_or_slice = timeidx if not multitime else slice(None) #time_idx_or_slice = timeidx if not multitime else slice(None)
@ -733,7 +821,7 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
if xarray_enabled() and meta: if xarray_enabled() and meta:
first_var = _build_data_array(next(wrf_iter), varname, first_var = _build_data_array(next(wrf_iter), varname,
ALL_TIMES, is_moving) ALL_TIMES, is_moving, True, _key)
else: else:
first_var = (next(wrf_iter)).variables[varname][:] first_var = (next(wrf_iter)).variables[varname][:]
@ -750,27 +838,62 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
outdata[startidx:endidx, :] = first_var[:] outdata[startidx:endidx, :] = first_var[:]
if xarray_enabled() and meta and is_moving: if xarray_enabled() and meta:
latname, lonname, timename = _find_coord_names(first_var.coords) latname, lonname, timename = _find_coord_names(first_var.coords)
timecached = False
latcached = False
loncached = False
projcached = False
outxtimes = None
outlats = None
outlons = None
outprojs = None
timekey = timename+"_cat" if timename is not None else None
latkey = latname + "_cat" if latname is not None else None
lonkey = lonname + "_cat" if lonname is not None else None
projkey = "projection_cat" if is_moving else None
if timename is not None:
outxtimes = get_cached_item(_key, timekey)
if outxtimes is None:
outxtimes = np.empty(outdims[0])
outxtimes[startidx:endidx] = npvalues(first_var.coords[timename][:])
else:
timecached = True
if is_moving:
outcoorddims = outdims[0:1] + outdims[-2:] outcoorddims = outdims[0:1] + outdims[-2:]
if latname is not None: if latname is not None:
# Try to pull from the coord cache
outlats = get_cached_item(_key, latkey)
if outlats is None:
outlats = np.empty(outcoorddims, first_var.dtype) outlats = np.empty(outcoorddims, first_var.dtype)
outlats[startidx:endidx, :] = first_var.coords[latname][:] outlats[startidx:endidx, :] = npvalues(first_var.coords[latname][:])
else:
latcached = True
if lonname is not None: if lonname is not None:
outlons = get_cached_item(_key, lonkey)
if outlons is None:
outlons = np.empty(outcoorddims, first_var.dtype) outlons = np.empty(outcoorddims, first_var.dtype)
outlons[startidx:endidx, :] = first_var.coords[lonname][:] outlons[startidx:endidx, :] = npvalues(first_var.coords[lonname][:])
else:
if timename is not None: loncached = True
outxtimes = np.empty(outdims[0])
outxtimes[startidx:endidx] = first_var.coords[timename][:]
# Projections also need to be aggregated # Projections also need to be aggregated
outprojs = get_cached_item(_key, projkey)
if outprojs is None:
outprojs = np.empty(outdims[0], np.object) outprojs = np.empty(outdims[0], np.object)
outprojs[startidx:endidx] = np.asarray(
first_var.attrs["projection"], np.object)[:]
else:
projcached = True
outprojs[startidx:endidx] = np.asarray(first_var.attrs["projection"],
np.object)[:]
startidx = endidx startidx = endidx
while True: while True:
@ -787,19 +910,22 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
outdata[startidx:endidx, :] = vardata[:] outdata[startidx:endidx, :] = vardata[:]
if xarray_enabled() and meta and is_moving: if xarray_enabled() and meta:
if latname is not None: # XTIME new in 3.7
if timename is not None and not timecached:
xtimedata = wrfnc.variables[timename][:]
outxtimes[startidx:endidx] = xtimedata[:]
if is_moving:
if latname is not None and not latcached:
latdata = wrfnc.variables[latname][:] latdata = wrfnc.variables[latname][:]
outlats[startidx:endidx, :] = latdata[:] outlats[startidx:endidx, :] = latdata[:]
if lonname is not None: if lonname is not None and not loncached:
londata = wrfnc.variables[lonname][:] londata = wrfnc.variables[lonname][:]
outlons[startidx:endidx, :] = londata[:] outlons[startidx:endidx, :] = londata[:]
if timename is not None: if not projcached:
xtimedata = wrfnc.variables[timename][:]
outxtimes[startidx:endidx] = xtimedata[:]
lats, lons, proj_params = get_proj_params(wrfnc, lats, lons, proj_params = get_proj_params(wrfnc,
ALL_TIMES, ALL_TIMES,
varname) varname)
@ -807,11 +933,23 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
lons=lons[i,:], lons=lons[i,:],
**proj_params) for i in py3range(lats.shape[0])] **proj_params) for i in py3range(lats.shape[0])]
outprojs[startidx:endidx] = np.asarray(projs, np.object)[:] outprojs[startidx:endidx] = np.asarray(projs,
np.object)[:]
startidx = endidx startidx = endidx
if xarray_enabled() and meta: if xarray_enabled() and meta:
# Cache the coords if applicable
if not latcached and outlats is not None:
cache_item(_key, latkey, outlats)
if not loncached and outlons is not None:
cache_item(_key, lonkey, outlons)
if not projcached and outprojs is not None:
cache_item(_key, projkey, outprojs)
if not timecached and outxtimes is not None:
cache_item(_key, timekey, outxtimes)
outname = ucode(first_var.name) outname = ucode(first_var.name)
outattrs = OrderedDict(first_var.attrs) outattrs = OrderedDict(first_var.attrs)
outcoords = OrderedDict(first_var.coords) outcoords = OrderedDict(first_var.coords)
@ -827,7 +965,11 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
outcoords["datetime"] = outdimnames[0], file_times outcoords["datetime"] = outdimnames[0], file_times
# If the domain is moving, need to create the lat/lon/xtime coords if timename is not None:
outxtimes = outxtimes[:]
outcoords[timename] = outdimnames[0], outxtimes
# If the domain is moving, need to create the lat/lon coords
# since they can't be copied # since they can't be copied
if is_moving: if is_moving:
outlatdims = [outdimnames[0]] + outdimnames[-2:] outlatdims = [outdimnames[0]] + outdimnames[-2:]
@ -838,9 +980,6 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
if lonname is not None: if lonname is not None:
outlons = outlons[:] outlons = outlons[:]
outcoords[lonname] = outlatdims, outlons outcoords[lonname] = outlatdims, outlons
if timename is not None:
outxtimes = outxtimes[:]
outcoords[timename] = outdimnames[0], outxtimes
outattrs["projection"] = outprojs[:] outattrs["projection"] = outprojs[:]
@ -855,6 +994,7 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta):
return outarr return outarr
def _get_numfiles(wrfseq): def _get_numfiles(wrfseq):
try: try:
return len(wrfseq) return len(wrfseq)
@ -862,10 +1002,11 @@ def _get_numfiles(wrfseq):
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
return sum(1 for _ in wrf_iter) return sum(1 for _ in wrf_iter)
# TODO: implement in C # TODO: implement in C
def _join_files(wrfseq, varname, timeidx, is_moving, meta): def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key):
if is_moving is None: if is_moving is None:
is_moving = is_moving_domain(wrfseq, varname) is_moving = is_moving_domain(wrfseq, varname, _key=_key)
multitime = is_multi_time_req(timeidx) multitime = is_multi_time_req(timeidx)
numfiles = _get_numfiles(wrfseq) numfiles = _get_numfiles(wrfseq)
maxtimes = _find_max_time_size(wrfseq) maxtimes = _find_max_time_size(wrfseq)
@ -880,7 +1021,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
numtimes = extract_dim(wrfnc, "Time") numtimes = extract_dim(wrfnc, "Time")
if xarray_enabled() and meta: if xarray_enabled() and meta:
first_var = _build_data_array(wrfnc, varname, ALL_TIMES, is_moving) first_var = _build_data_array(wrfnc, varname, ALL_TIMES, is_moving,
True, _key)
time_coord = np.full((numfiles, maxtimes), int(NaT), "datetime64[ns]") time_coord = np.full((numfiles, maxtimes), int(NaT), "datetime64[ns]")
time_coord[file_idx, 0:numtimes] = first_var.coords["Time"][:] time_coord[file_idx, 0:numtimes] = first_var.coords["Time"][:]
else: else:
@ -900,31 +1042,67 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
outdata[file_idx, 0:numtimes, :] = first_var[:] outdata[file_idx, 0:numtimes, :] = first_var[:]
# Create the secondary coordinate arrays # Create the secondary coordinate arrays
if xarray_enabled() and meta and is_moving: if xarray_enabled() and meta:
latname, lonname, timename = _find_coord_names(first_var.coords) latname, lonname, timename = _find_coord_names(first_var.coords)
outcoorddims = outdims[0:2] + outdims[-2:] outcoorddims = outdims[0:2] + outdims[-2:]
timecached = False
latcached = False
loncached = False
projcached = False
outxtimes = None
outlats = None
outlons = None
outprojs = None
timekey = timename+"_join" if timename is not None else None
latkey = latname + "_join" if latname is not None else None
lonkey = lonname + "_join" if lonname is not None else None
projkey = "projection_join" if is_moving else None
if timename is not None:
outxtimes = get_cached_item(_key, timekey)
if outxtimes is None:
outxtimes = np.full(outdims[0:2], Constants.DEFAULT_FILL,
first_var.dtype)
outxtimes[file_idx, 0:numtimes] = first_var.coords[timename][:]
else:
timecached = True
if is_moving:
if latname is not None: if latname is not None:
outlats = get_cached_item(_key, latkey)
if outlats is None:
outlats = np.full(outcoorddims, Constants.DEFAULT_FILL, outlats = np.full(outcoorddims, Constants.DEFAULT_FILL,
first_var.dtype) first_var.dtype)
outlats[file_idx, 0:numtimes, :] = first_var.coords[latname][:] outlats[file_idx, 0:numtimes, :] = (
first_var.coords[latname][:])
else:
latcached = True
if lonname is not None: if lonname is not None:
outlons = get_cached_item(_key, lonkey)
if outlons is None:
outlons = np.full(outcoorddims, Constants.DEFAULT_FILL, outlons = np.full(outcoorddims, Constants.DEFAULT_FILL,
first_var.dtype) first_var.dtype)
outlons[file_idx, 0:numtimes, :] = first_var.coords[lonname][:] outlons[file_idx, 0:numtimes, :] = (
first_var.coords[lonname][:])
if timename is not None: else:
outxtimes = np.full(outdims[0:2], Constants.DEFAULT_FILL, loncached = True
first_var.dtype)
outxtimes[file_idx, 0:numtimes] = first_var.coords[timename][:]
# Projections also need two dimensions # Projections also need two dimensions
outprojs = get_cached_item(_key, projkey)
if outprojs is None:
outprojs = np.full(outdims[0:2], NullProjection(), np.object) outprojs = np.full(outdims[0:2], NullProjection(), np.object)
outprojs[file_idx, 0:numtimes] = np.asarray( outprojs[file_idx, 0:numtimes] = np.asarray(
first_var.attrs["projection"], first_var.attrs["projection"],
np.object)[:] np.object)[:]
else:
projcached = True
file_idx=1 file_idx=1
while True: while True:
@ -944,23 +1122,26 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
outdata[file_idx, 0:numtimes, :] = outvar[:] outdata[file_idx, 0:numtimes, :] = outvar[:]
if xarray_enabled() and meta: if xarray_enabled() and meta:
file_times = extract_times(wrfnc, ALL_TIMES) # For join, the times are a function of fileidx
file_times = extract_times(wrfnc, ALL_TIMES, meta=False,
do_xtime=False)
time_coord[file_idx, 0:numtimes] = np.asarray(file_times, time_coord[file_idx, 0:numtimes] = np.asarray(file_times,
"datetime64[ns]")[:] "datetime64[ns]")[:]
if xarray_enabled() and meta and is_moving: if timename is not None and not timecached:
if latname is not None: xtimedata = wrfnc.variables[timename][:]
outxtimes[file_idx, 0:numtimes] = xtimedata[:]
if is_moving:
if latname is not None and not latcached:
latdata = wrfnc.variables[latname][:] latdata = wrfnc.variables[latname][:]
outlats[file_idx, 0:numtimes, :] = latdata[:] outlats[file_idx, 0:numtimes, :] = latdata[:]
if lonname is not None: if lonname is not None and not loncached:
londata = wrfnc.variables[lonname][:] londata = wrfnc.variables[lonname][:]
outlons[file_idx, 0:numtimes, :] = londata[:] outlons[file_idx, 0:numtimes, :] = londata[:]
if timename is not None: if not projcached:
xtimedata = wrfnc.variables[timename][:]
outxtimes[file_idx, 0:numtimes] = xtimedata[:]
lats, lons, proj_params = get_proj_params(wrfnc, lats, lons, proj_params = get_proj_params(wrfnc,
ALL_TIMES, ALL_TIMES,
varname) varname)
@ -981,6 +1162,16 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
outdata = np.ma.masked_values(outdata, Constants.DEFAULT_FILL) outdata = np.ma.masked_values(outdata, Constants.DEFAULT_FILL)
if xarray_enabled() and meta: if xarray_enabled() and meta:
# Cache the coords if applicable
if not latcached and outlats is not None:
cache_item(_key, latkey, outlats)
if not loncached and outlons is not None:
cache_item(_key, lonkey, outlons)
if not projcached and outprojs is not None:
cache_item(_key, projkey, outprojs)
if not timecached and outxtimes is not None:
cache_item(_key, timekey, outxtimes)
outname = ucode(first_var.name) outname = ucode(first_var.name)
outcoords = OrderedDict(first_var.coords) outcoords = OrderedDict(first_var.coords)
outattrs = OrderedDict(first_var.attrs) outattrs = OrderedDict(first_var.attrs)
@ -1000,7 +1191,13 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
outattrs["_FillValue"] = Constants.DEFAULT_FILL outattrs["_FillValue"] = Constants.DEFAULT_FILL
outattrs["missing_value"] = Constants.DEFAULT_FILL outattrs["missing_value"] = Constants.DEFAULT_FILL
# If the domain is moving, need to create the lat/lon/xtime coords if timename is not None:
outxtimes = outxtimes[:, time_idx_or_slice]
if not multitime:
outxtimes = outxtimes[:, np.newaxis]
outcoords[timename] = outdimnames[0:2], outxtimes[:]
# If the domain is moving, need to create the lat/lon coords
# since they can't be copied # since they can't be copied
if is_moving: if is_moving:
outlatdims = outdimnames[0:2] + outdimnames[-2:] outlatdims = outdimnames[0:2] + outdimnames[-2:]
@ -1015,11 +1212,6 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
if not multitime: if not multitime:
outlons = outlons[:, np.newaxis, :] outlons = outlons[:, np.newaxis, :]
outcoords[lonname] = outlatdims, outlons outcoords[lonname] = outlatdims, outlons
if timename is not None:
outxtimes = outxtimes[:, time_idx_or_slice]
if not multitime:
outxtimes = outxtimes[:, np.newaxis]
outcoords[timename] = outdimnames[0:2], outxtimes[:]
if not multitime: if not multitime:
outattrs["projection"] = outprojs[:, timeidx] outattrs["projection"] = outprojs[:, timeidx]
@ -1043,19 +1235,20 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta):
return outarr return outarr
def combine_files(wrfseq, varname, timeidx, is_moving=None, def combine_files(wrfseq, varname, timeidx, is_moving=None,
method="cat", squeeze=True, meta=True): method="cat", squeeze=True, meta=True,
_key=None):
# Handles generators, single files, lists, tuples, custom classes # Handles generators, single files, lists, tuples, custom classes
wrfseq = get_iterable(wrfseq) wrfseq = get_iterable(wrfseq)
# Dictionary is unique # Dictionary is unique
if is_mapping(wrfseq): if is_mapping(wrfseq):
outarr = _combine_dict(wrfseq, varname, timeidx, method, meta) outarr = _combine_dict(wrfseq, varname, timeidx, method, meta, _key)
elif method.lower() == "cat": elif method.lower() == "cat":
outarr = _cat_files(wrfseq, varname, timeidx, is_moving, outarr = _cat_files(wrfseq, varname, timeidx, is_moving,
squeeze, meta) squeeze, meta, _key)
elif method.lower() == "join": elif method.lower() == "join":
outarr = _join_files(wrfseq, varname, timeidx, is_moving, meta) outarr = _join_files(wrfseq, varname, timeidx, is_moving, meta, _key)
else: else:
raise ValueError("method must be 'cat' or 'join'") raise ValueError("method must be 'cat' or 'join'")
@ -1064,7 +1257,7 @@ def combine_files(wrfseq, varname, timeidx, is_moving=None,
# Cache is a dictionary of already extracted variables # Cache is a dictionary of already extracted variables
def _extract_var(wrfnc, varname, timeidx, is_moving, def _extract_var(wrfnc, varname, timeidx, is_moving,
method, squeeze, cache, meta): method, squeeze, cache, meta, _key):
# 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:
@ -1081,11 +1274,16 @@ def _extract_var(wrfnc, varname, timeidx, is_moving,
multitime = is_multi_time_req(timeidx) multitime = is_multi_time_req(timeidx)
multifile = is_multi_file(wrfnc) multifile = is_multi_file(wrfnc)
if is_time_coord_var(varname):
return extract_times(wrfnc, timeidx, method, squeeze, cache,
meta, do_xtime=True)
if not multifile: if not multifile:
if xarray_enabled() and meta: if xarray_enabled() and meta:
if is_moving is None: if is_moving is None:
is_moving = is_moving_domain(wrfnc, varname) is_moving = is_moving_domain(wrfnc, varname, _key=_key)
result = _build_data_array(wrfnc, varname, timeidx, is_moving) result = _build_data_array(wrfnc, varname, timeidx, is_moving,
multifile, _key)
else: else:
if not multitime: if not multitime:
result = wrfnc.variables[varname][timeidx,:] result = wrfnc.variables[varname][timeidx,:]
@ -1095,20 +1293,20 @@ def _extract_var(wrfnc, varname, timeidx, is_moving,
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, is_moving, return combine_files(wrfnc, varname, timeidx, is_moving,
method, squeeze, meta) method, squeeze, meta, _key)
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, meta=True): cache=None, meta=True, _key=None):
if isstr(varnames): if isstr(varnames):
varlist = [varnames] varlist = [varnames]
else: else:
varlist = varnames varlist = varnames
return {var:_extract_var(wrfnc, var, timeidx, None, return {var:_extract_var(wrfnc, var, timeidx, None,
method, squeeze, cache, meta) method, squeeze, cache, meta, _key)
for var in varlist} for var in varlist}
# Python 3 compatability # Python 3 compatability
@ -1120,27 +1318,79 @@ def _make_time(timearr):
return dt.datetime.strptime("".join(npbytes_to_str(timearr)), return dt.datetime.strptime("".join(npbytes_to_str(timearr)),
"%Y-%m-%d_%H:%M:%S") "%Y-%m-%d_%H:%M:%S")
def _file_times(wrfnc, timeidx):
multitime = is_multi_time_req(timeidx) def _file_times(wrfnc, do_xtime):
if multitime: if not do_xtime:
times = wrfnc.variables["Times"][:,:] times = wrfnc.variables["Times"][:,:]
for i in py3range(times.shape[0]): for i in py3range(times.shape[0]):
yield _make_time(times[i,:]) yield _make_time(times[i,:])
else: else:
times = wrfnc.variables["Times"][timeidx,:] xtimes = wrfnc.variables["XTIME"][:]
yield _make_time(times) for i in py3range(xtimes.shape[0]):
yield xtimes[i]
def _extract_time_map(wrfnc, timeidx, do_xtime, meta=False):
return {key : extract_times(wrfseq, timeidx, do_xtime, meta)
for key, wrfseq in viewitems(wrfnc)}
def extract_times(wrfnc, timeidx, method="cat", squeeze=True, cache=None,
meta=False, do_xtime=False):
if is_mapping(wrfnc):
return _extract_time_map(wrfnc, timeidx, do_xtime)
def extract_times(wrfnc, timeidx): multitime = is_multi_time_req(timeidx)
multi_file = is_multi_file(wrfnc) multi_file = is_multi_file(wrfnc)
if not multi_file: if not multi_file:
wrf_list = [wrfnc] wrf_list = [wrfnc]
else: else:
wrf_list = wrfnc wrf_list = wrfnc
return [file_time try:
if method.lower() == "cat":
time_list = [file_time
for wrf_file in wrf_list for wrf_file in wrf_list
for file_time in _file_times(wrf_file, timeidx)] for file_time in _file_times(wrf_file, do_xtime)]
elif method.lower() == "join":
time_list = [[file_time
for file_time in _file_times(wrf_file, do_xtime)]
for wrf_file in wrf_list]
else:
raise ValueError("invalid method argument '{}'".format(method))
except KeyError:
return None # Thrown for pre-3.7 XTIME not existing
if xarray_enabled() and meta:
outattrs = OrderedDict()
outcoords = None
if method.lower() == "cat":
outdimnames = ["Time"]
else:
outdimnames = ["fileidx", "Time"]
if not do_xtime:
outname = "times"
outattrs["description"] = "model times [np.datetime64]"
else:
ncfile = next(iter(wrf_list))
var = ncfile.variables["XTIME"]
outattrs.update(var.__dict__)
outname = "XTIME"
outarr = DataArray(time_list, name=outname, coords=outcoords,
dims=outdimnames, attrs=outattrs)
else:
outarr = np.asarray(time_list)
if not multitime:
return outarr[timeidx]
return outarr
def is_standard_wrf_var(wrfnc, var): def is_standard_wrf_var(wrfnc, var):
@ -1385,10 +1635,23 @@ def arg_location(func, argname, args, kwargs):
return _arg_location(func, argname, args, kwargs) return _arg_location(func, argname, args, kwargs)
def psafilepath(): def psafilepath():
return os.path.join(os.path.dirname(__file__), "data", "psadilookup.dat") return os.path.join(os.path.dirname(__file__), "data", "psadilookup.dat")
def get_id(seq):
if not is_mapping(seq):
return id(seq)
# For each key in the mapping, recurisvely call get_id until
# until a non-mapping is found
return {key : get_id(val) for key,val in viewitems(seq)}

32
src/wrf/uvmet.py

@ -17,31 +17,31 @@ from .util import extract_vars, extract_global_attrs, either
@convert_units("wind", "mps") @convert_units("wind", "mps")
def _get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True, def _get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
ten_m=False, units ="mps"): ten_m=False, units ="mps"):
""" 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,
meta=False) meta=False, _key=_key)
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,
meta=False) meta=False, _key=_key)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
else: else:
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,
meta=False) meta=False, _key=_key)
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,
meta=False) meta=False, _key=_key)
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))
@ -97,12 +97,14 @@ def _get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True,
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, meta=False) method, squeeze, cache, meta=False,
_key=_key)
lat = xlat_var[varname] lat = xlat_var[varname]
varname = either("XLONG_M", "XLONG")(wrfnc) varname = either("XLONG_M", "XLONG")(wrfnc)
xlon_var = extract_vars(wrfnc, timeidx, varname, xlon_var = extract_vars(wrfnc, timeidx, varname,
method, squeeze, cache, meta=False) method, squeeze, cache, meta=False,
_key=_key)
lon = xlon_var[varname] lon = xlon_var[varname]
if map_proj == 1: if map_proj == 1:
@ -132,10 +134,10 @@ def _get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True,
two_d=False, two_d=False,
wspd_wdir=False) wspd_wdir=False)
def get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True, def get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
return _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, return _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key,
False, units) False, units)
@ -145,10 +147,10 @@ def get_uvmet(wrfnc, timeidx=0, method="cat", squeeze=True,
two_d=True, two_d=True,
wspd_wdir=False) wspd_wdir=False)
def get_uvmet10(wrfnc, timeidx=0, method="cat", squeeze=True, def get_uvmet10(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
return _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, return _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key,
True, units) True, units)
@ -158,11 +160,11 @@ def get_uvmet10(wrfnc, timeidx=0, method="cat", squeeze=True,
two_d=False, two_d=False,
wspd_wdir=True) wspd_wdir=True)
def get_uvmet_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True, def get_uvmet_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
uvmet = _get_uvmet(wrfnc, timeidx, method, squeeze, uvmet = _get_uvmet(wrfnc, timeidx, method, squeeze,
cache, meta, False, units) cache, meta, _key, False, units)
return _calc_wspd_wdir(uvmet[0,...,:,:,:], uvmet[1,...,:,:,:], return _calc_wspd_wdir(uvmet[0,...,:,:,:], uvmet[1,...,:,:,:],
False, units) False, units)
@ -174,10 +176,10 @@ def get_uvmet_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True,
two_d=True, two_d=True,
wspd_wdir=True) wspd_wdir=True)
def get_uvmet10_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True, def get_uvmet10_wspd_wdir(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
uvmet10 = _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, uvmet10 = _get_uvmet(wrfnc, timeidx, method, squeeze, cache, meta, _key,
True, units) True, units)
return _calc_wspd_wdir(uvmet10[0,...,:,:], uvmet10[1,...,:,:], True, units) return _calc_wspd_wdir(uvmet10[0,...,:,:], uvmet10[1,...,:,:], True, units)

2
src/wrf/version.py

@ -1,2 +1,2 @@
__version__ = "1.0a1" __version__ = "1.0a2"

8
src/wrf/vorticity.py

@ -10,11 +10,11 @@ from .metadecorators import copy_and_set_metadata
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,
meta=True): meta=True, _key=None):
ncvars = extract_vars(wrfnc, timeidx, ("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, meta=False) method, squeeze, cache, meta=False, _key=_key)
attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]
@ -34,12 +34,12 @@ def get_avo(wrfnc, timeidx=0, method="cat", squeeze=True, cache=None,
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,
meta=True): meta=True, _key=None):
ncvars = extract_vars(wrfnc, timeidx, ("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, meta=False) method, squeeze, cache, meta=False, _key=_key)
attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY")) attrs = extract_global_attrs(wrfnc, attrs=("DX", "DY"))
u = ncvars["U"] u = ncvars["U"]

24
src/wrf/wind.py

@ -52,11 +52,11 @@ def _calc_wspd_wdir(u, v, two_d, units):
wspd_wdir=False) wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_u_destag(wrfnc, timeidx=0, method="cat", squeeze=True, def get_u_destag(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
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,
meta=False) meta=False, _key=_key)
u = destagger(u_vars[varname], -1) u = destagger(u_vars[varname], -1)
return u return u
@ -70,11 +70,11 @@ def get_u_destag(wrfnc, timeidx=0, method="cat", squeeze=True,
wspd_wdir=False) wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_v_destag(wrfnc, timeidx=0, method="cat", squeeze=True, def get_v_destag(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
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,
meta=False) meta=False, _key=_key)
v = destagger(v_vars[varname], -2) v = destagger(v_vars[varname], -2)
return v return v
@ -88,10 +88,10 @@ def get_v_destag(wrfnc, timeidx=0, method="cat", squeeze=True,
wspd_wdir=False) wspd_wdir=False)
@convert_units("wind", "mps") @convert_units("wind", "mps")
def get_w_destag(wrfnc, timeidx=0, method="cat", squeeze=True, def get_w_destag(wrfnc, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
w_vars = extract_vars(wrfnc, timeidx, "W", method, squeeze, cache, w_vars = extract_vars(wrfnc, timeidx, "W", method, squeeze, cache,
meta=False) meta=False, _key=_key)
w = destagger(w_vars["W"], -3) w = destagger(w_vars["W"], -3)
return w return w
@ -102,16 +102,16 @@ def get_w_destag(wrfnc, timeidx=0, method="cat", squeeze=True,
two_d=False, two_d=False,
wspd_wdir=True) wspd_wdir=True)
def get_destag_wspd_wdir(wrfnc, timeidx=0, method="cat", def get_destag_wspd_wdir(wrfnc, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, squeeze=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
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,
meta=False) meta=False, _key=_key)
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,
meta=False) meta=False, _key=_key)
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)
@ -123,18 +123,18 @@ def get_destag_wspd_wdir(wrfnc, timeidx=0, method="cat",
two_d=False, two_d=False,
wspd_wdir=True) wspd_wdir=True)
def get_destag_wspd_wdir10(wrfnc, timeidx=0, method="cat", def get_destag_wspd_wdir10(wrfnc, timeidx=0, method="cat",
squeeze=True, cache=None, meta=True, squeeze=True, cache=None, meta=True, _key=None,
units="mps"): units="mps"):
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,
meta=False) meta=False, _key=_key)
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,
meta=False) meta=False, _key=_key)
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
test/ipynb/WRF_Workshop_Demo.ipynb

@ -293,7 +293,7 @@
"angle = 90.0\n", "angle = 90.0\n",
"\n", "\n",
"# Compute the vertical cross-section interpolation. Also, include the lat/lon points along the cross-section.\n", "# Compute the vertical cross-section interpolation. Also, include the lat/lon points along the cross-section.\n",
"p_vertx = vertcross(p, z, pivot_point=pivot_point, angle=angle, include_latlon=True)\n", "p_vertx = vertcross(p, z, pivot_point=pivot_point, angle=angle, latlon=True)\n",
"\n", "\n",
"# Extract the numpy array\n", "# Extract the numpy array\n",
"p_vert_array = npvalues(p_vertx)\n", "p_vert_array = npvalues(p_vertx)\n",

25
test/ipynb/WRF_python_demo.ipynb

@ -565,8 +565,9 @@
"source": [ "source": [
"from wrf.latlon import xy_to_ll, ll_to_xy \n", "from wrf.latlon import xy_to_ll, ll_to_xy \n",
"\n", "\n",
"a = xy_to_ll(ncfile, 400, 200)\n",
"a = xy_to_ll(ncfile, [400,105], [200,205])\n", "a = xy_to_ll(ncfile, [400,105], [200,205])\n",
"b = ll_to_xy(ncfile, 45.5, -110.8)\n", "b = ll_to_xy(ncfile, 45.5, -110.8, as_int=True)\n",
"\n", "\n",
"# Note: Lists/Dictionaries of files will add a new dimension ('domain') only if the domain is moving\n", "# Note: Lists/Dictionaries of files will add a new dimension ('domain') only if the domain is moving\n",
"c = xy_to_ll([ncfile, ncfile, ncfile], [400,105], [200,205])\n", "c = xy_to_ll([ncfile, ncfile, ncfile], [400,105], [200,205])\n",
@ -991,11 +992,29 @@
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": { "metadata": {
"collapsed": true "collapsed": false
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"\n" "from wrf.latlon import xy_to_ll, ll_to_xy \n",
"\n",
"a = xy_to_ll(ncfiles, 400, 200)\n",
"a = xy_to_ll(ncfiles, [400,105], [200,205])\n",
"b = ll_to_xy(ncfiles, 45.5, -110.8, as_int=True)\n",
"\n",
"# Note: Lists/Dictionaries of files will add a new dimension ('domain') only if the domain is moving\n",
"c = xy_to_ll(ncfiles, [400,105], [200,205])\n",
"d = xy_to_ll({\"label1\" : ncfiles,\n",
" \"label2\" : ncfiles}, \n",
" [400,105], [200,205])\n",
"\n",
"print(a)\n",
"print(\"\\n\")\n",
"print(b)\n",
"print(\"\\n\")\n",
"print(c)\n",
"print(\"\\n\")\n",
"print(d)\n"
] ]
}, },
{ {

26
test/ncl_get_var.ncl

@ -326,6 +326,32 @@
filevarattdef(fout,"fld_thetae_pres", fld8_intrp) filevarattdef(fout,"fld_thetae_pres", fld8_intrp)
fout->fld_thetae_pres = (/fld8_intrp/) fout->fld_thetae_pres = (/fld8_intrp/)
; lat/lon to x/y and x/y to lat/lon routines
lats = (/-55, -60, -65 /)
lons = (/25, 30, 35 /)
i_s = (/10, 100, 150 /)
j_s = (/10, 100, 150 /)
ij = wrf_user_ll_to_ij(input_file, lons, lats, True)
ll = wrf_user_ij_to_ll(input_file, i_s, j_s, True)
ij_dims = dimsizes(ij)
ll_dims = dimsizes(ll)
filedimdef(fout, (/"i_j", "ij_idx"/), \
(/ij_dims(0), ij_dims(1)/), \
(/False,False/))
filedimdef(fout, (/"lat_lon", "ll_idx"/), \
(/ll_dims(0), ll_dims(1)/), \
(/False,False/))
filevardef(fout, "ij", typeof(ij), (/"i_j", "ij_idx"/))
filevardef(fout, "ll", typeof(ll), (/"lat_lon", "ll_idx"/))
filevarattdef(fout,"ij", ij)
filevarattdef(fout,"ll", ll)
fout->ij = (/ij/)
fout->ll = (/ll/)
delete(fout) delete(fout)

58
test/test_multi_cache.py

@ -0,0 +1,58 @@
import time
from wrf.cache import _get_cache
from wrf import getvar
from netCDF4 import Dataset as nc
#a = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_single/wrfout_d02_2005-08-28_00:00:00")
#b = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_single/wrfout_d02_2005-08-28_03:00:00")
a = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00")
b = nc("/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00")
q = {"outoutoutout" : {"outoutout" : {"outout" : {"out1" : {"blah" : [a,b], "blah2" : [a,b]}, "out2" : {"blah" : [a,b], "blah2" : [a,b]} } } } }
t1 = time.time()
c = getvar(q, "rh", method="cat", timeidx=None, squeeze=True)
t2 = time.time()
print (c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="cat", timeidx=None, squeeze=False)
t2 = time.time()
print (c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="cat", timeidx=1, squeeze=True)
t2 = time.time()
print (c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="cat", timeidx=1, squeeze=False)
t2 = time.time()
print(c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="join", timeidx=None, squeeze=True)
t2 = time.time()
print (c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="join", timeidx=None, squeeze=False)
t2 = time.time()
print(c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="join", timeidx=1, squeeze=True)
t2 = time.time()
print (c)
print ("time taken: {}".format((t2-t1)*1000.))
t1 = time.time()
c = getvar(q, "rh", method="join", timeidx=1, squeeze=False)
t2 = time.time()
print (c)
print ("time taken: {}".format((t2-t1)*1000.))

120
test/utests.py

@ -6,7 +6,8 @@ import os, sys
import subprocess import subprocess
from wrf import (getvar, interplevel, interpline, vertcross, vinterp, from wrf import (getvar, interplevel, interpline, vertcross, vinterp,
disable_xarray, xarray_enabled, npvalues) disable_xarray, xarray_enabled, npvalues,
xy_to_ll, ll_to_xy )
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"
@ -457,12 +458,101 @@ def make_interp_test(varname, wrf_in, referent, multi=False,
return test return test
def make_latlon_test(testid, wrf_in, referent, single, multi=False, repeat=3,
pynio=False):
def test(self):
try:
from netCDF4 import Dataset as NetCDF
except:
pass
try:
from PyNIO import Nio
except:
pass
if not multi:
timeidx = 0
if not pynio:
in_wrfnc = NetCDF(wrf_in)
else:
# Note: Python doesn't like it if you reassign an outer scoped
# variable (wrf_in in this case)
if not wrf_in.endswith(".nc"):
wrf_file = wrf_in + ".nc"
else:
wrf_file = wrf_in
in_wrfnc = Nio.open_file(wrf_file)
else:
timeidx = None
if not pynio:
nc = NetCDF(wrf_in)
in_wrfnc = [nc for i in xrange(repeat)]
else:
if not wrf_in.endswith(".nc"):
wrf_file = wrf_in + ".nc"
else:
wrf_file = wrf_in
nc = Nio.open_file(wrf_file)
in_wrfnc = [nc for i in xrange(repeat)]
refnc = NetCDF(referent)
if testid == "xy":
# Since this domain is not moving, the reference values are the
# same whether there are multiple or single files
ref_vals = refnc.variables["ij"][:]
# Lats/Lons taken from NCL script, just hard-coding for now
lats = [-55, -60, -65]
lons = [25, 30, 35]
# Just call with a single lat/lon
if single:
xy = ll_to_xy(in_wrfnc, lats[0], lons[0])
xy = xy + 1 # NCL uses fortran indexing
ref = ref_vals[:,0]
nt.assert_allclose(npvalues(xy), ref)
else:
xy = ll_to_xy(in_wrfnc, lats, lons)
xy = xy + 1 # NCL uses fortran indexing
ref = ref_vals[:]
nt.assert_allclose(npvalues(xy), ref)
else:
# Since this domain is not moving, the reference values are the
# same whether there are multiple or single files
ref_vals = refnc.variables["ll"][:]
# i_s, j_s taken from NCL script, just hard-coding for now
# NCL uses 1-based indexing for this, so need to subtract 1
i_s = np.asarray([10, 100, 150], int) - 1
j_s = np.asarray([10, 100, 150], int) - 1
if single:
ll = xy_to_ll(in_wrfnc, i_s[0], j_s[0])
ref = ref_vals[::-1,0]
nt.assert_allclose(npvalues(ll), ref)
else:
ll = xy_to_ll(in_wrfnc, i_s, j_s)
ref = ref_vals[::-1,:]
nt.assert_allclose(npvalues(ll), ref)
return test
class WRFVarsTest(ut.TestCase): class WRFVarsTest(ut.TestCase):
longMessage = True longMessage = True
class WRFInterpTest(ut.TestCase): class WRFInterpTest(ut.TestCase):
longMessage = True longMessage = True
class WRFLatLonTest(ut.TestCase):
longMessage = True
if __name__ == "__main__": if __name__ == "__main__":
ignore_vars = [] # Not testable yet ignore_vars = [] # Not testable yet
@ -472,6 +562,7 @@ if __name__ == "__main__":
"theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va",
"wa", "uvmet10", "uvmet", "z", "cfrac"] "wa", "uvmet10", "uvmet", "z", "cfrac"]
interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"]
latlon_tests = ["xy", "ll"]
try: try:
import netCDF4 import netCDF4
@ -497,6 +588,19 @@ if __name__ == "__main__":
setattr(WRFInterpTest, 'test_multi_{0}'.format(method), setattr(WRFInterpTest, 'test_multi_{0}'.format(method),
test_interp_func2) test_interp_func2)
for testid in latlon_tests:
for single in (True, False):
for multi in (True, False):
test_ll_func = make_latlon_test(testid, TEST_FILE,
OUT_NC_FILE,
single=single, multi=multi,
repeat=3, pynio=False)
multistr = "" if not multi else "_multi"
singlestr = "_nosingle" if not single else "_single"
test_name = "test_{}{}{}".format(testid, singlestr,
multistr)
setattr(WRFLatLonTest, test_name, test_ll_func)
try: try:
import PyNIO import PyNIO
except ImportError: except ImportError:
@ -523,5 +627,19 @@ if __name__ == "__main__":
setattr(WRFInterpTest, 'test_pynio_multi_{0}'.format(method), setattr(WRFInterpTest, 'test_pynio_multi_{0}'.format(method),
test_interp_func2) test_interp_func2)
for testid in latlon_tests:
for single in (True, False):
for multi in (True, False):
test_ll_func = make_latlon_test(testid, TEST_FILE,
OUT_NC_FILE,
single=single, multi=multi,
repeat=3, pynio=False)
multistr = "" if not multi else "_multi"
singlestr = "_nosingle" if not single else "_single"
test_name = "test_pynio_{}{}{}".format(testid,
singlestr,
multistr)
setattr(WRFLatLonTest, test_name, test_ll_func)
ut.main() ut.main()
Loading…
Cancel
Save