Browse Source

Merge branch 'feature/contrib' into develop

lon0
Bill Ladwig 6 years ago
parent
commit
b40d1d6e2d
  1. 63
      doc/source/contrib.rst
  2. 10
      doc/source/index.rst
  3. 19
      src/wrf/__init__.py
  4. 1
      src/wrf/api.py
  5. 5
      src/wrf/cache.py
  6. 27
      src/wrf/computation.py
  7. 4
      src/wrf/config.py
  8. 37
      src/wrf/constants.py
  9. 31
      src/wrf/coordpair.py
  10. 60
      src/wrf/decorators.py
  11. 2
      src/wrf/destag.py
  12. 185
      src/wrf/extension.py
  13. 27
      src/wrf/g_cape.py
  14. 24
      src/wrf/g_cloudfrac.py
  15. 5
      src/wrf/g_ctt.py
  16. 2
      src/wrf/g_dbz.py
  17. 7
      src/wrf/g_dewpoint.py
  18. 9
      src/wrf/g_geoht.py
  19. 13
      src/wrf/g_helicity.py
  20. 36
      src/wrf/g_latlon.py
  21. 3
      src/wrf/g_omega.py
  22. 2
      src/wrf/g_precip.py
  23. 5
      src/wrf/g_pressure.py
  24. 7
      src/wrf/g_pw.py
  25. 6
      src/wrf/g_rh.py
  26. 4
      src/wrf/g_slp.py
  27. 13
      src/wrf/g_temp.py
  28. 3
      src/wrf/g_terrain.py
  29. 1
      src/wrf/g_times.py
  30. 33
      src/wrf/g_uvmet.py
  31. 1
      src/wrf/g_vorticity.py
  32. 21
      src/wrf/g_wind.py
  33. 9
      src/wrf/geobnds.py
  34. 127
      src/wrf/interp.py
  35. 50
      src/wrf/interputils.py
  36. 35
      src/wrf/latlonutils.py
  37. 155
      src/wrf/metadecorators.py
  38. 198
      src/wrf/projection.py
  39. 3
      src/wrf/projutils.py
  40. 4
      src/wrf/py3compat.py
  41. 303
      src/wrf/routines.py
  42. 49
      src/wrf/specialdec.py
  43. 276
      src/wrf/units.py
  44. 219
      src/wrf/util.py
  45. 1
      src/wrf/version.py

63
doc/source/contrib.rst

@ -0,0 +1,63 @@
.. _contrib_guide:
Contributor Guide
=================================
.. note::
This contributor guide is written for wrf-python v1.3.x. In the
not-too-distant future, wrf-python will undergo a significant refactoring
to remove the wrapt decorators (which don't serialize for dask), but the
concepts will remain the same as described below.
Ways to Contribute
-----------------------------
Users are encouraged to contribute various ways. This includes:
- Submitting a bug report
- Submitting bug fixes
- Submitting new Fortran computational routines
- Submitting new Python computational routines
- Submitting fully wrapped computational routines
Getting the source code
------------------------------
The source code is available on GitHub:
https://github.com/NCAR/wrf-python
To checkout the code::
git clone https://github.com/NCAR/wrf-python
Git Flow
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This project follows the GitFlow Workflow, which you can read about here if it
is new to you:
https://leanpub.com/git-flow/read
When you first clone the repository, by default you will be on the 'develop'
branch, which is what you should use for your development.
Pull Requests
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In order to submit changes, you must use GitHub to issue a pull request.
Overview of WRF-Python Internals
----------------------------------
WRF-Python is a collection of diagnostic and interpolation routines for WRF-ARW
data. The API consists of a handful of functions

10
doc/source/index.rst

@ -13,6 +13,14 @@
university corporation for atmospheric research, university corporation for atmospheric research,
pynio, pyngl, interpolation pynio, pyngl, interpolation
.. .. image:: _static/images/nsf.png
.. :scale: 100%
.. :align: right
.. |
.. |
wrf-python wrf-python
=========== ===========
@ -57,6 +65,7 @@ Indices and tables
* :ref:`modindex` * :ref:`modindex`
* :ref:`search` * :ref:`search`
-------------------- --------------------
*The National Center for Atmospheric Research is sponsored by the National *The National Center for Atmospheric Research is sponsored by the National
@ -64,4 +73,3 @@ Science Foundation. Any opinions, findings and conclusions or recommendations
expressed in this material do not necessarily reflect the views of the expressed in this material do not necessarily reflect the views of the
National Science Foundation.* National Science Foundation.*

19
src/wrf/__init__.py

@ -2,19 +2,24 @@ from __future__ import (absolute_import, division, print_function)
import os import os
import pkg_resources import pkg_resources
# For gfortran+msvc combination, extra shared libraries may exist (stored by numpy.distutils) try:
if os.name == "nt": from . import api
try: from .api import *
except ImportError:
# For gfortran+msvc combination, extra shared libraries may exist
# (stored by numpy.distutils)
if os.name == "nt":
req = pkg_resources.Requirement.parse("wrf-python") req = pkg_resources.Requirement.parse("wrf-python")
extra_dll_dir = pkg_resources.resource_filename(req, extra_dll_dir = pkg_resources.resource_filename(req,
"wrf-python/.libs") "wrf-python/.libs")
if os.path.isdir(extra_dll_dir): if os.path.isdir(extra_dll_dir):
os.environ["PATH"] += os.pathsep + extra_dll_dir os.environ["PATH"] += os.pathsep + extra_dll_dir
except ImportError:
pass
from . import api from . import api
from .api import * from .api import *
else:
raise
__all__ = [] __all__ = []
__all__.extend(api.__all__) __all__.extend(api.__all__)

1
src/wrf/api.py

@ -110,4 +110,3 @@ __all__ += ["CoordPair"]
__all__ += ["to_xy_coords"] __all__ += ["to_xy_coords"]
__all__ += ["cache_item", "get_cached_item"] __all__ += ["cache_item", "get_cached_item"]
__all__ += ["__version__"] __all__ += ["__version__"]

5
src/wrf/cache.py

@ -161,8 +161,3 @@ def _get_cache():
_shrink_cache() _shrink_cache()
return getattr(_local_storage, "cache", None) return getattr(_local_storage, "cache", None)

27
src/wrf/computation.py

@ -15,6 +15,7 @@ from .metadecorators import (set_alg_metadata, set_uvmet_alg_metadata,
set_smooth_metdata) set_smooth_metdata)
from .interputils import get_xy from .interputils import get_xy
@set_interp_metadata("xy") @set_interp_metadata("xy")
def xy(field, pivot_point=None, angle=None, start_point=None, end_point=None, def xy(field, pivot_point=None, angle=None, start_point=None, end_point=None,
meta=True): meta=True):
@ -601,8 +602,8 @@ def uvmet(u, v, lat, lon, cen_long, cone, meta=True, units="m s-1"):
and *v*, but with rightmost dimensions south_north x and *v*, but with rightmost dimensions south_north x
west_east and the same leftmost dimensions as *u* and *v* west_east and the same leftmost dimensions as *u* and *v*
- multi-dimensional with one fewer dimensions as *u* and *v*, - multi-dimensional with one fewer dimensions as *u* and *v*,
with rightmost dimensions south_north x west_east and the same with rightmost dimensions south_north x west_east and the
leftmost dimensions as *u* and *v*, minus the same leftmost dimensions as *u* and *v*, minus the
third-from-the-right dimension of *u* and *v*. third-from-the-right dimension of *u* and *v*.
Note: Note:
@ -622,8 +623,8 @@ def uvmet(u, v, lat, lon, cen_long, cone, meta=True, units="m s-1"):
and *v*, but with rightmost dimensions south_north x and *v*, but with rightmost dimensions south_north x
west_east and the same leftmost dimensions as *u* and *v* west_east and the same leftmost dimensions as *u* and *v*
- multi-dimensional with one fewer dimensions as *u* and *v*, - multi-dimensional with one fewer dimensions as *u* and *v*,
with rightmost dimensions south_north x west_east and the same with rightmost dimensions south_north x west_east and the
leftmost dimensions as *u* and *v*, minus the same leftmost dimensions as *u* and *v*, minus the
third-from-the-right dimension of *u* and *v*. third-from-the-right dimension of *u* and *v*.
@ -870,10 +871,10 @@ def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow,
# Cape 2D output is not flipped in the vertical, so index from the # Cape 2D output is not flipped in the vertical, so index from the
# end # end
result[0,...,:,:] = cape_cin[0,...,-1,:,:] result[0, ..., :, :] = cape_cin[0, ..., -1, :, :]
result[1,...,:,:] = cape_cin[1,...,-1,:,:] result[1, ..., :, :] = cape_cin[1, ..., -1, :, :]
result[2,...,:,:] = cape_cin[1,...,-2,:,:] result[2, ..., :, :] = cape_cin[1, ..., -2, :, :]
result[3,...,:,:] = cape_cin[1,...,-3,:,:] result[3, ..., :, :] = cape_cin[1, ..., -3, :, :]
return ma.masked_values(result, missing) return ma.masked_values(result, missing)
@ -1201,7 +1202,6 @@ def ctt(pres_hpa, tkel, qv, qcld, height, terrain, qice=None,
return ma.masked_values(ctt, missing) return ma.masked_values(ctt, missing)
@set_alg_metadata(3, "pres", units="dBZ", @set_alg_metadata(3, "pres", units="dBZ",
description="radar reflectivity") description="radar reflectivity")
def dbz(pres, tkel, qv, qr, qs=None, qg=None, use_varint=False, def dbz(pres, tkel, qv, qr, qs=None, qg=None, use_varint=False,
@ -1374,9 +1374,9 @@ def srhel(u, v, height, terrain, top=3000.0, lats=None, meta=True):
""" """
# u, v get swapped in vertical # u, v get swapped in vertical
_u = np.ascontiguousarray(u[...,::-1,:,:]) _u = np.ascontiguousarray(u[..., ::-1, :, :])
_v = np.ascontiguousarray(v[...,::-1,:,:]) _v = np.ascontiguousarray(v[..., ::-1, :, :])
_height = np.ascontiguousarray(height[...,::-1,:,:]) _height = np.ascontiguousarray(height[..., ::-1, :, :])
if lats is None: if lats is None:
_lats = np.ones_like(terrain) _lats = np.ones_like(terrain)
@ -1955,6 +1955,3 @@ def pw(pres, tkel, qv, height, meta=True):
tv = _tv(tkel, qv) tv = _tv(tkel, qv)
return _pw(pres, tv, qv, height) return _pw(pres, tv, qv, height)

4
src/wrf/config.py

@ -9,6 +9,7 @@ from ._wrffortran import (fomp_enabled, fomp_set_num_threads,
_local_config = local() _local_config = local()
def _init_local(): def _init_local():
global _local_config global _local_config
@ -191,7 +192,8 @@ def set_cache_size(size):
@init_local() @init_local()
def get_cache_size(): def get_cache_size():
"""Return the maximum number of items that the threadlocal cache can retain. """Return the maximum number of items that the threadlocal cache can
retain.
Returns: Returns:

37
src/wrf/constants.py

@ -10,10 +10,12 @@ from ._wrffortran import wrf_constants, omp_constants
#: Indicates that all times should be used in a diagnostic routine. #: Indicates that all times should be used in a diagnostic routine.
ALL_TIMES = None ALL_TIMES = None
class Constants(object): class Constants(object):
pass pass
for key,val in viewitems(wrf_constants.__dict__):
for key, val in viewitems(wrf_constants.__dict__):
setattr(Constants, key.upper(), np.asscalar(val)) setattr(Constants, key.upper(), np.asscalar(val))
OMP_SCHED_STATIC = omp_constants.fomp_sched_static OMP_SCHED_STATIC = omp_constants.fomp_sched_static
@ -44,21 +46,22 @@ class ProjectionTypes(object):
MERCATOR = 3 MERCATOR = 3
LAT_LON = 6 LAT_LON = 6
# Create the default fill mapping based on type. # Create the default fill mapping based on type.
_DEFAULT_FILL_MAP = {None: Constants.DEFAULT_FILL, _DEFAULT_FILL_MAP = {None: Constants.DEFAULT_FILL,
np.dtype(np.bool_) : False, np.dtype(np.bool_): False,
np.dtype(np.intc) : Constants.DEFAULT_FILL_INT32, # Usually true np.dtype(np.intc): Constants.DEFAULT_FILL_INT32,
np.dtype(np.int8) : Constants.DEFAULT_FILL_INT8, np.dtype(np.int8): Constants.DEFAULT_FILL_INT8,
np.dtype(np.uint8) : 255, np.dtype(np.uint8): 255,
np.dtype(np.int16) : Constants.DEFAULT_FILL_INT16, np.dtype(np.int16): Constants.DEFAULT_FILL_INT16,
np.dtype(np.uint16) : 65535, np.dtype(np.uint16): 65535,
np.dtype(np.int32) : Constants.DEFAULT_FILL_INT32, np.dtype(np.int32): Constants.DEFAULT_FILL_INT32,
np.dtype(np.uint32) : 4294967295, np.dtype(np.uint32): 4294967295,
np.dtype(np.int64) : Constants.DEFAULT_FILL_INT64, np.dtype(np.int64): Constants.DEFAULT_FILL_INT64,
np.dtype(np.uint64) : 18446744073709551614, np.dtype(np.uint64): 18446744073709551614,
np.dtype(np.float_) : Constants.DEFAULT_FILL_DOUBLE, np.dtype(np.float_): Constants.DEFAULT_FILL_DOUBLE,
np.dtype(np.float32) : Constants.DEFAULT_FILL_FLOAT, np.dtype(np.float32): Constants.DEFAULT_FILL_FLOAT,
np.dtype(np.float64) : Constants.DEFAULT_FILL_DOUBLE np.dtype(np.float64): Constants.DEFAULT_FILL_DOUBLE
} }
if version_info >= (3, ): if version_info >= (3, ):
@ -76,9 +79,3 @@ else:
def default_fill(dtype=None): def default_fill(dtype=None):
dt = np.dtype(dtype) if dtype is not None else None dt = np.dtype(dtype) if dtype is not None else None
return _DEFAULT_FILL_MAP.get(dt, Constants.DEFAULT_FILL) return _DEFAULT_FILL_MAP.get(dt, Constants.DEFAULT_FILL)

31
src/wrf/coordpair.py

@ -35,13 +35,13 @@ def _binary_operator(operator):
""" """
if isinstance(other, CoordPair): if isinstance(other, CoordPair):
args = [ args = [None if getattr(self, attr) is None or
None if getattr(self, attr) is None or getattr(other, attr) is None getattr(other, attr) is None else
else getattr(getattr(self, attr), operator)(getattr(other, attr)) getattr(getattr(self, attr), operator)(getattr(other,
attr))
for attr in ("x", "y", "lat", "lon")] for attr in ("x", "y", "lat", "lon")]
else: else:
args = [ args = [None if getattr(self, attr) is None
None if getattr(self, attr) is None
else getattr(getattr(self, attr), operator)(other) else getattr(getattr(self, attr), operator)(other)
for attr in ("x", "y", "lat", "lon")] for attr in ("x", "y", "lat", "lon")]
@ -151,7 +151,6 @@ class CoordPair(object):
self.lat = lat self.lat = lat
self.lon = lon self.lon = lon
def __repr__(self): def __repr__(self):
args = [] args = []
if self.x is not None: if self.x is not None:
@ -166,11 +165,9 @@ class CoordPair(object):
return "{}({})".format(self.__class__.__name__, argstr) return "{}({})".format(self.__class__.__name__, argstr)
def __str__(self): def __str__(self):
return self.__repr__() return self.__repr__()
def xy_str(self, fmt="{:.4f}, {:.4f}"): def xy_str(self, fmt="{:.4f}, {:.4f}"):
"""Return a :obj:`str` for the (x,y) coordinate pair. """Return a :obj:`str` for the (x,y) coordinate pair.
@ -188,7 +185,6 @@ class CoordPair(object):
return fmt.format(self.x, self.y) return fmt.format(self.x, self.y)
def latlon_str(self, fmt="{:.4f}, {:.4f}"): def latlon_str(self, fmt="{:.4f}, {:.4f}"):
"""Return a :obj:`str` for the (latitude, longitude) coordinate pair. """Return a :obj:`str` for the (latitude, longitude) coordinate pair.
@ -206,7 +202,6 @@ class CoordPair(object):
return fmt.format(self.lat, self.lon) return fmt.format(self.lat, self.lon)
def __round__(self, ndigits=None): def __round__(self, ndigits=None):
"""Return a new :class:`CoordPair` object with all coordinate values """Return a new :class:`CoordPair` object with all coordinate values
rounded to the nearest integer. rounded to the nearest integer.
@ -226,23 +221,20 @@ class CoordPair(object):
return CoordPair(*args) return CoordPair(*args)
def __pow__(self, other, modulo=None): def __pow__(self, other, modulo=None):
if isinstance(other, CoordPair): if isinstance(other, CoordPair):
args = [ args = [None if getattr(self, attr) is None or
None if getattr(self, attr) is None or getattr(other, attr) is None getattr(other, attr) is None
else getattr(getattr(self, attr), "__pow__")(getattr(other, attr), else getattr(getattr(self, attr), "__pow__")(
modulo) getattr(other, attr), modulo)
for attr in ("x", "y", "lat", "lon")] for attr in ("x", "y", "lat", "lon")]
else: else:
args = [ args = [None if getattr(self, attr) is None
None if getattr(self, attr) is None
else getattr(getattr(self, attr), "__pow__")(other, modulo) else getattr(getattr(self, attr), "__pow__")(other, modulo)
for attr in ("x", "y", "lat", "lon")] for attr in ("x", "y", "lat", "lon")]
return CoordPair(*args) return CoordPair(*args)
def __rpow__(self, other): def __rpow__(self, other):
return self.__pow__(other) return self.__pow__(other)
@ -260,6 +252,3 @@ for operator in ("__neg__", "__pos__", "__abs__", "__invert__"):
for operator in ("__lt__", "__le__", "__eq__", "__ne__", "__gt__", "__ge__"): for operator in ("__lt__", "__le__", "__eq__", "__ne__", "__gt__", "__ge__"):
setattr(CoordPair, operator, _cmp_operator(operator)) setattr(CoordPair, operator, _cmp_operator(operator))

60
src/wrf/decorators.py

@ -15,6 +15,7 @@ from .constants import default_fill
if xarray_enabled(): if xarray_enabled():
from xarray import DataArray from xarray import DataArray
def convert_units(unit_type, alg_unit): def convert_units(unit_type, alg_unit):
"""A decorator that converts the units from the wrapped function's output. """A decorator that converts the units from the wrapped function's output.
@ -47,17 +48,6 @@ def convert_units(unit_type, alg_unit):
return func_wrapper return func_wrapper
#def _calc_out_dims(outvar, left_dims):
# """
#
# """
# #left_dims = [x for x in left_dims]
# #right_dims = [x for x in outvar.shape]
# #return left_dims + right_dims
#
# return left_dims + outvar.shape
def left_iteration(ref_var_expected_dims, def left_iteration(ref_var_expected_dims,
ref_var_right_ndims, ref_var_right_ndims,
insert_dims=None, insert_dims=None,
@ -171,16 +161,15 @@ def left_iteration(ref_var_expected_dims,
# the right (e.g. [1,1,:]) # the right (e.g. [1,1,:])
left_and_slice_idxs = left_idxs + (slice(None), ) left_and_slice_idxs = left_idxs + (slice(None), )
# Slice the args if applicable # Slice the args if applicable
new_args = [arg[left_and_slice_idxs] new_args = [arg[left_and_slice_idxs]
if i not in _ignore_args else arg if i not in _ignore_args else arg
for i,arg in enumerate(args)] for i, arg in enumerate(args)]
# Slice the kwargs if applicable # Slice the kwargs if applicable
new_kargs = {key:(val[left_and_slice_idxs] new_kargs = {key: (val[left_and_slice_idxs]
if key not in _ignore_kargs else val) if key not in _ignore_kargs else val)
for key,val in viewitems(kwargs)} for key, val in viewitems(kwargs)}
# Skip the possible empty/missing arrays for the join method # Skip the possible empty/missing arrays for the join method
skip_missing = False skip_missing = False
@ -210,7 +199,7 @@ def left_iteration(ref_var_expected_dims,
# Insert the output views if one hasn't been provided # Insert the output views if one hasn't been provided
if "outview" not in new_kargs: if "outview" not in new_kargs:
for outkey,output in viewitems(outd): for outkey, output in viewitems(outd):
outview = output[left_and_slice_idxs] outview = output[left_and_slice_idxs]
new_kargs[outkey] = outview new_kargs[outkey] = outview
@ -222,14 +211,11 @@ def left_iteration(ref_var_expected_dims,
outview.__array_interface__["data"][0]): outview.__array_interface__["data"][0]):
raise RuntimeError("output array was copied") raise RuntimeError("output array was copied")
if len(outd) == 1: if len(outd) == 1:
output = next(iter(viewvalues(outd))) output = next(iter(viewvalues(outd)))
else: else:
output = tuple(arr for arr in viewvalues(outd)) output = tuple(arr for arr in viewvalues(outd))
if cast_output: if cast_output:
if isinstance(output, np.ndarray): if isinstance(output, np.ndarray):
output = output.astype(ref_var_dtype) output = output.astype(ref_var_dtype)
@ -262,8 +248,8 @@ def cast_type(ref_idx=0, arg_idxs=None, karg_names=None,
positional arguments to be used as the reference variable for positional arguments to be used as the reference variable for
determining the :class:`numpy.dtype` to return. Default is 0. determining the :class:`numpy.dtype` to return. Default is 0.
arg_idxs (sequence of :obj:`int`, optional): A sequence of indexes in the arg_idxs (sequence of :obj:`int`, optional): A sequence of indexes in
wrapped function's positional arguments that indicate which the wrapped function's positional arguments that indicate which
arguments to cast. Must be specified if *karg_names* is None. arguments to cast. Must be specified if *karg_names* is None.
Default is None. Default is None.
@ -272,8 +258,8 @@ def cast_type(ref_idx=0, arg_idxs=None, karg_names=None,
arguments to cast. Must be specified if *arg_idxs* is None. arguments to cast. Must be specified if *arg_idxs* is None.
Default is None. Default is None.
alg_dtype (:class:`numpy.dtype` or :obj:`str`): The numpy data type used alg_dtype (:class:`numpy.dtype` or :obj:`str`): The numpy data type
in the wrapped function. used in the wrapped function.
outviews (:obj:`str` or a sequence): A single key or sequence of keys outviews (:obj:`str` or a sequence): A single key or sequence of keys
that indicate the wrapped function's keyword argument to use that indicate the wrapped function's keyword argument to use
@ -300,16 +286,15 @@ def cast_type(ref_idx=0, arg_idxs=None, karg_names=None,
if _outview is not None: if _outview is not None:
has_outview = True has_outview = True
orig_type = args[ref_idx].dtype orig_type = args[ref_idx].dtype
new_args = [arg.astype(alg_dtype) new_args = [arg.astype(alg_dtype)
if i in _arg_idxs else arg if i in _arg_idxs else arg
for i,arg in enumerate(args)] for i, arg in enumerate(args)]
new_kargs = {key:(val.astype(alg_dtype) new_kargs = {key: (val.astype(alg_dtype)
if key in _karg_names else val) if key in _karg_names else val)
for key,val in viewitems(kwargs)} for key, val in viewitems(kwargs)}
result = wrapped(*new_args, **new_kargs) result = wrapped(*new_args, **new_kargs)
@ -401,8 +386,8 @@ def extract_and_transpose(do_transpose=True, outviews="outview"):
new_args = [_extract_and_transpose(arg, do_transpose) for arg in args] new_args = [_extract_and_transpose(arg, do_transpose) for arg in args]
new_kargs = {key:_extract_and_transpose(val, do_transpose) new_kargs = {key: _extract_and_transpose(val, do_transpose)
for key,val in viewitems(kwargs)} for key, val in viewitems(kwargs)}
result = wrapped(*new_args, **new_kargs) result = wrapped(*new_args, **new_kargs)
@ -481,7 +466,7 @@ def check_args(refvaridx, refvarndim, rightdims, stagger=None,
else: else:
_stagger = stagger _stagger = stagger
for i,ndim in enumerate(rightdims): for i, ndim in enumerate(rightdims):
if ndim is None: if ndim is None:
continue continue
@ -498,7 +483,8 @@ def check_args(refvaridx, refvarndim, rightdims, stagger=None,
# Check that the number of dims is correct # Check that the number of dims is correct
if (var.ndim - extra_dims != right_var_ndims): if (var.ndim - extra_dims != right_var_ndims):
raise ValueError("invalid number of dimensions for argument " raise ValueError("invalid number of dimensions for argument "
"{} (got {}, expected {}).".format(i, "{} (got {}, expected {}).".format(
i,
var.ndim, var.ndim,
right_var_ndims + extra_dims)) right_var_ndims + extra_dims))
@ -517,19 +503,11 @@ def check_args(refvaridx, refvarndim, rightdims, stagger=None,
ref_right_sizes[-right_var_ndims:]): ref_right_sizes[-right_var_ndims:]):
raise ValueError("invalid shape for argument " raise ValueError("invalid shape for argument "
"{} (got {}, expected {})".format(i, "{} (got {}, expected {})".format(
i,
var.shape[-right_var_ndims:], var.shape[-right_var_ndims:],
ref_right_sizes[-right_var_ndims:])) ref_right_sizes[-right_var_ndims:]))
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return func_wrapper return func_wrapper

2
src/wrf/destag.py

@ -60,5 +60,3 @@ def destagger(var, stagger_dim, meta=False):
result = .5*(var[tuple(dim_ranges_1)] + var[tuple(dim_ranges_2)]) result = .5*(var[tuple(dim_ranges_1)] + var[tuple(dim_ranges_2)])
return result return result

185
src/wrf/extension.py

@ -5,20 +5,21 @@ import numpy as np
from .constants import Constants, default_fill from .constants import Constants, default_fill
from wrf._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, from wrf._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d,
dcomputeseaprs, dfilter2d, dcomputerh, dcomputeuvmet, dcomputeseaprs, dfilter2d, dcomputerh,
dcomputetd, dcapecalc2d, dcapecalc3d, dcloudfrac2, dcomputeuvmet, dcomputetd, dcapecalc2d,
wrfcttcalc, calcdbz, dcalrelhl, dcalcuh, dcomputepv, dcapecalc3d, dcloudfrac2, wrfcttcalc, calcdbz,
dcomputeabsvort, dlltoij, dijtoll, deqthecalc, dcalrelhl, dcalcuh, dcomputepv, dcomputeabsvort,
omgcalc, virtual_temp, wetbulbcalc, dcomputepw, dlltoij, dijtoll, deqthecalc, omgcalc,
virtual_temp, wetbulbcalc, dcomputepw,
wrf_monotonic, wrf_vintrp, dcomputewspd, wrf_monotonic, wrf_vintrp, dcomputewspd,
dcomputewdir, dinterp3dz_2dlev, dcomputewdir, dinterp3dz_2dlev,
fomp_set_num_threads, fomp_get_num_threads, fomp_set_num_threads, fomp_get_num_threads,
fomp_get_max_threads, fomp_get_thread_num, fomp_get_max_threads, fomp_get_thread_num,
fomp_get_num_procs, fomp_in_parallel, fomp_get_num_procs, fomp_in_parallel,
fomp_set_dynamic, fomp_get_dynamic, fomp_set_nested, fomp_set_dynamic, fomp_get_dynamic,
fomp_get_nested, fomp_set_schedule, fomp_set_nested, fomp_get_nested,
fomp_get_schedule, fomp_get_thread_limit, fomp_set_schedule, fomp_get_schedule,
fomp_set_max_active_levels, fomp_get_thread_limit, fomp_set_max_active_levels,
fomp_get_max_active_levels, fomp_get_level, fomp_get_max_active_levels, fomp_get_level,
fomp_get_ancestor_thread_num, fomp_get_team_size, fomp_get_ancestor_thread_num, fomp_get_team_size,
fomp_get_active_level, fomp_in_final, fomp_get_active_level, fomp_in_final,
@ -37,6 +38,7 @@ from .specialdec import (uvmet_left_iter, cape_left_iter,
cloudfrac_left_iter, check_cape_args, cloudfrac_left_iter, check_cape_args,
interplevel_left_iter, check_interplevel_args) interplevel_left_iter, check_interplevel_args)
class DiagnosticError(Exception): class DiagnosticError(Exception):
"""Raised when an error occurs in a diagnostic routine.""" """Raised when an error occurs in a diagnostic routine."""
def __init__(self, message=None): def __init__(self, message=None):
@ -66,6 +68,7 @@ class DiagnosticError(Exception):
""" """
raise self.__class__(message) raise self.__class__(message)
# The routines below are thin wrappers around the Fortran functions. These # The routines below are thin wrappers around the Fortran functions. These
# are not meant to be called by end users. Use the public API instead for # are not meant to be called by end users. Use the public API instead for
# that purpose. # that purpose.
@ -73,10 +76,9 @@ class DiagnosticError(Exception):
# IMPORTANT! Unless otherwise noted, all variables used in the routines # IMPORTANT! Unless otherwise noted, all variables used in the routines
# below assume that Fortran-ordered views are being used. This allows # below assume that Fortran-ordered views are being used. This allows
# f2py to pass the array pointers directly to the Fortran routine. # f2py to pass the array pointers directly to the Fortran routine.
@check_interplevel_args(is2dlev=False) @check_interplevel_args(is2dlev=False)
@interplevel_left_iter(is2dlev=False) @interplevel_left_iter(is2dlev=False)
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _interpz3d(field3d, z, desiredloc, missingval, outview=None): def _interpz3d(field3d, z, desiredloc, missingval, outview=None):
"""Wrapper for dinterp3dz. """Wrapper for dinterp3dz.
@ -98,7 +100,7 @@ def _interpz3d(field3d, z, desiredloc, missingval, outview=None):
@check_interplevel_args(is2dlev=True) @check_interplevel_args(is2dlev=True)
@interplevel_left_iter(is2dlev=True) @interplevel_left_iter(is2dlev=True)
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _interpz3d_lev2d(field3d, z, lev2d, missingval, outview=None): def _interpz3d_lev2d(field3d, z, lev2d, missingval, outview=None):
"""Wrapper for dinterp3dz. """Wrapper for dinterp3dz.
@ -117,10 +119,10 @@ def _interpz3d_lev2d(field3d, z, lev2d, missingval, outview=None):
return result return result
@check_args(0, 3, (3,)) @check_args(0, 3, (3, ))
@left_iteration(3, combine_dims([(0,-3),(1,-2)]), ref_var_idx=0, @left_iteration(3, combine_dims([(0, -3), (1, -2)]), ref_var_idx=0,
ignore_args=(1,)) ignore_args=(1, ))
@cast_type(arg_idxs=(0,1)) @cast_type(arg_idxs=(0, 1))
@extract_and_transpose() @extract_and_transpose()
def _interp2dxy(field3d, xy, outview=None): def _interp2dxy(field3d, xy, outview=None):
"""Wrapper for dinterp2dxy. """Wrapper for dinterp2dxy.
@ -138,9 +140,9 @@ def _interp2dxy(field3d, xy, outview=None):
return result return result
@check_args(0, 1, (1,1,None,None)) @check_args(0, 1, (1, 1, None, None))
@left_iteration(1, combine_dims([(2,0)]), ref_var_idx=0, ignore_args=(2,3)) @left_iteration(1, combine_dims([(2, 0)]), ref_var_idx=0, ignore_args=(2, 3))
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _interp1d(v_in, z_in, z_out, missingval, outview=None): def _interp1d(v_in, z_in, z_out, missingval, outview=None):
"""Wrapper for dinterp1d. """Wrapper for dinterp1d.
@ -160,9 +162,9 @@ def _interp1d(v_in, z_in, z_out, missingval, outview=None):
return result return result
@left_iteration(3, combine_dims([(3,0), (1,0)]), @left_iteration(3, combine_dims([(3, 0), (1, 0)]),
ref_var_idx=0, ignore_args=(1,3,4)) ref_var_idx=0, ignore_args=(1, 3, 4))
@cast_type(arg_idxs=(0,)) @cast_type(arg_idxs=(0, ))
@extract_and_transpose(do_transpose=False) @extract_and_transpose(do_transpose=False)
def _vertcross(field3d, xy, var2dz, z_var2d, missingval, outview=None): def _vertcross(field3d, xy, var2dz, z_var2d, missingval, outview=None):
"""Return the vertical cross section. """Return the vertical cross section.
@ -180,14 +182,14 @@ def _vertcross(field3d, xy, var2dz, z_var2d, missingval, outview=None):
var2dtmp = _interp2dxy(field3d, xy) var2dtmp = _interp2dxy(field3d, xy)
for i in py3range(xy.shape[0]): for i in py3range(xy.shape[0]):
outview[:,i] = _interp1d(var2dtmp[:,i], var2dz[:,i], z_var2d, outview[:, i] = _interp1d(var2dtmp[:, i], var2dz[:, i], z_var2d,
missingval) missingval)
return outview return outview
@left_iteration(2, combine_dims([(1,0)]), ref_var_idx=0, ignore_args=(1,)) @left_iteration(2, combine_dims([(1, 0)]), ref_var_idx=0, ignore_args=(1, ))
@cast_type(arg_idxs=(0,)) @cast_type(arg_idxs=(0, ))
@extract_and_transpose(do_transpose=False) @extract_and_transpose(do_transpose=False)
def _interpline(field2d, xy, outview=None): def _interpline(field2d, xy, outview=None):
"""Return the two-dimensional field interpolated to a line. """Return the two-dimensional field interpolated to a line.
@ -204,7 +206,7 @@ def _interpline(field2d, xy, outview=None):
tmp_shape = (1,) + field2d.shape tmp_shape = (1,) + field2d.shape
var2dtmp = np.empty(tmp_shape, field2d.dtype) var2dtmp = np.empty(tmp_shape, field2d.dtype)
var2dtmp[0,:,:] = field2d[:,:] var2dtmp[0, :, :] = field2d[:, :]
var1dtmp = _interp2dxy(var2dtmp, xy) var1dtmp = _interp2dxy(var2dtmp, xy)
@ -213,9 +215,9 @@ def _interpline(field2d, xy, outview=None):
return outview return outview
@check_args(0, 3, (3,3,3,3)) @check_args(0, 3, (3, 3, 3, 3))
@left_iteration(3, 2, ref_var_idx=0) @left_iteration(3, 2, ref_var_idx=0)
@cast_type(arg_idxs=(0,1,2,3)) @cast_type(arg_idxs=(0, 1, 2, 3))
@extract_and_transpose() @extract_and_transpose()
def _slp(z, t, p, q, outview=None): def _slp(z, t, p, q, outview=None):
"""Wrapper for dcomputeseaprs. """Wrapper for dcomputeseaprs.
@ -250,9 +252,9 @@ def _slp(z, t, p, q, outview=None):
return result return result
@check_args(0, 3, (3,3)) @check_args(0, 3, (3, 3))
@left_iteration(3, 3, ref_var_idx=0) @left_iteration(3, 3, ref_var_idx=0)
@cast_type(arg_idxs=(0,1)) @cast_type(arg_idxs=(0, 1))
@extract_and_transpose() @extract_and_transpose()
def _tk(pressure, theta, outview=None): def _tk(pressure, theta, outview=None):
"""Wrapper for dcomputetk. """Wrapper for dcomputetk.
@ -272,9 +274,9 @@ def _tk(pressure, theta, outview=None):
return result return result
@check_args(0, 2, (2,2)) @check_args(0, 2, (2, 2))
@left_iteration(2, 2, ref_var_idx=0) @left_iteration(2, 2, ref_var_idx=0)
@cast_type(arg_idxs=(0,1)) @cast_type(arg_idxs=(0, 1))
@extract_and_transpose() @extract_and_transpose()
def _td(pressure, qv_in, outview=None): def _td(pressure, qv_in, outview=None):
"""Wrapper for dcomputetd. """Wrapper for dcomputetd.
@ -294,9 +296,9 @@ def _td(pressure, qv_in, outview=None):
return result return result
@check_args(0, 2, (2,2,2)) @check_args(0, 2, (2, 2, 2))
@left_iteration(2, 2, ref_var_idx=0) @left_iteration(2, 2, ref_var_idx=0)
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _rh(qv, q, t, outview=None): def _rh(qv, q, t, outview=None):
"""Wrapper for dcomputerh. """Wrapper for dcomputerh.
@ -318,12 +320,12 @@ def _rh(qv, q, t, outview=None):
# Note: combining the -3 and -2 dimensions from u, then the -1 dimension # Note: combining the -3 and -2 dimensions from u, then the -1 dimension
# from v # from v
@check_args(0, 3, (3,3,2,2,2,2), stagger=(-1,-2,-1,-2,None,None), @check_args(0, 3, (3, 3, 2, 2, 2, 2), stagger=(-1, -2, -1, -2, None, None),
refstagdim=-1) refstagdim=-1)
@left_iteration(3, combine_dims([(0, (-3,-2)), @left_iteration(3, combine_dims([(0, (-3, -2)),
(1, (-1,))]), (1, (-1, ))]),
ref_var_idx=0, ignore_args=(6,7)) ref_var_idx=0, ignore_args=(6, 7))
@cast_type(arg_idxs=(0,1,2,3,4,5)) @cast_type(arg_idxs=(0, 1, 2, 3, 4, 5))
@extract_and_transpose() @extract_and_transpose()
def _avo(u, v, msfu, msfv, msfm, cor, dx, dy, outview=None): def _avo(u, v, msfu, msfv, msfm, cor, dx, dy, outview=None):
"""Wrapper for dcomputeabsvort. """Wrapper for dcomputeabsvort.
@ -332,7 +334,7 @@ def _avo(u, v, msfu, msfv, msfm, cor, dx, dy, outview=None):
""" """
if outview is None: if outview is None:
outshape = (v.shape[0],) + u.shape[1:] outshape = (v.shape[0], ) + u.shape[1:]
outview = np.empty(outshape, np.float64, order="F") outview = np.empty(outshape, np.float64, order="F")
result = dcomputeabsvort(outview, result = dcomputeabsvort(outview,
@ -348,11 +350,10 @@ def _avo(u, v, msfu, msfv, msfm, cor, dx, dy, outview=None):
return result return result
@check_args(0, 3, (3,3,3,3,2,2,2,2), stagger=(-1,-2,None,None,-1,-2,None, @check_args(0, 3, (3, 3, 3, 3, 2, 2, 2, 2),
None), stagger=(-1, -2, None, None, -1, -2, None, None), refstagdim=-1)
refstagdim=-1) @left_iteration(3, 3, ref_var_idx=2, ignore_args=(8, 9))
@left_iteration(3, 3, ref_var_idx=2, ignore_args=(8,9)) @cast_type(arg_idxs=(0, 1, 2, 3, 4, 5, 6, 7))
@cast_type(arg_idxs=(0,1,2,3,4,5,6,7))
@extract_and_transpose() @extract_and_transpose()
def _pvo(u, v, theta, prs, msfu, msfv, msfm, cor, dx, dy, outview=None): def _pvo(u, v, theta, prs, msfu, msfv, msfm, cor, dx, dy, outview=None):
"""Wrapper for dcomputepv. """Wrapper for dcomputepv.
@ -378,9 +379,9 @@ def _pvo(u, v, theta, prs, msfu, msfv, msfm, cor, dx, dy, outview=None):
return result return result
@check_args(0, 3, (3,3,3)) @check_args(0, 3, (3, 3, 3))
@left_iteration(3, 3, ref_var_idx=0) @left_iteration(3, 3, ref_var_idx=0)
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _eth(qv, tk, p, outview=None): def _eth(qv, tk, p, outview=None):
"""Wrapper for deqthecalc. """Wrapper for deqthecalc.
@ -400,11 +401,13 @@ def _eth(qv, tk, p, outview=None):
@uvmet_left_iter() @uvmet_left_iter()
@cast_type(arg_idxs=(0,1,2,3)) @cast_type(arg_idxs=(0, 1, 2, 3))
@extract_and_transpose() @extract_and_transpose()
def _uvmet(u, v, lat, lon, cen_long, cone, isstag=0, has_missing=False, def _uvmet(u, v, lat, lon, cen_long, cone, isstag=0, has_missing=False,
umissing=default_fill(np.float64), vmissing=default_fill(np.float64), umissing=default_fill(np.float64),
uvmetmissing=default_fill(np.float64), outview=None): vmissing=default_fill(np.float64),
uvmetmissing=default_fill(np.float64),
outview=None):
"""Wrapper for dcomputeuvmet. """Wrapper for dcomputeuvmet.
Located in wrf_user.f90. Located in wrf_user.f90.
@ -437,9 +440,9 @@ def _uvmet(u, v, lat, lon, cen_long, cone, isstag=0, has_missing=False,
return result return result
@check_args(0, 3, (3,3,3,3)) @check_args(0, 3, (3, 3, 3, 3))
@left_iteration(3, 3, ref_var_idx=0) @left_iteration(3, 3, ref_var_idx=0)
@cast_type(arg_idxs=(0,1,2,3)) @cast_type(arg_idxs=(0, 1, 2, 3))
@extract_and_transpose() @extract_and_transpose()
def _omega(qv, tk, w, p, outview=None): def _omega(qv, tk, w, p, outview=None):
"""Wrapper for omgcalc. """Wrapper for omgcalc.
@ -459,9 +462,9 @@ def _omega(qv, tk, w, p, outview=None):
return result return result
@check_args(0, 3, (3,3)) @check_args(0, 3, (3, 3))
@left_iteration(3, 3, ref_var_idx=0) @left_iteration(3, 3, ref_var_idx=0)
@cast_type(arg_idxs=(0,1)) @cast_type(arg_idxs=(0, 1))
@extract_and_transpose() @extract_and_transpose()
def _tv(tk, qv, outview=None): def _tv(tk, qv, outview=None):
"""Wrapper for virtual_temp. """Wrapper for virtual_temp.
@ -479,9 +482,9 @@ def _tv(tk, qv, outview=None):
return result return result
@check_args(0, 3, (3,3,3)) @check_args(0, 3, (3, 3, 3))
@left_iteration(3, 3, ref_var_idx=0, ignore_args=(3,)) @left_iteration(3, 3, ref_var_idx=0, ignore_args=(3,))
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _wetbulb(p, tk, qv, psafile=psafilepath(), outview=None): def _wetbulb(p, tk, qv, psafile=psafilepath(), outview=None):
"""Wrapper for wetbulbcalc. """Wrapper for wetbulbcalc.
@ -509,9 +512,9 @@ def _wetbulb(p, tk, qv, psafile=psafilepath(), outview=None):
return result return result
@check_args(0, 3, (3,3,3,2,2)) @check_args(0, 3, (3, 3, 3, 2, 2))
@left_iteration(3, 2, ref_var_idx=0, ignore_args=(5,)) @left_iteration(3, 2, ref_var_idx=0, ignore_args=(5, ))
@cast_type(arg_idxs=(0,1,2,3,4)) @cast_type(arg_idxs=(0, 1, 2, 3, 4))
@extract_and_transpose() @extract_and_transpose()
def _srhel(u, v, z, ter, lats, top, outview=None): def _srhel(u, v, z, ter, lats, top, outview=None):
"""Wrapper for dcalrelhl. """Wrapper for dcalrelhl.
@ -533,9 +536,9 @@ def _srhel(u, v, z, ter, lats, top, outview=None):
return result return result
@check_args(2, 3, (3,2,3,3,3), stagger=(-3,None,None,None,-3)) @check_args(2, 3, (3, 2, 3, 3, 3), stagger=(-3, None, None, None, -3))
@left_iteration(3, 2, ref_var_idx=2, ignore_args=(5,6,7,8)) @left_iteration(3, 2, ref_var_idx=2, ignore_args=(5, 6, 7, 8))
@cast_type(arg_idxs=(0,1,2,3,4)) @cast_type(arg_idxs=(0, 1, 2, 3, 4))
@extract_and_transpose() @extract_and_transpose()
def _udhel(zstag, mapfct, u, v, wstag, dx, dy, bottom, top, outview=None): def _udhel(zstag, mapfct, u, v, wstag, dx, dy, bottom, top, outview=None):
"""Wrapper for dcalcuh. """Wrapper for dcalcuh.
@ -567,9 +570,9 @@ def _udhel(zstag, mapfct, u, v, wstag, dx, dy, bottom, top, outview=None):
return result return result
@check_args(0, 3, (3,3,3,3), stagger=(None, None, None, -3)) @check_args(0, 3, (3, 3, 3, 3), stagger=(None, None, None, -3))
@left_iteration(3, 2, ref_var_idx=0) @left_iteration(3, 2, ref_var_idx=0)
@cast_type(arg_idxs=(0,1,2,3)) @cast_type(arg_idxs=(0, 1, 2, 3))
@extract_and_transpose() @extract_and_transpose()
def _pw(p, tv, qv, ht, outview=None): def _pw(p, tv, qv, ht, outview=None):
"""Wrapper for dcomputepw. """Wrapper for dcomputepw.
@ -589,9 +592,9 @@ def _pw(p, tv, qv, ht, outview=None):
return result return result
@check_args(0, 3, (3,3,3,3,3,3)) @check_args(0, 3, (3, 3, 3, 3, 3, 3))
@left_iteration(3, 3, ref_var_idx=0, ignore_args=(6,7,8)) @left_iteration(3, 3, ref_var_idx=0, ignore_args=(6, 7, 8))
@cast_type(arg_idxs=(0,1,2,3,4,5)) @cast_type(arg_idxs=(0, 1, 2, 3, 4, 5))
@extract_and_transpose() @extract_and_transpose()
def _dbz(p, tk, qv, qr, qs, qg, sn0, ivarint, iliqskin, outview=None): def _dbz(p, tk, qv, qr, qs, qg, sn0, ivarint, iliqskin, outview=None):
"""Wrapper for calcdbz. """Wrapper for calcdbz.
@ -618,7 +621,7 @@ def _dbz(p, tk, qv, qr, qs, qg, sn0, ivarint, iliqskin, outview=None):
@check_cape_args() @check_cape_args()
@cape_left_iter() @cape_left_iter()
@cast_type(arg_idxs=(0,1,2,3,4,5), outviews=("capeview", "cinview")) @cast_type(arg_idxs=(0, 1, 2, 3, 4, 5), outviews=("capeview", "cinview"))
@extract_and_transpose(outviews=("capeview", "cinview")) @extract_and_transpose(outviews=("capeview", "cinview"))
def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow,
psafile=psafilepath(), capeview=None, cinview=None): psafile=psafilepath(), capeview=None, cinview=None):
@ -674,12 +677,14 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow,
return result return result
@check_args(0, 3, (3,3))
@check_args(0, 3, (3, 3))
@cloudfrac_left_iter() @cloudfrac_left_iter()
@cast_type(arg_idxs=(0, 1), outviews=("lowview", "midview", "highview")) @cast_type(arg_idxs=(0, 1), outviews=("lowview", "midview", "highview"))
@extract_and_transpose(outviews=("lowview", "midview", "highview")) @extract_and_transpose(outviews=("lowview", "midview", "highview"))
def _cloudfrac(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, def _cloudfrac(vert, rh, vert_inc_w_height, low_thresh, mid_thresh,
high_thresh, missing, lowview=None, midview=None, highview=None): high_thresh, missing, lowview=None, midview=None,
highview=None):
"""Wrapper for dcloudfrac2. """Wrapper for dcloudfrac2.
Located in wrf_cloud_fracf.f90. Located in wrf_cloud_fracf.f90.
@ -789,9 +794,9 @@ def _xytoll(map_proj, truelat1, truelat2, stdlon, lat1, lon1,
return result return result
@check_args(0, 3, (3,3,3,3,3,3,2)) @check_args(0, 3, (3, 3, 3, 3, 3, 3, 2))
@left_iteration(3, 2, ref_var_idx=0, ignore_args=(7,8,9,10)) @left_iteration(3, 2, ref_var_idx=0, ignore_args=(7, 8, 9, 10))
@cast_type(arg_idxs=(0,1,2,3,4,5,6)) @cast_type(arg_idxs=(0, 1, 2, 3, 4, 5, 6))
@extract_and_transpose() @extract_and_transpose()
def _ctt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci, fill_nocloud, def _ctt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci, fill_nocloud,
missing, opt_thresh, outview=None): missing, opt_thresh, outview=None):
@ -822,9 +827,9 @@ def _ctt(p_hpa, tk, qice, qcld, qv, ght, ter, haveqci, fill_nocloud,
return result return result
@check_args(0, 2, (2,)) @check_args(0, 2, (2, ))
@left_iteration(2, 2, ref_var_idx=0, ignore_args=(1,2)) @left_iteration(2, 2, ref_var_idx=0, ignore_args=(1, 2))
@cast_type(arg_idxs=(0,)) @cast_type(arg_idxs=(0, ))
@extract_and_transpose() @extract_and_transpose()
def _smooth2d(field, passes, cenweight, outview=None): def _smooth2d(field, passes, cenweight, outview=None):
"""Wrapper for dfilter2d. """Wrapper for dfilter2d.
@ -856,9 +861,9 @@ def _smooth2d(field, passes, cenweight, outview=None):
return outview return outview
@check_args(0, 3, (3,3,2)) @check_args(0, 3, (3, 3, 2))
@left_iteration(3, 3, ref_var_idx=0, ignore_args=(3,4,5)) @left_iteration(3, 3, ref_var_idx=0, ignore_args=(3, 4, 5))
@cast_type(arg_idxs=(0,1,2)) @cast_type(arg_idxs=(0, 1, 2))
@extract_and_transpose() @extract_and_transpose()
def _monotonic(var, lvprs, coriolis, idir, delta, icorsw, outview=None): def _monotonic(var, lvprs, coriolis, idir, delta, icorsw, outview=None):
"""Wrapper for wrf_monotonic. """Wrapper for wrf_monotonic.
@ -885,11 +890,11 @@ def _monotonic(var, lvprs, coriolis, idir, delta, icorsw, outview=None):
# Output shape is interp_levels.shape + field.shape[-2:] # Output shape is interp_levels.shape + field.shape[-2:]
@check_args(0, 3, (3,3,3,3,3,2,2,2,3)) @check_args(0, 3, (3, 3, 3, 3, 3, 2, 2, 2, 3))
@left_iteration(3, combine_dims([(9, (-1,)), @left_iteration(3, combine_dims([(9, (-1, )),
(0, (-2,-1))]), (0, (-2, -1))]),
ref_var_idx=0, ignore_args=(9,10,11,12,13,14)) ref_var_idx=0, ignore_args=(9, 10, 11, 12, 13, 14))
@cast_type(arg_idxs=(0,1,2,3,4,5,6,7,8,9)) @cast_type(arg_idxs=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
@extract_and_transpose() @extract_and_transpose()
def _vintrp(field, pres, tk, qvp, ght, terrain, sfp, smsfp, def _vintrp(field, pres, tk, qvp, ght, terrain, sfp, smsfp,
vcarray, interp_levels, icase, extrap, vcor, logp, vcarray, interp_levels, icase, extrap, vcor, logp,
@ -933,9 +938,10 @@ def _vintrp(field, pres, tk, qvp, ght, terrain, sfp, smsfp,
return result return result
@check_args(0, 2, (2,2))
@check_args(0, 2, (2, 2))
@left_iteration(2, 2, ref_var_idx=0) @left_iteration(2, 2, ref_var_idx=0)
@cast_type(arg_idxs=(0,1)) @cast_type(arg_idxs=(0, 1))
@extract_and_transpose() @extract_and_transpose()
def _wspd(u, v, outview=None): def _wspd(u, v, outview=None):
"""Wrapper for dcomputewspd. """Wrapper for dcomputewspd.
@ -953,9 +959,9 @@ def _wspd(u, v, outview=None):
return result return result
@check_args(0, 2, (2,2)) @check_args(0, 2, (2, 2))
@left_iteration(2, 2, ref_var_idx=0) @left_iteration(2, 2, ref_var_idx=0)
@cast_type(arg_idxs=(0,1)) @cast_type(arg_idxs=(0, 1))
@extract_and_transpose() @extract_and_transpose()
def _wdir(u, v, outview=None): def _wdir(u, v, outview=None):
"""Wrapper for dcomputewdir. """Wrapper for dcomputewdir.
@ -1651,6 +1657,3 @@ def omp_get_wtick():
""" """
return fomp_get_wtick() return fomp_get_wtick()

27
src/wrf/g_cape.py

@ -9,6 +9,7 @@ from .constants import default_fill, Constants, ConversionFactors
from .util import extract_vars from .util import extract_vars
from .metadecorators import set_cape_metadata from .metadecorators import set_cape_metadata
@set_cape_metadata(is2d=True) @set_cape_metadata(is2d=True)
def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
meta=True, _key=None, missing=default_fill(np.float64)): meta=True, _key=None, missing=default_fill(np.float64)):
@ -77,7 +78,7 @@ def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
varnames = ("T", "P", "PB", "QVAPOR", "PH","PHB", "HGT", "PSFC") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB", "HGT", "PSFC")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -118,10 +119,10 @@ def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
# Cape 2D output is not flipped in the vertical, so index from the # Cape 2D output is not flipped in the vertical, so index from the
# end # end
result[0,...,:,:] = cape_cin[0,...,-1,:,:] result[0, ..., :, :] = cape_cin[0, ..., -1, :, :]
result[1,...,:,:] = cape_cin[1,...,-1,:,:] result[1, ..., :, :] = cape_cin[1, ..., -1, :, :]
result[2,...,:,:] = cape_cin[1,...,-2,:,:] result[2, ..., :, :] = cape_cin[1, ..., -2, :, :]
result[3,...,:,:] = cape_cin[1,...,-3,:,:] result[3, ..., :, :] = cape_cin[1, ..., -3, :, :]
return ma.masked_values(result, missing) return ma.masked_values(result, missing)
@ -221,7 +222,6 @@ def get_3dcape(wrfin, timeidx=0, method="cat",
cape_cin = _cape(p_hpa, tk, qv, z, ter, psfc_hpa, missing, i3dflag, cape_cin = _cape(p_hpa, tk, qv, z, ter, psfc_hpa, missing, i3dflag,
ter_follow) ter_follow)
return ma.masked_values(cape_cin, missing) return ma.masked_values(cape_cin, missing)
@ -284,7 +284,7 @@ def get_cape2d_only(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
result = get_2dcape(wrfin, timeidx, method, squeeze, cache, result = get_2dcape(wrfin, timeidx, method, squeeze, cache,
meta, _key, missing)[0,:] meta, _key, missing)[0, :]
if meta: if meta:
result.attrs["description"] = "mcape" result.attrs["description"] = "mcape"
@ -351,7 +351,7 @@ def get_cin2d_only(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
result = get_2dcape(wrfin, timeidx, method, squeeze, cache, result = get_2dcape(wrfin, timeidx, method, squeeze, cache,
meta, _key, missing)[1,:] meta, _key, missing)[1, :]
if meta: if meta:
result.attrs["description"] = "mcin" result.attrs["description"] = "mcin"
@ -418,7 +418,7 @@ def get_lcl(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
result = get_2dcape(wrfin, timeidx, method, squeeze, cache, result = get_2dcape(wrfin, timeidx, method, squeeze, cache,
meta, _key, missing)[2,:] meta, _key, missing)[2, :]
if meta: if meta:
result.attrs["description"] = "lcl" result.attrs["description"] = "lcl"
@ -485,7 +485,7 @@ def get_lfc(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
result = get_2dcape(wrfin, timeidx, method, squeeze, cache, result = get_2dcape(wrfin, timeidx, method, squeeze, cache,
meta, _key, missing)[3,:] meta, _key, missing)[3, :]
if meta: if meta:
result.attrs["description"] = "lfc" result.attrs["description"] = "lfc"
@ -554,7 +554,7 @@ def get_3dcape_only(wrfin, timeidx=0, method="cat",
""" """
result = get_3dcape(wrfin, timeidx, method, squeeze, cache, meta, result = get_3dcape(wrfin, timeidx, method, squeeze, cache, meta,
_key, missing)[0,:] _key, missing)[0, :]
if meta: if meta:
result.attrs["description"] = "cape" result.attrs["description"] = "cape"
@ -622,13 +622,10 @@ def get_3dcin_only(wrfin, timeidx=0, method="cat",
""" """
result = get_3dcape(wrfin, timeidx, method, squeeze, cache, meta, result = get_3dcape(wrfin, timeidx, method, squeeze, cache, meta,
_key, missing)[1,:] _key, missing)[1, :]
if meta: if meta:
result.attrs["description"] = "cin" result.attrs["description"] = "cin"
result.attrs["units"] = "J kg-1" result.attrs["units"] = "J kg-1"
return result return result

24
src/wrf/g_cloudfrac.py

@ -164,8 +164,9 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
def get_low_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, def get_low_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
vert_type="height_agl", low_thresh=None, mid_thresh=None, vert_type="height_agl", low_thresh=None,
high_thresh=None, missing=default_fill(np.float64)): mid_thresh=None, high_thresh=None,
missing=default_fill(np.float64)):
"""Return the cloud fraction for the low level clouds. """Return the cloud fraction for the low level clouds.
If the vertical coordinate type is 'height_agl' or 'height_msl', the If the vertical coordinate type is 'height_agl' or 'height_msl', the
@ -264,9 +265,8 @@ def get_low_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
result = get_cloudfrac(wrfin, timeidx, method, squeeze, result = get_cloudfrac(wrfin, timeidx, method, squeeze,
cache, meta, _key, cache, meta, _key, vert_type, low_thresh,
vert_type, low_thresh, mid_thresh, mid_thresh, high_thresh, missing)[0, :]
high_thresh, missing)[0,:]
if meta: if meta:
result.attrs["description"] = "low clouds" result.attrs["description"] = "low clouds"
@ -276,8 +276,9 @@ def get_low_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
def get_mid_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, def get_mid_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
vert_type="height_agl", low_thresh=None, mid_thresh=None, vert_type="height_agl", low_thresh=None,
high_thresh=None, missing=default_fill(np.float64)): mid_thresh=None, high_thresh=None,
missing=default_fill(np.float64)):
"""Return the cloud fraction for the mid level clouds. """Return the cloud fraction for the mid level clouds.
If the vertical coordinate type is 'height_agl' or 'height_msl', the If the vertical coordinate type is 'height_agl' or 'height_msl', the
@ -378,7 +379,7 @@ def get_mid_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
result = get_cloudfrac(wrfin, timeidx, method, squeeze, result = get_cloudfrac(wrfin, timeidx, method, squeeze,
cache, meta, _key, cache, meta, _key,
vert_type, low_thresh, mid_thresh, vert_type, low_thresh, mid_thresh,
high_thresh, missing)[1,:] high_thresh, missing)[1, :]
if meta: if meta:
result.attrs["description"] = "mid clouds" result.attrs["description"] = "mid clouds"
@ -388,8 +389,9 @@ def get_mid_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
def get_high_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, def get_high_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
vert_type="height_agl", low_thresh=None, mid_thresh=None, vert_type="height_agl", low_thresh=None,
high_thresh=None, missing=default_fill(np.float64)): mid_thresh=None, high_thresh=None,
missing=default_fill(np.float64)):
"""Return the cloud fraction for the high level clouds. """Return the cloud fraction for the high level clouds.
If the vertical coordinate type is 'height_agl' or 'height_msl', the If the vertical coordinate type is 'height_agl' or 'height_msl', the
@ -490,7 +492,7 @@ def get_high_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True,
result = get_cloudfrac(wrfin, timeidx, method, squeeze, result = get_cloudfrac(wrfin, timeidx, method, squeeze,
cache, meta, _key, cache, meta, _key,
vert_type, low_thresh, mid_thresh, vert_type, low_thresh, mid_thresh,
high_thresh, missing)[2,:] high_thresh, missing)[2, :]
if meta: if meta:
result.attrs["description"] = "high clouds" result.attrs["description"] = "high clouds"

5
src/wrf/g_ctt.py

@ -3,7 +3,6 @@ from __future__ import (absolute_import, division, print_function)
import numpy as np import numpy as np
import numpy.ma as ma import numpy.ma as ma
#from .extension import computectt, computetk
from .extension import _ctt, _tk from .extension import _ctt, _tk
from .constants import Constants, ConversionFactors, default_fill from .constants import Constants, ConversionFactors, default_fill
from .destag import destagger from .destag import destagger
@ -118,7 +117,7 @@ def get_ctt(wrfin, timeidx=0, method="cat",
qice = np.zeros(qv.shape, qv.dtype) qice = np.zeros(qv.shape, qv.dtype)
haveqci = 0 haveqci = 0
else: else:
qice = icevars["QICE"] * 1000.0 #g/kg qice = icevars["QICE"] * 1000.0 # g/kg
try: try:
cldvars = extract_vars(wrfin, timeidx, "QCLOUD", cldvars = extract_vars(wrfin, timeidx, "QCLOUD",
@ -127,7 +126,7 @@ def get_ctt(wrfin, timeidx=0, method="cat",
except KeyError: except KeyError:
raise RuntimeError("'QCLOUD' not found in NetCDF file") raise RuntimeError("'QCLOUD' not found in NetCDF file")
else: else:
qcld = cldvars["QCLOUD"] * 1000.0 #g/kg qcld = cldvars["QCLOUD"] * 1000.0 # g/kg
full_p = p + pb full_p = p + pb
p_hpa = full_p * ConversionFactors.PA_TO_HPA p_hpa = full_p * ConversionFactors.PA_TO_HPA

2
src/wrf/g_dbz.py

@ -2,7 +2,6 @@ from __future__ import (absolute_import, division, print_function)
import numpy as np import numpy as np
#from .extension import computedbz,computetk
from .extension import _dbz, _tk from .extension import _dbz, _tk
from .constants import Constants from .constants import Constants
from .util import extract_vars, to_np from .util import extract_vars, to_np
@ -196,4 +195,3 @@ def get_max_dbz(wrfin, timeidx=0, method="cat",
return np.amax(to_np(get_dbz(wrfin, timeidx, method, squeeze, cache, meta, return np.amax(to_np(get_dbz(wrfin, timeidx, method, squeeze, cache, meta,
_key, use_varint, use_liqskin)), _key, use_varint, use_liqskin)),
axis=-3) axis=-3)

7
src/wrf/g_dewpoint.py

@ -1,6 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
#from .extension import computetd
from .extension import _td from .extension import _td
from .decorators import convert_units from .decorators import convert_units
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
@ -69,7 +68,7 @@ def get_dp(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
varnames=("P", "PB", "QVAPOR") varnames = ("P", "PB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -86,6 +85,7 @@ def get_dp(wrfin, timeidx=0, method="cat", squeeze=True,
td = _td(full_p, qvapor) td = _td(full_p, qvapor)
return td return td
@copy_and_set_metadata(copy_varname="Q2", name="td2", @copy_and_set_metadata(copy_varname="Q2", name="td2",
description="2m dew point temperature") description="2m dew point temperature")
@convert_units("temp", "c") @convert_units("temp", "c")
@ -147,7 +147,7 @@ def get_dp_2m(wrfin, timeidx=0, method="cat", squeeze=True,
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
varnames=("PSFC", "Q2") varnames = ("PSFC", "Q2")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -161,4 +161,3 @@ def get_dp_2m(wrfin, timeidx=0, method="cat", squeeze=True,
td = _td(psfc, q2) td = _td(psfc, q2)
return td return td

9
src/wrf/g_geoht.py

@ -8,6 +8,7 @@ from .decorators import convert_units
from .metadecorators import set_height_metadata from .metadecorators import set_height_metadata
from .util import extract_vars, either from .util import extract_vars, either
def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, def _get_geoht(wrfin, timeidx, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None,
height=True, msl=True, stag=False): height=True, msl=True, stag=False):
@ -104,7 +105,7 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True,
if stag: if stag:
warnings.warn("file contains no vertically staggered geopotential " warnings.warn("file contains no vertically staggered geopotential "
"height variable, returning unstaggered result " "height variable, returning unstaggered result "
"instead" ) "instead")
if height: if height:
if msl: if msl:
return geopt_unstag / Constants.G return geopt_unstag / Constants.G
@ -113,7 +114,7 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True,
# array needs to be reshaped to a 3D array so the right dims # array needs to be reshaped to a 3D array so the right dims
# line up # line up
new_dims = list(hgt.shape) new_dims = list(hgt.shape)
new_dims.insert(-2,1) new_dims.insert(-2, 1)
hgt = hgt.reshape(new_dims) hgt = hgt.reshape(new_dims)
return (geopt_unstag / Constants.G) - hgt return (geopt_unstag / Constants.G) - hgt
@ -321,8 +322,7 @@ def get_stag_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
@set_height_metadata(geopt=False, stag=True) @set_height_metadata(geopt=False, stag=True)
@convert_units("height", "m") @convert_units("height", "m")
def get_stag_height(wrfin, timeidx=0, method="cat", squeeze=True, def get_stag_height(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None, cache=None, meta=True, _key=None, msl=True, units="m"):
msl=True, units="m"):
"""Return the geopotential height for the vertically staggered grid. """Return the geopotential height for the vertically staggered grid.
If *msl* is True, then geopotential height is returned as Mean Sea Level If *msl* is True, then geopotential height is returned as Mean Sea Level
@ -391,4 +391,3 @@ def get_stag_height(wrfin, timeidx=0, method="cat", squeeze=True,
return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key,
True, msl, stag=True) True, msl, stag=True)

13
src/wrf/g_helicity.py

@ -9,6 +9,7 @@ from .util import extract_vars, extract_global_attrs, either
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
from .g_latlon import get_lat from .g_latlon import get_lat
@copy_and_set_metadata(copy_varname="HGT", name="srh", @copy_and_set_metadata(copy_varname="HGT", name="srh",
description="storm relative helicity", description="storm relative helicity",
units="m2 s-2") units="m2 s-2")
@ -101,14 +102,15 @@ def get_srh(wrfin, timeidx=0, method="cat", squeeze=True,
z = geopt_unstag / Constants.G z = geopt_unstag / Constants.G
# Re-ordering from high to low # Re-ordering from high to low
u1 = np.ascontiguousarray(u[...,::-1,:,:]) u1 = np.ascontiguousarray(u[..., ::-1, :, :])
v1 = np.ascontiguousarray(v[...,::-1,:,:]) v1 = np.ascontiguousarray(v[..., ::-1, :, :])
z1 = np.ascontiguousarray(z[...,::-1,:,:]) z1 = np.ascontiguousarray(z[..., ::-1, :, :])
srh = _srhel(u1, v1, z1, ter, lats, top) srh = _srhel(u1, v1, z1, ter, lats, top)
return srh return srh
@copy_and_set_metadata(copy_varname="MAPFAC_M", name="updraft_helicity", @copy_and_set_metadata(copy_varname="MAPFAC_M", name="updraft_helicity",
description="updraft helicity", description="updraft helicity",
units="m2 s-2") units="m2 s-2")
@ -206,8 +208,3 @@ def get_uh(wrfin, timeidx=0, method="cat", squeeze=True,
uh = _udhel(zp, mapfct, u, v, wstag, dx, dy, bottom, top) uh = _udhel(zp, mapfct, u, v, wstag, dx, dy, bottom, top)
return uh return uh

36
src/wrf/g_latlon.py

@ -244,7 +244,7 @@ def _llxy_mapping(wrfin, x_or_lat, y_or_lon, func, timeidx, stagger,
outdims = [numkeys] outdims = [numkeys]
outdims += first_array.shape outdims += first_array.shape
outdata = np.empty(outdims, first_array.dtype) outdata = np.empty(outdims, first_array.dtype)
outdata[0,:] = first_array[:] outdata[0, :] = first_array[:]
idx = 1 idx = 1
while True: while True:
@ -265,7 +265,7 @@ def _llxy_mapping(wrfin, x_or_lat, y_or_lon, func, timeidx, stagger,
if outdata.shape[1:] != result_array.shape: if outdata.shape[1:] != result_array.shape:
raise ValueError("data sequences must have the " raise ValueError("data sequences must have the "
"same size for all dictionary keys") "same size for all dictionary keys")
outdata[idx,:] = to_np(result_array)[:] outdata[idx, :] = to_np(result_array)[:]
idx += 1 idx += 1
if xarray_enabled() and meta: if xarray_enabled() and meta:
@ -301,12 +301,10 @@ def _llxy_mapping(wrfin, x_or_lat, y_or_lon, func, timeidx, stagger,
# make it so that key_0 is leftmost # make it so that key_0 is leftmost
outdims = key_coordnames + list(first_array.dims[existing_cnt:]) outdims = key_coordnames + list(first_array.dims[existing_cnt:])
# Create the new 'key_n', value pairs # Create the new 'key_n', value pairs
for coordname, coordval in zip(key_coordnames, coord_vals): for coordname, coordval in zip(key_coordnames, coord_vals):
outcoords[coordname] = coordval outcoords[coordname] = coordval
outattrs = OrderedDict(first_array.attrs) outattrs = OrderedDict(first_array.attrs)
outarr = DataArray(outdata, name=outname, coords=outcoords, outarr = DataArray(outdata, name=outname, coords=outcoords,
@ -604,11 +602,12 @@ def ll_to_xy_proj(latitude, longitude, meta=True, squeeze=True, as_int=True,
""" """
loc = locals() loc = locals()
_projparams = {name : loc[name] for name in ("map_proj", "truelat1", _projparams = {name: loc[name] for name in ("map_proj", "truelat1",
"truelat2", "stand_lon", "ref_lat", "truelat2", "stand_lon",
"ref_lon", "pole_lat", "pole_lon", "ref_lat", "ref_lon",
"known_x", "known_y", "dx", "dy", "pole_lat", "pole_lon",
"latinc", "loninc")} "known_x", "known_y", "dx",
"dy", "latinc", "loninc")}
projparams = _set_defaults(_projparams) projparams = _set_defaults(_projparams)
@ -772,20 +771,13 @@ def xy_to_ll_proj(x, y, meta=True, squeeze=True, map_proj=None, truelat1=None,
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
loc = locals() loc = locals()
_projparams = {name : loc[name] for name in ("map_proj", "truelat1", _projparams = {name: loc[name] for name in ("map_proj", "truelat1",
"truelat2", "stand_lon", "ref_lat", "truelat2", "stand_lon",
"ref_lon", "pole_lat", "pole_lon", "ref_lat", "ref_lon",
"known_x", "known_y", "dx", "dy", "pole_lat", "pole_lon",
"latinc", "loninc")} "known_x", "known_y", "dx",
"dy", "latinc", "loninc")}
projparams = _set_defaults(_projparams) projparams = _set_defaults(_projparams)
return _xy_to_ll(x, y, None, 0, None, "cat", squeeze, None, None, return _xy_to_ll(x, y, None, 0, None, "cat", squeeze, None, None,
**projparams) **projparams)

3
src/wrf/g_omega.py

@ -64,7 +64,7 @@ def get_omega(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varnames=("T", "P", "W", "PB", "QVAPOR") varnames = ("T", "P", "W", "PB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
@ -81,4 +81,3 @@ def get_omega(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
omega = _omega(qv, tk, wa, full_p) omega = _omega(qv, tk, wa, full_p)
return omega return omega

2
src/wrf/g_precip.py

@ -2,7 +2,6 @@ from __future__ import (absolute_import, division, print_function)
from .util import extract_vars from .util import extract_vars
__all__ = ["get_accum_precip", "get_precip_diff"]
def get_accum_precip(wrfin, timeidx=0): def get_accum_precip(wrfin, timeidx=0):
ncvars = extract_vars(wrfin, timeidx, varnames=("RAINC", "RAINNC")) ncvars = extract_vars(wrfin, timeidx, varnames=("RAINC", "RAINNC"))
@ -13,6 +12,7 @@ def get_accum_precip(wrfin, timeidx=0):
return rainsum return rainsum
def get_precip_diff(wrfin1, wrfin2, timeidx=0): def get_precip_diff(wrfin1, wrfin2, timeidx=0):
vars1 = extract_vars(wrfin1, timeidx, varnames=("RAINC", "RAINNC")) vars1 = extract_vars(wrfin1, timeidx, varnames=("RAINC", "RAINNC"))
vars2 = extract_vars(wrfin2, timeidx, varnames=("RAINC", "RAINNC")) vars2 = extract_vars(wrfin2, timeidx, varnames=("RAINC", "RAINNC"))

5
src/wrf/g_pressure.py

@ -85,6 +85,7 @@ def get_pressure(wrfin, timeidx=0, method="cat", squeeze=True,
return pres return pres
def get_pressure_hpa(wrfin, timeidx=0, method="cat", squeeze=True, def get_pressure_hpa(wrfin, timeidx=0, method="cat", squeeze=True,
cache=None, meta=True, _key=None): cache=None, meta=True, _key=None):
"""Return the pressure in [hPa]. """Return the pressure in [hPa].
@ -142,7 +143,3 @@ def get_pressure_hpa(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
return get_pressure(wrfin, timeidx, method, squeeze, cache, meta, _key, return get_pressure(wrfin, timeidx, method, squeeze, cache, meta, _key,
units="hPa") units="hPa")

7
src/wrf/g_pw.py

@ -1,6 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
#from .extension import computepw,computetv,computetk
from .extension import _pw, _tv, _tk from .extension import _pw, _tv, _tk
from .constants import Constants from .constants import Constants
from .util import extract_vars from .util import extract_vars
@ -66,7 +65,7 @@ def get_pw(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varnames=("T", "P", "PB", "PH", "PHB", "QVAPOR") varnames = ("T", "P", "PB", "PH", "PHB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -85,7 +84,3 @@ def get_pw(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
tv = _tv(tk, qv) tv = _tv(tk, qv)
return _pw(full_p, tv, qv, ht) return _pw(full_p, tv, qv, ht)

6
src/wrf/g_rh.py

@ -1,7 +1,6 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
from .constants import Constants from .constants import Constants
#from .extension import computerh, computetk
from .extension import _rh, _tk from .extension import _rh, _tk
from .util import extract_vars from .util import extract_vars
from .metadecorators import copy_and_set_metadata from .metadecorators import copy_and_set_metadata
@ -64,7 +63,7 @@ def get_rh(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
@ -140,7 +139,7 @@ def get_rh_2m(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varnames=("T2", "PSFC", "Q2") varnames = ("T2", "PSFC", "Q2")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
t2 = ncvars["T2"] t2 = ncvars["T2"]
@ -153,4 +152,3 @@ def get_rh_2m(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
rh = _rh(q2, psfc, t2) rh = _rh(q2, psfc, t2)
return rh return rh

4
src/wrf/g_slp.py

@ -1,6 +1,5 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
#from .extension import computeslp, computetk
from .extension import _slp, _tk from .extension import _slp, _tk
from .constants import Constants from .constants import Constants
from .destag import destagger from .destag import destagger
@ -75,7 +74,7 @@ def get_slp(wrfin, timeidx=0, method="cat", squeeze=True,
:class:`numpy.ndarray` object with no metadata. :class:`numpy.ndarray` object with no metadata.
""" """
varnames=("T", "P", "PB", "QVAPOR", "PH", "PHB") varnames = ("T", "P", "PB", "QVAPOR", "PH", "PHB")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -100,4 +99,3 @@ def get_slp(wrfin, timeidx=0, method="cat", squeeze=True,
slp = _slp(destag_ph, tk, full_p, qvapor) slp = _slp(destag_ph, tk, full_p, qvapor)
return slp return slp

13
src/wrf/g_temp.py

@ -69,7 +69,7 @@ def get_theta(wrfin, timeidx=0, method="cat", squeeze=True,
be a :class:`numpy.ndarray` object with no metadata. be a :class:`numpy.ndarray` object with no metadata.
""" """
varnames = ("T",) varnames = ("T", )
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -142,7 +142,7 @@ def get_temp(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
varnames=("T", "P", "PB") varnames = ("T", "P", "PB")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
@ -219,7 +219,7 @@ def get_eth(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
@ -299,7 +299,7 @@ def get_tv(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
@ -380,7 +380,7 @@ def get_tw(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
varnames=("T", "P", "PB", "QVAPOR") varnames = ("T", "P", "PB", "QVAPOR")
ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache, ncvars = extract_vars(wrfin, timeidx, varnames, method, squeeze, cache,
meta=False, _key=_key) meta=False, _key=_key)
t = ncvars["T"] t = ncvars["T"]
@ -511,6 +511,3 @@ def get_tc(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
return get_temp(wrfin, timeidx, method, squeeze, cache, meta, _key, return get_temp(wrfin, timeidx, method, squeeze, cache, meta, _key,
units="degC") units="degC")

3
src/wrf/g_terrain.py

@ -73,6 +73,3 @@ def get_terrain(wrfin, timeidx=0, method="cat", squeeze=True,
return extract_vars(wrfin, timeidx, varname, return extract_vars(wrfin, timeidx, varname,
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
_key=_key)[varname] _key=_key)[varname]

1
src/wrf/g_times.py

@ -118,4 +118,3 @@ def get_xtimes(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
""" """
return extract_times(wrfin, timeidx, method, squeeze, cache, return extract_times(wrfin, timeidx, method, squeeze, cache,
meta=meta, do_xtime=True) meta=meta, do_xtime=True)

33
src/wrf/g_uvmet.py

@ -4,7 +4,6 @@ from math import fabs, log, tan, sin, cos
import numpy as np import numpy as np
#from .extension import computeuvmet
from .extension import _uvmet from .extension import _uvmet
from .destag import destagger from .destag import destagger
from .constants import Constants from .constants import Constants
@ -101,13 +100,13 @@ def _get_uvmet(wrfin, timeidx=0, method="cat", squeeze=True,
u_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache, u_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache,
meta=False, _key=_key) 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")(wrfin) varname = either("V10", "VV")(wrfin)
v_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache, v_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache,
meta=False, _key=_key) 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))
map_proj_attrs = extract_global_attrs(wrfin, attrs="MAP_PROJ") map_proj_attrs = extract_global_attrs(wrfin, attrs="MAP_PROJ")
map_proj = map_proj_attrs["MAP_PROJ"] map_proj = map_proj_attrs["MAP_PROJ"]
@ -119,7 +118,7 @@ def _get_uvmet(wrfin, timeidx=0, method="cat", squeeze=True,
# Note: NCL has no code to handle other projections (0,4,5) as they # Note: NCL has no code to handle other projections (0,4,5) as they
# don't appear to be supported any longer # don't appear to be supported any longer
if map_proj in (0,3,6): if map_proj in (0, 3, 6):
# No rotation needed for Mercator and Lat/Lon, but still need # No rotation needed for Mercator and Lat/Lon, but still need
# u,v aggregated in to one array # u,v aggregated in to one array
@ -145,7 +144,7 @@ def _get_uvmet(wrfin, timeidx=0, method="cat", squeeze=True,
result = np.ma.masked_values(result, fill) result = np.ma.masked_values(result, fill)
return result return result
elif map_proj in (1,2): elif map_proj in (1, 2):
lat_attrs = extract_global_attrs(wrfin, attrs=("TRUELAT1", lat_attrs = extract_global_attrs(wrfin, attrs=("TRUELAT1",
"TRUELAT2")) "TRUELAT2"))
radians_per_degree = Constants.PI/180.0 radians_per_degree = Constants.PI/180.0
@ -165,7 +164,6 @@ def _get_uvmet(wrfin, timeidx=0, method="cat", squeeze=True,
else: else:
cen_lon = lon_attrs["STAND_LON"] cen_lon = lon_attrs["STAND_LON"]
varname = either("XLAT_M", "XLAT")(wrfin) varname = either("XLAT_M", "XLAT")(wrfin)
xlat_var = extract_vars(wrfin, timeidx, varname, xlat_var = extract_vars(wrfin, timeidx, varname,
method, squeeze, cache, meta=False, method, squeeze, cache, meta=False,
@ -181,11 +179,12 @@ def _get_uvmet(wrfin, timeidx=0, method="cat", squeeze=True,
if map_proj == 1: if map_proj == 1:
if((fabs(true_lat1 - true_lat2) > 0.1) and if((fabs(true_lat1 - true_lat2) > 0.1) and
(fabs(true_lat2 - 90.) > 0.1)): (fabs(true_lat2 - 90.) > 0.1)):
cone = (log(cos(true_lat1*radians_per_degree)) cone = (log(cos(true_lat1*radians_per_degree)) -
- log(cos(true_lat2*radians_per_degree))) log(cos(true_lat2*radians_per_degree)))
cone = (cone / cone = (cone /
(log(tan((45.-fabs(true_lat1/2.))*radians_per_degree)) (log(tan((45.-fabs(true_lat1/2.))*radians_per_degree))
- log(tan((45.-fabs(true_lat2/2.))*radians_per_degree)))) - log(tan((45.-fabs(true_lat2/2.)) *
radians_per_degree))))
else: else:
cone = sin(fabs(true_lat1)*radians_per_degree) cone = sin(fabs(true_lat1)*radians_per_degree)
else: else:
@ -434,7 +433,7 @@ def get_uvmet_wspd_wdir(wrfin, timeidx=0, method="cat", squeeze=True,
uvmet = _get_uvmet(wrfin, timeidx, method, squeeze, uvmet = _get_uvmet(wrfin, timeidx, method, squeeze,
cache, meta, _key, False, units="m s-1") cache, meta, _key, False, units="m s-1")
return _calc_wspd_wdir(uvmet[0,...,:,:,:], uvmet[1,...,:,:,:], return _calc_wspd_wdir(uvmet[0, ..., :, :, :], uvmet[1, ..., :, :, :],
False, units) False, units)
@ -517,7 +516,8 @@ def get_uvmet10_wspd_wdir(wrfin, timeidx=0, method="cat", squeeze=True,
uvmet10 = _get_uvmet(wrfin, timeidx, method, squeeze, cache, meta, _key, uvmet10 = _get_uvmet(wrfin, timeidx, method, squeeze, cache, meta, _key,
True, units="m s-1") True, units="m s-1")
return _calc_wspd_wdir(uvmet10[0,...,:,:], uvmet10[1,...,:,:], True, units) return _calc_wspd_wdir(uvmet10[0, ..., :, :], uvmet10[1, ..., :, :],
True, units)
def get_uvmet_wspd(wrfin, timeidx=0, method="cat", squeeze=True, def get_uvmet_wspd(wrfin, timeidx=0, method="cat", squeeze=True,
@ -583,7 +583,7 @@ def get_uvmet_wspd(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
result = get_uvmet_wspd_wdir(wrfin, timeidx, method, squeeze, result = get_uvmet_wspd_wdir(wrfin, timeidx, method, squeeze,
cache, meta, _key, units)[0,:] cache, meta, _key, units)[0, :]
if meta: if meta:
result.attrs["description"] = "earth rotated wspd" result.attrs["description"] = "earth rotated wspd"
@ -654,7 +654,7 @@ def get_uvmet_wdir(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
result = get_uvmet_wspd_wdir(wrfin, timeidx, method, squeeze, result = get_uvmet_wspd_wdir(wrfin, timeidx, method, squeeze,
cache, meta, _key, units)[1,:] cache, meta, _key, units)[1, :]
if meta: if meta:
result.attrs["description"] = "earth rotated wdir" result.attrs["description"] = "earth rotated wdir"
@ -725,7 +725,7 @@ def get_uvmet10_wspd(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
result = get_uvmet10_wspd_wdir(wrfin, timeidx, method, squeeze, result = get_uvmet10_wspd_wdir(wrfin, timeidx, method, squeeze,
cache, meta, _key, units)[0,:] cache, meta, _key, units)[0, :]
if meta: if meta:
result.attrs["description"] = "10m earth rotated wspd" result.attrs["description"] = "10m earth rotated wspd"
@ -795,12 +795,9 @@ def get_uvmet10_wdir(wrfin, timeidx=0, method="cat", squeeze=True,
""" """
result = get_uvmet10_wspd_wdir(wrfin, timeidx, method, squeeze, result = get_uvmet10_wspd_wdir(wrfin, timeidx, method, squeeze,
cache, meta, _key, units)[1,:] cache, meta, _key, units)[1, :]
if meta: if meta:
result.attrs["description"] = "10m earth rotated wdir" result.attrs["description"] = "10m earth rotated wdir"
return result return result

1
src/wrf/g_vorticity.py

@ -164,4 +164,3 @@ def get_pvo(wrfin, timeidx=0, method="cat", squeeze=True, cache=None,
full_p = p + pb full_p = p + pb
return _pvo(u, v, full_t, full_p, msfu, msfv, msfm, cor, dx, dy) return _pvo(u, v, full_t, full_p, msfu, msfv, msfm, cor, dx, dy)

21
src/wrf/g_wind.py

@ -92,13 +92,11 @@ def _calc_wspd_wdir(u, v, two_d, units):
result = np.zeros(outdims, wspd.dtype) result = np.zeros(outdims, wspd.dtype)
idxs0 = ((0,Ellipsis, slice(None), slice(None), slice(None)) idxs0 = ((0, Ellipsis, slice(None), slice(None), slice(None))
if not two_d else if not two_d else (0, Ellipsis, slice(None), slice(None)))
(0, Ellipsis, slice(None), slice(None)))
idxs1 = ((1, Ellipsis, slice(None), slice(None), slice(None)) idxs1 = ((1, Ellipsis, slice(None), slice(None), slice(None))
if not two_d else if not two_d else (1, Ellipsis, slice(None), slice(None)))
(1, Ellipsis, slice(None), slice(None)))
result[idxs0] = wspd[:] result[idxs0] = wspd[:]
result[idxs1] = wdir[:] result[idxs1] = wdir[:]
@ -497,13 +495,13 @@ def get_destag_wspd_wdir10(wrfin, timeidx=0, method="cat",
u_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache, u_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache,
meta=False, _key=_key) 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")(wrfin) varname = either("V10", "VV")(wrfin)
v_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache, v_vars = extract_vars(wrfin, timeidx, varname, method, squeeze, cache,
meta=False, _key=_key) 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))
return _calc_wspd_wdir(u, v, True, units) return _calc_wspd_wdir(u, v, True, units)
@ -569,7 +567,7 @@ def get_destag_wspd(wrfin, timeidx=0, method="cat",
""" """
result = get_destag_wspd_wdir(wrfin, timeidx, method, squeeze, cache, result = get_destag_wspd_wdir(wrfin, timeidx, method, squeeze, cache,
meta, _key, units)[0,:] meta, _key, units)[0, :]
if meta: if meta:
result.attrs["description"] = "wspd in projection space" result.attrs["description"] = "wspd in projection space"
@ -638,7 +636,7 @@ def get_destag_wdir(wrfin, timeidx=0, method="cat",
""" """
result = get_destag_wspd_wdir(wrfin, timeidx, method, squeeze, cache, result = get_destag_wspd_wdir(wrfin, timeidx, method, squeeze, cache,
meta, _key, units)[1,:] meta, _key, units)[1, :]
if meta: if meta:
result.attrs["description"] = "wdir in projection space" result.attrs["description"] = "wdir in projection space"
@ -708,7 +706,7 @@ def get_destag_wspd10(wrfin, timeidx=0, method="cat",
""" """
result = get_destag_wspd_wdir10(wrfin, timeidx, method, result = get_destag_wspd_wdir10(wrfin, timeidx, method,
squeeze, cache, meta, _key, units)[0,:] squeeze, cache, meta, _key, units)[0, :]
if meta: if meta:
result.attrs["description"] = "10m wspd in projection space" result.attrs["description"] = "10m wspd in projection space"
@ -778,10 +776,9 @@ def get_destag_wdir10(wrfin, timeidx=0, method="cat",
""" """
result = get_destag_wspd_wdir10(wrfin, timeidx, method, result = get_destag_wspd_wdir10(wrfin, timeidx, method,
squeeze, cache, meta, _key, units)[1,:] squeeze, cache, meta, _key, units)[1, :]
if meta: if meta:
result.attrs["description"] = "10m wdir in projection space" result.attrs["description"] = "10m wdir in projection space"
return result return result

9
src/wrf/geobnds.py

@ -2,6 +2,7 @@ from __future__ import (absolute_import, division, print_function)
from .coordpair import CoordPair from .coordpair import CoordPair
class GeoBounds(object): class GeoBounds(object):
"""A class that stores the geographic boundaries. """A class that stores the geographic boundaries.
@ -56,8 +57,8 @@ class GeoBounds(object):
raise ValueError("'top_right' parameter does not contain a" raise ValueError("'top_right' parameter does not contain a"
"'lon' attribute") "'lon' attribute")
elif lats is not None and lons is not None: elif lats is not None and lons is not None:
self.bottom_left = CoordPair(lat=lats[0,0], lon=lons[0,0]) self.bottom_left = CoordPair(lat=lats[0, 0], lon=lons[0, 0])
self.top_right = CoordPair(lat=lats[-1,-1], lon=lons[-1,-1]) self.top_right = CoordPair(lat=lats[-1, -1], lon=lons[-1, -1])
else: else:
raise ValueError("must specify either 'bottom_top' and " raise ValueError("must specify either 'bottom_top' and "
"'top_right' parameters " "'top_right' parameters "
@ -84,7 +85,3 @@ class NullGeoBounds(GeoBounds):
def __repr__(self): def __repr__(self):
return "{}()".format(self.__class__.__name__) return "{}()".format(self.__class__.__name__)

127
src/wrf/interp.py

@ -76,11 +76,24 @@ def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64),
p = getvar(wrfin, "pressure") p = getvar(wrfin, "pressure")
ht = getvar(wrfin, "z", units="dm") ht = getvar(wrfin, "z", units="dm")
pblh = getvar(wrfin, "PBLH")
ht_500 = interplevel(ht, p, 500.0) ht_500 = interplevel(ht, p, 500.0)
Interpolate Relative Humidity to Boundary Layer Heights
.. code-block:: python
from netCDF4 import Dataset
from wrf import getvar, interplevel
wrfin = Dataset("wrfout_d02_2010-06-13_21:00:00")
rh = getvar(wrfin, "rh")
z = getvar(wrfin, "z")
pblh = getvar(wrfin, "PBLH")
rh_pblh = interplevel(rh, p, pblh)
""" """
@ -97,7 +110,7 @@ def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64),
else: else:
result = _interpz3d_lev2d(field3d, vert, _desiredlev, missing) result = _interpz3d_lev2d(field3d, vert, _desiredlev, missing)
masked = ma.masked_values (result, missing) masked = ma.masked_values(result, missing)
if not meta: if not meta:
if squeeze: if squeeze:
@ -162,9 +175,9 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
timeidx (:obj:`int`, optional): The timeidx (:obj:`int`, optional): The
desired time index when obtaining map boundary information desired time index when obtaining map boundary information
from moving nests. This value can be a positive or negative integer. from moving nests. This value can be a positive or negative
Only required when *wrfin* is specified and the nest is moving. integer. Only required when *wrfin* is specified and the nest is
Currently, :data:`wrf.ALL_TIMES` is not supported. moving. Currently, :data:`wrf.ALL_TIMES` is not supported.
Default is 0. Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs stagger (:obj:`str`): If using latitude, longitude coordinate pairs
@ -286,12 +299,12 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
# domain could move outside of the line, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving: if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving "
"is not supported when using lat/lon " "nest is not supported when using "
"cross sections because the domain could " "lat/lon cross sections because the "
"move outside of the cross section. " "domain could move outside of the "
"You must request each time " "cross section. You must request "
"individually.") "each time individually.")
else: else:
# Domain not moving, just use 0 # Domain not moving, just use 0
_timeidx = 0 _timeidx = 0
@ -343,7 +356,7 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64),
result = np.empty(outshape, dtype=field3d.dtype) result = np.empty(outshape, dtype=field3d.dtype)
for i in py3range(field3d.shape[0]): for i in py3range(field3d.shape[0]):
result[i,:] = _vertcross(field3d[i,:], xy, var2dz, z_var2d, result[i, :] = _vertcross(field3d[i, :], xy, var2dz, z_var2d,
missing)[:] missing)[:]
return ma.masked_values(result, missing) return ma.masked_values(result, missing)
@ -379,9 +392,9 @@ def interpline(field2d, wrfin=None, timeidx=0, stagger=None, projection=None,
timeidx (:obj:`int`, optional): The timeidx (:obj:`int`, optional): The
desired time index when obtaining map boundary information desired time index when obtaining map boundary information
from moving nests. This value can be a positive or negative integer. from moving nests. This value can be a positive or negative
Only required when *wrfin* is specified and the nest is moving. integer. Only required when *wrfin* is specified and the nest is
Currently, :data:`wrf.ALL_TIMES` is not supported. moving. Currently, :data:`wrf.ALL_TIMES` is not supported.
Default is 0. Default is 0.
stagger (:obj:`str`): If using latitude, longitude coordinate pairs stagger (:obj:`str`): If using latitude, longitude coordinate pairs
@ -496,10 +509,10 @@ def interpline(field2d, wrfin=None, timeidx=0, stagger=None, projection=None,
# domain could move outside of the line, which causes # domain could move outside of the line, which causes
# crashes or different line lengths. # crashes or different line lengths.
if is_moving: if is_moving:
raise ValueError("Requesting all times with a moving nest " raise ValueError("Requesting all times with a moving "
"is not supported when using a lat/lon " "nest is not supported when using a "
"line because the domain could " "lat/lon line because the domain "
"move outside of line. " "could move outside of line. "
"You must request each time " "You must request each time "
"individually.") "individually.")
else: else:
@ -572,8 +585,8 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
* 'ght_msl': grid point height msl [km] * 'ght_msl': grid point height msl [km]
* 'ght_agl': grid point height agl [km] * 'ght_agl': grid point height agl [km]
* 'theta', 'th': potential temperature [K] * 'theta', 'th': potential temperature [K]
* 'theta-e', 'thetae', 'eth': equivalent potential temperature \ * 'theta-e', 'thetae', 'eth': equivalent potential \
[K] temperature [K]
interp_levels (sequence): A 1D sequence of vertical levels to interp_levels (sequence): A 1D sequence of vertical levels to
interpolate to. Values must be in the same units as specified interpolate to. Values must be in the same units as specified
@ -662,39 +675,37 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
"tc", "tk", "theta", "th", "theta-e", "thetae", "tc", "tk", "theta", "th", "theta-e", "thetae",
"eth", "ght", 'z_km', 'ght_km') "eth", "ght", 'z_km', 'ght_km')
icase_lookup = {"none" : 0, icase_lookup = {"none": 0,
"p" : 1, "p": 1,
"pres" : 1, "pres": 1,
"pressure" : 1, "pressure": 1,
"p_hpa" : 1, "p_hpa": 1,
"pres_hpa" : 1, "pres_hpa": 1,
"pressure_hpa" : 1, "pressure_hpa": 1,
"z" : 2, "z": 2,
"ght" : 2, "ght": 2,
"z_km" : 2, "z_km": 2,
"ght_km" : 2, "ght_km": 2,
"tc" : 3, "tc": 3,
"tk" : 4, "tk": 4,
"theta" : 5, "theta": 5,
"th" : 5, "th": 5,
"theta-e" : 6, "theta-e": 6,
"thetae" : 6, "thetae": 6,
"eth" : 6} "eth": 6}
in_unitmap = {"p_hpa" : 1.0/ConversionFactors.PA_TO_HPA, in_unitmap = {"p_hpa": 1.0/ConversionFactors.PA_TO_HPA,
"pres_hpa" : 1.0/ConversionFactors.PA_TO_HPA, "pres_hpa": 1.0/ConversionFactors.PA_TO_HPA,
"pressure_hpa" : 1.0/ConversionFactors.PA_TO_HPA, "pressure_hpa": 1.0/ConversionFactors.PA_TO_HPA,
"z_km" : 1.0/ConversionFactors.M_TO_KM, "z_km": 1.0/ConversionFactors.M_TO_KM,
"ght_km" : 1.0/ConversionFactors.M_TO_KM, "ght_km": 1.0/ConversionFactors.M_TO_KM,
} }
out_unitmap = {"p_hpa" : ConversionFactors.PA_TO_HPA, out_unitmap = {"p_hpa": ConversionFactors.PA_TO_HPA,
"pres_hpa" : ConversionFactors.PA_TO_HPA, "pres_hpa": ConversionFactors.PA_TO_HPA,
"pressure_hpa" : ConversionFactors.PA_TO_HPA, "pressure_hpa": ConversionFactors.PA_TO_HPA,
"z_km" : ConversionFactors.M_TO_KM, "z_km": ConversionFactors.M_TO_KM,
"ght_km" : ConversionFactors.M_TO_KM, "ght_km": ConversionFactors.M_TO_KM,
} }
# These constants match what's in the fortran code. # These constants match what's in the fortran code.
@ -715,12 +726,12 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
# Check for valid coord # Check for valid coord
if vert_coord not in valid_coords: if vert_coord not in valid_coords:
raise ValueError("'%s' is not a valid vertical " raise ValueError("'{}' is not a valid vertical "
"coordinate type" % vert_coord) "coordinate type".format(vert_coord))
# Check for valid field type # Check for valid field type
if field_type not in valid_field_types: if field_type not in valid_field_types:
raise ValueError("'%s' is not a valid field type" % field_type) raise ValueError("'{}' is not a valid field type".format(field_type))
log_p_int = 1 if log_p else 0 log_p_int = 1 if log_p else 0
@ -848,9 +859,3 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False,
res_ = res res_ = res
return ma.masked_values(res_, missing) return ma.masked_values(res_, missing)

50
src/wrf/interputils.py

@ -31,7 +31,7 @@ def to_positive_idxs(shape, coord):
if (coord[-2] >= 0 and coord[-1] >= 0): if (coord[-2] >= 0 and coord[-1] >= 0):
return coord return coord
return [x if (x >= 0) else shape[-i-1]+x for (i,x) in enumerate(coord)] return [x if (x >= 0) else shape[-i-1]+x for (i, x) in enumerate(coord)]
def _calc_xy(xdim, ydim, pivot_point=None, angle=None, def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
@ -85,11 +85,10 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
if (angle > 315.0 or angle < 45.0 if (angle > 315.0 or angle < 45.0
or ((angle > 135.0) and (angle < 225.0))): or ((angle > 135.0) and (angle < 225.0))):
#x = y*slope + intercept
slope = -(360.-angle)/45. slope = -(360.-angle)/45.
if( angle < 45. ): if(angle < 45.):
slope = angle/45. slope = angle/45.
if( angle > 135.): if(angle > 135.):
slope = (angle-180.)/45. slope = (angle-180.)/45.
intercept = xp - yp*slope intercept = xp - yp*slope
@ -98,49 +97,49 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
y0 = 0. y0 = 0.
x0 = y0*slope + intercept x0 = y0*slope + intercept
if( x0 < 0.): # intersect outside of left boundary if(x0 < 0.): # intersect outside of left boundary
x0 = 0. x0 = 0.
y0 = (x0 - intercept)/slope y0 = (x0 - intercept)/slope
if( x0 > xdim-1): #intersect outside of right boundary if(x0 > xdim-1): # intersect outside of right boundary
x0 = xdim-1 x0 = xdim-1
y0 = (x0 - intercept)/slope y0 = (x0 - intercept)/slope
y1 = ydim-1. #need to make sure this will be a float? y1 = ydim-1. # need to make sure this will be a float?
x1 = y1*slope + intercept x1 = y1*slope + intercept
if( x1 < 0.): # intersect outside of left boundary if(x1 < 0.): # intersect outside of left boundary
x1 = 0. x1 = 0.
y1 = (x1 - intercept)/slope y1 = (x1 - intercept)/slope
if( x1 > xdim-1): # intersect outside of right boundary if(x1 > xdim-1): # intersect outside of right boundary
x1 = xdim-1 x1 = xdim-1
y1 = (x1 - intercept)/slope y1 = (x1 - intercept)/slope
else: else:
# y = x*slope + intercept # y = x*slope + intercept
slope = (90.-angle)/45. slope = (90.-angle)/45.
if( angle > 225. ): if (angle > 225.):
slope = (270.-angle)/45. slope = (270.-angle)/45.
intercept = yp - xp*slope intercept = yp - xp*slope
#find intersections with domain boundaries # Find intersections with domain boundaries
x0 = 0. x0 = 0.
y0 = x0*slope + intercept y0 = x0*slope + intercept
if( y0 < 0.): # intersect outside of bottom boundary if (y0 < 0.): # intersect outside of bottom boundary
y0 = 0. y0 = 0.
x0 = (y0 - intercept)/slope x0 = (y0 - intercept)/slope
if( y0 > ydim-1): # intersect outside of top boundary if (y0 > ydim-1): # intersect outside of top boundary
y0 = ydim-1 y0 = ydim-1
x0 = (y0 - intercept)/slope x0 = (y0 - intercept)/slope
x1 = xdim-1. # need to make sure this will be a float? x1 = xdim-1. # need to make sure this will be a float?
y1 = x1*slope + intercept y1 = x1*slope + intercept
if( y1 < 0.): # intersect outside of bottom boundary if (y1 < 0.): # intersect outside of bottom boundary
y1 = 0. y1 = 0.
x1 = (y1 - intercept)/slope x1 = (y1 - intercept)/slope
if( y1 > ydim-1):# intersect outside of top boundary if (y1 > ydim-1): # intersect outside of top boundary
y1 = ydim-1 y1 = ydim-1
x1 = (y1 - intercept)/slope x1 = (y1 - intercept)/slope
elif start_point is not None and end_point is not None: elif start_point is not None and end_point is not None:
@ -164,14 +163,14 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None,
distance = (dx*dx + dy*dy)**0.5 distance = (dx*dx + dy*dy)**0.5
npts = int(distance) + 1 npts = int(distance) + 1
xy = np.zeros((npts,2), "float") xy = np.zeros((npts, 2), "float")
dx = dx/(npts-1) dx = dx/(npts-1)
dy = dy/(npts-1) dy = dy/(npts-1)
for i in py3range(npts): for i in py3range(npts):
xy[i,0] = x0 + i*dx xy[i, 0] = x0 + i*dx
xy[i,1] = y0 + i*dy xy[i, 1] = y0 + i*dy
return xy return xy
@ -237,8 +236,8 @@ def get_xy_z_params(z, pivot_point=None, angle=None,
var2dz = _interp2dxy(z, xy) var2dz = _interp2dxy(z, xy)
extra_dim_num = z.ndim - 3 extra_dim_num = z.ndim - 3
idx1 = tuple([0]*extra_dim_num + [0,0]) idx1 = tuple([0]*extra_dim_num + [0, 0])
idx2 = tuple([0]*extra_dim_num + [-1,0]) idx2 = tuple([0]*extra_dim_num + [-1, 0])
if levels is None: if levels is None:
# interp to constant z grid # interp to constant z grid
@ -256,7 +255,7 @@ def get_xy_z_params(z, pivot_point=None, angle=None,
z_var2d = np.zeros((autolevels), dtype=z.dtype) z_var2d = np.zeros((autolevels), dtype=z.dtype)
z_var2d[0] = z_min z_var2d[0] = z_min
for i in py3range(1,autolevels): for i in py3range(1, autolevels):
z_var2d[i] = z_var2d[0] + i*dz z_var2d[i] = z_var2d[0] + i*dz
else: else:
z_var2d = np.asarray(levels, z.dtype) z_var2d = np.asarray(levels, z.dtype)
@ -323,6 +322,7 @@ def get_xy(var, pivot_point=None, angle=None,
return xy return xy
def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None, def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
ll_point=None): ll_point=None):
"""Return the coordinate pairs in grid space. """Return the coordinate pairs in grid space.
@ -381,7 +381,7 @@ def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
""" """
if (wrfin is None and (projection is None or ll_point is None)): if (wrfin is None and (projection is None or ll_point is None)):
raise ValueError ("'wrfin' parameter or " raise ValueError("'wrfin' parameter or "
"'projection' and 'll_point' parameters " "'projection' and 'll_point' parameters "
"are required") "are required")
@ -389,7 +389,8 @@ def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
if wrfin is not None: if wrfin is not None:
xy_vals = _ll_to_xy(lat, lon, wrfin=wrfin, timeidx=timeidx, xy_vals = _ll_to_xy(lat, lon, wrfin=wrfin, timeidx=timeidx,
squeeze=True, meta=False, stagger=stagger, as_int=True) squeeze=True, meta=False, stagger=stagger,
as_int=True)
else: else:
map_proj = projection.map_proj map_proj = projection.map_proj
@ -427,9 +428,8 @@ def to_xy_coords(pairs, wrfin=None, timeidx=0, stagger=None, projection=None,
xy_vals = xy_vals.squeeze() xy_vals = xy_vals.squeeze()
if xy_vals.ndim == 1: if xy_vals.ndim == 1:
return CoordPair(x=xy_vals[0], y=xy_vals[1]) return CoordPair(x=xy_vals[0], y=xy_vals[1])
else: else:
return [CoordPair(x=xy_vals[0,i], y=xy_vals[1,i]) return [CoordPair(x=xy_vals[0, i], y=xy_vals[1, i])
for i in py3range(xy_vals.shape[1])] for i in py3range(xy_vals.shape[1])]

35
src/wrf/latlonutils.py

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

155
src/wrf/metadecorators.py

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

198
src/wrf/projection.py

@ -61,7 +61,8 @@ if cartopy_enabled():
super(crs.Mercator, self).__init__(proj4_params, globe=globe) super(crs.Mercator, self).__init__(proj4_params, globe=globe)
# Calculate limits. # Calculate limits.
limits = self.transform_points(crs.Geodetic(), limits = self.transform_points(
crs.Geodetic(),
np.array([-180, 180]) + central_longitude, np.array([-180, 180]) + central_longitude,
np.array([min_latitude, max_latitude])) np.array([min_latitude, max_latitude]))
@ -195,7 +196,6 @@ class WrfProj(object):
if self.stand_lon is None: if self.stand_lon is None:
self.stand_lon = self._cen_lon self.stand_lon = self._cen_lon
@staticmethod @staticmethod
def _context_equal(x, y, ctx): def _context_equal(x, y, ctx):
"""Return True if both objects are equal based on the provided context. """Return True if both objects are equal based on the provided context.
@ -230,7 +230,6 @@ class WrfProj(object):
return True return True
def __eq__(self, other): def __eq__(self, other):
"""Return True if this projection object is the same as *other*. """Return True if this projection object is the same as *other*.
@ -262,7 +261,6 @@ class WrfProj(object):
WrfProj._context_equal(self.dx, other.dx, ctx) and WrfProj._context_equal(self.dx, other.dx, ctx) and
WrfProj._context_equal(self.dy, other.dy, ctx)) WrfProj._context_equal(self.dy, other.dy, ctx))
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
return None return None
@ -275,17 +273,15 @@ class WrfProj(object):
def _calc_extents(self, geobounds): def _calc_extents(self, geobounds):
# Need to modify the extents for the new projection # Need to modify the extents for the new projection
pc = crs.PlateCarree() pc = crs.PlateCarree()
xs, ys, _ = self._cartopy().transform_points(pc, xs, ys, _ = self._cartopy().transform_points(
np.array([geobounds.bottom_left.lon, pc,
geobounds.top_right.lon]), np.array([geobounds.bottom_left.lon, geobounds.top_right.lon]),
np.array([geobounds.bottom_left.lat, np.array([geobounds.bottom_left.lat, geobounds.top_right.lat])).T
geobounds.top_right.lat])).T
_xlimits = xs.tolist() _xlimits = xs.tolist()
_ylimits = ys.tolist() _ylimits = ys.tolist()
return (_xlimits, _ylimits) return (_xlimits, _ylimits)
def _cart_extents(self, geobounds): def _cart_extents(self, geobounds):
try: try:
_ = len(geobounds) _ = len(geobounds)
@ -327,7 +323,7 @@ class WrfProj(object):
try: try:
_ = len(geobounds) _ = len(geobounds)
except TypeError: except TypeError:
x_extents= self._cart_extents(geobounds)[0] x_extents = self._cart_extents(geobounds)[0]
else: else:
extents = self._cart_extents(geobounds) extents = self._cart_extents(geobounds)
x_extents = np.empty(extents.shape, np.object) x_extents = np.empty(extents.shape, np.object)
@ -352,7 +348,7 @@ class WrfProj(object):
try: try:
_ = len(geobounds) _ = len(geobounds)
except TypeError: except TypeError:
y_extents= self._cart_extents(geobounds)[1] y_extents = self._cart_extents(geobounds)[1]
else: else:
extents = self._cart_extents(geobounds) extents = self._cart_extents(geobounds)
y_extents = np.empty(extents.shape, np.object) y_extents = np.empty(extents.shape, np.object)
@ -529,10 +525,9 @@ class LambertConformal(WrfProj):
if self.truelat2 is not None: if self.truelat2 is not None:
self._std_parallels.append(self.truelat2) self._std_parallels.append(self.truelat2)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
_cf_params["grid_mapping_name"] = "lambert_conformal_conic"; _cf_params["grid_mapping_name"] = "lambert_conformal_conic"
_cf_params["standard_parallel"] = self._std_parallels _cf_params["standard_parallel"] = self._std_parallels
_cf_params["longitude_of_central_meridian"] = self.stand_lon _cf_params["longitude_of_central_meridian"] = self.stand_lon
_cf_params["latitude_of_projection_origin"] = self.moad_cen_lat _cf_params["latitude_of_projection_origin"] = self.moad_cen_lat
@ -540,13 +535,11 @@ class LambertConformal(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
truelat2 = (self.truelat1 truelat2 = (self.truelat1 if _ismissing(self.truelat2)
if _ismissing(self.truelat2)
else self.truelat2) else self.truelat2)
_pyngl = Resources() _pyngl = Resources()
@ -567,22 +560,21 @@ class LambertConformal(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
local_kwargs = dict(projection = "lcc", local_kwargs = dict(projection="lcc",
lon_0 = self.stand_lon, lon_0=self.stand_lon,
lat_0 = self.moad_cen_lat, lat_0=self.moad_cen_lat,
lat_1 = self.truelat1, lat_1=self.truelat1,
lat_2 = self.truelat2, lat_2=self.truelat2,
llcrnrlat = geobounds.bottom_left.lat, llcrnrlat=geobounds.bottom_left.lat,
urcrnrlat = geobounds.top_right.lat, urcrnrlat=geobounds.top_right.lat,
llcrnrlon = geobounds.bottom_left.lon, llcrnrlon=geobounds.bottom_left.lon,
urcrnrlon = geobounds.top_right.lon, urcrnrlon=geobounds.top_right.lon,
rsphere = Constants.WRF_EARTH_RADIUS, rsphere=Constants.WRF_EARTH_RADIUS,
resolution = 'l') resolution='l')
local_kwargs.update(kwargs) local_kwargs.update(kwargs)
_basemap = Basemap(**local_kwargs) _basemap = Basemap(**local_kwargs)
@ -597,11 +589,11 @@ class LambertConformal(WrfProj):
cutoff = -30.0 if self.moad_cen_lat >= 0 else 30.0 cutoff = -30.0 if self.moad_cen_lat >= 0 else 30.0
_cartopy = crs.LambertConformal( _cartopy = crs.LambertConformal(
central_longitude = self.stand_lon, central_longitude=self.stand_lon,
central_latitude = self.moad_cen_lat, central_latitude=self.moad_cen_lat,
standard_parallels = self._std_parallels, standard_parallels=self._std_parallels,
globe = self._globe(), globe=self._globe(),
cutoff = cutoff) cutoff=cutoff)
return _cartopy return _cartopy
@ -650,16 +642,14 @@ class Mercator(WrfProj):
""" """
super(Mercator, self).__init__(**proj_params) super(Mercator, self).__init__(**proj_params)
self._lat_ts = (None self._lat_ts = (
if self.truelat1 == 0. or _ismissing(self.truelat1) None if self.truelat1 == 0. or _ismissing(self.truelat1)
else self.truelat1) else self.truelat1)
self._stand_lon = (0. if _ismissing(self.stand_lon, islat=False) self._stand_lon = (0. if _ismissing(self.stand_lon, islat=False)
else self.stand_lon) else self.stand_lon)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
_cf_params["grid_mapping_name"] = "mercator" _cf_params["grid_mapping_name"] = "mercator"
_cf_params["longitude_of_projection_origin"] = self.stand_lon _cf_params["longitude_of_projection_origin"] = self.stand_lon
@ -667,7 +657,6 @@ class Mercator(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -689,46 +678,40 @@ class Mercator(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
local_kwargs = dict(projection = "merc", local_kwargs = dict(projection="merc",
lon_0 = self._stand_lon, lon_0=self._stand_lon,
lat_0 = self.moad_cen_lat, lat_0=self.moad_cen_lat,
lat_ts = self._lat_ts, lat_ts=self._lat_ts,
llcrnrlat = geobounds.bottom_left.lat, llcrnrlat=geobounds.bottom_left.lat,
urcrnrlat = geobounds.top_right.lat, urcrnrlat=geobounds.top_right.lat,
llcrnrlon = geobounds.bottom_left.lon, llcrnrlon=geobounds.bottom_left.lon,
urcrnrlon = geobounds.top_right.lon, urcrnrlon=geobounds.top_right.lon,
rsphere = Constants.WRF_EARTH_RADIUS, rsphere=Constants.WRF_EARTH_RADIUS,
resolution = "l") resolution="l")
local_kwargs.update(kwargs) local_kwargs.update(kwargs)
_basemap = Basemap(**local_kwargs) _basemap = Basemap(**local_kwargs)
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
if self._lat_ts == 0.0: if self._lat_ts == 0.0:
_cartopy = crs.Mercator( _cartopy = crs.Mercator(central_longitude=self._stand_lon,
central_longitude = self._stand_lon, globe=self._globe())
globe = self._globe())
else: else:
_cartopy = MercatorWithLatTS( _cartopy = MercatorWithLatTS(central_longitude=self._stand_lon,
central_longitude = self._stand_lon, latitude_true_scale=self._lat_ts,
latitude_true_scale = self._lat_ts, globe=self._globe())
globe = self._globe())
return _cartopy return _cartopy
def _proj4(self): def _proj4(self):
_proj4 = ("+proj=merc +units=meters +a={} +b={} " _proj4 = ("+proj=merc +units=meters +a={} +b={} "
@ -740,6 +723,7 @@ class Mercator(WrfProj):
return _proj4 return _proj4
class PolarStereographic(WrfProj): class PolarStereographic(WrfProj):
"""A :class:`wrf.WrfProj` subclass for Polar Stereographic projections. """A :class:`wrf.WrfProj` subclass for Polar Stereographic projections.
@ -750,7 +734,6 @@ class PolarStereographic(WrfProj):
:class:`Mercator`, :class:`LambertConformal` :class:`Mercator`, :class:`LambertConformal`
""" """
def __init__(self, **proj_params): def __init__(self, **proj_params):
"""Initialize a :class:`wrf.PolarStereographic` object. """Initialize a :class:`wrf.PolarStereographic` object.
@ -770,10 +753,7 @@ class PolarStereographic(WrfProj):
""" """
super(PolarStereographic, self).__init__(**proj_params) super(PolarStereographic, self).__init__(**proj_params)
self._hemi = -90. if self.truelat1 < 0 else 90. self._hemi = -90. if self.truelat1 < 0 else 90.
self._lat_ts = (None self._lat_ts = (None if _ismissing(self.truelat1) else self.truelat1)
if _ismissing(self.truelat1)
else self.truelat1)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
@ -785,7 +765,6 @@ class PolarStereographic(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -811,28 +790,26 @@ class PolarStereographic(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
local_kwargs = dict(projection = "stere", local_kwargs = dict(projection="stere",
lon_0 = self.stand_lon, lon_0=self.stand_lon,
lat_0 = self._hemi, lat_0=self._hemi,
lat_ts = self._lat_ts, lat_ts=self._lat_ts,
llcrnrlat = geobounds.bottom_left.lat, llcrnrlat=geobounds.bottom_left.lat,
urcrnrlat = geobounds.top_right.lat, urcrnrlat=geobounds.top_right.lat,
llcrnrlon = geobounds.bottom_left.lon, llcrnrlon=geobounds.bottom_left.lon,
urcrnrlon = geobounds.top_right.lon, urcrnrlon=geobounds.top_right.lon,
rsphere = Constants.WRF_EARTH_RADIUS, rsphere=Constants.WRF_EARTH_RADIUS,
resolution = "l") resolution="l")
local_kwargs.update(kwargs) local_kwargs.update(kwargs)
_basemap = Basemap(**local_kwargs) _basemap = Basemap(**local_kwargs)
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
@ -843,7 +820,6 @@ class PolarStereographic(WrfProj):
globe=self._globe()) globe=self._globe())
return _cartopy return _cartopy
def _proj4(self): def _proj4(self):
_proj4 = ("+proj=stere +units=meters +a={} +b={} " _proj4 = ("+proj=stere +units=meters +a={} +b={} "
"+lat0={} +lon_0={} +lat_ts={} +nadgrids=@null".format( "+lat0={} +lon_0={} +lat_ts={} +nadgrids=@null".format(
@ -856,7 +832,6 @@ class PolarStereographic(WrfProj):
return _proj4 return _proj4
class LatLon(WrfProj): class LatLon(WrfProj):
"""A :class:`wrf.WrfProj` subclass for Lat Lon projections. """A :class:`wrf.WrfProj` subclass for Lat Lon projections.
@ -886,13 +861,11 @@ class LatLon(WrfProj):
""" """
super(LatLon, self).__init__(**proj_params) super(LatLon, self).__init__(**proj_params)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
_cf_params["grid_mapping_name"] = "latitude_longitude" _cf_params["grid_mapping_name"] = "latitude_longitude"
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -914,27 +887,25 @@ class LatLon(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
local_kwargs = dict(projection = "cyl", local_kwargs = dict(projection="cyl",
lon_0 = self.stand_lon, lon_0=self.stand_lon,
lat_0 = self.moad_cen_lat, lat_0=self.moad_cen_lat,
llcrnrlat = geobounds.bottom_left.lat, llcrnrlat=geobounds.bottom_left.lat,
urcrnrlat = geobounds.top_right.lat, urcrnrlat=geobounds.top_right.lat,
llcrnrlon = geobounds.bottom_left.lon, llcrnrlon=geobounds.bottom_left.lon,
urcrnrlon = geobounds.top_right.lon, urcrnrlon=geobounds.top_right.lon,
rsphere = Constants.WRF_EARTH_RADIUS, rsphere=Constants.WRF_EARTH_RADIUS,
resolution = "l") resolution="l")
local_kwargs.update(kwargs) local_kwargs.update(kwargs)
_basemap = Basemap(**local_kwargs) _basemap = Basemap(**local_kwargs)
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
@ -944,19 +915,19 @@ class LatLon(WrfProj):
return _cartopy return _cartopy
def _cart_extents(self, geobounds): def _cart_extents(self, geobounds):
return ([geobounds.bottom_left.lon, geobounds.top_right.lon], return ([geobounds.bottom_left.lon, geobounds.top_right.lon],
[geobounds.bottom_left.lat, geobounds.top_right.lat]) [geobounds.bottom_left.lat, geobounds.top_right.lat])
def _proj4(self): def _proj4(self):
_proj4 = ("+proj=eqc +units=meters +a={} +b={} " _proj4 = ("+proj=eqc +units=meters +a={} +b={} "
"+lon_0={} +nadgrids=@null".format(Constants.WRF_EARTH_RADIUS, "+lon_0={} +nadgrids=@null".format(
Constants.WRF_EARTH_RADIUS,
Constants.WRF_EARTH_RADIUS, Constants.WRF_EARTH_RADIUS,
self.stand_lon)) self.stand_lon))
return _proj4 return _proj4
# Notes (may not be correct since this projection confuses me): # Notes (may not be correct since this projection confuses me):
# Each projection system handles this differently. # Each projection system handles this differently.
# 1) In WRF, if following the WPS instructions, POLE_LON is mainly used to # 1) In WRF, if following the WPS instructions, POLE_LON is mainly used to
@ -1042,7 +1013,7 @@ class RotatedLatLon(WrfProj):
self._bm_lon_0 = (-self.stand_lon if self._north self._bm_lon_0 = (-self.stand_lon if self._north
else 180.0 - self.stand_lon) else 180.0 - self.stand_lon)
self._bm_cart_pole_lat = (self.pole_lat if self._north self._bm_cart_pole_lat = (self.pole_lat if self._north
else -self.pole_lat ) else -self.pole_lat)
# The important point is that pole longitude is the position # The important point is that pole longitude is the position
# of the dateline of the new projection, not its central # of the dateline of the new projection, not its central
# longitude (per the creator of cartopy). This is based on # longitude (per the creator of cartopy). This is based on
@ -1059,7 +1030,6 @@ class RotatedLatLon(WrfProj):
self._cart_pole_lon = (-self.stand_lon - 180.0 if self._north self._cart_pole_lon = (-self.stand_lon - 180.0 if self._north
else -self.stand_lon) else -self.stand_lon)
def _cf_params(self): def _cf_params(self):
_cf_params = {} _cf_params = {}
# Assuming this follows the same guidelines as cartopy # Assuming this follows the same guidelines as cartopy
@ -1070,7 +1040,6 @@ class RotatedLatLon(WrfProj):
return _cf_params return _cf_params
def _pyngl(self, geobounds, **kwargs): def _pyngl(self, geobounds, **kwargs):
if not pyngl_enabled(): if not pyngl_enabled():
return None return None
@ -1092,38 +1061,35 @@ class RotatedLatLon(WrfProj):
return _pyngl return _pyngl
def _basemap(self, geobounds, **kwargs): def _basemap(self, geobounds, **kwargs):
if not basemap_enabled(): if not basemap_enabled():
return None return None
local_kwargs = dict(projection = "rotpole", local_kwargs = dict(projection="rotpole",
o_lat_p = self._bm_cart_pole_lat, o_lat_p=self._bm_cart_pole_lat,
o_lon_p = self.pole_lon, o_lon_p=self.pole_lon,
llcrnrlat = geobounds.bottom_left.lat, llcrnrlat=geobounds.bottom_left.lat,
urcrnrlat = geobounds.top_right.lat, urcrnrlat=geobounds.top_right.lat,
llcrnrlon = geobounds.bottom_left.lon, llcrnrlon=geobounds.bottom_left.lon,
urcrnrlon = geobounds.top_right.lon, urcrnrlon=geobounds.top_right.lon,
lon_0 = self._bm_lon_0, lon_0=self._bm_lon_0,
rsphere = Constants.WRF_EARTH_RADIUS, rsphere=Constants.WRF_EARTH_RADIUS,
resolution = "l") resolution="l")
local_kwargs.update(kwargs) local_kwargs.update(kwargs)
_basemap = Basemap(**local_kwargs) _basemap = Basemap(**local_kwargs)
return _basemap return _basemap
def _cartopy(self): def _cartopy(self):
if not cartopy_enabled(): if not cartopy_enabled():
return None return None
_cartopy = crs.RotatedPole( _cartopy = crs.RotatedPole(pole_longitude=self._cart_pole_lon,
pole_longitude=self._cart_pole_lon,
pole_latitude=self._bm_cart_pole_lat, pole_latitude=self._bm_cart_pole_lat,
central_rotated_longitude=( central_rotated_longitude=(
180.0 - self.pole_lon), # Probably 180.0 - self.pole_lon), # Probably
globe = self._globe()) globe=self._globe())
return _cartopy return _cartopy
def _proj4(self): def _proj4(self):
@ -1138,6 +1104,7 @@ class RotatedLatLon(WrfProj):
return _proj4 return _proj4
def getproj(**proj_params): def getproj(**proj_params):
"""Return a :class:`wrf.WrfProj` subclass. """Return a :class:`wrf.WrfProj` subclass.
@ -1164,7 +1131,6 @@ def getproj(**proj_params):
specified map projection parameters. specified map projection parameters.
""" """
up_proj_params = dict_keys_to_upper(proj_params) up_proj_params = dict_keys_to_upper(proj_params)
proj_type = up_proj_params.get("MAP_PROJ", 0) proj_type = up_proj_params.get("MAP_PROJ", 0)
@ -1184,5 +1150,3 @@ def getproj(**proj_params):
else: else:
# Unknown projection # Unknown projection
return WrfProj(**proj_params) return WrfProj(**proj_params)

3
src/wrf/projutils.py

@ -2,6 +2,7 @@ from __future__ import (absolute_import, division, print_function)
from .py3compat import viewitems from .py3compat import viewitems
def dict_keys_to_upper(d): def dict_keys_to_upper(d):
"""Return a dictionary with the keys changed to uppercase. """Return a dictionary with the keys changed to uppercase.
@ -14,4 +15,4 @@ def dict_keys_to_upper(d):
:obj:`dict`: A dictionary with uppercase keys. :obj:`dict`: A dictionary with uppercase keys.
""" """
return {key.upper() : val for key, val in viewitems(d)} return {key.upper(): val for key, val in viewitems(d)}

4
src/wrf/py3compat.py

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

303
src/wrf/routines.py

@ -16,7 +16,8 @@ from .g_pressure import get_pressure, get_pressure_hpa
from .g_pw import get_pw from .g_pw import get_pw
from .g_rh import get_rh, get_rh_2m from .g_rh import get_rh, get_rh_2m
from .g_slp import get_slp from .g_slp import get_slp
from .g_temp import get_tc, get_eth, get_temp, get_theta, get_tk, get_tv, get_tw from .g_temp import (get_tc, get_eth, get_temp, get_theta, get_tk, get_tv,
get_tw)
from .g_terrain import get_terrain from .g_terrain import get_terrain
from .g_uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir, from .g_uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir,
get_uvmet_wspd_wdir, get_uvmet_wspd, get_uvmet_wdir, get_uvmet_wspd_wdir, get_uvmet_wspd, get_uvmet_wdir,
@ -33,167 +34,168 @@ from .g_cloudfrac import (get_cloudfrac, get_low_cloudfrac, get_mid_cloudfrac,
# func is the function to call. kargs are required arguments that should # func is the function to call. kargs are required arguments that should
# not be altered by the user # not be altered by the user
_FUNC_MAP = {"cape2d" : get_2dcape, _FUNC_MAP = {"cape2d": get_2dcape,
"cape3d" : get_3dcape, "cape3d": get_3dcape,
"dbz" : get_dbz, "dbz": get_dbz,
"maxdbz" : get_max_dbz, "maxdbz": get_max_dbz,
"dp" : get_dp, "dp": get_dp,
"dp2m" : get_dp_2m, "dp2m": get_dp_2m,
"height" : get_height, "height": get_height,
"geopt" : get_geopt, "geopt": get_geopt,
"srh" : get_srh, "srh": get_srh,
"uhel" : get_uh, "uhel": get_uh,
"omega" : get_omega, "omega": get_omega,
"pw" : get_pw, "pw": get_pw,
"rh" : get_rh, "rh": get_rh,
"rh2m" : get_rh_2m, "rh2m": get_rh_2m,
"slp" : get_slp, "slp": get_slp,
"theta" : get_theta, "theta": get_theta,
"temp" : get_temp, "temp": get_temp,
"tk" : get_tk, "tk": get_tk,
"tc" : get_tc, "tc": get_tc,
"theta_e" : get_eth, "theta_e": get_eth,
"tv" : get_tv, "tv": get_tv,
"twb" : get_tw, "twb": get_tw,
"terrain" : get_terrain, "terrain": get_terrain,
"times" : get_times, "times": get_times,
"xtimes" : get_xtimes, "xtimes": get_xtimes,
"uvmet" : get_uvmet, "uvmet": get_uvmet,
"uvmet10" : get_uvmet10, "uvmet10": get_uvmet10,
"avo" : get_avo, "avo": get_avo,
"pvo" : get_pvo, "pvo": get_pvo,
"ua" : get_u_destag, "ua": get_u_destag,
"va" : get_v_destag, "va": get_v_destag,
"wa" : get_w_destag, "wa": get_w_destag,
"lat" : get_lat, "lat": get_lat,
"lon" : get_lon, "lon": get_lon,
"pressure" : get_pressure_hpa, "pressure": get_pressure_hpa,
"pres" : get_pressure, "pres": get_pressure,
"wspd_wdir" : get_destag_wspd_wdir, "wspd_wdir": get_destag_wspd_wdir,
"wspd_wdir10" : get_destag_wspd_wdir10, "wspd_wdir10": get_destag_wspd_wdir10,
"uvmet_wspd_wdir" : get_uvmet_wspd_wdir, "uvmet_wspd_wdir": get_uvmet_wspd_wdir,
"uvmet10_wspd_wdir" : get_uvmet10_wspd_wdir, "uvmet10_wspd_wdir": get_uvmet10_wspd_wdir,
"ctt" : get_ctt, "ctt": get_ctt,
"cloudfrac" : get_cloudfrac, "cloudfrac": get_cloudfrac,
"geopt_stag" : get_stag_geopt, "geopt_stag": get_stag_geopt,
"zstag" : get_stag_height, "zstag": get_stag_height,
# Diagnostics below are extracted from multi-product diagnostics # Diagnostics below are extracted from multi-product diagnostics
"cape2d_only" : get_cape2d_only, "cape2d_only": get_cape2d_only,
"cin2d_only" : get_cin2d_only, "cin2d_only": get_cin2d_only,
"lcl" : get_lcl, "lcl": get_lcl,
"lfc" : get_lfc, "lfc": get_lfc,
"cape3d_only" : get_3dcape_only, "cape3d_only": get_3dcape_only,
"cin3d_only": get_3dcin_only, "cin3d_only": get_3dcin_only,
"uvmet_wspd" : get_uvmet_wspd, "uvmet_wspd": get_uvmet_wspd,
"uvmet_wdir" : get_uvmet_wdir, "uvmet_wdir": get_uvmet_wdir,
"uvmet10_wspd" : get_uvmet10_wspd, "uvmet10_wspd": get_uvmet10_wspd,
"uvmet10_wdir" : get_uvmet10_wdir, "uvmet10_wdir": get_uvmet10_wdir,
"wspd" : get_destag_wspd, "wspd": get_destag_wspd,
"wdir" : get_destag_wdir, "wdir": get_destag_wdir,
"wspd10" : get_destag_wspd10, "wspd10": get_destag_wspd10,
"wdir10" : get_destag_wdir10, "wdir10": get_destag_wdir10,
"low_cloudfrac" : get_low_cloudfrac, "low_cloudfrac": get_low_cloudfrac,
"mid_cloudfrac" : get_mid_cloudfrac, "mid_cloudfrac": get_mid_cloudfrac,
"high_cloudfrac" : get_high_cloudfrac "high_cloudfrac": get_high_cloudfrac
} }
_VALID_KARGS = {"cape2d" : ["missing"], _VALID_KARGS = {"cape2d": ["missing"],
"cape3d" : ["missing"], "cape3d": ["missing"],
"dbz" : ["do_variant", "do_liqskin"], "dbz": ["do_variant", "do_liqskin"],
"maxdbz" : ["do_variant", "do_liqskin"], "maxdbz": ["do_variant", "do_liqskin"],
"dp" : ["units"], "dp": ["units"],
"dp2m" : ["units"], "dp2m": ["units"],
"height" : ["msl", "units"], "height": ["msl", "units"],
"geopt" : [], "geopt": [],
"srh" : ["top"], "srh": ["top"],
"uhel" : ["bottom", "top"], "uhel": ["bottom", "top"],
"omega" : [], "omega": [],
"pw" : [], "pw": [],
"rh" : [], "rh": [],
"rh2m" : [], "rh2m": [],
"slp" : ["units"], "slp": ["units"],
"temp" : ["units"], "temp": ["units"],
"tk" : [], "tk": [],
"tc" : [], "tc": [],
"theta" : ["units"], "theta": ["units"],
"theta_e" : ["units"], "theta_e": ["units"],
"tv" : ["units"], "tv": ["units"],
"twb" : ["units"], "twb": ["units"],
"terrain" : ["units"], "terrain": ["units"],
"times" : [], "times": [],
"xtimes" : [], "xtimes": [],
"uvmet" : ["units"], "uvmet": ["units"],
"uvmet10" : ["units"], "uvmet10": ["units"],
"avo" : [], "avo": [],
"pvo" : [], "pvo": [],
"ua" : ["units"], "ua": ["units"],
"va" : ["units"], "va": ["units"],
"wa" : ["units"], "wa": ["units"],
"lat" : [], "lat": [],
"lon" : [], "lon": [],
"pres" : ["units"], "pres": ["units"],
"pressure" : ["units"], "pressure": ["units"],
"wspd_wdir" : ["units"], "wspd_wdir": ["units"],
"wspd_wdir10" : ["units"], "wspd_wdir10": ["units"],
"uvmet_wspd_wdir" : ["units"], "uvmet_wspd_wdir": ["units"],
"uvmet10_wspd_wdir" : ["units"], "uvmet10_wspd_wdir": ["units"],
"ctt" : ["fill_nocloud", "missing", "opt_thresh", "units"], "ctt": ["fill_nocloud", "missing", "opt_thresh", "units"],
"cloudfrac" : ["vert_type", "low_thresh", "cloudfrac": ["vert_type", "low_thresh",
"mid_thresh", "high_thresh"], "mid_thresh", "high_thresh"],
"geopt_stag" : [], "geopt_stag": [],
"zstag" : ["msl", "units"], "zstag": ["msl", "units"],
"cape2d_only" : ["missing"], "cape2d_only": ["missing"],
"cin2d_only" : ["missing"], "cin2d_only": ["missing"],
"lcl" : ["missing"], "lcl": ["missing"],
"lfc" : ["missing"], "lfc": ["missing"],
"cape3d_only" : ["missing"], "cape3d_only": ["missing"],
"cin3d_only": ["missing"], "cin3d_only": ["missing"],
"uvmet_wspd" : ["units"], "uvmet_wspd": ["units"],
"uvmet_wdir" : ["units"], "uvmet_wdir": ["units"],
"uvmet10_wspd" : ["units"], "uvmet10_wspd": ["units"],
"uvmet10_wdir" : ["units"], "uvmet10_wdir": ["units"],
"wspd" : ["units"], "wspd": ["units"],
"wdir" : ["units"], "wdir": ["units"],
"wspd10" : ["units"], "wspd10": ["units"],
"wdir10" : ["units"], "wdir10": ["units"],
"low_cloudfrac" : ["vert_type", "low_thresh", "low_cloudfrac": ["vert_type", "low_thresh",
"mid_thresh", "high_thresh"], "mid_thresh", "high_thresh"],
"mid_cloudfrac" : ["vert_type", "low_thresh", "mid_cloudfrac": ["vert_type", "low_thresh",
"mid_thresh", "high_thresh"], "mid_thresh", "high_thresh"],
"high_cloudfrac" : ["vert_type", "low_thresh", "high_cloudfrac": ["vert_type", "low_thresh",
"mid_thresh", "high_thresh"], "mid_thresh", "high_thresh"],
"default" : [] "default": []
} }
_ALIASES = {"cape_2d" : "cape2d", _ALIASES = {"cape_2d": "cape2d",
"cape_3d" : "cape3d", "cape_3d": "cape3d",
"eth" : "theta_e", "eth": "theta_e",
"mdbz" : "maxdbz", "mdbz": "maxdbz",
"geopotential" : "geopt", "geopotential": "geopt",
"helicity" : "srh", "helicity": "srh",
"latitude" : "lat", "latitude": "lat",
"longitude" : "lon", "longitude": "lon",
"omg" : "omega", "omg": "omega",
"p" : "pres", "p": "pres",
"rh2" : "rh2m", "rh2": "rh2m",
"z": "height", "z": "height",
"ter" : "terrain", "ter": "terrain",
"updraft_helicity" : "uhel", "updraft_helicity": "uhel",
"td" : "dp", "td": "dp",
"td2" : "dp2m", "td2": "dp2m",
"cfrac" : "cloudfrac", "cfrac": "cloudfrac",
"wspd_wdir_uvmet" : "uvmet_wspd_wdir", "wspd_wdir_uvmet": "uvmet_wspd_wdir",
"wspd_wdir_uvmet10" : "uvmet10_wspd_wdir", "wspd_wdir_uvmet10": "uvmet10_wspd_wdir",
"th" : "theta", "th": "theta",
"low_cfrac" : "low_cloudfrac", "low_cfrac": "low_cloudfrac",
"mid_cfrac" : "mid_cloudfrac", "mid_cfrac": "mid_cloudfrac",
"high_cfrac" : "high_cloudfrac", "high_cfrac": "high_cloudfrac",
"wspd_uvmet" : "uvmet_wspd" , "wspd_uvmet": "uvmet_wspd",
"wdir_uvmet" : "uvmet_wdir" , "wdir_uvmet": "uvmet_wdir",
"wspd_uvmet10" : "uvmet10_wspd" , "wspd_uvmet10": "uvmet10_wspd",
"wdir_uvmet10" : "uvmet10_wdir" , "wdir_uvmet10": "uvmet10_wdir",
} }
class ArgumentError(Exception): class ArgumentError(Exception):
def __init__(self, msg): def __init__(self, msg):
self.msg = msg self.msg = msg
@ -201,6 +203,7 @@ class ArgumentError(Exception):
def __str__(self): def __str__(self):
return self.msg return self.msg
def _undo_alias(alias): def _undo_alias(alias):
actual = _ALIASES.get(alias, None) actual = _ALIASES.get(alias, None)
if actual is None: if actual is None:
@ -208,6 +211,7 @@ def _undo_alias(alias):
else: else:
return actual return actual
def _check_kargs(var, kargs): def _check_kargs(var, kargs):
for arg in viewkeys(kargs): for arg in viewkeys(kargs):
if arg not in _VALID_KARGS[var]: if arg not in _VALID_KARGS[var]:
@ -340,10 +344,9 @@ def getvar(wrfin, varname, timeidx=0,
actual_var = _undo_alias(varname) actual_var = _undo_alias(varname)
if actual_var not in _VALID_KARGS: if actual_var not in _VALID_KARGS:
raise ValueError("'%s' is not a valid variable name" % (varname)) raise ValueError("'{}' is not a valid variable name".format(varname))
_check_kargs(actual_var, kwargs) _check_kargs(actual_var, kwargs)
return _FUNC_MAP[actual_var](wrfin, timeidx, method, squeeze, cache, return _FUNC_MAP[actual_var](wrfin, timeidx, method, squeeze, cache,
meta, _key, **kwargs) meta, _key, **kwargs)

49
src/wrf/specialdec.py

@ -99,8 +99,6 @@ def uvmet_left_iter(alg_dtype=np.float64):
if (u.shape[-1] == lat.shape[-1] or u.shape[-2] == lat.shape[-2]): if (u.shape[-1] == lat.shape[-1] or u.shape[-2] == lat.shape[-2]):
raise ValueError("v is staggered but u is not") raise ValueError("v is staggered but u is not")
# No special left side iteration, return the function result # No special left side iteration, return the function result
if (num_left_dims_u == 0): if (num_left_dims_u == 0):
return wrapped(u, v, lat, lon, cen_long, cone, isstag=is_stag, return wrapped(u, v, lat, lon, cen_long, cone, isstag=is_stag,
@ -132,15 +130,14 @@ def uvmet_left_iter(alg_dtype=np.float64):
lat_left_and_slice = left_and_slice_idxs lat_left_and_slice = left_and_slice_idxs
elif mode == 2: elif mode == 2:
# Only need the left-most # Only need the left-most
lat_left_and_slice = tuple(left_idx lat_left_and_slice = tuple(
for left_idx in left_idxs[0:num_left_dims_lat]) left_idx for left_idx in left_idxs[0:num_left_dims_lat])
u_output_idxs = (0,) + left_idxs + (slice(None),) u_output_idxs = (0,) + left_idxs + (slice(None),)
v_output_idxs = (1,) + left_idxs + (slice(None),) v_output_idxs = (1,) + left_idxs + (slice(None),)
u_view_idxs = left_idxs + (0, slice(None)) u_view_idxs = left_idxs + (0, slice(None))
v_view_idxs = left_idxs + (1, slice(None)) v_view_idxs = left_idxs + (1, slice(None))
new_u = u[left_and_slice_idxs] new_u = u[left_and_slice_idxs]
new_v = v[left_and_slice_idxs] new_v = v[left_and_slice_idxs]
new_lat = lat[lat_left_and_slice] new_lat = lat[lat_left_and_slice]
@ -186,7 +183,6 @@ def uvmet_left_iter(alg_dtype=np.float64):
return func_wrapper return func_wrapper
def cape_left_iter(alg_dtype=np.float64): def cape_left_iter(alg_dtype=np.float64):
"""A decorator to handle iterating over the leftmost dimensions for the """A decorator to handle iterating over the leftmost dimensions for the
cape diagnostic. cape diagnostic.
@ -250,10 +246,10 @@ def cape_left_iter(alg_dtype=np.float64):
if p_hpa[bot_idxs] > p_hpa[top_idxs]: if p_hpa[bot_idxs] > p_hpa[top_idxs]:
flip = True flip = True
p_hpa = np.ascontiguousarray(p_hpa[...,::-1,:,:]) p_hpa = np.ascontiguousarray(p_hpa[..., ::-1, :, :])
tk = np.ascontiguousarray(tk[...,::-1,:,:]) tk = np.ascontiguousarray(tk[..., ::-1, :, :])
qv = np.ascontiguousarray(qv[...,::-1,:,:]) qv = np.ascontiguousarray(qv[..., ::-1, :, :])
ht = np.ascontiguousarray(ht[...,::-1,:,:]) ht = np.ascontiguousarray(ht[..., ::-1, :, :])
new_args[0] = p_hpa new_args[0] = p_hpa
new_args[1] = tk new_args[1] = tk
new_args[2] = qv new_args[2] = qv
@ -279,8 +275,8 @@ def cape_left_iter(alg_dtype=np.float64):
new_args[1] = tk.reshape((1, 1, tk.shape[0]), order='F') new_args[1] = tk.reshape((1, 1, tk.shape[0]), order='F')
new_args[2] = qv.reshape((1, 1, qv.shape[0]), order='F') new_args[2] = qv.reshape((1, 1, qv.shape[0]), order='F')
new_args[3] = ht.reshape((1, 1, ht.shape[0]), order='F') new_args[3] = ht.reshape((1, 1, ht.shape[0]), order='F')
new_args[4] = np.full((1,1), ter, orig_dtype) new_args[4] = np.full((1, 1), ter, orig_dtype)
new_args[5] = np.full((1,1), sfp, orig_dtype) new_args[5] = np.full((1, 1), sfp, orig_dtype)
num_left_dims = 0 num_left_dims = 0
@ -298,11 +294,11 @@ def cape_left_iter(alg_dtype=np.float64):
output = np.empty(output_dims, orig_dtype) output = np.empty(output_dims, orig_dtype)
if flip and not is2d: if flip and not is2d:
output[0,:] = cape[::-1,:,:] output[0, :] = cape[::-1, :, :]
output[1,:] = cin[::-1,:,:] output[1, :] = cin[::-1, :, :]
else: else:
output[0,:] = cape[:] output[0, :] = cape[:]
output[1,:] = cin[:] output[1, :] = cin[:]
return output return output
@ -329,9 +325,9 @@ def cape_left_iter(alg_dtype=np.float64):
cape_output_idxs = (0,) + left_idxs + (slice(None),) cape_output_idxs = (0,) + left_idxs + (slice(None),)
cin_output_idxs = (1,) + left_idxs + (slice(None),) cin_output_idxs = (1,) + left_idxs + (slice(None),)
view_cape_reverse_idxs = left_idxs + (0, slice(None,None,-1), view_cape_reverse_idxs = left_idxs + (0, slice(None, None, -1),
slice(None)) slice(None))
view_cin_reverse_idxs = left_idxs + (1, slice(None,None,-1), view_cin_reverse_idxs = left_idxs + (1, slice(None, None, -1),
slice(None)) slice(None))
new_args[0] = p_hpa[left_and_slice_idxs] new_args[0] = p_hpa[left_and_slice_idxs]
@ -374,7 +370,6 @@ def cape_left_iter(alg_dtype=np.float64):
capeview.__array_interface__["data"][0]): capeview.__array_interface__["data"][0]):
raise RuntimeError("output array was copied") raise RuntimeError("output array was copied")
if flip and not is2d: if flip and not is2d:
output[cape_output_idxs] = ( output[cape_output_idxs] = (
outview_array[view_cape_reverse_idxs].astype(orig_dtype)) outview_array[view_cape_reverse_idxs].astype(orig_dtype))
@ -435,14 +430,14 @@ def cloudfrac_left_iter(alg_dtype=np.float64):
output_dims += vert.shape[-2:] output_dims += vert.shape[-2:]
output = np.empty(output_dims, orig_dtype) output = np.empty(output_dims, orig_dtype)
output[0,:] = low[:] output[0, :] = low[:]
output[1,:] = mid[:] output[1, :] = mid[:]
output[2,:] = high[:] output[2, :] = high[:]
return output return output
# Initial output is ...,low_mid_high,nz,ny,nx to create contiguous
# Initial output is ...,low_mid_high,nz,ny,nx to create contiguous views # views
outdims = vert.shape[0:num_left_dims] outdims = vert.shape[0:num_left_dims]
extra_dims = tuple(outdims) # Copy the left-most dims for iteration extra_dims = tuple(outdims) # Copy the left-most dims for iteration
@ -548,8 +543,8 @@ def interplevel_left_iter(is2dlev, alg_dtype=np.float64):
output = np.empty(outshape, dtype=alg_dtype) output = np.empty(outshape, dtype=alg_dtype)
for i in py3range(field3d.shape[0]): for i in py3range(field3d.shape[0]):
new_args[0] = field3d[i,:] new_args[0] = field3d[i, :]
new_kwargs["outview"] = output[i,:] new_kwargs["outview"] = output[i, :]
_ = wrapped(*new_args, **new_kwargs) _ = wrapped(*new_args, **new_kwargs)
else: else:
output = wrapped(*args, **kwargs) output = wrapped(*args, **kwargs)
@ -579,7 +574,6 @@ def interplevel_left_iter(is2dlev, alg_dtype=np.float64):
else: else:
z_slice_idxs = left_idxs + (slice(None),) z_slice_idxs = left_idxs + (slice(None),)
new_args[0] = field3d[field_out_slice_idxs] new_args[0] = field3d[field_out_slice_idxs]
new_args[1] = z[z_slice_idxs] new_args[1] = z[z_slice_idxs]
@ -695,4 +689,3 @@ def check_interplevel_args(is2dlev):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return func_wrapper return func_wrapper

276
src/wrf/units.py

@ -129,175 +129,170 @@ def _apply_temp_conv(var, var_unit, dest_unit):
# A mapping of unit names to their dictionary key names # A mapping of unit names to their dictionary key names
_UNIT_ALIASES = {"mps" : "m s-1", _UNIT_ALIASES = {"mps": "m s-1",
"m/s" : "m s-1", "m/s": "m s-1",
"ms-1" : "m s-1", "ms-1": "m s-1",
"meters_per_second" : "m s-1", "meters_per_second": "m s-1",
"metres_per_second" : "m s-1", "metres_per_second": "m s-1",
"knots" : "kt", "knots": "kt",
"knot" : "kt", "knot": "kt",
"kts" : "kt", "kts": "kt",
"kn" : "kt", "kn": "kt",
"miles_per_hour" : "mi h-1", "miles_per_hour": "mi h-1",
"mih-1" : "mi h-1", "mih-1": "mi h-1",
"mph" : "mi h-1", "mph": "mi h-1",
"mi/h" : "mi h-1", "mi/h": "mi h-1",
"kmph" : "km h-1", "kmph": "km h-1",
"kmh-1" : "km h-1", "kmh-1": "km h-1",
"km/h" : "km h-1", "km/h": "km h-1",
"kilometers_per_hour" : "km h-1", "kilometers_per_hour": "km h-1",
"kilometres_per_hour" : "km h-1", "kilometres_per_hour": "km h-1",
"ft/s" : "ft s-1", "ft/s": "ft s-1",
"ft/sec" : "ft s-1", "ft/sec": "ft s-1",
"fps" : "ft s-1", "fps": "ft s-1",
"fs-1" : "ft s-1", "fs-1": "ft s-1",
"feet_per_second" : "ft s-1", "feet_per_second": "ft s-1",
"pascal": "pa",
"pascal" : "pa", "pascals": "pa",
"pascals" : "pa", "hecto_pascal": "hpa",
"hecto_pascal" : "hpa", "hecto_pascals": "hpa",
"hecto_pascals" : "hpa", "millibar": "mb",
"millibar" : "mb", "millibars": "mb",
"millibars" : "mb", "mbar": "mb",
"mbar" : "mb", "kelvin": "k",
"degree_kelvin": "k",
"kelvin" : "k", "degrees_kelvin": "k",
"degree_kelvin" : "k", "degree_k": "k",
"degrees_kelvin" : "k", "degrees_k": "k",
"degree_k" : "k", "degreek": "k",
"degrees_k" : "k", "degreesk": "k",
"degreek" : "k", "degk": "k",
"degreesk" : "k", "degsk": "k",
"degk" : "k", "deg_k": "k",
"degsk" : "k", "degs_k": "k",
"deg_k" : "k", "deg k": "k",
"degs_k" : "k", "degs k": "k",
"deg k" : "k", "celsius": "c",
"degs k" : "k", "degree_celsius": "c",
"degrees_celsius": "c",
"celsius" : "c", "degree_c": "c",
"degree_celsius" : "c", "degrees_c": "c",
"degrees_celsius" : "c", "degreec": "c",
"degree_c" : "c", "degreesc": "c",
"degrees_c" : "c", "degc": "c",
"degreec" : "c", "degsc": "c",
"degreesc" : "c", "deg_c": "c",
"degc" : "c", "degs_c": "c",
"degsc" : "c", "deg c": "c",
"deg_c" : "c", "degs c": "c",
"degs_c" : "c", "fahrenheit": "f",
"deg c" : "c", "degree_fahrenheit": "f",
"degs c" : "c", "degrees_fahrenheit": "f",
"degree_f": "f",
"fahrenheit" : "f", "degrees_f": "f",
"degree_fahrenheit" : "f", "degreef": "f",
"degrees_fahrenheit" : "f", "degreesf": "f",
"degree_f" : "f", "degf": "f",
"degrees_f" : "f", "degsf": "f",
"degreef" : "f", "deg_f": "f",
"degreesf" : "f", "degs_f": "f",
"degf" : "f", "deg f": "f",
"degsf" : "f", "degs f": "f",
"deg_f" : "f", "meter": "m",
"degs_f" : "f", "meters": "m",
"deg f" : "f", "metre": "m",
"degs f" : "f", "metres": "m",
"kilometer": "km",
"meter" : "m", "kilometers": "km",
"meters" : "m", "dekameter": "dm",
"metre" : "m", "dekameters": "dm",
"metres" : "m", "decameter": "dm",
"kilometer" : "km", "decameters": "dm",
"kilometers" : "km", "dekametre": "dm",
"dekameter" : "dm", "dekametres": "dm",
"dekameters" : "dm", "decametre": "dm",
"decameter" : "dm", "decametres": "dm",
"decameters" : "dm", "dam": "dm",
"dekametre" : "dm", "dkm": "dm",
"dekametres" : "dm", "feet": "ft",
"decametre" : "dm", "foot": "ft",
"decametres" : "dm", "mile": "mi",
"dam" : "dm", "miles": "mi"
"dkm" : "dm",
"feet" : "ft",
"foot" : "ft",
"mile" : "mi",
"miles" : "mi"
} }
# A mapping of unit types to the avaible units # A mapping of unit types to the avaible units
_VALID_UNITS = {"wind" : ["m s-1", "kt", "mi h-1", "km h-1", "ft s-1"], _VALID_UNITS = {"wind": ["m s-1", "kt", "mi h-1", "km h-1", "ft s-1"],
"pressure" : ["pa", "hpa", "mb", "torr", "mmhg", "atm"], "pressure": ["pa", "hpa", "mb", "torr", "mmhg", "atm"],
"temp" : ["k", "f", "c"], "temp": ["k", "f", "c"],
"height" : ["m", "km", "dm", "ft", "mi"] "height": ["m", "km", "dm", "ft", "mi"]
} }
# Conversion factor map for wind from base units # Conversion factor map for wind from base units
_WIND_BASE_FACTORS = {"kt" : ConversionFactors.MPS_TO_KTS, _WIND_BASE_FACTORS = {"kt": ConversionFactors.MPS_TO_KTS,
"km h-1" : ConversionFactors.MPS_TO_KMPH, "km h-1": ConversionFactors.MPS_TO_KMPH,
"mi h-1" : ConversionFactors.MPS_TO_MPH, "mi h-1": ConversionFactors.MPS_TO_MPH,
"ft s-1" : ConversionFactors.MPS_TO_FPS "ft s-1": ConversionFactors.MPS_TO_FPS
} }
# Conversion factor map to base units # Conversion factor map to base units
_WIND_TOBASE_FACTORS = {"kt" : 1.0/ConversionFactors.MPS_TO_KTS, _WIND_TOBASE_FACTORS = {"kt": 1.0/ConversionFactors.MPS_TO_KTS,
"km h-1" : 1.0/ConversionFactors.MPS_TO_KMPH, "km h-1": 1.0/ConversionFactors.MPS_TO_KMPH,
"mi h-1" : 1.0/ConversionFactors.MPS_TO_MPH, "mi h-1": 1.0/ConversionFactors.MPS_TO_MPH,
"ft s-1" : 1.0/ConversionFactors.MPS_TO_FPS "ft s-1": 1.0/ConversionFactors.MPS_TO_FPS
} }
# Conversion factor map for pressure from base units # Conversion factor map for pressure from base units
_PRES_BASE_FACTORS = {"hpa" : ConversionFactors.PA_TO_HPA, _PRES_BASE_FACTORS = {"hpa": ConversionFactors.PA_TO_HPA,
"mb" : ConversionFactors.PA_TO_HPA, "mb": ConversionFactors.PA_TO_HPA,
"torr" : ConversionFactors.PA_TO_TORR, "torr": ConversionFactors.PA_TO_TORR,
"mmhg" : ConversionFactors.PA_TO_MMHG, "mmhg": ConversionFactors.PA_TO_MMHG,
"atm" : ConversionFactors.PA_TO_ATM "atm": ConversionFactors.PA_TO_ATM
} }
# Conversion factor map for pressure to base units # Conversion factor map for pressure to base units
_PRES_TOBASE_FACTORS = {"hpa" : 1.0/ConversionFactors.PA_TO_HPA, _PRES_TOBASE_FACTORS = {"hpa": 1.0/ConversionFactors.PA_TO_HPA,
"mb" : 1.0/ConversionFactors.PA_TO_HPA, "mb": 1.0/ConversionFactors.PA_TO_HPA,
"torr" : 1.0/ConversionFactors.PA_TO_TORR, "torr": 1.0/ConversionFactors.PA_TO_TORR,
"mmhg" : 1.0/ConversionFactors.PA_TO_MMHG, "mmhg": 1.0/ConversionFactors.PA_TO_MMHG,
"atm" : 1.0/ConversionFactors.PA_TO_ATM "atm": 1.0/ConversionFactors.PA_TO_ATM
} }
# Conversion factor map for height from base units # Conversion factor map for height from base units
_HEIGHT_BASE_FACTORS = {"km" : ConversionFactors.M_TO_KM, _HEIGHT_BASE_FACTORS = {"km": ConversionFactors.M_TO_KM,
"dm" : ConversionFactors.M_TO_DM, "dm": ConversionFactors.M_TO_DM,
"ft" : ConversionFactors.M_TO_FT, "ft": ConversionFactors.M_TO_FT,
"mi" : ConversionFactors.M_TO_MILES "mi": ConversionFactors.M_TO_MILES
} }
# Conversion factor map for height to base units # Conversion factor map for height to base units
_HEIGHT_TOBASE_FACTORS = {"km" : 1.0/ConversionFactors.M_TO_KM, _HEIGHT_TOBASE_FACTORS = {"km": 1.0/ConversionFactors.M_TO_KM,
"dm" : 1.0/ConversionFactors.M_TO_DM, "dm": 1.0/ConversionFactors.M_TO_DM,
"ft" : 1.0/ConversionFactors.M_TO_FT, "ft": 1.0/ConversionFactors.M_TO_FT,
"mi" : 1.0/ConversionFactors.M_TO_MILES "mi": 1.0/ConversionFactors.M_TO_MILES
} }
# Mapping of unit type to base unit type # Mapping of unit type to base unit type
_BASE_UNITS = {"wind" : "m s-1", _BASE_UNITS = {"wind": "m s-1",
"pressure" : "pa", "pressure": "pa",
"temp" : "k", "temp": "k",
"height" : "m" "height": "m"
} }
# A mapping of unit type to a mapping of to/from base conversion factors # A mapping of unit type to a mapping of to/from base conversion factors
_CONV_FACTORS = {"wind" : {"to_dest" : _WIND_BASE_FACTORS, _CONV_FACTORS = {"wind": {"to_dest": _WIND_BASE_FACTORS,
"to_base" : _WIND_TOBASE_FACTORS}, "to_base": _WIND_TOBASE_FACTORS},
"pressure" : {"to_dest" : _PRES_BASE_FACTORS, "pressure": {"to_dest": _PRES_BASE_FACTORS,
"to_base" : _PRES_TOBASE_FACTORS}, "to_base": _PRES_TOBASE_FACTORS},
"height" : {"to_dest" : _HEIGHT_BASE_FACTORS, "height": {"to_dest": _HEIGHT_BASE_FACTORS,
"to_base" : _HEIGHT_TOBASE_FACTORS} "to_base": _HEIGHT_TOBASE_FACTORS}
} }
# A mapping of temperature type to the conversion function # A mapping of temperature type to the conversion function
_TEMP_CONV_METHODS = {"c" : _k_to_c, _TEMP_CONV_METHODS = {"c": _k_to_c,
"f" : _k_to_f "f": _k_to_f
} }
def dealias_and_clean_unit(unit): def dealias_and_clean_unit(unit):
"""Return the properly cleaned and dealiased unit name. """Return the properly cleaned and dealiased unit name.
@ -336,7 +331,7 @@ def check_units(unit, unit_type):
""" """
u_cleaned = dealias_and_clean_unit(unit) u_cleaned = dealias_and_clean_unit(unit)
if u_cleaned not in _VALID_UNITS[unit_type]: if u_cleaned not in _VALID_UNITS[unit_type]:
raise ValueError("invalid unit type '%s'" % unit) raise ValueError("invalid unit type '{}'".format(unit))
def do_conversion(var, vartype, var_unit, dest_unit): def do_conversion(var, vartype, var_unit, dest_unit):
@ -365,8 +360,3 @@ def do_conversion(var, vartype, var_unit, dest_unit):
return _apply_conv_fact(var, vartype, var_unit.lower(), u_cleaned) return _apply_conv_fact(var, vartype, var_unit.lower(), u_cleaned)
else: else:
return _apply_temp_conv(var, var_unit.lower(), u_cleaned) return _apply_temp_conv(var, var_unit.lower(), u_cleaned)

219
src/wrf/util.py

@ -41,16 +41,16 @@ if xarray_enabled():
from xarray import DataArray from xarray import DataArray
_COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"), _COORD_PAIR_MAP = {"XLAT": ("XLAT", "XLONG"),
"XLONG" : ("XLAT", "XLONG"), "XLONG": ("XLAT", "XLONG"),
"XLAT_M" : ("XLAT_M", "XLONG_M"), "XLAT_M": ("XLAT_M", "XLONG_M"),
"XLONG_M" : ("XLAT_M", "XLONG_M"), "XLONG_M": ("XLAT_M", "XLONG_M"),
"XLAT_U" : ("XLAT_U", "XLONG_U"), "XLAT_U": ("XLAT_U", "XLONG_U"),
"XLONG_U" : ("XLAT_U", "XLONG_U"), "XLONG_U": ("XLAT_U", "XLONG_U"),
"XLAT_V" : ("XLAT_V", "XLONG_V"), "XLAT_V": ("XLAT_V", "XLONG_V"),
"XLONG_V" : ("XLAT_V", "XLONG_V"), "XLONG_V": ("XLAT_V", "XLONG_V"),
"CLAT" : ("CLAT", "CLONG"), "CLAT": ("CLAT", "CLONG"),
"CLONG" : ("CLAT", "CLONG")} "CLONG": ("CLAT", "CLONG")}
_COORD_VARS = ("XLAT", "XLONG", "XLAT_M", "XLONG_M", "XLAT_U", "XLONG_U", _COORD_VARS = ("XLAT", "XLONG", "XLAT_M", "XLONG_M", "XLAT_U", "XLONG_U",
@ -58,7 +58,7 @@ _COORD_VARS = ("XLAT", "XLONG", "XLAT_M", "XLONG_M", "XLAT_U", "XLONG_U",
_LAT_COORDS = ("XLAT", "XLAT_M", "XLAT_U", "XLAT_V", "CLAT") _LAT_COORDS = ("XLAT", "XLAT_M", "XLAT_U", "XLAT_V", "CLAT")
_LON_COORDS = ("XLONG", "XLONG_M", "XLONG_U","XLONG_V", "CLONG") _LON_COORDS = ("XLONG", "XLONG_M", "XLONG_U", "XLONG_V", "CLONG")
_TIME_COORD_VARS = ("XTIME",) _TIME_COORD_VARS = ("XTIME",)
@ -210,11 +210,11 @@ def _generator_copy(gen):
if module is not None: if module is not None:
try: try:
try: try:
argd = {key:argvals.locals[key] for key in argvals.args} argd = {key: argvals.locals[key] for key in argvals.args}
res = module.get(funcname)(**argd) res = module.get(funcname)(**argd)
except AttributeError: except AttributeError:
res = getattr(module, funcname)(**argd) res = getattr(module, funcname)(**argd)
except: except Exception:
# This is the old way it used to work, but it looks like this was # This is the old way it used to work, but it looks like this was
# fixed by Python. # fixed by Python.
try: try:
@ -226,9 +226,9 @@ def _generator_copy(gen):
import __main__ import __main__
try: try:
argd = {key:argvals.locals[key] for key in argvals.args} argd = {key: argvals.locals[key] for key in argvals.args}
res = getattr(__main__, funcname)(**argd) res = getattr(__main__, funcname)(**argd)
except: except Exception:
# This was the old way it used to work, but appears to have # This was the old way it used to work, but appears to have
# been fixed by Python. # been fixed by Python.
res = getattr(__main__, funcname)(**argvals.locals) res = getattr(__main__, funcname)(**argvals.locals)
@ -237,7 +237,7 @@ def _generator_copy(gen):
def test(): def test():
q = [1,2,3] q = [1, 2, 3]
for i in q: for i in q:
yield i yield i
@ -661,18 +661,18 @@ def _corners_moved(wrfnc, first_ll_corner, first_ur_corner, latvar, lonvar):
# Need to check all times # Need to check all times
for i in py3range(lats.shape[-3]): for i in py3range(lats.shape[-3]):
start_idxs = [0]*len(lats.shape) # PyNIO does not support ndim start_idxs = [0] * len(lats.shape) # PyNIO does not support ndim
start_idxs[-3] = i start_idxs[-3] = i
start_idxs = tuple(start_idxs) start_idxs = tuple(start_idxs)
end_idxs = [-1]*len(lats.shape) end_idxs = [-1] * len(lats.shape)
end_idxs[-3] = i end_idxs[-3] = i
end_idxs = tuple(end_idxs) end_idxs = tuple(end_idxs)
if (first_ll_corner[0] != lats[start_idxs] or if (first_ll_corner[0] != lats[start_idxs]
first_ll_corner[1] != lons[start_idxs] or or first_ll_corner[1] != lons[start_idxs]
first_ur_corner[0] != lats[end_idxs] or or first_ur_corner[0] != lats[end_idxs]
first_ur_corner[1] != lons[end_idxs]): or first_ur_corner[1] != lons[end_idxs]):
return True return True
return False return False
@ -757,8 +757,14 @@ def is_moving_domain(wrfin, varname=None, latvar=either("XLAT", "XLAT_M"),
lon_coord = lonvar lon_coord = lonvar
lat_coord = latvar lat_coord = latvar
else: else:
lon_coord = coord_names[0] for name in coord_names:
lat_coord = coord_names[1] if name in _LAT_COORDS:
lat_coord = name
continue
if name in _LON_COORDS:
lon_coord = name
continue
else: else:
lon_coord = lonvar lon_coord = lonvar
lat_coord = latvar lat_coord = latvar
@ -871,7 +877,7 @@ def extract_global_attrs(wrfin, attrs):
entry = wrfin[next(iter(viewkeys(wrfin)))] entry = wrfin[next(iter(viewkeys(wrfin)))]
return extract_global_attrs(entry, attrs) return extract_global_attrs(entry, attrs)
return {attr:_get_global_attr(wrfin, attr) for attr in attrlist} return {attr: _get_global_attr(wrfin, attr) for attr in attrlist}
def extract_dim(wrfin, dim): def extract_dim(wrfin, dim):
@ -901,15 +907,16 @@ def extract_dim(wrfin, dim):
d = wrfin.dimensions[dim] d = wrfin.dimensions[dim]
if not isinstance(d, int): if not isinstance(d, int):
try: try:
return len(d) #netCDF4 return len(d) # netCDF4
except TypeError: #scipy.io.netcdf except TypeError: # scipy.io.netcdf
# Scipy can't handled unlimited dimensions, so now we have to # Scipy can't handled unlimited dimensions, so now we have to
# figure it out # figure it out
try: try:
s = wrfin.variables["P"].shape s = wrfin.variables["P"].shape
return s[-4] except Exception:
except:
raise ValueError("unsupported NetCDF reader") raise ValueError("unsupported NetCDF reader")
else:
return s[-4]
return d # PyNIO return d # PyNIO
@ -983,7 +990,7 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta, _key):
outdims = [numkeys] outdims = [numkeys]
outdims += first_array.shape outdims += first_array.shape
outdata = np.empty(outdims, first_array.dtype) outdata = np.empty(outdims, first_array.dtype)
outdata[0,:] = first_array[:] outdata[0, :] = first_array[:]
idx = 1 idx = 1
while True: while True:
@ -1002,7 +1009,7 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta, _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 "
"same size for all dictionary keys") "same size for all dictionary keys")
outdata[idx,:] = to_np(vardata)[:] outdata[idx, :] = to_np(vardata)[:]
idx += 1 idx += 1
if xarray_enabled() and meta: if xarray_enabled() and meta:
@ -1038,12 +1045,10 @@ def _combine_dict(wrfdict, varname, timeidx, method, meta, _key):
# make it so that key_0 is leftmost # make it so that key_0 is leftmost
outdims = key_coordnames + list(first_array.dims[existing_cnt:]) outdims = key_coordnames + list(first_array.dims[existing_cnt:])
# Create the new 'key_n', value pairs # Create the new 'key_n', value pairs
for coordname, coordval in zip(key_coordnames, coord_vals): for coordname, coordval in zip(key_coordnames, coord_vals):
outcoords[coordname] = coordval outcoords[coordname] = coordval
outattrs = OrderedDict(first_array.attrs) outattrs = OrderedDict(first_array.attrs)
outarr = DataArray(outdata, name=outname, coords=outcoords, outarr = DataArray(outdata, name=outname, coords=outcoords,
@ -1181,15 +1186,22 @@ def _get_coord_names(wrfin, varname):
coord_names = coord_attr.split() coord_names = coord_attr.split()
else: else:
coord_names = coord_attr.decode().split() coord_names = coord_attr.decode().split()
lon_coord = coord_names[0]
lat_coord = coord_names[1]
try: for name in coord_names:
time_coord = coord_names[2] if name in _LAT_COORDS:
except IndexError: lat_coord = name
time_coord = None continue
else:
# Make sure they time variable wasn't removed if name in _LON_COORDS:
lon_coord = name
continue
if name in _TIME_COORD_VARS:
time_coord = name
continue
if time_coord is not None:
# Make sure the time variable wasn't removed
try: try:
_ = wrfnc.variables[time_coord] _ = wrfnc.variables[time_coord]
except KeyError: except KeyError:
@ -1279,7 +1291,6 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile,
except IndexError: except IndexError:
pass pass
coords = OrderedDict() coords = OrderedDict()
# Handle lat/lon coordinates and projection information if available # Handle lat/lon coordinates and projection information if available
@ -1341,17 +1352,17 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile,
else: else:
coords[lon_coord] = (lon_coord_dims[1:], coords[lon_coord] = (lon_coord_dims[1:],
lon_coord_vals[0,:]) lon_coord_vals[0, :])
coords[lat_coord] = (lat_coord_dims[1:], coords[lat_coord] = (lat_coord_dims[1:],
lat_coord_vals[0,:]) lat_coord_vals[0, :])
if time_coord is not None: if time_coord is not None:
coords[time_coord] = (lon_coord_dims[0], time_coord_vals) coords[time_coord] = (lon_coord_dims[0], time_coord_vals)
else: else:
coords[lon_coord] = (lon_coord_dims[1:], coords[lon_coord] = (lon_coord_dims[1:],
lon_coord_vals[timeidx,:]) lon_coord_vals[timeidx, :])
coords[lat_coord] = (lat_coord_dims[1:], coords[lat_coord] = (lat_coord_dims[1:],
lat_coord_vals[timeidx,:]) lat_coord_vals[timeidx, :])
if time_coord is not None: if time_coord is not None:
coords[time_coord] = (lon_coord_dims[0], coords[time_coord] = (lon_coord_dims[0],
@ -1605,8 +1616,6 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
return _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta, return _find_arr_for_time(wrfseq, varname, timeidx, is_moving, meta,
_key) _key)
#time_idx_or_slice = timeidx if not multitime else slice(None)
# If all times are requested, need to build a new array and cat together # If all times are requested, need to build a new array and cat together
# all of the arrays in the sequence # all of the arrays in the sequence
wrf_iter = iter(wrfseq) wrf_iter = iter(wrfseq)
@ -1652,7 +1661,8 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
outxtimes = get_cached_item(_key, timekey) outxtimes = get_cached_item(_key, timekey)
if outxtimes is None: if outxtimes is None:
outxtimes = np.empty(outdims[0]) outxtimes = np.empty(outdims[0])
outxtimes[startidx:endidx] = to_np(first_var.coords[timename][:]) outxtimes[startidx:endidx] = to_np(
first_var.coords[timename][:])
else: else:
timecached = True timecached = True
@ -1664,7 +1674,8 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
outlats = get_cached_item(_key, latkey) outlats = get_cached_item(_key, latkey)
if outlats is None: if outlats is None:
outlats = np.empty(outcoorddims, first_var.dtype) outlats = np.empty(outcoorddims, first_var.dtype)
outlats[startidx:endidx, :] = to_np(first_var.coords[latname][:]) outlats[startidx:endidx, :] = to_np(
first_var.coords[latname][:])
else: else:
latcached = True latcached = True
@ -1672,11 +1683,11 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key):
outlons = get_cached_item(_key, lonkey) outlons = get_cached_item(_key, lonkey)
if outlons is None: if outlons is None:
outlons = np.empty(outcoorddims, first_var.dtype) outlons = np.empty(outcoorddims, first_var.dtype)
outlons[startidx:endidx, :] = to_np(first_var.coords[lonname][:]) outlons[startidx:endidx, :] = to_np(
first_var.coords[lonname][:])
else: else:
loncached = True loncached = True
startidx = endidx startidx = endidx
while True: while True:
try: try:
@ -1927,7 +1938,7 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key):
else: else:
loncached = True loncached = True
file_idx=1 file_idx = 1
while True: while True:
try: try:
wrfnc = next(wrf_iter) wrfnc = next(wrf_iter)
@ -1951,8 +1962,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key):
# For join, the times are a function of fileidx # For join, the times are a function of fileidx
file_times = extract_times(wrfnc, ALL_TIMES, meta=False, file_times = extract_times(wrfnc, ALL_TIMES, meta=False,
do_xtime=False) do_xtime=False)
time_coord[file_idx, 0:numtimes] = np.asarray(file_times, time_coord[file_idx, 0:numtimes] = np.asarray(
"datetime64[ns]")[:] file_times, "datetime64[ns]")[:]
if timename is not None and not timecached: if timename is not None and not timecached:
xtimedata = wrfnc.variables[timename][:] xtimedata = wrfnc.variables[timename][:]
@ -2206,7 +2217,7 @@ def _extract_var(wrfin, varname, timeidx, is_moving,
multifile, _key) multifile, _key)
else: else:
if not multitime: if not multitime:
result = wrfin.variables[varname][timeidx,:] result = wrfin.variables[varname][timeidx, :]
result = result[np.newaxis, :] # So that no squeeze works result = result[np.newaxis, :] # So that no squeeze works
else: else:
result = wrfin.variables[varname][:] result = wrfin.variables[varname][:]
@ -2275,7 +2286,7 @@ def extract_vars(wrfin, timeidx, varnames, method="cat", squeeze=True,
else: else:
varlist = varnames varlist = varnames
return {var:_extract_var(wrfin, var, timeidx, None, return {var: _extract_var(wrfin, var, timeidx, None,
method, squeeze, cache, meta, _key) method, squeeze, cache, meta, _key)
for var in varlist} for var in varlist}
@ -2338,9 +2349,9 @@ def _file_times(wrfin, do_xtime):
""" """
if not do_xtime: if not do_xtime:
times = wrfin.variables["Times"][:,:] times = wrfin.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:
xtimes = wrfin.variables["XTIME"][:] xtimes = wrfin.variables["XTIME"][:]
for i in py3range(xtimes.shape[0]): for i in py3range(xtimes.shape[0]):
@ -2377,7 +2388,7 @@ def _extract_time_map(wrfin, timeidx, do_xtime, meta=False):
otherwise the sequence is :class:`numpy.ndarray`. otherwise the sequence is :class:`numpy.ndarray`.
""" """
return {key : extract_times(wrfseq, timeidx, do_xtime, meta) return {key: extract_times(wrfseq, timeidx, do_xtime, meta)
for key, wrfseq in viewitems(wrfin)} for key, wrfseq in viewitems(wrfin)}
@ -2465,12 +2476,12 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None,
num_cols = len(time_list[0]) num_cols = len(time_list[0])
time_arr = np.full((num_rows, num_cols), fill_value, dtype=dt) time_arr = np.full((num_rows, num_cols), fill_value, dtype=dt)
for i,row in enumerate(time_list): for i, row in enumerate(time_list):
if len(row) == num_cols: if len(row) == num_cols:
time_arr[i,:] = row[:] time_arr[i, :] = row[:]
else: else:
for j,val in enumerate(row): for j, val in enumerate(row):
time_arr[i,j] = val time_arr[i, j] = val
time_arr = ma.masked_values(time_arr, fill_value) time_arr = ma.masked_values(time_arr, fill_value)
@ -2501,10 +2512,8 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None,
outname = "XTIME" outname = "XTIME"
outarr = DataArray(time_arr, name=outname, coords=outcoords, outarr = DataArray(time_arr, name=outname, coords=outcoords,
dims=outdimnames, attrs=outattrs) dims=outdimnames, attrs=outattrs)
else: else:
outarr = time_arr outarr = time_arr
@ -2657,7 +2666,7 @@ def get_right_slices(var, right_ndims, fixed_val=0):
[slice(None)]*right_ndims) [slice(None)]*right_ndims)
def get_proj_params(wrfin):#, timeidx=0, varname=None): def get_proj_params(wrfin):
"""Return a tuple of latitude, longitude, and projection parameters from """Return a tuple of latitude, longitude, and projection parameters from
a WRF output file object or a sequence of WRF output file objects. a WRF output file object or a sequence of WRF output file objects.
@ -2684,7 +2693,8 @@ def get_proj_params(wrfin):#, timeidx=0, varname=None):
longitude coordinate, and global projection attributes. longitude coordinate, and global projection attributes.
""" """
proj_params = extract_global_attrs(wrfin, attrs=("MAP_PROJ", proj_params = extract_global_attrs(wrfin,
attrs=("MAP_PROJ",
"CEN_LAT", "CEN_LON", "CEN_LAT", "CEN_LON",
"TRUELAT1", "TRUELAT2", "TRUELAT1", "TRUELAT2",
"MOAD_CEN_LAT", "STAND_LON", "MOAD_CEN_LAT", "STAND_LON",
@ -2697,7 +2707,7 @@ def get_proj_params(wrfin):#, timeidx=0, varname=None):
def from_args(func, argnames, *args, **kwargs): def from_args(func, argnames, *args, **kwargs):
"""Return a mapping of argument name to value for the called function. """Return a mapping of argument name to value for the called function.
This function parses the function \*args and \*\*kwargs to obtain the \ This function parses the function args and kwargs to obtain the
desired argument value. If the argument has not been passed in, the value desired argument value. If the argument has not been passed in, the value
is taken from the default keyword argument value. is taken from the default keyword argument value.
@ -2706,7 +2716,7 @@ def from_args(func, argnames, *args, **kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
\*args or \*\*kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -2750,7 +2760,7 @@ def _args_to_list2(func, args, kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
*args or **kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -2771,15 +2781,15 @@ def _args_to_list2(func, args, kwargs):
# Build the full tuple with defaults filled in # Build the full tuple with defaults filled in
outargs = [None]*len(argspec.args) outargs = [None]*len(argspec.args)
if argspec.defaults is not None: if argspec.defaults is not None:
for i,default in enumerate(argspec.defaults[::-1], 1): for i, default in enumerate(argspec.defaults[::-1], 1):
outargs[-i] = default outargs[-i] = default
# Add the supplied args # Add the supplied args
for i,arg in enumerate(args): for i, arg in enumerate(args):
outargs[i] = arg outargs[i] = arg
# Fill in the supplied kargs # Fill in the supplied kargs
for argname,val in viewitems(kwargs): for argname, val in viewitems(kwargs):
argidx = argspec.args.index(argname) argidx = argspec.args.index(argname)
outargs[argidx] = val outargs[argidx] = val
@ -2837,7 +2847,7 @@ def _args_to_list3(func, args, kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
*args or **kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -2872,7 +2882,7 @@ def args_to_list(func, args, kwargs):
Note: Note:
This function currently does not work with functions that contain This function currently does not work with functions that contain
\*args or \*\*kwargs arguments. variable length args or kwargs arguments.
Args: Args:
@ -3026,13 +3036,27 @@ def psafilepath():
def get_filepath(obj): def get_filepath(obj):
"""Return the file path for the specified object.
This is used to return the file path for a netcdf object. If the
particular object does not have the appropriate file path information,
then one is created based on the timestep in the file.
Args:
obj: An object.
Returns:
:obj:`str`: A string for a file path.
"""
try: try:
path = obj.filepath() path = obj.filepath()
except AttributeError: except AttributeError:
try: try:
path = obj.file.path path = obj.file.path
except: except AttributeError:
# Let's make up a filename from the first file time # Let's make up a filename from the first file time
found = False found = False
times = extract_times(obj, None, meta=False, do_xtime=False) times = extract_times(obj, None, meta=False, do_xtime=False)
@ -3046,6 +3070,7 @@ def get_filepath(obj):
return path return path
def get_id(obj, prefix=''): def get_id(obj, prefix=''):
"""Return the cache id. """Return the cache id.
@ -3078,7 +3103,7 @@ def get_id(obj, prefix=''):
# For each key in the mapping, recursively call get_id until # For each key in the mapping, recursively call get_id until
# until a non-mapping is found # until a non-mapping is found
return {key : get_id(val, prefix) for key,val in viewitems(obj)} return {key: get_id(val, prefix) for key, val in viewitems(obj)}
def geo_bounds(var=None, wrfin=None, varname=None, timeidx=0, method="cat", def geo_bounds(var=None, wrfin=None, varname=None, timeidx=0, method="cat",
@ -3221,6 +3246,7 @@ def geo_bounds(var=None, wrfin=None, varname=None, timeidx=0, method="cat",
# Non-moving domains # Non-moving domains
return GeoBounds(lats=lats, lons=lons) return GeoBounds(lats=lats, lons=lons)
def _get_wrf_proj_geobnds(var, wrfin, varname, timeidx, method, squeeze, def _get_wrf_proj_geobnds(var, wrfin, varname, timeidx, method, squeeze,
cache): cache):
"""Return the :class:`wrf.WrfProj` subclass and :class:`wrf.GeoBounds`. """Return the :class:`wrf.WrfProj` subclass and :class:`wrf.GeoBounds`.
@ -3724,7 +3750,8 @@ def cartopy_xlim(var=None, geobounds=None, wrfin=None, varname=None, timeidx=0,
""" """
wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname, wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname,
timeidx, method, squeeze, cache) timeidx, method, squeeze,
cache)
if geobounds is not None: if geobounds is not None:
return wrf_proj.cartopy_xlim(geobounds) return wrf_proj.cartopy_xlim(geobounds)
@ -3810,7 +3837,8 @@ def cartopy_ylim(var=None, geobounds=None, wrfin=None, varname=None, timeidx=0,
""" """
wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname, wrf_proj, native_geobnds = _get_wrf_proj_geobnds(var, wrfin, varname,
timeidx, method, squeeze, cache) timeidx, method, squeeze,
cache)
if geobounds is not None: if geobounds is not None:
return wrf_proj.cartopy_ylim(geobounds) return wrf_proj.cartopy_ylim(geobounds)
@ -3841,8 +3869,8 @@ def ll_points(lat, lon):
object or a list of :class:`wrf.CoordPair` objects. object or a list of :class:`wrf.CoordPair` objects.
""" """
latvals = np.ravel(to_np(lat)[...,0,0]) latvals = np.ravel(to_np(lat)[..., 0, 0])
lonvals = np.ravel(to_np(lon)[...,0,0]) lonvals = np.ravel(to_np(lon)[..., 0, 0])
if latvals.shape[0] == 1: if latvals.shape[0] == 1:
return CoordPair(lat=float(latvals), lon=float(lonvals)) return CoordPair(lat=float(latvals), lon=float(lonvals))
@ -3897,30 +3925,3 @@ def is_latlon_pair(pair):
return (pair.lat is not None and pair.lon is not None) return (pair.lat is not None and pair.lon is not None)
else: else:
return False return False

1
src/wrf/version.py

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

Loading…
Cancel
Save